@norman-else/dsh-claude 0.1.35 → 0.1.37

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/lib/client.js CHANGED
@@ -30,6 +30,10 @@ window.__ModuleLoader__.load({
30
30
  function isClaudeRenderMode(value) {
31
31
  return value === "plugin" || value === "native";
32
32
  }
33
+ const DEFAULT_CLAUDE_PROSE_MODE = "plain";
34
+ function isClaudeProseMode(value) {
35
+ return value === "plain" || value === "enhanced";
36
+ }
33
37
  //#endregion
34
38
  //#region src/client/task-projection.ts
35
39
  /** Tasks UI is reserved for detached work and genuine Claude subagents. */
@@ -270,6 +274,62 @@ window.__ModuleLoader__.load({
270
274
  }
271
275
  return failed ? `Failed to ${failedAction}` : completed;
272
276
  }
277
+ /** The call id a task's own lifecycle pings were dispatched from. */
278
+ function taskParentToolUseId(activities, taskId) {
279
+ for (const activity of activities) {
280
+ if (activity.taskId !== taskId) continue;
281
+ const id = inputRecord(activity.detail)?.tool_use_id;
282
+ if (typeof id === "string" && id.length > 0) return id;
283
+ }
284
+ }
285
+ /**
286
+ * The tools one task ran, in the shape the transcript's tool cards take.
287
+ *
288
+ * The task's own activities are lifecycle pings whose detail is the raw
289
+ * protocol message -- useful to nobody reading a panel. The work is in the
290
+ * activities addressed to the call that dispatched the task: a subagent's
291
+ * nested calls carry it as `parentToolUseId`, and a backgrounded command IS
292
+ * that call. Both are folded here the way {@link transcriptItemsForStep} folds
293
+ * a step, so one card renderer serves the transcript and the task panel.
294
+ */
295
+ function taskTools(activities, taskId) {
296
+ const parent = taskParentToolUseId(activities, taskId);
297
+ if (parent === void 0) return [];
298
+ const tools = /* @__PURE__ */ new Map();
299
+ const ordered = [...activities].sort((left, right) => left.ordinal - right.ordinal);
300
+ for (const activity of ordered) {
301
+ const own = activity.parentToolUseId === parent;
302
+ const isRoot = activity.parentToolUseId === void 0 && activity.toolUseId === parent;
303
+ if (!own && !isRoot) continue;
304
+ const toolUseId = activity.toolUseId;
305
+ if (toolUseId === void 0) continue;
306
+ const previous = tools.get(toolUseId);
307
+ if (previous === void 0) {
308
+ if (activity.toolName === void 0) continue;
309
+ const input = inputRecord(activity.detail);
310
+ tools.set(toolUseId, {
311
+ toolUseId,
312
+ toolName: activity.toolName,
313
+ description: toolDescription(activity.toolName, input),
314
+ ...activity.summary === void 0 ? {} : { summary: activity.summary },
315
+ ...activity.detail === void 0 ? {} : { input: activity.detail },
316
+ ...activity.phase === void 0 ? {} : { phase: activity.phase },
317
+ ...activity.isError === void 0 ? {} : { isError: activity.isError },
318
+ subcalls: []
319
+ });
320
+ continue;
321
+ }
322
+ const failed = activity.isError === true || activity.phase === "failed";
323
+ tools.set(toolUseId, {
324
+ ...previous,
325
+ description: toolDescription(previous.toolName, inputRecord(previous.input), failed),
326
+ ...activity.detail === void 0 ? {} : { output: activity.detail },
327
+ ...activity.phase === void 0 ? {} : { phase: activity.phase },
328
+ ...activity.isError === void 0 ? {} : { isError: activity.isError }
329
+ });
330
+ }
331
+ return [...tools.values()];
332
+ }
273
333
  /** Whether the Host drew this step with DSH's own renderer.
274
334
  *
275
335
  * The stamp rides on the records themselves rather than on a Client-side copy
@@ -342,6 +402,15 @@ window.__ModuleLoader__.load({
342
402
  });
343
403
  continue;
344
404
  }
405
+ if (activity.kind === "usage" && activity.usage !== void 0) {
406
+ flushGroup();
407
+ items.push({
408
+ kind: "usage",
409
+ ordinal: activity.ordinal,
410
+ usage: activity.usage
411
+ });
412
+ continue;
413
+ }
345
414
  if (activity.kind === "tool-call" && activity.toolUseId !== void 0 && activity.toolName !== void 0) {
346
415
  group ??= {
347
416
  ordinal: activity.ordinal,
@@ -1106,6 +1175,14 @@ window.__ModuleLoader__.load({
1106
1175
  cursor: "pointer"
1107
1176
  };
1108
1177
  const taskActivitySection = { margin: "7px 0 0 27px" };
1178
+ /** The task's tool cards. No inset panel of their own: each card already
1179
+ * carries its own surface, and nesting two would read as a box in a box. */
1180
+ const taskToolList = {
1181
+ display: "flex",
1182
+ flexDirection: "column",
1183
+ gap: 2,
1184
+ marginTop: 7
1185
+ };
1109
1186
  const taskActivityList = {
1110
1187
  display: "flex",
1111
1188
  flexDirection: "column",
@@ -3352,626 +3429,265 @@ window.__ModuleLoader__.load({
3352
3429
  lineHeight: "20px"
3353
3430
  };
3354
3431
  //#endregion
3355
- //#region src/client/token-format.ts
3356
- function formatTokenCount(tokens) {
3357
- if (tokens >= 1e6) return `${Number((tokens / 1e6).toFixed(tokens >= 1e7 ? 0 : 1))}M`;
3358
- if (tokens >= 1e3) return `${Number((tokens / 1e3).toFixed(tokens >= 1e5 ? 0 : 1))}K`;
3359
- return String(tokens);
3360
- }
3361
- //#endregion
3362
- //#region src/client/ClaudeTasksPanel.tsx
3363
- const STATUS_LABEL = {
3364
- running: "tasksRunning",
3365
- completed: "tasksCompleted",
3366
- failed: "tasksFailed",
3367
- stopped: "tasksStopped",
3368
- killed: "tasksKilled"
3432
+ //#region src/client/markdown-theme.ts
3433
+ /** Claude Code's code presentation over the Host's Markdown renderer.
3434
+ *
3435
+ * This package renders no Markdown of its own prose, fenced blocks and the
3436
+ * copy affordance all come from the Host's `MarkdownText` primitive. What it
3437
+ * can own is what that primitive reads: the palette (custom properties) and
3438
+ * the block's chrome (CSS over the primitive's own markup).
3439
+ *
3440
+ * PARITY IS PARTIAL BY CONSTRUCTION. Both renderers highlight with shiki, but
3441
+ * Claude Code's desktop build loads a full TextMate theme (Pierre Dark /
3442
+ * Pierre Light Soft: 248 tokenColor rules over 424 scopes) and bakes the
3443
+ * resolved colour into every span, while the Host loads shiki's legacy
3444
+ * `css-variables` theme, which collapses every scope into the eleven buckets
3445
+ * below. Those eleven carry the colours that dominate a code block —
3446
+ * keywords, strings, comments, functions, numbers — and nothing here can
3447
+ * recover the rest: `constant.numeric` and `constant` are two different
3448
+ * colours in Pierre and one bucket here, and Pierre's string-coloured string
3449
+ * delimiters share this sheet's single punctuation bucket. Matching the rest
3450
+ * means this package running its own shiki, which is a different decision
3451
+ * with a different cost.
3452
+ *
3453
+ * Every rule fails open the way `host-chrome` does: the palette is
3454
+ * declarations on this package's own wrapper, and the chrome rules match the
3455
+ * primitive's CSS Module local names, so a Host that renames either simply
3456
+ * stops matching and its stock presentation comes back.
3457
+ */
3458
+ /** Wrapper class carrying the palette and scoping the chrome rules.
3459
+ * `display:contents` so the extra element generates no box — custom
3460
+ * properties inherit through it regardless. */
3461
+ const CLAUDE_MARKDOWN_SCOPE = "dsh-claude-markdown";
3462
+ /** Pierre mapped onto the Host's eleven buckets. Each entry names the theme
3463
+ * scope the value was taken from, because the mapping — not the colour — is
3464
+ * the part a reader has to check. */
3465
+ const PIERRE_DARK = [
3466
+ [
3467
+ "--shiki-background",
3468
+ "#1a1a19",
3469
+ "UI surface (--cds-surface-2)"
3470
+ ],
3471
+ [
3472
+ "--shiki-foreground",
3473
+ "#fafafa",
3474
+ "editor.foreground"
3475
+ ],
3476
+ [
3477
+ "--shiki-token-comment",
3478
+ "#737373",
3479
+ "comment"
3480
+ ],
3481
+ [
3482
+ "--shiki-token-keyword",
3483
+ "#ff678d",
3484
+ "keyword, storage.type"
3485
+ ],
3486
+ [
3487
+ "--shiki-token-string",
3488
+ "#5ecc71",
3489
+ "string"
3490
+ ],
3491
+ [
3492
+ "--shiki-token-string-expression",
3493
+ "#ffa359",
3494
+ "punctuation.section.embedded"
3495
+ ],
3496
+ [
3497
+ "--shiki-token-function",
3498
+ "#9d6afb",
3499
+ "entity.name.function"
3500
+ ],
3501
+ [
3502
+ "--shiki-token-constant",
3503
+ "#68cdf2",
3504
+ "constant.numeric, constant.language"
3505
+ ],
3506
+ [
3507
+ "--shiki-token-parameter",
3508
+ "#a3a3a3",
3509
+ "variable.parameter"
3510
+ ],
3511
+ [
3512
+ "--shiki-token-punctuation",
3513
+ "#636363",
3514
+ "punctuation"
3515
+ ],
3516
+ [
3517
+ "--shiki-token-link",
3518
+ "#ff678d",
3519
+ "markup.underline.link.markdown"
3520
+ ]
3521
+ ];
3522
+ const PIERRE_LIGHT = [
3523
+ [
3524
+ "--shiki-background",
3525
+ "#ffffff",
3526
+ "UI surface (--cds-surface-2)"
3527
+ ],
3528
+ [
3529
+ "--shiki-foreground",
3530
+ "#525252",
3531
+ "editor.foreground"
3532
+ ],
3533
+ [
3534
+ "--shiki-token-comment",
3535
+ "#8a8a8a",
3536
+ "comment"
3537
+ ],
3538
+ [
3539
+ "--shiki-token-keyword",
3540
+ "#ff678d",
3541
+ "keyword, storage.type"
3542
+ ],
3543
+ [
3544
+ "--shiki-token-string",
3545
+ "#0dbe4e",
3546
+ "string"
3547
+ ],
3548
+ [
3549
+ "--shiki-token-string-expression",
3550
+ "#fe8c2c",
3551
+ "punctuation.section.embedded"
3552
+ ],
3553
+ [
3554
+ "--shiki-token-function",
3555
+ "#9d6afb",
3556
+ "entity.name.function"
3557
+ ],
3558
+ [
3559
+ "--shiki-token-constant",
3560
+ "#08c0ef",
3561
+ "constant.numeric, constant.language"
3562
+ ],
3563
+ [
3564
+ "--shiki-token-parameter",
3565
+ "#737373",
3566
+ "variable.parameter"
3567
+ ],
3568
+ [
3569
+ "--shiki-token-punctuation",
3570
+ "#737373",
3571
+ "punctuation"
3572
+ ],
3573
+ [
3574
+ "--shiki-token-link",
3575
+ "#ff678d",
3576
+ "markup.underline.link.markdown"
3577
+ ]
3578
+ ];
3579
+ /** Claude's brand clay (`--cds-hsl-clay`), its inline-code TEXT colour; the
3580
+ * emphasized ramp is the light-theme variant. The chip's fill is deliberately
3581
+ * NOT tinted with it — see {@link INLINE_FILL_DARK}. */
3582
+ const CLAY = "#d97757";
3583
+ const CLAY_EMPHASIZED = "#c8603f";
3584
+ /** Prose colours for the body of a Claude answer.
3585
+ *
3586
+ * These are NOT Pierre: the palette above exists to match Claude's desktop
3587
+ * build inside code blocks, and Claude paints prose in plain body text. This
3588
+ * block is a deliberate departure — headings, emphasis and links get their
3589
+ * own hues so a long answer is scannable, the way a Markdown-highlighting
3590
+ * editor shows it. Values are theme-independent by design (the same six read
3591
+ * acceptably on both surfaces); split them if the light theme ever needs its
3592
+ * own ramp.
3593
+ *
3594
+ * The inline-code entry REPLACES {@link CLAY} rather than sitting beside it:
3595
+ * two colours on the same chip is not a choice a stylesheet can make. */
3596
+ const PROSE = {
3597
+ heading: "#7C9EFF",
3598
+ bold: "#FFB454",
3599
+ italic: "#5BD6C0",
3600
+ inlineCode: "#FF7A93",
3601
+ listMarker: "#F2C94C",
3602
+ quote: "#9AA3B2",
3603
+ link: "#4DA3FF",
3604
+ codeBackground: "#0F1218",
3605
+ codeBorder: "#2E3546"
3369
3606
  };
3370
- function visibleTaskGroups(tasks, dismissedSettledIds) {
3371
- const projected = tasks.filter(isProjectedTask);
3372
- return {
3373
- running: projected.filter((task) => task.status === "running"),
3374
- finished: projected.filter((task) => task.status !== "running" && !dismissedSettledIds.has(task.taskId))
3375
- };
3607
+ /** The inline-code chip fill: a neutral 4% wash (Claude's `--t1`), not a tint
3608
+ * of the text colour. A clay-tinted fill reads as a coloured box around every
3609
+ * identifier; the neutral one disappears into the surface and lets the text
3610
+ * carry the accent, which is what the chip is for. */
3611
+ const INLINE_FILL_DARK = "hsl(0 0% 100% / .04)";
3612
+ const INLINE_FILL_LIGHT = "hsl(0 0% 4.3% / .04)";
3613
+ /** Block corner radius (Claude's `--r6`), against the Host's own 12px. */
3614
+ const BLOCK_RADIUS = "8px";
3615
+ /** @param banner - equal to `surface` on purpose: the bar is floated out of
3616
+ * the way below, so this colour is only reached if those chrome selectors
3617
+ * miss, and a bar that matches the block is the neutral fallback. */
3618
+ function palette(entries, surface, banner, inlineFill) {
3619
+ return [
3620
+ ...entries.map(([name, value]) => `${name}:${value};`),
3621
+ `--dsw-alias-markdown-code-block:${surface};`,
3622
+ `--dsw-alias-markdown-code-block-banner:${banner};`,
3623
+ `--dsw-alias-markdown-inline-code:${inlineFill}`
3624
+ ].join("");
3376
3625
  }
3377
- function activitiesForTask(activities, taskId) {
3378
- return activities.filter((activity) => activity.taskId === taskId);
3626
+ const CLAUDE_MARKDOWN_THEME_CSS = [
3627
+ `.${CLAUDE_MARKDOWN_SCOPE}{display:contents;`,
3628
+ palette(PIERRE_LIGHT, "#ffffff", "#ffffff", INLINE_FILL_LIGHT),
3629
+ "}",
3630
+ `body[data-ds-dark-theme] .${CLAUDE_MARKDOWN_SCOPE}{`,
3631
+ palette(PIERRE_DARK, "#1a1a19", "#1a1a19", INLINE_FILL_DARK),
3632
+ "}",
3633
+ `.${CLAUDE_MARKDOWN_SCOPE} :not(pre)>code{color:${CLAY_EMPHASIZED};border-radius:4px;padding:1px 2px;font-size:1.05em!important}`,
3634
+ `body[data-ds-dark-theme] .${CLAUDE_MARKDOWN_SCOPE} :not(pre)>code{color:${CLAY}}`,
3635
+ `.${CLAUDE_MARKDOWN_SCOPE} [class*="bannerWrap"]{position:absolute;top:0;right:0;z-index:7;background:transparent;border-radius:0}`,
3636
+ `.${CLAUDE_MARKDOWN_SCOPE} [class*="banner"]:not([class*="bannerWrap"]){background:transparent;padding:6px 8px}`,
3637
+ `.${CLAUDE_MARKDOWN_SCOPE} [class*="infostring"]{display:none}`,
3638
+ `.${CLAUDE_MARKDOWN_SCOPE} pre{white-space:pre;word-break:normal;overflow-x:auto}`,
3639
+ `.${CLAUDE_MARKDOWN_SCOPE} [class*="block"]{--dsl-code-block-border-radius:${BLOCK_RADIUS}}`
3640
+ ].join("");
3641
+ /** The class the review-comment card puts on a rendered comment body. Declared
3642
+ * here rather than imported because it is a literal inside `styles.ts`'s CSS
3643
+ * string, not a constant; a rename there makes these rules miss and the
3644
+ * comment keeps its stock colours, which is the same fail-open the chrome
3645
+ * rules above rely on. */
3646
+ const COMMENT_BODY_SCOPE = "dshClaudeDiffCommentBody";
3647
+ /** Everything the `prose: 'enhanced'` setting adds, and nothing the base sheet
3648
+ * needs. Appended AFTER the base sheet so its rules win ties on source order —
3649
+ * see the note on the inline-code selector below. */
3650
+ const CLAUDE_MARKDOWN_ENHANCED_CSS = [
3651
+ `.${CLAUDE_MARKDOWN_SCOPE} :is(h1,h2,h3,h4,h5,h6){color:${PROSE.heading}}`,
3652
+ `.${CLAUDE_MARKDOWN_SCOPE} strong{color:${PROSE.bold}}`,
3653
+ `.${CLAUDE_MARKDOWN_SCOPE} em{color:${PROSE.italic}}`,
3654
+ `.${CLAUDE_MARKDOWN_SCOPE} li::marker{color:${PROSE.listMarker}}`,
3655
+ `.${CLAUDE_MARKDOWN_SCOPE} blockquote{border-left-color:${PROSE.quote};color:${PROSE.quote}}`,
3656
+ `.${CLAUDE_MARKDOWN_SCOPE} a{color:${PROSE.link}}`,
3657
+ `.${CLAUDE_MARKDOWN_SCOPE} :not(pre)>code,body[data-ds-dark-theme] .${CLAUDE_MARKDOWN_SCOPE} :not(pre)>code{color:${PROSE.inlineCode}}`,
3658
+ `.${CLAUDE_MARKDOWN_SCOPE} [class*="block"]{background:${PROSE.codeBackground};box-shadow:0 0 0 1px ${PROSE.codeBorder}}`,
3659
+ `.${CLAUDE_MARKDOWN_SCOPE} pre{background:${PROSE.codeBackground}}`,
3660
+ `.${COMMENT_BODY_SCOPE} :is(h1,h2,h3,h4,h5,h6){color:${PROSE.heading}}`,
3661
+ `.${COMMENT_BODY_SCOPE} strong{color:${PROSE.bold}}`,
3662
+ `.${COMMENT_BODY_SCOPE} em{color:${PROSE.italic}}`,
3663
+ `.${COMMENT_BODY_SCOPE} li::marker{color:${PROSE.listMarker}}`,
3664
+ `.${COMMENT_BODY_SCOPE} blockquote{border-left-color:${PROSE.quote};color:${PROSE.quote}}`,
3665
+ `.${COMMENT_BODY_SCOPE} a{color:${PROSE.link}}`,
3666
+ `.${COMMENT_BODY_SCOPE} :not(pre)>code{color:${PROSE.inlineCode}}`
3667
+ ].join("");
3668
+ /** Live reference to this package's sheet, and the mode it currently holds.
3669
+ * The mode is remembered so `ensureClaudeMarkdownTheme` — called on every
3670
+ * Markdown render — cannot undo a choice boot or the settings panel made. */
3671
+ let styleTag = null;
3672
+ let mode = DEFAULT_CLAUDE_PROSE_MODE;
3673
+ function write() {
3674
+ if (typeof document === "undefined") return;
3675
+ if (styleTag === null || styleTag.parentNode === null) {
3676
+ styleTag = document.createElement("style");
3677
+ styleTag.dataset.dshClaudeMarkdownTheme = "";
3678
+ document.head.appendChild(styleTag);
3679
+ }
3680
+ styleTag.textContent = mode === "enhanced" ? CLAUDE_MARKDOWN_THEME_CSS + CLAUDE_MARKDOWN_ENHANCED_CSS : CLAUDE_MARKDOWN_THEME_CSS;
3379
3681
  }
3380
- function tasksForTurn(tasks, turn) {
3381
- return tasks.filter((task) => task.originTurn === turn && isProjectedTask(task));
3682
+ /** Attach the sheet, keeping whatever mode is already set. Idempotent. */
3683
+ function ensureClaudeMarkdownTheme() {
3684
+ write();
3382
3685
  }
3383
- function summarizeTurnTasks(tasks) {
3384
- if (tasks.length === 0) return void 0;
3385
- const running = tasks.filter((task) => task.status === "running").length;
3386
- const failed = tasks.filter((task) => task.status === "failed" || task.status === "stopped" || task.status === "killed").length;
3387
- const completed = tasks.filter((task) => task.status === "completed").length;
3388
- return {
3389
- state: running > 0 ? "running" : failed > 0 ? "failed" : "completed",
3390
- count: tasks.length,
3391
- running,
3392
- failed,
3393
- completed
3394
- };
3395
- }
3396
- function statusGlyph(status) {
3397
- if (status === "running") return "●";
3398
- if (status === "completed") return "✓";
3399
- if (status === "stopped") return "–";
3400
- return "×";
3401
- }
3402
- function formatDuration(ms) {
3403
- if (ms < 1e3) return String(Math.max(1, Math.round(ms))) + "ms";
3404
- const seconds = Math.round(ms / 1e3);
3405
- if (seconds < 60) return String(seconds) + "s";
3406
- const minutes = Math.floor(seconds / 60);
3407
- return String(minutes) + "m " + String(seconds % 60) + "s";
3408
- }
3409
- function taskMeta(task, t) {
3410
- const parts = [];
3411
- if (task.subagentType !== void 0) parts.push(task.subagentType);
3412
- else if (task.taskType !== void 0) parts.push(task.taskType);
3413
- if (task.usage?.durationMs !== void 0) parts.push(formatDuration(task.usage.durationMs));
3414
- if (task.usage?.totalTokens !== void 0) parts.push(t("tokens", { count: formatTokenCount(task.usage.totalTokens) }));
3415
- if (task.usage?.toolUses !== void 0) parts.push(t("tasksToolUses", { count: task.usage.toolUses }));
3416
- if (task.lastToolName !== void 0) parts.push(t("tasksLastTool", { tool: task.lastToolName }));
3417
- return parts;
3418
- }
3419
- function TaskActivity({ activity, t }) {
3420
- return /* @__PURE__ */ jsxs("li", {
3421
- style: taskActivityItem,
3422
- children: [/* @__PURE__ */ jsx("span", {
3423
- style: taskActivityGlyph,
3424
- "aria-hidden": "true",
3425
- children: activity.isError === true ? "×" : "›"
3426
- }), /* @__PURE__ */ jsxs("div", {
3427
- style: taskActivityBody,
3428
- children: [
3429
- /* @__PURE__ */ jsx("p", {
3430
- style: taskActivityTitle,
3431
- children: activity.title ?? activity.kind
3432
- }),
3433
- activity.summary === void 0 ? null : /* @__PURE__ */ jsx("p", {
3434
- style: taskActivitySummary,
3435
- children: activity.summary
3436
- }),
3437
- activity.detail === void 0 ? null : /* @__PURE__ */ jsxs("details", {
3438
- style: taskActivityDetail,
3439
- children: [/* @__PURE__ */ jsx("summary", {
3440
- style: taskActivityDetailSummary,
3441
- children: t("detail")
3442
- }), /* @__PURE__ */ jsx("pre", {
3443
- style: detailCode,
3444
- children: activity.detail
3445
- })]
3446
- })
3447
- ]
3448
- })]
3449
- });
3450
- }
3451
- function TaskCard(props) {
3452
- const { task, activities, t } = props;
3453
- const [activityOpen, setActivityOpen] = useState(false);
3454
- const running = task.status === "running";
3455
- const failed = task.status === "failed" || task.status === "killed";
3456
- const meta = taskMeta(task, t);
3457
- return /* @__PURE__ */ jsxs("article", {
3458
- style: {
3459
- ...taskCard,
3460
- ...running ? taskCardRunning : {}
3461
- },
3462
- children: [
3463
- /* @__PURE__ */ jsxs("div", {
3464
- style: taskCardTop,
3465
- children: [/* @__PURE__ */ jsx("span", {
3466
- className: running ? "dsh-claude-act-running" : void 0,
3467
- style: {
3468
- ...taskCardGlyph,
3469
- ...running ? iconChipRunning : {},
3470
- ...failed ? iconChipError : {}
3471
- },
3472
- "aria-hidden": "true",
3473
- children: statusGlyph(task.status)
3474
- }), /* @__PURE__ */ jsxs("div", {
3475
- style: taskCardBody,
3476
- children: [/* @__PURE__ */ jsx("p", {
3477
- style: {
3478
- ...taskTitle,
3479
- ...failed ? { color: "var(--dsw-alias-state-error-primary)" } : {}
3480
- },
3481
- children: task.description
3482
- }), /* @__PURE__ */ jsxs("p", {
3483
- style: taskStatusLine,
3484
- children: [/* @__PURE__ */ jsx("span", { children: t(STATUS_LABEL[task.status] ?? "tasksRunning") }), task.backgrounded === true ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
3485
- "aria-hidden": "true",
3486
- children: " · "
3487
- }), /* @__PURE__ */ jsx("span", { children: t("tasksBackground") })] }) : null]
3488
- })]
3489
- })]
3490
- }),
3491
- meta.length === 0 ? null : /* @__PURE__ */ jsx("p", {
3492
- style: taskMeta$1,
3493
- children: meta.join(" · ")
3494
- }),
3495
- task.summary === void 0 || running ? null : /* @__PURE__ */ jsx("p", {
3496
- style: taskSummary,
3497
- children: task.summary
3498
- }),
3499
- activities.length === 0 ? null : /* @__PURE__ */ jsxs("div", {
3500
- style: taskActivitySection,
3501
- children: [/* @__PURE__ */ jsx("button", {
3502
- type: "button",
3503
- style: taskTextButton,
3504
- "aria-expanded": activityOpen,
3505
- onClick: () => setActivityOpen((value) => !value),
3506
- children: activityOpen ? t("tasksHideActivity") : t("tasksViewActivity")
3507
- }), activityOpen ? /* @__PURE__ */ jsx("ul", {
3508
- style: taskActivityList,
3509
- children: activities.map((activity) => /* @__PURE__ */ jsx(TaskActivity, {
3510
- activity,
3511
- t
3512
- }, `${activity.turn}:${activity.step}:${activity.ordinal}`))
3513
- }) : null]
3514
- })
3515
- ]
3516
- });
3517
- }
3518
- function GroupHeading(props) {
3519
- const { label, count, collapsed, onToggle, action } = props;
3520
- const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", { children: label }), /* @__PURE__ */ jsx("span", {
3521
- style: tasksGroupCount,
3522
- children: count
3523
- })] });
3524
- return /* @__PURE__ */ jsxs("div", {
3525
- style: tasksGroupHeading,
3526
- children: [onToggle === void 0 ? /* @__PURE__ */ jsx("div", {
3527
- style: tasksGroupTitle,
3528
- children: content
3529
- }) : /* @__PURE__ */ jsxs("button", {
3530
- type: "button",
3531
- style: tasksGroupToggle,
3532
- "aria-expanded": !collapsed,
3533
- onClick: onToggle,
3534
- children: [/* @__PURE__ */ jsx("span", {
3535
- style: {
3536
- ...chevron,
3537
- ...collapsed === true ? {} : chevronOpen
3538
- },
3539
- children: "›"
3540
- }), content]
3541
- }), action === void 0 ? null : /* @__PURE__ */ jsx("button", {
3542
- type: "button",
3543
- style: taskTextButton,
3544
- onClick: action.onClick,
3545
- children: action.label
3546
- })]
3547
- });
3548
- }
3549
- function ClaudeTasksPanel({ useClaudeProjection, t, closeDetails, turn }) {
3550
- const projection = useClaudeProjection((value) => value);
3551
- const tasks = useMemo(() => tasksForTurn(projection.tasks?.tasks ?? [], turn), [projection.tasks, turn]);
3552
- useEffect(() => {
3553
- if (!projection.owned || tasks.length === 0) closeDetails();
3554
- }, [
3555
- closeDetails,
3556
- projection.owned,
3557
- tasks.length
3558
- ]);
3559
- const [finishedCollapsed, setFinishedCollapsed] = useState(false);
3560
- const [dismissedSettledIds, setDismissedSettledIds] = useState(() => /* @__PURE__ */ new Set());
3561
- const groups = useMemo(() => visibleTaskGroups(tasks, dismissedSettledIds), [tasks, dismissedSettledIds]);
3562
- const taskActivities = useMemo(() => new Map(tasks.map((task) => [task.taskId, activitiesForTask(projection.activities, task.taskId)])), [projection.activities, tasks]);
3563
- const clearFinished = () => setDismissedSettledIds((previous) => /* @__PURE__ */ new Set([...previous, ...tasks.filter((task) => task.status !== "running").map((task) => task.taskId)]));
3564
- if (!projection.owned) return null;
3565
- return /* @__PURE__ */ jsxs("div", {
3566
- className: detailsCardClass,
3567
- style: tasksPanel,
3568
- children: [
3569
- /* @__PURE__ */ jsxs("style", {
3570
- "data-dsh-claude-panel-icon-styles": true,
3571
- children: [detailsCardCss, panelIconButtonCss]
3572
- }),
3573
- /* @__PURE__ */ jsxs("div", {
3574
- style: tasksHeader,
3575
- children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("span", {
3576
- style: tasksHeading,
3577
- children: t("tasksPanelTurn")
3578
- }), /* @__PURE__ */ jsx("span", {
3579
- style: tasksTurnMeta,
3580
- children: t("tasksTurnNumber", { turn })
3581
- })] }), /* @__PURE__ */ jsx("button", {
3582
- type: "button",
3583
- className: panelIconButtonClass,
3584
- "aria-label": t("tasksClose"),
3585
- onClick: closeDetails,
3586
- children: /* @__PURE__ */ jsx(IconCloseOutline16, {})
3587
- })]
3588
- }),
3589
- /* @__PURE__ */ jsxs("div", {
3590
- style: tasksBody,
3591
- children: [/* @__PURE__ */ jsxs("section", {
3592
- "aria-label": t("tasksRunning"),
3593
- children: [/* @__PURE__ */ jsx(GroupHeading, {
3594
- label: t("tasksRunning"),
3595
- count: groups.running.length
3596
- }), groups.running.length === 0 ? /* @__PURE__ */ jsx("p", {
3597
- style: tasksGroupEmpty,
3598
- children: t("tasksNoneRunning")
3599
- }) : /* @__PURE__ */ jsx("div", {
3600
- style: taskCardList,
3601
- children: groups.running.map((task) => /* @__PURE__ */ jsx(TaskCard, {
3602
- task,
3603
- activities: taskActivities.get(task.taskId) ?? [],
3604
- t
3605
- }, task.taskId))
3606
- })]
3607
- }), /* @__PURE__ */ jsxs("section", {
3608
- "aria-label": t("tasksSettled"),
3609
- style: tasksFinishedSection,
3610
- children: [/* @__PURE__ */ jsx(GroupHeading, {
3611
- label: t("tasksSettled"),
3612
- count: groups.finished.length,
3613
- collapsed: finishedCollapsed,
3614
- onToggle: () => setFinishedCollapsed((value) => !value),
3615
- ...groups.finished.length === 0 ? {} : { action: {
3616
- label: t("tasksClear"),
3617
- onClick: clearFinished
3618
- } }
3619
- }), finishedCollapsed || groups.finished.length === 0 ? null : /* @__PURE__ */ jsx("div", {
3620
- style: taskCardList,
3621
- children: groups.finished.map((task) => /* @__PURE__ */ jsx(TaskCard, {
3622
- task,
3623
- activities: taskActivities.get(task.taskId) ?? [],
3624
- t
3625
- }, task.taskId))
3626
- })]
3627
- })]
3628
- })
3629
- ]
3630
- });
3631
- }
3632
- //#endregion
3633
- //#region src/client/ClaudeActivityTail.tsx
3634
- const MAX_HOVER_TASKS = 6;
3635
- function taskGlyph(status) {
3636
- if (status === "failed") return {
3637
- glyph: "×",
3638
- style: tasksHoverGlyphError
3639
- };
3640
- if (status === "completed") return {
3641
- glyph: "✓",
3642
- style: tasksHoverGlyphDone
3643
- };
3644
- return {
3645
- glyph: "●",
3646
- style: tasksHoverGlyphRunning
3647
- };
3648
- }
3649
- function ClaudeTaskLauncher({ turn, tasks, t, openTasks }) {
3650
- const [hovered, setHovered] = useState(false);
3651
- const closeTimer = useRef();
3652
- const turnTasks = useMemo(() => tasksForTurn(tasks, turn), [tasks, turn]);
3653
- const summary = useMemo(() => summarizeTurnTasks(turnTasks), [turnTasks]);
3654
- const open = () => {
3655
- if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
3656
- closeTimer.current = void 0;
3657
- setHovered(true);
3658
- };
3659
- const scheduleClose = () => {
3660
- if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
3661
- closeTimer.current = setTimeout(() => {
3662
- closeTimer.current = void 0;
3663
- setHovered(false);
3664
- }, 350);
3665
- };
3666
- useEffect(() => () => {
3667
- if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
3668
- }, []);
3669
- if (summary === void 0) return null;
3670
- const label = summary.state === "running" ? t("tasksTurnRunning", { count: summary.running }) : summary.state === "failed" ? t("tasksTurnFailed", {
3671
- failed: summary.failed,
3672
- completed: summary.completed
3673
- }) : t("tasksTurnCompleted", { count: summary.completed });
3674
- const stateStyle = summary.state === "completed" ? tasksBadgeDone : {};
3675
- const dotStyle = summary.state === "failed" ? tasksBadgeDotError : summary.state === "completed" ? tasksBadgeDotDone : {};
3676
- return /* @__PURE__ */ jsx("div", {
3677
- "data-claude-task-launcher": turn,
3678
- style: tasksBadgeWrap,
3679
- children: /* @__PURE__ */ jsxs("span", {
3680
- style: tasksBadgeSeat,
3681
- onMouseEnter: open,
3682
- onMouseLeave: scheduleClose,
3683
- onFocus: open,
3684
- onBlur: (event) => {
3685
- if (!event.currentTarget.contains(event.relatedTarget)) scheduleClose();
3686
- },
3687
- children: [hovered ? /* @__PURE__ */ jsxs("span", {
3688
- role: "tooltip",
3689
- style: tasksHoverCard,
3690
- onMouseEnter: open,
3691
- onMouseLeave: scheduleClose,
3692
- children: [
3693
- /* @__PURE__ */ jsx("span", {
3694
- style: tasksHoverHeader,
3695
- children: label
3696
- }),
3697
- turnTasks.slice(0, MAX_HOVER_TASKS).map((task) => {
3698
- const { glyph, style } = taskGlyph(task.status);
3699
- return /* @__PURE__ */ jsxs("span", {
3700
- style: tasksHoverRow,
3701
- children: [
3702
- /* @__PURE__ */ jsx("span", {
3703
- className: task.status === "running" ? "dsh-claude-act-running" : void 0,
3704
- style: {
3705
- ...tasksHoverGlyph,
3706
- ...style
3707
- },
3708
- "aria-hidden": "true",
3709
- children: glyph
3710
- }),
3711
- /* @__PURE__ */ jsx("span", {
3712
- style: tasksHoverDesc,
3713
- title: task.description,
3714
- children: task.description
3715
- }),
3716
- task.subagentType === void 0 ? null : /* @__PURE__ */ jsx("span", {
3717
- style: tasksHoverType,
3718
- children: task.subagentType
3719
- })
3720
- ]
3721
- }, task.taskId);
3722
- }),
3723
- turnTasks.length > MAX_HOVER_TASKS ? /* @__PURE__ */ jsxs("span", {
3724
- style: tasksHoverMore,
3725
- children: ["+", turnTasks.length - MAX_HOVER_TASKS]
3726
- }) : null,
3727
- /* @__PURE__ */ jsx("span", {
3728
- style: tasksHoverHint,
3729
- children: t("tasksOpen")
3730
- })
3731
- ]
3732
- }) : null, /* @__PURE__ */ jsxs("button", {
3733
- type: "button",
3734
- className: "dsh-claude-task-launcher",
3735
- style: {
3736
- ...tasksTurnBadge,
3737
- ...stateStyle,
3738
- ...hovered ? tasksBadgeHovered : {}
3739
- },
3740
- "aria-label": `${label} — ${t("tasksOpen")}`,
3741
- onClick: () => openTasks(turn),
3742
- children: [/* @__PURE__ */ jsx("span", {
3743
- className: summary.state === "running" ? "dsh-claude-act-running" : void 0,
3744
- style: {
3745
- ...tasksBadgeDot,
3746
- ...dotStyle
3747
- },
3748
- "aria-hidden": "true"
3749
- }), /* @__PURE__ */ jsx("span", {
3750
- style: tasksBadgeLabel,
3751
- children: label
3752
- })]
3753
- })]
3754
- })
3755
- });
3756
- }
3757
- function ClaudeActivityTail({ matched, useClaudeProjection, t, openTasks }) {
3758
- const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? []);
3759
- return /* @__PURE__ */ jsx(ClaudeTaskLauncher, {
3760
- turn: matched.turn,
3761
- tasks,
3762
- t,
3763
- openTasks
3764
- });
3765
- }
3766
- //#endregion
3767
- //#region src/client/ClaudeActiveTasksNode.tsx
3768
- const EMPTY_TASKS$1 = [];
3769
- /** Render the task launcher while the owning DSH turn is still open. */
3770
- function ClaudeActiveTasksNode({ node, useClaudeProjection, t, openTasks }) {
3771
- const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? EMPTY_TASKS$1);
3772
- return /* @__PURE__ */ jsx(ClaudeTaskLauncher, {
3773
- turn: node.data.turn,
3774
- tasks,
3775
- t,
3776
- openTasks
3777
- });
3778
- }
3779
- //#endregion
3780
- //#region src/client/markdown-theme.ts
3781
- /** Claude Code's code presentation over the Host's Markdown renderer.
3782
- *
3783
- * This package renders no Markdown of its own — prose, fenced blocks and the
3784
- * copy affordance all come from the Host's `MarkdownText` primitive. What it
3785
- * can own is what that primitive reads: the palette (custom properties) and
3786
- * the block's chrome (CSS over the primitive's own markup).
3787
- *
3788
- * PARITY IS PARTIAL BY CONSTRUCTION. Both renderers highlight with shiki, but
3789
- * Claude Code's desktop build loads a full TextMate theme (Pierre Dark /
3790
- * Pierre Light Soft: 248 tokenColor rules over 424 scopes) and bakes the
3791
- * resolved colour into every span, while the Host loads shiki's legacy
3792
- * `css-variables` theme, which collapses every scope into the eleven buckets
3793
- * below. Those eleven carry the colours that dominate a code block —
3794
- * keywords, strings, comments, functions, numbers — and nothing here can
3795
- * recover the rest: `constant.numeric` and `constant` are two different
3796
- * colours in Pierre and one bucket here, and Pierre's string-coloured string
3797
- * delimiters share this sheet's single punctuation bucket. Matching the rest
3798
- * means this package running its own shiki, which is a different decision
3799
- * with a different cost.
3800
- *
3801
- * Every rule fails open the way `host-chrome` does: the palette is
3802
- * declarations on this package's own wrapper, and the chrome rules match the
3803
- * primitive's CSS Module local names, so a Host that renames either simply
3804
- * stops matching and its stock presentation comes back.
3805
- */
3806
- /** Wrapper class carrying the palette and scoping the chrome rules.
3807
- * `display:contents` so the extra element generates no box — custom
3808
- * properties inherit through it regardless. */
3809
- const CLAUDE_MARKDOWN_SCOPE = "dsh-claude-markdown";
3810
- /** Pierre mapped onto the Host's eleven buckets. Each entry names the theme
3811
- * scope the value was taken from, because the mapping — not the colour — is
3812
- * the part a reader has to check. */
3813
- const PIERRE_DARK = [
3814
- [
3815
- "--shiki-background",
3816
- "#1a1a19",
3817
- "UI surface (--cds-surface-2)"
3818
- ],
3819
- [
3820
- "--shiki-foreground",
3821
- "#fafafa",
3822
- "editor.foreground"
3823
- ],
3824
- [
3825
- "--shiki-token-comment",
3826
- "#737373",
3827
- "comment"
3828
- ],
3829
- [
3830
- "--shiki-token-keyword",
3831
- "#ff678d",
3832
- "keyword, storage.type"
3833
- ],
3834
- [
3835
- "--shiki-token-string",
3836
- "#5ecc71",
3837
- "string"
3838
- ],
3839
- [
3840
- "--shiki-token-string-expression",
3841
- "#ffa359",
3842
- "punctuation.section.embedded"
3843
- ],
3844
- [
3845
- "--shiki-token-function",
3846
- "#9d6afb",
3847
- "entity.name.function"
3848
- ],
3849
- [
3850
- "--shiki-token-constant",
3851
- "#68cdf2",
3852
- "constant.numeric, constant.language"
3853
- ],
3854
- [
3855
- "--shiki-token-parameter",
3856
- "#a3a3a3",
3857
- "variable.parameter"
3858
- ],
3859
- [
3860
- "--shiki-token-punctuation",
3861
- "#636363",
3862
- "punctuation"
3863
- ],
3864
- [
3865
- "--shiki-token-link",
3866
- "#ff678d",
3867
- "markup.underline.link.markdown"
3868
- ]
3869
- ];
3870
- const PIERRE_LIGHT = [
3871
- [
3872
- "--shiki-background",
3873
- "#ffffff",
3874
- "UI surface (--cds-surface-2)"
3875
- ],
3876
- [
3877
- "--shiki-foreground",
3878
- "#525252",
3879
- "editor.foreground"
3880
- ],
3881
- [
3882
- "--shiki-token-comment",
3883
- "#8a8a8a",
3884
- "comment"
3885
- ],
3886
- [
3887
- "--shiki-token-keyword",
3888
- "#ff678d",
3889
- "keyword, storage.type"
3890
- ],
3891
- [
3892
- "--shiki-token-string",
3893
- "#0dbe4e",
3894
- "string"
3895
- ],
3896
- [
3897
- "--shiki-token-string-expression",
3898
- "#fe8c2c",
3899
- "punctuation.section.embedded"
3900
- ],
3901
- [
3902
- "--shiki-token-function",
3903
- "#9d6afb",
3904
- "entity.name.function"
3905
- ],
3906
- [
3907
- "--shiki-token-constant",
3908
- "#08c0ef",
3909
- "constant.numeric, constant.language"
3910
- ],
3911
- [
3912
- "--shiki-token-parameter",
3913
- "#737373",
3914
- "variable.parameter"
3915
- ],
3916
- [
3917
- "--shiki-token-punctuation",
3918
- "#737373",
3919
- "punctuation"
3920
- ],
3921
- [
3922
- "--shiki-token-link",
3923
- "#ff678d",
3924
- "markup.underline.link.markdown"
3925
- ]
3926
- ];
3927
- /** Claude's brand clay (`--cds-hsl-clay`), its inline-code TEXT colour; the
3928
- * emphasized ramp is the light-theme variant. The chip's fill is deliberately
3929
- * NOT tinted with it — see {@link INLINE_FILL_DARK}. */
3930
- const CLAY = "#d97757";
3931
- const CLAY_EMPHASIZED = "#c8603f";
3932
- /** The inline-code chip fill: a neutral 4% wash (Claude's `--t1`), not a tint
3933
- * of the text colour. A clay-tinted fill reads as a coloured box around every
3934
- * identifier; the neutral one disappears into the surface and lets the text
3935
- * carry the accent, which is what the chip is for. */
3936
- const INLINE_FILL_DARK = "hsl(0 0% 100% / .04)";
3937
- const INLINE_FILL_LIGHT = "hsl(0 0% 4.3% / .04)";
3938
- /** Block corner radius (Claude's `--r6`), against the Host's own 12px. */
3939
- const BLOCK_RADIUS = "8px";
3940
- /** @param banner - equal to `surface` on purpose: the bar is floated out of
3941
- * the way below, so this colour is only reached if those chrome selectors
3942
- * miss, and a bar that matches the block is the neutral fallback. */
3943
- function palette(entries, surface, banner, inlineFill) {
3944
- return [
3945
- ...entries.map(([name, value]) => `${name}:${value};`),
3946
- `--dsw-alias-markdown-code-block:${surface};`,
3947
- `--dsw-alias-markdown-code-block-banner:${banner};`,
3948
- `--dsw-alias-markdown-inline-code:${inlineFill}`
3949
- ].join("");
3950
- }
3951
- const CLAUDE_MARKDOWN_THEME_CSS = [
3952
- `.${CLAUDE_MARKDOWN_SCOPE}{display:contents;`,
3953
- palette(PIERRE_LIGHT, "#ffffff", "#ffffff", INLINE_FILL_LIGHT),
3954
- "}",
3955
- `body[data-ds-dark-theme] .${CLAUDE_MARKDOWN_SCOPE}{`,
3956
- palette(PIERRE_DARK, "#1a1a19", "#1a1a19", INLINE_FILL_DARK),
3957
- "}",
3958
- `.${CLAUDE_MARKDOWN_SCOPE} :not(pre)>code{color:${CLAY_EMPHASIZED};border-radius:4px;padding:1px 2px}`,
3959
- `body[data-ds-dark-theme] .${CLAUDE_MARKDOWN_SCOPE} :not(pre)>code{color:${CLAY}}`,
3960
- `.${CLAUDE_MARKDOWN_SCOPE} [class*="bannerWrap"]{position:absolute;top:0;right:0;z-index:7;background:transparent;border-radius:0}`,
3961
- `.${CLAUDE_MARKDOWN_SCOPE} [class*="banner"]:not([class*="bannerWrap"]){background:transparent;padding:6px 8px}`,
3962
- `.${CLAUDE_MARKDOWN_SCOPE} [class*="infostring"]{display:none}`,
3963
- `.${CLAUDE_MARKDOWN_SCOPE} pre{white-space:pre;word-break:normal;overflow-x:auto}`,
3964
- `.${CLAUDE_MARKDOWN_SCOPE} [class*="block"]{--dsl-code-block-border-radius:${BLOCK_RADIUS}}`
3965
- ].join("");
3966
- let injected = false;
3967
- /** Attach the sheet once per page. */
3968
- function ensureClaudeMarkdownTheme() {
3969
- if (injected || typeof document === "undefined") return;
3970
- injected = true;
3971
- const element = document.createElement("style");
3972
- element.dataset.dshClaudeMarkdownTheme = "";
3973
- element.textContent = CLAUDE_MARKDOWN_THEME_CSS;
3974
- document.head.appendChild(element);
3686
+ /** Switch the prose palette. Rewriting one global sheet repaints every mounted
3687
+ * Markdown block at once, so no render state has to carry the setting. */
3688
+ function applyClaudeMarkdownTheme(next) {
3689
+ mode = next;
3690
+ write();
3975
3691
  }
3976
3692
  //#endregion
3977
3693
  //#region src/client/markdown-labels.tsx
@@ -4848,13 +4564,21 @@ window.__ModuleLoader__.load({
4848
4564
  }
4849
4565
  };
4850
4566
  //#endregion
4567
+ //#region src/client/token-format.ts
4568
+ function formatTokenCount(tokens) {
4569
+ if (tokens >= 1e6) return `${Number((tokens / 1e6).toFixed(tokens >= 1e7 ? 0 : 1))}M`;
4570
+ if (tokens >= 1e3) return `${Number((tokens / 1e3).toFixed(tokens >= 1e5 ? 0 : 1))}K`;
4571
+ return String(tokens);
4572
+ }
4573
+ //#endregion
4851
4574
  //#region src/client/ClaudeActivityNode.tsx
4852
- const EMPTY_TASKS = [];
4575
+ const EMPTY_TASKS$1 = [];
4853
4576
  const ACTIVITY_CSS = [
4854
4577
  ".dsh-claude-flow{display:flex;flex-direction:column;gap:10px}",
4855
4578
  ".dsh-claude-transcript-text{color:var(--dsw-alias-label-primary);font-size:15px;line-height:24px;overflow-wrap:anywhere}",
4856
4579
  ".dsh-claude-tool-group-native{overflow:visible}",
4857
4580
  ".dsh-claude-tool-group-native>.dsh-claude-flow-row{padding:0}",
4581
+ ".dsh-claude-flow-row .dsh-claude-tool-stats{margin-left:8px}",
4858
4582
  ".dsh-claude-tool-list{max-height:min(420px,calc(100vh - 320px));overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;margin-top:4px;border:1px solid var(--dsw-alias-border-l1, color-mix(in srgb, currentColor 12%, transparent));border-radius:10px;scrollbar-gutter:stable}",
4859
4583
  ".dsh-claude-tool-item{border-top:1px solid var(--dsw-alias-border-l1, color-mix(in srgb, currentColor 12%, transparent))}",
4860
4584
  ".dsh-claude-tool-item:first-child{border-top:0}",
@@ -4882,6 +4606,13 @@ window.__ModuleLoader__.load({
4882
4606
  ".dsh-claude-diff-delete{color:var(--dsw-alias-state-error-primary)}",
4883
4607
  ".dsh-claude-tool-name{font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary)}",
4884
4608
  ".dsh-claude-tool-summary{margin-left:8px;color:var(--dsw-alias-label-tertiary)}",
4609
+ ".dsh-claude-turn-usage{display:flex;flex-wrap:wrap;align-items:center;gap:5px;margin-top:10px;",
4610
+ "color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}",
4611
+ ".dsh-claude-turn-usage-label{color:var(--dsw-alias-label-caption)}",
4612
+ ".dsh-claude-tool-terminal{display:flex;gap:8px;margin:6px 0 0;padding:8px 10px;max-height:220px;overflow:auto;",
4613
+ "border-radius:8px;background:var(--dsw-alias-markdown-code-block);font:var(--dsw-font-markdown-code-block-small)}",
4614
+ ".dsh-claude-tool-prompt{flex:none;user-select:none;color:var(--dsw-alias-label-caption)}",
4615
+ ".dsh-claude-tool-command{min-width:0;margin:0;color:var(--dsw-alias-label-primary);white-space:pre-wrap;overflow-wrap:anywhere}",
4885
4616
  ".dsh-claude-tool-detail{margin:6px 0 0;padding:8px 10px;max-height:220px;overflow:auto;border-radius:8px;background:var(--dsw-alias-markdown-code-block);font:var(--dsw-font-markdown-code-block-small);white-space:pre-wrap;overflow-wrap:anywhere}",
4886
4617
  ".dsh-claude-flow-row{position:relative;overflow:hidden}",
4887
4618
  ".dsh-claude-flow-leading{flex-shrink:0}",
@@ -5057,6 +4788,21 @@ window.__ModuleLoader__.load({
5057
4788
  }, index))
5058
4789
  });
5059
4790
  }
4791
+ /** The command as a shell prompt rather than a labelled field: it was typed
4792
+ * at one, and the prompt is what tells a reader that at a glance. */
4793
+ function Terminal({ command }) {
4794
+ return /* @__PURE__ */ jsxs("div", {
4795
+ className: "dsh-claude-tool-terminal",
4796
+ children: [/* @__PURE__ */ jsx("span", {
4797
+ className: "dsh-claude-tool-prompt",
4798
+ "aria-hidden": "true",
4799
+ children: "$"
4800
+ }), /* @__PURE__ */ jsx("pre", {
4801
+ className: "dsh-claude-tool-command",
4802
+ children: command
4803
+ })]
4804
+ });
4805
+ }
5060
4806
  function TextDetail({ title, value }) {
5061
4807
  if (value === void 0 || value.length === 0) return null;
5062
4808
  return /* @__PURE__ */ jsx(Section, {
@@ -5071,13 +4817,36 @@ window.__ModuleLoader__.load({
5071
4817
  if (!Array.isArray(value)) return [];
5072
4818
  return value.filter((item) => typeof item === "string");
5073
4819
  }
4820
+ /** The diff card's chrome, which primitives 0.1.2 moved onto the caller.
4821
+ *
4822
+ * The card reads `labels.copy` while it builds, so omitting them throws
4823
+ * mid-render -- and React answers by tearing down the whole conversation, not
4824
+ * the one tool row.
4825
+ *
4826
+ * The aria strings deliberately repeat the visible ones: both already say
4827
+ * exactly what the control does. */
4828
+ function diffBlockLabels(t) {
4829
+ const expand = (hidden) => t("diffCardExpand", { count: hidden });
4830
+ return {
4831
+ copy: t("markdownCopy"),
4832
+ copied: t("markdownCopied"),
4833
+ collapse: t("diffCardCollapse"),
4834
+ collapseAria: t("diffCardCollapse"),
4835
+ expand,
4836
+ expandAria: expand,
4837
+ files: (count) => t("diffCardFiles", { count })
4838
+ };
4839
+ }
5074
4840
  function ToolPresentation({ tool, t }) {
5075
4841
  const inputValue = parsedValue(tool.input);
5076
4842
  const outputValue = parsedValue(tool.output);
5077
4843
  const input = record$6(inputValue);
5078
4844
  const output = record$6(outputValue);
5079
4845
  const outputTitle = tool.isError === true ? t("toolError") : t("toolOutput");
5080
- if (tool.diffs !== void 0) return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(DiffBlock, { diffs: [...tool.diffs] }), /* @__PURE__ */ jsx(TextDetail, {
4846
+ if (tool.diffs !== void 0) return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(DiffBlock, {
4847
+ diffs: [...tool.diffs],
4848
+ labels: diffBlockLabels(t)
4849
+ }), /* @__PURE__ */ jsx(TextDetail, {
5081
4850
  title: outputTitle,
5082
4851
  value: typeof outputValue === "string" ? outputValue : void 0
5083
4852
  })] });
@@ -5101,180 +4870,660 @@ window.__ModuleLoader__.load({
5101
4870
  omit: ["file_path"]
5102
4871
  })
5103
4872
  }),
5104
- content === void 0 ? null : /* @__PURE__ */ jsx(Section, {
5105
- title: outputTitle,
5106
- children: /* @__PURE__ */ jsx(Source, {
5107
- content,
5108
- start: offset
5109
- })
5110
- })
5111
- ] });
5112
- }
5113
- if (tool.toolName === "Grep" || tool.toolName === "Glob") {
5114
- const filenames = filenameList(output?.filenames);
5115
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [input === void 0 ? /* @__PURE__ */ jsx(TextDetail, {
5116
- title: t("toolInput"),
5117
- value: typeof inputValue === "string" ? inputValue : void 0
5118
- }) : /* @__PURE__ */ jsx(Section, {
5119
- title: t("toolInput"),
5120
- children: /* @__PURE__ */ jsx(Fields, { value: input })
5121
- }), filenames.length === 0 ? /* @__PURE__ */ jsx(TextDetail, {
5122
- title: outputTitle,
5123
- value: typeof outputValue === "string" ? outputValue : output === void 0 ? void 0 : displayValue(output)
5124
- }) : /* @__PURE__ */ jsx(Section, {
5125
- title: outputTitle,
5126
- children: /* @__PURE__ */ jsx(Paths, { paths: filenames })
5127
- })] });
5128
- }
5129
- if (tool.toolName === "Bash" || tool.toolName === "PowerShell") {
5130
- const command = text$1(input?.command);
5131
- const terminal = [text$1(output?.stdout), text$1(output?.stderr)].filter((value) => value !== void 0).join("\n");
5132
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
5133
- /* @__PURE__ */ jsx(TextDetail, {
5134
- title: "Command",
5135
- value: command ?? (typeof inputValue === "string" ? inputValue : void 0)
4873
+ content === void 0 ? null : /* @__PURE__ */ jsx(Section, {
4874
+ title: outputTitle,
4875
+ children: /* @__PURE__ */ jsx(Source, {
4876
+ content,
4877
+ start: offset
4878
+ })
4879
+ })
4880
+ ] });
4881
+ }
4882
+ if (tool.toolName === "Grep" || tool.toolName === "Glob") {
4883
+ const filenames = filenameList(output?.filenames);
4884
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [input === void 0 ? /* @__PURE__ */ jsx(TextDetail, {
4885
+ title: t("toolInput"),
4886
+ value: typeof inputValue === "string" ? inputValue : void 0
4887
+ }) : /* @__PURE__ */ jsx(Section, {
4888
+ title: t("toolInput"),
4889
+ children: /* @__PURE__ */ jsx(Fields, { value: input })
4890
+ }), filenames.length === 0 ? /* @__PURE__ */ jsx(TextDetail, {
4891
+ title: outputTitle,
4892
+ value: typeof outputValue === "string" ? outputValue : output === void 0 ? void 0 : displayValue(output)
4893
+ }) : /* @__PURE__ */ jsx(Section, {
4894
+ title: outputTitle,
4895
+ children: /* @__PURE__ */ jsx(Paths, { paths: filenames })
4896
+ })] });
4897
+ }
4898
+ if (tool.toolName === "Bash" || tool.toolName === "PowerShell") {
4899
+ const command = text$1(input?.command);
4900
+ const terminal = [text$1(output?.stdout), text$1(output?.stderr)].filter((value) => value !== void 0).join("\n");
4901
+ const typed = command ?? (typeof inputValue === "string" ? inputValue : void 0);
4902
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
4903
+ typed === void 0 ? null : /* @__PURE__ */ jsx(Terminal, { command: typed }),
4904
+ input === void 0 ? null : /* @__PURE__ */ jsx(Section, {
4905
+ title: t("toolInput"),
4906
+ children: /* @__PURE__ */ jsx(Fields, {
4907
+ value: input,
4908
+ omit: ["command", "description"]
4909
+ })
4910
+ }),
4911
+ /* @__PURE__ */ jsx(TextDetail, {
4912
+ title: outputTitle,
4913
+ value: terminal || (typeof outputValue === "string" ? outputValue : void 0)
4914
+ })
4915
+ ] });
4916
+ }
4917
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [input === void 0 ? /* @__PURE__ */ jsx(TextDetail, {
4918
+ title: t("toolInput"),
4919
+ value: typeof inputValue === "string" ? inputValue : void 0
4920
+ }) : /* @__PURE__ */ jsx(Section, {
4921
+ title: t("toolInput"),
4922
+ children: /* @__PURE__ */ jsx(Fields, { value: input })
4923
+ }), output === void 0 ? /* @__PURE__ */ jsx(TextDetail, {
4924
+ title: outputTitle,
4925
+ value: typeof outputValue === "string" ? outputValue : void 0
4926
+ }) : /* @__PURE__ */ jsx(Section, {
4927
+ title: outputTitle,
4928
+ children: /* @__PURE__ */ jsx(Fields, { value: output })
4929
+ })] });
4930
+ }
4931
+ function ClaudeTranscriptToolItem({ tool, t }) {
4932
+ return /* @__PURE__ */ jsxs("details", {
4933
+ className: "dsh-claude-tool-item",
4934
+ children: [/* @__PURE__ */ jsxs("summary", {
4935
+ className: "dsh-claude-tool-summary-row",
4936
+ children: [
4937
+ /* @__PURE__ */ jsx("span", {
4938
+ className: "dsh-claude-tool-label",
4939
+ children: tool.toolName
4940
+ }),
4941
+ /* @__PURE__ */ jsx("span", {
4942
+ className: "dsh-claude-tool-description",
4943
+ children: tool.description
4944
+ }),
4945
+ tool.additions === void 0 && tool.deletions === void 0 ? null : /* @__PURE__ */ jsxs("span", {
4946
+ className: "dsh-claude-tool-stats",
4947
+ children: [/* @__PURE__ */ jsxs("span", {
4948
+ className: "dsh-claude-diff-add",
4949
+ children: ["+", tool.additions ?? 0]
4950
+ }), /* @__PURE__ */ jsxs("span", {
4951
+ className: "dsh-claude-diff-delete",
4952
+ children: ["−", tool.deletions ?? 0]
4953
+ })]
4954
+ })
4955
+ ]
4956
+ }), /* @__PURE__ */ jsxs("div", {
4957
+ className: "dsh-claude-tool-content",
4958
+ children: [tool.subcalls.length === 0 ? null : /* @__PURE__ */ jsx("div", {
4959
+ className: "dsh-claude-flow-subcalls",
4960
+ children: tool.subcalls.map((subcall) => /* @__PURE__ */ jsxs("div", { children: [
4961
+ subcallGlyph(subcall),
4962
+ " ",
4963
+ subcall.toolName ?? t("subagent"),
4964
+ subcall.summary === void 0 ? "" : ` · ${subcall.summary}`
4965
+ ] }, subcall.toolUseId))
4966
+ }), /* @__PURE__ */ jsx(ToolPresentation, {
4967
+ tool,
4968
+ t
4969
+ })]
4970
+ })]
4971
+ });
4972
+ }
4973
+ function ClaudeTranscriptToolGroup({ tools, additions, deletions, files: _files, t }) {
4974
+ const [open, setOpen] = useState(false);
4975
+ const failed = tools.some((tool) => tool.isError === true || tool.phase === "failed");
4976
+ const running = tools.some((tool) => tool.phase === "started" || tool.phase === "updated");
4977
+ const summary = tools.length === 1 ? t("usedTool") : t("usedTools", { count: tools.length });
4978
+ return /* @__PURE__ */ jsxs("div", {
4979
+ className: "dsh-claude-tool-group-native",
4980
+ children: [/* @__PURE__ */ jsx(DisclosureRow, {
4981
+ rowClassName: "dsh-claude-flow-row",
4982
+ leadingClassName: "dsh-claude-flow-leading",
4983
+ titleClassName: "dsh-claude-flow-title",
4984
+ chevronClassName: "dsh-claude-flow-chevron",
4985
+ icon: failed ? /* @__PURE__ */ jsx(StateDot, { state: "error" }) : running ? /* @__PURE__ */ jsx(StateDot, { state: "ongoing" }) : /* @__PURE__ */ jsx(IconApiOutline14, { size: 14 }),
4986
+ title: summary,
4987
+ open,
4988
+ expandable: true,
4989
+ expandOnRowClick: true,
4990
+ keepContentWhenOpen: true,
4991
+ onToggle: () => setOpen((value) => !value),
4992
+ collapsedContent: additions !== void 0 || deletions !== void 0 ? /* @__PURE__ */ jsxs("span", {
4993
+ className: "dsh-claude-tool-stats",
4994
+ children: [/* @__PURE__ */ jsxs("span", {
4995
+ className: "dsh-claude-diff-add",
4996
+ children: ["+", additions ?? 0]
4997
+ }), /* @__PURE__ */ jsxs("span", {
4998
+ className: "dsh-claude-diff-delete",
4999
+ children: ["−", deletions ?? 0]
5000
+ })]
5001
+ }) : void 0
5002
+ }), open ? /* @__PURE__ */ jsx("div", {
5003
+ className: "dsh-claude-tool-list",
5004
+ children: tools.map((tool) => /* @__PURE__ */ jsx(ClaudeTranscriptToolItem, {
5005
+ tool,
5006
+ t
5007
+ }, tool.toolUseId))
5008
+ }) : null]
5009
+ });
5010
+ }
5011
+ /** Round a duration the way a reader reads one: no more precision than the
5012
+ * number deserves. */
5013
+ function formatTurnDuration(ms) {
5014
+ if (ms < 1e3) return `${Math.max(1, Math.round(ms))}ms`;
5015
+ const seconds = ms / 1e3;
5016
+ if (seconds < 60) return `${seconds < 10 ? seconds.toFixed(1) : String(Math.round(seconds))}s`;
5017
+ const whole = Math.round(seconds);
5018
+ return `${Math.floor(whole / 60)}m ${String(whole % 60).padStart(2, "0")}s`;
5019
+ }
5020
+ /** Share of the prompt that was served from cache.
5021
+ *
5022
+ * Cache reads are counted against everything the prompt cost to assemble --
5023
+ * fresh input and cache writes included -- so a turn that read nothing scores
5024
+ * zero rather than dividing by nothing. */
5025
+ function cacheHitRate(usage) {
5026
+ const read = usage.cacheReadTokens ?? 0;
5027
+ const total = read + (usage.cacheCreationTokens ?? 0) + (usage.inputTokens ?? 0);
5028
+ return total === 0 ? void 0 : read / total;
5029
+ }
5030
+ /** The turn's accounting, in the order a reader wants it: size, then cost of
5031
+ * assembling it, then how long it took, then money. */
5032
+ function turnUsageParts(usage, t) {
5033
+ const tokens = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0) + (usage.cacheReadTokens ?? 0) + (usage.cacheCreationTokens ?? 0);
5034
+ const cached = cacheHitRate(usage);
5035
+ const parts = [];
5036
+ if (tokens > 0) parts.push(t("turnUsageTokens", { count: formatTokenCount(tokens) }));
5037
+ if (cached !== void 0) parts.push(t("turnUsageCache", { percent: (cached * 100).toFixed(1) }));
5038
+ if (usage.durationMs !== void 0) parts.push(formatTurnDuration(usage.durationMs));
5039
+ if (usage.ttftMs !== void 0) parts.push(t("turnUsageTtft", { duration: formatTurnDuration(usage.ttftMs) }));
5040
+ if (usage.cumulativeCostUsd !== void 0) parts.push(t("turnUsageCost", { cost: usage.cumulativeCostUsd.toFixed(2) }));
5041
+ return parts;
5042
+ }
5043
+ /** The footer the Host draws under its own assistant message, drawn here for
5044
+ * the steps the Host never had a message for. */
5045
+ function ClaudeTurnUsage({ usage, t }) {
5046
+ const parts = turnUsageParts(usage, t);
5047
+ if (parts.length === 0) return null;
5048
+ return /* @__PURE__ */ jsxs("div", {
5049
+ className: "dsh-claude-turn-usage",
5050
+ children: [
5051
+ /* @__PURE__ */ jsx("span", {
5052
+ className: "dsh-claude-turn-usage-label",
5053
+ children: t("turnUsage")
5054
+ }),
5055
+ /* @__PURE__ */ jsx("span", {
5056
+ "aria-hidden": "true",
5057
+ children: "·"
5058
+ }),
5059
+ /* @__PURE__ */ jsx("span", { children: parts.join(" · ") })
5060
+ ]
5061
+ });
5062
+ }
5063
+ function ClaudeActivityNode({ node, useClaudeProjection, t }) {
5064
+ ensureCss$3();
5065
+ const marker = node.data;
5066
+ const activities = useClaudeProjection((value) => selectStepActivities(value, marker.turn, marker.step));
5067
+ const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? EMPTY_TASKS$1);
5068
+ const items = useMemo(() => transcriptItemsForStep(activities, marker.turn, marker.step, tasks), [
5069
+ activities,
5070
+ marker.step,
5071
+ marker.turn,
5072
+ tasks
5073
+ ]);
5074
+ const markdownLabels = useClaudeMarkdownLabels(t);
5075
+ if (items.length === 0) return null;
5076
+ return /* @__PURE__ */ jsx("div", {
5077
+ className: "dsh-claude-flow",
5078
+ children: items.map((item) => item.kind === "text" ? /* @__PURE__ */ jsx("div", {
5079
+ className: "dsh-claude-transcript-text",
5080
+ children: /* @__PURE__ */ jsx(ClaudeMarkdown, {
5081
+ text: item.text,
5082
+ labels: markdownLabels
5083
+ })
5084
+ }, `text:${item.ordinal}`) : item.kind === "compaction" ? /* @__PURE__ */ jsx(ClaudeCompactionDivider, {
5085
+ compaction: item.compaction,
5086
+ t
5087
+ }, `compaction:${item.ordinal}`) : item.kind === "usage" ? /* @__PURE__ */ jsx(ClaudeTurnUsage, {
5088
+ usage: item.usage,
5089
+ t
5090
+ }, `usage:${item.ordinal}`) : item.kind === "tools" ? /* @__PURE__ */ jsx(ClaudeTranscriptToolGroup, {
5091
+ tools: item.tools,
5092
+ ...item.additions === void 0 ? {} : { additions: item.additions },
5093
+ ...item.deletions === void 0 ? {} : { deletions: item.deletions },
5094
+ ...item.files === void 0 ? {} : { files: item.files },
5095
+ t
5096
+ }, `tools:${item.ordinal}`) : /* @__PURE__ */ jsx(ActivityRow, {
5097
+ row: item.row,
5098
+ t
5099
+ }, `activity:${item.ordinal}`))
5100
+ });
5101
+ }
5102
+ //#endregion
5103
+ //#region src/client/ClaudeTasksPanel.tsx
5104
+ const STATUS_LABEL = {
5105
+ running: "tasksRunning",
5106
+ completed: "tasksCompleted",
5107
+ failed: "tasksFailed",
5108
+ stopped: "tasksStopped",
5109
+ killed: "tasksKilled"
5110
+ };
5111
+ function visibleTaskGroups(tasks, dismissedSettledIds) {
5112
+ const projected = tasks.filter(isProjectedTask);
5113
+ return {
5114
+ running: projected.filter((task) => task.status === "running"),
5115
+ finished: projected.filter((task) => task.status !== "running" && !dismissedSettledIds.has(task.taskId))
5116
+ };
5117
+ }
5118
+ function activitiesForTask(activities, taskId) {
5119
+ return activities.filter((activity) => activity.taskId === taskId);
5120
+ }
5121
+ function tasksForTurn(tasks, turn) {
5122
+ return tasks.filter((task) => task.originTurn === turn && isProjectedTask(task));
5123
+ }
5124
+ function summarizeTurnTasks(tasks) {
5125
+ if (tasks.length === 0) return void 0;
5126
+ const running = tasks.filter((task) => task.status === "running").length;
5127
+ const failed = tasks.filter((task) => task.status === "failed" || task.status === "stopped" || task.status === "killed").length;
5128
+ const completed = tasks.filter((task) => task.status === "completed").length;
5129
+ return {
5130
+ state: running > 0 ? "running" : failed > 0 ? "failed" : "completed",
5131
+ count: tasks.length,
5132
+ running,
5133
+ failed,
5134
+ completed
5135
+ };
5136
+ }
5137
+ function statusGlyph(status) {
5138
+ if (status === "running") return "●";
5139
+ if (status === "completed") return "✓";
5140
+ if (status === "stopped") return "–";
5141
+ return "×";
5142
+ }
5143
+ function formatDuration(ms) {
5144
+ if (ms < 1e3) return String(Math.max(1, Math.round(ms))) + "ms";
5145
+ const seconds = Math.round(ms / 1e3);
5146
+ if (seconds < 60) return String(seconds) + "s";
5147
+ const minutes = Math.floor(seconds / 60);
5148
+ return String(minutes) + "m " + String(seconds % 60) + "s";
5149
+ }
5150
+ function taskMeta(task, t) {
5151
+ const parts = [];
5152
+ if (task.subagentType !== void 0) parts.push(task.subagentType);
5153
+ else if (task.taskType !== void 0) parts.push(task.taskType);
5154
+ if (task.usage?.durationMs !== void 0) parts.push(formatDuration(task.usage.durationMs));
5155
+ if (task.usage?.totalTokens !== void 0) parts.push(t("tokens", { count: formatTokenCount(task.usage.totalTokens) }));
5156
+ if (task.usage?.toolUses !== void 0) parts.push(t("tasksToolUses", { count: task.usage.toolUses }));
5157
+ if (task.lastToolName !== void 0) parts.push(t("tasksLastTool", { tool: task.lastToolName }));
5158
+ return parts;
5159
+ }
5160
+ function TaskActivity({ activity, t }) {
5161
+ return /* @__PURE__ */ jsxs("li", {
5162
+ style: taskActivityItem,
5163
+ children: [/* @__PURE__ */ jsx("span", {
5164
+ style: taskActivityGlyph,
5165
+ "aria-hidden": "true",
5166
+ children: activity.isError === true ? "×" : "›"
5167
+ }), /* @__PURE__ */ jsxs("div", {
5168
+ style: taskActivityBody,
5169
+ children: [
5170
+ /* @__PURE__ */ jsx("p", {
5171
+ style: taskActivityTitle,
5172
+ children: activity.title ?? activity.kind
5173
+ }),
5174
+ activity.summary === void 0 ? null : /* @__PURE__ */ jsx("p", {
5175
+ style: taskActivitySummary,
5176
+ children: activity.summary
5177
+ }),
5178
+ activity.detail === void 0 ? null : /* @__PURE__ */ jsxs("details", {
5179
+ style: taskActivityDetail,
5180
+ children: [/* @__PURE__ */ jsx("summary", {
5181
+ style: taskActivityDetailSummary,
5182
+ children: t("detail")
5183
+ }), /* @__PURE__ */ jsx("pre", {
5184
+ style: detailCode,
5185
+ children: activity.detail
5186
+ })]
5187
+ })
5188
+ ]
5189
+ })]
5190
+ });
5191
+ }
5192
+ function TaskCard(props) {
5193
+ const { task, activities, allActivities, t } = props;
5194
+ const tools = useMemo(() => taskTools(allActivities, task.taskId), [allActivities, task.taskId]);
5195
+ const [activityOpen, setActivityOpen] = useState(false);
5196
+ const running = task.status === "running";
5197
+ const failed = task.status === "failed" || task.status === "killed";
5198
+ const meta = taskMeta(task, t);
5199
+ return /* @__PURE__ */ jsxs("article", {
5200
+ style: {
5201
+ ...taskCard,
5202
+ ...running ? taskCardRunning : {}
5203
+ },
5204
+ children: [
5205
+ /* @__PURE__ */ jsxs("div", {
5206
+ style: taskCardTop,
5207
+ children: [/* @__PURE__ */ jsx("span", {
5208
+ className: running ? "dsh-claude-act-running" : void 0,
5209
+ style: {
5210
+ ...taskCardGlyph,
5211
+ ...running ? iconChipRunning : {},
5212
+ ...failed ? iconChipError : {}
5213
+ },
5214
+ "aria-hidden": "true",
5215
+ children: statusGlyph(task.status)
5216
+ }), /* @__PURE__ */ jsxs("div", {
5217
+ style: taskCardBody,
5218
+ children: [/* @__PURE__ */ jsx("p", {
5219
+ style: {
5220
+ ...taskTitle,
5221
+ ...failed ? { color: "var(--dsw-alias-state-error-primary)" } : {}
5222
+ },
5223
+ children: task.description
5224
+ }), /* @__PURE__ */ jsxs("p", {
5225
+ style: taskStatusLine,
5226
+ children: [/* @__PURE__ */ jsx("span", { children: t(STATUS_LABEL[task.status] ?? "tasksRunning") }), task.backgrounded === true ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
5227
+ "aria-hidden": "true",
5228
+ children: " · "
5229
+ }), /* @__PURE__ */ jsx("span", { children: t("tasksBackground") })] }) : null]
5230
+ })]
5231
+ })]
5232
+ }),
5233
+ meta.length === 0 ? null : /* @__PURE__ */ jsx("p", {
5234
+ style: taskMeta$1,
5235
+ children: meta.join(" · ")
5136
5236
  }),
5137
- input === void 0 ? null : /* @__PURE__ */ jsx(Section, {
5138
- title: t("toolInput"),
5139
- children: /* @__PURE__ */ jsx(Fields, {
5140
- value: input,
5141
- omit: ["command"]
5142
- })
5237
+ task.summary === void 0 || running ? null : /* @__PURE__ */ jsx("p", {
5238
+ style: taskSummary,
5239
+ children: task.summary
5143
5240
  }),
5144
- /* @__PURE__ */ jsx(TextDetail, {
5145
- title: outputTitle,
5146
- value: terminal || (typeof outputValue === "string" ? outputValue : void 0)
5241
+ activities.length === 0 && tools.length === 0 ? null : /* @__PURE__ */ jsxs("div", {
5242
+ style: taskActivitySection,
5243
+ children: [/* @__PURE__ */ jsx("button", {
5244
+ type: "button",
5245
+ style: taskTextButton,
5246
+ "aria-expanded": activityOpen,
5247
+ onClick: () => setActivityOpen((value) => !value),
5248
+ children: activityOpen ? t("tasksHideActivity") : t("tasksViewActivity")
5249
+ }), !activityOpen ? null : tools.length > 0 ? /* @__PURE__ */ jsx("div", {
5250
+ style: taskToolList,
5251
+ children: tools.map((tool) => /* @__PURE__ */ jsx(ClaudeTranscriptToolItem, {
5252
+ tool,
5253
+ t
5254
+ }, tool.toolUseId))
5255
+ }) : /* @__PURE__ */ jsx("ul", {
5256
+ style: taskActivityList,
5257
+ children: activities.map((activity) => /* @__PURE__ */ jsx(TaskActivity, {
5258
+ activity,
5259
+ t
5260
+ }, `${activity.turn}:${activity.step}:${activity.ordinal}`))
5261
+ })]
5147
5262
  })
5148
- ] });
5149
- }
5150
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [input === void 0 ? /* @__PURE__ */ jsx(TextDetail, {
5151
- title: t("toolInput"),
5152
- value: typeof inputValue === "string" ? inputValue : void 0
5153
- }) : /* @__PURE__ */ jsx(Section, {
5154
- title: t("toolInput"),
5155
- children: /* @__PURE__ */ jsx(Fields, { value: input })
5156
- }), output === void 0 ? /* @__PURE__ */ jsx(TextDetail, {
5157
- title: outputTitle,
5158
- value: typeof outputValue === "string" ? outputValue : void 0
5159
- }) : /* @__PURE__ */ jsx(Section, {
5160
- title: outputTitle,
5161
- children: /* @__PURE__ */ jsx(Fields, { value: output })
5263
+ ]
5264
+ });
5265
+ }
5266
+ function GroupHeading(props) {
5267
+ const { label, count, collapsed, onToggle, action } = props;
5268
+ const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", { children: label }), /* @__PURE__ */ jsx("span", {
5269
+ style: tasksGroupCount,
5270
+ children: count
5162
5271
  })] });
5272
+ return /* @__PURE__ */ jsxs("div", {
5273
+ style: tasksGroupHeading,
5274
+ children: [onToggle === void 0 ? /* @__PURE__ */ jsx("div", {
5275
+ style: tasksGroupTitle,
5276
+ children: content
5277
+ }) : /* @__PURE__ */ jsxs("button", {
5278
+ type: "button",
5279
+ style: tasksGroupToggle,
5280
+ "aria-expanded": !collapsed,
5281
+ onClick: onToggle,
5282
+ children: [/* @__PURE__ */ jsx("span", {
5283
+ style: {
5284
+ ...chevron,
5285
+ ...collapsed === true ? {} : chevronOpen
5286
+ },
5287
+ children: "›"
5288
+ }), content]
5289
+ }), action === void 0 ? null : /* @__PURE__ */ jsx("button", {
5290
+ type: "button",
5291
+ style: taskTextButton,
5292
+ onClick: action.onClick,
5293
+ children: action.label
5294
+ })]
5295
+ });
5163
5296
  }
5164
- function ClaudeTranscriptToolItem({ tool, t }) {
5165
- return /* @__PURE__ */ jsxs("details", {
5166
- className: "dsh-claude-tool-item",
5167
- children: [/* @__PURE__ */ jsxs("summary", {
5168
- className: "dsh-claude-tool-summary-row",
5169
- children: [
5170
- /* @__PURE__ */ jsx("span", {
5171
- className: "dsh-claude-tool-label",
5172
- children: tool.toolName
5173
- }),
5174
- /* @__PURE__ */ jsx("span", {
5175
- className: "dsh-claude-tool-description",
5176
- children: tool.description
5177
- }),
5178
- tool.additions === void 0 && tool.deletions === void 0 ? null : /* @__PURE__ */ jsxs("span", {
5179
- className: "dsh-claude-tool-stats",
5180
- children: [/* @__PURE__ */ jsxs("span", {
5181
- className: "dsh-claude-diff-add",
5182
- children: ["+", tool.additions ?? 0]
5183
- }), /* @__PURE__ */ jsxs("span", {
5184
- className: "dsh-claude-diff-delete",
5185
- children: ["−", tool.deletions ?? 0]
5297
+ function ClaudeTasksPanel({ useClaudeProjection, t, closeDetails, turn }) {
5298
+ const projection = useClaudeProjection((value) => value);
5299
+ const tasks = useMemo(() => tasksForTurn(projection.tasks?.tasks ?? [], turn), [projection.tasks, turn]);
5300
+ useEffect(() => {
5301
+ if (!projection.owned || tasks.length === 0) closeDetails();
5302
+ }, [
5303
+ closeDetails,
5304
+ projection.owned,
5305
+ tasks.length
5306
+ ]);
5307
+ const [finishedCollapsed, setFinishedCollapsed] = useState(false);
5308
+ const [dismissedSettledIds, setDismissedSettledIds] = useState(() => /* @__PURE__ */ new Set());
5309
+ const groups = useMemo(() => visibleTaskGroups(tasks, dismissedSettledIds), [tasks, dismissedSettledIds]);
5310
+ const taskActivities = useMemo(() => new Map(tasks.map((task) => [task.taskId, activitiesForTask(projection.activities, task.taskId)])), [projection.activities, tasks]);
5311
+ const clearFinished = () => setDismissedSettledIds((previous) => /* @__PURE__ */ new Set([...previous, ...tasks.filter((task) => task.status !== "running").map((task) => task.taskId)]));
5312
+ if (!projection.owned) return null;
5313
+ return /* @__PURE__ */ jsxs("div", {
5314
+ className: detailsCardClass,
5315
+ style: tasksPanel,
5316
+ children: [
5317
+ /* @__PURE__ */ jsxs("style", {
5318
+ "data-dsh-claude-panel-icon-styles": true,
5319
+ children: [detailsCardCss, panelIconButtonCss]
5320
+ }),
5321
+ /* @__PURE__ */ jsxs("div", {
5322
+ style: tasksHeader,
5323
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("span", {
5324
+ style: tasksHeading,
5325
+ children: t("tasksPanelTurn")
5326
+ }), /* @__PURE__ */ jsx("span", {
5327
+ style: tasksTurnMeta,
5328
+ children: t("tasksTurnNumber", { turn })
5329
+ })] }), /* @__PURE__ */ jsx("button", {
5330
+ type: "button",
5331
+ className: panelIconButtonClass,
5332
+ "aria-label": t("tasksClose"),
5333
+ onClick: closeDetails,
5334
+ children: /* @__PURE__ */ jsx(IconCloseOutline16, {})
5335
+ })]
5336
+ }),
5337
+ /* @__PURE__ */ jsxs("div", {
5338
+ style: tasksBody,
5339
+ children: [/* @__PURE__ */ jsxs("section", {
5340
+ "aria-label": t("tasksRunning"),
5341
+ children: [/* @__PURE__ */ jsx(GroupHeading, {
5342
+ label: t("tasksRunning"),
5343
+ count: groups.running.length
5344
+ }), groups.running.length === 0 ? /* @__PURE__ */ jsx("p", {
5345
+ style: tasksGroupEmpty,
5346
+ children: t("tasksNoneRunning")
5347
+ }) : /* @__PURE__ */ jsx("div", {
5348
+ style: taskCardList,
5349
+ children: groups.running.map((task) => /* @__PURE__ */ jsx(TaskCard, {
5350
+ task,
5351
+ activities: taskActivities.get(task.taskId) ?? [],
5352
+ allActivities: projection.activities,
5353
+ t
5354
+ }, task.taskId))
5355
+ })]
5356
+ }), /* @__PURE__ */ jsxs("section", {
5357
+ "aria-label": t("tasksSettled"),
5358
+ style: tasksFinishedSection,
5359
+ children: [/* @__PURE__ */ jsx(GroupHeading, {
5360
+ label: t("tasksSettled"),
5361
+ count: groups.finished.length,
5362
+ collapsed: finishedCollapsed,
5363
+ onToggle: () => setFinishedCollapsed((value) => !value),
5364
+ ...groups.finished.length === 0 ? {} : { action: {
5365
+ label: t("tasksClear"),
5366
+ onClick: clearFinished
5367
+ } }
5368
+ }), finishedCollapsed || groups.finished.length === 0 ? null : /* @__PURE__ */ jsx("div", {
5369
+ style: taskCardList,
5370
+ children: groups.finished.map((task) => /* @__PURE__ */ jsx(TaskCard, {
5371
+ task,
5372
+ activities: taskActivities.get(task.taskId) ?? [],
5373
+ allActivities: projection.activities,
5374
+ t
5375
+ }, task.taskId))
5186
5376
  })]
5187
- })
5188
- ]
5189
- }), /* @__PURE__ */ jsxs("div", {
5190
- className: "dsh-claude-tool-content",
5191
- children: [tool.subcalls.length === 0 ? null : /* @__PURE__ */ jsx("div", {
5192
- className: "dsh-claude-flow-subcalls",
5193
- children: tool.subcalls.map((subcall) => /* @__PURE__ */ jsxs("div", { children: [
5194
- subcallGlyph(subcall),
5195
- " ",
5196
- subcall.toolName ?? t("subagent"),
5197
- subcall.summary === void 0 ? "" : ` · ${subcall.summary}`
5198
- ] }, subcall.toolUseId))
5199
- }), /* @__PURE__ */ jsx(ToolPresentation, {
5200
- tool,
5201
- t
5202
- })]
5203
- })]
5377
+ })]
5378
+ })
5379
+ ]
5204
5380
  });
5205
5381
  }
5206
- function ClaudeTranscriptToolGroup({ tools, additions, deletions, files: _files, t }) {
5207
- const [open, setOpen] = useState(false);
5208
- const failed = tools.some((tool) => tool.isError === true || tool.phase === "failed");
5209
- const running = tools.some((tool) => tool.phase === "started" || tool.phase === "updated");
5210
- const summary = tools.length === 1 ? t("usedTool") : t("usedTools", { count: tools.length });
5211
- return /* @__PURE__ */ jsxs("div", {
5212
- className: "dsh-claude-tool-group-native",
5213
- children: [/* @__PURE__ */ jsx(DisclosureRow, {
5214
- rowClassName: "dsh-claude-flow-row",
5215
- leadingClassName: "dsh-claude-flow-leading",
5216
- titleClassName: "dsh-claude-flow-title",
5217
- chevronClassName: "dsh-claude-flow-chevron",
5218
- icon: failed ? /* @__PURE__ */ jsx(StateDot, { state: "error" }) : running ? /* @__PURE__ */ jsx(StateDot, { state: "ongoing" }) : /* @__PURE__ */ jsx(IconApiOutline14, { size: 14 }),
5219
- title: summary,
5220
- open,
5221
- expandable: true,
5222
- expandOnRowClick: true,
5223
- keepContentWhenOpen: true,
5224
- onToggle: () => setOpen((value) => !value),
5225
- collapsedContent: additions !== void 0 || deletions !== void 0 ? /* @__PURE__ */ jsxs("span", {
5226
- className: "dsh-claude-tool-stats",
5227
- children: [/* @__PURE__ */ jsxs("span", {
5228
- className: "dsh-claude-diff-add",
5229
- children: ["+", additions ?? 0]
5230
- }), /* @__PURE__ */ jsxs("span", {
5231
- className: "dsh-claude-diff-delete",
5232
- children: ["−", deletions ?? 0]
5382
+ //#endregion
5383
+ //#region src/client/ClaudeActivityTail.tsx
5384
+ const MAX_HOVER_TASKS = 6;
5385
+ function taskGlyph(status) {
5386
+ if (status === "failed") return {
5387
+ glyph: "×",
5388
+ style: tasksHoverGlyphError
5389
+ };
5390
+ if (status === "completed") return {
5391
+ glyph: "",
5392
+ style: tasksHoverGlyphDone
5393
+ };
5394
+ return {
5395
+ glyph: "●",
5396
+ style: tasksHoverGlyphRunning
5397
+ };
5398
+ }
5399
+ function ClaudeTaskLauncher({ turn, tasks, t, openTasks }) {
5400
+ const [hovered, setHovered] = useState(false);
5401
+ const closeTimer = useRef();
5402
+ const turnTasks = useMemo(() => tasksForTurn(tasks, turn), [tasks, turn]);
5403
+ const summary = useMemo(() => summarizeTurnTasks(turnTasks), [turnTasks]);
5404
+ const open = () => {
5405
+ if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
5406
+ closeTimer.current = void 0;
5407
+ setHovered(true);
5408
+ };
5409
+ const scheduleClose = () => {
5410
+ if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
5411
+ closeTimer.current = setTimeout(() => {
5412
+ closeTimer.current = void 0;
5413
+ setHovered(false);
5414
+ }, 350);
5415
+ };
5416
+ useEffect(() => () => {
5417
+ if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
5418
+ }, []);
5419
+ if (summary === void 0) return null;
5420
+ const label = summary.state === "running" ? t("tasksTurnRunning", { count: summary.running }) : summary.state === "failed" ? t("tasksTurnFailed", {
5421
+ failed: summary.failed,
5422
+ completed: summary.completed
5423
+ }) : t("tasksTurnCompleted", { count: summary.completed });
5424
+ const stateStyle = summary.state === "completed" ? tasksBadgeDone : {};
5425
+ const dotStyle = summary.state === "failed" ? tasksBadgeDotError : summary.state === "completed" ? tasksBadgeDotDone : {};
5426
+ return /* @__PURE__ */ jsx("div", {
5427
+ "data-claude-task-launcher": turn,
5428
+ style: tasksBadgeWrap,
5429
+ children: /* @__PURE__ */ jsxs("span", {
5430
+ style: tasksBadgeSeat,
5431
+ onMouseEnter: open,
5432
+ onMouseLeave: scheduleClose,
5433
+ onFocus: open,
5434
+ onBlur: (event) => {
5435
+ if (!event.currentTarget.contains(event.relatedTarget)) scheduleClose();
5436
+ },
5437
+ children: [hovered ? /* @__PURE__ */ jsxs("span", {
5438
+ role: "tooltip",
5439
+ style: tasksHoverCard,
5440
+ onMouseEnter: open,
5441
+ onMouseLeave: scheduleClose,
5442
+ children: [
5443
+ /* @__PURE__ */ jsx("span", {
5444
+ style: tasksHoverHeader,
5445
+ children: label
5446
+ }),
5447
+ turnTasks.slice(0, MAX_HOVER_TASKS).map((task) => {
5448
+ const { glyph, style } = taskGlyph(task.status);
5449
+ return /* @__PURE__ */ jsxs("span", {
5450
+ style: tasksHoverRow,
5451
+ children: [
5452
+ /* @__PURE__ */ jsx("span", {
5453
+ className: task.status === "running" ? "dsh-claude-act-running" : void 0,
5454
+ style: {
5455
+ ...tasksHoverGlyph,
5456
+ ...style
5457
+ },
5458
+ "aria-hidden": "true",
5459
+ children: glyph
5460
+ }),
5461
+ /* @__PURE__ */ jsx("span", {
5462
+ style: tasksHoverDesc,
5463
+ title: task.description,
5464
+ children: task.description
5465
+ }),
5466
+ task.subagentType === void 0 ? null : /* @__PURE__ */ jsx("span", {
5467
+ style: tasksHoverType,
5468
+ children: task.subagentType
5469
+ })
5470
+ ]
5471
+ }, task.taskId);
5472
+ }),
5473
+ turnTasks.length > MAX_HOVER_TASKS ? /* @__PURE__ */ jsxs("span", {
5474
+ style: tasksHoverMore,
5475
+ children: ["+", turnTasks.length - MAX_HOVER_TASKS]
5476
+ }) : null,
5477
+ /* @__PURE__ */ jsx("span", {
5478
+ style: tasksHoverHint,
5479
+ children: t("tasksOpen")
5480
+ })
5481
+ ]
5482
+ }) : null, /* @__PURE__ */ jsxs("button", {
5483
+ type: "button",
5484
+ className: "dsh-claude-task-launcher",
5485
+ style: {
5486
+ ...tasksTurnBadge,
5487
+ ...stateStyle,
5488
+ ...hovered ? tasksBadgeHovered : {}
5489
+ },
5490
+ "aria-label": `${label} — ${t("tasksOpen")}`,
5491
+ onClick: () => openTasks(turn),
5492
+ children: [/* @__PURE__ */ jsx("span", {
5493
+ className: summary.state === "running" ? "dsh-claude-act-running" : void 0,
5494
+ style: {
5495
+ ...tasksBadgeDot,
5496
+ ...dotStyle
5497
+ },
5498
+ "aria-hidden": "true"
5499
+ }), /* @__PURE__ */ jsx("span", {
5500
+ style: tasksBadgeLabel,
5501
+ children: label
5233
5502
  })]
5234
- }) : void 0
5235
- }), open ? /* @__PURE__ */ jsx("div", {
5236
- className: "dsh-claude-tool-list",
5237
- children: tools.map((tool) => /* @__PURE__ */ jsx(ClaudeTranscriptToolItem, {
5238
- tool,
5239
- t
5240
- }, tool.toolUseId))
5241
- }) : null]
5503
+ })]
5504
+ })
5242
5505
  });
5243
5506
  }
5244
- function ClaudeActivityNode({ node, useClaudeProjection, t }) {
5245
- ensureCss$3();
5246
- const marker = node.data;
5247
- const activities = useClaudeProjection((value) => selectStepActivities(value, marker.turn, marker.step));
5507
+ function ClaudeActivityTail({ matched, useClaudeProjection, t, openTasks }) {
5508
+ const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? []);
5509
+ return /* @__PURE__ */ jsx(ClaudeTaskLauncher, {
5510
+ turn: matched.turn,
5511
+ tasks,
5512
+ t,
5513
+ openTasks
5514
+ });
5515
+ }
5516
+ //#endregion
5517
+ //#region src/client/ClaudeActiveTasksNode.tsx
5518
+ const EMPTY_TASKS = [];
5519
+ /** Render the task launcher while the owning DSH turn is still open. */
5520
+ function ClaudeActiveTasksNode({ node, useClaudeProjection, t, openTasks }) {
5248
5521
  const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? EMPTY_TASKS);
5249
- const items = useMemo(() => transcriptItemsForStep(activities, marker.turn, marker.step, tasks), [
5250
- activities,
5251
- marker.step,
5252
- marker.turn,
5253
- tasks
5254
- ]);
5255
- const markdownLabels = useClaudeMarkdownLabels(t);
5256
- if (items.length === 0) return null;
5257
- return /* @__PURE__ */ jsx("div", {
5258
- className: "dsh-claude-flow",
5259
- children: items.map((item) => item.kind === "text" ? /* @__PURE__ */ jsx("div", {
5260
- className: "dsh-claude-transcript-text",
5261
- children: /* @__PURE__ */ jsx(ClaudeMarkdown, {
5262
- text: item.text,
5263
- labels: markdownLabels
5264
- })
5265
- }, `text:${item.ordinal}`) : item.kind === "compaction" ? /* @__PURE__ */ jsx(ClaudeCompactionDivider, {
5266
- compaction: item.compaction,
5267
- t
5268
- }, `compaction:${item.ordinal}`) : item.kind === "tools" ? /* @__PURE__ */ jsx(ClaudeTranscriptToolGroup, {
5269
- tools: item.tools,
5270
- ...item.additions === void 0 ? {} : { additions: item.additions },
5271
- ...item.deletions === void 0 ? {} : { deletions: item.deletions },
5272
- ...item.files === void 0 ? {} : { files: item.files },
5273
- t
5274
- }, `tools:${item.ordinal}`) : /* @__PURE__ */ jsx(ActivityRow, {
5275
- row: item.row,
5276
- t
5277
- }, `activity:${item.ordinal}`))
5522
+ return /* @__PURE__ */ jsx(ClaudeTaskLauncher, {
5523
+ turn: node.data.turn,
5524
+ tasks,
5525
+ t,
5526
+ openTasks
5278
5527
  });
5279
5528
  }
5280
5529
  //#endregion
@@ -5394,6 +5643,7 @@ window.__ModuleLoader__.load({
5394
5643
  if (typeof setting !== "object" || setting === null || Array.isArray(setting)) return false;
5395
5644
  const item = setting;
5396
5645
  if (typeof item.key !== "string" || typeof item.value !== "string" || ![
5646
+ "immediate",
5397
5647
  "new-session",
5398
5648
  "next-turn",
5399
5649
  "next-worktree",
@@ -5584,6 +5834,23 @@ window.__ModuleLoader__.load({
5584
5834
  "seven_day_opus",
5585
5835
  "seven_day_sonnet"
5586
5836
  ]);
5837
+ /** The prose mode a settings payload carries, or the default when it carries
5838
+ * none — an older Host, or a response this Client does not fully understand,
5839
+ * leaves the palette alone rather than guessing. */
5840
+ function proseModeOf(settings) {
5841
+ const value = settings.find((setting) => setting.key === "prose")?.value;
5842
+ return isClaudeProseMode(value) ? value : DEFAULT_CLAUDE_PROSE_MODE;
5843
+ }
5844
+ /** Settings whose row only makes sense under a particular value of another.
5845
+ * Filtering here rather than server-side keeps the descriptor list flat: the
5846
+ * server has no view of what the Client can paint. Fails OPEN — a payload
5847
+ * missing the setting a row depends on shows the row rather than hiding it,
5848
+ * so an older Host cannot make a setting unreachable. */
5849
+ function visibleGlobalSettings(settings) {
5850
+ const renderer = settings.find((setting) => setting.key === "renderer");
5851
+ if (renderer === void 0 || renderer.value !== "native") return settings;
5852
+ return settings.filter((setting) => setting.key !== "prose");
5853
+ }
5587
5854
  /** Per-setting label and the effect note that used to sit as a standalone
5588
5855
  * paragraph under the card; it now hangs off the label as a hover hint. */
5589
5856
  const SETTING_COPY = {
@@ -5595,6 +5862,10 @@ window.__ModuleLoader__.load({
5595
5862
  label: "renderer",
5596
5863
  hint: "rendererEffect"
5597
5864
  },
5865
+ prose: {
5866
+ label: "prose",
5867
+ hint: "proseEffect"
5868
+ },
5598
5869
  worktreeBranchPrefix: {
5599
5870
  label: "worktreeBranchPrefix",
5600
5871
  hint: "worktreeBranchPrefixEffect"
@@ -5613,7 +5884,9 @@ window.__ModuleLoader__.load({
5613
5884
  * names) carry no entry and keep the label the route reported. */
5614
5885
  const SETTING_OPTION_COPY = {
5615
5886
  "renderer:plugin": "rendererPlugin",
5616
- "renderer:native": "rendererNative"
5887
+ "renderer:native": "rendererNative",
5888
+ "prose:plain": "prosePlain",
5889
+ "prose:enhanced": "proseEnhanced"
5617
5890
  };
5618
5891
  function settingOptionLabel(settingKey, option, t) {
5619
5892
  const key = SETTING_OPTION_COPY[`${settingKey}:${option.value}`];
@@ -5852,6 +6125,7 @@ window.__ModuleLoader__.load({
5852
6125
  });
5853
6126
  if (!isGlobalSettingsView(payload)) throw new Error("Invalid global settings response");
5854
6127
  setGlobalSettings(payload);
6128
+ applyClaudeMarkdownTheme(proseModeOf(payload.settings));
5855
6129
  } catch (cause) {
5856
6130
  setGlobalSettingsError(cardFailure(cause));
5857
6131
  } finally {
@@ -5962,7 +6236,7 @@ window.__ModuleLoader__.load({
5962
6236
  globalSettings === void 0 ? /* @__PURE__ */ jsx("p", {
5963
6237
  style: notice,
5964
6238
  children: t("globalSettingsLoading")
5965
- }) : globalSettings.settings.map((setting) => {
6239
+ }) : visibleGlobalSettings(globalSettings.settings).map((setting) => {
5966
6240
  const copy = SETTING_COPY[setting.key];
5967
6241
  return /* @__PURE__ */ jsxs("div", {
5968
6242
  style: diagnosticGrid,
@@ -6305,10 +6579,14 @@ window.__ModuleLoader__.load({
6305
6579
  const HOST_STAGES = /* @__PURE__ */ new Set([
6306
6580
  "inspecting",
6307
6581
  "fetching",
6582
+ "summarizing",
6308
6583
  "creating-worktree",
6309
6584
  "saving-worktree",
6310
6585
  "switching-branch"
6311
6586
  ]);
6587
+ /** The draft only has to describe the work; the host truncates it again before
6588
+ * summarizing, and the setup route caps the whole body at 16 KiB. */
6589
+ const MAX_INTENT_CHARS = 2e3;
6312
6590
  function record$3(value) {
6313
6591
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
6314
6592
  }
@@ -6344,7 +6622,7 @@ window.__ModuleLoader__.load({
6344
6622
  throw error;
6345
6623
  }
6346
6624
  }
6347
- async function prepareRepository(cwd, branch, worktree, branchName, onProgress = () => {}) {
6625
+ async function prepareRepository(cwd, branch, worktree, branchName, onProgress = () => {}, intent) {
6348
6626
  const carrier = new AbortController();
6349
6627
  try {
6350
6628
  const reader = await pluginNdjson(CLAUDE_REPOSITORY_SETUP_PATH, carrier.signal, {
@@ -6353,7 +6631,8 @@ window.__ModuleLoader__.load({
6353
6631
  cwd,
6354
6632
  branch,
6355
6633
  worktree,
6356
- ...branchName === void 0 ? {} : { branchName }
6634
+ ...branchName === void 0 ? {} : { branchName },
6635
+ ...intent === void 0 || intent.trim().length === 0 ? {} : { intent: intent.slice(0, MAX_INTENT_CHARS) }
6357
6636
  }
6358
6637
  });
6359
6638
  const decoder = new TextDecoder();
@@ -6642,10 +6921,40 @@ window.__ModuleLoader__.load({
6642
6921
  const CLAUDE_SCOPED_CSS_VARIABLES = ["--dsh-composer-card-max-width"];
6643
6922
  /** Marks the bar the scoped-property probe measures. */
6644
6923
  const CLAUDE_COMPOSER_BAR_ATTRIBUTE = "data-dsh-claude-composer-bar";
6645
- /** One line per service the Host no longer provides; empty when all resolve. */
6646
- function claudeBootCheckFindings(input) {
6924
+ /** Methods this plugin calls on the Host services it injects.
6925
+ *
6926
+ * A service that still resolves but has lost a method is the drift the
6927
+ * service list above cannot see: Desktop 0.1.2 moved `connectWorkspace` off
6928
+ * `workspaces` onto a new service, and the worktree flow went on registering
6929
+ * itself and only broke once a user ran it. Naming the methods here turns
6930
+ * that into a boot-time line. Methods with a runtime fallback stay out. */
6931
+ const CLAUDE_REQUIRED_SERVICE_METHODS = {
6932
+ sessions: [
6933
+ "scope",
6934
+ "open",
6935
+ "binding"
6936
+ ],
6937
+ workspaces: [
6938
+ "create",
6939
+ "delete",
6940
+ "archiveSession"
6941
+ ],
6942
+ uiConversation: ["binding"],
6943
+ uiSession: ["provide"],
6944
+ inputTriggers: ["registerSource"]
6945
+ };
6946
+ /** One line per service the Host no longer provides, or provides without a
6947
+ * method this plugin calls on it; empty when everything resolves. */
6948
+ function claudeBootCheckFindings(input, methods = CLAUDE_REQUIRED_SERVICE_METHODS) {
6647
6949
  const findings = [];
6648
- for (const name of input.services) if (input.resolve(name) === void 0) findings.push(`service "${name}" is declared in inject but the Host does not provide it`);
6950
+ for (const name of input.services) {
6951
+ const service = input.resolve(name);
6952
+ if (service === void 0) {
6953
+ findings.push(`service "${name}" is declared in inject but the Host does not provide it`);
6954
+ continue;
6955
+ }
6956
+ for (const method of methods[name] ?? []) if (typeof service[method] !== "function") findings.push(`service "${name}" no longer provides ${method}(); the features calling it are broken`);
6957
+ }
6649
6958
  return findings;
6650
6959
  }
6651
6960
  /** One line per scoped custom property the measured element cannot see.
@@ -7578,9 +7887,15 @@ window.__ModuleLoader__.load({
7578
7887
  style: repositoryRemote,
7579
7888
  children: repositoryName$1(repository.remote)
7580
7889
  }),
7581
- /* @__PURE__ */ jsx("span", {
7582
- style: repositoryBranch,
7583
- children: branch
7890
+ /* @__PURE__ */ jsx(Tooltip, {
7891
+ label: branch,
7892
+ side: "top",
7893
+ delayMs: 250,
7894
+ maxWidth: 420,
7895
+ children: /* @__PURE__ */ jsx("span", {
7896
+ style: repositoryBranch,
7897
+ children: branch
7898
+ })
7584
7899
  }),
7585
7900
  repository.worktree === true ? /* @__PURE__ */ jsx("span", {
7586
7901
  style: repositoryWorktree,
@@ -7772,7 +8087,9 @@ window.__ModuleLoader__.load({
7772
8087
  children: comment.text
7773
8088
  })]
7774
8089
  }),
7775
- openDelayMs: 250
8090
+ openDelayMs: 250,
8091
+ copyLabel: t("markdownCopy"),
8092
+ copiedLabel: t("markdownCopied")
7776
8093
  });
7777
8094
  }
7778
8095
  /** Pending review comments docked above the composer; drained by the next user turn. */
@@ -13841,7 +14158,7 @@ window.__ModuleLoader__.load({
13841
14158
  * @returns the preset id, or undefined when neither source carries one.
13842
14159
  */
13843
14160
  function sessionRowPreset(row) {
13844
- return row?.agentPreset ?? row?.projectionValues?.agentPreset;
14161
+ return row?.agentPreset ?? row?.projectionValues?.agentPreset ?? void 0;
13845
14162
  }
13846
14163
  //#endregion
13847
14164
  //#region src/client/ClaudePullRequestsPanel.tsx
@@ -15072,10 +15389,15 @@ window.__ModuleLoader__.load({
15072
15389
  }
15073
15390
  //#endregion
15074
15391
  //#region src/client/ClaudeHeroRepositoryControls.tsx
15392
+ /** The host composer was a textarea through rc.8 and is a Lexical
15393
+ * contenteditable from 0.1.2 on, so match the composer seat rather than the
15394
+ * element type: an element-type check silently stops intercepting Enter, and
15395
+ * the capsule's branch and Worktree choices go nowhere. */
15075
15396
  function shouldInterceptKey(event) {
15076
15397
  if (event.key !== "Enter" || event.shiftKey || event.repeat || event.isComposing) return false;
15077
15398
  const target = event.target;
15078
- return target instanceof HTMLTextAreaElement && target.closest("[data-phase=\"hero\"]") !== null;
15399
+ if (!(target instanceof Element)) return false;
15400
+ return target.closest("textarea, [data-composer-input]") !== null && target.closest("[data-phase=\"hero\"]") !== null;
15079
15401
  }
15080
15402
  function shouldInterceptClick(event) {
15081
15403
  const target = event.target;
@@ -15111,6 +15433,7 @@ window.__ModuleLoader__.load({
15111
15433
  const WORKTREE_PROGRESS_STAGES = [
15112
15434
  "inspecting",
15113
15435
  "fetching",
15436
+ "summarizing",
15114
15437
  "creating-worktree",
15115
15438
  "saving-worktree",
15116
15439
  "creating-workspace",
@@ -15121,6 +15444,7 @@ window.__ModuleLoader__.load({
15121
15444
  const PROGRESS_LABEL_KEYS = {
15122
15445
  inspecting: "repositoryProgress_inspecting",
15123
15446
  fetching: "repositoryProgress_fetching",
15447
+ summarizing: "repositoryProgress_summarizing",
15124
15448
  "creating-worktree": "repositoryProgress_creating-worktree",
15125
15449
  "saving-worktree": "repositoryProgress_saving-worktree",
15126
15450
  "switching-branch": "repositoryProgress_switching-branch",
@@ -16702,6 +17026,10 @@ window.__ModuleLoader__.load({
16702
17026
  rendererPlugin: "插件渲染器",
16703
17027
  rendererNative: "DSH 原生渲染器",
16704
17028
  rendererEffect: "插件渲染器沿用本插件自带的转录视图:交错的正文、成组的工具卡片与活动行。DSH 原生渲染器改由 DSH 自身绘制:正文作为普通助手文本块,思考作为推理块,Claude 的顶层工具会镜像成原生工具卡片。修改从下一个回合起生效;已经产生的回合仍按记录时的渲染器显示,不会重绘。",
17029
+ prose: "Markdown 彩色高亮",
17030
+ prosePlain: "关闭",
17031
+ proseEnhanced: "开启",
17032
+ proseEffect: "开启后,正文的标题、加粗、斜体、行内代码、列表符号、引用与链接各自着色,代码块换成深色描边底。这会覆盖本插件默认对齐 Claude 桌面版的配色。仅在使用插件渲染器时有效,改动立即生效,无需等待下一回合。",
16705
17033
  worktreeBranchPrefix: "Worktree 分支前缀",
16706
17034
  worktreeBranchPrefixEffect: "分支前缀修改会应用于之后自动创建的 Worktree 分支;显式指定的完整分支名不会被修改。",
16707
17035
  maxProcessesSetting: "Claude 进程上限",
@@ -16796,6 +17124,7 @@ window.__ModuleLoader__.load({
16796
17124
  repositoryProgressDismiss: "关闭 Worktree 进度",
16797
17125
  repositoryProgress_inspecting: "检查仓库和分支",
16798
17126
  repositoryProgress_fetching: "刷新远程引用",
17127
+ repositoryProgress_summarizing: "总结需求生成分支名",
16799
17128
  "repositoryProgress_creating-worktree": "创建分支和 Worktree",
16800
17129
  "repositoryProgress_saving-worktree": "保存 Worktree 状态",
16801
17130
  "repositoryProgress_switching-branch": "切换本地分支",
@@ -16853,6 +17182,9 @@ window.__ModuleLoader__.load({
16853
17182
  diffCommentNext: "下一条评论",
16854
17183
  diffCommentPosition: "第 {index} 条,共 {total} 条",
16855
17184
  diffCommentCounter: "{index}/{total}",
17185
+ diffCardExpand: "展开其余 {count} 行",
17186
+ diffCardCollapse: "收起",
17187
+ diffCardFiles: "{count} 个文件",
16856
17188
  diffExpandAll: "展开全部文件",
16857
17189
  diffCollapseAll: "收起全部文件",
16858
17190
  diffRestore: "还原 Diff 面板",
@@ -17014,7 +17346,12 @@ window.__ModuleLoader__.load({
17014
17346
  rewindSubmitting: "回退中…",
17015
17347
  rewindFailed: "回退失败({code})。",
17016
17348
  rewindBusy: "这个会话正在运行,请等本轮结束后再回退。",
17017
- rewindStale: "宿主进程里的插件还是旧版本(没有回退接口)。重启 DSH 后再试。"
17349
+ rewindStale: "宿主进程里的插件还是旧版本(没有回退接口)。重启 DSH 后再试。",
17350
+ turnUsage: "本回合用量",
17351
+ turnUsageTokens: "{count} tok",
17352
+ turnUsageCache: "缓存命中 {percent}%",
17353
+ turnUsageTtft: "首字 {duration}",
17354
+ turnUsageCost: "累计 ${cost}"
17018
17355
  };
17019
17356
  const en = {
17020
17357
  nav: "Claude Code",
@@ -17066,6 +17403,10 @@ window.__ModuleLoader__.load({
17066
17403
  rendererPlugin: "Plugin renderer",
17067
17404
  rendererNative: "DSH native renderer",
17068
17405
  rendererEffect: "The plugin renderer keeps this package’s own transcript: interleaved prose, grouped tool cards, and activity rows. The DSH native renderer hands the same turn to DSH itself — prose as ordinary assistant text blocks, thinking as reasoning blocks, and root Claude tools mirrored into native tool cards. A change applies from the next turn; turns already recorded keep the renderer they were recorded with and are not redrawn.",
17406
+ prose: "Markdown colour highlighting",
17407
+ prosePlain: "Off",
17408
+ proseEnhanced: "On",
17409
+ proseEffect: "Gives headings, bold, italics, inline code, list markers, quotes and links their own colours, and paints the code block on a dark outlined surface. This overrides the Claude-desktop palette this package matches by default. Only applies under the plugin renderer, and takes effect immediately rather than on the next turn.",
17069
17410
  worktreeBranchPrefix: "Worktree branch prefix",
17070
17411
  worktreeBranchPrefixEffect: "Prefix changes apply to subsequently generated Worktree branches. Explicit full branch names remain unchanged.",
17071
17412
  maxProcessesSetting: "Claude process limit",
@@ -17160,6 +17501,7 @@ window.__ModuleLoader__.load({
17160
17501
  repositoryProgressDismiss: "Dismiss Worktree progress",
17161
17502
  repositoryProgress_inspecting: "Checking repository and branch",
17162
17503
  repositoryProgress_fetching: "Refreshing remote references",
17504
+ repositoryProgress_summarizing: "Naming the branch after the request",
17163
17505
  "repositoryProgress_creating-worktree": "Creating branch and Worktree",
17164
17506
  "repositoryProgress_saving-worktree": "Saving Worktree state",
17165
17507
  "repositoryProgress_switching-branch": "Switching local branch",
@@ -17217,6 +17559,9 @@ window.__ModuleLoader__.load({
17217
17559
  diffCommentNext: "Next comment",
17218
17560
  diffCommentPosition: "Comment {index} of {total}",
17219
17561
  diffCommentCounter: "{index}/{total}",
17562
+ diffCardExpand: "Show {count} more lines",
17563
+ diffCardCollapse: "Show less",
17564
+ diffCardFiles: "{count} files",
17220
17565
  diffExpandAll: "Expand all files",
17221
17566
  diffCollapseAll: "Collapse all files",
17222
17567
  diffRestore: "Restore diff panel",
@@ -17378,7 +17723,12 @@ window.__ModuleLoader__.load({
17378
17723
  rewindSubmitting: "Rewinding…",
17379
17724
  rewindFailed: "The rewind failed ({code}).",
17380
17725
  rewindBusy: "This session is running; wait for the turn to finish before rewinding.",
17381
- rewindStale: "The Host process is still running the previous plugin build, which has no rewind route. Restart DSH and try again."
17726
+ rewindStale: "The Host process is still running the previous plugin build, which has no rewind route. Restart DSH and try again.",
17727
+ turnUsage: "Turn usage",
17728
+ turnUsageTokens: "{count} tok",
17729
+ turnUsageCache: "Cache hit {percent}%",
17730
+ turnUsageTtft: "TTFT {duration}",
17731
+ turnUsageCost: "${cost} total"
17382
17732
  };
17383
17733
  //#endregion
17384
17734
  //#region src/client/index.tsx
@@ -17412,6 +17762,17 @@ window.__ModuleLoader__.load({
17412
17762
  "conversation",
17413
17763
  "connection"
17414
17764
  ];
17765
+ /** Resolve one session's composer facade.
17766
+ *
17767
+ * `sessions.scope()` returns the client runtime's AgentContext, and the
17768
+ * conversation package -- a release ahead of the runtime in the graph the Host
17769
+ * itself ships -- types `input.for` against a Context whose `remote` has since
17770
+ * gained `$stream` and `$host`. It is the same object at runtime; only the two
17771
+ * declarations disagree, and they disagree inside the Host too, so the cast
17772
+ * lives here once instead of at all nine call sites. */
17773
+ function sessionInput(conversation, scope) {
17774
+ return conversation.input.for(scope);
17775
+ }
17415
17776
  function apply(ctx) {
17416
17777
  const namespace = "settings.claude-code";
17417
17778
  const diagnostics = createClaudeDiagnosticsReporter();
@@ -17428,12 +17789,17 @@ window.__ModuleLoader__.load({
17428
17789
  }), "dsh-claude: client copy");
17429
17790
  const t = ctx.locale.bind(namespace);
17430
17791
  ctx.effect(() => restyleHostChrome(), "dsh-claude: Host chrome restyling");
17792
+ pluginRead(CLAUDE_GLOBAL_SETTINGS_PATH, "fast").then((payload) => {
17793
+ if (isGlobalSettingsView(payload)) applyClaudeMarkdownTheme(proseModeOf(payload.settings));
17794
+ }).catch(() => {});
17431
17795
  const projections = new ClaudeProjectionStore({ report: (kind, detail) => {
17432
17796
  diagnostics.report(kind, detail);
17433
17797
  } });
17434
17798
  ctx.effect(() => ctx.inputTriggers.registerSource(createClaudeCommandSource(ctx, projections)), "dsh-claude: Claude slash source");
17435
17799
  const sessions = ctx.get("sessions");
17436
17800
  const workspaces = ctx.get("workspaces");
17801
+ const uiWorkspace = ctx.get("uiWorkspace");
17802
+ const connectWorkspace = uiWorkspace?.connectWorkspace?.bind(uiWorkspace) ?? (typeof workspaces?.connectWorkspace === "function" ? workspaces.connectWorkspace.bind(workspaces) : void 0);
17437
17803
  const conversation = ctx.get("conversation");
17438
17804
  const connection = ctx.get("connection");
17439
17805
  const remote = ctx.get("remote");
@@ -17445,7 +17811,7 @@ window.__ModuleLoader__.load({
17445
17811
  return (draft, mode = "append") => {
17446
17812
  const scope = sessions.scope(sessionId);
17447
17813
  if (scope === void 0) return false;
17448
- const input = conversation.input.for(scope);
17814
+ const input = sessionInput(conversation, scope);
17449
17815
  const current = input.state.getSnapshot().draft;
17450
17816
  if (current.trim() === "") input.setDraft(draft);
17451
17817
  else if (mode === "append") input.setDraft(`${current}\n\n${draft}`);
@@ -17691,7 +18057,7 @@ window.__ModuleLoader__.load({
17691
18057
  ...sessions === void 0 || conversation === void 0 ? {} : { submitWith: (fallbackDraft) => {
17692
18058
  const scope = sessions.scope(sessionId);
17693
18059
  if (scope === void 0) return;
17694
- const input = conversation.input.for(scope);
18060
+ const input = sessionInput(conversation, scope);
17695
18061
  if (input.state.getSnapshot().draft.trim() === "") input.setDraft(fallbackDraft);
17696
18062
  input.submit();
17697
18063
  } }
@@ -17729,7 +18095,7 @@ window.__ModuleLoader__.load({
17729
18095
  ...sessions === void 0 || conversation === void 0 ? {} : { insertIntoChat: (sessionId, text) => {
17730
18096
  const scope = sessions.scope(sessionId);
17731
18097
  if (scope === void 0) return;
17732
- const input = conversation.input.for(scope);
18098
+ const input = sessionInput(conversation, scope);
17733
18099
  const current = input.state.getSnapshot().draft;
17734
18100
  input.setDraft(current.trim() === "" ? text : `${current}\n\n${text}`);
17735
18101
  } }
@@ -17791,7 +18157,7 @@ window.__ModuleLoader__.load({
17791
18157
  ...conversation === void 0 ? {} : { setDraft: (sessionId, text) => {
17792
18158
  const scope = sessions.scope(sessionId);
17793
18159
  if (scope === void 0) return;
17794
- conversation.input.for(scope).setDraft(text);
18160
+ sessionInput(conversation, scope).setDraft(text);
17795
18161
  } }
17796
18162
  };
17797
18163
  ctx.slots.inject("shell.overlay", () => ctx.slots.register({
@@ -17813,12 +18179,12 @@ window.__ModuleLoader__.load({
17813
18179
  t,
17814
18180
  updateQueue: (itemId, action) => target.updateQueue(itemId, action),
17815
18181
  notify: (level, text) => {
17816
- if (scope !== void 0) conversation.input.for(scope).notify(level, text);
18182
+ if (scope !== void 0) sessionInput(conversation, scope).notify(level, text);
17817
18183
  }
17818
18184
  };
17819
18185
  }
17820
18186
  }, ClaudeQueueDock));
17821
- if (sessions !== void 0 && workspaces !== void 0 && conversation !== void 0 && connection !== void 0 && remote !== void 0) {
18187
+ if (sessions !== void 0 && workspaces !== void 0 && connectWorkspace !== void 0 && conversation !== void 0 && connection !== void 0 && remote !== void 0) {
17822
18188
  /** Attach a prepared worktree to its session without blocking the flow. */
17823
18189
  const bindLease = (leaseId, targetSessionId) => {
17824
18190
  if (leaseId === void 0) return;
@@ -17836,11 +18202,11 @@ window.__ModuleLoader__.load({
17836
18202
  prepare: async (cwd, branch, useWorktree, onProgress, ticket) => {
17837
18203
  const sourceScope = sessions.scope(sourceSessionId);
17838
18204
  if (sourceScope === void 0) throw new Error(t("repositorySessionUnavailable"));
17839
- const sourceInput = conversation.input.for(sourceScope);
18205
+ const sourceInput = sessionInput(conversation, sourceScope);
17840
18206
  const rawDraft = sourceInput.state.getSnapshot().draft;
17841
18207
  const draft = ticket === void 0 ? rawDraft : rawDraft.trim() === "" ? ticketPrompt(ticket) : `${rawDraft.trimEnd()}\n\n${ticketContext(ticket)}`;
17842
18208
  const imageIds = sourceInput.state.getSnapshot().imageIds;
17843
- const prepared = await prepareRepository(cwd, branch, useWorktree, ticket?.key, onProgress);
18209
+ const prepared = await prepareRepository(cwd, branch, useWorktree, ticket?.key, onProgress, ticket === void 0 ? draft : void 0);
17844
18210
  if (ticket !== void 0) assignJiraTicket(ticket.key).catch((reason) => {
17845
18211
  console.warn(`dsh-claude: could not assign ${ticket.key}: ${reason instanceof Error ? reason.message : String(reason)}`);
17846
18212
  });
@@ -17852,13 +18218,13 @@ window.__ModuleLoader__.load({
17852
18218
  onProgress("creating-workspace");
17853
18219
  const workspace = await workspaces.create({ path: prepared.path });
17854
18220
  onProgress("starting-session");
17855
- const targetSessionId = await workspaces.connectWorkspace(workspace.workspaceId);
18221
+ const targetSessionId = await connectWorkspace(workspace.workspaceId);
17856
18222
  const targetScope = sessions.scope(targetSessionId);
17857
18223
  if (targetScope === void 0) throw new Error(t("repositorySessionUnavailable"));
17858
18224
  const presetResponse = await remote.agentPresets.select(targetSessionId, "claude");
17859
18225
  if (!presetResponse.ok) throw new Error(presetResponse.error.message);
17860
- sessions.noteAgentPreset(targetSessionId, presetResponse.value);
17861
- const targetInput = conversation.input.for(targetScope);
18226
+ sessions.noteAgentPreset?.(targetSessionId, presetResponse.value);
18227
+ const targetInput = sessionInput(conversation, targetScope);
17862
18228
  onProgress("transferring-draft");
17863
18229
  if (imageIds.length > 0 && !targetInput.addImages(imageIds)) throw new Error(t("repositoryDraftTransferFailed"));
17864
18230
  if (draft !== "") targetInput.setDraft(draft);
@@ -17872,7 +18238,7 @@ window.__ModuleLoader__.load({
17872
18238
  prepareMany: async (cwd, branch, tickets, onProgress) => {
17873
18239
  const sourceScope = sessions.scope(sourceSessionId);
17874
18240
  if (sourceScope === void 0) throw new Error(t("repositorySessionUnavailable"));
17875
- const sourceInput = conversation.input.for(sourceScope);
18241
+ const sourceInput = sessionInput(conversation, sourceScope);
17876
18242
  const rawDraft = sourceInput.state.getSnapshot().draft;
17877
18243
  const failures = [];
17878
18244
  for (const [index, ticket] of tickets.entries()) {
@@ -17893,13 +18259,13 @@ window.__ModuleLoader__.load({
17893
18259
  report("creating-workspace");
17894
18260
  const workspace = await workspaces.create({ path: prepared.path });
17895
18261
  report("starting-session");
17896
- const targetSessionId = await workspaces.connectWorkspace(workspace.workspaceId);
18262
+ const targetSessionId = await connectWorkspace(workspace.workspaceId);
17897
18263
  const targetScope = sessions.scope(targetSessionId);
17898
18264
  if (targetScope === void 0) throw new Error(t("repositorySessionUnavailable"));
17899
18265
  const presetResponse = await remote.agentPresets.select(targetSessionId, "claude");
17900
18266
  if (!presetResponse.ok) throw new Error(presetResponse.error.message);
17901
- sessions.noteAgentPreset(targetSessionId, presetResponse.value);
17902
- const targetInput = conversation.input.for(targetScope);
18267
+ sessions.noteAgentPreset?.(targetSessionId, presetResponse.value);
18268
+ const targetInput = sessionInput(conversation, targetScope);
17903
18269
  report("transferring-draft");
17904
18270
  targetInput.setDraft(rawDraft.trim() === "" ? ticketPrompt(ticket) : `${rawDraft.trimEnd()}\n\n${ticketContext(ticket)}`);
17905
18271
  bindLease(prepared.leaseId, targetSessionId);