@fluxy-chat/create-fluxy-chat 0.5.9 → 0.5.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/index.js +111 -1
  3. package/package.json +1 -1
  4. package/readme.md +11 -0
  5. package/templates/basic/package.json +1 -1
  6. package/templates/comments-board/.env.example +11 -0
  7. package/templates/comments-board/README.md +9 -0
  8. package/templates/comments-board/index.html +12 -0
  9. package/templates/comments-board/package.json +26 -0
  10. package/templates/comments-board/src/App.tsx +86 -0
  11. package/templates/comments-board/src/index.css +75 -0
  12. package/templates/comments-board/src/main.tsx +10 -0
  13. package/templates/comments-board/src/session.ts +73 -0
  14. package/templates/comments-board/src/vite-env.d.ts +13 -0
  15. package/templates/comments-board/tsconfig.json +21 -0
  16. package/templates/comments-board/vite.config.ts +7 -0
  17. package/templates/deal-room/.env.example +11 -0
  18. package/templates/deal-room/README.md +11 -0
  19. package/templates/deal-room/index.html +12 -0
  20. package/templates/deal-room/package.json +25 -0
  21. package/templates/deal-room/src/App.tsx +110 -0
  22. package/templates/deal-room/src/index.css +75 -0
  23. package/templates/deal-room/src/main.tsx +10 -0
  24. package/templates/deal-room/src/session.ts +73 -0
  25. package/templates/deal-room/src/vite-env.d.ts +13 -0
  26. package/templates/deal-room/tsconfig.json +21 -0
  27. package/templates/deal-room/vite.config.ts +7 -0
  28. package/templates/discord/package.json +1 -1
  29. package/templates/draw/.env.example +7 -0
  30. package/templates/draw/README.md +7 -0
  31. package/templates/draw/index.html +12 -0
  32. package/templates/draw/package.json +25 -0
  33. package/templates/draw/src/App.tsx +91 -0
  34. package/templates/draw/src/index.css +75 -0
  35. package/templates/draw/src/main.tsx +10 -0
  36. package/templates/draw/src/session.ts +73 -0
  37. package/templates/draw/src/vite-env.d.ts +13 -0
  38. package/templates/draw/tsconfig.json +21 -0
  39. package/templates/draw/vite.config.ts +7 -0
  40. package/templates/fleet-panel/.env.example +11 -0
  41. package/templates/fleet-panel/README.md +9 -0
  42. package/templates/fleet-panel/index.html +12 -0
  43. package/templates/fleet-panel/package.json +25 -0
  44. package/templates/fleet-panel/src/App.tsx +83 -0
  45. package/templates/fleet-panel/src/index.css +75 -0
  46. package/templates/fleet-panel/src/main.tsx +10 -0
  47. package/templates/fleet-panel/src/session.ts +73 -0
  48. package/templates/fleet-panel/src/vite-env.d.ts +13 -0
  49. package/templates/fleet-panel/tsconfig.json +21 -0
  50. package/templates/fleet-panel/vite.config.ts +7 -0
  51. package/templates/full/package.json +4 -4
  52. package/templates/full/scripts/hoist-fluxy-sdk.mjs +1 -1
  53. package/templates/full/src/App.tsx +20 -6
  54. package/templates/game-tick/.env.example +11 -0
  55. package/templates/game-tick/README.md +9 -0
  56. package/templates/game-tick/index.html +12 -0
  57. package/templates/game-tick/package.json +25 -0
  58. package/templates/game-tick/src/App.tsx +87 -0
  59. package/templates/game-tick/src/index.css +75 -0
  60. package/templates/game-tick/src/main.tsx +10 -0
  61. package/templates/game-tick/src/session.ts +73 -0
  62. package/templates/game-tick/src/vite-env.d.ts +13 -0
  63. package/templates/game-tick/tsconfig.json +21 -0
  64. package/templates/game-tick/vite.config.ts +7 -0
  65. package/templates/iot-panel/.env.example +7 -0
  66. package/templates/iot-panel/README.md +16 -0
  67. package/templates/iot-panel/index.html +12 -0
  68. package/templates/iot-panel/package.json +25 -0
  69. package/templates/iot-panel/src/App.tsx +97 -0
  70. package/templates/iot-panel/src/index.css +75 -0
  71. package/templates/iot-panel/src/main.tsx +10 -0
  72. package/templates/iot-panel/src/session.ts +73 -0
  73. package/templates/iot-panel/src/vite-env.d.ts +13 -0
  74. package/templates/iot-panel/tsconfig.json +21 -0
  75. package/templates/iot-panel/vite.config.ts +7 -0
  76. package/templates/javascript-live-cursors/.env.example +9 -0
  77. package/templates/javascript-live-cursors/README.md +9 -0
  78. package/templates/javascript-live-cursors/index.html +17 -0
  79. package/templates/javascript-live-cursors/package.json +18 -0
  80. package/templates/javascript-live-cursors/src/index.css +31 -0
  81. package/templates/javascript-live-cursors/src/main.ts +89 -0
  82. package/templates/javascript-live-cursors/src/vite-env.d.ts +12 -0
  83. package/templates/javascript-live-cursors/tsconfig.json +13 -0
  84. package/templates/javascript-live-cursors/vite.config.ts +5 -0
  85. package/templates/live-cursors/.env.example +9 -0
  86. package/templates/live-cursors/README.md +20 -0
  87. package/templates/live-cursors/index.html +12 -0
  88. package/templates/live-cursors/package.json +25 -0
  89. package/templates/live-cursors/src/App.tsx +214 -0
  90. package/templates/live-cursors/src/index.css +94 -0
  91. package/templates/live-cursors/src/main.tsx +10 -0
  92. package/templates/live-cursors/src/vite-env.d.ts +12 -0
  93. package/templates/live-cursors/tsconfig.json +21 -0
  94. package/templates/live-cursors/vite.config.ts +7 -0
  95. package/templates/live-cursors-chat/.env.example +9 -0
  96. package/templates/live-cursors-chat/README.md +11 -0
  97. package/templates/live-cursors-chat/index.html +12 -0
  98. package/templates/live-cursors-chat/package.json +25 -0
  99. package/templates/live-cursors-chat/src/App.tsx +241 -0
  100. package/templates/live-cursors-chat/src/index.css +94 -0
  101. package/templates/live-cursors-chat/src/main.tsx +10 -0
  102. package/templates/live-cursors-chat/src/vite-env.d.ts +12 -0
  103. package/templates/live-cursors-chat/tsconfig.json +21 -0
  104. package/templates/live-cursors-chat/vite.config.ts +7 -0
  105. package/templates/minimal/package.json +4 -4
  106. package/templates/react/package.json +2 -2
  107. package/templates/react/src/App.tsx +8 -2
  108. package/templates/slack/package.json +1 -1
  109. package/templates/telegram/package.json +1 -1
  110. package/templates/tiptap-room/.env.example +9 -0
  111. package/templates/tiptap-room/README.md +14 -0
  112. package/templates/tiptap-room/index.html +12 -0
  113. package/templates/tiptap-room/package.json +29 -0
  114. package/templates/tiptap-room/src/App.tsx +217 -0
  115. package/templates/tiptap-room/src/index.css +107 -0
  116. package/templates/tiptap-room/src/main.tsx +10 -0
  117. package/templates/tiptap-room/src/vite-env.d.ts +12 -0
  118. package/templates/tiptap-room/tsconfig.json +21 -0
  119. package/templates/tiptap-room/vite.config.ts +7 -0
  120. package/templates/voice-stage/.env.example +11 -0
  121. package/templates/voice-stage/README.md +11 -0
  122. package/templates/voice-stage/index.html +12 -0
  123. package/templates/voice-stage/package.json +25 -0
  124. package/templates/voice-stage/src/App.tsx +61 -0
  125. package/templates/voice-stage/src/index.css +75 -0
  126. package/templates/voice-stage/src/main.tsx +10 -0
  127. package/templates/voice-stage/src/session.ts +73 -0
  128. package/templates/voice-stage/src/vite-env.d.ts +13 -0
  129. package/templates/voice-stage/tsconfig.json +21 -0
  130. package/templates/voice-stage/vite.config.ts +7 -0
  131. package/templates/war-room/.env.example +11 -0
  132. package/templates/war-room/README.md +11 -0
  133. package/templates/war-room/index.html +12 -0
  134. package/templates/war-room/package.json +26 -0
  135. package/templates/war-room/src/App.tsx +120 -0
  136. package/templates/war-room/src/index.css +75 -0
  137. package/templates/war-room/src/main.tsx +10 -0
  138. package/templates/war-room/src/session.ts +73 -0
  139. package/templates/war-room/src/vite-env.d.ts +13 -0
  140. package/templates/war-room/tsconfig.json +21 -0
  141. package/templates/war-room/vite.config.ts +7 -0
  142. package/templates/whiteboard/.env.example +11 -0
  143. package/templates/whiteboard/README.md +9 -0
  144. package/templates/whiteboard/index.html +12 -0
  145. package/templates/whiteboard/package.json +26 -0
  146. package/templates/whiteboard/src/App.tsx +122 -0
  147. package/templates/whiteboard/src/index.css +75 -0
  148. package/templates/whiteboard/src/main.tsx +10 -0
  149. package/templates/whiteboard/src/session.ts +73 -0
  150. package/templates/whiteboard/src/vite-env.d.ts +13 -0
  151. package/templates/whiteboard/tsconfig.json +21 -0
  152. package/templates/whiteboard/vite.config.ts +7 -0
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
5
+ "module": "ESNext",
6
+ "skipLibCheck": true,
7
+ "moduleResolution": "bundler",
8
+ "isolatedModules": true,
9
+ "noEmit": true,
10
+ "strict": true
11
+ },
12
+ "include": ["src"]
13
+ }
@@ -0,0 +1,5 @@
1
+ import { defineConfig } from "vite";
2
+
3
+ export default defineConfig({
4
+ server: { port: 5173 },
5
+ });
@@ -0,0 +1,9 @@
1
+ # FluxyChat worker URL (hosted or self-hosted)
2
+ VITE_FLUXYCHAT_WORKER_URL=https://your-worker.example.workers.dev
3
+
4
+ # Option A — member JWT (~2 min via onboarding)
5
+ VITE_FLUXYCHAT_MEMBER_JWT=eyJ...
6
+ VITE_FLUXYCHAT_ROOM_ID=demo
7
+
8
+ # Option B — public guest room (~60s, no signup)
9
+ # VITE_FLUXYCHAT_PUBLIC_ROOM_ID=your-public-room-id
@@ -0,0 +1,20 @@
1
+ # FluxyChat live cursors
2
+
3
+ Presence pointers on the room WebSocket (`type: "cursor"`). Not `client_event`. Open **two tabs**.
4
+
5
+ ```bash
6
+ npx @fluxy-chat/create-fluxy-chat@latest my-cursors --example live-cursors
7
+ cp .env.example .env
8
+ # VITE_FLUXYCHAT_WORKER_URL + PUBLIC_ROOM_ID or MEMBER_JWT
9
+ npm run dev
10
+ ```
11
+
12
+ ## How to try
13
+
14
+ 1. Set Worker URL and a public room id (guest) or member JWT.
15
+ 2. Open http://localhost:5173 in two tabs (or two browsers).
16
+ 3. Move the pointer. Peers see a labeled cursor.
17
+
18
+ ## Code
19
+
20
+ `useChat({ roomId, replay: "request" })` returns `sendCursor` and `liveCursors`. Same socket as chat — do not add a second realtime vendor.
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>FluxyChat live cursors</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "fluxy-live-cursors",
3
+ "private": true,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc -b && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@fluxy-chat/react": "^0.1.4",
13
+ "@fluxy-chat/sdk": "^0.6.4",
14
+ "react": "^19.0.0",
15
+ "react-dom": "^19.0.0",
16
+ "zustand": "^5.0.0"
17
+ },
18
+ "devDependencies": {
19
+ "@types/react": "^19.0.0",
20
+ "@types/react-dom": "^19.0.0",
21
+ "@vitejs/plugin-react": "^4.3.0",
22
+ "typescript": "^5.6.0",
23
+ "vite": "^6.0.0"
24
+ }
25
+ }
@@ -0,0 +1,214 @@
1
+ import { useEffect, useMemo, useState } from "react";
2
+ import { FluxyChatClient } from "@fluxy-chat/sdk";
3
+ import { FluxyRealtimeProvider, useChat } from "@fluxy-chat/react";
4
+
5
+ const workerUrl = import.meta.env.VITE_FLUXYCHAT_WORKER_URL?.trim();
6
+ const memberJwt = import.meta.env.VITE_FLUXYCHAT_MEMBER_JWT?.trim();
7
+ const publicRoomId = import.meta.env.VITE_FLUXYCHAT_PUBLIC_ROOM_ID?.trim();
8
+ const configuredRoomId = import.meta.env.VITE_FLUXYCHAT_ROOM_ID?.trim() || "demo";
9
+
10
+ const CURSOR_COLORS = ["#2563eb", "#db2777", "#059669", "#d97706", "#7c3aed"];
11
+
12
+ interface FluxySession {
13
+ workerUrl: string;
14
+ token: string;
15
+ userId: string;
16
+ roomId: string;
17
+ mode: "member" | "guest";
18
+ }
19
+
20
+ function useFluxySession(): {
21
+ session: FluxySession | null;
22
+ loading: boolean;
23
+ error: string | null;
24
+ } {
25
+ const [session, setSession] = useState<FluxySession | null>(null);
26
+ const [loading, setLoading] = useState(Boolean(workerUrl));
27
+ const [error, setError] = useState<string | null>(null);
28
+
29
+ useEffect(() => {
30
+ if (!workerUrl) {
31
+ setLoading(false);
32
+ return;
33
+ }
34
+
35
+ if (memberJwt) {
36
+ setSession({
37
+ workerUrl,
38
+ token: memberJwt,
39
+ userId: "demo-user",
40
+ roomId: configuredRoomId,
41
+ mode: "member",
42
+ });
43
+ setLoading(false);
44
+ return;
45
+ }
46
+
47
+ if (publicRoomId) {
48
+ let cancelled = false;
49
+ void FluxyChatClient.joinPublicRoomAsGuest(workerUrl, publicRoomId, {
50
+ displayName: "Guest",
51
+ })
52
+ .then((guest) => {
53
+ if (cancelled) return;
54
+ setSession({
55
+ workerUrl: workerUrl,
56
+ token: guest.token,
57
+ userId: guest.userId,
58
+ roomId: guest.roomId,
59
+ mode: "guest",
60
+ });
61
+ })
62
+ .catch((err) => {
63
+ if (!cancelled) {
64
+ setError(err instanceof Error ? err.message : "Guest session failed");
65
+ }
66
+ })
67
+ .finally(() => {
68
+ if (!cancelled) setLoading(false);
69
+ });
70
+ return () => {
71
+ cancelled = true;
72
+ };
73
+ }
74
+
75
+ setLoading(false);
76
+ }, []);
77
+
78
+ return { session, loading, error };
79
+ }
80
+
81
+ function CursorCanvas({
82
+ roomId,
83
+ selfUserId,
84
+ }: {
85
+ roomId: string;
86
+ selfUserId: string;
87
+ }) {
88
+ const { liveCursors, sendCursor, connected, connectionState } = useChat({
89
+ roomId,
90
+ replay: "request",
91
+ });
92
+ const color = CURSOR_COLORS[Math.abs(hashCode(selfUserId)) % CURSOR_COLORS.length];
93
+
94
+ return (
95
+ <section className="canvas-wrap">
96
+ <header className="chat-header">
97
+ <strong>{roomId}</strong>
98
+ <span className="status">
99
+ {connected ? "live" : connectionState.status} — open a second tab
100
+ </span>
101
+ </header>
102
+ <div
103
+ className="canvas"
104
+ onPointerMove={(event) => {
105
+ const rect = event.currentTarget.getBoundingClientRect();
106
+ sendCursor({
107
+ x: event.clientX - rect.left,
108
+ y: event.clientY - rect.top,
109
+ color,
110
+ label: selfUserId.slice(0, 12),
111
+ });
112
+ }}
113
+ onPointerLeave={() => {
114
+ /* last position stays until stale on peers */
115
+ }}
116
+ >
117
+ {Object.values(liveCursors)
118
+ .filter((c) => c.userId !== selfUserId)
119
+ .map((cursor) => (
120
+ <div
121
+ key={cursor.userId}
122
+ className="peer-cursor"
123
+ style={{
124
+ left: cursor.x,
125
+ top: cursor.y,
126
+ color: cursor.color || "#2563eb",
127
+ }}
128
+ >
129
+ <svg width="18" height="18" viewBox="0 0 24 24" aria-hidden>
130
+ <path
131
+ d="M5.5 3.2 19 12.4l-6.2 1.4 2.6 6.6-2.8 1.1-2.6-6.6L5.5 3.2Z"
132
+ fill="currentColor"
133
+ stroke="#0f172a"
134
+ strokeWidth="1"
135
+ />
136
+ </svg>
137
+ <span>{cursor.label || cursor.userId}</span>
138
+ </div>
139
+ ))}
140
+ </div>
141
+ </section>
142
+ );
143
+ }
144
+
145
+ function hashCode(value: string): number {
146
+ let hash = 0;
147
+ for (let i = 0; i < value.length; i += 1) hash = (hash * 31 + value.charCodeAt(i)) | 0;
148
+ return hash;
149
+ }
150
+
151
+ export function App() {
152
+ const { session, loading, error } = useFluxySession();
153
+ const [roomId, setRoomId] = useState(configuredRoomId);
154
+
155
+ const activeRoomId = useMemo(() => {
156
+ if (session?.mode === "guest") return session.roomId;
157
+ return roomId.trim() || configuredRoomId;
158
+ }, [session, roomId]);
159
+
160
+ if (!workerUrl) {
161
+ return (
162
+ <main className="shell">
163
+ <h1>FluxyChat — live cursors</h1>
164
+ <p>
165
+ Copy <code>.env.example</code> to <code>.env</code> and set{" "}
166
+ <code>VITE_FLUXYCHAT_WORKER_URL</code>.
167
+ </p>
168
+ <p>
169
+ Then either <code>VITE_FLUXYCHAT_MEMBER_JWT</code> or{" "}
170
+ <code>VITE_FLUXYCHAT_PUBLIC_ROOM_ID</code>.
171
+ </p>
172
+ </main>
173
+ );
174
+ }
175
+
176
+ if (loading) {
177
+ return (
178
+ <main className="shell">
179
+ <p>Connecting…</p>
180
+ </main>
181
+ );
182
+ }
183
+
184
+ if (error || !session) {
185
+ return (
186
+ <main className="shell">
187
+ <h1>Live cursors</h1>
188
+ <p className="error">{error ?? "Set JWT or public room ID in .env"}</p>
189
+ </main>
190
+ );
191
+ }
192
+
193
+ return (
194
+ <main className="shell">
195
+ <h1>Live cursors</h1>
196
+ <p className="mode-badge">
197
+ {session.mode === "guest" ? "Guest session" : "Member JWT"} — two tabs, same room
198
+ </p>
199
+ {session.mode === "member" ? (
200
+ <label className="room-picker">
201
+ Room
202
+ <input value={roomId} onChange={(e) => setRoomId(e.target.value)} />
203
+ </label>
204
+ ) : null}
205
+ <FluxyRealtimeProvider
206
+ workerUrl={session.workerUrl}
207
+ authTokenProvider={session.token}
208
+ userId={session.userId}
209
+ >
210
+ <CursorCanvas roomId={activeRoomId} selfUserId={session.userId} />
211
+ </FluxyRealtimeProvider>
212
+ </main>
213
+ );
214
+ }
@@ -0,0 +1,94 @@
1
+ :root {
2
+ font-family: system-ui, -apple-system, sans-serif;
3
+ line-height: 1.5;
4
+ color: #0f172a;
5
+ background: #f8fafc;
6
+ }
7
+
8
+ * {
9
+ box-sizing: border-box;
10
+ }
11
+
12
+ body {
13
+ margin: 0;
14
+ }
15
+
16
+ .shell {
17
+ max-width: 960px;
18
+ margin: 0 auto;
19
+ padding: 1.5rem;
20
+ }
21
+
22
+ .canvas-wrap {
23
+ border: 1px solid #e2e8f0;
24
+ border-radius: 12px;
25
+ overflow: hidden;
26
+ background: #fff;
27
+ }
28
+
29
+ .chat-header {
30
+ display: flex;
31
+ justify-content: space-between;
32
+ padding: 0.75rem 1rem;
33
+ border-bottom: 1px solid #e2e8f0;
34
+ background: #0f172a;
35
+ color: #fff;
36
+ }
37
+
38
+ .status {
39
+ font-size: 0.75rem;
40
+ opacity: 0.9;
41
+ }
42
+
43
+ .canvas {
44
+ position: relative;
45
+ height: min(70vh, 560px);
46
+ background:
47
+ linear-gradient(#e2e8f0 1px, transparent 1px) 0 0 / 32px 32px,
48
+ linear-gradient(90deg, #e2e8f0 1px, transparent 1px) 0 0 / 32px 32px,
49
+ #f8fafc;
50
+ cursor: none;
51
+ touch-action: none;
52
+ }
53
+
54
+ .peer-cursor {
55
+ position: absolute;
56
+ pointer-events: none;
57
+ transform: translate(-2px, -2px);
58
+ z-index: 2;
59
+ }
60
+
61
+ .peer-cursor span {
62
+ display: block;
63
+ margin-top: 2px;
64
+ padding: 1px 6px;
65
+ border-radius: 4px;
66
+ background: currentColor;
67
+ color: #fff;
68
+ font-size: 11px;
69
+ white-space: nowrap;
70
+ }
71
+
72
+ .mode-badge {
73
+ font-size: 0.75rem;
74
+ color: #64748b;
75
+ margin: 0 0 0.75rem;
76
+ }
77
+
78
+ .error {
79
+ color: #b91c1c;
80
+ }
81
+
82
+ .room-picker {
83
+ display: flex;
84
+ flex-direction: column;
85
+ gap: 0.25rem;
86
+ margin-bottom: 1rem;
87
+ font-size: 0.875rem;
88
+ }
89
+
90
+ .room-picker input {
91
+ padding: 0.5rem 0.75rem;
92
+ border: 1px solid #cbd5e1;
93
+ border-radius: 8px;
94
+ }
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import { App } from "./App";
4
+ import "./index.css";
5
+
6
+ createRoot(document.getElementById("root")!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>,
10
+ );
@@ -0,0 +1,12 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ interface ImportMetaEnv {
4
+ readonly VITE_FLUXYCHAT_WORKER_URL: string;
5
+ readonly VITE_FLUXYCHAT_MEMBER_JWT?: string;
6
+ readonly VITE_FLUXYCHAT_ROOM_ID?: string;
7
+ readonly VITE_FLUXYCHAT_PUBLIC_ROOM_ID?: string;
8
+ }
9
+
10
+ interface ImportMeta {
11
+ readonly env: ImportMetaEnv;
12
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+ "moduleResolution": "bundler",
9
+ "allowImportingTsExtensions": true,
10
+ "isolatedModules": true,
11
+ "moduleDetection": "force",
12
+ "noEmit": true,
13
+ "jsx": "react-jsx",
14
+ "strict": true,
15
+ "noUnusedLocals": true,
16
+ "noUnusedParameters": true,
17
+ "noFallthroughCasesInSwitch": true,
18
+ "noUncheckedSideEffectImports": true
19
+ },
20
+ "include": ["src"]
21
+ }
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ server: { port: 5173 },
7
+ });
@@ -0,0 +1,9 @@
1
+ # FluxyChat worker URL (hosted or self-hosted)
2
+ VITE_FLUXYCHAT_WORKER_URL=https://your-worker.example.workers.dev
3
+
4
+ # Option A — member JWT (~2 min via onboarding)
5
+ VITE_FLUXYCHAT_MEMBER_JWT=eyJ...
6
+ VITE_FLUXYCHAT_ROOM_ID=demo
7
+
8
+ # Option B — public guest room (~60s, no signup)
9
+ # VITE_FLUXYCHAT_PUBLIC_ROOM_ID=your-public-room-id
@@ -0,0 +1,11 @@
1
+ # FluxyChat live cursors + chat
2
+
3
+ Same room WebSocket: `sendCursor` + `useChat` messages. Not `client_event` for pointers.
4
+
5
+ ```bash
6
+ npx @fluxy-chat/create-fluxy-chat@latest my-cursors-chat --example live-cursors-chat
7
+ cp .env.example .env
8
+ npm run dev
9
+ ```
10
+
11
+ Open two tabs. Move the pointer and send a chat line.
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>FluxyChat live cursors + chat</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "fluxy-live-cursors-chat",
3
+ "private": true,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc -b && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@fluxy-chat/react": "^0.1.4",
13
+ "@fluxy-chat/sdk": "^0.6.4",
14
+ "react": "^19.0.0",
15
+ "react-dom": "^19.0.0",
16
+ "zustand": "^5.0.0"
17
+ },
18
+ "devDependencies": {
19
+ "@types/react": "^19.0.0",
20
+ "@types/react-dom": "^19.0.0",
21
+ "@vitejs/plugin-react": "^4.3.0",
22
+ "typescript": "^5.6.0",
23
+ "vite": "^6.0.0"
24
+ }
25
+ }