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/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$4 = {
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$5 = {
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$4,
319
+ style: SECTION_STYLE$5,
320
320
  "data-dsh-brief-sidebar": "progress-section"
321
- }, (0, react.createElement)("div", { style: HEADER_STYLE$5 }, t("section.progress")), (0, react.createElement)(Notice, {
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$4,
326
+ style: SECTION_STYLE$5,
327
327
  "data-dsh-brief-sidebar": "progress-section"
328
- }, (0, react.createElement)("div", { style: HEADER_STYLE$5 }, t("section.progress")), (0, react.createElement)(Notice, {
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$4,
334
+ style: SECTION_STYLE$5,
335
335
  "data-dsh-brief-sidebar": "progress-section"
336
- }, (0, react.createElement)("div", { style: HEADER_STYLE$5 }, 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, {
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$3 = {
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$4 = {
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$3,
1345
+ style: SECTION_STYLE$4,
1319
1346
  "data-dsh-brief-sidebar": "deliverables-section"
1320
- }, (0, react.createElement)("div", { style: HEADER_STYLE$4 }, t("section.deliverables")), (0, react.createElement)("div", { 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$3,
1360
+ style: SECTION_STYLE$4,
1334
1361
  "data-dsh-brief-sidebar": "deliverables-section"
1335
- }, (0, react.createElement)("div", { style: HEADER_STYLE$4 }, t("section.deliverables")), latestPaths.length > 0 && (0, react.createElement)(GroupHeader, {
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/SkillUsageSection.tsx
1486
+ //#region src/client/summary/memory-recall.ts
1460
1487
  /**
1461
- * The skill-usage section: the `dshSummarySkillUsage` projection drawn as one
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
- * Like the deliverables section this is an OPTIONAL capability on both sides:
1478
- * no projection registry (or a host half that never registered the unit) means
1479
- * the client reads capability absence and the section hides. No settings switch
1480
- * gates it either — the list is a read-only fact about the session, not a
1481
- * preference.
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
- * Two contracts this row honours, both learned on the recall section:
1484
- * - **no native `title` attribute** — it paints an unskinable tooltip that
1485
- * overlaps the plugin's own bubble; nothing here needs one;
1486
- * - **colours from `--dsw-alias-*` tokens only** (skin contract).
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
- const SECTION_STYLE$2 = {
1489
- flexShrink: 0,
1490
- borderBottom: "1px solid var(--dsw-alias-border-secondary)"
1491
- };
1492
- const HEADER_STYLE$3 = {
1493
- padding: "10px 12px 0",
1494
- color: "var(--dsw-alias-label-secondary)",
1495
- fontSize: 12,
1496
- fontWeight: 600
1497
- };
1498
- const LIST_STYLE$2 = {
1499
- listStyle: "none",
1500
- margin: 0,
1501
- padding: "6px 0",
1502
- display: "flex",
1503
- flexDirection: "column"
1504
- };
1505
- const ROW_STYLE$3 = {
1506
- display: "flex",
1507
- alignItems: "baseline",
1508
- gap: 8,
1509
- padding: "6px 12px"
1510
- };
1511
- /** The skill name: primary-ish but never the loudest thing in the row. */
1512
- const NAME_STYLE$1 = {
1513
- minWidth: 0,
1514
- overflowWrap: "anywhere",
1515
- color: "var(--dsw-alias-label-secondary)"
1516
- };
1517
- /** The per-skill load tally, tertiary so the name stays the reading target. */
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
- * The skill-usage section. The subscription is unconditional (a plain listener,
1550
- * not IO); the view decides whether anything is drawn.
1535
+ * Narrow a raw projection value into a recall view.
1551
1536
  *
1552
- * @param props - translation, client context, session scope.
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 SkillUsageSection(props) {
1555
- const { t, ctx, scope } = props;
1556
- const [availableOpen, setAvailableOpen] = (0, react.useState)(false);
1557
- const view = readSkillUsage(useProjectionValue(ctx, scope?.sessionId, SKILL_USAGE_KEY));
1558
- if (view === void 0) return null;
1559
- const referenced = view.session.map((entry) => entry.name);
1560
- const rest = availableExcept(view.available, referenced);
1561
- const latestNames = view.latest?.names ?? [];
1562
- return (0, react.createElement)("div", {
1563
- style: SECTION_STYLE$2,
1564
- "data-dsh-brief-sidebar": "skill-usage-section"
1565
- }, (0, react.createElement)("div", { style: HEADER_STYLE$3 }, t("section.skillUsage")), view.session.length === 0 && (0, react.createElement)("div", { style: EMPTY_STYLE$2 }, (0, react.createElement)("div", { style: {
1566
- marginBottom: 6,
1567
- color: "var(--dsw-alias-label-secondary)"
1568
- } }, t("skillUsage.empty")), (0, react.createElement)("div", null, t("skillUsage.emptyHint"))), view.session.length > 0 && (0, react.createElement)(GroupHeader, {
1569
- text: expandCount(t("skillUsage.invokedGroup"), view.session.length),
1570
- marker: "skill-usage-invoked-group"
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$1(raw["rev"]) || !isCounter$1(raw["count"]) || !isCounter$1(raw["openCount"])) return void 0;
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$1(value) {
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/MemorySlotsSection.tsx
1730
+ //#region src/client/summary/SkillUsageSection.tsx
1759
1731
  /**
1760
- * The memory-slots section: the `memorySlots` projection drawn as the third
1761
- * part of the summary tab (fixed last: 进展 → 产物 → 记忆).
1732
+ * The skill-usage section: the `dshSummarySkillUsage` projection drawn as one
1733
+ * part of the summary tab.
1762
1734
  *
1763
- * TWO gates, both fail-open to "hidden", so a host without dsh-prime-memory
1764
- * paints nothing here:
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
- * 1. **The switch.** This plugin's entry config (`configForms` → entry
1767
- * `dsh-brief-sidebar`, field `showMemorySlots`, default OFF — the pinned
1768
- * list moved out of the summary pane's way in favour of the session-recall
1769
- * section). When the config face cannot answer (service absent, scope not
1770
- * ready, field unset) the default applies — see `scope-face.ts`.
1771
- * 2. **The capability.** The `memorySlots` projection must exist for the
1772
- * session; `undefined` hides the section entirely, exactly like the
1773
- * optional deliverables section.
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
- * Colours come from `--dsw-alias-*` tokens only (skin contract, enforced by
1776
- * `tests/purity.spec.ts`).
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$1 = {
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$2 = {
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$1 = {
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$2 = {
1776
+ const ROW_STYLE$3 = {
1796
1777
  display: "flex",
1797
- alignItems: "flex-start",
1778
+ alignItems: "baseline",
1798
1779
  gap: 8,
1799
1780
  padding: "6px 12px"
1800
1781
  };
1801
- const TITLE_STYLE$1 = {
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
- const META_STYLE$1 = {
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 kind pill: same form as the codeplan pill, tertiary emphasis. */
1814
- const KIND_STYLE = {
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
- /** The status pill: filled tint for an open slot, plain text otherwise. */
1825
- const STATUS_OPEN_STYLE = {
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 TOTAL_STYLE = {
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
- * One slot row: kind pill, title (full body rides the tooltip), status pill,
1875
- * priority; body line underneath when the wire view carries it; refs as
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 MemorySlotsSection(props) {
1915
- const { t, ctx, scope } = props;
1916
- const show = useShowMemorySlots(ctx);
1917
- const view = readSlots(useProjectionValue(ctx, scope?.sessionId, MEMORY_SLOTS_KEY));
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
- if (view.slots.length === 0) return (0, react.createElement)("div", {
1921
- style: SECTION_STYLE$1,
1922
- "data-dsh-brief-sidebar": "memory-slots-section"
1923
- }, (0, react.createElement)("div", { style: HEADER_STYLE$2 }, t("section.memorySlots")), (0, react.createElement)("div", { style: EMPTY_STYLE$1 }, (0, react.createElement)("div", { style: {
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("slots.empty")), (0, react.createElement)("div", null, t("slots.emptyHint"))));
1927
- return (0, react.createElement)("div", {
1928
- style: SECTION_STYLE$1,
1929
- "data-dsh-brief-sidebar": "memory-slots-section"
1930
- }, (0, react.createElement)("div", { style: HEADER_STYLE$2 }, t("section.memorySlots")), (0, react.createElement)("ul", { style: LIST_STYLE$1 }, view.slots.map((slot) => (0, react.createElement)(SlotRow, {
1931
- key: slot.id,
1932
- slot,
1933
- t
1934
- }))), (0, react.createElement)("div", { style: TOTAL_STYLE }, expandCount(t("slots.openCount"), view.openCount)));
1935
- }
1936
- //#endregion
1937
- //#region src/client/summary/memory-recall.ts
1938
- /**
1939
- * The `memoryRecall` projection — reading it.
1940
- *
1941
- * The projection unit is registered by `dsh-prime-memory` (key `memoryRecall`,
1942
- * wire view `{rev, sessionId, count, items[]}`): the memories this session has
1943
- * actually injected. Unlike
1944
- * `memorySlots` (pinned, resident conventions written by hand), this set is
1945
- * NOT resident — it is the at-a-glance answer to "what did the model see".
1946
- * It survives compaction on purpose: `compact` only marks the entries whose
1947
- * text left the context as `superseded` (and `/clear` empties the set), so a
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$1 = {
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$1,
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$1 }, 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));
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$1 = {
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$1 = {
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$1,
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$1,
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$1 }, t("section.memoryRecall")), (0, react.createElement)("div", {
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$1 }, 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, {
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) → 技能引用 (which skills this session loaded) →
2769
- * 本会话召回 (what prime-memory injected into this session and compaction has
2770
- * not overwritten) → 记忆槽位 (hand-pinned resident conventions, off by default).
2771
- * Each section owns its own data chain and its own gating contract; this
2772
- * component owns only the shell.
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)(SkillUsageSection, {
2820
- key: "skill-usage",
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