@workerdeck/ui 0.11.0 → 0.12.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.
@@ -5572,6 +5572,48 @@ function useTranscriptVariant() {
5572
5572
  function useLines() {
5573
5573
  return useTranscriptVariant() === "lines";
5574
5574
  }
5575
+ const DensityContext = createContext("comfortable");
5576
+ function TranscriptDensityProvider({ value, children }) {
5577
+ return /* @__PURE__ */ jsx(DensityContext.Provider, {
5578
+ value,
5579
+ children
5580
+ });
5581
+ }
5582
+ function useTranscriptDensity() {
5583
+ return useContext(DensityContext);
5584
+ }
5585
+ /**
5586
+ * The gap between two rows, per variant and density — the whole of the density
5587
+ * feature, since it is the only vertical spacing between rows that exists.
5588
+ *
5589
+ * `className` goes on the **measured** wrapper (see `Transcript`), so the gap is
5590
+ * part of each row's measured height and no pixel constant is load-bearing.
5591
+ * `px` is fed to `estimateSize` alone, where being approximate is the contract:
5592
+ * it sets the scrollbar's length before rows mount and is replaced by a real
5593
+ * measurement the moment one does.
5594
+ *
5595
+ * `lines` + `compact` is the only combination with no gap at all: there the
5596
+ * row's own `py-0.5` is the entire separation, which is what makes it compact.
5597
+ */
5598
+ const ROW_GAP = {
5599
+ cards: {
5600
+ comfortable: {
5601
+ className: "pt-4",
5602
+ px: 16
5603
+ },
5604
+ compact: {
5605
+ className: "pt-2",
5606
+ px: 8
5607
+ }
5608
+ },
5609
+ lines: {
5610
+ comfortable: {
5611
+ className: "pt-4",
5612
+ px: 16
5613
+ },
5614
+ compact: { px: 0 }
5615
+ }
5616
+ };
5575
5617
  /**
5576
5618
  * The left gutter of a line item: one glyph, fixed width, so every row's text
5577
5619
  * starts on the same column no matter which kind of event it is. Decorative —
@@ -6818,27 +6860,68 @@ function FileCard({ item, href, className }) {
6818
6860
  });
6819
6861
  }
6820
6862
  //#endregion
6821
- //#region src/components/agent/Loader.tsx
6863
+ //#region src/components/agent/pulse.tsx
6822
6864
  /**
6823
- * The frames of the working marker, and the rate they turn over.
6865
+ * The brand mark's pulse, as characters — the working marker every surface in the
6866
+ * transcript animates.
6824
6867
  *
6825
- * A four-pointed star growing and shrinking it reads as *activity* at a glance
6826
- * without any of the pixel-fitting a braille or block spinner needs, and it is
6827
- * the same shape a terminal agent uses because a terminal is where this
6828
- * vocabulary comes from. ~8fps: fast enough to be alive, slow enough not to
6829
- * strobe next to streaming text.
6868
+ * These are the mark's own four states (`docs/assets/BRAND.md`, "The loading
6869
+ * state"): a dot, an outline, a semi and a full diamond, built in the SVG from
6870
+ * two shapes rather than four drawings. 150ms each, so one cycle is the 0.6s
6871
+ * clock the marker pulses on in `icon-loading.svg` the same rhythm, in the
6872
+ * medium a transcript row actually has.
6873
+ *
6874
+ * BRAND.md's caveat applies and is satisfied here: `U+25C6/7/8` are East-Asian
6875
+ * *ambiguous width*, so they can render double-width in a terminal under an
6876
+ * East-Asian locale and shift every line with them. They are safe wherever the
6877
+ * glyph is centred in a fixed-width box, which is what `LineGlyph` is. Anything
6878
+ * writing to a real terminal must use the ASCII set instead.
6830
6879
  */
6831
- const FRAMES = [
6832
- "",
6833
- "",
6834
- "",
6835
- "",
6836
- "✽",
6837
- "✻",
6838
- "✶",
6839
- "✳"
6880
+ const PULSE_FRAMES = [
6881
+ "",
6882
+ "",
6883
+ "",
6884
+ ""
6840
6885
  ];
6841
- const FRAME_MS = 120;
6886
+ /**
6887
+ * The resting state. Stopping the animation lands on the complete mark rather
6888
+ * than on a half-drawn frame — the same property that makes the SVG's
6889
+ * `prefers-reduced-motion` free (see BRAND.md: `translateY(0)` *is* the mark).
6890
+ */
6891
+ const PULSE_REST = PULSE_FRAMES[PULSE_FRAMES.length - 1];
6892
+ /** The OS-level "stop moving things" setting. A spinner is decoration — the word
6893
+ * beside it carries the meaning — so honouring this costs nothing. */
6894
+ function usePrefersReducedMotion() {
6895
+ const [reduced, setReduced] = useState(false);
6896
+ useEffect(() => {
6897
+ const query = window.matchMedia?.("(prefers-reduced-motion: reduce)");
6898
+ if (!query) return;
6899
+ setReduced(query.matches);
6900
+ const onChange = () => setReduced(query.matches);
6901
+ query.addEventListener("change", onChange);
6902
+ return () => query.removeEventListener("change", onChange);
6903
+ }, []);
6904
+ return reduced;
6905
+ }
6906
+ /**
6907
+ * The current pulse frame, ticking while `animated`. Callers mount this only
6908
+ * while something is actually in flight, so nothing here runs on an idle
6909
+ * session; with reduced motion, or when not animating, it holds at rest.
6910
+ */
6911
+ function usePulse(animated) {
6912
+ const reduced = usePrefersReducedMotion();
6913
+ const running = animated && !reduced;
6914
+ const [frame, setFrame] = useState(0);
6915
+ useEffect(() => {
6916
+ if (!running) return;
6917
+ const timer = setInterval(() => setFrame((f) => f + 1), 150);
6918
+ return () => clearInterval(timer);
6919
+ }, [running]);
6920
+ if (!running) return PULSE_REST;
6921
+ return PULSE_FRAMES[frame % PULSE_FRAMES.length];
6922
+ }
6923
+ //#endregion
6924
+ //#region src/components/agent/Loader.tsx
6842
6925
  /**
6843
6926
  * What it says it is doing while it hasn't said anything yet. Cycled on a slow
6844
6927
  * clock so a long turn doesn't sit under one frozen word — a still label reads
@@ -6859,42 +6942,17 @@ const VERBS = [
6859
6942
  "Noodling"
6860
6943
  ];
6861
6944
  const VERB_MS = 4e3;
6862
- /** Ticks while mounted, at the spinner's rate. Mounted only while a turn is in
6863
- * flight, so nothing here runs on an idle session. */
6864
- function useFrames(animated) {
6865
- const [frame, setFrame] = useState(0);
6866
- useEffect(() => {
6867
- if (!animated) return;
6868
- const timer = setInterval(() => setFrame((f) => f + 1), FRAME_MS);
6869
- return () => clearInterval(timer);
6870
- }, [animated]);
6871
- return frame;
6872
- }
6873
- /** The OS-level "stop moving things" setting. A spinner is decoration; the word
6874
- * beside it carries the meaning, so honouring this costs nothing. */
6875
- function usePrefersReducedMotion() {
6876
- const [reduced, setReduced] = useState(false);
6877
- useEffect(() => {
6878
- const query = window.matchMedia?.("(prefers-reduced-motion: reduce)");
6879
- if (!query) return;
6880
- setReduced(query.matches);
6881
- const onChange = () => setReduced(query.matches);
6882
- query.addEventListener("change", onChange);
6883
- return () => query.removeEventListener("change", onChange);
6884
- }, []);
6885
- return reduced;
6886
- }
6887
6945
  /**
6888
6946
  * "The agent is working and hasn't produced output yet."
6889
6947
  *
6890
- * `lines`: a terminal working line — animated glyph in the gutter, a verb, and
6891
- * the readings that answer "should I still be waiting?" in one parenthesis.
6892
- * `cards`: the three-dot pulse, unchanged.
6948
+ * `lines`: a terminal working line — the mark's own pulse in the gutter (see
6949
+ * `pulse.tsx`), a verb, and the readings that answer "should I still be
6950
+ * waiting?" in one parenthesis. `cards`: the three-dot pulse, unchanged — the
6951
+ * dashboard's loader is not a gutter glyph and has no column to pulse in.
6893
6952
  */
6894
6953
  function Loader({ label, startedAt, tokens, className }) {
6895
6954
  const lines = useLines();
6896
- const reducedMotion = usePrefersReducedMotion();
6897
- const frame = useFrames(lines && !reducedMotion);
6955
+ const pulse = usePulse(lines);
6898
6956
  if (!lines) return /* @__PURE__ */ jsxs("div", {
6899
6957
  "data-slot": "loader",
6900
6958
  className: cn("flex items-center gap-2 py-1 text-body-sm text-fg-4", className),
@@ -6918,7 +6976,7 @@ function Loader({ label, startedAt, tokens, className }) {
6918
6976
  className: cn("flex items-baseline gap-2", className),
6919
6977
  children: [/* @__PURE__ */ jsx(LineGlyph, {
6920
6978
  className: "text-accent",
6921
- children: FRAMES[frame % FRAMES.length]
6979
+ children: pulse
6922
6980
  }), /* @__PURE__ */ jsxs("span", {
6923
6981
  className: "min-w-0 flex-1 text-body-sm leading-5 text-fg-3",
6924
6982
  children: [
@@ -7229,6 +7287,7 @@ function ToolCallCard({ item, hostImage, className }) {
7229
7287
  const status = item.status ?? (item.result === void 0 ? "running" : "settled");
7230
7288
  const badge = STATE_BADGE[status];
7231
7289
  const isError = status === "failed" || item.result?.isError === true;
7290
+ const pulse = usePulse(lines && badge.busy);
7232
7291
  const Icon = toolIcon(item.name);
7233
7292
  const resultText = item.result?.text ?? "";
7234
7293
  const truncated = !fullResult && resultText.length > RESULT_PREVIEW_CHARS;
@@ -7284,7 +7343,7 @@ function ToolCallCard({ item, hostImage, className }) {
7284
7343
  children: [
7285
7344
  /* @__PURE__ */ jsx(LineGlyph, {
7286
7345
  className: status === "settled" && !isError && isMutatingTool(item.name) ? "text-success" : STATE_GLYPH[status],
7287
- children: badge.busy ? "◐" : "●"
7346
+ children: badge.busy ? pulse : "●"
7288
7347
  }),
7289
7348
  /* @__PURE__ */ jsxs("span", {
7290
7349
  className: "min-w-0 flex-1 truncate text-body-sm leading-5 text-fg-3",
@@ -7304,7 +7363,6 @@ function ToolCallCard({ item, hostImage, className }) {
7304
7363
  className: "shrink-0 text-label text-fg-4",
7305
7364
  children: item.backend
7306
7365
  }) : null,
7307
- badge.busy ? /* @__PURE__ */ jsx(Spinner, { className: "size-3 shrink-0 self-center text-fg-4" }) : null,
7308
7366
  status === "deferred" ? /* @__PURE__ */ jsx(Clock, { className: "size-3 shrink-0 self-center text-fg-4" }) : null,
7309
7367
  isError && !badge.busy ? /* @__PURE__ */ jsx("span", {
7310
7368
  className: "shrink-0 text-label text-danger",
@@ -7707,7 +7765,7 @@ function nestedClass(item, lines) {
7707
7765
  * transient UI state (an expanded tool card, an opened reasoning block) resets
7708
7766
  * once the row scrolls far enough away to unmount.
7709
7767
  */
7710
- function TranscriptRows({ rows, boundary, since, lines, fileUrl, attachmentUrl, hostImage, jumpToRecapRef }) {
7768
+ function TranscriptRows({ rows, boundary, since, lines, gap, fileUrl, attachmentUrl, hostImage, jumpToRecapRef }) {
7711
7769
  const stick = useStickToBottomContext();
7712
7770
  const [scrollElement, setScrollElement] = useState(null);
7713
7771
  useEffect(() => {
@@ -7719,7 +7777,7 @@ function TranscriptRows({ rows, boundary, since, lines, fileUrl, attachmentUrl,
7719
7777
  if (scrollElement && virtualizer.scrollElement !== scrollElement) virtualizer.scrollOffset = scrollElement.scrollTop;
7720
7778
  return scrollElement;
7721
7779
  },
7722
- estimateSize: () => lines ? 32 : 100,
7780
+ estimateSize: () => (lines ? 32 : 100) + gap.px,
7723
7781
  overscan: 8,
7724
7782
  getItemKey: (index) => rows[index].key,
7725
7783
  useFlushSync: true
@@ -7769,7 +7827,7 @@ function TranscriptRows({ rows, boundary, since, lines, fileUrl, attachmentUrl,
7769
7827
  return /* @__PURE__ */ jsx("div", {
7770
7828
  ref: virtualizer.measureElement,
7771
7829
  "data-index": virtualRow.index,
7772
- className: cn("absolute inset-x-0 top-0", !lines && virtualRow.index > 0 && "pt-4"),
7830
+ className: cn("absolute inset-x-0 top-0", virtualRow.index > 0 && gap.className),
7773
7831
  style: { transform: `translateY(${virtualRow.start}px)` },
7774
7832
  children: "item" in row ? /* @__PURE__ */ jsx("div", {
7775
7833
  className: cn(lines && "-mx-1 rounded-sm px-1 py-0.5 transition-colors hover:bg-surface-hover", boundary !== void 0 && row.index < boundary && "opacity-45", nestedClass(row.item, lines)),
@@ -7787,8 +7845,9 @@ function TranscriptRows({ rows, boundary, since, lines, fileUrl, attachmentUrl,
7787
7845
  })
7788
7846
  });
7789
7847
  }
7790
- function Transcript({ state, fileUrl, attachmentUrl, canBrowseFiles, hostImage, variant = "cards", catchUp, jumpToRecapRef, className }) {
7848
+ function Transcript({ state, fileUrl, attachmentUrl, canBrowseFiles, hostImage, variant = "cards", density = "comfortable", catchUp, jumpToRecapRef, className }) {
7791
7849
  const lines = variant === "lines";
7850
+ const gap = ROW_GAP[variant][density];
7792
7851
  const runStartedAt = useRunStart(state.status);
7793
7852
  const following = useSettled(state.items.length, state.status);
7794
7853
  const boundary = catchUp && catchUp.from > 0 && catchUp.from < state.items.length ? catchUp.from : void 0;
@@ -7829,6 +7888,7 @@ function Transcript({ state, fileUrl, attachmentUrl, canBrowseFiles, hostImage,
7829
7888
  boundary,
7830
7889
  since: catchUp?.since,
7831
7890
  lines,
7891
+ gap,
7832
7892
  fileUrl,
7833
7893
  attachmentUrl,
7834
7894
  hostImage,
@@ -7992,7 +8052,7 @@ const INTERACTIVE = [
7992
8052
  * the engine name — an absent capability hides the control instead of offering
7993
8053
  * one that can only fail.
7994
8054
  */
7995
- function SessionPanel({ client, sessionId, header, panelSurface = "internal", statusSurface = "internal", onOpenPanel, onVitals, transcriptVariant = "cards", controlsSurface = "internal", onControls, focusComposerOnClick = false, unseen, className }) {
8055
+ function SessionPanel({ client, sessionId, header, panelSurface = "internal", statusSurface = "internal", onOpenPanel, onVitals, transcriptVariant = "cards", transcriptDensity = "comfortable", controlsSurface = "internal", onControls, focusComposerOnClick = false, unseen, className }) {
7996
8056
  const external = panelSurface === "external";
7997
8057
  const statusExternal = statusSurface === "external";
7998
8058
  const controlsExternal = controlsSurface === "external";
@@ -8068,7 +8128,8 @@ function SessionPanel({ client, sessionId, header, panelSurface = "internal", st
8068
8128
  const controls = useRef({
8069
8129
  setModel: (model) => setters.current.setModel(model),
8070
8130
  setPermissionMode: (mode) => setters.current.setPermissionMode(mode),
8071
- interrupt: () => setters.current.interrupt()
8131
+ interrupt: () => setters.current.interrupt(),
8132
+ focusComposer: () => composerRef.current?.focus()
8072
8133
  });
8073
8134
  useEffect(() => {
8074
8135
  const handler = onControlsRef.current;
@@ -8151,168 +8212,172 @@ function SessionPanel({ client, sessionId, header, panelSurface = "internal", st
8151
8212
  };
8152
8213
  return /* @__PURE__ */ jsx(TranscriptVariantProvider, {
8153
8214
  value: transcriptVariant,
8154
- children: /* @__PURE__ */ jsxs("div", {
8155
- "data-slot": "session-panel",
8156
- onClick: handleClick,
8157
- className: cn("flex h-full min-h-0 flex-col overflow-hidden bg-bg", className),
8158
- children: [
8159
- headerTakesActions ? header({ actions: menu }) : header,
8160
- statusExternal ? null : /* @__PURE__ */ jsx(StatusBar, {
8161
- state,
8162
- connection,
8163
- onOpenStatus: external && !onOpenPanel ? void 0 : () => openPanel("info"),
8164
- onOpenContext: external && !onOpenPanel ? void 0 : () => openPanel("context"),
8165
- onOpenUsage: external && !onOpenPanel ? void 0 : () => openPanel("usage"),
8166
- actions: headerTakesActions ? void 0 : menu
8167
- }),
8168
- protocolMismatch !== void 0 ? /* @__PURE__ */ jsxs(Notice, {
8169
- level: "warning",
8170
- children: [
8171
- "Server speaks protocol v",
8172
- protocolMismatch,
8173
- ", this build renders v",
8174
- PROTOCOL_VERSION,
8175
- ". Some events may not render."
8176
- ]
8177
- }) : null,
8178
- protocolError ? /* @__PURE__ */ jsx(Notice, {
8179
- level: "error",
8180
- onDismiss: () => setProtocolError(void 0),
8181
- children: protocolError
8182
- }) : null,
8183
- /* @__PURE__ */ jsx(Transcript, {
8184
- state,
8185
- fileUrl: sessionId ? (path) => client.sessionFileUrl(sessionId, path) : void 0,
8186
- attachmentUrl: sessionId ? (id) => client.attachmentUrl(sessionId, id) : void 0,
8187
- canBrowseFiles: hostFiles.available,
8188
- hostImage,
8189
- variant: transcriptVariant,
8190
- catchUp: catchUp && newCount > 0 ? {
8191
- from: catchUp.itemCount,
8192
- since: catchUp.since
8193
- } : void 0,
8194
- jumpToRecapRef: jumpToRecap
8195
- }),
8196
- catchUp && newCount > 0 ? /* @__PURE__ */ jsx("div", {
8197
- className: "px-3 pb-1",
8198
- children: /* @__PURE__ */ jsxs("div", {
8199
- "data-slot": "catch-up",
8200
- className: "mx-auto flex w-full max-w-[var(--wd-content-max-w,48rem)] items-center gap-2 text-label text-fg-3",
8215
+ children: /* @__PURE__ */ jsx(TranscriptDensityProvider, {
8216
+ value: transcriptDensity,
8217
+ children: /* @__PURE__ */ jsxs("div", {
8218
+ "data-slot": "session-panel",
8219
+ onClick: handleClick,
8220
+ className: cn("flex h-full min-h-0 flex-col overflow-hidden bg-bg", className),
8221
+ children: [
8222
+ headerTakesActions ? header({ actions: menu }) : header,
8223
+ statusExternal ? null : /* @__PURE__ */ jsx(StatusBar, {
8224
+ state,
8225
+ connection,
8226
+ onOpenStatus: external && !onOpenPanel ? void 0 : () => openPanel("info"),
8227
+ onOpenContext: external && !onOpenPanel ? void 0 : () => openPanel("context"),
8228
+ onOpenUsage: external && !onOpenPanel ? void 0 : () => openPanel("usage"),
8229
+ actions: headerTakesActions ? void 0 : menu
8230
+ }),
8231
+ protocolMismatch !== void 0 ? /* @__PURE__ */ jsxs(Notice, {
8232
+ level: "warning",
8201
8233
  children: [
8202
- /* @__PURE__ */ jsx("span", {
8203
- "aria-hidden": true,
8204
- className: "select-none text-accent",
8205
- children: "※"
8206
- }),
8207
- /* @__PURE__ */ jsxs("span", {
8208
- className: "min-w-0 flex-1 truncate",
8209
- children: [
8210
- newCount,
8211
- " new ",
8212
- newCount === 1 ? "row" : "rows",
8213
- catchUp.since !== void 0 ? ` since you were last here` : ""
8214
- ]
8215
- }),
8216
- /* @__PURE__ */ jsx("button", {
8217
- type: "button",
8218
- onClick: () => jumpToRecap.current?.(),
8219
- className: "shrink-0 underline-offset-2 hover:text-fg-1 hover:underline",
8220
- children: "jump"
8221
- }),
8222
- /* @__PURE__ */ jsx("button", {
8223
- type: "button",
8224
- onClick: () => setCaughtUp(true),
8225
- className: "shrink-0 underline-offset-2 hover:text-fg-1 hover:underline",
8226
- children: "dismiss"
8227
- })
8234
+ "Server speaks protocol v",
8235
+ protocolMismatch,
8236
+ ", this build renders v",
8237
+ PROTOCOL_VERSION,
8238
+ ". Some events may not render."
8228
8239
  ]
8229
- })
8230
- }) : null,
8231
- capabilities.interactiveApprovals && state.pendingApprovals.length > 0 ? /* @__PURE__ */ jsx("div", {
8232
- className: "px-3 pb-2",
8233
- children: /* @__PURE__ */ jsx("div", {
8234
- className: "mx-auto flex w-full max-w-[var(--wd-content-max-w,48rem)] flex-col gap-2",
8235
- children: state.pendingApprovals.map((request) => request.toolName === "AskUserQuestion" && parseUserQuestions(request.input).length > 0 ? /* @__PURE__ */ jsx(QuestionPrompt, {
8236
- request,
8237
- onAnswer: approve,
8238
- onDismiss: (id) => deny(id, "Question dismissed by user")
8239
- }, request.id) : /* @__PURE__ */ jsx(PermissionPrompt, {
8240
- request,
8241
- onApprove: approve,
8242
- onDeny: deny
8243
- }, request.id))
8244
- })
8245
- }) : null,
8246
- /* @__PURE__ */ jsx(Composer, {
8247
- ref: composerRef,
8248
- onSend: handleSend,
8249
- onInterrupt: interrupt,
8250
- busy,
8251
- disabled: ended || !sessionId,
8252
- commands: capabilities.slashCommands ? commands : void 0,
8253
- skills: capabilities.skillsList ? state.skills : void 0,
8254
- attachments,
8255
- onSearchFiles: hostFiles.available ? (query, options) => hostFiles.search(query, {
8256
- ...options,
8257
- limit: 8
8258
- }) : void 0,
8259
- layout: controlsExternal ? "inline" : "stacked",
8260
- toolbar: controlsExternal ? void 0 : /* @__PURE__ */ jsxs(Fragment$1, { children: [models.length ? /* @__PURE__ */ jsx(ModelSelect, {
8261
- models,
8262
- model: effectiveModel,
8263
- onModelChange: setModel,
8264
- disabled: ended
8265
- }) : null, state.permissionMode ? /* @__PURE__ */ jsx(PermissionModeSelect, {
8266
- mode: state.permissionMode,
8267
- onModeChange: setPermissionMode,
8268
- modes: capabilities.permissionModes,
8269
- canBypass: state.session?.canBypassPermissions,
8270
- disabled: ended
8271
- }) : null] })
8272
- }),
8273
- !external ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
8274
- /* @__PURE__ */ jsx(SessionInfoDialog, {
8240
+ }) : null,
8241
+ protocolError ? /* @__PURE__ */ jsx(Notice, {
8242
+ level: "error",
8243
+ onDismiss: () => setProtocolError(void 0),
8244
+ children: protocolError
8245
+ }) : null,
8246
+ /* @__PURE__ */ jsx(Transcript, {
8275
8247
  state,
8276
- client,
8277
- sessionId,
8278
- open: panel === "info",
8279
- onOpenChange: (next) => setPanel(next ? "info" : void 0)
8248
+ fileUrl: sessionId ? (path) => client.sessionFileUrl(sessionId, path) : void 0,
8249
+ attachmentUrl: sessionId ? (id) => client.attachmentUrl(sessionId, id) : void 0,
8250
+ canBrowseFiles: hostFiles.available,
8251
+ hostImage,
8252
+ variant: transcriptVariant,
8253
+ density: transcriptDensity,
8254
+ catchUp: catchUp && newCount > 0 ? {
8255
+ from: catchUp.itemCount,
8256
+ since: catchUp.since
8257
+ } : void 0,
8258
+ jumpToRecapRef: jumpToRecap
8280
8259
  }),
8281
- /* @__PURE__ */ jsx(ContextDialog, {
8282
- usage: state.contextUsage,
8283
- open: panel === "context",
8284
- onOpenChange: (next) => setPanel(next ? "context" : void 0)
8285
- }),
8286
- /* @__PURE__ */ jsx(UsageDialog, {
8287
- rateLimits: windows,
8288
- subscriptionType: state.subscriptionType,
8289
- engine: state.engine ?? "claude",
8290
- totalCostUsd: state.totalCostUsd,
8291
- updatedAt: state.rateLimitsUpdatedAt,
8292
- open: panel === "usage",
8293
- onOpenChange: (next) => setPanel(next ? "usage" : void 0)
8294
- }),
8295
- /* @__PURE__ */ jsx(McpDialog, {
8296
- client,
8297
- sessionId,
8298
- canManageServers: capabilities.mcpServerActions,
8299
- open: panel === "mcp",
8300
- onOpenChange: (next) => setPanel(next ? "mcp" : void 0)
8301
- }),
8302
- /* @__PURE__ */ jsx(SkillsDialog, {
8303
- skills: state.skills,
8304
- open: panel === "skills",
8305
- onOpenChange: (next) => setPanel(next ? "skills" : void 0),
8306
- onUse: (skill) => composerRef.current?.insertText(skillPrompt(skill))
8260
+ catchUp && newCount > 0 ? /* @__PURE__ */ jsx("div", {
8261
+ className: "px-3 pb-1",
8262
+ children: /* @__PURE__ */ jsxs("div", {
8263
+ "data-slot": "catch-up",
8264
+ className: "mx-auto flex w-full max-w-[var(--wd-content-max-w,48rem)] items-center gap-2 text-label text-fg-3",
8265
+ children: [
8266
+ /* @__PURE__ */ jsx("span", {
8267
+ "aria-hidden": true,
8268
+ className: "select-none text-accent",
8269
+ children: "※"
8270
+ }),
8271
+ /* @__PURE__ */ jsxs("span", {
8272
+ className: "min-w-0 flex-1 truncate",
8273
+ children: [
8274
+ newCount,
8275
+ " new ",
8276
+ newCount === 1 ? "row" : "rows",
8277
+ catchUp.since !== void 0 ? ` since you were last here` : ""
8278
+ ]
8279
+ }),
8280
+ /* @__PURE__ */ jsx("button", {
8281
+ type: "button",
8282
+ onClick: () => jumpToRecap.current?.(),
8283
+ className: "shrink-0 underline-offset-2 hover:text-fg-1 hover:underline",
8284
+ children: "jump"
8285
+ }),
8286
+ /* @__PURE__ */ jsx("button", {
8287
+ type: "button",
8288
+ onClick: () => setCaughtUp(true),
8289
+ className: "shrink-0 underline-offset-2 hover:text-fg-1 hover:underline",
8290
+ children: "dismiss"
8291
+ })
8292
+ ]
8293
+ })
8294
+ }) : null,
8295
+ capabilities.interactiveApprovals && state.pendingApprovals.length > 0 ? /* @__PURE__ */ jsx("div", {
8296
+ className: "px-3 pb-2",
8297
+ children: /* @__PURE__ */ jsx("div", {
8298
+ className: "mx-auto flex w-full max-w-[var(--wd-content-max-w,48rem)] flex-col gap-2",
8299
+ children: state.pendingApprovals.map((request) => request.toolName === "AskUserQuestion" && parseUserQuestions(request.input).length > 0 ? /* @__PURE__ */ jsx(QuestionPrompt, {
8300
+ request,
8301
+ onAnswer: approve,
8302
+ onDismiss: (id) => deny(id, "Question dismissed by user")
8303
+ }, request.id) : /* @__PURE__ */ jsx(PermissionPrompt, {
8304
+ request,
8305
+ onApprove: approve,
8306
+ onDeny: deny
8307
+ }, request.id))
8308
+ })
8309
+ }) : null,
8310
+ /* @__PURE__ */ jsx(Composer, {
8311
+ ref: composerRef,
8312
+ onSend: handleSend,
8313
+ onInterrupt: interrupt,
8314
+ busy,
8315
+ disabled: ended || !sessionId,
8316
+ commands: capabilities.slashCommands ? commands : void 0,
8317
+ skills: capabilities.skillsList ? state.skills : void 0,
8318
+ attachments,
8319
+ onSearchFiles: hostFiles.available ? (query, options) => hostFiles.search(query, {
8320
+ ...options,
8321
+ limit: 8
8322
+ }) : void 0,
8323
+ layout: controlsExternal ? "inline" : "stacked",
8324
+ toolbar: controlsExternal ? void 0 : /* @__PURE__ */ jsxs(Fragment$1, { children: [models.length ? /* @__PURE__ */ jsx(ModelSelect, {
8325
+ models,
8326
+ model: effectiveModel,
8327
+ onModelChange: setModel,
8328
+ disabled: ended
8329
+ }) : null, state.permissionMode ? /* @__PURE__ */ jsx(PermissionModeSelect, {
8330
+ mode: state.permissionMode,
8331
+ onModeChange: setPermissionMode,
8332
+ modes: capabilities.permissionModes,
8333
+ canBypass: state.session?.canBypassPermissions,
8334
+ disabled: ended
8335
+ }) : null] })
8307
8336
  }),
8308
- /* @__PURE__ */ jsx(HostFilesDialog, {
8309
- client,
8310
- cwd: state.cwd,
8311
- open: panel === "files",
8312
- onOpenChange: (next) => setPanel(next ? "files" : void 0)
8313
- })
8314
- ] }) : null
8315
- ]
8337
+ !external ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
8338
+ /* @__PURE__ */ jsx(SessionInfoDialog, {
8339
+ state,
8340
+ client,
8341
+ sessionId,
8342
+ open: panel === "info",
8343
+ onOpenChange: (next) => setPanel(next ? "info" : void 0)
8344
+ }),
8345
+ /* @__PURE__ */ jsx(ContextDialog, {
8346
+ usage: state.contextUsage,
8347
+ open: panel === "context",
8348
+ onOpenChange: (next) => setPanel(next ? "context" : void 0)
8349
+ }),
8350
+ /* @__PURE__ */ jsx(UsageDialog, {
8351
+ rateLimits: windows,
8352
+ subscriptionType: state.subscriptionType,
8353
+ engine: state.engine ?? "claude",
8354
+ totalCostUsd: state.totalCostUsd,
8355
+ updatedAt: state.rateLimitsUpdatedAt,
8356
+ open: panel === "usage",
8357
+ onOpenChange: (next) => setPanel(next ? "usage" : void 0)
8358
+ }),
8359
+ /* @__PURE__ */ jsx(McpDialog, {
8360
+ client,
8361
+ sessionId,
8362
+ canManageServers: capabilities.mcpServerActions,
8363
+ open: panel === "mcp",
8364
+ onOpenChange: (next) => setPanel(next ? "mcp" : void 0)
8365
+ }),
8366
+ /* @__PURE__ */ jsx(SkillsDialog, {
8367
+ skills: state.skills,
8368
+ open: panel === "skills",
8369
+ onOpenChange: (next) => setPanel(next ? "skills" : void 0),
8370
+ onUse: (skill) => composerRef.current?.insertText(skillPrompt(skill))
8371
+ }),
8372
+ /* @__PURE__ */ jsx(HostFilesDialog, {
8373
+ client,
8374
+ cwd: state.cwd,
8375
+ open: panel === "files",
8376
+ onOpenChange: (next) => setPanel(next ? "files" : void 0)
8377
+ })
8378
+ ] }) : null
8379
+ ]
8380
+ })
8316
8381
  })
8317
8382
  });
8318
8383
  }
@@ -8404,6 +8469,6 @@ function Notice({ level, onDismiss, children }) {
8404
8469
  });
8405
8470
  }
8406
8471
  //#endregion
8407
- export { TooltipProvider as $, permissionModeMeta as A, mentionTrigger as B, TranscriptVariantProvider as C, cn as Ct, PERMISSION_MODES as D, toolIcon as E, ContextDialog as F, ProgressRing as G, PromptArea as H, Composer as I, Spinner as J, Splitter as K, skillPrompt as L, SkillsDialog as M, McpDialog as N, PermissionModeSelect as O, HostFilesDialog as P, TooltipContent as Q, commandTrigger as R, Response as S, buttonVariants as St, isMutatingTool as T, plainTextToSegments as U, usePromptAreaState as V, segmentsToPlainText as W, copyText as X, CopyButton as Y, Tip as Z, SessionInfoDialog as _, SelectValue as _t, SessionEmptyState as a, DialogRow as at, parseUserQuestions as b, badgeVariants as bt, Message as c, MenuContent as ct, FileCard as d, MenuTrigger as dt, Dialog$1 as et, Conversation as f, Select$1 as ft, STATUS_META as g, SelectTrigger as gt, StatusBar as h, SelectItemText as ht, ToolCallCard as i, DialogHeader as it, ModelSelect as j, permissionModeChoices as k, MessageContent as l, MenuItem as lt, ConversationScrollButton as m, SelectItem as mt, UsageDialog as n, DialogClose as nt, Reasoning as o, DialogTrigger as ot, ConversationContent as p, SelectContent as pt, CodeBlock as q, Transcript as r, DialogContent as rt, PromptTokenText as s, Menu$1 as st, SessionPanel as t, DialogBody as tt, Loader as u, MenuSeparator as ut, QUESTION_BEHAVIORS as v, Input as vt, useTranscriptVariant as w, PermissionPrompt as x, Button as xt, QuestionPrompt as y, Badge as yt, hashtagTrigger as z };
8472
+ export { Tip as $, PermissionModeSelect as A, commandTrigger as B, TranscriptDensityProvider as C, Button as Ct, isMutatingTool as D, useTranscriptVariant as E, McpDialog as F, plainTextToSegments as G, mentionTrigger as H, HostFilesDialog as I, Splitter as J, segmentsToPlainText as K, ContextDialog as L, permissionModeMeta as M, ModelSelect as N, toolIcon as O, SkillsDialog as P, copyText as Q, Composer as R, Response as S, badgeVariants as St, useTranscriptDensity as T, cn as Tt, usePromptAreaState as U, hashtagTrigger as V, PromptArea as W, Spinner as X, CodeBlock as Y, CopyButton as Z, SessionInfoDialog as _, SelectItemText as _t, SessionEmptyState as a, DialogContent as at, parseUserQuestions as b, Input as bt, Message as c, DialogTrigger as ct, FileCard as d, MenuItem as dt, TooltipContent as et, Conversation as f, MenuSeparator as ft, STATUS_META as g, SelectItem as gt, StatusBar as h, SelectContent as ht, ToolCallCard as i, DialogClose as it, permissionModeChoices as j, PERMISSION_MODES as k, MessageContent as l, Menu$1 as lt, ConversationScrollButton as m, Select$1 as mt, UsageDialog as n, Dialog$1 as nt, Reasoning as o, DialogHeader as ot, ConversationContent as p, MenuTrigger as pt, ProgressRing as q, Transcript as r, DialogBody as rt, PromptTokenText as s, DialogRow as st, SessionPanel as t, TooltipProvider as tt, Loader as u, MenuContent as ut, QUESTION_BEHAVIORS as v, SelectTrigger as vt, TranscriptVariantProvider as w, buttonVariants as wt, PermissionPrompt as x, Badge as xt, QuestionPrompt as y, SelectValue as yt, skillPrompt as z };
8408
8473
 
8409
- //# sourceMappingURL=SessionPanel-_U8tjX29.mjs.map
8474
+ //# sourceMappingURL=SessionPanel-NQ8ksCfj.mjs.map