@norman-else/dsh-claude 0.1.35 → 0.1.36
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/README.md +196 -195
- package/lib/bin.mjs +1 -1
- package/lib/client.d.ts +9 -0
- package/lib/client.js +1866 -1595
- package/lib/client.js.map +1 -1
- package/lib/{events-Doid-tq7.mjs → events-OhBoFNKO.mjs} +7 -2
- package/lib/events-OhBoFNKO.mjs.map +1 -0
- package/lib/index.d.mts +5 -0
- package/lib/index.mjs +80 -7
- package/lib/index.mjs.map +1 -1
- package/lib/{presenters-CXtE8qve.mjs → presenters-BBoM1Ju1.mjs} +2 -2
- package/lib/{presenters-CXtE8qve.mjs.map → presenters-BBoM1Ju1.mjs.map} +1 -1
- package/lib/{preset-installer-BMyKr5eQ.mjs → preset-installer-loenwnLS.mjs} +2 -2
- package/lib/{preset-installer-BMyKr5eQ.mjs.map → preset-installer-loenwnLS.mjs.map} +1 -1
- package/lib/preset-route.mjs +2 -2
- package/package.json +1 -1
- package/lib/events-Doid-tq7.mjs.map +0 -1
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,1079 +3429,718 @@ window.__ModuleLoader__.load({
|
|
|
3352
3429
|
lineHeight: "20px"
|
|
3353
3430
|
};
|
|
3354
3431
|
//#endregion
|
|
3355
|
-
//#region src/client/
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
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
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
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
|
-
|
|
3378
|
-
|
|
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}`,
|
|
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
|
-
|
|
3381
|
-
|
|
3682
|
+
/** Attach the sheet, keeping whatever mode is already set. Idempotent. */
|
|
3683
|
+
function ensureClaudeMarkdownTheme() {
|
|
3684
|
+
write();
|
|
3382
3685
|
}
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
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();
|
|
3691
|
+
}
|
|
3692
|
+
//#endregion
|
|
3693
|
+
//#region src/client/markdown-labels.tsx
|
|
3694
|
+
/** Stable across renders: MarkdownText keys its streaming renderer on this
|
|
3695
|
+
* object's identity and reparses from scratch whenever it changes. */
|
|
3696
|
+
function useClaudeMarkdownLabels(t) {
|
|
3697
|
+
return useMemo(() => ({
|
|
3698
|
+
code: {
|
|
3699
|
+
copyLabel: t("markdownCopy"),
|
|
3700
|
+
copiedLabel: t("markdownCopied")
|
|
3701
|
+
},
|
|
3702
|
+
footnotes: t("markdownFootnotes")
|
|
3703
|
+
}), [t]);
|
|
3704
|
+
}
|
|
3705
|
+
/** MarkdownText with the labels the running Host demands.
|
|
3706
|
+
*
|
|
3707
|
+
* The published primitives package still types the old `codeLabels` prop, so
|
|
3708
|
+
* the new shape cannot typecheck against it — the cast is confined here rather
|
|
3709
|
+
* than repeated at every call site. Drop it once the installed
|
|
3710
|
+
* @deepseek-ai/dsh-client-ui-primitives matches the Desktop build. */
|
|
3711
|
+
function ClaudeMarkdown({ text, labels, streaming }) {
|
|
3712
|
+
const props = {
|
|
3713
|
+
text,
|
|
3714
|
+
labels,
|
|
3715
|
+
...streaming === void 0 ? {} : { streaming }
|
|
3394
3716
|
};
|
|
3717
|
+
const Renderer = MarkdownText;
|
|
3718
|
+
ensureClaudeMarkdownTheme();
|
|
3719
|
+
return /* @__PURE__ */ jsx("div", {
|
|
3720
|
+
className: CLAUDE_MARKDOWN_SCOPE,
|
|
3721
|
+
children: /* @__PURE__ */ jsx(Renderer, { ...props })
|
|
3722
|
+
});
|
|
3395
3723
|
}
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3724
|
+
//#endregion
|
|
3725
|
+
//#region src/plugin-budget.ts
|
|
3726
|
+
/**
|
|
3727
|
+
* The plugin's connection budget, in one table.
|
|
3728
|
+
*
|
|
3729
|
+
* A browser opens a small fixed number of connections to one origin — six for
|
|
3730
|
+
* HTTP/1.1 in Chromium — and shares them with the Host's own traffic. Every
|
|
3731
|
+
* response this plugin holds open costs one of them for its lifetime, and a
|
|
3732
|
+
* request that cannot get one waits in the browser's queue where no server-side
|
|
3733
|
+
* deadline can reach it. When the pool is exhausted the panels that would
|
|
3734
|
+
* *diagnose* the problem are the first thing to stop answering, which is how
|
|
3735
|
+
* this failure has always presented: four settings cards timing out at once
|
|
3736
|
+
* against a Host that is demonstrably healthy.
|
|
3737
|
+
*
|
|
3738
|
+
* So the budget is a fixed constant rather than a function of how much work is
|
|
3739
|
+
* in flight. Steady state is one connection (the multiplexed projection
|
|
3740
|
+
* carrier); the peak is `PLUGIN_GLOBAL_PERMITS`, whatever the session count.
|
|
3741
|
+
*
|
|
3742
|
+
* Both halves read this file, which is the point: a route declares a budget
|
|
3743
|
+
* class and the client derives its wait from the same entry, so a server
|
|
3744
|
+
* deadline can never be quietly longer than the client's patience.
|
|
3745
|
+
*/
|
|
3746
|
+
/** Server-side budget classes. A route declares a class, never a number. */
|
|
3747
|
+
const ROUTE_BUDGET_MS = {
|
|
3748
|
+
/** Answers from memory or a single bounded probe. */
|
|
3749
|
+
fast: 5e3,
|
|
3750
|
+
/** Chains local Git work. */
|
|
3751
|
+
git: 45e3,
|
|
3752
|
+
/** Reaches the network: remote Git, `gh`, the npm registry. */
|
|
3753
|
+
remote: 15e4
|
|
3754
|
+
};
|
|
3755
|
+
/** The client waits one round trip longer, so the route's own 504 wins the
|
|
3756
|
+
* race and the caller learns which budget elapsed instead of guessing. */
|
|
3757
|
+
const CLIENT_GRACE_MS = 3e3;
|
|
3758
|
+
function clientBudgetMs(budget) {
|
|
3759
|
+
return ROUTE_BUDGET_MS[budget] + CLIENT_GRACE_MS;
|
|
3401
3760
|
}
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3761
|
+
/** A request that never got a permit fails fast and says so, rather than
|
|
3762
|
+
* spending its whole budget queued behind work it cannot see. */
|
|
3763
|
+
const QUEUE_WAIT_BUDGET_MS = 4e3;
|
|
3764
|
+
/** Per-lane ceilings inside the global budget.
|
|
3765
|
+
*
|
|
3766
|
+
* The projection carrier holds a permanently reserved permit outside these,
|
|
3767
|
+
* so three remain. `write + stream <= 2` leaves one permit that only a read
|
|
3768
|
+
* can take: a diagnostic read always has somewhere to go, however many slow
|
|
3769
|
+
* actions are in flight. That invariant is what stops a 150s repository
|
|
3770
|
+
* action from reproducing the original symptom through a new mechanism. */
|
|
3771
|
+
const PLUGIN_LANE_CAPS = {
|
|
3772
|
+
read: 2,
|
|
3773
|
+
write: 1,
|
|
3774
|
+
stream: 1
|
|
3775
|
+
};
|
|
3776
|
+
//#endregion
|
|
3777
|
+
//#region src/rewind.ts
|
|
3778
|
+
function isRewound(ranges, seq) {
|
|
3779
|
+
return ranges.some((range) => seq >= range.start && seq <= range.end);
|
|
3408
3780
|
}
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3781
|
+
//#endregion
|
|
3782
|
+
//#region src/client/plugin-transport.ts
|
|
3783
|
+
/**
|
|
3784
|
+
* The plugin's only door to the network.
|
|
3785
|
+
*
|
|
3786
|
+
* Every request the Client makes goes through here, and the shape of the
|
|
3787
|
+
* public functions is the point. There is no `signal` property, no `headers`,
|
|
3788
|
+
* no `RequestInit` and no millisecond number in any parameter: a caller cannot
|
|
3789
|
+
* hand this module an options object whose later spread silently overwrites
|
|
3790
|
+
* the deadline sitting above it. That is not a hypothetical — it is exactly
|
|
3791
|
+
* how four call sites lost their deadlines while looking completely ordinary,
|
|
3792
|
+
* because `exactOptionalPropertyTypes` forbids writing `signal: undefined` and
|
|
3793
|
+
* the spread idiom is what people reach for instead. `cancel` is positional
|
|
3794
|
+
* here, so `undefined` is simply passable and the idiom has nothing to do.
|
|
3795
|
+
*
|
|
3796
|
+
* The second seal is arithmetic. The browser shares a small fixed connection
|
|
3797
|
+
* budget between this plugin and the Host, and the plugin used to spend it
|
|
3798
|
+
* proportionally to how many Claude sessions existed. Here every request takes
|
|
3799
|
+
* a permit from a fixed pool first, so more call sites and more sessions
|
|
3800
|
+
* cannot become more sockets — a saturated plugin queues, and says `starved`
|
|
3801
|
+
* within `QUEUE_WAIT_BUDGET_MS` instead of dying silently at its full budget.
|
|
3802
|
+
*
|
|
3803
|
+
* Lane caps guarantee a read always has somewhere to go: the projection
|
|
3804
|
+
* carrier holds a reserved permit, and `write + stream` can occupy at most two
|
|
3805
|
+
* of the remaining three. The panel that diagnoses a saturated pool is
|
|
3806
|
+
* therefore the one request that cannot be starved by it.
|
|
3807
|
+
*/
|
|
3808
|
+
var PluginRequestError = class extends Error {
|
|
3809
|
+
reason;
|
|
3810
|
+
status;
|
|
3811
|
+
code;
|
|
3812
|
+
constructor(reason, message, status, code) {
|
|
3813
|
+
super(message);
|
|
3814
|
+
this.name = "PluginRequestError";
|
|
3815
|
+
this.reason = reason;
|
|
3816
|
+
if (status !== void 0) this.status = status;
|
|
3817
|
+
if (code !== void 0) this.code = code;
|
|
3818
|
+
}
|
|
3819
|
+
};
|
|
3820
|
+
let send = (...args) => fetch(...args);
|
|
3821
|
+
let held = 0;
|
|
3822
|
+
const laneHeld = {
|
|
3823
|
+
read: 0,
|
|
3824
|
+
write: 0,
|
|
3825
|
+
stream: 0
|
|
3826
|
+
};
|
|
3827
|
+
let projectionHeld = false;
|
|
3828
|
+
let queue = [];
|
|
3829
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
3830
|
+
/** The projection carrier owns a permit of its own, outside the lane caps, so
|
|
3831
|
+
* the transcript stream and the panels never compete for the same slot. */
|
|
3832
|
+
function laneHasRoom(lane) {
|
|
3833
|
+
return held < 4 && laneHeld[lane] < PLUGIN_LANE_CAPS[lane];
|
|
3418
3834
|
}
|
|
3419
|
-
function
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3835
|
+
function pump() {
|
|
3836
|
+
for (let index = 0; index < queue.length; index += 1) {
|
|
3837
|
+
const waiter = queue[index];
|
|
3838
|
+
if (waiter === void 0 || !laneHasRoom(waiter.lane)) continue;
|
|
3839
|
+
queue.splice(index, 1);
|
|
3840
|
+
index -= 1;
|
|
3841
|
+
clearTimeout(waiter.timer);
|
|
3842
|
+
held += 1;
|
|
3843
|
+
laneHeld[waiter.lane] += 1;
|
|
3844
|
+
waiter.admit();
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
function release(lane) {
|
|
3848
|
+
held -= 1;
|
|
3849
|
+
laneHeld[lane] -= 1;
|
|
3850
|
+
pump();
|
|
3851
|
+
}
|
|
3852
|
+
function acquire(lane) {
|
|
3853
|
+
let released = false;
|
|
3854
|
+
const releaseOnce = () => {
|
|
3855
|
+
if (released) return;
|
|
3856
|
+
released = true;
|
|
3857
|
+
release(lane);
|
|
3858
|
+
};
|
|
3859
|
+
if (laneHasRoom(lane)) {
|
|
3860
|
+
held += 1;
|
|
3861
|
+
laneHeld[lane] += 1;
|
|
3862
|
+
return Promise.resolve(releaseOnce);
|
|
3863
|
+
}
|
|
3864
|
+
return new Promise((resolve, reject) => {
|
|
3865
|
+
const waiter = {
|
|
3866
|
+
lane,
|
|
3867
|
+
admit: () => resolve(releaseOnce),
|
|
3868
|
+
reject,
|
|
3869
|
+
timer: setTimeout(() => {
|
|
3870
|
+
queue = queue.filter((item) => item !== waiter);
|
|
3871
|
+
reject(new PluginRequestError("starved", "The plugin is holding every connection it is allowed to open."));
|
|
3872
|
+
}, QUEUE_WAIT_BUDGET_MS)
|
|
3873
|
+
};
|
|
3874
|
+
queue.push(waiter);
|
|
3449
3875
|
});
|
|
3450
3876
|
}
|
|
3451
|
-
function
|
|
3452
|
-
|
|
3453
|
-
const
|
|
3454
|
-
|
|
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
|
-
});
|
|
3877
|
+
function withQuery(path, query) {
|
|
3878
|
+
if (query === void 0) return path;
|
|
3879
|
+
const encoded = new URLSearchParams(query).toString();
|
|
3880
|
+
return encoded.length === 0 ? path : `${path}?${encoded}`;
|
|
3517
3881
|
}
|
|
3518
|
-
function
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3882
|
+
function failureOf(error, cancel) {
|
|
3883
|
+
if (error instanceof PluginRequestError) return error;
|
|
3884
|
+
if (cancel?.aborted === true) return new PluginRequestError("cancelled", "The caller cancelled the request.");
|
|
3885
|
+
const name = error instanceof Error ? error.name : "";
|
|
3886
|
+
if (name === "TimeoutError" || name === "AbortError") return new PluginRequestError("timeout", "The plugin route did not answer inside its budget.");
|
|
3887
|
+
return new PluginRequestError("http", error instanceof Error ? error.message : String(error));
|
|
3888
|
+
}
|
|
3889
|
+
async function decode(response) {
|
|
3890
|
+
let payload;
|
|
3891
|
+
try {
|
|
3892
|
+
payload = await response.json();
|
|
3893
|
+
} catch {
|
|
3894
|
+
if (response.ok) throw new PluginRequestError("shape", "The plugin route answered with a body this build cannot read.");
|
|
3895
|
+
throw new PluginRequestError("http", `HTTP ${response.status}`, response.status);
|
|
3896
|
+
}
|
|
3897
|
+
if (response.ok) return payload;
|
|
3898
|
+
if (response.status === 404) throw new PluginRequestError("route-missing", "The Host is running an older plugin build without this route.", 404);
|
|
3899
|
+
const record = typeof payload === "object" && payload !== null ? payload : void 0;
|
|
3900
|
+
const message = typeof record?.message === "string" ? record.message : typeof record?.error === "string" ? record.error : `HTTP ${response.status}`;
|
|
3901
|
+
const code = typeof record?.error === "string" ? record.error : void 0;
|
|
3902
|
+
throw new PluginRequestError("http", message, response.status, code);
|
|
3903
|
+
}
|
|
3904
|
+
async function dispatch(lane, method, path, budget, cancel, options) {
|
|
3905
|
+
const url = withQuery(path, options?.query);
|
|
3906
|
+
const key = options?.key ?? `${method} ${url}`;
|
|
3907
|
+
const existing = inFlight.get(key);
|
|
3908
|
+
if (existing !== void 0) return await existing;
|
|
3909
|
+
const run = (async () => {
|
|
3910
|
+
const free = await acquire(lane);
|
|
3911
|
+
try {
|
|
3912
|
+
const timeout = AbortSignal.timeout(clientBudgetMs(budget));
|
|
3913
|
+
const signal = cancel === void 0 ? timeout : AbortSignal.any([cancel, timeout]);
|
|
3914
|
+
return await decode(await send(url, {
|
|
3915
|
+
method,
|
|
3916
|
+
credentials: "same-origin",
|
|
3917
|
+
signal,
|
|
3918
|
+
headers: options?.json === void 0 ? { accept: "application/json" } : {
|
|
3919
|
+
accept: "application/json",
|
|
3920
|
+
"content-type": "application/json"
|
|
3538
3921
|
},
|
|
3539
|
-
|
|
3540
|
-
})
|
|
3541
|
-
}
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3922
|
+
...options?.json === void 0 ? {} : { body: JSON.stringify(options.json) }
|
|
3923
|
+
}));
|
|
3924
|
+
} catch (error) {
|
|
3925
|
+
throw failureOf(error, cancel);
|
|
3926
|
+
} finally {
|
|
3927
|
+
free();
|
|
3928
|
+
}
|
|
3929
|
+
})();
|
|
3930
|
+
inFlight.set(key, run);
|
|
3931
|
+
try {
|
|
3932
|
+
return await run;
|
|
3933
|
+
} finally {
|
|
3934
|
+
if (inFlight.get(key) === run) inFlight.delete(key);
|
|
3935
|
+
}
|
|
3548
3936
|
}
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
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
|
-
]
|
|
3937
|
+
/** A bounded read of a plugin route. */
|
|
3938
|
+
function pluginRead(path, budget, cancel, options) {
|
|
3939
|
+
return dispatch("read", "GET", path, budget, cancel, options);
|
|
3940
|
+
}
|
|
3941
|
+
/** A bounded write. Writes never coalesce by default: two of them are two
|
|
3942
|
+
* intents, even when their bodies match. */
|
|
3943
|
+
function pluginWrite(path, budget, cancel, options) {
|
|
3944
|
+
const method = options?.method ?? "POST";
|
|
3945
|
+
return dispatch("write", method, path, budget, cancel, {
|
|
3946
|
+
...options,
|
|
3947
|
+
key: options?.key ?? `${method} ${withQuery(path, options?.query)} #${nextWriteId()}`
|
|
3630
3948
|
});
|
|
3631
3949
|
}
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
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
|
-
};
|
|
3950
|
+
let writeId = 0;
|
|
3951
|
+
function nextWriteId() {
|
|
3952
|
+
writeId += 1;
|
|
3953
|
+
return writeId;
|
|
3648
3954
|
}
|
|
3649
|
-
function
|
|
3650
|
-
const
|
|
3651
|
-
const
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
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();
|
|
3955
|
+
async function openStream(lane, path, cancel, options, reserved) {
|
|
3956
|
+
const url = withQuery(path, options?.query);
|
|
3957
|
+
const free = reserved ? () => {
|
|
3958
|
+
projectionHeld = false;
|
|
3959
|
+
} : await acquire(lane);
|
|
3960
|
+
try {
|
|
3961
|
+
const response = await send(url, {
|
|
3962
|
+
method: options?.method ?? "GET",
|
|
3963
|
+
credentials: "same-origin",
|
|
3964
|
+
signal: cancel,
|
|
3965
|
+
headers: options?.json === void 0 ? { accept: "application/x-ndjson" } : {
|
|
3966
|
+
accept: "application/x-ndjson",
|
|
3967
|
+
"content-type": "application/json"
|
|
3686
3968
|
},
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
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
|
-
});
|
|
3969
|
+
...options?.json === void 0 ? {} : { body: JSON.stringify(options.json) }
|
|
3970
|
+
});
|
|
3971
|
+
if (!response.ok || response.body === null) {
|
|
3972
|
+
free();
|
|
3973
|
+
if (response.status === 404) throw new PluginRequestError("route-missing", "The Host is running an older plugin build without this route.", 404);
|
|
3974
|
+
throw new PluginRequestError("http", `HTTP ${response.status}`, response.status);
|
|
3975
|
+
}
|
|
3976
|
+
cancel.addEventListener("abort", free, { once: true });
|
|
3977
|
+
return response.body.getReader();
|
|
3978
|
+
} catch (error) {
|
|
3979
|
+
free();
|
|
3980
|
+
throw failureOf(error, cancel);
|
|
3981
|
+
}
|
|
3756
3982
|
}
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
t,
|
|
3763
|
-
openTasks
|
|
3764
|
-
});
|
|
3983
|
+
/** A long-lived NDJSON response. No deadline — it is meant to stay open — but
|
|
3984
|
+
* it takes a counted permit for its whole life, which is the bound that
|
|
3985
|
+
* matters. */
|
|
3986
|
+
function pluginNdjson(path, cancel, options) {
|
|
3987
|
+
return openStream("stream", path, cancel, options, false);
|
|
3765
3988
|
}
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3989
|
+
/** The reserved projection carrier: exactly one live connection, ever,
|
|
3990
|
+
* whatever the session count. */
|
|
3991
|
+
function pluginProjectionStream(path, cancel, options) {
|
|
3992
|
+
if (projectionHeld) return Promise.reject(new PluginRequestError("starved", "The projection carrier is already open."));
|
|
3993
|
+
projectionHeld = true;
|
|
3994
|
+
return openStream("stream", path, cancel, options, true);
|
|
3995
|
+
}
|
|
3996
|
+
/** Fire-and-forget diagnostics. Dropped rather than queued when saturated:
|
|
3997
|
+
* the channel that reports the plugin's own failures must never be the
|
|
3998
|
+
* traffic that causes them. */
|
|
3999
|
+
function pluginBeacon(path, body) {
|
|
4000
|
+
if (!laneHasRoom("write")) return;
|
|
4001
|
+
pluginWrite(path, "fast", void 0, { json: body }).catch(() => void 0);
|
|
3778
4002
|
}
|
|
3779
4003
|
//#endregion
|
|
3780
|
-
//#region src/client/
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
*
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
*
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
*
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
*
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
const
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
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("");
|
|
4004
|
+
//#region src/client/projection.ts
|
|
4005
|
+
const EMPTY_CLAUDE_PROJECTION = {
|
|
4006
|
+
schemaVersion: 1,
|
|
4007
|
+
revision: 0,
|
|
4008
|
+
owned: false,
|
|
4009
|
+
commands: [],
|
|
4010
|
+
activities: []
|
|
4011
|
+
};
|
|
4012
|
+
const RETRY_DELAY_MS = 2e3;
|
|
4013
|
+
/** Floor between carrier reopens forced by a desync. A carrier that is losing
|
|
4014
|
+
* lines must not be answered with a reconnect per lost line. */
|
|
4015
|
+
const RESYNC_COOLDOWN_MS = 5e3;
|
|
4016
|
+
/** Wait for the subscribed set to stop moving before reopening the carrier:
|
|
4017
|
+
* mounting a session list changes it once per row. */
|
|
4018
|
+
const SUBSCRIPTION_SETTLE_MS = 250;
|
|
4019
|
+
const NDJSON_SEPARATOR = String.fromCharCode(10);
|
|
4020
|
+
/** Coalesce stream deltas into at most one React notification per frame. */
|
|
4021
|
+
const FRAME_MS = 16;
|
|
4022
|
+
/** Typewriter smoothing: drain newly arrived prose over roughly this window,
|
|
4023
|
+
* so the CLI's paragraph-sized deltas read as a continuous character flow. */
|
|
4024
|
+
const REVEAL_WINDOW_MS = 1200;
|
|
4025
|
+
/** A burst larger than this (redaction rewrite, reconnect catch-up) shows
|
|
4026
|
+
* instantly instead of animating for a long stretch. */
|
|
4027
|
+
const MAX_INSTANT_REVEAL = 4e3;
|
|
4028
|
+
const MAX_ACTIVITIES = 1e4;
|
|
4029
|
+
const MAX_COMMANDS = 2e3;
|
|
4030
|
+
const MAX_REPOSITORY_TEXT_CHARS = 1024;
|
|
4031
|
+
const MAX_DIFF_CHARS = 262144;
|
|
4032
|
+
const MAX_REVIEW_COMMENTS = 50;
|
|
4033
|
+
const MAX_REVIEW_COMMENT_CHARS = 2e3;
|
|
4034
|
+
const MAX_TRANSCRIPT_CHARS = 64e3;
|
|
4035
|
+
function record$7(value) {
|
|
4036
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
3950
4037
|
}
|
|
3951
|
-
|
|
3952
|
-
|
|
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);
|
|
4038
|
+
function nonNegativeInteger(value) {
|
|
4039
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
3975
4040
|
}
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
/** Stable across renders: MarkdownText keys its streaming renderer on this
|
|
3979
|
-
* object's identity and reparses from scratch whenever it changes. */
|
|
3980
|
-
function useClaudeMarkdownLabels(t) {
|
|
3981
|
-
return useMemo(() => ({
|
|
3982
|
-
code: {
|
|
3983
|
-
copyLabel: t("markdownCopy"),
|
|
3984
|
-
copiedLabel: t("markdownCopied")
|
|
3985
|
-
},
|
|
3986
|
-
footnotes: t("markdownFootnotes")
|
|
3987
|
-
}), [t]);
|
|
4041
|
+
function optionalBoundedString(value) {
|
|
4042
|
+
return value === void 0 || typeof value === "string" && value.length <= MAX_REPOSITORY_TEXT_CHARS;
|
|
3988
4043
|
}
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
const
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4044
|
+
function validateRepository(value) {
|
|
4045
|
+
const repository = record$7(value);
|
|
4046
|
+
if (repository === void 0 || ![
|
|
4047
|
+
"ready",
|
|
4048
|
+
"not-repository",
|
|
4049
|
+
"unavailable"
|
|
4050
|
+
].includes(String(repository.status)) || typeof repository.cwd !== "string" || repository.cwd.length > MAX_REPOSITORY_TEXT_CHARS || !optionalBoundedString(repository.root) || !optionalBoundedString(repository.branch) || !optionalBoundedString(repository.remote) || repository.detached !== void 0 && typeof repository.detached !== "boolean" || repository.worktree !== void 0 && typeof repository.worktree !== "boolean" || repository.dirty !== void 0 && typeof repository.dirty !== "boolean" || repository.upstream !== void 0 && typeof repository.upstream !== "boolean" || repository.ahead !== void 0 && !nonNegativeInteger(repository.ahead) || repository.behind !== void 0 && !nonNegativeInteger(repository.behind)) return false;
|
|
4051
|
+
if (repository.diff !== void 0) {
|
|
4052
|
+
const diff = record$7(repository.diff);
|
|
4053
|
+
if (diff === void 0 || !nonNegativeInteger(diff.additions) || !nonNegativeInteger(diff.deletions) || !nonNegativeInteger(diff.files) || typeof diff.truncated !== "boolean" || diff.patch !== void 0 && (typeof diff.patch !== "string" || diff.patch.length > MAX_DIFF_CHARS)) return false;
|
|
4054
|
+
}
|
|
4055
|
+
if (repository.pullRequest === void 0) return true;
|
|
4056
|
+
const pullRequest = record$7(repository.pullRequest);
|
|
4057
|
+
if (pullRequest === void 0 || !Number.isSafeInteger(pullRequest.number) || Number(pullRequest.number) <= 0 || typeof pullRequest.title !== "string" || pullRequest.title.length > MAX_REPOSITORY_TEXT_CHARS || typeof pullRequest.url !== "string" || pullRequest.url.length > MAX_REPOSITORY_TEXT_CHARS || ![
|
|
4058
|
+
"open",
|
|
4059
|
+
"closed",
|
|
4060
|
+
"merged"
|
|
4061
|
+
].includes(String(pullRequest.state)) || typeof pullRequest.draft !== "boolean" || ![
|
|
4062
|
+
"approved",
|
|
4063
|
+
"changes-requested",
|
|
4064
|
+
"review-required",
|
|
4065
|
+
"none"
|
|
4066
|
+
].includes(String(pullRequest.review)) || ![
|
|
4067
|
+
"passing",
|
|
4068
|
+
"pending",
|
|
4069
|
+
"failing",
|
|
4070
|
+
"none"
|
|
4071
|
+
].includes(String(pullRequest.checks)) || !optionalBoundedString(pullRequest.mergeState) || !optionalBoundedString(pullRequest.author) || !optionalBoundedString(pullRequest.baseBranch) || pullRequest.createdAt !== void 0 && (typeof pullRequest.createdAt !== "string" || !Number.isFinite(Date.parse(pullRequest.createdAt))) || pullRequest.mergedAt !== void 0 && (typeof pullRequest.mergedAt !== "string" || !Number.isFinite(Date.parse(pullRequest.mergedAt)))) return false;
|
|
4072
|
+
try {
|
|
4073
|
+
const url = new URL(pullRequest.url);
|
|
4074
|
+
return url.protocol === "https:" && url.hostname === "github.com";
|
|
4075
|
+
} catch {
|
|
4076
|
+
return false;
|
|
4077
|
+
}
|
|
4078
|
+
}
|
|
4079
|
+
/** Validate the public route envelope before publishing it to UI components. */
|
|
4080
|
+
function parseClaudeClientProjection(value) {
|
|
4081
|
+
const input = record$7(value);
|
|
4082
|
+
if (input === void 0 || input.schemaVersion !== 1 || !nonNegativeInteger(input.revision) || typeof input.owned !== "boolean" || !Array.isArray(input.commands) || input.commands.length > MAX_COMMANDS || !Array.isArray(input.activities) || input.activities.length > MAX_ACTIVITIES) throw new Error("invalid Claude sidecar projection");
|
|
4083
|
+
for (const item of input.commands) {
|
|
4084
|
+
const command = record$7(item);
|
|
4085
|
+
if (command === void 0 || typeof command.publicName !== "string" || typeof command.claudeName !== "string" || typeof command.description !== "string" || command.hint !== void 0 && typeof command.hint !== "string" || typeof command.prefixed !== "boolean") throw new Error("invalid Claude command projection");
|
|
4086
|
+
}
|
|
4087
|
+
for (const item of input.activities) {
|
|
4088
|
+
const activity = record$7(item);
|
|
4089
|
+
if (activity === void 0 || !nonNegativeInteger(activity.turn) || !nonNegativeInteger(activity.step) || !nonNegativeInteger(activity.ordinal) || typeof activity.kind !== "string") throw new Error("invalid Claude sidecar activity");
|
|
4090
|
+
}
|
|
4091
|
+
if (input.contextUsage !== void 0 && record$7(input.contextUsage) === void 0) throw new Error("invalid Claude context projection");
|
|
4092
|
+
const tasks = input.tasks === void 0 ? void 0 : record$7(input.tasks);
|
|
4093
|
+
if (tasks !== void 0 && !Array.isArray(tasks.tasks)) throw new Error("invalid Claude tasks projection");
|
|
4094
|
+
if (input.repository !== void 0 && !validateRepository(input.repository)) throw new Error("invalid Claude repository projection");
|
|
4095
|
+
if (input.reviewComments !== void 0) {
|
|
4096
|
+
if (!Array.isArray(input.reviewComments) || input.reviewComments.length > MAX_REVIEW_COMMENTS) throw new Error("invalid Claude review comment projection");
|
|
4097
|
+
for (const item of input.reviewComments) {
|
|
4098
|
+
const comment = record$7(item);
|
|
4099
|
+
if (comment === void 0 || typeof comment.id !== "string" || comment.id.length === 0 || comment.id.length > 128 || typeof comment.path !== "string" || comment.path.length === 0 || comment.path.length > MAX_REPOSITORY_TEXT_CHARS || !nonNegativeInteger(comment.line) || comment.side !== "old" && comment.side !== "new" || typeof comment.text !== "string" || comment.text.length > MAX_REVIEW_COMMENT_CHARS) throw new Error("invalid Claude review comment projection");
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
if (input.rewind !== void 0) {
|
|
4103
|
+
const ranges = record$7(input.rewind)?.ranges;
|
|
4104
|
+
if (!Array.isArray(ranges) || ranges.length > 200) throw new Error("invalid Claude rewind projection");
|
|
4105
|
+
for (const item of ranges) {
|
|
4106
|
+
const range = record$7(item);
|
|
4107
|
+
if (range === void 0 || !nonNegativeInteger(range.start) || !nonNegativeInteger(range.end)) throw new Error("invalid Claude rewind projection");
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
return input;
|
|
4111
|
+
}
|
|
4112
|
+
/** Validate one incremental delta payload with the same rules as a snapshot. */
|
|
4113
|
+
function validateEnvelopeFragment(fragment) {
|
|
4114
|
+
parseClaudeClientProjection({
|
|
4115
|
+
schemaVersion: 1,
|
|
4116
|
+
revision: 0,
|
|
4117
|
+
owned: false,
|
|
4118
|
+
commands: [],
|
|
4119
|
+
activities: [],
|
|
4120
|
+
...fragment
|
|
4006
4121
|
});
|
|
4007
4122
|
}
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
*
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
*/
|
|
4030
|
-
/** Server-side budget classes. A route declares a class, never a number. */
|
|
4031
|
-
const ROUTE_BUDGET_MS = {
|
|
4032
|
-
/** Answers from memory or a single bounded probe. */
|
|
4033
|
-
fast: 5e3,
|
|
4034
|
-
/** Chains local Git work. */
|
|
4035
|
-
git: 45e3,
|
|
4036
|
-
/** Reaches the network: remote Git, `gh`, the npm registry. */
|
|
4037
|
-
remote: 15e4
|
|
4038
|
-
};
|
|
4039
|
-
/** The client waits one round trip longer, so the route's own 504 wins the
|
|
4040
|
-
* race and the caller learns which budget elapsed instead of guessing. */
|
|
4041
|
-
const CLIENT_GRACE_MS = 3e3;
|
|
4042
|
-
function clientBudgetMs(budget) {
|
|
4043
|
-
return ROUTE_BUDGET_MS[budget] + CLIENT_GRACE_MS;
|
|
4044
|
-
}
|
|
4045
|
-
/** A request that never got a permit fails fast and says so, rather than
|
|
4046
|
-
* spending its whole budget queued behind work it cannot see. */
|
|
4047
|
-
const QUEUE_WAIT_BUDGET_MS = 4e3;
|
|
4048
|
-
/** Per-lane ceilings inside the global budget.
|
|
4049
|
-
*
|
|
4050
|
-
* The projection carrier holds a permanently reserved permit outside these,
|
|
4051
|
-
* so three remain. `write + stream <= 2` leaves one permit that only a read
|
|
4052
|
-
* can take: a diagnostic read always has somewhere to go, however many slow
|
|
4053
|
-
* actions are in flight. That invariant is what stops a 150s repository
|
|
4054
|
-
* action from reproducing the original symptom through a new mechanism. */
|
|
4055
|
-
const PLUGIN_LANE_CAPS = {
|
|
4056
|
-
read: 2,
|
|
4057
|
-
write: 1,
|
|
4058
|
-
stream: 1
|
|
4059
|
-
};
|
|
4060
|
-
//#endregion
|
|
4061
|
-
//#region src/rewind.ts
|
|
4062
|
-
function isRewound(ranges, seq) {
|
|
4063
|
-
return ranges.some((range) => seq >= range.start && seq <= range.end);
|
|
4064
|
-
}
|
|
4065
|
-
//#endregion
|
|
4066
|
-
//#region src/client/plugin-transport.ts
|
|
4067
|
-
/**
|
|
4068
|
-
* The plugin's only door to the network.
|
|
4069
|
-
*
|
|
4070
|
-
* Every request the Client makes goes through here, and the shape of the
|
|
4071
|
-
* public functions is the point. There is no `signal` property, no `headers`,
|
|
4072
|
-
* no `RequestInit` and no millisecond number in any parameter: a caller cannot
|
|
4073
|
-
* hand this module an options object whose later spread silently overwrites
|
|
4074
|
-
* the deadline sitting above it. That is not a hypothetical — it is exactly
|
|
4075
|
-
* how four call sites lost their deadlines while looking completely ordinary,
|
|
4076
|
-
* because `exactOptionalPropertyTypes` forbids writing `signal: undefined` and
|
|
4077
|
-
* the spread idiom is what people reach for instead. `cancel` is positional
|
|
4078
|
-
* here, so `undefined` is simply passable and the idiom has nothing to do.
|
|
4079
|
-
*
|
|
4080
|
-
* The second seal is arithmetic. The browser shares a small fixed connection
|
|
4081
|
-
* budget between this plugin and the Host, and the plugin used to spend it
|
|
4082
|
-
* proportionally to how many Claude sessions existed. Here every request takes
|
|
4083
|
-
* a permit from a fixed pool first, so more call sites and more sessions
|
|
4084
|
-
* cannot become more sockets — a saturated plugin queues, and says `starved`
|
|
4085
|
-
* within `QUEUE_WAIT_BUDGET_MS` instead of dying silently at its full budget.
|
|
4086
|
-
*
|
|
4087
|
-
* Lane caps guarantee a read always has somewhere to go: the projection
|
|
4088
|
-
* carrier holds a reserved permit, and `write + stream` can occupy at most two
|
|
4089
|
-
* of the remaining three. The panel that diagnoses a saturated pool is
|
|
4090
|
-
* therefore the one request that cannot be starved by it.
|
|
4091
|
-
*/
|
|
4092
|
-
var PluginRequestError = class extends Error {
|
|
4093
|
-
reason;
|
|
4094
|
-
status;
|
|
4095
|
-
code;
|
|
4096
|
-
constructor(reason, message, status, code) {
|
|
4097
|
-
super(message);
|
|
4098
|
-
this.name = "PluginRequestError";
|
|
4099
|
-
this.reason = reason;
|
|
4100
|
-
if (status !== void 0) this.status = status;
|
|
4101
|
-
if (code !== void 0) this.code = code;
|
|
4102
|
-
}
|
|
4103
|
-
};
|
|
4104
|
-
let send = (...args) => fetch(...args);
|
|
4105
|
-
let held = 0;
|
|
4106
|
-
const laneHeld = {
|
|
4107
|
-
read: 0,
|
|
4108
|
-
write: 0,
|
|
4109
|
-
stream: 0
|
|
4110
|
-
};
|
|
4111
|
-
let projectionHeld = false;
|
|
4112
|
-
let queue = [];
|
|
4113
|
-
const inFlight = /* @__PURE__ */ new Map();
|
|
4114
|
-
/** The projection carrier owns a permit of its own, outside the lane caps, so
|
|
4115
|
-
* the transcript stream and the panels never compete for the same slot. */
|
|
4116
|
-
function laneHasRoom(lane) {
|
|
4117
|
-
return held < 4 && laneHeld[lane] < PLUGIN_LANE_CAPS[lane];
|
|
4118
|
-
}
|
|
4119
|
-
function pump() {
|
|
4120
|
-
for (let index = 0; index < queue.length; index += 1) {
|
|
4121
|
-
const waiter = queue[index];
|
|
4122
|
-
if (waiter === void 0 || !laneHasRoom(waiter.lane)) continue;
|
|
4123
|
-
queue.splice(index, 1);
|
|
4124
|
-
index -= 1;
|
|
4125
|
-
clearTimeout(waiter.timer);
|
|
4126
|
-
held += 1;
|
|
4127
|
-
laneHeld[waiter.lane] += 1;
|
|
4128
|
-
waiter.admit();
|
|
4129
|
-
}
|
|
4130
|
-
}
|
|
4131
|
-
function release(lane) {
|
|
4132
|
-
held -= 1;
|
|
4133
|
-
laneHeld[lane] -= 1;
|
|
4134
|
-
pump();
|
|
4135
|
-
}
|
|
4136
|
-
function acquire(lane) {
|
|
4137
|
-
let released = false;
|
|
4138
|
-
const releaseOnce = () => {
|
|
4139
|
-
if (released) return;
|
|
4140
|
-
released = true;
|
|
4141
|
-
release(lane);
|
|
4142
|
-
};
|
|
4143
|
-
if (laneHasRoom(lane)) {
|
|
4144
|
-
held += 1;
|
|
4145
|
-
laneHeld[lane] += 1;
|
|
4146
|
-
return Promise.resolve(releaseOnce);
|
|
4147
|
-
}
|
|
4148
|
-
return new Promise((resolve, reject) => {
|
|
4149
|
-
const waiter = {
|
|
4150
|
-
lane,
|
|
4151
|
-
admit: () => resolve(releaseOnce),
|
|
4152
|
-
reject,
|
|
4153
|
-
timer: setTimeout(() => {
|
|
4154
|
-
queue = queue.filter((item) => item !== waiter);
|
|
4155
|
-
reject(new PluginRequestError("starved", "The plugin is holding every connection it is allowed to open."));
|
|
4156
|
-
}, QUEUE_WAIT_BUDGET_MS)
|
|
4157
|
-
};
|
|
4158
|
-
queue.push(waiter);
|
|
4159
|
-
});
|
|
4160
|
-
}
|
|
4161
|
-
function withQuery(path, query) {
|
|
4162
|
-
if (query === void 0) return path;
|
|
4163
|
-
const encoded = new URLSearchParams(query).toString();
|
|
4164
|
-
return encoded.length === 0 ? path : `${path}?${encoded}`;
|
|
4165
|
-
}
|
|
4166
|
-
function failureOf(error, cancel) {
|
|
4167
|
-
if (error instanceof PluginRequestError) return error;
|
|
4168
|
-
if (cancel?.aborted === true) return new PluginRequestError("cancelled", "The caller cancelled the request.");
|
|
4169
|
-
const name = error instanceof Error ? error.name : "";
|
|
4170
|
-
if (name === "TimeoutError" || name === "AbortError") return new PluginRequestError("timeout", "The plugin route did not answer inside its budget.");
|
|
4171
|
-
return new PluginRequestError("http", error instanceof Error ? error.message : String(error));
|
|
4172
|
-
}
|
|
4173
|
-
async function decode(response) {
|
|
4174
|
-
let payload;
|
|
4175
|
-
try {
|
|
4176
|
-
payload = await response.json();
|
|
4177
|
-
} catch {
|
|
4178
|
-
if (response.ok) throw new PluginRequestError("shape", "The plugin route answered with a body this build cannot read.");
|
|
4179
|
-
throw new PluginRequestError("http", `HTTP ${response.status}`, response.status);
|
|
4180
|
-
}
|
|
4181
|
-
if (response.ok) return payload;
|
|
4182
|
-
if (response.status === 404) throw new PluginRequestError("route-missing", "The Host is running an older plugin build without this route.", 404);
|
|
4183
|
-
const record = typeof payload === "object" && payload !== null ? payload : void 0;
|
|
4184
|
-
const message = typeof record?.message === "string" ? record.message : typeof record?.error === "string" ? record.error : `HTTP ${response.status}`;
|
|
4185
|
-
const code = typeof record?.error === "string" ? record.error : void 0;
|
|
4186
|
-
throw new PluginRequestError("http", message, response.status, code);
|
|
4187
|
-
}
|
|
4188
|
-
async function dispatch(lane, method, path, budget, cancel, options) {
|
|
4189
|
-
const url = withQuery(path, options?.query);
|
|
4190
|
-
const key = options?.key ?? `${method} ${url}`;
|
|
4191
|
-
const existing = inFlight.get(key);
|
|
4192
|
-
if (existing !== void 0) return await existing;
|
|
4193
|
-
const run = (async () => {
|
|
4194
|
-
const free = await acquire(lane);
|
|
4195
|
-
try {
|
|
4196
|
-
const timeout = AbortSignal.timeout(clientBudgetMs(budget));
|
|
4197
|
-
const signal = cancel === void 0 ? timeout : AbortSignal.any([cancel, timeout]);
|
|
4198
|
-
return await decode(await send(url, {
|
|
4199
|
-
method,
|
|
4200
|
-
credentials: "same-origin",
|
|
4201
|
-
signal,
|
|
4202
|
-
headers: options?.json === void 0 ? { accept: "application/json" } : {
|
|
4203
|
-
accept: "application/json",
|
|
4204
|
-
"content-type": "application/json"
|
|
4205
|
-
},
|
|
4206
|
-
...options?.json === void 0 ? {} : { body: JSON.stringify(options.json) }
|
|
4207
|
-
}));
|
|
4208
|
-
} catch (error) {
|
|
4209
|
-
throw failureOf(error, cancel);
|
|
4210
|
-
} finally {
|
|
4211
|
-
free();
|
|
4212
|
-
}
|
|
4213
|
-
})();
|
|
4214
|
-
inFlight.set(key, run);
|
|
4215
|
-
try {
|
|
4216
|
-
return await run;
|
|
4217
|
-
} finally {
|
|
4218
|
-
if (inFlight.get(key) === run) inFlight.delete(key);
|
|
4219
|
-
}
|
|
4220
|
-
}
|
|
4221
|
-
/** A bounded read of a plugin route. */
|
|
4222
|
-
function pluginRead(path, budget, cancel, options) {
|
|
4223
|
-
return dispatch("read", "GET", path, budget, cancel, options);
|
|
4224
|
-
}
|
|
4225
|
-
/** A bounded write. Writes never coalesce by default: two of them are two
|
|
4226
|
-
* intents, even when their bodies match. */
|
|
4227
|
-
function pluginWrite(path, budget, cancel, options) {
|
|
4228
|
-
const method = options?.method ?? "POST";
|
|
4229
|
-
return dispatch("write", method, path, budget, cancel, {
|
|
4230
|
-
...options,
|
|
4231
|
-
key: options?.key ?? `${method} ${withQuery(path, options?.query)} #${nextWriteId()}`
|
|
4232
|
-
});
|
|
4233
|
-
}
|
|
4234
|
-
let writeId = 0;
|
|
4235
|
-
function nextWriteId() {
|
|
4236
|
-
writeId += 1;
|
|
4237
|
-
return writeId;
|
|
4238
|
-
}
|
|
4239
|
-
async function openStream(lane, path, cancel, options, reserved) {
|
|
4240
|
-
const url = withQuery(path, options?.query);
|
|
4241
|
-
const free = reserved ? () => {
|
|
4242
|
-
projectionHeld = false;
|
|
4243
|
-
} : await acquire(lane);
|
|
4244
|
-
try {
|
|
4245
|
-
const response = await send(url, {
|
|
4246
|
-
method: options?.method ?? "GET",
|
|
4247
|
-
credentials: "same-origin",
|
|
4248
|
-
signal: cancel,
|
|
4249
|
-
headers: options?.json === void 0 ? { accept: "application/x-ndjson" } : {
|
|
4250
|
-
accept: "application/x-ndjson",
|
|
4251
|
-
"content-type": "application/json"
|
|
4252
|
-
},
|
|
4253
|
-
...options?.json === void 0 ? {} : { body: JSON.stringify(options.json) }
|
|
4254
|
-
});
|
|
4255
|
-
if (!response.ok || response.body === null) {
|
|
4256
|
-
free();
|
|
4257
|
-
if (response.status === 404) throw new PluginRequestError("route-missing", "The Host is running an older plugin build without this route.", 404);
|
|
4258
|
-
throw new PluginRequestError("http", `HTTP ${response.status}`, response.status);
|
|
4259
|
-
}
|
|
4260
|
-
cancel.addEventListener("abort", free, { once: true });
|
|
4261
|
-
return response.body.getReader();
|
|
4262
|
-
} catch (error) {
|
|
4263
|
-
free();
|
|
4264
|
-
throw failureOf(error, cancel);
|
|
4265
|
-
}
|
|
4266
|
-
}
|
|
4267
|
-
/** A long-lived NDJSON response. No deadline — it is meant to stay open — but
|
|
4268
|
-
* it takes a counted permit for its whole life, which is the bound that
|
|
4269
|
-
* matters. */
|
|
4270
|
-
function pluginNdjson(path, cancel, options) {
|
|
4271
|
-
return openStream("stream", path, cancel, options, false);
|
|
4272
|
-
}
|
|
4273
|
-
/** The reserved projection carrier: exactly one live connection, ever,
|
|
4274
|
-
* whatever the session count. */
|
|
4275
|
-
function pluginProjectionStream(path, cancel, options) {
|
|
4276
|
-
if (projectionHeld) return Promise.reject(new PluginRequestError("starved", "The projection carrier is already open."));
|
|
4277
|
-
projectionHeld = true;
|
|
4278
|
-
return openStream("stream", path, cancel, options, true);
|
|
4279
|
-
}
|
|
4280
|
-
/** Fire-and-forget diagnostics. Dropped rather than queued when saturated:
|
|
4281
|
-
* the channel that reports the plugin's own failures must never be the
|
|
4282
|
-
* traffic that causes them. */
|
|
4283
|
-
function pluginBeacon(path, body) {
|
|
4284
|
-
if (!laneHasRoom("write")) return;
|
|
4285
|
-
pluginWrite(path, "fast", void 0, { json: body }).catch(() => void 0);
|
|
4286
|
-
}
|
|
4287
|
-
//#endregion
|
|
4288
|
-
//#region src/client/projection.ts
|
|
4289
|
-
const EMPTY_CLAUDE_PROJECTION = {
|
|
4290
|
-
schemaVersion: 1,
|
|
4291
|
-
revision: 0,
|
|
4292
|
-
owned: false,
|
|
4293
|
-
commands: [],
|
|
4294
|
-
activities: []
|
|
4295
|
-
};
|
|
4296
|
-
const RETRY_DELAY_MS = 2e3;
|
|
4297
|
-
/** Floor between carrier reopens forced by a desync. A carrier that is losing
|
|
4298
|
-
* lines must not be answered with a reconnect per lost line. */
|
|
4299
|
-
const RESYNC_COOLDOWN_MS = 5e3;
|
|
4300
|
-
/** Wait for the subscribed set to stop moving before reopening the carrier:
|
|
4301
|
-
* mounting a session list changes it once per row. */
|
|
4302
|
-
const SUBSCRIPTION_SETTLE_MS = 250;
|
|
4303
|
-
const NDJSON_SEPARATOR = String.fromCharCode(10);
|
|
4304
|
-
/** Coalesce stream deltas into at most one React notification per frame. */
|
|
4305
|
-
const FRAME_MS = 16;
|
|
4306
|
-
/** Typewriter smoothing: drain newly arrived prose over roughly this window,
|
|
4307
|
-
* so the CLI's paragraph-sized deltas read as a continuous character flow. */
|
|
4308
|
-
const REVEAL_WINDOW_MS = 1200;
|
|
4309
|
-
/** A burst larger than this (redaction rewrite, reconnect catch-up) shows
|
|
4310
|
-
* instantly instead of animating for a long stretch. */
|
|
4311
|
-
const MAX_INSTANT_REVEAL = 4e3;
|
|
4312
|
-
const MAX_ACTIVITIES = 1e4;
|
|
4313
|
-
const MAX_COMMANDS = 2e3;
|
|
4314
|
-
const MAX_REPOSITORY_TEXT_CHARS = 1024;
|
|
4315
|
-
const MAX_DIFF_CHARS = 262144;
|
|
4316
|
-
const MAX_REVIEW_COMMENTS = 50;
|
|
4317
|
-
const MAX_REVIEW_COMMENT_CHARS = 2e3;
|
|
4318
|
-
const MAX_TRANSCRIPT_CHARS = 64e3;
|
|
4319
|
-
function record$7(value) {
|
|
4320
|
-
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
4321
|
-
}
|
|
4322
|
-
function nonNegativeInteger(value) {
|
|
4323
|
-
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
4324
|
-
}
|
|
4325
|
-
function optionalBoundedString(value) {
|
|
4326
|
-
return value === void 0 || typeof value === "string" && value.length <= MAX_REPOSITORY_TEXT_CHARS;
|
|
4327
|
-
}
|
|
4328
|
-
function validateRepository(value) {
|
|
4329
|
-
const repository = record$7(value);
|
|
4330
|
-
if (repository === void 0 || ![
|
|
4331
|
-
"ready",
|
|
4332
|
-
"not-repository",
|
|
4333
|
-
"unavailable"
|
|
4334
|
-
].includes(String(repository.status)) || typeof repository.cwd !== "string" || repository.cwd.length > MAX_REPOSITORY_TEXT_CHARS || !optionalBoundedString(repository.root) || !optionalBoundedString(repository.branch) || !optionalBoundedString(repository.remote) || repository.detached !== void 0 && typeof repository.detached !== "boolean" || repository.worktree !== void 0 && typeof repository.worktree !== "boolean" || repository.dirty !== void 0 && typeof repository.dirty !== "boolean" || repository.upstream !== void 0 && typeof repository.upstream !== "boolean" || repository.ahead !== void 0 && !nonNegativeInteger(repository.ahead) || repository.behind !== void 0 && !nonNegativeInteger(repository.behind)) return false;
|
|
4335
|
-
if (repository.diff !== void 0) {
|
|
4336
|
-
const diff = record$7(repository.diff);
|
|
4337
|
-
if (diff === void 0 || !nonNegativeInteger(diff.additions) || !nonNegativeInteger(diff.deletions) || !nonNegativeInteger(diff.files) || typeof diff.truncated !== "boolean" || diff.patch !== void 0 && (typeof diff.patch !== "string" || diff.patch.length > MAX_DIFF_CHARS)) return false;
|
|
4338
|
-
}
|
|
4339
|
-
if (repository.pullRequest === void 0) return true;
|
|
4340
|
-
const pullRequest = record$7(repository.pullRequest);
|
|
4341
|
-
if (pullRequest === void 0 || !Number.isSafeInteger(pullRequest.number) || Number(pullRequest.number) <= 0 || typeof pullRequest.title !== "string" || pullRequest.title.length > MAX_REPOSITORY_TEXT_CHARS || typeof pullRequest.url !== "string" || pullRequest.url.length > MAX_REPOSITORY_TEXT_CHARS || ![
|
|
4342
|
-
"open",
|
|
4343
|
-
"closed",
|
|
4344
|
-
"merged"
|
|
4345
|
-
].includes(String(pullRequest.state)) || typeof pullRequest.draft !== "boolean" || ![
|
|
4346
|
-
"approved",
|
|
4347
|
-
"changes-requested",
|
|
4348
|
-
"review-required",
|
|
4349
|
-
"none"
|
|
4350
|
-
].includes(String(pullRequest.review)) || ![
|
|
4351
|
-
"passing",
|
|
4352
|
-
"pending",
|
|
4353
|
-
"failing",
|
|
4354
|
-
"none"
|
|
4355
|
-
].includes(String(pullRequest.checks)) || !optionalBoundedString(pullRequest.mergeState) || !optionalBoundedString(pullRequest.author) || !optionalBoundedString(pullRequest.baseBranch) || pullRequest.createdAt !== void 0 && (typeof pullRequest.createdAt !== "string" || !Number.isFinite(Date.parse(pullRequest.createdAt))) || pullRequest.mergedAt !== void 0 && (typeof pullRequest.mergedAt !== "string" || !Number.isFinite(Date.parse(pullRequest.mergedAt)))) return false;
|
|
4356
|
-
try {
|
|
4357
|
-
const url = new URL(pullRequest.url);
|
|
4358
|
-
return url.protocol === "https:" && url.hostname === "github.com";
|
|
4359
|
-
} catch {
|
|
4360
|
-
return false;
|
|
4361
|
-
}
|
|
4362
|
-
}
|
|
4363
|
-
/** Validate the public route envelope before publishing it to UI components. */
|
|
4364
|
-
function parseClaudeClientProjection(value) {
|
|
4365
|
-
const input = record$7(value);
|
|
4366
|
-
if (input === void 0 || input.schemaVersion !== 1 || !nonNegativeInteger(input.revision) || typeof input.owned !== "boolean" || !Array.isArray(input.commands) || input.commands.length > MAX_COMMANDS || !Array.isArray(input.activities) || input.activities.length > MAX_ACTIVITIES) throw new Error("invalid Claude sidecar projection");
|
|
4367
|
-
for (const item of input.commands) {
|
|
4368
|
-
const command = record$7(item);
|
|
4369
|
-
if (command === void 0 || typeof command.publicName !== "string" || typeof command.claudeName !== "string" || typeof command.description !== "string" || command.hint !== void 0 && typeof command.hint !== "string" || typeof command.prefixed !== "boolean") throw new Error("invalid Claude command projection");
|
|
4370
|
-
}
|
|
4371
|
-
for (const item of input.activities) {
|
|
4372
|
-
const activity = record$7(item);
|
|
4373
|
-
if (activity === void 0 || !nonNegativeInteger(activity.turn) || !nonNegativeInteger(activity.step) || !nonNegativeInteger(activity.ordinal) || typeof activity.kind !== "string") throw new Error("invalid Claude sidecar activity");
|
|
4374
|
-
}
|
|
4375
|
-
if (input.contextUsage !== void 0 && record$7(input.contextUsage) === void 0) throw new Error("invalid Claude context projection");
|
|
4376
|
-
const tasks = input.tasks === void 0 ? void 0 : record$7(input.tasks);
|
|
4377
|
-
if (tasks !== void 0 && !Array.isArray(tasks.tasks)) throw new Error("invalid Claude tasks projection");
|
|
4378
|
-
if (input.repository !== void 0 && !validateRepository(input.repository)) throw new Error("invalid Claude repository projection");
|
|
4379
|
-
if (input.reviewComments !== void 0) {
|
|
4380
|
-
if (!Array.isArray(input.reviewComments) || input.reviewComments.length > MAX_REVIEW_COMMENTS) throw new Error("invalid Claude review comment projection");
|
|
4381
|
-
for (const item of input.reviewComments) {
|
|
4382
|
-
const comment = record$7(item);
|
|
4383
|
-
if (comment === void 0 || typeof comment.id !== "string" || comment.id.length === 0 || comment.id.length > 128 || typeof comment.path !== "string" || comment.path.length === 0 || comment.path.length > MAX_REPOSITORY_TEXT_CHARS || !nonNegativeInteger(comment.line) || comment.side !== "old" && comment.side !== "new" || typeof comment.text !== "string" || comment.text.length > MAX_REVIEW_COMMENT_CHARS) throw new Error("invalid Claude review comment projection");
|
|
4384
|
-
}
|
|
4385
|
-
}
|
|
4386
|
-
if (input.rewind !== void 0) {
|
|
4387
|
-
const ranges = record$7(input.rewind)?.ranges;
|
|
4388
|
-
if (!Array.isArray(ranges) || ranges.length > 200) throw new Error("invalid Claude rewind projection");
|
|
4389
|
-
for (const item of ranges) {
|
|
4390
|
-
const range = record$7(item);
|
|
4391
|
-
if (range === void 0 || !nonNegativeInteger(range.start) || !nonNegativeInteger(range.end)) throw new Error("invalid Claude rewind projection");
|
|
4392
|
-
}
|
|
4393
|
-
}
|
|
4394
|
-
return input;
|
|
4395
|
-
}
|
|
4396
|
-
/** Validate one incremental delta payload with the same rules as a snapshot. */
|
|
4397
|
-
function validateEnvelopeFragment(fragment) {
|
|
4398
|
-
parseClaudeClientProjection({
|
|
4399
|
-
schemaVersion: 1,
|
|
4400
|
-
revision: 0,
|
|
4401
|
-
owned: false,
|
|
4402
|
-
commands: [],
|
|
4403
|
-
activities: [],
|
|
4404
|
-
...fragment
|
|
4405
|
-
});
|
|
4406
|
-
}
|
|
4407
|
-
function stepKeyOf(turn, step) {
|
|
4408
|
-
return `${turn}:${step}`;
|
|
4409
|
-
}
|
|
4410
|
-
const EMPTY_ACTIVITIES = [];
|
|
4411
|
-
/** Identity-stable per-step slice; untouched steps never re-render while
|
|
4412
|
-
* another step streams. Falls back to filtering for snapshot-only hooks. */
|
|
4413
|
-
function selectStepActivities(value, turn, step) {
|
|
4414
|
-
const sliced = value.byStep?.get(stepKeyOf(turn, step));
|
|
4415
|
-
if (sliced !== void 0) return sliced;
|
|
4416
|
-
const filtered = value.activities.filter((activity) => activity.turn === turn && activity.step === step);
|
|
4417
|
-
return filtered.length === 0 ? EMPTY_ACTIVITIES : filtered;
|
|
4418
|
-
}
|
|
4419
|
-
function isAbort(error) {
|
|
4420
|
-
return error?.name === "AbortError";
|
|
4421
|
-
}
|
|
4422
|
-
function delay(ms) {
|
|
4423
|
-
return new Promise((resolve) => {
|
|
4424
|
-
setTimeout(resolve, ms).unref?.();
|
|
4425
|
-
});
|
|
4426
|
-
}
|
|
4427
|
-
/** One session's reducer over the shared carrier's lines.
|
|
4123
|
+
function stepKeyOf(turn, step) {
|
|
4124
|
+
return `${turn}:${step}`;
|
|
4125
|
+
}
|
|
4126
|
+
const EMPTY_ACTIVITIES = [];
|
|
4127
|
+
/** Identity-stable per-step slice; untouched steps never re-render while
|
|
4128
|
+
* another step streams. Falls back to filtering for snapshot-only hooks. */
|
|
4129
|
+
function selectStepActivities(value, turn, step) {
|
|
4130
|
+
const sliced = value.byStep?.get(stepKeyOf(turn, step));
|
|
4131
|
+
if (sliced !== void 0) return sliced;
|
|
4132
|
+
const filtered = value.activities.filter((activity) => activity.turn === turn && activity.step === step);
|
|
4133
|
+
return filtered.length === 0 ? EMPTY_ACTIVITIES : filtered;
|
|
4134
|
+
}
|
|
4135
|
+
function isAbort(error) {
|
|
4136
|
+
return error?.name === "AbortError";
|
|
4137
|
+
}
|
|
4138
|
+
function delay(ms) {
|
|
4139
|
+
return new Promise((resolve) => {
|
|
4140
|
+
setTimeout(resolve, ms).unref?.();
|
|
4141
|
+
});
|
|
4142
|
+
}
|
|
4143
|
+
/** One session's reducer over the shared carrier's lines.
|
|
4428
4144
|
*
|
|
4429
4145
|
* A source opens nothing. It used to hold a stream of its own, which made the
|
|
4430
4146
|
* plugin's connection count a function of how many sessions existed;
|
|
@@ -4695,586 +4411,1095 @@ window.__ModuleLoader__.load({
|
|
|
4695
4411
|
}
|
|
4696
4412
|
};
|
|
4697
4413
|
}
|
|
4698
|
-
/**
|
|
4699
|
-
* Every session's projection over ONE connection.
|
|
4700
|
-
*
|
|
4701
|
-
* The plugin used to open an NDJSON stream per session, so its share of the
|
|
4702
|
-
* browser's small per-origin connection budget grew with the number of Claude
|
|
4703
|
-
* sessions — and the overview panel subscribes one per LISTED session, not per
|
|
4704
|
-
* open one. Past a handful of sessions the plugin's own settings panel could no
|
|
4705
|
-
* longer get a connection at all, which is the failure this class exists to
|
|
4706
|
-
* make impossible: the carrier is one connection whatever the session count.
|
|
4707
|
-
*
|
|
4708
|
-
* `source(sessionId)` keeps its shape, so consumers are unaware of any of this.
|
|
4709
|
-
*/
|
|
4710
|
-
var ClaudeProjectionStore = class {
|
|
4711
|
-
#sources = /* @__PURE__ */ new Map();
|
|
4712
|
-
/** Sessions with at least one live subscriber, newest interest last. */
|
|
4713
|
-
#wanted = /* @__PURE__ */ new Set();
|
|
4714
|
-
#open;
|
|
4715
|
-
#retryDelayMs;
|
|
4716
|
-
#settleMs;
|
|
4717
|
-
#report;
|
|
4718
|
-
#resyncCooldownMs;
|
|
4719
|
-
#resyncedAt = 0;
|
|
4720
|
-
#controller;
|
|
4721
|
-
#settle;
|
|
4722
|
-
#running = false;
|
|
4723
|
-
#disposed = false;
|
|
4724
|
-
constructor(options = {}) {
|
|
4725
|
-
this.#open = options.open ?? ((path, cancel) => pluginProjectionStream(path, cancel));
|
|
4726
|
-
this.#retryDelayMs = options.retryDelayMs ?? RETRY_DELAY_MS;
|
|
4727
|
-
this.#settleMs = options.settleMs ?? SUBSCRIPTION_SETTLE_MS;
|
|
4728
|
-
this.#report = options.report ?? (() => {});
|
|
4729
|
-
this.#resyncCooldownMs = options.resyncCooldownMs ?? RESYNC_COOLDOWN_MS;
|
|
4730
|
-
}
|
|
4731
|
-
source(sessionId) {
|
|
4732
|
-
let source = this.#sources.get(sessionId);
|
|
4733
|
-
if (source === void 0) {
|
|
4734
|
-
source = createClaudeProjectionSource(sessionId, (active) => {
|
|
4735
|
-
this.#demand(sessionId, active);
|
|
4736
|
-
}, (kind, detail) => {
|
|
4737
|
-
this.#resync(kind, detail);
|
|
4738
|
-
});
|
|
4739
|
-
this.#sources.set(sessionId, source);
|
|
4740
|
-
}
|
|
4741
|
-
return source;
|
|
4742
|
-
}
|
|
4743
|
-
/** Reopen the carrier so every lane is restated from a fresh snapshot.
|
|
4744
|
-
*
|
|
4745
|
-
* One session noticed the hole, but the carrier is shared and a dropped
|
|
4746
|
-
* line is a property of the carrier, so the others are suspect too --
|
|
4747
|
-
* reopening restates all of them for the price of the one reconnect. */
|
|
4748
|
-
#resync(kind, detail) {
|
|
4749
|
-
if (this.#disposed) return;
|
|
4750
|
-
this.#report(kind, detail);
|
|
4751
|
-
const now = Date.now();
|
|
4752
|
-
if (now - this.#resyncedAt < this.#resyncCooldownMs) return;
|
|
4753
|
-
this.#resyncedAt = now;
|
|
4754
|
-
this.#reopen();
|
|
4755
|
-
}
|
|
4756
|
-
dispose() {
|
|
4757
|
-
this.#disposed = true;
|
|
4758
|
-
if (this.#settle !== void 0) clearTimeout(this.#settle);
|
|
4759
|
-
this.#settle = void 0;
|
|
4760
|
-
this.#controller?.abort();
|
|
4761
|
-
this.#controller = void 0;
|
|
4762
|
-
for (const source of this.#sources.values()) source.dispose();
|
|
4763
|
-
this.#sources.clear();
|
|
4764
|
-
this.#wanted.clear();
|
|
4765
|
-
}
|
|
4766
|
-
/** Note interest and reopen the carrier once the set stops moving. Mounting
|
|
4767
|
-
* a session list would otherwise reopen it once per row. */
|
|
4768
|
-
#demand(sessionId, active) {
|
|
4769
|
-
if (this.#disposed) return;
|
|
4770
|
-
if (active) {
|
|
4771
|
-
this.#wanted.delete(sessionId);
|
|
4772
|
-
this.#wanted.add(sessionId);
|
|
4773
|
-
} else if (!this.#wanted.delete(sessionId)) return;
|
|
4774
|
-
if (this.#settle !== void 0) clearTimeout(this.#settle);
|
|
4775
|
-
const timer = setTimeout(() => {
|
|
4776
|
-
this.#settle = void 0;
|
|
4777
|
-
this.#reopen();
|
|
4778
|
-
}, this.#settleMs);
|
|
4779
|
-
timer.unref?.();
|
|
4780
|
-
this.#settle = timer;
|
|
4781
|
-
}
|
|
4782
|
-
#lanes() {
|
|
4783
|
-
const wanted = [...this.#wanted];
|
|
4784
|
-
return wanted.slice(Math.max(0, wanted.length - 16));
|
|
4414
|
+
/**
|
|
4415
|
+
* Every session's projection over ONE connection.
|
|
4416
|
+
*
|
|
4417
|
+
* The plugin used to open an NDJSON stream per session, so its share of the
|
|
4418
|
+
* browser's small per-origin connection budget grew with the number of Claude
|
|
4419
|
+
* sessions — and the overview panel subscribes one per LISTED session, not per
|
|
4420
|
+
* open one. Past a handful of sessions the plugin's own settings panel could no
|
|
4421
|
+
* longer get a connection at all, which is the failure this class exists to
|
|
4422
|
+
* make impossible: the carrier is one connection whatever the session count.
|
|
4423
|
+
*
|
|
4424
|
+
* `source(sessionId)` keeps its shape, so consumers are unaware of any of this.
|
|
4425
|
+
*/
|
|
4426
|
+
var ClaudeProjectionStore = class {
|
|
4427
|
+
#sources = /* @__PURE__ */ new Map();
|
|
4428
|
+
/** Sessions with at least one live subscriber, newest interest last. */
|
|
4429
|
+
#wanted = /* @__PURE__ */ new Set();
|
|
4430
|
+
#open;
|
|
4431
|
+
#retryDelayMs;
|
|
4432
|
+
#settleMs;
|
|
4433
|
+
#report;
|
|
4434
|
+
#resyncCooldownMs;
|
|
4435
|
+
#resyncedAt = 0;
|
|
4436
|
+
#controller;
|
|
4437
|
+
#settle;
|
|
4438
|
+
#running = false;
|
|
4439
|
+
#disposed = false;
|
|
4440
|
+
constructor(options = {}) {
|
|
4441
|
+
this.#open = options.open ?? ((path, cancel) => pluginProjectionStream(path, cancel));
|
|
4442
|
+
this.#retryDelayMs = options.retryDelayMs ?? RETRY_DELAY_MS;
|
|
4443
|
+
this.#settleMs = options.settleMs ?? SUBSCRIPTION_SETTLE_MS;
|
|
4444
|
+
this.#report = options.report ?? (() => {});
|
|
4445
|
+
this.#resyncCooldownMs = options.resyncCooldownMs ?? RESYNC_COOLDOWN_MS;
|
|
4446
|
+
}
|
|
4447
|
+
source(sessionId) {
|
|
4448
|
+
let source = this.#sources.get(sessionId);
|
|
4449
|
+
if (source === void 0) {
|
|
4450
|
+
source = createClaudeProjectionSource(sessionId, (active) => {
|
|
4451
|
+
this.#demand(sessionId, active);
|
|
4452
|
+
}, (kind, detail) => {
|
|
4453
|
+
this.#resync(kind, detail);
|
|
4454
|
+
});
|
|
4455
|
+
this.#sources.set(sessionId, source);
|
|
4456
|
+
}
|
|
4457
|
+
return source;
|
|
4458
|
+
}
|
|
4459
|
+
/** Reopen the carrier so every lane is restated from a fresh snapshot.
|
|
4460
|
+
*
|
|
4461
|
+
* One session noticed the hole, but the carrier is shared and a dropped
|
|
4462
|
+
* line is a property of the carrier, so the others are suspect too --
|
|
4463
|
+
* reopening restates all of them for the price of the one reconnect. */
|
|
4464
|
+
#resync(kind, detail) {
|
|
4465
|
+
if (this.#disposed) return;
|
|
4466
|
+
this.#report(kind, detail);
|
|
4467
|
+
const now = Date.now();
|
|
4468
|
+
if (now - this.#resyncedAt < this.#resyncCooldownMs) return;
|
|
4469
|
+
this.#resyncedAt = now;
|
|
4470
|
+
this.#reopen();
|
|
4471
|
+
}
|
|
4472
|
+
dispose() {
|
|
4473
|
+
this.#disposed = true;
|
|
4474
|
+
if (this.#settle !== void 0) clearTimeout(this.#settle);
|
|
4475
|
+
this.#settle = void 0;
|
|
4476
|
+
this.#controller?.abort();
|
|
4477
|
+
this.#controller = void 0;
|
|
4478
|
+
for (const source of this.#sources.values()) source.dispose();
|
|
4479
|
+
this.#sources.clear();
|
|
4480
|
+
this.#wanted.clear();
|
|
4481
|
+
}
|
|
4482
|
+
/** Note interest and reopen the carrier once the set stops moving. Mounting
|
|
4483
|
+
* a session list would otherwise reopen it once per row. */
|
|
4484
|
+
#demand(sessionId, active) {
|
|
4485
|
+
if (this.#disposed) return;
|
|
4486
|
+
if (active) {
|
|
4487
|
+
this.#wanted.delete(sessionId);
|
|
4488
|
+
this.#wanted.add(sessionId);
|
|
4489
|
+
} else if (!this.#wanted.delete(sessionId)) return;
|
|
4490
|
+
if (this.#settle !== void 0) clearTimeout(this.#settle);
|
|
4491
|
+
const timer = setTimeout(() => {
|
|
4492
|
+
this.#settle = void 0;
|
|
4493
|
+
this.#reopen();
|
|
4494
|
+
}, this.#settleMs);
|
|
4495
|
+
timer.unref?.();
|
|
4496
|
+
this.#settle = timer;
|
|
4497
|
+
}
|
|
4498
|
+
#lanes() {
|
|
4499
|
+
const wanted = [...this.#wanted];
|
|
4500
|
+
return wanted.slice(Math.max(0, wanted.length - 16));
|
|
4501
|
+
}
|
|
4502
|
+
#reopen() {
|
|
4503
|
+
this.#controller?.abort();
|
|
4504
|
+
this.#controller = void 0;
|
|
4505
|
+
if (this.#disposed || this.#wanted.size === 0) return;
|
|
4506
|
+
this.#run();
|
|
4507
|
+
}
|
|
4508
|
+
async #run() {
|
|
4509
|
+
if (this.#running) return;
|
|
4510
|
+
this.#running = true;
|
|
4511
|
+
try {
|
|
4512
|
+
while (!this.#disposed && this.#wanted.size > 0) {
|
|
4513
|
+
const controller = new AbortController();
|
|
4514
|
+
this.#controller = controller;
|
|
4515
|
+
const lanes = this.#lanes();
|
|
4516
|
+
let superseded = false;
|
|
4517
|
+
try {
|
|
4518
|
+
const reader = await this.#open(`${CLAUDE_PROJECTION_PATH}/multi?sessions=${lanes.map(encodeURIComponent).join(",")}`, controller.signal);
|
|
4519
|
+
const stop = () => {
|
|
4520
|
+
superseded = true;
|
|
4521
|
+
reader.cancel().catch(() => void 0);
|
|
4522
|
+
};
|
|
4523
|
+
controller.signal.addEventListener("abort", stop, { once: true });
|
|
4524
|
+
const decoder = new TextDecoder();
|
|
4525
|
+
let buffer = "";
|
|
4526
|
+
while (!controller.signal.aborted) {
|
|
4527
|
+
const chunk = await reader.read();
|
|
4528
|
+
buffer += decoder.decode(chunk.value, { stream: !chunk.done });
|
|
4529
|
+
const lines = buffer.split(NDJSON_SEPARATOR);
|
|
4530
|
+
buffer = lines.pop() ?? "";
|
|
4531
|
+
for (const line of lines) this.#dispatch(line);
|
|
4532
|
+
if (chunk.done) break;
|
|
4533
|
+
}
|
|
4534
|
+
controller.signal.removeEventListener("abort", stop);
|
|
4535
|
+
await reader.cancel().catch(() => void 0);
|
|
4536
|
+
} catch (error) {
|
|
4537
|
+
if (isAbort(error)) {
|
|
4538
|
+
if (this.#controller !== controller) continue;
|
|
4539
|
+
return;
|
|
4540
|
+
}
|
|
4541
|
+
} finally {
|
|
4542
|
+
if (this.#controller === controller) this.#controller = void 0;
|
|
4543
|
+
}
|
|
4544
|
+
if (this.#disposed || this.#wanted.size === 0) return;
|
|
4545
|
+
if (superseded) continue;
|
|
4546
|
+
await delay(this.#retryDelayMs);
|
|
4547
|
+
}
|
|
4548
|
+
} finally {
|
|
4549
|
+
this.#running = false;
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4552
|
+
/** Route one carrier line to the session that owns it. A line for a session
|
|
4553
|
+
* nobody is watching any more is dropped rather than reviving its lane. */
|
|
4554
|
+
#dispatch(line) {
|
|
4555
|
+
if (line.length === 0) return;
|
|
4556
|
+
let session;
|
|
4557
|
+
try {
|
|
4558
|
+
session = JSON.parse(line).session;
|
|
4559
|
+
} catch {
|
|
4560
|
+
return;
|
|
4561
|
+
}
|
|
4562
|
+
if (typeof session !== "string") return;
|
|
4563
|
+
this.#sources.get(session)?.feed(line);
|
|
4564
|
+
}
|
|
4565
|
+
};
|
|
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
|
|
4574
|
+
//#region src/client/ClaudeActivityNode.tsx
|
|
4575
|
+
const EMPTY_TASKS$1 = [];
|
|
4576
|
+
const ACTIVITY_CSS = [
|
|
4577
|
+
".dsh-claude-flow{display:flex;flex-direction:column;gap:10px}",
|
|
4578
|
+
".dsh-claude-transcript-text{color:var(--dsw-alias-label-primary);font-size:15px;line-height:24px;overflow-wrap:anywhere}",
|
|
4579
|
+
".dsh-claude-tool-group-native{overflow:visible}",
|
|
4580
|
+
".dsh-claude-tool-group-native>.dsh-claude-flow-row{padding:0}",
|
|
4581
|
+
".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}",
|
|
4582
|
+
".dsh-claude-tool-item{border-top:1px solid var(--dsw-alias-border-l1, color-mix(in srgb, currentColor 12%, transparent))}",
|
|
4583
|
+
".dsh-claude-tool-item:first-child{border-top:0}",
|
|
4584
|
+
".dsh-claude-tool-summary-row{display:flex;align-items:center;min-height:34px;gap:8px;padding:0 10px;cursor:pointer;list-style:none}",
|
|
4585
|
+
".dsh-claude-tool-summary-row::-webkit-details-marker{display:none}",
|
|
4586
|
+
".dsh-claude-tool-summary-row::after{content:\"›\";margin-left:2px;flex:none;align-self:center;color:var(--dsw-alias-label-secondary);font-size:22px;line-height:1;transform-origin:center;transition:transform .15s ease}",
|
|
4587
|
+
".dsh-claude-tool-item[open]>.dsh-claude-tool-summary-row::after{transform:rotate(90deg)}",
|
|
4588
|
+
".dsh-claude-tool-content{min-width:0;padding:0 10px 8px}",
|
|
4589
|
+
".dsh-claude-tool-content>*{max-width:100%;box-sizing:border-box}",
|
|
4590
|
+
".dsh-claude-tool-section{margin-top:8px}",
|
|
4591
|
+
".dsh-claude-tool-section-title{margin-bottom:4px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;text-transform:uppercase;letter-spacing:.04em}",
|
|
4592
|
+
".dsh-claude-tool-fields{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:3px 12px;font-size:13px;line-height:20px}",
|
|
4593
|
+
".dsh-claude-tool-field-key{color:var(--dsw-alias-label-tertiary)}",
|
|
4594
|
+
".dsh-claude-tool-field-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:pre-wrap;overflow-wrap:anywhere}",
|
|
4595
|
+
".dsh-claude-tool-paths{display:flex;flex-direction:column;gap:2px;margin:0;padding:0;list-style:none;font:var(--dsw-font-markdown-code-block-small)}",
|
|
4596
|
+
".dsh-claude-tool-path{overflow-wrap:anywhere;color:var(--dsw-alias-label-primary)}",
|
|
4597
|
+
".dsh-claude-tool-code{max-height:260px;overflow:auto;margin:0;padding:8px 0;border-radius:8px;background:var(--dsw-alias-markdown-code-block);font:var(--dsw-font-markdown-code-block-small)}",
|
|
4598
|
+
".dsh-claude-tool-code-line{display:grid;grid-template-columns:44px minmax(max-content,1fr);min-height:18px}",
|
|
4599
|
+
".dsh-claude-tool-line-number{padding-right:10px;text-align:right;user-select:none;color:var(--dsw-alias-label-caption);border-right:1px solid var(--dsw-alias-border-l1, color-mix(in srgb, currentColor 12%, transparent))}",
|
|
4600
|
+
".dsh-claude-tool-line-text{padding:0 10px;white-space:pre}",
|
|
4601
|
+
".dsh-claude-tool-label{width:72px;flex:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}",
|
|
4602
|
+
".dsh-claude-tool-description{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;line-height:20px;color:var(--dsw-alias-label-primary)}",
|
|
4603
|
+
".dsh-claude-tool-stats{display:inline-flex;gap:4px;flex:none;font-size:13px;line-height:20px}",
|
|
4604
|
+
".dsh-claude-diff-add{color:var(--dsw-alias-state-success-primary,#21c55d)}",
|
|
4605
|
+
".dsh-claude-diff-delete{color:var(--dsw-alias-state-error-primary)}",
|
|
4606
|
+
".dsh-claude-tool-name{font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary)}",
|
|
4607
|
+
".dsh-claude-tool-summary{margin-left:8px;color:var(--dsw-alias-label-tertiary)}",
|
|
4608
|
+
".dsh-claude-turn-usage{display:flex;flex-wrap:wrap;align-items:center;gap:5px;margin-top:10px;",
|
|
4609
|
+
"color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}",
|
|
4610
|
+
".dsh-claude-turn-usage-label{color:var(--dsw-alias-label-caption)}",
|
|
4611
|
+
".dsh-claude-tool-terminal{display:flex;gap:8px;margin:6px 0 0;padding:8px 10px;max-height:220px;overflow:auto;",
|
|
4612
|
+
"border-radius:8px;background:var(--dsw-alias-markdown-code-block);font:var(--dsw-font-markdown-code-block-small)}",
|
|
4613
|
+
".dsh-claude-tool-prompt{flex:none;user-select:none;color:var(--dsw-alias-label-caption)}",
|
|
4614
|
+
".dsh-claude-tool-command{min-width:0;margin:0;color:var(--dsw-alias-label-primary);white-space:pre-wrap;overflow-wrap:anywhere}",
|
|
4615
|
+
".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}",
|
|
4616
|
+
".dsh-claude-flow-row{position:relative;overflow:hidden}",
|
|
4617
|
+
".dsh-claude-flow-leading{flex-shrink:0}",
|
|
4618
|
+
".dsh-claude-flow-title{font-weight:400}",
|
|
4619
|
+
".dsh-claude-flow-chevron{color:var(--dsw-alias-label-secondary)}",
|
|
4620
|
+
".dsh-claude-flow-separator{width:2px;height:2px;margin:0 8px;border-radius:1px;background:var(--dsw-alias-label-caption);flex:none}",
|
|
4621
|
+
".dsh-claude-flow-summary{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:24px;flex:auto}",
|
|
4622
|
+
".dsh-claude-flow-summary[data-error]{color:var(--dsw-alias-state-error-primary)}",
|
|
4623
|
+
".dsh-claude-flow-body{margin:4px 0 4px 22px;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:24px;white-space:pre-wrap;overflow-wrap:anywhere}",
|
|
4624
|
+
".dsh-claude-flow-detail{max-height:260px;overflow:auto;margin:4px 0 4px 4px;padding:12px 16px;border:1px solid var(--dsw-alias-border-l1, color-mix(in srgb, currentColor 12%, transparent));border-radius:12px;background:var(--dsw-alias-markdown-code-block);color:var(--dsw-alias-label-primary);font:var(--dsw-font-markdown-code-block-small);white-space:pre-wrap}",
|
|
4625
|
+
".dsh-claude-flow-subcalls{display:flex;flex-direction:column;gap:2px;margin:4px 0 4px 22px;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:22px}",
|
|
4626
|
+
".dsh-claude-compaction{display:flex;align-items:center;gap:10px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}",
|
|
4627
|
+
".dsh-claude-compaction::before,.dsh-claude-compaction::after{content:\"\";flex:1;height:1px;background:var(--dsw-alias-border-l1, color-mix(in srgb, currentColor 12%, transparent))}",
|
|
4628
|
+
".dsh-claude-compaction-label{flex:none;white-space:nowrap}",
|
|
4629
|
+
".dsh-claude-act-running{animation:dsh-claude-act-pulse 1.2s ease-in-out infinite}",
|
|
4630
|
+
"@keyframes dsh-claude-act-pulse{0%,100%{opacity:1}50%{opacity:.3}}"
|
|
4631
|
+
].join("");
|
|
4632
|
+
let cssInjected$3 = false;
|
|
4633
|
+
function ensureCss$3() {
|
|
4634
|
+
if (cssInjected$3 || typeof document === "undefined") return;
|
|
4635
|
+
cssInjected$3 = true;
|
|
4636
|
+
const element = document.createElement("style");
|
|
4637
|
+
element.dataset.dshClaudeActivity = "";
|
|
4638
|
+
element.textContent = ACTIVITY_CSS;
|
|
4639
|
+
document.head.appendChild(element);
|
|
4640
|
+
}
|
|
4641
|
+
function subcallGlyph(subcall) {
|
|
4642
|
+
if (subcall.isError === true || subcall.phase === "failed") return "×";
|
|
4643
|
+
if (subcall.phase === "started" || subcall.phase === "updated") return "●";
|
|
4644
|
+
return "✓";
|
|
4645
|
+
}
|
|
4646
|
+
function title(activity) {
|
|
4647
|
+
return activity.toolName ?? activity.title ?? activity.kind.replaceAll("-", " ");
|
|
4648
|
+
}
|
|
4649
|
+
function activityState(activity, running) {
|
|
4650
|
+
if (activity.isError === true || activity.kind === "error" || activity.phase === "denied" || activity.phase === "failed") return "error";
|
|
4651
|
+
if (running) return "ongoing";
|
|
4652
|
+
if (activity.kind === "warning") return "warning";
|
|
4653
|
+
return "done";
|
|
4654
|
+
}
|
|
4655
|
+
function ActivityRow({ row, t }) {
|
|
4656
|
+
const { activity, running, subcalls } = row;
|
|
4657
|
+
const [open, setOpen] = useState(false);
|
|
4658
|
+
const state = activityState(activity, running);
|
|
4659
|
+
const detail = activity.detail;
|
|
4660
|
+
const expandable = detail !== void 0 || subcalls.length > 0 || activity.kind === "thinking";
|
|
4661
|
+
const summary = activity.summary ?? (running ? t("running") : state === "error" ? t("failed") : t("done"));
|
|
4662
|
+
const body = activity.kind === "thinking" ? activity.summary : detail;
|
|
4663
|
+
return /* @__PURE__ */ jsxs(DisclosureRow, {
|
|
4664
|
+
rowClassName: "dsh-claude-flow-row",
|
|
4665
|
+
leadingClassName: "dsh-claude-flow-leading",
|
|
4666
|
+
titleClassName: "dsh-claude-flow-title",
|
|
4667
|
+
chevronClassName: "dsh-claude-flow-chevron",
|
|
4668
|
+
icon: activity.kind === "thinking" ? /* @__PURE__ */ jsx(IconThinkOutline14, { size: 14 }) : state === "done" ? /* @__PURE__ */ jsx(IconApiOutline14, { size: 14 }) : /* @__PURE__ */ jsx(StateDot, { state }),
|
|
4669
|
+
title: activity.kind === "thinking" ? t("thinking") : title(activity),
|
|
4670
|
+
open,
|
|
4671
|
+
expandable,
|
|
4672
|
+
expandOnRowClick: true,
|
|
4673
|
+
keepContentWhenOpen: true,
|
|
4674
|
+
onToggle: () => setOpen((value) => !value),
|
|
4675
|
+
collapsedContent: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
4676
|
+
className: "dsh-claude-flow-separator",
|
|
4677
|
+
"aria-hidden": "true"
|
|
4678
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
4679
|
+
className: "dsh-claude-flow-summary",
|
|
4680
|
+
"data-error": state === "error" || void 0,
|
|
4681
|
+
children: summary
|
|
4682
|
+
})] }),
|
|
4683
|
+
children: [subcalls.length === 0 ? null : /* @__PURE__ */ jsx("div", {
|
|
4684
|
+
className: "dsh-claude-flow-subcalls",
|
|
4685
|
+
children: subcalls.map((subcall) => /* @__PURE__ */ jsxs("div", { children: [
|
|
4686
|
+
subcallGlyph(subcall),
|
|
4687
|
+
" ",
|
|
4688
|
+
subcall.toolName ?? t("subagent"),
|
|
4689
|
+
subcall.summary === void 0 ? "" : ` · ${subcall.summary}`
|
|
4690
|
+
] }, subcall.toolUseId))
|
|
4691
|
+
}), body === void 0 ? null : activity.kind === "thinking" ? /* @__PURE__ */ jsx("div", {
|
|
4692
|
+
className: "dsh-claude-flow-body",
|
|
4693
|
+
children: body
|
|
4694
|
+
}) : /* @__PURE__ */ jsx("pre", {
|
|
4695
|
+
className: "dsh-claude-flow-detail",
|
|
4696
|
+
children: body
|
|
4697
|
+
})]
|
|
4698
|
+
});
|
|
4699
|
+
}
|
|
4700
|
+
/** Compaction has no turn of its own to render, so the transcript marks it with
|
|
4701
|
+
* a rule instead of a row: it separates prose rather than reporting work. */
|
|
4702
|
+
function ClaudeCompactionDivider({ compaction, t }) {
|
|
4703
|
+
const { trigger, preTokens, postTokens } = compaction;
|
|
4704
|
+
const label = trigger === "auto" ? t("compactedAuto") : t("compacted");
|
|
4705
|
+
const shrink = preTokens === void 0 || postTokens === void 0 ? void 0 : `${formatTokenCount(preTokens)} → ${formatTokenCount(postTokens)}`;
|
|
4706
|
+
return /* @__PURE__ */ jsx("div", {
|
|
4707
|
+
className: "dsh-claude-compaction",
|
|
4708
|
+
role: "separator",
|
|
4709
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
4710
|
+
className: "dsh-claude-compaction-label",
|
|
4711
|
+
children: shrink === void 0 ? label : `${label} · ${shrink}`
|
|
4712
|
+
})
|
|
4713
|
+
});
|
|
4714
|
+
}
|
|
4715
|
+
function parsedValue(value) {
|
|
4716
|
+
if (value === void 0 || value.length === 0) return void 0;
|
|
4717
|
+
try {
|
|
4718
|
+
return JSON.parse(value);
|
|
4719
|
+
} catch {
|
|
4720
|
+
return value;
|
|
4785
4721
|
}
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4722
|
+
}
|
|
4723
|
+
function record$6(value) {
|
|
4724
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
4725
|
+
}
|
|
4726
|
+
function text$1(value) {
|
|
4727
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
4728
|
+
}
|
|
4729
|
+
function numberValue(value) {
|
|
4730
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
4731
|
+
}
|
|
4732
|
+
function displayValue(value) {
|
|
4733
|
+
if (value === null) return "null";
|
|
4734
|
+
if (typeof value === "string") return value;
|
|
4735
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
4736
|
+
if (Array.isArray(value)) return value.map(displayValue).join(", ");
|
|
4737
|
+
return record$6(value) === void 0 ? String(value) : Object.entries(value).map(([key, item]) => `${key}: ${displayValue(item)}`).join("\n");
|
|
4738
|
+
}
|
|
4739
|
+
function Section({ title, children }) {
|
|
4740
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
4741
|
+
className: "dsh-claude-tool-section",
|
|
4742
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
4743
|
+
className: "dsh-claude-tool-section-title",
|
|
4744
|
+
children: title
|
|
4745
|
+
}), children]
|
|
4746
|
+
});
|
|
4747
|
+
}
|
|
4748
|
+
function Fields({ value, omit = [] }) {
|
|
4749
|
+
const entries = Object.entries(value).filter(([key, item]) => !omit.includes(key) && item !== void 0 && item !== "");
|
|
4750
|
+
if (entries.length === 0) return null;
|
|
4751
|
+
return /* @__PURE__ */ jsx("div", {
|
|
4752
|
+
className: "dsh-claude-tool-fields",
|
|
4753
|
+
children: entries.map(([key, item]) => /* @__PURE__ */ jsxs("div", {
|
|
4754
|
+
style: { display: "contents" },
|
|
4755
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
4756
|
+
className: "dsh-claude-tool-field-key",
|
|
4757
|
+
children: key.replaceAll("_", " ")
|
|
4758
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
4759
|
+
className: "dsh-claude-tool-field-value",
|
|
4760
|
+
children: displayValue(item)
|
|
4761
|
+
})]
|
|
4762
|
+
}, key))
|
|
4763
|
+
});
|
|
4764
|
+
}
|
|
4765
|
+
function Paths({ paths }) {
|
|
4766
|
+
if (paths.length === 0) return null;
|
|
4767
|
+
return /* @__PURE__ */ jsx("ul", {
|
|
4768
|
+
className: "dsh-claude-tool-paths",
|
|
4769
|
+
children: paths.map((path, index) => /* @__PURE__ */ jsx("li", {
|
|
4770
|
+
className: "dsh-claude-tool-path",
|
|
4771
|
+
children: path
|
|
4772
|
+
}, `${path}:${index}`))
|
|
4773
|
+
});
|
|
4774
|
+
}
|
|
4775
|
+
function Source({ content, start = 1 }) {
|
|
4776
|
+
return /* @__PURE__ */ jsx("div", {
|
|
4777
|
+
className: "dsh-claude-tool-code",
|
|
4778
|
+
children: content.split(/\r?\n/u).map((line, index) => /* @__PURE__ */ jsxs("div", {
|
|
4779
|
+
className: "dsh-claude-tool-code-line",
|
|
4780
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
4781
|
+
className: "dsh-claude-tool-line-number",
|
|
4782
|
+
children: start + index
|
|
4783
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
4784
|
+
className: "dsh-claude-tool-line-text",
|
|
4785
|
+
children: line || " "
|
|
4786
|
+
})]
|
|
4787
|
+
}, index))
|
|
4788
|
+
});
|
|
4789
|
+
}
|
|
4790
|
+
/** The command as a shell prompt rather than a labelled field: it was typed
|
|
4791
|
+
* at one, and the prompt is what tells a reader that at a glance. */
|
|
4792
|
+
function Terminal({ command }) {
|
|
4793
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4794
|
+
className: "dsh-claude-tool-terminal",
|
|
4795
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
4796
|
+
className: "dsh-claude-tool-prompt",
|
|
4797
|
+
"aria-hidden": "true",
|
|
4798
|
+
children: "$"
|
|
4799
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
4800
|
+
className: "dsh-claude-tool-command",
|
|
4801
|
+
children: command
|
|
4802
|
+
})]
|
|
4803
|
+
});
|
|
4804
|
+
}
|
|
4805
|
+
function TextDetail({ title, value }) {
|
|
4806
|
+
if (value === void 0 || value.length === 0) return null;
|
|
4807
|
+
return /* @__PURE__ */ jsx(Section, {
|
|
4808
|
+
title,
|
|
4809
|
+
children: /* @__PURE__ */ jsx("pre", {
|
|
4810
|
+
className: "dsh-claude-tool-detail",
|
|
4811
|
+
children: value
|
|
4812
|
+
})
|
|
4813
|
+
});
|
|
4814
|
+
}
|
|
4815
|
+
function filenameList(value) {
|
|
4816
|
+
if (!Array.isArray(value)) return [];
|
|
4817
|
+
return value.filter((item) => typeof item === "string");
|
|
4818
|
+
}
|
|
4819
|
+
function ToolPresentation({ tool, t }) {
|
|
4820
|
+
const inputValue = parsedValue(tool.input);
|
|
4821
|
+
const outputValue = parsedValue(tool.output);
|
|
4822
|
+
const input = record$6(inputValue);
|
|
4823
|
+
const output = record$6(outputValue);
|
|
4824
|
+
const outputTitle = tool.isError === true ? t("toolError") : t("toolOutput");
|
|
4825
|
+
if (tool.diffs !== void 0) return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(DiffBlock, { diffs: [...tool.diffs] }), /* @__PURE__ */ jsx(TextDetail, {
|
|
4826
|
+
title: outputTitle,
|
|
4827
|
+
value: typeof outputValue === "string" ? outputValue : void 0
|
|
4828
|
+
})] });
|
|
4829
|
+
if (tool.toolName === "Read") {
|
|
4830
|
+
const file = record$6(output?.file);
|
|
4831
|
+
const path = text$1(file?.filePath) ?? text$1(input?.file_path);
|
|
4832
|
+
const content = text$1(file?.content) ?? (typeof outputValue === "string" ? outputValue : void 0);
|
|
4833
|
+
const offset = numberValue(input?.offset) ?? 1;
|
|
4834
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
4835
|
+
path === void 0 ? null : /* @__PURE__ */ jsx(Section, {
|
|
4836
|
+
title: "File",
|
|
4837
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
4838
|
+
className: "dsh-claude-tool-path",
|
|
4839
|
+
children: path
|
|
4840
|
+
})
|
|
4841
|
+
}),
|
|
4842
|
+
input === void 0 ? null : /* @__PURE__ */ jsx(Section, {
|
|
4843
|
+
title: t("toolInput"),
|
|
4844
|
+
children: /* @__PURE__ */ jsx(Fields, {
|
|
4845
|
+
value: input,
|
|
4846
|
+
omit: ["file_path"]
|
|
4847
|
+
})
|
|
4848
|
+
}),
|
|
4849
|
+
content === void 0 ? null : /* @__PURE__ */ jsx(Section, {
|
|
4850
|
+
title: outputTitle,
|
|
4851
|
+
children: /* @__PURE__ */ jsx(Source, {
|
|
4852
|
+
content,
|
|
4853
|
+
start: offset
|
|
4854
|
+
})
|
|
4855
|
+
})
|
|
4856
|
+
] });
|
|
4791
4857
|
}
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
controller.signal.addEventListener("abort", stop, { once: true });
|
|
4808
|
-
const decoder = new TextDecoder();
|
|
4809
|
-
let buffer = "";
|
|
4810
|
-
while (!controller.signal.aborted) {
|
|
4811
|
-
const chunk = await reader.read();
|
|
4812
|
-
buffer += decoder.decode(chunk.value, { stream: !chunk.done });
|
|
4813
|
-
const lines = buffer.split(NDJSON_SEPARATOR);
|
|
4814
|
-
buffer = lines.pop() ?? "";
|
|
4815
|
-
for (const line of lines) this.#dispatch(line);
|
|
4816
|
-
if (chunk.done) break;
|
|
4817
|
-
}
|
|
4818
|
-
controller.signal.removeEventListener("abort", stop);
|
|
4819
|
-
await reader.cancel().catch(() => void 0);
|
|
4820
|
-
} catch (error) {
|
|
4821
|
-
if (isAbort(error)) {
|
|
4822
|
-
if (this.#controller !== controller) continue;
|
|
4823
|
-
return;
|
|
4824
|
-
}
|
|
4825
|
-
} finally {
|
|
4826
|
-
if (this.#controller === controller) this.#controller = void 0;
|
|
4827
|
-
}
|
|
4828
|
-
if (this.#disposed || this.#wanted.size === 0) return;
|
|
4829
|
-
if (superseded) continue;
|
|
4830
|
-
await delay(this.#retryDelayMs);
|
|
4831
|
-
}
|
|
4832
|
-
} finally {
|
|
4833
|
-
this.#running = false;
|
|
4834
|
-
}
|
|
4858
|
+
if (tool.toolName === "Grep" || tool.toolName === "Glob") {
|
|
4859
|
+
const filenames = filenameList(output?.filenames);
|
|
4860
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [input === void 0 ? /* @__PURE__ */ jsx(TextDetail, {
|
|
4861
|
+
title: t("toolInput"),
|
|
4862
|
+
value: typeof inputValue === "string" ? inputValue : void 0
|
|
4863
|
+
}) : /* @__PURE__ */ jsx(Section, {
|
|
4864
|
+
title: t("toolInput"),
|
|
4865
|
+
children: /* @__PURE__ */ jsx(Fields, { value: input })
|
|
4866
|
+
}), filenames.length === 0 ? /* @__PURE__ */ jsx(TextDetail, {
|
|
4867
|
+
title: outputTitle,
|
|
4868
|
+
value: typeof outputValue === "string" ? outputValue : output === void 0 ? void 0 : displayValue(output)
|
|
4869
|
+
}) : /* @__PURE__ */ jsx(Section, {
|
|
4870
|
+
title: outputTitle,
|
|
4871
|
+
children: /* @__PURE__ */ jsx(Paths, { paths: filenames })
|
|
4872
|
+
})] });
|
|
4835
4873
|
}
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4874
|
+
if (tool.toolName === "Bash" || tool.toolName === "PowerShell") {
|
|
4875
|
+
const command = text$1(input?.command);
|
|
4876
|
+
const terminal = [text$1(output?.stdout), text$1(output?.stderr)].filter((value) => value !== void 0).join("\n");
|
|
4877
|
+
const typed = command ?? (typeof inputValue === "string" ? inputValue : void 0);
|
|
4878
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
4879
|
+
typed === void 0 ? null : /* @__PURE__ */ jsx(Terminal, { command: typed }),
|
|
4880
|
+
input === void 0 ? null : /* @__PURE__ */ jsx(Section, {
|
|
4881
|
+
title: t("toolInput"),
|
|
4882
|
+
children: /* @__PURE__ */ jsx(Fields, {
|
|
4883
|
+
value: input,
|
|
4884
|
+
omit: ["command", "description"]
|
|
4885
|
+
})
|
|
4886
|
+
}),
|
|
4887
|
+
/* @__PURE__ */ jsx(TextDetail, {
|
|
4888
|
+
title: outputTitle,
|
|
4889
|
+
value: terminal || (typeof outputValue === "string" ? outputValue : void 0)
|
|
4890
|
+
})
|
|
4891
|
+
] });
|
|
4848
4892
|
}
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
".dsh-claude-tool-summary-row::-webkit-details-marker{display:none}",
|
|
4863
|
-
".dsh-claude-tool-summary-row::after{content:\"›\";margin-left:2px;flex:none;align-self:center;color:var(--dsw-alias-label-secondary);font-size:22px;line-height:1;transform-origin:center;transition:transform .15s ease}",
|
|
4864
|
-
".dsh-claude-tool-item[open]>.dsh-claude-tool-summary-row::after{transform:rotate(90deg)}",
|
|
4865
|
-
".dsh-claude-tool-content{min-width:0;padding:0 10px 8px}",
|
|
4866
|
-
".dsh-claude-tool-content>*{max-width:100%;box-sizing:border-box}",
|
|
4867
|
-
".dsh-claude-tool-section{margin-top:8px}",
|
|
4868
|
-
".dsh-claude-tool-section-title{margin-bottom:4px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;text-transform:uppercase;letter-spacing:.04em}",
|
|
4869
|
-
".dsh-claude-tool-fields{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:3px 12px;font-size:13px;line-height:20px}",
|
|
4870
|
-
".dsh-claude-tool-field-key{color:var(--dsw-alias-label-tertiary)}",
|
|
4871
|
-
".dsh-claude-tool-field-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:pre-wrap;overflow-wrap:anywhere}",
|
|
4872
|
-
".dsh-claude-tool-paths{display:flex;flex-direction:column;gap:2px;margin:0;padding:0;list-style:none;font:var(--dsw-font-markdown-code-block-small)}",
|
|
4873
|
-
".dsh-claude-tool-path{overflow-wrap:anywhere;color:var(--dsw-alias-label-primary)}",
|
|
4874
|
-
".dsh-claude-tool-code{max-height:260px;overflow:auto;margin:0;padding:8px 0;border-radius:8px;background:var(--dsw-alias-markdown-code-block);font:var(--dsw-font-markdown-code-block-small)}",
|
|
4875
|
-
".dsh-claude-tool-code-line{display:grid;grid-template-columns:44px minmax(max-content,1fr);min-height:18px}",
|
|
4876
|
-
".dsh-claude-tool-line-number{padding-right:10px;text-align:right;user-select:none;color:var(--dsw-alias-label-caption);border-right:1px solid var(--dsw-alias-border-l1, color-mix(in srgb, currentColor 12%, transparent))}",
|
|
4877
|
-
".dsh-claude-tool-line-text{padding:0 10px;white-space:pre}",
|
|
4878
|
-
".dsh-claude-tool-label{width:72px;flex:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}",
|
|
4879
|
-
".dsh-claude-tool-description{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;line-height:20px;color:var(--dsw-alias-label-primary)}",
|
|
4880
|
-
".dsh-claude-tool-stats{display:inline-flex;gap:4px;flex:none;font-size:13px;line-height:20px}",
|
|
4881
|
-
".dsh-claude-diff-add{color:var(--dsw-alias-state-success-primary,#21c55d)}",
|
|
4882
|
-
".dsh-claude-diff-delete{color:var(--dsw-alias-state-error-primary)}",
|
|
4883
|
-
".dsh-claude-tool-name{font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary)}",
|
|
4884
|
-
".dsh-claude-tool-summary{margin-left:8px;color:var(--dsw-alias-label-tertiary)}",
|
|
4885
|
-
".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
|
-
".dsh-claude-flow-row{position:relative;overflow:hidden}",
|
|
4887
|
-
".dsh-claude-flow-leading{flex-shrink:0}",
|
|
4888
|
-
".dsh-claude-flow-title{font-weight:400}",
|
|
4889
|
-
".dsh-claude-flow-chevron{color:var(--dsw-alias-label-secondary)}",
|
|
4890
|
-
".dsh-claude-flow-separator{width:2px;height:2px;margin:0 8px;border-radius:1px;background:var(--dsw-alias-label-caption);flex:none}",
|
|
4891
|
-
".dsh-claude-flow-summary{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:24px;flex:auto}",
|
|
4892
|
-
".dsh-claude-flow-summary[data-error]{color:var(--dsw-alias-state-error-primary)}",
|
|
4893
|
-
".dsh-claude-flow-body{margin:4px 0 4px 22px;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:24px;white-space:pre-wrap;overflow-wrap:anywhere}",
|
|
4894
|
-
".dsh-claude-flow-detail{max-height:260px;overflow:auto;margin:4px 0 4px 4px;padding:12px 16px;border:1px solid var(--dsw-alias-border-l1, color-mix(in srgb, currentColor 12%, transparent));border-radius:12px;background:var(--dsw-alias-markdown-code-block);color:var(--dsw-alias-label-primary);font:var(--dsw-font-markdown-code-block-small);white-space:pre-wrap}",
|
|
4895
|
-
".dsh-claude-flow-subcalls{display:flex;flex-direction:column;gap:2px;margin:4px 0 4px 22px;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:22px}",
|
|
4896
|
-
".dsh-claude-compaction{display:flex;align-items:center;gap:10px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}",
|
|
4897
|
-
".dsh-claude-compaction::before,.dsh-claude-compaction::after{content:\"\";flex:1;height:1px;background:var(--dsw-alias-border-l1, color-mix(in srgb, currentColor 12%, transparent))}",
|
|
4898
|
-
".dsh-claude-compaction-label{flex:none;white-space:nowrap}",
|
|
4899
|
-
".dsh-claude-act-running{animation:dsh-claude-act-pulse 1.2s ease-in-out infinite}",
|
|
4900
|
-
"@keyframes dsh-claude-act-pulse{0%,100%{opacity:1}50%{opacity:.3}}"
|
|
4901
|
-
].join("");
|
|
4902
|
-
let cssInjected$3 = false;
|
|
4903
|
-
function ensureCss$3() {
|
|
4904
|
-
if (cssInjected$3 || typeof document === "undefined") return;
|
|
4905
|
-
cssInjected$3 = true;
|
|
4906
|
-
const element = document.createElement("style");
|
|
4907
|
-
element.dataset.dshClaudeActivity = "";
|
|
4908
|
-
element.textContent = ACTIVITY_CSS;
|
|
4909
|
-
document.head.appendChild(element);
|
|
4893
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [input === void 0 ? /* @__PURE__ */ jsx(TextDetail, {
|
|
4894
|
+
title: t("toolInput"),
|
|
4895
|
+
value: typeof inputValue === "string" ? inputValue : void 0
|
|
4896
|
+
}) : /* @__PURE__ */ jsx(Section, {
|
|
4897
|
+
title: t("toolInput"),
|
|
4898
|
+
children: /* @__PURE__ */ jsx(Fields, { value: input })
|
|
4899
|
+
}), output === void 0 ? /* @__PURE__ */ jsx(TextDetail, {
|
|
4900
|
+
title: outputTitle,
|
|
4901
|
+
value: typeof outputValue === "string" ? outputValue : void 0
|
|
4902
|
+
}) : /* @__PURE__ */ jsx(Section, {
|
|
4903
|
+
title: outputTitle,
|
|
4904
|
+
children: /* @__PURE__ */ jsx(Fields, { value: output })
|
|
4905
|
+
})] });
|
|
4910
4906
|
}
|
|
4911
|
-
function
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4907
|
+
function ClaudeTranscriptToolItem({ tool, t }) {
|
|
4908
|
+
return /* @__PURE__ */ jsxs("details", {
|
|
4909
|
+
className: "dsh-claude-tool-item",
|
|
4910
|
+
children: [/* @__PURE__ */ jsxs("summary", {
|
|
4911
|
+
className: "dsh-claude-tool-summary-row",
|
|
4912
|
+
children: [
|
|
4913
|
+
/* @__PURE__ */ jsx("span", {
|
|
4914
|
+
className: "dsh-claude-tool-label",
|
|
4915
|
+
children: tool.toolName
|
|
4916
|
+
}),
|
|
4917
|
+
/* @__PURE__ */ jsx("span", {
|
|
4918
|
+
className: "dsh-claude-tool-description",
|
|
4919
|
+
children: tool.description
|
|
4920
|
+
}),
|
|
4921
|
+
tool.additions === void 0 && tool.deletions === void 0 ? null : /* @__PURE__ */ jsxs("span", {
|
|
4922
|
+
className: "dsh-claude-tool-stats",
|
|
4923
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
4924
|
+
className: "dsh-claude-diff-add",
|
|
4925
|
+
children: ["+", tool.additions ?? 0]
|
|
4926
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
4927
|
+
className: "dsh-claude-diff-delete",
|
|
4928
|
+
children: ["−", tool.deletions ?? 0]
|
|
4929
|
+
})]
|
|
4930
|
+
})
|
|
4931
|
+
]
|
|
4932
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
4933
|
+
className: "dsh-claude-tool-content",
|
|
4934
|
+
children: [tool.subcalls.length === 0 ? null : /* @__PURE__ */ jsx("div", {
|
|
4935
|
+
className: "dsh-claude-flow-subcalls",
|
|
4936
|
+
children: tool.subcalls.map((subcall) => /* @__PURE__ */ jsxs("div", { children: [
|
|
4937
|
+
subcallGlyph(subcall),
|
|
4938
|
+
" ",
|
|
4939
|
+
subcall.toolName ?? t("subagent"),
|
|
4940
|
+
subcall.summary === void 0 ? "" : ` · ${subcall.summary}`
|
|
4941
|
+
] }, subcall.toolUseId))
|
|
4942
|
+
}), /* @__PURE__ */ jsx(ToolPresentation, {
|
|
4943
|
+
tool,
|
|
4944
|
+
t
|
|
4945
|
+
})]
|
|
4946
|
+
})]
|
|
4947
|
+
});
|
|
4948
|
+
}
|
|
4949
|
+
function ClaudeTranscriptToolGroup({ tools, additions, deletions, files: _files, t }) {
|
|
4950
|
+
const [open, setOpen] = useState(false);
|
|
4951
|
+
const failed = tools.some((tool) => tool.isError === true || tool.phase === "failed");
|
|
4952
|
+
const running = tools.some((tool) => tool.phase === "started" || tool.phase === "updated");
|
|
4953
|
+
const summary = tools.length === 1 ? t("usedTool") : t("usedTools", { count: tools.length });
|
|
4954
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4955
|
+
className: "dsh-claude-tool-group-native",
|
|
4956
|
+
children: [/* @__PURE__ */ jsx(DisclosureRow, {
|
|
4957
|
+
rowClassName: "dsh-claude-flow-row",
|
|
4958
|
+
leadingClassName: "dsh-claude-flow-leading",
|
|
4959
|
+
titleClassName: "dsh-claude-flow-title",
|
|
4960
|
+
chevronClassName: "dsh-claude-flow-chevron",
|
|
4961
|
+
icon: failed ? /* @__PURE__ */ jsx(StateDot, { state: "error" }) : running ? /* @__PURE__ */ jsx(StateDot, { state: "ongoing" }) : /* @__PURE__ */ jsx(IconApiOutline14, { size: 14 }),
|
|
4962
|
+
title: summary,
|
|
4963
|
+
open,
|
|
4964
|
+
expandable: true,
|
|
4965
|
+
expandOnRowClick: true,
|
|
4966
|
+
keepContentWhenOpen: true,
|
|
4967
|
+
onToggle: () => setOpen((value) => !value),
|
|
4968
|
+
collapsedContent: additions !== void 0 || deletions !== void 0 ? /* @__PURE__ */ jsxs("span", {
|
|
4969
|
+
className: "dsh-claude-tool-stats",
|
|
4970
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
4971
|
+
className: "dsh-claude-diff-add",
|
|
4972
|
+
children: ["+", additions ?? 0]
|
|
4973
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
4974
|
+
className: "dsh-claude-diff-delete",
|
|
4975
|
+
children: ["−", deletions ?? 0]
|
|
4976
|
+
})]
|
|
4977
|
+
}) : void 0
|
|
4978
|
+
}), open ? /* @__PURE__ */ jsx("div", {
|
|
4979
|
+
className: "dsh-claude-tool-list",
|
|
4980
|
+
children: tools.map((tool) => /* @__PURE__ */ jsx(ClaudeTranscriptToolItem, {
|
|
4981
|
+
tool,
|
|
4982
|
+
t
|
|
4983
|
+
}, tool.toolUseId))
|
|
4984
|
+
}) : null]
|
|
4985
|
+
});
|
|
4915
4986
|
}
|
|
4916
|
-
|
|
4917
|
-
|
|
4987
|
+
/** Round a duration the way a reader reads one: no more precision than the
|
|
4988
|
+
* number deserves. */
|
|
4989
|
+
function formatTurnDuration(ms) {
|
|
4990
|
+
if (ms < 1e3) return `${Math.max(1, Math.round(ms))}ms`;
|
|
4991
|
+
const seconds = ms / 1e3;
|
|
4992
|
+
if (seconds < 60) return `${seconds < 10 ? seconds.toFixed(1) : String(Math.round(seconds))}s`;
|
|
4993
|
+
const whole = Math.round(seconds);
|
|
4994
|
+
return `${Math.floor(whole / 60)}m ${String(whole % 60).padStart(2, "0")}s`;
|
|
4918
4995
|
}
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4996
|
+
/** Share of the prompt that was served from cache.
|
|
4997
|
+
*
|
|
4998
|
+
* Cache reads are counted against everything the prompt cost to assemble --
|
|
4999
|
+
* fresh input and cache writes included -- so a turn that read nothing scores
|
|
5000
|
+
* zero rather than dividing by nothing. */
|
|
5001
|
+
function cacheHitRate(usage) {
|
|
5002
|
+
const read = usage.cacheReadTokens ?? 0;
|
|
5003
|
+
const total = read + (usage.cacheCreationTokens ?? 0) + (usage.inputTokens ?? 0);
|
|
5004
|
+
return total === 0 ? void 0 : read / total;
|
|
5005
|
+
}
|
|
5006
|
+
/** The turn's accounting, in the order a reader wants it: size, then cost of
|
|
5007
|
+
* assembling it, then how long it took, then money. */
|
|
5008
|
+
function turnUsageParts(usage, t) {
|
|
5009
|
+
const tokens = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0) + (usage.cacheReadTokens ?? 0) + (usage.cacheCreationTokens ?? 0);
|
|
5010
|
+
const cached = cacheHitRate(usage);
|
|
5011
|
+
const parts = [];
|
|
5012
|
+
if (tokens > 0) parts.push(t("turnUsageTokens", { count: formatTokenCount(tokens) }));
|
|
5013
|
+
if (cached !== void 0) parts.push(t("turnUsageCache", { percent: (cached * 100).toFixed(1) }));
|
|
5014
|
+
if (usage.durationMs !== void 0) parts.push(formatTurnDuration(usage.durationMs));
|
|
5015
|
+
if (usage.ttftMs !== void 0) parts.push(t("turnUsageTtft", { duration: formatTurnDuration(usage.ttftMs) }));
|
|
5016
|
+
if (usage.cumulativeCostUsd !== void 0) parts.push(t("turnUsageCost", { cost: usage.cumulativeCostUsd.toFixed(2) }));
|
|
5017
|
+
return parts;
|
|
4924
5018
|
}
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
const
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
keepContentWhenOpen: true,
|
|
4944
|
-
onToggle: () => setOpen((value) => !value),
|
|
4945
|
-
collapsedContent: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
4946
|
-
className: "dsh-claude-flow-separator",
|
|
4947
|
-
"aria-hidden": "true"
|
|
4948
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
4949
|
-
className: "dsh-claude-flow-summary",
|
|
4950
|
-
"data-error": state === "error" || void 0,
|
|
4951
|
-
children: summary
|
|
4952
|
-
})] }),
|
|
4953
|
-
children: [subcalls.length === 0 ? null : /* @__PURE__ */ jsx("div", {
|
|
4954
|
-
className: "dsh-claude-flow-subcalls",
|
|
4955
|
-
children: subcalls.map((subcall) => /* @__PURE__ */ jsxs("div", { children: [
|
|
4956
|
-
subcallGlyph(subcall),
|
|
4957
|
-
" ",
|
|
4958
|
-
subcall.toolName ?? t("subagent"),
|
|
4959
|
-
subcall.summary === void 0 ? "" : ` · ${subcall.summary}`
|
|
4960
|
-
] }, subcall.toolUseId))
|
|
4961
|
-
}), body === void 0 ? null : activity.kind === "thinking" ? /* @__PURE__ */ jsx("div", {
|
|
4962
|
-
className: "dsh-claude-flow-body",
|
|
4963
|
-
children: body
|
|
4964
|
-
}) : /* @__PURE__ */ jsx("pre", {
|
|
4965
|
-
className: "dsh-claude-flow-detail",
|
|
4966
|
-
children: body
|
|
4967
|
-
})]
|
|
5019
|
+
/** The footer the Host draws under its own assistant message, drawn here for
|
|
5020
|
+
* the steps the Host never had a message for. */
|
|
5021
|
+
function ClaudeTurnUsage({ usage, t }) {
|
|
5022
|
+
const parts = turnUsageParts(usage, t);
|
|
5023
|
+
if (parts.length === 0) return null;
|
|
5024
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5025
|
+
className: "dsh-claude-turn-usage",
|
|
5026
|
+
children: [
|
|
5027
|
+
/* @__PURE__ */ jsx("span", {
|
|
5028
|
+
className: "dsh-claude-turn-usage-label",
|
|
5029
|
+
children: t("turnUsage")
|
|
5030
|
+
}),
|
|
5031
|
+
/* @__PURE__ */ jsx("span", {
|
|
5032
|
+
"aria-hidden": "true",
|
|
5033
|
+
children: "·"
|
|
5034
|
+
}),
|
|
5035
|
+
/* @__PURE__ */ jsx("span", { children: parts.join(" · ") })
|
|
5036
|
+
]
|
|
4968
5037
|
});
|
|
4969
5038
|
}
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
const
|
|
4974
|
-
const
|
|
4975
|
-
const
|
|
5039
|
+
function ClaudeActivityNode({ node, useClaudeProjection, t }) {
|
|
5040
|
+
ensureCss$3();
|
|
5041
|
+
const marker = node.data;
|
|
5042
|
+
const activities = useClaudeProjection((value) => selectStepActivities(value, marker.turn, marker.step));
|
|
5043
|
+
const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? EMPTY_TASKS$1);
|
|
5044
|
+
const items = useMemo(() => transcriptItemsForStep(activities, marker.turn, marker.step, tasks), [
|
|
5045
|
+
activities,
|
|
5046
|
+
marker.step,
|
|
5047
|
+
marker.turn,
|
|
5048
|
+
tasks
|
|
5049
|
+
]);
|
|
5050
|
+
const markdownLabels = useClaudeMarkdownLabels(t);
|
|
5051
|
+
if (items.length === 0) return null;
|
|
4976
5052
|
return /* @__PURE__ */ jsx("div", {
|
|
4977
|
-
className: "dsh-claude-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
5053
|
+
className: "dsh-claude-flow",
|
|
5054
|
+
children: items.map((item) => item.kind === "text" ? /* @__PURE__ */ jsx("div", {
|
|
5055
|
+
className: "dsh-claude-transcript-text",
|
|
5056
|
+
children: /* @__PURE__ */ jsx(ClaudeMarkdown, {
|
|
5057
|
+
text: item.text,
|
|
5058
|
+
labels: markdownLabels
|
|
5059
|
+
})
|
|
5060
|
+
}, `text:${item.ordinal}`) : item.kind === "compaction" ? /* @__PURE__ */ jsx(ClaudeCompactionDivider, {
|
|
5061
|
+
compaction: item.compaction,
|
|
5062
|
+
t
|
|
5063
|
+
}, `compaction:${item.ordinal}`) : item.kind === "usage" ? /* @__PURE__ */ jsx(ClaudeTurnUsage, {
|
|
5064
|
+
usage: item.usage,
|
|
5065
|
+
t
|
|
5066
|
+
}, `usage:${item.ordinal}`) : item.kind === "tools" ? /* @__PURE__ */ jsx(ClaudeTranscriptToolGroup, {
|
|
5067
|
+
tools: item.tools,
|
|
5068
|
+
...item.additions === void 0 ? {} : { additions: item.additions },
|
|
5069
|
+
...item.deletions === void 0 ? {} : { deletions: item.deletions },
|
|
5070
|
+
...item.files === void 0 ? {} : { files: item.files },
|
|
5071
|
+
t
|
|
5072
|
+
}, `tools:${item.ordinal}`) : /* @__PURE__ */ jsx(ActivityRow, {
|
|
5073
|
+
row: item.row,
|
|
5074
|
+
t
|
|
5075
|
+
}, `activity:${item.ordinal}`))
|
|
4983
5076
|
});
|
|
4984
5077
|
}
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
5078
|
+
//#endregion
|
|
5079
|
+
//#region src/client/ClaudeTasksPanel.tsx
|
|
5080
|
+
const STATUS_LABEL = {
|
|
5081
|
+
running: "tasksRunning",
|
|
5082
|
+
completed: "tasksCompleted",
|
|
5083
|
+
failed: "tasksFailed",
|
|
5084
|
+
stopped: "tasksStopped",
|
|
5085
|
+
killed: "tasksKilled"
|
|
5086
|
+
};
|
|
5087
|
+
function visibleTaskGroups(tasks, dismissedSettledIds) {
|
|
5088
|
+
const projected = tasks.filter(isProjectedTask);
|
|
5089
|
+
return {
|
|
5090
|
+
running: projected.filter((task) => task.status === "running"),
|
|
5091
|
+
finished: projected.filter((task) => task.status !== "running" && !dismissedSettledIds.has(task.taskId))
|
|
5092
|
+
};
|
|
4998
5093
|
}
|
|
4999
|
-
function
|
|
5000
|
-
return
|
|
5094
|
+
function activitiesForTask(activities, taskId) {
|
|
5095
|
+
return activities.filter((activity) => activity.taskId === taskId);
|
|
5001
5096
|
}
|
|
5002
|
-
function
|
|
5003
|
-
|
|
5004
|
-
if (typeof value === "string") return value;
|
|
5005
|
-
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
5006
|
-
if (Array.isArray(value)) return value.map(displayValue).join(", ");
|
|
5007
|
-
return record$6(value) === void 0 ? String(value) : Object.entries(value).map(([key, item]) => `${key}: ${displayValue(item)}`).join("\n");
|
|
5097
|
+
function tasksForTurn(tasks, turn) {
|
|
5098
|
+
return tasks.filter((task) => task.originTurn === turn && isProjectedTask(task));
|
|
5008
5099
|
}
|
|
5009
|
-
function
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5100
|
+
function summarizeTurnTasks(tasks) {
|
|
5101
|
+
if (tasks.length === 0) return void 0;
|
|
5102
|
+
const running = tasks.filter((task) => task.status === "running").length;
|
|
5103
|
+
const failed = tasks.filter((task) => task.status === "failed" || task.status === "stopped" || task.status === "killed").length;
|
|
5104
|
+
const completed = tasks.filter((task) => task.status === "completed").length;
|
|
5105
|
+
return {
|
|
5106
|
+
state: running > 0 ? "running" : failed > 0 ? "failed" : "completed",
|
|
5107
|
+
count: tasks.length,
|
|
5108
|
+
running,
|
|
5109
|
+
failed,
|
|
5110
|
+
completed
|
|
5111
|
+
};
|
|
5017
5112
|
}
|
|
5018
|
-
function
|
|
5019
|
-
|
|
5020
|
-
if (
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
children: entries.map(([key, item]) => /* @__PURE__ */ jsxs("div", {
|
|
5024
|
-
style: { display: "contents" },
|
|
5025
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
5026
|
-
className: "dsh-claude-tool-field-key",
|
|
5027
|
-
children: key.replaceAll("_", " ")
|
|
5028
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
5029
|
-
className: "dsh-claude-tool-field-value",
|
|
5030
|
-
children: displayValue(item)
|
|
5031
|
-
})]
|
|
5032
|
-
}, key))
|
|
5033
|
-
});
|
|
5113
|
+
function statusGlyph(status) {
|
|
5114
|
+
if (status === "running") return "●";
|
|
5115
|
+
if (status === "completed") return "✓";
|
|
5116
|
+
if (status === "stopped") return "–";
|
|
5117
|
+
return "×";
|
|
5034
5118
|
}
|
|
5035
|
-
function
|
|
5036
|
-
if (
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
children: path
|
|
5042
|
-
}, `${path}:${index}`))
|
|
5043
|
-
});
|
|
5119
|
+
function formatDuration(ms) {
|
|
5120
|
+
if (ms < 1e3) return String(Math.max(1, Math.round(ms))) + "ms";
|
|
5121
|
+
const seconds = Math.round(ms / 1e3);
|
|
5122
|
+
if (seconds < 60) return String(seconds) + "s";
|
|
5123
|
+
const minutes = Math.floor(seconds / 60);
|
|
5124
|
+
return String(minutes) + "m " + String(seconds % 60) + "s";
|
|
5044
5125
|
}
|
|
5045
|
-
function
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
className: "dsh-claude-tool-line-text",
|
|
5055
|
-
children: line || " "
|
|
5056
|
-
})]
|
|
5057
|
-
}, index))
|
|
5058
|
-
});
|
|
5126
|
+
function taskMeta(task, t) {
|
|
5127
|
+
const parts = [];
|
|
5128
|
+
if (task.subagentType !== void 0) parts.push(task.subagentType);
|
|
5129
|
+
else if (task.taskType !== void 0) parts.push(task.taskType);
|
|
5130
|
+
if (task.usage?.durationMs !== void 0) parts.push(formatDuration(task.usage.durationMs));
|
|
5131
|
+
if (task.usage?.totalTokens !== void 0) parts.push(t("tokens", { count: formatTokenCount(task.usage.totalTokens) }));
|
|
5132
|
+
if (task.usage?.toolUses !== void 0) parts.push(t("tasksToolUses", { count: task.usage.toolUses }));
|
|
5133
|
+
if (task.lastToolName !== void 0) parts.push(t("tasksLastTool", { tool: task.lastToolName }));
|
|
5134
|
+
return parts;
|
|
5059
5135
|
}
|
|
5060
|
-
function
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
children:
|
|
5067
|
-
})
|
|
5136
|
+
function TaskActivity({ activity, t }) {
|
|
5137
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
5138
|
+
style: taskActivityItem,
|
|
5139
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
5140
|
+
style: taskActivityGlyph,
|
|
5141
|
+
"aria-hidden": "true",
|
|
5142
|
+
children: activity.isError === true ? "×" : "›"
|
|
5143
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
5144
|
+
style: taskActivityBody,
|
|
5145
|
+
children: [
|
|
5146
|
+
/* @__PURE__ */ jsx("p", {
|
|
5147
|
+
style: taskActivityTitle,
|
|
5148
|
+
children: activity.title ?? activity.kind
|
|
5149
|
+
}),
|
|
5150
|
+
activity.summary === void 0 ? null : /* @__PURE__ */ jsx("p", {
|
|
5151
|
+
style: taskActivitySummary,
|
|
5152
|
+
children: activity.summary
|
|
5153
|
+
}),
|
|
5154
|
+
activity.detail === void 0 ? null : /* @__PURE__ */ jsxs("details", {
|
|
5155
|
+
style: taskActivityDetail,
|
|
5156
|
+
children: [/* @__PURE__ */ jsx("summary", {
|
|
5157
|
+
style: taskActivityDetailSummary,
|
|
5158
|
+
children: t("detail")
|
|
5159
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
5160
|
+
style: detailCode,
|
|
5161
|
+
children: activity.detail
|
|
5162
|
+
})]
|
|
5163
|
+
})
|
|
5164
|
+
]
|
|
5165
|
+
})]
|
|
5068
5166
|
});
|
|
5069
5167
|
}
|
|
5070
|
-
function
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
const
|
|
5076
|
-
const
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5168
|
+
function TaskCard(props) {
|
|
5169
|
+
const { task, activities, allActivities, t } = props;
|
|
5170
|
+
const tools = useMemo(() => taskTools(allActivities, task.taskId), [allActivities, task.taskId]);
|
|
5171
|
+
const [activityOpen, setActivityOpen] = useState(false);
|
|
5172
|
+
const running = task.status === "running";
|
|
5173
|
+
const failed = task.status === "failed" || task.status === "killed";
|
|
5174
|
+
const meta = taskMeta(task, t);
|
|
5175
|
+
return /* @__PURE__ */ jsxs("article", {
|
|
5176
|
+
style: {
|
|
5177
|
+
...taskCard,
|
|
5178
|
+
...running ? taskCardRunning : {}
|
|
5179
|
+
},
|
|
5180
|
+
children: [
|
|
5181
|
+
/* @__PURE__ */ jsxs("div", {
|
|
5182
|
+
style: taskCardTop,
|
|
5183
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
5184
|
+
className: running ? "dsh-claude-act-running" : void 0,
|
|
5185
|
+
style: {
|
|
5186
|
+
...taskCardGlyph,
|
|
5187
|
+
...running ? iconChipRunning : {},
|
|
5188
|
+
...failed ? iconChipError : {}
|
|
5189
|
+
},
|
|
5190
|
+
"aria-hidden": "true",
|
|
5191
|
+
children: statusGlyph(task.status)
|
|
5192
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
5193
|
+
style: taskCardBody,
|
|
5194
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
5195
|
+
style: {
|
|
5196
|
+
...taskTitle,
|
|
5197
|
+
...failed ? { color: "var(--dsw-alias-state-error-primary)" } : {}
|
|
5198
|
+
},
|
|
5199
|
+
children: task.description
|
|
5200
|
+
}), /* @__PURE__ */ jsxs("p", {
|
|
5201
|
+
style: taskStatusLine,
|
|
5202
|
+
children: [/* @__PURE__ */ jsx("span", { children: t(STATUS_LABEL[task.status] ?? "tasksRunning") }), task.backgrounded === true ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
5203
|
+
"aria-hidden": "true",
|
|
5204
|
+
children: " · "
|
|
5205
|
+
}), /* @__PURE__ */ jsx("span", { children: t("tasksBackground") })] }) : null]
|
|
5206
|
+
})]
|
|
5207
|
+
})]
|
|
5103
5208
|
}),
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
children:
|
|
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)
|
|
5209
|
+
meta.length === 0 ? null : /* @__PURE__ */ jsx("p", {
|
|
5210
|
+
style: taskMeta$1,
|
|
5211
|
+
children: meta.join(" · ")
|
|
5136
5212
|
}),
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
children:
|
|
5140
|
-
value: input,
|
|
5141
|
-
omit: ["command"]
|
|
5142
|
-
})
|
|
5213
|
+
task.summary === void 0 || running ? null : /* @__PURE__ */ jsx("p", {
|
|
5214
|
+
style: taskSummary,
|
|
5215
|
+
children: task.summary
|
|
5143
5216
|
}),
|
|
5144
|
-
/* @__PURE__ */
|
|
5145
|
-
|
|
5146
|
-
|
|
5217
|
+
activities.length === 0 && tools.length === 0 ? null : /* @__PURE__ */ jsxs("div", {
|
|
5218
|
+
style: taskActivitySection,
|
|
5219
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
5220
|
+
type: "button",
|
|
5221
|
+
style: taskTextButton,
|
|
5222
|
+
"aria-expanded": activityOpen,
|
|
5223
|
+
onClick: () => setActivityOpen((value) => !value),
|
|
5224
|
+
children: activityOpen ? t("tasksHideActivity") : t("tasksViewActivity")
|
|
5225
|
+
}), !activityOpen ? null : tools.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
5226
|
+
style: taskToolList,
|
|
5227
|
+
children: tools.map((tool) => /* @__PURE__ */ jsx(ClaudeTranscriptToolItem, {
|
|
5228
|
+
tool,
|
|
5229
|
+
t
|
|
5230
|
+
}, tool.toolUseId))
|
|
5231
|
+
}) : /* @__PURE__ */ jsx("ul", {
|
|
5232
|
+
style: taskActivityList,
|
|
5233
|
+
children: activities.map((activity) => /* @__PURE__ */ jsx(TaskActivity, {
|
|
5234
|
+
activity,
|
|
5235
|
+
t
|
|
5236
|
+
}, `${activity.turn}:${activity.step}:${activity.ordinal}`))
|
|
5237
|
+
})]
|
|
5147
5238
|
})
|
|
5148
|
-
]
|
|
5149
|
-
}
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
children:
|
|
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 })
|
|
5239
|
+
]
|
|
5240
|
+
});
|
|
5241
|
+
}
|
|
5242
|
+
function GroupHeading(props) {
|
|
5243
|
+
const { label, count, collapsed, onToggle, action } = props;
|
|
5244
|
+
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", { children: label }), /* @__PURE__ */ jsx("span", {
|
|
5245
|
+
style: tasksGroupCount,
|
|
5246
|
+
children: count
|
|
5162
5247
|
})] });
|
|
5248
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5249
|
+
style: tasksGroupHeading,
|
|
5250
|
+
children: [onToggle === void 0 ? /* @__PURE__ */ jsx("div", {
|
|
5251
|
+
style: tasksGroupTitle,
|
|
5252
|
+
children: content
|
|
5253
|
+
}) : /* @__PURE__ */ jsxs("button", {
|
|
5254
|
+
type: "button",
|
|
5255
|
+
style: tasksGroupToggle,
|
|
5256
|
+
"aria-expanded": !collapsed,
|
|
5257
|
+
onClick: onToggle,
|
|
5258
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
5259
|
+
style: {
|
|
5260
|
+
...chevron,
|
|
5261
|
+
...collapsed === true ? {} : chevronOpen
|
|
5262
|
+
},
|
|
5263
|
+
children: "›"
|
|
5264
|
+
}), content]
|
|
5265
|
+
}), action === void 0 ? null : /* @__PURE__ */ jsx("button", {
|
|
5266
|
+
type: "button",
|
|
5267
|
+
style: taskTextButton,
|
|
5268
|
+
onClick: action.onClick,
|
|
5269
|
+
children: action.label
|
|
5270
|
+
})]
|
|
5271
|
+
});
|
|
5163
5272
|
}
|
|
5164
|
-
function
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5273
|
+
function ClaudeTasksPanel({ useClaudeProjection, t, closeDetails, turn }) {
|
|
5274
|
+
const projection = useClaudeProjection((value) => value);
|
|
5275
|
+
const tasks = useMemo(() => tasksForTurn(projection.tasks?.tasks ?? [], turn), [projection.tasks, turn]);
|
|
5276
|
+
useEffect(() => {
|
|
5277
|
+
if (!projection.owned || tasks.length === 0) closeDetails();
|
|
5278
|
+
}, [
|
|
5279
|
+
closeDetails,
|
|
5280
|
+
projection.owned,
|
|
5281
|
+
tasks.length
|
|
5282
|
+
]);
|
|
5283
|
+
const [finishedCollapsed, setFinishedCollapsed] = useState(false);
|
|
5284
|
+
const [dismissedSettledIds, setDismissedSettledIds] = useState(() => /* @__PURE__ */ new Set());
|
|
5285
|
+
const groups = useMemo(() => visibleTaskGroups(tasks, dismissedSettledIds), [tasks, dismissedSettledIds]);
|
|
5286
|
+
const taskActivities = useMemo(() => new Map(tasks.map((task) => [task.taskId, activitiesForTask(projection.activities, task.taskId)])), [projection.activities, tasks]);
|
|
5287
|
+
const clearFinished = () => setDismissedSettledIds((previous) => /* @__PURE__ */ new Set([...previous, ...tasks.filter((task) => task.status !== "running").map((task) => task.taskId)]));
|
|
5288
|
+
if (!projection.owned) return null;
|
|
5289
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
5290
|
+
className: detailsCardClass,
|
|
5291
|
+
style: tasksPanel,
|
|
5292
|
+
children: [
|
|
5293
|
+
/* @__PURE__ */ jsxs("style", {
|
|
5294
|
+
"data-dsh-claude-panel-icon-styles": true,
|
|
5295
|
+
children: [detailsCardCss, panelIconButtonCss]
|
|
5296
|
+
}),
|
|
5297
|
+
/* @__PURE__ */ jsxs("div", {
|
|
5298
|
+
style: tasksHeader,
|
|
5299
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("span", {
|
|
5300
|
+
style: tasksHeading,
|
|
5301
|
+
children: t("tasksPanelTurn")
|
|
5302
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
5303
|
+
style: tasksTurnMeta,
|
|
5304
|
+
children: t("tasksTurnNumber", { turn })
|
|
5305
|
+
})] }), /* @__PURE__ */ jsx("button", {
|
|
5306
|
+
type: "button",
|
|
5307
|
+
className: panelIconButtonClass,
|
|
5308
|
+
"aria-label": t("tasksClose"),
|
|
5309
|
+
onClick: closeDetails,
|
|
5310
|
+
children: /* @__PURE__ */ jsx(IconCloseOutline16, {})
|
|
5311
|
+
})]
|
|
5312
|
+
}),
|
|
5313
|
+
/* @__PURE__ */ jsxs("div", {
|
|
5314
|
+
style: tasksBody,
|
|
5315
|
+
children: [/* @__PURE__ */ jsxs("section", {
|
|
5316
|
+
"aria-label": t("tasksRunning"),
|
|
5317
|
+
children: [/* @__PURE__ */ jsx(GroupHeading, {
|
|
5318
|
+
label: t("tasksRunning"),
|
|
5319
|
+
count: groups.running.length
|
|
5320
|
+
}), groups.running.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
5321
|
+
style: tasksGroupEmpty,
|
|
5322
|
+
children: t("tasksNoneRunning")
|
|
5323
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
5324
|
+
style: taskCardList,
|
|
5325
|
+
children: groups.running.map((task) => /* @__PURE__ */ jsx(TaskCard, {
|
|
5326
|
+
task,
|
|
5327
|
+
activities: taskActivities.get(task.taskId) ?? [],
|
|
5328
|
+
allActivities: projection.activities,
|
|
5329
|
+
t
|
|
5330
|
+
}, task.taskId))
|
|
5186
5331
|
})]
|
|
5187
|
-
})
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5332
|
+
}), /* @__PURE__ */ jsxs("section", {
|
|
5333
|
+
"aria-label": t("tasksSettled"),
|
|
5334
|
+
style: tasksFinishedSection,
|
|
5335
|
+
children: [/* @__PURE__ */ jsx(GroupHeading, {
|
|
5336
|
+
label: t("tasksSettled"),
|
|
5337
|
+
count: groups.finished.length,
|
|
5338
|
+
collapsed: finishedCollapsed,
|
|
5339
|
+
onToggle: () => setFinishedCollapsed((value) => !value),
|
|
5340
|
+
...groups.finished.length === 0 ? {} : { action: {
|
|
5341
|
+
label: t("tasksClear"),
|
|
5342
|
+
onClick: clearFinished
|
|
5343
|
+
} }
|
|
5344
|
+
}), finishedCollapsed || groups.finished.length === 0 ? null : /* @__PURE__ */ jsx("div", {
|
|
5345
|
+
style: taskCardList,
|
|
5346
|
+
children: groups.finished.map((task) => /* @__PURE__ */ jsx(TaskCard, {
|
|
5347
|
+
task,
|
|
5348
|
+
activities: taskActivities.get(task.taskId) ?? [],
|
|
5349
|
+
allActivities: projection.activities,
|
|
5350
|
+
t
|
|
5351
|
+
}, task.taskId))
|
|
5352
|
+
})]
|
|
5353
|
+
})]
|
|
5354
|
+
})
|
|
5355
|
+
]
|
|
5204
5356
|
});
|
|
5205
5357
|
}
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5358
|
+
//#endregion
|
|
5359
|
+
//#region src/client/ClaudeActivityTail.tsx
|
|
5360
|
+
const MAX_HOVER_TASKS = 6;
|
|
5361
|
+
function taskGlyph(status) {
|
|
5362
|
+
if (status === "failed") return {
|
|
5363
|
+
glyph: "×",
|
|
5364
|
+
style: tasksHoverGlyphError
|
|
5365
|
+
};
|
|
5366
|
+
if (status === "completed") return {
|
|
5367
|
+
glyph: "✓",
|
|
5368
|
+
style: tasksHoverGlyphDone
|
|
5369
|
+
};
|
|
5370
|
+
return {
|
|
5371
|
+
glyph: "●",
|
|
5372
|
+
style: tasksHoverGlyphRunning
|
|
5373
|
+
};
|
|
5374
|
+
}
|
|
5375
|
+
function ClaudeTaskLauncher({ turn, tasks, t, openTasks }) {
|
|
5376
|
+
const [hovered, setHovered] = useState(false);
|
|
5377
|
+
const closeTimer = useRef();
|
|
5378
|
+
const turnTasks = useMemo(() => tasksForTurn(tasks, turn), [tasks, turn]);
|
|
5379
|
+
const summary = useMemo(() => summarizeTurnTasks(turnTasks), [turnTasks]);
|
|
5380
|
+
const open = () => {
|
|
5381
|
+
if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
|
|
5382
|
+
closeTimer.current = void 0;
|
|
5383
|
+
setHovered(true);
|
|
5384
|
+
};
|
|
5385
|
+
const scheduleClose = () => {
|
|
5386
|
+
if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
|
|
5387
|
+
closeTimer.current = setTimeout(() => {
|
|
5388
|
+
closeTimer.current = void 0;
|
|
5389
|
+
setHovered(false);
|
|
5390
|
+
}, 350);
|
|
5391
|
+
};
|
|
5392
|
+
useEffect(() => () => {
|
|
5393
|
+
if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
|
|
5394
|
+
}, []);
|
|
5395
|
+
if (summary === void 0) return null;
|
|
5396
|
+
const label = summary.state === "running" ? t("tasksTurnRunning", { count: summary.running }) : summary.state === "failed" ? t("tasksTurnFailed", {
|
|
5397
|
+
failed: summary.failed,
|
|
5398
|
+
completed: summary.completed
|
|
5399
|
+
}) : t("tasksTurnCompleted", { count: summary.completed });
|
|
5400
|
+
const stateStyle = summary.state === "completed" ? tasksBadgeDone : {};
|
|
5401
|
+
const dotStyle = summary.state === "failed" ? tasksBadgeDotError : summary.state === "completed" ? tasksBadgeDotDone : {};
|
|
5402
|
+
return /* @__PURE__ */ jsx("div", {
|
|
5403
|
+
"data-claude-task-launcher": turn,
|
|
5404
|
+
style: tasksBadgeWrap,
|
|
5405
|
+
children: /* @__PURE__ */ jsxs("span", {
|
|
5406
|
+
style: tasksBadgeSeat,
|
|
5407
|
+
onMouseEnter: open,
|
|
5408
|
+
onMouseLeave: scheduleClose,
|
|
5409
|
+
onFocus: open,
|
|
5410
|
+
onBlur: (event) => {
|
|
5411
|
+
if (!event.currentTarget.contains(event.relatedTarget)) scheduleClose();
|
|
5412
|
+
},
|
|
5413
|
+
children: [hovered ? /* @__PURE__ */ jsxs("span", {
|
|
5414
|
+
role: "tooltip",
|
|
5415
|
+
style: tasksHoverCard,
|
|
5416
|
+
onMouseEnter: open,
|
|
5417
|
+
onMouseLeave: scheduleClose,
|
|
5418
|
+
children: [
|
|
5419
|
+
/* @__PURE__ */ jsx("span", {
|
|
5420
|
+
style: tasksHoverHeader,
|
|
5421
|
+
children: label
|
|
5422
|
+
}),
|
|
5423
|
+
turnTasks.slice(0, MAX_HOVER_TASKS).map((task) => {
|
|
5424
|
+
const { glyph, style } = taskGlyph(task.status);
|
|
5425
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
5426
|
+
style: tasksHoverRow,
|
|
5427
|
+
children: [
|
|
5428
|
+
/* @__PURE__ */ jsx("span", {
|
|
5429
|
+
className: task.status === "running" ? "dsh-claude-act-running" : void 0,
|
|
5430
|
+
style: {
|
|
5431
|
+
...tasksHoverGlyph,
|
|
5432
|
+
...style
|
|
5433
|
+
},
|
|
5434
|
+
"aria-hidden": "true",
|
|
5435
|
+
children: glyph
|
|
5436
|
+
}),
|
|
5437
|
+
/* @__PURE__ */ jsx("span", {
|
|
5438
|
+
style: tasksHoverDesc,
|
|
5439
|
+
title: task.description,
|
|
5440
|
+
children: task.description
|
|
5441
|
+
}),
|
|
5442
|
+
task.subagentType === void 0 ? null : /* @__PURE__ */ jsx("span", {
|
|
5443
|
+
style: tasksHoverType,
|
|
5444
|
+
children: task.subagentType
|
|
5445
|
+
})
|
|
5446
|
+
]
|
|
5447
|
+
}, task.taskId);
|
|
5448
|
+
}),
|
|
5449
|
+
turnTasks.length > MAX_HOVER_TASKS ? /* @__PURE__ */ jsxs("span", {
|
|
5450
|
+
style: tasksHoverMore,
|
|
5451
|
+
children: ["+", turnTasks.length - MAX_HOVER_TASKS]
|
|
5452
|
+
}) : null,
|
|
5453
|
+
/* @__PURE__ */ jsx("span", {
|
|
5454
|
+
style: tasksHoverHint,
|
|
5455
|
+
children: t("tasksOpen")
|
|
5456
|
+
})
|
|
5457
|
+
]
|
|
5458
|
+
}) : null, /* @__PURE__ */ jsxs("button", {
|
|
5459
|
+
type: "button",
|
|
5460
|
+
className: "dsh-claude-task-launcher",
|
|
5461
|
+
style: {
|
|
5462
|
+
...tasksTurnBadge,
|
|
5463
|
+
...stateStyle,
|
|
5464
|
+
...hovered ? tasksBadgeHovered : {}
|
|
5465
|
+
},
|
|
5466
|
+
"aria-label": `${label} — ${t("tasksOpen")}`,
|
|
5467
|
+
onClick: () => openTasks(turn),
|
|
5468
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
5469
|
+
className: summary.state === "running" ? "dsh-claude-act-running" : void 0,
|
|
5470
|
+
style: {
|
|
5471
|
+
...tasksBadgeDot,
|
|
5472
|
+
...dotStyle
|
|
5473
|
+
},
|
|
5474
|
+
"aria-hidden": "true"
|
|
5475
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
5476
|
+
style: tasksBadgeLabel,
|
|
5477
|
+
children: label
|
|
5233
5478
|
})]
|
|
5234
|
-
})
|
|
5235
|
-
})
|
|
5236
|
-
className: "dsh-claude-tool-list",
|
|
5237
|
-
children: tools.map((tool) => /* @__PURE__ */ jsx(ClaudeTranscriptToolItem, {
|
|
5238
|
-
tool,
|
|
5239
|
-
t
|
|
5240
|
-
}, tool.toolUseId))
|
|
5241
|
-
}) : null]
|
|
5479
|
+
})]
|
|
5480
|
+
})
|
|
5242
5481
|
});
|
|
5243
5482
|
}
|
|
5244
|
-
function
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5483
|
+
function ClaudeActivityTail({ matched, useClaudeProjection, t, openTasks }) {
|
|
5484
|
+
const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? []);
|
|
5485
|
+
return /* @__PURE__ */ jsx(ClaudeTaskLauncher, {
|
|
5486
|
+
turn: matched.turn,
|
|
5487
|
+
tasks,
|
|
5488
|
+
t,
|
|
5489
|
+
openTasks
|
|
5490
|
+
});
|
|
5491
|
+
}
|
|
5492
|
+
//#endregion
|
|
5493
|
+
//#region src/client/ClaudeActiveTasksNode.tsx
|
|
5494
|
+
const EMPTY_TASKS = [];
|
|
5495
|
+
/** Render the task launcher while the owning DSH turn is still open. */
|
|
5496
|
+
function ClaudeActiveTasksNode({ node, useClaudeProjection, t, openTasks }) {
|
|
5248
5497
|
const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? EMPTY_TASKS);
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
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}`))
|
|
5498
|
+
return /* @__PURE__ */ jsx(ClaudeTaskLauncher, {
|
|
5499
|
+
turn: node.data.turn,
|
|
5500
|
+
tasks,
|
|
5501
|
+
t,
|
|
5502
|
+
openTasks
|
|
5278
5503
|
});
|
|
5279
5504
|
}
|
|
5280
5505
|
//#endregion
|
|
@@ -5394,6 +5619,7 @@ window.__ModuleLoader__.load({
|
|
|
5394
5619
|
if (typeof setting !== "object" || setting === null || Array.isArray(setting)) return false;
|
|
5395
5620
|
const item = setting;
|
|
5396
5621
|
if (typeof item.key !== "string" || typeof item.value !== "string" || ![
|
|
5622
|
+
"immediate",
|
|
5397
5623
|
"new-session",
|
|
5398
5624
|
"next-turn",
|
|
5399
5625
|
"next-worktree",
|
|
@@ -5584,6 +5810,23 @@ window.__ModuleLoader__.load({
|
|
|
5584
5810
|
"seven_day_opus",
|
|
5585
5811
|
"seven_day_sonnet"
|
|
5586
5812
|
]);
|
|
5813
|
+
/** The prose mode a settings payload carries, or the default when it carries
|
|
5814
|
+
* none — an older Host, or a response this Client does not fully understand,
|
|
5815
|
+
* leaves the palette alone rather than guessing. */
|
|
5816
|
+
function proseModeOf(settings) {
|
|
5817
|
+
const value = settings.find((setting) => setting.key === "prose")?.value;
|
|
5818
|
+
return isClaudeProseMode(value) ? value : DEFAULT_CLAUDE_PROSE_MODE;
|
|
5819
|
+
}
|
|
5820
|
+
/** Settings whose row only makes sense under a particular value of another.
|
|
5821
|
+
* Filtering here rather than server-side keeps the descriptor list flat: the
|
|
5822
|
+
* server has no view of what the Client can paint. Fails OPEN — a payload
|
|
5823
|
+
* missing the setting a row depends on shows the row rather than hiding it,
|
|
5824
|
+
* so an older Host cannot make a setting unreachable. */
|
|
5825
|
+
function visibleGlobalSettings(settings) {
|
|
5826
|
+
const renderer = settings.find((setting) => setting.key === "renderer");
|
|
5827
|
+
if (renderer === void 0 || renderer.value !== "native") return settings;
|
|
5828
|
+
return settings.filter((setting) => setting.key !== "prose");
|
|
5829
|
+
}
|
|
5587
5830
|
/** Per-setting label and the effect note that used to sit as a standalone
|
|
5588
5831
|
* paragraph under the card; it now hangs off the label as a hover hint. */
|
|
5589
5832
|
const SETTING_COPY = {
|
|
@@ -5595,6 +5838,10 @@ window.__ModuleLoader__.load({
|
|
|
5595
5838
|
label: "renderer",
|
|
5596
5839
|
hint: "rendererEffect"
|
|
5597
5840
|
},
|
|
5841
|
+
prose: {
|
|
5842
|
+
label: "prose",
|
|
5843
|
+
hint: "proseEffect"
|
|
5844
|
+
},
|
|
5598
5845
|
worktreeBranchPrefix: {
|
|
5599
5846
|
label: "worktreeBranchPrefix",
|
|
5600
5847
|
hint: "worktreeBranchPrefixEffect"
|
|
@@ -5613,7 +5860,9 @@ window.__ModuleLoader__.load({
|
|
|
5613
5860
|
* names) carry no entry and keep the label the route reported. */
|
|
5614
5861
|
const SETTING_OPTION_COPY = {
|
|
5615
5862
|
"renderer:plugin": "rendererPlugin",
|
|
5616
|
-
"renderer:native": "rendererNative"
|
|
5863
|
+
"renderer:native": "rendererNative",
|
|
5864
|
+
"prose:plain": "prosePlain",
|
|
5865
|
+
"prose:enhanced": "proseEnhanced"
|
|
5617
5866
|
};
|
|
5618
5867
|
function settingOptionLabel(settingKey, option, t) {
|
|
5619
5868
|
const key = SETTING_OPTION_COPY[`${settingKey}:${option.value}`];
|
|
@@ -5852,6 +6101,7 @@ window.__ModuleLoader__.load({
|
|
|
5852
6101
|
});
|
|
5853
6102
|
if (!isGlobalSettingsView(payload)) throw new Error("Invalid global settings response");
|
|
5854
6103
|
setGlobalSettings(payload);
|
|
6104
|
+
applyClaudeMarkdownTheme(proseModeOf(payload.settings));
|
|
5855
6105
|
} catch (cause) {
|
|
5856
6106
|
setGlobalSettingsError(cardFailure(cause));
|
|
5857
6107
|
} finally {
|
|
@@ -5962,7 +6212,7 @@ window.__ModuleLoader__.load({
|
|
|
5962
6212
|
globalSettings === void 0 ? /* @__PURE__ */ jsx("p", {
|
|
5963
6213
|
style: notice,
|
|
5964
6214
|
children: t("globalSettingsLoading")
|
|
5965
|
-
}) : globalSettings.settings.map((setting) => {
|
|
6215
|
+
}) : visibleGlobalSettings(globalSettings.settings).map((setting) => {
|
|
5966
6216
|
const copy = SETTING_COPY[setting.key];
|
|
5967
6217
|
return /* @__PURE__ */ jsxs("div", {
|
|
5968
6218
|
style: diagnosticGrid,
|
|
@@ -16702,6 +16952,10 @@ window.__ModuleLoader__.load({
|
|
|
16702
16952
|
rendererPlugin: "插件渲染器",
|
|
16703
16953
|
rendererNative: "DSH 原生渲染器",
|
|
16704
16954
|
rendererEffect: "插件渲染器沿用本插件自带的转录视图:交错的正文、成组的工具卡片与活动行。DSH 原生渲染器改由 DSH 自身绘制:正文作为普通助手文本块,思考作为推理块,Claude 的顶层工具会镜像成原生工具卡片。修改从下一个回合起生效;已经产生的回合仍按记录时的渲染器显示,不会重绘。",
|
|
16955
|
+
prose: "Markdown 彩色高亮",
|
|
16956
|
+
prosePlain: "关闭",
|
|
16957
|
+
proseEnhanced: "开启",
|
|
16958
|
+
proseEffect: "开启后,正文的标题、加粗、斜体、行内代码、列表符号、引用与链接各自着色,代码块换成深色描边底。这会覆盖本插件默认对齐 Claude 桌面版的配色。仅在使用插件渲染器时有效,改动立即生效,无需等待下一回合。",
|
|
16705
16959
|
worktreeBranchPrefix: "Worktree 分支前缀",
|
|
16706
16960
|
worktreeBranchPrefixEffect: "分支前缀修改会应用于之后自动创建的 Worktree 分支;显式指定的完整分支名不会被修改。",
|
|
16707
16961
|
maxProcessesSetting: "Claude 进程上限",
|
|
@@ -17014,7 +17268,12 @@ window.__ModuleLoader__.load({
|
|
|
17014
17268
|
rewindSubmitting: "回退中…",
|
|
17015
17269
|
rewindFailed: "回退失败({code})。",
|
|
17016
17270
|
rewindBusy: "这个会话正在运行,请等本轮结束后再回退。",
|
|
17017
|
-
rewindStale: "宿主进程里的插件还是旧版本(没有回退接口)。重启 DSH 后再试。"
|
|
17271
|
+
rewindStale: "宿主进程里的插件还是旧版本(没有回退接口)。重启 DSH 后再试。",
|
|
17272
|
+
turnUsage: "本回合用量",
|
|
17273
|
+
turnUsageTokens: "{count} tok",
|
|
17274
|
+
turnUsageCache: "缓存命中 {percent}%",
|
|
17275
|
+
turnUsageTtft: "首字 {duration}",
|
|
17276
|
+
turnUsageCost: "累计 ${cost}"
|
|
17018
17277
|
};
|
|
17019
17278
|
const en = {
|
|
17020
17279
|
nav: "Claude Code",
|
|
@@ -17066,6 +17325,10 @@ window.__ModuleLoader__.load({
|
|
|
17066
17325
|
rendererPlugin: "Plugin renderer",
|
|
17067
17326
|
rendererNative: "DSH native renderer",
|
|
17068
17327
|
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.",
|
|
17328
|
+
prose: "Markdown colour highlighting",
|
|
17329
|
+
prosePlain: "Off",
|
|
17330
|
+
proseEnhanced: "On",
|
|
17331
|
+
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
17332
|
worktreeBranchPrefix: "Worktree branch prefix",
|
|
17070
17333
|
worktreeBranchPrefixEffect: "Prefix changes apply to subsequently generated Worktree branches. Explicit full branch names remain unchanged.",
|
|
17071
17334
|
maxProcessesSetting: "Claude process limit",
|
|
@@ -17378,7 +17641,12 @@ window.__ModuleLoader__.load({
|
|
|
17378
17641
|
rewindSubmitting: "Rewinding…",
|
|
17379
17642
|
rewindFailed: "The rewind failed ({code}).",
|
|
17380
17643
|
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."
|
|
17644
|
+
rewindStale: "The Host process is still running the previous plugin build, which has no rewind route. Restart DSH and try again.",
|
|
17645
|
+
turnUsage: "Turn usage",
|
|
17646
|
+
turnUsageTokens: "{count} tok",
|
|
17647
|
+
turnUsageCache: "Cache hit {percent}%",
|
|
17648
|
+
turnUsageTtft: "TTFT {duration}",
|
|
17649
|
+
turnUsageCost: "${cost} total"
|
|
17382
17650
|
};
|
|
17383
17651
|
//#endregion
|
|
17384
17652
|
//#region src/client/index.tsx
|
|
@@ -17428,6 +17696,9 @@ window.__ModuleLoader__.load({
|
|
|
17428
17696
|
}), "dsh-claude: client copy");
|
|
17429
17697
|
const t = ctx.locale.bind(namespace);
|
|
17430
17698
|
ctx.effect(() => restyleHostChrome(), "dsh-claude: Host chrome restyling");
|
|
17699
|
+
pluginRead(CLAUDE_GLOBAL_SETTINGS_PATH, "fast").then((payload) => {
|
|
17700
|
+
if (isGlobalSettingsView(payload)) applyClaudeMarkdownTheme(proseModeOf(payload.settings));
|
|
17701
|
+
}).catch(() => {});
|
|
17431
17702
|
const projections = new ClaudeProjectionStore({ report: (kind, detail) => {
|
|
17432
17703
|
diagnostics.report(kind, detail);
|
|
17433
17704
|
} });
|