openclaw/apps/web/app/components/workspace/document-view.tsx

216 lines
6.8 KiB
TypeScript
Raw Normal View History

2026-02-11 16:45:07 -08:00
"use client";
Dench workspace: unified @ mention search, entry detail modal, and workspace link system New libraries: - workspace-links.ts: builders, parsers, and type guards for workspace URLs (/workspace?path=... for files/objects, /workspace?entry=objName:id for entries). Replaces ad-hoc relative-path links with real navigable URLs. - search-index.ts: useSearchIndex hook that fetches a unified search index from the API and provides Fuse.js-powered fuzzy search across files, objects, and database entries. Exposes a stable ref-based search function safe for capture in tiptap extensions. New API routes: - GET /api/workspace/search-index: builds a flat search index from the filesystem tree (knowledge/, reports/, top-level files) and all DuckDB object entries with display-field labels and preview fields. - GET /api/workspace/objects/[name]/entries/[id]: fetches a single entry with all field values, resolved relation labels, reverse relations (incoming links from other objects), and the effective display field. New component: - EntryDetailModal: slide-over modal for viewing an individual entry's fields, enum badges, user avatars, clickable relation chips (navigate to related entries), reverse relation sections, and timestamps. Supports Escape to close and backdrop click dismiss. Slash command refactor (slash-command.tsx): - New createWorkspaceMention(searchFn) replaces the old file-only @ mention with unified search across files, objects, and entries. - searchItemToSlashItem() converts search index items into tiptap suggestion items with proper icons, badges (object name pill for entries), and link insertion commands using real workspace URLs. - Legacy createFileMention(tree) now delegates to createWorkspaceMention with a simple substring-match fallback for when no search index is available. Editor integration (markdown-editor.tsx, document-view.tsx): - MarkdownEditor accepts optional searchFn prop; when provided, uses createWorkspaceMention instead of the legacy createFileMention. - Link click interception now uses the shared isWorkspaceLink() helper and registers handlers in capture phase for reliable interception. - DocumentView forwards searchFn to editor and adds a delegated click handler in read mode to intercept workspace links and navigate via onNavigate. Object table (object-table.tsx): - Added onEntryClick prop; table rows are now clickable with cursor-pointer styling, firing the callback with the entry ID. Workspace page (page.tsx): - Integrates useSearchIndex hook and passes search function down to editor. - Entry detail modal state with URL synchronization (?entry=objName:id param). - New resolveNode() with fallback strategies: exact match, knowledge/ prefix toggle, and last-segment object name matching. - Unified handleEditorNavigate() dispatches /workspace?entry=... to the modal and /workspace?path=... to file/object navigation. - URL bar syncs with activePath via router.replace (no full page reloads). - Top-level container click safety net catches any workspace link clicks that bubble up unhandled. Styles (globals.css): - Added .slash-cmd-item-badge for object-name pills in the @ mention popup.
2026-02-11 21:41:23 -08:00
import { useState, useCallback, type MouseEvent as ReactMouseEvent } from "react";
2026-02-11 16:45:07 -08:00
import dynamic from "next/dynamic";
2026-02-11 18:35:35 -08:00
import { splitReportBlocks, hasReportBlocks } from "@/lib/report-blocks";
Web app: add syntax highlighting, diff viewer, rich chat editor, and file search Syntax highlighting & code viewer: - Add shiki for syntax-highlighted fenced code blocks in chat messages - New SyntaxBlock component (lazy shiki, dual light/dark theme) - New CodeViewer for workspace file panel (routes code files via isCodeFile()) - API routes (browse-file, virtual-file) now return "code" type for known extensions Diff rendering: - New DiffCard component for rendering unified diffs with add/remove colors - diff-blocks.ts parser to extract fenced blocks from markdown - Chain-of-thought tool steps show inline diffs for edit/write tools (synthetic from old_string/new_string or direct from tool output) - Agent runner passes through diff/firstChangedLine from edit tool results - Document view handles diff blocks alongside report blocks Rich chat editor (Tiptap): - Replace plain textarea with Tiptap-based ChatEditor - File mention extension (@-mention files with autocomplete dropdown) - File mention list with keyboard navigation and search via suggest-files API - New suggest-files API endpoint for fuzzy file search File search & navigation: - FileSearch component in workspace sidebar (debounced search, keyboard nav) - Search results navigate sidebar to file location and open in panel - File picker modal for browsing/selecting workspace files Drag & drop: - File tree nodes support native HTML5 drag (application/x-file-mention) for cross-component drops (e.g. dragging files into chat editor) Chat attachments reworked: - Switch from browser File objects to path-based references (name + path) - Simplified attachment strip (no media previews, shows shortened paths) Also adds software-engineering skill and related CSS for code blocks/shiki.
2026-02-13 18:06:59 -08:00
import { splitDiffBlocks, hasDiffBlocks } from "@/lib/diff-blocks";
Dench workspace: unified @ mention search, entry detail modal, and workspace link system New libraries: - workspace-links.ts: builders, parsers, and type guards for workspace URLs (/workspace?path=... for files/objects, /workspace?entry=objName:id for entries). Replaces ad-hoc relative-path links with real navigable URLs. - search-index.ts: useSearchIndex hook that fetches a unified search index from the API and provides Fuse.js-powered fuzzy search across files, objects, and database entries. Exposes a stable ref-based search function safe for capture in tiptap extensions. New API routes: - GET /api/workspace/search-index: builds a flat search index from the filesystem tree (knowledge/, reports/, top-level files) and all DuckDB object entries with display-field labels and preview fields. - GET /api/workspace/objects/[name]/entries/[id]: fetches a single entry with all field values, resolved relation labels, reverse relations (incoming links from other objects), and the effective display field. New component: - EntryDetailModal: slide-over modal for viewing an individual entry's fields, enum badges, user avatars, clickable relation chips (navigate to related entries), reverse relation sections, and timestamps. Supports Escape to close and backdrop click dismiss. Slash command refactor (slash-command.tsx): - New createWorkspaceMention(searchFn) replaces the old file-only @ mention with unified search across files, objects, and entries. - searchItemToSlashItem() converts search index items into tiptap suggestion items with proper icons, badges (object name pill for entries), and link insertion commands using real workspace URLs. - Legacy createFileMention(tree) now delegates to createWorkspaceMention with a simple substring-match fallback for when no search index is available. Editor integration (markdown-editor.tsx, document-view.tsx): - MarkdownEditor accepts optional searchFn prop; when provided, uses createWorkspaceMention instead of the legacy createFileMention. - Link click interception now uses the shared isWorkspaceLink() helper and registers handlers in capture phase for reliable interception. - DocumentView forwards searchFn to editor and adds a delegated click handler in read mode to intercept workspace links and navigate via onNavigate. Object table (object-table.tsx): - Added onEntryClick prop; table rows are now clickable with cursor-pointer styling, firing the callback with the entry ID. Workspace page (page.tsx): - Integrates useSearchIndex hook and passes search function down to editor. - Entry detail modal state with URL synchronization (?entry=objName:id param). - New resolveNode() with fallback strategies: exact match, knowledge/ prefix toggle, and last-segment object name matching. - Unified handleEditorNavigate() dispatches /workspace?entry=... to the modal and /workspace?path=... to file/object navigation. - URL bar syncs with activePath via router.replace (no full page reloads). - Top-level container click safety net catches any workspace link clicks that bubble up unhandled. Styles (globals.css): - Added .slash-cmd-item-badge for object-name pills in the @ mention popup.
2026-02-11 21:41:23 -08:00
import { isWorkspaceLink } from "@/lib/workspace-links";
Web app: add syntax highlighting, diff viewer, rich chat editor, and file search Syntax highlighting & code viewer: - Add shiki for syntax-highlighted fenced code blocks in chat messages - New SyntaxBlock component (lazy shiki, dual light/dark theme) - New CodeViewer for workspace file panel (routes code files via isCodeFile()) - API routes (browse-file, virtual-file) now return "code" type for known extensions Diff rendering: - New DiffCard component for rendering unified diffs with add/remove colors - diff-blocks.ts parser to extract fenced blocks from markdown - Chain-of-thought tool steps show inline diffs for edit/write tools (synthetic from old_string/new_string or direct from tool output) - Agent runner passes through diff/firstChangedLine from edit tool results - Document view handles diff blocks alongside report blocks Rich chat editor (Tiptap): - Replace plain textarea with Tiptap-based ChatEditor - File mention extension (@-mention files with autocomplete dropdown) - File mention list with keyboard navigation and search via suggest-files API - New suggest-files API endpoint for fuzzy file search File search & navigation: - FileSearch component in workspace sidebar (debounced search, keyboard nav) - Search results navigate sidebar to file location and open in panel - File picker modal for browsing/selecting workspace files Drag & drop: - File tree nodes support native HTML5 drag (application/x-file-mention) for cross-component drops (e.g. dragging files into chat editor) Chat attachments reworked: - Switch from browser File objects to path-based references (name + path) - Simplified attachment strip (no media previews, shows shortened paths) Also adds software-engineering skill and related CSS for code blocks/shiki.
2026-02-13 18:06:59 -08:00
import { DiffCard } from "../diff-viewer";
Dench workspace: unified @ mention search, entry detail modal, and workspace link system New libraries: - workspace-links.ts: builders, parsers, and type guards for workspace URLs (/workspace?path=... for files/objects, /workspace?entry=objName:id for entries). Replaces ad-hoc relative-path links with real navigable URLs. - search-index.ts: useSearchIndex hook that fetches a unified search index from the API and provides Fuse.js-powered fuzzy search across files, objects, and database entries. Exposes a stable ref-based search function safe for capture in tiptap extensions. New API routes: - GET /api/workspace/search-index: builds a flat search index from the filesystem tree (knowledge/, reports/, top-level files) and all DuckDB object entries with display-field labels and preview fields. - GET /api/workspace/objects/[name]/entries/[id]: fetches a single entry with all field values, resolved relation labels, reverse relations (incoming links from other objects), and the effective display field. New component: - EntryDetailModal: slide-over modal for viewing an individual entry's fields, enum badges, user avatars, clickable relation chips (navigate to related entries), reverse relation sections, and timestamps. Supports Escape to close and backdrop click dismiss. Slash command refactor (slash-command.tsx): - New createWorkspaceMention(searchFn) replaces the old file-only @ mention with unified search across files, objects, and entries. - searchItemToSlashItem() converts search index items into tiptap suggestion items with proper icons, badges (object name pill for entries), and link insertion commands using real workspace URLs. - Legacy createFileMention(tree) now delegates to createWorkspaceMention with a simple substring-match fallback for when no search index is available. Editor integration (markdown-editor.tsx, document-view.tsx): - MarkdownEditor accepts optional searchFn prop; when provided, uses createWorkspaceMention instead of the legacy createFileMention. - Link click interception now uses the shared isWorkspaceLink() helper and registers handlers in capture phase for reliable interception. - DocumentView forwards searchFn to editor and adds a delegated click handler in read mode to intercept workspace links and navigate via onNavigate. Object table (object-table.tsx): - Added onEntryClick prop; table rows are now clickable with cursor-pointer styling, firing the callback with the entry ID. Workspace page (page.tsx): - Integrates useSearchIndex hook and passes search function down to editor. - Entry detail modal state with URL synchronization (?entry=objName:id param). - New resolveNode() with fallback strategies: exact match, knowledge/ prefix toggle, and last-segment object name matching. - Unified handleEditorNavigate() dispatches /workspace?entry=... to the modal and /workspace?path=... to file/object navigation. - URL bar syncs with activePath via router.replace (no full page reloads). - Top-level container click safety net catches any workspace link clicks that bubble up unhandled. Styles (globals.css): - Added .slash-cmd-item-badge for object-name pills in the @ mention popup.
2026-02-11 21:41:23 -08:00
import type { TreeNode, MentionSearchFn } from "./slash-command";
2026-02-11 16:45:07 -08:00
// Load markdown renderer client-only to avoid SSR issues with ESM-only packages
const MarkdownContent = dynamic(
() =>
import("./markdown-content").then((mod) => mod.MarkdownContent),
{
ssr: false,
loading: () => (
<div className="animate-pulse space-y-3 py-4">
<div className="h-4 rounded" style={{ background: "var(--color-surface)", width: "80%" }} />
<div className="h-4 rounded" style={{ background: "var(--color-surface)", width: "60%" }} />
<div className="h-4 rounded" style={{ background: "var(--color-surface)", width: "70%" }} />
</div>
),
},
);
2026-02-11 18:35:35 -08:00
// Lazy-load ReportCard (uses Recharts which is heavy)
const ReportCard = dynamic(
() =>
import("../charts/report-card").then((m) => ({ default: m.ReportCard })),
{
ssr: false,
loading: () => (
<div
className="h-48 rounded-xl animate-pulse my-4"
style={{ background: "var(--color-surface)" }}
/>
),
},
);
Dench workspace: Tiptap markdown editor, subagent sessions, and error surfacing ── Tiptap Markdown Editor ── - Add full Tiptap-based WYSIWYG markdown editor (markdown-editor.tsx, 709 LOC) with bubble menu, auto-save (debounced), image drag-and-drop/paste upload, table editing, task list checkboxes, and frontmatter preservation on save. - Add slash command system (slash-command.tsx, 607 LOC) with "/" trigger for block insertion (headings, lists, tables, code blocks, images, reports) and "@" trigger for file/document mention with fuzzy search across the workspace tree. - Add ReportBlockNode (report-block-node.tsx) — custom Tiptap node that renders embedded report-json blocks as interactive ReportCard widgets inline in the editor, with expand/collapse and edit-JSON support. - Add workspace asset serving API (api/workspace/assets/[...path]/route.ts) to serve images from the workspace with proper MIME types. - Add workspace file upload orkspace/upload/route.ts) for multipart image uploads (10 MB limit, image types only), saving to assets/ directory. - Add ~500 lines of Tiptap editor CSS to globals.css (editor layout, task lists, images, tables, slash command dropdown, bubble menu toolbar, code blocks, etc.). - Add 14 @tiptap/* dependencies to apps/web/package.json (react, starter-kit, markdown, image, link, table, task-list, suggestion, placeholder, etc.). ── Document View: Edit/Read Mode Toggle ── - document-view.tsx: Add edit/read mode toggle; defaults to edit mode when a filePath is available. Lazy-loads MarkdownEditor to keep initial bundle light. - workspace/page.tsx: Pass activePath, tree, onSave, onNavigate, and onRefreshTree through to DocumentView for full editor integration with workspace navigation and tree refresh after saves. ── Subagent Session Isolation ── - agent-runner.ts: Add RunAgentOptions with optional sessionId; when set, spawns the agent with --session-key agent:main:subagent:<id> ant so file-scoped sidebar chats run in isolated sessions independent of the main agent. - route.ts (chat API): Accept sessionId from request body and forward it to runAgent. Resolve workspace file path prefixes (resolveAgentWorkspacePrefix) so tree-relative paths become agent-cwd-relative. - chat-panel.tsx: Create per-instance DefaultChatTransport that injects sessionId via body function and a ref (avoids stale closures). On file change, auto-load the most recent session and its messages. Refresh session tab list after streaming ends. Stop ongoing stream when switching sessions. - register.agent.ts: Add --session-key <key> and --lane <lane> CLI flags. - agent-via-gateway.ts: Wire sessionKey into session resolution and validation for both interactive and --stream-json code paths. - workspace.ts: Add resolveAgentWorkspacePrefix() to map workspace-root-relative paths to repo-root-relative paths for the agent process. ── Error Surfacing ── - agent-runner.ts: Add onAgentError callback extraction helpers (parseAgentErrorMessage, parseErrorBody, parseErrorFromStderr) to surface API-level errors (402 payment, rate limits, etc.) to the UI. Captures stderr for fallback error detection on non-zero exit. - route.ts: Wire onAgentError into the SSE stream as [error]-prefixed text parts. Improve onError and onClose handlers with clearer error messages and exit code reporting. - chat-message.tsx: Detect [error]-prefixed text segments and render them as styled error banners with alert icon instead of plain text. - chat-panel.tsx: Restyle the transport-level error bar with themed colors and an alert icon consistent with in-message error styling.
2026-02-11 20:54:30 -08:00
// Lazy-load the Tiptap-based editor (heavy -- keep out of initial bundle)
const MarkdownEditor = dynamic(
() => import("./markdown-editor").then((m) => ({ default: m.MarkdownEditor })),
{
ssr: false,
loading: () => (
<div className="animate-pulse space-y-3 py-4 px-6">
<div className="h-4 rounded" style={{ background: "var(--color-surface)", width: "80%" }} />
<div className="h-4 rounded" style={{ background: "var(--color-surface)", width: "60%" }} />
<div className="h-4 rounded" style={{ background: "var(--color-surface)", width: "70%" }} />
</div>
),
},
);
2026-02-11 16:45:07 -08:00
type DocumentViewProps = {
content: string;
title?: string;
Dench workspace: Tiptap markdown editor, subagent sessions, and error surfacing ── Tiptap Markdown Editor ── - Add full Tiptap-based WYSIWYG markdown editor (markdown-editor.tsx, 709 LOC) with bubble menu, auto-save (debounced), image drag-and-drop/paste upload, table editing, task list checkboxes, and frontmatter preservation on save. - Add slash command system (slash-command.tsx, 607 LOC) with "/" trigger for block insertion (headings, lists, tables, code blocks, images, reports) and "@" trigger for file/document mention with fuzzy search across the workspace tree. - Add ReportBlockNode (report-block-node.tsx) — custom Tiptap node that renders embedded report-json blocks as interactive ReportCard widgets inline in the editor, with expand/collapse and edit-JSON support. - Add workspace asset serving API (api/workspace/assets/[...path]/route.ts) to serve images from the workspace with proper MIME types. - Add workspace file upload orkspace/upload/route.ts) for multipart image uploads (10 MB limit, image types only), saving to assets/ directory. - Add ~500 lines of Tiptap editor CSS to globals.css (editor layout, task lists, images, tables, slash command dropdown, bubble menu toolbar, code blocks, etc.). - Add 14 @tiptap/* dependencies to apps/web/package.json (react, starter-kit, markdown, image, link, table, task-list, suggestion, placeholder, etc.). ── Document View: Edit/Read Mode Toggle ── - document-view.tsx: Add edit/read mode toggle; defaults to edit mode when a filePath is available. Lazy-loads MarkdownEditor to keep initial bundle light. - workspace/page.tsx: Pass activePath, tree, onSave, onNavigate, and onRefreshTree through to DocumentView for full editor integration with workspace navigation and tree refresh after saves. ── Subagent Session Isolation ── - agent-runner.ts: Add RunAgentOptions with optional sessionId; when set, spawns the agent with --session-key agent:main:subagent:<id> ant so file-scoped sidebar chats run in isolated sessions independent of the main agent. - route.ts (chat API): Accept sessionId from request body and forward it to runAgent. Resolve workspace file path prefixes (resolveAgentWorkspacePrefix) so tree-relative paths become agent-cwd-relative. - chat-panel.tsx: Create per-instance DefaultChatTransport that injects sessionId via body function and a ref (avoids stale closures). On file change, auto-load the most recent session and its messages. Refresh session tab list after streaming ends. Stop ongoing stream when switching sessions. - register.agent.ts: Add --session-key <key> and --lane <lane> CLI flags. - agent-via-gateway.ts: Wire sessionKey into session resolution and validation for both interactive and --stream-json code paths. - workspace.ts: Add resolveAgentWorkspacePrefix() to map workspace-root-relative paths to repo-root-relative paths for the agent process. ── Error Surfacing ── - agent-runner.ts: Add onAgentError callback extraction helpers (parseAgentErrorMessage, parseErrorBody, parseErrorFromStderr) to surface API-level errors (402 payment, rate limits, etc.) to the UI. Captures stderr for fallback error detection on non-zero exit. - route.ts: Wire onAgentError into the SSE stream as [error]-prefixed text parts. Improve onError and onClose handlers with clearer error messages and exit code reporting. - chat-message.tsx: Detect [error]-prefixed text segments and render them as styled error banners with alert icon instead of plain text. - chat-panel.tsx: Restyle the transport-level error bar with themed colors and an alert icon consistent with in-message error styling.
2026-02-11 20:54:30 -08:00
filePath?: string;
tree?: TreeNode[];
onSave?: () => void;
onNavigate?: (path: string) => void;
Dench workspace: unified @ mention search, entry detail modal, and workspace link system New libraries: - workspace-links.ts: builders, parsers, and type guards for workspace URLs (/workspace?path=... for files/objects, /workspace?entry=objName:id for entries). Replaces ad-hoc relative-path links with real navigable URLs. - search-index.ts: useSearchIndex hook that fetches a unified search index from the API and provides Fuse.js-powered fuzzy search across files, objects, and database entries. Exposes a stable ref-based search function safe for capture in tiptap extensions. New API routes: - GET /api/workspace/search-index: builds a flat search index from the filesystem tree (knowledge/, reports/, top-level files) and all DuckDB object entries with display-field labels and preview fields. - GET /api/workspace/objects/[name]/entries/[id]: fetches a single entry with all field values, resolved relation labels, reverse relations (incoming links from other objects), and the effective display field. New component: - EntryDetailModal: slide-over modal for viewing an individual entry's fields, enum badges, user avatars, clickable relation chips (navigate to related entries), reverse relation sections, and timestamps. Supports Escape to close and backdrop click dismiss. Slash command refactor (slash-command.tsx): - New createWorkspaceMention(searchFn) replaces the old file-only @ mention with unified search across files, objects, and entries. - searchItemToSlashItem() converts search index items into tiptap suggestion items with proper icons, badges (object name pill for entries), and link insertion commands using real workspace URLs. - Legacy createFileMention(tree) now delegates to createWorkspaceMention with a simple substring-match fallback for when no search index is available. Editor integration (markdown-editor.tsx, document-view.tsx): - MarkdownEditor accepts optional searchFn prop; when provided, uses createWorkspaceMention instead of the legacy createFileMention. - Link click interception now uses the shared isWorkspaceLink() helper and registers handlers in capture phase for reliable interception. - DocumentView forwards searchFn to editor and adds a delegated click handler in read mode to intercept workspace links and navigate via onNavigate. Object table (object-table.tsx): - Added onEntryClick prop; table rows are now clickable with cursor-pointer styling, firing the callback with the entry ID. Workspace page (page.tsx): - Integrates useSearchIndex hook and passes search function down to editor. - Entry detail modal state with URL synchronization (?entry=objName:id param). - New resolveNode() with fallback strategies: exact match, knowledge/ prefix toggle, and last-segment object name matching. - Unified handleEditorNavigate() dispatches /workspace?entry=... to the modal and /workspace?path=... to file/object navigation. - URL bar syncs with activePath via router.replace (no full page reloads). - Top-level container click safety net catches any workspace link clicks that bubble up unhandled. Styles (globals.css): - Added .slash-cmd-item-badge for object-name pills in the @ mention popup.
2026-02-11 21:41:23 -08:00
searchFn?: MentionSearchFn;
2026-02-11 16:45:07 -08:00
};
Dench workspace: Tiptap markdown editor, subagent sessions, and error surfacing ── Tiptap Markdown Editor ── - Add full Tiptap-based WYSIWYG markdown editor (markdown-editor.tsx, 709 LOC) with bubble menu, auto-save (debounced), image drag-and-drop/paste upload, table editing, task list checkboxes, and frontmatter preservation on save. - Add slash command system (slash-command.tsx, 607 LOC) with "/" trigger for block insertion (headings, lists, tables, code blocks, images, reports) and "@" trigger for file/document mention with fuzzy search across the workspace tree. - Add ReportBlockNode (report-block-node.tsx) — custom Tiptap node that renders embedded report-json blocks as interactive ReportCard widgets inline in the editor, with expand/collapse and edit-JSON support. - Add workspace asset serving API (api/workspace/assets/[...path]/route.ts) to serve images from the workspace with proper MIME types. - Add workspace file upload orkspace/upload/route.ts) for multipart image uploads (10 MB limit, image types only), saving to assets/ directory. - Add ~500 lines of Tiptap editor CSS to globals.css (editor layout, task lists, images, tables, slash command dropdown, bubble menu toolbar, code blocks, etc.). - Add 14 @tiptap/* dependencies to apps/web/package.json (react, starter-kit, markdown, image, link, table, task-list, suggestion, placeholder, etc.). ── Document View: Edit/Read Mode Toggle ── - document-view.tsx: Add edit/read mode toggle; defaults to edit mode when a filePath is available. Lazy-loads MarkdownEditor to keep initial bundle light. - workspace/page.tsx: Pass activePath, tree, onSave, onNavigate, and onRefreshTree through to DocumentView for full editor integration with workspace navigation and tree refresh after saves. ── Subagent Session Isolation ── - agent-runner.ts: Add RunAgentOptions with optional sessionId; when set, spawns the agent with --session-key agent:main:subagent:<id> ant so file-scoped sidebar chats run in isolated sessions independent of the main agent. - route.ts (chat API): Accept sessionId from request body and forward it to runAgent. Resolve workspace file path prefixes (resolveAgentWorkspacePrefix) so tree-relative paths become agent-cwd-relative. - chat-panel.tsx: Create per-instance DefaultChatTransport that injects sessionId via body function and a ref (avoids stale closures). On file change, auto-load the most recent session and its messages. Refresh session tab list after streaming ends. Stop ongoing stream when switching sessions. - register.agent.ts: Add --session-key <key> and --lane <lane> CLI flags. - agent-via-gateway.ts: Wire sessionKey into session resolution and validation for both interactive and --stream-json code paths. - workspace.ts: Add resolveAgentWorkspacePrefix() to map workspace-root-relative paths to repo-root-relative paths for the agent process. ── Error Surfacing ── - agent-runner.ts: Add onAgentError callback extraction helpers (parseAgentErrorMessage, parseErrorBody, parseErrorFromStderr) to surface API-level errors (402 payment, rate limits, etc.) to the UI. Captures stderr for fallback error detection on non-zero exit. - route.ts: Wire onAgentError into the SSE stream as [error]-prefixed text parts. Improve onError and onClose handlers with clearer error messages and exit code reporting. - chat-message.tsx: Detect [error]-prefixed text segments and render them as styled error banners with alert icon instead of plain text. - chat-panel.tsx: Restyle the transport-level error bar with themed colors and an alert icon consistent with in-message error styling.
2026-02-11 20:54:30 -08:00
export function DocumentView({
content,
title,
filePath,
tree,
onSave,
onNavigate,
Dench workspace: unified @ mention search, entry detail modal, and workspace link system New libraries: - workspace-links.ts: builders, parsers, and type guards for workspace URLs (/workspace?path=... for files/objects, /workspace?entry=objName:id for entries). Replaces ad-hoc relative-path links with real navigable URLs. - search-index.ts: useSearchIndex hook that fetches a unified search index from the API and provides Fuse.js-powered fuzzy search across files, objects, and database entries. Exposes a stable ref-based search function safe for capture in tiptap extensions. New API routes: - GET /api/workspace/search-index: builds a flat search index from the filesystem tree (knowledge/, reports/, top-level files) and all DuckDB object entries with display-field labels and preview fields. - GET /api/workspace/objects/[name]/entries/[id]: fetches a single entry with all field values, resolved relation labels, reverse relations (incoming links from other objects), and the effective display field. New component: - EntryDetailModal: slide-over modal for viewing an individual entry's fields, enum badges, user avatars, clickable relation chips (navigate to related entries), reverse relation sections, and timestamps. Supports Escape to close and backdrop click dismiss. Slash command refactor (slash-command.tsx): - New createWorkspaceMention(searchFn) replaces the old file-only @ mention with unified search across files, objects, and entries. - searchItemToSlashItem() converts search index items into tiptap suggestion items with proper icons, badges (object name pill for entries), and link insertion commands using real workspace URLs. - Legacy createFileMention(tree) now delegates to createWorkspaceMention with a simple substring-match fallback for when no search index is available. Editor integration (markdown-editor.tsx, document-view.tsx): - MarkdownEditor accepts optional searchFn prop; when provided, uses createWorkspaceMention instead of the legacy createFileMention. - Link click interception now uses the shared isWorkspaceLink() helper and registers handlers in capture phase for reliable interception. - DocumentView forwards searchFn to editor and adds a delegated click handler in read mode to intercept workspace links and navigate via onNavigate. Object table (object-table.tsx): - Added onEntryClick prop; table rows are now clickable with cursor-pointer styling, firing the callback with the entry ID. Workspace page (page.tsx): - Integrates useSearchIndex hook and passes search function down to editor. - Entry detail modal state with URL synchronization (?entry=objName:id param). - New resolveNode() with fallback strategies: exact match, knowledge/ prefix toggle, and last-segment object name matching. - Unified handleEditorNavigate() dispatches /workspace?entry=... to the modal and /workspace?path=... to file/object navigation. - URL bar syncs with activePath via router.replace (no full page reloads). - Top-level container click safety net catches any workspace link clicks that bubble up unhandled. Styles (globals.css): - Added .slash-cmd-item-badge for object-name pills in the @ mention popup.
2026-02-11 21:41:23 -08:00
searchFn,
Dench workspace: Tiptap markdown editor, subagent sessions, and error surfacing ── Tiptap Markdown Editor ── - Add full Tiptap-based WYSIWYG markdown editor (markdown-editor.tsx, 709 LOC) with bubble menu, auto-save (debounced), image drag-and-drop/paste upload, table editing, task list checkboxes, and frontmatter preservation on save. - Add slash command system (slash-command.tsx, 607 LOC) with "/" trigger for block insertion (headings, lists, tables, code blocks, images, reports) and "@" trigger for file/document mention with fuzzy search across the workspace tree. - Add ReportBlockNode (report-block-node.tsx) — custom Tiptap node that renders embedded report-json blocks as interactive ReportCard widgets inline in the editor, with expand/collapse and edit-JSON support. - Add workspace asset serving API (api/workspace/assets/[...path]/route.ts) to serve images from the workspace with proper MIME types. - Add workspace file upload orkspace/upload/route.ts) for multipart image uploads (10 MB limit, image types only), saving to assets/ directory. - Add ~500 lines of Tiptap editor CSS to globals.css (editor layout, task lists, images, tables, slash command dropdown, bubble menu toolbar, code blocks, etc.). - Add 14 @tiptap/* dependencies to apps/web/package.json (react, starter-kit, markdown, image, link, table, task-list, suggestion, placeholder, etc.). ── Document View: Edit/Read Mode Toggle ── - document-view.tsx: Add edit/read mode toggle; defaults to edit mode when a filePath is available. Lazy-loads MarkdownEditor to keep initial bundle light. - workspace/page.tsx: Pass activePath, tree, onSave, onNavigate, and onRefreshTree through to DocumentView for full editor integration with workspace navigation and tree refresh after saves. ── Subagent Session Isolation ── - agent-runner.ts: Add RunAgentOptions with optional sessionId; when set, spawns the agent with --session-key agent:main:subagent:<id> ant so file-scoped sidebar chats run in isolated sessions independent of the main agent. - route.ts (chat API): Accept sessionId from request body and forward it to runAgent. Resolve workspace file path prefixes (resolveAgentWorkspacePrefix) so tree-relative paths become agent-cwd-relative. - chat-panel.tsx: Create per-instance DefaultChatTransport that injects sessionId via body function and a ref (avoids stale closures). On file change, auto-load the most recent session and its messages. Refresh session tab list after streaming ends. Stop ongoing stream when switching sessions. - register.agent.ts: Add --session-key <key> and --lane <lane> CLI flags. - agent-via-gateway.ts: Wire sessionKey into session resolution and validation for both interactive and --stream-json code paths. - workspace.ts: Add resolveAgentWorkspacePrefix() to map workspace-root-relative paths to repo-root-relative paths for the agent process. ── Error Surfacing ── - agent-runner.ts: Add onAgentError callback extraction helpers (parseAgentErrorMessage, parseErrorBody, parseErrorFromStderr) to surface API-level errors (402 payment, rate limits, etc.) to the UI. Captures stderr for fallback error detection on non-zero exit. - route.ts: Wire onAgentError into the SSE stream as [error]-prefixed text parts. Improve onError and onClose handlers with clearer error messages and exit code reporting. - chat-message.tsx: Detect [error]-prefixed text segments and render them as styled error banners with alert icon instead of plain text. - chat-panel.tsx: Restyle the transport-level error bar with themed colors and an alert icon consistent with in-message error styling.
2026-02-11 20:54:30 -08:00
}: DocumentViewProps) {
const [editMode, setEditMode] = useState(!!filePath);
2026-02-11 16:45:07 -08:00
// Strip YAML frontmatter if present
const body = content.replace(/^---\s*\n[\s\S]*?\n---\s*\n/, "");
// Extract title from first H1 if no title provided
const h1Match = body.match(/^#\s+(.+)/m);
const displayTitle = title ?? h1Match?.[1];
const markdownBody =
displayTitle && h1Match ? body.replace(/^#\s+.+\n?/, "") : body;
Dench workspace: Tiptap markdown editor, subagent sessions, and error surfacing ── Tiptap Markdown Editor ── - Add full Tiptap-based WYSIWYG markdown editor (markdown-editor.tsx, 709 LOC) with bubble menu, auto-save (debounced), image drag-and-drop/paste upload, table editing, task list checkboxes, and frontmatter preservation on save. - Add slash command system (slash-command.tsx, 607 LOC) with "/" trigger for block insertion (headings, lists, tables, code blocks, images, reports) and "@" trigger for file/document mention with fuzzy search across the workspace tree. - Add ReportBlockNode (report-block-node.tsx) — custom Tiptap node that renders embedded report-json blocks as interactive ReportCard widgets inline in the editor, with expand/collapse and edit-JSON support. - Add workspace asset serving API (api/workspace/assets/[...path]/route.ts) to serve images from the workspace with proper MIME types. - Add workspace file upload orkspace/upload/route.ts) for multipart image uploads (10 MB limit, image types only), saving to assets/ directory. - Add ~500 lines of Tiptap editor CSS to globals.css (editor layout, task lists, images, tables, slash command dropdown, bubble menu toolbar, code blocks, etc.). - Add 14 @tiptap/* dependencies to apps/web/package.json (react, starter-kit, markdown, image, link, table, task-list, suggestion, placeholder, etc.). ── Document View: Edit/Read Mode Toggle ── - document-view.tsx: Add edit/read mode toggle; defaults to edit mode when a filePath is available. Lazy-loads MarkdownEditor to keep initial bundle light. - workspace/page.tsx: Pass activePath, tree, onSave, onNavigate, and onRefreshTree through to DocumentView for full editor integration with workspace navigation and tree refresh after saves. ── Subagent Session Isolation ── - agent-runner.ts: Add RunAgentOptions with optional sessionId; when set, spawns the agent with --session-key agent:main:subagent:<id> ant so file-scoped sidebar chats run in isolated sessions independent of the main agent. - route.ts (chat API): Accept sessionId from request body and forward it to runAgent. Resolve workspace file path prefixes (resolveAgentWorkspacePrefix) so tree-relative paths become agent-cwd-relative. - chat-panel.tsx: Create per-instance DefaultChatTransport that injects sessionId via body function and a ref (avoids stale closures). On file change, auto-load the most recent session and its messages. Refresh session tab list after streaming ends. Stop ongoing stream when switching sessions. - register.agent.ts: Add --session-key <key> and --lane <lane> CLI flags. - agent-via-gateway.ts: Wire sessionKey into session resolution and validation for both interactive and --stream-json code paths. - workspace.ts: Add resolveAgentWorkspacePrefix() to map workspace-root-relative paths to repo-root-relative paths for the agent process. ── Error Surfacing ── - agent-runner.ts: Add onAgentError callback extraction helpers (parseAgentErrorMessage, parseErrorBody, parseErrorFromStderr) to surface API-level errors (402 payment, rate limits, etc.) to the UI. Captures stderr for fallback error detection on non-zero exit. - route.ts: Wire onAgentError into the SSE stream as [error]-prefixed text parts. Improve onError and onClose handlers with clearer error messages and exit code reporting. - chat-message.tsx: Detect [error]-prefixed text segments and render them as styled error banners with alert icon instead of plain text. - chat-panel.tsx: Restyle the transport-level error bar with themed colors and an alert icon consistent with in-message error styling.
2026-02-11 20:54:30 -08:00
// If we have a filePath and editing is enabled, render the Tiptap editor
if (editMode && filePath) {
return (
<div className="max-w-3xl mx-auto">
<MarkdownEditor
content={body}
rawContent={content}
filePath={filePath}
tree={tree ?? []}
onSave={onSave}
onNavigate={onNavigate}
Dench workspace: unified @ mention search, entry detail modal, and workspace link system New libraries: - workspace-links.ts: builders, parsers, and type guards for workspace URLs (/workspace?path=... for files/objects, /workspace?entry=objName:id for entries). Replaces ad-hoc relative-path links with real navigable URLs. - search-index.ts: useSearchIndex hook that fetches a unified search index from the API and provides Fuse.js-powered fuzzy search across files, objects, and database entries. Exposes a stable ref-based search function safe for capture in tiptap extensions. New API routes: - GET /api/workspace/search-index: builds a flat search index from the filesystem tree (knowledge/, reports/, top-level files) and all DuckDB object entries with display-field labels and preview fields. - GET /api/workspace/objects/[name]/entries/[id]: fetches a single entry with all field values, resolved relation labels, reverse relations (incoming links from other objects), and the effective display field. New component: - EntryDetailModal: slide-over modal for viewing an individual entry's fields, enum badges, user avatars, clickable relation chips (navigate to related entries), reverse relation sections, and timestamps. Supports Escape to close and backdrop click dismiss. Slash command refactor (slash-command.tsx): - New createWorkspaceMention(searchFn) replaces the old file-only @ mention with unified search across files, objects, and entries. - searchItemToSlashItem() converts search index items into tiptap suggestion items with proper icons, badges (object name pill for entries), and link insertion commands using real workspace URLs. - Legacy createFileMention(tree) now delegates to createWorkspaceMention with a simple substring-match fallback for when no search index is available. Editor integration (markdown-editor.tsx, document-view.tsx): - MarkdownEditor accepts optional searchFn prop; when provided, uses createWorkspaceMention instead of the legacy createFileMention. - Link click interception now uses the shared isWorkspaceLink() helper and registers handlers in capture phase for reliable interception. - DocumentView forwards searchFn to editor and adds a delegated click handler in read mode to intercept workspace links and navigate via onNavigate. Object table (object-table.tsx): - Added onEntryClick prop; table rows are now clickable with cursor-pointer styling, firing the callback with the entry ID. Workspace page (page.tsx): - Integrates useSearchIndex hook and passes search function down to editor. - Entry detail modal state with URL synchronization (?entry=objName:id param). - New resolveNode() with fallback strategies: exact match, knowledge/ prefix toggle, and last-segment object name matching. - Unified handleEditorNavigate() dispatches /workspace?entry=... to the modal and /workspace?path=... to file/object navigation. - URL bar syncs with activePath via router.replace (no full page reloads). - Top-level container click safety net catches any workspace link clicks that bubble up unhandled. Styles (globals.css): - Added .slash-cmd-item-badge for object-name pills in the @ mention popup.
2026-02-11 21:41:23 -08:00
searchFn={searchFn}
Dench workspace: Tiptap markdown editor, subagent sessions, and error surfacing ── Tiptap Markdown Editor ── - Add full Tiptap-based WYSIWYG markdown editor (markdown-editor.tsx, 709 LOC) with bubble menu, auto-save (debounced), image drag-and-drop/paste upload, table editing, task list checkboxes, and frontmatter preservation on save. - Add slash command system (slash-command.tsx, 607 LOC) with "/" trigger for block insertion (headings, lists, tables, code blocks, images, reports) and "@" trigger for file/document mention with fuzzy search across the workspace tree. - Add ReportBlockNode (report-block-node.tsx) — custom Tiptap node that renders embedded report-json blocks as interactive ReportCard widgets inline in the editor, with expand/collapse and edit-JSON support. - Add workspace asset serving API (api/workspace/assets/[...path]/route.ts) to serve images from the workspace with proper MIME types. - Add workspace file upload orkspace/upload/route.ts) for multipart image uploads (10 MB limit, image types only), saving to assets/ directory. - Add ~500 lines of Tiptap editor CSS to globals.css (editor layout, task lists, images, tables, slash command dropdown, bubble menu toolbar, code blocks, etc.). - Add 14 @tiptap/* dependencies to apps/web/package.json (react, starter-kit, markdown, image, link, table, task-list, suggestion, placeholder, etc.). ── Document View: Edit/Read Mode Toggle ── - document-view.tsx: Add edit/read mode toggle; defaults to edit mode when a filePath is available. Lazy-loads MarkdownEditor to keep initial bundle light. - workspace/page.tsx: Pass activePath, tree, onSave, onNavigate, and onRefreshTree through to DocumentView for full editor integration with workspace navigation and tree refresh after saves. ── Subagent Session Isolation ── - agent-runner.ts: Add RunAgentOptions with optional sessionId; when set, spawns the agent with --session-key agent:main:subagent:<id> ant so file-scoped sidebar chats run in isolated sessions independent of the main agent. - route.ts (chat API): Accept sessionId from request body and forward it to runAgent. Resolve workspace file path prefixes (resolveAgentWorkspacePrefix) so tree-relative paths become agent-cwd-relative. - chat-panel.tsx: Create per-instance DefaultChatTransport that injects sessionId via body function and a ref (avoids stale closures). On file change, auto-load the most recent session and its messages. Refresh session tab list after streaming ends. Stop ongoing stream when switching sessions. - register.agent.ts: Add --session-key <key> and --lane <lane> CLI flags. - agent-via-gateway.ts: Wire sessionKey into session resolution and validation for both interactive and --stream-json code paths. - workspace.ts: Add resolveAgentWorkspacePrefix() to map workspace-root-relative paths to repo-root-relative paths for the agent process. ── Error Surfacing ── - agent-runner.ts: Add onAgentError callback extraction helpers (parseAgentErrorMessage, parseErrorBody, parseErrorFromStderr) to surface API-level errors (402 payment, rate limits, etc.) to the UI. Captures stderr for fallback error detection on non-zero exit. - route.ts: Wire onAgentError into the SSE stream as [error]-prefixed text parts. Improve onError and onClose handlers with clearer error messages and exit code reporting. - chat-message.tsx: Detect [error]-prefixed text segments and render them as styled error banners with alert icon instead of plain text. - chat-panel.tsx: Restyle the transport-level error bar with themed colors and an alert icon consistent with in-message error styling.
2026-02-11 20:54:30 -08:00
/>
</div>
);
}
Web app: add syntax highlighting, diff viewer, rich chat editor, and file search Syntax highlighting & code viewer: - Add shiki for syntax-highlighted fenced code blocks in chat messages - New SyntaxBlock component (lazy shiki, dual light/dark theme) - New CodeViewer for workspace file panel (routes code files via isCodeFile()) - API routes (browse-file, virtual-file) now return "code" type for known extensions Diff rendering: - New DiffCard component for rendering unified diffs with add/remove colors - diff-blocks.ts parser to extract fenced blocks from markdown - Chain-of-thought tool steps show inline diffs for edit/write tools (synthetic from old_string/new_string or direct from tool output) - Agent runner passes through diff/firstChangedLine from edit tool results - Document view handles diff blocks alongside report blocks Rich chat editor (Tiptap): - Replace plain textarea with Tiptap-based ChatEditor - File mention extension (@-mention files with autocomplete dropdown) - File mention list with keyboard navigation and search via suggest-files API - New suggest-files API endpoint for fuzzy file search File search & navigation: - FileSearch component in workspace sidebar (debounced search, keyboard nav) - Search results navigate sidebar to file location and open in panel - File picker modal for browsing/selecting workspace files Drag & drop: - File tree nodes support native HTML5 drag (application/x-file-mention) for cross-component drops (e.g. dragging files into chat editor) Chat attachments reworked: - Switch from browser File objects to path-based references (name + path) - Simplified attachment strip (no media previews, shows shortened paths) Also adds software-engineering skill and related CSS for code blocks/shiki.
2026-02-13 18:06:59 -08:00
// Check if the markdown contains embedded rich blocks (reports or diffs)
const hasRichBlocks = hasReportBlocks(markdownBody) || hasDiffBlocks(markdownBody);
2026-02-11 18:35:35 -08:00
Dench workspace: unified @ mention search, entry detail modal, and workspace link system New libraries: - workspace-links.ts: builders, parsers, and type guards for workspace URLs (/workspace?path=... for files/objects, /workspace?entry=objName:id for entries). Replaces ad-hoc relative-path links with real navigable URLs. - search-index.ts: useSearchIndex hook that fetches a unified search index from the API and provides Fuse.js-powered fuzzy search across files, objects, and database entries. Exposes a stable ref-based search function safe for capture in tiptap extensions. New API routes: - GET /api/workspace/search-index: builds a flat search index from the filesystem tree (knowledge/, reports/, top-level files) and all DuckDB object entries with display-field labels and preview fields. - GET /api/workspace/objects/[name]/entries/[id]: fetches a single entry with all field values, resolved relation labels, reverse relations (incoming links from other objects), and the effective display field. New component: - EntryDetailModal: slide-over modal for viewing an individual entry's fields, enum badges, user avatars, clickable relation chips (navigate to related entries), reverse relation sections, and timestamps. Supports Escape to close and backdrop click dismiss. Slash command refactor (slash-command.tsx): - New createWorkspaceMention(searchFn) replaces the old file-only @ mention with unified search across files, objects, and entries. - searchItemToSlashItem() converts search index items into tiptap suggestion items with proper icons, badges (object name pill for entries), and link insertion commands using real workspace URLs. - Legacy createFileMention(tree) now delegates to createWorkspaceMention with a simple substring-match fallback for when no search index is available. Editor integration (markdown-editor.tsx, document-view.tsx): - MarkdownEditor accepts optional searchFn prop; when provided, uses createWorkspaceMention instead of the legacy createFileMention. - Link click interception now uses the shared isWorkspaceLink() helper and registers handlers in capture phase for reliable interception. - DocumentView forwards searchFn to editor and adds a delegated click handler in read mode to intercept workspace links and navigate via onNavigate. Object table (object-table.tsx): - Added onEntryClick prop; table rows are now clickable with cursor-pointer styling, firing the callback with the entry ID. Workspace page (page.tsx): - Integrates useSearchIndex hook and passes search function down to editor. - Entry detail modal state with URL synchronization (?entry=objName:id param). - New resolveNode() with fallback strategies: exact match, knowledge/ prefix toggle, and last-segment object name matching. - Unified handleEditorNavigate() dispatches /workspace?entry=... to the modal and /workspace?path=... to file/object navigation. - URL bar syncs with activePath via router.replace (no full page reloads). - Top-level container click safety net catches any workspace link clicks that bubble up unhandled. Styles (globals.css): - Added .slash-cmd-item-badge for object-name pills in the @ mention popup.
2026-02-11 21:41:23 -08:00
// Intercept workspace-internal links in read mode (delegated click handler)
const handleLinkClick = useCallback(
(event: ReactMouseEvent<HTMLDivElement>) => {
if (!onNavigate) {return;}
const target = event.target as HTMLElement;
const link = target.closest("a");
if (!link) {return;}
const href = link.getAttribute("href");
if (!href) {return;}
if (isWorkspaceLink(href)) {
event.preventDefault();
event.stopPropagation();
onNavigate(href);
}
},
[onNavigate],
);
2026-02-11 16:45:07 -08:00
return (
Dench workspace: unified @ mention search, entry detail modal, and workspace link system New libraries: - workspace-links.ts: builders, parsers, and type guards for workspace URLs (/workspace?path=... for files/objects, /workspace?entry=objName:id for entries). Replaces ad-hoc relative-path links with real navigable URLs. - search-index.ts: useSearchIndex hook that fetches a unified search index from the API and provides Fuse.js-powered fuzzy search across files, objects, and database entries. Exposes a stable ref-based search function safe for capture in tiptap extensions. New API routes: - GET /api/workspace/search-index: builds a flat search index from the filesystem tree (knowledge/, reports/, top-level files) and all DuckDB object entries with display-field labels and preview fields. - GET /api/workspace/objects/[name]/entries/[id]: fetches a single entry with all field values, resolved relation labels, reverse relations (incoming links from other objects), and the effective display field. New component: - EntryDetailModal: slide-over modal for viewing an individual entry's fields, enum badges, user avatars, clickable relation chips (navigate to related entries), reverse relation sections, and timestamps. Supports Escape to close and backdrop click dismiss. Slash command refactor (slash-command.tsx): - New createWorkspaceMention(searchFn) replaces the old file-only @ mention with unified search across files, objects, and entries. - searchItemToSlashItem() converts search index items into tiptap suggestion items with proper icons, badges (object name pill for entries), and link insertion commands using real workspace URLs. - Legacy createFileMention(tree) now delegates to createWorkspaceMention with a simple substring-match fallback for when no search index is available. Editor integration (markdown-editor.tsx, document-view.tsx): - MarkdownEditor accepts optional searchFn prop; when provided, uses createWorkspaceMention instead of the legacy createFileMention. - Link click interception now uses the shared isWorkspaceLink() helper and registers handlers in capture phase for reliable interception. - DocumentView forwards searchFn to editor and adds a delegated click handler in read mode to intercept workspace links and navigate via onNavigate. Object table (object-table.tsx): - Added onEntryClick prop; table rows are now clickable with cursor-pointer styling, firing the callback with the entry ID. Workspace page (page.tsx): - Integrates useSearchIndex hook and passes search function down to editor. - Entry detail modal state with URL synchronization (?entry=objName:id param). - New resolveNode() with fallback strategies: exact match, knowledge/ prefix toggle, and last-segment object name matching. - Unified handleEditorNavigate() dispatches /workspace?entry=... to the modal and /workspace?path=... to file/object navigation. - URL bar syncs with activePath via router.replace (no full page reloads). - Top-level container click safety net catches any workspace link clicks that bubble up unhandled. Styles (globals.css): - Added .slash-cmd-item-badge for object-name pills in the @ mention popup.
2026-02-11 21:41:23 -08:00
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<div className="max-w-3xl mx-auto px-6 py-8" onClick={handleLinkClick}>
Dench workspace: Tiptap markdown editor, subagent sessions, and error surfacing ── Tiptap Markdown Editor ── - Add full Tiptap-based WYSIWYG markdown editor (markdown-editor.tsx, 709 LOC) with bubble menu, auto-save (debounced), image drag-and-drop/paste upload, table editing, task list checkboxes, and frontmatter preservation on save. - Add slash command system (slash-command.tsx, 607 LOC) with "/" trigger for block insertion (headings, lists, tables, code blocks, images, reports) and "@" trigger for file/document mention with fuzzy search across the workspace tree. - Add ReportBlockNode (report-block-node.tsx) — custom Tiptap node that renders embedded report-json blocks as interactive ReportCard widgets inline in the editor, with expand/collapse and edit-JSON support. - Add workspace asset serving API (api/workspace/assets/[...path]/route.ts) to serve images from the workspace with proper MIME types. - Add workspace file upload orkspace/upload/route.ts) for multipart image uploads (10 MB limit, image types only), saving to assets/ directory. - Add ~500 lines of Tiptap editor CSS to globals.css (editor layout, task lists, images, tables, slash command dropdown, bubble menu toolbar, code blocks, etc.). - Add 14 @tiptap/* dependencies to apps/web/package.json (react, starter-kit, markdown, image, link, table, task-list, suggestion, placeholder, etc.). ── Document View: Edit/Read Mode Toggle ── - document-view.tsx: Add edit/read mode toggle; defaults to edit mode when a filePath is available. Lazy-loads MarkdownEditor to keep initial bundle light. - workspace/page.tsx: Pass activePath, tree, onSave, onNavigate, and onRefreshTree through to DocumentView for full editor integration with workspace navigation and tree refresh after saves. ── Subagent Session Isolation ── - agent-runner.ts: Add RunAgentOptions with optional sessionId; when set, spawns the agent with --session-key agent:main:subagent:<id> ant so file-scoped sidebar chats run in isolated sessions independent of the main agent. - route.ts (chat API): Accept sessionId from request body and forward it to runAgent. Resolve workspace file path prefixes (resolveAgentWorkspacePrefix) so tree-relative paths become agent-cwd-relative. - chat-panel.tsx: Create per-instance DefaultChatTransport that injects sessionId via body function and a ref (avoids stale closures). On file change, auto-load the most recent session and its messages. Refresh session tab list after streaming ends. Stop ongoing stream when switching sessions. - register.agent.ts: Add --session-key <key> and --lane <lane> CLI flags. - agent-via-gateway.ts: Wire sessionKey into session resolution and validation for both interactive and --stream-json code paths. - workspace.ts: Add resolveAgentWorkspacePrefix() to map workspace-root-relative paths to repo-root-relative paths for the agent process. ── Error Surfacing ── - agent-runner.ts: Add onAgentError callback extraction helpers (parseAgentErrorMessage, parseErrorBody, parseErrorFromStderr) to surface API-level errors (402 payment, rate limits, etc.) to the UI. Captures stderr for fallback error detection on non-zero exit. - route.ts: Wire onAgentError into the SSE stream as [error]-prefixed text parts. Improve onError and onClose handlers with clearer error messages and exit code reporting. - chat-message.tsx: Detect [error]-prefixed text segments and render them as styled error banners with alert icon instead of plain text. - chat-panel.tsx: Restyle the transport-level error bar with themed colors and an alert icon consistent with in-message error styling.
2026-02-11 20:54:30 -08:00
{/* Header row with title + edit button */}
<div className="flex items-start justify-between gap-4">
{displayTitle && (
<h1
className="text-3xl font-bold mb-6 flex-1"
style={{ color: "var(--color-text)" }}
>
{displayTitle}
</h1>
)}
{filePath && (
<button
type="button"
onClick={() => setEditMode(true)}
className="editor-mode-toggle flex-shrink-0 mt-1"
title="Edit this document"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
<path d="m15 5 4 4" />
</svg>
<span>Edit</span>
</button>
)}
</div>
2026-02-11 16:45:07 -08:00
Web app: add syntax highlighting, diff viewer, rich chat editor, and file search Syntax highlighting & code viewer: - Add shiki for syntax-highlighted fenced code blocks in chat messages - New SyntaxBlock component (lazy shiki, dual light/dark theme) - New CodeViewer for workspace file panel (routes code files via isCodeFile()) - API routes (browse-file, virtual-file) now return "code" type for known extensions Diff rendering: - New DiffCard component for rendering unified diffs with add/remove colors - diff-blocks.ts parser to extract fenced blocks from markdown - Chain-of-thought tool steps show inline diffs for edit/write tools (synthetic from old_string/new_string or direct from tool output) - Agent runner passes through diff/firstChangedLine from edit tool results - Document view handles diff blocks alongside report blocks Rich chat editor (Tiptap): - Replace plain textarea with Tiptap-based ChatEditor - File mention extension (@-mention files with autocomplete dropdown) - File mention list with keyboard navigation and search via suggest-files API - New suggest-files API endpoint for fuzzy file search File search & navigation: - FileSearch component in workspace sidebar (debounced search, keyboard nav) - Search results navigate sidebar to file location and open in panel - File picker modal for browsing/selecting workspace files Drag & drop: - File tree nodes support native HTML5 drag (application/x-file-mention) for cross-component drops (e.g. dragging files into chat editor) Chat attachments reworked: - Switch from browser File objects to path-based references (name + path) - Simplified attachment strip (no media previews, shows shortened paths) Also adds software-engineering skill and related CSS for code blocks/shiki.
2026-02-13 18:06:59 -08:00
{hasRichBlocks ? (
<EmbeddedRichContent content={markdownBody} />
2026-02-11 18:35:35 -08:00
) : (
<div className="workspace-prose">
<MarkdownContent content={markdownBody} />
</div>
)}
</div>
);
}
/**
Web app: add syntax highlighting, diff viewer, rich chat editor, and file search Syntax highlighting & code viewer: - Add shiki for syntax-highlighted fenced code blocks in chat messages - New SyntaxBlock component (lazy shiki, dual light/dark theme) - New CodeViewer for workspace file panel (routes code files via isCodeFile()) - API routes (browse-file, virtual-file) now return "code" type for known extensions Diff rendering: - New DiffCard component for rendering unified diffs with add/remove colors - diff-blocks.ts parser to extract fenced blocks from markdown - Chain-of-thought tool steps show inline diffs for edit/write tools (synthetic from old_string/new_string or direct from tool output) - Agent runner passes through diff/firstChangedLine from edit tool results - Document view handles diff blocks alongside report blocks Rich chat editor (Tiptap): - Replace plain textarea with Tiptap-based ChatEditor - File mention extension (@-mention files with autocomplete dropdown) - File mention list with keyboard navigation and search via suggest-files API - New suggest-files API endpoint for fuzzy file search File search & navigation: - FileSearch component in workspace sidebar (debounced search, keyboard nav) - Search results navigate sidebar to file location and open in panel - File picker modal for browsing/selecting workspace files Drag & drop: - File tree nodes support native HTML5 drag (application/x-file-mention) for cross-component drops (e.g. dragging files into chat editor) Chat attachments reworked: - Switch from browser File objects to path-based references (name + path) - Simplified attachment strip (no media previews, shows shortened paths) Also adds software-engineering skill and related CSS for code blocks/shiki.
2026-02-13 18:06:59 -08:00
* Renders markdown content that contains embedded rich blocks (reports and diffs).
* Splits the content into alternating markdown, chart, and diff sections.
2026-02-11 18:35:35 -08:00
*/
Web app: add syntax highlighting, diff viewer, rich chat editor, and file search Syntax highlighting & code viewer: - Add shiki for syntax-highlighted fenced code blocks in chat messages - New SyntaxBlock component (lazy shiki, dual light/dark theme) - New CodeViewer for workspace file panel (routes code files via isCodeFile()) - API routes (browse-file, virtual-file) now return "code" type for known extensions Diff rendering: - New DiffCard component for rendering unified diffs with add/remove colors - diff-blocks.ts parser to extract fenced blocks from markdown - Chain-of-thought tool steps show inline diffs for edit/write tools (synthetic from old_string/new_string or direct from tool output) - Agent runner passes through diff/firstChangedLine from edit tool results - Document view handles diff blocks alongside report blocks Rich chat editor (Tiptap): - Replace plain textarea with Tiptap-based ChatEditor - File mention extension (@-mention files with autocomplete dropdown) - File mention list with keyboard navigation and search via suggest-files API - New suggest-files API endpoint for fuzzy file search File search & navigation: - FileSearch component in workspace sidebar (debounced search, keyboard nav) - Search results navigate sidebar to file location and open in panel - File picker modal for browsing/selecting workspace files Drag & drop: - File tree nodes support native HTML5 drag (application/x-file-mention) for cross-component drops (e.g. dragging files into chat editor) Chat attachments reworked: - Switch from browser File objects to path-based references (name + path) - Simplified attachment strip (no media previews, shows shortened paths) Also adds software-engineering skill and related CSS for code blocks/shiki.
2026-02-13 18:06:59 -08:00
function EmbeddedRichContent({ content }: { content: string }) {
// First split on report blocks, then further split text segments on diff blocks
const reportSegments = splitReportBlocks(content);
type RichSegment =
| { type: "text"; text: string }
| { type: "report-artifact"; config: import("@/lib/report-blocks").ReportConfig }
| { type: "diff-artifact"; diff: string };
const segments: RichSegment[] = [];
for (const seg of reportSegments) {
if (seg.type === "text" && hasDiffBlocks(seg.text)) {
for (const ds of splitDiffBlocks(seg.text)) {
segments.push(ds);
}
} else {
segments.push(seg as RichSegment);
}
}
2026-02-11 18:35:35 -08:00
return (
<div className="space-y-4">
{segments.map((segment, index) => {
if (segment.type === "report-artifact") {
return (
<div key={index} className="my-6">
<ReportCard config={segment.config} />
</div>
);
}
Web app: add syntax highlighting, diff viewer, rich chat editor, and file search Syntax highlighting & code viewer: - Add shiki for syntax-highlighted fenced code blocks in chat messages - New SyntaxBlock component (lazy shiki, dual light/dark theme) - New CodeViewer for workspace file panel (routes code files via isCodeFile()) - API routes (browse-file, virtual-file) now return "code" type for known extensions Diff rendering: - New DiffCard component for rendering unified diffs with add/remove colors - diff-blocks.ts parser to extract fenced blocks from markdown - Chain-of-thought tool steps show inline diffs for edit/write tools (synthetic from old_string/new_string or direct from tool output) - Agent runner passes through diff/firstChangedLine from edit tool results - Document view handles diff blocks alongside report blocks Rich chat editor (Tiptap): - Replace plain textarea with Tiptap-based ChatEditor - File mention extension (@-mention files with autocomplete dropdown) - File mention list with keyboard navigation and search via suggest-files API - New suggest-files API endpoint for fuzzy file search File search & navigation: - FileSearch component in workspace sidebar (debounced search, keyboard nav) - Search results navigate sidebar to file location and open in panel - File picker modal for browsing/selecting workspace files Drag & drop: - File tree nodes support native HTML5 drag (application/x-file-mention) for cross-component drops (e.g. dragging files into chat editor) Chat attachments reworked: - Switch from browser File objects to path-based references (name + path) - Simplified attachment strip (no media previews, shows shortened paths) Also adds software-engineering skill and related CSS for code blocks/shiki.
2026-02-13 18:06:59 -08:00
if (segment.type === "diff-artifact") {
return (
<div key={index} className="my-4">
<DiffCard diff={segment.diff} />
</div>
);
}
2026-02-11 18:35:35 -08:00
// Text segment -- render as markdown
return (
<div key={index} className="workspace-prose">
<MarkdownContent content={segment.text} />
</div>
);
})}
2026-02-11 16:45:07 -08:00
</div>
);
}