@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,241 @@
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, messages, sendMessage } = useChat({
89
+ roomId,
90
+ replay: "request",
91
+ });
92
+ const [draft, setDraft] = useState("");
93
+ const color = CURSOR_COLORS[Math.abs(hashCode(selfUserId)) % CURSOR_COLORS.length];
94
+
95
+ return (
96
+ <section className="canvas-wrap">
97
+ <header className="chat-header">
98
+ <strong>{roomId}</strong>
99
+ <span className="status">
100
+ {connected ? "live" : connectionState.status} — open a second tab
101
+ </span>
102
+ </header>
103
+ <div
104
+ className="canvas"
105
+ onPointerMove={(event) => {
106
+ const rect = event.currentTarget.getBoundingClientRect();
107
+ sendCursor({
108
+ x: event.clientX - rect.left,
109
+ y: event.clientY - rect.top,
110
+ color,
111
+ label: selfUserId.slice(0, 12),
112
+ });
113
+ }}
114
+ onPointerLeave={() => {
115
+ /* last position stays until stale on peers */
116
+ }}
117
+ >
118
+ {Object.values(liveCursors)
119
+ .filter((c) => c.userId !== selfUserId)
120
+ .map((cursor) => (
121
+ <div
122
+ key={cursor.userId}
123
+ className="peer-cursor"
124
+ style={{
125
+ left: cursor.x,
126
+ top: cursor.y,
127
+ color: cursor.color || "#2563eb",
128
+ }}
129
+ >
130
+ <svg width="18" height="18" viewBox="0 0 24 24" aria-hidden>
131
+ <path
132
+ 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"
133
+ fill="currentColor"
134
+ stroke="#0f172a"
135
+ strokeWidth="1"
136
+ />
137
+ </svg>
138
+ <span>{cursor.label || cursor.userId}</span>
139
+ </div>
140
+ ))}
141
+ </div>
142
+ <div className="log" style={{ maxHeight: 160, overflow: "auto", padding: "0.75rem 1rem" }}>
143
+ {messages.slice(-8).map((msg) => (
144
+ <p key={msg.id} style={{ margin: "0 0 6px" }}>
145
+ <strong>{msg.userId}:</strong> {msg.content}
146
+ </p>
147
+ ))}
148
+ </div>
149
+ <form
150
+ className="composer"
151
+ style={{ display: "flex", gap: 8, padding: "0.75rem", borderTop: "1px solid #e2e8f0" }}
152
+ onSubmit={(event) => {
153
+ event.preventDefault();
154
+ const text = draft.trim();
155
+ if (!text) return;
156
+ setDraft("");
157
+ void sendMessage(text);
158
+ }}
159
+ >
160
+ <input
161
+ value={draft}
162
+ onChange={(event) => setDraft(event.target.value)}
163
+ placeholder="Chat overlay…"
164
+ style={{ flex: 1, padding: "0.5rem", border: "1px solid #cbd5e1", borderRadius: 8 }}
165
+ />
166
+ <button type="submit">Send</button>
167
+ </form>
168
+ </section>
169
+ );
170
+ }
171
+
172
+ function hashCode(value: string): number {
173
+ let hash = 0;
174
+ for (let i = 0; i < value.length; i += 1) hash = (hash * 31 + value.charCodeAt(i)) | 0;
175
+ return hash;
176
+ }
177
+
178
+ export function App() {
179
+ const { session, loading, error } = useFluxySession();
180
+ const [roomId, setRoomId] = useState(configuredRoomId);
181
+
182
+ const activeRoomId = useMemo(() => {
183
+ if (session?.mode === "guest") return session.roomId;
184
+ return roomId.trim() || configuredRoomId;
185
+ }, [session, roomId]);
186
+
187
+ if (!workerUrl) {
188
+ return (
189
+ <main className="shell">
190
+ <h1>FluxyChat — live cursors + chat</h1>
191
+ <p>
192
+ Copy <code>.env.example</code> to <code>.env</code> and set{" "}
193
+ <code>VITE_FLUXYCHAT_WORKER_URL</code>.
194
+ </p>
195
+ <p>
196
+ Then either <code>VITE_FLUXYCHAT_MEMBER_JWT</code> or{" "}
197
+ <code>VITE_FLUXYCHAT_PUBLIC_ROOM_ID</code>.
198
+ </p>
199
+ </main>
200
+ );
201
+ }
202
+
203
+ if (loading) {
204
+ return (
205
+ <main className="shell">
206
+ <p>Connecting…</p>
207
+ </main>
208
+ );
209
+ }
210
+
211
+ if (error || !session) {
212
+ return (
213
+ <main className="shell">
214
+ <h1>Live cursors + chat</h1>
215
+ <p className="error">{error ?? "Set JWT or public room ID in .env"}</p>
216
+ </main>
217
+ );
218
+ }
219
+
220
+ return (
221
+ <main className="shell">
222
+ <h1>Live cursors + chat</h1>
223
+ <p className="mode-badge">
224
+ {session.mode === "guest" ? "Guest session" : "Member JWT"} — two tabs, same room
225
+ </p>
226
+ {session.mode === "member" ? (
227
+ <label className="room-picker">
228
+ Room
229
+ <input value={roomId} onChange={(e) => setRoomId(e.target.value)} />
230
+ </label>
231
+ ) : null}
232
+ <FluxyRealtimeProvider
233
+ workerUrl={session.workerUrl}
234
+ authTokenProvider={session.token}
235
+ userId={session.userId}
236
+ >
237
+ <CursorCanvas roomId={activeRoomId} selfUserId={session.userId} />
238
+ </FluxyRealtimeProvider>
239
+ </main>
240
+ );
241
+ }
@@ -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
+ });
@@ -9,10 +9,10 @@
9
9
  "preview": "vite preview"
10
10
  },
11
11
  "dependencies": {
12
- "@fluxy-chat/ui-kit": "^0.1.0",
13
- "@fluxy-chat/ui": "^0.1.2",
14
- "@fluxy-chat/react": "^0.1.1",
15
- "@fluxy-chat/sdk": "^0.6.2",
12
+ "@fluxy-chat/ui-kit": "^0.1.1",
13
+ "@fluxy-chat/ui": "^0.1.4",
14
+ "@fluxy-chat/react": "^0.1.4",
15
+ "@fluxy-chat/sdk": "^0.6.4",
16
16
  "react": "^19.0.0",
17
17
  "react-dom": "^19.0.0"
18
18
  },
@@ -9,8 +9,8 @@
9
9
  "preview": "vite preview"
10
10
  },
11
11
  "dependencies": {
12
- "@fluxy-chat/react": "^0.1.1",
13
- "@fluxy-chat/sdk": "^0.6.2",
12
+ "@fluxy-chat/react": "^0.1.4",
13
+ "@fluxy-chat/sdk": "^0.6.4",
14
14
  "react": "^19.0.0",
15
15
  "react-dom": "^19.0.0",
16
16
  "zustand": "^5.0.0"
@@ -77,17 +77,23 @@ function useFluxySession(): {
77
77
  }
78
78
 
79
79
  function ChatPanel({ roomId }: { roomId: string }) {
80
- const { messages, sendMessage, connectionState } = useChat({
80
+ const { messages, sendMessage, connectionState, stopAgentStream } = useChat({
81
81
  roomId,
82
82
  markReadLatest: true,
83
83
  });
84
84
  const [draft, setDraft] = useState("");
85
+ const isStreaming = messages.some((m) => m.streaming);
85
86
 
86
87
  return (
87
88
  <section className="chat-panel">
88
89
  <header className="chat-header">
89
90
  <strong>{roomId}</strong>
90
91
  <span className="status">{connectionState.status}</span>
92
+ {isStreaming ? (
93
+ <button type="button" onClick={() => stopAgentStream()}>
94
+ Stop
95
+ </button>
96
+ ) : null}
91
97
  </header>
92
98
  <ul className="messages">
93
99
  {messages.map((m) => (
@@ -110,7 +116,7 @@ function ChatPanel({ roomId }: { roomId: string }) {
110
116
  <input
111
117
  value={draft}
112
118
  onChange={(e) => setDraft(e.target.value)}
113
- placeholder="Type a message"
119
+ placeholder="Type a message, or @assistant to mention the room agent"
114
120
  aria-label="Message"
115
121
  />
116
122
  <button type="submit">Send</button>
@@ -9,7 +9,7 @@
9
9
  "type-check": "tsc --noEmit"
10
10
  },
11
11
  "dependencies": {
12
- "@fluxy-chat/sdk": "^0.6.0",
12
+ "@fluxy-chat/sdk": "^0.6.4",
13
13
  "@slack/bolt": "^4.0.0",
14
14
  "@slack/web-api": "^7.0.0"
15
15
  },
@@ -9,7 +9,7 @@
9
9
  "type-check": "tsc --noEmit"
10
10
  },
11
11
  "dependencies": {
12
- "@fluxy-chat/sdk": "^0.6.0",
12
+ "@fluxy-chat/sdk": "^0.6.4",
13
13
  "node-telegram-bot-api": "^0.66.0"
14
14
  },
15
15
  "devDependencies": {
@@ -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,14 @@
1
+ # FluxyChat Tiptap room
2
+
3
+ Yjs Storage + Tiptap on the room Durable Object (binary WS, `yjs-sync.js`). Not a second CRDT. Open **two tabs**.
4
+
5
+ ```bash
6
+ npx @fluxy-chat/create-fluxy-chat@latest my-doc --example tiptap-room
7
+ cp .env.example .env
8
+ # VITE_FLUXYCHAT_WORKER_URL + PUBLIC_ROOM_ID or MEMBER_JWT
9
+ npm run dev
10
+ ```
11
+
12
+ ## Code
13
+
14
+ `FluxyYjsProvider` + `useStorage` / `useMutation` from `@fluxy-chat/sdk/yjs`. Tiptap `Collaboration` uses `useYjsDoc()` and `field: "prosemirror"`. LiveFile refs go through `uploadLiveFile` (existing `POST /upload`).
@@ -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 Tiptap room</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,29 @@
1
+ {
2
+ "name": "fluxy-tiptap-room",
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
+ "@tiptap/extension-collaboration": "^2.11.5",
15
+ "@tiptap/pm": "^2.11.5",
16
+ "@tiptap/react": "^2.11.5",
17
+ "@tiptap/starter-kit": "^2.11.5",
18
+ "react": "^19.0.0",
19
+ "react-dom": "^19.0.0",
20
+ "yjs": "^13.6.31"
21
+ },
22
+ "devDependencies": {
23
+ "@types/react": "^19.0.0",
24
+ "@types/react-dom": "^19.0.0",
25
+ "@vitejs/plugin-react": "^4.3.0",
26
+ "typescript": "^5.6.0",
27
+ "vite": "^6.0.0"
28
+ }
29
+ }