Skip to content

Commit 49dc5a0

Browse files
committed
docs(agents): document history channel and room binding
Capture how history stateless messages map to the collab room id and how the webapp should treat loading, pending watches, and hydration. Made-with: Cursor
1 parent 98c856a commit 49dc5a0

1 file changed

Lines changed: 1 addition & 0 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,6 @@
3535
- Production: docker-compose.prod.yml with Traefik; dev compose backend services need `context: .` (repo root) to match Dockerfile.bun
3636
- Supabase client architecture (Pages Router): browser singleton at `utils/supabase/index.ts`, factory in `component.ts`, GSSP in `server-props.ts`, API route in `api.ts`, URL resolver in `url.ts`; all browser code imports `supabaseClient` singleton; `types/supabase.ts` for generated DB types
3737
- Standalone extension packages (`extension-hyperlink`, `-hypermultimedia`, `-indent`, `-inline-code`, `-placeholder`) share identical structure: TypeScript + tsup build + `@tiptap/core` peer dep; GFM markdown via `@tiptap/markdown`, paste at `extensions/markdown-paste/`, import/export in `utils/markdown.ts` + `toolbar/desktop/DocumentSettingsPanel`; `sanitizeJsonContent` on paste and import paths
38+
- **Document version history (Hocuspocus):** Stateless `history.list` / `history.watch`; server **unicasts** `{ msg: 'history.response', type, response }` on the requesting connection (not `broadcastStateless`). **Prisma always uses the collab room’s document id** (Hocuspocus `document.name`); if the client sends a different `documentId`, respond `history_failed`. Current `history.list` returns **`{ versions, latestSnapshot }`** for one RTT; client still accepts a legacy plain `HistoryItem[]`. **`applyHistoryItemToEditor`** (`pages/history/applyHistoryToEditor.ts`) is the single TipTap hydration path. **`loadingHistory` clears only after a successful apply** (not merely after the network response); **`useHistoryEditorApplyWhenReady`** applies when the editor mounts after data arrives; while **`pendingWatchVersion` is set** (in-flight `history.watch`), the apply-when-ready hook must **not** re-apply stale `activeHistory`, and **late `history.list` must not** reset pending or hydrate from `latestSnapshot` over that watch. On **`history_failed`**, clear **`pendingWatchVersion`** so the next watch isn’t dropped
3839
- TOC + heading chrome: `components/toc/` with `tocClasses.ts` kept in sync with `styles/components/_tableOfContents.scss`; `--color-docsy` = `var(--color-primary)` in both `@theme` and `:root` (globals.scss), auto-tracks DaisyUI light/dark/HC; heading widgets in `TipTap/extensions/HeadingActions/plugins/` (`hoverChatPlugin`, `selectionChatPlugin`) styled by `styles/components/_heading-actions.scss` (shared `$ha-hit-size` with plugins, DRY `$ha-group-has-unread` `:has()` selector for unread tray visibility); `_unread-badge.scss` only styles `[data-unread-count]` on `.ha-chat-btn` + notification bell — no `.toc__chat-trigger`/`.ha-group` rules; TOC uses React `UnreadBadge` only, `UNREAD_SYNC` clears `data-unread-count` on `.toc__chat-trigger`; active chat icon uses `toc__chat-icon--active` class with `fill: none` (Lucide icons are stroke-based); when nested `ul.toc__children` lives under the parent `li`, hide folded subtrees with `&.closed > .toc__children { display: none }` — fold class still comes from editor state, not CSS alone. **TOC data path:** `useToc.ts` throttles heading-driven TOC rebuilds (`lodash/throttle`); flat heading list → recursive `NestedTocNode` tree via `buildNestedToc` at `TocDesktop`/`TocMobile` roots (`utils.ts`); `useHeadingScrollSpy.ts` debounces scroll/active-heading work (`lodash/debounce`)
3940
- Heading fold crinkle: widget decoration driven by `data-fold-phase` attr for CSS animation; unique `Decoration.widget` key per phase (`fold-${id}-folding`/`-unfolding`/`-${id}`) forces ProseMirror remount so animation fires each toggle; width uses `margin-left/right: calc(-1 * var(--tiptap-inline-pad-end))` to span full sheet; SCSS variables `$crinkle-fold-duration`/`$crinkle-easing` for timing (no CSS custom properties); `Decoration.node` on heading-section removed — animations live on the widget itself; strip count uses `MIN_FOLD_STRIPS` / `MAX_FOLD_STRIPS` / `CONTENT_HEIGHT_PER_STRIP` in `heading-fold-plugin.ts` — if `MIN_FOLD_STRIPS === MAX_FOLD_STRIPS`, strip count is fixed regardless of content height

0 commit comments

Comments
 (0)