@tangle-network/agent-app 0.32.0 → 0.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ import {
2
+ VaultPane
3
+ } from "./chunk-FNUEUOOZ.js";
4
+ export {
5
+ VaultPane
6
+ };
7
+ //# sourceMappingURL=VaultPane-BQ2MZIMM.js.map
@@ -35,6 +35,9 @@ interface VaultFile {
35
35
  mimeType?: string;
36
36
  /** Object URL for binary/media previews the artifact renderer streams. */
37
37
  blobUrl?: string;
38
+ /** Product-domain passthrough (media metadata, review state, …) the artifact
39
+ * renderer reads. The pane never inspects it. */
40
+ extras?: Record<string, unknown>;
38
41
  }
39
42
  /**
40
43
  * The data seam the product owns. Every method is async and product-backed —
@@ -48,8 +51,9 @@ interface VaultDataPort {
48
51
  readFile(path: string): Promise<VaultFile>;
49
52
  /** Persist `content` to `path`. */
50
53
  writeFile(path: string, content: string): Promise<void>;
51
- /** Create a new (empty or scaffolded) file at `path`. */
52
- createFile(path: string): Promise<void>;
54
+ /** Create a new file at `path`; returns the CANONICAL path actually created
55
+ * (the port may normalize, e.g. append an extension) so the pane opens it. */
56
+ createFile(path: string): Promise<string>;
53
57
  /** Delete the file at `path`. */
54
58
  deleteFile(path: string): Promise<void>;
55
59
  }
@@ -66,6 +70,11 @@ type VaultRichParts = unknown;
66
70
  * dirtiness against the saved content via `serialize`. Default: identity
67
71
  * passthrough (the rich draft IS the raw string) so source mode works without a
68
72
  * codec and there's no markdown dependency in the shell.
73
+ *
74
+ * CONTRACT: `parse`/`serialize` MUST be exact inverses — `serialize(parse(raw))
75
+ * === raw` for ALL content, INCLUDING the empty / no-metadata case. The pane
76
+ * uses that round-trip to detect edits, so a non-inverse opens files false-dirty
77
+ * and a save corrupts them.
69
78
  */
70
79
  interface VaultMarkdownCodec {
71
80
  parse(raw: string): VaultRichParts;
@@ -81,6 +90,20 @@ interface VaultTreeRenderProps {
81
90
  interface VaultArtifactRenderProps {
82
91
  file: VaultFile | null;
83
92
  loading: boolean;
93
+ /** Current editor mode. In 'rich' mode the product MAY host a WYSIWYG editor
94
+ * wired to `richDraft` + `onRichChange` + `onSave`; else render a read preview. */
95
+ mode: VaultEditorMode;
96
+ /** Whether editing is allowed (mirrors VaultPaneProps.canWrite). */
97
+ canWrite: boolean;
98
+ /** The live rich draft (the codec's parsed form). The product's rich editor
99
+ * edits this; reverting to the saved content clears dirtiness. */
100
+ richDraft: VaultRichParts;
101
+ /** Whether the draft has unsaved edits. */
102
+ dirty: boolean;
103
+ /** The product's rich editor reports edits here (updates draft + dirtiness). */
104
+ onRichChange: (parts: VaultRichParts) => void;
105
+ /** Persist the current draft through the data port. */
106
+ onSave: () => void;
84
107
  }
85
108
  /** Props the pane passes to the product's optional dock renderer (e.g. an agent dock). */
86
109
  interface VaultDockRenderProps {
@@ -327,6 +327,10 @@ function VaultPane(props) {
327
327
  setSourceDraft(next);
328
328
  setIsDirty(next !== savedContentRef.current);
329
329
  }, []);
330
+ const onRichChange = useCallback((next) => {
331
+ setRichDraft(next);
332
+ setIsDirty(activeCodec.serialize(next) !== savedContentRef.current);
333
+ }, [activeCodec]);
330
334
  const saveCurrent = useCallback(async () => {
331
335
  if (!selectedFile) return;
332
336
  const content = editorMode === "source" ? sourceDraft : activeCodec.serialize(richDraft);
@@ -347,11 +351,11 @@ function VaultPane(props) {
347
351
  if (!trimmed) return;
348
352
  setCreating(true);
349
353
  try {
350
- await port.createFile(trimmed);
354
+ const created = await port.createFile(trimmed);
351
355
  setCreateOpen(false);
352
356
  setNewPath("");
353
357
  await refresh();
354
- commitPath(trimmed);
358
+ commitPath(created);
355
359
  } finally {
356
360
  setCreating(false);
357
361
  }
@@ -479,7 +483,16 @@ function VaultPane(props) {
479
483
  onChange: onSourceChange,
480
484
  onSave: () => void saveCurrent()
481
485
  }
482
- ) : selectedFile ? renderArtifact({ file: selectedFile, loading: false }) : /* @__PURE__ */ jsx2(EmptyState, {}) })
486
+ ) : selectedFile ? renderArtifact({
487
+ file: selectedFile,
488
+ loading: false,
489
+ mode: editorMode,
490
+ canWrite,
491
+ richDraft,
492
+ dirty: isDirty,
493
+ onRichChange,
494
+ onSave: () => void saveCurrent()
495
+ }) : /* @__PURE__ */ jsx2(EmptyState, {}) })
483
496
  ] }),
484
497
  renderDock && selectedFile && renderDock({
485
498
  file: selectedFile,
@@ -581,4 +594,4 @@ export {
581
594
  ConfirmDialog,
582
595
  VaultPane
583
596
  };
584
- //# sourceMappingURL=chunk-SMVD76VC.js.map
597
+ //# sourceMappingURL=chunk-FNUEUOOZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/vault/VaultPane.tsx","../src/vault/ConfirmDialog.tsx"],"sourcesContent":["/**\n * The shared 3-pane vault: tree | artifact viewer | optional agent dock. This is\n * pure shell MECHANISM — selection, the dirty-guard + pending-nav state machine,\n * rich/source editor modes, create/delete/refresh, skeletons, an error boundary,\n * and an empty state. It renders NO file tree and NO artifact viewer of its own:\n * those arrive through the `renderTree` / `renderArtifact` / `renderDock` seams,\n * so a product wires sandbox-ui's RichFileTree + FileArtifactPane in ~10 lines.\n *\n * Data flows exclusively through `port` (a `VaultDataPort`). The pane never\n * imports a fetch client, a router, a toast system, or a markdown library — the\n * optional `codec` seam supplies rich/source parsing (identity passthrough by\n * default). Chrome uses the shared theme tokens (bg-card, border-border, …).\n */\n\nimport {\n Component,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ErrorInfo,\n type ReactNode,\n} from 'react'\nimport { ConfirmDialog } from './ConfirmDialog'\nimport type {\n VaultEditorMode,\n VaultFile,\n VaultMarkdownCodec,\n VaultPaneProps,\n VaultRichParts,\n VaultTreeNode,\n} from './contracts'\n\nconst IDENTITY_CODEC: VaultMarkdownCodec = {\n parse: (raw) => raw,\n serialize: (parts) => (typeof parts === 'string' ? parts : String(parts ?? '')),\n}\n\ntype PendingNav = { type: 'open'; path: string } | { type: 'close' } | null\n\nfunction collectFilePaths(nodes: VaultTreeNode[], into: Set<string>): Set<string> {\n for (const node of nodes) {\n if (node.type === 'file') into.add(node.path)\n if (node.children) collectFilePaths(node.children, into)\n }\n return into\n}\n\nfunction countFiles(nodes: VaultTreeNode[]): number {\n return nodes.reduce(\n (sum, node) => (node.type === 'file' ? sum + 1 : sum + countFiles(node.children ?? [])),\n 0,\n )\n}\n\nclass EditorErrorBoundary extends Component<{ children: ReactNode; onReset?: () => void }, { error: unknown }> {\n state: { error: unknown } = { error: null }\n static getDerivedStateFromError(error: unknown) {\n return { error }\n }\n componentDidCatch(error: unknown, info: ErrorInfo) {\n console.error('Vault crashed:', error, info)\n }\n render() {\n if (this.state.error) {\n const msg = this.state.error instanceof Error\n ? this.state.error.message\n : typeof this.state.error === 'string'\n ? this.state.error\n : 'Something went wrong loading the vault'\n return (\n <div className=\"flex h-full flex-1 flex-col items-center justify-center p-8 text-center\">\n <h3 className=\"mb-1 text-sm font-medium text-foreground\">Vault failed to load</h3>\n <p className=\"mb-4 max-w-xs text-xs text-muted-foreground\">{String(msg)}</p>\n <button\n type=\"button\"\n onClick={() => { this.setState({ error: null }); this.props.onReset?.() }}\n className=\"inline-flex h-8 items-center rounded-md border border-border px-3 text-xs font-medium text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/60\"\n >\n Try again\n </button>\n </div>\n )\n }\n return this.props.children\n }\n}\n\nfunction TreeSkeleton() {\n return (\n <div className=\"space-y-2 p-4\" aria-hidden=\"true\">\n {[32, 48, 40, 52].map((w, i) => (\n <div key={i} className=\"h-4 animate-pulse rounded bg-muted\" style={{ width: `${w * 4}px` }} />\n ))}\n </div>\n )\n}\n\nfunction EditorSkeleton() {\n return (\n <div className=\"space-y-3 p-8\" aria-hidden=\"true\">\n <div className=\"h-5 w-1/2 animate-pulse rounded bg-muted\" />\n <div className=\"h-4 w-full animate-pulse rounded bg-muted\" />\n <div className=\"h-4 w-3/4 animate-pulse rounded bg-muted\" />\n <div className=\"h-4 w-2/3 animate-pulse rounded bg-muted\" />\n </div>\n )\n}\n\nfunction EmptyState() {\n return (\n <div className=\"flex h-full flex-col items-center justify-center p-8 text-center\">\n <h3 className=\"text-sm font-medium text-foreground\">Open a vault document</h3>\n <p className=\"mt-1 max-w-xs text-xs text-muted-foreground\">\n Select a file from the directory, or create a new one.\n </p>\n </div>\n )\n}\n\nexport function VaultPane(props: VaultPaneProps) {\n const {\n port,\n renderTree,\n renderArtifact,\n renderDock,\n canWrite = true,\n selectedPath: controlledPath,\n onSelectedPathChange,\n codec,\n className,\n } = props\n\n const activeCodec = codec ?? IDENTITY_CODEC\n const controlled = controlledPath !== undefined\n const isMarkdownCapable = codec !== undefined\n\n const [tree, setTree] = useState<VaultTreeNode[]>([])\n const [treeLoading, setTreeLoading] = useState(true)\n const [internalPath, setInternalPath] = useState<string | null>(null)\n const selectedPath = controlled ? (controlledPath ?? null) : internalPath\n\n const [selectedFile, setSelectedFile] = useState<VaultFile | null>(null)\n const [fileLoading, setFileLoading] = useState(false)\n const [saving, setSaving] = useState(false)\n\n const [editorMode, setEditorMode] = useState<VaultEditorMode>('rich')\n const [richDraft, setRichDraft] = useState<VaultRichParts>('')\n const [sourceDraft, setSourceDraft] = useState('')\n const [isDirty, setIsDirty] = useState(false)\n\n const [createOpen, setCreateOpen] = useState(false)\n const [newPath, setNewPath] = useState('')\n const [creating, setCreating] = useState(false)\n const [deleteOpen, setDeleteOpen] = useState(false)\n const [deleting, setDeleting] = useState(false)\n const [dockOpen, setDockOpen] = useState(false)\n const [pendingNav, setPendingNav] = useState<PendingNav>(null)\n\n const savedContentRef = useRef('')\n const loadedPathRef = useRef<string | null>(null)\n\n const filePaths = useMemo(() => collectFilePaths(tree, new Set<string>()), [tree])\n const treeRoot = useMemo<VaultTreeNode>(\n () => ({ name: 'Vault', path: '', type: 'directory', children: tree }),\n [tree],\n )\n const fileCount = useMemo(() => countFiles(tree), [tree])\n\n const commitPath = useCallback(\n (next: string | null) => {\n if (!controlled) setInternalPath(next)\n onSelectedPathChange?.(next)\n },\n [controlled, onSelectedPathChange],\n )\n\n const refresh = useCallback(async () => {\n setTreeLoading(true)\n try {\n setTree(await port.listTree())\n } finally {\n setTreeLoading(false)\n }\n }, [port])\n\n useEffect(() => {\n void refresh()\n }, [refresh])\n\n useEffect(() => {\n if (!selectedPath) {\n setSelectedFile(null)\n setFileLoading(false)\n loadedPathRef.current = null\n return\n }\n let cancelled = false\n const path = selectedPath\n setFileLoading(true)\n void (async () => {\n try {\n const file = await port.readFile(path)\n if (!cancelled) setSelectedFile(file)\n } catch {\n if (!cancelled) setSelectedFile(null)\n } finally {\n if (!cancelled) setFileLoading(false)\n }\n })()\n return () => {\n cancelled = true\n }\n }, [port, selectedPath])\n\n useEffect(() => {\n if (!selectedFile) {\n loadedPathRef.current = null\n savedContentRef.current = ''\n setRichDraft('')\n setSourceDraft('')\n setEditorMode('rich')\n setIsDirty(false)\n return\n }\n const pathChanged = loadedPathRef.current !== selectedFile.path\n loadedPathRef.current = selectedFile.path\n savedContentRef.current = selectedFile.content\n setRichDraft(activeCodec.parse(selectedFile.content))\n setSourceDraft(selectedFile.content)\n if (pathChanged) setEditorMode('rich')\n setIsDirty(false)\n setDockOpen(false)\n }, [selectedFile, activeCodec])\n\n const guardedOpen = useCallback(\n (path: string) => {\n if (path === selectedPath) return\n if (isDirty) {\n setPendingNav({ type: 'open', path })\n return\n }\n commitPath(path)\n },\n [isDirty, selectedPath, commitPath],\n )\n\n const guardedClose = useCallback(() => {\n if (isDirty) {\n setPendingNav({ type: 'close' })\n return\n }\n commitPath(null)\n setSelectedFile(null)\n }, [isDirty, commitPath])\n\n const confirmDiscard = useCallback(() => {\n const nav = pendingNav\n setPendingNav(null)\n setIsDirty(false)\n if (!nav) return\n if (nav.type === 'open') {\n commitPath(nav.path)\n } else {\n commitPath(null)\n setSelectedFile(null)\n }\n }, [pendingNav, commitPath])\n\n const showRichMode = useCallback(() => {\n setEditorMode((mode) => {\n if (mode === 'rich') return mode\n setRichDraft(activeCodec.parse(sourceDraft))\n setIsDirty(sourceDraft !== savedContentRef.current)\n return 'rich'\n })\n }, [activeCodec, sourceDraft])\n\n const showSourceMode = useCallback(() => {\n setEditorMode((mode) => {\n if (mode === 'source') return mode\n const content = isDirty ? activeCodec.serialize(richDraft) : savedContentRef.current\n setSourceDraft(content)\n setIsDirty(content !== savedContentRef.current)\n return 'source'\n })\n }, [activeCodec, isDirty, richDraft])\n\n const onSourceChange = useCallback((next: string) => {\n setSourceDraft(next)\n setIsDirty(next !== savedContentRef.current)\n }, [])\n\n const onRichChange = useCallback((next: VaultRichParts) => {\n setRichDraft(next)\n setIsDirty(activeCodec.serialize(next) !== savedContentRef.current)\n }, [activeCodec])\n\n const saveCurrent = useCallback(async () => {\n if (!selectedFile) return\n const content = editorMode === 'source' ? sourceDraft : activeCodec.serialize(richDraft)\n setSaving(true)\n try {\n await port.writeFile(selectedFile.path, content)\n savedContentRef.current = content\n setSelectedFile({ ...selectedFile, content })\n setSourceDraft(content)\n setRichDraft(activeCodec.parse(content))\n setIsDirty(false)\n } finally {\n setSaving(false)\n }\n }, [selectedFile, editorMode, sourceDraft, richDraft, activeCodec, port])\n\n const handleCreate = useCallback(async () => {\n const trimmed = newPath.trim()\n if (!trimmed) return\n setCreating(true)\n try {\n const created = await port.createFile(trimmed)\n setCreateOpen(false)\n setNewPath('')\n await refresh()\n commitPath(created)\n } finally {\n setCreating(false)\n }\n }, [newPath, port, refresh, commitPath])\n\n const handleDelete = useCallback(async () => {\n if (!selectedFile) return\n setDeleting(true)\n try {\n await port.deleteFile(selectedFile.path)\n setDeleteOpen(false)\n setIsDirty(false)\n commitPath(null)\n setSelectedFile(null)\n await refresh()\n } finally {\n setDeleting(false)\n }\n }, [selectedFile, port, refresh, commitPath])\n\n return (\n <EditorErrorBoundary onReset={() => { commitPath(null); setSelectedFile(null) }}>\n <div className={`flex min-h-0 flex-1 overflow-hidden ${className ?? ''}`}>\n <div className=\"flex w-[23rem] min-w-[23rem] flex-col border-r border-border bg-background\">\n <div className=\"flex items-center justify-between gap-3 border-b border-border px-4 py-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"text-sm font-semibold text-foreground\">Vault</span>\n <span className=\"text-xs text-muted-foreground\">\n {fileCount} file{fileCount === 1 ? '' : 's'}\n </span>\n </div>\n <div className=\"flex shrink-0 items-center gap-1\">\n <button\n type=\"button\"\n aria-label=\"Refresh vault\"\n onClick={() => void refresh()}\n className=\"inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/60\"\n >\n ↻\n </button>\n {canWrite && (\n <button\n type=\"button\"\n aria-label=\"New vault file\"\n onClick={() => setCreateOpen(true)}\n className=\"inline-flex h-8 w-8 items-center justify-center rounded-md bg-primary text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/60\"\n >\n +\n </button>\n )}\n </div>\n </div>\n <div className=\"flex-1 overflow-y-auto\">\n {treeLoading ? (\n <TreeSkeleton />\n ) : (\n renderTree({\n root: treeRoot,\n selectedPath: selectedPath ?? undefined,\n onSelect: (path) => { if (filePaths.has(path)) guardedOpen(path) },\n })\n )}\n </div>\n </div>\n\n <div className=\"flex min-w-0 flex-1 flex-col overflow-hidden\">\n {selectedFile && (\n <div className=\"flex shrink-0 items-center justify-between border-b border-border bg-card px-4 py-1.5\">\n <span data-vault-path className=\"truncate text-xs text-muted-foreground\">{selectedFile.path}</span>\n <div className=\"flex items-center gap-1\">\n {canWrite && isMarkdownCapable && (\n <div className=\"mr-1 flex items-center gap-1\">\n <button\n type=\"button\"\n aria-label=\"Edit as rich text\"\n aria-pressed={editorMode === 'rich'}\n onClick={showRichMode}\n className={`inline-flex h-7 items-center rounded px-2 text-xs transition-colors ${\n editorMode === 'rich'\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground'\n }`}\n >\n Rich\n </button>\n <button\n type=\"button\"\n aria-label=\"Edit as source\"\n aria-pressed={editorMode === 'source'}\n onClick={showSourceMode}\n className={`inline-flex h-7 items-center rounded px-2 text-xs transition-colors ${\n editorMode === 'source'\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground'\n }`}\n >\n Source\n </button>\n </div>\n )}\n {renderDock && (\n <button\n type=\"button\"\n aria-label=\"Discuss with agent\"\n aria-pressed={dockOpen}\n disabled={isDirty}\n title={isDirty ? 'Save before discussing with agent' : 'Discuss with agent'}\n onClick={() => setDockOpen((v) => !v)}\n className={`inline-flex items-center gap-1 rounded px-2 py-0.5 text-xs transition-colors disabled:pointer-events-none disabled:opacity-40 ${\n dockOpen\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground'\n }`}\n >\n Discuss\n </button>\n )}\n {canWrite && (\n <button\n type=\"button\"\n aria-label=\"Delete this file\"\n title=\"Delete file\"\n onClick={() => setDeleteOpen(true)}\n className=\"p-1 text-muted-foreground transition-colors hover:text-destructive\"\n >\n ✕\n </button>\n )}\n </div>\n </div>\n )}\n <div className=\"flex-1 overflow-hidden\">\n {fileLoading ? (\n <EditorSkeleton />\n ) : selectedFile && canWrite && isMarkdownCapable && editorMode === 'source' ? (\n <SourceEditor\n path={selectedFile.path}\n content={sourceDraft}\n saving={saving}\n dirty={isDirty}\n onChange={onSourceChange}\n onSave={() => void saveCurrent()}\n />\n ) : selectedFile ? (\n renderArtifact({\n file: selectedFile,\n loading: false,\n mode: editorMode,\n canWrite,\n richDraft,\n dirty: isDirty,\n onRichChange,\n onSave: () => void saveCurrent(),\n })\n ) : (\n <EmptyState />\n )}\n </div>\n </div>\n\n {renderDock && selectedFile && renderDock({\n file: selectedFile,\n open: dockOpen,\n onClose: () => setDockOpen(false),\n })}\n\n <ConfirmDialog\n open={createOpen}\n title=\"Create vault file\"\n description=\"Add a new document to this vault.\"\n confirmLabel={creating ? 'Creating…' : 'Create'}\n confirmDisabled={creating || !newPath.trim()}\n onConfirm={() => void handleCreate()}\n onCancel={() => { setCreateOpen(false); setNewPath('') }}\n >\n <input\n value={newPath}\n autoFocus\n onChange={(e) => setNewPath(e.target.value)}\n placeholder=\"e.g. playbooks/new-strategy.md\"\n aria-label=\"New file path\"\n className=\"h-9 w-full rounded-md border border-border bg-background px-3 text-sm text-foreground outline-none placeholder:text-muted-foreground focus-visible:ring-1 focus-visible:ring-primary/60\"\n />\n </ConfirmDialog>\n\n <ConfirmDialog\n open={deleteOpen}\n title=\"Delete file?\"\n description={`This permanently removes ${selectedFile?.path ?? 'this file'} from the vault.`}\n confirmLabel={deleting ? 'Deleting…' : 'Delete file'}\n confirmDisabled={deleting}\n destructive\n onConfirm={() => void handleDelete()}\n onCancel={() => setDeleteOpen(false)}\n />\n\n <ConfirmDialog\n open={pendingNav !== null}\n title=\"Discard unsaved changes?\"\n description=\"Your edits to this document haven't been saved. Continue and lose them?\"\n confirmLabel=\"Discard changes\"\n destructive\n onConfirm={confirmDiscard}\n onCancel={() => setPendingNav(null)}\n />\n </div>\n </EditorErrorBoundary>\n )\n}\n\nfunction SourceEditor({\n path,\n content,\n saving,\n dirty,\n onChange,\n onSave,\n}: {\n path: string\n content: string\n saving: boolean\n dirty: boolean\n onChange: (content: string) => void\n onSave: () => void\n}) {\n return (\n <div className=\"flex h-full min-h-0 flex-col bg-background\">\n <div className=\"flex items-center justify-between gap-2 border-b border-border px-4 py-2\">\n <p className=\"truncate font-mono text-[11px] text-muted-foreground\">{path}</p>\n <div className=\"flex shrink-0 items-center gap-2\">\n <span className=\"rounded-full border border-border bg-background px-2.5 py-1 text-xs text-muted-foreground\">\n {dirty ? 'Unsaved changes' : 'Saved'}\n </span>\n <button\n type=\"button\"\n onClick={onSave}\n disabled={saving || !dirty}\n className=\"inline-flex h-7 items-center rounded-md bg-primary px-2 text-xs font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50\"\n >\n {saving ? 'Saving…' : 'Save'}\n </button>\n </div>\n </div>\n <textarea\n value={content}\n onChange={(event) => onChange(event.target.value)}\n spellCheck={false}\n aria-label=\"Source editor\"\n className=\"min-h-0 flex-1 resize-none border-0 bg-background p-4 font-mono text-sm leading-6 text-foreground outline-none\"\n />\n </div>\n )\n}\n","/**\n * Self-contained confirm dialog — no dialog library. A focus-trapped modal with\n * Esc-to-cancel and Enter-to-confirm, used for create / delete / discard-unsaved\n * flows so the pane carries zero UI-kit dependency.\n */\n\nimport { useEffect, useRef, type ReactNode } from 'react'\n\nexport interface ConfirmDialogProps {\n open: boolean\n title: string\n description?: ReactNode\n confirmLabel?: string\n cancelLabel?: string\n /** Styles the confirm button as a destructive action. */\n destructive?: boolean\n /** Disables the confirm button (e.g. while the action is in flight). */\n confirmDisabled?: boolean\n onConfirm: () => void\n onCancel: () => void\n /** Optional body (e.g. an input field for the create flow). */\n children?: ReactNode\n}\n\nexport function ConfirmDialog({\n open,\n title,\n description,\n confirmLabel = 'Confirm',\n cancelLabel = 'Cancel',\n destructive = false,\n confirmDisabled = false,\n onConfirm,\n onCancel,\n children,\n}: ConfirmDialogProps) {\n const panelRef = useRef<HTMLDivElement>(null)\n const confirmRef = useRef<HTMLButtonElement>(null)\n\n useEffect(() => {\n if (!open) return\n confirmRef.current?.focus()\n }, [open])\n\n if (!open) return null\n\n function onKeyDown(event: React.KeyboardEvent) {\n if (event.key === 'Escape') {\n event.preventDefault()\n onCancel()\n return\n }\n if (event.key === 'Enter' && !confirmDisabled) {\n event.preventDefault()\n onConfirm()\n return\n }\n if (event.key !== 'Tab') return\n const focusable = panelRef.current?.querySelectorAll<HTMLElement>(\n 'button:not([disabled]), input:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n )\n if (!focusable || focusable.length === 0) return\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n const active = document.activeElement as HTMLElement | null\n if (event.shiftKey && active === first) {\n event.preventDefault()\n last?.focus()\n } else if (!event.shiftKey && active === last) {\n event.preventDefault()\n first?.focus()\n }\n }\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\"\n onMouseDown={(e) => { if (e.target === e.currentTarget) onCancel() }}\n >\n <div\n ref={panelRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n onKeyDown={onKeyDown}\n className=\"w-full max-w-md rounded-lg border border-border bg-card p-5 shadow-lg\"\n >\n <h2 className=\"text-sm font-semibold text-foreground\">{title}</h2>\n {description && <p className=\"mt-1.5 text-xs text-muted-foreground\">{description}</p>}\n {children && <div className=\"mt-3\">{children}</div>}\n <div className=\"mt-5 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"inline-flex h-8 items-center rounded-md px-3 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/60\"\n >\n {cancelLabel}\n </button>\n <button\n ref={confirmRef}\n type=\"button\"\n onClick={onConfirm}\n disabled={confirmDisabled}\n className={`inline-flex h-8 items-center rounded-md px-3 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/60 disabled:pointer-events-none disabled:opacity-50 ${\n destructive\n ? 'bg-destructive text-destructive-foreground hover:bg-destructive/90'\n : 'bg-primary text-primary-foreground hover:bg-primary/90'\n }`}\n >\n {confirmLabel}\n </button>\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["useEffect","useRef","jsx","jsxs","useRef","useEffect"]}
@@ -1,4 +1,4 @@
1
- export { V as VaultArtifactRenderProps, a as VaultDataPort, b as VaultDockRenderProps, c as VaultEditorMode, d as VaultFile, e as VaultMarkdownCodec, f as VaultPane, g as VaultPaneProps, h as VaultRichParts, i as VaultTreeNode, j as VaultTreeRenderProps } from '../VaultPane-B8ipCqkB.js';
1
+ export { V as VaultArtifactRenderProps, a as VaultDataPort, b as VaultDockRenderProps, c as VaultEditorMode, d as VaultFile, e as VaultMarkdownCodec, f as VaultPane, g as VaultPaneProps, h as VaultRichParts, i as VaultTreeNode, j as VaultTreeRenderProps } from '../VaultPane-By2l5lol.js';
2
2
  import * as react from 'react';
3
3
  import { ReactNode } from 'react';
4
4
 
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ConfirmDialog,
3
3
  VaultPane
4
- } from "../chunk-SMVD76VC.js";
4
+ } from "../chunk-FNUEUOOZ.js";
5
5
  export {
6
6
  ConfirmDialog,
7
7
  VaultPane
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
- import { f as VaultPane } from '../VaultPane-B8ipCqkB.js';
3
- export { g as VaultPaneProps } from '../VaultPane-B8ipCqkB.js';
2
+ import { f as VaultPane } from '../VaultPane-By2l5lol.js';
3
+ export { g as VaultPaneProps } from '../VaultPane-By2l5lol.js';
4
4
 
5
5
  declare const VaultPaneLazy: react.LazyExoticComponent<typeof VaultPane>;
6
6
 
@@ -1,7 +1,7 @@
1
1
  // src/vault/lazy.tsx
2
2
  import { lazy } from "react";
3
3
  var VaultPaneLazy = lazy(
4
- () => import("../VaultPane-2BRLHKEB.js").then((m) => ({ default: m.VaultPane }))
4
+ () => import("../VaultPane-BQ2MZIMM.js").then((m) => ({ default: m.VaultPane }))
5
5
  );
6
6
  export {
7
7
  VaultPaneLazy
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-app",
3
- "version": "0.32.0",
3
+ "version": "0.33.1",
4
4
  "packageManager": "pnpm@10.33.4",
5
5
  "description": "Application-shell framework for Tangle agent products: a bounded tool loop, the structured agent→app tool side channel, integration-hub client, per-workspace billing, and crypto — composed over the Tangle agent substrate through typed seams.",
6
6
  "keywords": [
@@ -1,7 +0,0 @@
1
- import {
2
- VaultPane
3
- } from "./chunk-SMVD76VC.js";
4
- export {
5
- VaultPane
6
- };
7
- //# sourceMappingURL=VaultPane-2BRLHKEB.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/vault/VaultPane.tsx","../src/vault/ConfirmDialog.tsx"],"sourcesContent":["/**\n * The shared 3-pane vault: tree | artifact viewer | optional agent dock. This is\n * pure shell MECHANISM — selection, the dirty-guard + pending-nav state machine,\n * rich/source editor modes, create/delete/refresh, skeletons, an error boundary,\n * and an empty state. It renders NO file tree and NO artifact viewer of its own:\n * those arrive through the `renderTree` / `renderArtifact` / `renderDock` seams,\n * so a product wires sandbox-ui's RichFileTree + FileArtifactPane in ~10 lines.\n *\n * Data flows exclusively through `port` (a `VaultDataPort`). The pane never\n * imports a fetch client, a router, a toast system, or a markdown library — the\n * optional `codec` seam supplies rich/source parsing (identity passthrough by\n * default). Chrome uses the shared theme tokens (bg-card, border-border, …).\n */\n\nimport {\n Component,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ErrorInfo,\n type ReactNode,\n} from 'react'\nimport { ConfirmDialog } from './ConfirmDialog'\nimport type {\n VaultEditorMode,\n VaultFile,\n VaultMarkdownCodec,\n VaultPaneProps,\n VaultRichParts,\n VaultTreeNode,\n} from './contracts'\n\nconst IDENTITY_CODEC: VaultMarkdownCodec = {\n parse: (raw) => raw,\n serialize: (parts) => (typeof parts === 'string' ? parts : String(parts ?? '')),\n}\n\ntype PendingNav = { type: 'open'; path: string } | { type: 'close' } | null\n\nfunction collectFilePaths(nodes: VaultTreeNode[], into: Set<string>): Set<string> {\n for (const node of nodes) {\n if (node.type === 'file') into.add(node.path)\n if (node.children) collectFilePaths(node.children, into)\n }\n return into\n}\n\nfunction countFiles(nodes: VaultTreeNode[]): number {\n return nodes.reduce(\n (sum, node) => (node.type === 'file' ? sum + 1 : sum + countFiles(node.children ?? [])),\n 0,\n )\n}\n\nclass EditorErrorBoundary extends Component<{ children: ReactNode; onReset?: () => void }, { error: unknown }> {\n state: { error: unknown } = { error: null }\n static getDerivedStateFromError(error: unknown) {\n return { error }\n }\n componentDidCatch(error: unknown, info: ErrorInfo) {\n console.error('Vault crashed:', error, info)\n }\n render() {\n if (this.state.error) {\n const msg = this.state.error instanceof Error\n ? this.state.error.message\n : typeof this.state.error === 'string'\n ? this.state.error\n : 'Something went wrong loading the vault'\n return (\n <div className=\"flex h-full flex-1 flex-col items-center justify-center p-8 text-center\">\n <h3 className=\"mb-1 text-sm font-medium text-foreground\">Vault failed to load</h3>\n <p className=\"mb-4 max-w-xs text-xs text-muted-foreground\">{String(msg)}</p>\n <button\n type=\"button\"\n onClick={() => { this.setState({ error: null }); this.props.onReset?.() }}\n className=\"inline-flex h-8 items-center rounded-md border border-border px-3 text-xs font-medium text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/60\"\n >\n Try again\n </button>\n </div>\n )\n }\n return this.props.children\n }\n}\n\nfunction TreeSkeleton() {\n return (\n <div className=\"space-y-2 p-4\" aria-hidden=\"true\">\n {[32, 48, 40, 52].map((w, i) => (\n <div key={i} className=\"h-4 animate-pulse rounded bg-muted\" style={{ width: `${w * 4}px` }} />\n ))}\n </div>\n )\n}\n\nfunction EditorSkeleton() {\n return (\n <div className=\"space-y-3 p-8\" aria-hidden=\"true\">\n <div className=\"h-5 w-1/2 animate-pulse rounded bg-muted\" />\n <div className=\"h-4 w-full animate-pulse rounded bg-muted\" />\n <div className=\"h-4 w-3/4 animate-pulse rounded bg-muted\" />\n <div className=\"h-4 w-2/3 animate-pulse rounded bg-muted\" />\n </div>\n )\n}\n\nfunction EmptyState() {\n return (\n <div className=\"flex h-full flex-col items-center justify-center p-8 text-center\">\n <h3 className=\"text-sm font-medium text-foreground\">Open a vault document</h3>\n <p className=\"mt-1 max-w-xs text-xs text-muted-foreground\">\n Select a file from the directory, or create a new one.\n </p>\n </div>\n )\n}\n\nexport function VaultPane(props: VaultPaneProps) {\n const {\n port,\n renderTree,\n renderArtifact,\n renderDock,\n canWrite = true,\n selectedPath: controlledPath,\n onSelectedPathChange,\n codec,\n className,\n } = props\n\n const activeCodec = codec ?? IDENTITY_CODEC\n const controlled = controlledPath !== undefined\n const isMarkdownCapable = codec !== undefined\n\n const [tree, setTree] = useState<VaultTreeNode[]>([])\n const [treeLoading, setTreeLoading] = useState(true)\n const [internalPath, setInternalPath] = useState<string | null>(null)\n const selectedPath = controlled ? (controlledPath ?? null) : internalPath\n\n const [selectedFile, setSelectedFile] = useState<VaultFile | null>(null)\n const [fileLoading, setFileLoading] = useState(false)\n const [saving, setSaving] = useState(false)\n\n const [editorMode, setEditorMode] = useState<VaultEditorMode>('rich')\n const [richDraft, setRichDraft] = useState<VaultRichParts>('')\n const [sourceDraft, setSourceDraft] = useState('')\n const [isDirty, setIsDirty] = useState(false)\n\n const [createOpen, setCreateOpen] = useState(false)\n const [newPath, setNewPath] = useState('')\n const [creating, setCreating] = useState(false)\n const [deleteOpen, setDeleteOpen] = useState(false)\n const [deleting, setDeleting] = useState(false)\n const [dockOpen, setDockOpen] = useState(false)\n const [pendingNav, setPendingNav] = useState<PendingNav>(null)\n\n const savedContentRef = useRef('')\n const loadedPathRef = useRef<string | null>(null)\n\n const filePaths = useMemo(() => collectFilePaths(tree, new Set<string>()), [tree])\n const treeRoot = useMemo<VaultTreeNode>(\n () => ({ name: 'Vault', path: '', type: 'directory', children: tree }),\n [tree],\n )\n const fileCount = useMemo(() => countFiles(tree), [tree])\n\n const commitPath = useCallback(\n (next: string | null) => {\n if (!controlled) setInternalPath(next)\n onSelectedPathChange?.(next)\n },\n [controlled, onSelectedPathChange],\n )\n\n const refresh = useCallback(async () => {\n setTreeLoading(true)\n try {\n setTree(await port.listTree())\n } finally {\n setTreeLoading(false)\n }\n }, [port])\n\n useEffect(() => {\n void refresh()\n }, [refresh])\n\n useEffect(() => {\n if (!selectedPath) {\n setSelectedFile(null)\n setFileLoading(false)\n loadedPathRef.current = null\n return\n }\n let cancelled = false\n const path = selectedPath\n setFileLoading(true)\n void (async () => {\n try {\n const file = await port.readFile(path)\n if (!cancelled) setSelectedFile(file)\n } catch {\n if (!cancelled) setSelectedFile(null)\n } finally {\n if (!cancelled) setFileLoading(false)\n }\n })()\n return () => {\n cancelled = true\n }\n }, [port, selectedPath])\n\n useEffect(() => {\n if (!selectedFile) {\n loadedPathRef.current = null\n savedContentRef.current = ''\n setRichDraft('')\n setSourceDraft('')\n setEditorMode('rich')\n setIsDirty(false)\n return\n }\n const pathChanged = loadedPathRef.current !== selectedFile.path\n loadedPathRef.current = selectedFile.path\n savedContentRef.current = selectedFile.content\n setRichDraft(activeCodec.parse(selectedFile.content))\n setSourceDraft(selectedFile.content)\n if (pathChanged) setEditorMode('rich')\n setIsDirty(false)\n setDockOpen(false)\n }, [selectedFile, activeCodec])\n\n const guardedOpen = useCallback(\n (path: string) => {\n if (path === selectedPath) return\n if (isDirty) {\n setPendingNav({ type: 'open', path })\n return\n }\n commitPath(path)\n },\n [isDirty, selectedPath, commitPath],\n )\n\n const guardedClose = useCallback(() => {\n if (isDirty) {\n setPendingNav({ type: 'close' })\n return\n }\n commitPath(null)\n setSelectedFile(null)\n }, [isDirty, commitPath])\n\n const confirmDiscard = useCallback(() => {\n const nav = pendingNav\n setPendingNav(null)\n setIsDirty(false)\n if (!nav) return\n if (nav.type === 'open') {\n commitPath(nav.path)\n } else {\n commitPath(null)\n setSelectedFile(null)\n }\n }, [pendingNav, commitPath])\n\n const showRichMode = useCallback(() => {\n setEditorMode((mode) => {\n if (mode === 'rich') return mode\n setRichDraft(activeCodec.parse(sourceDraft))\n setIsDirty(sourceDraft !== savedContentRef.current)\n return 'rich'\n })\n }, [activeCodec, sourceDraft])\n\n const showSourceMode = useCallback(() => {\n setEditorMode((mode) => {\n if (mode === 'source') return mode\n const content = isDirty ? activeCodec.serialize(richDraft) : savedContentRef.current\n setSourceDraft(content)\n setIsDirty(content !== savedContentRef.current)\n return 'source'\n })\n }, [activeCodec, isDirty, richDraft])\n\n const onSourceChange = useCallback((next: string) => {\n setSourceDraft(next)\n setIsDirty(next !== savedContentRef.current)\n }, [])\n\n const saveCurrent = useCallback(async () => {\n if (!selectedFile) return\n const content = editorMode === 'source' ? sourceDraft : activeCodec.serialize(richDraft)\n setSaving(true)\n try {\n await port.writeFile(selectedFile.path, content)\n savedContentRef.current = content\n setSelectedFile({ ...selectedFile, content })\n setSourceDraft(content)\n setRichDraft(activeCodec.parse(content))\n setIsDirty(false)\n } finally {\n setSaving(false)\n }\n }, [selectedFile, editorMode, sourceDraft, richDraft, activeCodec, port])\n\n const handleCreate = useCallback(async () => {\n const trimmed = newPath.trim()\n if (!trimmed) return\n setCreating(true)\n try {\n await port.createFile(trimmed)\n setCreateOpen(false)\n setNewPath('')\n await refresh()\n commitPath(trimmed)\n } finally {\n setCreating(false)\n }\n }, [newPath, port, refresh, commitPath])\n\n const handleDelete = useCallback(async () => {\n if (!selectedFile) return\n setDeleting(true)\n try {\n await port.deleteFile(selectedFile.path)\n setDeleteOpen(false)\n setIsDirty(false)\n commitPath(null)\n setSelectedFile(null)\n await refresh()\n } finally {\n setDeleting(false)\n }\n }, [selectedFile, port, refresh, commitPath])\n\n return (\n <EditorErrorBoundary onReset={() => { commitPath(null); setSelectedFile(null) }}>\n <div className={`flex min-h-0 flex-1 overflow-hidden ${className ?? ''}`}>\n <div className=\"flex w-[23rem] min-w-[23rem] flex-col border-r border-border bg-background\">\n <div className=\"flex items-center justify-between gap-3 border-b border-border px-4 py-3\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"text-sm font-semibold text-foreground\">Vault</span>\n <span className=\"text-xs text-muted-foreground\">\n {fileCount} file{fileCount === 1 ? '' : 's'}\n </span>\n </div>\n <div className=\"flex shrink-0 items-center gap-1\">\n <button\n type=\"button\"\n aria-label=\"Refresh vault\"\n onClick={() => void refresh()}\n className=\"inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/60\"\n >\n ↻\n </button>\n {canWrite && (\n <button\n type=\"button\"\n aria-label=\"New vault file\"\n onClick={() => setCreateOpen(true)}\n className=\"inline-flex h-8 w-8 items-center justify-center rounded-md bg-primary text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/60\"\n >\n +\n </button>\n )}\n </div>\n </div>\n <div className=\"flex-1 overflow-y-auto\">\n {treeLoading ? (\n <TreeSkeleton />\n ) : (\n renderTree({\n root: treeRoot,\n selectedPath: selectedPath ?? undefined,\n onSelect: (path) => { if (filePaths.has(path)) guardedOpen(path) },\n })\n )}\n </div>\n </div>\n\n <div className=\"flex min-w-0 flex-1 flex-col overflow-hidden\">\n {selectedFile && (\n <div className=\"flex shrink-0 items-center justify-between border-b border-border bg-card px-4 py-1.5\">\n <span data-vault-path className=\"truncate text-xs text-muted-foreground\">{selectedFile.path}</span>\n <div className=\"flex items-center gap-1\">\n {canWrite && isMarkdownCapable && (\n <div className=\"mr-1 flex items-center gap-1\">\n <button\n type=\"button\"\n aria-label=\"Edit as rich text\"\n aria-pressed={editorMode === 'rich'}\n onClick={showRichMode}\n className={`inline-flex h-7 items-center rounded px-2 text-xs transition-colors ${\n editorMode === 'rich'\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground'\n }`}\n >\n Rich\n </button>\n <button\n type=\"button\"\n aria-label=\"Edit as source\"\n aria-pressed={editorMode === 'source'}\n onClick={showSourceMode}\n className={`inline-flex h-7 items-center rounded px-2 text-xs transition-colors ${\n editorMode === 'source'\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground'\n }`}\n >\n Source\n </button>\n </div>\n )}\n {renderDock && (\n <button\n type=\"button\"\n aria-label=\"Discuss with agent\"\n aria-pressed={dockOpen}\n disabled={isDirty}\n title={isDirty ? 'Save before discussing with agent' : 'Discuss with agent'}\n onClick={() => setDockOpen((v) => !v)}\n className={`inline-flex items-center gap-1 rounded px-2 py-0.5 text-xs transition-colors disabled:pointer-events-none disabled:opacity-40 ${\n dockOpen\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground'\n }`}\n >\n Discuss\n </button>\n )}\n {canWrite && (\n <button\n type=\"button\"\n aria-label=\"Delete this file\"\n title=\"Delete file\"\n onClick={() => setDeleteOpen(true)}\n className=\"p-1 text-muted-foreground transition-colors hover:text-destructive\"\n >\n ✕\n </button>\n )}\n </div>\n </div>\n )}\n <div className=\"flex-1 overflow-hidden\">\n {fileLoading ? (\n <EditorSkeleton />\n ) : selectedFile && canWrite && isMarkdownCapable && editorMode === 'source' ? (\n <SourceEditor\n path={selectedFile.path}\n content={sourceDraft}\n saving={saving}\n dirty={isDirty}\n onChange={onSourceChange}\n onSave={() => void saveCurrent()}\n />\n ) : selectedFile ? (\n renderArtifact({ file: selectedFile, loading: false })\n ) : (\n <EmptyState />\n )}\n </div>\n </div>\n\n {renderDock && selectedFile && renderDock({\n file: selectedFile,\n open: dockOpen,\n onClose: () => setDockOpen(false),\n })}\n\n <ConfirmDialog\n open={createOpen}\n title=\"Create vault file\"\n description=\"Add a new document to this vault.\"\n confirmLabel={creating ? 'Creating…' : 'Create'}\n confirmDisabled={creating || !newPath.trim()}\n onConfirm={() => void handleCreate()}\n onCancel={() => { setCreateOpen(false); setNewPath('') }}\n >\n <input\n value={newPath}\n autoFocus\n onChange={(e) => setNewPath(e.target.value)}\n placeholder=\"e.g. playbooks/new-strategy.md\"\n aria-label=\"New file path\"\n className=\"h-9 w-full rounded-md border border-border bg-background px-3 text-sm text-foreground outline-none placeholder:text-muted-foreground focus-visible:ring-1 focus-visible:ring-primary/60\"\n />\n </ConfirmDialog>\n\n <ConfirmDialog\n open={deleteOpen}\n title=\"Delete file?\"\n description={`This permanently removes ${selectedFile?.path ?? 'this file'} from the vault.`}\n confirmLabel={deleting ? 'Deleting…' : 'Delete file'}\n confirmDisabled={deleting}\n destructive\n onConfirm={() => void handleDelete()}\n onCancel={() => setDeleteOpen(false)}\n />\n\n <ConfirmDialog\n open={pendingNav !== null}\n title=\"Discard unsaved changes?\"\n description=\"Your edits to this document haven't been saved. Continue and lose them?\"\n confirmLabel=\"Discard changes\"\n destructive\n onConfirm={confirmDiscard}\n onCancel={() => setPendingNav(null)}\n />\n </div>\n </EditorErrorBoundary>\n )\n}\n\nfunction SourceEditor({\n path,\n content,\n saving,\n dirty,\n onChange,\n onSave,\n}: {\n path: string\n content: string\n saving: boolean\n dirty: boolean\n onChange: (content: string) => void\n onSave: () => void\n}) {\n return (\n <div className=\"flex h-full min-h-0 flex-col bg-background\">\n <div className=\"flex items-center justify-between gap-2 border-b border-border px-4 py-2\">\n <p className=\"truncate font-mono text-[11px] text-muted-foreground\">{path}</p>\n <div className=\"flex shrink-0 items-center gap-2\">\n <span className=\"rounded-full border border-border bg-background px-2.5 py-1 text-xs text-muted-foreground\">\n {dirty ? 'Unsaved changes' : 'Saved'}\n </span>\n <button\n type=\"button\"\n onClick={onSave}\n disabled={saving || !dirty}\n className=\"inline-flex h-7 items-center rounded-md bg-primary px-2 text-xs font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50\"\n >\n {saving ? 'Saving…' : 'Save'}\n </button>\n </div>\n </div>\n <textarea\n value={content}\n onChange={(event) => onChange(event.target.value)}\n spellCheck={false}\n aria-label=\"Source editor\"\n className=\"min-h-0 flex-1 resize-none border-0 bg-background p-4 font-mono text-sm leading-6 text-foreground outline-none\"\n />\n </div>\n )\n}\n","/**\n * Self-contained confirm dialog — no dialog library. A focus-trapped modal with\n * Esc-to-cancel and Enter-to-confirm, used for create / delete / discard-unsaved\n * flows so the pane carries zero UI-kit dependency.\n */\n\nimport { useEffect, useRef, type ReactNode } from 'react'\n\nexport interface ConfirmDialogProps {\n open: boolean\n title: string\n description?: ReactNode\n confirmLabel?: string\n cancelLabel?: string\n /** Styles the confirm button as a destructive action. */\n destructive?: boolean\n /** Disables the confirm button (e.g. while the action is in flight). */\n confirmDisabled?: boolean\n onConfirm: () => void\n onCancel: () => void\n /** Optional body (e.g. an input field for the create flow). */\n children?: ReactNode\n}\n\nexport function ConfirmDialog({\n open,\n title,\n description,\n confirmLabel = 'Confirm',\n cancelLabel = 'Cancel',\n destructive = false,\n confirmDisabled = false,\n onConfirm,\n onCancel,\n children,\n}: ConfirmDialogProps) {\n const panelRef = useRef<HTMLDivElement>(null)\n const confirmRef = useRef<HTMLButtonElement>(null)\n\n useEffect(() => {\n if (!open) return\n confirmRef.current?.focus()\n }, [open])\n\n if (!open) return null\n\n function onKeyDown(event: React.KeyboardEvent) {\n if (event.key === 'Escape') {\n event.preventDefault()\n onCancel()\n return\n }\n if (event.key === 'Enter' && !confirmDisabled) {\n event.preventDefault()\n onConfirm()\n return\n }\n if (event.key !== 'Tab') return\n const focusable = panelRef.current?.querySelectorAll<HTMLElement>(\n 'button:not([disabled]), input:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n )\n if (!focusable || focusable.length === 0) return\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n const active = document.activeElement as HTMLElement | null\n if (event.shiftKey && active === first) {\n event.preventDefault()\n last?.focus()\n } else if (!event.shiftKey && active === last) {\n event.preventDefault()\n first?.focus()\n }\n }\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\"\n onMouseDown={(e) => { if (e.target === e.currentTarget) onCancel() }}\n >\n <div\n ref={panelRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n onKeyDown={onKeyDown}\n className=\"w-full max-w-md rounded-lg border border-border bg-card p-5 shadow-lg\"\n >\n <h2 className=\"text-sm font-semibold text-foreground\">{title}</h2>\n {description && <p className=\"mt-1.5 text-xs text-muted-foreground\">{description}</p>}\n {children && <div className=\"mt-3\">{children}</div>}\n <div className=\"mt-5 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"inline-flex h-8 items-center rounded-md px-3 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/60\"\n >\n {cancelLabel}\n </button>\n <button\n ref={confirmRef}\n type=\"button\"\n onClick={onConfirm}\n disabled={confirmDisabled}\n className={`inline-flex h-8 items-center rounded-md px-3 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/60 disabled:pointer-events-none disabled:opacity-50 ${\n destructive\n ? 'bg-destructive text-destructive-foreground hover:bg-destructive/90'\n : 'bg-primary text-primary-foreground hover:bg-primary/90'\n }`}\n >\n {confirmLabel}\n </button>\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["useEffect","useRef","jsx","jsxs","useRef","useEffect"]}