@workerdeck/ui 2.1.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.d.mts CHANGED
@@ -15,7 +15,7 @@ import { ConnectionState, TranscriptItem, TranscriptState, UseAttachmentsResult
15
15
  import { WorkerDeckClient } from "@workerdeck/client";
16
16
  //#region src/components/ui/Button.d.ts
17
17
  declare const buttonVariants: (props?: ({
18
- variant?: "default" | "outline" | "secondary" | "ghost" | "destructive" | "link" | null | undefined;
18
+ variant?: "default" | "link" | "outline" | "secondary" | "ghost" | "destructive" | null | undefined;
19
19
  size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-sm" | null | undefined;
20
20
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
21
21
  interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {}
@@ -623,6 +623,18 @@ type HashtagTriggerOptions = TriggerPresetOptions & {
623
623
  * `resolveOnSpace: true`, accessible label `"tag"`.
624
624
  */
625
625
  declare function hashtagTrigger(opts?: HashtagTriggerOptions): TriggerConfig;
626
+ type LaunchTriggerOptions = Omit<Partial<TriggerConfig>, 'mode'> & {
627
+ /** The trigger character. Required. */
628
+ char: string;
629
+ };
630
+ /**
631
+ * Creates a **launch** trigger: `onActivate` fires on keydown and the character
632
+ * never enters the editor. For flipping the composer into another mode, or
633
+ * opening a surface outside it.
634
+ *
635
+ * Defaults: `position: 'start'`, `mode: 'launch'`.
636
+ */
637
+ declare function launchTrigger(opts: LaunchTriggerOptions): TriggerConfig;
626
638
  //#endregion
627
639
  //#region src/components/prompt-area/prompt-area-engine.d.ts
628
640
  /**
@@ -888,6 +900,7 @@ interface ComposerProps {
888
900
  onSearchFiles?: (query: string, options: {
889
901
  signal: AbortSignal;
890
902
  }) => Promise<ComposerFileMatch[]>;
903
+ onShellCommand?: (command: string) => void;
891
904
  attachments?: UseAttachmentsResult;
892
905
  toolbar?: ReactNode;
893
906
  layout?: 'stacked' | 'inline';
@@ -903,7 +916,7 @@ interface ComposerProps {
903
916
  ref?: Ref<ComposerHandle>;
904
917
  }
905
918
  declare function skillPrompt(skill: SkillInfo): string;
906
- declare function Composer({ onSend, onInterrupt, busy, disabled, placeholder, commands, skills, onSearchFiles, attachments, toolbar, layout, fontSize, lineHeight, affordances, draft, className, ref }: ComposerProps): import("react").JSX.Element;
919
+ declare function Composer({ onSend, onInterrupt, busy, disabled, placeholder, commands, skills, onSearchFiles, onShellCommand, attachments, toolbar, layout, fontSize, lineHeight, affordances, draft, className, ref }: ComposerProps): import("react").JSX.Element;
907
920
  //#endregion
908
921
  //#region src/components/agent/ModelSelect.d.ts
909
922
  interface ModelSelectProps {
@@ -1190,5 +1203,5 @@ declare function copyText(value: string): Promise<boolean>;
1190
1203
  declare function toolIcon(toolName: string): LucideIcon;
1191
1204
  declare function isMutatingTool(toolName: string): boolean;
1192
1205
  //#endregion
1193
- export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, AlertDialogTrigger, Badge, type BadgeProps, Band, Blank, Button, type ButtonProps, Card, CardContent, CardHeader, CardTitle, type ChipSegment, CodeBlock, type CodeBlockProps, Composer, type ComposerFileMatch, type ComposerHandle, type ComposerProps, ContextDialog, type ContextDialogProps, ContextRing, Conversation, ConversationContent, type ConversationProps, ConversationScrollButton, CopyAction, CopyButton, type CopyButtonProps, Dialog, DialogBody, DialogClose, DialogContent, DialogHeader, DialogRow, DialogTrigger, Empty, EmptyKey, EngineIcon, FileCard, type FileCardProps, type FileDeliveredItem, HostFilesDialog, type HostFilesDialogProps, Ink, Input, Loader, McpDialog, type McpDialogProps, Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, Message, MessageContent, type MessageProps, ModelSelect, type ModelSelectProps, PERMISSION_MODES, type PermissionModeChoice, type PermissionModeMeta, PermissionModeSelect, type PermissionModeSelectProps, PermissionPrompt, type PermissionPromptProps, ProgressRing, type ProgressRingProps, ProjectIcon, PromptArea, type PromptAreaHandle, type PromptAreaProps, PromptTokenText, QUESTION_BEHAVIORS, type QuestionBehaviorMeta, QuestionPrompt, type QuestionPromptProps, Reasoning, type ReasoningProps, Response, type ResponseProps, Row, type RowProps, STATUS_META, type Segment, Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue, SessionBrowser, type SessionBrowserProps, type SessionControls, SessionEmptyState, type SessionEmptyStateProps, SessionInfoDialog, type SessionInfoDialogProps, SessionItem, type SessionItemProps, SessionList, SessionListItem, type SessionListItemProps, type SessionListProps, SessionPanel, type SessionPanelProps, SessionStatusIcon, type SessionSurfacePanel, type SessionVitals, SkillsDialog, type SkillsDialogProps, Spinner, Splitter, type SplitterProps, StatusBar, type StatusBarProps, type Step, StepRow, StepToggle, SubagentStrip, TaskList, type TaskListProps, TasksDialog, type TasksDialogProps, type TerminalAffordances, TerminalDiff, TerminalItemView, TerminalMarkdown, type TerminalMarkdownProps, type TerminalMetrics, TerminalPermissionPrompt, type TerminalPermissionPromptProps, TerminalQuestionPrompt, type TerminalQuestionPromptProps, TerminalStatusLine, type TerminalStatusLineProps, TerminalSurface, type TerminalSurfaceProps, TerminalTranscript, type TerminalTranscriptProps, type TextSegment, Textarea, Tip, Toaster, type Tone, ToolCallCard, type ToolCallCardProps, type ToolCallItem, ToolTitleProvider, TooltipContent, TooltipProvider, Transcript, type TranscriptDensity, TranscriptDensityProvider, type TranscriptFont, type TranscriptProps, type TranscriptVariant, TranscriptVariantProvider, type TriggerConfig, type TriggerSuggestion, UsageDialog, type UsageDialogProps, UsageMeters, WithActions, badgeVariants, buttonVariants, cn, commandTrigger, copyText, engineMark, formatAgoPrecise, formatBytes, formatCost, formatCountdown, formatDuration, formatRateLimitWindow, formatRateLimitWindowLong, formatRelativeTime, formatTokens, friendlyModel, getChipsByTrigger, hashtagTrigger, isMutatingTool, isSegmentsEmpty, mentionTrigger, parseUserQuestions, permissionModeChoices, permissionModeMeta, plainTextToSegments, previewPatch, rateLimitWindowSeconds, rowShapeClass, runningSteps, segmentsToPlainText, sessionSteps, skillPrompt, toast, toolIcon, toolInputPreview, useAffordances, useMinuteClock, usePromptAreaState, useToolTitle, useTranscriptDensity, useTranscriptVariant, vendorMarkClass, vendorTextClass };
1206
+ export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, AlertDialogTrigger, Badge, type BadgeProps, Band, Blank, Button, type ButtonProps, Card, CardContent, CardHeader, CardTitle, type ChipSegment, CodeBlock, type CodeBlockProps, Composer, type ComposerFileMatch, type ComposerHandle, type ComposerProps, ContextDialog, type ContextDialogProps, ContextRing, Conversation, ConversationContent, type ConversationProps, ConversationScrollButton, CopyAction, CopyButton, type CopyButtonProps, Dialog, DialogBody, DialogClose, DialogContent, DialogHeader, DialogRow, DialogTrigger, Empty, EmptyKey, EngineIcon, FileCard, type FileCardProps, type FileDeliveredItem, HostFilesDialog, type HostFilesDialogProps, Ink, Input, Loader, McpDialog, type McpDialogProps, Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, Message, MessageContent, type MessageProps, ModelSelect, type ModelSelectProps, PERMISSION_MODES, type PermissionModeChoice, type PermissionModeMeta, PermissionModeSelect, type PermissionModeSelectProps, PermissionPrompt, type PermissionPromptProps, ProgressRing, type ProgressRingProps, ProjectIcon, PromptArea, type PromptAreaHandle, type PromptAreaProps, PromptTokenText, QUESTION_BEHAVIORS, type QuestionBehaviorMeta, QuestionPrompt, type QuestionPromptProps, Reasoning, type ReasoningProps, Response, type ResponseProps, Row, type RowProps, STATUS_META, type Segment, Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue, SessionBrowser, type SessionBrowserProps, type SessionControls, SessionEmptyState, type SessionEmptyStateProps, SessionInfoDialog, type SessionInfoDialogProps, SessionItem, type SessionItemProps, SessionList, SessionListItem, type SessionListItemProps, type SessionListProps, SessionPanel, type SessionPanelProps, SessionStatusIcon, type SessionSurfacePanel, type SessionVitals, SkillsDialog, type SkillsDialogProps, Spinner, Splitter, type SplitterProps, StatusBar, type StatusBarProps, type Step, StepRow, StepToggle, SubagentStrip, TaskList, type TaskListProps, TasksDialog, type TasksDialogProps, type TerminalAffordances, TerminalDiff, TerminalItemView, TerminalMarkdown, type TerminalMarkdownProps, type TerminalMetrics, TerminalPermissionPrompt, type TerminalPermissionPromptProps, TerminalQuestionPrompt, type TerminalQuestionPromptProps, TerminalStatusLine, type TerminalStatusLineProps, TerminalSurface, type TerminalSurfaceProps, TerminalTranscript, type TerminalTranscriptProps, type TextSegment, Textarea, Tip, Toaster, type Tone, ToolCallCard, type ToolCallCardProps, type ToolCallItem, ToolTitleProvider, TooltipContent, TooltipProvider, Transcript, type TranscriptDensity, TranscriptDensityProvider, type TranscriptFont, type TranscriptProps, type TranscriptVariant, TranscriptVariantProvider, type TriggerConfig, type TriggerSuggestion, UsageDialog, type UsageDialogProps, UsageMeters, WithActions, badgeVariants, buttonVariants, cn, commandTrigger, copyText, engineMark, formatAgoPrecise, formatBytes, formatCost, formatCountdown, formatDuration, formatRateLimitWindow, formatRateLimitWindowLong, formatRelativeTime, formatTokens, friendlyModel, getChipsByTrigger, hashtagTrigger, isMutatingTool, isSegmentsEmpty, launchTrigger, mentionTrigger, parseUserQuestions, permissionModeChoices, permissionModeMeta, plainTextToSegments, previewPatch, rateLimitWindowSeconds, rowShapeClass, runningSteps, segmentsToPlainText, sessionSteps, skillPrompt, toast, toolIcon, toolInputPreview, useAffordances, useMinuteClock, usePromptAreaState, useToolTitle, useTranscriptDensity, useTranscriptVariant, vendorMarkClass, vendorTextClass };
1194
1207
  //# sourceMappingURL=index.d.mts.map
package/build/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as Ink, A as PERMISSION_MODES, At as DialogHeader, B as TaskList, Bt as SelectItem, C as TerminalPermissionPrompt, Ct as Tip, D as SubagentStrip, Dt as DialogBody, E as parseUserQuestions, Et as Dialog, F as SkillsDialog, Ft as MenuItem, G as TerminalSurface, Gt as Input, H as contextNote, Ht as SelectTrigger, I as McpDialog, It as MenuSeparator, J as TerminalMarkdown, Jt as Button, K as ToolTitleProvider, Kt as Badge, L as HostFilesDialog, Lt as MenuTrigger, M as permissionModeChoices, Mt as DialogTrigger, N as permissionModeMeta, Nt as Menu, O as PermissionPrompt, Ot as DialogClose, P as ModelSelect, Pt as MenuContent, Q as Blank, Rt as Select, S as TerminalQuestionPrompt, St as copyText, T as QuestionPrompt, Tt as TooltipProvider, U as Composer, Ut as SelectValue, V as ContextDialog, Vt as SelectItemText, W as skillPrompt, Wt as PortalScope, X as previewPatch, Xt as cn, Y as TerminalDiff, Yt as buttonVariants, Z as Band, _ as ConversationContent, _t as ProgressRing, a as Transcript, at as toolIcon, b as STATUS_META, bt as Spinner, c as PromptTokenText, ct as useTranscriptDensity, d as FileCard, dt as hashtagTrigger, et as Row, f as TerminalItemView, ft as mentionTrigger, g as Conversation, gt as segmentsToPlainText, h as Loader, ht as plainTextToSegments, i as useMinuteClock, it as isMutatingTool, j as PermissionModeSelect, jt as DialogRow, k as Response, kt as DialogContent, l as Message, lt as useTranscriptVariant, m as SessionEmptyState, mt as PromptArea, n as UsageDialog, nt as WithActions, o as ToolCallCard, ot as TranscriptDensityProvider, p as TerminalTranscript, pt as usePromptAreaState, q as useToolTitle, qt as badgeVariants, r as UsageMeters, rt as useAffordances, s as Reasoning, st as TranscriptVariantProvider, t as SessionPanel, tt as CopyAction, u as MessageContent, ut as commandTrigger, v as ConversationScrollButton, vt as Splitter, w as QUESTION_BEHAVIORS, wt as TooltipContent, x as SessionInfoDialog, xt as CopyButton, y as StatusBar, yt as CodeBlock, z as TasksDialog, zt as SelectContent } from "./SessionPanel-DrMj8Yv0.mjs";
1
+ import { $ as Ink, A as PERMISSION_MODES, At as DialogContent, B as TaskList, Bt as SelectContent, C as TerminalPermissionPrompt, Ct as copyText, D as SubagentStrip, Dt as Dialog, E as parseUserQuestions, Et as TooltipProvider, F as SkillsDialog, Ft as MenuContent, G as TerminalSurface, Gt as PortalScope, H as contextNote, Ht as SelectItemText, I as McpDialog, It as MenuItem, J as TerminalMarkdown, Jt as badgeVariants, K as ToolTitleProvider, Kt as Input, L as HostFilesDialog, Lt as MenuSeparator, M as permissionModeChoices, Mt as DialogRow, N as permissionModeMeta, Nt as DialogTrigger, O as PermissionPrompt, Ot as DialogBody, P as ModelSelect, Pt as Menu, Q as Blank, Rt as MenuTrigger, S as TerminalQuestionPrompt, St as CopyButton, T as QuestionPrompt, Tt as TooltipContent, U as Composer, Ut as SelectTrigger, V as ContextDialog, Vt as SelectItem, W as skillPrompt, Wt as SelectValue, X as previewPatch, Xt as buttonVariants, Y as TerminalDiff, Yt as Button, Z as Band, Zt as cn, _ as ConversationContent, _t as segmentsToPlainText, a as Transcript, at as toolIcon, b as STATUS_META, bt as CodeBlock, c as PromptTokenText, ct as useTranscriptDensity, d as FileCard, dt as hashtagTrigger, et as Row, f as TerminalItemView, ft as launchTrigger, g as Conversation, gt as plainTextToSegments, h as Loader, ht as PromptArea, i as useMinuteClock, it as isMutatingTool, j as PermissionModeSelect, jt as DialogHeader, k as Response, kt as DialogClose, l as Message, lt as useTranscriptVariant, m as SessionEmptyState, mt as usePromptAreaState, n as UsageDialog, nt as WithActions, o as ToolCallCard, ot as TranscriptDensityProvider, p as TerminalTranscript, pt as mentionTrigger, q as useToolTitle, qt as Badge, r as UsageMeters, rt as useAffordances, s as Reasoning, st as TranscriptVariantProvider, t as SessionPanel, tt as CopyAction, u as MessageContent, ut as commandTrigger, v as ConversationScrollButton, vt as ProgressRing, w as QUESTION_BEHAVIORS, wt as Tip, x as SessionInfoDialog, xt as Spinner, y as StatusBar, yt as Splitter, z as TasksDialog, zt as Select } from "./SessionPanel-ErvQUyHe.mjs";
2
2
  import { C as toolInputPreview, S as rateLimitWindowSeconds, _ as formatRateLimitWindow, b as formatTokens, c as tightestWindow, f as formatAgoPrecise, g as formatDuration, h as formatCountdown, i as meterSeverity, m as formatCost, o as modelLabel, p as formatBytes, r as meterColorClass, s as statusPresentation, t as contextSeverity, u as windowLabel, v as formatRateLimitWindowLong, x as friendlyModel, y as formatRelativeTime } from "./status-C4TAgVSc.mjs";
3
3
  import { Fragment, forwardRef, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -1089,6 +1089,6 @@ function OneOfSelect({ label, value, options, onChange }) {
1089
1089
  });
1090
1090
  }
1091
1091
  //#endregion
1092
- export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, AlertDialogTrigger, Badge, Band, Blank, Button, Card, CardContent, CardHeader, CardTitle, CodeBlock, Composer, ContextDialog, ContextRing, Conversation, ConversationContent, ConversationScrollButton, CopyAction, CopyButton, Dialog, DialogBody, DialogClose, DialogContent, DialogHeader, DialogRow, DialogTrigger, Empty, EmptyKey, EngineIcon, FileCard, HostFilesDialog, Ink, Input, Loader, McpDialog, Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, Message, MessageContent, ModelSelect, PERMISSION_MODES, PermissionModeSelect, PermissionPrompt, ProgressRing, ProjectIcon, PromptArea, PromptTokenText, QUESTION_BEHAVIORS, QuestionPrompt, Reasoning, Response, Row, STATUS_META, Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue, SessionBrowser, SessionEmptyState, SessionInfoDialog, SessionItem, SessionList, SessionListItem, SessionPanel, SessionStatusIcon, SkillsDialog, Spinner, Splitter, StatusBar, StepRow, StepToggle, SubagentStrip, TaskList, TasksDialog, TerminalDiff, TerminalItemView, TerminalMarkdown, TerminalPermissionPrompt, TerminalQuestionPrompt, TerminalStatusLine, TerminalSurface, TerminalTranscript, Textarea, Tip, Toaster, ToolCallCard, ToolTitleProvider, TooltipContent, TooltipProvider, Transcript, TranscriptDensityProvider, TranscriptVariantProvider, UsageDialog, UsageMeters, WithActions, badgeVariants, buttonVariants, cn, commandTrigger, copyText, engineMark, formatAgoPrecise, formatBytes, formatCost, formatCountdown, formatDuration, formatRateLimitWindow, formatRateLimitWindowLong, formatRelativeTime, formatTokens, friendlyModel, getChipsByTrigger, hashtagTrigger, isMutatingTool, isSegmentsEmpty, mentionTrigger, parseUserQuestions, permissionModeChoices, permissionModeMeta, plainTextToSegments, previewPatch, rateLimitWindowSeconds, rowShapeClass, runningSteps, segmentsToPlainText, sessionSteps, skillPrompt, toast, toolIcon, toolInputPreview, useAffordances, useMinuteClock, usePromptAreaState, useToolTitle, useTranscriptDensity, useTranscriptVariant, vendorMarkClass, vendorTextClass };
1092
+ export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, AlertDialogTrigger, Badge, Band, Blank, Button, Card, CardContent, CardHeader, CardTitle, CodeBlock, Composer, ContextDialog, ContextRing, Conversation, ConversationContent, ConversationScrollButton, CopyAction, CopyButton, Dialog, DialogBody, DialogClose, DialogContent, DialogHeader, DialogRow, DialogTrigger, Empty, EmptyKey, EngineIcon, FileCard, HostFilesDialog, Ink, Input, Loader, McpDialog, Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, Message, MessageContent, ModelSelect, PERMISSION_MODES, PermissionModeSelect, PermissionPrompt, ProgressRing, ProjectIcon, PromptArea, PromptTokenText, QUESTION_BEHAVIORS, QuestionPrompt, Reasoning, Response, Row, STATUS_META, Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue, SessionBrowser, SessionEmptyState, SessionInfoDialog, SessionItem, SessionList, SessionListItem, SessionPanel, SessionStatusIcon, SkillsDialog, Spinner, Splitter, StatusBar, StepRow, StepToggle, SubagentStrip, TaskList, TasksDialog, TerminalDiff, TerminalItemView, TerminalMarkdown, TerminalPermissionPrompt, TerminalQuestionPrompt, TerminalStatusLine, TerminalSurface, TerminalTranscript, Textarea, Tip, Toaster, ToolCallCard, ToolTitleProvider, TooltipContent, TooltipProvider, Transcript, TranscriptDensityProvider, TranscriptVariantProvider, UsageDialog, UsageMeters, WithActions, badgeVariants, buttonVariants, cn, commandTrigger, copyText, engineMark, formatAgoPrecise, formatBytes, formatCost, formatCountdown, formatDuration, formatRateLimitWindow, formatRateLimitWindowLong, formatRelativeTime, formatTokens, friendlyModel, getChipsByTrigger, hashtagTrigger, isMutatingTool, isSegmentsEmpty, launchTrigger, mentionTrigger, parseUserQuestions, permissionModeChoices, permissionModeMeta, plainTextToSegments, previewPatch, rateLimitWindowSeconds, rowShapeClass, runningSteps, segmentsToPlainText, sessionSteps, skillPrompt, toast, toolIcon, toolInputPreview, useAffordances, useMinuteClock, usePromptAreaState, useToolTitle, useTranscriptDensity, useTranscriptVariant, vendorMarkClass, vendorTextClass };
1093
1093
 
1094
1094
  //# sourceMappingURL=index.mjs.map
package/build/scoped.css CHANGED
@@ -980,6 +980,9 @@
980
980
  border-left-style: var(--tw-border-style);
981
981
  border-left-width: 4px;
982
982
  }
983
+ .wd-root .border-\[var\(--wd-shell-accent\)\] {
984
+ border-color: var(--wd-shell-accent);
985
+ }
983
986
  .wd-root .border-border {
984
987
  border-color: var(--border);
985
988
  }
@@ -1536,6 +1539,9 @@
1536
1539
  .wd-root .text-\[var\(--sdm-c\,inherit\)\] {
1537
1540
  color: var(--sdm-c,inherit);
1538
1541
  }
1542
+ .wd-root .text-\[var\(--wd-shell-accent\)\] {
1543
+ color: var(--wd-shell-accent);
1544
+ }
1539
1545
  .wd-root .text-accent {
1540
1546
  color: var(--accent);
1541
1547
  }
@@ -1855,6 +1861,9 @@
1855
1861
  content: var(--tw-content);
1856
1862
  counter-increment: line;
1857
1863
  }
1864
+ .wd-root .focus-within\:border-\[var\(--wd-shell-accent\)\]:focus-within {
1865
+ border-color: var(--wd-shell-accent);
1866
+ }
1858
1867
  .wd-root .focus-within\:border-ring:focus-within {
1859
1868
  border-color: var(--ring);
1860
1869
  }
@@ -1862,6 +1871,12 @@
1862
1871
  --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
1863
1872
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1864
1873
  }
1874
+ .wd-root .focus-within\:ring-\[var\(--wd-shell-accent\)\]\/30:focus-within {
1875
+ --tw-ring-color: var(--wd-shell-accent);
1876
+ @supports (color: color-mix(in lab, red, red)) {
1877
+ --tw-ring-color: color-mix(in oklab, var(--wd-shell-accent) 30%, transparent);
1878
+ }
1879
+ }
1865
1880
  .wd-root .focus-within\:ring-ring\/30:focus-within {
1866
1881
  --tw-ring-color: var(--ring);
1867
1882
  @supports (color: color-mix(in lab, red, red)) {
@@ -2381,6 +2396,8 @@
2381
2396
  background: var(--term-user-bg);
2382
2397
  margin-inline: calc(-1 * var(--term-bleed, 0px));
2383
2398
  padding-inline: var(--term-bleed, 0px);
2399
+ user-select: text;
2400
+ cursor: text;
2384
2401
  }
2385
2402
  .wd-root [data-terminal] .term-link {
2386
2403
  text-decoration: underline;
@@ -2497,6 +2514,12 @@
2497
2514
  .wd-root [data-terminal].term-composer:focus-within, .wd-root [data-terminal].term-composer[data-dragging] {
2498
2515
  border-color: var(--term-blue);
2499
2516
  }
2517
+ .wd-root [data-terminal].term-composer-shell, .wd-root [data-terminal].term-composer-shell:focus-within {
2518
+ border-color: var(--term-magenta);
2519
+ }
2520
+ .wd-root [data-terminal] .term-composer-shell-hint {
2521
+ color: var(--term-dim);
2522
+ }
2500
2523
  .wd-root [data-terminal] .term-composer-field {
2501
2524
  font: inherit;
2502
2525
  color: var(--term-fg);
@@ -2834,6 +2857,7 @@
2834
2857
  --shine: #dcdcdc;
2835
2858
  --frame-shadow: var(--shadow-xs);
2836
2859
  --wd-scrub-bookmark: #8b3a8b;
2860
+ --wd-shell-accent: #8b3a8b;
2837
2861
  --surface: var(--bg-surface);
2838
2862
  --sidebar: #f4f4f4;
2839
2863
  --text: var(--fg-1);
@@ -2886,6 +2910,7 @@
2886
2910
  --shine: #303030;
2887
2911
  --frame-shadow: 0 4px 24px rgba(0, 0, 0, 0.55), 0 2px 6px rgba(0, 0, 0, 0.45);
2888
2912
  --wd-scrub-bookmark: #c586c0;
2913
+ --wd-shell-accent: #c586c0;
2889
2914
  --surface: var(--bg-surface);
2890
2915
  --sidebar: #131313;
2891
2916
  --text: var(--fg-1);
@@ -1,4 +1,4 @@
1
- import { Ct as Tip, Gt as Input, Jt as Button, R as EntryIcon, Tt as TooltipProvider, Xt as cn, bt as Spinner, t as SessionPanel, vt as Splitter } from "./SessionPanel-DrMj8Yv0.mjs";
1
+ import { Et as TooltipProvider, Kt as Input, R as EntryIcon, Yt as Button, Zt as cn, t as SessionPanel, wt as Tip, xt as Spinner, yt as Splitter } from "./SessionPanel-ErvQUyHe.mjs";
2
2
  import { p as formatBytes } from "./status-C4TAgVSc.mjs";
3
3
  import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
4
4
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workerdeck/ui",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "type": "module",
5
5
  "description": "Styled agent-control component library for WorkerDeck: session panel, transcript with streaming, tool-call cards, permission prompts, session list, composer. Tailwind v4 + Base UI + cva. Pairs with the headless @workerdeck/react layer.",
6
6
  "license": "MIT",
@@ -39,9 +39,9 @@
39
39
  "streamdown": "^2.6.0",
40
40
  "tailwind-merge": "^3.6.0",
41
41
  "use-stick-to-bottom": "^1.1.6",
42
- "@workerdeck/protocol": "2.1.0",
43
- "@workerdeck/react": "2.1.0",
44
- "@workerdeck/client": "2.1.0"
42
+ "@workerdeck/client": "2.2.1",
43
+ "@workerdeck/protocol": "2.2.1",
44
+ "@workerdeck/react": "2.2.1"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "monaco-editor": "^0.56.0",
@@ -1,4 +1,14 @@
1
- import { useEffect, useImperativeHandle, useMemo, useRef, useState, type DragEvent, type ReactNode, type Ref } from 'react'
1
+ import {
2
+ useEffect,
3
+ useImperativeHandle,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ type DragEvent,
8
+ type KeyboardEvent,
9
+ type ReactNode,
10
+ type Ref,
11
+ } from 'react'
2
12
  import type { SkillInfo, SlashCommandInfo } from '@workerdeck/protocol'
3
13
  import type { StagedAttachment, UseAttachmentsResult } from '@workerdeck/react'
4
14
  import { ArrowUp, FileText, Paperclip, RotateCw, Sparkles, Square, TriangleAlert, X } from 'lucide-react'
@@ -7,7 +17,7 @@ import { Spinner } from '../ui/Spinner.tsx'
7
17
  import { PromptArea } from '../prompt-area/prompt-area.tsx'
8
18
  import { usePromptAreaState } from '../prompt-area/use-prompt-area-state.ts'
9
19
  import { plainTextToSegments } from '../prompt-area/prompt-area-engine.ts'
10
- import { commandTrigger, mentionTrigger } from '../prompt-area/trigger-presets.ts'
20
+ import { commandTrigger, launchTrigger, mentionTrigger } from '../prompt-area/trigger-presets.ts'
11
21
  import { useTranscriptVariant } from './transcript-variant.tsx'
12
22
  import type { TerminalAffordances } from '../terminal/affordances.tsx'
13
23
  import { PROMPT_GLYPH } from '../terminal/items.tsx'
@@ -18,6 +28,9 @@ import { formatBytes } from '../../lib/format.ts'
18
28
 
19
29
  export type ComposerFileMatch = { path: string; relative: string }
20
30
 
31
+ const SHELL_PLACEHOLDER = 'Run a command on the host…'
32
+ const SHELL_HINT = '! shell mode · esc to exit'
33
+
21
34
  export type ComposerHandle = {
22
35
  insertText: (text: string) => void
23
36
  focus: () => void
@@ -32,6 +45,9 @@ export interface ComposerProps {
32
45
  commands?: SlashCommandInfo[]
33
46
  skills?: SkillInfo[]
34
47
  onSearchFiles?: (query: string, options: { signal: AbortSignal }) => Promise<ComposerFileMatch[]>
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.
50
+ onShellCommand?: (command: string) => void
35
51
  attachments?: UseAttachmentsResult
36
52
  toolbar?: ReactNode
37
53
  layout?: 'stacked' | 'inline'
@@ -72,6 +88,7 @@ export function Composer({
72
88
  commands,
73
89
  skills,
74
90
  onSearchFiles,
91
+ onShellCommand,
75
92
  attachments,
76
93
  toolbar,
77
94
  layout = 'stacked',
@@ -91,6 +108,7 @@ export function Composer({
91
108
  )
92
109
  const fileInput = useRef<HTMLInputElement>(null)
93
110
  const [dragging, setDragging] = useState(false)
111
+ const [shellMode, setShellMode] = useState(false)
94
112
 
95
113
  useImperativeHandle(
96
114
  ref,
@@ -196,8 +214,17 @@ export function Composer({
196
214
  }),
197
215
  )
198
216
  }
217
+ if (onShellCommand) {
218
+ configured.push(
219
+ launchTrigger({
220
+ char: '!',
221
+ accessibilityLabel: 'shell mode',
222
+ onActivate: () => setShellMode(true),
223
+ }),
224
+ )
225
+ }
199
226
  return configured.length > 0 ? configured : undefined
200
- }, [commands, skills, onSearchFiles])
227
+ }, [commands, skills, onSearchFiles, onShellCommand])
201
228
 
202
229
  const saveDraft = draft?.save
203
230
  useEffect(() => {
@@ -211,6 +238,18 @@ export function Composer({
211
238
  if (!canSend) {
212
239
  return
213
240
  }
241
+ if (shellMode) {
242
+ const command = plainText.trim()
243
+ if (command === '') {
244
+ return
245
+ }
246
+ onShellCommand?.(command)
247
+ setShellMode(false)
248
+ draft?.clear()
249
+ clear()
250
+ focus()
251
+ return
252
+ }
214
253
  onSend(plainText.trim(), attachments?.readyIds ?? [])
215
254
  attachments?.clear()
216
255
  draft?.clear()
@@ -218,6 +257,24 @@ export function Composer({
218
257
  focus()
219
258
  }
220
259
 
260
+ const leaveShellMode = () => {
261
+ setShellMode(false)
262
+ focus()
263
+ }
264
+ // Escape and backspace-on-empty both leave, because both are what a person reaches for when the
265
+ // pink frame was not what they meant. Backspace only when there is nothing left to delete.
266
+ const shellKeys = shellMode
267
+ ? {
268
+ onEscape: leaveShellMode,
269
+ onKeyDown: (e: KeyboardEvent<HTMLDivElement>) => {
270
+ if (e.key === 'Backspace' && isEmpty) {
271
+ e.preventDefault()
272
+ leaveShellMode()
273
+ }
274
+ },
275
+ }
276
+ : {}
277
+
221
278
  const pick = (files: FileList | null) => {
222
279
  if (files && files.length > 0) {
223
280
  attachments?.add(files)
@@ -250,7 +307,11 @@ export function Composer({
250
307
  </>
251
308
  ) : null
252
309
 
253
- const gutter = busy ? (
310
+ const gutter = shellMode ? (
311
+ <GlyphButton gutter label="Leave shell mode" tone="magenta" onClick={leaveShellMode}>
312
+ !
313
+ </GlyphButton>
314
+ ) : busy ? (
254
315
  <GlyphButton gutter label="Interrupt" tone="yellow" onClick={onInterrupt}>
255
316
  ✕
256
317
  </GlyphButton>
@@ -318,12 +379,12 @@ export function Composer({
318
379
  affordances={affordances}
319
380
  bleed="1ch"
320
381
  data-dragging={dragging || undefined}
321
- className={cn('term-composer', disabled && 'opacity-60')}
382
+ className={cn('term-composer', shellMode && 'term-composer-shell', disabled && 'opacity-60')}
322
383
  >
323
384
  <div className="term-composer-body">
324
385
  {staged.length > 0 && attachments ? <AttachmentStrip attachments={attachments} /> : null}
325
386
  <div className="term-row">
326
- {canAttach ? fileField : null}
387
+ {canAttach && !shellMode ? fileField : null}
327
388
  {gutter}
328
389
  <div className="flex min-w-0 items-start">
329
390
  <PromptArea
@@ -333,17 +394,23 @@ export function Composer({
333
394
  normalizeBullets={false}
334
395
  onSubmit={submit}
335
396
  disabled={disabled}
336
- placeholder={disabled ? 'Session ended' : placeholder}
397
+ placeholder={disabled ? 'Session ended' : shellMode ? SHELL_PLACEHOLDER : placeholder}
337
398
  minHeight={line}
338
399
  maxHeight={line * 10}
339
- aria-label="Message the agent"
400
+ aria-label={shellMode ? 'Run a shell command' : 'Message the agent'}
340
401
  className="term-composer-field min-w-0 flex-1"
341
402
  onImagePaste={(file) => attachments?.add([file])}
403
+ {...shellKeys}
342
404
  />
343
405
  {submitButton}
344
406
  </div>
345
407
  </div>
346
- {toolbar ? (
408
+ {shellMode ? (
409
+ <div className="term-row">
410
+ <span aria-hidden className="term-gutter" />
411
+ <span className="term-composer-shell-hint">{SHELL_HINT}</span>
412
+ </div>
413
+ ) : toolbar ? (
347
414
  <div className="term-row">
348
415
  <span aria-hidden className="term-gutter" />
349
416
  <div className="flex min-w-0 items-center gap-[1ch]">{toolbar}</div>
@@ -365,13 +432,15 @@ export function Composer({
365
432
  'transition-colors rounded-lg shadow-(--shadow-xs)',
366
433
  'focus-within:border-ring focus-within:ring-2 focus-within:ring-ring/30',
367
434
  dragging && 'border-ring ring-2 ring-ring/30',
435
+ shellMode &&
436
+ 'border-[var(--wd-shell-accent)] focus-within:border-[var(--wd-shell-accent)] focus-within:ring-[var(--wd-shell-accent)]/30',
368
437
  disabled && 'opacity-60',
369
438
  )}
370
439
  >
371
440
  {staged.length > 0 && attachments ? <AttachmentStrip attachments={attachments} /> : null}
372
441
  {inline ? (
373
442
  <div className="flex items-end gap-1 p-1">
374
- {attach}
443
+ {shellMode ? <ShellBadge onLeave={leaveShellMode} /> : attach}
375
444
  <PromptArea
376
445
  {...bind}
377
446
  triggers={triggers}
@@ -379,12 +448,13 @@ export function Composer({
379
448
  normalizeBullets={false}
380
449
  onSubmit={submit}
381
450
  disabled={disabled}
382
- placeholder={disabled ? 'Session ended' : placeholder}
451
+ placeholder={disabled ? 'Session ended' : shellMode ? SHELL_PLACEHOLDER : placeholder}
383
452
  minHeight={20}
384
453
  maxHeight={192}
385
- aria-label="Message the agent"
454
+ aria-label={shellMode ? 'Run a shell command' : 'Message the agent'}
386
455
  className="min-w-0 flex-1 py-1 text-body-sm text-text"
387
456
  onImagePaste={(file) => attachments?.add([file])}
457
+ {...shellKeys}
388
458
  />
389
459
  {submitButton}
390
460
  </div>
@@ -397,17 +467,18 @@ export function Composer({
397
467
  normalizeBullets={false}
398
468
  onSubmit={submit}
399
469
  disabled={disabled}
400
- placeholder={disabled ? 'Session ended' : placeholder}
470
+ placeholder={disabled ? 'Session ended' : shellMode ? SHELL_PLACEHOLDER : placeholder}
401
471
  minHeight={28}
402
472
  maxHeight={192}
403
- aria-label="Message the agent"
473
+ aria-label={shellMode ? 'Run a shell command' : 'Message the agent'}
404
474
  className="px-3 pt-2.5 pb-0 text-body-sm text-text"
405
475
  onImagePaste={(file) => attachments?.add([file])}
476
+ {...shellKeys}
406
477
  />
407
478
  <div className="flex items-center justify-between gap-2 px-2 pb-2">
408
479
  <div className="flex min-w-0 items-center gap-1">
409
- {attach}
410
- {toolbar}
480
+ {shellMode ? <ShellBadge onLeave={leaveShellMode} /> : attach}
481
+ {shellMode ? <span className="text-label text-text-muted">{SHELL_HINT}</span> : toolbar}
411
482
  </div>
412
483
  {submitButton}
413
484
  </div>
@@ -428,7 +499,7 @@ function GlyphButton({
428
499
  className,
429
500
  children,
430
501
  }: {
431
- tone?: 'blue' | 'yellow'
502
+ tone?: 'blue' | 'yellow' | 'magenta'
432
503
  label: string
433
504
  disabled?: boolean
434
505
  onClick: () => void
@@ -526,3 +597,17 @@ function extensionOf(name: string) {
526
597
  const dot = name.lastIndexOf('.')
527
598
  return dot > 0 ? name.slice(dot + 1).toUpperCase() : 'FILE'
528
599
  }
600
+
601
+ function ShellBadge({ onLeave }: { onLeave: () => void }) {
602
+ return (
603
+ <button
604
+ type="button"
605
+ aria-label="Leave shell mode"
606
+ title="Leave shell mode"
607
+ onClick={onLeave}
608
+ className="shrink-0 rounded px-1.5 py-0.5 font-mono text-label font-semibold text-[var(--wd-shell-accent)]"
609
+ >
610
+ !
611
+ </button>
612
+ )
613
+ }
@@ -229,10 +229,12 @@ export function SessionPanel({
229
229
  connection,
230
230
  replaying,
231
231
  protocolMismatch,
232
+ shell,
232
233
  models,
233
234
  effectiveModel,
234
235
  handle,
235
236
  send,
237
+ runShell,
236
238
  approve,
237
239
  deny,
238
240
  interrupt,
@@ -695,6 +697,7 @@ export function SessionPanel({
695
697
  attachments={attachments}
696
698
  draft={draft}
697
699
  onSearchFiles={hostFiles.available ? searchComposerFiles : undefined}
700
+ onShellCommand={shell ? runShell : undefined}
698
701
  layout={controlsExternal ? 'inline' : 'stacked'}
699
702
  toolbar={controlsExternal ? undefined : sessionControls}
700
703
  fontSize={effectiveTermFontSize}
@@ -5,6 +5,6 @@
5
5
  */
6
6
  export { PromptArea } from './prompt-area.tsx'
7
7
  export { usePromptAreaState } from './use-prompt-area-state.ts'
8
- export { commandTrigger, mentionTrigger, hashtagTrigger } from './trigger-presets.ts'
8
+ export { commandTrigger, mentionTrigger, hashtagTrigger, launchTrigger } from './trigger-presets.ts'
9
9
  export { segmentsToPlainText, plainTextToSegments, isSegmentsEmpty, getChipsByTrigger } from './segment-helpers.ts'
10
10
  export type { PromptAreaHandle, PromptAreaProps, Segment, ChipSegment, TextSegment, TriggerConfig, TriggerSuggestion } from './types.ts'
@@ -131,3 +131,27 @@ export function callbackTrigger(opts: CallbackTriggerOptions): TriggerConfig {
131
131
  ...rest,
132
132
  }
133
133
  }
134
+
135
+ // Launch trigger — fires onActivate and swallows the character
136
+
137
+ export type LaunchTriggerOptions = Omit<Partial<TriggerConfig>, 'mode'> & {
138
+ /** The trigger character. Required. */
139
+ char: string
140
+ }
141
+
142
+ /**
143
+ * Creates a **launch** trigger: `onActivate` fires on keydown and the character
144
+ * never enters the editor. For flipping the composer into another mode, or
145
+ * opening a surface outside it.
146
+ *
147
+ * Defaults: `position: 'start'`, `mode: 'launch'`.
148
+ */
149
+ export function launchTrigger(opts: LaunchTriggerOptions): TriggerConfig {
150
+ const { char, ...rest } = opts
151
+ return {
152
+ char,
153
+ position: 'start',
154
+ mode: 'launch',
155
+ ...rest,
156
+ }
157
+ }
package/src/index.ts CHANGED
@@ -28,6 +28,7 @@ export {
28
28
  commandTrigger,
29
29
  mentionTrigger,
30
30
  hashtagTrigger,
31
+ launchTrigger,
31
32
  segmentsToPlainText,
32
33
  plainTextToSegments,
33
34
  isSegmentsEmpty,
@@ -401,6 +401,14 @@
401
401
  background: var(--term-user-bg);
402
402
  margin-inline: calc(-1 * var(--term-bleed, 0px));
403
403
  padding-inline: var(--term-bleed, 0px);
404
+ /* Declared, never inherited. This band is the one transcript block that is
405
+ neither a `.term-press` nor plain flow text, so without saying so it takes
406
+ whatever the host gives it — which differs between the browser and the VS
407
+ Code webview, and a block that cannot host a selection endpoint does not
408
+ merely refuse the drag: the selection jumps to the next valid position
409
+ above it, swallowing everything in between. */
410
+ user-select: text;
411
+ cursor: text;
404
412
  }
405
413
 
406
414
  [data-terminal] .term-link {
@@ -643,6 +651,17 @@
643
651
  border-color: var(--term-blue);
644
652
  }
645
653
 
654
+ /* Shell mode. The frame and the gutter glyph move together and both go magenta, because the thing
655
+ the mode changes is where the text goes — a subtle cue would be the wrong kind of quiet. Listed
656
+ after the focus rule so it wins while the field is focused, which is the whole time it is on. */
657
+ [data-terminal].term-composer-shell,
658
+ [data-terminal].term-composer-shell:focus-within {
659
+ border-color: var(--term-magenta);
660
+ }
661
+ [data-terminal] .term-composer-shell-hint {
662
+ color: var(--term-dim);
663
+ }
664
+
646
665
  /* The contentEditable field. `font: inherit` is what makes it the cell — the
647
666
  surface above already carries the size and the line height, and anything that
648
667
  restates them here can disagree with the rows. */
@@ -155,6 +155,10 @@
155
155
  and every other colour on the rail is already spoken for. */
156
156
  --wd-scrub-bookmark: #8b3a8b;
157
157
 
158
+ /* Shell mode's frame and glyph. The same magenta as the bookmark rail, for the same reason: the app
159
+ palette has no magenta, and shell mode must not read as focus (blue) or as an error (red). */
160
+ --wd-shell-accent: #8b3a8b;
161
+
158
162
  /* ---------- aliases (bridge for the @theme utilities) ---------- */
159
163
  --surface: var(--bg-surface);
160
164
  --sidebar: #f4f4f4;
@@ -238,6 +242,7 @@
238
242
 
239
243
  /* See the light block — lifted for the dark ground. */
240
244
  --wd-scrub-bookmark: #c586c0;
245
+ --wd-shell-accent: #c586c0;
241
246
 
242
247
  /* ---------- aliases ---------- */
243
248
  --surface: var(--bg-surface);