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
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
useState,
|
|
|
|
|
useEffect,
|
|
|
|
|
useRef,
|
|
|
|
|
useCallback,
|
|
|
|
|
useLayoutEffect,
|
|
|
|
|
forwardRef,
|
|
|
|
|
useImperativeHandle,
|
|
|
|
|
} from "react";
|
|
|
|
|
import { Extension } from "@tiptap/core";
|
|
|
|
|
import Suggestion, { type SuggestionOptions } from "@tiptap/suggestion";
|
|
|
|
|
import { PluginKey } from "@tiptap/pm/state";
|
|
|
|
|
import { createPortal } from "react-dom";
|
|
|
|
|
import type { Editor, Range } from "@tiptap/core";
|
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 { SearchIndexItem } from "@/lib/search-index";
|
|
|
|
|
import { buildEntryLink, buildFileLink } from "@/lib/workspace-links";
|
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
|
|
|
|
|
|
|
|
// Unique plugin keys so both suggestions can coexist
|
|
|
|
|
const slashCommandPluginKey = new PluginKey("slashCommand");
|
|
|
|
|
const fileMentionPluginKey = new PluginKey("fileMention");
|
|
|
|
|
|
|
|
|
|
// --- Types ---
|
|
|
|
|
|
|
|
|
|
export type TreeNode = {
|
|
|
|
|
name: string;
|
|
|
|
|
path: string;
|
|
|
|
|
type: "object" | "document" | "folder" | "file" | "database" | "report";
|
|
|
|
|
icon?: string;
|
|
|
|
|
children?: TreeNode[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type SlashItem = {
|
|
|
|
|
title: string;
|
|
|
|
|
description?: string;
|
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
|
|
|
badge?: 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
|
|
|
icon: React.ReactNode;
|
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
|
|
|
category: "file" | "block" | "entry";
|
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
|
|
|
command: (props: { editor: Editor; range: Range }) => 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
|
|
|
/** Search function signature accepted by createWorkspaceMention. */
|
|
|
|
|
export type MentionSearchFn = (query: string, limit?: number) => SearchIndexItem[];
|
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
|
|
|
|
|
|
|
|
function nodeTypeIcon(type: string) {
|
|
|
|
|
switch (type) {
|
|
|
|
|
case "document":
|
|
|
|
|
return (
|
|
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
|
|
|
<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" />
|
|
|
|
|
<path d="M14 2v4a2 2 0 0 0 2 2h4" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
case "object":
|
|
|
|
|
return (
|
|
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
|
|
|
<path d="M12 3v18" /><rect width="18" height="18" x="3" y="3" rx="2" /><path d="M3 9h18" /><path d="M3 15h18" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
case "report":
|
|
|
|
|
return (
|
|
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
|
|
|
<line x1="12" x2="12" y1="20" y2="10" />
|
|
|
|
|
<line x1="18" x2="18" y1="20" y2="4" />
|
|
|
|
|
<line x1="6" x2="6" y1="20" y2="14" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
default:
|
|
|
|
|
return (
|
|
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
|
|
|
<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" />
|
|
|
|
|
<path d="M14 2v4a2 2 0 0 0 2 2h4" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Block command icons ---
|
|
|
|
|
|
|
|
|
|
const headingIcon = (level: number) => (
|
|
|
|
|
<span className="slash-cmd-icon-text">H{level}</span>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const bulletListIcon = (
|
|
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
|
|
|
<line x1="8" x2="21" y1="6" y2="6" /><line x1="8" x2="21" y1="12" y2="12" /><line x1="8" x2="21" y1="18" y2="18" />
|
|
|
|
|
<line x1="3" x2="3.01" y1="6" y2="6" /><line x1="3" x2="3.01" y1="12" y2="12" /><line x1="3" x2="3.01" y1="18" y2="18" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const orderedListIcon = (
|
|
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
|
|
|
<line x1="10" x2="21" y1="6" y2="6" /><line x1="10" x2="21" y1="12" y2="12" /><line x1="10" x2="21" y1="18" y2="18" />
|
|
|
|
|
<path d="M4 6h1v4" /><path d="M4 10h2" /><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const blockquoteIcon = (
|
|
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
|
|
|
<path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V21z" />
|
|
|
|
|
<path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3z" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const codeBlockIcon = (
|
|
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
|
|
|
<polyline points="16 18 22 12 16 6" /><polyline points="8 6 2 12 8 18" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const horizontalRuleIcon = (
|
|
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
|
|
|
<line x1="2" x2="22" y1="12" y2="12" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const imageIcon = (
|
|
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
|
|
|
<rect width="18" height="18" x="3" y="3" rx="2" ry="2" />
|
|
|
|
|
<circle cx="9" cy="9" r="2" />
|
|
|
|
|
<path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const tableIcon = (
|
|
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
|
|
|
<path d="M12 3v18" /><rect width="18" height="18" x="3" y="3" rx="2" /><path d="M3 9h18" /><path d="M3 15h18" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const taskListIcon = (
|
|
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
|
|
|
<rect x="3" y="5" width="6" height="6" rx="1" /><path d="m3 17 2 2 4-4" /><line x1="13" x2="21" y1="6" y2="6" /><line x1="13" x2="21" y1="18" y2="18" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const reportIcon = (
|
|
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
|
|
|
<line x1="12" x2="12" y1="20" y2="10" />
|
|
|
|
|
<line x1="18" x2="18" y1="20" y2="4" />
|
|
|
|
|
<line x1="6" x2="6" y1="20" y2="14" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
|
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
|
|
|
const entryIcon = (
|
|
|
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
|
|
|
<path d="M16 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8Z" />
|
|
|
|
|
<path d="M15 3v4a2 2 0 0 0 2 2h4" />
|
|
|
|
|
<path d="M10 16h4" /><path d="M10 12h4" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
|
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
|
|
|
// --- Build items ---
|
|
|
|
|
|
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
|
|
|
/** Convert a SearchIndexItem to a SlashItem for the @ mention popup. */
|
|
|
|
|
function searchItemToSlashItem(item: SearchIndexItem): SlashItem {
|
|
|
|
|
if (item.kind === "entry") {
|
|
|
|
|
const label = item.label || `(${item.objectName} entry)`;
|
|
|
|
|
return {
|
|
|
|
|
title: label,
|
|
|
|
|
description: item.fields
|
|
|
|
|
? Object.entries(item.fields)
|
|
|
|
|
.slice(0, 2)
|
|
|
|
|
.map(([k, v]) => `${k}: ${v}`)
|
|
|
|
|
.join(" | ")
|
|
|
|
|
: undefined,
|
|
|
|
|
badge: item.objectName,
|
|
|
|
|
icon: entryIcon,
|
|
|
|
|
category: "entry",
|
|
|
|
|
command: ({ editor, range }: { editor: Editor; range: Range }) => {
|
|
|
|
|
const href = buildEntryLink(item.objectName!, item.entryId!);
|
|
|
|
|
editor
|
|
|
|
|
.chain()
|
|
|
|
|
.focus()
|
|
|
|
|
.deleteRange(range)
|
|
|
|
|
.insertContent({
|
|
|
|
|
type: "text",
|
|
|
|
|
text: label,
|
|
|
|
|
marks: [{ type: "link", attrs: { href, target: null } }],
|
|
|
|
|
})
|
|
|
|
|
.run();
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const nodeType = item.nodeType ?? (item.kind === "object" ? "object" : "file");
|
|
|
|
|
const label = item.label || item.path || item.id;
|
|
|
|
|
return {
|
|
|
|
|
title: label,
|
|
|
|
|
description: item.sublabel,
|
|
|
|
|
icon: nodeTypeIcon(nodeType),
|
|
|
|
|
category: "file",
|
|
|
|
|
command: ({ editor, range }: { editor: Editor; range: Range }) => {
|
|
|
|
|
const href = buildFileLink(item.path ?? item.id);
|
|
|
|
|
editor
|
|
|
|
|
.chain()
|
|
|
|
|
.focus()
|
|
|
|
|
.deleteRange(range)
|
|
|
|
|
.insertContent({
|
|
|
|
|
type: "text",
|
|
|
|
|
text: label,
|
|
|
|
|
marks: [
|
|
|
|
|
{ type: "link", attrs: { href, target: null } },
|
|
|
|
|
],
|
|
|
|
|
})
|
|
|
|
|
.run();
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
function buildBlockCommands(): SlashItem[] {
|
|
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
title: "Heading 1",
|
|
|
|
|
description: "Large section heading",
|
|
|
|
|
icon: headingIcon(1),
|
|
|
|
|
category: "block",
|
|
|
|
|
command: ({ editor, range }) => {
|
|
|
|
|
editor.chain().focus().deleteRange(range).setNode("heading", { level: 1 }).run();
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Heading 2",
|
|
|
|
|
description: "Medium section heading",
|
|
|
|
|
icon: headingIcon(2),
|
|
|
|
|
category: "block",
|
|
|
|
|
command: ({ editor, range }) => {
|
|
|
|
|
editor.chain().focus().deleteRange(range).setNode("heading", { level: 2 }).run();
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Heading 3",
|
|
|
|
|
description: "Small section heading",
|
|
|
|
|
icon: headingIcon(3),
|
|
|
|
|
category: "block",
|
|
|
|
|
command: ({ editor, range }) => {
|
|
|
|
|
editor.chain().focus().deleteRange(range).setNode("heading", { level: 3 }).run();
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Bullet List",
|
|
|
|
|
description: "Unordered list",
|
|
|
|
|
icon: bulletListIcon,
|
|
|
|
|
category: "block",
|
|
|
|
|
command: ({ editor, range }) => {
|
|
|
|
|
editor.chain().focus().deleteRange(range).toggleBulletList().run();
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Numbered List",
|
|
|
|
|
description: "Ordered list",
|
|
|
|
|
icon: orderedListIcon,
|
|
|
|
|
category: "block",
|
|
|
|
|
command: ({ editor, range }) => {
|
|
|
|
|
editor.chain().focus().deleteRange(range).toggleOrderedList().run();
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Task List",
|
|
|
|
|
description: "Checklist with checkboxes",
|
|
|
|
|
icon: taskListIcon,
|
|
|
|
|
category: "block",
|
|
|
|
|
command: ({ editor, range }) => {
|
|
|
|
|
editor.chain().focus().deleteRange(range).toggleTaskList().run();
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Blockquote",
|
|
|
|
|
description: "Quote block",
|
|
|
|
|
icon: blockquoteIcon,
|
|
|
|
|
category: "block",
|
|
|
|
|
command: ({ editor, range }) => {
|
|
|
|
|
editor.chain().focus().deleteRange(range).toggleBlockquote().run();
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Code Block",
|
|
|
|
|
description: "Fenced code block",
|
|
|
|
|
icon: codeBlockIcon,
|
|
|
|
|
category: "block",
|
|
|
|
|
command: ({ editor, range }) => {
|
|
|
|
|
editor.chain().focus().deleteRange(range).toggleCodeBlock().run();
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Horizontal Rule",
|
|
|
|
|
description: "Divider line",
|
|
|
|
|
icon: horizontalRuleIcon,
|
|
|
|
|
category: "block",
|
|
|
|
|
command: ({ editor, range }) => {
|
|
|
|
|
editor.chain().focus().deleteRange(range).setHorizontalRule().run();
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Image",
|
|
|
|
|
description: "Insert image from URL",
|
|
|
|
|
icon: imageIcon,
|
|
|
|
|
category: "block",
|
|
|
|
|
command: ({ editor, range }) => {
|
|
|
|
|
const url = window.prompt("Image URL:");
|
|
|
|
|
if (url) {
|
|
|
|
|
editor.chain().focus().deleteRange(range).setImage({ src: url }).run();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Table",
|
|
|
|
|
description: "Insert a 3x3 table",
|
|
|
|
|
icon: tableIcon,
|
|
|
|
|
category: "block",
|
|
|
|
|
command: ({ editor, range }) => {
|
|
|
|
|
editor
|
|
|
|
|
.chain()
|
|
|
|
|
.focus()
|
|
|
|
|
.deleteRange(range)
|
|
|
|
|
.insertTable({ rows: 3, cols: 3, withHeaderRow: true })
|
|
|
|
|
.run();
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Report Chart",
|
|
|
|
|
description: "Interactive report-json block",
|
|
|
|
|
icon: reportIcon,
|
|
|
|
|
category: "block",
|
|
|
|
|
command: ({ editor, range }) => {
|
|
|
|
|
const template = JSON.stringify(
|
|
|
|
|
{
|
|
|
|
|
version: 1,
|
|
|
|
|
title: "New Report",
|
|
|
|
|
panels: [
|
|
|
|
|
{
|
|
|
|
|
id: "panel-1",
|
|
|
|
|
title: "Chart",
|
|
|
|
|
type: "bar",
|
|
|
|
|
sql: "SELECT 1 as x, 10 as y",
|
|
|
|
|
mapping: { xAxis: "x", yAxis: ["y"] },
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
null,
|
|
|
|
|
2,
|
|
|
|
|
);
|
|
|
|
|
editor
|
|
|
|
|
.chain()
|
|
|
|
|
.focus()
|
|
|
|
|
.deleteRange(range)
|
|
|
|
|
.insertContent({
|
|
|
|
|
type: "reportBlock",
|
|
|
|
|
attrs: { config: template },
|
|
|
|
|
})
|
|
|
|
|
.run();
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// buildFileItems removed -- replaced by searchItemToSlashItem + search index
|
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
|
|
|
|
|
|
|
|
// --- Popup Component ---
|
|
|
|
|
|
|
|
|
|
type CommandListRef = {
|
|
|
|
|
onKeyDown: (props: { event: KeyboardEvent }) => boolean;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type CommandListProps = {
|
|
|
|
|
items: SlashItem[];
|
|
|
|
|
command: (item: SlashItem) => void;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const CommandList = forwardRef<CommandListRef, CommandListProps>(
|
|
|
|
|
({ items, command }, ref) => {
|
|
|
|
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
|
|
|
const listRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setSelectedIndex(0);
|
|
|
|
|
}, [items]);
|
|
|
|
|
|
|
|
|
|
// Scroll selected into view
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const el = listRef.current?.children[selectedIndex] as HTMLElement | undefined;
|
|
|
|
|
el?.scrollIntoView({ block: "nearest" });
|
|
|
|
|
}, [selectedIndex]);
|
|
|
|
|
|
|
|
|
|
const selectItem = useCallback(
|
|
|
|
|
(index: number) => {
|
|
|
|
|
const item = items[index];
|
|
|
|
|
if (item) {
|
|
|
|
|
command(item);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[items, command],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
useImperativeHandle(ref, () => ({
|
|
|
|
|
onKeyDown: ({ event }: { event: KeyboardEvent }) => {
|
|
|
|
|
if (event.key === "ArrowUp") {
|
|
|
|
|
setSelectedIndex((i) => (i + items.length - 1) % items.length);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (event.key === "ArrowDown") {
|
|
|
|
|
setSelectedIndex((i) => (i + 1) % items.length);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (event.key === "Enter") {
|
|
|
|
|
selectItem(selectedIndex);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
},
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
if (items.length === 0) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="slash-cmd-popup">
|
|
|
|
|
<div className="slash-cmd-empty">No results</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="slash-cmd-popup" ref={listRef}>
|
|
|
|
|
{items.map((item, index) => (
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
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
|
|
|
key={`${item.category}-${item.title}-${index}`}
|
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
|
|
|
className={`slash-cmd-item ${index === selectedIndex ? "slash-cmd-item-active" : ""}`}
|
|
|
|
|
onClick={() => selectItem(index)}
|
|
|
|
|
onMouseEnter={() => setSelectedIndex(index)}
|
|
|
|
|
>
|
|
|
|
|
<span className="slash-cmd-item-icon">{item.icon}</span>
|
|
|
|
|
<span className="slash-cmd-item-body">
|
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
|
|
|
<span className="slash-cmd-item-title">
|
|
|
|
|
{item.title}
|
|
|
|
|
{item.badge && (
|
|
|
|
|
<span className="slash-cmd-item-badge">{item.badge}</span>
|
|
|
|
|
)}
|
|
|
|
|
</span>
|
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
|
|
|
{item.description && (
|
|
|
|
|
<span className="slash-cmd-item-desc">{item.description}</span>
|
|
|
|
|
)}
|
|
|
|
|
</span>
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
CommandList.displayName = "CommandList";
|
|
|
|
|
|
|
|
|
|
// --- Floating wrapper that renders into a portal ---
|
|
|
|
|
|
|
|
|
|
function SlashPopupRenderer({
|
|
|
|
|
items,
|
|
|
|
|
command,
|
|
|
|
|
clientRect,
|
|
|
|
|
componentRef,
|
|
|
|
|
}: {
|
|
|
|
|
items: SlashItem[];
|
|
|
|
|
command: (item: SlashItem) => void;
|
2026-02-12 00:29:39 -08:00
|
|
|
clientRect: (() => DOMRect | null) | null | undefined;
|
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
|
|
|
componentRef: React.RefObject<CommandListRef | null>;
|
|
|
|
|
}) {
|
|
|
|
|
const popupRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
|
|
|
|
|
// Position popup near the cursor
|
|
|
|
|
useLayoutEffect(() => {
|
|
|
|
|
if (!popupRef.current || !clientRect) {return;}
|
|
|
|
|
const rect = clientRect();
|
|
|
|
|
if (!rect) {return;}
|
|
|
|
|
const el = popupRef.current;
|
|
|
|
|
el.style.position = "fixed";
|
|
|
|
|
el.style.left = `${rect.left}px`;
|
|
|
|
|
el.style.top = `${rect.bottom + 4}px`;
|
|
|
|
|
el.style.zIndex = "50";
|
|
|
|
|
}, [clientRect, items]);
|
|
|
|
|
|
|
|
|
|
return createPortal(
|
|
|
|
|
<div ref={popupRef}>
|
|
|
|
|
<CommandList ref={componentRef} items={items} command={command} />
|
|
|
|
|
</div>,
|
|
|
|
|
document.body,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Shared suggestion render factory ---
|
|
|
|
|
|
|
|
|
|
function createSuggestionRenderer() {
|
|
|
|
|
return () => {
|
|
|
|
|
let container: HTMLDivElement | null = null;
|
|
|
|
|
let root: ReturnType<typeof import("react-dom/client").createRoot> | null = null;
|
|
|
|
|
const componentRef: React.RefObject<CommandListRef | null> = { current: null };
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
onStart: (props: {
|
|
|
|
|
items: SlashItem[];
|
|
|
|
|
command: (item: SlashItem) => void;
|
2026-02-12 00:29:39 -08:00
|
|
|
clientRect?: (() => DOMRect | null) | null;
|
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
|
|
|
}) => {
|
|
|
|
|
container = document.createElement("div");
|
|
|
|
|
document.body.appendChild(container);
|
|
|
|
|
|
2026-02-16 00:30:13 -08:00
|
|
|
void import("react-dom/client").then(({ createRoot }) => {
|
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
|
|
|
root = createRoot(container!);
|
|
|
|
|
root.render(
|
|
|
|
|
<SlashPopupRenderer
|
|
|
|
|
items={props.items}
|
|
|
|
|
command={props.command}
|
|
|
|
|
clientRect={props.clientRect}
|
|
|
|
|
componentRef={componentRef}
|
|
|
|
|
/>,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
onUpdate: (props: {
|
|
|
|
|
items: SlashItem[];
|
|
|
|
|
command: (item: SlashItem) => void;
|
2026-02-12 00:29:39 -08:00
|
|
|
clientRect?: (() => DOMRect | null) | null;
|
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
|
|
|
}) => {
|
|
|
|
|
root?.render(
|
|
|
|
|
<SlashPopupRenderer
|
|
|
|
|
items={props.items}
|
|
|
|
|
command={props.command}
|
|
|
|
|
clientRect={props.clientRect}
|
|
|
|
|
componentRef={componentRef}
|
|
|
|
|
/>,
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
onKeyDown: (props: { event: KeyboardEvent }) => {
|
|
|
|
|
if (props.event.key === "Escape") {
|
|
|
|
|
root?.unmount();
|
|
|
|
|
container?.remove();
|
|
|
|
|
container = null;
|
|
|
|
|
root = null;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return componentRef.current?.onKeyDown(props) ?? false;
|
|
|
|
|
},
|
|
|
|
|
onExit: () => {
|
|
|
|
|
root?.unmount();
|
|
|
|
|
container?.remove();
|
|
|
|
|
container = null;
|
|
|
|
|
root = null;
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Extension factories ---
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* "/" slash command -- markdown block commands only (headings, lists, code, etc.)
|
|
|
|
|
*/
|
|
|
|
|
export function createSlashCommand() {
|
|
|
|
|
const blockCommands = buildBlockCommands();
|
|
|
|
|
|
|
|
|
|
return Extension.create({
|
|
|
|
|
name: "slashCommand",
|
|
|
|
|
|
|
|
|
|
addOptions() {
|
|
|
|
|
return {
|
|
|
|
|
suggestion: {
|
|
|
|
|
char: "/",
|
|
|
|
|
pluginKey: slashCommandPluginKey,
|
|
|
|
|
startOfLine: false,
|
|
|
|
|
command: ({ editor, range, props: item }: { editor: Editor; range: Range; props: SlashItem }) => {
|
|
|
|
|
item.command({ editor, range });
|
|
|
|
|
},
|
|
|
|
|
items: ({ query }: { query: string }) => {
|
|
|
|
|
const q = query.toLowerCase();
|
|
|
|
|
if (!q) {return blockCommands;}
|
|
|
|
|
return blockCommands.filter(
|
|
|
|
|
(item) =>
|
|
|
|
|
item.title.toLowerCase().includes(q) ||
|
|
|
|
|
(item.description?.toLowerCase().includes(q) ?? false),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
render: createSuggestionRenderer(),
|
|
|
|
|
} satisfies Partial<SuggestionOptions<SlashItem>>,
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
addProseMirrorPlugins() {
|
|
|
|
|
return [
|
|
|
|
|
Suggestion({
|
|
|
|
|
editor: this.editor,
|
|
|
|
|
...this.options.suggestion,
|
|
|
|
|
}),
|
|
|
|
|
];
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
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
|
|
|
* "@" mention command -- unified workspace search (files + objects + entries).
|
|
|
|
|
* Accepts a search function from the useSearchIndex hook for fast fuzzy matching.
|
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
|
|
|
*/
|
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
|
|
|
export function createWorkspaceMention(searchFn: MentionSearchFn) {
|
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
|
|
|
return Extension.create({
|
|
|
|
|
name: "fileMention",
|
|
|
|
|
|
|
|
|
|
addOptions() {
|
|
|
|
|
return {
|
|
|
|
|
suggestion: {
|
|
|
|
|
char: "@",
|
|
|
|
|
pluginKey: fileMentionPluginKey,
|
|
|
|
|
startOfLine: false,
|
|
|
|
|
command: ({ editor, range, props: item }: { editor: Editor; range: Range; props: SlashItem }) => {
|
|
|
|
|
item.command({ editor, range });
|
|
|
|
|
},
|
|
|
|
|
items: ({ query }: { query: string }) => {
|
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
|
|
|
const results = searchFn(query, 15);
|
|
|
|
|
return results.map(searchItemToSlashItem);
|
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
|
|
|
},
|
|
|
|
|
render: createSuggestionRenderer(),
|
|
|
|
|
} satisfies Partial<SuggestionOptions<SlashItem>>,
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
addProseMirrorPlugins() {
|
|
|
|
|
return [
|
|
|
|
|
Suggestion({
|
|
|
|
|
editor: this.editor,
|
|
|
|
|
...this.options.suggestion,
|
|
|
|
|
}),
|
|
|
|
|
];
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
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
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* "@" mention command -- legacy file-only cross-linking (fallback).
|
|
|
|
|
* @deprecated Use createWorkspaceMention with useSearchIndex instead.
|
|
|
|
|
*/
|
|
|
|
|
export function createFileMention(tree: TreeNode[]) {
|
|
|
|
|
function flattenTree(nodes: TreeNode[]): TreeNode[] {
|
|
|
|
|
const result: TreeNode[] = [];
|
|
|
|
|
for (const node of nodes) {
|
|
|
|
|
if (node.type !== "folder") {result.push(node);}
|
|
|
|
|
if (node.children) {result.push(...flattenTree(node.children));}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const flatFiles = flattenTree(tree);
|
|
|
|
|
const searchItems: SearchIndexItem[] = flatFiles.map((node) => ({
|
|
|
|
|
id: node.path,
|
|
|
|
|
label: node.name.replace(/\.md$/, ""),
|
|
|
|
|
sublabel: node.path,
|
|
|
|
|
kind: (node.type === "object" ? "object" : "file") as SearchIndexItem["kind"],
|
|
|
|
|
path: node.path,
|
|
|
|
|
nodeType: node.type as SearchIndexItem["nodeType"],
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
const searchFn: MentionSearchFn = (query, limit = 15) => {
|
|
|
|
|
if (!query) {return searchItems.slice(0, limit);}
|
|
|
|
|
const q = query.toLowerCase();
|
|
|
|
|
return searchItems
|
|
|
|
|
.filter(
|
|
|
|
|
(item) =>
|
|
|
|
|
item.label.toLowerCase().includes(q) ||
|
|
|
|
|
(item.sublabel?.toLowerCase().includes(q) ?? false),
|
|
|
|
|
)
|
|
|
|
|
.slice(0, limit);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return createWorkspaceMention(searchFn);
|
|
|
|
|
}
|