@workerdeck/ui 2.1.0 → 2.2.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.
@@ -4385,6 +4385,22 @@ function hashtagTrigger(opts = {}) {
4385
4385
  ...rest
4386
4386
  };
4387
4387
  }
4388
+ /**
4389
+ * Creates a **launch** trigger: `onActivate` fires on keydown and the character
4390
+ * never enters the editor. For flipping the composer into another mode, or
4391
+ * opening a surface outside it.
4392
+ *
4393
+ * Defaults: `position: 'start'`, `mode: 'launch'`.
4394
+ */
4395
+ function launchTrigger(opts) {
4396
+ const { char, ...rest } = opts;
4397
+ return {
4398
+ char,
4399
+ position: "start",
4400
+ mode: "launch",
4401
+ ...rest
4402
+ };
4403
+ }
4388
4404
  //#endregion
4389
4405
  //#region src/components/agent/transcript-variant.tsx
4390
4406
  const VariantContext = createContext("cards");
@@ -5529,6 +5545,8 @@ function TerminalSurface({ fontSize, lineHeight, bleed, affordances, className,
5529
5545
  }
5530
5546
  //#endregion
5531
5547
  //#region src/components/agent/Composer.tsx
5548
+ const SHELL_PLACEHOLDER = "Run a command on the host…";
5549
+ const SHELL_HINT = "! shell mode · esc to exit";
5532
5550
  function cleanName(name) {
5533
5551
  return name.replace(/\s*\(MCP\)$/i, "");
5534
5552
  }
@@ -5542,13 +5560,14 @@ function matchScore(query, haystacks) {
5542
5560
  if (lowered.some((h) => h.startsWith(needle))) return 2;
5543
5561
  return lowered.some((h) => h.includes(needle)) ? 1 : 0;
5544
5562
  }
5545
- function Composer({ onSend, onInterrupt, busy, disabled, placeholder = "Message the agent…", commands, skills, onSearchFiles, attachments, toolbar, layout = "stacked", fontSize, lineHeight, affordances, draft, className, ref }) {
5563
+ function Composer({ onSend, onInterrupt, busy, disabled, placeholder = "Message the agent…", commands, skills, onSearchFiles, onShellCommand, attachments, toolbar, layout = "stacked", fontSize, lineHeight, affordances, draft, className, ref }) {
5546
5564
  const inline = layout === "inline";
5547
5565
  const terminal = useTranscriptVariant() === "terminal";
5548
5566
  const [initialDraft] = useState(() => draft?.initialText ?? "");
5549
5567
  const { bind, plainText, isEmpty, clear, focus } = usePromptAreaState(initialDraft === "" ? void 0 : { initialValue: plainTextToSegments(initialDraft) });
5550
5568
  const fileInput = useRef(null);
5551
5569
  const [dragging, setDragging] = useState(false);
5570
+ const [shellMode, setShellMode] = useState(false);
5552
5571
  useImperativeHandle(ref, () => ({
5553
5572
  insertText: (text) => {
5554
5573
  const prefix = plainText.length > 0 && !/\s$/.test(plainText) ? " " : "";
@@ -5642,11 +5661,17 @@ function Composer({ onSend, onInterrupt, busy, disabled, placeholder = "Message
5642
5661
  chipClassName: "font-mono",
5643
5662
  emptyMessage: "No matching files"
5644
5663
  }));
5664
+ if (onShellCommand) configured.push(launchTrigger({
5665
+ char: "!",
5666
+ accessibilityLabel: "shell mode",
5667
+ onActivate: () => setShellMode(true)
5668
+ }));
5645
5669
  return configured.length > 0 ? configured : void 0;
5646
5670
  }, [
5647
5671
  commands,
5648
5672
  skills,
5649
- onSearchFiles
5673
+ onSearchFiles,
5674
+ onShellCommand
5650
5675
  ]);
5651
5676
  const saveDraft = draft?.save;
5652
5677
  useEffect(() => {
@@ -5656,12 +5681,35 @@ function Composer({ onSend, onInterrupt, busy, disabled, placeholder = "Message
5656
5681
  const canSend = !disabled && (!isEmpty || staged.length > 0) && !attachments?.uploading && !attachments?.hasFailure;
5657
5682
  const submit = () => {
5658
5683
  if (!canSend) return;
5684
+ if (shellMode) {
5685
+ const command = plainText.trim();
5686
+ if (command === "") return;
5687
+ onShellCommand?.(command);
5688
+ setShellMode(false);
5689
+ draft?.clear();
5690
+ clear();
5691
+ focus();
5692
+ return;
5693
+ }
5659
5694
  onSend(plainText.trim(), attachments?.readyIds ?? []);
5660
5695
  attachments?.clear();
5661
5696
  draft?.clear();
5662
5697
  clear();
5663
5698
  focus();
5664
5699
  };
5700
+ const leaveShellMode = () => {
5701
+ setShellMode(false);
5702
+ focus();
5703
+ };
5704
+ const shellKeys = shellMode ? {
5705
+ onEscape: leaveShellMode,
5706
+ onKeyDown: (e) => {
5707
+ if (e.key === "Backspace" && isEmpty) {
5708
+ e.preventDefault();
5709
+ leaveShellMode();
5710
+ }
5711
+ }
5712
+ } : {};
5665
5713
  const pick = (files) => {
5666
5714
  if (files && files.length > 0) attachments?.add(files);
5667
5715
  };
@@ -5685,7 +5733,13 @@ function Composer({ onSend, onInterrupt, busy, disabled, placeholder = "Message
5685
5733
  onClick: () => fileInput.current?.click(),
5686
5734
  children: /* @__PURE__ */ jsx(Paperclip, { className: "size-4" })
5687
5735
  })] }) : null;
5688
- const gutter = busy ? /* @__PURE__ */ jsx(GlyphButton, {
5736
+ const gutter = shellMode ? /* @__PURE__ */ jsx(GlyphButton, {
5737
+ gutter: true,
5738
+ label: "Leave shell mode",
5739
+ tone: "magenta",
5740
+ onClick: leaveShellMode,
5741
+ children: "!"
5742
+ }) : busy ? /* @__PURE__ */ jsx(GlyphButton, {
5689
5743
  gutter: true,
5690
5744
  label: "Interrupt",
5691
5745
  tone: "yellow",
@@ -5768,7 +5822,7 @@ function Composer({ onSend, onInterrupt, busy, disabled, placeholder = "Message
5768
5822
  affordances,
5769
5823
  bleed: "1ch",
5770
5824
  "data-dragging": dragging || void 0,
5771
- className: cn("term-composer", disabled && "opacity-60"),
5825
+ className: cn("term-composer", shellMode && "term-composer-shell", disabled && "opacity-60"),
5772
5826
  children: /* @__PURE__ */ jsxs("div", {
5773
5827
  className: "term-composer-body",
5774
5828
  children: [
@@ -5776,7 +5830,7 @@ function Composer({ onSend, onInterrupt, busy, disabled, placeholder = "Message
5776
5830
  /* @__PURE__ */ jsxs("div", {
5777
5831
  className: "term-row",
5778
5832
  children: [
5779
- canAttach ? fileField : null,
5833
+ canAttach && !shellMode ? fileField : null,
5780
5834
  gutter,
5781
5835
  /* @__PURE__ */ jsxs("div", {
5782
5836
  className: "flex min-w-0 items-start",
@@ -5787,17 +5841,27 @@ function Composer({ onSend, onInterrupt, busy, disabled, placeholder = "Message
5787
5841
  normalizeBullets: false,
5788
5842
  onSubmit: submit,
5789
5843
  disabled,
5790
- placeholder: disabled ? "Session ended" : placeholder,
5844
+ placeholder: disabled ? "Session ended" : shellMode ? SHELL_PLACEHOLDER : placeholder,
5791
5845
  minHeight: line,
5792
5846
  maxHeight: line * 10,
5793
- "aria-label": "Message the agent",
5847
+ "aria-label": shellMode ? "Run a shell command" : "Message the agent",
5794
5848
  className: "term-composer-field min-w-0 flex-1",
5795
- onImagePaste: (file) => attachments?.add([file])
5849
+ onImagePaste: (file) => attachments?.add([file]),
5850
+ ...shellKeys
5796
5851
  }), submitButton]
5797
5852
  })
5798
5853
  ]
5799
5854
  }),
5800
- toolbar ? /* @__PURE__ */ jsxs("div", {
5855
+ shellMode ? /* @__PURE__ */ jsxs("div", {
5856
+ className: "term-row",
5857
+ children: [/* @__PURE__ */ jsx("span", {
5858
+ "aria-hidden": true,
5859
+ className: "term-gutter"
5860
+ }), /* @__PURE__ */ jsx("span", {
5861
+ className: "term-composer-shell-hint",
5862
+ children: SHELL_HINT
5863
+ })]
5864
+ }) : toolbar ? /* @__PURE__ */ jsxs("div", {
5801
5865
  className: "term-row",
5802
5866
  children: [/* @__PURE__ */ jsx("span", {
5803
5867
  "aria-hidden": true,
@@ -5817,11 +5881,11 @@ function Composer({ onSend, onInterrupt, busy, disabled, placeholder = "Message
5817
5881
  className: cn("px-[var(--wd-composer-padding)] pb-[var(--wd-composer-padding)]", className),
5818
5882
  children: [/* @__PURE__ */ jsxs("div", {
5819
5883
  ...dropHandlers,
5820
- className: cn("mx-auto w-full max-w-[var(--wd-transcript-max-width)] overflow-hidden border border-border bg-bg", "transition-colors rounded-lg shadow-(--shadow-xs)", "focus-within:border-ring focus-within:ring-2 focus-within:ring-ring/30", dragging && "border-ring ring-2 ring-ring/30", disabled && "opacity-60"),
5884
+ className: cn("mx-auto w-full max-w-[var(--wd-transcript-max-width)] overflow-hidden border border-border bg-bg", "transition-colors rounded-lg shadow-(--shadow-xs)", "focus-within:border-ring focus-within:ring-2 focus-within:ring-ring/30", dragging && "border-ring ring-2 ring-ring/30", shellMode && "border-[var(--wd-shell-accent)] focus-within:border-[var(--wd-shell-accent)] focus-within:ring-[var(--wd-shell-accent)]/30", disabled && "opacity-60"),
5821
5885
  children: [staged.length > 0 && attachments ? /* @__PURE__ */ jsx(AttachmentStrip, { attachments }) : null, inline ? /* @__PURE__ */ jsxs("div", {
5822
5886
  className: "flex items-end gap-1 p-1",
5823
5887
  children: [
5824
- attach,
5888
+ shellMode ? /* @__PURE__ */ jsx(ShellBadge, { onLeave: leaveShellMode }) : attach,
5825
5889
  /* @__PURE__ */ jsx(PromptArea, {
5826
5890
  ...bind,
5827
5891
  triggers,
@@ -5829,12 +5893,13 @@ function Composer({ onSend, onInterrupt, busy, disabled, placeholder = "Message
5829
5893
  normalizeBullets: false,
5830
5894
  onSubmit: submit,
5831
5895
  disabled,
5832
- placeholder: disabled ? "Session ended" : placeholder,
5896
+ placeholder: disabled ? "Session ended" : shellMode ? SHELL_PLACEHOLDER : placeholder,
5833
5897
  minHeight: 20,
5834
5898
  maxHeight: 192,
5835
- "aria-label": "Message the agent",
5899
+ "aria-label": shellMode ? "Run a shell command" : "Message the agent",
5836
5900
  className: "min-w-0 flex-1 py-1 text-body-sm text-text",
5837
- onImagePaste: (file) => attachments?.add([file])
5901
+ onImagePaste: (file) => attachments?.add([file]),
5902
+ ...shellKeys
5838
5903
  }),
5839
5904
  submitButton
5840
5905
  ]
@@ -5845,17 +5910,21 @@ function Composer({ onSend, onInterrupt, busy, disabled, placeholder = "Message
5845
5910
  normalizeBullets: false,
5846
5911
  onSubmit: submit,
5847
5912
  disabled,
5848
- placeholder: disabled ? "Session ended" : placeholder,
5913
+ placeholder: disabled ? "Session ended" : shellMode ? SHELL_PLACEHOLDER : placeholder,
5849
5914
  minHeight: 28,
5850
5915
  maxHeight: 192,
5851
- "aria-label": "Message the agent",
5916
+ "aria-label": shellMode ? "Run a shell command" : "Message the agent",
5852
5917
  className: "px-3 pt-2.5 pb-0 text-body-sm text-text",
5853
- onImagePaste: (file) => attachments?.add([file])
5918
+ onImagePaste: (file) => attachments?.add([file]),
5919
+ ...shellKeys
5854
5920
  }), /* @__PURE__ */ jsxs("div", {
5855
5921
  className: "flex items-center justify-between gap-2 px-2 pb-2",
5856
5922
  children: [/* @__PURE__ */ jsxs("div", {
5857
5923
  className: "flex min-w-0 items-center gap-1",
5858
- children: [attach, toolbar]
5924
+ children: [shellMode ? /* @__PURE__ */ jsx(ShellBadge, { onLeave: leaveShellMode }) : attach, shellMode ? /* @__PURE__ */ jsx("span", {
5925
+ className: "text-label text-text-muted",
5926
+ children: SHELL_HINT
5927
+ }) : toolbar]
5859
5928
  }), submitButton]
5860
5929
  })] })]
5861
5930
  }), errorRow]
@@ -5933,6 +6002,16 @@ function extensionOf(name) {
5933
6002
  const dot = name.lastIndexOf(".");
5934
6003
  return dot > 0 ? name.slice(dot + 1).toUpperCase() : "FILE";
5935
6004
  }
6005
+ function ShellBadge({ onLeave }) {
6006
+ return /* @__PURE__ */ jsx("button", {
6007
+ type: "button",
6008
+ "aria-label": "Leave shell mode",
6009
+ title: "Leave shell mode",
6010
+ onClick: onLeave,
6011
+ className: "shrink-0 rounded px-1.5 py-0.5 font-mono text-label font-semibold text-[var(--wd-shell-accent)]",
6012
+ children: "!"
6013
+ });
6014
+ }
5936
6015
  //#endregion
5937
6016
  //#region src/lib/css.ts
5938
6017
  function cssColor(color) {
@@ -11069,7 +11148,7 @@ function SessionPanel({ client, sessionId, header, panelSurface = "internal", st
11069
11148
  const [panel, setPanel] = useState();
11070
11149
  const [showCompletedTasks, setShowCompletedTasks] = useState(false);
11071
11150
  const [taskReveal, setTaskReveal] = useState();
11072
- const { state, connection, replaying, protocolMismatch, models, effectiveModel, handle, send, approve, deny, interrupt, setModel, setPermissionMode, reconnectNow, loadFullResult } = useClaudeSession(client, sessionId, {
11151
+ const { state, connection, replaying, protocolMismatch, shell, models, effectiveModel, handle, send, runShell, approve, deny, interrupt, setModel, setPermissionMode, reconnectNow, loadFullResult } = useClaudeSession(client, sessionId, {
11073
11152
  onProtocolError: setProtocolError,
11074
11153
  cacheTranscript
11075
11154
  });
@@ -11479,6 +11558,7 @@ function SessionPanel({ client, sessionId, header, panelSurface = "internal", st
11479
11558
  attachments,
11480
11559
  draft,
11481
11560
  onSearchFiles: hostFiles.available ? searchComposerFiles : void 0,
11561
+ onShellCommand: shell ? runShell : void 0,
11482
11562
  layout: controlsExternal ? "inline" : "stacked",
11483
11563
  toolbar: controlsExternal ? void 0 : sessionControls,
11484
11564
  fontSize: effectiveTermFontSize,
@@ -11612,6 +11692,6 @@ function Notice({ level, onDismiss, children }) {
11612
11692
  });
11613
11693
  }
11614
11694
  //#endregion
11615
- export { Ink as $, PERMISSION_MODES as A, DialogHeader as At, TaskList as B, SelectItem as Bt, TerminalPermissionPrompt as C, Tip as Ct, SubagentStrip as D, DialogBody as Dt, parseUserQuestions as E, Dialog$1 as Et, SkillsDialog as F, MenuItem as Ft, TerminalSurface as G, Input as Gt, contextNote as H, SelectTrigger as Ht, McpDialog as I, MenuSeparator as It, TerminalMarkdown as J, Button as Jt, ToolTitleProvider as K, Badge as Kt, HostFilesDialog as L, MenuTrigger as Lt, permissionModeChoices as M, DialogTrigger as Mt, permissionModeMeta as N, Menu$1 as Nt, PermissionPrompt as O, DialogClose as Ot, ModelSelect as P, MenuContent as Pt, Blank as Q, EntryIcon as R, Select$1 as Rt, TerminalQuestionPrompt as S, copyText as St, QuestionPrompt as T, TooltipProvider as Tt, Composer as U, SelectValue as Ut, ContextDialog as V, SelectItemText as Vt, skillPrompt as W, PortalScope as Wt, previewPatch as X, cn as Xt, TerminalDiff as Y, buttonVariants as Yt, Band as Z, ConversationContent as _, ProgressRing as _t, Transcript as a, toolIcon as at, STATUS_META as b, Spinner as bt, PromptTokenText as c, useTranscriptDensity as ct, FileCard as d, hashtagTrigger as dt, Row as et, TerminalItemView as f, mentionTrigger as ft, Conversation as g, segmentsToPlainText as gt, Loader as h, plainTextToSegments as ht, useMinuteClock as i, isMutatingTool as it, PermissionModeSelect as j, DialogRow as jt, Response as k, DialogContent as kt, Message as l, useTranscriptVariant as lt, SessionEmptyState as m, PromptArea as mt, UsageDialog as n, WithActions as nt, ToolCallCard as o, TranscriptDensityProvider as ot, TerminalTranscript as p, usePromptAreaState as pt, useToolTitle as q, badgeVariants as qt, UsageMeters as r, useAffordances as rt, Reasoning as s, TranscriptVariantProvider as st, SessionPanel as t, CopyAction as tt, MessageContent as u, commandTrigger as ut, ConversationScrollButton as v, Splitter as vt, QUESTION_BEHAVIORS as w, TooltipContent as wt, SessionInfoDialog as x, CopyButton as xt, StatusBar as y, CodeBlock as yt, TasksDialog as z, SelectContent as zt };
11695
+ export { Ink as $, PERMISSION_MODES as A, DialogContent as At, TaskList as B, SelectContent as Bt, TerminalPermissionPrompt as C, copyText as Ct, SubagentStrip as D, Dialog$1 as Dt, parseUserQuestions as E, TooltipProvider as Et, SkillsDialog as F, MenuContent as Ft, TerminalSurface as G, PortalScope as Gt, contextNote as H, SelectItemText as Ht, McpDialog as I, MenuItem as It, TerminalMarkdown as J, badgeVariants as Jt, ToolTitleProvider as K, Input as Kt, HostFilesDialog as L, MenuSeparator as Lt, permissionModeChoices as M, DialogRow as Mt, permissionModeMeta as N, DialogTrigger as Nt, PermissionPrompt as O, DialogBody as Ot, ModelSelect as P, Menu$1 as Pt, Blank as Q, EntryIcon as R, MenuTrigger as Rt, TerminalQuestionPrompt as S, CopyButton as St, QuestionPrompt as T, TooltipContent as Tt, Composer as U, SelectTrigger as Ut, ContextDialog as V, SelectItem as Vt, skillPrompt as W, SelectValue as Wt, previewPatch as X, buttonVariants as Xt, TerminalDiff as Y, Button as Yt, Band as Z, cn as Zt, ConversationContent as _, segmentsToPlainText as _t, Transcript as a, toolIcon as at, STATUS_META as b, CodeBlock as bt, PromptTokenText as c, useTranscriptDensity as ct, FileCard as d, hashtagTrigger as dt, Row as et, TerminalItemView as f, launchTrigger as ft, Conversation as g, plainTextToSegments as gt, Loader as h, PromptArea as ht, useMinuteClock as i, isMutatingTool as it, PermissionModeSelect as j, DialogHeader as jt, Response as k, DialogClose as kt, Message as l, useTranscriptVariant as lt, SessionEmptyState as m, usePromptAreaState as mt, UsageDialog as n, WithActions as nt, ToolCallCard as o, TranscriptDensityProvider as ot, TerminalTranscript as p, mentionTrigger as pt, useToolTitle as q, Badge as qt, UsageMeters as r, useAffordances as rt, Reasoning as s, TranscriptVariantProvider as st, SessionPanel as t, CopyAction as tt, MessageContent as u, commandTrigger as ut, ConversationScrollButton as v, ProgressRing as vt, QUESTION_BEHAVIORS as w, Tip as wt, SessionInfoDialog as x, Spinner as xt, StatusBar as y, Splitter as yt, TasksDialog as z, Select$1 as zt };
11616
11696
 
11617
- //# sourceMappingURL=SessionPanel-DrMj8Yv0.mjs.map
11697
+ //# sourceMappingURL=SessionPanel-ErvQUyHe.mjs.map