@workerdeck/ui 0.23.0 → 1.0.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.
- package/build/{SessionPanel-DUt2VzXG.mjs → SessionPanel-DDgBkHsi.mjs} +2151 -3357
- package/build/SessionPanel-DDgBkHsi.mjs.map +1 -0
- package/build/SessionPanel-E_L_ldQ5.d.mts +207 -0
- package/build/format-B8ZM9LPy.d.mts +16 -0
- package/build/format.d.mts +8 -75
- package/build/format.mjs +2 -2
- package/build/index.d.mts +63 -1138
- package/build/index.mjs +4 -300
- package/build/index.mjs.map +1 -1
- package/build/scoped.css +15 -23
- package/build/{status-BE-zg88x.mjs → status-C0HSLZas.mjs} +14 -85
- package/build/status-C0HSLZas.mjs.map +1 -0
- package/build/workspace.d.mts +5 -126
- package/build/workspace.mjs +11 -143
- package/build/workspace.mjs.map +1 -1
- package/package.json +4 -4
- package/src/components/agent/CodeEditor.tsx +31 -103
- package/src/components/agent/Composer.tsx +114 -333
- package/src/components/agent/ContextDialog.tsx +20 -41
- package/src/components/agent/ContextRing.tsx +6 -28
- package/src/components/agent/Conversation.tsx +21 -33
- package/src/components/agent/EditorTabs.tsx +39 -67
- package/src/components/agent/EngineIcon.tsx +29 -72
- package/src/components/agent/EntryIcon.tsx +16 -0
- package/src/components/agent/FileCard.tsx +10 -16
- package/src/components/agent/FileTree.tsx +52 -125
- package/src/components/agent/FileViewer.tsx +30 -60
- package/src/components/agent/HostFilesDialog.tsx +40 -69
- package/src/components/agent/Loader.tsx +3 -24
- package/src/components/agent/McpDialog.tsx +64 -137
- package/src/components/agent/Message.tsx +2 -21
- package/src/components/agent/ModelSelect.tsx +33 -62
- package/src/components/agent/PermissionModeSelect.tsx +21 -79
- package/src/components/agent/PermissionPrompt.tsx +59 -63
- package/src/components/agent/ProjectIcon.tsx +228 -93
- package/src/components/agent/PromptTokenText.tsx +10 -16
- package/src/components/agent/QuestionPrompt.tsx +43 -64
- package/src/components/agent/Reasoning.tsx +13 -16
- package/src/components/agent/Response.tsx +3 -9
- package/src/components/agent/Scrubber.tsx +31 -80
- package/src/components/agent/SessionBrowser.tsx +64 -243
- package/src/components/agent/SessionEmptyState.tsx +10 -40
- package/src/components/agent/SessionInfoDialog.tsx +46 -78
- package/src/components/agent/SessionItem.tsx +53 -320
- package/src/components/agent/SessionList.tsx +23 -39
- package/src/components/agent/SessionPanel.tsx +315 -996
- package/src/components/agent/SessionStatusIcon.tsx +14 -28
- package/src/components/agent/SessionSteps.tsx +34 -187
- package/src/components/agent/SessionWorkspace.tsx +104 -219
- package/src/components/agent/SkillsDialog.tsx +35 -80
- package/src/components/agent/StatusBar.tsx +51 -136
- package/src/components/agent/SubagentStrip.tsx +12 -77
- package/src/components/agent/ToolCallCard.tsx +94 -135
- package/src/components/agent/Transcript.tsx +103 -1288
- package/src/components/agent/TranscriptItemView.tsx +155 -0
- package/src/components/agent/TranscriptRows.tsx +418 -0
- package/src/components/agent/UsageDialog.tsx +7 -29
- package/src/components/agent/UsageMeters.tsx +18 -73
- package/src/components/agent/pulse.tsx +9 -32
- package/src/components/agent/scrubber-marks.ts +33 -103
- package/src/components/agent/status.ts +1 -5
- package/src/components/agent/tool-result-fetch.tsx +3 -25
- package/src/components/agent/tool-result-image.tsx +32 -111
- package/src/components/agent/transcript-rows.ts +41 -118
- package/src/components/agent/transcript-variant.tsx +3 -80
- package/src/components/agent/use-height-epoch.ts +10 -24
- package/src/components/agent/use-path-links.ts +45 -63
- package/src/components/agent/use-subagent-frame.ts +106 -0
- package/src/components/agent/use-transcript-jumps.ts +49 -107
- package/src/components/prompt-area/animated-placeholder.tsx +6 -5
- package/src/components/prompt-area/clipboard-helpers.ts +26 -21
- package/src/components/prompt-area/cursor-helpers.ts +79 -37
- package/src/components/prompt-area/dom-helpers.ts +97 -69
- package/src/components/prompt-area/file-strip.tsx +90 -80
- package/src/components/prompt-area/html-to-markdown.ts +67 -46
- package/src/components/prompt-area/image-strip.tsx +8 -18
- package/src/components/prompt-area/index.ts +2 -15
- package/src/components/prompt-area/prompt-area-engine.ts +100 -115
- package/src/components/prompt-area/prompt-area-list-ops.ts +66 -59
- package/src/components/prompt-area/prompt-area.tsx +31 -38
- package/src/components/prompt-area/remove-button.tsx +3 -9
- package/src/components/prompt-area/segment-helpers.ts +4 -12
- package/src/components/prompt-area/trigger-popover.tsx +15 -27
- package/src/components/prompt-area/trigger-presets.ts +0 -10
- package/src/components/prompt-area/types.ts +3 -8
- package/src/components/prompt-area/use-chip-editing.ts +305 -0
- package/src/components/prompt-area/use-markdown-mode.ts +7 -17
- package/src/components/prompt-area/use-prompt-area-events.ts +80 -86
- package/src/components/prompt-area/use-prompt-area-keydown.ts +598 -0
- package/src/components/prompt-area/use-prompt-area-state.ts +5 -23
- package/src/components/prompt-area/use-prompt-area.ts +170 -896
- package/src/components/prompt-area/use-trigger-search.ts +21 -19
- package/src/components/terminal/PermissionPrompt.tsx +56 -59
- package/src/components/terminal/QuestionPrompt.tsx +28 -79
- package/src/components/terminal/StatusLine.tsx +13 -63
- package/src/components/terminal/TerminalTranscript.tsx +44 -160
- package/src/components/terminal/affordances.tsx +68 -98
- package/src/components/terminal/blocks.ts +20 -156
- package/src/components/terminal/diff.tsx +19 -67
- package/src/components/terminal/height.ts +163 -313
- package/src/components/terminal/image-box.ts +0 -44
- package/src/components/terminal/items.tsx +150 -305
- package/src/components/terminal/markdown.tsx +36 -95
- package/src/components/terminal/press.tsx +26 -53
- package/src/components/terminal/prompt.tsx +40 -170
- package/src/components/terminal/result-preview.ts +8 -56
- package/src/components/terminal/row.tsx +11 -98
- package/src/components/terminal/scrubber.tsx +108 -401
- package/src/components/terminal/surface.tsx +6 -54
- package/src/components/terminal/todos.ts +66 -0
- package/src/components/terminal/tool-run.ts +21 -192
- package/src/components/ui/AlertDialog.tsx +8 -23
- package/src/components/ui/Badge.tsx +18 -29
- package/src/components/ui/Button.tsx +7 -15
- package/src/components/ui/Card.tsx +2 -13
- package/src/components/ui/CodeBlock.tsx +10 -37
- package/src/components/ui/CopyButton.tsx +10 -23
- package/src/components/ui/Dialog.tsx +23 -36
- package/src/components/ui/Empty.tsx +6 -29
- package/src/components/ui/Input.tsx +14 -16
- package/src/components/ui/Menu.tsx +6 -18
- package/src/components/ui/PortalScope.tsx +1 -21
- package/src/components/ui/ProgressRing.tsx +8 -19
- package/src/components/ui/Select.tsx +18 -31
- package/src/components/ui/Sonner.tsx +1 -2
- package/src/components/ui/Spinner.tsx +1 -1
- package/src/components/ui/Splitter.tsx +30 -64
- package/src/components/ui/Textarea.tsx +2 -5
- package/src/components/ui/Tooltip.tsx +3 -13
- package/src/format.ts +0 -9
- package/src/index.ts +16 -113
- package/src/lib/clipboard.ts +8 -28
- package/src/lib/css.ts +8 -0
- package/src/lib/format.ts +101 -86
- package/src/lib/plan-request.ts +16 -0
- package/src/lib/status.ts +45 -95
- package/src/lib/tool-icon.ts +39 -47
- package/src/lib/utils.ts +1 -3
- package/src/styles/terminal.css +81 -49
- package/src/styles/theme.css +96 -32
- package/src/workspace.ts +1 -24
- package/build/SessionPanel-BobynUo4.d.mts +0 -669
- package/build/SessionPanel-DUt2VzXG.mjs.map +0 -1
- package/build/format-ljc3lKpA.d.mts +0 -59
- package/build/status-BE-zg88x.mjs.map +0 -1
package/build/scoped.css
CHANGED
|
@@ -590,6 +590,9 @@
|
|
|
590
590
|
.wd-root .max-h-64 {
|
|
591
591
|
max-height: calc(var(--spacing) * 64);
|
|
592
592
|
}
|
|
593
|
+
.wd-root .max-h-72 {
|
|
594
|
+
max-height: calc(var(--spacing) * 72);
|
|
595
|
+
}
|
|
593
596
|
.wd-root .max-h-96 {
|
|
594
597
|
max-height: calc(var(--spacing) * 96);
|
|
595
598
|
}
|
|
@@ -1667,10 +1670,6 @@
|
|
|
1667
1670
|
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
|
1668
1671
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1669
1672
|
}
|
|
1670
|
-
.wd-root .ring {
|
|
1671
|
-
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
|
1672
|
-
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
1673
|
-
}
|
|
1674
1673
|
.wd-root .ring-2 {
|
|
1675
1674
|
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
|
1676
1675
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
@@ -1689,9 +1688,6 @@
|
|
|
1689
1688
|
--tw-blur: blur(8px);
|
|
1690
1689
|
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
|
1691
1690
|
}
|
|
1692
|
-
.wd-root .filter {
|
|
1693
|
-
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
|
1694
|
-
}
|
|
1695
1691
|
.wd-root .backdrop-blur-\[1px\] {
|
|
1696
1692
|
--tw-backdrop-blur: blur(1px);
|
|
1697
1693
|
-webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
|
|
@@ -1760,10 +1756,6 @@
|
|
|
1760
1756
|
--tw-outline-style: none;
|
|
1761
1757
|
outline-style: none;
|
|
1762
1758
|
}
|
|
1763
|
-
.wd-root .select-all {
|
|
1764
|
-
-webkit-user-select: all;
|
|
1765
|
-
user-select: all;
|
|
1766
|
-
}
|
|
1767
1759
|
.wd-root .select-none {
|
|
1768
1760
|
-webkit-user-select: none;
|
|
1769
1761
|
user-select: none;
|
|
@@ -2230,6 +2222,7 @@
|
|
|
2230
2222
|
.wd-root .term-gutter {
|
|
2231
2223
|
white-space: pre;
|
|
2232
2224
|
user-select: none;
|
|
2225
|
+
pointer-events: none;
|
|
2233
2226
|
color: var(--term-dim);
|
|
2234
2227
|
}
|
|
2235
2228
|
.wd-root .term-body {
|
|
@@ -2385,7 +2378,7 @@
|
|
|
2385
2378
|
background: linear-gradient(var(--term-dim), var(--term-dim)) left center / 100% 1px no-repeat;
|
|
2386
2379
|
}
|
|
2387
2380
|
.wd-root [data-terminal] .term-rule-dashed {
|
|
2388
|
-
background: repeating-linear-gradient(
|
|
2381
|
+
background: repeating-linear-gradient(to right, var(--term-faint) 0 4px, transparent 4px 8px) left center / 100% 1px no-repeat;
|
|
2389
2382
|
}
|
|
2390
2383
|
.wd-root [data-terminal] .term-row[data-focused] {
|
|
2391
2384
|
background: var(--term-row-hover);
|
|
@@ -2675,27 +2668,27 @@
|
|
|
2675
2668
|
width: 12px;
|
|
2676
2669
|
}
|
|
2677
2670
|
.wd-root .term-scrubber .term-scrub-mark[data-kind='user'] {
|
|
2678
|
-
background: linear-gradient(
|
|
2671
|
+
background: linear-gradient(to bottom, var(--term-blue) 0 2px, var(--term-blue) 2px);
|
|
2679
2672
|
@supports (color: color-mix(in lab, red, red)) {
|
|
2680
|
-
background: linear-gradient(
|
|
2673
|
+
background: linear-gradient(to bottom, var(--term-blue) 0 2px, color-mix(in srgb, var(--term-blue) 25%, transparent) 2px);
|
|
2681
2674
|
}
|
|
2682
2675
|
}
|
|
2683
2676
|
.wd-root .term-scrubber .term-scrub-mark[data-kind='subagent'] {
|
|
2684
|
-
background: linear-gradient(
|
|
2677
|
+
background: linear-gradient(to bottom, var(--term-green) 0 2px, var(--term-green) 2px);
|
|
2685
2678
|
@supports (color: color-mix(in lab, red, red)) {
|
|
2686
|
-
background: linear-gradient(
|
|
2679
|
+
background: linear-gradient(to bottom, var(--term-green) 0 2px, color-mix(in srgb, var(--term-green) 25%, transparent) 2px);
|
|
2687
2680
|
}
|
|
2688
2681
|
}
|
|
2689
2682
|
.wd-root .term-scrubber .term-scrub-mark[data-kind='turn'] {
|
|
2690
|
-
background: linear-gradient(
|
|
2683
|
+
background: linear-gradient(to bottom, var(--term-fg) 0 2px, var(--term-fg) 2px);
|
|
2691
2684
|
@supports (color: color-mix(in lab, red, red)) {
|
|
2692
|
-
background: linear-gradient(
|
|
2685
|
+
background: linear-gradient(to bottom, var(--term-fg) 0 2px, color-mix(in srgb, var(--term-fg) 25%, transparent) 2px);
|
|
2693
2686
|
}
|
|
2694
2687
|
}
|
|
2695
2688
|
.wd-root .term-scrubber .term-scrub-mark[data-kind='turnFailed'] {
|
|
2696
|
-
background: linear-gradient(
|
|
2689
|
+
background: linear-gradient(to bottom, var(--term-red) 0 2px, var(--term-red) 2px);
|
|
2697
2690
|
@supports (color: color-mix(in lab, red, red)) {
|
|
2698
|
-
background: linear-gradient(
|
|
2691
|
+
background: linear-gradient(to bottom, var(--term-red) 0 2px, color-mix(in srgb, var(--term-red) 25%, transparent) 2px);
|
|
2699
2692
|
}
|
|
2700
2693
|
}
|
|
2701
2694
|
.wd-root .term-scrubber .term-scrub-mark[data-kind='error'] {
|
|
@@ -2826,7 +2819,7 @@
|
|
|
2826
2819
|
--bg-code: #141414;
|
|
2827
2820
|
--bg-elevated: #0a0a0acc;
|
|
2828
2821
|
--row-hover: #2a2d2e;
|
|
2829
|
-
--row-active: rgb(255 255 255 / 0.
|
|
2822
|
+
--row-active: rgb(255 255 255 / 0.1);
|
|
2830
2823
|
--row-selected: #04395e;
|
|
2831
2824
|
--row-selected-weak: #37373d;
|
|
2832
2825
|
--badge: #616161;
|
|
@@ -2860,8 +2853,7 @@
|
|
|
2860
2853
|
--shadow-md: 0 8px 20px rgba(0, 0, 0, 0.6);
|
|
2861
2854
|
--shadow-lg: 0 24px 48px rgba(0, 0, 0, 0.7);
|
|
2862
2855
|
--shine: #303030;
|
|
2863
|
-
--frame-shadow: 0 4px 24px rgba(0, 0, 0, 0.55),
|
|
2864
|
-
0 2px 6px rgba(0, 0, 0, 0.45);
|
|
2856
|
+
--frame-shadow: 0 4px 24px rgba(0, 0, 0, 0.55), 0 2px 6px rgba(0, 0, 0, 0.45);
|
|
2865
2857
|
--wd-scrub-bookmark: #c586c0;
|
|
2866
2858
|
--surface: var(--bg-surface);
|
|
2867
2859
|
--sidebar: #131313;
|
|
@@ -11,7 +11,6 @@ function formatDuration(ms) {
|
|
|
11
11
|
if (s < 60) return `${s.toFixed(1)}s`;
|
|
12
12
|
return `${Math.floor(s / 60)}m ${Math.round(s % 60)}s`;
|
|
13
13
|
}
|
|
14
|
-
/** Compact token count, Claude Code-style: 850 → "850", 359_000 → "359.0k", 1_200_000 → "1.2M". */
|
|
15
14
|
function formatTokens(tokens) {
|
|
16
15
|
if (tokens >= 1e6) return `${(tokens / 1e6).toFixed(1)}M`;
|
|
17
16
|
if (tokens >= 1e3) return `${(tokens / 1e3).toFixed(1)}k`;
|
|
@@ -22,7 +21,6 @@ function formatBytes(bytes) {
|
|
|
22
21
|
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
23
22
|
return `${bytes} B`;
|
|
24
23
|
}
|
|
25
|
-
/** Countdown to an epoch-ms deadline: "2h 18m", "12m", "<1m"; "now" once passed. */
|
|
26
24
|
function formatCountdown(untilEpochMs, now = Date.now()) {
|
|
27
25
|
const remaining = untilEpochMs - now;
|
|
28
26
|
if (remaining <= 0) return "now";
|
|
@@ -44,19 +42,12 @@ function formatRelativeTime(epochMs, now = Date.now()) {
|
|
|
44
42
|
if (h < 24) return `${h}h ago`;
|
|
45
43
|
return `${Math.floor(h / 24)}d ago`;
|
|
46
44
|
}
|
|
47
|
-
/**
|
|
48
|
-
* Human label for a rate-limit window key, compact: 'five_hour' → "5h",
|
|
49
|
-
* 'seven_day_opus' → "7d opus". The per-model suffix is an open set — the CLI
|
|
50
|
-
* adds buckets as plans gain them — so it is rewritten rather than enumerated.
|
|
51
|
-
*/
|
|
52
45
|
function formatRateLimitWindow(key) {
|
|
53
46
|
if (key === "five_hour") return "5h";
|
|
54
47
|
if (key === "seven_day") return "7d";
|
|
55
48
|
const spaced = key.replaceAll("_", " ");
|
|
56
49
|
return key.startsWith("seven_day_") ? `7d ${spaced.slice(10)}` : spaced;
|
|
57
50
|
}
|
|
58
|
-
/** The same key spelled out, where there is room: 'five_hour' → "5-hour
|
|
59
|
-
* session", 'seven_day_fable' → "Weekly · Fable". */
|
|
60
51
|
function formatRateLimitWindowLong(key) {
|
|
61
52
|
if (key === "five_hour") return "5-hour session";
|
|
62
53
|
if (key === "seven_day") return "Weekly";
|
|
@@ -65,18 +56,10 @@ function formatRateLimitWindowLong(key) {
|
|
|
65
56
|
if (!key.startsWith("seven_day_")) return capitalize(key.replaceAll("_", " "));
|
|
66
57
|
return `Weekly · ${capitalize(key.slice(10).replaceAll("_", " "))}`;
|
|
67
58
|
}
|
|
68
|
-
/**
|
|
69
|
-
* How long a rate-limit window is, in seconds — the denominator behind the pace
|
|
70
|
-
* marker. Derived from the key rather than reported: the CLI sends a reset time
|
|
71
|
-
* and a percentage, never a duration. `undefined` for a window whose key doesn't
|
|
72
|
-
* say, and the marker is then simply not drawn rather than guessed.
|
|
73
|
-
*/
|
|
74
59
|
function rateLimitWindowSeconds(key) {
|
|
75
60
|
if (key === "five_hour") return 5 * 3600;
|
|
76
61
|
if (key.startsWith("seven_day")) return 7 * 86400;
|
|
77
62
|
}
|
|
78
|
-
/** "8 secs ago" / "3 mins ago" — a freshness line finer-grained than
|
|
79
|
-
* {@link formatRelativeTime}, because a poll that just landed should say so. */
|
|
80
63
|
function formatAgoPrecise(epochMs, now = Date.now()) {
|
|
81
64
|
const seconds = Math.max(0, Math.floor((now - epochMs) / 1e3));
|
|
82
65
|
if (seconds < 60) return `${seconds} sec${seconds === 1 ? "" : "s"} ago`;
|
|
@@ -87,7 +70,6 @@ function formatAgoPrecise(epochMs, now = Date.now()) {
|
|
|
87
70
|
const hours = Math.floor(seconds / 3600);
|
|
88
71
|
return `${hours} hour${hours === 1 ? "" : "s"} ago`;
|
|
89
72
|
}
|
|
90
|
-
/** Compact one-line preview of a tool input for card headers. */
|
|
91
73
|
function toolInputPreview(input, max = 80) {
|
|
92
74
|
if (input === null || input === void 0) return "";
|
|
93
75
|
if (typeof input === "object") {
|
|
@@ -98,9 +80,6 @@ function toolInputPreview(input, max = 80) {
|
|
|
98
80
|
const text = JSON.stringify(input) ?? "";
|
|
99
81
|
return text.length > max ? text.slice(0, max - 1) + "…" : text;
|
|
100
82
|
}
|
|
101
|
-
/** Families whose name isn't just a capitalised first letter, and how the
|
|
102
|
-
* vendor writes the version after it. GPT is `GPT-5.6`; everyone else spaces
|
|
103
|
-
* it. Anything unlisted is title-cased and spaced. */
|
|
104
83
|
const MODEL_FAMILIES = {
|
|
105
84
|
gpt: {
|
|
106
85
|
name: "GPT",
|
|
@@ -114,33 +93,8 @@ const MODEL_FAMILIES = {
|
|
|
114
93
|
mistral: { name: "Mistral" },
|
|
115
94
|
grok: { name: "Grok" }
|
|
116
95
|
};
|
|
117
|
-
/**
|
|
118
|
-
* The name a person says, from a wire model id:
|
|
119
|
-
*
|
|
120
|
-
* - `claude-opus-5[1m]` → "Opus 5"
|
|
121
|
-
* - `claude-haiku-4-5-20251001` → "Haiku 4.5"
|
|
122
|
-
* - `gpt-5.6-luna` → "GPT-5.6 Luna"
|
|
123
|
-
* - `gemini-2.5-pro` → "Gemini 2.5 Pro"
|
|
124
|
-
* - `o3-mini` → "o3 Mini"
|
|
125
|
-
*
|
|
126
|
-
* Three kinds of token after the family, because vendors mix them freely: a
|
|
127
|
-
* **version** (`5`, `4-5`, `5.6` — joined with dots, since Anthropic splits what
|
|
128
|
-
* OpenAI writes as one token), a **code name or tier** (`luna`, `codex`, `pro`,
|
|
129
|
-
* `mini` — kept and capitalised, since it is often the only thing telling two
|
|
130
|
-
* models apart), and a **snapshot date** (`20251001` — dropped; it is a build,
|
|
131
|
-
* not a version).
|
|
132
|
-
*
|
|
133
|
-
* Anything genuinely unreadable falls back to the id: a wrong name is worse than
|
|
134
|
-
* a raw one, which is at least true.
|
|
135
|
-
*
|
|
136
|
-
* The server has a narrower version of this (`friendlyModelName` in core's
|
|
137
|
-
* `normalize.ts`) that derives Claude catalog names at authoring time. This one
|
|
138
|
-
* is the *render-time* fallback for an id with no catalog row behind it — the
|
|
139
|
-
* sidebar has only `SessionInfo.model` — so it has to cope with every vendor the
|
|
140
|
-
* provider engine can reach, not just the CLI's own.
|
|
141
|
-
*/
|
|
142
96
|
function friendlyModel(id) {
|
|
143
|
-
if (!id) return
|
|
97
|
+
if (!id) return;
|
|
144
98
|
const parts = (id.split("[")[0] ?? id).toLowerCase().split("-").filter(Boolean);
|
|
145
99
|
if (parts[0] === "claude") parts.shift();
|
|
146
100
|
const familyToken = parts.shift();
|
|
@@ -195,11 +149,6 @@ const STATUS_META = {
|
|
|
195
149
|
severity: "none"
|
|
196
150
|
}
|
|
197
151
|
};
|
|
198
|
-
/**
|
|
199
|
-
* The status slot, connection first. A session status held over a dead socket is
|
|
200
|
-
* the last thing we heard, not the current state — so a lost link takes the slot
|
|
201
|
-
* rather than letting "Running" imply a turn is still streaming.
|
|
202
|
-
*/
|
|
203
152
|
function statusPresentation(vitals) {
|
|
204
153
|
if (!vitals) return {
|
|
205
154
|
icon: "hubot",
|
|
@@ -222,16 +171,14 @@ function statusPresentation(vitals) {
|
|
|
222
171
|
severity: "none"
|
|
223
172
|
};
|
|
224
173
|
}
|
|
225
|
-
/** 0–100 → the colour a meter wears. One pair of thresholds for every surface. */
|
|
226
174
|
function meterSeverity(pct) {
|
|
227
175
|
if (pct === void 0) return "none";
|
|
228
176
|
if (pct >= 95) return "error";
|
|
229
177
|
if (pct >= 80) return "warning";
|
|
230
178
|
return "none";
|
|
231
179
|
}
|
|
232
|
-
/** The window a lane points at, or `undefined` when this account has none. */
|
|
233
180
|
function usageWindow(rateLimits, lane) {
|
|
234
|
-
if (!rateLimits) return
|
|
181
|
+
if (!rateLimits) return;
|
|
235
182
|
if (lane === "session") {
|
|
236
183
|
const info = rateLimits.five_hour;
|
|
237
184
|
return info ? {
|
|
@@ -248,13 +195,9 @@ function usageWindow(rateLimits, lane) {
|
|
|
248
195
|
}
|
|
249
196
|
return tightestWindow(Object.fromEntries(Object.entries(rateLimits).filter(([key]) => key.startsWith("seven_day_") && key !== "seven_day_oauth_apps")));
|
|
250
197
|
}
|
|
251
|
-
/** The rate-limit window that gets the one visible slot: whichever is fullest,
|
|
252
|
-
* since the binding constraint is the one worth glancing at. Still the right
|
|
253
|
-
* rule for a surface with exactly one slot; {@link usageWindow} is for one with
|
|
254
|
-
* three. */
|
|
255
198
|
function tightestWindow(rateLimits) {
|
|
256
199
|
const entries = Object.entries(rateLimits ?? {});
|
|
257
|
-
if (entries.length === 0) return
|
|
200
|
+
if (entries.length === 0) return;
|
|
258
201
|
let best;
|
|
259
202
|
for (const [key, info] of entries) if ((info.status === "rejected" ? Number.POSITIVE_INFINITY : info.utilization ?? -1) > (best === void 0 ? Number.NEGATIVE_INFINITY : best.info.status === "rejected" ? Number.POSITIVE_INFINITY : best.info.utilization ?? -1)) best = {
|
|
260
203
|
key,
|
|
@@ -262,53 +205,39 @@ function tightestWindow(rateLimits) {
|
|
|
262
205
|
};
|
|
263
206
|
return best;
|
|
264
207
|
}
|
|
265
|
-
/** A rate-limit window's key, named for a human. A model-scoped bucket is named
|
|
266
|
-
* for its model alone (`seven_day_fable` → "Fable"): the lane it sits in
|
|
267
|
-
* already says weekly, and "Seven day fable" in a status bar is three words to
|
|
268
|
-
* say one. */
|
|
269
208
|
function windowLabel(key) {
|
|
270
209
|
if (key === "five_hour") return "Session";
|
|
271
210
|
if (key === "seven_day") return "Weekly";
|
|
272
211
|
const words = (key.startsWith("seven_day_") ? key.slice(10) : key).replaceAll("_", " ");
|
|
273
212
|
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
274
213
|
}
|
|
275
|
-
/**
|
|
276
|
-
* The catalog row a session is actually running, or `undefined` for a model the
|
|
277
|
-
* list doesn't name. Matched leniently: a session reports the *resolved* id
|
|
278
|
-
* (`claude-sonnet-5`) where the row may be keyed on the alias (`sonnet`), and
|
|
279
|
-
* either can carry a `[1m]` context-window suffix.
|
|
280
|
-
*/
|
|
281
214
|
function currentModel(vitals) {
|
|
282
215
|
const id = vitals?.model;
|
|
283
|
-
if (!id) return
|
|
216
|
+
if (!id) return;
|
|
284
217
|
const bare = (value) => value.replace(/\[.*\]$/, "");
|
|
285
218
|
const wanted = bare(id);
|
|
286
219
|
return vitals.models.find((m) => bare(m.value) === wanted || m.resolvedModel && bare(m.resolvedModel) === wanted);
|
|
287
220
|
}
|
|
288
|
-
/** A session's model, named the way the picker names it. Falls back to the raw
|
|
289
|
-
* id, and to "Default" while the session is on the CLI's own pick. */
|
|
290
221
|
function modelLabel(vitals) {
|
|
291
222
|
if (!vitals?.model) return "Default";
|
|
292
223
|
return currentModel(vitals)?.displayName ?? vitals.model;
|
|
293
224
|
}
|
|
294
|
-
/** Context percentage as its meter severity — the reading and the colour come
|
|
295
|
-
* from one place so a panel and a status bar never disagree.
|
|
296
|
-
*
|
|
297
|
-
* Takes only the number it reads, so the compact `ContextReading` that rides
|
|
298
|
-
* the sessions list and the full `ContextUsage` on the event stream are coloured
|
|
299
|
-
* by one rule rather than two. */
|
|
300
225
|
function contextSeverity(usage) {
|
|
301
226
|
return meterSeverity(usage?.percentage);
|
|
302
227
|
}
|
|
303
|
-
/** {@link meterSeverity} as a text colour class. Here rather than beside each
|
|
304
|
-
* meter because the status bar, the context dialog and a sessions-list row all
|
|
305
|
-
* paint the same reading, and a fourth copy of the thresholds is a fourth
|
|
306
|
-
* chance for one surface to call 81% orange and another call it grey. */
|
|
307
228
|
function meterColorClass(pct) {
|
|
308
229
|
const severity = meterSeverity(pct);
|
|
309
230
|
return severity === "error" ? "text-danger" : severity === "warning" ? "text-warning" : "text-fg-3";
|
|
310
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* The fill colour for a meter bar. Deliberately a different ramp from
|
|
234
|
+
* {@link meterColorClass}: a bar reads as alarming later than a number does, so the tint
|
|
235
|
+
* turns at 70/90 where the text severity turns at 80/95.
|
|
236
|
+
*/
|
|
237
|
+
function meterTintClass(pct) {
|
|
238
|
+
return pct >= 90 ? "bg-danger" : pct >= 70 ? "bg-warning" : "bg-accent";
|
|
239
|
+
}
|
|
311
240
|
//#endregion
|
|
312
|
-
export {
|
|
241
|
+
export { toolInputPreview as S, formatRateLimitWindowLong as _, meterTintClass as a, friendlyModel as b, tightestWindow as c, formatAgoPrecise as d, formatBytes as f, formatRateLimitWindow as g, formatDuration as h, meterSeverity as i, usageWindow as l, formatCountdown as m, currentModel as n, modelLabel as o, formatCost as p, meterColorClass as r, statusPresentation as s, contextSeverity as t, windowLabel as u, formatRelativeTime as v, rateLimitWindowSeconds as x, formatTokens as y };
|
|
313
242
|
|
|
314
|
-
//# sourceMappingURL=status-
|
|
243
|
+
//# sourceMappingURL=status-C0HSLZas.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-C0HSLZas.mjs","names":[],"sources":["../src/lib/format.ts","../src/lib/status.ts"],"sourcesContent":["export 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/**\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.\n */\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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"}
|
package/build/workspace.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as SessionPanelProps } from "./SessionPanel-
|
|
1
|
+
import { r as SessionPanelProps } from "./SessionPanel-E_L_ldQ5.mjs";
|
|
2
2
|
import * as _$react from "react";
|
|
3
3
|
import { CSSProperties } from "react";
|
|
4
4
|
import { OpenFile, UseHostFileSearchResult, UseHostFileTreeResult } from "@workerdeck/react";
|
|
@@ -8,84 +8,32 @@ import { WorkerDeckClient } from "@workerdeck/client";
|
|
|
8
8
|
interface SessionWorkspaceProps {
|
|
9
9
|
client: WorkerDeckClient;
|
|
10
10
|
sessionId: string | undefined;
|
|
11
|
-
/** Passed straight through to {@link SessionPanel} — including the render-prop
|
|
12
|
-
* form that claims the session-actions menu. */
|
|
13
11
|
header?: SessionPanelProps['header'];
|
|
14
|
-
/**
|
|
15
|
-
* Panel seams the workspace does not interpret, forwarded verbatim.
|
|
16
|
-
*
|
|
17
|
-
* They are listed rather than spread so the workspace stays explicit about
|
|
18
|
-
* what it passes on: the panel owns the session's one attach, and a seam that
|
|
19
|
-
* silently arrived here would be a second place to look for why a session
|
|
20
|
-
* renders the way it does.
|
|
21
|
-
*/
|
|
22
12
|
transcriptVariant?: SessionPanelProps['transcriptVariant'];
|
|
23
13
|
transcriptDensity?: SessionPanelProps['transcriptDensity'];
|
|
24
14
|
transcriptFont?: SessionPanelProps['transcriptFont'];
|
|
25
|
-
/** The overview ruler in place of the scrollbar — see `SessionPanel`. */
|
|
26
15
|
scrubber?: SessionPanelProps['scrubber'];
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
bookmarks?: SessionPanelProps['bookmarks'];
|
|
17
|
+
onToggleBookmark?: SessionPanelProps['onToggleBookmark'];
|
|
29
18
|
stickyPrompt?: SessionPanelProps['stickyPrompt'];
|
|
30
|
-
/** Take the panel body over with one sub-agent's work — see `SessionPanel`. */
|
|
31
19
|
openSubagent?: SessionPanelProps['openSubagent'];
|
|
32
|
-
/** Travel to a row in the conversation without framing anything — where a
|
|
33
|
-
* **task** press lands, as opposed to `openSubagent`'s takeover. See
|
|
34
|
-
* `SessionPanel`. */
|
|
35
20
|
reveal?: SessionPanelProps['reveal'];
|
|
36
|
-
/** The outward half of `openSubagent`: which sub-agent the panel now has
|
|
37
|
-
* framed, or `undefined` for the conversation — see `SessionPanel`. */
|
|
38
21
|
onSubagentChange?: SessionPanelProps['onSubagentChange'];
|
|
39
|
-
/** Which end of the panel the status bar sits at — see `SessionPanel`. */
|
|
40
22
|
statusPlacement?: SessionPanelProps['statusPlacement'];
|
|
41
23
|
controlsSurface?: SessionPanelProps['controlsSurface'];
|
|
42
|
-
/** Base font size in whole pixels — see `SessionPanel.fontSize`. */
|
|
43
24
|
fontSize?: SessionPanelProps['fontSize'];
|
|
44
|
-
/** Link click handler — see `SessionPanel.onLinkClick`. */
|
|
45
25
|
onLinkClick?: SessionPanelProps['onLinkClick'];
|
|
46
26
|
unseen?: SessionPanelProps['unseen'];
|
|
47
|
-
/** Viewer mode — no composer, no approval prompts. Forwarded verbatim to
|
|
48
|
-
* {@link SessionPanel}; the file tree and editor are unaffected, since reading
|
|
49
|
-
* a run's files is the point of a read-only view. */
|
|
50
27
|
readOnly?: SessionPanelProps['readOnly'];
|
|
51
28
|
onVitals?: SessionPanelProps['onVitals'];
|
|
52
|
-
/** Rail width in pixels on first render. */
|
|
53
29
|
defaultRailWidth?: number;
|
|
54
|
-
/** Start with the file rail collapsed even on a wide viewport. */
|
|
55
30
|
defaultRailCollapsed?: boolean;
|
|
56
|
-
/**
|
|
57
|
-
* The rail moved. Paired with the two defaults so an embedder can persist the
|
|
58
|
-
* layout — the workspace deliberately does not, because *where* to keep it (a
|
|
59
|
-
* Memento, localStorage, a workspace file) is the embedder's call, and a
|
|
60
|
-
* component that picked one would be wrong in the other hosts.
|
|
61
|
-
*/
|
|
62
31
|
onRailChange?: (rail: {
|
|
63
32
|
width: number;
|
|
64
33
|
collapsed: boolean;
|
|
65
34
|
}) => void;
|
|
66
35
|
className?: string;
|
|
67
36
|
}
|
|
68
|
-
/**
|
|
69
|
-
* A VS Code-shaped workspace around a live session: file tree on the left, open
|
|
70
|
-
* files above, the agent below.
|
|
71
|
-
*
|
|
72
|
-
* **Strictly additive.** {@link SessionPanel} is untouched and still the whole
|
|
73
|
-
* session surface on its own — an embedder picks one or the other, and one that
|
|
74
|
-
* has its own file tree keeps using the panel.
|
|
75
|
-
*
|
|
76
|
-
* Two things here are load-bearing and easy to break:
|
|
77
|
-
*
|
|
78
|
-
* 1. **The editor region is absent from the layout when nothing is open**, not
|
|
79
|
-
* collapsed to zero height. A zero-height pane leaves a draggable splitter and
|
|
80
|
-
* parks the composer at an odd offset; absence is what makes the agent's
|
|
81
|
-
* "claims the full column" state actually look like the panel alone.
|
|
82
|
-
* 2. **`SessionPanel` keeps its position in the tree across that transition.** It
|
|
83
|
-
* holds the WebSocket attach and the entire transcript, so moving it between
|
|
84
|
-
* parents — or wrapping it conditionally — would remount it and drop the
|
|
85
|
-
* session's rendered history on the floor the first time someone opens a file.
|
|
86
|
-
* The conditional children before it are `? :` expressions that leave a null in
|
|
87
|
-
* their slot, which is exactly what keeps its index stable.
|
|
88
|
-
*/
|
|
89
37
|
declare function SessionWorkspace({
|
|
90
38
|
client,
|
|
91
39
|
sessionId,
|
|
@@ -94,7 +42,8 @@ declare function SessionWorkspace({
|
|
|
94
42
|
transcriptDensity,
|
|
95
43
|
transcriptFont,
|
|
96
44
|
scrubber,
|
|
97
|
-
|
|
45
|
+
bookmarks,
|
|
46
|
+
onToggleBookmark,
|
|
98
47
|
stickyPrompt,
|
|
99
48
|
openSubagent,
|
|
100
49
|
reveal,
|
|
@@ -114,30 +63,14 @@ declare function SessionWorkspace({
|
|
|
114
63
|
//#endregion
|
|
115
64
|
//#region src/components/agent/FileTree.d.ts
|
|
116
65
|
interface FileTreeProps {
|
|
117
|
-
/** Tree state from `useHostFileTree` — this component holds none of its own. */
|
|
118
66
|
tree: UseHostFileTreeResult;
|
|
119
|
-
/** Optional search from `useHostFileSearch`; omit and the box is not offered. */
|
|
120
67
|
search?: UseHostFileSearchResult;
|
|
121
|
-
/** Path of the focused file, highlighted in the tree. */
|
|
122
68
|
activePath?: string;
|
|
123
69
|
onOpenFile: (path: string) => void;
|
|
124
|
-
/** Offered as a button in the header when given. */
|
|
125
70
|
onCollapse?: () => void;
|
|
126
71
|
style?: CSSProperties;
|
|
127
72
|
className?: string;
|
|
128
73
|
}
|
|
129
|
-
/**
|
|
130
|
-
* The workspace's left rail: an expandable tree of the session's project,
|
|
131
|
-
* with a search box over the same fuzzy route `@file` completion uses.
|
|
132
|
-
*
|
|
133
|
-
* Presentational by construction — every piece of state it renders comes from
|
|
134
|
-
* the hooks in `@workerdeck/react`, and the only thing it owns is the search
|
|
135
|
-
* query, which is the text in its own input.
|
|
136
|
-
*
|
|
137
|
-
* Searching replaces the tree with matches rather than filtering it: the route
|
|
138
|
-
* answers with paths from all over the project, and threading those back into
|
|
139
|
-
* tree positions would mean expanding a dozen directories to show six results.
|
|
140
|
-
*/
|
|
141
74
|
declare function FileTree({
|
|
142
75
|
tree,
|
|
143
76
|
search,
|
|
@@ -156,19 +89,6 @@ interface EditorTabsProps {
|
|
|
156
89
|
onClose: (path: string) => void;
|
|
157
90
|
className?: string;
|
|
158
91
|
}
|
|
159
|
-
/**
|
|
160
|
-
* The open-file tab strip.
|
|
161
|
-
*
|
|
162
|
-
* Hand-rolled rather than built on `@base-ui/react`'s `Tabs`: a VS Code tab
|
|
163
|
-
* carries a close button, and a `<button>` inside a `<button>` is invalid HTML —
|
|
164
|
-
* getting the primitive to render something else costs more than the roving
|
|
165
|
-
* tabindex it would have provided. So that part is here, explicitly, along with
|
|
166
|
-
* the two affordances that actually make a tab strip feel right: middle-click to
|
|
167
|
-
* close, and the active tab scrolling itself into view.
|
|
168
|
-
*
|
|
169
|
-
* Deliberately state-free. Which files are open, which is focused and what
|
|
170
|
-
* closing does are all decided by `useOpenFiles`.
|
|
171
|
-
*/
|
|
172
92
|
declare function EditorTabs({
|
|
173
93
|
files,
|
|
174
94
|
activePath,
|
|
@@ -180,27 +100,15 @@ declare function EditorTabs({
|
|
|
180
100
|
//#region src/components/agent/FileViewer.d.ts
|
|
181
101
|
interface FileViewerProps {
|
|
182
102
|
file: OpenFile | undefined;
|
|
183
|
-
/** From `/fs/roots`. False renders the editor read-only rather than letting
|
|
184
|
-
* someone type into a file this gateway will refuse to write. */
|
|
185
103
|
canWrite?: boolean;
|
|
186
104
|
onChange?: (path: string, content: string) => void;
|
|
187
105
|
onSave?: (path: string) => void;
|
|
188
|
-
/** Discard this tab's edits — local only, no re-read. */
|
|
189
106
|
onRevert?: (path: string) => void;
|
|
190
|
-
/** Take the version on disk, discarding this tab's edits. */
|
|
191
107
|
onReload?: (path: string) => void;
|
|
192
|
-
/** Take this tab's version, over whatever is on disk now. */
|
|
193
108
|
onOverwrite?: (path: string) => void;
|
|
194
109
|
onDismissConflict?: (path: string) => void;
|
|
195
110
|
className?: string;
|
|
196
111
|
}
|
|
197
|
-
/**
|
|
198
|
-
* The focused file: Monaco, plus the states a file can be in that are not
|
|
199
|
-
* "here is some text".
|
|
200
|
-
*
|
|
201
|
-
* No path row — the tab's tooltip carries the path and the size, and a line of
|
|
202
|
-
* monospace above every file is chrome that never earns its height.
|
|
203
|
-
*/
|
|
204
112
|
declare function FileViewer({
|
|
205
113
|
file,
|
|
206
114
|
canWrite,
|
|
@@ -215,42 +123,13 @@ declare function FileViewer({
|
|
|
215
123
|
//#endregion
|
|
216
124
|
//#region src/components/agent/CodeEditor.d.ts
|
|
217
125
|
interface CodeEditorProps {
|
|
218
|
-
/** Absolute path — decides the language and identifies the model. */
|
|
219
126
|
path: string;
|
|
220
|
-
/** Text to show. Applied to the model when it differs from what is on screen,
|
|
221
|
-
* so an external reload lands without fighting the user's cursor. */
|
|
222
127
|
value: string;
|
|
223
128
|
onChange?: (value: string) => void;
|
|
224
|
-
/** Ctrl/Cmd+S. Wired inside Monaco because the editor swallows keydown. */
|
|
225
129
|
onSave?: () => void;
|
|
226
130
|
readOnly?: boolean;
|
|
227
131
|
className?: string;
|
|
228
132
|
}
|
|
229
|
-
/**
|
|
230
|
-
* Monaco — VS Code's own editor — behind a small React surface.
|
|
231
|
-
*
|
|
232
|
-
* Two deliberate choices, both about `@workerdeck/ui` being a **published
|
|
233
|
-
* library** rather than an app:
|
|
234
|
-
*
|
|
235
|
-
* 1. **Loaded on demand.** The `import()` is inside an effect, so Monaco is a
|
|
236
|
-
* separate chunk that arrives when someone first opens a file. A dashboard
|
|
237
|
-
* that never opens one never pays for it, and Monaco's ~90 language grammars
|
|
238
|
-
* are themselves lazy (each `registerLanguage` carries an `import()` loader),
|
|
239
|
-
* so opening a `.ts` file fetches the TypeScript grammar and nothing else.
|
|
240
|
-
* 2. **No `MonacoEnvironment` is configured here, and none is needed.** Workers
|
|
241
|
-
* in a library become every embedder's bootstrapping problem, and
|
|
242
|
-
* `packages/web` ships prebuilt static files at a domain root, which is
|
|
243
|
-
* exactly where hardcoded worker URLs break. The editor is configured so it
|
|
244
|
-
* never asks for one: `wordBasedSuggestions` and `quickSuggestions` off,
|
|
245
|
-
* no diff editor. A host that wants the worker-backed language services
|
|
246
|
-
* (TypeScript IntelliSense, JSON schema validation) sets `MonacoEnvironment`
|
|
247
|
-
* itself before the first file is opened — Monaco is a singleton and nothing
|
|
248
|
-
* here fights that.
|
|
249
|
-
*
|
|
250
|
-
* One model per path, kept across tab switches, so undo history and view state
|
|
251
|
-
* survive clicking away and back — which is most of what makes tabs feel like
|
|
252
|
-
* tabs rather than like re-opening a file.
|
|
253
|
-
*/
|
|
254
133
|
declare function CodeEditor({
|
|
255
134
|
path,
|
|
256
135
|
value,
|