dsh-brief-sidebar 0.9.2 → 0.9.3
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/dsh.plugin.json +1 -1
- package/lib/client.js +537 -378
- package/lib/client.js.map +1 -1
- package/lib/types/client/locales.d.ts +3 -0
- package/lib/types/client/summary/ReferenceSection.d.ts +24 -0
- package/lib/types/client/summary/SkillUsageSection.d.ts +6 -0
- package/package.json +1 -1
- package/src/client/SummaryTab.tsx +12 -18
- package/src/client/locales.ts +27 -0
- package/src/client/summary/ReferenceSection.tsx +193 -0
- package/src/client/summary/SkillUsageSection.tsx +8 -2
package/lib/client.js
CHANGED
|
@@ -231,11 +231,11 @@ window.__ModuleLoader__.load({
|
|
|
231
231
|
in_progress: "var(--dsw-alias-state-business-primary)",
|
|
232
232
|
pending: "var(--dsw-alias-label-tertiary)"
|
|
233
233
|
};
|
|
234
|
-
const SECTION_STYLE$
|
|
234
|
+
const SECTION_STYLE$5 = {
|
|
235
235
|
flexShrink: 0,
|
|
236
236
|
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
237
237
|
};
|
|
238
|
-
const HEADER_STYLE$
|
|
238
|
+
const HEADER_STYLE$6 = {
|
|
239
239
|
padding: "10px 12px 0",
|
|
240
240
|
color: "var(--dsw-alias-label-secondary)",
|
|
241
241
|
fontSize: 12,
|
|
@@ -316,24 +316,24 @@ window.__ModuleLoader__.load({
|
|
|
316
316
|
const { t, ctx, scope } = props;
|
|
317
317
|
const todos = useTodos(ctx, scope?.sessionId);
|
|
318
318
|
if (todos === void 0) return (0, react.createElement)("div", {
|
|
319
|
-
style: SECTION_STYLE$
|
|
319
|
+
style: SECTION_STYLE$5,
|
|
320
320
|
"data-dsh-brief-sidebar": "progress-section"
|
|
321
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
321
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$6 }, t("section.progress")), (0, react.createElement)(Notice, {
|
|
322
322
|
title: t("board.unavailable"),
|
|
323
323
|
detail: t("board.unavailableHint")
|
|
324
324
|
}));
|
|
325
325
|
if (todos.length === 0) return (0, react.createElement)("div", {
|
|
326
|
-
style: SECTION_STYLE$
|
|
326
|
+
style: SECTION_STYLE$5,
|
|
327
327
|
"data-dsh-brief-sidebar": "progress-section"
|
|
328
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
328
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$6 }, t("section.progress")), (0, react.createElement)(Notice, {
|
|
329
329
|
title: t("board.empty"),
|
|
330
330
|
detail: t("board.emptyHint")
|
|
331
331
|
}));
|
|
332
332
|
const counts = countTodos(todos);
|
|
333
333
|
return (0, react.createElement)("div", {
|
|
334
|
-
style: SECTION_STYLE$
|
|
334
|
+
style: SECTION_STYLE$5,
|
|
335
335
|
"data-dsh-brief-sidebar": "progress-section"
|
|
336
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
336
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$6 }, t("section.progress")), (0, react.createElement)("div", { style: PROGRESS_STYLE }, progressLine(counts, t)), (0, react.createElement)("ul", { style: LIST_STYLE$4 }, todos.map((item, index) => (0, react.createElement)(TodoRow, {
|
|
337
337
|
key: `${index}:${item.content}`,
|
|
338
338
|
item,
|
|
339
339
|
t
|
|
@@ -688,6 +688,9 @@ window.__ModuleLoader__.load({
|
|
|
688
688
|
"recall.supersededTag": "已被摘要取代",
|
|
689
689
|
"recall.noBody": "(本帧没有这条记忆的全文)",
|
|
690
690
|
"section.skillUsage": "技能引用",
|
|
691
|
+
"section.references": "引用",
|
|
692
|
+
"references.tabSkills": "技能",
|
|
693
|
+
"references.tabMemory": "记忆",
|
|
691
694
|
"skillUsage.empty": "本会话还没载入过技能",
|
|
692
695
|
"skillUsage.emptyHint": "模型用 skill 工具载入、或你直接点用某个技能后,技能名会实时出现在这里。",
|
|
693
696
|
"skillUsage.uses": "{count} 次",
|
|
@@ -748,6 +751,9 @@ window.__ModuleLoader__.load({
|
|
|
748
751
|
"recall.supersededTag": "replaced by summary",
|
|
749
752
|
"recall.noBody": "(this frame carries no full body for this record)",
|
|
750
753
|
"section.skillUsage": "Skill references",
|
|
754
|
+
"section.references": "References",
|
|
755
|
+
"references.tabSkills": "Skills",
|
|
756
|
+
"references.tabMemory": "Memory",
|
|
751
757
|
"skillUsage.empty": "No skills loaded in this session",
|
|
752
758
|
"skillUsage.emptyHint": "Skills show up here once the model loads one with the `skill` tool, or you invoke one directly.",
|
|
753
759
|
"skillUsage.uses": "{count}x",
|
|
@@ -808,6 +814,9 @@ window.__ModuleLoader__.load({
|
|
|
808
814
|
"recall.supersededTag": "要約に置換済み",
|
|
809
815
|
"recall.noBody": "このフレームには全文が含まれていない",
|
|
810
816
|
"section.skillUsage": "スキル参照",
|
|
817
|
+
"section.references": "参照",
|
|
818
|
+
"references.tabSkills": "スキル",
|
|
819
|
+
"references.tabMemory": "メモリ",
|
|
811
820
|
"skillUsage.empty": "このセッションではスキルを読み込んでいません",
|
|
812
821
|
"skillUsage.emptyHint": "モデルが skill ツールで読み込むか、あなたがスキルを直接起動すると、ここにリアルタイムで表示されます。",
|
|
813
822
|
"skillUsage.uses": "{count} 回",
|
|
@@ -868,6 +877,9 @@ window.__ModuleLoader__.load({
|
|
|
868
877
|
"recall.supersededTag": "요약에 대체됨",
|
|
869
878
|
"recall.noBody": "이 프레임에는 이 기록의 전문이 없습니다",
|
|
870
879
|
"section.skillUsage": "스킬 참조",
|
|
880
|
+
"section.references": "참조",
|
|
881
|
+
"references.tabSkills": "스킬",
|
|
882
|
+
"references.tabMemory": "메모리",
|
|
871
883
|
"skillUsage.empty": "이 세션에서 스킬을 불러오지 않았습니다",
|
|
872
884
|
"skillUsage.emptyHint": "모델이 skill 도구로 불러오거나 당신이 스킬을 직접 실행하면 여기에 실시간으로 나타납니다.",
|
|
873
885
|
"skillUsage.uses": "{count}회",
|
|
@@ -928,6 +940,9 @@ window.__ModuleLoader__.load({
|
|
|
928
940
|
"recall.supersededTag": "remplacé par le résumé",
|
|
929
941
|
"recall.noBody": "(aucun texte complet pour cette entrée dans cette trame)",
|
|
930
942
|
"section.skillUsage": "Références de compétences",
|
|
943
|
+
"section.references": "Références",
|
|
944
|
+
"references.tabSkills": "Compétences",
|
|
945
|
+
"references.tabMemory": "Mémoire",
|
|
931
946
|
"skillUsage.empty": "Aucune compétence chargée dans cette session",
|
|
932
947
|
"skillUsage.emptyHint": "Les compétences apparaissent ici dès que le modèle en charge une via l’outil `skill`, ou quand vous en lancez une directement.",
|
|
933
948
|
"skillUsage.uses": "{count}×",
|
|
@@ -988,6 +1003,9 @@ window.__ModuleLoader__.load({
|
|
|
988
1003
|
"recall.supersededTag": "durch Zusammenfassung ersetzt",
|
|
989
1004
|
"recall.noBody": "(dieser Rahmen enthält keinen vollständigen Text für diesen Eintrag)",
|
|
990
1005
|
"section.skillUsage": "Skill-Referenzen",
|
|
1006
|
+
"section.references": "Referenzen",
|
|
1007
|
+
"references.tabSkills": "Skills",
|
|
1008
|
+
"references.tabMemory": "Speicher",
|
|
991
1009
|
"skillUsage.empty": "In dieser Sitzung wurden keine Skills geladen",
|
|
992
1010
|
"skillUsage.emptyHint": "Skills erscheinen hier, sobald das Modell einen über das `skill`-Tool lädt oder du einen direkt aufrufst.",
|
|
993
1011
|
"skillUsage.uses": "{count}x",
|
|
@@ -1048,6 +1066,9 @@ window.__ModuleLoader__.load({
|
|
|
1048
1066
|
"recall.supersededTag": "sostituito dal riassunto",
|
|
1049
1067
|
"recall.noBody": "(questo frame non contiene il testo completo di questa voce)",
|
|
1050
1068
|
"section.skillUsage": "Riferimenti alle skill",
|
|
1069
|
+
"section.references": "Riferimenti",
|
|
1070
|
+
"references.tabSkills": "Skill",
|
|
1071
|
+
"references.tabMemory": "Memoria",
|
|
1051
1072
|
"skillUsage.empty": "Nessuna skill caricata in questa sessione",
|
|
1052
1073
|
"skillUsage.emptyHint": "Le skill compaiono qui quando il modello ne carica una con il tool `skill` o quando ne avvii una direttamente.",
|
|
1053
1074
|
"skillUsage.uses": "{count}x",
|
|
@@ -1108,6 +1129,9 @@ window.__ModuleLoader__.load({
|
|
|
1108
1129
|
"recall.supersededTag": "заменено сводкой",
|
|
1109
1130
|
"recall.noBody": "В этом кадре нет полного текста этой записи",
|
|
1110
1131
|
"section.skillUsage": "Отсылки к скиллам",
|
|
1132
|
+
"section.references": "Отсылки",
|
|
1133
|
+
"references.tabSkills": "Навыки",
|
|
1134
|
+
"references.tabMemory": "Память",
|
|
1111
1135
|
"skillUsage.empty": "В этой сессии скиллы не загружались",
|
|
1112
1136
|
"skillUsage.emptyHint": "Навыки появятся здесь, когда модель загрузит их через инструмент `skill` или вы вызовете навык напрямую.",
|
|
1113
1137
|
"skillUsage.uses": "{count} раз",
|
|
@@ -1168,6 +1192,9 @@ window.__ModuleLoader__.load({
|
|
|
1168
1192
|
"recall.supersededTag": "reemplazado por el resumen",
|
|
1169
1193
|
"recall.noBody": "(este fotograma no trae el texto completo de este registro)",
|
|
1170
1194
|
"section.skillUsage": "Referencias de skills",
|
|
1195
|
+
"section.references": "Referencias",
|
|
1196
|
+
"references.tabSkills": "Skills",
|
|
1197
|
+
"references.tabMemory": "Memoria",
|
|
1171
1198
|
"skillUsage.empty": "Esta sesión no ha cargado ningún skill",
|
|
1172
1199
|
"skillUsage.emptyHint": "Las skills aparecen aquí cuando el modelo carga una con la herramienta `skill` o cuando invocas una directamente.",
|
|
1173
1200
|
"skillUsage.uses": "{count}x",
|
|
@@ -1222,11 +1249,11 @@ window.__ModuleLoader__.load({
|
|
|
1222
1249
|
*
|
|
1223
1250
|
* Colours come from `--dsw-alias-*` tokens only (skin contract).
|
|
1224
1251
|
*/
|
|
1225
|
-
const SECTION_STYLE$
|
|
1252
|
+
const SECTION_STYLE$4 = {
|
|
1226
1253
|
flexShrink: 0,
|
|
1227
1254
|
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
1228
1255
|
};
|
|
1229
|
-
const HEADER_STYLE$
|
|
1256
|
+
const HEADER_STYLE$5 = {
|
|
1230
1257
|
padding: "10px 12px 0",
|
|
1231
1258
|
color: "var(--dsw-alias-label-secondary)",
|
|
1232
1259
|
fontSize: 12,
|
|
@@ -1315,9 +1342,9 @@ window.__ModuleLoader__.load({
|
|
|
1315
1342
|
const latestPaths = view.latest?.paths ?? [];
|
|
1316
1343
|
const history = historyPaths(view.sessionPaths, latestPaths);
|
|
1317
1344
|
if (latestPaths.length === 0 && view.sessionTotal === 0) return (0, react.createElement)("div", {
|
|
1318
|
-
style: SECTION_STYLE$
|
|
1345
|
+
style: SECTION_STYLE$4,
|
|
1319
1346
|
"data-dsh-brief-sidebar": "deliverables-section"
|
|
1320
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
1347
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$5 }, t("section.deliverables")), (0, react.createElement)("div", { style: {
|
|
1321
1348
|
margin: "0 auto",
|
|
1322
1349
|
padding: 24,
|
|
1323
1350
|
maxWidth: 460,
|
|
@@ -1330,9 +1357,9 @@ window.__ModuleLoader__.load({
|
|
|
1330
1357
|
color: "var(--dsw-alias-label-secondary)"
|
|
1331
1358
|
} }, t("deliverables.empty")), (0, react.createElement)("div", null, t("deliverables.emptyHint"))));
|
|
1332
1359
|
return (0, react.createElement)("div", {
|
|
1333
|
-
style: SECTION_STYLE$
|
|
1360
|
+
style: SECTION_STYLE$4,
|
|
1334
1361
|
"data-dsh-brief-sidebar": "deliverables-section"
|
|
1335
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
1362
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$5 }, t("section.deliverables")), latestPaths.length > 0 && (0, react.createElement)(GroupHeader, {
|
|
1336
1363
|
text: expandCount(t("deliverables.currentTurn"), latestPaths.length),
|
|
1337
1364
|
marker: "deliverables-current-group"
|
|
1338
1365
|
}), (0, react.createElement)("ul", { style: LIST_STYLE$3 }, latestPaths.map((path) => (0, react.createElement)(PathRow, {
|
|
@@ -1456,137 +1483,82 @@ window.__ModuleLoader__.load({
|
|
|
1456
1483
|
return template.replace("{count}", String(count)).replace("{total}", String(total));
|
|
1457
1484
|
}
|
|
1458
1485
|
//#endregion
|
|
1459
|
-
//#region src/client/summary/
|
|
1486
|
+
//#region src/client/summary/memory-recall.ts
|
|
1460
1487
|
/**
|
|
1461
|
-
* The
|
|
1462
|
-
* part of the summary tab.
|
|
1463
|
-
*
|
|
1464
|
-
* Where `MemoryRecallSection` answers "what memories did the model see", this
|
|
1465
|
-
* answers "which instruction packs entered the context, and which ones were on
|
|
1466
|
-
* the menu". Two groups, deliberately not merged:
|
|
1467
|
-
*
|
|
1468
|
-
* - **已引用** — the skill's instructions reached the conversation, either
|
|
1469
|
-
* because the model called the `skill` tool or because the user invoked it
|
|
1470
|
-
* directly (the host then injects `<skill_content>` and tells the model not
|
|
1471
|
-
* to call the tool — reading only the tool would miss over half of those
|
|
1472
|
-
* sessions, measured on 250 real ones).
|
|
1473
|
-
* - **本会话可用** — the session's current catalog. Available is NOT used: the
|
|
1474
|
-
* model may never touch any of these, so it is collapsed behind a counted
|
|
1475
|
-
* toggle and never mixed into the reference rows.
|
|
1488
|
+
* The `memoryRecall` projection — reading it.
|
|
1476
1489
|
*
|
|
1477
|
-
*
|
|
1478
|
-
*
|
|
1479
|
-
*
|
|
1480
|
-
*
|
|
1481
|
-
*
|
|
1490
|
+
* The projection unit is registered by `dsh-prime-memory` (key `memoryRecall`,
|
|
1491
|
+
* wire view `{rev, sessionId, count, items[]}`): the memories this session has
|
|
1492
|
+
* actually injected. Unlike
|
|
1493
|
+
* `memorySlots` (pinned, resident conventions written by hand), this set is
|
|
1494
|
+
* NOT resident — it is the at-a-glance answer to "what did the model see".
|
|
1495
|
+
* It survives compaction on purpose: `compact` only marks the entries whose
|
|
1496
|
+
* text left the context as `superseded` (and `/clear` empties the set), so a
|
|
1497
|
+
* retired row keeps its strike-through instead of vanishing.
|
|
1482
1498
|
*
|
|
1483
|
-
*
|
|
1484
|
-
*
|
|
1485
|
-
*
|
|
1486
|
-
*
|
|
1499
|
+
* This plugin does NOT depend on prime-memory: with the unit unregistered the
|
|
1500
|
+
* client reads `undefined` from the face and the section hides. The module is
|
|
1501
|
+
* deliberately PURE — no React, no context, no IO — so the narrowing is
|
|
1502
|
+
* testable directly, exactly like `memory-slots.ts`.
|
|
1487
1503
|
*/
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
const COUNT_STYLE = {
|
|
1519
|
-
flexShrink: 0,
|
|
1520
|
-
color: "var(--dsw-alias-label-tertiary)",
|
|
1521
|
-
fontSize: 11
|
|
1522
|
-
};
|
|
1523
|
-
/** The "本轮" pill: same form as the deliverables codeplan tag. */
|
|
1524
|
-
const TAG_STYLE = {
|
|
1525
|
-
flexShrink: 0,
|
|
1526
|
-
color: "var(--dsw-alias-label-tertiary)",
|
|
1527
|
-
border: "1px solid var(--dsw-alias-label-tertiary)",
|
|
1528
|
-
borderRadius: 999,
|
|
1529
|
-
padding: "0 6px",
|
|
1530
|
-
fontSize: 11,
|
|
1531
|
-
lineHeight: "16px",
|
|
1532
|
-
whiteSpace: "nowrap"
|
|
1533
|
-
};
|
|
1534
|
-
const FOOTER_STYLE$2 = {
|
|
1535
|
-
padding: "0 12px 10px",
|
|
1536
|
-
color: "var(--dsw-alias-label-tertiary)",
|
|
1537
|
-
fontSize: 11
|
|
1538
|
-
};
|
|
1539
|
-
const EMPTY_STYLE$2 = {
|
|
1540
|
-
margin: "0 auto",
|
|
1541
|
-
padding: 24,
|
|
1542
|
-
maxWidth: 460,
|
|
1543
|
-
textAlign: "center",
|
|
1544
|
-
color: "var(--dsw-alias-label-tertiary)",
|
|
1545
|
-
lineHeight: 1.7,
|
|
1546
|
-
fontSize: 12
|
|
1547
|
-
};
|
|
1504
|
+
/** The projection key `dsh-prime-memory` registers (its canonical constant). */
|
|
1505
|
+
const MEMORY_RECALL_KEY = "memoryRecall";
|
|
1506
|
+
/** Whether a value is a non-negative safe integer (the view's counter shape). */
|
|
1507
|
+
function isCounter$1(value) {
|
|
1508
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
1509
|
+
}
|
|
1510
|
+
/** Narrow one item entry; `undefined` = malformed, dropped. */
|
|
1511
|
+
function readItem(value) {
|
|
1512
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1513
|
+
const raw = value;
|
|
1514
|
+
if (typeof raw["id"] !== "string" || raw["id"].length === 0) return void 0;
|
|
1515
|
+
if (typeof raw["type"] !== "string" || typeof raw["title"] !== "string") return void 0;
|
|
1516
|
+
if (typeof raw["priority"] !== "number") return void 0;
|
|
1517
|
+
const item = {
|
|
1518
|
+
id: raw["id"],
|
|
1519
|
+
type: raw["type"],
|
|
1520
|
+
title: raw["title"],
|
|
1521
|
+
priority: raw["priority"]
|
|
1522
|
+
};
|
|
1523
|
+
if (Array.isArray(raw["scenario"])) {
|
|
1524
|
+
const phrases = raw["scenario"].filter((s) => typeof s === "string" && s.length > 0);
|
|
1525
|
+
if (phrases.length > 0) item.scenario = phrases;
|
|
1526
|
+
}
|
|
1527
|
+
const rating = raw["rating"];
|
|
1528
|
+
if (typeof rating === "number" && Number.isInteger(rating) && rating >= -1 && rating <= 5) item.rating = rating;
|
|
1529
|
+
if (typeof raw["body"] === "string") item.body = raw["body"];
|
|
1530
|
+
if (raw["retired"] === true) item.retired = true;
|
|
1531
|
+
if (raw["superseded"] === true) item.superseded = true;
|
|
1532
|
+
return item;
|
|
1533
|
+
}
|
|
1548
1534
|
/**
|
|
1549
|
-
*
|
|
1550
|
-
* not IO); the view decides whether anything is drawn.
|
|
1535
|
+
* Narrow a raw projection value into a recall view.
|
|
1551
1536
|
*
|
|
1552
|
-
*
|
|
1537
|
+
* `undefined` means the capability is ABSENT (prime-memory not in the
|
|
1538
|
+
* composition, or no frame carried the key) or the frame drifted beyond repair;
|
|
1539
|
+
* a view with `items: []` IS the honest empty state (prime-memory publishes as
|
|
1540
|
+
* soon as the unit registers). Malformed items are dropped, never thrown on.
|
|
1541
|
+
*
|
|
1542
|
+
* @param value - the raw `unknown` snapshot of the `memoryRecall` face.
|
|
1543
|
+
* @returns the validated view, or `undefined` when the capability is absent.
|
|
1553
1544
|
*/
|
|
1554
|
-
function
|
|
1555
|
-
|
|
1556
|
-
const
|
|
1557
|
-
|
|
1558
|
-
if (
|
|
1559
|
-
|
|
1560
|
-
const
|
|
1561
|
-
const
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
}), (0, react.createElement)("ul", { style: LIST_STYLE$2 }, view.session.map((entry) => (0, react.createElement)("li", {
|
|
1572
|
-
key: entry.name,
|
|
1573
|
-
style: ROW_STYLE$3,
|
|
1574
|
-
"data-dsh-brief-sidebar": "skill-usage-row"
|
|
1575
|
-
}, (0, react.createElement)("span", { style: NAME_STYLE$1 }, entry.name), (0, react.createElement)("span", { style: COUNT_STYLE }, expandPair(t("skillUsage.uses"), entry.count, entry.count)), latestNames.includes(entry.name) && (0, react.createElement)("span", { style: TAG_STYLE }, t("skillUsage.latestTag"))))), rest.length > 0 && (0, react.createElement)(FoldToggle, {
|
|
1576
|
-
label: expandCount(t("skillUsage.availableGroup"), rest.length),
|
|
1577
|
-
open: availableOpen,
|
|
1578
|
-
marker: "skill-usage-available-toggle",
|
|
1579
|
-
onToggle: () => {
|
|
1580
|
-
setAvailableOpen((current) => !current);
|
|
1581
|
-
}
|
|
1582
|
-
}), rest.length > 0 && view.availableCount > view.available.length && (0, react.createElement)(GroupCaption, {
|
|
1583
|
-
text: expandCount(t("skillUsage.availableCapped"), view.availableCount),
|
|
1584
|
-
marker: "skill-usage-available-capped"
|
|
1585
|
-
}), availableOpen && (0, react.createElement)("ul", { style: LIST_STYLE$2 }, rest.map((name) => (0, react.createElement)("li", {
|
|
1586
|
-
key: name,
|
|
1587
|
-
style: ROW_STYLE$3,
|
|
1588
|
-
"data-dsh-brief-sidebar": "skill-usage-available-row"
|
|
1589
|
-
}, (0, react.createElement)("span", { style: NAME_STYLE$1 }, name)))), view.session.length > 0 && (0, react.createElement)("div", { style: FOOTER_STYLE$2 }, expandPair(t("skillUsage.count"), view.callTotal, view.sessionTotal)));
|
|
1545
|
+
function readRecall(value) {
|
|
1546
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1547
|
+
const raw = value;
|
|
1548
|
+
if (!isCounter$1(raw["rev"]) || !isCounter$1(raw["count"])) return void 0;
|
|
1549
|
+
if (typeof raw["sessionId"] !== "string") return void 0;
|
|
1550
|
+
if (!Array.isArray(raw["items"])) return void 0;
|
|
1551
|
+
const items = [];
|
|
1552
|
+
for (const entry of raw["items"]) {
|
|
1553
|
+
const item = readItem(entry);
|
|
1554
|
+
if (item !== void 0) items.push(item);
|
|
1555
|
+
}
|
|
1556
|
+
return {
|
|
1557
|
+
rev: raw["rev"],
|
|
1558
|
+
sessionId: raw["sessionId"],
|
|
1559
|
+
count: raw["count"],
|
|
1560
|
+
items
|
|
1561
|
+
};
|
|
1590
1562
|
}
|
|
1591
1563
|
//#endregion
|
|
1592
1564
|
//#region src/client/summary/memory-slots.ts
|
|
@@ -1629,7 +1601,7 @@ window.__ModuleLoader__.load({
|
|
|
1629
1601
|
function readSlots(value) {
|
|
1630
1602
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1631
1603
|
const raw = value;
|
|
1632
|
-
if (!isCounter
|
|
1604
|
+
if (!isCounter(raw["rev"]) || !isCounter(raw["count"]) || !isCounter(raw["openCount"])) return void 0;
|
|
1633
1605
|
if (!Array.isArray(raw["slots"])) return void 0;
|
|
1634
1606
|
const slots = [];
|
|
1635
1607
|
for (const entry of raw["slots"]) {
|
|
@@ -1644,7 +1616,7 @@ window.__ModuleLoader__.load({
|
|
|
1644
1616
|
};
|
|
1645
1617
|
}
|
|
1646
1618
|
/** Whether a value is a non-negative safe integer (the view's counter shape). */
|
|
1647
|
-
function isCounter
|
|
1619
|
+
function isCounter(value) {
|
|
1648
1620
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
1649
1621
|
}
|
|
1650
1622
|
/** Narrow one slot entry; `undefined` = malformed, dropped. */
|
|
@@ -1755,63 +1727,72 @@ window.__ModuleLoader__.load({
|
|
|
1755
1727
|
return resolve((0, react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot));
|
|
1756
1728
|
}
|
|
1757
1729
|
//#endregion
|
|
1758
|
-
//#region src/client/summary/
|
|
1730
|
+
//#region src/client/summary/SkillUsageSection.tsx
|
|
1759
1731
|
/**
|
|
1760
|
-
* The
|
|
1761
|
-
* part of the summary tab
|
|
1732
|
+
* The skill-usage section: the `dshSummarySkillUsage` projection drawn as one
|
|
1733
|
+
* part of the summary tab.
|
|
1762
1734
|
*
|
|
1763
|
-
*
|
|
1764
|
-
*
|
|
1735
|
+
* Where `MemoryRecallSection` answers "what memories did the model see", this
|
|
1736
|
+
* answers "which instruction packs entered the context, and which ones were on
|
|
1737
|
+
* the menu". Two groups, deliberately not merged:
|
|
1765
1738
|
*
|
|
1766
|
-
*
|
|
1767
|
-
*
|
|
1768
|
-
*
|
|
1769
|
-
*
|
|
1770
|
-
*
|
|
1771
|
-
*
|
|
1772
|
-
*
|
|
1773
|
-
*
|
|
1739
|
+
* - **已引用** — the skill's instructions reached the conversation, either
|
|
1740
|
+
* because the model called the `skill` tool or because the user invoked it
|
|
1741
|
+
* directly (the host then injects `<skill_content>` and tells the model not
|
|
1742
|
+
* to call the tool — reading only the tool would miss over half of those
|
|
1743
|
+
* sessions, measured on 250 real ones).
|
|
1744
|
+
* - **本会话可用** — the session's current catalog. Available is NOT used: the
|
|
1745
|
+
* model may never touch any of these, so it is collapsed behind a counted
|
|
1746
|
+
* toggle and never mixed into the reference rows.
|
|
1774
1747
|
*
|
|
1775
|
-
*
|
|
1776
|
-
*
|
|
1748
|
+
* Like the deliverables section this is an OPTIONAL capability on both sides:
|
|
1749
|
+
* no projection registry (or a host half that never registered the unit) means
|
|
1750
|
+
* the client reads capability absence and the section hides. No settings switch
|
|
1751
|
+
* gates it either — the list is a read-only fact about the session, not a
|
|
1752
|
+
* preference.
|
|
1753
|
+
*
|
|
1754
|
+
* Two contracts this row honours, both learned on the recall section:
|
|
1755
|
+
* - **no native `title` attribute** — it paints an unskinable tooltip that
|
|
1756
|
+
* overlaps the plugin's own bubble; nothing here needs one;
|
|
1757
|
+
* - **colours from `--dsw-alias-*` tokens only** (skin contract).
|
|
1777
1758
|
*/
|
|
1778
|
-
const SECTION_STYLE$
|
|
1759
|
+
const SECTION_STYLE$3 = {
|
|
1779
1760
|
flexShrink: 0,
|
|
1780
1761
|
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
1781
1762
|
};
|
|
1782
|
-
const HEADER_STYLE$
|
|
1763
|
+
const HEADER_STYLE$4 = {
|
|
1783
1764
|
padding: "10px 12px 0",
|
|
1784
1765
|
color: "var(--dsw-alias-label-secondary)",
|
|
1785
1766
|
fontSize: 12,
|
|
1786
1767
|
fontWeight: 600
|
|
1787
1768
|
};
|
|
1788
|
-
const LIST_STYLE$
|
|
1769
|
+
const LIST_STYLE$2 = {
|
|
1789
1770
|
listStyle: "none",
|
|
1790
1771
|
margin: 0,
|
|
1791
1772
|
padding: "6px 0",
|
|
1792
1773
|
display: "flex",
|
|
1793
1774
|
flexDirection: "column"
|
|
1794
1775
|
};
|
|
1795
|
-
const ROW_STYLE$
|
|
1776
|
+
const ROW_STYLE$3 = {
|
|
1796
1777
|
display: "flex",
|
|
1797
|
-
alignItems: "
|
|
1778
|
+
alignItems: "baseline",
|
|
1798
1779
|
gap: 8,
|
|
1799
1780
|
padding: "6px 12px"
|
|
1800
1781
|
};
|
|
1801
|
-
|
|
1782
|
+
/** The skill name: primary-ish but never the loudest thing in the row. */
|
|
1783
|
+
const NAME_STYLE$1 = {
|
|
1802
1784
|
minWidth: 0,
|
|
1803
1785
|
overflowWrap: "anywhere",
|
|
1804
1786
|
color: "var(--dsw-alias-label-secondary)"
|
|
1805
1787
|
};
|
|
1806
|
-
|
|
1788
|
+
/** The per-skill load tally, tertiary so the name stays the reading target. */
|
|
1789
|
+
const COUNT_STYLE = {
|
|
1807
1790
|
flexShrink: 0,
|
|
1808
1791
|
color: "var(--dsw-alias-label-tertiary)",
|
|
1809
|
-
fontSize: 11
|
|
1810
|
-
lineHeight: "18px",
|
|
1811
|
-
whiteSpace: "nowrap"
|
|
1792
|
+
fontSize: 11
|
|
1812
1793
|
};
|
|
1813
|
-
/** The
|
|
1814
|
-
const
|
|
1794
|
+
/** The "本轮" pill: same form as the deliverables codeplan tag. */
|
|
1795
|
+
const TAG_STYLE = {
|
|
1815
1796
|
flexShrink: 0,
|
|
1816
1797
|
color: "var(--dsw-alias-label-tertiary)",
|
|
1817
1798
|
border: "1px solid var(--dsw-alias-label-tertiary)",
|
|
@@ -1821,40 +1802,12 @@ window.__ModuleLoader__.load({
|
|
|
1821
1802
|
lineHeight: "16px",
|
|
1822
1803
|
whiteSpace: "nowrap"
|
|
1823
1804
|
};
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
flexShrink: 0,
|
|
1827
|
-
color: "var(--dsw-alias-label-primary)",
|
|
1828
|
-
border: "1px solid var(--dsw-alias-label-secondary)",
|
|
1829
|
-
borderRadius: 999,
|
|
1830
|
-
padding: "0 6px",
|
|
1831
|
-
fontSize: 11,
|
|
1832
|
-
lineHeight: "16px",
|
|
1833
|
-
whiteSpace: "nowrap"
|
|
1834
|
-
};
|
|
1835
|
-
const STATUS_SETTLED_STYLE = {
|
|
1836
|
-
...STATUS_OPEN_STYLE,
|
|
1837
|
-
color: "var(--dsw-alias-label-tertiary)",
|
|
1838
|
-
border: "1px solid var(--dsw-alias-label-tertiary)"
|
|
1839
|
-
};
|
|
1840
|
-
const BODY_STYLE$2 = {
|
|
1841
|
-
margin: 0,
|
|
1842
|
-
minWidth: 0,
|
|
1843
|
-
overflowWrap: "anywhere",
|
|
1805
|
+
const FOOTER_STYLE$2 = {
|
|
1806
|
+
padding: "0 12px 10px",
|
|
1844
1807
|
color: "var(--dsw-alias-label-tertiary)",
|
|
1845
|
-
fontSize: 12,
|
|
1846
|
-
lineHeight: "18px"
|
|
1847
|
-
};
|
|
1848
|
-
const REFS_STYLE = {
|
|
1849
|
-
...BODY_STYLE$2,
|
|
1850
1808
|
fontSize: 11
|
|
1851
1809
|
};
|
|
1852
|
-
const
|
|
1853
|
-
padding: "6px 12px 10px",
|
|
1854
|
-
color: "var(--dsw-alias-label-tertiary)",
|
|
1855
|
-
fontSize: 12
|
|
1856
|
-
};
|
|
1857
|
-
const EMPTY_STYLE$1 = {
|
|
1810
|
+
const EMPTY_STYLE$2 = {
|
|
1858
1811
|
margin: "0 auto",
|
|
1859
1812
|
padding: 24,
|
|
1860
1813
|
maxWidth: 460,
|
|
@@ -1863,153 +1816,48 @@ window.__ModuleLoader__.load({
|
|
|
1863
1816
|
lineHeight: 1.7,
|
|
1864
1817
|
fontSize: 12
|
|
1865
1818
|
};
|
|
1866
|
-
/** Locale key per known slot status; unknown statuses fall back to the raw wire value. */
|
|
1867
|
-
const STATUS_KEY = {
|
|
1868
|
-
open: "slots.status.open",
|
|
1869
|
-
done: "slots.status.done",
|
|
1870
|
-
dropped: "slots.status.dropped",
|
|
1871
|
-
expired: "slots.status.expired"
|
|
1872
|
-
};
|
|
1873
1819
|
/**
|
|
1874
|
-
*
|
|
1875
|
-
*
|
|
1876
|
-
* resolved 名称简述 (record_id → title) with the raw refs on the tooltip, or
|
|
1877
|
-
* raw refs when the host has not resolved them.
|
|
1878
|
-
*/
|
|
1879
|
-
function SlotRow(props) {
|
|
1880
|
-
const { slot, t } = props;
|
|
1881
|
-
const statusLabel = STATUS_KEY[slot.status] === void 0 ? slot.status : t(STATUS_KEY[slot.status]);
|
|
1882
|
-
const title = slot.body === void 0 ? slot.title : `${slot.title} — ${slot.body}`;
|
|
1883
|
-
const refsLine = slot.refViews !== void 0 ? slot.refViews.map((view) => view.title).join(" · ") : slot.refs !== void 0 ? slot.refs.join(" · ") : void 0;
|
|
1884
|
-
return (0, react.createElement)("li", { style: ROW_STYLE$2 }, (0, react.createElement)("span", { style: KIND_STYLE }, slot.kind), (0, react.createElement)("div", { style: {
|
|
1885
|
-
minWidth: 0,
|
|
1886
|
-
flex: "1 1 auto",
|
|
1887
|
-
display: "flex",
|
|
1888
|
-
flexDirection: "column",
|
|
1889
|
-
gap: 2
|
|
1890
|
-
} }, (0, react.createElement)("span", {
|
|
1891
|
-
style: TITLE_STYLE$1,
|
|
1892
|
-
title
|
|
1893
|
-
}, slot.title), slot.body !== void 0 && (0, react.createElement)("p", { style: BODY_STYLE$2 }, truncateBody(slot.body)), refsLine !== void 0 && (0, react.createElement)("p", {
|
|
1894
|
-
style: REFS_STYLE,
|
|
1895
|
-
title: slot.refs !== void 0 ? slot.refs.join("\n") : void 0
|
|
1896
|
-
}, refsLine)), (0, react.createElement)("span", { style: META_STYLE$1 }, `P${slot.priority}`), (0, react.createElement)("span", { style: slot.status === "open" ? STATUS_OPEN_STYLE : STATUS_SETTLED_STYLE }, statusLabel));
|
|
1897
|
-
}
|
|
1898
|
-
/**
|
|
1899
|
-
* Subscribe to this plugin's entry config and resolve the slots switch.
|
|
1900
|
-
* Plumbing lives in `use-config-flag`; the default (off) lives in the resolver.
|
|
1820
|
+
* The skill-usage section. The subscription is unconditional (a plain listener,
|
|
1821
|
+
* not IO); the view decides whether anything is drawn.
|
|
1901
1822
|
*
|
|
1902
|
-
* @param ctx - the client root context handed to the tab body.
|
|
1903
|
-
* @returns whether the memory-slots section may render at all.
|
|
1904
|
-
*/
|
|
1905
|
-
function useShowMemorySlots(ctx) {
|
|
1906
|
-
return useConfigFlag(ctx, resolveShowMemorySlots);
|
|
1907
|
-
}
|
|
1908
|
-
/**
|
|
1909
|
-
* The memory-slots section. Both hooks run unconditionally (a conditional
|
|
1910
|
-
* hook order would break the moment the switch flips); the switch only
|
|
1911
|
-
* decides whether the resolved view paints.
|
|
1912
1823
|
* @param props - translation, client context, session scope.
|
|
1913
1824
|
*/
|
|
1914
|
-
function
|
|
1915
|
-
const { t, ctx, scope } = props;
|
|
1916
|
-
const
|
|
1917
|
-
const view =
|
|
1918
|
-
if (show === false) return null;
|
|
1825
|
+
function SkillUsageSection(props) {
|
|
1826
|
+
const { t, ctx, scope, hideHeader = false } = props;
|
|
1827
|
+
const [availableOpen, setAvailableOpen] = (0, react.useState)(false);
|
|
1828
|
+
const view = readSkillUsage(useProjectionValue(ctx, scope?.sessionId, SKILL_USAGE_KEY));
|
|
1919
1829
|
if (view === void 0) return null;
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1830
|
+
const referenced = view.session.map((entry) => entry.name);
|
|
1831
|
+
const rest = availableExcept(view.available, referenced);
|
|
1832
|
+
const latestNames = view.latest?.names ?? [];
|
|
1833
|
+
return (0, react.createElement)("div", {
|
|
1834
|
+
style: SECTION_STYLE$3,
|
|
1835
|
+
"data-dsh-brief-sidebar": "skill-usage-section"
|
|
1836
|
+
}, !hideHeader && (0, react.createElement)("div", { style: HEADER_STYLE$4 }, t("section.skillUsage")), view.session.length === 0 && (0, react.createElement)("div", { style: EMPTY_STYLE$2 }, (0, react.createElement)("div", { style: {
|
|
1924
1837
|
marginBottom: 6,
|
|
1925
1838
|
color: "var(--dsw-alias-label-secondary)"
|
|
1926
|
-
} }, t("
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
* retired row keeps its strike-through instead of vanishing.
|
|
1949
|
-
*
|
|
1950
|
-
* This plugin does NOT depend on prime-memory: with the unit unregistered the
|
|
1951
|
-
* client reads `undefined` from the face and the section hides. The module is
|
|
1952
|
-
* deliberately PURE — no React, no context, no IO — so the narrowing is
|
|
1953
|
-
* testable directly, exactly like `memory-slots.ts`.
|
|
1954
|
-
*/
|
|
1955
|
-
/** The projection key `dsh-prime-memory` registers (its canonical constant). */
|
|
1956
|
-
const MEMORY_RECALL_KEY = "memoryRecall";
|
|
1957
|
-
/** Whether a value is a non-negative safe integer (the view's counter shape). */
|
|
1958
|
-
function isCounter(value) {
|
|
1959
|
-
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
1960
|
-
}
|
|
1961
|
-
/** Narrow one item entry; `undefined` = malformed, dropped. */
|
|
1962
|
-
function readItem(value) {
|
|
1963
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1964
|
-
const raw = value;
|
|
1965
|
-
if (typeof raw["id"] !== "string" || raw["id"].length === 0) return void 0;
|
|
1966
|
-
if (typeof raw["type"] !== "string" || typeof raw["title"] !== "string") return void 0;
|
|
1967
|
-
if (typeof raw["priority"] !== "number") return void 0;
|
|
1968
|
-
const item = {
|
|
1969
|
-
id: raw["id"],
|
|
1970
|
-
type: raw["type"],
|
|
1971
|
-
title: raw["title"],
|
|
1972
|
-
priority: raw["priority"]
|
|
1973
|
-
};
|
|
1974
|
-
if (Array.isArray(raw["scenario"])) {
|
|
1975
|
-
const phrases = raw["scenario"].filter((s) => typeof s === "string" && s.length > 0);
|
|
1976
|
-
if (phrases.length > 0) item.scenario = phrases;
|
|
1977
|
-
}
|
|
1978
|
-
const rating = raw["rating"];
|
|
1979
|
-
if (typeof rating === "number" && Number.isInteger(rating) && rating >= -1 && rating <= 5) item.rating = rating;
|
|
1980
|
-
if (typeof raw["body"] === "string") item.body = raw["body"];
|
|
1981
|
-
if (raw["retired"] === true) item.retired = true;
|
|
1982
|
-
if (raw["superseded"] === true) item.superseded = true;
|
|
1983
|
-
return item;
|
|
1984
|
-
}
|
|
1985
|
-
/**
|
|
1986
|
-
* Narrow a raw projection value into a recall view.
|
|
1987
|
-
*
|
|
1988
|
-
* `undefined` means the capability is ABSENT (prime-memory not in the
|
|
1989
|
-
* composition, or no frame carried the key) or the frame drifted beyond repair;
|
|
1990
|
-
* a view with `items: []` IS the honest empty state (prime-memory publishes as
|
|
1991
|
-
* soon as the unit registers). Malformed items are dropped, never thrown on.
|
|
1992
|
-
*
|
|
1993
|
-
* @param value - the raw `unknown` snapshot of the `memoryRecall` face.
|
|
1994
|
-
* @returns the validated view, or `undefined` when the capability is absent.
|
|
1995
|
-
*/
|
|
1996
|
-
function readRecall(value) {
|
|
1997
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1998
|
-
const raw = value;
|
|
1999
|
-
if (!isCounter(raw["rev"]) || !isCounter(raw["count"])) return void 0;
|
|
2000
|
-
if (typeof raw["sessionId"] !== "string") return void 0;
|
|
2001
|
-
if (!Array.isArray(raw["items"])) return void 0;
|
|
2002
|
-
const items = [];
|
|
2003
|
-
for (const entry of raw["items"]) {
|
|
2004
|
-
const item = readItem(entry);
|
|
2005
|
-
if (item !== void 0) items.push(item);
|
|
2006
|
-
}
|
|
2007
|
-
return {
|
|
2008
|
-
rev: raw["rev"],
|
|
2009
|
-
sessionId: raw["sessionId"],
|
|
2010
|
-
count: raw["count"],
|
|
2011
|
-
items
|
|
2012
|
-
};
|
|
1839
|
+
} }, t("skillUsage.empty")), (0, react.createElement)("div", null, t("skillUsage.emptyHint"))), view.session.length > 0 && (0, react.createElement)(GroupHeader, {
|
|
1840
|
+
text: expandCount(t("skillUsage.invokedGroup"), view.session.length),
|
|
1841
|
+
marker: "skill-usage-invoked-group"
|
|
1842
|
+
}), (0, react.createElement)("ul", { style: LIST_STYLE$2 }, view.session.map((entry) => (0, react.createElement)("li", {
|
|
1843
|
+
key: entry.name,
|
|
1844
|
+
style: ROW_STYLE$3,
|
|
1845
|
+
"data-dsh-brief-sidebar": "skill-usage-row"
|
|
1846
|
+
}, (0, react.createElement)("span", { style: NAME_STYLE$1 }, entry.name), (0, react.createElement)("span", { style: COUNT_STYLE }, expandPair(t("skillUsage.uses"), entry.count, entry.count)), latestNames.includes(entry.name) && (0, react.createElement)("span", { style: TAG_STYLE }, t("skillUsage.latestTag"))))), rest.length > 0 && (0, react.createElement)(FoldToggle, {
|
|
1847
|
+
label: expandCount(t("skillUsage.availableGroup"), rest.length),
|
|
1848
|
+
open: availableOpen,
|
|
1849
|
+
marker: "skill-usage-available-toggle",
|
|
1850
|
+
onToggle: () => {
|
|
1851
|
+
setAvailableOpen((current) => !current);
|
|
1852
|
+
}
|
|
1853
|
+
}), rest.length > 0 && view.availableCount > view.available.length && (0, react.createElement)(GroupCaption, {
|
|
1854
|
+
text: expandCount(t("skillUsage.availableCapped"), view.availableCount),
|
|
1855
|
+
marker: "skill-usage-available-capped"
|
|
1856
|
+
}), availableOpen && (0, react.createElement)("ul", { style: LIST_STYLE$2 }, rest.map((name) => (0, react.createElement)("li", {
|
|
1857
|
+
key: name,
|
|
1858
|
+
style: ROW_STYLE$3,
|
|
1859
|
+
"data-dsh-brief-sidebar": "skill-usage-available-row"
|
|
1860
|
+
}, (0, react.createElement)("span", { style: NAME_STYLE$1 }, name)))), view.session.length > 0 && (0, react.createElement)("div", { style: FOOTER_STYLE$2 }, expandPair(t("skillUsage.count"), view.callTotal, view.sessionTotal)));
|
|
2013
1861
|
}
|
|
2014
1862
|
//#endregion
|
|
2015
1863
|
//#region src/client/summary/recall-feedback.ts
|
|
@@ -2236,7 +2084,7 @@ window.__ModuleLoader__.load({
|
|
|
2236
2084
|
fontSize: 11,
|
|
2237
2085
|
lineHeight: "16px"
|
|
2238
2086
|
};
|
|
2239
|
-
const BODY_STYLE$
|
|
2087
|
+
const BODY_STYLE$2 = {
|
|
2240
2088
|
minWidth: 0,
|
|
2241
2089
|
overflowWrap: "anywhere",
|
|
2242
2090
|
whiteSpace: "pre-wrap",
|
|
@@ -2258,7 +2106,7 @@ window.__ModuleLoader__.load({
|
|
|
2258
2106
|
};
|
|
2259
2107
|
/** A retired record stays readable in the bubble, just marked. */
|
|
2260
2108
|
const STALE_BODY_STYLE = {
|
|
2261
|
-
...BODY_STYLE$
|
|
2109
|
+
...BODY_STYLE$2,
|
|
2262
2110
|
textDecoration: "line-through"
|
|
2263
2111
|
};
|
|
2264
2112
|
/**
|
|
@@ -2285,7 +2133,7 @@ window.__ModuleLoader__.load({
|
|
|
2285
2133
|
},
|
|
2286
2134
|
"data-dsh-brief-sidebar": "recall-bubble",
|
|
2287
2135
|
role: "tooltip"
|
|
2288
|
-
}, (0, react.createElement)("span", { style: TYPE_STYLE$1 }, item.type), (0, react.createElement)("div", { style: stale ? STALE_BODY_STYLE : BODY_STYLE$
|
|
2136
|
+
}, (0, react.createElement)("span", { style: TYPE_STYLE$1 }, item.type), (0, react.createElement)("div", { style: stale ? STALE_BODY_STYLE : BODY_STYLE$2 }, item.body ?? `${item.title}\n${t("recall.noBody")}`), scenario !== void 0 && (0, react.createElement)("div", { style: SCENARIO_STYLE$1 }, scenario), (0, react.createElement)("div", { style: FOOTER_STYLE$1 }, foot));
|
|
2289
2137
|
}
|
|
2290
2138
|
//#endregion
|
|
2291
2139
|
//#region src/client/summary/MemoryRecallSection.tsx
|
|
@@ -2331,11 +2179,11 @@ window.__ModuleLoader__.load({
|
|
|
2331
2179
|
* Colours come from `--dsw-alias-*` tokens only (skin contract, enforced by
|
|
2332
2180
|
* `tests/purity.spec.ts`).
|
|
2333
2181
|
*/
|
|
2334
|
-
const SECTION_STYLE = {
|
|
2182
|
+
const SECTION_STYLE$2 = {
|
|
2335
2183
|
flexShrink: 0,
|
|
2336
2184
|
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
2337
2185
|
};
|
|
2338
|
-
const HEADER_STYLE$
|
|
2186
|
+
const HEADER_STYLE$3 = {
|
|
2339
2187
|
padding: "10px 12px 0",
|
|
2340
2188
|
color: "var(--dsw-alias-label-secondary)",
|
|
2341
2189
|
fontSize: 12,
|
|
@@ -2347,14 +2195,14 @@ window.__ModuleLoader__.load({
|
|
|
2347
2195
|
fontSize: 11,
|
|
2348
2196
|
lineHeight: "15px"
|
|
2349
2197
|
};
|
|
2350
|
-
const LIST_STYLE = {
|
|
2198
|
+
const LIST_STYLE$1 = {
|
|
2351
2199
|
listStyle: "none",
|
|
2352
2200
|
margin: 0,
|
|
2353
2201
|
padding: "6px 0",
|
|
2354
2202
|
display: "flex",
|
|
2355
2203
|
flexDirection: "column"
|
|
2356
2204
|
};
|
|
2357
|
-
const ROW_STYLE$
|
|
2205
|
+
const ROW_STYLE$2 = {
|
|
2358
2206
|
display: "flex",
|
|
2359
2207
|
alignItems: "flex-start",
|
|
2360
2208
|
gap: 8,
|
|
@@ -2362,7 +2210,7 @@ window.__ModuleLoader__.load({
|
|
|
2362
2210
|
};
|
|
2363
2211
|
/** Selected row: one hairline of accent, no colour literal (skin contract). */
|
|
2364
2212
|
const ROW_SELECTED_STYLE = {
|
|
2365
|
-
...ROW_STYLE$
|
|
2213
|
+
...ROW_STYLE$2,
|
|
2366
2214
|
background: "var(--dsw-alias-fill-quaternary)"
|
|
2367
2215
|
};
|
|
2368
2216
|
/** The type pill: same form as the slot kind pill, tertiary emphasis. */
|
|
@@ -2376,7 +2224,7 @@ window.__ModuleLoader__.load({
|
|
|
2376
2224
|
lineHeight: "16px",
|
|
2377
2225
|
whiteSpace: "nowrap"
|
|
2378
2226
|
};
|
|
2379
|
-
const TITLE_STYLE = {
|
|
2227
|
+
const TITLE_STYLE$1 = {
|
|
2380
2228
|
minWidth: 0,
|
|
2381
2229
|
color: "var(--dsw-alias-label-secondary)",
|
|
2382
2230
|
display: "-webkit-box",
|
|
@@ -2402,7 +2250,7 @@ window.__ModuleLoader__.load({
|
|
|
2402
2250
|
fontSize: 11,
|
|
2403
2251
|
lineHeight: "16px"
|
|
2404
2252
|
};
|
|
2405
|
-
const META_STYLE = {
|
|
2253
|
+
const META_STYLE$1 = {
|
|
2406
2254
|
flexShrink: 0,
|
|
2407
2255
|
color: "var(--dsw-alias-label-tertiary)",
|
|
2408
2256
|
fontSize: 11,
|
|
@@ -2414,7 +2262,7 @@ window.__ModuleLoader__.load({
|
|
|
2414
2262
|
color: "var(--dsw-alias-label-tertiary)",
|
|
2415
2263
|
fontSize: 12
|
|
2416
2264
|
};
|
|
2417
|
-
const EMPTY_STYLE = {
|
|
2265
|
+
const EMPTY_STYLE$1 = {
|
|
2418
2266
|
margin: "0 auto",
|
|
2419
2267
|
padding: 24,
|
|
2420
2268
|
maxWidth: 460,
|
|
@@ -2530,11 +2378,11 @@ window.__ModuleLoader__.load({
|
|
|
2530
2378
|
const staleText = stale && mark !== -1 ? t("recall.obsoleted") : void 0;
|
|
2531
2379
|
const selectable = props.rating && !stale;
|
|
2532
2380
|
const titleStyle = stale ? {
|
|
2533
|
-
...TITLE_STYLE,
|
|
2381
|
+
...TITLE_STYLE$1,
|
|
2534
2382
|
...STRIKE_STYLE
|
|
2535
|
-
} : TITLE_STYLE;
|
|
2383
|
+
} : TITLE_STYLE$1;
|
|
2536
2384
|
return (0, react.createElement)("li", {
|
|
2537
|
-
style: selected && props.rating ? ROW_SELECTED_STYLE : ROW_STYLE$
|
|
2385
|
+
style: selected && props.rating ? ROW_SELECTED_STYLE : ROW_STYLE$2,
|
|
2538
2386
|
onClick: selectable ? props.onSelect : void 0,
|
|
2539
2387
|
onMouseEnter: (e) => {
|
|
2540
2388
|
const el = e.currentTarget;
|
|
@@ -2563,7 +2411,7 @@ window.__ModuleLoader__.load({
|
|
|
2563
2411
|
confirmLabel: t("recall.obsoleteConfirm"),
|
|
2564
2412
|
whyLabel: t("recall.obsoleteWhy"),
|
|
2565
2413
|
onScore: props.onScore
|
|
2566
|
-
})), (0, react.createElement)("span", { style: META_STYLE }, `P${item.priority}`));
|
|
2414
|
+
})), (0, react.createElement)("span", { style: META_STYLE$1 }, `P${item.priority}`));
|
|
2567
2415
|
}
|
|
2568
2416
|
/** How long the `-1` button stays armed before reverting to its plain label. */
|
|
2569
2417
|
const ARM_WINDOW_MS = 5e3;
|
|
@@ -2669,10 +2517,10 @@ window.__ModuleLoader__.load({
|
|
|
2669
2517
|
const historyRows = view.items.filter((item) => item.superseded === true && item.retired !== true);
|
|
2670
2518
|
const hidden = historyRows.length;
|
|
2671
2519
|
if (rows.length === 0) return (0, react.createElement)("div", {
|
|
2672
|
-
style: SECTION_STYLE,
|
|
2520
|
+
style: SECTION_STYLE$2,
|
|
2673
2521
|
"data-dsh-brief-sidebar": "memory-recall-section"
|
|
2674
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
2675
|
-
style: EMPTY_STYLE,
|
|
2522
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$3 }, t("section.memoryRecall")), (0, react.createElement)("div", {
|
|
2523
|
+
style: EMPTY_STYLE$1,
|
|
2676
2524
|
...hidden > 0 ? { "data-dsh-brief-sidebar": "recall-superseded-note" } : {}
|
|
2677
2525
|
}, hidden > 0 ? expandCount(t("recall.supersededHidden"), hidden) : t("recall.empty")), historyRows.length > 0 && (0, react.createElement)(FoldToggle, {
|
|
2678
2526
|
label: expandCount(t("recall.historyGroup"), historyRows.length),
|
|
@@ -2681,7 +2529,7 @@ window.__ModuleLoader__.load({
|
|
|
2681
2529
|
onToggle: () => {
|
|
2682
2530
|
setHistoryOpen((current) => !current);
|
|
2683
2531
|
}
|
|
2684
|
-
}), historyOpen && (0, react.createElement)("ul", { style: LIST_STYLE }, historyRows.map((item) => (0, react.createElement)(RecallRow, {
|
|
2532
|
+
}), historyOpen && (0, react.createElement)("ul", { style: LIST_STYLE$1 }, historyRows.map((item) => (0, react.createElement)(RecallRow, {
|
|
2685
2533
|
key: item.id,
|
|
2686
2534
|
item,
|
|
2687
2535
|
selected: false,
|
|
@@ -2699,12 +2547,12 @@ window.__ModuleLoader__.load({
|
|
|
2699
2547
|
onLeave: () => {}
|
|
2700
2548
|
}))));
|
|
2701
2549
|
return (0, react.createElement)("div", {
|
|
2702
|
-
style: SECTION_STYLE,
|
|
2550
|
+
style: SECTION_STYLE$2,
|
|
2703
2551
|
"data-dsh-brief-sidebar": "memory-recall-section"
|
|
2704
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
2552
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$3 }, t("section.memoryRecall")), rating && (0, react.createElement)("div", { style: HINT_STYLE$1 }, `${t("recall.rateHint")}${t("recall.strikeHint")}`), rows.length > 0 && (0, react.createElement)(GroupHeader, {
|
|
2705
2553
|
text: expandCount(t("recall.activeGroup"), rows.length),
|
|
2706
2554
|
marker: "recall-active-group"
|
|
2707
|
-
}), (0, react.createElement)("ul", { style: LIST_STYLE }, rows.map((item) => (0, react.createElement)(RecallRow, {
|
|
2555
|
+
}), (0, react.createElement)("ul", { style: LIST_STYLE$1 }, rows.map((item) => (0, react.createElement)(RecallRow, {
|
|
2708
2556
|
key: item.id,
|
|
2709
2557
|
item,
|
|
2710
2558
|
selected: selectedId === item.id,
|
|
@@ -2733,7 +2581,7 @@ window.__ModuleLoader__.load({
|
|
|
2733
2581
|
onToggle: () => {
|
|
2734
2582
|
setHistoryOpen((current) => !current);
|
|
2735
2583
|
}
|
|
2736
|
-
}), historyOpen && (0, react.createElement)("ul", { style: LIST_STYLE }, historyRows.map((item) => (0, react.createElement)(RecallRow, {
|
|
2584
|
+
}), historyOpen && (0, react.createElement)("ul", { style: LIST_STYLE$1 }, historyRows.map((item) => (0, react.createElement)(RecallRow, {
|
|
2737
2585
|
key: item.id,
|
|
2738
2586
|
item,
|
|
2739
2587
|
selected: false,
|
|
@@ -2760,16 +2608,337 @@ window.__ModuleLoader__.load({
|
|
|
2760
2608
|
})());
|
|
2761
2609
|
}
|
|
2762
2610
|
//#endregion
|
|
2611
|
+
//#region src/client/summary/MemorySlotsSection.tsx
|
|
2612
|
+
/**
|
|
2613
|
+
* The memory-slots section: the `memorySlots` projection drawn as the third
|
|
2614
|
+
* part of the summary tab (fixed last: 进展 → 产物 → 记忆).
|
|
2615
|
+
*
|
|
2616
|
+
* TWO gates, both fail-open to "hidden", so a host without dsh-prime-memory
|
|
2617
|
+
* paints nothing here:
|
|
2618
|
+
*
|
|
2619
|
+
* 1. **The switch.** This plugin's entry config (`configForms` → entry
|
|
2620
|
+
* `dsh-brief-sidebar`, field `showMemorySlots`, default OFF — the pinned
|
|
2621
|
+
* list moved out of the summary pane's way in favour of the session-recall
|
|
2622
|
+
* section). When the config face cannot answer (service absent, scope not
|
|
2623
|
+
* ready, field unset) the default applies — see `scope-face.ts`.
|
|
2624
|
+
* 2. **The capability.** The `memorySlots` projection must exist for the
|
|
2625
|
+
* session; `undefined` hides the section entirely, exactly like the
|
|
2626
|
+
* optional deliverables section.
|
|
2627
|
+
*
|
|
2628
|
+
* Colours come from `--dsw-alias-*` tokens only (skin contract, enforced by
|
|
2629
|
+
* `tests/purity.spec.ts`).
|
|
2630
|
+
*/
|
|
2631
|
+
const SECTION_STYLE$1 = {
|
|
2632
|
+
flexShrink: 0,
|
|
2633
|
+
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
2634
|
+
};
|
|
2635
|
+
const HEADER_STYLE$2 = {
|
|
2636
|
+
padding: "10px 12px 0",
|
|
2637
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
2638
|
+
fontSize: 12,
|
|
2639
|
+
fontWeight: 600
|
|
2640
|
+
};
|
|
2641
|
+
const LIST_STYLE = {
|
|
2642
|
+
listStyle: "none",
|
|
2643
|
+
margin: 0,
|
|
2644
|
+
padding: "6px 0",
|
|
2645
|
+
display: "flex",
|
|
2646
|
+
flexDirection: "column"
|
|
2647
|
+
};
|
|
2648
|
+
const ROW_STYLE$1 = {
|
|
2649
|
+
display: "flex",
|
|
2650
|
+
alignItems: "flex-start",
|
|
2651
|
+
gap: 8,
|
|
2652
|
+
padding: "6px 12px"
|
|
2653
|
+
};
|
|
2654
|
+
const TITLE_STYLE = {
|
|
2655
|
+
minWidth: 0,
|
|
2656
|
+
overflowWrap: "anywhere",
|
|
2657
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
2658
|
+
};
|
|
2659
|
+
const META_STYLE = {
|
|
2660
|
+
flexShrink: 0,
|
|
2661
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
2662
|
+
fontSize: 11,
|
|
2663
|
+
lineHeight: "18px",
|
|
2664
|
+
whiteSpace: "nowrap"
|
|
2665
|
+
};
|
|
2666
|
+
/** The kind pill: same form as the codeplan pill, tertiary emphasis. */
|
|
2667
|
+
const KIND_STYLE = {
|
|
2668
|
+
flexShrink: 0,
|
|
2669
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
2670
|
+
border: "1px solid var(--dsw-alias-label-tertiary)",
|
|
2671
|
+
borderRadius: 999,
|
|
2672
|
+
padding: "0 6px",
|
|
2673
|
+
fontSize: 11,
|
|
2674
|
+
lineHeight: "16px",
|
|
2675
|
+
whiteSpace: "nowrap"
|
|
2676
|
+
};
|
|
2677
|
+
/** The status pill: filled tint for an open slot, plain text otherwise. */
|
|
2678
|
+
const STATUS_OPEN_STYLE = {
|
|
2679
|
+
flexShrink: 0,
|
|
2680
|
+
color: "var(--dsw-alias-label-primary)",
|
|
2681
|
+
border: "1px solid var(--dsw-alias-label-secondary)",
|
|
2682
|
+
borderRadius: 999,
|
|
2683
|
+
padding: "0 6px",
|
|
2684
|
+
fontSize: 11,
|
|
2685
|
+
lineHeight: "16px",
|
|
2686
|
+
whiteSpace: "nowrap"
|
|
2687
|
+
};
|
|
2688
|
+
const STATUS_SETTLED_STYLE = {
|
|
2689
|
+
...STATUS_OPEN_STYLE,
|
|
2690
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
2691
|
+
border: "1px solid var(--dsw-alias-label-tertiary)"
|
|
2692
|
+
};
|
|
2693
|
+
const BODY_STYLE$1 = {
|
|
2694
|
+
margin: 0,
|
|
2695
|
+
minWidth: 0,
|
|
2696
|
+
overflowWrap: "anywhere",
|
|
2697
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
2698
|
+
fontSize: 12,
|
|
2699
|
+
lineHeight: "18px"
|
|
2700
|
+
};
|
|
2701
|
+
const REFS_STYLE = {
|
|
2702
|
+
...BODY_STYLE$1,
|
|
2703
|
+
fontSize: 11
|
|
2704
|
+
};
|
|
2705
|
+
const TOTAL_STYLE = {
|
|
2706
|
+
padding: "6px 12px 10px",
|
|
2707
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
2708
|
+
fontSize: 12
|
|
2709
|
+
};
|
|
2710
|
+
const EMPTY_STYLE = {
|
|
2711
|
+
margin: "0 auto",
|
|
2712
|
+
padding: 24,
|
|
2713
|
+
maxWidth: 460,
|
|
2714
|
+
textAlign: "center",
|
|
2715
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
2716
|
+
lineHeight: 1.7,
|
|
2717
|
+
fontSize: 12
|
|
2718
|
+
};
|
|
2719
|
+
/** Locale key per known slot status; unknown statuses fall back to the raw wire value. */
|
|
2720
|
+
const STATUS_KEY = {
|
|
2721
|
+
open: "slots.status.open",
|
|
2722
|
+
done: "slots.status.done",
|
|
2723
|
+
dropped: "slots.status.dropped",
|
|
2724
|
+
expired: "slots.status.expired"
|
|
2725
|
+
};
|
|
2726
|
+
/**
|
|
2727
|
+
* One slot row: kind pill, title (full body rides the tooltip), status pill,
|
|
2728
|
+
* priority; body line underneath when the wire view carries it; refs as
|
|
2729
|
+
* resolved 名称简述 (record_id → title) with the raw refs on the tooltip, or
|
|
2730
|
+
* raw refs when the host has not resolved them.
|
|
2731
|
+
*/
|
|
2732
|
+
function SlotRow(props) {
|
|
2733
|
+
const { slot, t } = props;
|
|
2734
|
+
const statusLabel = STATUS_KEY[slot.status] === void 0 ? slot.status : t(STATUS_KEY[slot.status]);
|
|
2735
|
+
const title = slot.body === void 0 ? slot.title : `${slot.title} — ${slot.body}`;
|
|
2736
|
+
const refsLine = slot.refViews !== void 0 ? slot.refViews.map((view) => view.title).join(" · ") : slot.refs !== void 0 ? slot.refs.join(" · ") : void 0;
|
|
2737
|
+
return (0, react.createElement)("li", { style: ROW_STYLE$1 }, (0, react.createElement)("span", { style: KIND_STYLE }, slot.kind), (0, react.createElement)("div", { style: {
|
|
2738
|
+
minWidth: 0,
|
|
2739
|
+
flex: "1 1 auto",
|
|
2740
|
+
display: "flex",
|
|
2741
|
+
flexDirection: "column",
|
|
2742
|
+
gap: 2
|
|
2743
|
+
} }, (0, react.createElement)("span", {
|
|
2744
|
+
style: TITLE_STYLE,
|
|
2745
|
+
title
|
|
2746
|
+
}, slot.title), slot.body !== void 0 && (0, react.createElement)("p", { style: BODY_STYLE$1 }, truncateBody(slot.body)), refsLine !== void 0 && (0, react.createElement)("p", {
|
|
2747
|
+
style: REFS_STYLE,
|
|
2748
|
+
title: slot.refs !== void 0 ? slot.refs.join("\n") : void 0
|
|
2749
|
+
}, refsLine)), (0, react.createElement)("span", { style: META_STYLE }, `P${slot.priority}`), (0, react.createElement)("span", { style: slot.status === "open" ? STATUS_OPEN_STYLE : STATUS_SETTLED_STYLE }, statusLabel));
|
|
2750
|
+
}
|
|
2751
|
+
/**
|
|
2752
|
+
* Subscribe to this plugin's entry config and resolve the slots switch.
|
|
2753
|
+
* Plumbing lives in `use-config-flag`; the default (off) lives in the resolver.
|
|
2754
|
+
*
|
|
2755
|
+
* @param ctx - the client root context handed to the tab body.
|
|
2756
|
+
* @returns whether the memory-slots section may render at all.
|
|
2757
|
+
*/
|
|
2758
|
+
function useShowMemorySlots(ctx) {
|
|
2759
|
+
return useConfigFlag(ctx, resolveShowMemorySlots);
|
|
2760
|
+
}
|
|
2761
|
+
/**
|
|
2762
|
+
* The memory-slots section. Both hooks run unconditionally (a conditional
|
|
2763
|
+
* hook order would break the moment the switch flips); the switch only
|
|
2764
|
+
* decides whether the resolved view paints.
|
|
2765
|
+
* @param props - translation, client context, session scope.
|
|
2766
|
+
*/
|
|
2767
|
+
function MemorySlotsSection(props) {
|
|
2768
|
+
const { t, ctx, scope } = props;
|
|
2769
|
+
const show = useShowMemorySlots(ctx);
|
|
2770
|
+
const view = readSlots(useProjectionValue(ctx, scope?.sessionId, MEMORY_SLOTS_KEY));
|
|
2771
|
+
if (show === false) return null;
|
|
2772
|
+
if (view === void 0) return null;
|
|
2773
|
+
if (view.slots.length === 0) return (0, react.createElement)("div", {
|
|
2774
|
+
style: SECTION_STYLE$1,
|
|
2775
|
+
"data-dsh-brief-sidebar": "memory-slots-section"
|
|
2776
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$2 }, t("section.memorySlots")), (0, react.createElement)("div", { style: EMPTY_STYLE }, (0, react.createElement)("div", { style: {
|
|
2777
|
+
marginBottom: 6,
|
|
2778
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
2779
|
+
} }, t("slots.empty")), (0, react.createElement)("div", null, t("slots.emptyHint"))));
|
|
2780
|
+
return (0, react.createElement)("div", {
|
|
2781
|
+
style: SECTION_STYLE$1,
|
|
2782
|
+
"data-dsh-brief-sidebar": "memory-slots-section"
|
|
2783
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$2 }, t("section.memorySlots")), (0, react.createElement)("ul", { style: LIST_STYLE }, view.slots.map((slot) => (0, react.createElement)(SlotRow, {
|
|
2784
|
+
key: slot.id,
|
|
2785
|
+
slot,
|
|
2786
|
+
t
|
|
2787
|
+
}))), (0, react.createElement)("div", { style: TOTAL_STYLE }, expandCount(t("slots.openCount"), view.openCount)));
|
|
2788
|
+
}
|
|
2789
|
+
//#endregion
|
|
2790
|
+
//#region src/client/summary/ReferenceSection.tsx
|
|
2791
|
+
/**
|
|
2792
|
+
* The references section: 技能与记忆并列成两个可左右切换的页签。
|
|
2793
|
+
*
|
|
2794
|
+
* Why a tab strip instead of three stacked sections: the two lists answer the
|
|
2795
|
+
* same question — "what entered this session's context" — from two different
|
|
2796
|
+
* providers (this plugin's own skill fold, and prime-memory's recall/slots).
|
|
2797
|
+
* Stacked, they pushed the board and the produced files off screen on a narrow
|
|
2798
|
+
* sidebar; side by side, each gets the full width when the reader actually
|
|
2799
|
+
* wants it.
|
|
2800
|
+
*
|
|
2801
|
+
* Two decisions worth knowing when editing this file:
|
|
2802
|
+
*
|
|
2803
|
+
* 1. **The inactive pane stays mounted and is hidden with `display:none`.**
|
|
2804
|
+
* The recall pane carries real interaction state (selected row, pending
|
|
2805
|
+
* rating, the expanded history group); unmounting it would silently reset
|
|
2806
|
+
* what the human just did every time they glanced at the skills tab. The
|
|
2807
|
+
* cost is that a hidden pane keeps re-rendering on projection frames —
|
|
2808
|
+
* bounded, because the whole tab still renders nothing while `visible` is
|
|
2809
|
+
* false (see `SummaryTab`).
|
|
2810
|
+
* 2. **The parent repeats its children's gates.** A section that hides itself
|
|
2811
|
+
* leaves the tab strip pointing at an empty pane, so the availability
|
|
2812
|
+
* question is answered here with the SAME readers and resolvers the children
|
|
2813
|
+
* use. `tests/reference-section.spec.tsx` pins both sides against the same
|
|
2814
|
+
* fixtures — if a child gate ever drifts from this one, that test goes red
|
|
2815
|
+
* rather than the UI growing a dead tab.
|
|
2816
|
+
*
|
|
2817
|
+
* Colours come from `--dsw-alias-*` tokens only (skin contract).
|
|
2818
|
+
*/
|
|
2819
|
+
const SECTION_STYLE = {
|
|
2820
|
+
flexShrink: 0,
|
|
2821
|
+
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
2822
|
+
};
|
|
2823
|
+
const HEADER_STYLE$1 = {
|
|
2824
|
+
padding: "10px 12px 0",
|
|
2825
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
2826
|
+
fontSize: 12,
|
|
2827
|
+
fontWeight: 600
|
|
2828
|
+
};
|
|
2829
|
+
/** The tab strip: flush-left, same gutter as section headers. */
|
|
2830
|
+
const TABS_STYLE = {
|
|
2831
|
+
display: "flex",
|
|
2832
|
+
gap: 4,
|
|
2833
|
+
padding: "6px 12px 0"
|
|
2834
|
+
};
|
|
2835
|
+
/** One tab. Selected gets a hairline of secondary label; never a colour literal. */
|
|
2836
|
+
const TAB_STYLE = {
|
|
2837
|
+
background: "none",
|
|
2838
|
+
border: "none",
|
|
2839
|
+
borderBottom: "1px solid transparent",
|
|
2840
|
+
font: "inherit",
|
|
2841
|
+
fontSize: 12,
|
|
2842
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
2843
|
+
cursor: "pointer",
|
|
2844
|
+
padding: "4px 8px 6px"
|
|
2845
|
+
};
|
|
2846
|
+
const TAB_ACTIVE_STYLE = {
|
|
2847
|
+
...TAB_STYLE,
|
|
2848
|
+
borderBottom: "1px solid var(--dsw-alias-label-secondary)",
|
|
2849
|
+
color: "var(--dsw-alias-label-primary)",
|
|
2850
|
+
fontWeight: 600
|
|
2851
|
+
};
|
|
2852
|
+
const ORDER = ["skills", "memory"];
|
|
2853
|
+
/**
|
|
2854
|
+
* The references section. All hooks run before any early return, so the tab
|
|
2855
|
+
* strip can disappear (no capability at all) without ever changing hook order.
|
|
2856
|
+
*
|
|
2857
|
+
* @param props - translation, client context, session scope, rating transport.
|
|
2858
|
+
*/
|
|
2859
|
+
function ReferenceSection(props) {
|
|
2860
|
+
const { t, ctx, scope, fetchImpl } = props;
|
|
2861
|
+
const [tab, setTab] = (0, react.useState)("skills");
|
|
2862
|
+
const skillsLive = readSkillUsage(useProjectionValue(ctx, scope?.sessionId, SKILL_USAGE_KEY)) !== void 0;
|
|
2863
|
+
const recallFrame = readRecall(useProjectionValue(ctx, scope?.sessionId, MEMORY_RECALL_KEY));
|
|
2864
|
+
const slotsFrame = readSlots(useProjectionValue(ctx, scope?.sessionId, MEMORY_SLOTS_KEY));
|
|
2865
|
+
const recallOn = useConfigFlag(ctx, resolveShowMemoryRecall) !== false;
|
|
2866
|
+
const slotsOn = useConfigFlag(ctx, resolveShowMemorySlots) === true;
|
|
2867
|
+
const memoryLive = recallFrame !== void 0 && recallOn || slotsFrame !== void 0 && slotsOn;
|
|
2868
|
+
const onTabsKeyDown = (0, react.useCallback)((event) => {
|
|
2869
|
+
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
|
|
2870
|
+
event.preventDefault();
|
|
2871
|
+
const at = ORDER.indexOf(tab);
|
|
2872
|
+
const step = event.key === "ArrowRight" ? 1 : ORDER.length - 1;
|
|
2873
|
+
setTab(ORDER[(at + step) % ORDER.length]);
|
|
2874
|
+
}, [tab]);
|
|
2875
|
+
if (!skillsLive && !memoryLive) return null;
|
|
2876
|
+
const tabButton = (id, label) => (0, react.createElement)("button", {
|
|
2877
|
+
key: id,
|
|
2878
|
+
type: "button",
|
|
2879
|
+
role: "tab",
|
|
2880
|
+
id: `dsh-brief-reference-tab-${id}`,
|
|
2881
|
+
"aria-selected": active === id,
|
|
2882
|
+
"aria-controls": `dsh-brief-reference-pane-${id}`,
|
|
2883
|
+
style: active === id ? TAB_ACTIVE_STYLE : TAB_STYLE,
|
|
2884
|
+
"data-dsh-brief-sidebar": `reference-tab-${id}`,
|
|
2885
|
+
onClick: () => {
|
|
2886
|
+
setTab(id);
|
|
2887
|
+
}
|
|
2888
|
+
}, label);
|
|
2889
|
+
const active = tab === "skills" && !skillsLive ? "memory" : tab;
|
|
2890
|
+
return (0, react.createElement)("div", {
|
|
2891
|
+
style: SECTION_STYLE,
|
|
2892
|
+
"data-dsh-brief-sidebar": "references-section"
|
|
2893
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$1 }, t("section.references")), (0, react.createElement)("div", {
|
|
2894
|
+
role: "tablist",
|
|
2895
|
+
"aria-label": t("section.references"),
|
|
2896
|
+
style: TABS_STYLE,
|
|
2897
|
+
onKeyDown: onTabsKeyDown,
|
|
2898
|
+
"data-dsh-brief-sidebar": "reference-tabs"
|
|
2899
|
+
}, skillsLive && tabButton("skills", t("references.tabSkills")), memoryLive && tabButton("memory", t("references.tabMemory"))), skillsLive && (0, react.createElement)("div", {
|
|
2900
|
+
role: "tabpanel",
|
|
2901
|
+
id: "dsh-brief-reference-pane-skills",
|
|
2902
|
+
"aria-labelledby": "dsh-brief-reference-tab-skills",
|
|
2903
|
+
hidden: active !== "skills",
|
|
2904
|
+
style: active === "skills" ? void 0 : { display: "none" },
|
|
2905
|
+
"data-dsh-brief-sidebar": "reference-pane-skills"
|
|
2906
|
+
}, (0, react.createElement)(SkillUsageSection, {
|
|
2907
|
+
t,
|
|
2908
|
+
ctx,
|
|
2909
|
+
scope,
|
|
2910
|
+
hideHeader: true
|
|
2911
|
+
})), memoryLive && (0, react.createElement)("div", {
|
|
2912
|
+
role: "tabpanel",
|
|
2913
|
+
id: "dsh-brief-reference-pane-memory",
|
|
2914
|
+
"aria-labelledby": "dsh-brief-reference-tab-memory",
|
|
2915
|
+
hidden: active !== "memory",
|
|
2916
|
+
style: active === "memory" ? void 0 : { display: "none" },
|
|
2917
|
+
"data-dsh-brief-sidebar": "reference-pane-memory"
|
|
2918
|
+
}, (0, react.createElement)(MemoryRecallSection, {
|
|
2919
|
+
t,
|
|
2920
|
+
ctx,
|
|
2921
|
+
scope,
|
|
2922
|
+
fetchImpl
|
|
2923
|
+
}), (0, react.createElement)(MemorySlotsSection, {
|
|
2924
|
+
t,
|
|
2925
|
+
ctx,
|
|
2926
|
+
scope
|
|
2927
|
+
})));
|
|
2928
|
+
}
|
|
2929
|
+
//#endregion
|
|
2763
2930
|
//#region src/client/SummaryTab.tsx
|
|
2764
2931
|
/**
|
|
2765
2932
|
* The summary tab body: the session's at-a-glance state, composed of sections.
|
|
2766
2933
|
*
|
|
2767
2934
|
* Section order: 进展 (the todos board) → 产物 (produced files, codeplan
|
|
2768
|
-
* artifacts marked inline) →
|
|
2769
|
-
*
|
|
2770
|
-
*
|
|
2771
|
-
*
|
|
2772
|
-
*
|
|
2935
|
+
* artifacts marked inline) → 引用 (two switchable tabs: 技能 and 记忆).
|
|
2936
|
+
* The reference tabs replaced three stacked sections: which skills entered the
|
|
2937
|
+
* session, what prime-memory injected and compaction has not overwritten, and
|
|
2938
|
+
* the hand-pinned resident slots — all three answer "what is in this session's
|
|
2939
|
+
* context", and stacking them pushed the board off a narrow sidebar. Each pane
|
|
2940
|
+
* keeps its own data chain and its own gating contract; this component owns
|
|
2941
|
+
* only the shell.
|
|
2773
2942
|
*
|
|
2774
2943
|
* Two better-sidebar contracts are honoured:
|
|
2775
2944
|
*
|
|
@@ -2816,18 +2985,8 @@ window.__ModuleLoader__.load({
|
|
|
2816
2985
|
t,
|
|
2817
2986
|
ctx,
|
|
2818
2987
|
scope
|
|
2819
|
-
}), (0, react.createElement)(
|
|
2820
|
-
key: "
|
|
2821
|
-
t,
|
|
2822
|
-
ctx,
|
|
2823
|
-
scope
|
|
2824
|
-
}), (0, react.createElement)(MemoryRecallSection, {
|
|
2825
|
-
key: "memory-recall",
|
|
2826
|
-
t,
|
|
2827
|
-
ctx,
|
|
2828
|
-
scope
|
|
2829
|
-
}), (0, react.createElement)(MemorySlotsSection, {
|
|
2830
|
-
key: "memory-slots",
|
|
2988
|
+
}), (0, react.createElement)(ReferenceSection, {
|
|
2989
|
+
key: "references",
|
|
2831
2990
|
t,
|
|
2832
2991
|
ctx,
|
|
2833
2992
|
scope
|