dsh-brief-sidebar 0.9.3 → 0.9.4

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$5 = {
234
+ const SECTION_STYLE$6 = {
235
235
  flexShrink: 0,
236
236
  borderBottom: "1px solid var(--dsw-alias-border-secondary)"
237
237
  };
238
- const HEADER_STYLE$6 = {
238
+ const HEADER_STYLE$7 = {
239
239
  padding: "10px 12px 0",
240
240
  color: "var(--dsw-alias-label-secondary)",
241
241
  fontSize: 12,
@@ -246,14 +246,14 @@ window.__ModuleLoader__.load({
246
246
  color: "var(--dsw-alias-label-secondary)",
247
247
  fontSize: 12
248
248
  };
249
- const LIST_STYLE$4 = {
249
+ const LIST_STYLE$5 = {
250
250
  listStyle: "none",
251
251
  margin: 0,
252
252
  padding: "6px 0",
253
253
  display: "flex",
254
254
  flexDirection: "column"
255
255
  };
256
- const ROW_STYLE$5 = {
256
+ const ROW_STYLE$6 = {
257
257
  display: "flex",
258
258
  alignItems: "flex-start",
259
259
  gap: 8,
@@ -305,7 +305,7 @@ window.__ModuleLoader__.load({
305
305
  /** One task row. */
306
306
  function TodoRow(props) {
307
307
  const { item, t } = props;
308
- return (0, react.createElement)("li", { style: ROW_STYLE$5 }, (0, react.createElement)("span", { style: pillStyle(item.status) }, t(statusKey(item.status))), (0, react.createElement)("span", { style: contentStyle(item.status) }, item.content));
308
+ return (0, react.createElement)("li", { style: ROW_STYLE$6 }, (0, react.createElement)("span", { style: pillStyle(item.status) }, t(statusKey(item.status))), (0, react.createElement)("span", { style: contentStyle(item.status) }, item.content));
309
309
  }
310
310
  /**
311
311
  * The progress section. The subscription is unconditional (a plain listener,
@@ -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$5,
319
+ style: SECTION_STYLE$6,
320
320
  "data-dsh-brief-sidebar": "progress-section"
321
- }, (0, react.createElement)("div", { style: HEADER_STYLE$6 }, t("section.progress")), (0, react.createElement)(Notice, {
321
+ }, (0, react.createElement)("div", { style: HEADER_STYLE$7 }, 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$5,
326
+ style: SECTION_STYLE$6,
327
327
  "data-dsh-brief-sidebar": "progress-section"
328
- }, (0, react.createElement)("div", { style: HEADER_STYLE$6 }, t("section.progress")), (0, react.createElement)(Notice, {
328
+ }, (0, react.createElement)("div", { style: HEADER_STYLE$7 }, 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$5,
334
+ style: SECTION_STYLE$6,
335
335
  "data-dsh-brief-sidebar": "progress-section"
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, {
336
+ }, (0, react.createElement)("div", { style: HEADER_STYLE$7 }, t("section.progress")), (0, react.createElement)("div", { style: PROGRESS_STYLE }, progressLine(counts, t)), (0, react.createElement)("ul", { style: LIST_STYLE$5 }, todos.map((item, index) => (0, react.createElement)(TodoRow, {
337
337
  key: `${index}:${item.content}`,
338
338
  item,
339
339
  t
@@ -362,6 +362,12 @@ window.__ModuleLoader__.load({
362
362
  * to collide silently.
363
363
  */
364
364
  const SKILL_USAGE_KEY = "dshSummarySkillUsage";
365
+ /**
366
+ * The search-reference key (queries and returned sources of this session's
367
+ * `advanced_search` calls). Same rule as above: the name is ours, so no other
368
+ * host-side unit can answer for it by accident.
369
+ */
370
+ const SEARCH_REFS_KEY = "dshSummarySearchRefs";
365
371
  //#endregion
366
372
  //#region src/client/file-open.ts
367
373
  /** The address scheme every file resource opens with. */
@@ -699,6 +705,16 @@ window.__ModuleLoader__.load({
699
705
  "skillUsage.invokedGroup": "已引用 · {count}",
700
706
  "skillUsage.availableGroup": "本会话可用 · {count}",
701
707
  "skillUsage.availableCapped": "(可用清单共 {count} 条,列表已截断)",
708
+ "section.searchRefs": "搜索引用",
709
+ "references.tabSearch": "搜索",
710
+ "searchRefs.empty": "本会话还没搜索过",
711
+ "searchRefs.emptyHint": "模型调用 advanced_search 之后,查询与取回的外部来源会实时出现在这里。",
712
+ "searchRefs.queryGroup": "查询 · {count}",
713
+ "searchRefs.sourceGroup": "来源 · {count}",
714
+ "searchRefs.hits": "返回 {count} 条",
715
+ "searchRefs.latestTag": "本轮",
716
+ "searchRefs.noSourcesHint": "(该版本未回传来源清单,升级 dsh-free-search 后可见)",
717
+ "searchRefs.capped": "(会话共 {total} 条,列表已截断)",
702
718
  "settings.title": "概要侧栏",
703
719
  "settings.desc": "右侧栏概要 tab 的分区开关(只读展示)。",
704
720
  "settings.memorySlots": "记忆槽位分区",
@@ -762,6 +778,16 @@ window.__ModuleLoader__.load({
762
778
  "skillUsage.invokedGroup": "Referenced · {count}",
763
779
  "skillUsage.availableGroup": "Available this session · {count}",
764
780
  "skillUsage.availableCapped": "({count} skills in the catalog; the list is capped)",
781
+ "section.searchRefs": "Web searches",
782
+ "references.tabSearch": "Searches",
783
+ "searchRefs.empty": "No searches in this session",
784
+ "searchRefs.emptyHint": "Queries and the external sources they returned appear here once the model calls advanced_search.",
785
+ "searchRefs.queryGroup": "Queries · {count}",
786
+ "searchRefs.sourceGroup": "Sources · {count}",
787
+ "searchRefs.hits": "{count} returned",
788
+ "searchRefs.latestTag": "This turn",
789
+ "searchRefs.noSourcesHint": "(this dsh-free-search build returns no source list; upgrade to see sources)",
790
+ "searchRefs.capped": "({total} in the session; the list is capped)",
765
791
  "settings.title": "Brief sidebar",
766
792
  "settings.desc": "Section switches for the summary tab in the right sidebar (read-only views).",
767
793
  "settings.memorySlots": "Memory slots section",
@@ -825,6 +851,16 @@ window.__ModuleLoader__.load({
825
851
  "skillUsage.invokedGroup": "参照済み · {count}",
826
852
  "skillUsage.availableGroup": "このセッションで利用可能 · {count}",
827
853
  "skillUsage.availableCapped": "(カタログは全 {count} 件、一覧は上限で省略)",
854
+ "section.searchRefs": "Web 検索",
855
+ "references.tabSearch": "検索",
856
+ "searchRefs.empty": "このセッションではまだ検索していません",
857
+ "searchRefs.emptyHint": "モデルが advanced_search を呼び出すと、クエリと取得した外部ソースがここに表示されます。",
858
+ "searchRefs.queryGroup": "クエリ · {count}",
859
+ "searchRefs.sourceGroup": "ソース · {count}",
860
+ "searchRefs.hits": "{count} 件返却",
861
+ "searchRefs.latestTag": "今回のターン",
862
+ "searchRefs.noSourcesHint": "(この dsh-free-search ビルドはソース一覧を返しません。アップグレードすると表示されます)",
863
+ "searchRefs.capped": "(セッション全体で {total} 件、一覧は上限で省略)",
828
864
  "settings.title": "概要サイドバー",
829
865
  "settings.desc": "右サイドバー概要タブのセクション表示切り替え(読み取り専用)。",
830
866
  "settings.memorySlots": "メモリスロットセクション",
@@ -888,6 +924,16 @@ window.__ModuleLoader__.load({
888
924
  "skillUsage.invokedGroup": "참조됨 · {count}",
889
925
  "skillUsage.availableGroup": "이 세션에서 사용 가능 · {count}",
890
926
  "skillUsage.availableCapped": "(카탈로그 전체 {count}개, 목록은 잘림)",
927
+ "section.searchRefs": "웹 검색",
928
+ "references.tabSearch": "검색",
929
+ "searchRefs.empty": "이 세션에서는 아직 검색하지 않았습니다",
930
+ "searchRefs.emptyHint": "모델이 advanced_search를 호출하면 쿼리와 가져온 외부 소스가 여기에 표시됩니다.",
931
+ "searchRefs.queryGroup": "쿼리 · {count}",
932
+ "searchRefs.sourceGroup": "소스 · {count}",
933
+ "searchRefs.hits": "{count}件 반환",
934
+ "searchRefs.latestTag": "이번 턴",
935
+ "searchRefs.noSourcesHint": "(이 dsh-free-search 빌드는 소스 목록을 반환하지 않습니다. 업그레이드 필요)",
936
+ "searchRefs.capped": "(세션 전체 {total}건, 목록은 잘림)",
891
937
  "settings.title": "요약 사이드바",
892
938
  "settings.desc": "오른쪽 사이드바 요약 탭의 섹션 표시 전환(읽기 전용).",
893
939
  "settings.memorySlots": "메모리 슬롯 섹션",
@@ -951,6 +997,16 @@ window.__ModuleLoader__.load({
951
997
  "skillUsage.invokedGroup": "Référencées · {count}",
952
998
  "skillUsage.availableGroup": "Disponibles dans cette session · {count}",
953
999
  "skillUsage.availableCapped": "({count} compétences au catalogue ; la liste est tronquée)",
1000
+ "section.searchRefs": "Recherches web",
1001
+ "references.tabSearch": "Recherches",
1002
+ "searchRefs.empty": "Aucune recherche dans cette session",
1003
+ "searchRefs.emptyHint": "Les requêtes et les sources externes renvoyées apparaissent ici dès que le modèle appelle advanced_search.",
1004
+ "searchRefs.queryGroup": "Requêtes · {count}",
1005
+ "searchRefs.sourceGroup": "Sources · {count}",
1006
+ "searchRefs.hits": "{count} renvoyées",
1007
+ "searchRefs.latestTag": "Ce tour-ci",
1008
+ "searchRefs.noSourcesHint": "(cette version de dsh-free-search ne renvoie pas la liste des sources ; mettez à jour)",
1009
+ "searchRefs.capped": "({total} dans la session ; la liste est tronquée)",
954
1010
  "settings.title": "Barre latérale Résumé",
955
1011
  "settings.desc": "Interrupteurs des sections de l’onglet Résumé de la barre latérale (lecture seule).",
956
1012
  "settings.memorySlots": "Section des emplacements mémoire",
@@ -1014,6 +1070,16 @@ window.__ModuleLoader__.load({
1014
1070
  "skillUsage.invokedGroup": "Referenziert · {count}",
1015
1071
  "skillUsage.availableGroup": "In dieser Sitzung verfügbar · {count}",
1016
1072
  "skillUsage.availableCapped": "({count} Skills im Katalog; die Liste ist gekürzt)",
1073
+ "section.searchRefs": "Websuchen",
1074
+ "references.tabSearch": "Suchen",
1075
+ "searchRefs.empty": "Keine Suchen in dieser Sitzung",
1076
+ "searchRefs.emptyHint": "Abfragen und die zurückgegebenen externen Quellen erscheinen hier, sobald das Modell advanced_search aufruft.",
1077
+ "searchRefs.queryGroup": "Abfragen · {count}",
1078
+ "searchRefs.sourceGroup": "Quellen · {count}",
1079
+ "searchRefs.hits": "{count} zurückgegeben",
1080
+ "searchRefs.latestTag": "Dieser Durchlauf",
1081
+ "searchRefs.noSourcesHint": "(dieser dsh-free-search-Build liefert keine Quellenliste; aktualisieren, um Quellen zu sehen)",
1082
+ "searchRefs.capped": "({total} in der Sitzung; die Liste ist gekürzt)",
1017
1083
  "settings.title": "Übersicht-Seitenleiste",
1018
1084
  "settings.desc": "Schalter für die Abschnitte des Übersicht-Tabs in der rechten Seitenleiste (schreibgeschützt).",
1019
1085
  "settings.memorySlots": "Speicherplätze-Abschnitt",
@@ -1077,6 +1143,16 @@ window.__ModuleLoader__.load({
1077
1143
  "skillUsage.invokedGroup": "Referenziate · {count}",
1078
1144
  "skillUsage.availableGroup": "Disponibili in questa sessione · {count}",
1079
1145
  "skillUsage.availableCapped": "({count} skill nel catalogo; l’elenco è troncato)",
1146
+ "section.searchRefs": "Ricerche web",
1147
+ "references.tabSearch": "Ricerche",
1148
+ "searchRefs.empty": "Nessuna ricerca in questa sessione",
1149
+ "searchRefs.emptyHint": "Le query e le fonti esterne restituite compaiono qui quando il modello chiama advanced_search.",
1150
+ "searchRefs.queryGroup": "Query · {count}",
1151
+ "searchRefs.sourceGroup": "Fonti · {count}",
1152
+ "searchRefs.hits": "{count} restituite",
1153
+ "searchRefs.latestTag": "Questo turno",
1154
+ "searchRefs.noSourcesHint": "(questa build di dsh-free-search non restituisce l’elenco delle fonti; aggiorna per vederlo)",
1155
+ "searchRefs.capped": "({total} nella sessione; l’elenco è troncato)",
1080
1156
  "settings.title": "Barra laterale Riepilogo",
1081
1157
  "settings.desc": "Interruttori delle sezioni della scheda Riepilogo nella barra laterale destra (sola lettura).",
1082
1158
  "settings.memorySlots": "Sezione slot di memoria",
@@ -1140,6 +1216,16 @@ window.__ModuleLoader__.load({
1140
1216
  "skillUsage.invokedGroup": "Использованы · {count}",
1141
1217
  "skillUsage.availableGroup": "Доступны в этой сессии · {count}",
1142
1218
  "skillUsage.availableCapped": "({count} навыков в каталоге; список усечён)",
1219
+ "section.searchRefs": "Поиск в сети",
1220
+ "references.tabSearch": "Поиск",
1221
+ "searchRefs.empty": "В этой сессии ещё не было поиска",
1222
+ "searchRefs.emptyHint": "Запросы и возвращённые внешние источники появятся здесь, как только модель вызовет advanced_search.",
1223
+ "searchRefs.queryGroup": "Запросы · {count}",
1224
+ "searchRefs.sourceGroup": "Источники · {count}",
1225
+ "searchRefs.hits": "возвращено {count}",
1226
+ "searchRefs.latestTag": "Этот ход",
1227
+ "searchRefs.noSourcesHint": "(эта сборка dsh-free-search не возвращает список источников; обновите, чтобы видеть их)",
1228
+ "searchRefs.capped": "(в сессии всего {total}; список усечён)",
1143
1229
  "settings.title": "Боковая панель «Сводка»",
1144
1230
  "settings.desc": "Переключатели разделов вкладки «Сводка» на правой панели (только чтение).",
1145
1231
  "settings.memorySlots": "Раздел слотов памяти",
@@ -1203,6 +1289,16 @@ window.__ModuleLoader__.load({
1203
1289
  "skillUsage.invokedGroup": "Referenciadas · {count}",
1204
1290
  "skillUsage.availableGroup": "Disponibles en esta sesión · {count}",
1205
1291
  "skillUsage.availableCapped": "({count} skills en el catálogo; la lista está truncada)",
1292
+ "section.searchRefs": "Búsquedas web",
1293
+ "references.tabSearch": "Búsquedas",
1294
+ "searchRefs.empty": "Sin búsquedas en esta sesión",
1295
+ "searchRefs.emptyHint": "Las consultas y las fuentes externas devueltas aparecen aquí cuando el modelo llama a advanced_search.",
1296
+ "searchRefs.queryGroup": "Consultas · {count}",
1297
+ "searchRefs.sourceGroup": "Fuentes · {count}",
1298
+ "searchRefs.hits": "{count} devueltas",
1299
+ "searchRefs.latestTag": "Este turno",
1300
+ "searchRefs.noSourcesHint": "(esta versión de dsh-free-search no devuelve la lista de fuentes; actualiza para verla)",
1301
+ "searchRefs.capped": "({total} en la sesión; la lista está truncada)",
1206
1302
  "settings.title": "Barra lateral Resumen",
1207
1303
  "settings.desc": "Interruptores de las secciones de la pestaña Resumen en la barra lateral derecha (solo lectura).",
1208
1304
  "settings.memorySlots": "Sección de ranuras de memoria",
@@ -1249,24 +1345,24 @@ window.__ModuleLoader__.load({
1249
1345
  *
1250
1346
  * Colours come from `--dsw-alias-*` tokens only (skin contract).
1251
1347
  */
1252
- const SECTION_STYLE$4 = {
1348
+ const SECTION_STYLE$5 = {
1253
1349
  flexShrink: 0,
1254
1350
  borderBottom: "1px solid var(--dsw-alias-border-secondary)"
1255
1351
  };
1256
- const HEADER_STYLE$5 = {
1352
+ const HEADER_STYLE$6 = {
1257
1353
  padding: "10px 12px 0",
1258
1354
  color: "var(--dsw-alias-label-secondary)",
1259
1355
  fontSize: 12,
1260
1356
  fontWeight: 600
1261
1357
  };
1262
- const LIST_STYLE$3 = {
1358
+ const LIST_STYLE$4 = {
1263
1359
  listStyle: "none",
1264
1360
  margin: 0,
1265
1361
  padding: "6px 0",
1266
1362
  display: "flex",
1267
1363
  flexDirection: "column"
1268
1364
  };
1269
- const ROW_STYLE$4 = {
1365
+ const ROW_STYLE$5 = {
1270
1366
  display: "flex",
1271
1367
  alignItems: "flex-start",
1272
1368
  gap: 8,
@@ -1290,7 +1386,7 @@ window.__ModuleLoader__.load({
1290
1386
  cursor: "pointer"
1291
1387
  };
1292
1388
  /** The codeplan pill: same form as the status pills, tertiary emphasis. */
1293
- const TAG_STYLE$1 = {
1389
+ const TAG_STYLE$2 = {
1294
1390
  flexShrink: 0,
1295
1391
  color: "var(--dsw-alias-label-tertiary)",
1296
1392
  border: "1px solid var(--dsw-alias-label-tertiary)",
@@ -1311,12 +1407,12 @@ window.__ModuleLoader__.load({
1311
1407
  const plan = splitCodeplanPath(path);
1312
1408
  const title = plan === null ? path : `${t("deliverable.codeplanTag")}: ${plan.task} · ${path}`;
1313
1409
  const label = basename(path);
1314
- const content = plan === null ? null : (0, react.createElement)("span", { style: TAG_STYLE$1 }, t("deliverable.codeplanTag"));
1315
- if (open === void 0) return (0, react.createElement)("li", { style: ROW_STYLE$4 }, content, (0, react.createElement)("span", {
1410
+ const content = plan === null ? null : (0, react.createElement)("span", { style: TAG_STYLE$2 }, t("deliverable.codeplanTag"));
1411
+ if (open === void 0) return (0, react.createElement)("li", { style: ROW_STYLE$5 }, content, (0, react.createElement)("span", {
1316
1412
  style: PATH_STYLE,
1317
1413
  title
1318
1414
  }, label));
1319
- return (0, react.createElement)("li", { style: ROW_STYLE$4 }, content, (0, react.createElement)("button", {
1415
+ return (0, react.createElement)("li", { style: ROW_STYLE$5 }, content, (0, react.createElement)("button", {
1320
1416
  style: BUTTON_STYLE,
1321
1417
  title,
1322
1418
  onClick: () => {
@@ -1342,9 +1438,9 @@ window.__ModuleLoader__.load({
1342
1438
  const latestPaths = view.latest?.paths ?? [];
1343
1439
  const history = historyPaths(view.sessionPaths, latestPaths);
1344
1440
  if (latestPaths.length === 0 && view.sessionTotal === 0) return (0, react.createElement)("div", {
1345
- style: SECTION_STYLE$4,
1441
+ style: SECTION_STYLE$5,
1346
1442
  "data-dsh-brief-sidebar": "deliverables-section"
1347
- }, (0, react.createElement)("div", { style: HEADER_STYLE$5 }, t("section.deliverables")), (0, react.createElement)("div", { style: {
1443
+ }, (0, react.createElement)("div", { style: HEADER_STYLE$6 }, t("section.deliverables")), (0, react.createElement)("div", { style: {
1348
1444
  margin: "0 auto",
1349
1445
  padding: 24,
1350
1446
  maxWidth: 460,
@@ -1357,12 +1453,12 @@ window.__ModuleLoader__.load({
1357
1453
  color: "var(--dsw-alias-label-secondary)"
1358
1454
  } }, t("deliverables.empty")), (0, react.createElement)("div", null, t("deliverables.emptyHint"))));
1359
1455
  return (0, react.createElement)("div", {
1360
- style: SECTION_STYLE$4,
1456
+ style: SECTION_STYLE$5,
1361
1457
  "data-dsh-brief-sidebar": "deliverables-section"
1362
- }, (0, react.createElement)("div", { style: HEADER_STYLE$5 }, t("section.deliverables")), latestPaths.length > 0 && (0, react.createElement)(GroupHeader, {
1458
+ }, (0, react.createElement)("div", { style: HEADER_STYLE$6 }, t("section.deliverables")), latestPaths.length > 0 && (0, react.createElement)(GroupHeader, {
1363
1459
  text: expandCount(t("deliverables.currentTurn"), latestPaths.length),
1364
1460
  marker: "deliverables-current-group"
1365
- }), (0, react.createElement)("ul", { style: LIST_STYLE$3 }, latestPaths.map((path) => (0, react.createElement)(PathRow, {
1461
+ }), (0, react.createElement)("ul", { style: LIST_STYLE$4 }, latestPaths.map((path) => (0, react.createElement)(PathRow, {
1366
1462
  key: path,
1367
1463
  path,
1368
1464
  t,
@@ -1377,7 +1473,7 @@ window.__ModuleLoader__.load({
1377
1473
  }), history.length > 0 && view.sessionTotal > view.sessionPaths.length && (0, react.createElement)(GroupCaption, {
1378
1474
  text: expandTotal(t("deliverables.historyCapped"), view.sessionTotal),
1379
1475
  marker: "deliverables-history-capped"
1380
- }), historyOpen && (0, react.createElement)("ul", { style: LIST_STYLE$3 }, history.map((path) => (0, react.createElement)(PathRow, {
1476
+ }), historyOpen && (0, react.createElement)("ul", { style: LIST_STYLE$4 }, history.map((path) => (0, react.createElement)(PathRow, {
1381
1477
  key: path,
1382
1478
  path,
1383
1479
  t,
@@ -1387,7 +1483,7 @@ window.__ModuleLoader__.load({
1387
1483
  //#endregion
1388
1484
  //#region src/client/summary/skill-usage.ts
1389
1485
  /** Whether a value is a non-negative safe integer (the view's counter shape). */
1390
- function isCounter$2(value) {
1486
+ function isCounter$3(value) {
1391
1487
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
1392
1488
  }
1393
1489
  /** Narrow one skill tally; `undefined` = malformed, dropped. */
@@ -1395,7 +1491,7 @@ window.__ModuleLoader__.load({
1395
1491
  if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
1396
1492
  const raw = value;
1397
1493
  if (typeof raw["name"] !== "string" || raw["name"].length === 0) return void 0;
1398
- if (!isCounter$2(raw["count"]) || !isCounter$2(raw["lastTurn"])) return void 0;
1494
+ if (!isCounter$3(raw["count"]) || !isCounter$3(raw["lastTurn"])) return void 0;
1399
1495
  return {
1400
1496
  name: raw["name"],
1401
1497
  count: raw["count"],
@@ -1406,10 +1502,10 @@ window.__ModuleLoader__.load({
1406
1502
  * The latest-turn bucket, or `null` when it is absent OR drifted. A drifted
1407
1503
  * bucket only costs the "本轮" tag, so it degrades instead of voiding the view.
1408
1504
  */
1409
- function readLatest(value) {
1505
+ function readLatest$1(value) {
1410
1506
  if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
1411
1507
  const raw = value;
1412
- if (!isCounter$2(raw["turn"]) || !Array.isArray(raw["names"])) return null;
1508
+ if (!isCounter$3(raw["turn"]) || !Array.isArray(raw["names"])) return null;
1413
1509
  return {
1414
1510
  turn: raw["turn"],
1415
1511
  names: raw["names"].filter((n) => typeof n === "string" && n.length > 0)
@@ -1437,16 +1533,16 @@ window.__ModuleLoader__.load({
1437
1533
  if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
1438
1534
  const raw = value;
1439
1535
  if (!Array.isArray(raw["session"])) return void 0;
1440
- if (!isCounter$2(raw["sessionTotal"]) || !isCounter$2(raw["callTotal"])) return void 0;
1536
+ if (!isCounter$3(raw["sessionTotal"]) || !isCounter$3(raw["callTotal"])) return void 0;
1441
1537
  const session = [];
1442
1538
  for (const entry of raw["session"]) {
1443
1539
  const narrowed = readEntry(entry);
1444
1540
  if (narrowed !== void 0) session.push(narrowed);
1445
1541
  }
1446
1542
  const available = readAvailable(raw["available"]);
1447
- const availableCount = available.length === 0 ? 0 : isCounter$2(raw["availableCount"]) ? raw["availableCount"] : available.length;
1543
+ const availableCount = available.length === 0 ? 0 : isCounter$3(raw["availableCount"]) ? raw["availableCount"] : available.length;
1448
1544
  return {
1449
- latest: readLatest(raw["latest"]),
1545
+ latest: readLatest$1(raw["latest"]),
1450
1546
  session,
1451
1547
  sessionTotal: raw["sessionTotal"],
1452
1548
  callTotal: raw["callTotal"],
@@ -1483,6 +1579,129 @@ window.__ModuleLoader__.load({
1483
1579
  return template.replace("{count}", String(count)).replace("{total}", String(total));
1484
1580
  }
1485
1581
  //#endregion
1582
+ //#region src/projection/search-ref-fold.ts
1583
+ /**
1584
+ * The dedupe key. Host case and a trailing slash are not meaningful for
1585
+ * identity; path case IS, so it is left alone. Exported because the client
1586
+ * matches a row against the latest-turn bucket with the same algorithm.
1587
+ */
1588
+ function urlKey(url) {
1589
+ const trimmed = url.trim();
1590
+ return (trimmed.endsWith("/") ? trimmed.slice(0, -1) : trimmed).replace(/^[a-z][a-z0-9+.-]*:\/\/[^/]+/i, (match) => match.toLowerCase());
1591
+ }
1592
+ //#endregion
1593
+ //#region src/client/summary/search-refs.ts
1594
+ /**
1595
+ * The `dshSummarySearchRefs` projection — reading it.
1596
+ *
1597
+ * The unit is registered by THIS plugin's host half (key `dshSummarySearchRefs`):
1598
+ * the queries this session ran through `advanced_search`, and the sources those
1599
+ * searches brought back. It answers "what did the session pull in from outside"
1600
+ * — where the skill tab says which instruction packs were loaded and the memory
1601
+ * tab says which of the user's own knowledge was injected.
1602
+ *
1603
+ * Reading discipline, same as every other brief section:
1604
+ * - `undefined` = the capability is ABSENT (no projection registry, or a frame
1605
+ * drifted past repair) → the tab hides;
1606
+ * - empty lists = the honest empty state (the session has not searched yet);
1607
+ * - a malformed row is dropped, never thrown.
1608
+ *
1609
+ * The module is deliberately PURE — no React, no context, no IO.
1610
+ */
1611
+ /** Whether a value is a non-negative safe integer (the view's counters). */
1612
+ function isCounter$2(value) {
1613
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
1614
+ }
1615
+ /** Narrow one source row; `undefined` = malformed, dropped. */
1616
+ function readItem$1(value) {
1617
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
1618
+ const raw = value;
1619
+ if (typeof raw["url"] !== "string" || raw["url"].length === 0) return void 0;
1620
+ if (typeof raw["host"] !== "string") return void 0;
1621
+ if (typeof raw["query"] !== "string") return void 0;
1622
+ if (!isCounter$2(raw["count"]) || !isCounter$2(raw["lastTurn"])) return void 0;
1623
+ const item = {
1624
+ url: raw["url"],
1625
+ host: raw["host"],
1626
+ query: raw["query"],
1627
+ count: raw["count"],
1628
+ lastTurn: raw["lastTurn"]
1629
+ };
1630
+ if (typeof raw["title"] === "string" && raw["title"].length > 0) item.title = raw["title"];
1631
+ if (typeof raw["snippet"] === "string" && raw["snippet"].length > 0) item.snippet = raw["snippet"];
1632
+ return item;
1633
+ }
1634
+ /** Narrow one query row; `undefined` = malformed, dropped. */
1635
+ function readQuery(value) {
1636
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
1637
+ const raw = value;
1638
+ if (typeof raw["query"] !== "string" || raw["query"].length === 0) return void 0;
1639
+ if (!isCounter$2(raw["turn"]) || !isCounter$2(raw["hits"])) return void 0;
1640
+ return {
1641
+ query: raw["query"],
1642
+ turn: raw["turn"],
1643
+ hits: raw["hits"]
1644
+ };
1645
+ }
1646
+ /** The latest-turn key set, or `null` when absent OR drifted (only a tag rides it). */
1647
+ function readLatest(value) {
1648
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
1649
+ const raw = value;
1650
+ if (!isCounter$2(raw["turn"]) || !Array.isArray(raw["urls"])) return null;
1651
+ return {
1652
+ turn: raw["turn"],
1653
+ urls: raw["urls"].filter((u) => typeof u === "string" && u.length > 0)
1654
+ };
1655
+ }
1656
+ /**
1657
+ * Narrow a raw projection value into a search-reference view.
1658
+ *
1659
+ * @param value - the raw `unknown` snapshot of the `dshSummarySearchRefs` face.
1660
+ * @returns the validated view, or `undefined` when the capability is absent.
1661
+ */
1662
+ function readSearchRefs(value) {
1663
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
1664
+ const raw = value;
1665
+ if (!Array.isArray(raw["queries"]) || !Array.isArray(raw["sources"])) return void 0;
1666
+ if (!isCounter$2(raw["queryTotal"]) || !isCounter$2(raw["sourceTotal"])) return void 0;
1667
+ const queries = [];
1668
+ for (const entry of raw["queries"]) {
1669
+ const narrowed = readQuery(entry);
1670
+ if (narrowed !== void 0) queries.push(narrowed);
1671
+ }
1672
+ const sources = [];
1673
+ for (const entry of raw["sources"]) {
1674
+ const narrowed = readItem$1(entry);
1675
+ if (narrowed !== void 0) sources.push(narrowed);
1676
+ }
1677
+ return {
1678
+ latest: readLatest(raw["latest"]),
1679
+ queries,
1680
+ sources,
1681
+ queryTotal: raw["queryTotal"],
1682
+ sourceTotal: raw["sourceTotal"]
1683
+ };
1684
+ }
1685
+ /** Whether one source row was surfaced by the latest turn's searches. */
1686
+ function isLatestSource(view, url) {
1687
+ return view.latest?.urls.includes(urlKey(url)) === true;
1688
+ }
1689
+ /**
1690
+ * The href to render for a source URL, or `undefined` when it must not be a link.
1691
+ *
1692
+ * Search results are untrusted external data: a `javascript:` or `data:` URL in
1693
+ * a provider response would otherwise become a clickable footgun in the user's
1694
+ * own sidebar. Only web schemes get an anchor; everything else renders as text.
1695
+ */
1696
+ function safeHref(url) {
1697
+ try {
1698
+ const parsed = new URL(url);
1699
+ return parsed.protocol === "https:" || parsed.protocol === "http:" ? parsed.href : void 0;
1700
+ } catch {
1701
+ return;
1702
+ }
1703
+ }
1704
+ //#endregion
1486
1705
  //#region src/client/summary/memory-recall.ts
1487
1706
  /**
1488
1707
  * The `memoryRecall` projection — reading it.
@@ -1756,24 +1975,24 @@ window.__ModuleLoader__.load({
1756
1975
  * overlaps the plugin's own bubble; nothing here needs one;
1757
1976
  * - **colours from `--dsw-alias-*` tokens only** (skin contract).
1758
1977
  */
1759
- const SECTION_STYLE$3 = {
1978
+ const SECTION_STYLE$4 = {
1760
1979
  flexShrink: 0,
1761
1980
  borderBottom: "1px solid var(--dsw-alias-border-secondary)"
1762
1981
  };
1763
- const HEADER_STYLE$4 = {
1982
+ const HEADER_STYLE$5 = {
1764
1983
  padding: "10px 12px 0",
1765
1984
  color: "var(--dsw-alias-label-secondary)",
1766
1985
  fontSize: 12,
1767
1986
  fontWeight: 600
1768
1987
  };
1769
- const LIST_STYLE$2 = {
1988
+ const LIST_STYLE$3 = {
1770
1989
  listStyle: "none",
1771
1990
  margin: 0,
1772
1991
  padding: "6px 0",
1773
1992
  display: "flex",
1774
1993
  flexDirection: "column"
1775
1994
  };
1776
- const ROW_STYLE$3 = {
1995
+ const ROW_STYLE$4 = {
1777
1996
  display: "flex",
1778
1997
  alignItems: "baseline",
1779
1998
  gap: 8,
@@ -1792,7 +2011,7 @@ window.__ModuleLoader__.load({
1792
2011
  fontSize: 11
1793
2012
  };
1794
2013
  /** The "本轮" pill: same form as the deliverables codeplan tag. */
1795
- const TAG_STYLE = {
2014
+ const TAG_STYLE$1 = {
1796
2015
  flexShrink: 0,
1797
2016
  color: "var(--dsw-alias-label-tertiary)",
1798
2017
  border: "1px solid var(--dsw-alias-label-tertiary)",
@@ -1802,12 +2021,12 @@ window.__ModuleLoader__.load({
1802
2021
  lineHeight: "16px",
1803
2022
  whiteSpace: "nowrap"
1804
2023
  };
1805
- const FOOTER_STYLE$2 = {
2024
+ const FOOTER_STYLE$3 = {
1806
2025
  padding: "0 12px 10px",
1807
2026
  color: "var(--dsw-alias-label-tertiary)",
1808
2027
  fontSize: 11
1809
2028
  };
1810
- const EMPTY_STYLE$2 = {
2029
+ const EMPTY_STYLE$3 = {
1811
2030
  margin: "0 auto",
1812
2031
  padding: 24,
1813
2032
  maxWidth: 460,
@@ -1831,19 +2050,19 @@ window.__ModuleLoader__.load({
1831
2050
  const rest = availableExcept(view.available, referenced);
1832
2051
  const latestNames = view.latest?.names ?? [];
1833
2052
  return (0, react.createElement)("div", {
1834
- style: SECTION_STYLE$3,
2053
+ style: SECTION_STYLE$4,
1835
2054
  "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: {
2055
+ }, !hideHeader && (0, react.createElement)("div", { style: HEADER_STYLE$5 }, t("section.skillUsage")), view.session.length === 0 && (0, react.createElement)("div", { style: EMPTY_STYLE$3 }, (0, react.createElement)("div", { style: {
1837
2056
  marginBottom: 6,
1838
2057
  color: "var(--dsw-alias-label-secondary)"
1839
2058
  } }, t("skillUsage.empty")), (0, react.createElement)("div", null, t("skillUsage.emptyHint"))), view.session.length > 0 && (0, react.createElement)(GroupHeader, {
1840
2059
  text: expandCount(t("skillUsage.invokedGroup"), view.session.length),
1841
2060
  marker: "skill-usage-invoked-group"
1842
- }), (0, react.createElement)("ul", { style: LIST_STYLE$2 }, view.session.map((entry) => (0, react.createElement)("li", {
2061
+ }), (0, react.createElement)("ul", { style: LIST_STYLE$3 }, view.session.map((entry) => (0, react.createElement)("li", {
1843
2062
  key: entry.name,
1844
- style: ROW_STYLE$3,
2063
+ style: ROW_STYLE$4,
1845
2064
  "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, {
2065
+ }, (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$1 }, t("skillUsage.latestTag"))))), rest.length > 0 && (0, react.createElement)(FoldToggle, {
1847
2066
  label: expandCount(t("skillUsage.availableGroup"), rest.length),
1848
2067
  open: availableOpen,
1849
2068
  marker: "skill-usage-available-toggle",
@@ -1853,11 +2072,193 @@ window.__ModuleLoader__.load({
1853
2072
  }), rest.length > 0 && view.availableCount > view.available.length && (0, react.createElement)(GroupCaption, {
1854
2073
  text: expandCount(t("skillUsage.availableCapped"), view.availableCount),
1855
2074
  marker: "skill-usage-available-capped"
1856
- }), availableOpen && (0, react.createElement)("ul", { style: LIST_STYLE$2 }, rest.map((name) => (0, react.createElement)("li", {
2075
+ }), availableOpen && (0, react.createElement)("ul", { style: LIST_STYLE$3 }, rest.map((name) => (0, react.createElement)("li", {
1857
2076
  key: name,
1858
- style: ROW_STYLE$3,
2077
+ style: ROW_STYLE$4,
1859
2078
  "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)));
2079
+ }, (0, react.createElement)("span", { style: NAME_STYLE$1 }, name)))), view.session.length > 0 && (0, react.createElement)("div", { style: FOOTER_STYLE$3 }, expandPair(t("skillUsage.count"), view.callTotal, view.sessionTotal)));
2080
+ }
2081
+ //#endregion
2082
+ //#region src/client/summary/SearchRefSection.tsx
2083
+ /**
2084
+ * The search-reference section: the `dshSummarySearchRefs` projection drawn as
2085
+ * one tab of the references section.
2086
+ *
2087
+ * It answers "what did this session pull in from outside": the queries it ran
2088
+ * through `advanced_search`, and the sources those searches returned. Two
2089
+ * groups, because they are two different facts — a query says what was asked,
2090
+ * a source says what came back, and an older `dsh-free-search` without
2091
+ * `presentationMeta` produces queries with no sources at all. That degraded
2092
+ * case is stated rather than hidden, because an empty source list would
2093
+ * otherwise read as "the search found nothing".
2094
+ *
2095
+ * Optional on both sides: no registry, or a host half that never registered the
2096
+ * unit, means the client reads capability absence and the section hides.
2097
+ *
2098
+ * Two contracts inherited from the recall section:
2099
+ * - **no native `title` attribute** — the whole text rides the row or is left
2100
+ * out; an unskinable tooltip would overlap the plugin's own surfaces;
2101
+ * - **colours from `--dsw-alias-*` tokens only** (skin contract).
2102
+ *
2103
+ * Source links are untrusted external data, so only `http`/`https` become
2104
+ * anchors (see `safeHref`) and they open with `rel="noreferrer noopener"`.
2105
+ */
2106
+ const SECTION_STYLE$3 = {
2107
+ flexShrink: 0,
2108
+ borderBottom: "1px solid var(--dsw-alias-border-secondary)"
2109
+ };
2110
+ const HEADER_STYLE$4 = {
2111
+ padding: "10px 12px 0",
2112
+ color: "var(--dsw-alias-label-secondary)",
2113
+ fontSize: 12,
2114
+ fontWeight: 600
2115
+ };
2116
+ const LIST_STYLE$2 = {
2117
+ listStyle: "none",
2118
+ margin: 0,
2119
+ padding: "6px 0",
2120
+ display: "flex",
2121
+ flexDirection: "column"
2122
+ };
2123
+ const ROW_STYLE$3 = {
2124
+ display: "flex",
2125
+ alignItems: "flex-start",
2126
+ gap: 8,
2127
+ padding: "6px 12px"
2128
+ };
2129
+ const QUERY_STYLE = {
2130
+ minWidth: 0,
2131
+ flex: "1 1 auto",
2132
+ overflowWrap: "anywhere",
2133
+ color: "var(--dsw-alias-label-secondary)"
2134
+ };
2135
+ /** The source title, one line: the host and URL carry the identification. */
2136
+ const TITLE_STYLE$2 = {
2137
+ minWidth: 0,
2138
+ flex: "1 1 auto",
2139
+ display: "-webkit-box",
2140
+ WebkitLineClamp: 2,
2141
+ WebkitBoxOrient: "vertical",
2142
+ overflow: "hidden",
2143
+ color: "var(--dsw-alias-label-secondary)"
2144
+ };
2145
+ const SNIPPET_STYLE = {
2146
+ margin: 0,
2147
+ minWidth: 0,
2148
+ overflowWrap: "anywhere",
2149
+ color: "var(--dsw-alias-label-tertiary)",
2150
+ fontSize: 11,
2151
+ lineHeight: "16px"
2152
+ };
2153
+ const META_STYLE$2 = {
2154
+ flexShrink: 0,
2155
+ color: "var(--dsw-alias-label-tertiary)",
2156
+ fontSize: 11,
2157
+ lineHeight: "18px",
2158
+ whiteSpace: "nowrap"
2159
+ };
2160
+ /** The "本轮" pill: same form as the skill tag, tertiary emphasis. */
2161
+ const TAG_STYLE = {
2162
+ flexShrink: 0,
2163
+ color: "var(--dsw-alias-label-tertiary)",
2164
+ border: "1px solid var(--dsw-alias-label-tertiary)",
2165
+ borderRadius: 999,
2166
+ padding: "0 6px",
2167
+ fontSize: 11,
2168
+ lineHeight: "16px",
2169
+ whiteSpace: "nowrap"
2170
+ };
2171
+ const FOOTER_STYLE$2 = {
2172
+ padding: "0 12px 10px",
2173
+ color: "var(--dsw-alias-label-tertiary)",
2174
+ fontSize: 11
2175
+ };
2176
+ const EMPTY_STYLE$2 = {
2177
+ margin: "0 auto",
2178
+ padding: 24,
2179
+ maxWidth: 460,
2180
+ textAlign: "center",
2181
+ color: "var(--dsw-alias-label-tertiary)",
2182
+ lineHeight: 1.7,
2183
+ fontSize: 12
2184
+ };
2185
+ /**
2186
+ * The search-reference section. The subscription is unconditional (a plain
2187
+ * listener, not IO); the view decides whether anything is drawn.
2188
+ *
2189
+ * @param props - translation, client context, session scope, header switch.
2190
+ */
2191
+ function SearchRefSection(props) {
2192
+ const { t, ctx, scope, hideHeader = false } = props;
2193
+ const view = readSearchRefs(useProjectionValue(ctx, scope?.sessionId, SEARCH_REFS_KEY));
2194
+ if (view === void 0) return null;
2195
+ if (view.queries.length === 0 && view.sources.length === 0) return (0, react.createElement)("div", {
2196
+ style: SECTION_STYLE$3,
2197
+ "data-dsh-brief-sidebar": "search-refs-section"
2198
+ }, !hideHeader && (0, react.createElement)("div", { style: HEADER_STYLE$4 }, t("section.searchRefs")), (0, react.createElement)("div", { style: EMPTY_STYLE$2 }, (0, react.createElement)("div", { style: {
2199
+ marginBottom: 6,
2200
+ color: "var(--dsw-alias-label-secondary)"
2201
+ } }, t("searchRefs.empty")), (0, react.createElement)("div", null, t("searchRefs.emptyHint"))));
2202
+ const body = [];
2203
+ if (view.queries.length > 0) {
2204
+ body.push((0, react.createElement)(GroupHeader, {
2205
+ key: "qh",
2206
+ text: expandCount(t("searchRefs.queryGroup"), view.queries.length),
2207
+ marker: "search-refs-query-group"
2208
+ }));
2209
+ body.push((0, react.createElement)("ul", {
2210
+ key: "ql",
2211
+ style: LIST_STYLE$2
2212
+ }, view.queries.map((row) => (0, react.createElement)("li", {
2213
+ key: `${row.turn}:${row.query}`,
2214
+ style: ROW_STYLE$3,
2215
+ "data-dsh-brief-sidebar": "search-refs-query"
2216
+ }, (0, react.createElement)("span", { style: QUERY_STYLE }, row.query), (0, react.createElement)("span", { style: META_STYLE$2 }, expandCount(t("searchRefs.hits"), row.hits))))));
2217
+ }
2218
+ if (view.sources.length > 0) {
2219
+ body.push((0, react.createElement)(GroupHeader, {
2220
+ key: "sh",
2221
+ text: expandCount(t("searchRefs.sourceGroup"), view.sources.length),
2222
+ marker: "search-refs-source-group"
2223
+ }));
2224
+ body.push((0, react.createElement)("ul", {
2225
+ key: "sl",
2226
+ style: LIST_STYLE$2
2227
+ }, view.sources.map((item) => {
2228
+ const href = safeHref(item.url);
2229
+ const label = item.title ?? item.url;
2230
+ const link = href === void 0 ? (0, react.createElement)("span", { style: TITLE_STYLE$2 }, label) : (0, react.createElement)("a", {
2231
+ style: TITLE_STYLE$2,
2232
+ href,
2233
+ target: "_blank",
2234
+ rel: "noreferrer noopener"
2235
+ }, label);
2236
+ return (0, react.createElement)("li", {
2237
+ key: item.url,
2238
+ style: ROW_STYLE$3,
2239
+ "data-dsh-brief-sidebar": "search-refs-source"
2240
+ }, (0, react.createElement)("div", { style: {
2241
+ minWidth: 0,
2242
+ flex: "1 1 auto",
2243
+ display: "flex",
2244
+ flexDirection: "column",
2245
+ gap: 2
2246
+ } }, link, (0, react.createElement)("span", { style: META_STYLE$2 }, item.host), item.snippet !== void 0 && (0, react.createElement)("p", { style: SNIPPET_STYLE }, item.snippet)), isLatestSource(view, item.url) && (0, react.createElement)("span", { style: TAG_STYLE }, t("searchRefs.latestTag")));
2247
+ })));
2248
+ } else body.push((0, react.createElement)("div", {
2249
+ key: "ns",
2250
+ style: FOOTER_STYLE$2,
2251
+ "data-dsh-brief-sidebar": "search-refs-no-sources"
2252
+ }, t("searchRefs.noSourcesHint")));
2253
+ if (view.queryTotal > view.queries.length || view.sourceTotal > view.sources.length) body.push((0, react.createElement)("div", {
2254
+ key: "cap",
2255
+ style: FOOTER_STYLE$2,
2256
+ "data-dsh-brief-sidebar": "search-refs-capped"
2257
+ }, expandTotal(t("searchRefs.capped"), Math.max(view.queryTotal, view.sourceTotal))));
2258
+ return (0, react.createElement)("div", {
2259
+ style: SECTION_STYLE$3,
2260
+ "data-dsh-brief-sidebar": "search-refs-section"
2261
+ }, !hideHeader && (0, react.createElement)("div", { style: HEADER_STYLE$4 }, t("section.searchRefs")), ...body);
1861
2262
  }
1862
2263
  //#endregion
1863
2264
  //#region src/client/summary/recall-feedback.ts
@@ -2849,7 +3250,11 @@ window.__ModuleLoader__.load({
2849
3250
  color: "var(--dsw-alias-label-primary)",
2850
3251
  fontWeight: 600
2851
3252
  };
2852
- const ORDER = ["skills", "memory"];
3253
+ const ORDER = [
3254
+ "skills",
3255
+ "memory",
3256
+ "search"
3257
+ ];
2853
3258
  /**
2854
3259
  * The references section. All hooks run before any early return, so the tab
2855
3260
  * strip can disappear (no capability at all) without ever changing hook order.
@@ -2860,19 +3265,28 @@ window.__ModuleLoader__.load({
2860
3265
  const { t, ctx, scope, fetchImpl } = props;
2861
3266
  const [tab, setTab] = (0, react.useState)("skills");
2862
3267
  const skillsLive = readSkillUsage(useProjectionValue(ctx, scope?.sessionId, SKILL_USAGE_KEY)) !== void 0;
3268
+ const searchLive = readSearchRefs(useProjectionValue(ctx, scope?.sessionId, SEARCH_REFS_KEY)) !== void 0;
2863
3269
  const recallFrame = readRecall(useProjectionValue(ctx, scope?.sessionId, MEMORY_RECALL_KEY));
2864
3270
  const slotsFrame = readSlots(useProjectionValue(ctx, scope?.sessionId, MEMORY_SLOTS_KEY));
2865
3271
  const recallOn = useConfigFlag(ctx, resolveShowMemoryRecall) !== false;
2866
3272
  const slotsOn = useConfigFlag(ctx, resolveShowMemorySlots) === true;
2867
3273
  const memoryLive = recallFrame !== void 0 && recallOn || slotsFrame !== void 0 && slotsOn;
3274
+ const live = {
3275
+ skills: skillsLive,
3276
+ memory: memoryLive,
3277
+ search: searchLive
3278
+ };
3279
+ const active = live[tab] ? tab : ORDER.find((id) => live[id]) ?? "skills";
2868
3280
  const onTabsKeyDown = (0, react.useCallback)((event) => {
2869
3281
  if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
2870
3282
  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;
3283
+ const shown = ORDER.filter((id) => live[id]);
3284
+ if (shown.length < 2) return;
3285
+ const at = shown.indexOf(active);
3286
+ const step = event.key === "ArrowRight" ? 1 : shown.length - 1;
3287
+ setTab(shown[(at + step) % shown.length]);
3288
+ }, [live, active]);
3289
+ if (!skillsLive && !memoryLive && !searchLive) return null;
2876
3290
  const tabButton = (id, label) => (0, react.createElement)("button", {
2877
3291
  key: id,
2878
3292
  type: "button",
@@ -2886,7 +3300,6 @@ window.__ModuleLoader__.load({
2886
3300
  setTab(id);
2887
3301
  }
2888
3302
  }, label);
2889
- const active = tab === "skills" && !skillsLive ? "memory" : tab;
2890
3303
  return (0, react.createElement)("div", {
2891
3304
  style: SECTION_STYLE,
2892
3305
  "data-dsh-brief-sidebar": "references-section"
@@ -2896,7 +3309,7 @@ window.__ModuleLoader__.load({
2896
3309
  style: TABS_STYLE,
2897
3310
  onKeyDown: onTabsKeyDown,
2898
3311
  "data-dsh-brief-sidebar": "reference-tabs"
2899
- }, skillsLive && tabButton("skills", t("references.tabSkills")), memoryLive && tabButton("memory", t("references.tabMemory"))), skillsLive && (0, react.createElement)("div", {
3312
+ }, skillsLive && tabButton("skills", t("references.tabSkills")), memoryLive && tabButton("memory", t("references.tabMemory")), searchLive && tabButton("search", t("references.tabSearch"))), skillsLive && (0, react.createElement)("div", {
2900
3313
  role: "tabpanel",
2901
3314
  id: "dsh-brief-reference-pane-skills",
2902
3315
  "aria-labelledby": "dsh-brief-reference-tab-skills",
@@ -2924,6 +3337,18 @@ window.__ModuleLoader__.load({
2924
3337
  t,
2925
3338
  ctx,
2926
3339
  scope
3340
+ })), searchLive && (0, react.createElement)("div", {
3341
+ role: "tabpanel",
3342
+ id: "dsh-brief-reference-pane-search",
3343
+ "aria-labelledby": "dsh-brief-reference-tab-search",
3344
+ hidden: active !== "search",
3345
+ style: active === "search" ? void 0 : { display: "none" },
3346
+ "data-dsh-brief-sidebar": "reference-pane-search"
3347
+ }, (0, react.createElement)(SearchRefSection, {
3348
+ t,
3349
+ ctx,
3350
+ scope,
3351
+ hideHeader: true
2927
3352
  })));
2928
3353
  }
2929
3354
  //#endregion