@norman-else/dsh-claude 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js ADDED
@@ -0,0 +1,1417 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-claude",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ module.exports;
6
+ var { useCallback, useEffect, useMemo, useState, useSyncExternalStore } = require("react");
7
+ var { Fragment, jsx, jsxs } = require("react/jsx-runtime");
8
+ var { DisclosureRow, IconApiOutline14, IconThinkOutline14, StateDot } = require("@deepseek-ai/dsh-client-ui-primitives");
9
+ function isClaudeProvider(value) {
10
+ return value === "claude" || value === "claude-code-cli";
11
+ }
12
+ /** Claude's subagent dispatch tools; rendered as plugin-owned group cards
13
+ * gathering subagent activity instead of native tool cards. */
14
+ const TASK_TOOL_NAMES = /* @__PURE__ */ new Set(["Task", "Agent"]);
15
+ const CLAUDE_DOCTOR_PATH = "/plugins/dsh-claude/doctor";
16
+ const CLAUDE_PROJECTION_PATH = "/plugins/dsh-claude/projection";
17
+ //#endregion
18
+ //#region src/client/conversation-sidecar.ts
19
+ function isTaskActivity(value) {
20
+ return (value.kind === "tool-call" || value.kind === "tool-result") && value.toolName !== void 0 && TASK_TOOL_NAMES.has(value.toolName);
21
+ }
22
+ function presentable(activity) {
23
+ if (isTaskActivity(activity)) return true;
24
+ if (activity.kind === "tool-call" || activity.kind === "tool-result") return false;
25
+ if (activity.isError === true) return true;
26
+ if (activity.title?.startsWith("Unknown Claude SDK message") === true) return false;
27
+ switch (activity.kind) {
28
+ case "subagent":
29
+ case "permission":
30
+ case "warning":
31
+ case "error": return true;
32
+ case "thinking": return activity.summary !== void 0 && activity.summary.length > 0;
33
+ default: return false;
34
+ }
35
+ }
36
+ function running(activity) {
37
+ return activity.phase === "started" || activity.phase === "updated";
38
+ }
39
+ function subcallOf(value) {
40
+ if (value.kind !== "subagent" || value.toolUseId === void 0) return void 0;
41
+ return {
42
+ toolUseId: value.toolUseId,
43
+ ...value.toolName === void 0 ? {} : { toolName: value.toolName },
44
+ ...value.phase === void 0 ? {} : { phase: value.phase },
45
+ ...value.summary === void 0 ? {} : { summary: value.summary },
46
+ ...value.isError === void 0 ? {} : { isError: value.isError }
47
+ };
48
+ }
49
+ /** Fold raw sidecar activity into the same lifecycle cards the old event projection rendered. */
50
+ function activityRows(activities, accepts, tasks = []) {
51
+ const rows = [];
52
+ const byId = /* @__PURE__ */ new Map();
53
+ for (const value of activities) {
54
+ if (!accepts(value)) continue;
55
+ const existingTaskId = value.toolUseId === void 0 ? void 0 : `task-${value.toolUseId}`;
56
+ const updatesExistingTask = value.kind === "tool-result" && existingTaskId !== void 0 && byId.has(existingTaskId);
57
+ if (!presentable(value) && !updatesExistingTask) continue;
58
+ let id;
59
+ if ((isTaskActivity(value) || updatesExistingTask) && existingTaskId !== void 0) id = existingTaskId;
60
+ else if (value.kind === "subagent" && value.parentToolUseId !== void 0) id = `task-${value.parentToolUseId}`;
61
+ else if (value.kind === "subagent" && value.toolUseId !== void 0) id = `call-${value.toolUseId}`;
62
+ else id = `act-${value.turn}-${value.step}-${value.ordinal}`;
63
+ const index = byId.get(id);
64
+ if (index === void 0) {
65
+ byId.set(id, rows.length);
66
+ rows.push({
67
+ activity: value,
68
+ running: running(value),
69
+ subcalls: []
70
+ });
71
+ continue;
72
+ }
73
+ const previous = rows[index];
74
+ if (previous === void 0) continue;
75
+ if (value.kind === "subagent" && value.parentToolUseId !== void 0) {
76
+ const nested = subcallOf(value);
77
+ if (nested === void 0) continue;
78
+ const nestedIndex = previous.subcalls.findIndex((item) => item.toolUseId === nested.toolUseId);
79
+ const subcalls = nestedIndex === -1 ? [...previous.subcalls, nested] : previous.subcalls.map((item, position) => position === nestedIndex ? {
80
+ ...item,
81
+ ...nested
82
+ } : item);
83
+ rows[index] = {
84
+ ...previous,
85
+ subcalls
86
+ };
87
+ continue;
88
+ }
89
+ rows[index] = {
90
+ activity: {
91
+ ...value,
92
+ ...value.toolName === void 0 && previous.activity.toolName !== void 0 ? { toolName: previous.activity.toolName } : {}
93
+ },
94
+ running: running(value),
95
+ subcalls: previous.subcalls
96
+ };
97
+ }
98
+ const taskStatus = new Map(tasks.map((task) => [task.taskId, task.status]));
99
+ return rows.map((row) => {
100
+ const taskId = row.activity.taskId;
101
+ const status = taskId === void 0 ? void 0 : taskStatus.get(taskId);
102
+ if (status === void 0 || status === "running") return row;
103
+ const failed = status === "failed" || status === "stopped" || status === "killed";
104
+ const settledTitle = row.activity.title?.replace(/^Running\s+/u, "");
105
+ return {
106
+ ...row,
107
+ running: false,
108
+ activity: {
109
+ ...row.activity,
110
+ phase: failed ? "failed" : "completed",
111
+ ...settledTitle === void 0 ? {} : { title: settledTitle },
112
+ ...failed ? { isError: true } : {}
113
+ }
114
+ };
115
+ });
116
+ }
117
+ function activityRowsForStep(activities, turn, step, tasks = []) {
118
+ return activityRows(activities, (activity) => activity.turn === turn && activity.step === step, tasks);
119
+ }
120
+ /** Anchor one sidecar-backed activity group immediately before its Claude assistant step. */
121
+ const claudeActivityStepDefinition = {
122
+ kind: "claude-activity-step",
123
+ target: "chat",
124
+ match(event) {
125
+ if (event.type === "step/start") return {
126
+ id: `${event.data.turn}:${event.data.step}`,
127
+ role: "start"
128
+ };
129
+ if (event.type !== "assistant/message" || !isClaudeProvider(event.data.message.source.provider)) return null;
130
+ return {
131
+ id: `${event.data.turn}:${event.data.step}`,
132
+ role: "update"
133
+ };
134
+ },
135
+ start(_context, match) {
136
+ if (match.event.type !== "step/start") throw new Error("Claude activity step requires step/start");
137
+ return {
138
+ turn: match.event.data.turn,
139
+ step: match.event.data.step
140
+ };
141
+ },
142
+ update(context, match) {
143
+ if (match.event.type !== "assistant/message") throw new Error("Claude activity step update requires assistant/message");
144
+ return {
145
+ ...context.state,
146
+ assistantSeq: match.event.seq
147
+ };
148
+ },
149
+ buildViewNode(context) {
150
+ const state = context.state;
151
+ if (state?.assistantSeq === void 0) return null;
152
+ return {
153
+ key: context.key,
154
+ kind: "claude-activity-step",
155
+ id: context.id,
156
+ target: "chat",
157
+ anchorSeq: state.assistantSeq - .1,
158
+ location: context.start?.location ?? { kind: "unresolved" },
159
+ visibility: "visible",
160
+ data: {
161
+ turn: state.turn,
162
+ step: state.step
163
+ }
164
+ };
165
+ }
166
+ };
167
+ /** Publish a marker for turns containing standard assistant output from the Claude adapter. */
168
+ const claudeTurnDefinition = {
169
+ kind: "claudeCode",
170
+ match(event) {
171
+ if (event.type === "turn/start") return {
172
+ id: String(event.data.turn),
173
+ role: "start"
174
+ };
175
+ if (event.type !== "assistant/message" || !isClaudeProvider(event.data.message.source.provider)) return null;
176
+ return {
177
+ id: String(event.data.turn),
178
+ role: "update"
179
+ };
180
+ },
181
+ start(_context, match) {
182
+ if (match.event.type !== "turn/start") throw new Error("Claude turn marker requires turn/start");
183
+ return {
184
+ turn: match.event.data.turn,
185
+ claude: false
186
+ };
187
+ },
188
+ update(context, match) {
189
+ if (match.event.type !== "assistant/message") throw new Error("Claude turn marker update requires assistant/message");
190
+ return context.state.claude ? context.state : {
191
+ turn: context.state.turn,
192
+ claude: true
193
+ };
194
+ },
195
+ buildLocationData(context, scope) {
196
+ if (scope !== "turn" || context.state?.claude !== true) return null;
197
+ const value = { turn: context.state.turn };
198
+ return {
199
+ kind: "turn",
200
+ turn: context.state.turn,
201
+ key: "claudeCode",
202
+ value
203
+ };
204
+ }
205
+ };
206
+ /** Pure chain selector: only Claude-produced turns mount the sidecar-backed tail. */
207
+ function selectClaudeTurn(owner) {
208
+ return owner.turn.data.get("claudeCode") ?? null;
209
+ }
210
+ //#endregion
211
+ //#region src/client/styles.ts
212
+ const iconChipRunning = { color: "var(--dsw-static-blue-450)" };
213
+ const iconChipError = { color: "var(--dsw-alias-state-error-primary)" };
214
+ const chevron = {
215
+ flex: "none",
216
+ color: "var(--dsw-alias-label-tertiary)",
217
+ fontSize: 11,
218
+ lineHeight: "20px",
219
+ transition: "transform 120ms ease",
220
+ userSelect: "none"
221
+ };
222
+ const chevronOpen = { transform: "rotate(90deg)" };
223
+ const detailCode = {
224
+ maxHeight: 220,
225
+ overflow: "auto",
226
+ margin: "5px 0 0",
227
+ padding: "8px 10px",
228
+ borderRadius: 6,
229
+ background: "var(--dsw-alias-bg-layer-2)",
230
+ color: "var(--dsw-alias-label-secondary)",
231
+ fontSize: 11,
232
+ lineHeight: "17px",
233
+ whiteSpace: "pre-wrap",
234
+ overflowWrap: "anywhere"
235
+ };
236
+ const settingsPage = {
237
+ display: "flex",
238
+ flexDirection: "column",
239
+ gap: 18,
240
+ maxWidth: 760
241
+ };
242
+ const settingsHeading = {
243
+ margin: 0,
244
+ color: "var(--dsw-alias-label-primary)",
245
+ fontSize: 20,
246
+ lineHeight: "28px",
247
+ fontWeight: 650
248
+ };
249
+ const settingsBody = {
250
+ margin: 0,
251
+ color: "var(--dsw-alias-label-secondary)",
252
+ fontSize: 14,
253
+ lineHeight: "22px"
254
+ };
255
+ const diagnosticGrid = {
256
+ display: "grid",
257
+ gridTemplateColumns: "minmax(130px, 0.4fr) minmax(0, 1fr)",
258
+ gap: "9px 18px",
259
+ padding: "16px 0",
260
+ borderTop: "1px solid var(--dsw-alias-border-l2)",
261
+ borderBottom: "1px solid var(--dsw-alias-border-l2)",
262
+ fontSize: 13
263
+ };
264
+ const diagnosticLabel = { color: "var(--dsw-alias-label-tertiary)" };
265
+ const diagnosticValue = {
266
+ color: "var(--dsw-alias-label-primary)",
267
+ overflowWrap: "anywhere"
268
+ };
269
+ const button = {
270
+ alignSelf: "flex-start",
271
+ minHeight: 34,
272
+ padding: "6px 14px",
273
+ border: "1px solid var(--dsw-alias-border-l2)",
274
+ borderRadius: 18,
275
+ background: "var(--dsw-alias-bg-layer-1)",
276
+ color: "var(--dsw-alias-label-primary)",
277
+ font: "inherit",
278
+ fontSize: 13,
279
+ cursor: "pointer"
280
+ };
281
+ const tasksPanel = {
282
+ display: "flex",
283
+ flexDirection: "column",
284
+ height: "100%",
285
+ minWidth: 0,
286
+ background: "var(--dsw-alias-bg-base)"
287
+ };
288
+ const tasksHeader = {
289
+ display: "flex",
290
+ alignItems: "center",
291
+ justifyContent: "space-between",
292
+ gap: 8,
293
+ padding: "12px 14px",
294
+ borderBottom: "1px solid var(--dsw-alias-border-l2)"
295
+ };
296
+ const tasksHeading = {
297
+ color: "var(--dsw-alias-label-primary)",
298
+ fontSize: 14,
299
+ lineHeight: "20px",
300
+ fontWeight: 600
301
+ };
302
+ const tasksClose = {
303
+ width: 26,
304
+ height: 26,
305
+ display: "grid",
306
+ placeItems: "center",
307
+ border: "none",
308
+ borderRadius: 7,
309
+ background: "transparent",
310
+ color: "var(--dsw-alias-label-tertiary)",
311
+ fontSize: 15,
312
+ lineHeight: "1",
313
+ cursor: "pointer"
314
+ };
315
+ const tasksBody = {
316
+ flex: 1,
317
+ minHeight: 0,
318
+ overflowY: "auto",
319
+ padding: "10px 12px 20px"
320
+ };
321
+ const tasksGroupHeading = {
322
+ minHeight: 30,
323
+ display: "flex",
324
+ alignItems: "center",
325
+ justifyContent: "space-between",
326
+ gap: 8
327
+ };
328
+ const tasksGroupTitle = {
329
+ display: "inline-flex",
330
+ alignItems: "center",
331
+ gap: 6,
332
+ color: "var(--dsw-alias-label-secondary)",
333
+ fontSize: 12,
334
+ lineHeight: "20px",
335
+ fontWeight: 600
336
+ };
337
+ const tasksGroupToggle = {
338
+ display: "inline-flex",
339
+ alignItems: "center",
340
+ gap: 5,
341
+ padding: 0,
342
+ border: "none",
343
+ background: "transparent",
344
+ color: "var(--dsw-alias-label-secondary)",
345
+ font: "inherit",
346
+ fontSize: 12,
347
+ lineHeight: "20px",
348
+ fontWeight: 600,
349
+ cursor: "pointer"
350
+ };
351
+ const tasksGroupCount = {
352
+ minWidth: 18,
353
+ padding: "0 5px",
354
+ borderRadius: 999,
355
+ background: "var(--dsw-alias-bg-layer-2)",
356
+ color: "var(--dsw-alias-label-tertiary)",
357
+ fontSize: 10,
358
+ lineHeight: "17px",
359
+ textAlign: "center",
360
+ fontVariantNumeric: "tabular-nums"
361
+ };
362
+ const tasksGroupEmpty = {
363
+ margin: "5px 4px 2px",
364
+ color: "var(--dsw-alias-label-tertiary)",
365
+ fontSize: 12,
366
+ lineHeight: "18px"
367
+ };
368
+ const tasksFinishedSection = {
369
+ marginTop: 12,
370
+ paddingTop: 8,
371
+ borderTop: "1px solid var(--dsw-alias-border-l2)"
372
+ };
373
+ const taskCardList = {
374
+ display: "flex",
375
+ flexDirection: "column",
376
+ gap: 8,
377
+ marginTop: 4
378
+ };
379
+ const taskCard = {
380
+ padding: "10px 11px",
381
+ border: "1px solid var(--dsw-alias-border-l2)",
382
+ borderRadius: 10,
383
+ background: "var(--dsw-alias-bg-layer-1)"
384
+ };
385
+ const taskCardRunning = { borderColor: "var(--dsw-alias-border-l3)" };
386
+ const taskCardTop = {
387
+ display: "flex",
388
+ alignItems: "flex-start",
389
+ gap: 9
390
+ };
391
+ const taskCardGlyph = {
392
+ width: 18,
393
+ height: 18,
394
+ flex: "none",
395
+ display: "grid",
396
+ placeItems: "center",
397
+ marginTop: 1,
398
+ color: "var(--dsw-alias-label-tertiary)",
399
+ fontSize: 11,
400
+ lineHeight: 1
401
+ };
402
+ const taskCardBody = {
403
+ flex: 1,
404
+ minWidth: 0
405
+ };
406
+ const taskTitle = {
407
+ margin: 0,
408
+ color: "var(--dsw-alias-label-primary)",
409
+ fontSize: 13,
410
+ lineHeight: "19px",
411
+ fontWeight: 550,
412
+ overflowWrap: "anywhere"
413
+ };
414
+ const taskStatusLine = {
415
+ margin: "1px 0 0",
416
+ color: "var(--dsw-alias-label-tertiary)",
417
+ fontSize: 11,
418
+ lineHeight: "17px"
419
+ };
420
+ const taskMeta$1 = {
421
+ margin: "7px 0 0 27px",
422
+ color: "var(--dsw-alias-label-tertiary)",
423
+ fontSize: 11,
424
+ lineHeight: "17px",
425
+ overflowWrap: "anywhere"
426
+ };
427
+ const taskSummary = {
428
+ margin: "6px 0 0 27px",
429
+ color: "var(--dsw-alias-label-secondary)",
430
+ fontSize: 12,
431
+ lineHeight: "18px",
432
+ whiteSpace: "pre-wrap",
433
+ overflowWrap: "anywhere"
434
+ };
435
+ const taskTextButton = {
436
+ padding: 0,
437
+ border: "none",
438
+ background: "transparent",
439
+ color: "var(--dsw-static-blue-450)",
440
+ font: "inherit",
441
+ fontSize: 11,
442
+ lineHeight: "18px",
443
+ cursor: "pointer"
444
+ };
445
+ const taskActivitySection = { margin: "7px 0 0 27px" };
446
+ const taskActivityList = {
447
+ display: "flex",
448
+ flexDirection: "column",
449
+ gap: 7,
450
+ margin: "7px 0 0",
451
+ padding: "8px 9px",
452
+ borderRadius: 8,
453
+ background: "var(--dsw-alias-bg-layer-2)",
454
+ listStyle: "none"
455
+ };
456
+ const taskActivityItem = {
457
+ display: "flex",
458
+ alignItems: "flex-start",
459
+ gap: 6
460
+ };
461
+ const taskActivityGlyph = {
462
+ flex: "none",
463
+ color: "var(--dsw-alias-label-tertiary)",
464
+ fontSize: 11,
465
+ lineHeight: "17px"
466
+ };
467
+ const taskActivityBody = {
468
+ flex: 1,
469
+ minWidth: 0
470
+ };
471
+ const taskActivityTitle = {
472
+ margin: 0,
473
+ color: "var(--dsw-alias-label-secondary)",
474
+ fontSize: 11,
475
+ lineHeight: "17px",
476
+ fontWeight: 500,
477
+ overflowWrap: "anywhere"
478
+ };
479
+ const taskActivitySummary = {
480
+ margin: "1px 0 0",
481
+ color: "var(--dsw-alias-label-tertiary)",
482
+ fontSize: 11,
483
+ lineHeight: "17px",
484
+ whiteSpace: "pre-wrap",
485
+ overflowWrap: "anywhere"
486
+ };
487
+ const taskActivityDetail = { marginTop: 3 };
488
+ const taskActivityDetailSummary = {
489
+ color: "var(--dsw-alias-label-tertiary)",
490
+ fontSize: 10,
491
+ lineHeight: "16px",
492
+ cursor: "pointer"
493
+ };
494
+ const tasksTurnLauncher = {
495
+ display: "inline-flex",
496
+ alignItems: "center",
497
+ gap: 7,
498
+ margin: "6px 0 2px",
499
+ padding: "5px 8px",
500
+ border: "none",
501
+ borderRadius: 8,
502
+ background: "transparent",
503
+ color: "var(--dsw-alias-label-secondary)",
504
+ font: "inherit",
505
+ fontSize: 12,
506
+ lineHeight: "18px",
507
+ cursor: "pointer"
508
+ };
509
+ const tasksTurnLauncherDot = {
510
+ color: "var(--dsw-static-blue-450)",
511
+ fontSize: 9
512
+ };
513
+ const tasksTriggerHoverCss = [
514
+ ".dsh-claude-tasks-trigger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}",
515
+ ".dsh-claude-tasks-trigger:focus:not(:focus-visible){outline:none;border-color:var(--dsw-alias-border-l2)}",
516
+ ".dsh-claude-tasks-trigger[aria-pressed=\"true\"]:focus:not(:focus-visible){border-color:var(--dsw-alias-border-l3)}",
517
+ ".dsh-claude-tasks-trigger>span,.dsh-claude-tasks-trigger>svg{flex:none}"
518
+ ].join("");
519
+ const tasksHeaderButton = {
520
+ minWidth: 111,
521
+ height: 32,
522
+ display: "inline-flex",
523
+ alignItems: "center",
524
+ justifyContent: "center",
525
+ gap: 4,
526
+ padding: "6px 12px",
527
+ border: "1px solid var(--dsw-alias-border-l2)",
528
+ borderRadius: 18,
529
+ color: "var(--dsw-alias-label-primary)",
530
+ fontFamily: "var(--dsw-font-family)",
531
+ fontSize: 13,
532
+ fontWeight: 400,
533
+ lineHeight: "20px",
534
+ whiteSpace: "nowrap",
535
+ cursor: "pointer"
536
+ };
537
+ const tasksHeaderButtonActive = {
538
+ borderColor: "var(--dsw-alias-border-l3)",
539
+ background: "var(--dsw-alias-interactive-bg-hover)"
540
+ };
541
+ const tasksBadgeInline = {
542
+ minWidth: 14,
543
+ height: 14,
544
+ display: "grid",
545
+ placeItems: "center",
546
+ padding: "0 3px",
547
+ borderRadius: 999,
548
+ background: "var(--dsw-static-blue-450)",
549
+ color: "var(--dsw-alias-label-on-accent, #fff)",
550
+ fontSize: 9,
551
+ lineHeight: "1",
552
+ fontWeight: 600
553
+ };
554
+ //#endregion
555
+ //#region src/client/token-format.ts
556
+ function formatTokenCount(tokens) {
557
+ if (tokens >= 1e6) return `${Number((tokens / 1e6).toFixed(tokens >= 1e7 ? 0 : 1))}M`;
558
+ if (tokens >= 1e3) return `${Number((tokens / 1e3).toFixed(tokens >= 1e5 ? 0 : 1))}K`;
559
+ return String(tokens);
560
+ }
561
+ //#endregion
562
+ //#region src/client/ClaudeTasksPanel.tsx
563
+ const STATUS_LABEL = {
564
+ running: "tasksRunning",
565
+ completed: "tasksCompleted",
566
+ failed: "tasksFailed",
567
+ stopped: "tasksStopped",
568
+ killed: "tasksKilled"
569
+ };
570
+ function visibleTaskGroups(tasks, dismissedSettledIds) {
571
+ return {
572
+ running: tasks.filter((task) => task.status === "running"),
573
+ finished: tasks.filter((task) => task.status !== "running" && !dismissedSettledIds.has(task.taskId))
574
+ };
575
+ }
576
+ function activitiesForTask(activities, taskId) {
577
+ return activities.filter((activity) => activity.taskId === taskId);
578
+ }
579
+ function runningTasksForTurn(tasks, turn) {
580
+ return tasks.filter((task) => task.status === "running" && task.originTurn === turn);
581
+ }
582
+ function statusGlyph(status) {
583
+ if (status === "running") return "●";
584
+ if (status === "completed") return "✓";
585
+ if (status === "stopped") return "–";
586
+ return "×";
587
+ }
588
+ function formatDuration(ms) {
589
+ if (ms < 1e3) return String(Math.max(1, Math.round(ms))) + "ms";
590
+ const seconds = Math.round(ms / 1e3);
591
+ if (seconds < 60) return String(seconds) + "s";
592
+ const minutes = Math.floor(seconds / 60);
593
+ return String(minutes) + "m " + String(seconds % 60) + "s";
594
+ }
595
+ function taskMeta(task, t) {
596
+ const parts = [];
597
+ if (task.subagentType !== void 0) parts.push(task.subagentType);
598
+ else if (task.taskType !== void 0) parts.push(task.taskType);
599
+ if (task.usage?.durationMs !== void 0) parts.push(formatDuration(task.usage.durationMs));
600
+ if (task.usage?.totalTokens !== void 0) parts.push(t("tokens", { count: formatTokenCount(task.usage.totalTokens) }));
601
+ if (task.usage?.toolUses !== void 0) parts.push(t("tasksToolUses", { count: task.usage.toolUses }));
602
+ if (task.lastToolName !== void 0) parts.push(t("tasksLastTool", { tool: task.lastToolName }));
603
+ return parts;
604
+ }
605
+ function TaskActivity({ activity, t }) {
606
+ return /* @__PURE__ */ jsxs("li", {
607
+ style: taskActivityItem,
608
+ children: [/* @__PURE__ */ jsx("span", {
609
+ style: taskActivityGlyph,
610
+ "aria-hidden": "true",
611
+ children: activity.isError === true ? "×" : "›"
612
+ }), /* @__PURE__ */ jsxs("div", {
613
+ style: taskActivityBody,
614
+ children: [
615
+ /* @__PURE__ */ jsx("p", {
616
+ style: taskActivityTitle,
617
+ children: activity.title ?? activity.kind
618
+ }),
619
+ activity.summary === void 0 ? null : /* @__PURE__ */ jsx("p", {
620
+ style: taskActivitySummary,
621
+ children: activity.summary
622
+ }),
623
+ activity.detail === void 0 ? null : /* @__PURE__ */ jsxs("details", {
624
+ style: taskActivityDetail,
625
+ children: [/* @__PURE__ */ jsx("summary", {
626
+ style: taskActivityDetailSummary,
627
+ children: t("detail")
628
+ }), /* @__PURE__ */ jsx("pre", {
629
+ style: detailCode,
630
+ children: activity.detail
631
+ })]
632
+ })
633
+ ]
634
+ })]
635
+ });
636
+ }
637
+ function TaskCard(props) {
638
+ const { task, activities, t } = props;
639
+ const [activityOpen, setActivityOpen] = useState(false);
640
+ const running = task.status === "running";
641
+ const failed = task.status === "failed" || task.status === "killed";
642
+ const meta = taskMeta(task, t);
643
+ return /* @__PURE__ */ jsxs("article", {
644
+ style: {
645
+ ...taskCard,
646
+ ...running ? taskCardRunning : {}
647
+ },
648
+ children: [
649
+ /* @__PURE__ */ jsxs("div", {
650
+ style: taskCardTop,
651
+ children: [/* @__PURE__ */ jsx("span", {
652
+ className: running ? "dsh-claude-act-running" : void 0,
653
+ style: {
654
+ ...taskCardGlyph,
655
+ ...running ? iconChipRunning : {},
656
+ ...failed ? iconChipError : {}
657
+ },
658
+ "aria-hidden": "true",
659
+ children: statusGlyph(task.status)
660
+ }), /* @__PURE__ */ jsxs("div", {
661
+ style: taskCardBody,
662
+ children: [/* @__PURE__ */ jsx("p", {
663
+ style: {
664
+ ...taskTitle,
665
+ ...failed ? { color: "var(--dsw-alias-state-error-primary)" } : {}
666
+ },
667
+ children: task.description
668
+ }), /* @__PURE__ */ jsxs("p", {
669
+ style: taskStatusLine,
670
+ children: [/* @__PURE__ */ jsx("span", { children: t(STATUS_LABEL[task.status] ?? "tasksRunning") }), task.backgrounded === true ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
671
+ "aria-hidden": "true",
672
+ children: " · "
673
+ }), /* @__PURE__ */ jsx("span", { children: t("tasksBackground") })] }) : null]
674
+ })]
675
+ })]
676
+ }),
677
+ meta.length === 0 ? null : /* @__PURE__ */ jsx("p", {
678
+ style: taskMeta$1,
679
+ children: meta.join(" · ")
680
+ }),
681
+ task.summary === void 0 || running ? null : /* @__PURE__ */ jsx("p", {
682
+ style: taskSummary,
683
+ children: task.summary
684
+ }),
685
+ activities.length === 0 ? null : /* @__PURE__ */ jsxs("div", {
686
+ style: taskActivitySection,
687
+ children: [/* @__PURE__ */ jsx("button", {
688
+ type: "button",
689
+ style: taskTextButton,
690
+ "aria-expanded": activityOpen,
691
+ onClick: () => setActivityOpen((value) => !value),
692
+ children: activityOpen ? t("tasksHideActivity") : t("tasksViewActivity")
693
+ }), activityOpen ? /* @__PURE__ */ jsx("ul", {
694
+ style: taskActivityList,
695
+ children: activities.map((activity) => /* @__PURE__ */ jsx(TaskActivity, {
696
+ activity,
697
+ t
698
+ }, `${activity.turn}:${activity.step}:${activity.ordinal}`))
699
+ }) : null]
700
+ })
701
+ ]
702
+ });
703
+ }
704
+ function GroupHeading(props) {
705
+ const { label, count, collapsed, onToggle, action } = props;
706
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", { children: label }), /* @__PURE__ */ jsx("span", {
707
+ style: tasksGroupCount,
708
+ children: count
709
+ })] });
710
+ return /* @__PURE__ */ jsxs("div", {
711
+ style: tasksGroupHeading,
712
+ children: [onToggle === void 0 ? /* @__PURE__ */ jsx("div", {
713
+ style: tasksGroupTitle,
714
+ children: content
715
+ }) : /* @__PURE__ */ jsxs("button", {
716
+ type: "button",
717
+ style: tasksGroupToggle,
718
+ "aria-expanded": !collapsed,
719
+ onClick: onToggle,
720
+ children: [/* @__PURE__ */ jsx("span", {
721
+ style: {
722
+ ...chevron,
723
+ ...collapsed === true ? {} : chevronOpen
724
+ },
725
+ children: "›"
726
+ }), content]
727
+ }), action === void 0 ? null : /* @__PURE__ */ jsx("button", {
728
+ type: "button",
729
+ style: taskTextButton,
730
+ onClick: action.onClick,
731
+ children: action.label
732
+ })]
733
+ });
734
+ }
735
+ function ClaudeTasksPanel({ useClaudeProjection, t, closeDetails }) {
736
+ const projection = useClaudeProjection((value) => value);
737
+ useEffect(() => {
738
+ if (!projection.owned) closeDetails();
739
+ }, [closeDetails, projection.owned]);
740
+ const tasks = projection.tasks?.tasks ?? [];
741
+ const [finishedCollapsed, setFinishedCollapsed] = useState(false);
742
+ const [dismissedSettledIds, setDismissedSettledIds] = useState(() => /* @__PURE__ */ new Set());
743
+ const groups = useMemo(() => visibleTaskGroups(tasks, dismissedSettledIds), [tasks, dismissedSettledIds]);
744
+ const taskActivities = useMemo(() => new Map(tasks.map((task) => [task.taskId, activitiesForTask(projection.activities, task.taskId)])), [projection.activities, tasks]);
745
+ const clearFinished = () => setDismissedSettledIds((previous) => /* @__PURE__ */ new Set([...previous, ...tasks.filter((task) => task.status !== "running").map((task) => task.taskId)]));
746
+ if (!projection.owned) return null;
747
+ return /* @__PURE__ */ jsxs("div", {
748
+ style: tasksPanel,
749
+ children: [/* @__PURE__ */ jsxs("div", {
750
+ style: tasksHeader,
751
+ children: [/* @__PURE__ */ jsx("span", {
752
+ style: tasksHeading,
753
+ children: t("tasksPanel")
754
+ }), /* @__PURE__ */ jsx("button", {
755
+ type: "button",
756
+ style: tasksClose,
757
+ "aria-label": t("tasksClose"),
758
+ onClick: closeDetails,
759
+ children: "×"
760
+ })]
761
+ }), /* @__PURE__ */ jsxs("div", {
762
+ style: tasksBody,
763
+ children: [/* @__PURE__ */ jsxs("section", {
764
+ "aria-label": t("tasksRunning"),
765
+ children: [/* @__PURE__ */ jsx(GroupHeading, {
766
+ label: t("tasksRunning"),
767
+ count: groups.running.length
768
+ }), groups.running.length === 0 ? /* @__PURE__ */ jsx("p", {
769
+ style: tasksGroupEmpty,
770
+ children: t("tasksNoneRunning")
771
+ }) : /* @__PURE__ */ jsx("div", {
772
+ style: taskCardList,
773
+ children: groups.running.map((task) => /* @__PURE__ */ jsx(TaskCard, {
774
+ task,
775
+ activities: taskActivities.get(task.taskId) ?? [],
776
+ t
777
+ }, task.taskId))
778
+ })]
779
+ }), /* @__PURE__ */ jsxs("section", {
780
+ "aria-label": t("tasksSettled"),
781
+ style: tasksFinishedSection,
782
+ children: [/* @__PURE__ */ jsx(GroupHeading, {
783
+ label: t("tasksSettled"),
784
+ count: groups.finished.length,
785
+ collapsed: finishedCollapsed,
786
+ onToggle: () => setFinishedCollapsed((value) => !value),
787
+ ...groups.finished.length === 0 ? {} : { action: {
788
+ label: t("tasksClear"),
789
+ onClick: clearFinished
790
+ } }
791
+ }), finishedCollapsed || groups.finished.length === 0 ? null : /* @__PURE__ */ jsx("div", {
792
+ style: taskCardList,
793
+ children: groups.finished.map((task) => /* @__PURE__ */ jsx(TaskCard, {
794
+ task,
795
+ activities: taskActivities.get(task.taskId) ?? [],
796
+ t
797
+ }, task.taskId))
798
+ })]
799
+ })]
800
+ })]
801
+ });
802
+ }
803
+ function ClaudeTasksHeaderButton({ useClaudeProjection, t, isOpen, toggle, subscribe }) {
804
+ const open = useSyncExternalStore(subscribe, isOpen, isOpen);
805
+ const projection = useClaudeProjection((value) => value);
806
+ if (!projection.owned) return null;
807
+ const runningCount = projection.tasks?.tasks.filter((task) => task.status === "running").length ?? 0;
808
+ return /* @__PURE__ */ jsxs("button", {
809
+ type: "button",
810
+ className: "dsh-claude-tasks-trigger",
811
+ style: {
812
+ ...tasksHeaderButton,
813
+ ...open ? tasksHeaderButtonActive : {}
814
+ },
815
+ "aria-label": t("tasksOpen"),
816
+ "aria-pressed": open,
817
+ onClick: (event) => {
818
+ toggle();
819
+ event.currentTarget.blur();
820
+ },
821
+ children: [
822
+ /* @__PURE__ */ jsx("style", { children: tasksTriggerHoverCss }),
823
+ /* @__PURE__ */ jsxs("svg", {
824
+ width: "14",
825
+ height: "14",
826
+ viewBox: "0 0 24 24",
827
+ fill: "none",
828
+ stroke: "currentColor",
829
+ strokeWidth: "2",
830
+ strokeLinecap: "round",
831
+ strokeLinejoin: "round",
832
+ "aria-hidden": "true",
833
+ children: [
834
+ /* @__PURE__ */ jsx("path", { d: "m3 17 2 2 4-4" }),
835
+ /* @__PURE__ */ jsx("path", { d: "m3 7 2 2 4-4" }),
836
+ /* @__PURE__ */ jsx("path", { d: "M13 6h8" }),
837
+ /* @__PURE__ */ jsx("path", { d: "M13 12h8" }),
838
+ /* @__PURE__ */ jsx("path", { d: "M13 18h8" })
839
+ ]
840
+ }),
841
+ /* @__PURE__ */ jsx("span", { children: t("tasks") }),
842
+ runningCount === 0 ? null : /* @__PURE__ */ jsx("span", {
843
+ className: "dsh-claude-act-running",
844
+ style: tasksBadgeInline,
845
+ "aria-hidden": "true",
846
+ children: runningCount
847
+ })
848
+ ]
849
+ });
850
+ }
851
+ //#endregion
852
+ //#region src/client/ClaudeActivityTail.tsx
853
+ function ClaudeActivityTail({ matched, useClaudeProjection, t, openTasks }) {
854
+ const projection = useClaudeProjection((value) => value);
855
+ const runningTasks = useMemo(() => runningTasksForTurn(projection.tasks?.tasks ?? [], matched.turn), [projection.tasks, matched.turn]);
856
+ if (runningTasks.length === 0) return null;
857
+ return /* @__PURE__ */ jsx("div", {
858
+ "data-claude-activity-tail": matched.turn,
859
+ children: runningTasks.length === 0 ? null : /* @__PURE__ */ jsxs("button", {
860
+ type: "button",
861
+ style: tasksTurnLauncher,
862
+ onClick: openTasks,
863
+ children: [
864
+ /* @__PURE__ */ jsx("span", {
865
+ className: "dsh-claude-act-running",
866
+ style: tasksTurnLauncherDot,
867
+ "aria-hidden": "true",
868
+ children: "●"
869
+ }),
870
+ /* @__PURE__ */ jsx("span", { children: t("tasksRunningCount", { count: runningTasks.length }) }),
871
+ /* @__PURE__ */ jsx("span", {
872
+ "aria-hidden": "true",
873
+ children: "›"
874
+ })
875
+ ]
876
+ })
877
+ });
878
+ }
879
+ //#endregion
880
+ //#region src/client/ClaudeActivityNode.tsx
881
+ const EMPTY_TASKS = [];
882
+ const ACTIVITY_CSS = [
883
+ ".dsh-claude-flow{display:flex;flex-direction:column;gap:4px}",
884
+ ".dsh-claude-flow-row{position:relative;overflow:hidden}",
885
+ ".dsh-claude-flow-leading{flex-shrink:0}",
886
+ ".dsh-claude-flow-title{font-weight:400}",
887
+ ".dsh-claude-flow-chevron{color:var(--dsw-alias-label-secondary)}",
888
+ ".dsh-claude-flow-separator{width:2px;height:2px;margin:0 8px;border-radius:1px;background:var(--dsw-alias-label-caption);flex:none}",
889
+ ".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}",
890
+ ".dsh-claude-flow-summary[data-error]{color:var(--dsw-alias-state-error-primary)}",
891
+ ".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}",
892
+ ".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);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}",
893
+ ".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}",
894
+ ".dsh-claude-act-running{animation:dsh-claude-act-pulse 1.2s ease-in-out infinite}",
895
+ "@keyframes dsh-claude-act-pulse{0%,100%{opacity:1}50%{opacity:.3}}"
896
+ ].join("");
897
+ let cssInjected = false;
898
+ function ensureCss() {
899
+ if (cssInjected || typeof document === "undefined") return;
900
+ cssInjected = true;
901
+ const element = document.createElement("style");
902
+ element.dataset.dshClaudeActivity = "";
903
+ element.textContent = ACTIVITY_CSS;
904
+ document.head.appendChild(element);
905
+ }
906
+ function subcallGlyph(subcall) {
907
+ if (subcall.isError === true || subcall.phase === "failed") return "×";
908
+ if (subcall.phase === "started" || subcall.phase === "updated") return "●";
909
+ return "✓";
910
+ }
911
+ function title(activity) {
912
+ return activity.toolName ?? activity.title ?? activity.kind.replaceAll("-", " ");
913
+ }
914
+ function activityState(activity, running) {
915
+ if (activity.isError === true || activity.kind === "error" || activity.phase === "denied" || activity.phase === "failed") return "error";
916
+ if (running) return "ongoing";
917
+ if (activity.kind === "warning") return "warning";
918
+ return "done";
919
+ }
920
+ function ActivityRow({ row, t }) {
921
+ const { activity, running, subcalls } = row;
922
+ const [open, setOpen] = useState(false);
923
+ const state = activityState(activity, running);
924
+ const detail = activity.detail;
925
+ const expandable = detail !== void 0 || subcalls.length > 0 || activity.kind === "thinking";
926
+ const summary = activity.summary ?? (running ? t("running") : state === "error" ? t("failed") : t("done"));
927
+ const body = activity.kind === "thinking" ? activity.summary : detail;
928
+ return /* @__PURE__ */ jsxs(DisclosureRow, {
929
+ rowClassName: "dsh-claude-flow-row",
930
+ leadingClassName: "dsh-claude-flow-leading",
931
+ titleClassName: "dsh-claude-flow-title",
932
+ chevronClassName: "dsh-claude-flow-chevron",
933
+ icon: activity.kind === "thinking" ? /* @__PURE__ */ jsx(IconThinkOutline14, { size: 14 }) : state === "done" ? /* @__PURE__ */ jsx(IconApiOutline14, { size: 14 }) : /* @__PURE__ */ jsx(StateDot, { state }),
934
+ title: activity.kind === "thinking" ? t("thinking") : title(activity),
935
+ open,
936
+ expandable,
937
+ expandOnRowClick: true,
938
+ keepContentWhenOpen: true,
939
+ onToggle: () => setOpen((value) => !value),
940
+ collapsedContent: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
941
+ className: "dsh-claude-flow-separator",
942
+ "aria-hidden": "true"
943
+ }), /* @__PURE__ */ jsx("span", {
944
+ className: "dsh-claude-flow-summary",
945
+ "data-error": state === "error" || void 0,
946
+ children: summary
947
+ })] }),
948
+ children: [subcalls.length === 0 ? null : /* @__PURE__ */ jsx("div", {
949
+ className: "dsh-claude-flow-subcalls",
950
+ children: subcalls.map((subcall) => /* @__PURE__ */ jsxs("div", { children: [
951
+ subcallGlyph(subcall),
952
+ " ",
953
+ subcall.toolName ?? t("subagent"),
954
+ subcall.summary === void 0 ? "" : ` · ${subcall.summary}`
955
+ ] }, subcall.toolUseId))
956
+ }), body === void 0 ? null : activity.kind === "thinking" ? /* @__PURE__ */ jsx("div", {
957
+ className: "dsh-claude-flow-body",
958
+ children: body
959
+ }) : /* @__PURE__ */ jsx("pre", {
960
+ className: "dsh-claude-flow-detail",
961
+ children: body
962
+ })]
963
+ });
964
+ }
965
+ function ClaudeActivityNode({ node, useClaudeProjection, t }) {
966
+ ensureCss();
967
+ const marker = node.data;
968
+ const activities = useClaudeProjection((value) => value.activities);
969
+ const tasks = useClaudeProjection((value) => value.tasks?.tasks ?? EMPTY_TASKS);
970
+ const rows = useMemo(() => activityRowsForStep(activities, marker.turn, marker.step, tasks), [
971
+ activities,
972
+ marker.step,
973
+ marker.turn,
974
+ tasks
975
+ ]);
976
+ if (rows.length === 0) return null;
977
+ return /* @__PURE__ */ jsx("div", {
978
+ className: "dsh-claude-flow",
979
+ children: rows.map((row, index) => /* @__PURE__ */ jsx(ActivityRow, {
980
+ row,
981
+ t
982
+ }, `${row.activity.ordinal}:${index}`))
983
+ });
984
+ }
985
+ //#endregion
986
+ //#region src/client/ClaudeCodeSettings.tsx
987
+ function value(status, detail) {
988
+ return detail === void 0 ? status : `${status} · ${detail}`;
989
+ }
990
+ function ClaudeCodeSettings({ t }) {
991
+ const [report, setReport] = useState();
992
+ const [error, setError] = useState();
993
+ const [busy, setBusy] = useState(false);
994
+ const refresh = useCallback(async () => {
995
+ setBusy(true);
996
+ setError(void 0);
997
+ setReport(void 0);
998
+ try {
999
+ const response = await fetch(CLAUDE_DOCTOR_PATH, {
1000
+ credentials: "same-origin",
1001
+ headers: { accept: "application/json" }
1002
+ });
1003
+ const payload = await response.json();
1004
+ if (!response.ok) throw new Error("error" in payload ? payload.error : `HTTP ${response.status}`);
1005
+ setReport(payload);
1006
+ } catch (cause) {
1007
+ setError(cause instanceof Error ? cause.message : String(cause));
1008
+ } finally {
1009
+ setBusy(false);
1010
+ }
1011
+ }, []);
1012
+ useEffect(() => {
1013
+ refresh();
1014
+ }, [refresh]);
1015
+ const rows = report === void 0 ? [] : [
1016
+ [t("executable"), report.executable.status === "found" ? report.executable.path ?? t("unknown") : `${t("missing")} · ${report.executable.searched.join(", ")}`],
1017
+ [t("version"), value(report.version.status, report.version.value ?? report.version.message)],
1018
+ [t("authentication"), value(report.authentication.status, [report.authentication.method, report.authentication.subscription].filter(Boolean).join(" · ") || report.authentication.message)],
1019
+ [t("handshake"), report.handshake],
1020
+ [t("processes"), t("processSummary", {
1021
+ total: report.processes.count,
1022
+ active: report.processes.active
1023
+ })],
1024
+ [t("limits"), t("limitSummary", {
1025
+ max: report.limits.maxProcesses,
1026
+ minutes: Math.round(report.limits.idleTimeoutMs / 6e4)
1027
+ })]
1028
+ ];
1029
+ return /* @__PURE__ */ jsxs("div", {
1030
+ style: settingsPage,
1031
+ children: [
1032
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h2", {
1033
+ style: settingsHeading,
1034
+ children: t("title")
1035
+ }), /* @__PURE__ */ jsx("p", {
1036
+ style: settingsBody,
1037
+ children: t("description")
1038
+ })] }),
1039
+ rows.length === 0 ? null : /* @__PURE__ */ jsx("div", {
1040
+ style: diagnosticGrid,
1041
+ children: rows.flatMap(([label, rowValue]) => [/* @__PURE__ */ jsx("span", {
1042
+ style: diagnosticLabel,
1043
+ children: label
1044
+ }, `${label}-label`), /* @__PURE__ */ jsx("span", {
1045
+ style: diagnosticValue,
1046
+ children: rowValue
1047
+ }, `${label}-value`)])
1048
+ }),
1049
+ error === void 0 ? null : /* @__PURE__ */ jsxs("p", {
1050
+ style: {
1051
+ ...settingsBody,
1052
+ color: "var(--dsw-alias-state-error-primary)"
1053
+ },
1054
+ children: [
1055
+ t("error"),
1056
+ ": ",
1057
+ error
1058
+ ]
1059
+ }),
1060
+ /* @__PURE__ */ jsx("button", {
1061
+ type: "button",
1062
+ style: button,
1063
+ onClick: () => {
1064
+ refresh();
1065
+ },
1066
+ disabled: busy,
1067
+ children: busy ? t("refreshing") : t("doctor")
1068
+ }),
1069
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", {
1070
+ style: {
1071
+ ...settingsHeading,
1072
+ fontSize: 15,
1073
+ lineHeight: "22px"
1074
+ },
1075
+ children: t("security")
1076
+ }), /* @__PURE__ */ jsx("p", {
1077
+ style: settingsBody,
1078
+ children: t("securityBody")
1079
+ })] })
1080
+ ]
1081
+ });
1082
+ }
1083
+ //#endregion
1084
+ //#region src/client/projection.ts
1085
+ const EMPTY_CLAUDE_PROJECTION = {
1086
+ schemaVersion: 1,
1087
+ revision: 0,
1088
+ owned: false,
1089
+ activities: []
1090
+ };
1091
+ const POLL_INTERVAL_MS = 2e3;
1092
+ const MAX_ACTIVITIES = 1e4;
1093
+ function record(value) {
1094
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1095
+ }
1096
+ function nonNegativeInteger(value) {
1097
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
1098
+ }
1099
+ /** Validate the public route envelope before publishing it to UI components. */
1100
+ function parseClaudeClientProjection(value) {
1101
+ const input = record(value);
1102
+ if (input === void 0 || input.schemaVersion !== 1 || !nonNegativeInteger(input.revision) || typeof input.owned !== "boolean" || !Array.isArray(input.activities) || input.activities.length > MAX_ACTIVITIES) throw new Error("invalid Claude sidecar projection");
1103
+ for (const item of input.activities) {
1104
+ const activity = record(item);
1105
+ if (activity === void 0 || !nonNegativeInteger(activity.turn) || !nonNegativeInteger(activity.step) || !nonNegativeInteger(activity.ordinal) || typeof activity.kind !== "string") throw new Error("invalid Claude sidecar activity");
1106
+ }
1107
+ if (input.contextUsage !== void 0 && record(input.contextUsage) === void 0) throw new Error("invalid Claude context projection");
1108
+ const tasks = input.tasks === void 0 ? void 0 : record(input.tasks);
1109
+ if (tasks !== void 0 && !Array.isArray(tasks.tasks)) throw new Error("invalid Claude tasks projection");
1110
+ return input;
1111
+ }
1112
+ /** Create one lazy source: active subscribers trigger an immediate load and bounded polling. */
1113
+ function createClaudeProjectionSource(sessionId, fetchProjection = fetch, pollIntervalMs = POLL_INTERVAL_MS) {
1114
+ let snapshot = EMPTY_CLAUDE_PROJECTION;
1115
+ let timer;
1116
+ let controller;
1117
+ let disposed = false;
1118
+ const listeners = /* @__PURE__ */ new Set();
1119
+ const schedule = () => {
1120
+ if (disposed || listeners.size === 0) return;
1121
+ timer = setTimeout(() => {
1122
+ refresh();
1123
+ }, pollIntervalMs);
1124
+ };
1125
+ const refresh = async () => {
1126
+ if (disposed || listeners.size === 0) return;
1127
+ controller?.abort();
1128
+ controller = new AbortController();
1129
+ try {
1130
+ const response = await fetchProjection(`${CLAUDE_PROJECTION_PATH}/${encodeURIComponent(sessionId)}`, {
1131
+ headers: { accept: "application/json" },
1132
+ signal: controller.signal
1133
+ });
1134
+ if (!response.ok) throw new Error(`Claude projection request failed (${response.status})`);
1135
+ const next = parseClaudeClientProjection(await response.json());
1136
+ if (next.revision !== snapshot.revision || next.owned !== snapshot.owned) {
1137
+ snapshot = next;
1138
+ for (const listener of [...listeners]) listener();
1139
+ }
1140
+ } catch (error) {
1141
+ if (!(error instanceof DOMException && error.name === "AbortError") && snapshot !== EMPTY_CLAUDE_PROJECTION) {
1142
+ snapshot = EMPTY_CLAUDE_PROJECTION;
1143
+ for (const listener of [...listeners]) listener();
1144
+ }
1145
+ } finally {
1146
+ controller = void 0;
1147
+ schedule();
1148
+ }
1149
+ };
1150
+ return {
1151
+ getSnapshot: () => snapshot,
1152
+ subscribe(listener) {
1153
+ if (disposed) return () => {};
1154
+ const wasIdle = listeners.size === 0;
1155
+ listeners.add(listener);
1156
+ if (wasIdle) refresh();
1157
+ return () => {
1158
+ listeners.delete(listener);
1159
+ if (listeners.size !== 0) return;
1160
+ if (timer !== void 0) clearTimeout(timer);
1161
+ timer = void 0;
1162
+ controller?.abort();
1163
+ controller = void 0;
1164
+ };
1165
+ },
1166
+ dispose() {
1167
+ disposed = true;
1168
+ listeners.clear();
1169
+ if (timer !== void 0) clearTimeout(timer);
1170
+ timer = void 0;
1171
+ controller?.abort();
1172
+ controller = void 0;
1173
+ }
1174
+ };
1175
+ }
1176
+ var ClaudeProjectionStore = class {
1177
+ #sources = /* @__PURE__ */ new Map();
1178
+ source(sessionId) {
1179
+ let source = this.#sources.get(sessionId);
1180
+ if (source === void 0) {
1181
+ source = createClaudeProjectionSource(sessionId);
1182
+ this.#sources.set(sessionId, source);
1183
+ }
1184
+ return source;
1185
+ }
1186
+ dispose() {
1187
+ for (const source of this.#sources.values()) source.dispose();
1188
+ this.#sources.clear();
1189
+ }
1190
+ };
1191
+ //#endregion
1192
+ //#region src/client/locales.ts
1193
+ const zh = {
1194
+ nav: "Claude Code",
1195
+ title: "Claude Code",
1196
+ description: "DSH 使用本机 Claude Code,并保留其 Agent Loop、工具、CLAUDE.md、Skills、Hooks 与 MCP 配置。",
1197
+ activity: "Claude Code 活动",
1198
+ detail: "查看已脱敏详情",
1199
+ thinking: "思考",
1200
+ running: "运行中",
1201
+ failed: "失败",
1202
+ done: "完成",
1203
+ subagent: "子代理",
1204
+ tokens: "{count} 个 token",
1205
+ doctor: "运行诊断",
1206
+ refreshing: "诊断中…",
1207
+ executable: "可执行文件",
1208
+ version: "版本",
1209
+ authentication: "登录状态",
1210
+ handshake: "协议握手",
1211
+ processes: "活动进程",
1212
+ limits: "进程限制",
1213
+ processSummary: "共 {total} 个 · {active} 个活动中",
1214
+ limitSummary: "最多 {max} 个 · 空闲 {minutes} 分钟后回收",
1215
+ missing: "未找到",
1216
+ unknown: "未知",
1217
+ error: "诊断失败",
1218
+ security: "权限边界",
1219
+ securityBody: "Claude 工具权限通过 DSH 审批界面决定;当前版本不宣称对 ~/.claude 与工作区之外路径提供内核级写入隔离。",
1220
+ tasks: "任务",
1221
+ tasksPanel: "后台任务",
1222
+ tasksEmpty: "当前没有子代理或后台任务",
1223
+ tasksOpen: "查看后台任务",
1224
+ tasksClose: "关闭任务面板",
1225
+ tasksRunning: "运行中",
1226
+ tasksNoneRunning: "当前没有运行中的任务",
1227
+ tasksSettled: "已结束",
1228
+ tasksClear: "清除",
1229
+ tasksViewActivity: "查看活动",
1230
+ tasksHideActivity: "收起活动",
1231
+ tasksRunningCount: "{count} 个运行中任务",
1232
+ tasksToolUses: "{count} 次工具调用",
1233
+ tasksCompleted: "已完成",
1234
+ tasksFailed: "失败",
1235
+ tasksStopped: "已停止",
1236
+ tasksKilled: "已终止",
1237
+ tasksBackground: "后台",
1238
+ tasksLastTool: "最近工具 {tool}"
1239
+ };
1240
+ const en = {
1241
+ nav: "Claude Code",
1242
+ title: "Claude Code",
1243
+ description: "DSH uses the local Claude Code and preserves its agent loop, tools, CLAUDE.md, Skills, Hooks, and MCP configuration.",
1244
+ activity: "Claude Code activity",
1245
+ detail: "Show redacted detail",
1246
+ thinking: "Think",
1247
+ running: "Running",
1248
+ failed: "Failed",
1249
+ done: "Done",
1250
+ subagent: "Subagent",
1251
+ tokens: "{count} tokens",
1252
+ doctor: "Run Doctor",
1253
+ refreshing: "Running Doctor…",
1254
+ executable: "Executable",
1255
+ version: "Version",
1256
+ authentication: "Authentication",
1257
+ handshake: "Protocol handshake",
1258
+ processes: "Live processes",
1259
+ limits: "Process limits",
1260
+ processSummary: "{total} total · {active} active",
1261
+ limitSummary: "{max} max · {minutes} min idle",
1262
+ missing: "Not found",
1263
+ unknown: "Unknown",
1264
+ error: "Doctor failed",
1265
+ security: "Permission boundary",
1266
+ securityBody: "Claude tool permissions are decided through the DSH approval UI. This version does not claim kernel-level write isolation for ~/.claude or paths outside the workspace.",
1267
+ tasks: "Tasks",
1268
+ tasksPanel: "Background tasks",
1269
+ tasksEmpty: "No subagents or background tasks",
1270
+ tasksOpen: "Show background tasks",
1271
+ tasksClose: "Close tasks panel",
1272
+ tasksRunning: "Running",
1273
+ tasksNoneRunning: "No tasks are running",
1274
+ tasksSettled: "Finished",
1275
+ tasksClear: "Clear",
1276
+ tasksViewActivity: "View activity",
1277
+ tasksHideActivity: "Hide activity",
1278
+ tasksRunningCount: "{count} running task(s)",
1279
+ tasksToolUses: "{count} tool use(s)",
1280
+ tasksCompleted: "Completed",
1281
+ tasksFailed: "Failed",
1282
+ tasksStopped: "Stopped",
1283
+ tasksKilled: "Killed",
1284
+ tasksBackground: "Background",
1285
+ tasksLastTool: "Last tool {tool}"
1286
+ };
1287
+ //#endregion
1288
+ //#region src/client/index.tsx
1289
+ const name = "dsh-claude-client";
1290
+ const inject = [
1291
+ "slots",
1292
+ "locale",
1293
+ "conversationEvents",
1294
+ "sessions"
1295
+ ];
1296
+ function apply(ctx) {
1297
+ const namespace = "settings.claude-code";
1298
+ ctx.effect(() => ctx.locale.register(namespace, {
1299
+ zh,
1300
+ en
1301
+ }), "dsh-claude: client copy");
1302
+ const t = ctx.locale.bind(namespace);
1303
+ const projections = new ClaudeProjectionStore();
1304
+ const sessions = ctx.get("sessions");
1305
+ if (sessions !== void 0) ctx.effect(() => sessions.provide({
1306
+ hooks: ["claudeProjection"],
1307
+ resolve: (binding) => ({ hooks: { claudeProjection: projections.source(binding.sessionId) } })
1308
+ }), "dsh-claude: sidecar projection provider");
1309
+ ctx.effect(() => () => projections.dispose(), "dsh-claude: sidecar projection lifecycle");
1310
+ ctx.effect(() => ctx.conversationEvents.register(claudeTurnDefinition), "dsh-claude: Claude turn marker");
1311
+ ctx.effect(() => ctx.conversationEvents.register(claudeActivityStepDefinition), "dsh-claude: Claude activity flow node");
1312
+ ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
1313
+ name: "conversation.chat.node",
1314
+ key: "claude-activity-step",
1315
+ locale: namespace
1316
+ }, ClaudeActivityNode));
1317
+ const layout = ctx.get("layout");
1318
+ const tasksPanelListeners = /* @__PURE__ */ new Set();
1319
+ let disposeTasksDetails;
1320
+ let tasksPanelSession;
1321
+ const notifyTasksPanel = () => {
1322
+ for (const fn of [...tasksPanelListeners]) fn();
1323
+ };
1324
+ const closeTasksPanel = () => {
1325
+ if (disposeTasksDetails === void 0) return;
1326
+ disposeTasksDetails();
1327
+ disposeTasksDetails = void 0;
1328
+ tasksPanelSession = void 0;
1329
+ layout?.closeDetails();
1330
+ notifyTasksPanel();
1331
+ };
1332
+ const openTasksPanel = (sessionId) => {
1333
+ closeTasksPanel();
1334
+ try {
1335
+ disposeTasksDetails = ctx.slots.register({
1336
+ name: "details",
1337
+ priority: -10,
1338
+ locale: namespace,
1339
+ inject: () => ({
1340
+ t,
1341
+ closeDetails: closeTasksPanel
1342
+ })
1343
+ }, ClaudeTasksPanel);
1344
+ } catch {
1345
+ return;
1346
+ }
1347
+ tasksPanelSession = sessionId;
1348
+ layout?.openDetails();
1349
+ notifyTasksPanel();
1350
+ };
1351
+ const toggleTasksPanel = (sessionId) => {
1352
+ if (tasksPanelSession === sessionId) closeTasksPanel();
1353
+ else openTasksPanel(sessionId);
1354
+ };
1355
+ ctx.effect(() => {
1356
+ if (typeof document === "undefined" || typeof MutationObserver === "undefined") return () => closeTasksPanel();
1357
+ const observer = new MutationObserver(() => {
1358
+ if (tasksPanelSession !== void 0 && document.querySelector("[data-details-collapsed]") !== null) closeTasksPanel();
1359
+ });
1360
+ observer.observe(document.body, {
1361
+ attributes: true,
1362
+ attributeFilter: ["data-details-collapsed"],
1363
+ subtree: true
1364
+ });
1365
+ return () => {
1366
+ observer.disconnect();
1367
+ closeTasksPanel();
1368
+ };
1369
+ }, "dsh-claude: tasks panel lifecycle");
1370
+ const tasksLauncher = (sessionId) => ({
1371
+ isOpen: () => tasksPanelSession === sessionId,
1372
+ toggle: () => toggleTasksPanel(sessionId),
1373
+ subscribe: (fn) => {
1374
+ tasksPanelListeners.add(fn);
1375
+ return () => {
1376
+ tasksPanelListeners.delete(fn);
1377
+ };
1378
+ }
1379
+ });
1380
+ ctx.slots.inject("conversation.chat.turnTail", () => ctx.slots.register({
1381
+ name: "conversation.chat.turnTail",
1382
+ select: selectClaudeTurn,
1383
+ inject: (sessionId) => ({
1384
+ t,
1385
+ openTasks: () => openTasksPanel(sessionId)
1386
+ })
1387
+ }, ClaudeActivityTail));
1388
+ if (sessions !== void 0) ctx.effect(() => sessions.list.subscribe(() => {
1389
+ if (tasksPanelSession !== void 0 && sessions.list.getSnapshot().current !== tasksPanelSession) closeTasksPanel();
1390
+ }), "dsh-claude: tasks panel session tracking");
1391
+ ctx.slots.inject("conversation.session.header.utilities", () => ctx.slots.register({
1392
+ name: "conversation.session.header.utilities",
1393
+ id: "claude-tasks",
1394
+ order: -10,
1395
+ label: () => t("tasksOpen"),
1396
+ inject: (sessionId) => ({
1397
+ t,
1398
+ ...tasksLauncher(sessionId)
1399
+ })
1400
+ }, ClaudeTasksHeaderButton));
1401
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
1402
+ name: "settings.section",
1403
+ id: "claude-code",
1404
+ order: 16,
1405
+ label: () => t("nav"),
1406
+ inject: () => ({ t })
1407
+ }, ClaudeCodeSettings));
1408
+ }
1409
+ //#endregion
1410
+ module.exports.apply = apply;
1411
+ module.exports.inject = inject;
1412
+ module.exports.name = name;
1413
+ return module.exports;
1414
+ }
1415
+ });
1416
+
1417
+ //# sourceMappingURL=client.js.map