From d3f94b14393a62f091084a3642169d0a76b00b85 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Tue, 19 May 2026 15:21:04 -0300 Subject: [PATCH 1/3] chore(demos): move 4 demos under demos/editor/ per classification (SD-3217) First path-moves PR after #3395 (classification). Moves four demos into their classified homes under demos/editor/: - demos/custom-ui -> demos/editor/custom-ui (section: editor, subsection: custom-ui, kind: reference-workspace). - demos/chrome-extension -> demos/editor/integrations/chrome-extension (section: editor, subsection: integrations, kind: integration-example). - demos/word-addin -> demos/editor/integrations/word-addin (section: editor, subsection: integrations, kind: integration-example). - demos/docx-from-html -> demos/editor/superdoc/docx-from-html (section: editor, subsection: superdoc, kind: minimal-example). Each old path gets a shim README pointing at the new location, in the same shape as the existing demos/{react, vue, vanilla, cdn, custom-mark, custom-node} shims. Updated alongside the moves: - demos/manifest.json: sourcePath updated to the new paths for the four entries. No other field changed. - pnpm-workspace.yaml: added demos/*/*/* and demos/*/*/*/* globs so pnpm picks up the nested workspaces. Old globs (demos/*, demos/*/*) kept for the shim slots. - apps/docs/document-api/migration.mdx, apps/docs/editor/custom-ui/overview.mdx, apps/docs/editor/custom-ui/context-menu.mdx: 3 link references to github.com/.../demos/custom-ui repointed to the new path. The other three moved entries had no inbound docs links to update. Validator (bun scripts/validate-examples-demos.ts) passes. Zero em-dashes in any new shim README. Stacks on caio-pizzol/manifest-classification (#3395); merge order matters. --- apps/docs/document-api/migration.mdx | 2 +- apps/docs/editor/custom-ui/context-menu.mdx | 2 +- apps/docs/editor/custom-ui/overview.mdx | 2 +- demos/chrome-extension/README.md | 5 + demos/custom-ui/README.md | 111 +----------- demos/docx-from-html/README.md | 8 +- demos/editor/custom-ui/README.md | 110 ++++++++++++ demos/{ => editor}/custom-ui/index.html | 0 demos/{ => editor}/custom-ui/package.json | 0 .../custom-ui/public/sample-review.docx | Bin demos/{ => editor}/custom-ui/src/App.tsx | 0 .../src/components/ActivitySidebar.tsx | 0 .../src/components/CitationHighlights.tsx | 0 .../src/components/CitationPopover.tsx | 0 .../src/components/CitationsPanel.tsx | 0 .../src/components/CommentComposer.tsx | 0 .../custom-ui/src/components/ContextMenu.tsx | 0 .../components/ContextMenuRegistrations.tsx | 0 .../src/components/DisplaySettings.tsx | 0 .../src/components/GenerateDraftButton.tsx | 0 .../src/components/InsertClauseButton.tsx | 0 .../src/components/SelectionPopover.tsx | 0 .../custom-ui/src/components/Toolbar.tsx | 0 .../src/components/citations-types.ts | 0 .../custom-ui/src/components/mockDraft.ts | 0 .../custom-ui/src/components/useCitations.ts | 0 .../src/components/useDecidedChanges.ts | 0 .../custom-ui/src/editor/EditorMount.tsx | 0 demos/{ => editor}/custom-ui/src/main.tsx | 0 demos/{ => editor}/custom-ui/src/styles.css | 0 demos/{ => editor}/custom-ui/tsconfig.json | 0 demos/{ => editor}/custom-ui/vite.config.ts | 0 .../chrome-extension/README.md | 0 .../chrome-extension/background.js | 0 .../chrome-extension/content.js | 0 .../dist/docx-validator.bundle.js | 0 .../dist/docx-validator.bundle.js.LICENSE.txt | 0 .../chrome-extension/docx-validator.js | 0 .../icons/icon-128x128-disabled.png | Bin .../chrome-extension/icons/icon-128x128.png | Bin .../icons/icon-16x16-disabled.png | Bin .../chrome-extension/icons/icon-16x16.png | Bin .../icons/icon-19x19-disabled.png | Bin .../chrome-extension/icons/icon-19x19.png | Bin .../icons/icon-48x48-disabled.png | Bin .../chrome-extension/icons/icon-48x48.png | Bin .../chrome-extension/icons/logo.webp | Bin .../chrome-extension/lib/style.css | 0 .../chrome-extension/lib/superdoc.min.js | 0 .../chrome-extension/manifest.json | 0 .../chrome-extension/modal.css | 0 .../chrome-extension/modal.html | 0 .../chrome-extension/package.json | 0 .../chrome-extension/popup.html | 0 .../chrome-extension/popup.js | 0 .../test_docs/Lunch Haiku (5).docx | Bin .../test_docs/Mutual NDA_draft (1).docx | Bin .../test_docs/Nda Formatted Doc MS WORD.docx | Bin .../test_docs/Nda Formatted Doc.docx | Bin .../test_docs/nda_formatted_doc (1).md | 0 .../chrome-extension/test_docs/sdpr (23).docx | Bin .../chrome-extension/tester.html | 0 .../chrome-extension/webpack.config.js | 0 .../chrome-extension/demo-config.json | 0 .../chrome-extension/demo-thumbnail.png | Bin .../chrome-extension/demo-video.mp4 | Bin .../integrations}/word-addin/.gitignore | 0 .../MS-Word-Add-in-Sample.code-workspace | 0 .../editor/integrations/word-addin/README.md | 165 +++++++++++++++++ .../word-addin/assets/icon-128.png | Bin .../word-addin/assets/icon-128x128.png | Bin .../word-addin/assets/icon-16.png | Bin .../word-addin/assets/icon-16x16.png | Bin .../word-addin/assets/icon-32.png | Bin .../word-addin/assets/icon-32x32.png | Bin .../word-addin/assets/icon-64.png | Bin .../word-addin/assets/icon-64x64.png | Bin .../word-addin/assets/icon-80.png | Bin .../word-addin/assets/icon-80x80.png | Bin .../word-addin/assets/logo-filled.png | Bin .../integrations}/word-addin/assets/logo.png | Bin .../word-addin/assets/sample-document.docx | Bin .../word-addin/babel.config.json | 0 .../integrations}/word-addin/demo-config.json | 0 .../word-addin/demo-thumbnail.png | Bin .../integrations}/word-addin/demo-video.mp4 | Bin .../integrations}/word-addin/manifest.xml | 0 .../integrations}/word-addin/package.json | 0 .../word-addin/server/.env.example | 0 .../word-addin/server/package.json | 0 .../word-addin/server/public/editor.css | 0 .../word-addin/server/public/editor.html | 0 .../word-addin/server/public/editor.js | 0 .../integrations}/word-addin/server/server.js | 0 .../src/auth-dialog/auth-dialog.css | 0 .../src/auth-dialog/auth-dialog.html | 0 .../word-addin/src/auth-dialog/auth-dialog.js | 0 .../word-addin/src/auth0-config.js.example | 0 .../word-addin/src/server-domain.js.example | 0 .../word-addin/src/taskpane/taskpane.css | 0 .../word-addin/src/taskpane/taskpane.html | 0 .../word-addin/src/taskpane/taskpane.js | 0 .../word-addin/webpack.config.js | 0 .../superdoc}/docx-from-html/.gitignore | 0 .../editor/superdoc/docx-from-html/README.md | 7 + .../superdoc}/docx-from-html/demo-config.json | 0 .../docx-from-html/demo-thumbnail.png | Bin .../superdoc}/docx-from-html/demo-video.mp4 | Bin .../superdoc}/docx-from-html/index.html | 0 .../superdoc}/docx-from-html/package.json | 0 .../superdoc}/docx-from-html/public/logo.webp | Bin .../public/sample-document.docx | Bin .../docx-from-html/public/superdoc-logo.png | Bin .../superdoc}/docx-from-html/src/App.vue | 0 .../superdoc}/docx-from-html/src/main.js | 0 .../superdoc}/docx-from-html/src/style.css | 0 .../superdoc}/docx-from-html/vite.config.js | 0 demos/manifest.json | 8 +- demos/word-addin/README.md | 166 +----------------- pnpm-workspace.yaml | 2 + 120 files changed, 305 insertions(+), 283 deletions(-) create mode 100644 demos/chrome-extension/README.md create mode 100644 demos/editor/custom-ui/README.md rename demos/{ => editor}/custom-ui/index.html (100%) rename demos/{ => editor}/custom-ui/package.json (100%) rename demos/{ => editor}/custom-ui/public/sample-review.docx (100%) rename demos/{ => editor}/custom-ui/src/App.tsx (100%) rename demos/{ => editor}/custom-ui/src/components/ActivitySidebar.tsx (100%) rename demos/{ => editor}/custom-ui/src/components/CitationHighlights.tsx (100%) rename demos/{ => editor}/custom-ui/src/components/CitationPopover.tsx (100%) rename demos/{ => editor}/custom-ui/src/components/CitationsPanel.tsx (100%) rename demos/{ => editor}/custom-ui/src/components/CommentComposer.tsx (100%) rename demos/{ => editor}/custom-ui/src/components/ContextMenu.tsx (100%) rename demos/{ => editor}/custom-ui/src/components/ContextMenuRegistrations.tsx (100%) rename demos/{ => editor}/custom-ui/src/components/DisplaySettings.tsx (100%) rename demos/{ => editor}/custom-ui/src/components/GenerateDraftButton.tsx (100%) rename demos/{ => editor}/custom-ui/src/components/InsertClauseButton.tsx (100%) rename demos/{ => editor}/custom-ui/src/components/SelectionPopover.tsx (100%) rename demos/{ => editor}/custom-ui/src/components/Toolbar.tsx (100%) rename demos/{ => editor}/custom-ui/src/components/citations-types.ts (100%) rename demos/{ => editor}/custom-ui/src/components/mockDraft.ts (100%) rename demos/{ => editor}/custom-ui/src/components/useCitations.ts (100%) rename demos/{ => editor}/custom-ui/src/components/useDecidedChanges.ts (100%) rename demos/{ => editor}/custom-ui/src/editor/EditorMount.tsx (100%) rename demos/{ => editor}/custom-ui/src/main.tsx (100%) rename demos/{ => editor}/custom-ui/src/styles.css (100%) rename demos/{ => editor}/custom-ui/tsconfig.json (100%) rename demos/{ => editor}/custom-ui/vite.config.ts (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/README.md (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/background.js (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/content.js (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/dist/docx-validator.bundle.js (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/dist/docx-validator.bundle.js.LICENSE.txt (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/docx-validator.js (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/icons/icon-128x128-disabled.png (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/icons/icon-128x128.png (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/icons/icon-16x16-disabled.png (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/icons/icon-16x16.png (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/icons/icon-19x19-disabled.png (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/icons/icon-19x19.png (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/icons/icon-48x48-disabled.png (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/icons/icon-48x48.png (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/icons/logo.webp (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/lib/style.css (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/lib/superdoc.min.js (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/manifest.json (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/modal.css (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/modal.html (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/package.json (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/popup.html (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/popup.js (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/test_docs/Lunch Haiku (5).docx (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/test_docs/Mutual NDA_draft (1).docx (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/test_docs/Nda Formatted Doc MS WORD.docx (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/test_docs/Nda Formatted Doc.docx (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/test_docs/nda_formatted_doc (1).md (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/test_docs/sdpr (23).docx (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/tester.html (100%) rename demos/{ => editor/integrations}/chrome-extension/chrome-extension/webpack.config.js (100%) rename demos/{ => editor/integrations}/chrome-extension/demo-config.json (100%) rename demos/{ => editor/integrations}/chrome-extension/demo-thumbnail.png (100%) rename demos/{ => editor/integrations}/chrome-extension/demo-video.mp4 (100%) rename demos/{ => editor/integrations}/word-addin/.gitignore (100%) rename demos/{ => editor/integrations}/word-addin/MS-Word-Add-in-Sample.code-workspace (100%) create mode 100644 demos/editor/integrations/word-addin/README.md rename demos/{ => editor/integrations}/word-addin/assets/icon-128.png (100%) rename demos/{ => editor/integrations}/word-addin/assets/icon-128x128.png (100%) rename demos/{ => editor/integrations}/word-addin/assets/icon-16.png (100%) rename demos/{ => editor/integrations}/word-addin/assets/icon-16x16.png (100%) rename demos/{ => editor/integrations}/word-addin/assets/icon-32.png (100%) rename demos/{ => editor/integrations}/word-addin/assets/icon-32x32.png (100%) rename demos/{ => editor/integrations}/word-addin/assets/icon-64.png (100%) rename demos/{ => editor/integrations}/word-addin/assets/icon-64x64.png (100%) rename demos/{ => editor/integrations}/word-addin/assets/icon-80.png (100%) rename demos/{ => editor/integrations}/word-addin/assets/icon-80x80.png (100%) rename demos/{ => editor/integrations}/word-addin/assets/logo-filled.png (100%) rename demos/{ => editor/integrations}/word-addin/assets/logo.png (100%) rename demos/{ => editor/integrations}/word-addin/assets/sample-document.docx (100%) rename demos/{ => editor/integrations}/word-addin/babel.config.json (100%) rename demos/{ => editor/integrations}/word-addin/demo-config.json (100%) rename demos/{ => editor/integrations}/word-addin/demo-thumbnail.png (100%) rename demos/{ => editor/integrations}/word-addin/demo-video.mp4 (100%) rename demos/{ => editor/integrations}/word-addin/manifest.xml (100%) rename demos/{ => editor/integrations}/word-addin/package.json (100%) rename demos/{ => editor/integrations}/word-addin/server/.env.example (100%) rename demos/{ => editor/integrations}/word-addin/server/package.json (100%) rename demos/{ => editor/integrations}/word-addin/server/public/editor.css (100%) rename demos/{ => editor/integrations}/word-addin/server/public/editor.html (100%) rename demos/{ => editor/integrations}/word-addin/server/public/editor.js (100%) rename demos/{ => editor/integrations}/word-addin/server/server.js (100%) rename demos/{ => editor/integrations}/word-addin/src/auth-dialog/auth-dialog.css (100%) rename demos/{ => editor/integrations}/word-addin/src/auth-dialog/auth-dialog.html (100%) rename demos/{ => editor/integrations}/word-addin/src/auth-dialog/auth-dialog.js (100%) rename demos/{ => editor/integrations}/word-addin/src/auth0-config.js.example (100%) rename demos/{ => editor/integrations}/word-addin/src/server-domain.js.example (100%) rename demos/{ => editor/integrations}/word-addin/src/taskpane/taskpane.css (100%) rename demos/{ => editor/integrations}/word-addin/src/taskpane/taskpane.html (100%) rename demos/{ => editor/integrations}/word-addin/src/taskpane/taskpane.js (100%) rename demos/{ => editor/integrations}/word-addin/webpack.config.js (100%) rename demos/{ => editor/superdoc}/docx-from-html/.gitignore (100%) create mode 100644 demos/editor/superdoc/docx-from-html/README.md rename demos/{ => editor/superdoc}/docx-from-html/demo-config.json (100%) rename demos/{ => editor/superdoc}/docx-from-html/demo-thumbnail.png (100%) rename demos/{ => editor/superdoc}/docx-from-html/demo-video.mp4 (100%) rename demos/{ => editor/superdoc}/docx-from-html/index.html (100%) rename demos/{ => editor/superdoc}/docx-from-html/package.json (100%) rename demos/{ => editor/superdoc}/docx-from-html/public/logo.webp (100%) rename demos/{ => editor/superdoc}/docx-from-html/public/sample-document.docx (100%) rename demos/{ => editor/superdoc}/docx-from-html/public/superdoc-logo.png (100%) rename demos/{ => editor/superdoc}/docx-from-html/src/App.vue (100%) rename demos/{ => editor/superdoc}/docx-from-html/src/main.js (100%) rename demos/{ => editor/superdoc}/docx-from-html/src/style.css (100%) rename demos/{ => editor/superdoc}/docx-from-html/vite.config.js (100%) diff --git a/apps/docs/document-api/migration.mdx b/apps/docs/document-api/migration.mdx index 7c5be39486..ee609c342f 100644 --- a/apps/docs/document-api/migration.mdx +++ b/apps/docs/document-api/migration.mdx @@ -175,7 +175,7 @@ superdoc.on('editorCreate', ({ editor }) => { ## Driving custom React UI -If your migration is also moving the UI side off legacy chains, the destination is `superdoc/ui/react`. Replace `superdoc.on('editor-update', ...)` loops and `useState(superdoc.activeEditor.commands.X)` patterns with the typed hooks: `useSuperDocCommand`, `useSuperDocSelection`, `useSuperDocComments`, `useSuperDocTrackChanges`, `useSuperDocDocument`. See [Custom UI](/editor/custom-ui/overview) for the full surface and the [reference workspace on GitHub](https://github.com/superdoc-dev/superdoc/tree/main/demos/custom-ui). +If your migration is also moving the UI side off legacy chains, the destination is `superdoc/ui/react`. Replace `superdoc.on('editor-update', ...)` loops and `useState(superdoc.activeEditor.commands.X)` patterns with the typed hooks: `useSuperDocCommand`, `useSuperDocSelection`, `useSuperDocComments`, `useSuperDocTrackChanges`, `useSuperDocDocument`. See [Custom UI](/editor/custom-ui/overview) for the full surface and the [reference workspace on GitHub](https://github.com/superdoc-dev/superdoc/tree/main/demos/editor/custom-ui). ## Full reference diff --git a/apps/docs/editor/custom-ui/context-menu.mdx b/apps/docs/editor/custom-ui/context-menu.mdx index 3d609f4c1d..9cd2b95a88 100644 --- a/apps/docs/editor/custom-ui/context-menu.mdx +++ b/apps/docs/editor/custom-ui/context-menu.mdx @@ -151,7 +151,7 @@ If you'd rather suppress the native menu in the empty case too, call `event.prev ## Worked example -The reference workspace at [`demos/custom-ui`](https://github.com/superdoc-dev/superdoc/tree/main/demos/custom-ui) wires the full pattern end-to-end. The four registrations below mirror the demo's `ContextMenuRegistrations.tsx`. They cover the three subjects the menu can act on: an entity, the selection, or the click point. +The reference workspace at [`demos/editor/custom-ui`](https://github.com/superdoc-dev/superdoc/tree/main/demos/editor/custom-ui) wires the full pattern end-to-end. The four registrations below mirror the demo's `ContextMenuRegistrations.tsx`. They cover the three subjects the menu can act on: an entity, the selection, or the click point. diff --git a/apps/docs/editor/custom-ui/overview.mdx b/apps/docs/editor/custom-ui/overview.mdx index a67478ec74..a6ac6eb02d 100644 --- a/apps/docs/editor/custom-ui/overview.mdx +++ b/apps/docs/editor/custom-ui/overview.mdx @@ -86,4 +86,4 @@ The controller surfaces this split directly. The toolbar reads `state.selection` ## Worked example -The [reference workspace on GitHub](https://github.com/superdoc-dev/superdoc/tree/main/demos/custom-ui) ships a full app on these surfaces: toolbar, custom command with keyboard shortcut, floating bubble menu, right-click context menu, comments sidebar with reply threads, tracked-change review, selection capture / restore, DOCX export and reimport. +The [reference workspace on GitHub](https://github.com/superdoc-dev/superdoc/tree/main/demos/editor/custom-ui) ships a full app on these surfaces: toolbar, custom command with keyboard shortcut, floating bubble menu, right-click context menu, comments sidebar with reply threads, tracked-change review, selection capture / restore, DOCX export and reimport. diff --git a/demos/chrome-extension/README.md b/demos/chrome-extension/README.md new file mode 100644 index 0000000000..0d8e366c7a --- /dev/null +++ b/demos/chrome-extension/README.md @@ -0,0 +1,5 @@ +# Moved to demos/editor/integrations/chrome-extension + +The Chrome extension demo moved to [`demos/editor/integrations/chrome-extension`](../editor/integrations/chrome-extension). + +It now sits under `demos/editor/integrations/` to reflect that it integrates SuperDoc into a host browser surface, rather than being its own product workflow. diff --git a/demos/custom-ui/README.md b/demos/custom-ui/README.md index 4aaf6686a9..4b694d3079 100644 --- a/demos/custom-ui/README.md +++ b/demos/custom-ui/README.md @@ -1,110 +1,5 @@ -# SuperDoc Custom UI demo +# Moved to demos/editor/custom-ui -A reference workspace built on the `superdoc/ui/react` surface. The headline use case is **source-grounded citations**: insert a mock RAG-generated draft, see anchored citation highlights with hover previews, navigate from a sources panel, edit or remove citations. Wrapped in a full editor workspace: custom toolbar, comment threads, tracked-change review, custom commands, and DOCX round-trip. +The custom-UI reference workspace moved to [`demos/editor/custom-ui`](../editor/custom-ui). -See the [Custom UI docs](https://docs.superdoc.dev/editor/custom-ui/overview) for the conceptual guide, and the upcoming [source-grounded citations feature page](https://docs.superdoc.dev/document-api/features/anchored-metadata) for the citation story. - -This demo shows how the pieces compose in a real product, not a single-concept recipe. Read it alongside the docs above when you're wiring your own toolbar, sources panel, or citation overlay. - -## Run - -Prerequisites: Node 20+, pnpm 9+, run from inside the SuperDoc monorepo. - -```bash -pnpm install -pnpm --filter superdoc run build -pnpm --filter @superdoc-dev/react run build -pnpm --filter custom-ui run dev -``` - -Open http://localhost:5189. - -## What you can do here - -- Open the **Sources** tab and click **Insert sample cited draft**. A mocked RAG-generated paragraph is inserted at the end of the document; each cited span is anchored with `editor.doc.metadata.attach` and rendered with a highlight overlay. -- Hover a citation highlight to see the source's display text, locator, provider, and confidence. The popover reads the payload via `ui.viewport.entityAt` + `metadata.get`. -- Click **Scroll to** in the sources panel to navigate to a cited span. Uses `ui.metadata.scrollIntoView({ id })`. -- Click **Edit** on a citation to change `displayText`, `locator`, or `excerpt`. Calls `editor.doc.metadata.update`. -- Click **Remove** to strip the anchor and payload. Calls `editor.doc.metadata.remove`. -- Click toolbar buttons (bold, italic, lists, undo, redo) wired through `useSuperDocCommand`. -- Insert a custom clause registered with `ui.commands.register`. The button works, and so does its keyboard shortcut `Mod-Shift-C`, declared on the registration rather than wired in a separate keydown listener. -- Switch between Edit and Suggest. In Suggest, every edit lands as a tracked change. -- Select text and watch the floating bubble menu appear next to the selection (anchored via `ui.selection.getAnchorRect()`, not `window.getSelection()`). -- Right-click on a tracked change, comment, inside a selection, or on plain text. The menu adapts to the click target: Accept / Reject / Resolve on entities, Copy / Comment on a selection, Insert clause here on plain caret-only text. -- Add a comment. The composer captures the selection on open, posts on submit, and restores the visible range on close so the user keeps their place. -- Accept or reject tracked changes. Decided ones move to a Resolved section. -- Export the doc, edit it in Word, click Import, watch the activity feed update. - -## Source-grounded citations - -The demo composes anchored citation pointers on top of `editor.doc.metadata.*` and `ui.metadata.*`: - -| Layer | What it does | Code | -| --- | --- | --- | -| **Mock RAG output** | Pre-canned text + per-citation payloads (sourceId, displayText, locator, excerpt, confidence). Stand-in for a real generation pipeline. | `mockDraft.ts` | -| **Insert + attach** | Inserts the text via `editor.doc.insert`, then computes a `SelectionTarget` for each cited span and calls `editor.doc.metadata.attach` per citation. | `GenerateDraftButton.tsx`, `useCitations.ts` | -| **Sources panel** | Lists citations grouped by `sourceId`. Scroll-to navigation uses `ui.metadata.scrollIntoView({ id })`. Edit form calls `editor.doc.metadata.update`. | `CitationsPanel.tsx` | -| **Highlight overlay** | Renders one absolute-positioned rectangle per painted line of each cited span. Rects come from `ui.metadata.getRect({ id })`. Remeasures on scroll, resize, ResizeObserver, and MutationObserver. | `CitationHighlights.tsx` | -| **Hover popover** | `ui.viewport.entityAt({ x, y })` returns the content control under the cursor; `metadata.get({ id })` fetches the payload to render. | `CitationPopover.tsx` | -| **Persistence** | Hidden inline content controls in the body carry the stable id in `w:tag`; payloads live in a namespaced custom XML data part. Survives DOCX export, reopen, and Word save (validated by the `word-roundtrip` fixtures in the monorepo). | `editor.doc.metadata.*` | - -`ui.metadata.*` is the supported public surface for consumer-side geometry and navigation; consumers carry the metadata id and never see the SDT node id underneath. - -## Architecture - -``` -SuperDocUIProvider one controller per app -└── EditorMount + onReady + disableContextMenu - ├── Toolbar ui.commands + setDocumentMode - ├── SelectionPopover ui.selection.getAnchorRect, bubble menu over the selection - ├── ContextMenu ui.viewport.contextAt + ui.commands.getContextMenuItems(context) + item.invoke() - ├── ContextMenuRegistrations ui.commands.register({ contextMenu: { when } }) - ├── CitationHighlights ui.metadata.getRect, painted overlay across cited spans - ├── CitationPopover ui.viewport.entityAt + metadata.get, hover preview - └── ActivitySidebar ui.comments + ui.trackChanges + ui.selection (Activity tab) - ├── CitationsPanel editor.doc.metadata.list/get/update/remove + ui.metadata.scrollIntoView (Sources tab) - └── CommentComposer ui.selection.capture / restore + ui.comments.createFromCapture -``` - -Components consume the controller via `useSuperDocUI()`. They never reach into `editor.state` or `editor.view`. - -## Three surfaces, three subjects - -The demo keeps a strict separation between the three editor UI surfaces. Each one answers a different "what's the subject of this action?" question: - -| Surface | Subject | Items in the demo | -| --- | --- | --- | -| **Toolbar** | The **document** | Bold, Italic, Lists, Undo, Redo, Mode toggle, Insert clause, Export, Import. | -| **Floating bubble menu** | The **selection** | Bold, Italic, Comment on selection. | -| **Right-click context menu** | The **clicked target** | Accept / Reject on tracked change, Resolve on comment, Copy / Comment on selection (when the click is inside the selection rect), Insert clause here (when the click lands on plain caret-only text). | - -`ui.viewport.contextAt({ x, y })` returns one bundle with the click point, the entities under it, the resolved caret position, the live selection, and `insideSelection` (whether the click landed in the painted selection rects). Each predicate filters on the same shape its handler receives, so "Copy" / "Comment on selection" gate themselves on `insideSelection === true` and "Insert clause here" gates on `position !== null && entities.length === 0 && insideSelection !== true`. A stale selection elsewhere on the page can't leak into a right-click somewhere else. - -The `Insert clause here` handler reads `context.position.target` (a collapsed `SelectionTarget` at the click point) and passes it straight to `editor.doc.insert`. The same predicate the menu was filtered with becomes the target the action acts on. Without the bundle, the registration would have to insert against the user's prior selection somewhere else in the doc, making the label a lie. - -Right-click on plain text where no item matches falls through to the browser's native menu. The handler deliberately doesn't `preventDefault` when `getContextMenuItems(context)` returns nothing, so the user gets Copy / Paste / Inspect from the browser instead of a dead right-click. - -## The four custom-UI patterns - -1. **Floating selection toolbar.** `ui.selection.getAnchorRect({ placement: 'start' })` returns viewport-relative coords for the painted selection. Re-position on `useSuperDocSelection()` change plus `scroll`/`resize`. Don't reach for `window.getSelection()`; SuperDoc's painted DOM is separate from the offscreen ProseMirror DOM and the browser API returns the wrong rect. See `SelectionPopover.tsx`. - -2. **Right-click context menu.** Set `disableContextMenu` on `` to suppress the built-in. On `contextmenu`, call `ui.viewport.contextAt({ x, y })` to get the bundle, then `ui.commands.getContextMenuItems(context)` to get items contributed via `register({ contextMenu })`. Each item carries `invoke()`, which fires the registered `execute({ context })` with the bundle bound, so handlers act on the click target without the menu component threading payloads. Scope the listener with `ui.viewport.getHost()` instead of a CSS class. See `ContextMenu.tsx` and `ContextMenuRegistrations.tsx`. - -3. **Custom command + keyboard shortcut.** Declare `shortcut: 'Mod-Shift-C'` on the registration. The controller installs a single bubble-phase keydown listener scoped to the painted host; matched shortcuts dispatch through the same path the toolbar button uses. No per-command keymap wiring. See `InsertClauseButton.tsx`. - -4. **Composer capture + restore.** `ui.selection.capture()` on open holds the selection across focus moves. `ui.comments.createFromCapture(captured, { text })` posts the comment using the frozen target. `ui.selection.restore(captured)` puts the visible selection back so the user keeps their place. See `CommentComposer.tsx`. - -## Adapting this to your stack - -- **One provider, many components.** Toolbar, sidebar, and review panel all subscribe to the same controller via hooks. They don't pass props down a tree. -- **No design system.** Plain React, plain CSS. Drop the same patterns into Tailwind / shadcn / MUI / Mantine. -- **`modules: { comments: false }` and your own panel.** The demo turns off the built-in comments UI and renders its own. Imported comments still flow through export and import. -- **Capture, then restore.** Composers freeze the selection at open, post on submit, then `restore(capture)` on close. The user sees their range come back instead of typing into a vanished selection. -- **Activity feed merge.** `ActivitySidebar.tsx` interleaves `ui.comments` and `ui.trackChanges` into one panel with about thirty lines of merge logic. The two slices stay separate on the controller so apps that only render one don't pay for the other. - -## What this demo deliberately doesn't do - -- No design system. Patterns over CSS, copy them into yours. -- No backend. The clause library in `` is hardcoded. Real consumers fetch from their own API and call `reg.invalidate()` when permissions or availability change. -- No live AI provider. The citation flow uses pre-canned draft text + payloads in `mockDraft.ts` instead of calling an LLM. Real consumers replace this with their RAG output, but the shape that flows into `editor.doc.metadata.attach` (text + cited ranges + payloads) stays the same. -- Telemetry is off (`telemetry: { enabled: false }` in `EditorMount.tsx`) because there's no analytics endpoint to receive events. SuperDoc defaults to enabled. +It now sits under `demos/editor/` to mirror the docs nav (Editor > Custom UI). The workspace is a reference for composing many SuperDoc UI surfaces (toolbar, comments, tracked changes, citations, custom commands, context menus) in one app, including the source-grounded citation flow. diff --git a/demos/docx-from-html/README.md b/demos/docx-from-html/README.md index fa29f456fa..a1db7ffa0d 100644 --- a/demos/docx-from-html/README.md +++ b/demos/docx-from-html/README.md @@ -1,7 +1,5 @@ -# SuperDoc: Init a DOCX from HTML Content +# Moved to demos/editor/superdoc/docx-from-html -An example of initializing SuperDoc with HTML content. +The "init a DOCX from HTML" demo moved to [`demos/editor/superdoc/docx-from-html`](../editor/superdoc/docx-from-html). -This will load a DOCX file (or a blank document), replacing the main contents with the provided HTML. - -In the example we pass `document: sample-document.docx` to load a template with a header and footer. You can omit this key to start with a blank document. +It now sits under `demos/editor/superdoc/` because it teaches an editor-side initialization pattern (passing HTML to ``), not a headless Document API operation. diff --git a/demos/editor/custom-ui/README.md b/demos/editor/custom-ui/README.md new file mode 100644 index 0000000000..4aaf6686a9 --- /dev/null +++ b/demos/editor/custom-ui/README.md @@ -0,0 +1,110 @@ +# SuperDoc Custom UI demo + +A reference workspace built on the `superdoc/ui/react` surface. The headline use case is **source-grounded citations**: insert a mock RAG-generated draft, see anchored citation highlights with hover previews, navigate from a sources panel, edit or remove citations. Wrapped in a full editor workspace: custom toolbar, comment threads, tracked-change review, custom commands, and DOCX round-trip. + +See the [Custom UI docs](https://docs.superdoc.dev/editor/custom-ui/overview) for the conceptual guide, and the upcoming [source-grounded citations feature page](https://docs.superdoc.dev/document-api/features/anchored-metadata) for the citation story. + +This demo shows how the pieces compose in a real product, not a single-concept recipe. Read it alongside the docs above when you're wiring your own toolbar, sources panel, or citation overlay. + +## Run + +Prerequisites: Node 20+, pnpm 9+, run from inside the SuperDoc monorepo. + +```bash +pnpm install +pnpm --filter superdoc run build +pnpm --filter @superdoc-dev/react run build +pnpm --filter custom-ui run dev +``` + +Open http://localhost:5189. + +## What you can do here + +- Open the **Sources** tab and click **Insert sample cited draft**. A mocked RAG-generated paragraph is inserted at the end of the document; each cited span is anchored with `editor.doc.metadata.attach` and rendered with a highlight overlay. +- Hover a citation highlight to see the source's display text, locator, provider, and confidence. The popover reads the payload via `ui.viewport.entityAt` + `metadata.get`. +- Click **Scroll to** in the sources panel to navigate to a cited span. Uses `ui.metadata.scrollIntoView({ id })`. +- Click **Edit** on a citation to change `displayText`, `locator`, or `excerpt`. Calls `editor.doc.metadata.update`. +- Click **Remove** to strip the anchor and payload. Calls `editor.doc.metadata.remove`. +- Click toolbar buttons (bold, italic, lists, undo, redo) wired through `useSuperDocCommand`. +- Insert a custom clause registered with `ui.commands.register`. The button works, and so does its keyboard shortcut `Mod-Shift-C`, declared on the registration rather than wired in a separate keydown listener. +- Switch between Edit and Suggest. In Suggest, every edit lands as a tracked change. +- Select text and watch the floating bubble menu appear next to the selection (anchored via `ui.selection.getAnchorRect()`, not `window.getSelection()`). +- Right-click on a tracked change, comment, inside a selection, or on plain text. The menu adapts to the click target: Accept / Reject / Resolve on entities, Copy / Comment on a selection, Insert clause here on plain caret-only text. +- Add a comment. The composer captures the selection on open, posts on submit, and restores the visible range on close so the user keeps their place. +- Accept or reject tracked changes. Decided ones move to a Resolved section. +- Export the doc, edit it in Word, click Import, watch the activity feed update. + +## Source-grounded citations + +The demo composes anchored citation pointers on top of `editor.doc.metadata.*` and `ui.metadata.*`: + +| Layer | What it does | Code | +| --- | --- | --- | +| **Mock RAG output** | Pre-canned text + per-citation payloads (sourceId, displayText, locator, excerpt, confidence). Stand-in for a real generation pipeline. | `mockDraft.ts` | +| **Insert + attach** | Inserts the text via `editor.doc.insert`, then computes a `SelectionTarget` for each cited span and calls `editor.doc.metadata.attach` per citation. | `GenerateDraftButton.tsx`, `useCitations.ts` | +| **Sources panel** | Lists citations grouped by `sourceId`. Scroll-to navigation uses `ui.metadata.scrollIntoView({ id })`. Edit form calls `editor.doc.metadata.update`. | `CitationsPanel.tsx` | +| **Highlight overlay** | Renders one absolute-positioned rectangle per painted line of each cited span. Rects come from `ui.metadata.getRect({ id })`. Remeasures on scroll, resize, ResizeObserver, and MutationObserver. | `CitationHighlights.tsx` | +| **Hover popover** | `ui.viewport.entityAt({ x, y })` returns the content control under the cursor; `metadata.get({ id })` fetches the payload to render. | `CitationPopover.tsx` | +| **Persistence** | Hidden inline content controls in the body carry the stable id in `w:tag`; payloads live in a namespaced custom XML data part. Survives DOCX export, reopen, and Word save (validated by the `word-roundtrip` fixtures in the monorepo). | `editor.doc.metadata.*` | + +`ui.metadata.*` is the supported public surface for consumer-side geometry and navigation; consumers carry the metadata id and never see the SDT node id underneath. + +## Architecture + +``` +SuperDocUIProvider one controller per app +└── EditorMount + onReady + disableContextMenu + ├── Toolbar ui.commands + setDocumentMode + ├── SelectionPopover ui.selection.getAnchorRect, bubble menu over the selection + ├── ContextMenu ui.viewport.contextAt + ui.commands.getContextMenuItems(context) + item.invoke() + ├── ContextMenuRegistrations ui.commands.register({ contextMenu: { when } }) + ├── CitationHighlights ui.metadata.getRect, painted overlay across cited spans + ├── CitationPopover ui.viewport.entityAt + metadata.get, hover preview + └── ActivitySidebar ui.comments + ui.trackChanges + ui.selection (Activity tab) + ├── CitationsPanel editor.doc.metadata.list/get/update/remove + ui.metadata.scrollIntoView (Sources tab) + └── CommentComposer ui.selection.capture / restore + ui.comments.createFromCapture +``` + +Components consume the controller via `useSuperDocUI()`. They never reach into `editor.state` or `editor.view`. + +## Three surfaces, three subjects + +The demo keeps a strict separation between the three editor UI surfaces. Each one answers a different "what's the subject of this action?" question: + +| Surface | Subject | Items in the demo | +| --- | --- | --- | +| **Toolbar** | The **document** | Bold, Italic, Lists, Undo, Redo, Mode toggle, Insert clause, Export, Import. | +| **Floating bubble menu** | The **selection** | Bold, Italic, Comment on selection. | +| **Right-click context menu** | The **clicked target** | Accept / Reject on tracked change, Resolve on comment, Copy / Comment on selection (when the click is inside the selection rect), Insert clause here (when the click lands on plain caret-only text). | + +`ui.viewport.contextAt({ x, y })` returns one bundle with the click point, the entities under it, the resolved caret position, the live selection, and `insideSelection` (whether the click landed in the painted selection rects). Each predicate filters on the same shape its handler receives, so "Copy" / "Comment on selection" gate themselves on `insideSelection === true` and "Insert clause here" gates on `position !== null && entities.length === 0 && insideSelection !== true`. A stale selection elsewhere on the page can't leak into a right-click somewhere else. + +The `Insert clause here` handler reads `context.position.target` (a collapsed `SelectionTarget` at the click point) and passes it straight to `editor.doc.insert`. The same predicate the menu was filtered with becomes the target the action acts on. Without the bundle, the registration would have to insert against the user's prior selection somewhere else in the doc, making the label a lie. + +Right-click on plain text where no item matches falls through to the browser's native menu. The handler deliberately doesn't `preventDefault` when `getContextMenuItems(context)` returns nothing, so the user gets Copy / Paste / Inspect from the browser instead of a dead right-click. + +## The four custom-UI patterns + +1. **Floating selection toolbar.** `ui.selection.getAnchorRect({ placement: 'start' })` returns viewport-relative coords for the painted selection. Re-position on `useSuperDocSelection()` change plus `scroll`/`resize`. Don't reach for `window.getSelection()`; SuperDoc's painted DOM is separate from the offscreen ProseMirror DOM and the browser API returns the wrong rect. See `SelectionPopover.tsx`. + +2. **Right-click context menu.** Set `disableContextMenu` on `` to suppress the built-in. On `contextmenu`, call `ui.viewport.contextAt({ x, y })` to get the bundle, then `ui.commands.getContextMenuItems(context)` to get items contributed via `register({ contextMenu })`. Each item carries `invoke()`, which fires the registered `execute({ context })` with the bundle bound, so handlers act on the click target without the menu component threading payloads. Scope the listener with `ui.viewport.getHost()` instead of a CSS class. See `ContextMenu.tsx` and `ContextMenuRegistrations.tsx`. + +3. **Custom command + keyboard shortcut.** Declare `shortcut: 'Mod-Shift-C'` on the registration. The controller installs a single bubble-phase keydown listener scoped to the painted host; matched shortcuts dispatch through the same path the toolbar button uses. No per-command keymap wiring. See `InsertClauseButton.tsx`. + +4. **Composer capture + restore.** `ui.selection.capture()` on open holds the selection across focus moves. `ui.comments.createFromCapture(captured, { text })` posts the comment using the frozen target. `ui.selection.restore(captured)` puts the visible selection back so the user keeps their place. See `CommentComposer.tsx`. + +## Adapting this to your stack + +- **One provider, many components.** Toolbar, sidebar, and review panel all subscribe to the same controller via hooks. They don't pass props down a tree. +- **No design system.** Plain React, plain CSS. Drop the same patterns into Tailwind / shadcn / MUI / Mantine. +- **`modules: { comments: false }` and your own panel.** The demo turns off the built-in comments UI and renders its own. Imported comments still flow through export and import. +- **Capture, then restore.** Composers freeze the selection at open, post on submit, then `restore(capture)` on close. The user sees their range come back instead of typing into a vanished selection. +- **Activity feed merge.** `ActivitySidebar.tsx` interleaves `ui.comments` and `ui.trackChanges` into one panel with about thirty lines of merge logic. The two slices stay separate on the controller so apps that only render one don't pay for the other. + +## What this demo deliberately doesn't do + +- No design system. Patterns over CSS, copy them into yours. +- No backend. The clause library in `` is hardcoded. Real consumers fetch from their own API and call `reg.invalidate()` when permissions or availability change. +- No live AI provider. The citation flow uses pre-canned draft text + payloads in `mockDraft.ts` instead of calling an LLM. Real consumers replace this with their RAG output, but the shape that flows into `editor.doc.metadata.attach` (text + cited ranges + payloads) stays the same. +- Telemetry is off (`telemetry: { enabled: false }` in `EditorMount.tsx`) because there's no analytics endpoint to receive events. SuperDoc defaults to enabled. diff --git a/demos/custom-ui/index.html b/demos/editor/custom-ui/index.html similarity index 100% rename from demos/custom-ui/index.html rename to demos/editor/custom-ui/index.html diff --git a/demos/custom-ui/package.json b/demos/editor/custom-ui/package.json similarity index 100% rename from demos/custom-ui/package.json rename to demos/editor/custom-ui/package.json diff --git a/demos/custom-ui/public/sample-review.docx b/demos/editor/custom-ui/public/sample-review.docx similarity index 100% rename from demos/custom-ui/public/sample-review.docx rename to demos/editor/custom-ui/public/sample-review.docx diff --git a/demos/custom-ui/src/App.tsx b/demos/editor/custom-ui/src/App.tsx similarity index 100% rename from demos/custom-ui/src/App.tsx rename to demos/editor/custom-ui/src/App.tsx diff --git a/demos/custom-ui/src/components/ActivitySidebar.tsx b/demos/editor/custom-ui/src/components/ActivitySidebar.tsx similarity index 100% rename from demos/custom-ui/src/components/ActivitySidebar.tsx rename to demos/editor/custom-ui/src/components/ActivitySidebar.tsx diff --git a/demos/custom-ui/src/components/CitationHighlights.tsx b/demos/editor/custom-ui/src/components/CitationHighlights.tsx similarity index 100% rename from demos/custom-ui/src/components/CitationHighlights.tsx rename to demos/editor/custom-ui/src/components/CitationHighlights.tsx diff --git a/demos/custom-ui/src/components/CitationPopover.tsx b/demos/editor/custom-ui/src/components/CitationPopover.tsx similarity index 100% rename from demos/custom-ui/src/components/CitationPopover.tsx rename to demos/editor/custom-ui/src/components/CitationPopover.tsx diff --git a/demos/custom-ui/src/components/CitationsPanel.tsx b/demos/editor/custom-ui/src/components/CitationsPanel.tsx similarity index 100% rename from demos/custom-ui/src/components/CitationsPanel.tsx rename to demos/editor/custom-ui/src/components/CitationsPanel.tsx diff --git a/demos/custom-ui/src/components/CommentComposer.tsx b/demos/editor/custom-ui/src/components/CommentComposer.tsx similarity index 100% rename from demos/custom-ui/src/components/CommentComposer.tsx rename to demos/editor/custom-ui/src/components/CommentComposer.tsx diff --git a/demos/custom-ui/src/components/ContextMenu.tsx b/demos/editor/custom-ui/src/components/ContextMenu.tsx similarity index 100% rename from demos/custom-ui/src/components/ContextMenu.tsx rename to demos/editor/custom-ui/src/components/ContextMenu.tsx diff --git a/demos/custom-ui/src/components/ContextMenuRegistrations.tsx b/demos/editor/custom-ui/src/components/ContextMenuRegistrations.tsx similarity index 100% rename from demos/custom-ui/src/components/ContextMenuRegistrations.tsx rename to demos/editor/custom-ui/src/components/ContextMenuRegistrations.tsx diff --git a/demos/custom-ui/src/components/DisplaySettings.tsx b/demos/editor/custom-ui/src/components/DisplaySettings.tsx similarity index 100% rename from demos/custom-ui/src/components/DisplaySettings.tsx rename to demos/editor/custom-ui/src/components/DisplaySettings.tsx diff --git a/demos/custom-ui/src/components/GenerateDraftButton.tsx b/demos/editor/custom-ui/src/components/GenerateDraftButton.tsx similarity index 100% rename from demos/custom-ui/src/components/GenerateDraftButton.tsx rename to demos/editor/custom-ui/src/components/GenerateDraftButton.tsx diff --git a/demos/custom-ui/src/components/InsertClauseButton.tsx b/demos/editor/custom-ui/src/components/InsertClauseButton.tsx similarity index 100% rename from demos/custom-ui/src/components/InsertClauseButton.tsx rename to demos/editor/custom-ui/src/components/InsertClauseButton.tsx diff --git a/demos/custom-ui/src/components/SelectionPopover.tsx b/demos/editor/custom-ui/src/components/SelectionPopover.tsx similarity index 100% rename from demos/custom-ui/src/components/SelectionPopover.tsx rename to demos/editor/custom-ui/src/components/SelectionPopover.tsx diff --git a/demos/custom-ui/src/components/Toolbar.tsx b/demos/editor/custom-ui/src/components/Toolbar.tsx similarity index 100% rename from demos/custom-ui/src/components/Toolbar.tsx rename to demos/editor/custom-ui/src/components/Toolbar.tsx diff --git a/demos/custom-ui/src/components/citations-types.ts b/demos/editor/custom-ui/src/components/citations-types.ts similarity index 100% rename from demos/custom-ui/src/components/citations-types.ts rename to demos/editor/custom-ui/src/components/citations-types.ts diff --git a/demos/custom-ui/src/components/mockDraft.ts b/demos/editor/custom-ui/src/components/mockDraft.ts similarity index 100% rename from demos/custom-ui/src/components/mockDraft.ts rename to demos/editor/custom-ui/src/components/mockDraft.ts diff --git a/demos/custom-ui/src/components/useCitations.ts b/demos/editor/custom-ui/src/components/useCitations.ts similarity index 100% rename from demos/custom-ui/src/components/useCitations.ts rename to demos/editor/custom-ui/src/components/useCitations.ts diff --git a/demos/custom-ui/src/components/useDecidedChanges.ts b/demos/editor/custom-ui/src/components/useDecidedChanges.ts similarity index 100% rename from demos/custom-ui/src/components/useDecidedChanges.ts rename to demos/editor/custom-ui/src/components/useDecidedChanges.ts diff --git a/demos/custom-ui/src/editor/EditorMount.tsx b/demos/editor/custom-ui/src/editor/EditorMount.tsx similarity index 100% rename from demos/custom-ui/src/editor/EditorMount.tsx rename to demos/editor/custom-ui/src/editor/EditorMount.tsx diff --git a/demos/custom-ui/src/main.tsx b/demos/editor/custom-ui/src/main.tsx similarity index 100% rename from demos/custom-ui/src/main.tsx rename to demos/editor/custom-ui/src/main.tsx diff --git a/demos/custom-ui/src/styles.css b/demos/editor/custom-ui/src/styles.css similarity index 100% rename from demos/custom-ui/src/styles.css rename to demos/editor/custom-ui/src/styles.css diff --git a/demos/custom-ui/tsconfig.json b/demos/editor/custom-ui/tsconfig.json similarity index 100% rename from demos/custom-ui/tsconfig.json rename to demos/editor/custom-ui/tsconfig.json diff --git a/demos/custom-ui/vite.config.ts b/demos/editor/custom-ui/vite.config.ts similarity index 100% rename from demos/custom-ui/vite.config.ts rename to demos/editor/custom-ui/vite.config.ts diff --git a/demos/chrome-extension/chrome-extension/README.md b/demos/editor/integrations/chrome-extension/chrome-extension/README.md similarity index 100% rename from demos/chrome-extension/chrome-extension/README.md rename to demos/editor/integrations/chrome-extension/chrome-extension/README.md diff --git a/demos/chrome-extension/chrome-extension/background.js b/demos/editor/integrations/chrome-extension/chrome-extension/background.js similarity index 100% rename from demos/chrome-extension/chrome-extension/background.js rename to demos/editor/integrations/chrome-extension/chrome-extension/background.js diff --git a/demos/chrome-extension/chrome-extension/content.js b/demos/editor/integrations/chrome-extension/chrome-extension/content.js similarity index 100% rename from demos/chrome-extension/chrome-extension/content.js rename to demos/editor/integrations/chrome-extension/chrome-extension/content.js diff --git a/demos/chrome-extension/chrome-extension/dist/docx-validator.bundle.js b/demos/editor/integrations/chrome-extension/chrome-extension/dist/docx-validator.bundle.js similarity index 100% rename from demos/chrome-extension/chrome-extension/dist/docx-validator.bundle.js rename to demos/editor/integrations/chrome-extension/chrome-extension/dist/docx-validator.bundle.js diff --git a/demos/chrome-extension/chrome-extension/dist/docx-validator.bundle.js.LICENSE.txt b/demos/editor/integrations/chrome-extension/chrome-extension/dist/docx-validator.bundle.js.LICENSE.txt similarity index 100% rename from demos/chrome-extension/chrome-extension/dist/docx-validator.bundle.js.LICENSE.txt rename to demos/editor/integrations/chrome-extension/chrome-extension/dist/docx-validator.bundle.js.LICENSE.txt diff --git a/demos/chrome-extension/chrome-extension/docx-validator.js b/demos/editor/integrations/chrome-extension/chrome-extension/docx-validator.js similarity index 100% rename from demos/chrome-extension/chrome-extension/docx-validator.js rename to demos/editor/integrations/chrome-extension/chrome-extension/docx-validator.js diff --git a/demos/chrome-extension/chrome-extension/icons/icon-128x128-disabled.png b/demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-128x128-disabled.png similarity index 100% rename from demos/chrome-extension/chrome-extension/icons/icon-128x128-disabled.png rename to demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-128x128-disabled.png diff --git a/demos/chrome-extension/chrome-extension/icons/icon-128x128.png b/demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-128x128.png similarity index 100% rename from demos/chrome-extension/chrome-extension/icons/icon-128x128.png rename to demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-128x128.png diff --git a/demos/chrome-extension/chrome-extension/icons/icon-16x16-disabled.png b/demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-16x16-disabled.png similarity index 100% rename from demos/chrome-extension/chrome-extension/icons/icon-16x16-disabled.png rename to demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-16x16-disabled.png diff --git a/demos/chrome-extension/chrome-extension/icons/icon-16x16.png b/demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-16x16.png similarity index 100% rename from demos/chrome-extension/chrome-extension/icons/icon-16x16.png rename to demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-16x16.png diff --git a/demos/chrome-extension/chrome-extension/icons/icon-19x19-disabled.png b/demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-19x19-disabled.png similarity index 100% rename from demos/chrome-extension/chrome-extension/icons/icon-19x19-disabled.png rename to demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-19x19-disabled.png diff --git a/demos/chrome-extension/chrome-extension/icons/icon-19x19.png b/demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-19x19.png similarity index 100% rename from demos/chrome-extension/chrome-extension/icons/icon-19x19.png rename to demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-19x19.png diff --git a/demos/chrome-extension/chrome-extension/icons/icon-48x48-disabled.png b/demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-48x48-disabled.png similarity index 100% rename from demos/chrome-extension/chrome-extension/icons/icon-48x48-disabled.png rename to demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-48x48-disabled.png diff --git a/demos/chrome-extension/chrome-extension/icons/icon-48x48.png b/demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-48x48.png similarity index 100% rename from demos/chrome-extension/chrome-extension/icons/icon-48x48.png rename to demos/editor/integrations/chrome-extension/chrome-extension/icons/icon-48x48.png diff --git a/demos/chrome-extension/chrome-extension/icons/logo.webp b/demos/editor/integrations/chrome-extension/chrome-extension/icons/logo.webp similarity index 100% rename from demos/chrome-extension/chrome-extension/icons/logo.webp rename to demos/editor/integrations/chrome-extension/chrome-extension/icons/logo.webp diff --git a/demos/chrome-extension/chrome-extension/lib/style.css b/demos/editor/integrations/chrome-extension/chrome-extension/lib/style.css similarity index 100% rename from demos/chrome-extension/chrome-extension/lib/style.css rename to demos/editor/integrations/chrome-extension/chrome-extension/lib/style.css diff --git a/demos/chrome-extension/chrome-extension/lib/superdoc.min.js b/demos/editor/integrations/chrome-extension/chrome-extension/lib/superdoc.min.js similarity index 100% rename from demos/chrome-extension/chrome-extension/lib/superdoc.min.js rename to demos/editor/integrations/chrome-extension/chrome-extension/lib/superdoc.min.js diff --git a/demos/chrome-extension/chrome-extension/manifest.json b/demos/editor/integrations/chrome-extension/chrome-extension/manifest.json similarity index 100% rename from demos/chrome-extension/chrome-extension/manifest.json rename to demos/editor/integrations/chrome-extension/chrome-extension/manifest.json diff --git a/demos/chrome-extension/chrome-extension/modal.css b/demos/editor/integrations/chrome-extension/chrome-extension/modal.css similarity index 100% rename from demos/chrome-extension/chrome-extension/modal.css rename to demos/editor/integrations/chrome-extension/chrome-extension/modal.css diff --git a/demos/chrome-extension/chrome-extension/modal.html b/demos/editor/integrations/chrome-extension/chrome-extension/modal.html similarity index 100% rename from demos/chrome-extension/chrome-extension/modal.html rename to demos/editor/integrations/chrome-extension/chrome-extension/modal.html diff --git a/demos/chrome-extension/chrome-extension/package.json b/demos/editor/integrations/chrome-extension/chrome-extension/package.json similarity index 100% rename from demos/chrome-extension/chrome-extension/package.json rename to demos/editor/integrations/chrome-extension/chrome-extension/package.json diff --git a/demos/chrome-extension/chrome-extension/popup.html b/demos/editor/integrations/chrome-extension/chrome-extension/popup.html similarity index 100% rename from demos/chrome-extension/chrome-extension/popup.html rename to demos/editor/integrations/chrome-extension/chrome-extension/popup.html diff --git a/demos/chrome-extension/chrome-extension/popup.js b/demos/editor/integrations/chrome-extension/chrome-extension/popup.js similarity index 100% rename from demos/chrome-extension/chrome-extension/popup.js rename to demos/editor/integrations/chrome-extension/chrome-extension/popup.js diff --git a/demos/chrome-extension/chrome-extension/test_docs/Lunch Haiku (5).docx b/demos/editor/integrations/chrome-extension/chrome-extension/test_docs/Lunch Haiku (5).docx similarity index 100% rename from demos/chrome-extension/chrome-extension/test_docs/Lunch Haiku (5).docx rename to demos/editor/integrations/chrome-extension/chrome-extension/test_docs/Lunch Haiku (5).docx diff --git a/demos/chrome-extension/chrome-extension/test_docs/Mutual NDA_draft (1).docx b/demos/editor/integrations/chrome-extension/chrome-extension/test_docs/Mutual NDA_draft (1).docx similarity index 100% rename from demos/chrome-extension/chrome-extension/test_docs/Mutual NDA_draft (1).docx rename to demos/editor/integrations/chrome-extension/chrome-extension/test_docs/Mutual NDA_draft (1).docx diff --git a/demos/chrome-extension/chrome-extension/test_docs/Nda Formatted Doc MS WORD.docx b/demos/editor/integrations/chrome-extension/chrome-extension/test_docs/Nda Formatted Doc MS WORD.docx similarity index 100% rename from demos/chrome-extension/chrome-extension/test_docs/Nda Formatted Doc MS WORD.docx rename to demos/editor/integrations/chrome-extension/chrome-extension/test_docs/Nda Formatted Doc MS WORD.docx diff --git a/demos/chrome-extension/chrome-extension/test_docs/Nda Formatted Doc.docx b/demos/editor/integrations/chrome-extension/chrome-extension/test_docs/Nda Formatted Doc.docx similarity index 100% rename from demos/chrome-extension/chrome-extension/test_docs/Nda Formatted Doc.docx rename to demos/editor/integrations/chrome-extension/chrome-extension/test_docs/Nda Formatted Doc.docx diff --git a/demos/chrome-extension/chrome-extension/test_docs/nda_formatted_doc (1).md b/demos/editor/integrations/chrome-extension/chrome-extension/test_docs/nda_formatted_doc (1).md similarity index 100% rename from demos/chrome-extension/chrome-extension/test_docs/nda_formatted_doc (1).md rename to demos/editor/integrations/chrome-extension/chrome-extension/test_docs/nda_formatted_doc (1).md diff --git a/demos/chrome-extension/chrome-extension/test_docs/sdpr (23).docx b/demos/editor/integrations/chrome-extension/chrome-extension/test_docs/sdpr (23).docx similarity index 100% rename from demos/chrome-extension/chrome-extension/test_docs/sdpr (23).docx rename to demos/editor/integrations/chrome-extension/chrome-extension/test_docs/sdpr (23).docx diff --git a/demos/chrome-extension/chrome-extension/tester.html b/demos/editor/integrations/chrome-extension/chrome-extension/tester.html similarity index 100% rename from demos/chrome-extension/chrome-extension/tester.html rename to demos/editor/integrations/chrome-extension/chrome-extension/tester.html diff --git a/demos/chrome-extension/chrome-extension/webpack.config.js b/demos/editor/integrations/chrome-extension/chrome-extension/webpack.config.js similarity index 100% rename from demos/chrome-extension/chrome-extension/webpack.config.js rename to demos/editor/integrations/chrome-extension/chrome-extension/webpack.config.js diff --git a/demos/chrome-extension/demo-config.json b/demos/editor/integrations/chrome-extension/demo-config.json similarity index 100% rename from demos/chrome-extension/demo-config.json rename to demos/editor/integrations/chrome-extension/demo-config.json diff --git a/demos/chrome-extension/demo-thumbnail.png b/demos/editor/integrations/chrome-extension/demo-thumbnail.png similarity index 100% rename from demos/chrome-extension/demo-thumbnail.png rename to demos/editor/integrations/chrome-extension/demo-thumbnail.png diff --git a/demos/chrome-extension/demo-video.mp4 b/demos/editor/integrations/chrome-extension/demo-video.mp4 similarity index 100% rename from demos/chrome-extension/demo-video.mp4 rename to demos/editor/integrations/chrome-extension/demo-video.mp4 diff --git a/demos/word-addin/.gitignore b/demos/editor/integrations/word-addin/.gitignore similarity index 100% rename from demos/word-addin/.gitignore rename to demos/editor/integrations/word-addin/.gitignore diff --git a/demos/word-addin/MS-Word-Add-in-Sample.code-workspace b/demos/editor/integrations/word-addin/MS-Word-Add-in-Sample.code-workspace similarity index 100% rename from demos/word-addin/MS-Word-Add-in-Sample.code-workspace rename to demos/editor/integrations/word-addin/MS-Word-Add-in-Sample.code-workspace diff --git a/demos/editor/integrations/word-addin/README.md b/demos/editor/integrations/word-addin/README.md new file mode 100644 index 0000000000..0208008a31 --- /dev/null +++ b/demos/editor/integrations/word-addin/README.md @@ -0,0 +1,165 @@ +# SuperDoc MS Add-in Sync + +Real-time document synchronization between Microsoft Word Add-in and web editor using WebSocket communication. + +## Architecture + +The system consists of three main components: +- **MS Word Add-in** (`src/taskpane/taskpane.js`) - Runs inside Microsoft Word +- **Web Editor** (`server/public/editor.js`) - Browser-based document editor +- **Node.js Server** (`server/server.js`) - WebSocket server handling real-time sync + +## WebSocket Events + +The WebSocket communication uses the following event types: + +### `client_ready` +**Sent by:** Web Editor +**Handled by:** Server (broadcasts to other clients) +**Purpose:** Signals that a browser client has loaded and is ready to receive authentication + +```javascript +// Sent by editor.js +websocket.send(JSON.stringify({ + type: 'client_ready' +})); + +// Broadcasted by server.js to other clients +{ + type: 'client_ready', + timestamp: '2024-01-01T00:00:00.000Z' +} +``` + +### `token_transfer` +**Sent by:** MS Word Add-in +**Handled by:** Server (validates and broadcasts to other clients), Web Editor +**Purpose:** Transfers authentication token from Word add-in to web editor + +```javascript +// Sent by taskpane.js +websocket.send(JSON.stringify({ + type: 'token_transfer', + token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik...' +})); + +// Broadcasted by server.js after validation +{ + type: 'token_transfer', + token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik...', + user: { + email: 'user@example.com', + name: 'User Name', + picture: 'https://example.com/avatar.jpg' + }, + timestamp: '2024-01-01T00:00:00.000Z' +} + +// Error response +{ + type: 'token_transfer', + error: 'Invalid token', + timestamp: '2024-01-01T00:00:00.000Z' +} +``` + +### `document_update` +**Sent by:** MS Word Add-in, Web Editor +**Handled by:** Server (validates and broadcasts to other clients), MS Word Add-in, Web Editor +**Purpose:** Real-time document synchronization between clients + +```javascript +// Sent by taskpane.js or editor.js +websocket.send(JSON.stringify({ + type: 'document_update', + token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik...', + document: 'UEsDBBQABgAIAAAAIQDb4fbL7...' // Base64 encoded .docx +})); + +// Broadcasted by server.js after validation +{ + type: 'document_update', + document: 'UEsDBBQABgAIAAAAIQDb4fbL7...', + author: 'user@example.com', + timestamp: '2024-01-01T00:00:00.000Z' +} +``` + +### `close` +**Sent by:** Server +**Handled by:** MS Word Add-in, Web Editor +**Purpose:** Notifies clients when another connection closes + +```javascript +// Broadcasted by server.js +{ + type: 'close', + timestamp: '2024-01-01T00:00:00.000Z' +} +``` + +### `error` +**Sent by:** Server +**Handled by:** MS Word Add-in, Web Editor +**Purpose:** Notifies clients when a connection error occurs + +```javascript +// Broadcasted by server.js +{ + type: 'error', + timestamp: '2024-01-01T00:00:00.000Z' +} +``` + +## Authentication Flow + +1. Web editor loads and sends `client_ready` to server +2. Server broadcasts `client_ready` to Word add-in +3. Word add-in sends `token_transfer` with user's authentication token +4. Server validates token against Auth0 and broadcasts `token_transfer` with user info to web editor +5. Both clients are now authenticated and can send `document_update` events + +## Real-time Synchronization + +- Document changes in Word trigger `document_update` events via selection change detection +- Document changes in web editor trigger `document_update` events via SuperDoc's `onEditorUpdate` callback +- All `document_update` events include the full document as Base64-encoded .docx +- Server validates authentication token before broadcasting updates +- Clients update their document content when receiving `document_update` events + +## Setup + +1. Install dependencies: + ```bash + npm install + cd server && npm install + ``` + +2. Configure environment variables: + + **Auth0 Configuration** - Set up `src/auth0-config.js`: + - Get your Auth0 domain, client ID, and audience from your [Auth0 Dashboard](https://manage.auth0.com/) + - Create a new application or use an existing Single Page Application + - Configure the redirect URLs to include your add-in's domain + + **Server Configuration** - Set up `server/.env`: + - `AUTH0_DOMAIN`: Your Auth0 domain (e.g., `yourapp.us.auth0.com`) + - `AUTH0_AUDIENCE`: Your Auth0 API identifier + - Any additional environment variables required by your cloud function + + These environment variables are required for: + - **Auth0**: Authenticating users and validating JWT tokens + - **Cloud Function**: Server-side token validation and WebSocket communication + + You can reference the example files in the same directories. + +3. Start the server: + ```bash + npm run server + ``` + +4. Build and run the add-in: + ```bash + npm run build + npm start + ``` \ No newline at end of file diff --git a/demos/word-addin/assets/icon-128.png b/demos/editor/integrations/word-addin/assets/icon-128.png similarity index 100% rename from demos/word-addin/assets/icon-128.png rename to demos/editor/integrations/word-addin/assets/icon-128.png diff --git a/demos/word-addin/assets/icon-128x128.png b/demos/editor/integrations/word-addin/assets/icon-128x128.png similarity index 100% rename from demos/word-addin/assets/icon-128x128.png rename to demos/editor/integrations/word-addin/assets/icon-128x128.png diff --git a/demos/word-addin/assets/icon-16.png b/demos/editor/integrations/word-addin/assets/icon-16.png similarity index 100% rename from demos/word-addin/assets/icon-16.png rename to demos/editor/integrations/word-addin/assets/icon-16.png diff --git a/demos/word-addin/assets/icon-16x16.png b/demos/editor/integrations/word-addin/assets/icon-16x16.png similarity index 100% rename from demos/word-addin/assets/icon-16x16.png rename to demos/editor/integrations/word-addin/assets/icon-16x16.png diff --git a/demos/word-addin/assets/icon-32.png b/demos/editor/integrations/word-addin/assets/icon-32.png similarity index 100% rename from demos/word-addin/assets/icon-32.png rename to demos/editor/integrations/word-addin/assets/icon-32.png diff --git a/demos/word-addin/assets/icon-32x32.png b/demos/editor/integrations/word-addin/assets/icon-32x32.png similarity index 100% rename from demos/word-addin/assets/icon-32x32.png rename to demos/editor/integrations/word-addin/assets/icon-32x32.png diff --git a/demos/word-addin/assets/icon-64.png b/demos/editor/integrations/word-addin/assets/icon-64.png similarity index 100% rename from demos/word-addin/assets/icon-64.png rename to demos/editor/integrations/word-addin/assets/icon-64.png diff --git a/demos/word-addin/assets/icon-64x64.png b/demos/editor/integrations/word-addin/assets/icon-64x64.png similarity index 100% rename from demos/word-addin/assets/icon-64x64.png rename to demos/editor/integrations/word-addin/assets/icon-64x64.png diff --git a/demos/word-addin/assets/icon-80.png b/demos/editor/integrations/word-addin/assets/icon-80.png similarity index 100% rename from demos/word-addin/assets/icon-80.png rename to demos/editor/integrations/word-addin/assets/icon-80.png diff --git a/demos/word-addin/assets/icon-80x80.png b/demos/editor/integrations/word-addin/assets/icon-80x80.png similarity index 100% rename from demos/word-addin/assets/icon-80x80.png rename to demos/editor/integrations/word-addin/assets/icon-80x80.png diff --git a/demos/word-addin/assets/logo-filled.png b/demos/editor/integrations/word-addin/assets/logo-filled.png similarity index 100% rename from demos/word-addin/assets/logo-filled.png rename to demos/editor/integrations/word-addin/assets/logo-filled.png diff --git a/demos/word-addin/assets/logo.png b/demos/editor/integrations/word-addin/assets/logo.png similarity index 100% rename from demos/word-addin/assets/logo.png rename to demos/editor/integrations/word-addin/assets/logo.png diff --git a/demos/word-addin/assets/sample-document.docx b/demos/editor/integrations/word-addin/assets/sample-document.docx similarity index 100% rename from demos/word-addin/assets/sample-document.docx rename to demos/editor/integrations/word-addin/assets/sample-document.docx diff --git a/demos/word-addin/babel.config.json b/demos/editor/integrations/word-addin/babel.config.json similarity index 100% rename from demos/word-addin/babel.config.json rename to demos/editor/integrations/word-addin/babel.config.json diff --git a/demos/word-addin/demo-config.json b/demos/editor/integrations/word-addin/demo-config.json similarity index 100% rename from demos/word-addin/demo-config.json rename to demos/editor/integrations/word-addin/demo-config.json diff --git a/demos/word-addin/demo-thumbnail.png b/demos/editor/integrations/word-addin/demo-thumbnail.png similarity index 100% rename from demos/word-addin/demo-thumbnail.png rename to demos/editor/integrations/word-addin/demo-thumbnail.png diff --git a/demos/word-addin/demo-video.mp4 b/demos/editor/integrations/word-addin/demo-video.mp4 similarity index 100% rename from demos/word-addin/demo-video.mp4 rename to demos/editor/integrations/word-addin/demo-video.mp4 diff --git a/demos/word-addin/manifest.xml b/demos/editor/integrations/word-addin/manifest.xml similarity index 100% rename from demos/word-addin/manifest.xml rename to demos/editor/integrations/word-addin/manifest.xml diff --git a/demos/word-addin/package.json b/demos/editor/integrations/word-addin/package.json similarity index 100% rename from demos/word-addin/package.json rename to demos/editor/integrations/word-addin/package.json diff --git a/demos/word-addin/server/.env.example b/demos/editor/integrations/word-addin/server/.env.example similarity index 100% rename from demos/word-addin/server/.env.example rename to demos/editor/integrations/word-addin/server/.env.example diff --git a/demos/word-addin/server/package.json b/demos/editor/integrations/word-addin/server/package.json similarity index 100% rename from demos/word-addin/server/package.json rename to demos/editor/integrations/word-addin/server/package.json diff --git a/demos/word-addin/server/public/editor.css b/demos/editor/integrations/word-addin/server/public/editor.css similarity index 100% rename from demos/word-addin/server/public/editor.css rename to demos/editor/integrations/word-addin/server/public/editor.css diff --git a/demos/word-addin/server/public/editor.html b/demos/editor/integrations/word-addin/server/public/editor.html similarity index 100% rename from demos/word-addin/server/public/editor.html rename to demos/editor/integrations/word-addin/server/public/editor.html diff --git a/demos/word-addin/server/public/editor.js b/demos/editor/integrations/word-addin/server/public/editor.js similarity index 100% rename from demos/word-addin/server/public/editor.js rename to demos/editor/integrations/word-addin/server/public/editor.js diff --git a/demos/word-addin/server/server.js b/demos/editor/integrations/word-addin/server/server.js similarity index 100% rename from demos/word-addin/server/server.js rename to demos/editor/integrations/word-addin/server/server.js diff --git a/demos/word-addin/src/auth-dialog/auth-dialog.css b/demos/editor/integrations/word-addin/src/auth-dialog/auth-dialog.css similarity index 100% rename from demos/word-addin/src/auth-dialog/auth-dialog.css rename to demos/editor/integrations/word-addin/src/auth-dialog/auth-dialog.css diff --git a/demos/word-addin/src/auth-dialog/auth-dialog.html b/demos/editor/integrations/word-addin/src/auth-dialog/auth-dialog.html similarity index 100% rename from demos/word-addin/src/auth-dialog/auth-dialog.html rename to demos/editor/integrations/word-addin/src/auth-dialog/auth-dialog.html diff --git a/demos/word-addin/src/auth-dialog/auth-dialog.js b/demos/editor/integrations/word-addin/src/auth-dialog/auth-dialog.js similarity index 100% rename from demos/word-addin/src/auth-dialog/auth-dialog.js rename to demos/editor/integrations/word-addin/src/auth-dialog/auth-dialog.js diff --git a/demos/word-addin/src/auth0-config.js.example b/demos/editor/integrations/word-addin/src/auth0-config.js.example similarity index 100% rename from demos/word-addin/src/auth0-config.js.example rename to demos/editor/integrations/word-addin/src/auth0-config.js.example diff --git a/demos/word-addin/src/server-domain.js.example b/demos/editor/integrations/word-addin/src/server-domain.js.example similarity index 100% rename from demos/word-addin/src/server-domain.js.example rename to demos/editor/integrations/word-addin/src/server-domain.js.example diff --git a/demos/word-addin/src/taskpane/taskpane.css b/demos/editor/integrations/word-addin/src/taskpane/taskpane.css similarity index 100% rename from demos/word-addin/src/taskpane/taskpane.css rename to demos/editor/integrations/word-addin/src/taskpane/taskpane.css diff --git a/demos/word-addin/src/taskpane/taskpane.html b/demos/editor/integrations/word-addin/src/taskpane/taskpane.html similarity index 100% rename from demos/word-addin/src/taskpane/taskpane.html rename to demos/editor/integrations/word-addin/src/taskpane/taskpane.html diff --git a/demos/word-addin/src/taskpane/taskpane.js b/demos/editor/integrations/word-addin/src/taskpane/taskpane.js similarity index 100% rename from demos/word-addin/src/taskpane/taskpane.js rename to demos/editor/integrations/word-addin/src/taskpane/taskpane.js diff --git a/demos/word-addin/webpack.config.js b/demos/editor/integrations/word-addin/webpack.config.js similarity index 100% rename from demos/word-addin/webpack.config.js rename to demos/editor/integrations/word-addin/webpack.config.js diff --git a/demos/docx-from-html/.gitignore b/demos/editor/superdoc/docx-from-html/.gitignore similarity index 100% rename from demos/docx-from-html/.gitignore rename to demos/editor/superdoc/docx-from-html/.gitignore diff --git a/demos/editor/superdoc/docx-from-html/README.md b/demos/editor/superdoc/docx-from-html/README.md new file mode 100644 index 0000000000..fa29f456fa --- /dev/null +++ b/demos/editor/superdoc/docx-from-html/README.md @@ -0,0 +1,7 @@ +# SuperDoc: Init a DOCX from HTML Content + +An example of initializing SuperDoc with HTML content. + +This will load a DOCX file (or a blank document), replacing the main contents with the provided HTML. + +In the example we pass `document: sample-document.docx` to load a template with a header and footer. You can omit this key to start with a blank document. diff --git a/demos/docx-from-html/demo-config.json b/demos/editor/superdoc/docx-from-html/demo-config.json similarity index 100% rename from demos/docx-from-html/demo-config.json rename to demos/editor/superdoc/docx-from-html/demo-config.json diff --git a/demos/docx-from-html/demo-thumbnail.png b/demos/editor/superdoc/docx-from-html/demo-thumbnail.png similarity index 100% rename from demos/docx-from-html/demo-thumbnail.png rename to demos/editor/superdoc/docx-from-html/demo-thumbnail.png diff --git a/demos/docx-from-html/demo-video.mp4 b/demos/editor/superdoc/docx-from-html/demo-video.mp4 similarity index 100% rename from demos/docx-from-html/demo-video.mp4 rename to demos/editor/superdoc/docx-from-html/demo-video.mp4 diff --git a/demos/docx-from-html/index.html b/demos/editor/superdoc/docx-from-html/index.html similarity index 100% rename from demos/docx-from-html/index.html rename to demos/editor/superdoc/docx-from-html/index.html diff --git a/demos/docx-from-html/package.json b/demos/editor/superdoc/docx-from-html/package.json similarity index 100% rename from demos/docx-from-html/package.json rename to demos/editor/superdoc/docx-from-html/package.json diff --git a/demos/docx-from-html/public/logo.webp b/demos/editor/superdoc/docx-from-html/public/logo.webp similarity index 100% rename from demos/docx-from-html/public/logo.webp rename to demos/editor/superdoc/docx-from-html/public/logo.webp diff --git a/demos/docx-from-html/public/sample-document.docx b/demos/editor/superdoc/docx-from-html/public/sample-document.docx similarity index 100% rename from demos/docx-from-html/public/sample-document.docx rename to demos/editor/superdoc/docx-from-html/public/sample-document.docx diff --git a/demos/docx-from-html/public/superdoc-logo.png b/demos/editor/superdoc/docx-from-html/public/superdoc-logo.png similarity index 100% rename from demos/docx-from-html/public/superdoc-logo.png rename to demos/editor/superdoc/docx-from-html/public/superdoc-logo.png diff --git a/demos/docx-from-html/src/App.vue b/demos/editor/superdoc/docx-from-html/src/App.vue similarity index 100% rename from demos/docx-from-html/src/App.vue rename to demos/editor/superdoc/docx-from-html/src/App.vue diff --git a/demos/docx-from-html/src/main.js b/demos/editor/superdoc/docx-from-html/src/main.js similarity index 100% rename from demos/docx-from-html/src/main.js rename to demos/editor/superdoc/docx-from-html/src/main.js diff --git a/demos/docx-from-html/src/style.css b/demos/editor/superdoc/docx-from-html/src/style.css similarity index 100% rename from demos/docx-from-html/src/style.css rename to demos/editor/superdoc/docx-from-html/src/style.css diff --git a/demos/docx-from-html/vite.config.js b/demos/editor/superdoc/docx-from-html/vite.config.js similarity index 100% rename from demos/docx-from-html/vite.config.js rename to demos/editor/superdoc/docx-from-html/vite.config.js diff --git a/demos/manifest.json b/demos/manifest.json index 980671b769..a825686e7e 100644 --- a/demos/manifest.json +++ b/demos/manifest.json @@ -28,7 +28,7 @@ "description": "Source-grounded citations grounded in the document: insert a mock RAG-generated draft, see anchored citation highlights with hover popovers, and navigate from a sources panel. Wrapped in a full editor workspace with a custom toolbar, activity panel, comments, tracked changes, custom commands, import, and export.", "category": "Editor", "sourceRepo": "superdoc-dev/superdoc", - "sourcePath": "demos/custom-ui", + "sourcePath": "demos/editor/custom-ui", "docs": "https://docs.superdoc.dev/editor/custom-ui/overview", "thumbnail": null, "liveUrl": null, @@ -82,7 +82,7 @@ "description": "Open downloaded documents in a SuperDoc-powered Chrome extension.", "category": "Integrations", "sourceRepo": "superdoc-dev/superdoc", - "sourcePath": "demos/chrome-extension", + "sourcePath": "demos/editor/integrations/chrome-extension", "docs": null, "thumbnail": "demos/chrome-extension/demo-thumbnail.png", "liveUrl": null, @@ -100,7 +100,7 @@ "description": "Synchronize documents between Microsoft Word and a SuperDoc web editor.", "category": "Integrations", "sourceRepo": "superdoc-dev/superdoc", - "sourcePath": "demos/word-addin", + "sourcePath": "demos/editor/integrations/word-addin", "docs": null, "thumbnail": "demos/word-addin/demo-thumbnail.png", "liveUrl": null, @@ -208,7 +208,7 @@ "description": "Initialize a document from HTML content.", "category": "Document Engine", "sourceRepo": "superdoc-dev/superdoc", - "sourcePath": "demos/docx-from-html", + "sourcePath": "demos/editor/superdoc/docx-from-html", "docs": "https://docs.superdoc.dev/editor/superdoc/import-export", "thumbnail": "demos/docx-from-html/demo-thumbnail.png", "liveUrl": null, diff --git a/demos/word-addin/README.md b/demos/word-addin/README.md index 0208008a31..c54abcacd9 100644 --- a/demos/word-addin/README.md +++ b/demos/word-addin/README.md @@ -1,165 +1,5 @@ -# SuperDoc MS Add-in Sync +# Moved to demos/editor/integrations/word-addin -Real-time document synchronization between Microsoft Word Add-in and web editor using WebSocket communication. +The Word add-in demo moved to [`demos/editor/integrations/word-addin`](../editor/integrations/word-addin). -## Architecture - -The system consists of three main components: -- **MS Word Add-in** (`src/taskpane/taskpane.js`) - Runs inside Microsoft Word -- **Web Editor** (`server/public/editor.js`) - Browser-based document editor -- **Node.js Server** (`server/server.js`) - WebSocket server handling real-time sync - -## WebSocket Events - -The WebSocket communication uses the following event types: - -### `client_ready` -**Sent by:** Web Editor -**Handled by:** Server (broadcasts to other clients) -**Purpose:** Signals that a browser client has loaded and is ready to receive authentication - -```javascript -// Sent by editor.js -websocket.send(JSON.stringify({ - type: 'client_ready' -})); - -// Broadcasted by server.js to other clients -{ - type: 'client_ready', - timestamp: '2024-01-01T00:00:00.000Z' -} -``` - -### `token_transfer` -**Sent by:** MS Word Add-in -**Handled by:** Server (validates and broadcasts to other clients), Web Editor -**Purpose:** Transfers authentication token from Word add-in to web editor - -```javascript -// Sent by taskpane.js -websocket.send(JSON.stringify({ - type: 'token_transfer', - token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik...' -})); - -// Broadcasted by server.js after validation -{ - type: 'token_transfer', - token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik...', - user: { - email: 'user@example.com', - name: 'User Name', - picture: 'https://example.com/avatar.jpg' - }, - timestamp: '2024-01-01T00:00:00.000Z' -} - -// Error response -{ - type: 'token_transfer', - error: 'Invalid token', - timestamp: '2024-01-01T00:00:00.000Z' -} -``` - -### `document_update` -**Sent by:** MS Word Add-in, Web Editor -**Handled by:** Server (validates and broadcasts to other clients), MS Word Add-in, Web Editor -**Purpose:** Real-time document synchronization between clients - -```javascript -// Sent by taskpane.js or editor.js -websocket.send(JSON.stringify({ - type: 'document_update', - token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik...', - document: 'UEsDBBQABgAIAAAAIQDb4fbL7...' // Base64 encoded .docx -})); - -// Broadcasted by server.js after validation -{ - type: 'document_update', - document: 'UEsDBBQABgAIAAAAIQDb4fbL7...', - author: 'user@example.com', - timestamp: '2024-01-01T00:00:00.000Z' -} -``` - -### `close` -**Sent by:** Server -**Handled by:** MS Word Add-in, Web Editor -**Purpose:** Notifies clients when another connection closes - -```javascript -// Broadcasted by server.js -{ - type: 'close', - timestamp: '2024-01-01T00:00:00.000Z' -} -``` - -### `error` -**Sent by:** Server -**Handled by:** MS Word Add-in, Web Editor -**Purpose:** Notifies clients when a connection error occurs - -```javascript -// Broadcasted by server.js -{ - type: 'error', - timestamp: '2024-01-01T00:00:00.000Z' -} -``` - -## Authentication Flow - -1. Web editor loads and sends `client_ready` to server -2. Server broadcasts `client_ready` to Word add-in -3. Word add-in sends `token_transfer` with user's authentication token -4. Server validates token against Auth0 and broadcasts `token_transfer` with user info to web editor -5. Both clients are now authenticated and can send `document_update` events - -## Real-time Synchronization - -- Document changes in Word trigger `document_update` events via selection change detection -- Document changes in web editor trigger `document_update` events via SuperDoc's `onEditorUpdate` callback -- All `document_update` events include the full document as Base64-encoded .docx -- Server validates authentication token before broadcasting updates -- Clients update their document content when receiving `document_update` events - -## Setup - -1. Install dependencies: - ```bash - npm install - cd server && npm install - ``` - -2. Configure environment variables: - - **Auth0 Configuration** - Set up `src/auth0-config.js`: - - Get your Auth0 domain, client ID, and audience from your [Auth0 Dashboard](https://manage.auth0.com/) - - Create a new application or use an existing Single Page Application - - Configure the redirect URLs to include your add-in's domain - - **Server Configuration** - Set up `server/.env`: - - `AUTH0_DOMAIN`: Your Auth0 domain (e.g., `yourapp.us.auth0.com`) - - `AUTH0_AUDIENCE`: Your Auth0 API identifier - - Any additional environment variables required by your cloud function - - These environment variables are required for: - - **Auth0**: Authenticating users and validating JWT tokens - - **Cloud Function**: Server-side token validation and WebSocket communication - - You can reference the example files in the same directories. - -3. Start the server: - ```bash - npm run server - ``` - -4. Build and run the add-in: - ```bash - npm run build - npm start - ``` \ No newline at end of file +It now sits under `demos/editor/integrations/` to reflect that it integrates SuperDoc into Word as a host surface, rather than being its own product workflow. diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index aab3cadaac..c9b9055ca0 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -15,6 +15,8 @@ packages: - examples/*/*/*/*/* - demos/* - demos/*/* + - demos/*/*/* + - demos/*/*/*/* - evals catalog: From 0e8459d2eed474629d5560a1f9926a18daef7601 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Tue, 19 May 2026 15:32:15 -0300 Subject: [PATCH 2/3] chore(deps): regenerate pnpm-lock after demos/editor/ moves (SD-3217) CI failed on PR #3397 build job with ERR_PNPM_OUTDATED_LOCKFILE. git mv changed the workspace paths under demos/editor/* but the lockfile still referenced the old paths, so pnpm install --frozen-lockfile failed in CI. Regenerated with 'pnpm install' locally and verified 'pnpm install --frozen-lockfile' now passes. No dependency changes; the lockfile delta is path-keyed updates for the four moved workspaces (custom-ui, chrome-extension, word-addin, docx-from-html). --- pnpm-lock.yaml | 438 ++++++++++++++++++++++++------------------------- 1 file changed, 219 insertions(+), 219 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fdd6affd43..e2d43c8c17 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -638,31 +638,6 @@ importers: specifier: ^1.50.0 version: 1.58.2 - demos/chrome-extension/chrome-extension: - dependencies: - fast-xml-parser: - specifier: ^5.2.5 - version: 5.5.9 - jszip: - specifier: ^3.10.1 - version: 3.10.1 - prosemirror-model: - specifier: ^1.25.1 - version: 1.25.4 - prosemirror-state: - specifier: ^1.4.3 - version: 1.4.4 - prosemirror-transform: - specifier: ^1.10.4 - version: 1.11.0 - devDependencies: - webpack: - specifier: ^5.99.9 - version: 5.105.4(esbuild@0.27.7)(webpack-cli@6.0.1) - webpack-cli: - specifier: ^6.0.1 - version: 6.0.1(webpack@5.105.4) - demos/collaborative-agent/client: dependencies: '@radix-ui/react-collapsible': @@ -798,53 +773,6 @@ importers: specifier: npm:rolldown-vite@7.3.1 version: rolldown-vite@7.3.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - demos/custom-ui: - dependencies: - '@superdoc-dev/react': - specifier: workspace:* - version: link:../../packages/react - react: - specifier: 'catalog:' - version: 19.2.4 - react-dom: - specifier: 'catalog:' - version: 19.2.4(react@19.2.4) - superdoc: - specifier: workspace:* - version: link:../../packages/superdoc - devDependencies: - '@types/react': - specifier: 'catalog:' - version: 19.2.14 - '@types/react-dom': - specifier: 'catalog:' - version: 19.2.3(@types/react@19.2.14) - '@vitejs/plugin-react': - specifier: 'catalog:' - version: 5.2.0(rolldown-vite@7.3.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - typescript: - specifier: 'catalog:' - version: 5.9.3 - vite: - specifier: npm:rolldown-vite@7.3.1 - version: rolldown-vite@7.3.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - - demos/docx-from-html: - dependencies: - superdoc: - specifier: workspace:* - version: link:../../packages/superdoc - vue: - specifier: 3.5.32 - version: 3.5.32(typescript@5.9.3) - devDependencies: - '@vitejs/plugin-vue': - specifier: ^5.2.2 - version: 5.2.4(rolldown-vite@7.3.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)) - vite: - specifier: npm:rolldown-vite@7.3.1 - version: rolldown-vite@7.3.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - demos/docxtemplater: dependencies: '@fortawesome/fontawesome-svg-core': @@ -897,6 +825,221 @@ importers: specifier: ^7.7.1 version: 7.7.9(rolldown-vite@7.3.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(rollup@4.60.2)(vue@3.5.32(typescript@5.9.3)) + demos/editor/custom-ui: + dependencies: + '@superdoc-dev/react': + specifier: workspace:* + version: link:../../../packages/react + react: + specifier: 'catalog:' + version: 19.2.4 + react-dom: + specifier: 'catalog:' + version: 19.2.4(react@19.2.4) + superdoc: + specifier: workspace:* + version: link:../../../packages/superdoc + devDependencies: + '@types/react': + specifier: 'catalog:' + version: 19.2.14 + '@types/react-dom': + specifier: 'catalog:' + version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: 'catalog:' + version: 5.2.0(rolldown-vite@7.3.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + typescript: + specifier: 'catalog:' + version: 5.9.3 + vite: + specifier: npm:rolldown-vite@7.3.1 + version: rolldown-vite@7.3.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + + demos/editor/integrations/chrome-extension/chrome-extension: + dependencies: + fast-xml-parser: + specifier: ^5.2.5 + version: 5.5.9 + jszip: + specifier: ^3.10.1 + version: 3.10.1 + prosemirror-model: + specifier: ^1.25.1 + version: 1.25.4 + prosemirror-state: + specifier: ^1.4.3 + version: 1.4.4 + prosemirror-transform: + specifier: ^1.10.4 + version: 1.11.0 + devDependencies: + webpack: + specifier: ^5.99.9 + version: 5.105.4(esbuild@0.27.7)(webpack-cli@6.0.1) + webpack-cli: + specifier: ^6.0.1 + version: 6.0.1(webpack@5.105.4) + + demos/editor/integrations/word-addin: + dependencies: + '@google-cloud/storage': + specifier: ^7.13.0 + version: 7.19.0 + auth0-js: + specifier: ^9.28.0 + version: 9.32.0 + core-js: + specifier: ^3.36.0 + version: 3.49.0 + cors: + specifier: ^2.8.5 + version: 2.8.6 + dotenv: + specifier: ^16.4.5 + version: 16.6.1 + express: + specifier: ^5.1.0 + version: 5.2.1 + express-oauth2-jwt-bearer: + specifier: ^1.6.0 + version: 1.7.4 + jsdom: + specifier: 27.3.0 + version: 27.3.0(canvas@3.2.3) + regenerator-runtime: + specifier: ^0.14.1 + version: 0.14.1 + ws: + specifier: ^8.18.0 + version: 8.20.0 + devDependencies: + '@babel/core': + specifier: ^7.24.0 + version: 7.29.0 + '@babel/preset-env': + specifier: ^7.25.4 + version: 7.29.2(@babel/core@7.29.0) + '@types/office-js': + specifier: ^1.0.377 + version: 1.0.585 + '@types/office-runtime': + specifier: ^1.0.35 + version: 1.0.36 + acorn: + specifier: ^8.11.3 + version: 8.16.0 + babel-loader: + specifier: ^9.1.3 + version: 9.2.1(@babel/core@7.29.0)(webpack@5.105.4) + copy-webpack-plugin: + specifier: ^12.0.2 + version: 12.0.2(webpack@5.105.4) + eslint-plugin-office-addins: + specifier: ^3.0.2 + version: 3.0.3(@eslint/eslintrc@3.3.5)(@types/eslint@9.6.1) + file-loader: + specifier: ^6.2.0 + version: 6.2.0(webpack@5.105.4) + html-loader: + specifier: ^5.0.0 + version: 5.1.0(webpack@5.105.4) + html-webpack-plugin: + specifier: ^5.6.3 + version: 5.6.6(webpack@5.105.4) + office-addin-cli: + specifier: ^1.6.5 + version: 1.6.5 + office-addin-debugging: + specifier: ^5.1.6 + version: 5.1.6(@microsoft/teamsapp-cli@3.0.2) + office-addin-dev-certs: + specifier: ^1.13.5 + version: 1.13.5 + office-addin-lint: + specifier: ^2.3.5 + version: 2.3.5(@eslint/eslintrc@3.3.5)(@types/eslint@9.6.1)(typescript@5.9.3) + office-addin-manifest: + specifier: ^1.13.6 + version: 1.13.6 + office-addin-prettier-config: + specifier: ^1.2.1 + version: 1.2.1 + os-browserify: + specifier: ^0.3.0 + version: 0.3.0 + process: + specifier: ^0.11.10 + version: 0.11.10 + source-map-loader: + specifier: ^5.0.0 + version: 5.0.0(webpack@5.105.4) + webpack: + specifier: ^5.95.0 + version: 5.105.4(esbuild@0.27.7)(webpack-cli@5.1.4) + webpack-cli: + specifier: ^5.1.4 + version: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.105.4) + webpack-dev-server: + specifier: 5.1.0 + version: 5.1.0(tslib@2.8.1)(webpack-cli@5.1.4)(webpack@5.105.4) + + demos/editor/integrations/word-addin/server: + dependencies: + '@google-cloud/storage': + specifier: ^7.16.0 + version: 7.19.0 + busboy: + specifier: 0.3.0 + version: 0.3.0 + cors: + specifier: ^2.8.5 + version: 2.8.6 + dotenv: + specifier: ^16.4.5 + version: 16.6.1 + express: + specifier: ^4.21.2 + version: 4.22.1 + express-oauth2-jwt-bearer: + specifier: ^1.6.0 + version: 1.7.4 + jsdom: + specifier: 27.3.0 + version: 27.3.0(canvas@3.2.3) + multer: + specifier: ^2.0.0 + version: 2.1.1 + prosemirror-state: + specifier: ^1.4.3 + version: 1.4.4 + superdoc: + specifier: workspace:* + version: link:../../../../../packages/superdoc + ws: + specifier: ^8.18.0 + version: 8.20.0 + devDependencies: + nodemon: + specifier: ^3.1.9 + version: 3.1.14 + + demos/editor/superdoc/docx-from-html: + dependencies: + superdoc: + specifier: workspace:* + version: link:../../../../packages/superdoc + vue: + specifier: 3.5.32 + version: 3.5.32(typescript@5.9.3) + devDependencies: + '@vitejs/plugin-vue': + specifier: ^5.2.2 + version: 5.2.4(rolldown-vite@7.3.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)) + vite: + specifier: npm:rolldown-vite@7.3.1 + version: rolldown-vite@7.3.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + demos/fields: dependencies: superdoc: @@ -1255,149 +1398,6 @@ importers: specifier: npm:rolldown-vite@7.3.1 version: rolldown-vite@7.3.1(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.6.0)(esbuild@0.27.7)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - demos/word-addin: - dependencies: - '@google-cloud/storage': - specifier: ^7.13.0 - version: 7.19.0 - auth0-js: - specifier: ^9.28.0 - version: 9.32.0 - core-js: - specifier: ^3.36.0 - version: 3.49.0 - cors: - specifier: ^2.8.5 - version: 2.8.6 - dotenv: - specifier: ^16.4.5 - version: 16.6.1 - express: - specifier: ^5.1.0 - version: 5.2.1 - express-oauth2-jwt-bearer: - specifier: ^1.6.0 - version: 1.7.4 - jsdom: - specifier: 27.3.0 - version: 27.3.0(canvas@3.2.3) - regenerator-runtime: - specifier: ^0.14.1 - version: 0.14.1 - ws: - specifier: ^8.18.0 - version: 8.20.0 - devDependencies: - '@babel/core': - specifier: ^7.24.0 - version: 7.29.0 - '@babel/preset-env': - specifier: ^7.25.4 - version: 7.29.2(@babel/core@7.29.0) - '@types/office-js': - specifier: ^1.0.377 - version: 1.0.585 - '@types/office-runtime': - specifier: ^1.0.35 - version: 1.0.36 - acorn: - specifier: ^8.11.3 - version: 8.16.0 - babel-loader: - specifier: ^9.1.3 - version: 9.2.1(@babel/core@7.29.0)(webpack@5.105.4) - copy-webpack-plugin: - specifier: ^12.0.2 - version: 12.0.2(webpack@5.105.4) - eslint-plugin-office-addins: - specifier: ^3.0.2 - version: 3.0.3(@eslint/eslintrc@3.3.5)(@types/eslint@9.6.1) - file-loader: - specifier: ^6.2.0 - version: 6.2.0(webpack@5.105.4) - html-loader: - specifier: ^5.0.0 - version: 5.1.0(webpack@5.105.4) - html-webpack-plugin: - specifier: ^5.6.3 - version: 5.6.6(webpack@5.105.4) - office-addin-cli: - specifier: ^1.6.5 - version: 1.6.5 - office-addin-debugging: - specifier: ^5.1.6 - version: 5.1.6(@microsoft/teamsapp-cli@3.0.2) - office-addin-dev-certs: - specifier: ^1.13.5 - version: 1.13.5 - office-addin-lint: - specifier: ^2.3.5 - version: 2.3.5(@eslint/eslintrc@3.3.5)(@types/eslint@9.6.1)(typescript@5.9.3) - office-addin-manifest: - specifier: ^1.13.6 - version: 1.13.6 - office-addin-prettier-config: - specifier: ^1.2.1 - version: 1.2.1 - os-browserify: - specifier: ^0.3.0 - version: 0.3.0 - process: - specifier: ^0.11.10 - version: 0.11.10 - source-map-loader: - specifier: ^5.0.0 - version: 5.0.0(webpack@5.105.4) - webpack: - specifier: ^5.95.0 - version: 5.105.4(esbuild@0.27.7)(webpack-cli@5.1.4) - webpack-cli: - specifier: ^5.1.4 - version: 5.1.4(webpack-dev-server@5.1.0)(webpack@5.105.4) - webpack-dev-server: - specifier: 5.1.0 - version: 5.1.0(tslib@2.8.1)(webpack-cli@5.1.4)(webpack@5.105.4) - - demos/word-addin/server: - dependencies: - '@google-cloud/storage': - specifier: ^7.16.0 - version: 7.19.0 - busboy: - specifier: 0.3.0 - version: 0.3.0 - cors: - specifier: ^2.8.5 - version: 2.8.6 - dotenv: - specifier: ^16.4.5 - version: 16.6.1 - express: - specifier: ^4.21.2 - version: 4.22.1 - express-oauth2-jwt-bearer: - specifier: ^1.6.0 - version: 1.7.4 - jsdom: - specifier: 27.3.0 - version: 27.3.0(canvas@3.2.3) - multer: - specifier: ^2.0.0 - version: 2.1.1 - prosemirror-state: - specifier: ^1.4.3 - version: 1.4.4 - superdoc: - specifier: workspace:* - version: link:../../../packages/superdoc - ws: - specifier: ^8.18.0 - version: 8.20.0 - devDependencies: - nodemon: - specifier: ^3.1.9 - version: 3.1.14 - evals: devDependencies: '@anthropic-ai/claude-agent-sdk': @@ -34085,7 +34085,7 @@ snapshots: graceful-fs: 4.2.11 is-stream: 2.0.1 lazystream: 1.0.1 - lodash: 4.17.23 + lodash: 4.18.1 normalize-path: 3.0.0 readable-stream: 4.7.0 @@ -38584,7 +38584,7 @@ snapshots: dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 - lodash: 4.17.23 + lodash: 4.18.1 pretty-error: 4.0.0 tapable: 2.3.2 optionalDependencies: @@ -38595,7 +38595,7 @@ snapshots: dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 - lodash: 4.17.23 + lodash: 4.18.1 pretty-error: 4.0.0 tapable: 2.3.2 optionalDependencies: @@ -43586,7 +43586,7 @@ snapshots: pretty-error@4.0.0: dependencies: - lodash: 4.17.23 + lodash: 4.18.1 renderkid: 3.0.0 pretty-format@27.5.1: From 2aa4f500cdb021f584e03b16dd642c42a98f9563 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Tue, 19 May 2026 15:45:23 -0300 Subject: [PATCH 3/3] ci(demos): resolve demo paths via manifest sourcePath The playwright config hard-coded demos//, which breaks every time a demo moves. Read demos/manifest.json and look up sourcePath by id so the smoke matrix follows the manifest as source of truth. Also drop archived demos from the smoke matrix (docxtemplater, fields, loading-from-json, text-selection, toolbar) that #3391 removed from the manifest. Replace fields with fields-source, the local rename. --- .github/workflows/ci-demos.yml | 7 +------ demos/__tests__/playwright.config.ts | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci-demos.yml b/.github/workflows/ci-demos.yml index 47da237ef3..22db83c3d8 100644 --- a/.github/workflows/ci-demos.yml +++ b/.github/workflows/ci-demos.yml @@ -59,16 +59,11 @@ jobs: - contract-templates - custom-ui - docx-from-html - - docxtemplater - - fields + - fields-source - grading-papers # - html-editor # broken: imports unpublished superdoc/super-editor/style.css subpath - linked-sections - - loading-from-json - nextjs-ssr - # - replace-content # broken: runtime nextSibling error in SuperDoc - - text-selection - - toolbar steps: - name: Restore workspace uses: actions/cache/restore@v4 diff --git a/demos/__tests__/playwright.config.ts b/demos/__tests__/playwright.config.ts index d3624f4110..66f3757703 100644 --- a/demos/__tests__/playwright.config.ts +++ b/demos/__tests__/playwright.config.ts @@ -1,6 +1,6 @@ import { defineConfig, devices } from '@playwright/test'; -import { existsSync } from 'node:fs'; -import { resolve, dirname } from 'node:path'; +import { existsSync, readFileSync } from 'node:fs'; +import { resolve, dirname, relative } from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -11,8 +11,22 @@ const __dirname = dirname(fileURLToPath(import.meta.url)); // when running this suite locally without an explicit DEMO override. const demo = process.env.DEMO || 'custom-ui'; -// Demos are flat: demos// -const demoPath = `../${demo}`; +// Resolve the demo's working directory via the manifest. Old paths under +// demos// may now be shim READMEs; manifest sourcePath is the source +// of truth post-SD-3217. +const manifestPath = resolve(__dirname, '../manifest.json'); +const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as Array<{ + id: string; + sourcePath?: string | null; + sourceRepo?: string; +}>; +const entry = manifest.find((e) => e.id === demo); +const sourcePath = entry?.sourceRepo === 'superdoc-dev/superdoc' ? entry?.sourcePath : null; +if (!sourcePath) { + throw new Error(`DEMO="${demo}" not found in demos/manifest.json or is not a local demo`); +} +const repoRoot = resolve(__dirname, '../..'); +const demoPath = relative(__dirname, resolve(repoRoot, sourcePath)); // Port mapping for non-Vite demos (these use their framework's default port) const portMap: Record = {