@workerdeck/ui 2.9.0 → 2.10.0

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.
Files changed (34) hide show
  1. package/build/index.d.mts +52 -6
  2. package/build/index.mjs +23 -9
  3. package/build/index.mjs.map +1 -1
  4. package/build/scoped.css +20 -0
  5. package/build/{SessionPanel-BClL7zUS.mjs → use-path-links-CvcxdUXL.mjs} +437 -135
  6. package/build/use-path-links-CvcxdUXL.mjs.map +1 -0
  7. package/build/workspace.mjs +3 -83
  8. package/build/workspace.mjs.map +1 -1
  9. package/package.json +4 -4
  10. package/src/components/agent/Composer.tsx +7 -4
  11. package/src/components/agent/PromptTokenText.tsx +11 -3
  12. package/src/components/agent/SessionInfoDialog.tsx +1 -1
  13. package/src/components/agent/SessionItem.tsx +9 -5
  14. package/src/components/agent/SessionList.tsx +1 -1
  15. package/src/components/agent/SessionPanel.tsx +4 -1
  16. package/src/components/agent/StatusBar.tsx +1 -1
  17. package/src/components/agent/Transcript.tsx +83 -79
  18. package/src/components/agent/TranscriptItemView.tsx +1 -1
  19. package/src/components/agent/UsageDialog.tsx +87 -6
  20. package/src/components/agent/composer-commands.ts +14 -2
  21. package/src/components/agent/use-path-links.ts +5 -19
  22. package/src/components/prompt-area/clipboard-helpers.ts +13 -4
  23. package/src/components/prompt-area/dom-helpers.ts +10 -1
  24. package/src/components/prompt-area/index.ts +10 -1
  25. package/src/components/prompt-area/prompt-area-engine.ts +23 -14
  26. package/src/components/prompt-area/types.ts +20 -0
  27. package/src/components/prompt-area/use-prompt-area-keydown.ts +2 -1
  28. package/src/components/prompt-area/use-prompt-area.ts +8 -2
  29. package/src/components/terminal/StatusLine.tsx +3 -2
  30. package/src/components/terminal/items.tsx +3 -2
  31. package/src/index.ts +3 -1
  32. package/src/lib/path-match.ts +51 -0
  33. package/src/lib/session-cost.ts +65 -0
  34. package/build/SessionPanel-BClL7zUS.mjs.map +0 -1
@@ -1,4 +1,4 @@
1
- import { $t as Input, At as Tip, Dt as Spinner, Mt as TooltipProvider, R as EntryIcon, Tt as Splitter, X as parseFileLink, in as cn, nn as Button, t as SessionPanel } from "./SessionPanel-BClL7zUS.mjs";
1
+ import { At as Spinner, B as EntryIcon, Ft as TooltipProvider, Nt as Tip, Ot as Splitter, an as Button, i as SessionPanel, n as usePathLinks, nn as Input, sn as cn, t as resolveAgainstCwd } from "./use-path-links-CvcxdUXL.mjs";
2
2
  import { m as formatBytes } from "./status-kmsBhuFG.mjs";
3
3
  import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
4
4
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -573,87 +573,6 @@ function Centred({ children }) {
573
573
  });
574
574
  }
575
575
  //#endregion
576
- //#region src/components/agent/use-path-links.ts
577
- const LINKISH = "wd-path-link";
578
- const PATH_RE = /^(\/[^\s:'"`()[\]{}]+|[\w.@~-]+(?:\/[\w.@~-]+)+)(?::(\d+))?$/;
579
- const FILE_RE = /^([\w.@-]+\.[A-Za-z0-9]{1,10})(?::(\d+))?$/;
580
- const COVERAGE = .6;
581
- function matchPath(text, inCode) {
582
- const trimmed = text.trim();
583
- if (!trimmed || trimmed.endsWith("/")) return;
584
- const hit = PATH_RE.exec(trimmed) ?? (inCode ? FILE_RE.exec(trimmed) : null);
585
- if (!hit) return;
586
- return {
587
- path: hit[1],
588
- line: hit[2] ? Number(hit[2]) : void 0
589
- };
590
- }
591
- function hitFor(element) {
592
- if (!element) return;
593
- const inCode = element.closest("code") !== null;
594
- const text = element.textContent ?? "";
595
- const hit = matchPath(text, inCode);
596
- if (!hit) return;
597
- if (!inCode && hit.path.length < text.trim().length * COVERAGE) return;
598
- return hit;
599
- }
600
- function usePathLinks({ container, onOpen, enabled = true, ignore }) {
601
- useEffect(() => {
602
- const root = container.current;
603
- if (!root || !enabled) return;
604
- const excluded = (element) => ignore !== void 0 && element?.closest(ignore) !== null && element !== void 0;
605
- let marked;
606
- const unmark = () => {
607
- marked?.classList.remove(LINKISH);
608
- marked = void 0;
609
- };
610
- const onClick = (event) => {
611
- if (!event.metaKey && !event.ctrlKey) return;
612
- const target = event.target instanceof HTMLElement ? event.target : void 0;
613
- if (excluded(target)) return;
614
- const hit = hitFor(target);
615
- if (!hit) return;
616
- event.preventDefault();
617
- event.stopPropagation();
618
- unmark();
619
- onOpen(hit);
620
- };
621
- const onMove = (event) => {
622
- if (!event.metaKey && !event.ctrlKey) return unmark();
623
- const element = event.target instanceof HTMLElement ? event.target : void 0;
624
- if (element === marked) return;
625
- unmark();
626
- if (!element || excluded(element) || !hitFor(element)) return;
627
- marked = element;
628
- element.classList.add(LINKISH);
629
- };
630
- const onKey = (event) => {
631
- if (!event.metaKey && !event.ctrlKey) unmark();
632
- };
633
- root.addEventListener("click", onClick, true);
634
- root.addEventListener("mousemove", onMove);
635
- document.addEventListener("keydown", onKey);
636
- document.addEventListener("keyup", onKey);
637
- window.addEventListener("blur", unmark);
638
- return () => {
639
- unmark();
640
- root.removeEventListener("click", onClick, true);
641
- root.removeEventListener("mousemove", onMove);
642
- document.removeEventListener("keydown", onKey);
643
- document.removeEventListener("keyup", onKey);
644
- window.removeEventListener("blur", unmark);
645
- };
646
- }, [
647
- container,
648
- onOpen,
649
- enabled,
650
- ignore
651
- ]);
652
- }
653
- function resolveAgainstCwd(path, cwd) {
654
- return parseFileLink(path, cwd)?.path ?? path;
655
- }
656
- //#endregion
657
576
  //#region src/components/agent/SessionWorkspace.tsx
658
577
  const RAIL_MIN = 180;
659
578
  const RAIL_MAX = 520;
@@ -700,9 +619,10 @@ function SessionWorkspace({ client, sessionId, header, transcriptVariant, transc
700
619
  }, [files]);
701
620
  const column = useRef(null);
702
621
  const columnHeight = useElementHeight(column);
622
+ const openPath = useCallback(({ path }) => files.open(resolveAgainstCwd(path, cwd)), [files.open, cwd]);
703
623
  usePathLinks({
704
624
  container: column,
705
- onOpen: useCallback(({ path }) => files.open(resolveAgainstCwd(path, cwd)), [files.open, cwd]),
625
+ onOpen: openPath,
706
626
  enabled: tree.available,
707
627
  ignore: ".monaco-editor"
708
628
  });
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.mjs","names":[],"sources":["../src/components/agent/EditorTabs.tsx","../src/components/agent/FileTree.tsx","../src/components/agent/CodeEditor.tsx","../src/components/agent/FileViewer.tsx","../src/components/agent/use-path-links.ts","../src/components/agent/SessionWorkspace.tsx"],"sourcesContent":["import { useEffect, useRef, type MouseEvent as ReactMouseEvent } from 'react'\nimport { isDirty, type OpenFile } from '@workerdeck/react'\nimport { X } from 'lucide-react'\nimport { cn } from '../../lib/utils.ts'\nimport { Tip, TooltipProvider } from '../ui/Tooltip.tsx'\nimport { formatBytes } from '../../lib/format.ts'\n\nexport interface EditorTabsProps {\n files: OpenFile[]\n activePath?: string\n onActivate: (path: string) => void\n onClose: (path: string) => void\n className?: string\n}\n\nexport function EditorTabs({ files, activePath, onActivate, onClose, className }: EditorTabsProps) {\n return (\n <TooltipProvider delay={500} closeDelay={0}>\n <div\n data-slot=\"editor-tabs\"\n role=\"tablist\"\n aria-label=\"Open files\"\n className={cn('flex shrink-0 items-stretch overflow-x-auto border-b border-border bg-surface', className)}\n >\n {files.map((file) => (\n <Tab\n key={file.path}\n file={file}\n active={file.path === activePath}\n onActivate={() => onActivate(file.path)}\n onClose={() => onClose(file.path)}\n onArrow={(direction) => {\n const index = files.findIndex((f) => f.path === file.path)\n const next = files[index + direction]\n if (next) {\n onActivate(next.path)\n }\n }}\n />\n ))}\n </div>\n </TooltipProvider>\n )\n}\n\nfunction Tab({\n file,\n active,\n onActivate,\n onClose,\n onArrow,\n}: {\n file: OpenFile\n active: boolean\n onActivate: () => void\n onClose: () => void\n onArrow: (direction: 1 | -1) => void\n}) {\n const dirty = isDirty(file)\n const ref = useRef<HTMLDivElement>(null)\n useEffect(() => {\n if (active) {\n ref.current?.scrollIntoView({ block: 'nearest', inline: 'nearest' })\n }\n }, [active])\n\n const onAuxClick = (event: ReactMouseEvent) => {\n if (event.button !== 1) {\n return\n }\n event.preventDefault()\n onClose()\n }\n\n return (\n <Tip\n side=\"bottom\"\n render={\n <div\n ref={ref}\n role=\"tab\"\n tabIndex={active ? 0 : -1}\n aria-selected={active}\n onClick={onActivate}\n onAuxClick={onAuxClick}\n onKeyDown={(event) => {\n if (event.key === 'ArrowRight') {\n onArrow(1)\n } else if (event.key === 'ArrowLeft') {\n onArrow(-1)\n } else if (event.key === 'Enter' || event.key === ' ') {\n onActivate()\n } else {\n return\n }\n event.preventDefault()\n }}\n className={cn(\n 'group flex min-w-0 shrink-0 cursor-pointer items-center gap-1.5 border-r border-border px-3 py-1.5 transition-colors',\n 'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent focus-visible:ring-inset',\n active ? 'bg-bg text-fg-1' : 'text-fg-3 hover:bg-surface-hover hover:text-fg-2',\n )}\n />\n }\n content={\n <span className=\"flex flex-col gap-0.5\">\n <span className=\"font-mono break-all\">{file.path}</span>\n {file.bytes !== undefined ? <span className=\"text-fg-4\">{formatBytes(file.bytes)}</span> : null}\n </span>\n }\n >\n <span className={cn('max-w-40 truncate font-mono text-label', dirty && 'italic')}>{file.name}</span>\n {file.status === 'error' ? <span className=\"shrink-0 text-danger\">!</span> : null}\n <button\n type=\"button\"\n aria-label={dirty ? `Close ${file.name} (unsaved changes)` : `Close ${file.name}`}\n onClick={(event) => {\n event.stopPropagation()\n onClose()\n }}\n className={cn(\n 'shrink-0 rounded p-0.5 text-fg-4 transition-opacity hover:bg-surface-hover hover:text-fg-1',\n active || dirty ? 'opacity-70' : 'opacity-0 group-hover:opacity-70 focus-visible:opacity-100',\n )}\n >\n <span className={cn('block', dirty && 'group-hover:hidden')}>\n {dirty ? <span className=\"block size-3 rounded-full bg-fg-2\" /> : <X className=\"size-3\" />}\n </span>\n {dirty ? (\n <span className=\"hidden group-hover:block\">\n <X className=\"size-3\" />\n </span>\n ) : null}\n </button>\n </Tip>\n )\n}\n","import { useEffect, useRef, useState, type CSSProperties, type ReactNode } from 'react'\nimport type { UseHostFileSearchResult, UseHostFileTreeResult } from '@workerdeck/react'\nimport type { HostFileMatch } from '@workerdeck/protocol'\nimport { ChevronRight, File, Folder, FolderOpen, PanelLeftClose, RefreshCw, Search, X } from 'lucide-react'\nimport { cn } from '../../lib/utils.ts'\nimport { Button } from '../ui/Button.tsx'\nimport { Input } from '../ui/Input.tsx'\nimport { Spinner } from '../ui/Spinner.tsx'\nimport { EntryIcon } from './EntryIcon.tsx'\n\nexport interface FileTreeProps {\n tree: UseHostFileTreeResult\n search?: UseHostFileSearchResult\n activePath?: string\n onOpenFile: (path: string) => void\n onCollapse?: () => void\n style?: CSSProperties\n className?: string\n}\n\nconst INDENT = 12\n\nexport function FileTree({ tree, search, activePath, onOpenFile, onCollapse, style, className }: FileTreeProps) {\n const [query, setQuery] = useState('')\n const [matches, setMatches] = useState<HostFileMatch[] | undefined>()\n const searching = query.trim().length > 0\n\n useEffect(() => {\n if (!search?.available) {\n return\n }\n const q = query.trim()\n if (!q) {\n setMatches(undefined)\n return\n }\n const controller = new AbortController()\n const timer = setTimeout(() => {\n void search.search(q, { limit: 60, signal: controller.signal }).then((found) => {\n if (!controller.signal.aborted) {\n setMatches(found)\n }\n })\n }, 150)\n return () => {\n controller.abort()\n clearTimeout(timer)\n }\n }, [search, query])\n\n return (\n <div data-slot=\"file-tree\" style={style} className={cn('flex min-h-0 min-w-0 flex-col bg-surface', className)}>\n <div className=\"flex items-center gap-1 px-2 pt-2\">\n {search?.available ? (\n <div className=\"relative min-w-0 flex-1\">\n <Search className=\"absolute top-1/2 left-2 size-3.5 -translate-y-1/2 text-fg-4\" />\n <Input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search files…\"\n className=\"h-7 pr-7 pl-7 text-label\"\n spellCheck={false}\n />\n {searching ? (\n <button\n type=\"button\"\n onClick={() => setQuery('')}\n aria-label=\"Clear search\"\n className=\"absolute top-1/2 right-1.5 -translate-y-1/2 text-fg-4 transition-colors hover:text-fg-2\"\n >\n <X className=\"size-3.5\" />\n </button>\n ) : null}\n </div>\n ) : (\n <span className=\"min-w-0 flex-1 truncate px-1 text-label text-fg-4\">{tree.root}</span>\n )}\n <Button variant=\"ghost\" size=\"icon-sm\" aria-label=\"Refresh the file tree\" onClick={() => tree.refresh()}>\n <RefreshCw className=\"size-3.5 text-fg-3\" />\n </Button>\n {onCollapse ? (\n <Button variant=\"ghost\" size=\"icon-sm\" aria-label=\"Hide project files\" onClick={onCollapse}>\n <PanelLeftClose className=\"size-3.5 text-fg-3\" />\n </Button>\n ) : null}\n </div>\n\n <div className=\"min-h-0 flex-1 overflow-auto px-1 py-1\">\n {tree.error ? (\n <p className=\"px-2 py-3 text-body-sm text-danger\">{tree.error}</p>\n ) : searching ? (\n matches === undefined ? (\n <div className=\"py-6 text-center\">\n <Spinner className=\"size-4 text-fg-4\" />\n </div>\n ) : matches.length === 0 ? (\n <p className=\"px-2 py-6 text-center text-body-sm text-fg-4\">No matching files.</p>\n ) : (\n <ul>\n {matches.map((match) => (\n <li key={match.path}>\n <Row\n label={fileName(match.relative)}\n detail={directoryOf(match.relative)}\n title={match.relative}\n icon={<File className=\"size-3.5 shrink-0 text-fg-4\" />}\n active={match.path === activePath}\n onClick={() => onOpenFile(match.path)}\n />\n </li>\n ))}\n </ul>\n )\n ) : tree.loading ? (\n <div className=\"py-6 text-center\">\n <Spinner className=\"size-4 text-fg-4\" />\n </div>\n ) : tree.rows.length === 0 ? (\n <p className=\"px-2 py-6 text-center text-body-sm text-fg-4\">This project is empty.</p>\n ) : (\n <ul role=\"tree\" aria-label=\"Project files\">\n {tree.rows.map((row) => (\n <li key={row.entry.path} role=\"treeitem\" aria-expanded={row.expanded} aria-level={row.depth + 1}>\n <Row\n label={row.entry.name}\n indent={row.depth}\n active={row.entry.path === activePath}\n icon={\n row.entry.type === 'dir' ? (\n row.loading ? (\n <Spinner className=\"size-3.5 shrink-0 text-fg-4\" />\n ) : row.expanded ? (\n <FolderOpen className=\"size-3.5 shrink-0 text-accent\" />\n ) : (\n <Folder className=\"size-3.5 shrink-0 text-accent\" />\n )\n ) : (\n <EntryIcon type={row.entry.type} />\n )\n }\n chevron={\n row.entry.type === 'dir' ? (\n <ChevronRight className={cn('size-3 shrink-0 text-fg-4 transition-transform', row.expanded && 'rotate-90')} />\n ) : undefined\n }\n onClick={() => (row.entry.type === 'dir' ? tree.toggle(row.entry.path) : onOpenFile(row.entry.path))}\n />\n {row.truncated ? (\n <p className=\"truncate py-0.5 text-label text-fg-4\" style={{ paddingLeft: (row.depth + 1) * INDENT + 22 }}>\n More entries than the server will return - use search.\n </p>\n ) : null}\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n )\n}\n\nfunction Row({\n label,\n detail,\n title,\n icon,\n chevron,\n indent = 0,\n active,\n onClick,\n}: {\n label: string\n detail?: string\n title?: string\n icon: ReactNode\n chevron?: ReactNode\n indent?: number\n active?: boolean\n onClick: () => void\n}) {\n const ref = useRef<HTMLButtonElement>(null)\n useEffect(() => {\n if (active) {\n ref.current?.scrollIntoView({ block: 'nearest' })\n }\n }, [active])\n\n return (\n <button\n ref={ref}\n type=\"button\"\n onClick={onClick}\n title={title ?? label}\n style={{ paddingLeft: indent * INDENT + 4 }}\n className={cn(\n 'flex w-full items-center gap-1 rounded py-1 pr-2 text-left transition-colors',\n active ? 'bg-surface-hover text-fg-1' : 'text-fg-2 hover:bg-surface-hover',\n )}\n >\n <span className=\"flex size-3 shrink-0 items-center justify-center\">{chevron}</span>\n {icon}\n <span className=\"shrink-0 truncate text-label\">{label}</span>\n {detail ? <span className=\"min-w-0 flex-1 truncate text-label text-fg-4\">{detail}</span> : null}\n </button>\n )\n}\n\nfunction fileName(relative: string): string {\n return relative.slice(relative.lastIndexOf('/') + 1)\n}\n\nfunction directoryOf(relative: string): string | undefined {\n const cut = relative.lastIndexOf('/')\n return cut === -1 ? undefined : relative.slice(0, cut)\n}\n","import { useEffect, useRef, useState } from 'react'\nimport type * as Monaco from 'monaco-editor'\nimport { cn } from '../../lib/utils.ts'\nimport { Spinner } from '../ui/Spinner.tsx'\n\nexport interface CodeEditorProps {\n path: string\n value: string\n onChange?: (value: string) => void\n onSave?: () => void\n readOnly?: boolean\n className?: string\n}\n\nexport function CodeEditor({ path, value, onChange, onSave, readOnly, className }: CodeEditorProps) {\n const host = useRef<HTMLDivElement>(null)\n const editor = useRef<Monaco.editor.IStandaloneCodeEditor | null>(null)\n const monaco = useRef<typeof Monaco | null>(null)\n const [ready, setReady] = useState(false)\n const theme = useDocumentTheme()\n\n const handlers = useRef({ onChange, onSave })\n handlers.current = { onChange, onSave }\n\n useEffect(() => {\n let disposed = false\n void loadMonaco().then((api) => {\n if (disposed || !host.current) {\n return\n }\n monaco.current = api\n const instance = api.editor.create(host.current, {\n value,\n language: languageOf(path),\n readOnly,\n automaticLayout: true,\n theme: monacoTheme(document.documentElement.getAttribute('data-theme')),\n minimap: { enabled: false },\n scrollBeyondLastLine: false,\n fontSize: 12,\n lineHeight: 20,\n fontFamily: 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace',\n renderLineHighlight: 'line',\n smoothScrolling: true,\n padding: { top: 8, bottom: 8 },\n scrollbar: { verticalScrollbarSize: 10, horizontalScrollbarSize: 10 },\n wordBasedSuggestions: 'off',\n quickSuggestions: false,\n })\n editor.current = instance\n instance.onDidChangeModelContent(() => {\n handlers.current.onChange?.(instance.getValue())\n })\n instance.addCommand(api.KeyMod.CtrlCmd | api.KeyCode.KeyS, () => {\n handlers.current.onSave?.()\n })\n setReady(true)\n })\n return () => {\n disposed = true\n // The editor, never its models: a model is keyed by path and outlives this mount, which is what restores a reopened tab's undo stack.\n editor.current?.dispose()\n editor.current = null\n setReady(false)\n }\n }, [])\n\n useEffect(() => {\n const api = monaco.current\n const instance = editor.current\n if (!api || !instance || !ready) {\n return\n }\n const uri = api.Uri.parse(`workerdeck://host${path}`)\n const model = api.editor.getModel(uri) ?? api.editor.createModel(value, languageOf(path), uri)\n if (instance.getModel() !== model) {\n instance.setModel(model)\n }\n }, [path, ready, value])\n\n useEffect(() => {\n const instance = editor.current\n if (!instance || !ready) {\n return\n }\n const model = instance.getModel()\n if (!model || model.getValue() === value) {\n return\n }\n model.pushEditOperations([], [{ range: model.getFullModelRange(), text: value }], () => null)\n }, [value, ready])\n\n useEffect(() => {\n if (ready) {\n editor.current?.updateOptions({ readOnly })\n }\n }, [readOnly, ready])\n\n useEffect(() => {\n if (ready) {\n monaco.current?.editor.setTheme(monacoTheme(theme))\n }\n }, [theme, ready])\n\n return (\n <div className={cn('relative min-h-0 min-w-0 flex-1', className)}>\n <div ref={host} className=\"absolute inset-0\" />\n {!ready ? (\n <div className=\"absolute inset-0 flex items-center justify-center bg-bg\">\n <Spinner className=\"size-4 text-fg-4\" />\n </div>\n ) : null}\n </div>\n )\n}\n\nfunction useDocumentTheme(): string | null {\n const [theme, setTheme] = useState<string | null>(() =>\n typeof document === 'undefined' ? null : document.documentElement.getAttribute('data-theme'),\n )\n useEffect(() => {\n const root = document.documentElement\n const sync = () => setTheme(root.getAttribute('data-theme'))\n sync()\n const observer = new MutationObserver(sync)\n observer.observe(root, { attributes: true, attributeFilter: ['data-theme'] })\n return () => observer.disconnect()\n }, [])\n return theme\n}\n\nfunction monacoTheme(theme: string | null): string {\n return theme === 'light' ? 'wd-light' : 'wd-dark'\n}\n\nlet monacoPromise: Promise<typeof Monaco> | undefined\nfunction loadMonaco(): Promise<typeof Monaco> {\n monacoPromise ??= (async () => {\n const api = await import('monaco-editor')\n api.editor.defineTheme('wd-dark', {\n base: 'vs-dark',\n inherit: true,\n rules: [],\n colors: { 'editor.background': '#00000000' },\n })\n api.editor.defineTheme('wd-light', {\n base: 'vs',\n inherit: true,\n rules: [],\n colors: { 'editor.background': '#00000000' },\n })\n return api\n })()\n return monacoPromise\n}\n\nfunction languageOf(path: string): string {\n const name = path.slice(path.lastIndexOf('/') + 1)\n const byName = FILENAME_LANGUAGES[name.toLowerCase()]\n if (byName) {\n return byName\n }\n const extension = name.slice(name.lastIndexOf('.') + 1).toLowerCase()\n return EXTENSION_LANGUAGES[extension] ?? 'plaintext'\n}\n\nconst FILENAME_LANGUAGES: Record<string, string> = {\n dockerfile: 'dockerfile',\n makefile: 'plaintext',\n '.gitignore': 'plaintext',\n '.env': 'shell',\n}\n\nconst EXTENSION_LANGUAGES: Record<string, string> = {\n ts: 'typescript',\n tsx: 'typescript',\n mts: 'typescript',\n cts: 'typescript',\n js: 'javascript',\n jsx: 'javascript',\n mjs: 'javascript',\n cjs: 'javascript',\n json: 'json',\n jsonc: 'json',\n md: 'markdown',\n mdx: 'markdown',\n css: 'css',\n scss: 'scss',\n less: 'less',\n html: 'html',\n htm: 'html',\n xml: 'xml',\n svg: 'xml',\n yml: 'yaml',\n yaml: 'yaml',\n toml: 'plaintext',\n sh: 'shell',\n bash: 'shell',\n zsh: 'shell',\n fish: 'shell',\n py: 'python',\n rb: 'ruby',\n rs: 'rust',\n go: 'go',\n swift: 'swift',\n java: 'java',\n kt: 'kotlin',\n c: 'c',\n h: 'c',\n cpp: 'cpp',\n cc: 'cpp',\n hpp: 'cpp',\n cs: 'csharp',\n php: 'php',\n sql: 'sql',\n graphql: 'graphql',\n gql: 'graphql',\n lua: 'lua',\n r: 'r',\n pl: 'perl',\n dart: 'dart',\n scala: 'scala',\n ini: 'ini',\n cfg: 'ini',\n conf: 'ini',\n txt: 'plaintext',\n log: 'plaintext',\n}\n","import type { ReactNode } from 'react'\nimport type { OpenFile } from '@workerdeck/react'\nimport { currentText, isDirty } from '@workerdeck/react'\nimport { FileWarning, TriangleAlert } from 'lucide-react'\nimport { cn } from '../../lib/utils.ts'\nimport { Button } from '../ui/Button.tsx'\nimport { Spinner } from '../ui/Spinner.tsx'\nimport { CodeEditor } from './CodeEditor.tsx'\n\nexport interface FileViewerProps {\n file: OpenFile | undefined\n canWrite?: boolean\n onChange?: (path: string, content: string) => void\n onSave?: (path: string) => void\n onRevert?: (path: string) => void\n onReload?: (path: string) => void\n onOverwrite?: (path: string) => void\n onDismissConflict?: (path: string) => void\n className?: string\n}\n\nexport function FileViewer({\n file,\n canWrite,\n onChange,\n onSave,\n onRevert,\n onReload,\n onOverwrite,\n onDismissConflict,\n className,\n}: FileViewerProps) {\n if (!file) {\n return null\n }\n\n return (\n <div data-slot=\"file-viewer\" className={cn('flex min-h-0 min-w-0 flex-1 flex-col bg-bg', className)}>\n {file.conflict ? (\n <div className=\"flex shrink-0 flex-wrap items-center gap-2 border-b border-warning/40 bg-warning-bg px-3 py-2\">\n <TriangleAlert className=\"size-3.5 shrink-0 text-warning\" />\n <span className=\"min-w-0 flex-1 text-body-sm text-warning\">This file changed on disk since you opened it.</span>\n <Button variant=\"outline\" size=\"xs\" onClick={() => onReload?.(file.path)}>\n Use the version on disk\n </Button>\n <Button variant=\"outline\" size=\"xs\" onClick={() => onOverwrite?.(file.path)}>\n Keep mine\n </Button>\n <Button variant=\"ghost\" size=\"xs\" onClick={() => onDismissConflict?.(file.path)}>\n Dismiss\n </Button>\n </div>\n ) : file.saveError ? (\n <div className=\"flex shrink-0 items-center gap-2 border-b border-danger/40 bg-danger-bg px-3 py-2\">\n <TriangleAlert className=\"size-3.5 shrink-0 text-danger\" />\n <span className=\"min-w-0 flex-1 text-body-sm text-danger\">{file.saveError}</span>\n <Button variant=\"ghost\" size=\"xs\" onClick={() => onDismissConflict?.(file.path)}>\n Dismiss\n </Button>\n </div>\n ) : null}\n\n {file.status === 'loading' ? (\n <Centred>\n <Spinner className=\"size-4 text-fg-4\" />\n </Centred>\n ) : file.status === 'error' ? (\n <Centred>\n <TriangleAlert className=\"size-4 text-danger\" />\n <p className=\"text-body-sm text-danger\">{file.error}</p>\n </Centred>\n ) : file.status === 'binary' ? (\n <Centred>\n <FileWarning className=\"size-4 text-fg-4\" />\n <p className=\"text-body-sm text-fg-4\">This file isn’t text.</p>\n <p className=\"text-label text-fg-4\">It can’t be edited here without corrupting it.</p>\n </Centred>\n ) : (\n <CodeEditor\n path={file.path}\n value={currentText(file)}\n readOnly={!canWrite}\n onChange={onChange ? (content) => onChange(file.path, content) : undefined}\n onSave={onSave ? () => onSave(file.path) : undefined}\n />\n )}\n\n {file.status === 'ready' && (file.saving || isDirty(file) || !canWrite) ? (\n <div className=\"flex shrink-0 items-center gap-2 border-t border-border px-3 py-1\">\n {file.saving ? (\n <>\n <Spinner className=\"size-3 text-fg-4\" />\n <span className=\"text-label text-fg-4\">Saving…</span>\n </>\n ) : !canWrite ? (\n <span className=\"text-label text-fg-4\">Read-only - this gateway doesn’t allow writes.</span>\n ) : (\n <>\n <span className=\"text-label text-fg-3\">Unsaved changes</span>\n <span className=\"flex-1\" />\n <Button variant=\"ghost\" size=\"xs\" onClick={() => onRevert?.(file.path)}>\n Revert\n </Button>\n <Button variant=\"outline\" size=\"xs\" onClick={() => onSave?.(file.path)}>\n Save\n <span className=\"ml-1 text-fg-4\">⌘S</span>\n </Button>\n </>\n )}\n </div>\n ) : null}\n </div>\n )\n}\n\nfunction Centred({ children }: { children: ReactNode }) {\n return <div className=\"flex min-h-0 flex-1 flex-col items-center justify-center gap-2 p-6\">{children}</div>\n}\n","import { useEffect, type RefObject } from 'react'\nimport { parseFileLink } from '../../lib/file-link.ts'\n\nexport type PathHit = { path: string; line?: number }\n\nconst LINKISH = 'wd-path-link'\n\nconst PATH_RE = /^(\\/[^\\s:'\"`()[\\]{}]+|[\\w.@~-]+(?:\\/[\\w.@~-]+)+)(?::(\\d+))?$/\nconst FILE_RE = /^([\\w.@-]+\\.[A-Za-z0-9]{1,10})(?::(\\d+))?$/\n\nconst COVERAGE = 0.6\n\nexport function matchPath(text: string, inCode: boolean): PathHit | undefined {\n const trimmed = text.trim()\n if (!trimmed || trimmed.endsWith('/')) {\n return undefined\n }\n const hit = PATH_RE.exec(trimmed) ?? (inCode ? FILE_RE.exec(trimmed) : null)\n if (!hit) {\n return undefined\n }\n return { path: hit[1]!, line: hit[2] ? Number(hit[2]) : undefined }\n}\n\nfunction hitFor(element: HTMLElement | undefined): PathHit | undefined {\n if (!element) {\n return undefined\n }\n const inCode = element.closest('code') !== null\n const text = element.textContent ?? ''\n const hit = matchPath(text, inCode)\n if (!hit) {\n return undefined\n }\n if (!inCode && hit.path.length < text.trim().length * COVERAGE) {\n return undefined\n }\n return hit\n}\n\nexport function usePathLinks({\n container,\n onOpen,\n enabled = true,\n ignore,\n}: {\n container: RefObject<HTMLElement | null>\n onOpen: (hit: PathHit) => void\n enabled?: boolean\n ignore?: string\n}) {\n useEffect(() => {\n const root = container.current\n if (!root || !enabled) {\n return\n }\n const excluded = (element: HTMLElement | undefined) =>\n ignore !== undefined && element?.closest(ignore) !== null && element !== undefined\n\n let marked: HTMLElement | undefined\n const unmark = () => {\n marked?.classList.remove(LINKISH)\n marked = undefined\n }\n\n const onClick = (event: MouseEvent) => {\n if (!event.metaKey && !event.ctrlKey) {\n return\n }\n const target = event.target instanceof HTMLElement ? event.target : undefined\n if (excluded(target)) {\n return\n }\n const hit = hitFor(target)\n if (!hit) {\n return\n }\n event.preventDefault()\n event.stopPropagation()\n unmark()\n onOpen(hit)\n }\n\n const onMove = (event: MouseEvent) => {\n if (!event.metaKey && !event.ctrlKey) {\n return unmark()\n }\n const element = event.target instanceof HTMLElement ? event.target : undefined\n if (element === marked) {\n return\n }\n unmark()\n if (!element || excluded(element) || !hitFor(element)) {\n return\n }\n marked = element\n element.classList.add(LINKISH)\n }\n\n const onKey = (event: KeyboardEvent) => {\n if (!event.metaKey && !event.ctrlKey) {\n unmark()\n }\n }\n\n root.addEventListener('click', onClick, true)\n root.addEventListener('mousemove', onMove)\n document.addEventListener('keydown', onKey)\n document.addEventListener('keyup', onKey)\n window.addEventListener('blur', unmark)\n return () => {\n unmark()\n root.removeEventListener('click', onClick, true)\n root.removeEventListener('mousemove', onMove)\n document.removeEventListener('keydown', onKey)\n document.removeEventListener('keyup', onKey)\n window.removeEventListener('blur', unmark)\n }\n }, [container, onOpen, enabled, ignore])\n}\n\nexport function resolveAgainstCwd(path: string, cwd: string | undefined): string {\n return parseFileLink(path, cwd)?.path ?? path\n}\n","import { useCallback, useEffect, useLayoutEffect, useRef, useState, type RefObject } from 'react'\nimport { createPortal } from 'react-dom'\nimport type { WorkerDeckClient } from '@workerdeck/client'\nimport { useHostFileRoots, useHostFileSearch, useHostFileTree, useOpenFiles, useSessionInfo, isDirty } from '@workerdeck/react'\nimport { PanelLeftOpen } from 'lucide-react'\nimport { cn } from '../../lib/utils.ts'\nimport { Button } from '../ui/Button.tsx'\nimport { Splitter } from '../ui/Splitter.tsx'\nimport { EditorTabs } from './EditorTabs.tsx'\nimport { FileTree } from './FileTree.tsx'\nimport { FileViewer } from './FileViewer.tsx'\nimport { SessionPanel, type SessionPanelProps } from './SessionPanel.tsx'\nimport { resolveAgainstCwd, usePathLinks } from './use-path-links.ts'\n\nexport interface SessionWorkspaceProps {\n client: WorkerDeckClient\n sessionId: string | undefined\n header?: SessionPanelProps['header']\n transcriptVariant?: SessionPanelProps['transcriptVariant']\n transcriptDensity?: SessionPanelProps['transcriptDensity']\n transcriptFont?: SessionPanelProps['transcriptFont']\n scrubber?: SessionPanelProps['scrubber']\n bookmarks?: SessionPanelProps['bookmarks']\n onToggleBookmark?: SessionPanelProps['onToggleBookmark']\n stickyPrompt?: SessionPanelProps['stickyPrompt']\n openSubagent?: SessionPanelProps['openSubagent']\n reveal?: SessionPanelProps['reveal']\n subagents?: SessionPanelProps['subagents']\n onSubagentChange?: SessionPanelProps['onSubagentChange']\n statusPlacement?: SessionPanelProps['statusPlacement']\n controlsSurface?: SessionPanelProps['controlsSurface']\n fontSize?: SessionPanelProps['fontSize']\n onLinkClick?: SessionPanelProps['onLinkClick']\n onOpenFile?: SessionPanelProps['onOpenFile']\n unseen?: SessionPanelProps['unseen']\n readOnly?: SessionPanelProps['readOnly']\n onVitals?: SessionPanelProps['onVitals']\n defaultRailWidth?: number\n defaultRailCollapsed?: boolean\n onRailChange?: (rail: { width: number; collapsed: boolean }) => void\n className?: string\n}\n\nconst RAIL_MIN = 180\nconst RAIL_MAX = 520\nconst AGENT_MIN = 220\nconst EDITOR_MIN = 120\n\nexport function SessionWorkspace({\n client,\n sessionId,\n header,\n transcriptVariant,\n transcriptDensity,\n transcriptFont,\n scrubber,\n bookmarks,\n onToggleBookmark,\n stickyPrompt,\n openSubagent,\n reveal,\n subagents,\n onSubagentChange,\n statusPlacement,\n controlsSurface,\n fontSize,\n onLinkClick,\n onOpenFile,\n unseen,\n readOnly,\n onVitals,\n defaultRailWidth = 260,\n defaultRailCollapsed,\n onRailChange,\n className,\n}: SessionWorkspaceProps) {\n const { info } = useSessionInfo(client, sessionId)\n const cwd = info?.cwd\n\n const tree = useHostFileTree(client, cwd)\n const search = useHostFileSearch(client, cwd)\n const files = useOpenFiles(client)\n const { canWrite } = useHostFileRoots(client)\n\n useEffect(() => {\n if (!files.hasUnsaved) {\n return\n }\n const warn = (event: BeforeUnloadEvent) => event.preventDefault()\n window.addEventListener('beforeunload', warn)\n return () => window.removeEventListener('beforeunload', warn)\n }, [files.hasUnsaved])\n\n const wide = useIsWide()\n const [railCollapsed, setRailCollapsed] = useState(defaultRailCollapsed ?? false)\n const [railWidth, setRailWidth] = useState(defaultRailWidth)\n const onRailChangeRef = useRef(onRailChange)\n onRailChangeRef.current = onRailChange\n useEffect(() => {\n onRailChangeRef.current?.({ width: railWidth, collapsed: railCollapsed })\n }, [railWidth, railCollapsed])\n const [editorHeight, setEditorHeight] = useState(360)\n\n const hasFiles = files.files.length > 0\n\n const overlayRail = !wide\n const railOpen = tree.available && !railCollapsed\n useEffect(() => {\n if (overlayRail) {\n setRailCollapsed(true)\n }\n }, [overlayRail])\n\n const closeTab = useCallback(\n (path: string) => {\n const file = files.files.find((f) => f.path === path)\n if (file && isDirty(file)) {\n const name = file.name\n if (!window.confirm(`${name} has unsaved changes. Close it and lose them?`)) {\n return\n }\n }\n files.close(path)\n },\n [files],\n )\n\n const column = useRef<HTMLDivElement>(null)\n const columnHeight = useElementHeight(column)\n const openPath = useCallback(({ path }: { path: string }) => files.open(resolveAgainstCwd(path, cwd)), [files.open, cwd])\n usePathLinks({\n container: column,\n onOpen: openPath,\n enabled: tree.available,\n ignore: '.monaco-editor',\n })\n const editorMax = Math.max(EDITOR_MIN, columnHeight - AGENT_MIN)\n\n const [topBar, setTopBar] = useState<HTMLDivElement | null>(null)\n const hoisted: SessionPanelProps['header'] =\n header === undefined || topBar === null\n ? undefined\n : typeof header === 'function'\n ? (slots) => createPortal(header(slots), topBar)\n : createPortal(header, topBar)\n\n return (\n <div data-slot=\"session-workspace\" className={cn('flex h-full min-h-0 w-full flex-col overflow-hidden bg-bg', className)}>\n {header !== undefined ? <div ref={setTopBar} className=\"shrink-0\" /> : null}\n <div className=\"relative flex min-h-0 flex-1\">\n {railOpen ? (\n <FileTree\n tree={tree}\n search={search}\n activePath={files.activePath}\n onOpenFile={files.open}\n onCollapse={() => setRailCollapsed(true)}\n style={{ width: overlayRail ? Math.min(railWidth, 320) : railWidth }}\n className={cn('shrink-0 border-r border-border', overlayRail && 'absolute inset-y-0 left-0 z-20 shadow-lg')}\n />\n ) : tree.available ? (\n <div className=\"flex w-8 shrink-0 flex-col items-center border-r border-border bg-surface pt-1.5\">\n <Button variant=\"ghost\" size=\"icon-sm\" aria-label=\"Show project files\" onClick={() => setRailCollapsed(false)}>\n <PanelLeftOpen className=\"size-4 text-fg-3\" />\n </Button>\n </div>\n ) : null}\n {railOpen && !overlayRail ? (\n <Splitter\n orientation=\"vertical\"\n value={railWidth}\n onValueChange={setRailWidth}\n min={RAIL_MIN}\n max={RAIL_MAX}\n defaultValue={defaultRailWidth}\n aria-label=\"Resize the file tree\"\n />\n ) : null}\n\n <div ref={column} className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n {hasFiles ? (\n <div\n className=\"flex min-h-0 shrink-0 flex-col overflow-hidden\"\n style={{ height: Math.min(editorHeight, editorMax || editorHeight) }}\n >\n <EditorTabs files={files.files} activePath={files.activePath} onActivate={files.activate} onClose={closeTab} />\n <FileViewer\n file={files.active}\n canWrite={canWrite}\n onChange={files.edit}\n onSave={(path) => void files.save(path)}\n onRevert={files.revert}\n onReload={files.reload}\n onOverwrite={(path) => void files.overwrite(path)}\n onDismissConflict={files.dismissConflict}\n />\n </div>\n ) : null}\n {hasFiles ? (\n <Splitter\n orientation=\"horizontal\"\n value={Math.min(editorHeight, editorMax || editorHeight)}\n onValueChange={setEditorHeight}\n min={EDITOR_MIN}\n max={editorMax}\n aria-label=\"Resize the open file\"\n />\n ) : null}\n <SessionPanel\n client={client}\n sessionId={sessionId}\n header={hoisted}\n transcriptVariant={transcriptVariant}\n transcriptDensity={transcriptDensity}\n transcriptFont={transcriptFont}\n scrubber={scrubber}\n bookmarks={bookmarks}\n onToggleBookmark={onToggleBookmark}\n stickyPrompt={stickyPrompt}\n openSubagent={openSubagent}\n reveal={reveal}\n subagents={subagents}\n onSubagentChange={onSubagentChange}\n controlsSurface={controlsSurface}\n fontSize={fontSize}\n onLinkClick={onLinkClick}\n onOpenFile={onOpenFile ?? files.open}\n statusPlacement={statusPlacement}\n unseen={unseen}\n readOnly={readOnly}\n onVitals={onVitals}\n className=\"min-h-0 flex-1\"\n />\n </div>\n\n {overlayRail && railOpen ? (\n <button\n type=\"button\"\n aria-label=\"Close the file tree\"\n onClick={() => setRailCollapsed(true)}\n className=\"absolute inset-0 z-10 bg-black/30\"\n />\n ) : null}\n </div>\n </div>\n )\n}\n\nfunction useElementHeight(ref: RefObject<HTMLElement | null>): number {\n const [height, setHeight] = useState(0)\n useLayoutEffect(() => {\n const element = ref.current\n if (!element) {\n return\n }\n const observer = new ResizeObserver(([entry]) => {\n if (entry) {\n setHeight(entry.contentRect.height)\n }\n })\n observer.observe(element)\n return () => observer.disconnect()\n }, [ref])\n return height\n}\n\nfunction useIsWide(): boolean {\n const [wide, setWide] = useState(true)\n useEffect(() => {\n const query = window.matchMedia('(min-width: 768px)')\n const sync = () => setWide(query.matches)\n sync()\n query.addEventListener('change', sync)\n return () => query.removeEventListener('change', sync)\n }, [])\n return wide\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"workspace.mjs","names":[],"sources":["../src/components/agent/EditorTabs.tsx","../src/components/agent/FileTree.tsx","../src/components/agent/CodeEditor.tsx","../src/components/agent/FileViewer.tsx","../src/components/agent/SessionWorkspace.tsx"],"sourcesContent":["import { useEffect, useRef, type MouseEvent as ReactMouseEvent } from 'react'\nimport { isDirty, type OpenFile } from '@workerdeck/react'\nimport { X } from 'lucide-react'\nimport { cn } from '../../lib/utils.ts'\nimport { Tip, TooltipProvider } from '../ui/Tooltip.tsx'\nimport { formatBytes } from '../../lib/format.ts'\n\nexport interface EditorTabsProps {\n files: OpenFile[]\n activePath?: string\n onActivate: (path: string) => void\n onClose: (path: string) => void\n className?: string\n}\n\nexport function EditorTabs({ files, activePath, onActivate, onClose, className }: EditorTabsProps) {\n return (\n <TooltipProvider delay={500} closeDelay={0}>\n <div\n data-slot=\"editor-tabs\"\n role=\"tablist\"\n aria-label=\"Open files\"\n className={cn('flex shrink-0 items-stretch overflow-x-auto border-b border-border bg-surface', className)}\n >\n {files.map((file) => (\n <Tab\n key={file.path}\n file={file}\n active={file.path === activePath}\n onActivate={() => onActivate(file.path)}\n onClose={() => onClose(file.path)}\n onArrow={(direction) => {\n const index = files.findIndex((f) => f.path === file.path)\n const next = files[index + direction]\n if (next) {\n onActivate(next.path)\n }\n }}\n />\n ))}\n </div>\n </TooltipProvider>\n )\n}\n\nfunction Tab({\n file,\n active,\n onActivate,\n onClose,\n onArrow,\n}: {\n file: OpenFile\n active: boolean\n onActivate: () => void\n onClose: () => void\n onArrow: (direction: 1 | -1) => void\n}) {\n const dirty = isDirty(file)\n const ref = useRef<HTMLDivElement>(null)\n useEffect(() => {\n if (active) {\n ref.current?.scrollIntoView({ block: 'nearest', inline: 'nearest' })\n }\n }, [active])\n\n const onAuxClick = (event: ReactMouseEvent) => {\n if (event.button !== 1) {\n return\n }\n event.preventDefault()\n onClose()\n }\n\n return (\n <Tip\n side=\"bottom\"\n render={\n <div\n ref={ref}\n role=\"tab\"\n tabIndex={active ? 0 : -1}\n aria-selected={active}\n onClick={onActivate}\n onAuxClick={onAuxClick}\n onKeyDown={(event) => {\n if (event.key === 'ArrowRight') {\n onArrow(1)\n } else if (event.key === 'ArrowLeft') {\n onArrow(-1)\n } else if (event.key === 'Enter' || event.key === ' ') {\n onActivate()\n } else {\n return\n }\n event.preventDefault()\n }}\n className={cn(\n 'group flex min-w-0 shrink-0 cursor-pointer items-center gap-1.5 border-r border-border px-3 py-1.5 transition-colors',\n 'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent focus-visible:ring-inset',\n active ? 'bg-bg text-fg-1' : 'text-fg-3 hover:bg-surface-hover hover:text-fg-2',\n )}\n />\n }\n content={\n <span className=\"flex flex-col gap-0.5\">\n <span className=\"font-mono break-all\">{file.path}</span>\n {file.bytes !== undefined ? <span className=\"text-fg-4\">{formatBytes(file.bytes)}</span> : null}\n </span>\n }\n >\n <span className={cn('max-w-40 truncate font-mono text-label', dirty && 'italic')}>{file.name}</span>\n {file.status === 'error' ? <span className=\"shrink-0 text-danger\">!</span> : null}\n <button\n type=\"button\"\n aria-label={dirty ? `Close ${file.name} (unsaved changes)` : `Close ${file.name}`}\n onClick={(event) => {\n event.stopPropagation()\n onClose()\n }}\n className={cn(\n 'shrink-0 rounded p-0.5 text-fg-4 transition-opacity hover:bg-surface-hover hover:text-fg-1',\n active || dirty ? 'opacity-70' : 'opacity-0 group-hover:opacity-70 focus-visible:opacity-100',\n )}\n >\n <span className={cn('block', dirty && 'group-hover:hidden')}>\n {dirty ? <span className=\"block size-3 rounded-full bg-fg-2\" /> : <X className=\"size-3\" />}\n </span>\n {dirty ? (\n <span className=\"hidden group-hover:block\">\n <X className=\"size-3\" />\n </span>\n ) : null}\n </button>\n </Tip>\n )\n}\n","import { useEffect, useRef, useState, type CSSProperties, type ReactNode } from 'react'\nimport type { UseHostFileSearchResult, UseHostFileTreeResult } from '@workerdeck/react'\nimport type { HostFileMatch } from '@workerdeck/protocol'\nimport { ChevronRight, File, Folder, FolderOpen, PanelLeftClose, RefreshCw, Search, X } from 'lucide-react'\nimport { cn } from '../../lib/utils.ts'\nimport { Button } from '../ui/Button.tsx'\nimport { Input } from '../ui/Input.tsx'\nimport { Spinner } from '../ui/Spinner.tsx'\nimport { EntryIcon } from './EntryIcon.tsx'\n\nexport interface FileTreeProps {\n tree: UseHostFileTreeResult\n search?: UseHostFileSearchResult\n activePath?: string\n onOpenFile: (path: string) => void\n onCollapse?: () => void\n style?: CSSProperties\n className?: string\n}\n\nconst INDENT = 12\n\nexport function FileTree({ tree, search, activePath, onOpenFile, onCollapse, style, className }: FileTreeProps) {\n const [query, setQuery] = useState('')\n const [matches, setMatches] = useState<HostFileMatch[] | undefined>()\n const searching = query.trim().length > 0\n\n useEffect(() => {\n if (!search?.available) {\n return\n }\n const q = query.trim()\n if (!q) {\n setMatches(undefined)\n return\n }\n const controller = new AbortController()\n const timer = setTimeout(() => {\n void search.search(q, { limit: 60, signal: controller.signal }).then((found) => {\n if (!controller.signal.aborted) {\n setMatches(found)\n }\n })\n }, 150)\n return () => {\n controller.abort()\n clearTimeout(timer)\n }\n }, [search, query])\n\n return (\n <div data-slot=\"file-tree\" style={style} className={cn('flex min-h-0 min-w-0 flex-col bg-surface', className)}>\n <div className=\"flex items-center gap-1 px-2 pt-2\">\n {search?.available ? (\n <div className=\"relative min-w-0 flex-1\">\n <Search className=\"absolute top-1/2 left-2 size-3.5 -translate-y-1/2 text-fg-4\" />\n <Input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search files…\"\n className=\"h-7 pr-7 pl-7 text-label\"\n spellCheck={false}\n />\n {searching ? (\n <button\n type=\"button\"\n onClick={() => setQuery('')}\n aria-label=\"Clear search\"\n className=\"absolute top-1/2 right-1.5 -translate-y-1/2 text-fg-4 transition-colors hover:text-fg-2\"\n >\n <X className=\"size-3.5\" />\n </button>\n ) : null}\n </div>\n ) : (\n <span className=\"min-w-0 flex-1 truncate px-1 text-label text-fg-4\">{tree.root}</span>\n )}\n <Button variant=\"ghost\" size=\"icon-sm\" aria-label=\"Refresh the file tree\" onClick={() => tree.refresh()}>\n <RefreshCw className=\"size-3.5 text-fg-3\" />\n </Button>\n {onCollapse ? (\n <Button variant=\"ghost\" size=\"icon-sm\" aria-label=\"Hide project files\" onClick={onCollapse}>\n <PanelLeftClose className=\"size-3.5 text-fg-3\" />\n </Button>\n ) : null}\n </div>\n\n <div className=\"min-h-0 flex-1 overflow-auto px-1 py-1\">\n {tree.error ? (\n <p className=\"px-2 py-3 text-body-sm text-danger\">{tree.error}</p>\n ) : searching ? (\n matches === undefined ? (\n <div className=\"py-6 text-center\">\n <Spinner className=\"size-4 text-fg-4\" />\n </div>\n ) : matches.length === 0 ? (\n <p className=\"px-2 py-6 text-center text-body-sm text-fg-4\">No matching files.</p>\n ) : (\n <ul>\n {matches.map((match) => (\n <li key={match.path}>\n <Row\n label={fileName(match.relative)}\n detail={directoryOf(match.relative)}\n title={match.relative}\n icon={<File className=\"size-3.5 shrink-0 text-fg-4\" />}\n active={match.path === activePath}\n onClick={() => onOpenFile(match.path)}\n />\n </li>\n ))}\n </ul>\n )\n ) : tree.loading ? (\n <div className=\"py-6 text-center\">\n <Spinner className=\"size-4 text-fg-4\" />\n </div>\n ) : tree.rows.length === 0 ? (\n <p className=\"px-2 py-6 text-center text-body-sm text-fg-4\">This project is empty.</p>\n ) : (\n <ul role=\"tree\" aria-label=\"Project files\">\n {tree.rows.map((row) => (\n <li key={row.entry.path} role=\"treeitem\" aria-expanded={row.expanded} aria-level={row.depth + 1}>\n <Row\n label={row.entry.name}\n indent={row.depth}\n active={row.entry.path === activePath}\n icon={\n row.entry.type === 'dir' ? (\n row.loading ? (\n <Spinner className=\"size-3.5 shrink-0 text-fg-4\" />\n ) : row.expanded ? (\n <FolderOpen className=\"size-3.5 shrink-0 text-accent\" />\n ) : (\n <Folder className=\"size-3.5 shrink-0 text-accent\" />\n )\n ) : (\n <EntryIcon type={row.entry.type} />\n )\n }\n chevron={\n row.entry.type === 'dir' ? (\n <ChevronRight className={cn('size-3 shrink-0 text-fg-4 transition-transform', row.expanded && 'rotate-90')} />\n ) : undefined\n }\n onClick={() => (row.entry.type === 'dir' ? tree.toggle(row.entry.path) : onOpenFile(row.entry.path))}\n />\n {row.truncated ? (\n <p className=\"truncate py-0.5 text-label text-fg-4\" style={{ paddingLeft: (row.depth + 1) * INDENT + 22 }}>\n More entries than the server will return - use search.\n </p>\n ) : null}\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n )\n}\n\nfunction Row({\n label,\n detail,\n title,\n icon,\n chevron,\n indent = 0,\n active,\n onClick,\n}: {\n label: string\n detail?: string\n title?: string\n icon: ReactNode\n chevron?: ReactNode\n indent?: number\n active?: boolean\n onClick: () => void\n}) {\n const ref = useRef<HTMLButtonElement>(null)\n useEffect(() => {\n if (active) {\n ref.current?.scrollIntoView({ block: 'nearest' })\n }\n }, [active])\n\n return (\n <button\n ref={ref}\n type=\"button\"\n onClick={onClick}\n title={title ?? label}\n style={{ paddingLeft: indent * INDENT + 4 }}\n className={cn(\n 'flex w-full items-center gap-1 rounded py-1 pr-2 text-left transition-colors',\n active ? 'bg-surface-hover text-fg-1' : 'text-fg-2 hover:bg-surface-hover',\n )}\n >\n <span className=\"flex size-3 shrink-0 items-center justify-center\">{chevron}</span>\n {icon}\n <span className=\"shrink-0 truncate text-label\">{label}</span>\n {detail ? <span className=\"min-w-0 flex-1 truncate text-label text-fg-4\">{detail}</span> : null}\n </button>\n )\n}\n\nfunction fileName(relative: string): string {\n return relative.slice(relative.lastIndexOf('/') + 1)\n}\n\nfunction directoryOf(relative: string): string | undefined {\n const cut = relative.lastIndexOf('/')\n return cut === -1 ? undefined : relative.slice(0, cut)\n}\n","import { useEffect, useRef, useState } from 'react'\nimport type * as Monaco from 'monaco-editor'\nimport { cn } from '../../lib/utils.ts'\nimport { Spinner } from '../ui/Spinner.tsx'\n\nexport interface CodeEditorProps {\n path: string\n value: string\n onChange?: (value: string) => void\n onSave?: () => void\n readOnly?: boolean\n className?: string\n}\n\nexport function CodeEditor({ path, value, onChange, onSave, readOnly, className }: CodeEditorProps) {\n const host = useRef<HTMLDivElement>(null)\n const editor = useRef<Monaco.editor.IStandaloneCodeEditor | null>(null)\n const monaco = useRef<typeof Monaco | null>(null)\n const [ready, setReady] = useState(false)\n const theme = useDocumentTheme()\n\n const handlers = useRef({ onChange, onSave })\n handlers.current = { onChange, onSave }\n\n useEffect(() => {\n let disposed = false\n void loadMonaco().then((api) => {\n if (disposed || !host.current) {\n return\n }\n monaco.current = api\n const instance = api.editor.create(host.current, {\n value,\n language: languageOf(path),\n readOnly,\n automaticLayout: true,\n theme: monacoTheme(document.documentElement.getAttribute('data-theme')),\n minimap: { enabled: false },\n scrollBeyondLastLine: false,\n fontSize: 12,\n lineHeight: 20,\n fontFamily: 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace',\n renderLineHighlight: 'line',\n smoothScrolling: true,\n padding: { top: 8, bottom: 8 },\n scrollbar: { verticalScrollbarSize: 10, horizontalScrollbarSize: 10 },\n wordBasedSuggestions: 'off',\n quickSuggestions: false,\n })\n editor.current = instance\n instance.onDidChangeModelContent(() => {\n handlers.current.onChange?.(instance.getValue())\n })\n instance.addCommand(api.KeyMod.CtrlCmd | api.KeyCode.KeyS, () => {\n handlers.current.onSave?.()\n })\n setReady(true)\n })\n return () => {\n disposed = true\n // The editor, never its models: a model is keyed by path and outlives this mount, which is what restores a reopened tab's undo stack.\n editor.current?.dispose()\n editor.current = null\n setReady(false)\n }\n }, [])\n\n useEffect(() => {\n const api = monaco.current\n const instance = editor.current\n if (!api || !instance || !ready) {\n return\n }\n const uri = api.Uri.parse(`workerdeck://host${path}`)\n const model = api.editor.getModel(uri) ?? api.editor.createModel(value, languageOf(path), uri)\n if (instance.getModel() !== model) {\n instance.setModel(model)\n }\n }, [path, ready, value])\n\n useEffect(() => {\n const instance = editor.current\n if (!instance || !ready) {\n return\n }\n const model = instance.getModel()\n if (!model || model.getValue() === value) {\n return\n }\n model.pushEditOperations([], [{ range: model.getFullModelRange(), text: value }], () => null)\n }, [value, ready])\n\n useEffect(() => {\n if (ready) {\n editor.current?.updateOptions({ readOnly })\n }\n }, [readOnly, ready])\n\n useEffect(() => {\n if (ready) {\n monaco.current?.editor.setTheme(monacoTheme(theme))\n }\n }, [theme, ready])\n\n return (\n <div className={cn('relative min-h-0 min-w-0 flex-1', className)}>\n <div ref={host} className=\"absolute inset-0\" />\n {!ready ? (\n <div className=\"absolute inset-0 flex items-center justify-center bg-bg\">\n <Spinner className=\"size-4 text-fg-4\" />\n </div>\n ) : null}\n </div>\n )\n}\n\nfunction useDocumentTheme(): string | null {\n const [theme, setTheme] = useState<string | null>(() =>\n typeof document === 'undefined' ? null : document.documentElement.getAttribute('data-theme'),\n )\n useEffect(() => {\n const root = document.documentElement\n const sync = () => setTheme(root.getAttribute('data-theme'))\n sync()\n const observer = new MutationObserver(sync)\n observer.observe(root, { attributes: true, attributeFilter: ['data-theme'] })\n return () => observer.disconnect()\n }, [])\n return theme\n}\n\nfunction monacoTheme(theme: string | null): string {\n return theme === 'light' ? 'wd-light' : 'wd-dark'\n}\n\nlet monacoPromise: Promise<typeof Monaco> | undefined\nfunction loadMonaco(): Promise<typeof Monaco> {\n monacoPromise ??= (async () => {\n const api = await import('monaco-editor')\n api.editor.defineTheme('wd-dark', {\n base: 'vs-dark',\n inherit: true,\n rules: [],\n colors: { 'editor.background': '#00000000' },\n })\n api.editor.defineTheme('wd-light', {\n base: 'vs',\n inherit: true,\n rules: [],\n colors: { 'editor.background': '#00000000' },\n })\n return api\n })()\n return monacoPromise\n}\n\nfunction languageOf(path: string): string {\n const name = path.slice(path.lastIndexOf('/') + 1)\n const byName = FILENAME_LANGUAGES[name.toLowerCase()]\n if (byName) {\n return byName\n }\n const extension = name.slice(name.lastIndexOf('.') + 1).toLowerCase()\n return EXTENSION_LANGUAGES[extension] ?? 'plaintext'\n}\n\nconst FILENAME_LANGUAGES: Record<string, string> = {\n dockerfile: 'dockerfile',\n makefile: 'plaintext',\n '.gitignore': 'plaintext',\n '.env': 'shell',\n}\n\nconst EXTENSION_LANGUAGES: Record<string, string> = {\n ts: 'typescript',\n tsx: 'typescript',\n mts: 'typescript',\n cts: 'typescript',\n js: 'javascript',\n jsx: 'javascript',\n mjs: 'javascript',\n cjs: 'javascript',\n json: 'json',\n jsonc: 'json',\n md: 'markdown',\n mdx: 'markdown',\n css: 'css',\n scss: 'scss',\n less: 'less',\n html: 'html',\n htm: 'html',\n xml: 'xml',\n svg: 'xml',\n yml: 'yaml',\n yaml: 'yaml',\n toml: 'plaintext',\n sh: 'shell',\n bash: 'shell',\n zsh: 'shell',\n fish: 'shell',\n py: 'python',\n rb: 'ruby',\n rs: 'rust',\n go: 'go',\n swift: 'swift',\n java: 'java',\n kt: 'kotlin',\n c: 'c',\n h: 'c',\n cpp: 'cpp',\n cc: 'cpp',\n hpp: 'cpp',\n cs: 'csharp',\n php: 'php',\n sql: 'sql',\n graphql: 'graphql',\n gql: 'graphql',\n lua: 'lua',\n r: 'r',\n pl: 'perl',\n dart: 'dart',\n scala: 'scala',\n ini: 'ini',\n cfg: 'ini',\n conf: 'ini',\n txt: 'plaintext',\n log: 'plaintext',\n}\n","import type { ReactNode } from 'react'\nimport type { OpenFile } from '@workerdeck/react'\nimport { currentText, isDirty } from '@workerdeck/react'\nimport { FileWarning, TriangleAlert } from 'lucide-react'\nimport { cn } from '../../lib/utils.ts'\nimport { Button } from '../ui/Button.tsx'\nimport { Spinner } from '../ui/Spinner.tsx'\nimport { CodeEditor } from './CodeEditor.tsx'\n\nexport interface FileViewerProps {\n file: OpenFile | undefined\n canWrite?: boolean\n onChange?: (path: string, content: string) => void\n onSave?: (path: string) => void\n onRevert?: (path: string) => void\n onReload?: (path: string) => void\n onOverwrite?: (path: string) => void\n onDismissConflict?: (path: string) => void\n className?: string\n}\n\nexport function FileViewer({\n file,\n canWrite,\n onChange,\n onSave,\n onRevert,\n onReload,\n onOverwrite,\n onDismissConflict,\n className,\n}: FileViewerProps) {\n if (!file) {\n return null\n }\n\n return (\n <div data-slot=\"file-viewer\" className={cn('flex min-h-0 min-w-0 flex-1 flex-col bg-bg', className)}>\n {file.conflict ? (\n <div className=\"flex shrink-0 flex-wrap items-center gap-2 border-b border-warning/40 bg-warning-bg px-3 py-2\">\n <TriangleAlert className=\"size-3.5 shrink-0 text-warning\" />\n <span className=\"min-w-0 flex-1 text-body-sm text-warning\">This file changed on disk since you opened it.</span>\n <Button variant=\"outline\" size=\"xs\" onClick={() => onReload?.(file.path)}>\n Use the version on disk\n </Button>\n <Button variant=\"outline\" size=\"xs\" onClick={() => onOverwrite?.(file.path)}>\n Keep mine\n </Button>\n <Button variant=\"ghost\" size=\"xs\" onClick={() => onDismissConflict?.(file.path)}>\n Dismiss\n </Button>\n </div>\n ) : file.saveError ? (\n <div className=\"flex shrink-0 items-center gap-2 border-b border-danger/40 bg-danger-bg px-3 py-2\">\n <TriangleAlert className=\"size-3.5 shrink-0 text-danger\" />\n <span className=\"min-w-0 flex-1 text-body-sm text-danger\">{file.saveError}</span>\n <Button variant=\"ghost\" size=\"xs\" onClick={() => onDismissConflict?.(file.path)}>\n Dismiss\n </Button>\n </div>\n ) : null}\n\n {file.status === 'loading' ? (\n <Centred>\n <Spinner className=\"size-4 text-fg-4\" />\n </Centred>\n ) : file.status === 'error' ? (\n <Centred>\n <TriangleAlert className=\"size-4 text-danger\" />\n <p className=\"text-body-sm text-danger\">{file.error}</p>\n </Centred>\n ) : file.status === 'binary' ? (\n <Centred>\n <FileWarning className=\"size-4 text-fg-4\" />\n <p className=\"text-body-sm text-fg-4\">This file isn’t text.</p>\n <p className=\"text-label text-fg-4\">It can’t be edited here without corrupting it.</p>\n </Centred>\n ) : (\n <CodeEditor\n path={file.path}\n value={currentText(file)}\n readOnly={!canWrite}\n onChange={onChange ? (content) => onChange(file.path, content) : undefined}\n onSave={onSave ? () => onSave(file.path) : undefined}\n />\n )}\n\n {file.status === 'ready' && (file.saving || isDirty(file) || !canWrite) ? (\n <div className=\"flex shrink-0 items-center gap-2 border-t border-border px-3 py-1\">\n {file.saving ? (\n <>\n <Spinner className=\"size-3 text-fg-4\" />\n <span className=\"text-label text-fg-4\">Saving…</span>\n </>\n ) : !canWrite ? (\n <span className=\"text-label text-fg-4\">Read-only - this gateway doesn’t allow writes.</span>\n ) : (\n <>\n <span className=\"text-label text-fg-3\">Unsaved changes</span>\n <span className=\"flex-1\" />\n <Button variant=\"ghost\" size=\"xs\" onClick={() => onRevert?.(file.path)}>\n Revert\n </Button>\n <Button variant=\"outline\" size=\"xs\" onClick={() => onSave?.(file.path)}>\n Save\n <span className=\"ml-1 text-fg-4\">⌘S</span>\n </Button>\n </>\n )}\n </div>\n ) : null}\n </div>\n )\n}\n\nfunction Centred({ children }: { children: ReactNode }) {\n return <div className=\"flex min-h-0 flex-1 flex-col items-center justify-center gap-2 p-6\">{children}</div>\n}\n","import { useCallback, useEffect, useLayoutEffect, useRef, useState, type RefObject } from 'react'\nimport { createPortal } from 'react-dom'\nimport type { WorkerDeckClient } from '@workerdeck/client'\nimport { useHostFileRoots, useHostFileSearch, useHostFileTree, useOpenFiles, useSessionInfo, isDirty } from '@workerdeck/react'\nimport { PanelLeftOpen } from 'lucide-react'\nimport { cn } from '../../lib/utils.ts'\nimport { Button } from '../ui/Button.tsx'\nimport { Splitter } from '../ui/Splitter.tsx'\nimport { EditorTabs } from './EditorTabs.tsx'\nimport { FileTree } from './FileTree.tsx'\nimport { FileViewer } from './FileViewer.tsx'\nimport { SessionPanel, type SessionPanelProps } from './SessionPanel.tsx'\nimport { resolveAgainstCwd, usePathLinks } from './use-path-links.ts'\n\nexport interface SessionWorkspaceProps {\n client: WorkerDeckClient\n sessionId: string | undefined\n header?: SessionPanelProps['header']\n transcriptVariant?: SessionPanelProps['transcriptVariant']\n transcriptDensity?: SessionPanelProps['transcriptDensity']\n transcriptFont?: SessionPanelProps['transcriptFont']\n scrubber?: SessionPanelProps['scrubber']\n bookmarks?: SessionPanelProps['bookmarks']\n onToggleBookmark?: SessionPanelProps['onToggleBookmark']\n stickyPrompt?: SessionPanelProps['stickyPrompt']\n openSubagent?: SessionPanelProps['openSubagent']\n reveal?: SessionPanelProps['reveal']\n subagents?: SessionPanelProps['subagents']\n onSubagentChange?: SessionPanelProps['onSubagentChange']\n statusPlacement?: SessionPanelProps['statusPlacement']\n controlsSurface?: SessionPanelProps['controlsSurface']\n fontSize?: SessionPanelProps['fontSize']\n onLinkClick?: SessionPanelProps['onLinkClick']\n onOpenFile?: SessionPanelProps['onOpenFile']\n unseen?: SessionPanelProps['unseen']\n readOnly?: SessionPanelProps['readOnly']\n onVitals?: SessionPanelProps['onVitals']\n defaultRailWidth?: number\n defaultRailCollapsed?: boolean\n onRailChange?: (rail: { width: number; collapsed: boolean }) => void\n className?: string\n}\n\nconst RAIL_MIN = 180\nconst RAIL_MAX = 520\nconst AGENT_MIN = 220\nconst EDITOR_MIN = 120\n\nexport function SessionWorkspace({\n client,\n sessionId,\n header,\n transcriptVariant,\n transcriptDensity,\n transcriptFont,\n scrubber,\n bookmarks,\n onToggleBookmark,\n stickyPrompt,\n openSubagent,\n reveal,\n subagents,\n onSubagentChange,\n statusPlacement,\n controlsSurface,\n fontSize,\n onLinkClick,\n onOpenFile,\n unseen,\n readOnly,\n onVitals,\n defaultRailWidth = 260,\n defaultRailCollapsed,\n onRailChange,\n className,\n}: SessionWorkspaceProps) {\n const { info } = useSessionInfo(client, sessionId)\n const cwd = info?.cwd\n\n const tree = useHostFileTree(client, cwd)\n const search = useHostFileSearch(client, cwd)\n const files = useOpenFiles(client)\n const { canWrite } = useHostFileRoots(client)\n\n useEffect(() => {\n if (!files.hasUnsaved) {\n return\n }\n const warn = (event: BeforeUnloadEvent) => event.preventDefault()\n window.addEventListener('beforeunload', warn)\n return () => window.removeEventListener('beforeunload', warn)\n }, [files.hasUnsaved])\n\n const wide = useIsWide()\n const [railCollapsed, setRailCollapsed] = useState(defaultRailCollapsed ?? false)\n const [railWidth, setRailWidth] = useState(defaultRailWidth)\n const onRailChangeRef = useRef(onRailChange)\n onRailChangeRef.current = onRailChange\n useEffect(() => {\n onRailChangeRef.current?.({ width: railWidth, collapsed: railCollapsed })\n }, [railWidth, railCollapsed])\n const [editorHeight, setEditorHeight] = useState(360)\n\n const hasFiles = files.files.length > 0\n\n const overlayRail = !wide\n const railOpen = tree.available && !railCollapsed\n useEffect(() => {\n if (overlayRail) {\n setRailCollapsed(true)\n }\n }, [overlayRail])\n\n const closeTab = useCallback(\n (path: string) => {\n const file = files.files.find((f) => f.path === path)\n if (file && isDirty(file)) {\n const name = file.name\n if (!window.confirm(`${name} has unsaved changes. Close it and lose them?`)) {\n return\n }\n }\n files.close(path)\n },\n [files],\n )\n\n const column = useRef<HTMLDivElement>(null)\n const columnHeight = useElementHeight(column)\n const openPath = useCallback(({ path }: { path: string }) => files.open(resolveAgainstCwd(path, cwd)), [files.open, cwd])\n usePathLinks({\n container: column,\n onOpen: openPath,\n enabled: tree.available,\n ignore: '.monaco-editor',\n })\n const editorMax = Math.max(EDITOR_MIN, columnHeight - AGENT_MIN)\n\n const [topBar, setTopBar] = useState<HTMLDivElement | null>(null)\n const hoisted: SessionPanelProps['header'] =\n header === undefined || topBar === null\n ? undefined\n : typeof header === 'function'\n ? (slots) => createPortal(header(slots), topBar)\n : createPortal(header, topBar)\n\n return (\n <div data-slot=\"session-workspace\" className={cn('flex h-full min-h-0 w-full flex-col overflow-hidden bg-bg', className)}>\n {header !== undefined ? <div ref={setTopBar} className=\"shrink-0\" /> : null}\n <div className=\"relative flex min-h-0 flex-1\">\n {railOpen ? (\n <FileTree\n tree={tree}\n search={search}\n activePath={files.activePath}\n onOpenFile={files.open}\n onCollapse={() => setRailCollapsed(true)}\n style={{ width: overlayRail ? Math.min(railWidth, 320) : railWidth }}\n className={cn('shrink-0 border-r border-border', overlayRail && 'absolute inset-y-0 left-0 z-20 shadow-lg')}\n />\n ) : tree.available ? (\n <div className=\"flex w-8 shrink-0 flex-col items-center border-r border-border bg-surface pt-1.5\">\n <Button variant=\"ghost\" size=\"icon-sm\" aria-label=\"Show project files\" onClick={() => setRailCollapsed(false)}>\n <PanelLeftOpen className=\"size-4 text-fg-3\" />\n </Button>\n </div>\n ) : null}\n {railOpen && !overlayRail ? (\n <Splitter\n orientation=\"vertical\"\n value={railWidth}\n onValueChange={setRailWidth}\n min={RAIL_MIN}\n max={RAIL_MAX}\n defaultValue={defaultRailWidth}\n aria-label=\"Resize the file tree\"\n />\n ) : null}\n\n <div ref={column} className=\"flex min-h-0 min-w-0 flex-1 flex-col\">\n {hasFiles ? (\n <div\n className=\"flex min-h-0 shrink-0 flex-col overflow-hidden\"\n style={{ height: Math.min(editorHeight, editorMax || editorHeight) }}\n >\n <EditorTabs files={files.files} activePath={files.activePath} onActivate={files.activate} onClose={closeTab} />\n <FileViewer\n file={files.active}\n canWrite={canWrite}\n onChange={files.edit}\n onSave={(path) => void files.save(path)}\n onRevert={files.revert}\n onReload={files.reload}\n onOverwrite={(path) => void files.overwrite(path)}\n onDismissConflict={files.dismissConflict}\n />\n </div>\n ) : null}\n {hasFiles ? (\n <Splitter\n orientation=\"horizontal\"\n value={Math.min(editorHeight, editorMax || editorHeight)}\n onValueChange={setEditorHeight}\n min={EDITOR_MIN}\n max={editorMax}\n aria-label=\"Resize the open file\"\n />\n ) : null}\n <SessionPanel\n client={client}\n sessionId={sessionId}\n header={hoisted}\n transcriptVariant={transcriptVariant}\n transcriptDensity={transcriptDensity}\n transcriptFont={transcriptFont}\n scrubber={scrubber}\n bookmarks={bookmarks}\n onToggleBookmark={onToggleBookmark}\n stickyPrompt={stickyPrompt}\n openSubagent={openSubagent}\n reveal={reveal}\n subagents={subagents}\n onSubagentChange={onSubagentChange}\n controlsSurface={controlsSurface}\n fontSize={fontSize}\n onLinkClick={onLinkClick}\n onOpenFile={onOpenFile ?? files.open}\n statusPlacement={statusPlacement}\n unseen={unseen}\n readOnly={readOnly}\n onVitals={onVitals}\n className=\"min-h-0 flex-1\"\n />\n </div>\n\n {overlayRail && railOpen ? (\n <button\n type=\"button\"\n aria-label=\"Close the file tree\"\n onClick={() => setRailCollapsed(true)}\n className=\"absolute inset-0 z-10 bg-black/30\"\n />\n ) : null}\n </div>\n </div>\n )\n}\n\nfunction useElementHeight(ref: RefObject<HTMLElement | null>): number {\n const [height, setHeight] = useState(0)\n useLayoutEffect(() => {\n const element = ref.current\n if (!element) {\n return\n }\n const observer = new ResizeObserver(([entry]) => {\n if (entry) {\n setHeight(entry.contentRect.height)\n }\n })\n observer.observe(element)\n return () => observer.disconnect()\n }, [ref])\n return height\n}\n\nfunction useIsWide(): boolean {\n const [wide, setWide] = useState(true)\n useEffect(() => {\n const query = window.matchMedia('(min-width: 768px)')\n const sync = () => setWide(query.matches)\n sync()\n query.addEventListener('change', sync)\n return () => query.removeEventListener('change', sync)\n }, [])\n return wide\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workerdeck/ui",
3
- "version": "2.9.0",
3
+ "version": "2.10.0",
4
4
  "type": "module",
5
5
  "description": "Styled agent-control component library for WorkerDeck: session panel, transcript with streaming, tool-call cards, permission prompts, session list, composer. Tailwind v4 + Base UI + cva. Pairs with the headless @workerdeck/react layer.",
6
6
  "license": "MIT",
@@ -39,9 +39,9 @@
39
39
  "streamdown": "^2.6.0",
40
40
  "tailwind-merge": "^3.7.0",
41
41
  "use-stick-to-bottom": "^1.1.6",
42
- "@workerdeck/client": "2.9.0",
43
- "@workerdeck/protocol": "2.9.0",
44
- "@workerdeck/react": "2.9.0"
42
+ "@workerdeck/client": "2.10.0",
43
+ "@workerdeck/react": "2.10.0",
44
+ "@workerdeck/protocol": "2.10.0"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "monaco-editor": "^0.56.0",
@@ -18,7 +18,7 @@ import { PromptArea } from '../prompt-area/prompt-area.tsx'
18
18
  import { usePromptAreaState } from '../prompt-area/use-prompt-area-state.ts'
19
19
  import { plainTextToSegments } from '../prompt-area/prompt-area-engine.ts'
20
20
  import { commandTrigger, launchTrigger, mentionTrigger } from '../prompt-area/trigger-presets.ts'
21
- import { mergeComposerRows, rankComposerRows, skillPrompt, type ClientCommand } from './composer-commands.ts'
21
+ import { mergeComposerRows, rankComposerRows, skillTrailingText, type ClientCommand } from './composer-commands.ts'
22
22
  import { useTranscriptVariant } from './transcript-variant.tsx'
23
23
  import type { TerminalAffordances } from '../terminal/affordances.tsx'
24
24
  import { PROMPT_GLYPH } from '../terminal/items.tsx'
@@ -140,11 +140,14 @@ export function Composer({
140
140
  ),
141
141
  insertAsText: (suggestion) => {
142
142
  const row = byTag.get(String(suggestion.data))
143
- if (row?.kind === 'skill') {
144
- return skillPrompt(row.skill)
145
- }
146
143
  return row?.kind === 'client' && row.command.requiresArgs ? `/${row.name} ` : undefined
147
144
  },
145
+ // A skill row came off the `/` menu but is codex's `$name` token, so the chip serialises with
146
+ // that sigil and the default prompt's remainder lands after it as editable text.
147
+ chipOptions: (suggestion) => {
148
+ const row = byTag.get(String(suggestion.data))
149
+ return row?.kind === 'skill' ? { sigil: '$', trailingText: skillTrailingText(row.skill) } : undefined
150
+ },
148
151
  onSelect: (suggestion) => suggestion.value,
149
152
  chipClassName: 'font-mono',
150
153
  }),
@@ -1,13 +1,21 @@
1
- import { Fragment } from 'react'
1
+ import { createContext, Fragment, useContext, type ReactNode } from 'react'
2
2
  import { scanPromptTokens } from '@workerdeck/react'
3
3
  import { cn } from '../../lib/utils.ts'
4
4
 
5
+ const SkillNamesContext = createContext<readonly string[] | undefined>(undefined)
6
+
7
+ // The skill names a `$name` in a sent message may resolve to; unset, nothing is a skill token.
8
+ export function SkillNamesProvider({ names, children }: { names: readonly string[] | undefined; children: ReactNode }) {
9
+ return <SkillNamesContext.Provider value={names}>{children}</SkillNamesContext.Provider>
10
+ }
11
+
5
12
  export function PromptTokenText({ text, className }: { text: string; className?: string }) {
6
- const tokens = scanPromptTokens(text)
13
+ const skills = useContext(SkillNamesContext)
14
+ const tokens = scanPromptTokens(text, { skills })
7
15
  if (tokens.length === 0) {
8
16
  return <span className={className}>{text}</span>
9
17
  }
10
- const parts: React.ReactNode[] = []
18
+ const parts: ReactNode[] = []
11
19
  let cursor = 0
12
20
  for (const [index, token] of tokens.entries()) {
13
21
  if (token.start > cursor) {
@@ -46,7 +46,7 @@ export function SessionInfoDialog({ state, client, sessionId, open, onOpenChange
46
46
  {session?.createdAt ? <DialogRow label="Started">{formatRelativeTime(session.createdAt)}</DialogRow> : null}
47
47
  {session?.numTurns !== undefined ? <DialogRow label="Turns">{session.numTurns}</DialogRow> : null}
48
48
  <DialogRow label="Cost" mono>
49
- {formatCost(state.totalCostUsd)}
49
+ {formatCost(state.costUsd ?? (state.totalCostUsd || undefined))}
50
50
  </DialogRow>
51
51
  </div>
52
52
  {state.capabilities.vfs ? <SessionFiles client={client} sessionId={sessionId} open={open} /> : null}
@@ -11,6 +11,8 @@ import type { Step } from './SessionSteps.tsx'
11
11
  import { cn } from '../../lib/utils.ts'
12
12
  import { formatCost, formatRelativeTime, friendlyModel } from '../../lib/format.ts'
13
13
 
14
+ export type SelectModifiers = { meta: boolean; ctrl: boolean; alt: boolean }
15
+
14
16
  export interface SessionItemProps {
15
17
  row: SessionRow
16
18
  active?: boolean
@@ -20,7 +22,7 @@ export interface SessionItemProps {
20
22
  projectIcons?: Record<string, string>
21
23
  expanded?: boolean
22
24
  onExpandedChange?: (expanded: boolean) => void
23
- onSelect?: () => void
25
+ onSelect?: (modifiers: SelectModifiers) => void
24
26
  onSelectSubagent?: (toolUseId: string) => void
25
27
  onRename?: (title: string) => void
26
28
  renameOn?: 'doubleClick' | 'external'
@@ -30,6 +32,8 @@ export interface SessionItemProps {
30
32
  className?: string
31
33
  }
32
34
 
35
+ const NO_MODIFIERS: SelectModifiers = { meta: false, ctrl: false, alt: false }
36
+
33
37
  export function SessionItem({
34
38
  row,
35
39
  active = false,
@@ -66,7 +70,7 @@ export function SessionItem({
66
70
  const project = showProject ? projectLabel(row) : projectSubpath(row)
67
71
  const projectIcon = showProject ? info.project?.icon : undefined
68
72
  const iconSrc = projectIcon?.type === 'image' ? projectIcons?.[projectIcon.hash] : undefined
69
- const cost = formatCost(info.totalCostUsd)
73
+ const cost = formatCost(info.costUsd ?? info.totalCostUsd)
70
74
  const extras = [
71
75
  showGateway ? row.hostName : undefined,
72
76
  info.profile ? `@${info.profile}` : undefined,
@@ -93,7 +97,7 @@ export function SessionItem({
93
97
  for (const extra of extras) {
94
98
  parts.push(<span key={extra}>{extra}</span>)
95
99
  }
96
- const steps = sessionSteps(info, (toolUseId) => (onSelectSubagent ? onSelectSubagent(toolUseId) : onSelect?.()))
100
+ const steps = sessionSteps(info, (toolUseId) => (onSelectSubagent ? onSelectSubagent(toolUseId) : onSelect?.(NO_MODIFIERS)))
97
101
  const holdsOpenAgent = steps.some((s) => s.key === activeStepKey)
98
102
 
99
103
  return (
@@ -106,7 +110,7 @@ export function SessionItem({
106
110
  if (e.detail > 1 || isEditing) {
107
111
  return
108
112
  }
109
- onSelect?.()
113
+ onSelect?.({ meta: e.metaKey, ctrl: e.ctrlKey, alt: e.altKey })
110
114
  }}
111
115
  onKeyDown={(e) => {
112
116
  if (e.target !== e.currentTarget) {
@@ -114,7 +118,7 @@ export function SessionItem({
114
118
  }
115
119
  if (e.key === 'Enter' || e.key === ' ') {
116
120
  e.preventDefault()
117
- onSelect?.()
121
+ onSelect?.({ meta: e.metaKey, ctrl: e.ctrlKey, alt: e.altKey })
118
122
  }
119
123
  }}
120
124
  className={cn(
@@ -36,7 +36,7 @@ export function SessionListItem({ session, active, onSelect, onDelete, className
36
36
  <div className="mt-0.5 flex items-center gap-2 font-mono text-label text-fg-4">
37
37
  <span className="truncate">{session.cwd}</span>
38
38
  {session.profile ? <span className="shrink-0">@{session.profile}</span> : null}
39
- <span className="shrink-0">{formatCost(session.totalCostUsd)}</span>
39
+ <span className="shrink-0">{formatCost(session.costUsd ?? session.totalCostUsd)}</span>
40
40
  <span className="shrink-0">{formatRelativeTime(session.lastActivityAt ?? session.createdAt)}</span>
41
41
  </div>
42
42
  </button>
@@ -328,7 +328,7 @@ export function SessionPanel({
328
328
 
329
329
  const capabilities = state.capabilities
330
330
 
331
- const { usage: profileUsage } = useProfileUsage(client, state.session?.profile, {
331
+ const { usage: profileUsage, spend: profileSpend } = useProfileUsage(client, state.session?.profile, {
332
332
  enabled: capabilities.rateLimits,
333
333
  })
334
334
  const usage = useMemo(
@@ -833,6 +833,9 @@ export function SessionPanel({
833
833
  subscriptionType={state.subscriptionType}
834
834
  engine={state.engine ?? 'claude'}
835
835
  totalCostUsd={state.totalCostUsd}
836
+ costUsd={state.costUsd}
837
+ usageByModel={state.usageByModel}
838
+ spend={profileSpend}
836
839
  updatedAt={usageUpdatedAt}
837
840
  open={panel === 'usage'}
838
841
  onOpenChange={(next) => setPanel(next ? 'usage' : undefined)}
@@ -200,7 +200,7 @@ export function StatusBar({
200
200
  ) : null}
201
201
  {controls ? <span className="self-center">{controls}</span> : null}
202
202
  <span className="flex-1" />
203
- <span className="font-mono text-label text-fg-3">{formatCost(state.totalCostUsd)}</span>
203
+ <span className="font-mono text-label text-fg-3">{formatCost(state.costUsd ?? (state.totalCostUsd || undefined))}</span>
204
204
  {actions ? <span className="self-center">{actions}</span> : null}
205
205
  </div>
206
206
  )