Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ __pycache__/
# TypeScript and Next.js build output.
/packages/client/dist/
/examples/collaboration/.next/
/examples/collaboration/.next-browser-test/
/examples/collaboration/.next-build-check/
/examples/collaboration/out/
*.tsbuildinfo
/examples/collaboration/next-env.d.ts
Expand Down
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,9 @@ Create a room from **Your rooms**. Its creator becomes the first owner.
3. Owners and editors can type, select, replace, undo, and redo their own changes.
Viewers receive the document and presence but cannot edit. Their status reads
**View only** rather than claiming that they saved changes.
4. Disconnect an editor, make an offline edit, and reconnect. Wait for **Saved**
before closing the tab. Offline edits survive only while the tab remains open.
4. Click the connection status in the footer and choose **Disconnect**. Make an
offline edit, then open the status dialog and choose **Connect**. Wait for
**Saved** before closing the tab. Offline edits survive only while the tab remains open.
5. Restart Phoenix and open the same room to restore its saved state.
6. Owners can change roles or remove members. Affected channels close and the
example requires a reload to obtain current permissions. Copy any unsaved
Expand Down Expand Up @@ -183,16 +184,14 @@ transfers, and save tracking; account forms and editor controls stay in the app.

See the [SDK interface and integration guide](packages/client/README.md) for
installation, connection and save states, cancellation, cleanup, and custom
access callbacks. `/sdk.html?room=<room_id>` provides a second small consumer
that synchronizes a title in a Y.Map without CodeMirror. It uses the same signed-in
account and room membership as the editor.
access callbacks.

## Try the Kanban example

Open [127.0.0.1:5173/kanban.html](http://127.0.0.1:5173/kanban.html) with the same
Open [127.0.0.1:5173/kanban](http://127.0.0.1:5173/kanban) with the same
development servers running. Sign in and create a board, or open an existing room.
The text editor and board link to each other and share account and room access.
The production build also includes `/kanban.html`.
The production build also includes `/kanban`.

- Add cards to Backlog, In progress, or Done. Open a card to edit its title,
description, color, and status. Changes save as you type.
Expand Down Expand Up @@ -234,7 +233,7 @@ npm test --workspace synixir-collaboration-example -- kanban.spec.ts

## Try the whiteboard example

Open [127.0.0.1:5173/whiteboard.html](http://127.0.0.1:5173/whiteboard.html) with the
Open [127.0.0.1:5173/whiteboard](http://127.0.0.1:5173/whiteboard) with the
same development servers running. Sign in and create a room, or open an existing
one. Links between the examples retain the room.
Each example stores its data separately inside that room's Yjs document.
Expand Down Expand Up @@ -263,7 +262,7 @@ migration updates. See the [Excalidraw integration notes](examples/collaboration
for the scene model, asset setup, and package constraints.

Offline edits remain in the open tab until reconnecting. Wait for **Saved** before
closing it. The production build includes `/whiteboard.html` and uses the same
closing it. The production build includes `/whiteboard` and uses the same
room permissions, PostgreSQL persistence, and recovery flow as the other examples.

`npm test` includes whiteboard concurrency and undo checks. Run its browser
Expand All @@ -275,7 +274,7 @@ npm test --workspace synixir-collaboration-example -- whiteboard.spec.ts

## Try the rich-text example

Open [127.0.0.1:5173/rich-text.html](http://127.0.0.1:5173/rich-text.html) with the
Open [127.0.0.1:5173/rich-text](http://127.0.0.1:5173/rich-text) with the
same development servers running. Sign in and create a room, or open an existing
one. The plain-text editor remains available as its own example.

Expand Down Expand Up @@ -310,7 +309,7 @@ npm test --workspace synixir-collaboration-example -- rich-text.spec.ts

## Try the multiplayer form example

Open [127.0.0.1:5173/multiplayer-form.html](http://127.0.0.1:5173/multiplayer-form.html)
Open [127.0.0.1:5173/multiplayer-form](http://127.0.0.1:5173/multiplayer-form)
with the same development servers running. Create a room or open an existing one,
then open the form in another tab to collaborate on a project brief.

Expand Down Expand Up @@ -352,7 +351,7 @@ npm test --workspace synixir-collaboration-example -- multiplayer-form.spec.ts

## Try the flowchart builder

Open [127.0.0.1:5173/flowchart.html](http://127.0.0.1:5173/flowchart.html) with the
Open [127.0.0.1:5173/flowchart](http://127.0.0.1:5173/flowchart) with the
same development servers running. Create a room or use an existing one, then open
another tab to build a workflow together.

Expand Down Expand Up @@ -396,7 +395,7 @@ npm test --workspace synixir-collaboration-example -- flowchart.spec.ts

## Try the collaborative table

Open [127.0.0.1:5173/table.html](http://127.0.0.1:5173/table.html) with the same
Open [127.0.0.1:5173/table](http://127.0.0.1:5173/table) with the same
development servers running. Create a room or use an existing one, then open
another tab to work on the same table.

Expand Down Expand Up @@ -509,6 +508,7 @@ bearer grant.
| `DELETE /api/session` | Revoke the current session and sign out |
| `GET /api/rooms` | List the account's active memberships |
| `POST /api/rooms` | Create `room_id` and its initial owner atomically |
| `GET /api/rooms/:room_id` | Room info and member usernames/roles for current room members |
| `POST /api/rooms/:room_id/token` | Issue a grant using current session and membership |
| `GET /api/rooms/:room_id/members` | Owner-only membership list |
| `PUT /api/rooms/:room_id/members/:username` | Owner grants or changes `role` |
Expand Down
5 changes: 2 additions & 3 deletions docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ elixir scripts/staging.exs up
elixir scripts/staging.exs status
```

Open [local staging](https://localhost:8443) and create an account. The SDK settings
example is at [sdk.html](https://localhost:8443/sdk.html). The generated certificate
Open [local staging](https://localhost:8443) and create an account. The generated certificate
is self-signed, so a browser requires a local certificate exception. The pilot
accepts this certificate only in its browser contexts. It does not install a
system trust root. For a verified command-line request:
Expand Down Expand Up @@ -112,7 +111,7 @@ elixir scripts/staging.exs pilot
```

The pilot creates owner, editor and viewer accounts. It checks shared editing,
read-only access, offline reconnect, the packaged SDK settings page, cookie flags,
read-only access, offline reconnect, the packaged example pages, cookie flags,
origin rejection and forged proxy headers. It stops only this installation's
database to check liveness 200, readiness 503 and recovery. Then it verifies a
backup, replaces the app with the same image and checks saved sessions, passwords,
Expand Down
4 changes: 0 additions & 4 deletions docs/research/example-libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,6 @@ The local table currently binds each cell to its own Y.Text and CodeMirror insta

For this small form, I would add Zod only when the schema needs to grow, and add React Hook Form when touched/error/submission state warrants it. These packages do not establish the synchronization contract. Keep Yjs as shared state, preserve the Y.Text editor bindings, and bridge local field updates and remote observations without resetting another user's active text. This recommendation follows the local [form model](../../examples/collaboration/multiplayer-form/model.ts) and [form UI](../../examples/collaboration/multiplayer-form/form.ts).

## Shared settings

Keep the minimal React controls with `@synixir/client` and Yjs. This route exists to demonstrate the SDK; an additional state-management package would obscure that example. Local sources: [example descriptions](../../examples/collaboration/lib/examples.ts), [workspace implementation](../../examples/collaboration/components/workspace/workspace.tsx).

## Scope of the findings

These recommendations combine official documentation, published package metadata, and the current source code. No replacement package was installed or tested in the application. UI packages would still use Synixir for room access, transport, durable saves, and reconnection; integration work must preserve those behaviors.
36 changes: 26 additions & 10 deletions examples/collaboration/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Next.js collaboration examples
# Synixir playground

This app uses TypeScript, React, Next.js App Router, shadcn/ui (Radix), and
Tailwind CSS. Elixir/Phoenix owns accounts, room permissions, WebSockets, and
Expand All @@ -16,14 +16,30 @@ server in `dev.ts` proxies `/api` and `/socket` to Phoenix at
http://127.0.0.1:4000; set `SYNIXIR_ENDPOINT` to override it. The proxy preserves
the browser's Origin header so Phoenix can enforce its origin allowlist.

`app/` contains the eight routes. `components/example-app.tsx` manages accounts,
`app/` contains the seven example routes. `components/example-app.tsx` manages accounts,
room selection, and navigation. `components/workspace/workspace.tsx` owns each
SDK connection and its subscriptions, permissions, presence, and teardown.
The editor/canvas modules mount inside a React-owned host and release their
listeners and bindings when that host unmounts. They load only when selected.
`components/ui/` contains the generated shadcn primitives, and
`app/globals.css` contains Tailwind, theme tokens, and shared layout styles.

The playground gives the selected example the full workspace. In the header,
the example dropdown keeps the current room when switching examples. Click the
room ID for room information, the full member roster with live online status,
and a second tab for creating a room. The avatar group opens access management;
only owners can grant, change, or remove access. The username menu contains
theme switching and sign out. Click the connection status in the footer to open
a shadcn alert dialog with connection details and Connect, Disconnect, or Reload
actions. Save status and help are on the other side of the footer.

Switch examples with single letter keys: **E** for Text editor, **K** for Kanban,
**W** for Whiteboard, **R** for Rich text, **F** for Form, **C** for Flowchart,
and **T** for Table.
Each option shows its key with a shadcn Kbd component. These shortcuts work
outside text fields, editors, and dialogs, retain the current room, and keep
the warning before leaving with unsaved edits.

## Libraries by example

| Example | UI and editing packages | Shared data |
Expand All @@ -32,14 +48,12 @@ listeners and bindings when that host unmounts. They load only when selected.
| [Kanban](kanban/README.md) | `@dnd-kit/react`, `@dnd-kit/collision` | Y.Map cards and sortable placements |
| [Whiteboard](whiteboard/README.md) | `@excalidraw/excalidraw` | Yjs creation records and field changes |
| Rich text | Tiptap and its collaboration extension | Y.XmlFragment |
| [Project brief](multiplayer-form/README.md) | React Hook Form, `@hookform/resolvers`, Zod, CodeMirror | Y.Text fields and Y.Map choices |
| [Form](multiplayer-form/README.md) | React Hook Form, `@hookform/resolvers`, Zod, CodeMirror | Y.Text fields and Y.Map choices |
| [Flowchart](flowchart/README.md) | `@xyflow/react` | Y.Map nodes and connections |
| [Table](table/README.md) | `react-data-grid`, CodeMirror | Y.Text cells and Y.Map schema |
| Shared settings | Native controls and `@synixir/client` | Y.Map |

Every example uses the same Synixir/Phoenix connection and persistence. The UI
packages do not introduce another collaboration service. The text, rich-text,
and shared-settings examples retain their existing integrations.
packages do not introduce another collaboration service.

## Checks and production build

Expand All @@ -52,12 +66,14 @@ npm run build

The build compiles the SDK to `packages/client/dist` and exports Next.js pages
and assets to `examples/collaboration/out`. Docker copies this export into
Phoenix's `priv/static`; the runtime needs no Node process. Existing public
URLs such as `/kanban.html?room=planning` and `/sdk.html?room=planning` are
preserved in development and production. Navigation uses full page loads to
retain the unsaved-edit warning and tear down old room connections.
Phoenix's `priv/static`; the runtime needs no Node process. Navigation uses
URLs such as `/kanban?room=planning` and `/table?room=planning` in development and
production. Previously shared `.html` links also work. Navigation uses full page
loads to retain the unsaved-edit warning and tear down old room connections.

Browser tests run on ports 5174 and 4010 with a separate Phoenix test database.
They use `.next-browser-test` so an existing development server can stay open.
Set `SYNIXIR_NEXT_DIST_DIR` to use another Next.js build directory.
When testing separate Git worktrees in parallel, set `SYNIXIR_TEST_FRONTEND_PORT`
and `SYNIXIR_TEST_BACKEND_PORT` to a unique pair for each run. The test server's
origin allowlist follows the selected frontend port.
Expand Down
Loading
Loading