feat(ui): collaborative document editing workspace#1729
Open
ebarroca wants to merge 51 commits into
Open
Conversation
Add CollaborativeMarkdownRenderer with block-level comment/edit controls and source-position-stable anchors (heading path, occurrence index). Wire it into ContentOverview via an Edit with AI panel that starts or resumes a document-scoped agent run, and into ArtifactsTab so Markdown artifacts open inline against the live conversation. Preserve structured UPDATE events in the timeline and expose sendMessage/onMessage hooks on ModernAgentConversation.
…revision - Extract DocumentEditingWorkspace from DocumentEditingPanel so host applications can embed the workspace in their own layout; the panel is now a full-screen modal wrapper. sendMessageRef, onClose, and onDocumentUpdated are optional in the embeddable component. - Resolve the head revision when the workspace opens from a historical revision page: hydration, the review baseline, and Save to document now always target the current head instead of failing with a 400 and showing a false unsaved-changes state. Resolution is atomic — a failed head lookup never falls back to editing a historical revision. - Confirm before starting a new session while the working copy has unsaved changes. - Clear the optimistic first message when startWorkflow declines to start, so the conversation no longer spins in the preparing state forever after a start failure.
…on for editing sessions - Detect both save-conflict shapes (If-Match 412 and head-moved 400), re-point the save target at the new head, and show a conflict banner offering an agent-assisted merge: the agent fetches the new canonical revision into a reference artifact, merges it into the working copy, and reports what was merged for review before saving again. - Add a "Summarize changes" action to the review view: the agent compares the working copy against the session baseline and replies with a changelog suitable for a revision note. - Pass a hard excluded_tools denylist on editing runs: document/store mutation tools and sandbox execution stay unavailable even if a skill would unlock them; the working copy is edited only through edit_artifact and published only via Save to document. fetch_document is enabled read-only for reconcile and summary comparisons. - @vertesia/common: excluded_tools on conversation payloads and agent run creation.
…e header
- Save to document now runs the same persistence pass as create_document:
run-local artifact: links (session-generated charts, files) are copied
into durable documents/{batch}/ storage server-side and the links
rewritten before publishing, so canonical documents never depend on
run-scoped artifact storage. Failed copies pin the link to the explicit
agents/ path instead of leaving it run-relative. The rewritten links are
synced back into the working copy after a successful save (CAS-guarded).
- Editing prompt: forbid inline base64 image data and rewriting durable
artifact:documents/… references; run-local references are allowed and
made durable on publish.
- Consolidate the workspace chrome into a single header bar: status line
with working-copy and baseline chips (full values in tooltips), view
toggle + summarize + refresh, session group (model, run id, new
session), and actions — the second toolbar row is gone, giving the
document the vertical space back.
- Model chip shows the model display name (or the final id segment)
instead of the full provider resource path.
The previous commit unintentionally included the @vertesia/ui references to the in-progress @vertesia/rich-text workspace package (dependency, externals, tsconfig reference) while the package itself is not committed yet, leaving the branch uninstallable from a clean clone. Remove the references until the package is ready; re-apply them from 42ac546 together with the package.
# Conflicts: # pnpm-lock.yaml
Builds on the feat-editing collaborative-editing work but replaces its block-only selection with feat-agent-collab's arbitrary-span model, and makes the properly-styled TipTap editor the primary surface for agent artifacts. - The agent Artifacts tab now opens Markdown artifacts in the rich-text document editor (viewMode="document") — inline editing on a clean prose surface — instead of the read-only block-wrapped renderer. - Add SpanSelectionComment: a toolbar that lets the user highlight ANY text span (not a whole block), attach a note, and send it to the agent. It builds a content-anchored MarkdownBlockAnchor (exact_text + prefix/suffix, the same TextQuoteSelector model, minus the source range) from the ProseMirror selection and feeds the existing editing-action pipeline (handleAction -> onSendMessage), so run reattachment, conflict handling, and the model-facing instruction are all reused unchanged. - Re-export the Tiptap Editor type from @vertesia/rich-text so consumers can type the editor without a direct @tiptap/core dependency. - captureSpanAnchor unit tests (real headless editor): span capture with surrounding context, block-type labelling, empty-selection guard.
The display_artifact agent tool emits a `display_artifact` stream event. The conversation now watches for the latest such event and reveals the artifact in the document editor (selects the path, switches to the Artifacts tab, opens the panel), once per distinct event so the user can still close it.
…oolbar Deletes @vertesia/rich-text's text-symbol MarkdownEditorToolbar (Paragraph | B | I | S | <> | ...) and its wiring, and adds a new icon-based EditorToolbar in the UI layer (lucide icons + @vertesia/ui Button/DropdownMenu): a heading dropdown, bold/italic/strike/inline-code, bullet/ordered list, blockquote/code-block, horizontal-rule/insert-table, and undo/redo. Active/enabled state stays live via a lightweight editor-transaction subscription (no @tiptap/react dependency in ui). - The framework-agnostic rich-text package no longer ships a toolbar; the document editor renders EditorToolbar, the compact component editor is chrome-free. - Reuses the existing richText.* i18n strings for accessible names. - Table row/column editing and list indent/outdent are intentionally dropped from the toolbar (list nesting works via Tab/Shift-Tab; table structure editing is a planned context menu). Tests updated accordingly; the component-editor interaction tests type synchronously to avoid ProseMirror char-drop flake.
- Crisp foreground icons (was muted), shadcn-style hover (bg-muted), neutral active highlight. - Undo/redo moved to the left of the bar, matching the reference layout. - The Table control is now a context dropdown: "Insert table" when outside a table; add/delete row & column and delete-table when the caret is in one — restoring the table structure actions that the first toolbar pass dropped.
…sage Reworks the document comment bar so the user can queue several comments before sending: "Comment on selection" adds each note (with its quoted span) to a pending list instead of firing a message per comment. A "Send to agent" button composes the whole batch into a single conversation message and sends it. Because the batch is sent as a normal message (no editing_action metadata), it renders as an ordinary message bubble in the timeline rather than the heavy "Comment on document" card that clashed with the app. - ArtifactEditingSurface gains an onSendMessage prop; the agent Artifacts tab wires it to the conversation's plain-message sender. - Component-editor interaction tests paste (one op) instead of typing char by char, which raced ProseMirror in jsdom and dropped characters.
…n card - Merge the comment controls into the formatting toolbar so it is one row: the "Comment on selection" button + queued-comment count + "Send to agent" live at the end of the toolbar, and the composer/pending list drop below it. Removes the separate comment bar and the toolbar's gray background. - EditorToolbar gains an optional onSendComment prop; VertesiaMarkdownDocumentEditor forwards it; ArtifactEditingSurface wires it to the conversation sender. Deletes the standalone SpanSelectionComment. - Lighten DocumentEditingActionCard: drop the heavy colored box, icon circle, and collapsible; render a compact muted header + content (+ a subtle quote) so it reads like an ordinary message instead of a mismatched card.
michaelva
approved these changes
Jul 21, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a collaborative, artifact-backed Markdown editing workflow in @vertesia/ui, adds a new Markdown-first TipTap-based rich-text package (@vertesia/rich-text), and wires agent conversation UI to support document-edit actions, artifact editing, and artifact viewing.
Changes:
- Add
@vertesia/rich-textpackage and UI adapters for Vertesia-specific Markdown rendering/editing. - Add artifact-backed collaborative editing surfaces, diff/rebase helpers, and structured “document edit/comment” cards in the agent chat UI.
- Extend client/common types and UI plumbing for artifact content reads/writes, run reattachment, and expanded i18n coverage.
Reviewed changes
Copilot reviewed 86 out of 87 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/ui/vitest.config.ts | Add aliases for @vertesia/ui/rich-text subpath and @vertesia/rich-text in UI tests. |
| packages/ui/tsconfig.json | Add project reference to ../rich-text. |
| packages/ui/src/widgets/markdown/useArtifactContent.ts | Add blob-storage wording + expose artifact response ETag. |
| packages/ui/src/widgets/markdown/textDiff.test.ts | Add tests for word/line diff, unified diff, and rebase behavior. |
| packages/ui/src/widgets/markdown/MarkdownRenderer.tsx | Add onArtifactOpen and preserveSourcePositions options. |
| packages/ui/src/widgets/markdown/MarkdownLink.tsx | Allow host to intercept Markdown artifact clicks via onArtifactOpen. |
| packages/ui/src/widgets/markdown/MarkdownLink.test.tsx | Test artifact-open interception behavior. |
| packages/ui/src/widgets/markdown/index.ts | Export collaborative editing, artifact editing surface, and text diff utilities. |
| packages/ui/src/rich-text/VertesiaRichTextEditor.tsx | Add Vertesia UI adapters around @vertesia/rich-text editors/renderers. |
| packages/ui/src/rich-text/VertesiaRichTextEditor.test.tsx | Add integration tests for Vertesia rich-text adapters + source-preserve flow. |
| packages/ui/src/rich-text/index.ts | Export Vertesia rich-text editor adapters from UI package. |
| packages/ui/src/i18n/locales/zh.json | Add document-editing + rich-text strings (zh). |
| packages/ui/src/i18n/locales/zh-TW.json | Add document-editing + rich-text strings (zh-TW). |
| packages/ui/src/i18n/locales/tr.json | Add document-editing + rich-text strings (tr). |
| packages/ui/src/i18n/locales/ru.json | Add document-editing + rich-text strings (ru). |
| packages/ui/src/i18n/locales/pt.json | Add document-editing + rich-text strings (pt). |
| packages/ui/src/i18n/locales/ko.json | Add document-editing + rich-text strings (ko). |
| packages/ui/src/i18n/locales/ja.json | Add document-editing + rich-text strings (ja). |
| packages/ui/src/i18n/locales/it.json | Add document-editing + rich-text strings (it). |
| packages/ui/src/i18n/locales/fr.json | Add document-editing + rich-text strings (fr). |
| packages/ui/src/i18n/locales/es.json | Add document-editing + rich-text strings (es). |
| packages/ui/src/i18n/locales/de.json | Add document-editing + rich-text strings (de). |
| packages/ui/src/i18n/locales/ar.json | Add document-editing + rich-text strings (ar). |
| packages/ui/src/features/store/objects/components/useContentPanelHooks.ts | Add change-highlighting + safer async loading/cropping behavior for object text. |
| packages/ui/src/features/store/objects/components/TextEditorPanel.tsx | Use rich-text document editor for Markdown objects. |
| packages/ui/src/features/store/objects/components/TextEditorPanel.test.tsx | Test Markdown objects render rich-text editor in the panel. |
| packages/ui/src/features/store/objects/components/index.ts | Export DocumentEditingPanel from components barrel. |
| packages/ui/src/features/store/objects/components/documentEditingTarget.ts | Resolve “edit target” to current head revision + load ETag/content. |
| packages/ui/src/features/store/objects/components/documentEditingTarget.test.ts | Tests for head-resolution and failure modes. |
| packages/ui/src/features/store/objects/components/documentEditingRun.ts | Identify/reattach editing runs via tags/properties + scope open tracking. |
| packages/ui/src/features/store/objects/components/documentEditingRun.test.ts | Tests for identity matching and lookup fallback behavior. |
| packages/ui/src/features/store/objects/components/DocumentEditingLockBanner.tsx | Add persistent lock/unlock banner for agent-editing periods. |
| packages/ui/src/features/store/objects/components/DocumentEditingLockBanner.test.tsx | Tests for lock banner messaging and toggle callback. |
| packages/ui/src/features/store/objects/components/DocumentEditingConfigurationSelector.tsx | Add environment/model selector for document-editing sessions. |
| packages/ui/src/features/store/objects/components/DocumentEditingConfigurationSelector.test.ts | Tests for project default selection logic. |
| packages/ui/src/features/store/objects/components/documentEditingAgentConfig.ts | Define tools/skills allowlist + excluded tools for document-editing agent sessions. |
| packages/ui/src/features/store/objects/components/documentEditingAgentConfig.test.ts | Tests ensuring shell stays allowed and canonical writes remain excluded. |
| packages/ui/src/features/store/objects/components/documentArtifactRefs.ts | Persist run-local artifact references to durable documents/ storage on publish. |
| packages/ui/src/features/store/objects/components/documentArtifactRefs.test.ts | Tests for reference collection, rewrite, and failure pinning. |
| packages/ui/src/features/agent/chat/ModernAgentOutput/utils.ts | Ignore passive artifact autosave updates when computing “in progress”. |
| packages/ui/src/features/agent/chat/ModernAgentOutput/utils.test.ts | Test “passive artifact update does not resurrect Working” behavior. |
| packages/ui/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx | Thread artifact-open callback + render document edit/comment cards. |
| packages/ui/src/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.tsx | Add shared prose rendering with “show more/less” collapsing. |
| packages/ui/src/features/agent/chat/ModernAgentOutput/CollapsibleAgentMarkdown.test.tsx | Tests for collapse toggle + table rendering. |
| packages/ui/src/features/agent/chat/ModernAgentOutput/AttachmentPreview.tsx | Add onOpenArtifact path for Markdown artifact attachments. |
| packages/ui/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx | Wire artifact-open and structured edit action cards into summary/stacked views. |
| packages/ui/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx | Test summary rendering of structured editing cards. |
| packages/ui/src/features/agent/chat/ModernAgentConversation.test.tsx | Add tests for right-panel artifact opening + collaboration availability. |
| packages/ui/src/features/agent/chat/index.ts | Export SendAgentMessageFn type. |
| packages/ui/src/features/agent/chat/hooks/useAgentStream.ts | Add onMessage callback + retain structured UPDATE events and guard replayed history. |
| packages/ui/src/features/agent/chat/hooks/useAgentStream.test.tsx | Tests for structured updates retention + callback forwarding + replay suppression. |
| packages/ui/src/features/agent/chat/DocumentEditingActionCard.tsx | Render structured edit/comment actions as compact cards (with inline diffs). |
| packages/ui/src/features/agent/chat/DocumentEditingActionCard.test.tsx | Tests for parsing/validation and rendering for comment/edit/applied cases. |
| packages/ui/src/features/agent/chat/ArtifactsTab.tsx | Add in-panel Markdown artifact editor (block/doc modes), autosave status, and host open/download paths. |
| packages/ui/src/features/agent/chat/AgentRightPanel.tsx | Add selected-artifact plumbing + send-message callback for artifacts editor. |
| packages/ui/src/core/components/shadcn/modal/dialog.tsx | Add size="full" modal option. |
| packages/ui/src/tests/setup.ts | Add jsdom shims for canvas + caret geometry used by chart/mermaid/ProseMirror. |
| packages/ui/src/tests/axe-helper.ts | Add shared axe run options + per-test exclude support. |
| packages/ui/src/tests/a11y.test.tsx | Exclude Radix focus guards from axe run to avoid false positives. |
| packages/ui/package.json | Add dependency + export subpath for @vertesia/ui/rich-text. |
| packages/ui/externals.js | Mark @vertesia/rich-text as external. |
| packages/rich-text/tsconfig.test.json | Add test tsconfig for @vertesia/rich-text. |
| packages/rich-text/tsconfig.json | Add library tsconfig for @vertesia/rich-text. |
| packages/rich-text/src/types.ts | Define rich-text renderer prop contracts and opaque markdown kinds. |
| packages/rich-text/src/MarkdownRichTextEditor.tsx | Implement Markdown-first TipTap editor with stable renderer proxies + external sync policies. |
| packages/rich-text/src/MarkdownEditors.tsx | Provide component/document editor shells with default debounce policies. |
| packages/rich-text/src/markdown.test.ts | Add parse/serialize conformance tests and editing command round-trips. |
| packages/rich-text/src/index.ts | Export rich-text editor APIs, helpers, and types. |
| packages/rich-text/rolldown.config.js | Add ESM bundling config for rich-text runtime. |
| packages/rich-text/package.json | Add @vertesia/rich-text package definition, deps, build/test scripts. |
| packages/common/src/store/conversation-state.ts | Add initialization_call_ids to conversation state. |
| packages/common/src/store/agent-run.ts | Add initial skills/tool calls/excluded tools + artifact content update types. |
| packages/common/src/interaction.ts | Add InitialToolCall and conversation execution fields for initial skills/tools/exclusions. |
| packages/client/src/store/AgentsApi.ts | Add artifact content read + conditional update endpoints. |
| package.json | Pin vite version to 8.1.0. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Add the 8 keys introduced for the artifact editor (block/edit mode toggle and comment controls) to every non-English locale so they no longer fall back to the English source. Addresses PR #1729 review feedback.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
contextual insertion, exact-change refresh, changed-block highlighting, manual reload, and generation-based
conflict recovery.
project-default environment/model selection, review mode, explicit publish, save/version selection, new-version
navigation, and agent-assisted conflict reconciliation.
@vertesia/rich-text, a Markdown-first TipTap package with component and full-document editors, contextualtable/list actions, opaque preservation for unsupported Vertesia Markdown, injected renderers, stable external
value synchronization, and parse/serialize conformance tests.
agent using a bounded unified diff.
Save to document action.
visual banner; an explicit manual unlock/relock remains available as a recovery control.
editing-session bookkeeping. Add all new UI strings to every locale.
Verification
pnpm installpnpm --prefix packages/rich-text lintpnpm --prefix packages/rich-text typecheck:testpnpm --prefix packages/rich-text test— 12 testspnpm --prefix packages/ui lint— 527 files plus strict RTL validationpnpm --prefix packages/ui typecheck:testpnpm --prefix packages/ui test— 54 files, 544 testspnpm build— 23/23 tasksNotes
source and offers exact Markdown-source editing instead. YAML frontmatter is preserved as an opaque header.
streamed updates arrive.