feat(CollabStatus): transport-agnostic live-collaboration status chip - #350
Open
horner wants to merge 3 commits into
Open
feat(CollabStatus): transport-agnostic live-collaboration status chip#350horner wants to merge 3 commits into
horner wants to merge 3 commits into
Conversation
Adds `CollabStatus`, a connection dot + `Live`/`Connecting…` label + "who is editing" summary with a click-to-open panel showing room identity and a rolling event log. The component is transport-agnostic — it renders whatever presence state it is handed. `useYjsCollabStatus` binds it to a Yjs `y-websocket` room in three lines. The doc/awareness/provider are typed structurally, so the library keeps a zero runtime dependency on Yjs (yjs, y-protocols and y-websocket are devDependencies used only by the stories and tests). Stories include a simulated room (a real Y.Doc + Awareness relayed in memory, so it works with no server and in CI) and a live server room, plus autodocs covering the binding recipe.
Deploying ui with
|
| Latest commit: |
6b24f22
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://3bb916d3.ui-6d0.pages.dev |
| Branch Preview URL: | https://feat-collab-status.ui-6d0.pages.dev |
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new transport-agnostic CollabStatus status chip component (with optional debug panel) plus a useYjsCollabStatus hook that binds it to Yjs rooms without introducing a runtime Yjs dependency in the library.
Changes:
- Export new
CollabStatuscomponent + types anduseYjsCollabStatushook from the public API and build entrypoints. - Add Storybook stories (including an in-memory simulated Yjs room) and Vitest coverage for the component + hook.
- Add Yjs-related packages as devDependencies to support stories/tests.
Reviewed changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tsup.config.ts | Adds a tsup entrypoint for the new component package export. |
| src/index.ts | Re-exports CollabStatus from the library root. |
| src/components/CollabStatus/index.ts | Barrel exports for component, types, and Yjs binding hook. |
| src/components/CollabStatus/CollabStatus.tsx | New UI: status chip + optional portal-based panel + log rendering. |
| src/components/CollabStatus/useYjsCollabStatus.ts | New hook: observes Yjs doc/provider/awareness structurally and builds CollabStatus props + log. |
| src/components/CollabStatus/storyData.ts | In-memory Yjs room simulation used by stories/tests. |
| src/components/CollabStatus/CollabStatus.stories.tsx | Storybook docs + simulated room + live-server demo story. |
| src/components/CollabStatus/CollabStatus.test.tsx | Tests for UI text behavior and hook integration via the simulated room. |
| package.json | Adds Yjs packages (for story/test support). |
| pnpm-lock.yaml | Lockfile updates for added Yjs packages. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Comment on lines
+174
to
+179
| const KIND_TONE: Record<CollabLogKind, string> = { | ||
| doc: 'text-muted-foreground', | ||
| awareness: 'text-primary-600 dark:text-primary-400', | ||
| sync: 'text-success-700 dark:text-success-400', | ||
| patch: 'text-muted-foreground', | ||
| }; |
Comment on lines
+120
to
+142
| const member: LocalYjsMember = { | ||
| doc, | ||
| awareness, | ||
| provider, | ||
| leave: () => { | ||
| this.members.delete(member); | ||
| doc.off('update', onDocUpdate); | ||
| awareness.off('update', onAwareness); | ||
| // Clear presence, then push that removal so peers log "left the room". | ||
| awareness.setLocalState(null); | ||
| const removal = encodeAwarenessUpdate(awareness, [awareness.clientID]); | ||
| for (const peer of this.members) { | ||
| applyAwarenessUpdate(peer.awareness, removal, peer.provider); | ||
| } | ||
| provider.markSynced(false); | ||
| awareness.destroy(); | ||
| doc.destroy(); | ||
| }, | ||
| }; | ||
|
|
||
| this.members.add(member); | ||
| window.setTimeout(() => provider.markSynced(true), syncDelay); | ||
| return member; |
horner
added a commit
to mieweb/yorm
that referenced
this pull request
Aug 1, 2026
The header's wide connection/projection badge is replaced by two dots: - a projection dot next to the autosave picker (green saved / amber pending), with the wording kept for screen readers; - `CollabStatus` in compact mode (RoomStatus.tsx) whose popup lists the room occupants and a merged activity log: peers joining/leaving, doc updates, sync transitions, field edits, policy changes, proposals, and SQL projection commits. `@mieweb/ui` now builds from the new vendor/ui submodule (mieweb/ui#350), which carries the `compact` + occupants additions this demo needs.
- `compact` prop renders the dot alone; status and editing text move into the trigger's title/accessible name so the chip costs no header space. - The panel gains an "In the room (N)" section listing occupants with their presence colors, with an `alone` fallback. - New `peersTitle`/`alone` labels; `peerLabels()` now returns label+color. - Tests + a `Compact` story.
An app-level condition (unsaved work, a pending save) had no way to reach the header without adding a second dot beside this one, so `attention` lets it share this dot: amber while set, joined into the trigger's accessible name, and headlining the panel. The panel stays narrow by clipping long values (socket URL, log details) to one line, which left them unreadable. Every clipped value now carries its full text in a `title`, and a `Wrap long values` toggle in the panel header unclips all of them at once for the cases where a tooltip is not enough.
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 10 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Suppressed comments (3)
src/components/CollabStatus/CollabStatus.tsx:361
- This component introduces several arbitrary-value Tailwind classes (e.g.
w-[26rem],max-w-[calc(100vw-1rem)],grid-cols-[auto_1fr],grid-cols-[auto_auto_1fr]). These are not currently present inmiewebUISafelist(src/tailwind-preset.ts), so Tailwind CSS 3 consumers who rely on the preset safelist (and don’t scan node_modules) may miss critical styles for this panel/layout.
className={cn(
'bg-card text-card-foreground border-border fixed z-50 w-[26rem] max-w-[calc(100vw-1rem)]',
'rounded-lg border p-3 text-xs shadow-xl'
)}
src/components/CollabStatus/CollabStatus.tsx:300
- In compact mode,
triggerLabelis built by joining an array that can includeattention(typed asReact.ReactNode). Ifattentionis a React element, it will be coerced to the string "[object Object]" and end up inaria-label/title, which breaks accessibility names/tooltips.
attention,
names.length > 0 && labels.editing(names),
]
.filter(Boolean)
.join(' — ')
src/components/CollabStatus/CollabStatus.stories.tsx:288
LiveServerDemokeepserrorstate acrossserverUrl/roomNamechanges; once an import/connection fails, the story will continue to render the error even if the next effect run succeeds. Resettingerrorat the start of the effect avoids a stale error UI.
React.useEffect(() => {
let disposed = false;
let cleanup = () => {};
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
CollabStatus, a connection dot +Live/Connecting…label + "who is editing" summary with a click-to-open panel showing room identity and a rolling event log. The component is transport-agnostic — it renders whatever presence state it is handed.useYjsCollabStatusbinds it to a Yjsy-websocketroom in three lines. The doc/awareness/provider are typed structurally, so the library keeps a zero runtime dependency on Yjs (yjs, y-protocols and y-websocket are devDependencies used only by the stories and tests).Stories include a simulated room (a real Y.Doc + Awareness relayed in memory, so it works with no server and in CI) and a live server room, plus autodocs covering the binding recipe.