@shendeguize/dsh-agent-sidecar 0.1.0 → 0.1.1
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 +874 -168
- package/lib/client.js.map +1 -1
- package/package.json +1 -1
- package/src/client/board/Board.tsx +168 -15
- package/src/client/board/board.module.css +100 -0
- package/src/client/board/logic.ts +99 -21
- package/src/client/board/project-view-logic.ts +9 -3
- package/src/client/board/project-view.module.css +18 -0
- package/src/client/board/project-view.tsx +27 -1
- package/src/client/board/strings.ts +29 -5
- package/src/client/controller.ts +27 -7
- package/src/client/detail/SessionDetail.tsx +177 -14
- package/src/client/detail/detail.module.css +105 -2
- package/src/client/detail/logic.ts +220 -29
- package/src/client/detail/strings.ts +13 -0
- package/src/client/detail-glue.ts +33 -0
- package/src/client/detail-view.module.css +36 -3
- package/src/client/detail-view.tsx +76 -28
- package/src/client/dsh-tools/dsh-tools.module.css +6 -4
- package/src/client/inject/InjectPanel.tsx +57 -4
- package/src/client/inject/inject.module.css +68 -67
- package/src/client/inject/logic.ts +38 -0
- package/src/client/inject/overlay.module.css +1 -1
- package/src/client/locales/en.ts +17 -2
- package/src/client/locales/zh.ts +17 -2
- package/src/client/mount.tsx +3 -3
- package/src/client/widget.tsx +37 -12
package/lib/client.js
CHANGED
|
@@ -155,11 +155,12 @@ window.__ModuleLoader__.load({
|
|
|
155
155
|
dead: "已结束",
|
|
156
156
|
unknown: "未知"
|
|
157
157
|
},
|
|
158
|
-
/**
|
|
159
|
-
attention
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
158
|
+
/**
|
|
159
|
+
* Badge attention markers. Only the per-session gap marker renders at
|
|
160
|
+
* card level; the global stream-health notice lives in the top banner
|
|
161
|
+
* alone (UX-18: repeating it on every card was pure noise).
|
|
162
|
+
*/
|
|
163
|
+
attention: { gap: "事件缺口" },
|
|
163
164
|
/** Daemon supervisor state labels for the top-bar badge. */
|
|
164
165
|
daemon: {
|
|
165
166
|
probe: "探测中",
|
|
@@ -189,7 +190,7 @@ window.__ModuleLoader__.load({
|
|
|
189
190
|
daemonDeferTitle: "等待系统服务拉起 daemon",
|
|
190
191
|
daemonDeferHint: "检测到 LaunchAgent 托管,插件不会自行启动 daemon;服务拉起后看板会自动出数。",
|
|
191
192
|
filteredTitle: "当前过滤条件下没有会话",
|
|
192
|
-
filteredHint: "
|
|
193
|
+
filteredHint: "试试放宽时间窗、取消状态过滤,或打开「显示已结束」。",
|
|
193
194
|
noSessionsTitle: "暂无被观测的会话",
|
|
194
195
|
noSessionsHint: "本机 agent(claude / codex / cursor / dsh …)开始工作后会自动出现在这里。"
|
|
195
196
|
},
|
|
@@ -197,9 +198,25 @@ window.__ModuleLoader__.load({
|
|
|
197
198
|
topbar: {
|
|
198
199
|
title: "Sidecar 多 agent 看板",
|
|
199
200
|
refresh: "刷新",
|
|
201
|
+
refreshing: "刷新中…",
|
|
200
202
|
refreshTitle: "手动拉取最新快照",
|
|
203
|
+
refreshFailed: "手动刷新失败,看板仍显示原有快照",
|
|
204
|
+
dismiss: "知道了",
|
|
201
205
|
showDead: "显示已结束",
|
|
202
|
-
timeWindow: "时间窗"
|
|
206
|
+
timeWindow: "时间窗",
|
|
207
|
+
/** Count badges (UX-01); working/waiting are clickable status filters. */
|
|
208
|
+
countWorking: "{n} 工作中",
|
|
209
|
+
countWaiting: "{n} 等待中",
|
|
210
|
+
countTotal: "共 {n} 个会话",
|
|
211
|
+
filterByStatusTitle: "只看「{label}」的会话",
|
|
212
|
+
clearStatusFilterTitle: "取消状态过滤,显示全部会话"
|
|
213
|
+
},
|
|
214
|
+
/** Group collapse / truncation controls (UX-02). */
|
|
215
|
+
group: {
|
|
216
|
+
collapseTitle: "收起该分组",
|
|
217
|
+
expandTitle: "展开该分组",
|
|
218
|
+
showAll: "展开全部 {n} 个会话",
|
|
219
|
+
showLess: "只看前 {n} 个"
|
|
203
220
|
},
|
|
204
221
|
/** Session card texts. */
|
|
205
222
|
card: {
|
|
@@ -209,14 +226,19 @@ window.__ModuleLoader__.load({
|
|
|
209
226
|
observedDisclaimer: "状态为从持久化数据推断的观察值,可能滞后",
|
|
210
227
|
observedValue: "观察值: {status}",
|
|
211
228
|
lastReconcile: "最近对账: {time}",
|
|
212
|
-
neverReconciled: "尚未对账"
|
|
229
|
+
neverReconciled: "尚未对账",
|
|
230
|
+
copyId: "点击复制完整会话 id",
|
|
231
|
+
copied: "已复制"
|
|
213
232
|
},
|
|
214
|
-
/**
|
|
233
|
+
/**
|
|
234
|
+
* Relative time templates. Ages ≥ 24h render as an absolute short date
|
|
235
|
+
* (`MM-DD HH:mm`, see formatRelativeTime) instead of a `{n} 天前` bucket
|
|
236
|
+
* (UX-13: whole columns of "3 天前" carried zero information).
|
|
237
|
+
*/
|
|
215
238
|
time: {
|
|
216
239
|
justNow: "刚刚",
|
|
217
240
|
minutesAgo: "{n} 分钟前",
|
|
218
|
-
hoursAgo: "{n} 小时前"
|
|
219
|
-
daysAgo: "{n} 天前"
|
|
241
|
+
hoursAgo: "{n} 小时前"
|
|
220
242
|
},
|
|
221
243
|
/** Time-window option templates. */
|
|
222
244
|
timeWindow: {
|
|
@@ -297,6 +319,9 @@ window.__ModuleLoader__.load({
|
|
|
297
319
|
};
|
|
298
320
|
/**
|
|
299
321
|
* Visibility rules (task spec):
|
|
322
|
+
* - an active `statusFilter` (UX-01) overrides everything: only sessions
|
|
323
|
+
* of that status are visible, regardless of window or showDead — the
|
|
324
|
+
* count badge and the filtered board therefore always agree;
|
|
300
325
|
* - dead sessions are hidden unless `showDead`;
|
|
301
326
|
* - working sessions are always visible (even outside the window);
|
|
302
327
|
* - everything else hides once `updatedAtMs` falls strictly beyond the
|
|
@@ -305,6 +330,7 @@ window.__ModuleLoader__.load({
|
|
|
305
330
|
*/
|
|
306
331
|
function isSessionVisible(session, filters, nowMs) {
|
|
307
332
|
const status = normalizeStatus(session.status);
|
|
333
|
+
if (filters.statusFilter !== void 0) return status === filters.statusFilter;
|
|
308
334
|
if (status === "dead" && !filters.showDead) return false;
|
|
309
335
|
if (status === "working") return true;
|
|
310
336
|
const windowMs = filters.timeWindowHours * HOUR_MS$2;
|
|
@@ -364,20 +390,19 @@ window.__ModuleLoader__.load({
|
|
|
364
390
|
return groups;
|
|
365
391
|
}
|
|
366
392
|
/**
|
|
367
|
-
* status + gap
|
|
393
|
+
* status + gap → badge tone/label/attention.
|
|
368
394
|
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
395
|
+
* Card-level attention carries ONLY the per-session `gap` marker (a known
|
|
396
|
+
* data hole for THIS session). The global stream-health state is a
|
|
397
|
+
* board-wide fact and lives in the top banner alone — repeating it on
|
|
398
|
+
* every card was noise, not signal (UX-18). Unknown raw statuses keep
|
|
372
399
|
* their raw text as the label — the board never invents a state.
|
|
373
400
|
*/
|
|
374
|
-
function deriveBadge(rawStatus, gap
|
|
401
|
+
function deriveBadge(rawStatus, gap) {
|
|
375
402
|
const status = normalizeStatus(rawStatus);
|
|
376
403
|
const trimmed = rawStatus.trim();
|
|
377
404
|
const label = status === "unknown" ? trimmed === "" ? BOARD_STRINGS.status.unknown : trimmed : BOARD_STRINGS.status[status];
|
|
378
|
-
|
|
379
|
-
if (gap) attention = "gap";
|
|
380
|
-
else if (streamHealth !== "ok") attention = "stale";
|
|
405
|
+
const attention = gap ? "gap" : null;
|
|
381
406
|
return {
|
|
382
407
|
status,
|
|
383
408
|
tone: STATUS_TONE$1[status],
|
|
@@ -400,8 +425,19 @@ window.__ModuleLoader__.load({
|
|
|
400
425
|
].join("\n");
|
|
401
426
|
}
|
|
402
427
|
/**
|
|
403
|
-
*
|
|
404
|
-
*
|
|
428
|
+
* Absolute short timestamp in local time: `MM-DD HH:mm`. Used for ages
|
|
429
|
+
* beyond 24h where relative buckets stop discriminating (UX-13).
|
|
430
|
+
*/
|
|
431
|
+
function formatAbsoluteShort(thenMs) {
|
|
432
|
+
const date = new Date(thenMs);
|
|
433
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
434
|
+
return `${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Card time: <60s (including clock skew into the future) is 刚刚, then
|
|
438
|
+
* whole minutes/hours; from 24h on the absolute short date takes over —
|
|
439
|
+
* a column of "3 天前" carries no information, "08-22 14:03" does
|
|
440
|
+
* (UX-13). Non-finite input renders empty.
|
|
405
441
|
*/
|
|
406
442
|
function formatRelativeTime$1(thenMs, nowMs) {
|
|
407
443
|
if (!Number.isFinite(thenMs)) return "";
|
|
@@ -409,7 +445,7 @@ window.__ModuleLoader__.load({
|
|
|
409
445
|
if (delta < MINUTE_MS$2) return BOARD_STRINGS.time.justNow;
|
|
410
446
|
if (delta < HOUR_MS$2) return formatTemplate$2(BOARD_STRINGS.time.minutesAgo, { n: Math.floor(delta / MINUTE_MS$2) });
|
|
411
447
|
if (delta < DAY_MS$2) return formatTemplate$2(BOARD_STRINGS.time.hoursAgo, { n: Math.floor(delta / HOUR_MS$2) });
|
|
412
|
-
return
|
|
448
|
+
return formatAbsoluteShort(thenMs);
|
|
413
449
|
}
|
|
414
450
|
/** Label for a time-window option: whole days as 天, otherwise 小时. */
|
|
415
451
|
function timeWindowLabel(hours) {
|
|
@@ -504,6 +540,30 @@ window.__ModuleLoader__.load({
|
|
|
504
540
|
return `${id.slice(0, 12)}…${id.slice(-6)}`;
|
|
505
541
|
}
|
|
506
542
|
/**
|
|
543
|
+
* Slice a status-sorted card list down to `limit` for display, unless
|
|
544
|
+
* `expanded`. The cut never lands inside the leading working/waiting run:
|
|
545
|
+
* attention-worthy sessions are the reason the board exists, so the
|
|
546
|
+
* effective limit grows to cover all of them (an all-active group renders
|
|
547
|
+
* fully — honest, and rare). Non-positive limits disable truncation.
|
|
548
|
+
*/
|
|
549
|
+
function sliceCardsForDisplay(cards, limit, expanded) {
|
|
550
|
+
if (expanded || limit <= 0 || cards.length <= limit) return {
|
|
551
|
+
shown: [...cards],
|
|
552
|
+
hiddenCount: 0
|
|
553
|
+
};
|
|
554
|
+
let activeRun = 0;
|
|
555
|
+
while (activeRun < cards.length) {
|
|
556
|
+
const status = normalizeStatus(cards[activeRun].status);
|
|
557
|
+
if (status !== "working" && status !== "waiting") break;
|
|
558
|
+
activeRun += 1;
|
|
559
|
+
}
|
|
560
|
+
const effectiveLimit = Math.max(limit, activeRun);
|
|
561
|
+
return {
|
|
562
|
+
shown: cards.slice(0, effectiveLimit),
|
|
563
|
+
hiddenCount: cards.length - Math.min(cards.length, effectiveLimit)
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
507
567
|
* Connection dot: green only when the daemon is connected (adopted/hosted)
|
|
508
568
|
* AND the event stream is healthy; FAILED is the only hard-off state;
|
|
509
569
|
* every transitional state shows the cautious yellow.
|
|
@@ -513,12 +573,16 @@ window.__ModuleLoader__.load({
|
|
|
513
573
|
if ((daemonState === "adopted" || daemonState === "hosted") && streamHealth === "ok") return "ok";
|
|
514
574
|
return "degraded";
|
|
515
575
|
}
|
|
516
|
-
/** Count of sessions
|
|
517
|
-
function
|
|
576
|
+
/** Count of sessions observed in one normalized status. */
|
|
577
|
+
function countByStatus(sessions, status) {
|
|
518
578
|
let count = 0;
|
|
519
|
-
for (const session of sessions) if (normalizeStatus(session.status) ===
|
|
579
|
+
for (const session of sessions) if (normalizeStatus(session.status) === status) count += 1;
|
|
520
580
|
return count;
|
|
521
581
|
}
|
|
582
|
+
/** Count of sessions currently observed as working. */
|
|
583
|
+
function countWorking(sessions) {
|
|
584
|
+
return countByStatus(sessions, "working");
|
|
585
|
+
}
|
|
522
586
|
/** Widget hover/aria text: connection state, plus the count when nonzero. */
|
|
523
587
|
function widgetTitle(connection, workingCount) {
|
|
524
588
|
const base = `${BOARD_STRINGS.widget.label}: ${BOARD_STRINGS.widget.connection[connection]}`;
|
|
@@ -532,7 +596,7 @@ window.__ModuleLoader__.load({
|
|
|
532
596
|
return {
|
|
533
597
|
groups: groupSessions(visible.map((session) => ({
|
|
534
598
|
...session,
|
|
535
|
-
badge: deriveBadge(session.status, session.gap
|
|
599
|
+
badge: deriveBadge(session.status, session.gap),
|
|
536
600
|
glyph: agentGlyph$1(session.agent),
|
|
537
601
|
shortId: abbreviateSessionId(session.sessionId),
|
|
538
602
|
relativeTime: formatRelativeTime$1(session.updatedAtMs, nowMs),
|
|
@@ -545,7 +609,8 @@ window.__ModuleLoader__.load({
|
|
|
545
609
|
streamTone: streamHealthTone(streamHealth),
|
|
546
610
|
visibleCount: visible.length,
|
|
547
611
|
totalCount: sessions.length,
|
|
548
|
-
workingCount: countWorking(sessions)
|
|
612
|
+
workingCount: countWorking(sessions),
|
|
613
|
+
waitingCount: countByStatus(sessions, "waiting")
|
|
549
614
|
};
|
|
550
615
|
}
|
|
551
616
|
//#endregion
|
|
@@ -571,6 +636,11 @@ window.__ModuleLoader__.load({
|
|
|
571
636
|
listenOn: "监听中",
|
|
572
637
|
listenOff: "监听",
|
|
573
638
|
listenHint: "开启后新事件将实时追加并高亮",
|
|
639
|
+
refresh: "刷新",
|
|
640
|
+
refreshing: "刷新中…",
|
|
641
|
+
refreshHint: "手动拉取最新时间线窗口",
|
|
642
|
+
copyIdTitle: "点击复制会话 ID",
|
|
643
|
+
copied: "已复制",
|
|
574
644
|
untitled: "(无标题)",
|
|
575
645
|
unknownProject: "未知项目",
|
|
576
646
|
/** Design §5.3 / SKILL.md wording: statuses are inferred observations. */
|
|
@@ -607,6 +677,12 @@ window.__ModuleLoader__.load({
|
|
|
607
677
|
},
|
|
608
678
|
/** Seq-discontinuity marker row (design §4.b.3 honest presentation). */
|
|
609
679
|
gap: { label: "缺口:可能有 {n} 条事件未捕获(256 队列上限或未持久化)" },
|
|
680
|
+
/** Kind filter chips (UX-03): protocol noise hidden by default, honestly counted. */
|
|
681
|
+
filter: {
|
|
682
|
+
conversation: "只看对话",
|
|
683
|
+
all: "全部事件",
|
|
684
|
+
hiddenNotice: "已隐藏 {n} 条协议事件"
|
|
685
|
+
},
|
|
610
686
|
/** Timeline list chrome. */
|
|
611
687
|
timeline: {
|
|
612
688
|
loadMore: "加载更多历史",
|
|
@@ -617,7 +693,9 @@ window.__ModuleLoader__.load({
|
|
|
617
693
|
newBadge: "新",
|
|
618
694
|
seq: "seq {n}",
|
|
619
695
|
hiddenNotice: "为保持流畅,较早的 {n} 条已折叠",
|
|
620
|
-
showAll: "全部显示"
|
|
696
|
+
showAll: "全部显示",
|
|
697
|
+
/** Aggregated row for an adjacent run of empty streaming chunks (UX-03). */
|
|
698
|
+
chunkRun: "{n} 个流式分块"
|
|
621
699
|
},
|
|
622
700
|
/** Loading / empty / error body states. */
|
|
623
701
|
states: {
|
|
@@ -773,6 +851,9 @@ window.__ModuleLoader__.load({
|
|
|
773
851
|
liveChip: "实时",
|
|
774
852
|
/** Untitled-session fallback. */
|
|
775
853
|
untitled: "(无标题)",
|
|
854
|
+
/** Lane truncation fold (UX-20; mirrors the board's group fold). */
|
|
855
|
+
showAllSessions: "展开全部 {n} 个会话",
|
|
856
|
+
showLessSessions: "只看前 {n} 个",
|
|
776
857
|
/** Empty state (no groups at all). */
|
|
777
858
|
empty: {
|
|
778
859
|
title: "暂无项目关联",
|
|
@@ -835,7 +916,7 @@ window.__ModuleLoader__.load({
|
|
|
835
916
|
lastActivityAt: session.lastActivityAt,
|
|
836
917
|
live,
|
|
837
918
|
gap,
|
|
838
|
-
badge: deriveBadge(session.status, gap
|
|
919
|
+
badge: deriveBadge(session.status, gap),
|
|
839
920
|
glyph: agentGlyph$1(session.agent),
|
|
840
921
|
shortId: abbreviateSessionId(session.sessionId),
|
|
841
922
|
relativeTime: formatRelativeTime$1(session.lastActivityAt, nowMs),
|
|
@@ -1010,7 +1091,7 @@ window.__ModuleLoader__.load({
|
|
|
1010
1091
|
"settings.streamIdleMsHint": "无会话工作时的 status 快照周期。",
|
|
1011
1092
|
"settings.sectionInject": "消息注入",
|
|
1012
1093
|
"settings.injectEnabledLabel": "启用注入",
|
|
1013
|
-
"settings.injectEnabledHint": "
|
|
1094
|
+
"settings.injectEnabledHint": "关闭后注入面板为只读禁用态,写接口在服务端同步拒绝。",
|
|
1014
1095
|
"settings.injectDefaultModeLabel": "默认注入模式",
|
|
1015
1096
|
"settings.injectDefaultModeHint": "注入面板打开时预选的模式。",
|
|
1016
1097
|
"settings.injectModeQueue": "queue(排队下一轮)",
|
|
@@ -1064,6 +1145,7 @@ window.__ModuleLoader__.load({
|
|
|
1064
1145
|
"inject.resultUnknown": "结果未知:消息可能已投递。请勿重试;请前往目标会话核对后再决定下一步。",
|
|
1065
1146
|
"inject.resultReplayed": "幂等重放:返回的是此前同一请求的结果,未发生二次注入。",
|
|
1066
1147
|
"inject.reprepare": "重新准备",
|
|
1148
|
+
"inject.observeListen": "开启监听观察反应",
|
|
1067
1149
|
"inject.errInjectDisabled": "注入功能已在服务端关闭;请在设置中开启注入。",
|
|
1068
1150
|
"inject.errInvalidMessage": "消息未通过服务端校验。",
|
|
1069
1151
|
"inject.errTargetNotFound": "目标会话不存在或已离开观测范围。",
|
|
@@ -1086,6 +1168,11 @@ window.__ModuleLoader__.load({
|
|
|
1086
1168
|
"detail.header.listenOn": D.header.listenOn,
|
|
1087
1169
|
"detail.header.listenOff": D.header.listenOff,
|
|
1088
1170
|
"detail.header.listenHint": D.header.listenHint,
|
|
1171
|
+
"detail.header.refresh": D.header.refresh,
|
|
1172
|
+
"detail.header.refreshing": D.header.refreshing,
|
|
1173
|
+
"detail.header.refreshHint": D.header.refreshHint,
|
|
1174
|
+
"detail.header.copyIdTitle": D.header.copyIdTitle,
|
|
1175
|
+
"detail.header.copied": D.header.copied,
|
|
1089
1176
|
"detail.header.untitled": D.header.untitled,
|
|
1090
1177
|
"detail.header.unknownProject": D.header.unknownProject,
|
|
1091
1178
|
"detail.header.observedDisclaimer": D.header.observedDisclaimer,
|
|
@@ -1110,6 +1197,9 @@ window.__ModuleLoader__.load({
|
|
|
1110
1197
|
"detail.kind.error": D.kind.error,
|
|
1111
1198
|
"detail.kind.other": D.kind.other,
|
|
1112
1199
|
"detail.gap.label": D.gap.label,
|
|
1200
|
+
"detail.filter.conversation": D.filter.conversation,
|
|
1201
|
+
"detail.filter.all": D.filter.all,
|
|
1202
|
+
"detail.filter.hiddenNotice": D.filter.hiddenNotice,
|
|
1113
1203
|
"detail.timeline.loadMore": D.timeline.loadMore,
|
|
1114
1204
|
"detail.timeline.loadingMore": D.timeline.loadingMore,
|
|
1115
1205
|
"detail.timeline.noMore": D.timeline.noMore,
|
|
@@ -1119,6 +1209,7 @@ window.__ModuleLoader__.load({
|
|
|
1119
1209
|
"detail.timeline.seq": D.timeline.seq,
|
|
1120
1210
|
"detail.timeline.hiddenNotice": D.timeline.hiddenNotice,
|
|
1121
1211
|
"detail.timeline.showAll": D.timeline.showAll,
|
|
1212
|
+
"detail.timeline.chunkRun": D.timeline.chunkRun,
|
|
1122
1213
|
"detail.states.loadingTitle": D.states.loadingTitle,
|
|
1123
1214
|
"detail.states.emptyTitle": D.states.emptyTitle,
|
|
1124
1215
|
"detail.states.emptyHint": D.states.emptyHint,
|
|
@@ -1136,6 +1227,9 @@ window.__ModuleLoader__.load({
|
|
|
1136
1227
|
"detail.actions.inject": "注入",
|
|
1137
1228
|
"detail.actions.analyze": "AI 分析",
|
|
1138
1229
|
"detail.actions.analyzeDisabledHint": "在设置中开启「启用 AI 旁路分析」后可用",
|
|
1230
|
+
"detail.tools.title": "谱系与检索",
|
|
1231
|
+
"detail.tools.show": "展开",
|
|
1232
|
+
"detail.tools.hide": "收起",
|
|
1139
1233
|
"dshtools.lineage.title": Q.lineage.title,
|
|
1140
1234
|
"dshtools.lineage.loading": Q.lineage.loading,
|
|
1141
1235
|
"dshtools.lineage.error": Q.lineage.error,
|
|
@@ -1181,6 +1275,8 @@ window.__ModuleLoader__.load({
|
|
|
1181
1275
|
"project.lastActive": P.lastActive,
|
|
1182
1276
|
"project.liveChip": P.liveChip,
|
|
1183
1277
|
"project.untitled": P.untitled,
|
|
1278
|
+
"project.showAllSessions": P.showAllSessions,
|
|
1279
|
+
"project.showLessSessions": P.showLessSessions,
|
|
1184
1280
|
"project.empty.title": P.empty.title,
|
|
1185
1281
|
"project.empty.hint": P.empty.hint,
|
|
1186
1282
|
"project.loading": P.loading,
|
|
@@ -1239,7 +1335,7 @@ window.__ModuleLoader__.load({
|
|
|
1239
1335
|
"command.noSessions": "暂无被观测的会话",
|
|
1240
1336
|
"command.unknownProject": "未知项目",
|
|
1241
1337
|
"command.untitled": "(无标题)",
|
|
1242
|
-
"command.truncated": "还有 {n}
|
|
1338
|
+
"command.truncated": "还有 {n} 个会话未列出",
|
|
1243
1339
|
"command.boardHint": "打开会话视图的「Sidecar」Tab 查看完整看板",
|
|
1244
1340
|
"command.unreachable": "sidecar 未连接",
|
|
1245
1341
|
"command.unreachableHint": "无法获取状态快照;请确认 agent-sidecar 插件已启用、daemon 可用后重试。",
|
|
@@ -1308,7 +1404,7 @@ window.__ModuleLoader__.load({
|
|
|
1308
1404
|
"settings.streamIdleMsHint": "Status snapshot cadence while no session is working.",
|
|
1309
1405
|
"settings.sectionInject": "Message injection",
|
|
1310
1406
|
"settings.injectEnabledLabel": "Enable injection",
|
|
1311
|
-
"settings.injectEnabledHint": "When off, the
|
|
1407
|
+
"settings.injectEnabledHint": "When off, the inject panel renders read-only and disabled, and the server rejects write actions.",
|
|
1312
1408
|
"settings.injectDefaultModeLabel": "Default injection mode",
|
|
1313
1409
|
"settings.injectDefaultModeHint": "The mode preselected when the inject panel opens.",
|
|
1314
1410
|
"settings.injectModeQueue": "queue (next turn)",
|
|
@@ -1362,6 +1458,7 @@ window.__ModuleLoader__.load({
|
|
|
1362
1458
|
"inject.resultUnknown": "Outcome unknown: the message may have been delivered. Do NOT retry; check the target session before deciding anything.",
|
|
1363
1459
|
"inject.resultReplayed": "Idempotent replay: this is the earlier result of the same request — no second injection happened.",
|
|
1364
1460
|
"inject.reprepare": "Prepare again",
|
|
1461
|
+
"inject.observeListen": "Listen for the reaction",
|
|
1365
1462
|
"inject.errInjectDisabled": "Injection is disabled on the server; enable it in Settings.",
|
|
1366
1463
|
"inject.errInvalidMessage": "The message failed server-side validation.",
|
|
1367
1464
|
"inject.errTargetNotFound": "The target session does not exist or left the observation window.",
|
|
@@ -1384,6 +1481,11 @@ window.__ModuleLoader__.load({
|
|
|
1384
1481
|
"detail.header.listenOn": "Listening",
|
|
1385
1482
|
"detail.header.listenOff": "Listen",
|
|
1386
1483
|
"detail.header.listenHint": "New events append live and get highlighted while on",
|
|
1484
|
+
"detail.header.refresh": "Refresh",
|
|
1485
|
+
"detail.header.refreshing": "Refreshing…",
|
|
1486
|
+
"detail.header.refreshHint": "Pull the newest timeline window",
|
|
1487
|
+
"detail.header.copyIdTitle": "Click to copy the session ID",
|
|
1488
|
+
"detail.header.copied": "Copied",
|
|
1387
1489
|
"detail.header.untitled": "(untitled)",
|
|
1388
1490
|
"detail.header.unknownProject": "Unknown project",
|
|
1389
1491
|
"detail.header.observedDisclaimer": "Status is an observed value inferred from persisted data and may lag",
|
|
@@ -1408,6 +1510,9 @@ window.__ModuleLoader__.load({
|
|
|
1408
1510
|
"detail.kind.error": "Error",
|
|
1409
1511
|
"detail.kind.other": "Event",
|
|
1410
1512
|
"detail.gap.label": "Gap: about {n} events may be uncaptured (256-slot queue cap or not persisted)",
|
|
1513
|
+
"detail.filter.conversation": "Conversation only",
|
|
1514
|
+
"detail.filter.all": "All events",
|
|
1515
|
+
"detail.filter.hiddenNotice": "{n} protocol events hidden",
|
|
1411
1516
|
"detail.timeline.loadMore": "Load older history",
|
|
1412
1517
|
"detail.timeline.loadingMore": "Loading…",
|
|
1413
1518
|
"detail.timeline.noMore": "Start of the timeline",
|
|
@@ -1417,6 +1522,7 @@ window.__ModuleLoader__.load({
|
|
|
1417
1522
|
"detail.timeline.seq": "seq {n}",
|
|
1418
1523
|
"detail.timeline.hiddenNotice": "{n} earlier entries collapsed to stay smooth",
|
|
1419
1524
|
"detail.timeline.showAll": "Show all",
|
|
1525
|
+
"detail.timeline.chunkRun": "{n} streaming chunks",
|
|
1420
1526
|
"detail.states.loadingTitle": "Loading the timeline…",
|
|
1421
1527
|
"detail.states.emptyTitle": "No events yet",
|
|
1422
1528
|
"detail.states.emptyHint": "This session has no normalized events to show yet.",
|
|
@@ -1434,6 +1540,9 @@ window.__ModuleLoader__.load({
|
|
|
1434
1540
|
"detail.actions.inject": "Inject",
|
|
1435
1541
|
"detail.actions.analyze": "AI analysis",
|
|
1436
1542
|
"detail.actions.analyzeDisabledHint": "Enable \"AI bypass analysis\" in Settings to use this",
|
|
1543
|
+
"detail.tools.title": "Lineage & search",
|
|
1544
|
+
"detail.tools.show": "Show",
|
|
1545
|
+
"detail.tools.hide": "Hide",
|
|
1437
1546
|
"dshtools.lineage.title": "Session lineage",
|
|
1438
1547
|
"dshtools.lineage.loading": "Loading lineage…",
|
|
1439
1548
|
"dshtools.lineage.error": "Lineage failed to load",
|
|
@@ -1479,6 +1588,8 @@ window.__ModuleLoader__.load({
|
|
|
1479
1588
|
"project.lastActive": "Last active {time}",
|
|
1480
1589
|
"project.liveChip": "Live",
|
|
1481
1590
|
"project.untitled": "(untitled)",
|
|
1591
|
+
"project.showAllSessions": "Show all {n} sessions",
|
|
1592
|
+
"project.showLessSessions": "Show first {n} only",
|
|
1482
1593
|
"project.empty.title": "No project correlation yet",
|
|
1483
1594
|
"project.empty.hint": "No cross-agent project activity within the time window; projects appear here once an agent works inside a project directory.",
|
|
1484
1595
|
"project.loading": "Loading project correlation…",
|
|
@@ -1537,7 +1648,7 @@ window.__ModuleLoader__.load({
|
|
|
1537
1648
|
"command.noSessions": "No observed sessions yet",
|
|
1538
1649
|
"command.unknownProject": "Unknown project",
|
|
1539
1650
|
"command.untitled": "(untitled)",
|
|
1540
|
-
"command.truncated": "{n} more
|
|
1651
|
+
"command.truncated": "{n} more sessions not listed",
|
|
1541
1652
|
"command.boardHint": "Open the \"Sidecar\" tab in the conversation view for the full board",
|
|
1542
1653
|
"command.unreachable": "sidecar is not connected",
|
|
1543
1654
|
"command.unreachableHint": "The state snapshot could not be fetched; check that the agent-sidecar plugin is enabled and the daemon is available, then retry.",
|
|
@@ -2286,7 +2397,11 @@ window.__ModuleLoader__.load({
|
|
|
2286
2397
|
return null;
|
|
2287
2398
|
}
|
|
2288
2399
|
}
|
|
2289
|
-
/**
|
|
2400
|
+
/**
|
|
2401
|
+
* Parse + validate persisted filters; anything malformed reads as absent.
|
|
2402
|
+
* The optional statusFilter (UX-01) survives only as one of its two legal
|
|
2403
|
+
* values — an unrecognized token is dropped, not the whole record.
|
|
2404
|
+
*/
|
|
2290
2405
|
function readStoredFilters(storage) {
|
|
2291
2406
|
if (storage === null) return null;
|
|
2292
2407
|
try {
|
|
@@ -2296,10 +2411,12 @@ window.__ModuleLoader__.load({
|
|
|
2296
2411
|
if (typeof parsed !== "object" || parsed === null) return null;
|
|
2297
2412
|
const candidate = parsed;
|
|
2298
2413
|
if (typeof candidate.timeWindowHours !== "number" || !Number.isFinite(candidate.timeWindowHours) || candidate.timeWindowHours <= 0 || typeof candidate.showDead !== "boolean") return null;
|
|
2299
|
-
|
|
2414
|
+
const filters = {
|
|
2300
2415
|
timeWindowHours: candidate.timeWindowHours,
|
|
2301
2416
|
showDead: candidate.showDead
|
|
2302
2417
|
};
|
|
2418
|
+
if (candidate.statusFilter === "working" || candidate.statusFilter === "waiting") filters.statusFilter = candidate.statusFilter;
|
|
2419
|
+
return filters;
|
|
2303
2420
|
} catch {
|
|
2304
2421
|
return null;
|
|
2305
2422
|
}
|
|
@@ -2394,13 +2511,20 @@ window.__ModuleLoader__.load({
|
|
|
2394
2511
|
};
|
|
2395
2512
|
this.notify();
|
|
2396
2513
|
}
|
|
2397
|
-
/**
|
|
2514
|
+
/**
|
|
2515
|
+
* Manual refresh (board's refresh button): one out-of-band snapshot
|
|
2516
|
+
* pull. Resolves true when the snapshot applied, false on failure so
|
|
2517
|
+
* the button can surface honest feedback (UX-07) — never rejects; the
|
|
2518
|
+
* stream (and its status surface) remains the health authority.
|
|
2519
|
+
*/
|
|
2398
2520
|
async refresh() {
|
|
2399
2521
|
try {
|
|
2400
2522
|
const snapshot = await this.fetchFn({});
|
|
2401
2523
|
this.applySnapshot(snapshot);
|
|
2524
|
+
return true;
|
|
2402
2525
|
} catch (err) {
|
|
2403
2526
|
console.error("agent-sidecar: manual refresh failed", err);
|
|
2527
|
+
return false;
|
|
2404
2528
|
}
|
|
2405
2529
|
}
|
|
2406
2530
|
applySnapshot(snapshot) {
|
|
@@ -2522,7 +2646,7 @@ window.__ModuleLoader__.load({
|
|
|
2522
2646
|
}
|
|
2523
2647
|
//#endregion
|
|
2524
2648
|
//#region \0dsh-css:/Users/jingyu/Workspace/Projects/agent_sidecar/plugin/src/client/board/board.module.css.mjs
|
|
2525
|
-
const css$8 = ".aJ0YNW_root{box-sizing:border-box;height:100%;min-height:420px;color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-base,transparent);flex-direction:column;gap:12px;padding:16px 20px;display:flex;overflow-y:auto}.aJ0YNW_topbar{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.aJ0YNW_title{color:var(--dsw-alias-label-primary,#1f2328);margin-right:2px;font-size:15px;font-weight:650}.aJ0YNW_badge{border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-layer-1,#00000008);color:var(--dsw-alias-label-secondary,#57606a);white-space:nowrap;border-radius:999px;align-items:center;gap:5px;padding:0 8px;font-size:12px;line-height:20px;display:inline-flex}.aJ0YNW_dot{background:var(--dsw-alias-label-tertiary,#6e7781);border-radius:50%;flex:none;width:8px;height:8px}.aJ0YNW_dot[data-tone=success]{background:var(--dsw-alias-state-success-primary,#1a7f37)}.aJ0YNW_dot[data-tone=warn]{background:var(--dsw-alias-state-warn-primary,#9a6700)}.aJ0YNW_dot[data-tone=danger]{background:var(--dsw-alias-state-error-primary,#cf222e)}.aJ0YNW_dot[data-tone=neutral]{background:var(--dsw-alias-label-tertiary,#6e7781)}.aJ0YNW_dot[data-tone=muted]{background:var(--dsw-alias-label-dimmed,#8c959f)}.aJ0YNW_spacer{flex:1}.aJ0YNW_control{color:var(--dsw-alias-label-secondary,#57606a);white-space:nowrap;align-items:center;gap:5px;font-size:12px;display:inline-flex}.aJ0YNW_select{color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-layer-1,transparent);border:1px solid var(--dsw-alias-border-l2,#0000001f);border-radius:6px;padding:2px 6px;font-family:inherit;font-size:12px}.aJ0YNW_checkbox{accent-color:var(--dsw-alias-brand-primary,#4d6bfe);margin:0}.aJ0YNW_refresh{color:var(--dsw-alias-label-secondary,#57606a);border:1px solid var(--dsw-alias-border-l1,#00000014);cursor:pointer;background:0 0;border-radius:6px;padding:2px 10px;font-family:inherit;font-size:12px}.aJ0YNW_refresh:hover{color:var(--dsw-alias-label-primary,#1f2328);border-color:var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.aJ0YNW_banner{border:1px solid var(--dsw-alias-border-l1,#00000014);border-radius:8px;padding:6px 10px;font-size:12px;line-height:18px}.aJ0YNW_banner[data-tone=warn]{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary,#9a6700));background:var(--dsw-alias-state-warn-tertiary,#9a670014);border-color:var(--dsw-alias-state-warn-secondary,#9a67003d)}.aJ0YNW_banner[data-tone=danger]{color:var(--dsw-alias-state-error-primary,#cf222e);background:var(--dsw-alias-state-error-secondary,#cf222e14);border-color:var(--dsw-alias-state-error-secondary,#cf222e3d)}.aJ0YNW_empty{text-align:center;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:6px;min-height:180px;padding:24px;display:flex}.aJ0YNW_emptyTitle{color:var(--dsw-alias-label-primary,#1f2328);font-size:14px;font-weight:600}.aJ0YNW_emptyHint{max-width:420px;color:var(--dsw-alias-label-secondary,#57606a);font-size:12px;line-height:20px}.aJ0YNW_group{flex-direction:column;gap:8px;display:flex}.aJ0YNW_groupHead{align-items:baseline;gap:8px;min-width:0;display:flex}.aJ0YNW_groupName{color:var(--dsw-alias-label-primary,#1f2328);text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:600;overflow:hidden}.aJ0YNW_groupCount{color:var(--dsw-alias-label-secondary,#57606a);background:var(--dsw-alias-bg-layer-2,#0000000a);border-radius:999px;flex:none;padding:0 8px;font-size:11px;line-height:18px}.aJ0YNW_grid{grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:8px;display:grid}.aJ0YNW_card{text-align:left;border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-layer-1,#00000005);cursor:pointer;border-radius:8px;flex-direction:column;gap:6px;min-width:0;padding:10px 12px;font-family:inherit;display:flex}.aJ0YNW_card:hover{border-color:var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.aJ0YNW_card:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4d6bfe);outline-offset:1px}.aJ0YNW_cardHead{justify-content:space-between;align-items:center;gap:8px;min-width:0;display:flex}.aJ0YNW_agent{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary,#1f2328);align-items:center;gap:5px;font-size:12px;font-weight:600;display:inline-flex;overflow:hidden}.aJ0YNW_glyph{color:var(--dsw-alias-brand-primary,#4d6bfe);flex:none}.aJ0YNW_attention{color:var(--dsw-alias-state-warn-primary,#9a6700);font-size:11px}.aJ0YNW_attention[data-kind=gap]{color:var(--dsw-alias-state-error-primary,#cf222e)}.aJ0YNW_cardTitle{color:var(--dsw-alias-label-primary,#1f2328);text-overflow:ellipsis;white-space:nowrap;font-size:13px;overflow:hidden}.aJ0YNW_cardId{color:var(--dsw-alias-label-tertiary,#6e7781);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow:hidden}.aJ0YNW_cardEvent{color:var(--dsw-alias-label-secondary,#57606a);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.aJ0YNW_cardTime{color:var(--dsw-alias-label-caption,var(--dsw-alias-label-tertiary,#6e7781));font-size:11px}";
|
|
2649
|
+
const css$8 = ".aJ0YNW_root{box-sizing:border-box;height:100%;min-height:420px;color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-base,transparent);flex-direction:column;gap:12px;padding:16px 20px;display:flex;overflow-y:auto}.aJ0YNW_topbar{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.aJ0YNW_title{color:var(--dsw-alias-label-primary,#1f2328);margin-right:2px;font-size:15px;font-weight:650}.aJ0YNW_badge{border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-layer-1,#00000008);color:var(--dsw-alias-label-secondary,#57606a);white-space:nowrap;border-radius:999px;align-items:center;gap:5px;padding:0 8px;font-size:12px;line-height:20px;display:inline-flex}.aJ0YNW_dot{background:var(--dsw-alias-label-tertiary,#6e7781);border-radius:50%;flex:none;width:8px;height:8px}.aJ0YNW_dot[data-tone=success]{background:var(--dsw-alias-state-success-primary,#1a7f37)}.aJ0YNW_dot[data-tone=warn]{background:var(--dsw-alias-state-warn-primary,#9a6700)}.aJ0YNW_dot[data-tone=danger]{background:var(--dsw-alias-state-error-primary,#cf222e)}.aJ0YNW_dot[data-tone=neutral]{background:var(--dsw-alias-label-tertiary,#6e7781)}.aJ0YNW_dot[data-tone=muted]{background:var(--dsw-alias-label-dimmed,#8c959f)}.aJ0YNW_countBadge{border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-layer-1,#00000008);color:var(--dsw-alias-label-secondary,#57606a);white-space:nowrap;cursor:pointer;border-radius:999px;align-items:center;gap:5px;padding:0 8px;font-family:inherit;font-size:12px;line-height:20px;display:inline-flex}.aJ0YNW_countBadge:hover{color:var(--dsw-alias-label-primary,#1f2328);border-color:var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.aJ0YNW_countBadge[aria-pressed=true]{color:var(--dsw-alias-brand-primary,#4d6bfe);border-color:var(--dsw-alias-brand-primary,#4d6bfe)}.aJ0YNW_countTotal{color:var(--dsw-alias-label-secondary,#57606a);white-space:nowrap;font-size:12px}.aJ0YNW_spacer{flex:1}.aJ0YNW_control{color:var(--dsw-alias-label-secondary,#57606a);white-space:nowrap;align-items:center;gap:5px;font-size:12px;display:inline-flex}.aJ0YNW_select{color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-layer-1,transparent);border:1px solid var(--dsw-alias-border-l2,#0000001f);border-radius:6px;padding:2px 6px;font-family:inherit;font-size:12px}.aJ0YNW_checkbox{accent-color:var(--dsw-alias-brand-primary,#4d6bfe);margin:0}.aJ0YNW_refresh{color:var(--dsw-alias-label-secondary,#57606a);border:1px solid var(--dsw-alias-border-l1,#00000014);cursor:pointer;background:0 0;border-radius:6px;padding:2px 10px;font-family:inherit;font-size:12px}.aJ0YNW_refresh:hover{color:var(--dsw-alias-label-primary,#1f2328);border-color:var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.aJ0YNW_refresh:disabled{cursor:default;opacity:.6}.aJ0YNW_banner{border:1px solid var(--dsw-alias-border-l1,#00000014);border-radius:8px;padding:6px 10px;font-size:12px;line-height:18px}.aJ0YNW_banner[data-tone=warn]{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary,#9a6700));background:var(--dsw-alias-state-warn-tertiary,#9a670014);border-color:var(--dsw-alias-state-warn-secondary,#9a67003d)}.aJ0YNW_banner[data-tone=danger]{color:var(--dsw-alias-state-error-primary,#cf222e);background:var(--dsw-alias-state-error-secondary,#cf222e14);border-color:var(--dsw-alias-state-error-secondary,#cf222e3d)}.aJ0YNW_bannerDismiss{color:inherit;cursor:pointer;background:0 0;border:none;margin-left:10px;padding:0;font-family:inherit;font-size:12px;text-decoration:underline}.aJ0YNW_empty{text-align:center;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:6px;min-height:180px;padding:24px;display:flex}.aJ0YNW_emptyTitle{color:var(--dsw-alias-label-primary,#1f2328);font-size:14px;font-weight:600}.aJ0YNW_emptyHint{max-width:420px;color:var(--dsw-alias-label-secondary,#57606a);font-size:12px;line-height:20px}.aJ0YNW_group{flex-direction:column;gap:8px;display:flex}.aJ0YNW_groupHead{text-align:left;cursor:pointer;background:0 0;border:none;align-items:baseline;gap:8px;width:100%;min-width:0;padding:0;font-family:inherit;display:flex}.aJ0YNW_chevron{color:var(--dsw-alias-label-tertiary,#6e7781);flex:none;font-size:10px}.aJ0YNW_groupAttention{color:var(--dsw-alias-state-warn-primary,#9a6700);flex:none;font-size:11px}.aJ0YNW_showMore{color:var(--dsw-alias-label-secondary,#57606a);border:1px dashed var(--dsw-alias-border-l2,#0000001f);cursor:pointer;background:0 0;border-radius:6px;align-self:flex-start;padding:2px 10px;font-family:inherit;font-size:12px}.aJ0YNW_showMore:hover{color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.aJ0YNW_groupName{color:var(--dsw-alias-label-primary,#1f2328);text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:600;overflow:hidden}.aJ0YNW_groupCount{color:var(--dsw-alias-label-secondary,#57606a);background:var(--dsw-alias-bg-layer-2,#0000000a);border-radius:999px;flex:none;padding:0 8px;font-size:11px;line-height:18px}.aJ0YNW_grid{grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:8px;display:grid}.aJ0YNW_card{text-align:left;border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-layer-1,#00000005);cursor:pointer;border-radius:8px;flex-direction:column;gap:6px;min-width:0;padding:10px 12px;font-family:inherit;display:flex}.aJ0YNW_card:hover{border-color:var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.aJ0YNW_card:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4d6bfe);outline-offset:1px}.aJ0YNW_cardHead{justify-content:space-between;align-items:center;gap:8px;min-width:0;display:flex}.aJ0YNW_agent{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary,#1f2328);align-items:center;gap:5px;font-size:12px;font-weight:600;display:inline-flex;overflow:hidden}.aJ0YNW_glyph{color:var(--dsw-alias-brand-primary,#4d6bfe);flex:none}.aJ0YNW_attention{color:var(--dsw-alias-state-warn-primary,#9a6700);font-size:11px}.aJ0YNW_attention[data-kind=gap]{color:var(--dsw-alias-state-error-primary,#cf222e)}.aJ0YNW_cardTitle{color:var(--dsw-alias-label-primary,#1f2328);text-overflow:ellipsis;white-space:nowrap;font-size:13px;overflow:hidden}.aJ0YNW_cardId{color:var(--dsw-alias-label-tertiary,#6e7781);text-overflow:ellipsis;white-space:nowrap;cursor:copy;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow:hidden}.aJ0YNW_cardId:hover{color:var(--dsw-alias-label-secondary,#57606a)}.aJ0YNW_copied{color:var(--dsw-alias-state-success-primary,#1a7f37);margin-left:6px;font-family:inherit}.aJ0YNW_cardEvent{color:var(--dsw-alias-label-secondary,#57606a);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.aJ0YNW_cardTime{color:var(--dsw-alias-label-caption,var(--dsw-alias-label-tertiary,#6e7781));font-size:11px}";
|
|
2526
2650
|
const tagId$8 = "@shendeguize/dsh-agent-sidecar/board.module.css";
|
|
2527
2651
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$8) + "]") === null) {
|
|
2528
2652
|
const tag = document.createElement("style");
|
|
@@ -2536,6 +2660,7 @@ window.__ModuleLoader__.load({
|
|
|
2536
2660
|
"attention": "aJ0YNW_attention",
|
|
2537
2661
|
"badge": "aJ0YNW_badge",
|
|
2538
2662
|
"banner": "aJ0YNW_banner",
|
|
2663
|
+
"bannerDismiss": "aJ0YNW_bannerDismiss",
|
|
2539
2664
|
"card": "aJ0YNW_card",
|
|
2540
2665
|
"cardEvent": "aJ0YNW_cardEvent",
|
|
2541
2666
|
"cardHead": "aJ0YNW_cardHead",
|
|
@@ -2543,7 +2668,11 @@ window.__ModuleLoader__.load({
|
|
|
2543
2668
|
"cardTime": "aJ0YNW_cardTime",
|
|
2544
2669
|
"cardTitle": "aJ0YNW_cardTitle",
|
|
2545
2670
|
"checkbox": "aJ0YNW_checkbox",
|
|
2671
|
+
"chevron": "aJ0YNW_chevron",
|
|
2546
2672
|
"control": "aJ0YNW_control",
|
|
2673
|
+
"copied": "aJ0YNW_copied",
|
|
2674
|
+
"countBadge": "aJ0YNW_countBadge",
|
|
2675
|
+
"countTotal": "aJ0YNW_countTotal",
|
|
2547
2676
|
"dot": "aJ0YNW_dot",
|
|
2548
2677
|
"empty": "aJ0YNW_empty",
|
|
2549
2678
|
"emptyHint": "aJ0YNW_emptyHint",
|
|
@@ -2551,18 +2680,39 @@ window.__ModuleLoader__.load({
|
|
|
2551
2680
|
"glyph": "aJ0YNW_glyph",
|
|
2552
2681
|
"grid": "aJ0YNW_grid",
|
|
2553
2682
|
"group": "aJ0YNW_group",
|
|
2683
|
+
"groupAttention": "aJ0YNW_groupAttention",
|
|
2554
2684
|
"groupCount": "aJ0YNW_groupCount",
|
|
2555
2685
|
"groupHead": "aJ0YNW_groupHead",
|
|
2556
2686
|
"groupName": "aJ0YNW_groupName",
|
|
2557
2687
|
"refresh": "aJ0YNW_refresh",
|
|
2558
2688
|
"root": "aJ0YNW_root",
|
|
2559
2689
|
"select": "aJ0YNW_select",
|
|
2690
|
+
"showMore": "aJ0YNW_showMore",
|
|
2560
2691
|
"spacer": "aJ0YNW_spacer",
|
|
2561
2692
|
"title": "aJ0YNW_title",
|
|
2562
2693
|
"topbar": "aJ0YNW_topbar"
|
|
2563
2694
|
};
|
|
2564
2695
|
//#endregion
|
|
2565
2696
|
//#region src/client/board/Board.tsx
|
|
2697
|
+
/**
|
|
2698
|
+
* Cross-agent session board (design §5.1 view 1).
|
|
2699
|
+
*
|
|
2700
|
+
* Presentation-only: no data fetching, no api/sse imports. Everything
|
|
2701
|
+
* arrives through props already shaped as the view models of `logic.ts`;
|
|
2702
|
+
* the integration layer (T2.4) owns state, transport, and the mapping
|
|
2703
|
+
* from the host wire types (epoch-seconds → epoch-ms conversion included).
|
|
2704
|
+
*
|
|
2705
|
+
* Interaction surface handed back to the owner:
|
|
2706
|
+
* - `onFiltersChange` — time-window select / show-dead checkbox / the
|
|
2707
|
+
* top-bar status-filter badges (controlled, UX-01);
|
|
2708
|
+
* - `onRefresh` — manual snapshot pull button; a Promise<boolean>
|
|
2709
|
+
* return drives the in-flight/failure feedback (UX-07);
|
|
2710
|
+
* - `onSelectSession` — card click, pass-through for the M3 detail view.
|
|
2711
|
+
*
|
|
2712
|
+
* Local UI state (deliberately NOT lifted into the controller stores):
|
|
2713
|
+
* group collapse and per-group truncation (UX-02) are ephemeral view
|
|
2714
|
+
* concerns — useState here, reset on tab remount.
|
|
2715
|
+
*/
|
|
2566
2716
|
/** Time-window choices offered by the top bar (hours). */
|
|
2567
2717
|
const TIME_WINDOW_OPTIONS = [
|
|
2568
2718
|
6,
|
|
@@ -2573,6 +2723,18 @@ window.__ModuleLoader__.load({
|
|
|
2573
2723
|
];
|
|
2574
2724
|
function SessionCard(props) {
|
|
2575
2725
|
const { card, onSelect } = props;
|
|
2726
|
+
const [copied, setCopied] = (0, react.useState)(false);
|
|
2727
|
+
const onCopyId = (ev) => {
|
|
2728
|
+
ev.stopPropagation();
|
|
2729
|
+
const clipboard = typeof navigator === "undefined" ? void 0 : navigator.clipboard;
|
|
2730
|
+
if (clipboard === void 0) return;
|
|
2731
|
+
clipboard.writeText(card.sessionId).then(() => {
|
|
2732
|
+
setCopied(true);
|
|
2733
|
+
setTimeout(() => {
|
|
2734
|
+
setCopied(false);
|
|
2735
|
+
}, 2e3);
|
|
2736
|
+
}, () => {});
|
|
2737
|
+
};
|
|
2576
2738
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2577
2739
|
type: "button",
|
|
2578
2740
|
className: board_module_css_default["card"],
|
|
@@ -2611,10 +2773,16 @@ window.__ModuleLoader__.load({
|
|
|
2611
2773
|
title: card.title,
|
|
2612
2774
|
children: card.title.trim() === "" ? BOARD_STRINGS.card.untitled : card.title
|
|
2613
2775
|
}),
|
|
2614
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
2776
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2615
2777
|
className: board_module_css_default["cardId"],
|
|
2616
|
-
title: card.sessionId
|
|
2617
|
-
|
|
2778
|
+
title: `${card.sessionId}\n${BOARD_STRINGS.card.copyId}`,
|
|
2779
|
+
onClick: onCopyId,
|
|
2780
|
+
"data-testid": "agent-sidecar-card-id",
|
|
2781
|
+
children: [card.shortId, copied && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2782
|
+
className: board_module_css_default["copied"],
|
|
2783
|
+
role: "status",
|
|
2784
|
+
children: BOARD_STRINGS.card.copied
|
|
2785
|
+
})]
|
|
2618
2786
|
}),
|
|
2619
2787
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2620
2788
|
className: board_module_css_default["cardEvent"],
|
|
@@ -2629,25 +2797,65 @@ window.__ModuleLoader__.load({
|
|
|
2629
2797
|
}
|
|
2630
2798
|
function ProjectGroup(props) {
|
|
2631
2799
|
const { group, onSelect } = props;
|
|
2800
|
+
const [collapsed, setCollapsed] = (0, react.useState)(false);
|
|
2801
|
+
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
2802
|
+
const { shown, hiddenCount } = sliceCardsForDisplay(group.cards, 20, expanded);
|
|
2803
|
+
const waitingInGroup = group.cards.filter((card) => card.badge.status === "waiting").length;
|
|
2632
2804
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2633
2805
|
className: board_module_css_default["group"],
|
|
2634
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
2806
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2807
|
+
type: "button",
|
|
2635
2808
|
className: board_module_css_default["groupHead"],
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
}
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2809
|
+
"aria-expanded": !collapsed,
|
|
2810
|
+
title: collapsed ? BOARD_STRINGS.group.expandTitle : BOARD_STRINGS.group.collapseTitle,
|
|
2811
|
+
onClick: () => {
|
|
2812
|
+
setCollapsed(!collapsed);
|
|
2813
|
+
},
|
|
2814
|
+
children: [
|
|
2815
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2816
|
+
className: board_module_css_default["chevron"],
|
|
2817
|
+
"aria-hidden": true,
|
|
2818
|
+
children: collapsed ? "▸" : "▾"
|
|
2819
|
+
}),
|
|
2820
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2821
|
+
className: board_module_css_default["groupName"],
|
|
2822
|
+
title: group.fullPath === "" ? void 0 : group.fullPath,
|
|
2823
|
+
children: group.label
|
|
2824
|
+
}),
|
|
2825
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2826
|
+
className: board_module_css_default["groupCount"],
|
|
2827
|
+
children: formatTemplate$2(BOARD_STRINGS.groupCount, { n: group.cards.length })
|
|
2828
|
+
}),
|
|
2829
|
+
collapsed && waitingInGroup > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2830
|
+
className: board_module_css_default["groupAttention"],
|
|
2831
|
+
children: formatTemplate$2(BOARD_STRINGS.topbar.countWaiting, { n: waitingInGroup })
|
|
2832
|
+
})
|
|
2833
|
+
]
|
|
2834
|
+
}), !collapsed && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2835
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2836
|
+
className: board_module_css_default["grid"],
|
|
2837
|
+
children: shown.map((card) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionCard, {
|
|
2838
|
+
card,
|
|
2839
|
+
onSelect
|
|
2840
|
+
}, `${card.agent}:${card.sessionId}`))
|
|
2841
|
+
}),
|
|
2842
|
+
hiddenCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2843
|
+
type: "button",
|
|
2844
|
+
className: board_module_css_default["showMore"],
|
|
2845
|
+
onClick: () => {
|
|
2846
|
+
setExpanded(true);
|
|
2847
|
+
},
|
|
2848
|
+
children: formatTemplate$2(BOARD_STRINGS.group.showAll, { n: group.cards.length })
|
|
2849
|
+
}),
|
|
2850
|
+
expanded && group.cards.length > 20 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2851
|
+
type: "button",
|
|
2852
|
+
className: board_module_css_default["showMore"],
|
|
2853
|
+
onClick: () => {
|
|
2854
|
+
setExpanded(false);
|
|
2855
|
+
},
|
|
2856
|
+
children: formatTemplate$2(BOARD_STRINGS.group.showLess, { n: 20 })
|
|
2857
|
+
})
|
|
2858
|
+
] })]
|
|
2651
2859
|
});
|
|
2652
2860
|
}
|
|
2653
2861
|
/** The board view. Pure render of `buildBoardViewModel` over the props. */
|
|
@@ -2662,6 +2870,30 @@ window.__ModuleLoader__.load({
|
|
|
2662
2870
|
nowMs
|
|
2663
2871
|
});
|
|
2664
2872
|
const windowOptions = TIME_WINDOW_OPTIONS.includes(props.filters.timeWindowHours) ? TIME_WINDOW_OPTIONS : [...TIME_WINDOW_OPTIONS, props.filters.timeWindowHours].sort((a, b) => a - b);
|
|
2873
|
+
const [refreshing, setRefreshing] = (0, react.useState)(false);
|
|
2874
|
+
const [refreshFailed, setRefreshFailed] = (0, react.useState)(false);
|
|
2875
|
+
const onRefreshClick = () => {
|
|
2876
|
+
if (refreshing) return;
|
|
2877
|
+
setRefreshFailed(false);
|
|
2878
|
+
const result = props.onRefresh();
|
|
2879
|
+
if (result instanceof Promise) {
|
|
2880
|
+
setRefreshing(true);
|
|
2881
|
+
result.then((ok) => {
|
|
2882
|
+
setRefreshFailed(!ok);
|
|
2883
|
+
}).catch(() => {
|
|
2884
|
+
setRefreshFailed(true);
|
|
2885
|
+
}).finally(() => {
|
|
2886
|
+
setRefreshing(false);
|
|
2887
|
+
});
|
|
2888
|
+
}
|
|
2889
|
+
};
|
|
2890
|
+
const toggleStatusFilter = (status) => {
|
|
2891
|
+
const next = { ...props.filters };
|
|
2892
|
+
if (next.statusFilter === status) delete next.statusFilter;
|
|
2893
|
+
else next.statusFilter = status;
|
|
2894
|
+
props.onFiltersChange(next);
|
|
2895
|
+
};
|
|
2896
|
+
const statusBadgeTitle = (status) => props.filters.statusFilter === status ? BOARD_STRINGS.topbar.clearStatusFilterTitle : formatTemplate$2(BOARD_STRINGS.topbar.filterByStatusTitle, { label: BOARD_STRINGS.status[status] });
|
|
2665
2897
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2666
2898
|
className: board_module_css_default["root"],
|
|
2667
2899
|
"data-testid": "agent-sidecar-board",
|
|
@@ -2690,6 +2922,39 @@ window.__ModuleLoader__.load({
|
|
|
2690
2922
|
"data-tone": vm.streamTone
|
|
2691
2923
|
}), vm.streamLabel]
|
|
2692
2924
|
}),
|
|
2925
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2926
|
+
type: "button",
|
|
2927
|
+
className: board_module_css_default["countBadge"],
|
|
2928
|
+
"aria-pressed": props.filters.statusFilter === "working",
|
|
2929
|
+
title: statusBadgeTitle("working"),
|
|
2930
|
+
onClick: () => {
|
|
2931
|
+
toggleStatusFilter("working");
|
|
2932
|
+
},
|
|
2933
|
+
"data-testid": "agent-sidecar-count-working",
|
|
2934
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2935
|
+
className: board_module_css_default["dot"],
|
|
2936
|
+
"data-tone": vm.workingCount > 0 ? "success" : "neutral"
|
|
2937
|
+
}), formatTemplate$2(BOARD_STRINGS.topbar.countWorking, { n: vm.workingCount })]
|
|
2938
|
+
}),
|
|
2939
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2940
|
+
type: "button",
|
|
2941
|
+
className: board_module_css_default["countBadge"],
|
|
2942
|
+
"aria-pressed": props.filters.statusFilter === "waiting",
|
|
2943
|
+
title: statusBadgeTitle("waiting"),
|
|
2944
|
+
onClick: () => {
|
|
2945
|
+
toggleStatusFilter("waiting");
|
|
2946
|
+
},
|
|
2947
|
+
"data-testid": "agent-sidecar-count-waiting",
|
|
2948
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2949
|
+
className: board_module_css_default["dot"],
|
|
2950
|
+
"data-tone": vm.waitingCount > 0 ? "warn" : "neutral"
|
|
2951
|
+
}), formatTemplate$2(BOARD_STRINGS.topbar.countWaiting, { n: vm.waitingCount })]
|
|
2952
|
+
}),
|
|
2953
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2954
|
+
className: board_module_css_default["countTotal"],
|
|
2955
|
+
"data-testid": "agent-sidecar-count-total",
|
|
2956
|
+
children: formatTemplate$2(BOARD_STRINGS.topbar.countTotal, { n: vm.totalCount })
|
|
2957
|
+
}),
|
|
2693
2958
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: board_module_css_default["spacer"] }),
|
|
2694
2959
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2695
2960
|
className: board_module_css_default["control"],
|
|
@@ -2722,11 +2987,25 @@ window.__ModuleLoader__.load({
|
|
|
2722
2987
|
type: "button",
|
|
2723
2988
|
className: board_module_css_default["refresh"],
|
|
2724
2989
|
title: BOARD_STRINGS.topbar.refreshTitle,
|
|
2725
|
-
|
|
2726
|
-
|
|
2990
|
+
disabled: refreshing,
|
|
2991
|
+
onClick: onRefreshClick,
|
|
2992
|
+
children: refreshing ? BOARD_STRINGS.topbar.refreshing : BOARD_STRINGS.topbar.refresh
|
|
2727
2993
|
})
|
|
2728
2994
|
]
|
|
2729
2995
|
}),
|
|
2996
|
+
refreshFailed && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2997
|
+
className: board_module_css_default["banner"],
|
|
2998
|
+
"data-tone": "warn",
|
|
2999
|
+
role: "status",
|
|
3000
|
+
children: [BOARD_STRINGS.topbar.refreshFailed, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3001
|
+
type: "button",
|
|
3002
|
+
className: board_module_css_default["bannerDismiss"],
|
|
3003
|
+
onClick: () => {
|
|
3004
|
+
setRefreshFailed(false);
|
|
3005
|
+
},
|
|
3006
|
+
children: BOARD_STRINGS.topbar.dismiss
|
|
3007
|
+
})]
|
|
3008
|
+
}),
|
|
2730
3009
|
vm.banner !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2731
3010
|
className: board_module_css_default["banner"],
|
|
2732
3011
|
"data-tone": vm.banner.tone,
|
|
@@ -2752,7 +3031,7 @@ window.__ModuleLoader__.load({
|
|
|
2752
3031
|
}
|
|
2753
3032
|
//#endregion
|
|
2754
3033
|
//#region \0dsh-css:/Users/jingyu/Workspace/Projects/agent_sidecar/plugin/src/client/board/project-view.module.css.mjs
|
|
2755
|
-
const css$7 = ".i_F0aW_root{box-sizing:border-box;height:100%;min-height:320px;color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-base,transparent);flex-direction:column;gap:12px;padding:16px 20px;display:flex;overflow-y:auto}.i_F0aW_topbar{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.i_F0aW_title{color:var(--dsw-alias-label-primary,#1f2328);font-size:15px;font-weight:650}.i_F0aW_summary{color:var(--dsw-alias-label-secondary,#57606a);font-size:12px}.i_F0aW_loadingChip{color:var(--dsw-alias-label-secondary,#57606a);background:var(--dsw-alias-bg-layer-2,#0000000a);border-radius:999px;padding:0 8px;font-size:11px;line-height:18px}.i_F0aW_banner{border:1px solid var(--dsw-alias-border-l1,#00000014);border-radius:8px;padding:6px 10px;font-size:12px;line-height:18px}.i_F0aW_banner[data-tone=danger]{color:var(--dsw-alias-state-error-primary,#cf222e);background:var(--dsw-alias-state-error-secondary,#cf222e14);border-color:var(--dsw-alias-state-error-secondary,#cf222e3d)}.i_F0aW_empty{text-align:center;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:6px;min-height:160px;padding:24px;display:flex}.i_F0aW_emptyTitle{color:var(--dsw-alias-label-primary,#1f2328);font-size:14px;font-weight:600}.i_F0aW_emptyHint{max-width:420px;color:var(--dsw-alias-label-secondary,#57606a);font-size:12px;line-height:20px}.i_F0aW_group{border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-layer-1,#00000005);border-radius:10px;flex-direction:column;gap:8px;padding:10px 12px;display:flex}.i_F0aW_groupHead{flex-wrap:wrap;align-items:baseline;gap:8px;min-width:0;display:flex}.i_F0aW_groupName{color:var(--dsw-alias-label-primary,#1f2328);text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:600;overflow:hidden}.i_F0aW_agentBadges{flex-wrap:wrap;align-items:center;gap:4px;display:inline-flex}.i_F0aW_agentBadge{border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-layer-2,#0000000a);color:var(--dsw-alias-label-secondary,#57606a);white-space:nowrap;border-radius:999px;align-items:center;gap:4px;padding:0 7px;font-size:11px;line-height:18px;display:inline-flex}.i_F0aW_crossBadge{color:var(--dsw-alias-static-white,#fff);background:var(--dsw-alias-brand-primary,#4d6bfe);white-space:nowrap;border-radius:999px;padding:0 7px;font-size:11px;line-height:18px}.i_F0aW_spacer{flex:1}.i_F0aW_groupMeta{color:var(--dsw-alias-label-tertiary,#6e7781);white-space:nowrap;flex:none;font-size:11px}.i_F0aW_lanes{flex-direction:column;gap:6px;display:flex}.i_F0aW_lane{flex-direction:column;gap:4px;display:flex}.i_F0aW_laneHead{color:var(--dsw-alias-label-primary,#1f2328);align-items:center;gap:5px;font-size:12px;font-weight:600;display:inline-flex}.i_F0aW_glyph{color:var(--dsw-alias-brand-primary,#4d6bfe);flex:none}.i_F0aW_laneSessions{flex-direction:column;gap:4px;display:flex}.i_F0aW_session{text-align:left;border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-base,transparent);min-width:0;color:var(--dsw-alias-label-primary,#1f2328);cursor:pointer;border-radius:7px;align-items:center;gap:8px;padding:5px 8px;font-family:inherit;font-size:12px;display:flex}.i_F0aW_session:hover{border-color:var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.i_F0aW_session:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4d6bfe);outline-offset:1px}.i_F0aW_badge{border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-layer-1,#00000008);color:var(--dsw-alias-label-secondary,#57606a);white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:5px;padding:0 7px;font-size:11px;line-height:18px;display:inline-flex}.i_F0aW_dot{background:var(--dsw-alias-label-tertiary,#6e7781);border-radius:50%;flex:none;width:7px;height:7px}.i_F0aW_dot[data-tone=success]{background:var(--dsw-alias-state-success-primary,#1a7f37)}.i_F0aW_dot[data-tone=warn]{background:var(--dsw-alias-state-warn-primary,#9a6700)}.i_F0aW_dot[data-tone=danger]{background:var(--dsw-alias-state-error-primary,#cf222e)}.i_F0aW_dot[data-tone=neutral]{background:var(--dsw-alias-label-tertiary,#6e7781)}.i_F0aW_dot[data-tone=muted]{background:var(--dsw-alias-label-dimmed,#8c959f)}.i_F0aW_attention{color:var(--dsw-alias-state-warn-primary,#9a6700);font-size:11px}.i_F0aW_attention[data-kind=gap]{color:var(--dsw-alias-state-error-primary,#cf222e)}.i_F0aW_sessionTitle{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.i_F0aW_liveChip{color:var(--dsw-alias-state-success-primary,#1a7f37);border:1px solid var(--dsw-alias-state-success-primary,#1a7f3766);border-radius:999px;flex:none;padding:0 6px;font-size:10px;line-height:16px}.i_F0aW_sessionId{color:var(--dsw-alias-label-tertiary,#6e7781);flex:none;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}.i_F0aW_sessionTime{color:var(--dsw-alias-label-caption,var(--dsw-alias-label-tertiary,#6e7781));flex:none;margin-left:auto;font-size:11px}";
|
|
3034
|
+
const css$7 = ".i_F0aW_root{box-sizing:border-box;height:100%;min-height:320px;color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-base,transparent);flex-direction:column;gap:12px;padding:16px 20px;display:flex;overflow-y:auto}.i_F0aW_topbar{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.i_F0aW_title{color:var(--dsw-alias-label-primary,#1f2328);font-size:15px;font-weight:650}.i_F0aW_summary{color:var(--dsw-alias-label-secondary,#57606a);font-size:12px}.i_F0aW_loadingChip{color:var(--dsw-alias-label-secondary,#57606a);background:var(--dsw-alias-bg-layer-2,#0000000a);border-radius:999px;padding:0 8px;font-size:11px;line-height:18px}.i_F0aW_banner{border:1px solid var(--dsw-alias-border-l1,#00000014);border-radius:8px;padding:6px 10px;font-size:12px;line-height:18px}.i_F0aW_banner[data-tone=danger]{color:var(--dsw-alias-state-error-primary,#cf222e);background:var(--dsw-alias-state-error-secondary,#cf222e14);border-color:var(--dsw-alias-state-error-secondary,#cf222e3d)}.i_F0aW_empty{text-align:center;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:6px;min-height:160px;padding:24px;display:flex}.i_F0aW_emptyTitle{color:var(--dsw-alias-label-primary,#1f2328);font-size:14px;font-weight:600}.i_F0aW_emptyHint{max-width:420px;color:var(--dsw-alias-label-secondary,#57606a);font-size:12px;line-height:20px}.i_F0aW_group{border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-layer-1,#00000005);border-radius:10px;flex-direction:column;gap:8px;padding:10px 12px;display:flex}.i_F0aW_groupHead{flex-wrap:wrap;align-items:baseline;gap:8px;min-width:0;display:flex}.i_F0aW_groupName{color:var(--dsw-alias-label-primary,#1f2328);text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:600;overflow:hidden}.i_F0aW_agentBadges{flex-wrap:wrap;align-items:center;gap:4px;display:inline-flex}.i_F0aW_agentBadge{border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-layer-2,#0000000a);color:var(--dsw-alias-label-secondary,#57606a);white-space:nowrap;border-radius:999px;align-items:center;gap:4px;padding:0 7px;font-size:11px;line-height:18px;display:inline-flex}.i_F0aW_crossBadge{color:var(--dsw-alias-static-white,#fff);background:var(--dsw-alias-brand-primary,#4d6bfe);white-space:nowrap;border-radius:999px;padding:0 7px;font-size:11px;line-height:18px}.i_F0aW_spacer{flex:1}.i_F0aW_groupMeta{color:var(--dsw-alias-label-tertiary,#6e7781);white-space:nowrap;flex:none;font-size:11px}.i_F0aW_lanes{flex-direction:column;gap:6px;display:flex}.i_F0aW_lane{flex-direction:column;gap:4px;display:flex}.i_F0aW_laneHead{color:var(--dsw-alias-label-primary,#1f2328);align-items:center;gap:5px;font-size:12px;font-weight:600;display:inline-flex}.i_F0aW_glyph{color:var(--dsw-alias-brand-primary,#4d6bfe);flex:none}.i_F0aW_laneSessions{flex-direction:column;gap:4px;display:flex}.i_F0aW_showMore{color:var(--dsw-alias-label-secondary,#57606a);border:1px dashed var(--dsw-alias-border-l2,#0000001f);cursor:pointer;background:0 0;border-radius:6px;align-self:flex-start;padding:2px 10px;font-family:inherit;font-size:12px}.i_F0aW_showMore:hover{color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.i_F0aW_session{text-align:left;border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-base,transparent);min-width:0;color:var(--dsw-alias-label-primary,#1f2328);cursor:pointer;border-radius:7px;align-items:center;gap:8px;padding:5px 8px;font-family:inherit;font-size:12px;display:flex}.i_F0aW_session:hover{border-color:var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.i_F0aW_session:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4d6bfe);outline-offset:1px}.i_F0aW_badge{border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-layer-1,#00000008);color:var(--dsw-alias-label-secondary,#57606a);white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:5px;padding:0 7px;font-size:11px;line-height:18px;display:inline-flex}.i_F0aW_dot{background:var(--dsw-alias-label-tertiary,#6e7781);border-radius:50%;flex:none;width:7px;height:7px}.i_F0aW_dot[data-tone=success]{background:var(--dsw-alias-state-success-primary,#1a7f37)}.i_F0aW_dot[data-tone=warn]{background:var(--dsw-alias-state-warn-primary,#9a6700)}.i_F0aW_dot[data-tone=danger]{background:var(--dsw-alias-state-error-primary,#cf222e)}.i_F0aW_dot[data-tone=neutral]{background:var(--dsw-alias-label-tertiary,#6e7781)}.i_F0aW_dot[data-tone=muted]{background:var(--dsw-alias-label-dimmed,#8c959f)}.i_F0aW_attention{color:var(--dsw-alias-state-warn-primary,#9a6700);font-size:11px}.i_F0aW_attention[data-kind=gap]{color:var(--dsw-alias-state-error-primary,#cf222e)}.i_F0aW_sessionTitle{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.i_F0aW_liveChip{color:var(--dsw-alias-state-success-primary,#1a7f37);border:1px solid var(--dsw-alias-state-success-primary,#1a7f3766);border-radius:999px;flex:none;padding:0 6px;font-size:10px;line-height:16px}.i_F0aW_sessionId{color:var(--dsw-alias-label-tertiary,#6e7781);flex:none;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}.i_F0aW_sessionTime{color:var(--dsw-alias-label-caption,var(--dsw-alias-label-tertiary,#6e7781));flex:none;margin-left:auto;font-size:11px}";
|
|
2756
3035
|
const tagId$7 = "@shendeguize/dsh-agent-sidecar/project-view.module.css";
|
|
2757
3036
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$7) + "]") === null) {
|
|
2758
3037
|
const tag = document.createElement("style");
|
|
@@ -2788,6 +3067,7 @@ window.__ModuleLoader__.load({
|
|
|
2788
3067
|
"sessionId": "i_F0aW_sessionId",
|
|
2789
3068
|
"sessionTime": "i_F0aW_sessionTime",
|
|
2790
3069
|
"sessionTitle": "i_F0aW_sessionTitle",
|
|
3070
|
+
"showMore": "i_F0aW_showMore",
|
|
2791
3071
|
"spacer": "i_F0aW_spacer",
|
|
2792
3072
|
"summary": "i_F0aW_summary",
|
|
2793
3073
|
"title": "i_F0aW_title",
|
|
@@ -2795,6 +3075,21 @@ window.__ModuleLoader__.load({
|
|
|
2795
3075
|
};
|
|
2796
3076
|
//#endregion
|
|
2797
3077
|
//#region src/client/board/project-view.tsx
|
|
3078
|
+
/**
|
|
3079
|
+
* Cross-agent project correlation view (design §4.e.2 / §5.1 M3, T5.5).
|
|
3080
|
+
*
|
|
3081
|
+
* Presentation-only and fully controlled: no data fetching, no api/sse
|
|
3082
|
+
* imports. The owner fetches `GET <prefix>/projects`, hands the wire
|
|
3083
|
+
* groups in as props (the wire shape IS the input view model — camelCase,
|
|
3084
|
+
* epoch-ms `lastActivityAt`), and receives session clicks back through
|
|
3085
|
+
* `onSelectSession` (pass-through to the detail view, same as Board).
|
|
3086
|
+
*
|
|
3087
|
+
* Render precedence when there is nothing to show: error > loading >
|
|
3088
|
+
* empty state. When groups ARE available, an error renders as a banner
|
|
3089
|
+
* above the (possibly stale) content instead of replacing it, and
|
|
3090
|
+
* `loading` shows as a quiet header chip — honest degradation without
|
|
3091
|
+
* blanking data the user already has.
|
|
3092
|
+
*/
|
|
2798
3093
|
function SessionRow$1(props) {
|
|
2799
3094
|
const { session, onSelect } = props;
|
|
2800
3095
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
@@ -2842,22 +3137,43 @@ window.__ModuleLoader__.load({
|
|
|
2842
3137
|
}
|
|
2843
3138
|
function AgentLane(props) {
|
|
2844
3139
|
const { lane, onSelect } = props;
|
|
3140
|
+
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
3141
|
+
const { shown, hiddenCount } = sliceCardsForDisplay(lane.sessions, 10, expanded);
|
|
2845
3142
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2846
3143
|
className: project_view_module_css_default["lane"],
|
|
2847
|
-
children: [
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
3144
|
+
children: [
|
|
3145
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3146
|
+
className: project_view_module_css_default["laneHead"],
|
|
3147
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3148
|
+
className: project_view_module_css_default["glyph"],
|
|
3149
|
+
"aria-hidden": true,
|
|
3150
|
+
children: lane.glyph
|
|
3151
|
+
}), lane.agent]
|
|
3152
|
+
}),
|
|
3153
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3154
|
+
className: project_view_module_css_default["laneSessions"],
|
|
3155
|
+
children: shown.map((session) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionRow$1, {
|
|
3156
|
+
session,
|
|
3157
|
+
onSelect
|
|
3158
|
+
}, `${session.agent}:${session.sessionId}`))
|
|
3159
|
+
}),
|
|
3160
|
+
hiddenCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3161
|
+
type: "button",
|
|
3162
|
+
className: project_view_module_css_default["showMore"],
|
|
3163
|
+
onClick: () => {
|
|
3164
|
+
setExpanded(true);
|
|
3165
|
+
},
|
|
3166
|
+
children: formatTemplate$2(PROJECT_VIEW_STRINGS.showAllSessions, { n: lane.sessions.length })
|
|
3167
|
+
}),
|
|
3168
|
+
expanded && lane.sessions.length > 10 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3169
|
+
type: "button",
|
|
3170
|
+
className: project_view_module_css_default["showMore"],
|
|
3171
|
+
onClick: () => {
|
|
3172
|
+
setExpanded(false);
|
|
3173
|
+
},
|
|
3174
|
+
children: formatTemplate$2(PROJECT_VIEW_STRINGS.showLessSessions, { n: 10 })
|
|
3175
|
+
})
|
|
3176
|
+
]
|
|
2861
3177
|
});
|
|
2862
3178
|
}
|
|
2863
3179
|
function ProjectSection(props) {
|
|
@@ -2998,7 +3314,6 @@ window.__ModuleLoader__.load({
|
|
|
2998
3314
|
border: "none",
|
|
2999
3315
|
borderRadius: 6,
|
|
3000
3316
|
background: "transparent",
|
|
3001
|
-
cursor: "pointer",
|
|
3002
3317
|
font: "inherit",
|
|
3003
3318
|
color: "var(--dsw-alias-label-secondary, #57606a)"
|
|
3004
3319
|
};
|
|
@@ -3008,31 +3323,51 @@ window.__ModuleLoader__.load({
|
|
|
3008
3323
|
lineHeight: "14px",
|
|
3009
3324
|
whiteSpace: "nowrap"
|
|
3010
3325
|
};
|
|
3011
|
-
/**
|
|
3326
|
+
/**
|
|
3327
|
+
* Connection dot + working-session counter (e.g. `▸2`) for the footer.
|
|
3328
|
+
*
|
|
3329
|
+
* Without a wired `onOpen` the widget renders as an inert status `<span>`
|
|
3330
|
+
* (UX-06): button semantics + pointer cursor on a control that does
|
|
3331
|
+
* nothing would be a lie. The button form returns as soon as the
|
|
3332
|
+
* integration layer supplies the callback.
|
|
3333
|
+
*/
|
|
3012
3334
|
function SidecarWidget(props) {
|
|
3013
3335
|
const title = widgetTitle(props.connection, props.workingCount);
|
|
3014
|
-
|
|
3015
|
-
|
|
3336
|
+
const body = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3337
|
+
"aria-hidden": true,
|
|
3338
|
+
style: {
|
|
3339
|
+
width: 8,
|
|
3340
|
+
height: 8,
|
|
3341
|
+
borderRadius: "50%",
|
|
3342
|
+
flex: "none",
|
|
3343
|
+
background: DOT_COLORS$1[props.connection]
|
|
3344
|
+
}
|
|
3345
|
+
}), props.workingCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3346
|
+
style: countStyle,
|
|
3347
|
+
"data-testid": "agent-sidecar-widget-count",
|
|
3348
|
+
children: `▸${props.workingCount}`
|
|
3349
|
+
})] });
|
|
3350
|
+
if (props.onOpen === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3016
3351
|
style: rootStyle$1,
|
|
3017
3352
|
title,
|
|
3018
3353
|
"aria-label": title,
|
|
3354
|
+
role: "status",
|
|
3355
|
+
"data-testid": "agent-sidecar-widget",
|
|
3356
|
+
"data-connection": props.connection,
|
|
3357
|
+
children: body
|
|
3358
|
+
});
|
|
3359
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3360
|
+
type: "button",
|
|
3361
|
+
style: {
|
|
3362
|
+
...rootStyle$1,
|
|
3363
|
+
cursor: "pointer"
|
|
3364
|
+
},
|
|
3365
|
+
title,
|
|
3366
|
+
"aria-label": title,
|
|
3019
3367
|
onClick: props.onOpen,
|
|
3020
3368
|
"data-testid": "agent-sidecar-widget",
|
|
3021
3369
|
"data-connection": props.connection,
|
|
3022
|
-
children:
|
|
3023
|
-
"aria-hidden": true,
|
|
3024
|
-
style: {
|
|
3025
|
-
width: 8,
|
|
3026
|
-
height: 8,
|
|
3027
|
-
borderRadius: "50%",
|
|
3028
|
-
flex: "none",
|
|
3029
|
-
background: DOT_COLORS$1[props.connection]
|
|
3030
|
-
}
|
|
3031
|
-
}), props.workingCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3032
|
-
style: countStyle,
|
|
3033
|
-
"data-testid": "agent-sidecar-widget-count",
|
|
3034
|
-
children: `▸${props.workingCount}`
|
|
3035
|
-
})]
|
|
3370
|
+
children: body
|
|
3036
3371
|
});
|
|
3037
3372
|
}
|
|
3038
3373
|
//#endregion
|
|
@@ -3614,6 +3949,24 @@ window.__ModuleLoader__.load({
|
|
|
3614
3949
|
if (delta < DAY_MS) return formatTemplate$1(DETAIL_STRINGS.time.hoursAgo, { n: Math.floor(delta / HOUR_MS) });
|
|
3615
3950
|
return formatTemplate$1(DETAIL_STRINGS.time.daysAgo, { n: Math.floor(delta / DAY_MS) });
|
|
3616
3951
|
}
|
|
3952
|
+
function pad2(n) {
|
|
3953
|
+
return n < 10 ? `0${n}` : String(n);
|
|
3954
|
+
}
|
|
3955
|
+
/**
|
|
3956
|
+
* Absolute short timestamp for the time column (UX-13): the same local
|
|
3957
|
+
* calendar day renders「HH:mm」, anything older (or a different day)
|
|
3958
|
+
* renders「MM-DD HH:mm」— so a column of same-age rows stays tellable
|
|
3959
|
+
* apart, unlike the coarse relative buckets. The format rule is
|
|
3960
|
+
* copy-implemented to match the board column (no cross-surface import);
|
|
3961
|
+
* the full ISO timestamp stays in the hover title.
|
|
3962
|
+
*/
|
|
3963
|
+
function formatEventTime(thenMs, nowMs) {
|
|
3964
|
+
if (!Number.isFinite(thenMs) || !Number.isFinite(nowMs)) return "";
|
|
3965
|
+
const then = new Date(thenMs);
|
|
3966
|
+
const now = new Date(nowMs);
|
|
3967
|
+
const hhmm = `${pad2(then.getHours())}:${pad2(then.getMinutes())}`;
|
|
3968
|
+
return then.getFullYear() === now.getFullYear() && then.getMonth() === now.getMonth() && then.getDate() === now.getDate() ? hhmm : `${pad2(then.getMonth() + 1)}-${pad2(then.getDate())} ${hhmm}`;
|
|
3969
|
+
}
|
|
3617
3970
|
const KIND_GLYPHS = {
|
|
3618
3971
|
user: "▷",
|
|
3619
3972
|
assistant: "◁",
|
|
@@ -3625,24 +3978,38 @@ window.__ModuleLoader__.load({
|
|
|
3625
3978
|
error: "✕",
|
|
3626
3979
|
other: "•"
|
|
3627
3980
|
};
|
|
3981
|
+
/** One path segment → family token, or null when it names no family. */
|
|
3982
|
+
function segmentToken(segment) {
|
|
3983
|
+
if (segment === "user") return "user";
|
|
3984
|
+
if (segment === "assistant") return "assistant";
|
|
3985
|
+
if (segment === "thinking" || segment === "reasoning") return "thinking";
|
|
3986
|
+
if (segment === "tool_call" || segment === "tool-call" || segment === "toolcall") return "toolCall";
|
|
3987
|
+
if (segment === "tool_result" || segment === "tool-result" || segment === "toolresult") return "toolResult";
|
|
3988
|
+
if (segment.startsWith("turn_") || segment === "turn") return "turn";
|
|
3989
|
+
if (segment.startsWith("step_") || segment === "step") return "step";
|
|
3990
|
+
if (segment === "error") return "error";
|
|
3991
|
+
return null;
|
|
3992
|
+
}
|
|
3628
3993
|
/**
|
|
3629
|
-
* Map a raw event kind onto the glyph/label vocabulary. Covers
|
|
3630
|
-
*
|
|
3631
|
-
*
|
|
3632
|
-
* slash-path types
|
|
3633
|
-
*
|
|
3994
|
+
* Map a raw event kind onto the glyph/label vocabulary. Covers the sidecar
|
|
3995
|
+
* normalized kinds — user/assistant/thinking/tool_call/tool_result plus
|
|
3996
|
+
* the turn_ and step_ prefixes (sidecar/model.py) — and dsh native
|
|
3997
|
+
* slash-path types in BOTH orders: `message/user` style (family last) and
|
|
3998
|
+
* the observed `user/message` / `assistant/chunk` / `turn/start` style
|
|
3999
|
+
* (family first; live-data fact, UX-03 — without it the conversation
|
|
4000
|
+
* filter would misfile real dsh user messages as protocol noise). The
|
|
4001
|
+
* last segment stays authoritative; the first is only a fallback.
|
|
4002
|
+
* Everything else is honestly 'other'.
|
|
3634
4003
|
*/
|
|
3635
4004
|
function classifyKind(kind) {
|
|
3636
4005
|
const k = kind.trim().toLowerCase();
|
|
3637
|
-
const
|
|
3638
|
-
|
|
3639
|
-
if (last
|
|
3640
|
-
if (
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
if (last.startsWith("step_") || last === "step") return "step";
|
|
3645
|
-
if (last === "error") return "error";
|
|
4006
|
+
const segments = k.split("/");
|
|
4007
|
+
const last = segmentToken(segments[segments.length - 1] ?? k);
|
|
4008
|
+
if (last !== null) return last;
|
|
4009
|
+
if (segments.length > 1) {
|
|
4010
|
+
const first = segmentToken(segments[0] ?? "");
|
|
4011
|
+
if (first !== null) return first;
|
|
4012
|
+
}
|
|
3646
4013
|
return "other";
|
|
3647
4014
|
}
|
|
3648
4015
|
/** Glyph for a kind token. */
|
|
@@ -3906,13 +4273,14 @@ window.__ModuleLoader__.load({
|
|
|
3906
4273
|
return "";
|
|
3907
4274
|
}
|
|
3908
4275
|
}
|
|
3909
|
-
function eventHoverTitle(entry) {
|
|
4276
|
+
function eventHoverTitle(entry, nowMs) {
|
|
3910
4277
|
const parts = [
|
|
3911
4278
|
isoOrEmpty(entry.ts),
|
|
3912
4279
|
entry.kindRaw,
|
|
3913
4280
|
entry.origin
|
|
3914
4281
|
];
|
|
3915
4282
|
if (entry.seq !== null) parts.push(formatTemplate$1(DETAIL_STRINGS.timeline.seq, { n: entry.seq }));
|
|
4283
|
+
parts.push(formatRelativeTime(entry.ts, nowMs));
|
|
3916
4284
|
return parts.filter((p) => p !== "").join(" · ");
|
|
3917
4285
|
}
|
|
3918
4286
|
/** Gap marker text: 「缺口:可能有 N 条事件未捕获(256 队列上限或未持久化)」. */
|
|
@@ -3949,13 +4317,108 @@ window.__ModuleLoader__.load({
|
|
|
3949
4317
|
type: "event",
|
|
3950
4318
|
key: entry.key,
|
|
3951
4319
|
entry,
|
|
3952
|
-
|
|
3953
|
-
hoverTitle: eventHoverTitle(entry),
|
|
4320
|
+
timeLabel: formatEventTime(entry.ts, nowMs),
|
|
4321
|
+
hoverTitle: eventHoverTitle(entry, nowMs),
|
|
3954
4322
|
isNew: newKeys.has(entry.key)
|
|
3955
4323
|
});
|
|
3956
4324
|
}
|
|
3957
4325
|
return rows;
|
|
3958
4326
|
}
|
|
4327
|
+
/** The kinds that count as conversation (review UX-03 vocabulary). */
|
|
4328
|
+
const CONVERSATION_KINDS = /* @__PURE__ */ new Set([
|
|
4329
|
+
"user",
|
|
4330
|
+
"assistant",
|
|
4331
|
+
"error"
|
|
4332
|
+
]);
|
|
4333
|
+
/**
|
|
4334
|
+
* Kind filter over derived rows: 'conversation' keeps user/assistant/error
|
|
4335
|
+
* events only; 'all' passes everything through. Gap markers ALWAYS stay —
|
|
4336
|
+
* honesty rows are not noise and hiding them could fake a clean timeline.
|
|
4337
|
+
* Runs BEFORE {@link aggregateChunkRows} (gaps are detected on the full
|
|
4338
|
+
* entry list upstream, so filtering can never fabricate a gap).
|
|
4339
|
+
*/
|
|
4340
|
+
function filterTimelineRows(rows, mode) {
|
|
4341
|
+
if (mode === "all") return {
|
|
4342
|
+
rows: [...rows],
|
|
4343
|
+
hiddenCount: 0
|
|
4344
|
+
};
|
|
4345
|
+
const out = [];
|
|
4346
|
+
let hiddenCount = 0;
|
|
4347
|
+
for (const row of rows) {
|
|
4348
|
+
if (row.type === "event" && !CONVERSATION_KINDS.has(row.entry.kind)) {
|
|
4349
|
+
hiddenCount += 1;
|
|
4350
|
+
continue;
|
|
4351
|
+
}
|
|
4352
|
+
out.push(row);
|
|
4353
|
+
}
|
|
4354
|
+
return {
|
|
4355
|
+
rows: out,
|
|
4356
|
+
hiddenCount
|
|
4357
|
+
};
|
|
4358
|
+
}
|
|
4359
|
+
/**
|
|
4360
|
+
* True for a protocol streaming-chunk entry: an empty one-line summary and
|
|
4361
|
+
* a chunk-flavored kind (`assistant/chunk` style, matched on the last
|
|
4362
|
+
* slash segment). These are the rows that drowned real conversation in
|
|
4363
|
+
* the walkthrough (18 of 38 rows, review UX-03).
|
|
4364
|
+
*/
|
|
4365
|
+
function isStreamChunkEntry(entry) {
|
|
4366
|
+
if (entry.summary !== "") return false;
|
|
4367
|
+
const k = entry.kindRaw.trim().toLowerCase();
|
|
4368
|
+
const last = k.includes("/") ? k.split("/").pop() ?? k : k;
|
|
4369
|
+
return last === "chunk" || last.endsWith("_chunk") || last.endsWith("-chunk");
|
|
4370
|
+
}
|
|
4371
|
+
/**
|
|
4372
|
+
* Collapse each maximal run of ≥2 adjacent same-kind streaming-chunk rows
|
|
4373
|
+
* into one 'chunks' row carrying the members verbatim (lossless — the view
|
|
4374
|
+
* offers 展开). Gap markers and any non-chunk row break a run, so the
|
|
4375
|
+
* aggregation can never paper over a seq discontinuity. Single chunks stay
|
|
4376
|
+
* as plain rows (a 1-run header would add noise, not remove it).
|
|
4377
|
+
*/
|
|
4378
|
+
function aggregateChunkRows(rows) {
|
|
4379
|
+
const out = [];
|
|
4380
|
+
let run = [];
|
|
4381
|
+
const flush = () => {
|
|
4382
|
+
if (run.length >= 2) {
|
|
4383
|
+
const first = run[0];
|
|
4384
|
+
const last = run[run.length - 1];
|
|
4385
|
+
out.push({
|
|
4386
|
+
type: "chunks",
|
|
4387
|
+
key: `chunks:${first.key}`,
|
|
4388
|
+
kindRaw: first.entry.kindRaw,
|
|
4389
|
+
count: run.length,
|
|
4390
|
+
label: formatTemplate$1(DETAIL_STRINGS.timeline.chunkRun, { n: run.length }),
|
|
4391
|
+
timeLabel: last.timeLabel,
|
|
4392
|
+
hoverTitle: `${first.entry.kindRaw} ×${run.length}`,
|
|
4393
|
+
isNew: run.some((r) => r.isNew),
|
|
4394
|
+
members: run
|
|
4395
|
+
});
|
|
4396
|
+
} else out.push(...run);
|
|
4397
|
+
run = [];
|
|
4398
|
+
};
|
|
4399
|
+
for (const row of rows) {
|
|
4400
|
+
if (row.type === "event" && isStreamChunkEntry(row.entry)) {
|
|
4401
|
+
if (!(run.length === 0 || run[run.length - 1].entry.kindRaw === row.entry.kindRaw)) flush();
|
|
4402
|
+
run.push(row);
|
|
4403
|
+
continue;
|
|
4404
|
+
}
|
|
4405
|
+
flush();
|
|
4406
|
+
out.push(row);
|
|
4407
|
+
}
|
|
4408
|
+
flush();
|
|
4409
|
+
return out;
|
|
4410
|
+
}
|
|
4411
|
+
/**
|
|
4412
|
+
* Whether the view should pin its scroll position to the newest rows:
|
|
4413
|
+
* on the first non-empty render (initial landing — understanding the
|
|
4414
|
+
* current context needs the latest events, review UX-04), and on every
|
|
4415
|
+
* append while listen mode is on. Loading older history must never yank
|
|
4416
|
+
* the viewport (`positioned` stays true after the first landing).
|
|
4417
|
+
*/
|
|
4418
|
+
function shouldStickToLatest(input) {
|
|
4419
|
+
if (input.entryCount === 0) return false;
|
|
4420
|
+
return !input.positioned || input.listening;
|
|
4421
|
+
}
|
|
3959
4422
|
/**
|
|
3960
4423
|
* Keep only the newest `max` rows (the tail — listen mode appends there).
|
|
3961
4424
|
* The data stays in the VM; this is purely a render bound the component
|
|
@@ -4091,7 +4554,7 @@ window.__ModuleLoader__.load({
|
|
|
4091
4554
|
}
|
|
4092
4555
|
//#endregion
|
|
4093
4556
|
//#region \0dsh-css:/Users/jingyu/Workspace/Projects/agent_sidecar/plugin/src/client/detail/detail.module.css.mjs
|
|
4094
|
-
const css$5 = ".V1TWBW_root{box-sizing:border-box;height:100%;min-height:420px;color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-base,transparent);flex-direction:column;gap:10px;padding:16px 20px;display:flex}.V1TWBW_header{flex-direction:column;gap:6px;display:flex}.V1TWBW_headerTop{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.V1TWBW_closeButton{color:var(--dsw-alias-label-secondary,#57606a);border:1px solid var(--dsw-alias-border-l1,#00000014);cursor:pointer;background:0 0;border-radius:6px;padding:2px 10px;font-family:inherit;font-size:12px}.V1TWBW_closeButton:hover{color:var(--dsw-alias-label-primary,#1f2328);border-color:var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.V1TWBW_agent{color:var(--dsw-alias-label-primary,#1f2328);align-items:center;gap:5px;font-size:13px;font-weight:650;display:inline-flex}.V1TWBW_agentGlyph{color:var(--dsw-alias-brand-primary,#4d6bfe);flex:none}.V1TWBW_badge{border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-layer-1,#00000008);color:var(--dsw-alias-label-secondary,#57606a);white-space:nowrap;border-radius:999px;align-items:center;gap:5px;padding:0 8px;font-size:12px;line-height:20px;display:inline-flex}.V1TWBW_dot{background:var(--dsw-alias-label-tertiary,#6e7781);border-radius:50%;flex:none;width:8px;height:8px}.V1TWBW_dot[data-tone=success]{background:var(--dsw-alias-state-success-primary,#1a7f37)}.V1TWBW_dot[data-tone=warn]{background:var(--dsw-alias-state-warn-primary,#9a6700)}.V1TWBW_dot[data-tone=danger]{background:var(--dsw-alias-state-error-primary,#cf222e)}.V1TWBW_dot[data-tone=neutral]{background:var(--dsw-alias-label-tertiary,#6e7781)}.V1TWBW_dot[data-tone=muted]{background:var(--dsw-alias-label-dimmed,#8c959f)}.V1TWBW_spacer{flex:1}.V1TWBW_listenButton{color:var(--dsw-alias-label-secondary,#57606a);border:1px solid var(--dsw-alias-border-l1,#00000014);cursor:pointer;background:0 0;border-radius:999px;padding:2px 12px;font-family:inherit;font-size:12px}.V1TWBW_listenButton:hover{color:var(--dsw-alias-label-primary,#1f2328);border-color:var(--dsw-alias-border-l2,#0000001f)}.V1TWBW_listenButton[data-active]{color:var(--dsw-alias-brand-primary,#4d6bfe);border-color:var(--dsw-alias-brand-primary,#4d6bfe);background:var(--dsw-alias-brand-
|
|
4557
|
+
const css$5 = ".V1TWBW_root{box-sizing:border-box;height:100%;min-height:420px;color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-base,transparent);flex-direction:column;gap:10px;padding:16px 20px;display:flex}.V1TWBW_header{flex-direction:column;gap:6px;display:flex}.V1TWBW_headerTop{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.V1TWBW_closeButton{color:var(--dsw-alias-label-secondary,#57606a);border:1px solid var(--dsw-alias-border-l1,#00000014);cursor:pointer;background:0 0;border-radius:6px;padding:2px 10px;font-family:inherit;font-size:12px}.V1TWBW_closeButton:hover{color:var(--dsw-alias-label-primary,#1f2328);border-color:var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.V1TWBW_agent{color:var(--dsw-alias-label-primary,#1f2328);align-items:center;gap:5px;font-size:13px;font-weight:650;display:inline-flex}.V1TWBW_agentGlyph{color:var(--dsw-alias-brand-primary,#4d6bfe);flex:none}.V1TWBW_badge{border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-layer-1,#00000008);color:var(--dsw-alias-label-secondary,#57606a);white-space:nowrap;border-radius:999px;align-items:center;gap:5px;padding:0 8px;font-size:12px;line-height:20px;display:inline-flex}.V1TWBW_dot{background:var(--dsw-alias-label-tertiary,#6e7781);border-radius:50%;flex:none;width:8px;height:8px}.V1TWBW_dot[data-tone=success]{background:var(--dsw-alias-state-success-primary,#1a7f37)}.V1TWBW_dot[data-tone=warn]{background:var(--dsw-alias-state-warn-primary,#9a6700)}.V1TWBW_dot[data-tone=danger]{background:var(--dsw-alias-state-error-primary,#cf222e)}.V1TWBW_dot[data-tone=neutral]{background:var(--dsw-alias-label-tertiary,#6e7781)}.V1TWBW_dot[data-tone=muted]{background:var(--dsw-alias-label-dimmed,#8c959f)}.V1TWBW_spacer{flex:1}.V1TWBW_listenButton{color:var(--dsw-alias-label-secondary,#57606a);border:1px solid var(--dsw-alias-border-l1,#00000014);cursor:pointer;background:0 0;border-radius:999px;padding:2px 12px;font-family:inherit;font-size:12px}.V1TWBW_listenButton:hover{color:var(--dsw-alias-label-primary,#1f2328);border-color:var(--dsw-alias-border-l2,#0000001f)}.V1TWBW_listenButton[data-active]{color:var(--dsw-alias-brand-primary,#4d6bfe);border-color:var(--dsw-alias-brand-primary,#4d6bfe);background:color-mix(in srgb, var(--dsw-alias-brand-primary,#4d6bfe) 8%, transparent)}.V1TWBW_refreshButton{color:var(--dsw-alias-label-secondary,#57606a);border:1px solid var(--dsw-alias-border-l1,#00000014);cursor:pointer;background:0 0;border-radius:999px;padding:2px 12px;font-family:inherit;font-size:12px}.V1TWBW_refreshButton:hover:enabled{color:var(--dsw-alias-label-primary,#1f2328);border-color:var(--dsw-alias-border-l2,#0000001f)}.V1TWBW_refreshButton:disabled{cursor:default;opacity:.6}.V1TWBW_title{color:var(--dsw-alias-label-primary,#1f2328);text-overflow:ellipsis;white-space:nowrap;font-size:15px;font-weight:600;overflow:hidden}.V1TWBW_meta{align-items:baseline;gap:10px;min-width:0;display:flex}.V1TWBW_project{color:var(--dsw-alias-label-secondary,#57606a);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.V1TWBW_sessionId{color:var(--dsw-alias-label-tertiary,#6e7781);text-overflow:ellipsis;white-space:nowrap;cursor:pointer;background:0 0;border:none;flex:none;max-width:40%;padding:0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow:hidden}.V1TWBW_sessionId:hover{color:var(--dsw-alias-label-primary,#1f2328);text-decoration:underline}.V1TWBW_copiedBubble{color:var(--dsw-alias-state-success-primary,#1a7f37);border:1px solid var(--dsw-alias-state-success-secondary,#1a7f373d);border-radius:999px;flex:none;padding:0 6px;font-size:10px;line-height:16px}.V1TWBW_metaRow{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.V1TWBW_disclaimer{color:var(--dsw-alias-label-caption,var(--dsw-alias-label-tertiary,#6e7781));font-size:11px}.V1TWBW_sourceList{align-items:center;gap:4px;display:inline-flex}.V1TWBW_sourceBadge{border:1px solid var(--dsw-alias-border-l1,#00000014);color:var(--dsw-alias-label-secondary,#57606a);background:var(--dsw-alias-bg-layer-1,#00000008);white-space:nowrap;border-radius:999px;padding:0 6px;font-size:11px;line-height:16px}.V1TWBW_sourceBadge[data-tone=success]{color:var(--dsw-alias-state-success-primary,#1a7f37);border-color:var(--dsw-alias-state-success-secondary,#1a7f373d)}.V1TWBW_sourceBadge[data-tone=muted]{color:var(--dsw-alias-label-dimmed,#8c959f)}.V1TWBW_banner{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary,#9a6700));background:var(--dsw-alias-state-warn-tertiary,#9a670014);border:1px solid var(--dsw-alias-state-warn-secondary,#9a67003d);border-radius:8px;padding:6px 10px;font-size:12px;line-height:18px}.V1TWBW_bodyState{text-align:center;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:6px;min-height:160px;padding:24px;display:flex}.V1TWBW_bodyStateTitle{color:var(--dsw-alias-label-primary,#1f2328);font-size:14px;font-weight:600}.V1TWBW_bodyState[data-kind=error] .V1TWBW_bodyStateTitle{color:var(--dsw-alias-state-error-primary,#cf222e)}.V1TWBW_bodyStateHint{max-width:420px;color:var(--dsw-alias-label-secondary,#57606a);font-size:12px;line-height:20px}.V1TWBW_filterRow{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.V1TWBW_filterChip{color:var(--dsw-alias-label-secondary,#57606a);border:1px solid var(--dsw-alias-border-l1,#00000014);cursor:pointer;background:0 0;border-radius:999px;padding:0 10px;font-family:inherit;font-size:11px;line-height:18px}.V1TWBW_filterChip:hover{color:var(--dsw-alias-label-primary,#1f2328);border-color:var(--dsw-alias-border-l2,#0000001f)}.V1TWBW_filterChip[data-active]{color:var(--dsw-alias-brand-primary,#4d6bfe);border-color:var(--dsw-alias-brand-primary,#4d6bfe);background:color-mix(in srgb, var(--dsw-alias-brand-primary,#4d6bfe) 8%, transparent)}.V1TWBW_filterHiddenNote{color:var(--dsw-alias-label-tertiary,#6e7781);font-size:11px}.V1TWBW_pager{justify-content:center;display:flex}.V1TWBW_loadMoreButton{color:var(--dsw-alias-label-secondary,#57606a);border:1px solid var(--dsw-alias-border-l1,#00000014);cursor:pointer;background:0 0;border-radius:6px;padding:3px 14px;font-family:inherit;font-size:12px}.V1TWBW_loadMoreButton:hover:enabled{color:var(--dsw-alias-label-primary,#1f2328);border-color:var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.V1TWBW_loadMoreButton:disabled{cursor:default;opacity:.6}.V1TWBW_pagerNote{color:var(--dsw-alias-label-tertiary,#6e7781);font-size:11px}.V1TWBW_hiddenNotice{color:var(--dsw-alias-label-tertiary,#6e7781);justify-content:center;align-items:center;gap:8px;font-size:11px;display:flex}.V1TWBW_showAllButton{color:var(--dsw-alias-brand-primary,#4d6bfe);cursor:pointer;background:0 0;border:none;padding:0;font-family:inherit;font-size:11px}.V1TWBW_timeline{flex-direction:column;flex:1;gap:6px;margin:0;padding:0 2px 8px 0;list-style:none;display:flex;overflow-y:auto}.V1TWBW_event{border:1px solid var(--dsw-alias-border-l1,#0000000f);background:var(--dsw-alias-bg-layer-1,#00000005);border-radius:8px;flex-direction:column;gap:4px;padding:6px 10px;display:flex}.V1TWBW_event[data-new]{border-color:var(--dsw-alias-brand-primary,#4d6bfe);background:color-mix(in srgb, var(--dsw-alias-brand-primary,#4d6bfe) 6%, transparent)}.V1TWBW_chunkRun{color:var(--dsw-alias-label-tertiary,#6e7781);background:var(--dsw-alias-bg-layer-1,#00000005);border:1px dashed var(--dsw-alias-border-l2,#0000001f);border-radius:6px;align-items:center;gap:8px;padding:4px 10px;font-size:11px;line-height:16px;display:flex}.V1TWBW_chunkRun[data-new]{border-color:var(--dsw-alias-brand-primary,#4d6bfe)}.V1TWBW_chunkRunLabel{flex:none}.V1TWBW_eventHead{align-items:center;gap:6px;min-width:0;display:flex}.V1TWBW_eventGlyph{color:var(--dsw-alias-label-tertiary,#6e7781);flex:none;font-size:12px}.V1TWBW_event[data-kind=user] .V1TWBW_eventGlyph,.V1TWBW_event[data-kind=assistant] .V1TWBW_eventGlyph{color:var(--dsw-alias-brand-primary,#4d6bfe)}.V1TWBW_event[data-kind=error] .V1TWBW_eventGlyph{color:var(--dsw-alias-state-error-primary,#cf222e)}.V1TWBW_eventLabel{color:var(--dsw-alias-label-primary,#1f2328);white-space:nowrap;text-overflow:ellipsis;font-size:12px;font-weight:600;overflow:hidden}.V1TWBW_eventSeq{color:var(--dsw-alias-label-tertiary,#6e7781);flex:none;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10px}.V1TWBW_eventNew{color:var(--dsw-alias-brand-primary,#4d6bfe);border:1px solid var(--dsw-alias-brand-primary,#4d6bfe);border-radius:999px;flex:none;padding:0 5px;font-size:10px;line-height:14px}.V1TWBW_eventSpacer{flex:1}.V1TWBW_eventTime{color:var(--dsw-alias-label-caption,var(--dsw-alias-label-tertiary,#6e7781));flex:none;font-size:11px}.V1TWBW_eventSummary{color:var(--dsw-alias-label-secondary,#57606a);overflow-wrap:anywhere;font-size:12px;line-height:18px}.V1TWBW_expandButton{color:var(--dsw-alias-brand-primary,#4d6bfe);cursor:pointer;background:0 0;border:none;align-self:flex-start;padding:0;font-family:inherit;font-size:11px}.V1TWBW_eventBody{color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-layer-2,#0000000a);white-space:pre-wrap;overflow-wrap:anywhere;border-radius:6px;max-height:320px;margin:0;padding:8px 10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:16px;overflow-y:auto}.V1TWBW_gap{text-align:center;color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary,#9a6700));background:var(--dsw-alias-state-warn-tertiary,#9a67000f);border:1px dashed var(--dsw-alias-state-warn-secondary,#9a670052);border-radius:6px;padding:4px 10px;font-size:11px;line-height:16px}";
|
|
4095
4558
|
const tagId$5 = "@shendeguize/dsh-agent-sidecar/detail.module.css";
|
|
4096
4559
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$5) + "]") === null) {
|
|
4097
4560
|
const tag = document.createElement("style");
|
|
@@ -4108,7 +4571,10 @@ window.__ModuleLoader__.load({
|
|
|
4108
4571
|
"bodyState": "V1TWBW_bodyState",
|
|
4109
4572
|
"bodyStateHint": "V1TWBW_bodyStateHint",
|
|
4110
4573
|
"bodyStateTitle": "V1TWBW_bodyStateTitle",
|
|
4574
|
+
"chunkRun": "V1TWBW_chunkRun",
|
|
4575
|
+
"chunkRunLabel": "V1TWBW_chunkRunLabel",
|
|
4111
4576
|
"closeButton": "V1TWBW_closeButton",
|
|
4577
|
+
"copiedBubble": "V1TWBW_copiedBubble",
|
|
4112
4578
|
"disclaimer": "V1TWBW_disclaimer",
|
|
4113
4579
|
"dot": "V1TWBW_dot",
|
|
4114
4580
|
"event": "V1TWBW_event",
|
|
@@ -4122,6 +4588,9 @@ window.__ModuleLoader__.load({
|
|
|
4122
4588
|
"eventSummary": "V1TWBW_eventSummary",
|
|
4123
4589
|
"eventTime": "V1TWBW_eventTime",
|
|
4124
4590
|
"expandButton": "V1TWBW_expandButton",
|
|
4591
|
+
"filterChip": "V1TWBW_filterChip",
|
|
4592
|
+
"filterHiddenNote": "V1TWBW_filterHiddenNote",
|
|
4593
|
+
"filterRow": "V1TWBW_filterRow",
|
|
4125
4594
|
"gap": "V1TWBW_gap",
|
|
4126
4595
|
"header": "V1TWBW_header",
|
|
4127
4596
|
"headerTop": "V1TWBW_headerTop",
|
|
@@ -4133,6 +4602,7 @@ window.__ModuleLoader__.load({
|
|
|
4133
4602
|
"pager": "V1TWBW_pager",
|
|
4134
4603
|
"pagerNote": "V1TWBW_pagerNote",
|
|
4135
4604
|
"project": "V1TWBW_project",
|
|
4605
|
+
"refreshButton": "V1TWBW_refreshButton",
|
|
4136
4606
|
"root": "V1TWBW_root",
|
|
4137
4607
|
"sessionId": "V1TWBW_sessionId",
|
|
4138
4608
|
"showAllButton": "V1TWBW_showAllButton",
|
|
@@ -4151,13 +4621,19 @@ window.__ModuleLoader__.load({
|
|
|
4151
4621
|
* imports. The integration layer (S7) owns transport and accumulation —
|
|
4152
4622
|
* it feeds the {@link TimelineVM} built via logic.ts (`applyTimelinePage`
|
|
4153
4623
|
* for history pages, `applyListenPage` for listen-mode refetches) and
|
|
4154
|
-
* handles `onLoadMore` / `onToggleListen`.
|
|
4624
|
+
* handles `onLoadMore` / `onToggleListen` / `onRefresh`.
|
|
4625
|
+
*
|
|
4626
|
+
* Row pipeline (all pure, logic.ts): buildTimelineRows (gaps on the FULL
|
|
4627
|
+
* entry list) → filterTimelineRows (UX-03 kind filter, conversation-first
|
|
4628
|
+
* by default with an honest hidden count) → aggregateChunkRows (UX-03
|
|
4629
|
+
* adjacent empty streaming chunks collapse into one expandable run) →
|
|
4630
|
+
* limitTimelineRows (render cap with a 全部显示 escape hatch).
|
|
4155
4631
|
*
|
|
4156
4632
|
* Long-list posture (task report): no full virtualization — history only
|
|
4157
4633
|
* grows page-by-page on explicit 加载更多, and rendering is additionally
|
|
4158
|
-
* capped at {@link DEFAULT_MAX_RENDER_ROWS} newest rows
|
|
4159
|
-
*
|
|
4160
|
-
*
|
|
4634
|
+
* capped at {@link DEFAULT_MAX_RENDER_ROWS} newest rows. View-local
|
|
4635
|
+
* concerns (expanded bodies/runs, filter mode, the lift-cap flag, the
|
|
4636
|
+
* UX-04 initial landing, copy feedback) are component state; everything
|
|
4161
4637
|
* else comes through props.
|
|
4162
4638
|
*/
|
|
4163
4639
|
function EventRow(props) {
|
|
@@ -4193,7 +4669,7 @@ window.__ModuleLoader__.load({
|
|
|
4193
4669
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: detail_module_css_default["eventSpacer"] }),
|
|
4194
4670
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4195
4671
|
className: detail_module_css_default["eventTime"],
|
|
4196
|
-
children: row.
|
|
4672
|
+
children: row.timeLabel
|
|
4197
4673
|
})
|
|
4198
4674
|
]
|
|
4199
4675
|
}),
|
|
@@ -4214,12 +4690,48 @@ window.__ModuleLoader__.load({
|
|
|
4214
4690
|
]
|
|
4215
4691
|
});
|
|
4216
4692
|
}
|
|
4693
|
+
/** Collapsed run of adjacent streaming chunks (UX-03); expandable lossless. */
|
|
4694
|
+
function ChunkRunRow(props) {
|
|
4695
|
+
const { row } = props;
|
|
4696
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
4697
|
+
className: detail_module_css_default["chunkRun"],
|
|
4698
|
+
"data-new": row.isNew || void 0,
|
|
4699
|
+
title: row.hoverTitle,
|
|
4700
|
+
"data-testid": "agent-sidecar-detail-chunks",
|
|
4701
|
+
children: [
|
|
4702
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4703
|
+
className: detail_module_css_default["chunkRunLabel"],
|
|
4704
|
+
children: row.label
|
|
4705
|
+
}),
|
|
4706
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4707
|
+
type: "button",
|
|
4708
|
+
className: detail_module_css_default["expandButton"],
|
|
4709
|
+
onClick: () => props.onToggleRun(row.key),
|
|
4710
|
+
children: props.expanded ? DETAIL_STRINGS.timeline.collapse : DETAIL_STRINGS.timeline.expand
|
|
4711
|
+
}),
|
|
4712
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: detail_module_css_default["eventSpacer"] }),
|
|
4713
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4714
|
+
className: detail_module_css_default["eventTime"],
|
|
4715
|
+
children: row.timeLabel
|
|
4716
|
+
})
|
|
4717
|
+
]
|
|
4718
|
+
}), props.expanded && row.members.map((member) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EventRow, {
|
|
4719
|
+
row: member,
|
|
4720
|
+
expanded: props.expandedKeys.has(member.key),
|
|
4721
|
+
onToggleExpand: props.onToggleExpand
|
|
4722
|
+
}, member.key))] });
|
|
4723
|
+
}
|
|
4217
4724
|
/** The session-detail view. Pure render of the logic.ts pipelines over props. */
|
|
4218
4725
|
function SessionDetail(props) {
|
|
4219
4726
|
const nowMs = props.nowMs ?? Date.now();
|
|
4220
4727
|
const [expandedKeys, setExpandedKeys] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
4728
|
+
const [expandedRuns, setExpandedRuns] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
4729
|
+
const [filterMode, setFilterMode] = (0, react.useState)("conversation");
|
|
4221
4730
|
const [renderAll, setRenderAll] = (0, react.useState)(false);
|
|
4731
|
+
const [copied, setCopied] = (0, react.useState)(false);
|
|
4222
4732
|
const listRef = (0, react.useRef)(null);
|
|
4733
|
+
const positionedRef = (0, react.useRef)(false);
|
|
4734
|
+
const copyTimerRef = (0, react.useRef)(null);
|
|
4223
4735
|
const status = deriveDetailStatus(props.header.status);
|
|
4224
4736
|
const sourceBadges = deriveSourceBadges(props.timeline.sources);
|
|
4225
4737
|
const bodyState = deriveDetailBodyState({
|
|
@@ -4227,19 +4739,30 @@ window.__ModuleLoader__.load({
|
|
|
4227
4739
|
error: props.error,
|
|
4228
4740
|
entryCount: props.timeline.entries.length
|
|
4229
4741
|
});
|
|
4230
|
-
const
|
|
4742
|
+
const filtered = filterTimelineRows(buildTimelineRows(props.timeline, nowMs), filterMode);
|
|
4743
|
+
const aggregated = aggregateChunkRows(filtered.rows);
|
|
4231
4744
|
const limited = renderAll ? {
|
|
4232
|
-
rows:
|
|
4745
|
+
rows: aggregated,
|
|
4233
4746
|
hiddenCount: 0,
|
|
4234
4747
|
notice: null
|
|
4235
|
-
} : limitTimelineRows(
|
|
4748
|
+
} : limitTimelineRows(aggregated, props.maxRenderRows ?? 400);
|
|
4236
4749
|
const entryCount = props.timeline.entries.length;
|
|
4237
4750
|
const listening = props.listening;
|
|
4238
4751
|
(0, react.useEffect)(() => {
|
|
4239
|
-
if (!listening) return;
|
|
4240
4752
|
const list = listRef.current;
|
|
4241
|
-
if (list
|
|
4753
|
+
if (list === null) return;
|
|
4754
|
+
if (shouldStickToLatest({
|
|
4755
|
+
entryCount,
|
|
4756
|
+
positioned: positionedRef.current,
|
|
4757
|
+
listening
|
|
4758
|
+
})) {
|
|
4759
|
+
list.scrollTop = list.scrollHeight;
|
|
4760
|
+
positionedRef.current = true;
|
|
4761
|
+
}
|
|
4242
4762
|
}, [listening, entryCount]);
|
|
4763
|
+
(0, react.useEffect)(() => () => {
|
|
4764
|
+
if (copyTimerRef.current !== null) clearTimeout(copyTimerRef.current);
|
|
4765
|
+
}, []);
|
|
4243
4766
|
const toggleExpand = (key) => {
|
|
4244
4767
|
setExpandedKeys((prev) => {
|
|
4245
4768
|
const next = new Set(prev);
|
|
@@ -4248,6 +4771,25 @@ window.__ModuleLoader__.load({
|
|
|
4248
4771
|
return next;
|
|
4249
4772
|
});
|
|
4250
4773
|
};
|
|
4774
|
+
const toggleRun = (key) => {
|
|
4775
|
+
setExpandedRuns((prev) => {
|
|
4776
|
+
const next = new Set(prev);
|
|
4777
|
+
if (next.has(key)) next.delete(key);
|
|
4778
|
+
else next.add(key);
|
|
4779
|
+
return next;
|
|
4780
|
+
});
|
|
4781
|
+
};
|
|
4782
|
+
const copySessionId = () => {
|
|
4783
|
+
const clipboard = typeof navigator === "undefined" ? void 0 : navigator.clipboard;
|
|
4784
|
+
if (clipboard === void 0) return;
|
|
4785
|
+
clipboard.writeText(props.sessionId).then(() => {
|
|
4786
|
+
setCopied(true);
|
|
4787
|
+
if (copyTimerRef.current !== null) clearTimeout(copyTimerRef.current);
|
|
4788
|
+
copyTimerRef.current = setTimeout(() => {
|
|
4789
|
+
setCopied(false);
|
|
4790
|
+
}, 2e3);
|
|
4791
|
+
}, () => {});
|
|
4792
|
+
};
|
|
4251
4793
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4252
4794
|
className: detail_module_css_default["root"],
|
|
4253
4795
|
"data-testid": "agent-sidecar-detail",
|
|
@@ -4282,6 +4824,15 @@ window.__ModuleLoader__.load({
|
|
|
4282
4824
|
}), status.label]
|
|
4283
4825
|
}),
|
|
4284
4826
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: detail_module_css_default["spacer"] }),
|
|
4827
|
+
props.onRefresh !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4828
|
+
type: "button",
|
|
4829
|
+
className: detail_module_css_default["refreshButton"],
|
|
4830
|
+
disabled: props.refreshing === true,
|
|
4831
|
+
title: DETAIL_STRINGS.header.refreshHint,
|
|
4832
|
+
onClick: props.onRefresh,
|
|
4833
|
+
"data-testid": "agent-sidecar-detail-refresh",
|
|
4834
|
+
children: props.refreshing === true ? DETAIL_STRINGS.header.refreshing : DETAIL_STRINGS.header.refresh
|
|
4835
|
+
}),
|
|
4285
4836
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4286
4837
|
type: "button",
|
|
4287
4838
|
className: detail_module_css_default["listenButton"],
|
|
@@ -4300,15 +4851,26 @@ window.__ModuleLoader__.load({
|
|
|
4300
4851
|
}),
|
|
4301
4852
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4302
4853
|
className: detail_module_css_default["meta"],
|
|
4303
|
-
children: [
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4854
|
+
children: [
|
|
4855
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4856
|
+
className: detail_module_css_default["project"],
|
|
4857
|
+
title: props.header.project,
|
|
4858
|
+
children: props.header.project.trim() === "" ? DETAIL_STRINGS.header.unknownProject : props.header.project
|
|
4859
|
+
}),
|
|
4860
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4861
|
+
type: "button",
|
|
4862
|
+
className: detail_module_css_default["sessionId"],
|
|
4863
|
+
title: `${props.sessionId} · ${DETAIL_STRINGS.header.copyIdTitle}`,
|
|
4864
|
+
onClick: copySessionId,
|
|
4865
|
+
"data-testid": "agent-sidecar-detail-copy-id",
|
|
4866
|
+
children: props.sessionId
|
|
4867
|
+
}),
|
|
4868
|
+
copied && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4869
|
+
className: detail_module_css_default["copiedBubble"],
|
|
4870
|
+
role: "status",
|
|
4871
|
+
children: DETAIL_STRINGS.header.copied
|
|
4872
|
+
})
|
|
4873
|
+
]
|
|
4312
4874
|
}),
|
|
4313
4875
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4314
4876
|
className: detail_module_css_default["metaRow"],
|
|
@@ -4343,6 +4905,23 @@ window.__ModuleLoader__.load({
|
|
|
4343
4905
|
children: bodyState.hint
|
|
4344
4906
|
})]
|
|
4345
4907
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4908
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4909
|
+
className: detail_module_css_default["filterRow"],
|
|
4910
|
+
"data-testid": "agent-sidecar-detail-filter",
|
|
4911
|
+
children: [["conversation", "all"].map((mode) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4912
|
+
type: "button",
|
|
4913
|
+
className: detail_module_css_default["filterChip"],
|
|
4914
|
+
"data-active": filterMode === mode || void 0,
|
|
4915
|
+
"aria-pressed": filterMode === mode,
|
|
4916
|
+
onClick: () => {
|
|
4917
|
+
setFilterMode(mode);
|
|
4918
|
+
},
|
|
4919
|
+
children: DETAIL_STRINGS.filter[mode]
|
|
4920
|
+
}, mode)), filtered.hiddenCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4921
|
+
className: detail_module_css_default["filterHiddenNote"],
|
|
4922
|
+
children: formatTemplate$1(DETAIL_STRINGS.filter.hiddenNotice, { n: filtered.hiddenCount })
|
|
4923
|
+
})]
|
|
4924
|
+
}),
|
|
4346
4925
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4347
4926
|
className: detail_module_css_default["pager"],
|
|
4348
4927
|
children: props.hasMore ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -4373,6 +4952,12 @@ window.__ModuleLoader__.load({
|
|
|
4373
4952
|
role: "note",
|
|
4374
4953
|
"data-testid": "agent-sidecar-detail-gap",
|
|
4375
4954
|
children: row.label
|
|
4955
|
+
}, row.key) : row.type === "chunks" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChunkRunRow, {
|
|
4956
|
+
row,
|
|
4957
|
+
expanded: expandedRuns.has(row.key),
|
|
4958
|
+
onToggleRun: toggleRun,
|
|
4959
|
+
expandedKeys,
|
|
4960
|
+
onToggleExpand: toggleExpand
|
|
4376
4961
|
}, row.key) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EventRow, {
|
|
4377
4962
|
row,
|
|
4378
4963
|
expanded: expandedKeys.has(row.key),
|
|
@@ -4385,7 +4970,7 @@ window.__ModuleLoader__.load({
|
|
|
4385
4970
|
}
|
|
4386
4971
|
//#endregion
|
|
4387
4972
|
//#region \0dsh-css:/Users/jingyu/Workspace/Projects/agent_sidecar/plugin/src/client/dsh-tools/dsh-tools.module.css.mjs
|
|
4388
|
-
const css$4 = ".f29_TW_panel{min-width:0;color:var(--dsw-alias-label-primary,#1f2328);flex-direction:column;gap:8px;display:flex}.f29_TW_panelHead{align-items:baseline;gap:8px;min-width:0;display:flex}.f29_TW_panelTitle{color:var(--dsw-alias-label-primary,#1f2328);font-size:13px;font-weight:600}.f29_TW_panelCount{color:var(--dsw-alias-label-secondary,#57606a);background:var(--dsw-alias-bg-layer-2,#0000000a);border-radius:999px;flex:none;padding:0 8px;font-size:11px;line-height:18px}.f29_TW_mutedLine{color:var(--dsw-alias-label-secondary,#57606a);font-size:12px}.f29_TW_errorCard{color:var(--dsw-alias-state-error-primary,#cf222e);background:var(--dsw-alias-state-error-secondary,#cf222e14);border:1px solid var(--dsw-alias-state-error-secondary,#cf222e3d);border-radius:8px;padding:8px 10px;font-size:12px;line-height:18px}.f29_TW_errorDetail{color:var(--dsw-alias-label-secondary,#57606a);word-break:break-all;margin-top:2px;font-size:11px;display:block}.f29_TW_degradeCard{border:1px dashed var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-bg-layer-1,#00000005);border-radius:8px;flex-direction:column;gap:4px;padding:10px 12px;display:flex}.f29_TW_degradeTitle{color:var(--dsw-alias-label-primary,#1f2328);font-size:12px;font-weight:600}.f29_TW_degradeBody{color:var(--dsw-alias-label-secondary,#57606a);font-size:12px;line-height:18px}.f29_TW_degradeDetail{color:var(--dsw-alias-label-tertiary,#6e7781);word-break:break-all;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}.f29_TW_noticeBar{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary,#9a6700));background:var(--dsw-alias-state-warn-tertiary,#9a670014);border:1px solid var(--dsw-alias-state-warn-secondary,#9a67003d);border-radius:8px;padding:5px 10px;font-size:12px;line-height:18px}.f29_TW_tree{flex-direction:column;gap:2px;min-width:0;display:flex}.f29_TW_treeRow{align-items:center;gap:4px;min-width:0;display:flex}.f29_TW_toggle{width:18px;height:18px;color:var(--dsw-alias-label-tertiary,#6e7781);cursor:pointer;background:0 0;border:none;border-radius:4px;flex:none;padding:0;font-size:10px;line-height:1}.f29_TW_toggle:hover{color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.f29_TW_toggleSpacer{flex:none;width:18px}.f29_TW_node{text-align:left;min-width:0;color:var(--dsw-alias-label-primary,#1f2328);cursor:pointer;background:0 0;border:1px solid #0000;border-radius:6px;align-items:center;gap:6px;padding:2px 8px;font-family:inherit;font-size:12px;display:inline-flex}.f29_TW_node:hover{border-color:var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.f29_TW_node:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4d6bfe);outline-offset:1px}.f29_TW_node[data-current=true]{border-color:var(--dsw-alias-brand-primary,#4d6bfe);background:var(--dsw-alias-brand-
|
|
4973
|
+
const css$4 = ".f29_TW_panel{min-width:0;color:var(--dsw-alias-label-primary,#1f2328);flex-direction:column;gap:8px;display:flex}.f29_TW_panelHead{align-items:baseline;gap:8px;min-width:0;display:flex}.f29_TW_panelTitle{color:var(--dsw-alias-label-primary,#1f2328);font-size:13px;font-weight:600}.f29_TW_panelCount{color:var(--dsw-alias-label-secondary,#57606a);background:var(--dsw-alias-bg-layer-2,#0000000a);border-radius:999px;flex:none;padding:0 8px;font-size:11px;line-height:18px}.f29_TW_mutedLine{color:var(--dsw-alias-label-secondary,#57606a);font-size:12px}.f29_TW_errorCard{color:var(--dsw-alias-state-error-primary,#cf222e);background:var(--dsw-alias-state-error-secondary,#cf222e14);border:1px solid var(--dsw-alias-state-error-secondary,#cf222e3d);border-radius:8px;padding:8px 10px;font-size:12px;line-height:18px}.f29_TW_errorDetail{color:var(--dsw-alias-label-secondary,#57606a);word-break:break-all;margin-top:2px;font-size:11px;display:block}.f29_TW_degradeCard{border:1px dashed var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-bg-layer-1,#00000005);border-radius:8px;flex-direction:column;gap:4px;padding:10px 12px;display:flex}.f29_TW_degradeTitle{color:var(--dsw-alias-label-primary,#1f2328);font-size:12px;font-weight:600}.f29_TW_degradeBody{color:var(--dsw-alias-label-secondary,#57606a);font-size:12px;line-height:18px}.f29_TW_degradeDetail{color:var(--dsw-alias-label-tertiary,#6e7781);word-break:break-all;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}.f29_TW_noticeBar{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-state-warn-primary,#9a6700));background:var(--dsw-alias-state-warn-tertiary,#9a670014);border:1px solid var(--dsw-alias-state-warn-secondary,#9a67003d);border-radius:8px;padding:5px 10px;font-size:12px;line-height:18px}.f29_TW_tree{flex-direction:column;gap:2px;min-width:0;display:flex}.f29_TW_treeRow{align-items:center;gap:4px;min-width:0;display:flex}.f29_TW_toggle{width:18px;height:18px;color:var(--dsw-alias-label-tertiary,#6e7781);cursor:pointer;background:0 0;border:none;border-radius:4px;flex:none;padding:0;font-size:10px;line-height:1}.f29_TW_toggle:hover{color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.f29_TW_toggleSpacer{flex:none;width:18px}.f29_TW_node{text-align:left;min-width:0;color:var(--dsw-alias-label-primary,#1f2328);cursor:pointer;background:0 0;border:1px solid #0000;border-radius:6px;align-items:center;gap:6px;padding:2px 8px;font-family:inherit;font-size:12px;display:inline-flex}.f29_TW_node:hover{border-color:var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.f29_TW_node:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4d6bfe);outline-offset:1px}.f29_TW_node[data-current=true]{border-color:var(--dsw-alias-brand-primary,#4d6bfe);background:color-mix(in srgb, var(--dsw-alias-brand-primary,#4d6bfe) 8%, transparent);cursor:default}.f29_TW_nodeId{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;overflow:hidden}.f29_TW_nodeRole{color:var(--dsw-alias-label-tertiary,#6e7781);flex:none;font-size:11px}.f29_TW_nodeBadge{color:var(--dsw-alias-label-secondary,#57606a);background:var(--dsw-alias-bg-layer-2,#0000000a);border:1px solid var(--dsw-alias-border-l1,#00000014);border-radius:999px;flex:none;padding:0 6px;font-size:11px;line-height:16px}.f29_TW_nodeBadge[data-kind=current]{color:var(--dsw-alias-brand-primary,#4d6bfe);border-color:var(--dsw-alias-brand-primary,#4d6bfe);background:0 0}.f29_TW_nodeBadge[data-kind=live]{color:var(--dsw-alias-state-success-primary,#1a7f37);border-color:var(--dsw-alias-state-success-secondary,#1a7f3752);background:0 0}.f29_TW_searchForm{align-items:center;gap:8px;display:flex}.f29_TW_searchInput{min-width:0;color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-layer-1,transparent);border:1px solid var(--dsw-alias-border-l2,#0000001f);border-radius:6px;flex:1;padding:4px 8px;font-family:inherit;font-size:12px}.f29_TW_searchInput:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4d6bfe);outline-offset:0}.f29_TW_searchSubmit{color:var(--dsw-alias-label-secondary,#57606a);border:1px solid var(--dsw-alias-border-l1,#00000014);cursor:pointer;background:0 0;border-radius:6px;flex:none;padding:3px 10px;font-family:inherit;font-size:12px}.f29_TW_searchSubmit:hover{color:var(--dsw-alias-label-primary,#1f2328);border-color:var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.f29_TW_projectChip{color:var(--dsw-alias-label-secondary,#57606a);background:var(--dsw-alias-bg-layer-2,#0000000a);border:1px solid var(--dsw-alias-border-l1,#00000014);border-radius:999px;align-self:flex-start;padding:0 8px;font-size:11px;line-height:18px}.f29_TW_resultList{flex-direction:column;gap:4px;min-width:0;display:flex}.f29_TW_resultItem{text-align:left;border:1px solid var(--dsw-alias-border-l1,#00000014);background:var(--dsw-alias-bg-layer-1,#00000005);cursor:pointer;border-radius:8px;flex-direction:column;gap:3px;min-width:0;padding:6px 10px;font-family:inherit;display:flex}.f29_TW_resultItem:hover{border-color:var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.f29_TW_resultItem:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4d6bfe);outline-offset:1px}.f29_TW_resultHead{align-items:center;gap:6px;min-width:0;display:flex}.f29_TW_resultAgent{color:var(--dsw-alias-label-secondary,#57606a);flex:none;font-size:11px;font-weight:600}.f29_TW_resultTitle{color:var(--dsw-alias-label-primary,#1f2328);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.f29_TW_matchTag{color:var(--dsw-alias-label-secondary,#57606a);background:var(--dsw-alias-bg-layer-2,#0000000a);border:1px solid var(--dsw-alias-border-l1,#00000014);border-radius:999px;flex:none;margin-left:auto;padding:0 6px;font-size:11px;line-height:16px}.f29_TW_matchTag[data-kind=full-text]{color:var(--dsw-alias-brand-primary,#4d6bfe);border-color:color-mix(in srgb, var(--dsw-alias-brand-primary,#4d6bfe) 32%, transparent);background:0 0}.f29_TW_resultMeta{min-width:0;color:var(--dsw-alias-label-tertiary,#6e7781);align-items:center;gap:8px;font-size:11px;display:flex}.f29_TW_resultProject{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.f29_TW_resultId{flex:none;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.f29_TW_snippet{color:var(--dsw-alias-label-secondary,#57606a);text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px;overflow:hidden}.f29_TW_snippetMark{color:var(--dsw-alias-brand-primary,#4d6bfe);background:color-mix(in srgb, var(--dsw-alias-brand-primary,#4d6bfe) 12%, transparent);border-radius:2px;font-weight:600}";
|
|
4389
4974
|
const tagId$4 = "@shendeguize/dsh-agent-sidecar/dsh-tools.module.css";
|
|
4390
4975
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
|
|
4391
4976
|
const tag = document.createElement("style");
|
|
@@ -5273,7 +5858,7 @@ window.__ModuleLoader__.load({
|
|
|
5273
5858
|
}
|
|
5274
5859
|
//#endregion
|
|
5275
5860
|
//#region \0dsh-css:/Users/jingyu/Workspace/Projects/agent_sidecar/plugin/src/client/inject/inject.module.css.mjs
|
|
5276
|
-
const css$2 = ".KlwADW_panel{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:12px;flex-direction:column;max-width:560px;display:flex}.KlwADW_header{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:12px;padding:14px 16px 10px;display:flex}.KlwADW_title{min-width:0;color:var(--dsw-alias-label-primary);flex:1;margin:0;font-size:15px;font-weight:600;line-height:1.4}.KlwADW_body{flex-direction:column;gap:10px;padding:12px 16px 14px;display:flex}.KlwADW_capabilityOff{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:8px;margin:0;padding:10px 12px;font-size:13px;line-height:1.6}.KlwADW_noticeWarn,.KlwADW_noticeError{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:8px;margin:0;padding:8px 10px;font-size:12px;line-height:1.6}.KlwADW_noticeError{border:1px solid var(--dsw-alias-
|
|
5861
|
+
const css$2 = ".KlwADW_panel{border:1px solid var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-bg-layer-2,#0000000a);border-radius:12px;flex-direction:column;max-width:560px;display:flex}.KlwADW_header{border-bottom:1px solid var(--dsw-alias-border-l2,#0000001f);align-items:center;gap:12px;padding:14px 16px 10px;display:flex}.KlwADW_title{min-width:0;color:var(--dsw-alias-label-primary,#1f2328);flex:1;margin:0;font-size:15px;font-weight:600;line-height:1.4}.KlwADW_body{flex-direction:column;gap:10px;padding:12px 16px 14px;display:flex}.KlwADW_capabilityOff{background:var(--dsw-alias-bg-module-platform,#00000008);color:var(--dsw-alias-label-secondary,#57606a);border-radius:8px;margin:0;padding:10px 12px;font-size:13px;line-height:1.6}.KlwADW_noticeWarn,.KlwADW_noticeError{background:var(--dsw-alias-bg-module-platform,#00000008);color:var(--dsw-alias-label-secondary,#57606a);border-radius:8px;margin:0;padding:8px 10px;font-size:12px;line-height:1.6}.KlwADW_noticeError{border:1px solid var(--dsw-alias-state-error-primary,#cf222e);color:var(--dsw-alias-state-error-primary,#cf222e)}.KlwADW_noticeDetail{color:var(--dsw-alias-label-tertiary,#6e7781)}.KlwADW_targetRow{align-items:baseline;gap:10px;min-width:0;display:flex}.KlwADW_noTarget{color:var(--dsw-alias-label-tertiary,#6e7781);font-size:12px;line-height:1.5}.KlwADW_agentTag{background:var(--dsw-alias-bg-module-platform,#00000008);color:var(--dsw-alias-label-secondary,#57606a);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.KlwADW_planTitle{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary,#1f2328);font-size:13px;line-height:1.5;overflow:hidden}.KlwADW_field{flex-direction:column;gap:6px;display:flex}.KlwADW_label{color:var(--dsw-alias-label-primary,#1f2328);font-size:13px;line-height:1.5}.KlwADW_textarea{appearance:none;resize:vertical;border:1px solid var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-bg-layer-3,#fff);min-height:96px;font:inherit;color:var(--dsw-alias-label-primary,#1f2328);border-radius:8px;padding:8px 10px;font-size:13px;line-height:1.6}.KlwADW_textarea:focus-visible{border-color:var(--dsw-alias-brand-primary,#4d6bfe);outline:none}.KlwADW_textarea::placeholder{color:var(--dsw-alias-label-tertiary,#6e7781)}.KlwADW_textarea:disabled{opacity:.5;cursor:default}.KlwADW_byteRow{align-items:center;gap:10px;display:flex}.KlwADW_byteBar{background:var(--dsw-alias-bg-module-platform,#00000008);border-radius:2px;flex:1;height:3px;overflow:hidden}.KlwADW_byteFill{background:var(--dsw-alias-brand-primary,#4d6bfe);border-radius:2px;height:100%;transition:width .12s}.KlwADW_byteFillOver{background:var(--dsw-alias-state-error-primary,#cf222e)}.KlwADW_byteText,.KlwADW_byteTextOver{font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-tertiary,#6e7781);flex:none;font-size:11px;line-height:1.5}.KlwADW_byteTextOver{color:var(--dsw-alias-state-error-primary,#cf222e)}.KlwADW_invalid{color:var(--dsw-alias-state-error-primary,#cf222e);margin:0;font-size:12px;line-height:1.5}.KlwADW_modes{border:0;flex-direction:column;gap:8px;margin:0;padding:0;display:flex}.KlwADW_modeOption{cursor:pointer;align-items:flex-start;gap:10px;display:flex}.KlwADW_modeOption input{accent-color:var(--dsw-alias-brand-primary,#4d6bfe);cursor:pointer;width:14px;height:14px;margin:3px 0 0}.KlwADW_modeOption input:disabled{cursor:default}.KlwADW_modeText{flex-direction:column;gap:2px;min-width:0;display:flex}.KlwADW_modeLabel{color:var(--dsw-alias-label-primary,#1f2328);font-size:13px;line-height:1.5}.KlwADW_modeHint{color:var(--dsw-alias-label-tertiary,#6e7781);font-size:12px;line-height:1.5}.KlwADW_warnBar{border:1px solid var(--dsw-alias-state-error-primary,#cf222e);background:var(--dsw-alias-bg-module-platform,#00000008);color:var(--dsw-alias-state-error-primary,#cf222e);border-radius:8px;margin:0;padding:8px 10px;font-size:12px;line-height:1.6}.KlwADW_auditNote{background:var(--dsw-alias-bg-module-platform,#00000008);color:var(--dsw-alias-label-secondary,#57606a);border-radius:8px;margin:0;padding:8px 10px;font-size:12px;line-height:1.6}.KlwADW_planBox{border:1px solid var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-bg-layer-3,#fff);border-radius:8px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}.KlwADW_planRow{align-items:baseline;gap:10px;min-width:0;display:flex}.KlwADW_planKey{color:var(--dsw-alias-label-tertiary,#6e7781);flex:none;font-size:12px;line-height:1.5}.KlwADW_planValue{min-width:0;color:var(--dsw-alias-label-primary,#1f2328);align-items:baseline;gap:8px;font-size:13px;line-height:1.5;display:flex}.KlwADW_observedNote{color:var(--dsw-alias-label-tertiary,#6e7781);margin:0;font-size:11px;line-height:1.5}.KlwADW_planPreview{flex-direction:column;gap:4px;min-width:0;display:flex}.KlwADW_preview{border:1px solid var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-bg-layer-2,#0000000a);white-space:pre-wrap;word-break:break-word;max-height:120px;color:var(--dsw-alias-label-secondary,#57606a);border-radius:6px;margin:0;padding:8px 10px;font-size:12px;line-height:1.6;overflow:auto}.KlwADW_countdown{font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary,#57606a);margin:0;font-size:12px;line-height:1.5}.KlwADW_resultOk,.KlwADW_resultFail,.KlwADW_resultUnknown{background:var(--dsw-alias-bg-module-platform,#00000008);color:var(--dsw-alias-label-primary,#1f2328);border-radius:8px;margin:0;padding:10px 12px;font-size:13px;line-height:1.6}.KlwADW_resultOk{border:1px solid var(--dsw-alias-brand-primary,#4d6bfe)}.KlwADW_resultFail{border:1px solid var(--dsw-alias-state-error-primary,#cf222e);color:var(--dsw-alias-state-error-primary,#cf222e)}.KlwADW_resultUnknown{border:1px solid var(--dsw-alias-label-dimmed,#8c959f);color:var(--dsw-alias-label-secondary,#57606a)}.KlwADW_resultDetail{color:var(--dsw-alias-label-tertiary,#6e7781);margin:0;font-size:12px;line-height:1.6}.KlwADW_footer{justify-content:flex-end;align-items:center;gap:8px;padding-top:4px;display:flex}.KlwADW_btn,.KlwADW_btnPrimary,.KlwADW_btnDanger{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.KlwADW_btn{border-color:var(--dsw-alias-border-l2,#0000001f);color:var(--dsw-alias-label-secondary,#57606a);background:0 0}.KlwADW_btn:hover:not(:disabled){color:var(--dsw-alias-label-primary,#1f2328);border-color:var(--dsw-alias-label-dimmed,#8c959f)}.KlwADW_btnPrimary{background:var(--dsw-alias-label-primary,#1f2328);color:var(--dsw-alias-bg-layer-3,#fff)}.KlwADW_btnDanger{border-color:var(--dsw-alias-state-error-primary,#cf222e);color:var(--dsw-alias-state-error-primary,#cf222e);background:0 0;font-weight:600}.KlwADW_btnDanger:hover:not(:disabled){background:var(--dsw-alias-state-error-primary,#cf222e);color:var(--dsw-alias-bg-layer-3,#fff)}.KlwADW_btn:disabled,.KlwADW_btnPrimary:disabled,.KlwADW_btnDanger:disabled{opacity:.4;cursor:default}.KlwADW_btn:focus-visible,.KlwADW_btnPrimary:focus-visible,.KlwADW_btnDanger:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4d6bfe);outline-offset:1px}";
|
|
5277
5862
|
const tagId$2 = "@shendeguize/dsh-agent-sidecar/inject.module.css";
|
|
5278
5863
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
5279
5864
|
const tag = document.createElement("style");
|
|
@@ -5571,6 +6156,18 @@ window.__ModuleLoader__.load({
|
|
|
5571
6156
|
block: null
|
|
5572
6157
|
};
|
|
5573
6158
|
}
|
|
6159
|
+
/**
|
|
6160
|
+
* Map a keydown to a panel intent: Escape closes in every phase;
|
|
6161
|
+
* Cmd/Ctrl+Enter submits the PREPARE step only (editor phase with a valid
|
|
6162
|
+
* message). The confirm phase deliberately binds nothing — executing the
|
|
6163
|
+
* injection stays an explicit click (two-phase discipline, §5.3), so no
|
|
6164
|
+
* keyboard path can shortcut the confirmation.
|
|
6165
|
+
*/
|
|
6166
|
+
function classifyPanelKey(input) {
|
|
6167
|
+
if (input.key === "Escape") return "close";
|
|
6168
|
+
if (input.key === "Enter" && (input.metaKey || input.ctrlKey)) return input.phase === "idle" && input.canPrepare ? "prepare" : null;
|
|
6169
|
+
return null;
|
|
6170
|
+
}
|
|
5574
6171
|
/** Which follow-up affordances a terminal outcome earns. */
|
|
5575
6172
|
function resultActions(outcome) {
|
|
5576
6173
|
return {
|
|
@@ -5578,6 +6175,16 @@ window.__ModuleLoader__.load({
|
|
|
5578
6175
|
showCheckSessionHint: outcome === "unknown"
|
|
5579
6176
|
};
|
|
5580
6177
|
}
|
|
6178
|
+
/**
|
|
6179
|
+
* True when an execute response reports the message actually reached the
|
|
6180
|
+
* target (UX-05 observation loop): a delivered outcome — including an
|
|
6181
|
+
* idempotent replay of one — never an error envelope. failed/unknown must
|
|
6182
|
+
* NOT trigger observation aids: unknown is a locked terminal state (S6)
|
|
6183
|
+
* and any follow-up activity could read as "retrying is fine".
|
|
6184
|
+
*/
|
|
6185
|
+
function isDeliveredResult(value) {
|
|
6186
|
+
return !isApiErrorLike(value) && value.outcome === "delivered";
|
|
6187
|
+
}
|
|
5581
6188
|
/** Mode radio copy (label + semantics hint) per injection mode. */
|
|
5582
6189
|
const MODE_COPY = {
|
|
5583
6190
|
queue: {
|
|
@@ -5833,6 +6440,22 @@ window.__ModuleLoader__.load({
|
|
|
5833
6440
|
}
|
|
5834
6441
|
dispatch(event);
|
|
5835
6442
|
};
|
|
6443
|
+
const handleKeyDown = (event) => {
|
|
6444
|
+
const intent = classifyPanelKey({
|
|
6445
|
+
key: event.key,
|
|
6446
|
+
metaKey: event.metaKey,
|
|
6447
|
+
ctrlKey: event.ctrlKey,
|
|
6448
|
+
phase: state.phase,
|
|
6449
|
+
canPrepare: gate.canPrepare
|
|
6450
|
+
});
|
|
6451
|
+
if (intent === "close" && props.onClose !== void 0) {
|
|
6452
|
+
event.stopPropagation();
|
|
6453
|
+
props.onClose();
|
|
6454
|
+
} else if (intent === "prepare") {
|
|
6455
|
+
event.preventDefault();
|
|
6456
|
+
handlePrepare();
|
|
6457
|
+
}
|
|
6458
|
+
};
|
|
5836
6459
|
const closeButton = props.onClose !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5837
6460
|
type: "button",
|
|
5838
6461
|
className: inject_module_css_default["btn"],
|
|
@@ -5842,6 +6465,7 @@ window.__ModuleLoader__.load({
|
|
|
5842
6465
|
if (!props.capability.inject) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
5843
6466
|
className: inject_module_css_default["panel"],
|
|
5844
6467
|
"aria-label": t$1("inject.title"),
|
|
6468
|
+
onKeyDown: handleKeyDown,
|
|
5845
6469
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
5846
6470
|
className: inject_module_css_default["header"],
|
|
5847
6471
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
@@ -5867,6 +6491,7 @@ window.__ModuleLoader__.load({
|
|
|
5867
6491
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
5868
6492
|
className: inject_module_css_default["panel"],
|
|
5869
6493
|
"aria-label": t$1("inject.title"),
|
|
6494
|
+
onKeyDown: handleKeyDown,
|
|
5870
6495
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
5871
6496
|
className: inject_module_css_default["header"],
|
|
5872
6497
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
@@ -5893,6 +6518,13 @@ window.__ModuleLoader__.load({
|
|
|
5893
6518
|
className: inject_module_css_default["footer"],
|
|
5894
6519
|
children: [
|
|
5895
6520
|
closeButton,
|
|
6521
|
+
isDeliveredResult(result) && props.onObserve !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6522
|
+
type: "button",
|
|
6523
|
+
className: inject_module_css_default["btnPrimary"],
|
|
6524
|
+
onClick: props.onObserve,
|
|
6525
|
+
"data-testid": "agent-sidecar-inject-observe",
|
|
6526
|
+
children: t$1("inject.observeListen")
|
|
6527
|
+
}) : null,
|
|
5896
6528
|
actions.canReprepare ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5897
6529
|
type: "button",
|
|
5898
6530
|
className: inject_module_css_default["btnPrimary"],
|
|
@@ -5921,6 +6553,7 @@ window.__ModuleLoader__.load({
|
|
|
5921
6553
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
5922
6554
|
className: inject_module_css_default["panel"],
|
|
5923
6555
|
"aria-label": t$1("inject.confirmTitle"),
|
|
6556
|
+
onKeyDown: handleKeyDown,
|
|
5924
6557
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
5925
6558
|
className: inject_module_css_default["header"],
|
|
5926
6559
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
@@ -5975,6 +6608,7 @@ window.__ModuleLoader__.load({
|
|
|
5975
6608
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
5976
6609
|
className: inject_module_css_default["panel"],
|
|
5977
6610
|
"aria-label": t$1("inject.title"),
|
|
6611
|
+
onKeyDown: handleKeyDown,
|
|
5978
6612
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
5979
6613
|
className: inject_module_css_default["header"],
|
|
5980
6614
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
@@ -6027,6 +6661,7 @@ window.__ModuleLoader__.load({
|
|
|
6027
6661
|
placeholder: t$1("inject.messagePlaceholder"),
|
|
6028
6662
|
disabled: !canEdit,
|
|
6029
6663
|
rows: 5,
|
|
6664
|
+
autoFocus: true,
|
|
6030
6665
|
onChange: (event) => {
|
|
6031
6666
|
setDraft(event.target.value);
|
|
6032
6667
|
}
|
|
@@ -6361,6 +6996,7 @@ window.__ModuleLoader__.load({
|
|
|
6361
6996
|
paging = false;
|
|
6362
6997
|
listenInFlight = false;
|
|
6363
6998
|
listenQueued = false;
|
|
6999
|
+
refreshInFlight = false;
|
|
6364
7000
|
lineageStarted = false;
|
|
6365
7001
|
constructor(sessionId, options = {}) {
|
|
6366
7002
|
this.fetchDetailFn = options.fetchDetailFn ?? fetchSessionDetail;
|
|
@@ -6375,6 +7011,7 @@ window.__ModuleLoader__.load({
|
|
|
6375
7011
|
error: null,
|
|
6376
7012
|
hasMore: false,
|
|
6377
7013
|
listening: false,
|
|
7014
|
+
refreshing: false,
|
|
6378
7015
|
ready: false,
|
|
6379
7016
|
lineage: INITIAL_LINEAGE
|
|
6380
7017
|
};
|
|
@@ -6468,6 +7105,38 @@ window.__ModuleLoader__.load({
|
|
|
6468
7105
|
if (listening) this.scheduleListenRefetch();
|
|
6469
7106
|
}
|
|
6470
7107
|
/**
|
|
7108
|
+
* Manual newest-window refetch with visible feedback (UX-07), also fired
|
|
7109
|
+
* once after a delivered injection (UX-05 observation loop). Unlike the
|
|
7110
|
+
* silent best-effort listen refetch, it reports in-flight state and
|
|
7111
|
+
* surfaces a failure reason (rendered as the inline banner). Appended
|
|
7112
|
+
* entries get the listen-merge highlight. Coalesced: at most one manual
|
|
7113
|
+
* refresh in flight, extra calls are dropped.
|
|
7114
|
+
*/
|
|
7115
|
+
async refreshNewest() {
|
|
7116
|
+
if (this.disposed || !this.state.ready || this.refreshInFlight) return;
|
|
7117
|
+
this.refreshInFlight = true;
|
|
7118
|
+
this.setState({
|
|
7119
|
+
refreshing: true,
|
|
7120
|
+
error: null
|
|
7121
|
+
});
|
|
7122
|
+
try {
|
|
7123
|
+
const page = await this.fetchPageFn(this.state.sessionId, { ...this.listenLimit !== void 0 ? { limit: this.listenLimit } : {} });
|
|
7124
|
+
if (this.disposed) return;
|
|
7125
|
+
this.setState({
|
|
7126
|
+
timeline: applyListenPage(this.state.timeline, page),
|
|
7127
|
+
refreshing: false
|
|
7128
|
+
});
|
|
7129
|
+
} catch (err) {
|
|
7130
|
+
if (this.disposed) return;
|
|
7131
|
+
this.setState({
|
|
7132
|
+
refreshing: false,
|
|
7133
|
+
error: reasonOf(err)
|
|
7134
|
+
});
|
|
7135
|
+
} finally {
|
|
7136
|
+
this.refreshInFlight = false;
|
|
7137
|
+
}
|
|
7138
|
+
}
|
|
7139
|
+
/**
|
|
6471
7140
|
* SSE `state` frame hook (one call per controller notification). Refreshes
|
|
6472
7141
|
* the header from the live board card when given, and in listen mode
|
|
6473
7142
|
* triggers a coalesced newest-window refetch.
|
|
@@ -7003,7 +7672,7 @@ window.__ModuleLoader__.load({
|
|
|
7003
7672
|
}
|
|
7004
7673
|
//#endregion
|
|
7005
7674
|
//#region \0dsh-css:/Users/jingyu/Workspace/Projects/agent_sidecar/plugin/src/client/detail-view.module.css.mjs
|
|
7006
|
-
const css$1 = ".p-2YEW_switcherBar{align-items:center;gap:4px;padding:8px 12px 0;display:flex}.p-2YEW_switcherButton{color:var(--dsw-alias-label-secondary,#57606a);cursor:pointer;background:0 0;border:1px solid #0000;border-radius:999px;padding:3px 10px;font-size:12px}.p-2YEW_switcherButton:hover{background:var(--dsw-alias-bg-layer-1,#00000005)}.p-2YEW_switcherButton[data-active=true]{color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-layer-2,#0000000a);border-color:var(--dsw-alias-border-l2,#0000001f);font-weight:600}.p-2YEW_detailRoot{flex-direction:column;gap:12px;min-width:0;padding:12px;display:flex}.p-2YEW_actionsRow{align-items:center;gap:8px;display:flex}.p-2YEW_actionButton{border:1px solid var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-bg-layer-1,#00000005);color:var(--dsw-alias-label-primary,#1f2328);cursor:pointer;border-radius:6px;padding:3px 12px;font-size:12px}.p-2YEW_actionButton:hover{background:var(--dsw-alias-bg-layer-2,#0000000a)}.p-2YEW_actionButton:disabled{opacity:.5;cursor:not-allowed}.p-2YEW_toolsSection{border
|
|
7675
|
+
const css$1 = ".p-2YEW_switcherBar{align-items:center;gap:4px;padding:8px 12px 0;display:flex}.p-2YEW_switcherButton{color:var(--dsw-alias-label-secondary,#57606a);cursor:pointer;background:0 0;border:1px solid #0000;border-radius:999px;padding:3px 10px;font-size:12px}.p-2YEW_switcherButton:hover{background:var(--dsw-alias-bg-layer-1,#00000005)}.p-2YEW_switcherButton[data-active=true]{color:var(--dsw-alias-label-primary,#1f2328);background:var(--dsw-alias-bg-layer-2,#0000000a);border-color:var(--dsw-alias-border-l2,#0000001f);font-weight:600}.p-2YEW_detailRoot{box-sizing:border-box;flex-direction:column;gap:12px;min-width:0;height:100%;padding:12px;display:flex;overflow-y:auto}.p-2YEW_actionsRow{align-items:center;gap:8px;display:flex}.p-2YEW_actionButton{border:1px solid var(--dsw-alias-border-l2,#0000001f);background:var(--dsw-alias-bg-layer-1,#00000005);color:var(--dsw-alias-label-primary,#1f2328);cursor:pointer;border-radius:6px;padding:3px 12px;font-size:12px}.p-2YEW_actionButton:hover{background:var(--dsw-alias-bg-layer-2,#0000000a)}.p-2YEW_actionButton:disabled{opacity:.5;cursor:not-allowed}.p-2YEW_toolsSection{border:1px solid var(--dsw-alias-border-l1,#00000014);border-radius:8px;flex-direction:column;gap:16px;padding:8px 12px;display:flex}.p-2YEW_toolsToggle{color:var(--dsw-alias-label-secondary,#57606a);cursor:pointer;background:0 0;border:none;align-self:flex-start;align-items:center;gap:6px;padding:0;font-family:inherit;font-size:12px;display:inline-flex}.p-2YEW_toolsToggle:hover{color:var(--dsw-alias-label-primary,#1f2328)}.p-2YEW_toolsToggleGlyph{color:var(--dsw-alias-label-tertiary,#6e7781);flex:none;font-size:10px}";
|
|
7007
7676
|
const tagId$1 = "@shendeguize/dsh-agent-sidecar/detail-view.module.css";
|
|
7008
7677
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
7009
7678
|
const tag = document.createElement("style");
|
|
@@ -7018,11 +7687,13 @@ window.__ModuleLoader__.load({
|
|
|
7018
7687
|
"detailRoot": "p-2YEW_detailRoot",
|
|
7019
7688
|
"switcherBar": "p-2YEW_switcherBar",
|
|
7020
7689
|
"switcherButton": "p-2YEW_switcherButton",
|
|
7021
|
-
"toolsSection": "p-2YEW_toolsSection"
|
|
7690
|
+
"toolsSection": "p-2YEW_toolsSection",
|
|
7691
|
+
"toolsToggle": "p-2YEW_toolsToggle",
|
|
7692
|
+
"toolsToggleGlyph": "p-2YEW_toolsToggleGlyph"
|
|
7022
7693
|
};
|
|
7023
7694
|
//#endregion
|
|
7024
7695
|
//#region \0dsh-css:/Users/jingyu/Workspace/Projects/agent_sidecar/plugin/src/client/inject/overlay.module.css.mjs
|
|
7025
|
-
const css = "._2WYemW_backdrop{z-index:1000;background:var(--dsw-alias-bg-mask-1);justify-content:center;align-items:center;padding:24px;display:flex;position:fixed;inset:0}._2WYemW_dialog{border-radius:12px;width:min(560px,100%);max-height:min(85vh,720px);overflow:auto}";
|
|
7696
|
+
const css = "._2WYemW_backdrop{z-index:1000;background:var(--dsw-alias-bg-mask-1,#00000073);justify-content:center;align-items:center;padding:24px;display:flex;position:fixed;inset:0}._2WYemW_dialog{border-radius:12px;width:min(560px,100%);max-height:min(85vh,720px);overflow:auto}";
|
|
7026
7697
|
const tagId = "@shendeguize/dsh-agent-sidecar/overlay.module.css";
|
|
7027
7698
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
7028
7699
|
const tag = document.createElement("style");
|
|
@@ -7081,6 +7752,7 @@ window.__ModuleLoader__.load({
|
|
|
7081
7752
|
const [analysisStore] = (0, react.useState)(() => integration.createAnalysisStore());
|
|
7082
7753
|
const [injectOpen, setInjectOpen] = (0, react.useState)(false);
|
|
7083
7754
|
const [analysisOpen, setAnalysisOpen] = (0, react.useState)(false);
|
|
7755
|
+
const [toolsOpen, setToolsOpen] = (0, react.useState)(false);
|
|
7084
7756
|
(0, react.useEffect)(() => {
|
|
7085
7757
|
detailStore.open();
|
|
7086
7758
|
const unsubscribe = controller.subscribe(() => {
|
|
@@ -7109,6 +7781,18 @@ window.__ModuleLoader__.load({
|
|
|
7109
7781
|
setInjectOpen(false);
|
|
7110
7782
|
};
|
|
7111
7783
|
const title = detail.header.title.trim();
|
|
7784
|
+
const injectActions = injectIntegration === void 0 ? void 0 : {
|
|
7785
|
+
onPrepare: injectIntegration.actions.onPrepare,
|
|
7786
|
+
onExecute: async (req) => {
|
|
7787
|
+
const result = await injectIntegration.actions.onExecute(req);
|
|
7788
|
+
if (isDeliveredResult(result)) detailStore.refreshNewest();
|
|
7789
|
+
return result;
|
|
7790
|
+
}
|
|
7791
|
+
};
|
|
7792
|
+
const observeReaction = () => {
|
|
7793
|
+
if (!detailStore.getState().listening) detailStore.toggleListen();
|
|
7794
|
+
closeInject();
|
|
7795
|
+
};
|
|
7112
7796
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7113
7797
|
className: detail_view_module_css_default["detailRoot"],
|
|
7114
7798
|
"data-testid": "agent-sidecar-detail-view",
|
|
@@ -7135,6 +7819,53 @@ window.__ModuleLoader__.load({
|
|
|
7135
7819
|
children: t("detail.actions.analyze")
|
|
7136
7820
|
})]
|
|
7137
7821
|
}),
|
|
7822
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7823
|
+
className: detail_view_module_css_default["toolsSection"],
|
|
7824
|
+
"data-testid": "agent-sidecar-detail-tools",
|
|
7825
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7826
|
+
type: "button",
|
|
7827
|
+
className: detail_view_module_css_default["toolsToggle"],
|
|
7828
|
+
"aria-expanded": toolsOpen,
|
|
7829
|
+
onClick: () => {
|
|
7830
|
+
setToolsOpen((open) => !open);
|
|
7831
|
+
},
|
|
7832
|
+
children: [
|
|
7833
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7834
|
+
className: detail_view_module_css_default["toolsToggleGlyph"],
|
|
7835
|
+
"aria-hidden": true,
|
|
7836
|
+
children: toolsOpen ? "▾" : "▸"
|
|
7837
|
+
}),
|
|
7838
|
+
t("detail.tools.title"),
|
|
7839
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7840
|
+
className: detail_view_module_css_default["toolsToggleGlyph"],
|
|
7841
|
+
children: toolsOpen ? t("detail.tools.hide") : t("detail.tools.show")
|
|
7842
|
+
})
|
|
7843
|
+
]
|
|
7844
|
+
}), toolsOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LineageTree, {
|
|
7845
|
+
trace: detail.lineage.trace,
|
|
7846
|
+
available: detail.lineage.available,
|
|
7847
|
+
reason: detail.lineage.reason,
|
|
7848
|
+
detail: detail.lineage.detail,
|
|
7849
|
+
currentSessionId: sessionId,
|
|
7850
|
+
onSelectSession: props.onSelectSession,
|
|
7851
|
+
loading: detail.lineage.loading,
|
|
7852
|
+
error: detail.lineage.error
|
|
7853
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchPanel, {
|
|
7854
|
+
query: search.query,
|
|
7855
|
+
project: search.project,
|
|
7856
|
+
mode: search.mode,
|
|
7857
|
+
items: search.items,
|
|
7858
|
+
loading: search.loading,
|
|
7859
|
+
error: search.error,
|
|
7860
|
+
onQueryChange: (query) => {
|
|
7861
|
+
searchStore.setQuery(query);
|
|
7862
|
+
},
|
|
7863
|
+
onSubmit: () => {
|
|
7864
|
+
searchStore.submit();
|
|
7865
|
+
},
|
|
7866
|
+
onSelectSession: props.onSelectSession
|
|
7867
|
+
})] })]
|
|
7868
|
+
}),
|
|
7138
7869
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionDetail, {
|
|
7139
7870
|
sessionId,
|
|
7140
7871
|
header: detail.header,
|
|
@@ -7143,12 +7874,16 @@ window.__ModuleLoader__.load({
|
|
|
7143
7874
|
error: detail.error,
|
|
7144
7875
|
hasMore: detail.hasMore,
|
|
7145
7876
|
listening: detail.listening,
|
|
7877
|
+
refreshing: detail.refreshing,
|
|
7146
7878
|
onLoadMore: () => {
|
|
7147
7879
|
detailStore.loadMore();
|
|
7148
7880
|
},
|
|
7149
7881
|
onToggleListen: () => {
|
|
7150
7882
|
detailStore.toggleListen();
|
|
7151
7883
|
},
|
|
7884
|
+
onRefresh: () => {
|
|
7885
|
+
detailStore.refreshNewest();
|
|
7886
|
+
},
|
|
7152
7887
|
onClose: props.onClose
|
|
7153
7888
|
}),
|
|
7154
7889
|
analysisOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnalysisPanel, {
|
|
@@ -7170,35 +7905,7 @@ window.__ModuleLoader__.load({
|
|
|
7170
7905
|
setAnalysisOpen(false);
|
|
7171
7906
|
}
|
|
7172
7907
|
}),
|
|
7173
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
7174
|
-
className: detail_view_module_css_default["toolsSection"],
|
|
7175
|
-
"data-testid": "agent-sidecar-detail-tools",
|
|
7176
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LineageTree, {
|
|
7177
|
-
trace: detail.lineage.trace,
|
|
7178
|
-
available: detail.lineage.available,
|
|
7179
|
-
reason: detail.lineage.reason,
|
|
7180
|
-
detail: detail.lineage.detail,
|
|
7181
|
-
currentSessionId: sessionId,
|
|
7182
|
-
onSelectSession: props.onSelectSession,
|
|
7183
|
-
loading: detail.lineage.loading,
|
|
7184
|
-
error: detail.lineage.error
|
|
7185
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchPanel, {
|
|
7186
|
-
query: search.query,
|
|
7187
|
-
project: search.project,
|
|
7188
|
-
mode: search.mode,
|
|
7189
|
-
items: search.items,
|
|
7190
|
-
loading: search.loading,
|
|
7191
|
-
error: search.error,
|
|
7192
|
-
onQueryChange: (query) => {
|
|
7193
|
-
searchStore.setQuery(query);
|
|
7194
|
-
},
|
|
7195
|
-
onSubmit: () => {
|
|
7196
|
-
searchStore.submit();
|
|
7197
|
-
},
|
|
7198
|
-
onSelectSession: props.onSelectSession
|
|
7199
|
-
})]
|
|
7200
|
-
}),
|
|
7201
|
-
injectIntegration !== void 0 && injectOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7908
|
+
injectIntegration !== void 0 && injectActions !== void 0 && injectOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7202
7909
|
className: overlay_module_css_default["backdrop"],
|
|
7203
7910
|
role: "presentation",
|
|
7204
7911
|
onClick: closeInject,
|
|
@@ -7217,9 +7924,10 @@ window.__ModuleLoader__.load({
|
|
|
7217
7924
|
...title !== "" ? { title } : {}
|
|
7218
7925
|
},
|
|
7219
7926
|
defaultMode: injectIntegration.getDefaultMode(),
|
|
7220
|
-
onPrepare:
|
|
7221
|
-
onExecute:
|
|
7222
|
-
onClose: closeInject
|
|
7927
|
+
onPrepare: injectActions.onPrepare,
|
|
7928
|
+
onExecute: injectActions.onExecute,
|
|
7929
|
+
onClose: closeInject,
|
|
7930
|
+
onObserve: observeReaction
|
|
7223
7931
|
})
|
|
7224
7932
|
})
|
|
7225
7933
|
})
|
|
@@ -7450,9 +8158,7 @@ window.__ModuleLoader__.load({
|
|
|
7450
8158
|
onFiltersChange: (next) => {
|
|
7451
8159
|
controller.setFilters(next);
|
|
7452
8160
|
},
|
|
7453
|
-
onRefresh: () =>
|
|
7454
|
-
controller.refresh();
|
|
7455
|
-
},
|
|
8161
|
+
onRefresh: () => controller.refresh(),
|
|
7456
8162
|
onSelectSession: openDetail
|
|
7457
8163
|
})] });
|
|
7458
8164
|
};
|