@@ -67,6 +67,7 @@ Vue-reactive modules. Use inside `<script setup>` or `setup()`.
6767| Data fetching | usePagination, useInfiniteScroll, useLiveData, useLazyLoad |
6868| State | useQueryState |
6969| Auth & Chat | useAuth, useChat |
70+ | Collaboration | usePresence |
7071| Routing | useRouter |
7172
7273## File Uploads
@@ -256,6 +257,67 @@ router.navigate('/users/123')
256257// Reactive: router.currentPath, router.params
257258```
258259
260+ ### usePresence
261+
262+ Real-time collaborative presence: who's here, where their cursor is, what they're focused on.
263+
264+ ``` javascript
265+ import { usePresence } from ' stellify-framework'
266+
267+ const { users , cursor , focus } = usePresence ({
268+ channel: ' customers' ,
269+ user: { id: currentUser .id , name: currentUser .name },
270+ })
271+ ```
272+
273+ Wire ` cursor ` to a ` @mousemove ` handler. Wire ` focus(key) ` to row hover or field focus. Other connected users appear in ` users ` with their cursor positions, focus state, and metadata.
274+
275+ ``` vue
276+ <template>
277+ <div @mousemove="cursor" class="relative">
278+ <div v-for="row in rows" :key="row.id"
279+ @mouseenter="focus(row.id)"
280+ @mouseleave="focus(null)">
281+ {{ row.name }}
282+ <span v-if="users.find(u => u.focus === row.id)">
283+ {{ users.find(u => u.focus === row.id).name }} viewing
284+ </span>
285+ </div>
286+ <UserCursor v-for="user in users" :key="user.id" :user="user" />
287+ </div>
288+ </template>
289+ ```
290+
291+ Pairs with Laravel Reverb / Pusher on the backend. Channel name should match a presence channel defined in ` routes/channels.php ` .
292+
293+ ** Options:**
294+ - ` channel ` (required) - Laravel broadcast channel name
295+ - ` user ` (required) - ` { id, ...metadata } ` current user's identity
296+ - ` autoJoin ` - Join on mount (default: true)
297+ - ` throttleMs ` - Cursor broadcast throttle (default: 50ms)
298+
299+ ** Returns:**
300+ - ` users ` - ` ComputedRef<PresenceUser[]> ` other users present (excludes self)
301+ - ` self ` - ` Ref<PresenceUser | null> ` current user's presence record
302+ - ` cursor ` - ` (event: MouseEvent) => void ` call from @mousemove
303+ - ` focus ` - ` (key: string | null) => void ` broadcast focus state
304+ - ` setMeta ` - ` (meta: Record<string, unknown>) => void ` broadcast arbitrary metadata
305+ - ` isConnected ` - ` Ref<boolean> ` WebSocket connection state
306+ - ` error ` - ` Ref<Error | null> ` connection errors
307+ - ` join ` / ` leave ` - Manual channel control
308+
309+ ** PresenceUser shape:**
310+ ``` typescript
311+ {
312+ id : string | number
313+ joinedAt : number
314+ cursor : { x : number ; y : number } | null
315+ focus : string | null
316+ meta : Record < string , unknown >
317+ // ...additional fields from user config
318+ }
319+ ```
320+
259321## Design Principles
260322
2613231 . ** Chainable APIs** - Fluent method chaining for readable code
0 commit comments