@workerdeck/ui 2.7.1 → 2.8.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 (61) hide show
  1. package/README.md +30 -30
  2. package/build/{SessionPanel-B5FfiBxE.mjs → SessionPanel-Dg4xf-ql.mjs} +46 -46
  3. package/build/SessionPanel-Dg4xf-ql.mjs.map +1 -0
  4. package/build/format.mjs +1 -1
  5. package/build/index.d.mts +11 -11
  6. package/build/index.mjs +4 -4
  7. package/build/index.mjs.map +1 -1
  8. package/build/scoped.css +2 -2
  9. package/build/{status-BO9wJloK.mjs → status-kmsBhuFG.mjs} +3 -3
  10. package/build/status-kmsBhuFG.mjs.map +1 -0
  11. package/build/workspace.mjs +4 -4
  12. package/build/workspace.mjs.map +1 -1
  13. package/package.json +18 -18
  14. package/src/components/agent/Composer.tsx +2 -2
  15. package/src/components/agent/ContextDialog.tsx +1 -1
  16. package/src/components/agent/EntryIcon.tsx +1 -1
  17. package/src/components/agent/FileTree.tsx +1 -1
  18. package/src/components/agent/FileViewer.tsx +1 -1
  19. package/src/components/agent/HostFilesDialog.tsx +2 -2
  20. package/src/components/agent/PermissionModeSelect.tsx +2 -2
  21. package/src/components/agent/PermissionPrompt.tsx +2 -2
  22. package/src/components/agent/SessionBrowser.tsx +1 -1
  23. package/src/components/agent/SessionItem.tsx +1 -1
  24. package/src/components/agent/SessionPanel.tsx +2 -2
  25. package/src/components/agent/SkillsDialog.tsx +3 -3
  26. package/src/components/agent/StatusBar.tsx +1 -1
  27. package/src/components/agent/TaskList.tsx +2 -2
  28. package/src/components/agent/TranscriptRows.tsx +1 -1
  29. package/src/components/agent/UsageDialog.tsx +1 -1
  30. package/src/components/agent/use-subagent-frame.ts +2 -2
  31. package/src/components/agent/use-transcript-jumps.ts +2 -2
  32. package/src/components/prompt-area/clipboard-helpers.ts +2 -2
  33. package/src/components/prompt-area/cursor-helpers.ts +7 -7
  34. package/src/components/prompt-area/dom-helpers.ts +8 -8
  35. package/src/components/prompt-area/html-to-markdown.ts +2 -2
  36. package/src/components/prompt-area/prompt-area-engine.ts +4 -4
  37. package/src/components/prompt-area/prompt-area-list-ops.ts +10 -10
  38. package/src/components/prompt-area/prompt-area.tsx +2 -2
  39. package/src/components/prompt-area/trigger-presets.ts +6 -6
  40. package/src/components/prompt-area/types.ts +6 -6
  41. package/src/components/prompt-area/use-chip-editing.ts +8 -8
  42. package/src/components/prompt-area/use-markdown-mode.ts +3 -3
  43. package/src/components/prompt-area/use-prompt-area-events.ts +3 -3
  44. package/src/components/prompt-area/use-prompt-area-keydown.ts +3 -3
  45. package/src/components/prompt-area/use-prompt-area-state.ts +4 -4
  46. package/src/components/prompt-area/use-prompt-area.ts +5 -5
  47. package/src/components/prompt-area/use-trigger-search.ts +1 -1
  48. package/src/components/terminal/PermissionPrompt.tsx +4 -4
  49. package/src/components/terminal/affordances.tsx +1 -1
  50. package/src/components/terminal/height.ts +2 -2
  51. package/src/components/terminal/items.tsx +2 -2
  52. package/src/components/terminal/scrubber.tsx +1 -1
  53. package/src/components/terminal/todos.ts +1 -1
  54. package/src/components/ui/Splitter.tsx +1 -1
  55. package/src/lib/context-note.ts +3 -3
  56. package/src/lib/format.ts +2 -2
  57. package/src/styles/scoped.entry.css +2 -2
  58. package/src/styles/terminal.css +79 -79
  59. package/src/styles/theme.css +39 -39
  60. package/build/SessionPanel-B5FfiBxE.mjs.map +0 -1
  61. package/build/status-BO9wJloK.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-kmsBhuFG.mjs","names":[],"sources":["../src/lib/format.ts","../src/lib/status.ts"],"sourcesContent":["// Here rather than beside the row: height.ts must measure the string items.tsx draws, and cannot import a .tsx.\nexport const COMPACTION_TEXT = 'context compacted · earlier turns summarised to fit the window'\n\nexport function compactionText(item: {\n pending?: boolean\n trigger?: 'manual' | 'auto'\n preTokens?: number\n postTokens?: number\n error?: string\n}): string {\n if (item.error) {\n return `context compaction failed · ${item.error}`\n }\n if (item.pending) {\n return 'compacting context · summarising earlier turns to fit the window'\n }\n const window =\n item.preTokens === undefined\n ? undefined\n : item.postTokens === undefined\n ? `from ${formatTokens(item.preTokens)}`\n : `${formatTokens(item.preTokens)} → ${formatTokens(item.postTokens)}`\n // The trigger is worth a word only when nobody asked for it: an automatic compaction mid-turn\n // is the one a reader is surprised by.\n const auto = item.trigger === 'auto' ? 'automatic' : undefined\n return [COMPACTION_TEXT, window, auto].filter(Boolean).join(' · ')\n}\n\nexport function formatCost(usd: number | undefined): string {\n if (usd === undefined || Number.isNaN(usd)) {\n return '-'\n }\n if (usd === 0) {\n return '$0.00'\n }\n if (usd < 0.01) {\n return '<$0.01'\n }\n return `$${usd.toFixed(2)}`\n}\n\nexport function formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)}ms`\n }\n const s = ms / 1000\n if (s < 60) {\n return `${s.toFixed(1)}s`\n }\n const m = Math.floor(s / 60)\n return `${m}m ${Math.round(s % 60)}s`\n}\n\nexport function formatTokens(tokens: number): string {\n if (tokens >= 1_000_000) {\n return `${(tokens / 1_000_000).toFixed(1)}M`\n }\n if (tokens >= 1000) {\n return `${(tokens / 1000).toFixed(1)}k`\n }\n return String(Math.round(tokens))\n}\n\nexport function formatBytes(bytes: number): string {\n if (bytes >= 1024 * 1024) {\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n }\n if (bytes >= 1024) {\n return `${(bytes / 1024).toFixed(1)} KB`\n }\n return `${bytes} B`\n}\n\nexport function formatCountdown(untilEpochMs: number, now = Date.now()): string {\n const remaining = untilEpochMs - now\n if (remaining <= 0) {\n return 'now'\n }\n const minutes = Math.ceil(remaining / 60_000)\n if (minutes < 1) {\n return '<1m'\n }\n if (minutes < 60) {\n return `${minutes}m`\n }\n const days = Math.floor(minutes / (60 * 24))\n if (days >= 1) {\n return `${days}d ${Math.floor((minutes % (60 * 24)) / 60)}h`\n }\n return `${Math.floor(minutes / 60)}h ${minutes % 60}m`\n}\n\nexport function formatRelativeTime(epochMs: number | undefined, now = Date.now()): string {\n if (!epochMs) {\n return '-'\n }\n const diff = Math.max(0, now - epochMs)\n const s = Math.floor(diff / 1000)\n if (s < 60) {\n return 'just now'\n }\n const m = Math.floor(s / 60)\n if (m < 60) {\n return `${m}m ago`\n }\n const h = Math.floor(m / 60)\n if (h < 24) {\n return `${h}h ago`\n }\n const d = Math.floor(h / 24)\n return `${d}d ago`\n}\n\nexport function formatRateLimitWindow(key: string): string {\n if (key === 'five_hour') {\n return '5h'\n }\n if (key === 'seven_day') {\n return '7d'\n }\n const spaced = key.replaceAll('_', ' ')\n return key.startsWith('seven_day_') ? `7d ${spaced.slice('seven day '.length)}` : spaced\n}\n\nexport function formatRateLimitWindowLong(key: string): string {\n if (key === 'five_hour') {\n return '5-hour session'\n }\n if (key === 'seven_day') {\n return 'Weekly'\n }\n if (key === 'seven_day_oauth_apps') {\n return 'Weekly · apps'\n }\n const capitalize = (s: string) => s.replace(/\\b\\w/g, (c) => c.toUpperCase())\n if (!key.startsWith('seven_day_')) {\n return capitalize(key.replaceAll('_', ' '))\n }\n return `Weekly · ${capitalize(key.slice('seven_day_'.length).replaceAll('_', ' '))}`\n}\n\nexport function rateLimitWindowSeconds(key: string): number | undefined {\n if (key === 'five_hour') {\n return 5 * 3600\n }\n if (key.startsWith('seven_day')) {\n return 7 * 86_400\n }\n return undefined\n}\n\nexport function formatAgoPrecise(epochMs: number, now = Date.now()): string {\n const seconds = Math.max(0, Math.floor((now - epochMs) / 1000))\n if (seconds < 60) {\n return `${seconds} sec${seconds === 1 ? '' : 's'} ago`\n }\n if (seconds < 3600) {\n const minutes = Math.floor(seconds / 60)\n return `${minutes} min${minutes === 1 ? '' : 's'} ago`\n }\n const hours = Math.floor(seconds / 3600)\n return `${hours} hour${hours === 1 ? '' : 's'} ago`\n}\n\nexport function toolInputPreview(input: unknown, max = 80): string {\n if (input === null || input === undefined) {\n return ''\n }\n if (typeof input === 'object') {\n const o = input as Record<string, unknown>\n const primary = o.command ?? o.file_path ?? o.path ?? o.url ?? o.pattern ?? o.query ?? o.description\n if (typeof primary === 'string') {\n return primary.length > max ? primary.slice(0, max - 1) + '…' : primary\n }\n }\n const text = JSON.stringify(input) ?? ''\n return text.length > max ? text.slice(0, max - 1) + '…' : text\n}\n\nconst MODEL_FAMILIES: Record<string, { name: string; joiner?: string }> = {\n gpt: { name: 'GPT', joiner: '-' },\n deepseek: { name: 'DeepSeek' },\n glm: { name: 'GLM' },\n qwen: { name: 'Qwen' },\n kimi: { name: 'Kimi' },\n llama: { name: 'Llama' },\n mistral: { name: 'Mistral' },\n grok: { name: 'Grok' },\n}\n\nexport function friendlyModel(id: string | undefined): string | undefined {\n if (!id) {\n return undefined\n }\n const withoutVariant = id.split('[')[0] ?? id\n const parts = withoutVariant.toLowerCase().split('-').filter(Boolean)\n if (parts[0] === 'claude') {\n parts.shift()\n }\n const familyToken = parts.shift()\n if (!familyToken) {\n return id\n }\n const family = MODEL_FAMILIES[familyToken]\n const name =\n family?.name ??\n // OpenAI's reasoning series is lower-case by its own convention ('o3-mini'); \"O3\" reads as a different product.\n (/^o\\d+$/.test(familyToken) ? familyToken : `${familyToken.charAt(0).toUpperCase()}${familyToken.slice(1)}`)\n\n const version: string[] = []\n const words: string[] = []\n for (const part of parts) {\n if (/^\\d{8}$/.test(part)) {\n continue\n }\n if (/^\\d+(\\.\\d+)?$/.test(part)) {\n version.push(part)\n } else {\n words.push(`${part.charAt(0).toUpperCase()}${part.slice(1)}`)\n }\n }\n const versioned = version.length > 0 ? `${name}${family?.joiner ?? ' '}${version.join('.')}` : name\n return [versioned, ...words].join(' ')\n}\n","import type { ModelOption, RateLimitInfo, SessionStatus } from '@workerdeck/protocol'\n\nexport type StatusSeverity = 'none' | 'warning' | 'error'\n\nexport type StatusPresentation = {\n icon: string\n label: string\n severity: StatusSeverity\n}\n\nexport type StatusReadings = {\n status: SessionStatus\n connection?: 'live' | 'reconnecting' | 'offline'\n}\n\nconst STATUS_META: Record<SessionStatus, StatusPresentation> = {\n starting: { icon: 'loading~spin', label: 'Starting', severity: 'none' },\n running: { icon: 'loading~spin', label: 'Running', severity: 'none' },\n awaiting_approval: { icon: 'warning', label: 'Needs approval', severity: 'warning' },\n idle: { icon: 'check', label: 'Idle', severity: 'none' },\n parked: { icon: 'debug-pause', label: 'Parked', severity: 'none' },\n failed: { icon: 'error', label: 'Failed', severity: 'error' },\n closed: { icon: 'circle-slash', label: 'Closed', severity: 'none' },\n}\n\nexport function statusPresentation(vitals: StatusReadings | undefined): StatusPresentation {\n if (!vitals) {\n return { icon: 'hubot', label: 'Connecting…', severity: 'none' }\n }\n if (vitals.connection === 'offline') {\n return { icon: 'debug-disconnect', label: 'Offline', severity: 'error' }\n }\n if (vitals.connection === 'reconnecting') {\n return { icon: 'sync~spin', label: 'Reconnecting…', severity: 'warning' }\n }\n return STATUS_META[vitals.status] ?? { icon: 'hubot', label: vitals.status, severity: 'none' }\n}\n\nexport function meterSeverity(pct: number | undefined): StatusSeverity {\n if (pct === undefined) {\n return 'none'\n }\n if (pct >= 95) {\n return 'error'\n }\n if (pct >= 80) {\n return 'warning'\n }\n return 'none'\n}\n\nexport type UsageLane = 'session' | 'weekly' | 'model'\n\nexport function usageWindow(\n rateLimits: Record<string, RateLimitInfo> | undefined,\n lane: UsageLane,\n): { key: string; info: RateLimitInfo } | undefined {\n if (!rateLimits) {\n return undefined\n }\n if (lane === 'session') {\n const info = rateLimits.five_hour\n return info ? { key: 'five_hour', info } : undefined\n }\n if (lane === 'weekly') {\n const info = rateLimits.seven_day\n return info ? { key: 'seven_day', info } : undefined\n }\n const scoped = Object.fromEntries(\n Object.entries(rateLimits).filter(([key]) => key.startsWith('seven_day_') && key !== 'seven_day_oauth_apps'),\n )\n return tightestWindow(scoped)\n}\n\nexport function tightestWindow(rateLimits: Record<string, RateLimitInfo> | undefined): { key: string; info: RateLimitInfo } | undefined {\n const entries = Object.entries(rateLimits ?? {})\n if (entries.length === 0) {\n return undefined\n }\n let best: { key: string; info: RateLimitInfo } | undefined\n for (const [key, info] of entries) {\n const rank = info.status === 'rejected' ? Number.POSITIVE_INFINITY : (info.utilization ?? -1)\n const bestRank =\n best === undefined\n ? Number.NEGATIVE_INFINITY\n : best.info.status === 'rejected'\n ? Number.POSITIVE_INFINITY\n : (best.info.utilization ?? -1)\n if (rank > bestRank) {\n best = { key, info }\n }\n }\n return best\n}\n\nexport function windowLabel(key: string): string {\n if (key === 'five_hour') {\n return 'Session'\n }\n if (key === 'seven_day') {\n return 'Weekly'\n }\n const scoped = key.startsWith('seven_day_') ? key.slice('seven_day_'.length) : key\n const words = scoped.replaceAll('_', ' ')\n return words.charAt(0).toUpperCase() + words.slice(1)\n}\n\nexport type ModelReadings = { model?: string; models: readonly ModelOption[] }\n\nexport function currentModel(vitals: ModelReadings | undefined): ModelOption | undefined {\n const id = vitals?.model\n if (!id) {\n return undefined\n }\n const bare = (value: string) => value.replace(/\\[.*\\]$/, '')\n const wanted = bare(id)\n return vitals.models.find((m) => bare(m.value) === wanted || (m.resolvedModel && bare(m.resolvedModel) === wanted))\n}\n\nexport function modelLabel(vitals: ModelReadings | undefined): string {\n if (!vitals?.model) {\n return 'Default'\n }\n return currentModel(vitals)?.displayName ?? vitals.model\n}\n\nexport function contextSeverity(usage: { percentage: number } | undefined): StatusSeverity {\n return meterSeverity(usage?.percentage)\n}\n\nexport function meterColorClass(pct: number | undefined): string {\n const severity = meterSeverity(pct)\n return severity === 'error' ? 'text-danger' : severity === 'warning' ? 'text-warning' : 'text-fg-3'\n}\n\n// The fill colour for a meter bar. Deliberately a different ramp from\n// {@link meterColorClass}: a bar reads as alarming later than a number does, so the tint\n// turns at 70/90 where the text severity turns at 80/95.\nexport function meterTintClass(pct: number): string {\n return pct >= 90 ? 'bg-danger' : pct >= 70 ? 'bg-warning' : 'bg-accent'\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { Et as TooltipProvider, Qt as cn, R as EntryIcon, Xt as Button, qt as Input, t as SessionPanel, wt as Tip, xt as Spinner, yt as Splitter } from "./SessionPanel-B5FfiBxE.mjs";
2
- import { m as formatBytes } from "./status-BO9wJloK.mjs";
1
+ import { Et as TooltipProvider, Qt as cn, R as EntryIcon, Xt as Button, qt as Input, t as SessionPanel, wt as Tip, xt as Spinner, yt as Splitter } from "./SessionPanel-Dg4xf-ql.mjs";
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";
5
5
  import { ChevronRight, File, FileWarning, Folder, FolderOpen, PanelLeftClose, PanelLeftOpen, RefreshCw, Search, TriangleAlert, X } from "lucide-react";
@@ -212,7 +212,7 @@ function FileTree({ tree, search, activePath, onOpenFile, onCollapse, style, cla
212
212
  }), row.truncated ? /* @__PURE__ */ jsx("p", {
213
213
  className: "truncate py-0.5 text-label text-fg-4",
214
214
  style: { paddingLeft: (row.depth + 1) * INDENT + 22 },
215
- children: "More entries than the server will return — use search."
215
+ children: "More entries than the server will return - use search."
216
216
  }) : null]
217
217
  }, row.entry.path))
218
218
  })
@@ -539,7 +539,7 @@ function FileViewer({ file, canWrite, onChange, onSave, onRevert, onReload, onOv
539
539
  children: "Saving…"
540
540
  })] }) : !canWrite ? /* @__PURE__ */ jsx("span", {
541
541
  className: "text-label text-fg-4",
542
- children: "Read-only — this gateway doesn’t allow writes."
542
+ children: "Read-only - this gateway doesn’t allow writes."
543
543
  }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
544
544
  /* @__PURE__ */ jsx("span", {
545
545
  className: "text-label text-fg-3",
@@ -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'\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 if (path.startsWith('/')) {\n return path\n }\n if (!cwd) {\n return path\n }\n return `${cwd.replace(/\\/$/, '')}/${path.replace(/^\\.\\//, '')}`\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 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 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 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/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'\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 if (path.startsWith('/')) {\n return path\n }\n if (!cwd) {\n return path\n }\n return `${cwd.replace(/\\/$/, '')}/${path.replace(/^\\.\\//, '')}`\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 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 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 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.7.1",
3
+ "version": "2.8.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",
@@ -30,18 +30,18 @@
30
30
  "./scoped.css": "./build/scoped.css"
31
31
  },
32
32
  "dependencies": {
33
- "@base-ui/react": "^1.7.0",
34
- "@tanstack/react-virtual": "^3.14.10",
33
+ "@base-ui/react": "^1.8.0",
34
+ "@tanstack/react-virtual": "^3.14.13",
35
35
  "class-variance-authority": "^0.7.1",
36
36
  "clsx": "^2.1.1",
37
- "lucide-react": "^1.38.0",
37
+ "lucide-react": "^1.47.0",
38
38
  "sonner": "^2.0.8",
39
39
  "streamdown": "^2.6.0",
40
- "tailwind-merge": "^3.6.0",
40
+ "tailwind-merge": "^3.7.0",
41
41
  "use-stick-to-bottom": "^1.1.6",
42
- "@workerdeck/react": "2.7.1",
43
- "@workerdeck/protocol": "2.7.1",
44
- "@workerdeck/client": "2.7.1"
42
+ "@workerdeck/client": "2.8.0",
43
+ "@workerdeck/protocol": "2.8.0",
44
+ "@workerdeck/react": "2.8.0"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "monaco-editor": "^0.56.0",
@@ -56,23 +56,23 @@
56
56
  "devDependencies": {
57
57
  "@fontsource/inter": "^5.3.0",
58
58
  "@fontsource/jetbrains-mono": "^5.3.0",
59
- "@storybook/react-vite": "^10.5.10",
59
+ "@storybook/react-vite": "^10.6.0",
60
60
  "@tailwindcss/postcss": "^4.3.3",
61
61
  "@tailwindcss/vite": "^4.3.3",
62
- "@types/node": "^26.4.0",
63
- "@types/react": "^19.2.18",
64
- "@types/react-dom": "^19.2.5",
62
+ "@types/node": "^26.6.1",
63
+ "@types/react": "^19.3.0",
64
+ "@types/react-dom": "^19.3.0",
65
65
  "@vitejs/plugin-react-swc": "^4.3.3",
66
66
  "monaco-editor": "^0.56.0",
67
- "postcss": "^8.5.26",
68
- "postcss-selector-parser": "^7.1.5",
69
- "react": "^19.2.8",
70
- "react-dom": "^19.2.8",
67
+ "postcss": "^8.5.28",
68
+ "postcss-selector-parser": "^7.1.6",
69
+ "react": "^19.3.0",
70
+ "react-dom": "^19.3.0",
71
71
  "rimraf": "^6.1.3",
72
- "storybook": "^10.5.10",
72
+ "storybook": "^10.6.0",
73
73
  "tailwindcss": "^4.3.3",
74
74
  "tsdown": "^0.22.14",
75
- "vite": "^8.2.2",
75
+ "vite": "^8.3.0",
76
76
  "vitest": "^4.1.11"
77
77
  },
78
78
  "author": "Tobias Strebitzer",
@@ -46,7 +46,7 @@ export interface ComposerProps {
46
46
  skills?: SkillInfo[]
47
47
  onSearchFiles?: (query: string, options: { signal: AbortSignal }) => Promise<ComposerFileMatch[]>
48
48
  // Shell mode: `!` as the first character turns the composer into a host shell prompt. Omit to leave the
49
- // mode off entirely — the gateway only offers it to an operator on a session whose engine reaches a host cwd.
49
+ // mode off entirely - the gateway only offers it to an operator on a session whose engine reaches a host cwd.
50
50
  onShellCommand?: (command: string) => void
51
51
  attachments?: UseAttachmentsResult
52
52
  toolbar?: ReactNode
@@ -544,7 +544,7 @@ function AttachmentChip({ item, onRetry, onRemove }: { item: StagedAttachment; o
544
544
  const terminal = useTranscriptVariant() === 'terminal'
545
545
  const round = terminal ? '' : 'rounded-md'
546
546
  return (
547
- <div className="group relative shrink-0" title={failed ? `${item.name} — ${item.error}` : `${item.name} · ${formatBytes(item.bytes)}`}>
547
+ <div className="group relative shrink-0" title={failed ? `${item.name} - ${item.error}` : `${item.name} · ${formatBytes(item.bytes)}`}>
548
548
  <div
549
549
  className={cn(
550
550
  'flex size-14 items-center justify-center overflow-hidden bg-surface',
@@ -23,7 +23,7 @@ export function ContextDialog({ usage, engine, open, onOpenChange, className }:
23
23
  <DialogBody>
24
24
  {!usage ? (
25
25
  <p className="py-6 text-center text-body-sm text-fg-4">
26
- No reading yet — the context window is measured after a turn completes.
26
+ No reading yet - the context window is measured after a turn completes.
27
27
  </p>
28
28
  ) : (
29
29
  <>
@@ -2,7 +2,7 @@ import { File, Folder, Link2 } from 'lucide-react'
2
2
  import type { HostDirEntry } from '@workerdeck/protocol'
3
3
 
4
4
  // The icon for one host directory entry. A symlink is drawn as a link and never silently
5
- // resolved — the caller decides what a symlink means, the tree only says it is one.
5
+ // resolved - the caller decides what a symlink means, the tree only says it is one.
6
6
  export function EntryIcon({ type }: { type: HostDirEntry['type'] }) {
7
7
  if (type === 'dir') {
8
8
  return <Folder className="size-3.5 shrink-0 text-accent" />
@@ -147,7 +147,7 @@ export function FileTree({ tree, search, activePath, onOpenFile, onCollapse, sty
147
147
  />
148
148
  {row.truncated ? (
149
149
  <p className="truncate py-0.5 text-label text-fg-4" style={{ paddingLeft: (row.depth + 1) * INDENT + 22 }}>
150
- More entries than the server will return — use search.
150
+ More entries than the server will return - use search.
151
151
  </p>
152
152
  ) : null}
153
153
  </li>
@@ -93,7 +93,7 @@ export function FileViewer({
93
93
  <span className="text-label text-fg-4">Saving…</span>
94
94
  </>
95
95
  ) : !canWrite ? (
96
- <span className="text-label text-fg-4">Read-only — this gateway doesn’t allow writes.</span>
96
+ <span className="text-label text-fg-4">Read-only - this gateway doesn’t allow writes.</span>
97
97
  ) : (
98
98
  <>
99
99
  <span className="text-label text-fg-3">Unsaved changes</span>
@@ -84,7 +84,7 @@ export function HostFilesDialog({ client, cwd, open, onOpenChange }: HostFilesDi
84
84
  setFile({
85
85
  path: response.path,
86
86
  bytes: response.bytes,
87
- content: response.encoding === 'utf8' ? response.content : '(binary file — not shown)',
87
+ content: response.encoding === 'utf8' ? response.content : '(binary file - not shown)',
88
88
  })
89
89
  } catch (e) {
90
90
  setError(e instanceof Error ? e.message : 'Could not read that file')
@@ -178,7 +178,7 @@ export function HostFilesDialog({ client, cwd, open, onOpenChange }: HostFilesDi
178
178
  </ul>
179
179
  )}
180
180
  {truncated && !matches ? (
181
- <p className="text-label text-fg-4">More entries than the server will return — use the search box.</p>
181
+ <p className="text-label text-fg-4">More entries than the server will return - use the search box.</p>
182
182
  ) : null}
183
183
  </>
184
184
  )}
@@ -46,14 +46,14 @@ export const PERMISSION_MODES: PermissionModeMeta[] = [
46
46
  value: 'dontAsk',
47
47
  label: "Don't ask",
48
48
  shortLabel: "Don't ask",
49
- description: 'Never ask — deny anything not pre-approved',
49
+ description: 'Never ask - deny anything not pre-approved',
50
50
  icon: ShieldCheck,
51
51
  },
52
52
  {
53
53
  value: 'bypassPermissions',
54
54
  label: 'Bypass permissions',
55
55
  shortLabel: 'Bypass',
56
- description: 'Skip every approval — the agent is unsupervised',
56
+ description: 'Skip every approval - the agent is unsupervised',
57
57
  icon: TriangleAlert,
58
58
  dangerous: true,
59
59
  },
@@ -96,8 +96,8 @@ export function PermissionPrompt({ request, onApprove, onDeny, className }: Perm
96
96
  }}
97
97
  placeholder={
98
98
  plan
99
- ? 'What should change? (optional) — the agent keeps planning and reads this'
100
- : 'Reason (optional) — the agent reads this and can try something else'
99
+ ? 'What should change? (optional) - the agent keeps planning and reads this'
100
+ : 'Reason (optional) - the agent reads this and can try something else'
101
101
  }
102
102
  className="h-7 flex-1"
103
103
  />
@@ -284,7 +284,7 @@ function SessionRowItem({
284
284
  {onClearContext && info.capabilities?.clearContext ? (
285
285
  <RowAction
286
286
  label="Clear context"
287
- title="Clear the conversation — the session keeps running and the old conversation stays resumable"
287
+ title="Clear the conversation - the session keeps running and the old conversation stays resumable"
288
288
  onClick={() => onClearContext(row)}
289
289
  >
290
290
  <Eraser className="size-3 text-fg-3" />
@@ -70,7 +70,7 @@ export function SessionItem({
70
70
  const extras = [
71
71
  showGateway ? row.hostName : undefined,
72
72
  info.profile ? `@${info.profile}` : undefined,
73
- cost === '—' ? undefined : cost,
73
+ cost === '-' ? undefined : cost,
74
74
  ].filter((part): part is string => Boolean(part))
75
75
 
76
76
  const model = friendlyModel(info.model)
@@ -122,7 +122,7 @@ export interface SessionPanelProps {
122
122
  affordances?: TerminalAffordances | boolean
123
123
  terminalMetrics?: TerminalMetrics
124
124
  scrubber?: boolean
125
- // Bookmarked transcript item ids — the host owns membership and persistence.
125
+ // Bookmarked transcript item ids - the host owns membership and persistence.
126
126
  bookmarks?: readonly string[]
127
127
  onToggleBookmark?: (itemId: string) => void
128
128
  reveal?: { toolUseId: string; nonce: number }
@@ -189,7 +189,7 @@ export type SessionVitals = {
189
189
  contextUsage: TranscriptState['contextUsage']
190
190
  tasks: SessionTask[]
191
191
  rateLimits: TranscriptState['rateLimits']
192
- // When the newest window in `rateLimits` was reported, as event time — not receive time. External chrome (the
192
+ // When the newest window in `rateLimits` was reported, as event time - not receive time. External chrome (the
193
193
  // VS Code status bar, iOS) cannot otherwise tell a live reading from one a days-old session just replayed.
194
194
  // Absent means unknown, which is not the same as fresh.
195
195
  rateLimitsUpdatedAt: number | undefined
@@ -42,7 +42,7 @@ export function SkillsDialog({ skills, open, onOpenChange, onUse, className }: S
42
42
  ? skill.name !== (skill.displayName ?? skill.name)
43
43
  ? skill.name
44
44
  : undefined
45
- : 'Capabilities the agent can choose to use. Not commands — the model picks them from their descriptions.'
45
+ : 'Capabilities the agent can choose to use. Not commands - the model picks them from their descriptions.'
46
46
  }
47
47
  actions={
48
48
  skill ? (
@@ -67,7 +67,7 @@ export function SkillsDialog({ skills, open, onOpenChange, onUse, className }: S
67
67
 
68
68
  function SkillList({ skills, onSelect }: { skills: SkillInfo[] | undefined; onSelect: (name: string) => void }) {
69
69
  if (!skills) {
70
- return <p className="py-6 text-center text-body-sm text-fg-4">Skills are listed once the session connects — send a message first.</p>
70
+ return <p className="py-6 text-center text-body-sm text-fg-4">Skills are listed once the session connects - send a message first.</p>
71
71
  }
72
72
  if (skills.length === 0) {
73
73
  return <p className="py-6 text-center text-body-sm text-fg-4">This session found no skills.</p>
@@ -142,7 +142,7 @@ function SkillView({ skill, onUse, onUsed }: { skill: SkillInfo; onUse?: (skill:
142
142
  Use this skill
143
143
  </Button>
144
144
  <p className="mt-1.5 text-label text-fg-4">
145
- Writes an opening message into the composer for you to edit — it isn’t sent, and there is no command that runs a skill directly.
145
+ Writes an opening message into the composer for you to edit - it isn’t sent, and there is no command that runs a skill directly.
146
146
  </p>
147
147
  </div>
148
148
  ) : null}
@@ -85,7 +85,7 @@ function RateLimitMeter({ label, info, now }: { label: string; info: RateLimitIn
85
85
  <div className="flex min-w-36 flex-col gap-1 py-0.5">
86
86
  <div className="flex items-center justify-between gap-2">
87
87
  <span>{label} usage</span>
88
- <span className="font-mono text-fg-3">{pct !== undefined ? `${pct.toFixed(1)}%` : '—'}</span>
88
+ <span className="font-mono text-fg-3">{pct !== undefined ? `${pct.toFixed(1)}%` : '-'}</span>
89
89
  </div>
90
90
  {resetsAtMs !== undefined ? (
91
91
  <div className="flex items-center justify-between gap-2">