@cloudsignal/collaborate 0.1.0

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.
package/PROMPT.md ADDED
@@ -0,0 +1,128 @@
1
+ # CloudSignal Collaborate — AI Integration Guide
2
+
3
+ Use this guide when generating code that adds real-time collaboration features.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npm install @cloudsignal/collaborate @cloudsignal/mqtt-client
9
+ ```
10
+
11
+ ## Core Pattern
12
+
13
+ Wrap your app (or a page) with `<Space>`, then use hooks and components inside it:
14
+
15
+ ```tsx
16
+ import { Space, AvatarStack, CursorOverlay, TypingIndicator } from "@cloudsignal/collaborate";
17
+
18
+ function CollaborativePage() {
19
+ return (
20
+ <Space
21
+ id="my-room"
22
+ connection={{
23
+ host: "wss://connect.cloudsignal.app:18885/",
24
+ username: "user@org_abc123",
25
+ password: "user-password",
26
+ }}
27
+ userName="Alice"
28
+ >
29
+ <AvatarStack className="absolute top-4 right-4" />
30
+ <CursorOverlay>
31
+ <div style={{ width: "100%", height: "400px" }}>
32
+ Your content here
33
+ </div>
34
+ </CursorOverlay>
35
+ <TypingIndicator />
36
+ </Space>
37
+ );
38
+ }
39
+ ```
40
+
41
+ ## Available Primitives
42
+
43
+ ### Provider
44
+ - `<Space id="..." connection={...} userName="...">` — wraps everything, manages connection
45
+
46
+ ### Hooks (use inside `<Space>`)
47
+ - `useSpace()` — access connection state, self user
48
+ - `usePresence()` — who's online: `{ members, count, onJoin, onLeave }`
49
+ - `useCursors()` — live cursors: `{ cursors, publishCursor }`
50
+ - `useLock(componentId)` — component locking: `{ isLocked, lockedBy, lock, unlock }`
51
+ - `useTypingIndicator(inputId?)` — typing state: `{ typingUsers, startTyping, stopTyping }`
52
+ - `useReactions()` — emoji reactions: `{ reactions, sendReaction }`
53
+ - `useBroadcast<T>(event?)` — custom events: `{ broadcast, onMessage }`
54
+ - `useSharedState<T>(key, initial)` — synced KV state: `[value, setValue]`
55
+
56
+ ### Components (drop-in UI)
57
+ - `<AvatarStack max={5} size={32} />` — online user avatars
58
+ - `<CursorOverlay>` — wraps content, renders live cursors
59
+ - `<TypingIndicator inputId="..." />` — "Alice is typing..."
60
+ - `<LockIndicator componentId="...">` — lock status badge + border
61
+ - `<ReactionBar emojis={['👍','❤️','🎉']} />` — emoji bar + floating animations
62
+ - `<PresenceBorder componentId="...">` — auto-locks on focus, shows who's editing
63
+
64
+ ## Connection Options
65
+
66
+ ```tsx
67
+ // Direct credentials
68
+ connection={{ host: "wss://...", username: "user@org_id", password: "pass" }}
69
+
70
+ // Token-based (secret key)
71
+ connection={{ host: "wss://...", organizationId: "org-uuid", secretKey: "sk_..." }}
72
+
73
+ // External IdP (Supabase, Clerk, etc.)
74
+ connection={{ host: "wss://...", organizationId: "org-uuid", externalToken: "jwt...", tokenServiceUrl: "https://auth.cloudsignal.app" }}
75
+ ```
76
+
77
+ ## Common Patterns
78
+
79
+ ### Collaborative Form
80
+ ```tsx
81
+ <Space id="form-123" connection={conn} userName="Alice">
82
+ <AvatarStack />
83
+ <PresenceBorder componentId="title">
84
+ <input onChange={() => startTyping()} placeholder="Title" />
85
+ </PresenceBorder>
86
+ <PresenceBorder componentId="body">
87
+ <textarea onChange={() => startTyping()} placeholder="Body" />
88
+ </PresenceBorder>
89
+ <TypingIndicator />
90
+ </Space>
91
+ ```
92
+
93
+ ### Shared Counter
94
+ ```tsx
95
+ function Counter() {
96
+ const [count, setCount] = useSharedState('counter', 0);
97
+ return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
98
+ }
99
+ ```
100
+
101
+ ### Custom Chat Events
102
+ ```tsx
103
+ function Chat() {
104
+ const { broadcast, onMessage } = useBroadcast<{ text: string }>('chat');
105
+ const [messages, setMessages] = useState([]);
106
+
107
+ useEffect(() => onMessage(msg => setMessages(prev => [...prev, msg])), []);
108
+
109
+ return (
110
+ <>
111
+ {messages.map(m => <p>{m.text}</p>)}
112
+ <input onKeyDown={e => {
113
+ if (e.key === 'Enter') broadcast({ text: e.currentTarget.value });
114
+ }} />
115
+ </>
116
+ );
117
+ }
118
+ ```
119
+
120
+ ## Rules for AI Code Generation
121
+
122
+ 1. Always wrap collaborative areas with `<Space>` before using any hooks/components
123
+ 2. `connection` prop is required — never omit it
124
+ 3. All hooks must be called inside a `<Space>` provider
125
+ 4. `<CursorOverlay>` needs a child with defined dimensions (width/height)
126
+ 5. Use `<PresenceBorder>` for form fields — it auto-locks on focus
127
+ 6. `useSharedState` is last-write-wins — don't use for conflict-sensitive data
128
+ 7. Components accept `className` for Tailwind/CSS styling
package/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # @cloudsignal/collaborate
2
+
3
+ Real-time collaboration primitives for React — powered by [CloudSignal](https://cloudsignal.io) MQTT.
4
+
5
+ Drop-in components for **cursors**, **presence**, **component locking**, **typing indicators**, **emoji reactions**, **shared state**, and **custom broadcast events**.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @cloudsignal/collaborate @cloudsignal/mqtt-client
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```tsx
16
+ import { Space, AvatarStack, CursorOverlay, TypingIndicator } from "@cloudsignal/collaborate";
17
+
18
+ export default function App() {
19
+ return (
20
+ <Space
21
+ id="my-room"
22
+ connection={{
23
+ host: "wss://connect.cloudsignal.app:18885/",
24
+ username: "alice@org_k7xm4pqr2n5t",
25
+ password: "alice-password",
26
+ }}
27
+ userName="Alice"
28
+ >
29
+ <AvatarStack />
30
+
31
+ <CursorOverlay>
32
+ <div style={{ width: "100%", height: "500px", background: "#fafafa" }}>
33
+ Move your mouse here
34
+ </div>
35
+ </CursorOverlay>
36
+
37
+ <TypingIndicator />
38
+ </Space>
39
+ );
40
+ }
41
+ ```
42
+
43
+ ## Primitives
44
+
45
+ ### Provider
46
+
47
+ | Component | Description |
48
+ |-----------|-------------|
49
+ | `<Space>` | Wraps your app. Manages MQTT connection, presence heartbeats, and topic routing. |
50
+
51
+ ### Hooks
52
+
53
+ | Hook | Returns | Description |
54
+ |------|---------|-------------|
55
+ | `useSpace()` | `{ spaceId, self, isConnected, error }` | Access space context |
56
+ | `usePresence()` | `{ members, count, onJoin, onLeave }` | Who's online |
57
+ | `useCursors()` | `{ cursors, publishCursor }` | Live cursor positions |
58
+ | `useLock(id)` | `{ isLocked, lockedBy, lock, unlock }` | Component locking |
59
+ | `useTypingIndicator()` | `{ typingUsers, startTyping, stopTyping }` | Typing state |
60
+ | `useReactions()` | `{ reactions, sendReaction }` | Emoji reactions |
61
+ | `useBroadcast(event?)` | `{ broadcast, lastMessage, onMessage }` | Custom pub/sub |
62
+ | `useSharedState(key, init)` | `[value, setValue]` | Synced key-value state |
63
+
64
+ ### Components
65
+
66
+ | Component | Description |
67
+ |-----------|-------------|
68
+ | `<AvatarStack>` | Overlapping user avatars with "+N" overflow |
69
+ | `<CursorOverlay>` | Wraps content, renders live cursor SVGs |
70
+ | `<TypingIndicator>` | "Alice is typing..." with animated dots |
71
+ | `<LockIndicator>` | Lock badge showing who's editing |
72
+ | `<ReactionBar>` | Emoji buttons with floating animations |
73
+ | `<PresenceBorder>` | Auto-locks on focus, colored border per user |
74
+
75
+ ## Connection Methods
76
+
77
+ ```tsx
78
+ // Direct credentials
79
+ <Space connection={{ host: "wss://...", username: "user@org_id", password: "pass" }} />
80
+
81
+ // Token auth (secret key)
82
+ <Space connection={{ host: "wss://...", organizationId: "uuid", secretKey: "sk_..." }} />
83
+
84
+ // External IdP (Supabase, Clerk, Firebase, Auth0)
85
+ <Space connection={{
86
+ host: "wss://...",
87
+ organizationId: "uuid",
88
+ externalToken: jwt,
89
+ tokenServiceUrl: "https://auth.cloudsignal.app"
90
+ }} />
91
+ ```
92
+
93
+ ## Examples
94
+
95
+ ### Collaborative Form
96
+
97
+ ```tsx
98
+ import { Space, AvatarStack, PresenceBorder, TypingIndicator, useTypingIndicator } from "@cloudsignal/collaborate";
99
+
100
+ function FormFields() {
101
+ const { startTyping } = useTypingIndicator();
102
+
103
+ return (
104
+ <>
105
+ <PresenceBorder componentId="title">
106
+ <input onChange={() => startTyping()} placeholder="Title" />
107
+ </PresenceBorder>
108
+ <PresenceBorder componentId="body">
109
+ <textarea onChange={() => startTyping()} placeholder="Description" />
110
+ </PresenceBorder>
111
+ <TypingIndicator />
112
+ </>
113
+ );
114
+ }
115
+
116
+ export default function Page() {
117
+ return (
118
+ <Space id="form-123" connection={conn} userName="Alice">
119
+ <AvatarStack />
120
+ <FormFields />
121
+ </Space>
122
+ );
123
+ }
124
+ ```
125
+
126
+ ### Shared Counter
127
+
128
+ ```tsx
129
+ import { useSharedState } from "@cloudsignal/collaborate";
130
+
131
+ function Counter() {
132
+ const [count, setCount] = useSharedState("counter", 0);
133
+ return <button onClick={() => setCount(count + 1)}>Clicks: {count}</button>;
134
+ }
135
+ ```
136
+
137
+ ### Real-time Chat via Broadcast
138
+
139
+ ```tsx
140
+ import { useBroadcast } from "@cloudsignal/collaborate";
141
+
142
+ function Chat() {
143
+ const { broadcast, onMessage } = useBroadcast<{ text: string; from: string }>("chat");
144
+ const [messages, setMessages] = useState<{ text: string; from: string }[]>([]);
145
+
146
+ useEffect(() => onMessage(msg => setMessages(prev => [...prev, msg])), []);
147
+
148
+ return (
149
+ <div>
150
+ {messages.map((m, i) => <p key={i}><b>{m.from}:</b> {m.text}</p>)}
151
+ <input onKeyDown={e => {
152
+ if (e.key === "Enter") {
153
+ broadcast({ text: e.currentTarget.value, from: "Alice" });
154
+ e.currentTarget.value = "";
155
+ }
156
+ }} />
157
+ </div>
158
+ );
159
+ }
160
+ ```
161
+
162
+ ## Architecture
163
+
164
+ ```
165
+ @cloudsignal/collaborate (this package)
166
+ └── @cloudsignal/mqtt-client (peer dependency — MQTT transport)
167
+ └── VerneMQ broker (CloudSignal infrastructure)
168
+ ```
169
+
170
+ All collaboration data flows over MQTT topics under `$spaces/{spaceId}/`:
171
+
172
+ | Topic | QoS | Retain | Purpose |
173
+ |-------|-----|--------|---------|
174
+ | `$spaces/{id}/presence` | 0 | No | Heartbeats, join/leave |
175
+ | `$spaces/{id}/cursors` | 0 | No | Cursor positions |
176
+ | `$spaces/{id}/locks` | 1 | No | Lock acquire/release |
177
+ | `$spaces/{id}/typing` | 0 | No | Typing indicators |
178
+ | `$spaces/{id}/reactions` | 0 | No | Emoji reactions |
179
+ | `$spaces/{id}/broadcast/{event}` | 0 | No | Custom events |
180
+ | `$spaces/{id}/state/{key}` | 1 | Yes | Shared state (LWW) |
181
+
182
+ ## Performance
183
+
184
+ - **Cursors** use ref-based storage + imperative DOM updates — zero React re-renders per message
185
+ - **Presence** and **typing** use `useState` since they update at human speed (<1Hz)
186
+ - **Single wildcard subscription** per space — `$spaces/{id}/#`
187
+ - **Throttled publishing** — cursors at ~33Hz, typing at max 1 publish/2s
188
+ - **Stale cleanup** — cursors fade after 3s, typing clears after 4s, presence after 30s
189
+
190
+ ## License
191
+
192
+ MIT