dsh-conversation-overview 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -6
- package/client.js +704 -90
- package/cordis.patch.yml +4 -0
- package/index.js +5 -1
- package/package.json +3 -2
package/client.js
CHANGED
|
@@ -18,8 +18,10 @@ window.__ModuleLoader__.load({
|
|
|
18
18
|
const OVERLAY_KEY = "dsh.conversationOverview.config";
|
|
19
19
|
const ROW_ID = "conversation-overview";
|
|
20
20
|
const ROW_KEY = "dsh-conversation-overview#conversation-overview";
|
|
21
|
+
const FEEDBACK_URL = "https://github.com/LingF/dsh-conversation-overview/issues";
|
|
21
22
|
const PLACEMENTS = ["conversation", "sidebar", "both"];
|
|
22
|
-
const
|
|
23
|
+
const TIME_MODES = ["clock", "elapsed"];
|
|
24
|
+
const CONFIG_FIELDS = ["showProgress", "showArtifacts", "showReferences", "showStats", "timeMode", "placement", "sectionFullHeight", "todoPrompt", "todoPromptTemplate", "quickNav", "quickNavLink", "vikingPreview", "vikingUrl", "vikingAccount", "vikingUser"];
|
|
23
25
|
const VIKING_TAB_ID = "dsh-conversation-overview/viking";
|
|
24
26
|
const VIKING_TAB_KIND = "viking-memory";
|
|
25
27
|
const VIKING_ADDRESS_PREFIX = "dsh-resource://viking-memory/";
|
|
@@ -46,7 +48,18 @@ window.__ModuleLoader__.load({
|
|
|
46
48
|
"progress.empty": "生成任务/待办后将在此展示进展",
|
|
47
49
|
"progress.emptyTurn": "本轮未创建任务计划",
|
|
48
50
|
"progress.recentSteps": "本轮关键步骤(最近 6 条)",
|
|
49
|
-
"progress.
|
|
51
|
+
"progress.allSteps": "本轮关键步骤(全部)",
|
|
52
|
+
"progress.showEarlierSteps": "显示更早的步骤",
|
|
53
|
+
"progress.moreStepsCount": "还有 {n} 条",
|
|
54
|
+
"progress.hideEarlierSteps": "收起早期步骤",
|
|
55
|
+
"team.phase.provisioning": "创建中",
|
|
56
|
+
"team.phase.active": "运行中",
|
|
57
|
+
"team.phase.inactive": "空闲",
|
|
58
|
+
"team.phase.failed": "失败",
|
|
59
|
+
"team.blocked": "阻塞 {n}",
|
|
60
|
+
"progress.expandTeam": "展开团队详情",
|
|
61
|
+
"progress.collapseTeam": "收起团队详情",
|
|
62
|
+
"progress.todoHint": "双击将该步骤的重执行提示词填入对话框",
|
|
50
63
|
"progress.todoInserted": "已填入对话框",
|
|
51
64
|
"progress.todoCopied": "未找到对话框,提示词已复制到剪贴板",
|
|
52
65
|
"progress.todoFailed": "填入失败,请重试",
|
|
@@ -61,6 +74,12 @@ window.__ModuleLoader__.load({
|
|
|
61
74
|
"settings.showProgress": "进展(任务与待办)",
|
|
62
75
|
"settings.showArtifacts": "产物(本次对话产出)",
|
|
63
76
|
"settings.showReferences": "引用(上下文、记忆等)",
|
|
77
|
+
"settings.showStats": "统计(用时、执行次数等)",
|
|
78
|
+
"settings.timeMode": "时间显示",
|
|
79
|
+
"settings.timeMode.clock": "时间点",
|
|
80
|
+
"settings.timeMode.elapsed": "本轮计时(从 0s 起)",
|
|
81
|
+
"settings.feedbackGroup": "反馈",
|
|
82
|
+
"settings.feedback": "问题反馈(GitHub Issues)",
|
|
64
83
|
"settings.fullHeightRow": "分区完整展示(不限高)",
|
|
65
84
|
"settings.fullHeightNote": "关闭时进展/产物/引用各自限制最大高度,超出部分在分区内滚动。",
|
|
66
85
|
"settings.layout": "布局",
|
|
@@ -78,6 +97,11 @@ window.__ModuleLoader__.load({
|
|
|
78
97
|
"settings.quickNavLinkRow": "导航联动概览",
|
|
79
98
|
"settings.quickNavNote": "对话区右侧的轮次刻度条:悬停预览该轮问答,点击跳转;开启后窄窗口下也保持显示,刻度悬停可见轮次号。",
|
|
80
99
|
"settings.quickNavLinkNote": "点击导航刻度跳转对话时,已打开的概览同步切换到对应轮次。",
|
|
100
|
+
"settings.interaction": "交互",
|
|
101
|
+
"settings.todoPromptRow": "双击待办生成提示词",
|
|
102
|
+
"settings.todoPromptTemplate": "提示词模板",
|
|
103
|
+
"settings.todoPromptNote": "双击进展中的待办行,把重执行提示词填入输入框。",
|
|
104
|
+
"settings.todoPromptTemplateNote": "模板留空则使用内置提示词;支持占位符 {index}(步骤序号)与 {content}(步骤内容)。",
|
|
81
105
|
"settings.placementRow": "概览显示位置",
|
|
82
106
|
"settings.viking": "OpenViking 记忆预览",
|
|
83
107
|
"settings.vikingEnable": "启用记忆预览",
|
|
@@ -95,12 +119,12 @@ window.__ModuleLoader__.load({
|
|
|
95
119
|
"viking.dirEmpty": "此目录为空",
|
|
96
120
|
"viking.field.scenarios": "使用场景",
|
|
97
121
|
"viking.field.keywords": "关键词",
|
|
122
|
+
"viking.field.source": "来源",
|
|
98
123
|
"reference.sort.time": "时间",
|
|
99
|
-
"reference.sort.kind": "类型",
|
|
100
124
|
"reference.sort.count": "次数",
|
|
101
125
|
"reference.sort.time.title": "按最近引用排序",
|
|
102
|
-
"reference.sort.kind.title": "按类型分组排序(记忆、文件、技能、会话、上下文)",
|
|
103
126
|
"reference.sort.count.title": "按引用次数排序",
|
|
127
|
+
"reference.sort.cancel": "点击取消排序",
|
|
104
128
|
"reference.previewHint": "点击预览注入的内容快照",
|
|
105
129
|
"scope.label": "概览范围",
|
|
106
130
|
"scope.latest": "最新一轮",
|
|
@@ -178,11 +202,22 @@ window.__ModuleLoader__.load({
|
|
|
178
202
|
"progress.empty": "Progress appears here once tasks or todos are generated",
|
|
179
203
|
"progress.emptyTurn": "No task plan was created in this turn",
|
|
180
204
|
"progress.recentSteps": "Key steps in this turn (latest 6)",
|
|
181
|
-
"progress.
|
|
205
|
+
"progress.allSteps": "Key steps in this turn (all)",
|
|
206
|
+
"progress.showEarlierSteps": "Show earlier steps",
|
|
207
|
+
"progress.moreStepsCount": "{n} more steps",
|
|
208
|
+
"progress.hideEarlierSteps": "Hide earlier steps",
|
|
209
|
+
"progress.expandTeam": "Expand team details",
|
|
210
|
+
"progress.collapseTeam": "Collapse team details",
|
|
211
|
+
"progress.todoHint": "Double-click to fill the composer with a re-run prompt for this step",
|
|
182
212
|
"progress.todoInserted": "Added to the composer",
|
|
183
213
|
"progress.todoCopied": "Composer not found — prompt copied to the clipboard",
|
|
184
214
|
"progress.todoFailed": "Could not fill the composer, try again",
|
|
185
215
|
"progress.waiting": "Waiting for confirmation",
|
|
216
|
+
"team.phase.provisioning": "Provisioning",
|
|
217
|
+
"team.phase.active": "Active",
|
|
218
|
+
"team.phase.inactive": "Inactive",
|
|
219
|
+
"team.phase.failed": "Failed",
|
|
220
|
+
"team.blocked": "Blocked {n}",
|
|
186
221
|
"progress.counts": "{completed} of {total} completed",
|
|
187
222
|
"artifacts.empty": "Files produced in this conversation appear here",
|
|
188
223
|
"artifacts.viewAll": "Review all changes",
|
|
@@ -193,6 +228,12 @@ window.__ModuleLoader__.load({
|
|
|
193
228
|
"settings.showProgress": "Progress (tasks and todos)",
|
|
194
229
|
"settings.showArtifacts": "Artifacts (files produced)",
|
|
195
230
|
"settings.showReferences": "References (context, memories)",
|
|
231
|
+
"settings.showStats": "Stats (duration, tool calls, …)",
|
|
232
|
+
"settings.timeMode": "Time display",
|
|
233
|
+
"settings.timeMode.clock": "Clock time",
|
|
234
|
+
"settings.timeMode.elapsed": "Turn elapsed (from 0s)",
|
|
235
|
+
"settings.feedbackGroup": "Feedback",
|
|
236
|
+
"settings.feedback": "Report an issue (GitHub Issues)",
|
|
196
237
|
"settings.fullHeightRow": "Show sections in full (no max height)",
|
|
197
238
|
"settings.fullHeightNote": "When off, Progress/Artifacts/References cap their height and scroll inside each section.",
|
|
198
239
|
"settings.layout": "Layout",
|
|
@@ -210,6 +251,11 @@ window.__ModuleLoader__.load({
|
|
|
210
251
|
"settings.quickNavLinkRow": "Link rail with overview",
|
|
211
252
|
"settings.quickNavNote": "The turn rail on the right edge of the chat: hover for a turn preview, click to jump. When enabled it stays visible in narrow windows too, and ticks show their turn number on hover.",
|
|
212
253
|
"settings.quickNavLinkNote": "When you click a rail tick to jump, an open overview switches to that turn as well.",
|
|
254
|
+
"settings.interaction": "Interaction",
|
|
255
|
+
"settings.todoPromptRow": "Double-click todo prompt",
|
|
256
|
+
"settings.todoPromptTemplate": "Prompt template",
|
|
257
|
+
"settings.todoPromptNote": "Double-click a todo row in Progress to fill the rerun prompt into the composer.",
|
|
258
|
+
"settings.todoPromptTemplateNote": "Leave empty for the built-in prompt; placeholders: {index} (step number), {content} (step text).",
|
|
213
259
|
"settings.placementRow": "Show the overview in",
|
|
214
260
|
"settings.viking": "OpenViking memory preview",
|
|
215
261
|
"settings.vikingEnable": "Enable memory preview",
|
|
@@ -227,12 +273,12 @@ window.__ModuleLoader__.load({
|
|
|
227
273
|
"viking.dirEmpty": "This directory is empty",
|
|
228
274
|
"viking.field.scenarios": "Usage scenarios",
|
|
229
275
|
"viking.field.keywords": "Keywords",
|
|
276
|
+
"viking.field.source": "Source",
|
|
230
277
|
"reference.sort.time": "Time",
|
|
231
|
-
"reference.sort.kind": "Kind",
|
|
232
278
|
"reference.sort.count": "Count",
|
|
233
279
|
"reference.sort.time.title": "Sort by most recently referenced",
|
|
234
|
-
"reference.sort.kind.title": "Group by kind (memories, files, skills, sessions, context)",
|
|
235
280
|
"reference.sort.count.title": "Sort by reference count",
|
|
281
|
+
"reference.sort.cancel": "Click again to clear",
|
|
236
282
|
"reference.previewHint": "Click to preview the injected content snapshot",
|
|
237
283
|
"scope.label": "Scope",
|
|
238
284
|
"scope.latest": "Latest turn",
|
|
@@ -322,8 +368,12 @@ window.__ModuleLoader__.load({
|
|
|
322
368
|
showProgress: flag(source.showProgress),
|
|
323
369
|
showArtifacts: flag(source.showArtifacts),
|
|
324
370
|
showReferences: flag(source.showReferences),
|
|
371
|
+
showStats: flag(source.showStats),
|
|
372
|
+
timeMode: TIME_MODES.indexOf(source.timeMode) !== -1 ? source.timeMode : "clock",
|
|
325
373
|
placement: PLACEMENTS.indexOf(source.placement) !== -1 ? source.placement : "both",
|
|
326
374
|
sectionFullHeight: source.sectionFullHeight === true,
|
|
375
|
+
todoPrompt: typeof source.todoPrompt === "boolean" ? source.todoPrompt : true,
|
|
376
|
+
todoPromptTemplate: typeof source.todoPromptTemplate === "string" ? source.todoPromptTemplate : "",
|
|
327
377
|
quickNav: typeof source.quickNav === "boolean" ? source.quickNav : true,
|
|
328
378
|
quickNavLink: typeof source.quickNavLink === "boolean" ? source.quickNavLink : true,
|
|
329
379
|
vikingPreview: typeof source.vikingPreview === "boolean" ? source.vikingPreview : true,
|
|
@@ -452,8 +502,18 @@ window.__ModuleLoader__.load({
|
|
|
452
502
|
return String(value).padStart(2, "0");
|
|
453
503
|
}
|
|
454
504
|
|
|
455
|
-
|
|
505
|
+
/** Compact "since turn start" reading: 0s → 59s → 3m05s → 1h02m. */
|
|
506
|
+
function formatElapsed(ms) {
|
|
507
|
+
const sec = Math.max(0, Math.floor(ms / 1000));
|
|
508
|
+
if (sec < 60) return `${sec}s`;
|
|
509
|
+
const min = Math.floor(sec / 60);
|
|
510
|
+
if (min < 60) return `${min}m${pad2(sec % 60)}s`;
|
|
511
|
+
return `${Math.floor(min / 60)}h${pad2(min % 60)}m`;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function formatTime(time, mode, base) {
|
|
456
515
|
if (typeof time !== "number" || !Number.isFinite(time) || time <= 0) return "";
|
|
516
|
+
if (mode === "elapsed" && typeof base === "number" && base > 0 && time >= base) return formatElapsed(time - base);
|
|
457
517
|
const date = new Date(time);
|
|
458
518
|
const now = new Date();
|
|
459
519
|
const hm = `${pad2(date.getHours())}:${pad2(date.getMinutes())}`;
|
|
@@ -1106,18 +1166,28 @@ window.__ModuleLoader__.load({
|
|
|
1106
1166
|
pending: new Map(),
|
|
1107
1167
|
changes: [],
|
|
1108
1168
|
steps: [],
|
|
1169
|
+
anchors: [],
|
|
1109
1170
|
stepByCall: new Map(),
|
|
1110
1171
|
byTurn: new Map(),
|
|
1172
|
+
members: new Map(),
|
|
1173
|
+
teamTasks: new Map(),
|
|
1174
|
+
subagents: new Map(),
|
|
1111
1175
|
stats: createStats(),
|
|
1112
1176
|
changed: false,
|
|
1113
1177
|
};
|
|
1114
1178
|
}
|
|
1115
1179
|
|
|
1180
|
+
/** Upsert one team/member, team/task or subagent/catalog record; first observation time is kept. */
|
|
1181
|
+
function upsertTeamRecord(map, key, record) {
|
|
1182
|
+
const existing = map.get(key);
|
|
1183
|
+
map.set(key, existing === undefined ? { ...record, firstTime: record.time } : { ...existing, ...record, firstTime: existing.firstTime });
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1116
1186
|
function createTurnBucket() {
|
|
1117
|
-
return { artifacts: new Map(), refs: new Map(), refOrder: [], changes: [], steps: [], stats: createStats() };
|
|
1187
|
+
return { artifacts: new Map(), refs: new Map(), refOrder: [], changes: [], steps: [], anchors: [], members: new Map(), teamTasks: new Map(), subagents: new Map(), stats: createStats() };
|
|
1118
1188
|
}
|
|
1119
1189
|
|
|
1120
|
-
const EMPTY_BUCKET = Object.freeze({ artifacts: new Map(), refs: new Map(), refOrder: [], changes: [], steps: [], stats: Object.freeze(createStats()) });
|
|
1190
|
+
const EMPTY_BUCKET = Object.freeze({ artifacts: new Map(), refs: new Map(), refOrder: [], changes: [], steps: [], anchors: [], members: new Map(), teamTasks: new Map(), subagents: new Map(), stats: Object.freeze(createStats()) });
|
|
1121
1191
|
|
|
1122
1192
|
function turnBucket(folder, turn) {
|
|
1123
1193
|
let bucket = folder.byTurn.get(turn);
|
|
@@ -1182,23 +1252,27 @@ window.__ModuleLoader__.load({
|
|
|
1182
1252
|
for (const listener of [...navScopeStore.listeners]) listener();
|
|
1183
1253
|
}
|
|
1184
1254
|
|
|
1185
|
-
/* Re-run prompt for one todo step
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1255
|
+
/* Re-run prompt for one todo step. Placeholder {index}/{content} are substituted;
|
|
1256
|
+
an empty template falls back to the built-in default. */
|
|
1257
|
+
const DEFAULT_TODO_TEMPLATE = [
|
|
1258
|
+
"请查看当前任务的完整 TODO 计划,重新执行以下指定步骤:",
|
|
1259
|
+
"",
|
|
1260
|
+
"**目标步骤:{index}. {content}**",
|
|
1261
|
+
"",
|
|
1262
|
+
"要求:",
|
|
1263
|
+
"1. 查看完整计划及相关上下文,明确目标步骤的执行要求和前置依赖。",
|
|
1264
|
+
"2. 无论该步骤当前状态如何,都必须重新检查并实际执行,不得直接跳过。",
|
|
1265
|
+
"3. 仅处理目标步骤所需内容,避免重复执行其他步骤或修改无关内容。",
|
|
1266
|
+
"4. 执行必要的测试或验证,如实反馈结果。",
|
|
1267
|
+
"5. 更新目标步骤的 TODO 状态,保持其他步骤状态不变。",
|
|
1268
|
+
"6. 汇报执行结果、修改内容及未解决的问题。",
|
|
1269
|
+
"",
|
|
1270
|
+
"**请直接执行,不要只提供方案。**",
|
|
1271
|
+
].join("\n");
|
|
1272
|
+
|
|
1273
|
+
function rerunPrompt(number, content, template) {
|
|
1274
|
+
const source = typeof template === "string" && template.trim() !== "" ? template : DEFAULT_TODO_TEMPLATE;
|
|
1275
|
+
return source.split("{index}").join(String(number)).split("{content}").join(String(content));
|
|
1202
1276
|
}
|
|
1203
1277
|
|
|
1204
1278
|
/** The visible Lexical chat composer root (contenteditable textbox), or null. */
|
|
@@ -1370,9 +1444,18 @@ window.__ModuleLoader__.load({
|
|
|
1370
1444
|
|
|
1371
1445
|
/* Key-step stream: every tool call is one step; the result marks it done. Shown by
|
|
1372
1446
|
the Progress section when a turn has no todo plan of its own. */
|
|
1373
|
-
const STEP_CAP =
|
|
1447
|
+
const STEP_CAP = 200;
|
|
1374
1448
|
|
|
1375
1449
|
/** One-line human target for a tool call: file, command, query, url, name… */
|
|
1450
|
+
/** Team-record key a tool call creates: teammate name / task subject / subagent label. */
|
|
1451
|
+
function teamStepKey(name, args) {
|
|
1452
|
+
if (!isRecord(args)) return undefined;
|
|
1453
|
+
if (name === "spawn_teammate" && typeof args.name === "string" && args.name !== "") return args.name;
|
|
1454
|
+
if (name === "team_task_create" && typeof args.subject === "string" && args.subject !== "") return args.subject;
|
|
1455
|
+
if ((name === "subagent" || name === "subagent_fork") && typeof args.description === "string" && args.description !== "") return args.description;
|
|
1456
|
+
return undefined;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1376
1459
|
function stepDetail(name, args) {
|
|
1377
1460
|
if (!isRecord(args)) return "";
|
|
1378
1461
|
const keys = ["file_path", "path", "command", "pattern", "url", "query", "prompt", "subject", "description", "name", "uri", "target", "id"];
|
|
@@ -1405,7 +1488,16 @@ window.__ModuleLoader__.load({
|
|
|
1405
1488
|
return text.length > 4000 ? text.slice(0, 4000) : text;
|
|
1406
1489
|
}
|
|
1407
1490
|
|
|
1491
|
+
const ANCHOR_CAP = 8;
|
|
1408
1492
|
function addStep(folder, bucket, step) {
|
|
1493
|
+
if (typeof step.teamKey === "string") {
|
|
1494
|
+
folder.anchors.push(step);
|
|
1495
|
+
if (folder.anchors.length > ANCHOR_CAP) folder.anchors.shift();
|
|
1496
|
+
if (isRecord(bucket) && Array.isArray(bucket.anchors)) {
|
|
1497
|
+
bucket.anchors.push(step);
|
|
1498
|
+
if (bucket.anchors.length > ANCHOR_CAP) bucket.anchors.shift();
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1409
1501
|
folder.steps.push(step);
|
|
1410
1502
|
if (folder.steps.length > STEP_CAP) {
|
|
1411
1503
|
const dropped = folder.steps.shift();
|
|
@@ -1465,6 +1557,57 @@ window.__ModuleLoader__.load({
|
|
|
1465
1557
|
touch(folder);
|
|
1466
1558
|
return;
|
|
1467
1559
|
}
|
|
1560
|
+
case "team/member": {
|
|
1561
|
+
if (!isRecord(data) || !isRecord(data.member)) return;
|
|
1562
|
+
const member = data.member;
|
|
1563
|
+
if (typeof member.id !== "string" || member.id === "" || typeof member.name !== "string") return;
|
|
1564
|
+
const memberRecord = {
|
|
1565
|
+
id: member.id,
|
|
1566
|
+
name: member.name,
|
|
1567
|
+
description: typeof member.description === "string" ? member.description : "",
|
|
1568
|
+
phase: typeof member.phase === "string" ? member.phase : "",
|
|
1569
|
+
seq: event.seq,
|
|
1570
|
+
time: typeof event.time === "number" ? event.time : 0,
|
|
1571
|
+
};
|
|
1572
|
+
upsertTeamRecord(folder.members, member.id, memberRecord);
|
|
1573
|
+
upsertTeamRecord(turnBucket(folder, folder.currentTurn).members, member.id, memberRecord);
|
|
1574
|
+
touch(folder);
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1577
|
+
case "team/task": {
|
|
1578
|
+
if (!isRecord(data) || !isRecord(data.task)) return;
|
|
1579
|
+
const task = data.task;
|
|
1580
|
+
if (typeof task.id !== "string" || task.id === "" || typeof task.subject !== "string") return;
|
|
1581
|
+
const taskRecord = {
|
|
1582
|
+
id: task.id,
|
|
1583
|
+
subject: task.subject,
|
|
1584
|
+
description: typeof task.description === "string" ? task.description : "",
|
|
1585
|
+
status: typeof task.status === "string" ? task.status : "",
|
|
1586
|
+
ownerId: typeof task.ownerId === "string" ? task.ownerId : undefined,
|
|
1587
|
+
blockedBy: Array.isArray(task.blockedBy) ? task.blockedBy.filter((item) => typeof item === "string") : [],
|
|
1588
|
+
revision: typeof task.revision === "number" ? task.revision : 0,
|
|
1589
|
+
seq: event.seq,
|
|
1590
|
+
time: typeof event.time === "number" ? event.time : 0,
|
|
1591
|
+
};
|
|
1592
|
+
upsertTeamRecord(folder.teamTasks, task.id, taskRecord);
|
|
1593
|
+
upsertTeamRecord(turnBucket(folder, folder.currentTurn).teamTasks, task.id, taskRecord);
|
|
1594
|
+
touch(folder);
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
case "subagent/catalog": {
|
|
1598
|
+
if (!isRecord(data) || typeof data.childId !== "string" || data.childId === "") return;
|
|
1599
|
+
const subRecord = {
|
|
1600
|
+
childId: data.childId,
|
|
1601
|
+
label: typeof data.label === "string" ? data.label : "",
|
|
1602
|
+
mode: typeof data.mode === "string" ? data.mode : "",
|
|
1603
|
+
seq: event.seq,
|
|
1604
|
+
time: typeof event.time === "number" ? event.time : 0,
|
|
1605
|
+
};
|
|
1606
|
+
upsertTeamRecord(folder.subagents, data.childId, subRecord);
|
|
1607
|
+
upsertTeamRecord(turnBucket(folder, folder.currentTurn).subagents, data.childId, subRecord);
|
|
1608
|
+
touch(folder);
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1468
1611
|
case "workspace/changes": {
|
|
1469
1612
|
if (!isRecord(data) || typeof data.turn !== "number" || !Number.isSafeInteger(data.turn) || data.turn < 1) return;
|
|
1470
1613
|
if (typeof event.seq !== "number") return;
|
|
@@ -1495,7 +1638,7 @@ window.__ModuleLoader__.load({
|
|
|
1495
1638
|
const name = data.name;
|
|
1496
1639
|
const args = parseArgs(data.arguments);
|
|
1497
1640
|
const callBucket = turnBucket(folder, folder.currentTurn);
|
|
1498
|
-
addStep(folder, callBucket, { callId, name, detail: stepDetail(name, args), full: stepFull(name, args), done: false, seq: event.seq, time: event.time });
|
|
1641
|
+
addStep(folder, callBucket, { callId, name, detail: stepDetail(name, args), full: stepFull(name, args), teamKey: teamStepKey(name, args), done: false, seq: event.seq, time: event.time });
|
|
1499
1642
|
folder.stats.toolCalls += 1;
|
|
1500
1643
|
callBucket.stats.toolCalls += 1;
|
|
1501
1644
|
if (name === "bash") {
|
|
@@ -1715,13 +1858,32 @@ window.__ModuleLoader__.load({
|
|
|
1715
1858
|
errors: st.errors,
|
|
1716
1859
|
files: st.files.size,
|
|
1717
1860
|
};
|
|
1861
|
+
/* Agent-team projections: members, shared board tasks, and non-team subagents. */
|
|
1862
|
+
const byFirstTime = (left, right) => (left.firstTime || left.time || 0) - (right.firstTime || right.time || 0) || (left.seq || 0) - (right.seq || 0);
|
|
1863
|
+
const mapValues = (map) => (map instanceof Map ? [...map.values()] : []);
|
|
1864
|
+
const members = mapValues(source.members).sort(byFirstTime);
|
|
1865
|
+
const subagents = mapValues(source.subagents)
|
|
1866
|
+
.filter((sub) => !(source.members instanceof Map && source.members.has(sub.childId)))
|
|
1867
|
+
.sort(byFirstTime);
|
|
1868
|
+
const team = { members, tasks: mapValues(source.teamTasks).sort(byFirstTime), subagents };
|
|
1869
|
+
/* team anchor steps (spawn/task-create/subagent) stay pinned above the recent window
|
|
1870
|
+
so member/task rows can nest under their creating step */
|
|
1871
|
+
const allSteps = Array.isArray(source.steps) ? source.steps : [];
|
|
1872
|
+
const recentSteps = allSteps.slice(-6);
|
|
1873
|
+
const anchorCandidates = [...(Array.isArray(source.anchors) ? source.anchors : []), ...allSteps].filter(
|
|
1874
|
+
(step, index, list) => isRecord(step) && typeof step.teamKey === "string" && list.indexOf(step) === index && recentSteps.indexOf(step) === -1
|
|
1875
|
+
);
|
|
1876
|
+
const anchorSteps = anchorCandidates.slice(-6);
|
|
1718
1877
|
return {
|
|
1719
1878
|
todos,
|
|
1720
1879
|
todosTime,
|
|
1721
1880
|
artifacts,
|
|
1722
1881
|
references,
|
|
1723
|
-
steps:
|
|
1882
|
+
steps: [...anchorSteps, ...recentSteps],
|
|
1883
|
+
stepsAll: allSteps.slice(),
|
|
1884
|
+
team,
|
|
1724
1885
|
stats,
|
|
1886
|
+
turnStart: st.start > 0 ? st.start : null,
|
|
1725
1887
|
changeTurns: changeTurns.slice(),
|
|
1726
1888
|
hasMore: windowSnapshot.hasMore === true,
|
|
1727
1889
|
localeRev,
|
|
@@ -1731,7 +1893,7 @@ window.__ModuleLoader__.load({
|
|
|
1731
1893
|
};
|
|
1732
1894
|
}
|
|
1733
1895
|
|
|
1734
|
-
const EMPTY_STATE = { todos: null, todosTime: 0, artifacts: [], references: [], steps: [], stats: { durationMs: 0, toolCalls: 0, commands: 0, errors: 0, files: 0 }, changeTurns: [], hasMore: false, localeRev: 0, turns: [], latestTurn: null, scope: "latest" };
|
|
1896
|
+
const EMPTY_STATE = { todos: null, todosTime: 0, artifacts: [], references: [], steps: [], stepsAll: [], team: { members: [], tasks: [], subagents: [] }, stats: { durationMs: 0, toolCalls: 0, commands: 0, errors: 0, files: 0 }, turnStart: null, changeTurns: [], hasMore: false, localeRev: 0, turns: [], latestTurn: null, scope: "latest" };
|
|
1735
1897
|
|
|
1736
1898
|
function createSummaryStore(binding) {
|
|
1737
1899
|
const source = binding.eventSource;
|
|
@@ -1903,7 +2065,7 @@ window.__ModuleLoader__.load({
|
|
|
1903
2065
|
/* Prefer the jump loader: it pages back until the window covers seq and
|
|
1904
2066
|
survives concurrent loaders. loadOlder() is a no-op while another load
|
|
1905
2067
|
is in flight, so retry until the window actually stops reporting more. */
|
|
1906
|
-
for (let attempt = 0; attempt <
|
|
2068
|
+
for (let attempt = 0; attempt < 200 && !cancelled; attempt += 1) {
|
|
1907
2069
|
const snapshot = binding.eventSource.getSnapshot();
|
|
1908
2070
|
if (!isRecord(snapshot) || snapshot.hasMore !== true) return;
|
|
1909
2071
|
const session = binding.session;
|
|
@@ -2677,6 +2839,14 @@ function FileTypeIcon(props) {
|
|
|
2677
2839
|
return svg(size, className, [h("path", { key: "a", d: "m5.8 3.6 4.4 4.4-4.4 4.4", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round" })]);
|
|
2678
2840
|
}
|
|
2679
2841
|
|
|
2842
|
+
function IconHistory({ size = 16, className }) {
|
|
2843
|
+
return svg(size, className, [
|
|
2844
|
+
h("path", { key: "a", d: "M2.6 8a5.4 5.4 0 1 0 5.4-5.4A5.9 5.9 0 0 0 3.9 4.3L2.6 5.6", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
2845
|
+
h("path", { key: "b", d: "M2.6 2.4v3.2h3.2", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
2846
|
+
h("path", { key: "c", d: "M8 4.9v3.2l2.7 1.5", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
2847
|
+
]);
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2680
2850
|
function IconCompare({ size = 16, className }) {
|
|
2681
2851
|
return svg(size, className, [
|
|
2682
2852
|
h("path", { key: "a", d: "M3 5.4h8.2M9.2 3.4l2 2-2 2", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
@@ -2698,6 +2868,13 @@ function FileTypeIcon(props) {
|
|
|
2698
2868
|
]);
|
|
2699
2869
|
}
|
|
2700
2870
|
|
|
2871
|
+
function IconAgent({ size = 16, className }) {
|
|
2872
|
+
return svg(size, className, [
|
|
2873
|
+
h("circle", { key: "a", cx: 8, cy: 5.4, r: 2.5, stroke: "currentColor" }),
|
|
2874
|
+
h("path", { key: "b", d: "M3.4 13c.7-2.2 2.4-3.4 4.6-3.4S12 10.8 12.6 13", stroke: "currentColor", strokeLinecap: "round" }),
|
|
2875
|
+
]);
|
|
2876
|
+
}
|
|
2877
|
+
|
|
2701
2878
|
function TodoStatusIcon({ status }) {
|
|
2702
2879
|
if (status === "completed") {
|
|
2703
2880
|
return svg(15, undefined, [
|
|
@@ -2732,12 +2909,13 @@ function FileTypeIcon(props) {
|
|
|
2732
2909
|
|
|
2733
2910
|
/* References ordering, shared by both surfaces and persisted per browser. */
|
|
2734
2911
|
const REF_SORT_KEY = "dsh.conversationOverview.refSort";
|
|
2735
|
-
const REF_SORT_MODES = ["time", "
|
|
2912
|
+
const REF_SORT_MODES = ["time", "count"];
|
|
2736
2913
|
const KIND_ORDER = ["memory-file", "memory", "file", "skill", "session", "context"];
|
|
2737
2914
|
|
|
2738
2915
|
function readStoredRefSort() {
|
|
2739
2916
|
try {
|
|
2740
2917
|
const value = globalThis.localStorage ? globalThis.localStorage.getItem(REF_SORT_KEY) : null;
|
|
2918
|
+
if (value === "none") return "";
|
|
2741
2919
|
return REF_SORT_MODES.indexOf(value) !== -1 ? value : "time";
|
|
2742
2920
|
} catch (_unreadable) {
|
|
2743
2921
|
return "time";
|
|
@@ -2747,10 +2925,11 @@ function FileTypeIcon(props) {
|
|
|
2747
2925
|
const refSortStore = { mode: readStoredRefSort(), listeners: new Set() };
|
|
2748
2926
|
|
|
2749
2927
|
function setRefSort(mode) {
|
|
2750
|
-
|
|
2751
|
-
refSortStore.mode
|
|
2928
|
+
const next = mode === "" ? "" : REF_SORT_MODES.indexOf(mode) !== -1 ? mode : undefined;
|
|
2929
|
+
if (next === undefined || refSortStore.mode === next) return;
|
|
2930
|
+
refSortStore.mode = next;
|
|
2752
2931
|
try {
|
|
2753
|
-
if (globalThis.localStorage) globalThis.localStorage.setItem(REF_SORT_KEY,
|
|
2932
|
+
if (globalThis.localStorage) globalThis.localStorage.setItem(REF_SORT_KEY, next === "" ? "none" : next);
|
|
2754
2933
|
} catch (_unwritable) {
|
|
2755
2934
|
/* the choice still applies for this page load */
|
|
2756
2935
|
}
|
|
@@ -2867,7 +3046,6 @@ function FileTypeIcon(props) {
|
|
|
2867
3046
|
/** Incoming order is newest-first; kind/count modes re-rank, breaking ties by recency. */
|
|
2868
3047
|
function sortReferences(refs, mode) {
|
|
2869
3048
|
const byRecency = (left, right) => (right.time || right.seq || 0) - (left.time || left.seq || 0) || (right.seq || 0) - (left.seq || 0);
|
|
2870
|
-
if (mode === "kind") return refs.slice().sort((left, right) => kindRank(left.kind) - kindRank(right.kind) || byRecency(left, right));
|
|
2871
3049
|
if (mode === "count") return refs.slice().sort((left, right) => (right.count || 1) - (left.count || 1) || byRecency(left, right));
|
|
2872
3050
|
return refs;
|
|
2873
3051
|
}
|
|
@@ -2907,6 +3085,14 @@ function FileTypeIcon(props) {
|
|
|
2907
3085
|
completed: "todo.completed",
|
|
2908
3086
|
};
|
|
2909
3087
|
|
|
3088
|
+
/* Agent-team member phases (tooltip text; status is conveyed by icons). */
|
|
3089
|
+
const MEMBER_PHASE_KEYS = {
|
|
3090
|
+
provisioning: "team.phase.provisioning",
|
|
3091
|
+
active: "team.phase.active",
|
|
3092
|
+
inactive: "team.phase.inactive",
|
|
3093
|
+
failed: "team.phase.failed",
|
|
3094
|
+
};
|
|
3095
|
+
|
|
2910
3096
|
const GOAL_PHASE_KEYS = {
|
|
2911
3097
|
active: "goal.active",
|
|
2912
3098
|
paused: "goal.paused",
|
|
@@ -2921,13 +3107,17 @@ function FileTypeIcon(props) {
|
|
|
2921
3107
|
complete: "success",
|
|
2922
3108
|
};
|
|
2923
3109
|
|
|
2924
|
-
function ProgressSection({ summary, goal, t, waiting }) {
|
|
3110
|
+
function ProgressSection({ summary, goal, t, waiting, cfg }) {
|
|
3111
|
+
const fmtTime = (ts) => formatTime(ts, cfg.timeMode, summary.turnStart);
|
|
3112
|
+
const timeTitle = (ts) => (cfg.timeMode === "elapsed" ? formatTime(ts) : undefined);
|
|
2925
3113
|
/* todo → composer prompt flash */
|
|
2926
3114
|
const [promptFlash, setPromptFlash] = React.useState(null);
|
|
2927
3115
|
const flashTimer = React.useRef(undefined);
|
|
2928
3116
|
/* step → copy summary flash */
|
|
2929
3117
|
const [stepFlash, setStepFlash] = React.useState(null);
|
|
2930
3118
|
const copyTimer = React.useRef(undefined);
|
|
3119
|
+
/* per-member expansion overrides for the merged team rows */
|
|
3120
|
+
const [teamExpanded, setTeamExpanded] = React.useState(null);
|
|
2931
3121
|
React.useEffect(
|
|
2932
3122
|
() => () => {
|
|
2933
3123
|
if (flashTimer.current !== undefined) clearTimeout(flashTimer.current);
|
|
@@ -2935,8 +3125,11 @@ function FileTypeIcon(props) {
|
|
|
2935
3125
|
},
|
|
2936
3126
|
[]
|
|
2937
3127
|
);
|
|
3128
|
+
const todoPromptOn = !isRecord(cfg) || cfg.todoPrompt !== false;
|
|
3129
|
+
const todoPromptTemplate = isRecord(cfg) && typeof cfg.todoPromptTemplate === "string" ? cfg.todoPromptTemplate : "";
|
|
2938
3130
|
const onTodoClick = (index, content) => {
|
|
2939
|
-
|
|
3131
|
+
if (!todoPromptOn) return;
|
|
3132
|
+
const result = insertIntoComposer(rerunPrompt(index + 1, content, todoPromptTemplate));
|
|
2940
3133
|
if (flashTimer.current !== undefined) clearTimeout(flashTimer.current);
|
|
2941
3134
|
setPromptFlash({ index, state: result });
|
|
2942
3135
|
flashTimer.current = setTimeout(() => {
|
|
@@ -2972,15 +3165,24 @@ function FileTypeIcon(props) {
|
|
|
2972
3165
|
};
|
|
2973
3166
|
/* steps: newest-first window; keep the evicted bottom row as a fading ghost */
|
|
2974
3167
|
const steps = Array.isArray(summary.steps) ? summary.steps.filter((step) => isRecord(step)) : [];
|
|
3168
|
+
const stepsAll = Array.isArray(summary.stepsAll) ? summary.stepsAll.filter((step) => isRecord(step)) : [];
|
|
2975
3169
|
const [ghost, setGhost] = React.useState(null);
|
|
3170
|
+
const [stepsExpanded, setStepsExpanded] = React.useState(false);
|
|
2976
3171
|
const ghostTimer = React.useRef(undefined);
|
|
2977
3172
|
const prevSteps = React.useRef([]);
|
|
2978
3173
|
const prevScope = React.useRef(summary.scope);
|
|
2979
3174
|
/* enter animation only for rows that arrive while mounted; first paint per scope is silent */
|
|
2980
3175
|
const stepKeyOf = (step, index) => (typeof step.callId === "string" && step.callId !== "" ? `step-${step.callId}` : `step-idx-${index}`);
|
|
2981
3176
|
const seenSteps = React.useRef(null);
|
|
3177
|
+
/* full retained history for the expander: anchors outside the window + the whole bucket */
|
|
3178
|
+
const allStepIds = new Set(stepsAll.map((step) => step.callId).filter((id) => typeof id === "string"));
|
|
3179
|
+
const anchorExtras = steps.filter((step) => typeof step.callId === "string" && !allStepIds.has(step.callId));
|
|
3180
|
+
const expandedSteps = [...anchorExtras, ...stepsAll];
|
|
3181
|
+
const canExpand = expandedSteps.length > steps.length;
|
|
3182
|
+
const stepsOpen = stepsExpanded && canExpand;
|
|
3183
|
+
const displaySteps = stepsOpen ? expandedSteps : steps;
|
|
2982
3184
|
if (seenSteps.current === null || seenSteps.current.scope !== summary.scope) {
|
|
2983
|
-
seenSteps.current = { scope: summary.scope, keys: new Set(
|
|
3185
|
+
seenSteps.current = { scope: summary.scope, keys: new Set(displaySteps.map(stepKeyOf)) };
|
|
2984
3186
|
}
|
|
2985
3187
|
React.useEffect(
|
|
2986
3188
|
() => () => {
|
|
@@ -3006,6 +3208,173 @@ function FileTypeIcon(props) {
|
|
|
3006
3208
|
setGhost(null);
|
|
3007
3209
|
}, 400);
|
|
3008
3210
|
}, [steps]);
|
|
3211
|
+
/* team data + todos drive both the merged list and the step nesting */
|
|
3212
|
+
const team = isRecord(summary.team) ? summary.team : null;
|
|
3213
|
+
const members = team && Array.isArray(team.members) ? team.members.filter((member) => isRecord(member)) : [];
|
|
3214
|
+
const teamTasks = team && Array.isArray(team.tasks) ? team.tasks.filter((task) => isRecord(task)) : [];
|
|
3215
|
+
const subagents = team && Array.isArray(team.subagents) ? team.subagents.filter((sub) => isRecord(sub)) : [];
|
|
3216
|
+
const todos = Array.isArray(summary.todos) ? summary.todos : null;
|
|
3217
|
+
const hasTodos = todos !== null && todos.length > 0;
|
|
3218
|
+
const hasTeam = members.length + teamTasks.length + subagents.length > 0;
|
|
3219
|
+
const showSteps = !hasTodos && steps.length > 0;
|
|
3220
|
+
const flashOf = (key) => (stepFlash !== null && isRecord(stepFlash) && stepFlash.key === key ? stepFlash.state : null);
|
|
3221
|
+
const overrideOf = (key, fallback) => {
|
|
3222
|
+
const stored = teamExpanded !== null && isRecord(teamExpanded) ? teamExpanded[key] : undefined;
|
|
3223
|
+
return typeof stored === "boolean" ? stored : fallback;
|
|
3224
|
+
};
|
|
3225
|
+
const toggleKey = (key, next) => setTeamExpanded({ ...(teamExpanded !== null && isRecord(teamExpanded) ? teamExpanded : {}), [key]: next });
|
|
3226
|
+
const ownedBy = (id) => teamTasks.filter((task) => task.ownerId === id);
|
|
3227
|
+
const taskRow = (task, depth) => {
|
|
3228
|
+
const status = TODO_KEYS[task.status] ? task.status : "pending";
|
|
3229
|
+
const blocked = Array.isArray(task.blockedBy) ? task.blockedBy.length : 0;
|
|
3230
|
+
const flash = flashOf(`teamtask-${task.id}`);
|
|
3231
|
+
const titleParts = [task.subject];
|
|
3232
|
+
if (task.description !== "") titleParts.push(task.description);
|
|
3233
|
+
titleParts.push(blocked > 0 ? `${t(TODO_KEYS[status])} · ${t("team.blocked", { n: blocked })}` : t(TODO_KEYS[status]));
|
|
3234
|
+
return h(
|
|
3235
|
+
"div",
|
|
3236
|
+
{
|
|
3237
|
+
className: "dsov-todo dsov-task",
|
|
3238
|
+
key: `task-${task.id}`,
|
|
3239
|
+
"data-status": status,
|
|
3240
|
+
"data-depth": depth > 0 ? String(depth) : undefined,
|
|
3241
|
+
"data-flash": flash === "ok" ? "ok" : flash === "err" ? "err" : undefined,
|
|
3242
|
+
title: titleParts.join("\n\n"),
|
|
3243
|
+
onClick: () => onStepClick({ callId: `teamtask-${task.id}`, name: task.subject, full: task.subject, seq: task.seq }),
|
|
3244
|
+
},
|
|
3245
|
+
h("span", { className: "dsov-todoIcon", "data-status": status }, h(TodoStatusIcon, { status })),
|
|
3246
|
+
h("span", { className: "dsov-taskSubject" }, task.subject),
|
|
3247
|
+
h("span", { className: "dsov-time", title: timeTitle(task.time) }, fmtTime(task.time))
|
|
3248
|
+
);
|
|
3249
|
+
};
|
|
3250
|
+
const subRow = (sub, depth) => {
|
|
3251
|
+
const flash = flashOf(`subagent-${sub.childId}`);
|
|
3252
|
+
return h(
|
|
3253
|
+
"div",
|
|
3254
|
+
{
|
|
3255
|
+
className: "dsov-todo dsov-subagent",
|
|
3256
|
+
key: `subagent-${sub.childId}`,
|
|
3257
|
+
"data-depth": depth > 0 ? String(depth) : undefined,
|
|
3258
|
+
"data-flash": flash === "ok" ? "ok" : flash === "err" ? "err" : undefined,
|
|
3259
|
+
title: sub.label !== "" ? sub.label : sub.childId,
|
|
3260
|
+
onClick: () => onStepClick({ callId: `subagent-${sub.childId}`, name: sub.label, full: sub.label !== "" ? sub.label : sub.childId, seq: sub.seq }),
|
|
3261
|
+
},
|
|
3262
|
+
h("span", { className: "dsov-todoIcon dsov-agentIcon" }, h(IconAgent, { size: 14 })),
|
|
3263
|
+
h("span", { className: "dsov-memberName" }, sub.label !== "" ? sub.label : sub.childId),
|
|
3264
|
+
sub.mode !== "" ? h("span", { className: "dsov-badge", "data-tone": "quiet" }, sub.mode) : null,
|
|
3265
|
+
h("span", { className: "dsov-time", title: timeTitle(sub.time) }, fmtTime(sub.time))
|
|
3266
|
+
);
|
|
3267
|
+
};
|
|
3268
|
+
const memberIcon = (member, owned) => {
|
|
3269
|
+
const phase = typeof member.phase === "string" ? member.phase : "";
|
|
3270
|
+
if (phase === "failed") {
|
|
3271
|
+
return h(
|
|
3272
|
+
"span",
|
|
3273
|
+
{ className: "dsov-todoIcon", "data-status": "failed" },
|
|
3274
|
+
h(
|
|
3275
|
+
"svg",
|
|
3276
|
+
{ width: 14, height: 14, viewBox: "0 0 14 14", fill: "none", "aria-hidden": "true" },
|
|
3277
|
+
h("circle", { cx: 7, cy: 7, r: 5.6, stroke: "currentColor", strokeWidth: 1.3 }),
|
|
3278
|
+
h("path", { d: "M7 4v3.6", stroke: "currentColor", strokeWidth: 1.3, strokeLinecap: "round" }),
|
|
3279
|
+
h("circle", { cx: 7, cy: 9.9, r: 0.8, fill: "currentColor" })
|
|
3280
|
+
)
|
|
3281
|
+
);
|
|
3282
|
+
}
|
|
3283
|
+
if (owned.length > 0 && owned.every((task) => task.status === "completed")) {
|
|
3284
|
+
return h("span", { className: "dsov-todoIcon", "data-status": "completed" }, h(TodoStatusIcon, { status: "completed" }));
|
|
3285
|
+
}
|
|
3286
|
+
return h("span", { className: "dsov-todoIcon dsov-agentIcon" }, h(IconAgent, { size: 14 }));
|
|
3287
|
+
};
|
|
3288
|
+
const memberRow = (member, depth, expandable, open) => {
|
|
3289
|
+
const owned = ownedBy(member.id);
|
|
3290
|
+
const phase = typeof member.phase === "string" ? member.phase : "";
|
|
3291
|
+
const titleParts = [];
|
|
3292
|
+
if (member.description !== "") titleParts.push(member.description);
|
|
3293
|
+
if (MEMBER_PHASE_KEYS[phase]) titleParts.push(t(MEMBER_PHASE_KEYS[phase]));
|
|
3294
|
+
return h(
|
|
3295
|
+
"div",
|
|
3296
|
+
{
|
|
3297
|
+
className: "dsov-todo dsov-member",
|
|
3298
|
+
key: `member-${member.id}`,
|
|
3299
|
+
"data-depth": depth > 0 ? String(depth) : undefined,
|
|
3300
|
+
"data-expandable": expandable ? "true" : "false",
|
|
3301
|
+
"data-open": expandable ? (open ? "true" : "false") : undefined,
|
|
3302
|
+
title: titleParts.length > 0 ? titleParts.join(" · ") : member.name,
|
|
3303
|
+
onClick: expandable ? () => toggleKey(`member:${member.id}`, !open) : undefined,
|
|
3304
|
+
},
|
|
3305
|
+
expandable ? h("span", { className: "dsov-chevron", "data-open": open ? "true" : "false" }, h(IconChevron, { size: 11 })) : null,
|
|
3306
|
+
memberIcon(member, owned),
|
|
3307
|
+
h("span", { className: "dsov-memberName" }, member.name),
|
|
3308
|
+
h("span", { className: "dsov-memberDesc" }, member.description),
|
|
3309
|
+
h("span", { className: "dsov-time", title: timeTitle(member.time) }, fmtTime(member.time))
|
|
3310
|
+
);
|
|
3311
|
+
};
|
|
3312
|
+
/* attach team records under their creating step (steps view only) */
|
|
3313
|
+
const consumedMembers = new Set();
|
|
3314
|
+
const consumedTasks = new Set();
|
|
3315
|
+
const consumedSubs = new Set();
|
|
3316
|
+
const stepAttachments = new Map();
|
|
3317
|
+
if (showSteps && hasTeam) {
|
|
3318
|
+
displaySteps.forEach((step, index) => {
|
|
3319
|
+
const teamKey = typeof step.teamKey === "string" ? step.teamKey : "";
|
|
3320
|
+
if (teamKey === "" || typeof step.name !== "string") return;
|
|
3321
|
+
const attached = [];
|
|
3322
|
+
if (step.name === "spawn_teammate") {
|
|
3323
|
+
const member = members.find((candidate) => candidate.name === teamKey && !consumedMembers.has(candidate.id));
|
|
3324
|
+
if (member !== undefined) {
|
|
3325
|
+
consumedMembers.add(member.id);
|
|
3326
|
+
const phase = typeof member.phase === "string" ? member.phase : "";
|
|
3327
|
+
attached.push({ row: memberRow(member, 1, false, false), unfinished: phase === "provisioning" || phase === "active" });
|
|
3328
|
+
ownedBy(member.id).forEach((task) => {
|
|
3329
|
+
if (consumedTasks.has(task.id)) return;
|
|
3330
|
+
consumedTasks.add(task.id);
|
|
3331
|
+
attached.push({ row: taskRow(task, 2), unfinished: task.status !== "completed" });
|
|
3332
|
+
});
|
|
3333
|
+
}
|
|
3334
|
+
} else if (step.name === "subagent" || step.name === "subagent_fork") {
|
|
3335
|
+
const sub = subagents.find((candidate) => (candidate.label === teamKey || candidate.childId === teamKey) && !consumedSubs.has(candidate.childId));
|
|
3336
|
+
if (sub !== undefined) {
|
|
3337
|
+
consumedSubs.add(sub.childId);
|
|
3338
|
+
attached.push({ row: subRow(sub, 1), unfinished: false });
|
|
3339
|
+
}
|
|
3340
|
+
} else if (step.name === "team_task_create") {
|
|
3341
|
+
/* owned tasks nest under their member instead; only unowned tasks attach here */
|
|
3342
|
+
const task = teamTasks.find(
|
|
3343
|
+
(candidate) =>
|
|
3344
|
+
candidate.subject === teamKey &&
|
|
3345
|
+
!consumedTasks.has(candidate.id) &&
|
|
3346
|
+
!(typeof candidate.ownerId === "string" && members.some((member) => member.id === candidate.ownerId))
|
|
3347
|
+
);
|
|
3348
|
+
if (task !== undefined) {
|
|
3349
|
+
consumedTasks.add(task.id);
|
|
3350
|
+
attached.push({ row: taskRow(task, 1), unfinished: task.status !== "completed" });
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
if (attached.length > 0) stepAttachments.set(stepKeyOf(step, index), attached);
|
|
3354
|
+
});
|
|
3355
|
+
}
|
|
3356
|
+
/* records not attached to a visible step render flat in the list */
|
|
3357
|
+
const pushFlatTeam = (target) => {
|
|
3358
|
+
teamTasks.forEach((task) => {
|
|
3359
|
+
if (consumedTasks.has(task.id)) return;
|
|
3360
|
+
const ownerKnown = typeof task.ownerId === "string" && members.some((member) => member.id === task.ownerId);
|
|
3361
|
+
if (ownerKnown) return;
|
|
3362
|
+
target.push(taskRow(task, 0));
|
|
3363
|
+
});
|
|
3364
|
+
members.forEach((member) => {
|
|
3365
|
+
if (consumedMembers.has(member.id)) return;
|
|
3366
|
+
const owned = ownedBy(member.id).filter((task) => !consumedTasks.has(task.id));
|
|
3367
|
+
const expandable = owned.length > 0;
|
|
3368
|
+
const phase = typeof member.phase === "string" ? member.phase : "";
|
|
3369
|
+
const open = expandable ? overrideOf(`member:${member.id}`, owned.some((task) => task.status !== "completed") || phase === "provisioning") : false;
|
|
3370
|
+
target.push(memberRow(member, 0, expandable, open));
|
|
3371
|
+
if (expandable && open) owned.forEach((task) => target.push(taskRow(task, 1)));
|
|
3372
|
+
});
|
|
3373
|
+
subagents.forEach((sub) => {
|
|
3374
|
+
if (consumedSubs.has(sub.childId)) return;
|
|
3375
|
+
target.push(subRow(sub, 0));
|
|
3376
|
+
});
|
|
3377
|
+
};
|
|
3009
3378
|
const stepRow = (step, key, extraProps, isWaiting) => {
|
|
3010
3379
|
const stepRunning = step.done !== true;
|
|
3011
3380
|
const failed = step.error === true;
|
|
@@ -3013,7 +3382,10 @@ function FileTypeIcon(props) {
|
|
|
3013
3382
|
const detail = typeof step.detail === "string" && step.detail !== "" ? step.detail : "";
|
|
3014
3383
|
const full = typeof step.full === "string" && step.full !== "" ? step.full : detail !== "" ? detail : typeof step.name === "string" ? step.name : "";
|
|
3015
3384
|
const flash = stepFlash !== null && isRecord(stepFlash) && stepFlash.key === (typeof step.callId === "string" && step.callId !== "" ? step.callId : `${step.name}|${step.seq}`) ? stepFlash.state : null;
|
|
3016
|
-
|
|
3385
|
+
const attached = stepAttachments.get(key);
|
|
3386
|
+
const expandable = Array.isArray(attached) && attached.length > 0;
|
|
3387
|
+
const open = expandable ? overrideOf(`step:${key}`, attached.some((item) => item.unfinished)) : false;
|
|
3388
|
+
const row = h(
|
|
3017
3389
|
"div",
|
|
3018
3390
|
{
|
|
3019
3391
|
className: "dsov-step",
|
|
@@ -3025,6 +3397,21 @@ function FileTypeIcon(props) {
|
|
|
3025
3397
|
...(extraProps === undefined ? {} : extraProps),
|
|
3026
3398
|
onClick: () => onStepClick(step),
|
|
3027
3399
|
},
|
|
3400
|
+
expandable
|
|
3401
|
+
? h(
|
|
3402
|
+
"span",
|
|
3403
|
+
{
|
|
3404
|
+
className: "dsov-chevron dsov-stepChevron",
|
|
3405
|
+
"data-open": open ? "true" : "false",
|
|
3406
|
+
title: t(open ? "progress.collapseTeam" : "progress.expandTeam"),
|
|
3407
|
+
onClick: (event) => {
|
|
3408
|
+
event.stopPropagation();
|
|
3409
|
+
toggleKey(`step:${key}`, !open);
|
|
3410
|
+
},
|
|
3411
|
+
},
|
|
3412
|
+
h(IconChevron, { size: 11 })
|
|
3413
|
+
)
|
|
3414
|
+
: null,
|
|
3028
3415
|
waitingRow
|
|
3029
3416
|
? h(
|
|
3030
3417
|
"span",
|
|
@@ -3039,8 +3426,14 @@ function FileTypeIcon(props) {
|
|
|
3039
3426
|
: h("span", { className: "dsov-todoIcon", "data-status": stepRunning ? "in_progress" : "completed" }, h(TodoStatusIcon, { status: stepRunning ? "in_progress" : "completed" })),
|
|
3040
3427
|
h("span", { className: "dsov-stepName" }, typeof step.name === "string" ? step.name : ""),
|
|
3041
3428
|
h("span", { className: "dsov-stepDetail" }, detail),
|
|
3042
|
-
h("span", { className: "dsov-time" },
|
|
3429
|
+
h("span", { className: "dsov-time", title: timeTitle(typeof step.endTime === "number" ? step.endTime : step.time) }, fmtTime(typeof step.endTime === "number" ? step.endTime : step.time))
|
|
3043
3430
|
);
|
|
3431
|
+
return expandable && open ? [row, ...attached.map((item) => item.row)] : row;
|
|
3432
|
+
};
|
|
3433
|
+
const pushStepRow = (target, step, key, extraProps, isWaiting) => {
|
|
3434
|
+
const rendered = stepRow(step, key, extraProps, isWaiting);
|
|
3435
|
+
if (Array.isArray(rendered)) target.push(...rendered);
|
|
3436
|
+
else target.push(rendered);
|
|
3044
3437
|
};
|
|
3045
3438
|
const kids = [];
|
|
3046
3439
|
if (goal) {
|
|
@@ -3055,8 +3448,7 @@ function FileTypeIcon(props) {
|
|
|
3055
3448
|
)
|
|
3056
3449
|
);
|
|
3057
3450
|
}
|
|
3058
|
-
|
|
3059
|
-
if (todos && todos.length > 0) {
|
|
3451
|
+
if (hasTodos) {
|
|
3060
3452
|
const total = todos.length;
|
|
3061
3453
|
const done = todos.filter((item) => item.status === "completed").length;
|
|
3062
3454
|
kids.push(
|
|
@@ -3070,8 +3462,9 @@ function FileTypeIcon(props) {
|
|
|
3070
3462
|
todos.forEach((todo, index) => {
|
|
3071
3463
|
const status = TODO_KEYS[todo.status] ? todo.status : "pending";
|
|
3072
3464
|
const flash = promptFlash !== null && isRecord(promptFlash) && promptFlash.index === index ? promptFlash.state : null;
|
|
3073
|
-
const title =
|
|
3074
|
-
|
|
3465
|
+
const title = !todoPromptOn
|
|
3466
|
+
? todo.content
|
|
3467
|
+
: flash === "inserted"
|
|
3075
3468
|
? t("progress.todoInserted")
|
|
3076
3469
|
: flash === "copied"
|
|
3077
3470
|
? t("progress.todoCopied")
|
|
@@ -3087,9 +3480,9 @@ function FileTypeIcon(props) {
|
|
|
3087
3480
|
"data-status": status,
|
|
3088
3481
|
"data-flash": flash === "inserted" || flash === "copied" ? "ok" : flash === "failed" ? "err" : undefined,
|
|
3089
3482
|
role: "button",
|
|
3090
|
-
tabIndex: 0,
|
|
3483
|
+
tabIndex: todoPromptOn ? 0 : undefined,
|
|
3091
3484
|
title,
|
|
3092
|
-
|
|
3485
|
+
onDoubleClick: () => onTodoClick(index, todo.content),
|
|
3093
3486
|
onKeyDown: (event) => {
|
|
3094
3487
|
if (event.key === "Enter" || event.key === " ") {
|
|
3095
3488
|
event.preventDefault();
|
|
@@ -3103,12 +3496,14 @@ function FileTypeIcon(props) {
|
|
|
3103
3496
|
)
|
|
3104
3497
|
);
|
|
3105
3498
|
});
|
|
3106
|
-
}
|
|
3499
|
+
}
|
|
3500
|
+
if (hasTeam && !showSteps) pushFlatTeam(kids);
|
|
3501
|
+
if (showSteps) {
|
|
3107
3502
|
const markWaiting = waiting === true && summary.scope === "latest";
|
|
3108
3503
|
let waitingIndex = -1;
|
|
3109
3504
|
if (markWaiting) {
|
|
3110
|
-
for (let index =
|
|
3111
|
-
if (
|
|
3505
|
+
for (let index = displaySteps.length - 1; index >= 0; index -= 1) {
|
|
3506
|
+
if (displaySteps[index].done !== true && displaySteps[index].error !== true) {
|
|
3112
3507
|
waitingIndex = index;
|
|
3113
3508
|
break;
|
|
3114
3509
|
}
|
|
@@ -3116,25 +3511,48 @@ function FileTypeIcon(props) {
|
|
|
3116
3511
|
}
|
|
3117
3512
|
const rows = [];
|
|
3118
3513
|
let freshCount = 0;
|
|
3119
|
-
|
|
3514
|
+
displaySteps.forEach((step, index) => {
|
|
3120
3515
|
const key = stepKeyOf(step, index);
|
|
3121
3516
|
const fresh = !seenSteps.current.keys.has(key);
|
|
3122
3517
|
if (fresh) {
|
|
3123
3518
|
seenSteps.current.keys.add(key);
|
|
3124
3519
|
freshCount += 1;
|
|
3125
3520
|
}
|
|
3126
|
-
rows
|
|
3521
|
+
pushStepRow(rows, step, key, fresh ? { "data-enter": "true", style: { animationDelay: `${(freshCount - 1) * 45}ms` } } : undefined, index === waitingIndex);
|
|
3127
3522
|
});
|
|
3128
|
-
if (ghost !== null && isRecord(ghost)) {
|
|
3129
|
-
rows
|
|
3523
|
+
if (ghost !== null && isRecord(ghost) && !stepsOpen) {
|
|
3524
|
+
pushStepRow(rows, ghost, `ghost-${ghost.callId}`, { "data-exit": "true" }, false);
|
|
3130
3525
|
}
|
|
3131
|
-
|
|
3526
|
+
if (hasTeam) pushFlatTeam(rows);
|
|
3527
|
+
kids.push(
|
|
3528
|
+
h(
|
|
3529
|
+
"div",
|
|
3530
|
+
{ className: "dsov-stepsHead", key: "stepsHead" },
|
|
3531
|
+
h("span", null, t(stepsOpen ? "progress.allSteps" : "progress.recentSteps")),
|
|
3532
|
+
canExpand
|
|
3533
|
+
? h(
|
|
3534
|
+
"button",
|
|
3535
|
+
{
|
|
3536
|
+
type: "button",
|
|
3537
|
+
className: "dsov-moreSteps",
|
|
3538
|
+
title: stepsOpen ? t("progress.hideEarlierSteps") : t("progress.moreStepsCount", { n: expandedSteps.length - steps.length }),
|
|
3539
|
+
onClick: () => setStepsExpanded(!stepsOpen),
|
|
3540
|
+
},
|
|
3541
|
+
t(stepsOpen ? "progress.hideEarlierSteps" : "progress.showEarlierSteps"),
|
|
3542
|
+
h(IconHistory, { size: 12, className: "dsov-moreIcon" })
|
|
3543
|
+
)
|
|
3544
|
+
: null
|
|
3545
|
+
)
|
|
3546
|
+
);
|
|
3132
3547
|
kids.push(h("div", { className: "dsov-steps", key: "steps" }, rows));
|
|
3133
3548
|
}
|
|
3134
3549
|
return h(Section, { id: "progress", icon: h(IconProgress, { size: 15 }), title: t("section.progress"), empty: t(summary.scope === "all" ? "progress.empty" : "progress.emptyTurn") }, kids);
|
|
3135
3550
|
}
|
|
3136
3551
|
|
|
3137
3552
|
function ArtifactsSection({ summary, t, binding, stats }) {
|
|
3553
|
+
const cfg = useConfig();
|
|
3554
|
+
const fmtTime = (ts) => formatTime(ts, cfg.timeMode, summary.turnStart);
|
|
3555
|
+
const timeTitle = (ts) => (cfg.timeMode === "elapsed" ? formatTime(ts) : undefined);
|
|
3138
3556
|
const sessionId = binding ? binding.sessionId : undefined;
|
|
3139
3557
|
const cwd = sessionCwd(sessionId);
|
|
3140
3558
|
const turns = summary.changeTurns;
|
|
@@ -3173,7 +3591,7 @@ function FileTypeIcon(props) {
|
|
|
3173
3591
|
? h("span", { className: "dsov-badge", "data-tone": "quiet" }, t("artifact.binary"))
|
|
3174
3592
|
: null,
|
|
3175
3593
|
artifact.presented ? h("span", { className: "dsov-badge", "data-tone": "brand" }, t("artifact.presented")) : null,
|
|
3176
|
-
h("span", { className: "dsov-rowTail" }, h("span", { className: "dsov-time" },
|
|
3594
|
+
h("span", { className: "dsov-rowTail" }, h("span", { className: "dsov-time", title: timeTitle(artifact.time) }, fmtTime(artifact.time)))
|
|
3177
3595
|
);
|
|
3178
3596
|
});
|
|
3179
3597
|
return h(
|
|
@@ -3290,6 +3708,8 @@ function FileTypeIcon(props) {
|
|
|
3290
3708
|
|
|
3291
3709
|
function ReferencesSection({ summary, t, sessionId }) {
|
|
3292
3710
|
const cfg = useConfig();
|
|
3711
|
+
const fmtTime = (ts) => formatTime(ts, cfg.timeMode, summary.turnStart);
|
|
3712
|
+
const timeTitle = (ts) => (cfg.timeMode === "elapsed" ? formatTime(ts) : undefined);
|
|
3293
3713
|
const sortMode = useRefSort();
|
|
3294
3714
|
const hideCommon = useHideCommonRefs();
|
|
3295
3715
|
const typeFilter = useRefTypes();
|
|
@@ -3386,7 +3806,7 @@ function FileTypeIcon(props) {
|
|
|
3386
3806
|
)
|
|
3387
3807
|
: null
|
|
3388
3808
|
),
|
|
3389
|
-
h("span", { className: "dsov-time", key: "time" },
|
|
3809
|
+
h("span", { className: "dsov-time", key: "time", title: timeTitle(ref.time) }, fmtTime(ref.time)),
|
|
3390
3810
|
];
|
|
3391
3811
|
const isFile = ref.kind === "file";
|
|
3392
3812
|
if (isFile) {
|
|
@@ -3504,8 +3924,8 @@ function FileTypeIcon(props) {
|
|
|
3504
3924
|
key: mode,
|
|
3505
3925
|
className: "dsov-segBtn",
|
|
3506
3926
|
"data-active": sortMode === mode ? "true" : "false",
|
|
3507
|
-
title: t(`reference.sort.${mode}.title`),
|
|
3508
|
-
onClick: () => setRefSort(mode),
|
|
3927
|
+
title: sortMode === mode ? `${t(`reference.sort.${mode}.title`)} · ${t("reference.sort.cancel")}` : t(`reference.sort.${mode}.title`),
|
|
3928
|
+
onClick: () => setRefSort(sortMode === mode ? "" : mode),
|
|
3509
3929
|
},
|
|
3510
3930
|
t(`reference.sort.${mode}`)
|
|
3511
3931
|
)
|
|
@@ -3571,6 +3991,7 @@ function FileTypeIcon(props) {
|
|
|
3571
3991
|
const cfg = useConfig();
|
|
3572
3992
|
const sessionId = binding ? binding.sessionId : undefined;
|
|
3573
3993
|
const rootClass = variant === "panel" ? "dsov-panel" : "dsov-view";
|
|
3994
|
+
const rootRef = React.useRef(null);
|
|
3574
3995
|
const scope = summary.scope === selection || selection === "latest" ? summary.scope : selection;
|
|
3575
3996
|
const hasTurns = Array.isArray(summary.turns) && summary.turns.length >= 2;
|
|
3576
3997
|
const turnOptions = Array.isArray(summary.turns) ? summary.turns.slice().reverse() : [];
|
|
@@ -3591,33 +4012,92 @@ function FileTypeIcon(props) {
|
|
|
3591
4012
|
h("option", { value: "all", key: "all" }, t("scope.all"))
|
|
3592
4013
|
)
|
|
3593
4014
|
: null;
|
|
3594
|
-
const statsBar = h(StatsBar, { stats: summary.stats, t });
|
|
3595
|
-
const hasStats = isRecord(summary.stats) && (summary.stats.durationMs > 0 || summary.stats.toolCalls > 0);
|
|
4015
|
+
const statsBar = cfg.showStats !== false ? h(StatsBar, { stats: summary.stats, t }) : null;
|
|
4016
|
+
const hasStats = cfg.showStats !== false && isRecord(summary.stats) && (summary.stats.durationMs > 0 || summary.stats.toolCalls > 0);
|
|
4017
|
+
const barPresent = variant === "panel" ? scopeSelect !== null || hasStats : !(scopeSelect === null && cfg.placement === "conversation" && !hasStats);
|
|
4018
|
+
/* toggle data-stuck on the sticky wrapper: background only shows while pinned */
|
|
4019
|
+
React.useEffect(() => {
|
|
4020
|
+
const root = rootRef.current;
|
|
4021
|
+
if (root === null || typeof window === "undefined" || typeof getComputedStyle !== "function") return undefined;
|
|
4022
|
+
const wrapper = root.querySelector('[data-sticky-bar="true"]');
|
|
4023
|
+
if (wrapper === null) return undefined;
|
|
4024
|
+
let scroller = root;
|
|
4025
|
+
while (scroller !== null && scroller !== document.body && scroller !== document.documentElement) {
|
|
4026
|
+
const oy = getComputedStyle(scroller).overflowY;
|
|
4027
|
+
if (oy === "auto" || oy === "scroll" || oy === "overlay") break;
|
|
4028
|
+
/* an overflow:hidden ancestor only counts when it actually scrolls; otherwise the
|
|
4029
|
+
search continues to the real scroller (the conversation tab has hidden wrappers) */
|
|
4030
|
+
if (oy === "hidden" && scroller.scrollHeight > scroller.clientHeight + 1) break;
|
|
4031
|
+
scroller = scroller.parentElement;
|
|
4032
|
+
}
|
|
4033
|
+
const target = scroller !== null && scroller !== document.body && scroller !== document.documentElement ? scroller : null;
|
|
4034
|
+
/* Transform-based pinning: CSS sticky binds to the nearest scrollport, which an
|
|
4035
|
+
overflow:hidden wrapper can hijack, leaving the bar to slide under the tab bar. */
|
|
4036
|
+
let lastDelta = 0;
|
|
4037
|
+
const update = () => {
|
|
4038
|
+
const frameTop = target === null ? 0 : target.getBoundingClientRect().top;
|
|
4039
|
+
const scrolled = target === null ? window.scrollY : target.scrollTop;
|
|
4040
|
+
const natural = wrapper.getBoundingClientRect().top - lastDelta - frameTop + scrolled;
|
|
4041
|
+
const delta = Math.max(0, scrolled - natural);
|
|
4042
|
+
if (delta !== lastDelta) {
|
|
4043
|
+
wrapper.style.transform = delta > 0 ? `translateY(${delta}px)` : "";
|
|
4044
|
+
lastDelta = delta;
|
|
4045
|
+
}
|
|
4046
|
+
const flag = delta > 0.5 ? "true" : "false";
|
|
4047
|
+
if (wrapper.getAttribute("data-stuck") !== flag) wrapper.setAttribute("data-stuck", flag);
|
|
4048
|
+
};
|
|
4049
|
+
update();
|
|
4050
|
+
const host = target === null ? window : target;
|
|
4051
|
+
host.addEventListener("scroll", update, { passive: true });
|
|
4052
|
+
window.addEventListener("resize", update);
|
|
4053
|
+
return () => {
|
|
4054
|
+
host.removeEventListener("scroll", update);
|
|
4055
|
+
window.removeEventListener("resize", update);
|
|
4056
|
+
wrapper.style.transform = "";
|
|
4057
|
+
};
|
|
4058
|
+
}, [variant, barPresent]);
|
|
3596
4059
|
return h(
|
|
3597
4060
|
"div",
|
|
3598
|
-
{ className: rootClass, "data-conversation-overview": variant === "panel" ? "panel" : "view" },
|
|
4061
|
+
{ className: rootClass, ref: rootRef, "data-conversation-overview": variant === "panel" ? "panel" : "view" },
|
|
3599
4062
|
variant === "panel"
|
|
3600
4063
|
? scopeSelect !== null || hasStats
|
|
3601
|
-
? h(
|
|
4064
|
+
? h(
|
|
4065
|
+
"div",
|
|
4066
|
+
{ className: "dsov-turnbarWrap", "data-sticky-bar": "true", "data-stuck": "false" },
|
|
4067
|
+
h(
|
|
4068
|
+
"div",
|
|
4069
|
+
{ className: "dsov-turnbar" },
|
|
4070
|
+
statsBar !== null ? h("div", { className: "dsov-turnbarScroll" }, statsBar) : null,
|
|
4071
|
+
scopeSelect !== null ? h("div", { className: "dsov-turnbarFixed" }, scopeSelect) : null
|
|
4072
|
+
)
|
|
4073
|
+
)
|
|
3602
4074
|
: null
|
|
3603
4075
|
: scopeSelect === null && cfg.placement === "conversation" && !hasStats
|
|
3604
4076
|
? null
|
|
3605
4077
|
: h(
|
|
3606
4078
|
"div",
|
|
3607
|
-
{ className: "dsov-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
? null
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
4079
|
+
{ className: "dsov-toolbarWrap", "data-sticky-bar": "true", "data-stuck": "false" },
|
|
4080
|
+
h(
|
|
4081
|
+
"div",
|
|
4082
|
+
{ className: "dsov-toolbar" },
|
|
4083
|
+
statsBar !== null ? h("div", { className: "dsov-toolbarScroll" }, statsBar) : null,
|
|
4084
|
+
h(
|
|
4085
|
+
"div",
|
|
4086
|
+
{ className: "dsov-toolbarFixed" },
|
|
4087
|
+
scopeSelect,
|
|
4088
|
+
cfg.placement === "conversation"
|
|
4089
|
+
? null
|
|
4090
|
+
: h(
|
|
4091
|
+
"button",
|
|
4092
|
+
{ type: "button", className: "dsov-iconBtn", onClick: openOverviewTab, title: t("action.openInSidebar") },
|
|
4093
|
+
h(IconOpenSidebar, { size: 14 }),
|
|
4094
|
+
t("action.openInSidebar")
|
|
4095
|
+
)
|
|
4096
|
+
)
|
|
4097
|
+
)
|
|
3618
4098
|
),
|
|
3619
4099
|
summary.hasMore ? h("div", { className: "dsov-loading" }, t("state.loadingHistory")) : null,
|
|
3620
|
-
cfg.showProgress ? h(ProgressSection, { summary, goal, t, waiting: sessionWaiting }) : null,
|
|
4100
|
+
cfg.showProgress ? h(ProgressSection, { summary, goal, t, waiting: sessionWaiting, cfg }) : null,
|
|
3621
4101
|
cfg.showArtifacts ? h(ArtifactsSection, { summary, t, binding, stats }) : null,
|
|
3622
4102
|
cfg.showReferences ? h(ReferencesSection, { summary, t, sessionId }) : null
|
|
3623
4103
|
);
|
|
@@ -3803,7 +4283,8 @@ function FileTypeIcon(props) {
|
|
|
3803
4283
|
const FRONTMATTER_TITLE_KEYS = ["title", "name"];
|
|
3804
4284
|
const FRONTMATTER_TAG_KEYS = ["keywords", "tags"];
|
|
3805
4285
|
const FRONTMATTER_SCENARIO_KEYS = ["usage_scenario", "when_to_use"];
|
|
3806
|
-
const
|
|
4286
|
+
const FRONTMATTER_SOURCE_KEYS = ["source", "sources"];
|
|
4287
|
+
const FRONTMATTER_SKIP_KEYS = new Set([...FRONTMATTER_TITLE_KEYS, ...FRONTMATTER_TAG_KEYS, ...FRONTMATTER_SCENARIO_KEYS, ...FRONTMATTER_SOURCE_KEYS, "description"]);
|
|
3807
4288
|
|
|
3808
4289
|
function frontmatterTitle(fields) {
|
|
3809
4290
|
for (const key of FRONTMATTER_TITLE_KEYS) {
|
|
@@ -3822,6 +4303,26 @@ function FileTypeIcon(props) {
|
|
|
3822
4303
|
if (description && description.value !== "") {
|
|
3823
4304
|
blocks.push(h("p", { className: "dsov-vikingDesc", key: "description" }, description.value));
|
|
3824
4305
|
}
|
|
4306
|
+
const sourceField = fields.find((field) => FRONTMATTER_SOURCE_KEYS.indexOf(field.key) !== -1 && (field.value !== "" || field.items.length > 0));
|
|
4307
|
+
if (sourceField) {
|
|
4308
|
+
const values = sourceField.items.length > 0 ? sourceField.items : [sourceField.value];
|
|
4309
|
+
blocks.push(
|
|
4310
|
+
h(
|
|
4311
|
+
"div",
|
|
4312
|
+
{ className: "dsov-vikingField", key: "source" },
|
|
4313
|
+
h("div", { className: "dsov-vikingFieldLabel" }, t("viking.field.source")),
|
|
4314
|
+
h(
|
|
4315
|
+
"div",
|
|
4316
|
+
{ className: "dsov-vikingSource" },
|
|
4317
|
+
values.map((value, index) =>
|
|
4318
|
+
/^https?:\/\//i.test(value)
|
|
4319
|
+
? h("a", { key: `${value}#${index}`, className: "dsov-vikingSourceLink", href: value, target: "_blank", rel: "noreferrer" }, value)
|
|
4320
|
+
: h("span", { key: `${value}#${index}`, className: "dsov-vikingFieldValue" }, value)
|
|
4321
|
+
)
|
|
4322
|
+
)
|
|
4323
|
+
)
|
|
4324
|
+
);
|
|
4325
|
+
}
|
|
3825
4326
|
const scenarios = fields.filter((field) => FRONTMATTER_SCENARIO_KEYS.indexOf(field.key) !== -1 && field.items.length > 0);
|
|
3826
4327
|
if (scenarios.length > 0) {
|
|
3827
4328
|
blocks.push(
|
|
@@ -4042,6 +4543,41 @@ function FileTypeIcon(props) {
|
|
|
4042
4543
|
);
|
|
4043
4544
|
}
|
|
4044
4545
|
|
|
4546
|
+
/* Multi-line text row committing on blur and on unmount (prompt template). */
|
|
4547
|
+
function SettingTextarea({ label, value, placeholder, onCommit }) {
|
|
4548
|
+
const [draft, setDraft] = React.useState(value);
|
|
4549
|
+
const [focused, setFocused] = React.useState(false);
|
|
4550
|
+
const pending = React.useRef({ draft: value, value, commit: onCommit });
|
|
4551
|
+
pending.current = { draft, value, commit: onCommit };
|
|
4552
|
+
React.useEffect(
|
|
4553
|
+
() => () => {
|
|
4554
|
+
const state = pending.current;
|
|
4555
|
+
if (typeof state.draft === "string" && state.draft !== state.value) state.commit(state.draft);
|
|
4556
|
+
},
|
|
4557
|
+
[]
|
|
4558
|
+
);
|
|
4559
|
+
return h(
|
|
4560
|
+
"div",
|
|
4561
|
+
{ className: "dsov-set-row dsov-set-arearow" },
|
|
4562
|
+
h("span", { className: "dsov-set-label" }, label),
|
|
4563
|
+
h("textarea", {
|
|
4564
|
+
className: "dsov-set-textarea",
|
|
4565
|
+
rows: 7,
|
|
4566
|
+
value: focused === true ? draft : value,
|
|
4567
|
+
placeholder,
|
|
4568
|
+
onFocus: () => {
|
|
4569
|
+
setDraft(value);
|
|
4570
|
+
setFocused(true);
|
|
4571
|
+
},
|
|
4572
|
+
onBlur: () => {
|
|
4573
|
+
setFocused(false);
|
|
4574
|
+
if (typeof draft === "string" && draft !== value) onCommit(draft);
|
|
4575
|
+
},
|
|
4576
|
+
onChange: (event) => setDraft(event.target.value),
|
|
4577
|
+
})
|
|
4578
|
+
);
|
|
4579
|
+
}
|
|
4580
|
+
|
|
4045
4581
|
function SettingsForm(props) {
|
|
4046
4582
|
const t = typeof props.t === "function" ? props.t : translate;
|
|
4047
4583
|
const hookForm = useSettingsForm();
|
|
@@ -4070,6 +4606,10 @@ function FileTypeIcon(props) {
|
|
|
4070
4606
|
{ id: "sidebar", label: t("settings.placement.sidebar") },
|
|
4071
4607
|
{ id: "both", label: t("settings.placement.both") },
|
|
4072
4608
|
];
|
|
4609
|
+
const timeOptions = [
|
|
4610
|
+
{ id: "clock", label: t("settings.timeMode.clock") },
|
|
4611
|
+
{ id: "elapsed", label: t("settings.timeMode.elapsed") },
|
|
4612
|
+
];
|
|
4073
4613
|
const boolRow = (label, field) =>
|
|
4074
4614
|
h(SettingSelect, {
|
|
4075
4615
|
label,
|
|
@@ -4089,7 +4629,23 @@ function FileTypeIcon(props) {
|
|
|
4089
4629
|
t("settings.sections"),
|
|
4090
4630
|
boolRow(t("settings.showProgress"), "showProgress"),
|
|
4091
4631
|
boolRow(t("settings.showArtifacts"), "showArtifacts"),
|
|
4092
|
-
boolRow(t("settings.showReferences"), "showReferences")
|
|
4632
|
+
boolRow(t("settings.showReferences"), "showReferences"),
|
|
4633
|
+
boolRow(t("settings.showStats"), "showStats")
|
|
4634
|
+
),
|
|
4635
|
+
group(
|
|
4636
|
+
t("settings.interaction"),
|
|
4637
|
+
h(SettingSelect, { label: t("settings.timeMode"), value: cfg.timeMode, options: timeOptions, onPick: (id) => set("timeMode", id) }),
|
|
4638
|
+
boolRow(t("settings.todoPromptRow"), "todoPrompt"),
|
|
4639
|
+
cfg.todoPrompt
|
|
4640
|
+
? h(SettingTextarea, {
|
|
4641
|
+
label: t("settings.todoPromptTemplate"),
|
|
4642
|
+
value: cfg.todoPromptTemplate,
|
|
4643
|
+
placeholder: DEFAULT_TODO_TEMPLATE,
|
|
4644
|
+
onCommit: (value) => set("todoPromptTemplate", value),
|
|
4645
|
+
})
|
|
4646
|
+
: null,
|
|
4647
|
+
note(t("settings.todoPromptNote")),
|
|
4648
|
+
cfg.todoPrompt ? note(t("settings.todoPromptTemplateNote")) : null
|
|
4093
4649
|
),
|
|
4094
4650
|
group(
|
|
4095
4651
|
t("settings.quickNav"),
|
|
@@ -4123,6 +4679,19 @@ function FileTypeIcon(props) {
|
|
|
4123
4679
|
: null,
|
|
4124
4680
|
note(cfg.vikingPreview ? t("settings.vikingNote") : t("settings.vikingOffNote"))
|
|
4125
4681
|
),
|
|
4682
|
+
group(
|
|
4683
|
+
t("settings.feedbackGroup"),
|
|
4684
|
+
h(
|
|
4685
|
+
"a",
|
|
4686
|
+
{ className: "dsov-set-link", href: FEEDBACK_URL, target: "_blank", rel: "noreferrer", title: FEEDBACK_URL },
|
|
4687
|
+
h("span", null, t("settings.feedback")),
|
|
4688
|
+
h(
|
|
4689
|
+
"svg",
|
|
4690
|
+
{ width: 11, height: 11, viewBox: "0 0 12 12", fill: "none", "aria-hidden": "true" },
|
|
4691
|
+
h("path", { d: "M4.6 2.4H2.8c-.7 0-1.3.6-1.3 1.3v5.5c0 .7.6 1.3 1.3 1.3h5.5c.7 0 1.3-.6 1.3-1.3V7.4M7.2 1.5h3.3v3.3M6.3 5.7 10.2 1.8", stroke: "currentColor", strokeWidth: 1.1, strokeLinecap: "round", strokeLinejoin: "round" })
|
|
4692
|
+
)
|
|
4693
|
+
)
|
|
4694
|
+
),
|
|
4126
4695
|
formUsable ? note(t("settings.hint")) : null
|
|
4127
4696
|
);
|
|
4128
4697
|
}
|
|
@@ -4134,8 +4703,15 @@ function FileTypeIcon(props) {
|
|
|
4134
4703
|
const CSS = `
|
|
4135
4704
|
.dsov-view{box-sizing:border-box;width:100%;max-width:880px;margin:0 auto;padding:20px 28px 64px;display:flex;flex-direction:column;gap:14px}
|
|
4136
4705
|
.dsov-panel{box-sizing:border-box;height:100%;min-height:0;overflow-y:auto;padding:14px 16px 28px;display:flex;flex-direction:column;gap:12px}
|
|
4137
|
-
.dsov-
|
|
4138
|
-
.dsov-
|
|
4706
|
+
.dsov-panel,.dsov-viking,.dsov-secBody,.dsov-typeMenu,.dsov-md-pre,.dsov-turnbarScroll,.dsov-toolbarScroll{scrollbar-width:thin;scrollbar-color:color-mix(in srgb,var(--dsw-alias-label-secondary) 35%,transparent) transparent}
|
|
4707
|
+
.dsov-panel::-webkit-scrollbar,.dsov-viking::-webkit-scrollbar,.dsov-secBody::-webkit-scrollbar,.dsov-typeMenu::-webkit-scrollbar,.dsov-md-pre::-webkit-scrollbar,.dsov-turnbarScroll::-webkit-scrollbar,.dsov-toolbarScroll::-webkit-scrollbar{width:6px;height:5px}
|
|
4708
|
+
.dsov-panel::-webkit-scrollbar-thumb,.dsov-viking::-webkit-scrollbar-thumb,.dsov-secBody::-webkit-scrollbar-thumb,.dsov-typeMenu::-webkit-scrollbar-thumb,.dsov-md-pre::-webkit-scrollbar-thumb,.dsov-turnbarScroll::-webkit-scrollbar-thumb,.dsov-toolbarScroll::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--dsw-alias-label-secondary) 35%,transparent);border-radius:3px}
|
|
4709
|
+
.dsov-panel::-webkit-scrollbar-track,.dsov-viking::-webkit-scrollbar-track,.dsov-secBody::-webkit-scrollbar-track,.dsov-typeMenu::-webkit-scrollbar-track,.dsov-md-pre::-webkit-scrollbar-track,.dsov-turnbarScroll::-webkit-scrollbar-track,.dsov-toolbarScroll::-webkit-scrollbar-track{background:transparent}
|
|
4710
|
+
.dsov-toolbar{display:flex;align-items:center;gap:8px}
|
|
4711
|
+
.dsov-toolbarScroll{flex:1 1 auto;min-width:0;display:flex;align-items:center;overflow-x:auto;overflow-y:hidden;padding-bottom:5px}
|
|
4712
|
+
.dsov-toolbarFixed{flex:none;display:flex;align-items:center;gap:8px;margin-left:auto;padding-bottom:5px}
|
|
4713
|
+
.dsov-iconBtn{display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 10px;border-radius:8px;border:.5px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;cursor:pointer;white-space:nowrap;flex:none}
|
|
4714
|
+
.dsov-turnSelect{flex:none}
|
|
4139
4715
|
.dsov-iconBtn:hover{background:color-mix(in srgb,var(--dsw-alias-label-primary) 7%,transparent);color:var(--dsw-alias-label-primary)}
|
|
4140
4716
|
.dsov-section{background:var(--dsw-alias-bg-layer-1);border:.5px solid var(--dsw-alias-border-l1);border-radius:12px;padding:12px 14px;display:flex;flex-direction:column;gap:8px}
|
|
4141
4717
|
.dsov-sectionHead{display:flex;align-items:center;gap:6px}
|
|
@@ -4143,6 +4719,9 @@ function FileTypeIcon(props) {
|
|
|
4143
4719
|
.dsov-sectionToggle:hover{background:color-mix(in srgb,var(--dsw-alias-label-primary) 6%,transparent)}
|
|
4144
4720
|
.dsov-chevron{flex:none;display:inline-flex;color:var(--dsw-alias-label-secondary);transition:transform .15s ease}
|
|
4145
4721
|
.dsov-chevron[data-open=true]{transform:rotate(90deg)}
|
|
4722
|
+
.dsov-moreSteps{appearance:none;background:transparent;border:none;cursor:pointer;display:inline-flex;align-items:center;gap:4px;flex:none;margin-left:auto;padding:1px 6px;border-radius:8px;font-size:10.5px;line-height:16px;color:var(--dsw-alias-label-secondary);white-space:nowrap}
|
|
4723
|
+
.dsov-moreSteps:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
|
|
4724
|
+
.dsov-moreIcon{flex:none;display:inline-flex;opacity:.75}
|
|
4146
4725
|
.dsov-sectionIcon{flex:none;display:inline-flex;color:var(--dsw-alias-label-secondary)}
|
|
4147
4726
|
.dsov-sectionTitle{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);white-space:nowrap}
|
|
4148
4727
|
.dsov-count{flex:none;font-size:11px;line-height:16px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);border-radius:999px;padding:0 8px;font-variant-numeric:tabular-nums}
|
|
@@ -4178,7 +4757,7 @@ function FileTypeIcon(props) {
|
|
|
4178
4757
|
.dsov-todoText{flex:1;min-width:0;overflow-wrap:anywhere}
|
|
4179
4758
|
.dsov-todo[data-status=completed] .dsov-todoText{color:var(--dsw-alias-label-secondary)}
|
|
4180
4759
|
.dsov-todoStatus{flex:none;font-size:11px;line-height:20px;color:var(--dsw-alias-label-secondary)}
|
|
4181
|
-
.dsov-stepsHead{font-size:10.5px;line-height:16px;color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));letter-spacing:.02em;padding:1px 2px 4px}
|
|
4760
|
+
.dsov-stepsHead{display:flex;align-items:center;gap:8px;font-size:10.5px;line-height:16px;color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));letter-spacing:.02em;padding:1px 2px 4px}
|
|
4182
4761
|
.dsov-steps{position:relative;display:flex;flex-direction:column;gap:3px}
|
|
4183
4762
|
.dsov-step{display:flex;align-items:center;gap:6px;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-primary);padding:3px 6px;min-width:0;border-radius:8px;cursor:pointer;transition:background-color .18s ease}
|
|
4184
4763
|
.dsov-step:hover{background:color-mix(in srgb,var(--dsw-alias-label-primary) 5%,transparent)}
|
|
@@ -4192,6 +4771,18 @@ function FileTypeIcon(props) {
|
|
|
4192
4771
|
.dsov-step[data-waiting=true]{background:color-mix(in srgb,var(--dsw-alias-state-warn-primary,#ff9f0a) 9%,transparent)}
|
|
4193
4772
|
.dsov-step[data-waiting=true] .dsov-stepName{color:var(--dsw-alias-state-warn-primary,#ff9f0a)}
|
|
4194
4773
|
.dsov-step[data-waiting=true] .dsov-todoIcon[data-status=waiting]{color:var(--dsw-alias-state-warn-primary,#ff9f0a)}
|
|
4774
|
+
.dsov-agentIcon{color:var(--dsw-alias-brand-primary)}
|
|
4775
|
+
.dsov-memberName{flex:none;font-weight:500}
|
|
4776
|
+
.dsov-memberDesc{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;color:var(--dsw-alias-label-secondary)}
|
|
4777
|
+
.dsov-todo .dsov-chevron{margin-top:4px}
|
|
4778
|
+
.dsov-todoIcon[data-status=failed]{color:var(--dsw-alias-state-error-primary)}
|
|
4779
|
+
.dsov-taskSubject{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
4780
|
+
.dsov-todo[data-depth="1"]{position:relative;margin-left:26px}
|
|
4781
|
+
.dsov-todo[data-depth="2"]{position:relative;margin-left:46px}
|
|
4782
|
+
.dsov-todo[data-depth]::before{content:"";position:absolute;left:-15px;top:-4px;bottom:50%;width:9px;border-left:1px solid var(--dsw-alias-border-l2);border-bottom:1px solid var(--dsw-alias-border-l2);border-bottom-left-radius:6px}
|
|
4783
|
+
.dsov-member[data-expandable=false]{cursor:default}
|
|
4784
|
+
.dsov-member[data-expandable=false]:hover{background:transparent}
|
|
4785
|
+
.dsov-stepChevron{flex:none;margin-right:-2px}
|
|
4195
4786
|
.dsov-row{display:flex;align-items:center;gap:10px;width:100%;text-align:left;padding:6px 8px;margin:0 -8px;border-radius:10px;border:none;background:transparent;cursor:pointer;font:inherit;color:inherit;box-sizing:content-box}
|
|
4196
4787
|
.dsov-row:hover{background:color-mix(in srgb,var(--dsw-alias-label-primary) 6%,transparent)}
|
|
4197
4788
|
.dsov-rowStatic{cursor:default}
|
|
@@ -4214,13 +4805,15 @@ function FileTypeIcon(props) {
|
|
|
4214
4805
|
.dsov-settingsPage{display:flex;flex-direction:column;gap:14px;padding:6px 2px;max-width:560px}
|
|
4215
4806
|
.dsov-settingsHeading{font-size:16px;font-weight:600;color:var(--dsw-alias-label-primary);margin:0}
|
|
4216
4807
|
.dsov-set-prefs{padding:4px 0}
|
|
4217
|
-
.dsov-set-group{border-top:.5px solid var(--dsw-alias-border-l2);margin-top:
|
|
4808
|
+
.dsov-set-group{border-top:.5px solid var(--dsw-alias-border-l2);margin-top:20px;padding-top:16px}
|
|
4218
4809
|
.dsov-set-group:first-child{border-top:none;margin-top:0;padding-top:0}
|
|
4219
|
-
.dsov-set-
|
|
4220
|
-
.dsov-set-
|
|
4221
|
-
.dsov-set-
|
|
4810
|
+
.dsov-set-link{display:inline-flex;align-items:center;gap:6px;padding:6px 0;font-size:13px;line-height:20px;color:var(--dsw-alias-brand-primary);text-decoration:none;width:fit-content}
|
|
4811
|
+
.dsov-set-link:hover{text-decoration:underline}
|
|
4812
|
+
.dsov-set-group-title{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600;line-height:18px;letter-spacing:.02em;margin-bottom:10px}
|
|
4813
|
+
.dsov-set-row{align-items:center;gap:10px;padding:8px 0;display:flex}
|
|
4814
|
+
.dsov-set-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:400;line-height:20px}
|
|
4222
4815
|
.dsov-set-control{position:relative;flex:none;display:inline-flex}
|
|
4223
|
-
.dsov-set-select{background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-2));height:
|
|
4816
|
+
.dsov-set-select{background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-2));height:32px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border:none;border-radius:16px;align-items:center;gap:10px;padding:0 12px;font-size:13px;line-height:20px;display:inline-flex}
|
|
4224
4817
|
.dsov-set-select:hover{background:var(--dsw-alias-interactive-bg-hover,color-mix(in srgb,var(--dsw-alias-label-primary) 8%,transparent))}
|
|
4225
4818
|
.dsov-set-selectLabel{max-width:180px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
4226
4819
|
.dsov-set-caret{flex:none;display:inline-flex;color:var(--dsw-alias-label-secondary);transform:rotate(90deg);transition:transform .15s ease}
|
|
@@ -4229,11 +4822,16 @@ function FileTypeIcon(props) {
|
|
|
4229
4822
|
.dsov-set-menuItem{display:flex;align-items:center;gap:8px;width:100%;padding:7px 10px;border:none;border-radius:7px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;text-align:left;cursor:pointer}
|
|
4230
4823
|
.dsov-set-menuItem:hover{background:color-mix(in srgb,var(--dsw-alias-label-primary) 7%,transparent)}
|
|
4231
4824
|
.dsov-set-menuItem[data-selected=true]{color:var(--dsw-alias-brand-primary);font-weight:500}
|
|
4232
|
-
.dsov-set-note{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));margin:
|
|
4233
|
-
.dsov-set-note+.dsov-set-note{margin-top:
|
|
4825
|
+
.dsov-set-note{color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));margin:4px 0 0;font-size:12px;line-height:1.55}
|
|
4826
|
+
.dsov-set-note+.dsov-set-note{margin-top:2px}
|
|
4234
4827
|
.dsov-set-textrow{flex-direction:column;align-items:stretch;gap:6px}
|
|
4235
4828
|
.dsov-set-input{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4,var(--dsw-alias-border-l2));border-radius:var(--dsw-radius-md,8px);background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2));width:100%;color:var(--dsw-alias-label-primary);font:inherit;padding:6px 12px;font-size:13px;line-height:1.5}
|
|
4236
4829
|
.dsov-set-input:focus{outline:none;border-color:color-mix(in srgb,var(--dsw-alias-brand-primary) 55%,transparent)}
|
|
4830
|
+
.dsov-set-arearow{display:block}
|
|
4831
|
+
.dsov-set-arearow .dsov-set-label{display:block;margin-bottom:6px}
|
|
4832
|
+
.dsov-set-textarea{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4,var(--dsw-alias-border-l2));border-radius:var(--dsw-radius-md,8px);background:var(--dsw-alias-bg-layer-3,var(--dsw-alias-bg-layer-2));width:100%;color:var(--dsw-alias-label-primary);font:inherit;font-size:12.5px;line-height:1.55;padding:8px 12px;resize:vertical}
|
|
4833
|
+
.dsov-set-textarea:focus{outline:none;border-color:color-mix(in srgb,var(--dsw-alias-brand-primary) 55%,transparent)}
|
|
4834
|
+
.dsov-set-textarea::placeholder{color:var(--dsw-alias-label-quaternary,var(--dsw-alias-label-secondary));opacity:.75}
|
|
4237
4835
|
html[data-dsov-quicknav="on"] nav:has(button[data-index][aria-label]),html[data-dsov-quicknav="on"] nav[class*="_frame"]{display:block!important}
|
|
4238
4836
|
html[data-dsov-quicknav="off"] nav:has(button[data-index][aria-label]),html[data-dsov-quicknav="off"] nav[class*="_frame"]{display:none!important}
|
|
4239
4837
|
.dsov-viking{box-sizing:border-box;height:100%;min-height:0;overflow-y:auto;padding:14px 16px 28px;display:flex;flex-direction:column;gap:10px}
|
|
@@ -4267,8 +4865,14 @@ html[data-dsov-quicknav="off"] nav:has(button[data-index][aria-label]),html[data
|
|
|
4267
4865
|
.dsov-filterBtn{display:inline-flex;align-items:center;gap:4px;height:20px;padding:0 8px;border:.5px solid var(--dsw-alias-border-l2);border-radius:10px;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:10.5px;line-height:1;cursor:pointer;white-space:nowrap}
|
|
4268
4866
|
.dsov-filterBtn:hover{color:var(--dsw-alias-label-primary);background:color-mix(in srgb,var(--dsw-alias-label-primary) 6%,transparent)}
|
|
4269
4867
|
.dsov-filterBtn[data-active=true]{color:var(--dsw-alias-brand-primary);border-color:color-mix(in srgb,var(--dsw-alias-brand-primary) 45%,transparent);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 8%,transparent)}
|
|
4270
|
-
.dsov-turnbar{display:flex;justify-content:flex-
|
|
4271
|
-
.dsov-
|
|
4868
|
+
.dsov-turnbar{display:flex;justify-content:flex-start;align-items:center;gap:8px;padding:0}
|
|
4869
|
+
.dsov-turnbarScroll{flex:1 1 auto;min-width:0;display:flex;align-items:center;overflow-x:auto;overflow-y:hidden;padding-bottom:5px}
|
|
4870
|
+
.dsov-turnbarFixed{flex:none;display:flex;align-items:center;margin-left:auto;padding-bottom:5px}
|
|
4871
|
+
.dsov-panel .dsov-turnbarWrap{position:relative;z-index:5;margin:-14px -16px 0;padding:8px 16px 3px;background:transparent;transition:background-color .18s ease,box-shadow .18s ease}
|
|
4872
|
+
.dsov-panel .dsov-turnbarWrap[data-stuck=true]{background:var(--dsw-specific-sidebar-fill,var(--dsw-alias-bg-base));box-shadow:0 6px 10px -8px color-mix(in srgb,var(--dsw-alias-label-primary) 25%,transparent)}
|
|
4873
|
+
.dsov-view .dsov-toolbarWrap{position:relative;z-index:4;margin:-20px -28px 0;padding:10px 28px 5px;background:transparent;transition:background-color .18s ease,box-shadow .18s ease}
|
|
4874
|
+
.dsov-view .dsov-toolbarWrap[data-stuck=true]{background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-1));backdrop-filter:var(--dsw-menu-backdrop-filter,none);box-shadow:0 6px 10px -8px color-mix(in srgb,var(--dsw-alias-label-primary) 22%,transparent)}
|
|
4875
|
+
.dsov-stats{display:flex;align-items:center;gap:5px;margin-right:auto;flex:none}
|
|
4272
4876
|
.dsov-stat{flex:none;font-size:10.5px;line-height:18px;padding:0 8px;border-radius:999px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);white-space:nowrap;font-variant-numeric:tabular-nums}
|
|
4273
4877
|
.dsov-stat[data-tone=err]{color:var(--dsw-alias-state-error-primary,#ff453a);background:color-mix(in srgb,var(--dsw-alias-state-error-primary,#ff453a) 10%,transparent)}
|
|
4274
4878
|
.dsov-turnSelect{flex:none;max-width:150px;height:24px;padding:0 22px 0 9px;border:.5px solid var(--dsw-alias-border-l2);border-radius:12px;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:11px;cursor:pointer;appearance:none;-webkit-appearance:none;background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,transparent 50%);background-position:calc(100% - 12px) 10px,calc(100% - 8px) 10px;background-size:4px 4px,4px 4px;background-repeat:no-repeat}
|
|
@@ -4287,6 +4891,12 @@ html[data-dsov-quicknav="off"] nav:has(button[data-index][aria-label]),html[data
|
|
|
4287
4891
|
.dsov-segBtn{border:none;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:11px;line-height:18px;padding:0 8px;border-radius:6px;cursor:pointer;white-space:nowrap}
|
|
4288
4892
|
.dsov-segBtn:hover{color:var(--dsw-alias-label-primary)}
|
|
4289
4893
|
.dsov-segBtn[data-active=true]{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font-weight:500;box-shadow:0 0 0 .5px var(--dsw-alias-border-l1)}
|
|
4894
|
+
.dsov-refControls .dsov-seg{display:inline-flex;align-items:stretch;gap:0;padding:0;border:.5px solid var(--dsw-alias-border-l2);border-radius:10px;background:transparent;overflow:hidden}
|
|
4895
|
+
.dsov-refControls .dsov-segBtn{height:19px;align-self:center;line-height:1;padding:0 9px;border:none;border-radius:0;background:transparent;color:var(--dsw-alias-label-secondary);font-size:10.5px}
|
|
4896
|
+
.dsov-refControls .dsov-segBtn+.dsov-segBtn{border-left:.5px solid var(--dsw-alias-border-l2)}
|
|
4897
|
+
.dsov-refControls .dsov-segBtn:hover{color:var(--dsw-alias-label-primary);background:color-mix(in srgb,var(--dsw-alias-label-primary) 6%,transparent)}
|
|
4898
|
+
.dsov-refControls .dsov-segBtn[data-active=true]{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 8%,transparent);font-weight:500;box-shadow:none}
|
|
4899
|
+
.dsov-refControls .dsov-segBtn[data-active=true]:hover{background:color-mix(in srgb,var(--dsw-alias-brand-primary) 12%,transparent)}
|
|
4290
4900
|
.dsov-vikingList{display:flex;flex-direction:column}
|
|
4291
4901
|
.dsov-vikingSize{font-size:11px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}
|
|
4292
4902
|
.dsov-vikingSource{white-space:pre-wrap;overflow-wrap:anywhere;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l1);border-radius:8px;padding:10px 12px;margin:0}
|
|
@@ -4298,6 +4908,9 @@ html[data-dsov-quicknav="off"] nav:has(button[data-index][aria-label]),html[data
|
|
|
4298
4908
|
.dsov-vikingTags{display:flex;flex-wrap:wrap;gap:5px}
|
|
4299
4909
|
.dsov-tag{font-size:11px;line-height:19px;padding:0 8px;border-radius:999px;color:var(--dsw-alias-brand-primary);background:color-mix(in srgb,var(--dsw-alias-brand-primary) 10%,transparent);border:.5px solid color-mix(in srgb,var(--dsw-alias-brand-primary) 30%,transparent)}
|
|
4300
4910
|
.dsov-vikingFieldValue{font-size:12.5px;line-height:1.6;color:var(--dsw-alias-label-primary);overflow-wrap:anywhere}
|
|
4911
|
+
.dsov-vikingSource{display:flex;flex-direction:column;gap:2px}
|
|
4912
|
+
.dsov-vikingSourceLink{font-size:12.5px;line-height:1.6;color:var(--dsw-alias-brand-primary);text-decoration:none;overflow-wrap:anywhere}
|
|
4913
|
+
.dsov-vikingSourceLink:hover{text-decoration:underline}
|
|
4301
4914
|
.dsov-vikingError{display:flex;flex-direction:column;align-items:flex-start;gap:8px;font-size:13px;color:var(--dsw-alias-state-error-primary)}
|
|
4302
4915
|
.dsov-vikingErrorDetail{font-size:12px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}
|
|
4303
4916
|
.dsov-md{font-size:13.5px;line-height:1.65;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:2px}
|
|
@@ -4698,6 +5311,7 @@ html[data-dsov-quicknav="off"] nav:has(button[data-index][aria-label]),html[data
|
|
|
4698
5311
|
refDisplayType,
|
|
4699
5312
|
stepDetail,
|
|
4700
5313
|
stepFull,
|
|
5314
|
+
teamStepKey,
|
|
4701
5315
|
formatDuration,
|
|
4702
5316
|
rerunPrompt,
|
|
4703
5317
|
insertIntoComposer,
|