dsh-brief-sidebar 0.9.2 → 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/dsh.plugin.json +1 -1
- package/lib/client.js +1025 -441
- package/lib/client.js.map +1 -1
- package/lib/index.mjs +303 -11
- package/lib/index.mjs.map +1 -1
- package/lib/types/client/locales.d.ts +13 -0
- package/lib/types/client/summary/ReferenceSection.d.ts +24 -0
- package/lib/types/client/summary/SearchRefSection.d.ts +21 -0
- package/lib/types/client/summary/SkillUsageSection.d.ts +6 -0
- package/lib/types/client/summary/search-refs.d.ts +38 -0
- package/lib/types/projection/keys.d.ts +42 -0
- package/lib/types/projection/register.d.ts +6 -0
- package/lib/types/projection/search-ref-fold.d.ts +116 -0
- package/package.json +1 -1
- package/src/client/SummaryTab.tsx +12 -18
- package/src/client/locales.ts +117 -0
- package/src/client/summary/ReferenceSection.tsx +211 -0
- package/src/client/summary/SearchRefSection.tsx +241 -0
- package/src/client/summary/SkillUsageSection.tsx +8 -2
- package/src/client/summary/search-refs.ts +116 -0
- package/src/index.ts +4 -1
- package/src/projection/keys.ts +43 -0
- package/src/projection/register.ts +82 -2
- package/src/projection/search-ref-fold.ts +258 -0
package/lib/client.js
CHANGED
|
@@ -231,11 +231,11 @@ window.__ModuleLoader__.load({
|
|
|
231
231
|
in_progress: "var(--dsw-alias-state-business-primary)",
|
|
232
232
|
pending: "var(--dsw-alias-label-tertiary)"
|
|
233
233
|
};
|
|
234
|
-
const SECTION_STYLE$
|
|
234
|
+
const SECTION_STYLE$6 = {
|
|
235
235
|
flexShrink: 0,
|
|
236
236
|
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
237
237
|
};
|
|
238
|
-
const HEADER_STYLE$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
319
|
+
style: SECTION_STYLE$6,
|
|
320
320
|
"data-dsh-brief-sidebar": "progress-section"
|
|
321
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
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$
|
|
326
|
+
style: SECTION_STYLE$6,
|
|
327
327
|
"data-dsh-brief-sidebar": "progress-section"
|
|
328
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
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$
|
|
334
|
+
style: SECTION_STYLE$6,
|
|
335
335
|
"data-dsh-brief-sidebar": "progress-section"
|
|
336
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
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. */
|
|
@@ -688,6 +694,9 @@ window.__ModuleLoader__.load({
|
|
|
688
694
|
"recall.supersededTag": "已被摘要取代",
|
|
689
695
|
"recall.noBody": "(本帧没有这条记忆的全文)",
|
|
690
696
|
"section.skillUsage": "技能引用",
|
|
697
|
+
"section.references": "引用",
|
|
698
|
+
"references.tabSkills": "技能",
|
|
699
|
+
"references.tabMemory": "记忆",
|
|
691
700
|
"skillUsage.empty": "本会话还没载入过技能",
|
|
692
701
|
"skillUsage.emptyHint": "模型用 skill 工具载入、或你直接点用某个技能后,技能名会实时出现在这里。",
|
|
693
702
|
"skillUsage.uses": "{count} 次",
|
|
@@ -696,6 +705,16 @@ window.__ModuleLoader__.load({
|
|
|
696
705
|
"skillUsage.invokedGroup": "已引用 · {count}",
|
|
697
706
|
"skillUsage.availableGroup": "本会话可用 · {count}",
|
|
698
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} 条,列表已截断)",
|
|
699
718
|
"settings.title": "概要侧栏",
|
|
700
719
|
"settings.desc": "右侧栏概要 tab 的分区开关(只读展示)。",
|
|
701
720
|
"settings.memorySlots": "记忆槽位分区",
|
|
@@ -748,6 +767,9 @@ window.__ModuleLoader__.load({
|
|
|
748
767
|
"recall.supersededTag": "replaced by summary",
|
|
749
768
|
"recall.noBody": "(this frame carries no full body for this record)",
|
|
750
769
|
"section.skillUsage": "Skill references",
|
|
770
|
+
"section.references": "References",
|
|
771
|
+
"references.tabSkills": "Skills",
|
|
772
|
+
"references.tabMemory": "Memory",
|
|
751
773
|
"skillUsage.empty": "No skills loaded in this session",
|
|
752
774
|
"skillUsage.emptyHint": "Skills show up here once the model loads one with the `skill` tool, or you invoke one directly.",
|
|
753
775
|
"skillUsage.uses": "{count}x",
|
|
@@ -756,6 +778,16 @@ window.__ModuleLoader__.load({
|
|
|
756
778
|
"skillUsage.invokedGroup": "Referenced · {count}",
|
|
757
779
|
"skillUsage.availableGroup": "Available this session · {count}",
|
|
758
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)",
|
|
759
791
|
"settings.title": "Brief sidebar",
|
|
760
792
|
"settings.desc": "Section switches for the summary tab in the right sidebar (read-only views).",
|
|
761
793
|
"settings.memorySlots": "Memory slots section",
|
|
@@ -808,6 +840,9 @@ window.__ModuleLoader__.load({
|
|
|
808
840
|
"recall.supersededTag": "要約に置換済み",
|
|
809
841
|
"recall.noBody": "このフレームには全文が含まれていない",
|
|
810
842
|
"section.skillUsage": "スキル参照",
|
|
843
|
+
"section.references": "参照",
|
|
844
|
+
"references.tabSkills": "スキル",
|
|
845
|
+
"references.tabMemory": "メモリ",
|
|
811
846
|
"skillUsage.empty": "このセッションではスキルを読み込んでいません",
|
|
812
847
|
"skillUsage.emptyHint": "モデルが skill ツールで読み込むか、あなたがスキルを直接起動すると、ここにリアルタイムで表示されます。",
|
|
813
848
|
"skillUsage.uses": "{count} 回",
|
|
@@ -816,6 +851,16 @@ window.__ModuleLoader__.load({
|
|
|
816
851
|
"skillUsage.invokedGroup": "参照済み · {count}",
|
|
817
852
|
"skillUsage.availableGroup": "このセッションで利用可能 · {count}",
|
|
818
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} 件、一覧は上限で省略)",
|
|
819
864
|
"settings.title": "概要サイドバー",
|
|
820
865
|
"settings.desc": "右サイドバー概要タブのセクション表示切り替え(読み取り専用)。",
|
|
821
866
|
"settings.memorySlots": "メモリスロットセクション",
|
|
@@ -868,6 +913,9 @@ window.__ModuleLoader__.load({
|
|
|
868
913
|
"recall.supersededTag": "요약에 대체됨",
|
|
869
914
|
"recall.noBody": "이 프레임에는 이 기록의 전문이 없습니다",
|
|
870
915
|
"section.skillUsage": "스킬 참조",
|
|
916
|
+
"section.references": "참조",
|
|
917
|
+
"references.tabSkills": "스킬",
|
|
918
|
+
"references.tabMemory": "메모리",
|
|
871
919
|
"skillUsage.empty": "이 세션에서 스킬을 불러오지 않았습니다",
|
|
872
920
|
"skillUsage.emptyHint": "모델이 skill 도구로 불러오거나 당신이 스킬을 직접 실행하면 여기에 실시간으로 나타납니다.",
|
|
873
921
|
"skillUsage.uses": "{count}회",
|
|
@@ -876,6 +924,16 @@ window.__ModuleLoader__.load({
|
|
|
876
924
|
"skillUsage.invokedGroup": "참조됨 · {count}",
|
|
877
925
|
"skillUsage.availableGroup": "이 세션에서 사용 가능 · {count}",
|
|
878
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}건, 목록은 잘림)",
|
|
879
937
|
"settings.title": "요약 사이드바",
|
|
880
938
|
"settings.desc": "오른쪽 사이드바 요약 탭의 섹션 표시 전환(읽기 전용).",
|
|
881
939
|
"settings.memorySlots": "메모리 슬롯 섹션",
|
|
@@ -928,6 +986,9 @@ window.__ModuleLoader__.load({
|
|
|
928
986
|
"recall.supersededTag": "remplacé par le résumé",
|
|
929
987
|
"recall.noBody": "(aucun texte complet pour cette entrée dans cette trame)",
|
|
930
988
|
"section.skillUsage": "Références de compétences",
|
|
989
|
+
"section.references": "Références",
|
|
990
|
+
"references.tabSkills": "Compétences",
|
|
991
|
+
"references.tabMemory": "Mémoire",
|
|
931
992
|
"skillUsage.empty": "Aucune compétence chargée dans cette session",
|
|
932
993
|
"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
994
|
"skillUsage.uses": "{count}×",
|
|
@@ -936,6 +997,16 @@ window.__ModuleLoader__.load({
|
|
|
936
997
|
"skillUsage.invokedGroup": "Référencées · {count}",
|
|
937
998
|
"skillUsage.availableGroup": "Disponibles dans cette session · {count}",
|
|
938
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)",
|
|
939
1010
|
"settings.title": "Barre latérale Résumé",
|
|
940
1011
|
"settings.desc": "Interrupteurs des sections de l’onglet Résumé de la barre latérale (lecture seule).",
|
|
941
1012
|
"settings.memorySlots": "Section des emplacements mémoire",
|
|
@@ -988,6 +1059,9 @@ window.__ModuleLoader__.load({
|
|
|
988
1059
|
"recall.supersededTag": "durch Zusammenfassung ersetzt",
|
|
989
1060
|
"recall.noBody": "(dieser Rahmen enthält keinen vollständigen Text für diesen Eintrag)",
|
|
990
1061
|
"section.skillUsage": "Skill-Referenzen",
|
|
1062
|
+
"section.references": "Referenzen",
|
|
1063
|
+
"references.tabSkills": "Skills",
|
|
1064
|
+
"references.tabMemory": "Speicher",
|
|
991
1065
|
"skillUsage.empty": "In dieser Sitzung wurden keine Skills geladen",
|
|
992
1066
|
"skillUsage.emptyHint": "Skills erscheinen hier, sobald das Modell einen über das `skill`-Tool lädt oder du einen direkt aufrufst.",
|
|
993
1067
|
"skillUsage.uses": "{count}x",
|
|
@@ -996,6 +1070,16 @@ window.__ModuleLoader__.load({
|
|
|
996
1070
|
"skillUsage.invokedGroup": "Referenziert · {count}",
|
|
997
1071
|
"skillUsage.availableGroup": "In dieser Sitzung verfügbar · {count}",
|
|
998
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)",
|
|
999
1083
|
"settings.title": "Übersicht-Seitenleiste",
|
|
1000
1084
|
"settings.desc": "Schalter für die Abschnitte des Übersicht-Tabs in der rechten Seitenleiste (schreibgeschützt).",
|
|
1001
1085
|
"settings.memorySlots": "Speicherplätze-Abschnitt",
|
|
@@ -1048,6 +1132,9 @@ window.__ModuleLoader__.load({
|
|
|
1048
1132
|
"recall.supersededTag": "sostituito dal riassunto",
|
|
1049
1133
|
"recall.noBody": "(questo frame non contiene il testo completo di questa voce)",
|
|
1050
1134
|
"section.skillUsage": "Riferimenti alle skill",
|
|
1135
|
+
"section.references": "Riferimenti",
|
|
1136
|
+
"references.tabSkills": "Skill",
|
|
1137
|
+
"references.tabMemory": "Memoria",
|
|
1051
1138
|
"skillUsage.empty": "Nessuna skill caricata in questa sessione",
|
|
1052
1139
|
"skillUsage.emptyHint": "Le skill compaiono qui quando il modello ne carica una con il tool `skill` o quando ne avvii una direttamente.",
|
|
1053
1140
|
"skillUsage.uses": "{count}x",
|
|
@@ -1056,6 +1143,16 @@ window.__ModuleLoader__.load({
|
|
|
1056
1143
|
"skillUsage.invokedGroup": "Referenziate · {count}",
|
|
1057
1144
|
"skillUsage.availableGroup": "Disponibili in questa sessione · {count}",
|
|
1058
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)",
|
|
1059
1156
|
"settings.title": "Barra laterale Riepilogo",
|
|
1060
1157
|
"settings.desc": "Interruttori delle sezioni della scheda Riepilogo nella barra laterale destra (sola lettura).",
|
|
1061
1158
|
"settings.memorySlots": "Sezione slot di memoria",
|
|
@@ -1108,6 +1205,9 @@ window.__ModuleLoader__.load({
|
|
|
1108
1205
|
"recall.supersededTag": "заменено сводкой",
|
|
1109
1206
|
"recall.noBody": "В этом кадре нет полного текста этой записи",
|
|
1110
1207
|
"section.skillUsage": "Отсылки к скиллам",
|
|
1208
|
+
"section.references": "Отсылки",
|
|
1209
|
+
"references.tabSkills": "Навыки",
|
|
1210
|
+
"references.tabMemory": "Память",
|
|
1111
1211
|
"skillUsage.empty": "В этой сессии скиллы не загружались",
|
|
1112
1212
|
"skillUsage.emptyHint": "Навыки появятся здесь, когда модель загрузит их через инструмент `skill` или вы вызовете навык напрямую.",
|
|
1113
1213
|
"skillUsage.uses": "{count} раз",
|
|
@@ -1116,6 +1216,16 @@ window.__ModuleLoader__.load({
|
|
|
1116
1216
|
"skillUsage.invokedGroup": "Использованы · {count}",
|
|
1117
1217
|
"skillUsage.availableGroup": "Доступны в этой сессии · {count}",
|
|
1118
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}; список усечён)",
|
|
1119
1229
|
"settings.title": "Боковая панель «Сводка»",
|
|
1120
1230
|
"settings.desc": "Переключатели разделов вкладки «Сводка» на правой панели (только чтение).",
|
|
1121
1231
|
"settings.memorySlots": "Раздел слотов памяти",
|
|
@@ -1168,6 +1278,9 @@ window.__ModuleLoader__.load({
|
|
|
1168
1278
|
"recall.supersededTag": "reemplazado por el resumen",
|
|
1169
1279
|
"recall.noBody": "(este fotograma no trae el texto completo de este registro)",
|
|
1170
1280
|
"section.skillUsage": "Referencias de skills",
|
|
1281
|
+
"section.references": "Referencias",
|
|
1282
|
+
"references.tabSkills": "Skills",
|
|
1283
|
+
"references.tabMemory": "Memoria",
|
|
1171
1284
|
"skillUsage.empty": "Esta sesión no ha cargado ningún skill",
|
|
1172
1285
|
"skillUsage.emptyHint": "Las skills aparecen aquí cuando el modelo carga una con la herramienta `skill` o cuando invocas una directamente.",
|
|
1173
1286
|
"skillUsage.uses": "{count}x",
|
|
@@ -1176,6 +1289,16 @@ window.__ModuleLoader__.load({
|
|
|
1176
1289
|
"skillUsage.invokedGroup": "Referenciadas · {count}",
|
|
1177
1290
|
"skillUsage.availableGroup": "Disponibles en esta sesión · {count}",
|
|
1178
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)",
|
|
1179
1302
|
"settings.title": "Barra lateral Resumen",
|
|
1180
1303
|
"settings.desc": "Interruptores de las secciones de la pestaña Resumen en la barra lateral derecha (solo lectura).",
|
|
1181
1304
|
"settings.memorySlots": "Sección de ranuras de memoria",
|
|
@@ -1222,24 +1345,24 @@ window.__ModuleLoader__.load({
|
|
|
1222
1345
|
*
|
|
1223
1346
|
* Colours come from `--dsw-alias-*` tokens only (skin contract).
|
|
1224
1347
|
*/
|
|
1225
|
-
const SECTION_STYLE$
|
|
1348
|
+
const SECTION_STYLE$5 = {
|
|
1226
1349
|
flexShrink: 0,
|
|
1227
1350
|
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
1228
1351
|
};
|
|
1229
|
-
const HEADER_STYLE$
|
|
1352
|
+
const HEADER_STYLE$6 = {
|
|
1230
1353
|
padding: "10px 12px 0",
|
|
1231
1354
|
color: "var(--dsw-alias-label-secondary)",
|
|
1232
1355
|
fontSize: 12,
|
|
1233
1356
|
fontWeight: 600
|
|
1234
1357
|
};
|
|
1235
|
-
const LIST_STYLE$
|
|
1358
|
+
const LIST_STYLE$4 = {
|
|
1236
1359
|
listStyle: "none",
|
|
1237
1360
|
margin: 0,
|
|
1238
1361
|
padding: "6px 0",
|
|
1239
1362
|
display: "flex",
|
|
1240
1363
|
flexDirection: "column"
|
|
1241
1364
|
};
|
|
1242
|
-
const ROW_STYLE$
|
|
1365
|
+
const ROW_STYLE$5 = {
|
|
1243
1366
|
display: "flex",
|
|
1244
1367
|
alignItems: "flex-start",
|
|
1245
1368
|
gap: 8,
|
|
@@ -1263,7 +1386,7 @@ window.__ModuleLoader__.load({
|
|
|
1263
1386
|
cursor: "pointer"
|
|
1264
1387
|
};
|
|
1265
1388
|
/** The codeplan pill: same form as the status pills, tertiary emphasis. */
|
|
1266
|
-
const TAG_STYLE$
|
|
1389
|
+
const TAG_STYLE$2 = {
|
|
1267
1390
|
flexShrink: 0,
|
|
1268
1391
|
color: "var(--dsw-alias-label-tertiary)",
|
|
1269
1392
|
border: "1px solid var(--dsw-alias-label-tertiary)",
|
|
@@ -1284,12 +1407,12 @@ window.__ModuleLoader__.load({
|
|
|
1284
1407
|
const plan = splitCodeplanPath(path);
|
|
1285
1408
|
const title = plan === null ? path : `${t("deliverable.codeplanTag")}: ${plan.task} · ${path}`;
|
|
1286
1409
|
const label = basename(path);
|
|
1287
|
-
const content = plan === null ? null : (0, react.createElement)("span", { style: TAG_STYLE$
|
|
1288
|
-
if (open === void 0) return (0, react.createElement)("li", { style: ROW_STYLE$
|
|
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", {
|
|
1289
1412
|
style: PATH_STYLE,
|
|
1290
1413
|
title
|
|
1291
1414
|
}, label));
|
|
1292
|
-
return (0, react.createElement)("li", { style: ROW_STYLE$
|
|
1415
|
+
return (0, react.createElement)("li", { style: ROW_STYLE$5 }, content, (0, react.createElement)("button", {
|
|
1293
1416
|
style: BUTTON_STYLE,
|
|
1294
1417
|
title,
|
|
1295
1418
|
onClick: () => {
|
|
@@ -1315,9 +1438,9 @@ window.__ModuleLoader__.load({
|
|
|
1315
1438
|
const latestPaths = view.latest?.paths ?? [];
|
|
1316
1439
|
const history = historyPaths(view.sessionPaths, latestPaths);
|
|
1317
1440
|
if (latestPaths.length === 0 && view.sessionTotal === 0) return (0, react.createElement)("div", {
|
|
1318
|
-
style: SECTION_STYLE$
|
|
1441
|
+
style: SECTION_STYLE$5,
|
|
1319
1442
|
"data-dsh-brief-sidebar": "deliverables-section"
|
|
1320
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
1443
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$6 }, t("section.deliverables")), (0, react.createElement)("div", { style: {
|
|
1321
1444
|
margin: "0 auto",
|
|
1322
1445
|
padding: 24,
|
|
1323
1446
|
maxWidth: 460,
|
|
@@ -1330,12 +1453,12 @@ window.__ModuleLoader__.load({
|
|
|
1330
1453
|
color: "var(--dsw-alias-label-secondary)"
|
|
1331
1454
|
} }, t("deliverables.empty")), (0, react.createElement)("div", null, t("deliverables.emptyHint"))));
|
|
1332
1455
|
return (0, react.createElement)("div", {
|
|
1333
|
-
style: SECTION_STYLE$
|
|
1456
|
+
style: SECTION_STYLE$5,
|
|
1334
1457
|
"data-dsh-brief-sidebar": "deliverables-section"
|
|
1335
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
1458
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$6 }, t("section.deliverables")), latestPaths.length > 0 && (0, react.createElement)(GroupHeader, {
|
|
1336
1459
|
text: expandCount(t("deliverables.currentTurn"), latestPaths.length),
|
|
1337
1460
|
marker: "deliverables-current-group"
|
|
1338
|
-
}), (0, react.createElement)("ul", { style: LIST_STYLE$
|
|
1461
|
+
}), (0, react.createElement)("ul", { style: LIST_STYLE$4 }, latestPaths.map((path) => (0, react.createElement)(PathRow, {
|
|
1339
1462
|
key: path,
|
|
1340
1463
|
path,
|
|
1341
1464
|
t,
|
|
@@ -1350,7 +1473,7 @@ window.__ModuleLoader__.load({
|
|
|
1350
1473
|
}), history.length > 0 && view.sessionTotal > view.sessionPaths.length && (0, react.createElement)(GroupCaption, {
|
|
1351
1474
|
text: expandTotal(t("deliverables.historyCapped"), view.sessionTotal),
|
|
1352
1475
|
marker: "deliverables-history-capped"
|
|
1353
|
-
}), historyOpen && (0, react.createElement)("ul", { style: LIST_STYLE$
|
|
1476
|
+
}), historyOpen && (0, react.createElement)("ul", { style: LIST_STYLE$4 }, history.map((path) => (0, react.createElement)(PathRow, {
|
|
1354
1477
|
key: path,
|
|
1355
1478
|
path,
|
|
1356
1479
|
t,
|
|
@@ -1360,7 +1483,7 @@ window.__ModuleLoader__.load({
|
|
|
1360
1483
|
//#endregion
|
|
1361
1484
|
//#region src/client/summary/skill-usage.ts
|
|
1362
1485
|
/** Whether a value is a non-negative safe integer (the view's counter shape). */
|
|
1363
|
-
function isCounter$
|
|
1486
|
+
function isCounter$3(value) {
|
|
1364
1487
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
1365
1488
|
}
|
|
1366
1489
|
/** Narrow one skill tally; `undefined` = malformed, dropped. */
|
|
@@ -1368,7 +1491,7 @@ window.__ModuleLoader__.load({
|
|
|
1368
1491
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1369
1492
|
const raw = value;
|
|
1370
1493
|
if (typeof raw["name"] !== "string" || raw["name"].length === 0) return void 0;
|
|
1371
|
-
if (!isCounter$
|
|
1494
|
+
if (!isCounter$3(raw["count"]) || !isCounter$3(raw["lastTurn"])) return void 0;
|
|
1372
1495
|
return {
|
|
1373
1496
|
name: raw["name"],
|
|
1374
1497
|
count: raw["count"],
|
|
@@ -1379,10 +1502,10 @@ window.__ModuleLoader__.load({
|
|
|
1379
1502
|
* The latest-turn bucket, or `null` when it is absent OR drifted. A drifted
|
|
1380
1503
|
* bucket only costs the "本轮" tag, so it degrades instead of voiding the view.
|
|
1381
1504
|
*/
|
|
1382
|
-
function readLatest(value) {
|
|
1505
|
+
function readLatest$1(value) {
|
|
1383
1506
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
1384
1507
|
const raw = value;
|
|
1385
|
-
if (!isCounter$
|
|
1508
|
+
if (!isCounter$3(raw["turn"]) || !Array.isArray(raw["names"])) return null;
|
|
1386
1509
|
return {
|
|
1387
1510
|
turn: raw["turn"],
|
|
1388
1511
|
names: raw["names"].filter((n) => typeof n === "string" && n.length > 0)
|
|
@@ -1410,16 +1533,16 @@ window.__ModuleLoader__.load({
|
|
|
1410
1533
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1411
1534
|
const raw = value;
|
|
1412
1535
|
if (!Array.isArray(raw["session"])) return void 0;
|
|
1413
|
-
if (!isCounter$
|
|
1536
|
+
if (!isCounter$3(raw["sessionTotal"]) || !isCounter$3(raw["callTotal"])) return void 0;
|
|
1414
1537
|
const session = [];
|
|
1415
1538
|
for (const entry of raw["session"]) {
|
|
1416
1539
|
const narrowed = readEntry(entry);
|
|
1417
1540
|
if (narrowed !== void 0) session.push(narrowed);
|
|
1418
1541
|
}
|
|
1419
1542
|
const available = readAvailable(raw["available"]);
|
|
1420
|
-
const availableCount = available.length === 0 ? 0 : isCounter$
|
|
1543
|
+
const availableCount = available.length === 0 ? 0 : isCounter$3(raw["availableCount"]) ? raw["availableCount"] : available.length;
|
|
1421
1544
|
return {
|
|
1422
|
-
latest: readLatest(raw["latest"]),
|
|
1545
|
+
latest: readLatest$1(raw["latest"]),
|
|
1423
1546
|
session,
|
|
1424
1547
|
sessionTotal: raw["sessionTotal"],
|
|
1425
1548
|
callTotal: raw["callTotal"],
|
|
@@ -1456,216 +1579,284 @@ window.__ModuleLoader__.load({
|
|
|
1456
1579
|
return template.replace("{count}", String(count)).replace("{total}", String(total));
|
|
1457
1580
|
}
|
|
1458
1581
|
//#endregion
|
|
1459
|
-
//#region src/
|
|
1460
|
-
/**
|
|
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.
|
|
1476
|
-
*
|
|
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.
|
|
1482
|
-
*
|
|
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).
|
|
1487
|
-
*/
|
|
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
|
-
};
|
|
1582
|
+
//#region src/projection/search-ref-fold.ts
|
|
1548
1583
|
/**
|
|
1549
|
-
* The
|
|
1550
|
-
*
|
|
1551
|
-
*
|
|
1552
|
-
* @param props - translation, client context, session scope.
|
|
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.
|
|
1553
1587
|
*/
|
|
1554
|
-
function
|
|
1555
|
-
const
|
|
1556
|
-
|
|
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)));
|
|
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());
|
|
1590
1591
|
}
|
|
1591
1592
|
//#endregion
|
|
1592
|
-
//#region src/client/summary/
|
|
1593
|
+
//#region src/client/summary/search-refs.ts
|
|
1593
1594
|
/**
|
|
1594
|
-
* The `
|
|
1595
|
+
* The `dshSummarySearchRefs` projection — reading it.
|
|
1595
1596
|
*
|
|
1596
|
-
* The
|
|
1597
|
-
*
|
|
1598
|
-
*
|
|
1599
|
-
*
|
|
1600
|
-
*
|
|
1601
|
-
* narrowing can be tested directly.
|
|
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
1602
|
*
|
|
1603
|
-
*
|
|
1604
|
-
*
|
|
1605
|
-
*
|
|
1606
|
-
*
|
|
1607
|
-
*
|
|
1608
|
-
*
|
|
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.
|
|
1609
1610
|
*/
|
|
1610
|
-
/**
|
|
1611
|
-
|
|
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
|
+
}
|
|
1612
1656
|
/**
|
|
1613
|
-
* Narrow a raw projection value into a
|
|
1614
|
-
*
|
|
1615
|
-
* Two outcomes are kept apart, mirroring the deliverables read side:
|
|
1616
|
-
* - `undefined` — the capability is ABSENT (prime-memory is not in the
|
|
1617
|
-
* composition, or no frame has carried the key yet), or the frame is
|
|
1618
|
-
* malformed beyond repair (a shape drift must not blank the whole tab);
|
|
1619
|
-
* - a view — the projection is live. A view with `slots: []` IS the empty
|
|
1620
|
-
* state: prime-memory publishes as soon as the unit registers, so "present
|
|
1621
|
-
* but empty" is a real value, not a sentinel.
|
|
1622
|
-
*
|
|
1623
|
-
* Malformed slot entries are dropped rather than thrown on (trusted-Host
|
|
1624
|
-
* value, defensive display), like `readTodos`.
|
|
1657
|
+
* Narrow a raw projection value into a search-reference view.
|
|
1625
1658
|
*
|
|
1626
|
-
* @param value - the raw `unknown` snapshot of the `
|
|
1659
|
+
* @param value - the raw `unknown` snapshot of the `dshSummarySearchRefs` face.
|
|
1627
1660
|
* @returns the validated view, or `undefined` when the capability is absent.
|
|
1628
1661
|
*/
|
|
1629
|
-
function
|
|
1662
|
+
function readSearchRefs(value) {
|
|
1630
1663
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1631
1664
|
const raw = value;
|
|
1632
|
-
if (!
|
|
1633
|
-
if (!
|
|
1634
|
-
const
|
|
1635
|
-
for (const entry of raw["
|
|
1636
|
-
const
|
|
1637
|
-
if (
|
|
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);
|
|
1638
1676
|
}
|
|
1639
1677
|
return {
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1678
|
+
latest: readLatest(raw["latest"]),
|
|
1679
|
+
queries,
|
|
1680
|
+
sources,
|
|
1681
|
+
queryTotal: raw["queryTotal"],
|
|
1682
|
+
sourceTotal: raw["sourceTotal"]
|
|
1644
1683
|
};
|
|
1645
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
|
|
1705
|
+
//#region src/client/summary/memory-recall.ts
|
|
1706
|
+
/**
|
|
1707
|
+
* The `memoryRecall` projection — reading it.
|
|
1708
|
+
*
|
|
1709
|
+
* The projection unit is registered by `dsh-prime-memory` (key `memoryRecall`,
|
|
1710
|
+
* wire view `{rev, sessionId, count, items[]}`): the memories this session has
|
|
1711
|
+
* actually injected. Unlike
|
|
1712
|
+
* `memorySlots` (pinned, resident conventions written by hand), this set is
|
|
1713
|
+
* NOT resident — it is the at-a-glance answer to "what did the model see".
|
|
1714
|
+
* It survives compaction on purpose: `compact` only marks the entries whose
|
|
1715
|
+
* text left the context as `superseded` (and `/clear` empties the set), so a
|
|
1716
|
+
* retired row keeps its strike-through instead of vanishing.
|
|
1717
|
+
*
|
|
1718
|
+
* This plugin does NOT depend on prime-memory: with the unit unregistered the
|
|
1719
|
+
* client reads `undefined` from the face and the section hides. The module is
|
|
1720
|
+
* deliberately PURE — no React, no context, no IO — so the narrowing is
|
|
1721
|
+
* testable directly, exactly like `memory-slots.ts`.
|
|
1722
|
+
*/
|
|
1723
|
+
/** The projection key `dsh-prime-memory` registers (its canonical constant). */
|
|
1724
|
+
const MEMORY_RECALL_KEY = "memoryRecall";
|
|
1646
1725
|
/** Whether a value is a non-negative safe integer (the view's counter shape). */
|
|
1647
1726
|
function isCounter$1(value) {
|
|
1648
1727
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
1649
1728
|
}
|
|
1650
|
-
/** Narrow one
|
|
1651
|
-
function
|
|
1729
|
+
/** Narrow one item entry; `undefined` = malformed, dropped. */
|
|
1730
|
+
function readItem(value) {
|
|
1652
1731
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1653
1732
|
const raw = value;
|
|
1654
1733
|
if (typeof raw["id"] !== "string" || raw["id"].length === 0) return void 0;
|
|
1655
|
-
if (typeof raw["title"] !== "string") return void 0;
|
|
1656
|
-
if (typeof raw["kind"] !== "string" || typeof raw["status"] !== "string") return void 0;
|
|
1734
|
+
if (typeof raw["type"] !== "string" || typeof raw["title"] !== "string") return void 0;
|
|
1657
1735
|
if (typeof raw["priority"] !== "number") return void 0;
|
|
1658
|
-
const
|
|
1736
|
+
const item = {
|
|
1659
1737
|
id: raw["id"],
|
|
1738
|
+
type: raw["type"],
|
|
1660
1739
|
title: raw["title"],
|
|
1661
|
-
kind: raw["kind"],
|
|
1662
|
-
status: raw["status"],
|
|
1663
1740
|
priority: raw["priority"]
|
|
1664
1741
|
};
|
|
1665
|
-
if (
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1742
|
+
if (Array.isArray(raw["scenario"])) {
|
|
1743
|
+
const phrases = raw["scenario"].filter((s) => typeof s === "string" && s.length > 0);
|
|
1744
|
+
if (phrases.length > 0) item.scenario = phrases;
|
|
1745
|
+
}
|
|
1746
|
+
const rating = raw["rating"];
|
|
1747
|
+
if (typeof rating === "number" && Number.isInteger(rating) && rating >= -1 && rating <= 5) item.rating = rating;
|
|
1748
|
+
if (typeof raw["body"] === "string") item.body = raw["body"];
|
|
1749
|
+
if (raw["retired"] === true) item.retired = true;
|
|
1750
|
+
if (raw["superseded"] === true) item.superseded = true;
|
|
1751
|
+
return item;
|
|
1752
|
+
}
|
|
1753
|
+
/**
|
|
1754
|
+
* Narrow a raw projection value into a recall view.
|
|
1755
|
+
*
|
|
1756
|
+
* `undefined` means the capability is ABSENT (prime-memory not in the
|
|
1757
|
+
* composition, or no frame carried the key) or the frame drifted beyond repair;
|
|
1758
|
+
* a view with `items: []` IS the honest empty state (prime-memory publishes as
|
|
1759
|
+
* soon as the unit registers). Malformed items are dropped, never thrown on.
|
|
1760
|
+
*
|
|
1761
|
+
* @param value - the raw `unknown` snapshot of the `memoryRecall` face.
|
|
1762
|
+
* @returns the validated view, or `undefined` when the capability is absent.
|
|
1763
|
+
*/
|
|
1764
|
+
function readRecall(value) {
|
|
1765
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1766
|
+
const raw = value;
|
|
1767
|
+
if (!isCounter$1(raw["rev"]) || !isCounter$1(raw["count"])) return void 0;
|
|
1768
|
+
if (typeof raw["sessionId"] !== "string") return void 0;
|
|
1769
|
+
if (!Array.isArray(raw["items"])) return void 0;
|
|
1770
|
+
const items = [];
|
|
1771
|
+
for (const entry of raw["items"]) {
|
|
1772
|
+
const item = readItem(entry);
|
|
1773
|
+
if (item !== void 0) items.push(item);
|
|
1774
|
+
}
|
|
1775
|
+
return {
|
|
1776
|
+
rev: raw["rev"],
|
|
1777
|
+
sessionId: raw["sessionId"],
|
|
1778
|
+
count: raw["count"],
|
|
1779
|
+
items
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
//#endregion
|
|
1783
|
+
//#region src/client/summary/memory-slots.ts
|
|
1784
|
+
/**
|
|
1785
|
+
* The `memorySlots` projection — reading it.
|
|
1786
|
+
*
|
|
1787
|
+
* The projection unit is registered by `dsh-prime-memory` (key `memorySlots`,
|
|
1788
|
+
* wire view `{rev, count, openCount, slots[]}`); this plugin does NOT depend
|
|
1789
|
+
* on it — when the unit is not registered the client reads `undefined` from
|
|
1790
|
+
* `faceOf` and the section hides. This module is the read side of that
|
|
1791
|
+
* contract and is deliberately PURE — no React, no context, no IO — so the
|
|
1792
|
+
* narrowing can be tested directly.
|
|
1793
|
+
*
|
|
1794
|
+
* Forward compatibility: the shipped view carries `{id, title, kind, status,
|
|
1795
|
+
* priority}` only. A future prime-memory release may add `body`/`refs` to the
|
|
1796
|
+
* wire view (its projection plan keeps that door open); those fields are
|
|
1797
|
+
* narrowed opportunistically — present and well-formed → kept, absent or
|
|
1798
|
+
* malformed → dropped — so the section renders them without a brief-sidebar
|
|
1799
|
+
* change, and a malformed extra field can never blank the section.
|
|
1800
|
+
*/
|
|
1801
|
+
/** The projection key `dsh-prime-memory` registers (its canonical constant). */
|
|
1802
|
+
const MEMORY_SLOTS_KEY = "memorySlots";
|
|
1803
|
+
/**
|
|
1804
|
+
* Narrow a raw projection value into a memory-slots view.
|
|
1805
|
+
*
|
|
1806
|
+
* Two outcomes are kept apart, mirroring the deliverables read side:
|
|
1807
|
+
* - `undefined` — the capability is ABSENT (prime-memory is not in the
|
|
1808
|
+
* composition, or no frame has carried the key yet), or the frame is
|
|
1809
|
+
* malformed beyond repair (a shape drift must not blank the whole tab);
|
|
1810
|
+
* - a view — the projection is live. A view with `slots: []` IS the empty
|
|
1811
|
+
* state: prime-memory publishes as soon as the unit registers, so "present
|
|
1812
|
+
* but empty" is a real value, not a sentinel.
|
|
1813
|
+
*
|
|
1814
|
+
* Malformed slot entries are dropped rather than thrown on (trusted-Host
|
|
1815
|
+
* value, defensive display), like `readTodos`.
|
|
1816
|
+
*
|
|
1817
|
+
* @param value - the raw `unknown` snapshot of the `memorySlots` face.
|
|
1818
|
+
* @returns the validated view, or `undefined` when the capability is absent.
|
|
1819
|
+
*/
|
|
1820
|
+
function readSlots(value) {
|
|
1821
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1822
|
+
const raw = value;
|
|
1823
|
+
if (!isCounter(raw["rev"]) || !isCounter(raw["count"]) || !isCounter(raw["openCount"])) return void 0;
|
|
1824
|
+
if (!Array.isArray(raw["slots"])) return void 0;
|
|
1825
|
+
const slots = [];
|
|
1826
|
+
for (const entry of raw["slots"]) {
|
|
1827
|
+
const slot = readSlot(entry);
|
|
1828
|
+
if (slot !== void 0) slots.push(slot);
|
|
1829
|
+
}
|
|
1830
|
+
return {
|
|
1831
|
+
rev: raw["rev"],
|
|
1832
|
+
count: raw["count"],
|
|
1833
|
+
openCount: raw["openCount"],
|
|
1834
|
+
slots
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
/** Whether a value is a non-negative safe integer (the view's counter shape). */
|
|
1838
|
+
function isCounter(value) {
|
|
1839
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
1840
|
+
}
|
|
1841
|
+
/** Narrow one slot entry; `undefined` = malformed, dropped. */
|
|
1842
|
+
function readSlot(value) {
|
|
1843
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1844
|
+
const raw = value;
|
|
1845
|
+
if (typeof raw["id"] !== "string" || raw["id"].length === 0) return void 0;
|
|
1846
|
+
if (typeof raw["title"] !== "string") return void 0;
|
|
1847
|
+
if (typeof raw["kind"] !== "string" || typeof raw["status"] !== "string") return void 0;
|
|
1848
|
+
if (typeof raw["priority"] !== "number") return void 0;
|
|
1849
|
+
const slot = {
|
|
1850
|
+
id: raw["id"],
|
|
1851
|
+
title: raw["title"],
|
|
1852
|
+
kind: raw["kind"],
|
|
1853
|
+
status: raw["status"],
|
|
1854
|
+
priority: raw["priority"]
|
|
1855
|
+
};
|
|
1856
|
+
if (typeof raw["body"] === "string" && raw["body"].length > 0) slot.body = raw["body"];
|
|
1857
|
+
if (Array.isArray(raw["refs"])) {
|
|
1858
|
+
const refs = raw["refs"].filter((ref) => typeof ref === "string" && ref.length > 0);
|
|
1859
|
+
if (refs.length > 0) slot.refs = refs;
|
|
1669
1860
|
}
|
|
1670
1861
|
if (Array.isArray(raw["refViews"])) {
|
|
1671
1862
|
const views = [];
|
|
@@ -1755,63 +1946,72 @@ window.__ModuleLoader__.load({
|
|
|
1755
1946
|
return resolve((0, react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot));
|
|
1756
1947
|
}
|
|
1757
1948
|
//#endregion
|
|
1758
|
-
//#region src/client/summary/
|
|
1949
|
+
//#region src/client/summary/SkillUsageSection.tsx
|
|
1759
1950
|
/**
|
|
1760
|
-
* The
|
|
1761
|
-
* part of the summary tab
|
|
1951
|
+
* The skill-usage section: the `dshSummarySkillUsage` projection drawn as one
|
|
1952
|
+
* part of the summary tab.
|
|
1762
1953
|
*
|
|
1763
|
-
*
|
|
1764
|
-
*
|
|
1954
|
+
* Where `MemoryRecallSection` answers "what memories did the model see", this
|
|
1955
|
+
* answers "which instruction packs entered the context, and which ones were on
|
|
1956
|
+
* the menu". Two groups, deliberately not merged:
|
|
1765
1957
|
*
|
|
1766
|
-
*
|
|
1767
|
-
*
|
|
1768
|
-
*
|
|
1769
|
-
*
|
|
1770
|
-
*
|
|
1771
|
-
*
|
|
1772
|
-
*
|
|
1773
|
-
*
|
|
1958
|
+
* - **已引用** — the skill's instructions reached the conversation, either
|
|
1959
|
+
* because the model called the `skill` tool or because the user invoked it
|
|
1960
|
+
* directly (the host then injects `<skill_content>` and tells the model not
|
|
1961
|
+
* to call the tool — reading only the tool would miss over half of those
|
|
1962
|
+
* sessions, measured on 250 real ones).
|
|
1963
|
+
* - **本会话可用** — the session's current catalog. Available is NOT used: the
|
|
1964
|
+
* model may never touch any of these, so it is collapsed behind a counted
|
|
1965
|
+
* toggle and never mixed into the reference rows.
|
|
1774
1966
|
*
|
|
1775
|
-
*
|
|
1776
|
-
*
|
|
1967
|
+
* Like the deliverables section this is an OPTIONAL capability on both sides:
|
|
1968
|
+
* no projection registry (or a host half that never registered the unit) means
|
|
1969
|
+
* the client reads capability absence and the section hides. No settings switch
|
|
1970
|
+
* gates it either — the list is a read-only fact about the session, not a
|
|
1971
|
+
* preference.
|
|
1972
|
+
*
|
|
1973
|
+
* Two contracts this row honours, both learned on the recall section:
|
|
1974
|
+
* - **no native `title` attribute** — it paints an unskinable tooltip that
|
|
1975
|
+
* overlaps the plugin's own bubble; nothing here needs one;
|
|
1976
|
+
* - **colours from `--dsw-alias-*` tokens only** (skin contract).
|
|
1777
1977
|
*/
|
|
1778
|
-
const SECTION_STYLE$
|
|
1978
|
+
const SECTION_STYLE$4 = {
|
|
1779
1979
|
flexShrink: 0,
|
|
1780
1980
|
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
1781
1981
|
};
|
|
1782
|
-
const HEADER_STYLE$
|
|
1982
|
+
const HEADER_STYLE$5 = {
|
|
1783
1983
|
padding: "10px 12px 0",
|
|
1784
1984
|
color: "var(--dsw-alias-label-secondary)",
|
|
1785
1985
|
fontSize: 12,
|
|
1786
1986
|
fontWeight: 600
|
|
1787
1987
|
};
|
|
1788
|
-
const LIST_STYLE$
|
|
1988
|
+
const LIST_STYLE$3 = {
|
|
1789
1989
|
listStyle: "none",
|
|
1790
1990
|
margin: 0,
|
|
1791
1991
|
padding: "6px 0",
|
|
1792
1992
|
display: "flex",
|
|
1793
1993
|
flexDirection: "column"
|
|
1794
1994
|
};
|
|
1795
|
-
const ROW_STYLE$
|
|
1995
|
+
const ROW_STYLE$4 = {
|
|
1796
1996
|
display: "flex",
|
|
1797
|
-
alignItems: "
|
|
1997
|
+
alignItems: "baseline",
|
|
1798
1998
|
gap: 8,
|
|
1799
1999
|
padding: "6px 12px"
|
|
1800
2000
|
};
|
|
1801
|
-
|
|
2001
|
+
/** The skill name: primary-ish but never the loudest thing in the row. */
|
|
2002
|
+
const NAME_STYLE$1 = {
|
|
1802
2003
|
minWidth: 0,
|
|
1803
2004
|
overflowWrap: "anywhere",
|
|
1804
2005
|
color: "var(--dsw-alias-label-secondary)"
|
|
1805
2006
|
};
|
|
1806
|
-
|
|
2007
|
+
/** The per-skill load tally, tertiary so the name stays the reading target. */
|
|
2008
|
+
const COUNT_STYLE = {
|
|
1807
2009
|
flexShrink: 0,
|
|
1808
2010
|
color: "var(--dsw-alias-label-tertiary)",
|
|
1809
|
-
fontSize: 11
|
|
1810
|
-
lineHeight: "18px",
|
|
1811
|
-
whiteSpace: "nowrap"
|
|
2011
|
+
fontSize: 11
|
|
1812
2012
|
};
|
|
1813
|
-
/** The
|
|
1814
|
-
const
|
|
2013
|
+
/** The "本轮" pill: same form as the deliverables codeplan tag. */
|
|
2014
|
+
const TAG_STYLE$1 = {
|
|
1815
2015
|
flexShrink: 0,
|
|
1816
2016
|
color: "var(--dsw-alias-label-tertiary)",
|
|
1817
2017
|
border: "1px solid var(--dsw-alias-label-tertiary)",
|
|
@@ -1821,40 +2021,12 @@ window.__ModuleLoader__.load({
|
|
|
1821
2021
|
lineHeight: "16px",
|
|
1822
2022
|
whiteSpace: "nowrap"
|
|
1823
2023
|
};
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
flexShrink: 0,
|
|
1827
|
-
color: "var(--dsw-alias-label-primary)",
|
|
1828
|
-
border: "1px solid var(--dsw-alias-label-secondary)",
|
|
1829
|
-
borderRadius: 999,
|
|
1830
|
-
padding: "0 6px",
|
|
1831
|
-
fontSize: 11,
|
|
1832
|
-
lineHeight: "16px",
|
|
1833
|
-
whiteSpace: "nowrap"
|
|
1834
|
-
};
|
|
1835
|
-
const STATUS_SETTLED_STYLE = {
|
|
1836
|
-
...STATUS_OPEN_STYLE,
|
|
1837
|
-
color: "var(--dsw-alias-label-tertiary)",
|
|
1838
|
-
border: "1px solid var(--dsw-alias-label-tertiary)"
|
|
1839
|
-
};
|
|
1840
|
-
const BODY_STYLE$2 = {
|
|
1841
|
-
margin: 0,
|
|
1842
|
-
minWidth: 0,
|
|
1843
|
-
overflowWrap: "anywhere",
|
|
2024
|
+
const FOOTER_STYLE$3 = {
|
|
2025
|
+
padding: "0 12px 10px",
|
|
1844
2026
|
color: "var(--dsw-alias-label-tertiary)",
|
|
1845
|
-
fontSize: 12,
|
|
1846
|
-
lineHeight: "18px"
|
|
1847
|
-
};
|
|
1848
|
-
const REFS_STYLE = {
|
|
1849
|
-
...BODY_STYLE$2,
|
|
1850
2027
|
fontSize: 11
|
|
1851
2028
|
};
|
|
1852
|
-
const
|
|
1853
|
-
padding: "6px 12px 10px",
|
|
1854
|
-
color: "var(--dsw-alias-label-tertiary)",
|
|
1855
|
-
fontSize: 12
|
|
1856
|
-
};
|
|
1857
|
-
const EMPTY_STYLE$1 = {
|
|
2029
|
+
const EMPTY_STYLE$3 = {
|
|
1858
2030
|
margin: "0 auto",
|
|
1859
2031
|
padding: 24,
|
|
1860
2032
|
maxWidth: 460,
|
|
@@ -1863,153 +2035,230 @@ window.__ModuleLoader__.load({
|
|
|
1863
2035
|
lineHeight: 1.7,
|
|
1864
2036
|
fontSize: 12
|
|
1865
2037
|
};
|
|
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
2038
|
/**
|
|
1874
|
-
*
|
|
1875
|
-
*
|
|
1876
|
-
* resolved 名称简述 (record_id → title) with the raw refs on the tooltip, or
|
|
1877
|
-
* raw refs when the host has not resolved them.
|
|
1878
|
-
*/
|
|
1879
|
-
function SlotRow(props) {
|
|
1880
|
-
const { slot, t } = props;
|
|
1881
|
-
const statusLabel = STATUS_KEY[slot.status] === void 0 ? slot.status : t(STATUS_KEY[slot.status]);
|
|
1882
|
-
const title = slot.body === void 0 ? slot.title : `${slot.title} — ${slot.body}`;
|
|
1883
|
-
const refsLine = slot.refViews !== void 0 ? slot.refViews.map((view) => view.title).join(" · ") : slot.refs !== void 0 ? slot.refs.join(" · ") : void 0;
|
|
1884
|
-
return (0, react.createElement)("li", { style: ROW_STYLE$2 }, (0, react.createElement)("span", { style: KIND_STYLE }, slot.kind), (0, react.createElement)("div", { style: {
|
|
1885
|
-
minWidth: 0,
|
|
1886
|
-
flex: "1 1 auto",
|
|
1887
|
-
display: "flex",
|
|
1888
|
-
flexDirection: "column",
|
|
1889
|
-
gap: 2
|
|
1890
|
-
} }, (0, react.createElement)("span", {
|
|
1891
|
-
style: TITLE_STYLE$1,
|
|
1892
|
-
title
|
|
1893
|
-
}, slot.title), slot.body !== void 0 && (0, react.createElement)("p", { style: BODY_STYLE$2 }, truncateBody(slot.body)), refsLine !== void 0 && (0, react.createElement)("p", {
|
|
1894
|
-
style: REFS_STYLE,
|
|
1895
|
-
title: slot.refs !== void 0 ? slot.refs.join("\n") : void 0
|
|
1896
|
-
}, refsLine)), (0, react.createElement)("span", { style: META_STYLE$1 }, `P${slot.priority}`), (0, react.createElement)("span", { style: slot.status === "open" ? STATUS_OPEN_STYLE : STATUS_SETTLED_STYLE }, statusLabel));
|
|
1897
|
-
}
|
|
1898
|
-
/**
|
|
1899
|
-
* Subscribe to this plugin's entry config and resolve the slots switch.
|
|
1900
|
-
* Plumbing lives in `use-config-flag`; the default (off) lives in the resolver.
|
|
2039
|
+
* The skill-usage section. The subscription is unconditional (a plain listener,
|
|
2040
|
+
* not IO); the view decides whether anything is drawn.
|
|
1901
2041
|
*
|
|
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
2042
|
* @param props - translation, client context, session scope.
|
|
1913
2043
|
*/
|
|
1914
|
-
function
|
|
1915
|
-
const { t, ctx, scope } = props;
|
|
1916
|
-
const
|
|
1917
|
-
const view =
|
|
1918
|
-
if (show === false) return null;
|
|
2044
|
+
function SkillUsageSection(props) {
|
|
2045
|
+
const { t, ctx, scope, hideHeader = false } = props;
|
|
2046
|
+
const [availableOpen, setAvailableOpen] = (0, react.useState)(false);
|
|
2047
|
+
const view = readSkillUsage(useProjectionValue(ctx, scope?.sessionId, SKILL_USAGE_KEY));
|
|
1919
2048
|
if (view === void 0) return null;
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
2049
|
+
const referenced = view.session.map((entry) => entry.name);
|
|
2050
|
+
const rest = availableExcept(view.available, referenced);
|
|
2051
|
+
const latestNames = view.latest?.names ?? [];
|
|
2052
|
+
return (0, react.createElement)("div", {
|
|
2053
|
+
style: SECTION_STYLE$4,
|
|
2054
|
+
"data-dsh-brief-sidebar": "skill-usage-section"
|
|
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: {
|
|
1924
2056
|
marginBottom: 6,
|
|
1925
2057
|
color: "var(--dsw-alias-label-secondary)"
|
|
1926
|
-
} }, t("
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
2058
|
+
} }, t("skillUsage.empty")), (0, react.createElement)("div", null, t("skillUsage.emptyHint"))), view.session.length > 0 && (0, react.createElement)(GroupHeader, {
|
|
2059
|
+
text: expandCount(t("skillUsage.invokedGroup"), view.session.length),
|
|
2060
|
+
marker: "skill-usage-invoked-group"
|
|
2061
|
+
}), (0, react.createElement)("ul", { style: LIST_STYLE$3 }, view.session.map((entry) => (0, react.createElement)("li", {
|
|
2062
|
+
key: entry.name,
|
|
2063
|
+
style: ROW_STYLE$4,
|
|
2064
|
+
"data-dsh-brief-sidebar": "skill-usage-row"
|
|
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, {
|
|
2066
|
+
label: expandCount(t("skillUsage.availableGroup"), rest.length),
|
|
2067
|
+
open: availableOpen,
|
|
2068
|
+
marker: "skill-usage-available-toggle",
|
|
2069
|
+
onToggle: () => {
|
|
2070
|
+
setAvailableOpen((current) => !current);
|
|
2071
|
+
}
|
|
2072
|
+
}), rest.length > 0 && view.availableCount > view.available.length && (0, react.createElement)(GroupCaption, {
|
|
2073
|
+
text: expandCount(t("skillUsage.availableCapped"), view.availableCount),
|
|
2074
|
+
marker: "skill-usage-available-capped"
|
|
2075
|
+
}), availableOpen && (0, react.createElement)("ul", { style: LIST_STYLE$3 }, rest.map((name) => (0, react.createElement)("li", {
|
|
2076
|
+
key: name,
|
|
2077
|
+
style: ROW_STYLE$4,
|
|
2078
|
+
"data-dsh-brief-sidebar": "skill-usage-available-row"
|
|
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)));
|
|
1935
2080
|
}
|
|
1936
2081
|
//#endregion
|
|
1937
|
-
//#region src/client/summary/
|
|
2082
|
+
//#region src/client/summary/SearchRefSection.tsx
|
|
1938
2083
|
/**
|
|
1939
|
-
* The `
|
|
2084
|
+
* The search-reference section: the `dshSummarySearchRefs` projection drawn as
|
|
2085
|
+
* one tab of the references section.
|
|
1940
2086
|
*
|
|
1941
|
-
*
|
|
1942
|
-
*
|
|
1943
|
-
*
|
|
1944
|
-
*
|
|
1945
|
-
*
|
|
1946
|
-
*
|
|
1947
|
-
*
|
|
1948
|
-
* retired row keeps its strike-through instead of vanishing.
|
|
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".
|
|
1949
2094
|
*
|
|
1950
|
-
*
|
|
1951
|
-
* client reads
|
|
1952
|
-
*
|
|
1953
|
-
*
|
|
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"`.
|
|
1954
2105
|
*/
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
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
|
+
};
|
|
1985
2185
|
/**
|
|
1986
|
-
*
|
|
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.
|
|
2186
|
+
* The search-reference section. The subscription is unconditional (a plain
|
|
2187
|
+
* listener, not IO); the view decides whether anything is drawn.
|
|
1992
2188
|
*
|
|
1993
|
-
* @param
|
|
1994
|
-
* @returns the validated view, or `undefined` when the capability is absent.
|
|
2189
|
+
* @param props - translation, client context, session scope, header switch.
|
|
1995
2190
|
*/
|
|
1996
|
-
function
|
|
1997
|
-
|
|
1998
|
-
const
|
|
1999
|
-
if (
|
|
2000
|
-
if (
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
}
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
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);
|
|
2013
2262
|
}
|
|
2014
2263
|
//#endregion
|
|
2015
2264
|
//#region src/client/summary/recall-feedback.ts
|
|
@@ -2236,7 +2485,7 @@ window.__ModuleLoader__.load({
|
|
|
2236
2485
|
fontSize: 11,
|
|
2237
2486
|
lineHeight: "16px"
|
|
2238
2487
|
};
|
|
2239
|
-
const BODY_STYLE$
|
|
2488
|
+
const BODY_STYLE$2 = {
|
|
2240
2489
|
minWidth: 0,
|
|
2241
2490
|
overflowWrap: "anywhere",
|
|
2242
2491
|
whiteSpace: "pre-wrap",
|
|
@@ -2258,7 +2507,7 @@ window.__ModuleLoader__.load({
|
|
|
2258
2507
|
};
|
|
2259
2508
|
/** A retired record stays readable in the bubble, just marked. */
|
|
2260
2509
|
const STALE_BODY_STYLE = {
|
|
2261
|
-
...BODY_STYLE$
|
|
2510
|
+
...BODY_STYLE$2,
|
|
2262
2511
|
textDecoration: "line-through"
|
|
2263
2512
|
};
|
|
2264
2513
|
/**
|
|
@@ -2285,7 +2534,7 @@ window.__ModuleLoader__.load({
|
|
|
2285
2534
|
},
|
|
2286
2535
|
"data-dsh-brief-sidebar": "recall-bubble",
|
|
2287
2536
|
role: "tooltip"
|
|
2288
|
-
}, (0, react.createElement)("span", { style: TYPE_STYLE$1 }, item.type), (0, react.createElement)("div", { style: stale ? STALE_BODY_STYLE : BODY_STYLE$
|
|
2537
|
+
}, (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
2538
|
}
|
|
2290
2539
|
//#endregion
|
|
2291
2540
|
//#region src/client/summary/MemoryRecallSection.tsx
|
|
@@ -2331,11 +2580,11 @@ window.__ModuleLoader__.load({
|
|
|
2331
2580
|
* Colours come from `--dsw-alias-*` tokens only (skin contract, enforced by
|
|
2332
2581
|
* `tests/purity.spec.ts`).
|
|
2333
2582
|
*/
|
|
2334
|
-
const SECTION_STYLE = {
|
|
2583
|
+
const SECTION_STYLE$2 = {
|
|
2335
2584
|
flexShrink: 0,
|
|
2336
2585
|
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
2337
2586
|
};
|
|
2338
|
-
const HEADER_STYLE$
|
|
2587
|
+
const HEADER_STYLE$3 = {
|
|
2339
2588
|
padding: "10px 12px 0",
|
|
2340
2589
|
color: "var(--dsw-alias-label-secondary)",
|
|
2341
2590
|
fontSize: 12,
|
|
@@ -2347,14 +2596,14 @@ window.__ModuleLoader__.load({
|
|
|
2347
2596
|
fontSize: 11,
|
|
2348
2597
|
lineHeight: "15px"
|
|
2349
2598
|
};
|
|
2350
|
-
const LIST_STYLE = {
|
|
2599
|
+
const LIST_STYLE$1 = {
|
|
2351
2600
|
listStyle: "none",
|
|
2352
2601
|
margin: 0,
|
|
2353
2602
|
padding: "6px 0",
|
|
2354
2603
|
display: "flex",
|
|
2355
2604
|
flexDirection: "column"
|
|
2356
2605
|
};
|
|
2357
|
-
const ROW_STYLE$
|
|
2606
|
+
const ROW_STYLE$2 = {
|
|
2358
2607
|
display: "flex",
|
|
2359
2608
|
alignItems: "flex-start",
|
|
2360
2609
|
gap: 8,
|
|
@@ -2362,7 +2611,7 @@ window.__ModuleLoader__.load({
|
|
|
2362
2611
|
};
|
|
2363
2612
|
/** Selected row: one hairline of accent, no colour literal (skin contract). */
|
|
2364
2613
|
const ROW_SELECTED_STYLE = {
|
|
2365
|
-
...ROW_STYLE$
|
|
2614
|
+
...ROW_STYLE$2,
|
|
2366
2615
|
background: "var(--dsw-alias-fill-quaternary)"
|
|
2367
2616
|
};
|
|
2368
2617
|
/** The type pill: same form as the slot kind pill, tertiary emphasis. */
|
|
@@ -2376,7 +2625,7 @@ window.__ModuleLoader__.load({
|
|
|
2376
2625
|
lineHeight: "16px",
|
|
2377
2626
|
whiteSpace: "nowrap"
|
|
2378
2627
|
};
|
|
2379
|
-
const TITLE_STYLE = {
|
|
2628
|
+
const TITLE_STYLE$1 = {
|
|
2380
2629
|
minWidth: 0,
|
|
2381
2630
|
color: "var(--dsw-alias-label-secondary)",
|
|
2382
2631
|
display: "-webkit-box",
|
|
@@ -2402,7 +2651,7 @@ window.__ModuleLoader__.load({
|
|
|
2402
2651
|
fontSize: 11,
|
|
2403
2652
|
lineHeight: "16px"
|
|
2404
2653
|
};
|
|
2405
|
-
const META_STYLE = {
|
|
2654
|
+
const META_STYLE$1 = {
|
|
2406
2655
|
flexShrink: 0,
|
|
2407
2656
|
color: "var(--dsw-alias-label-tertiary)",
|
|
2408
2657
|
fontSize: 11,
|
|
@@ -2414,7 +2663,7 @@ window.__ModuleLoader__.load({
|
|
|
2414
2663
|
color: "var(--dsw-alias-label-tertiary)",
|
|
2415
2664
|
fontSize: 12
|
|
2416
2665
|
};
|
|
2417
|
-
const EMPTY_STYLE = {
|
|
2666
|
+
const EMPTY_STYLE$1 = {
|
|
2418
2667
|
margin: "0 auto",
|
|
2419
2668
|
padding: 24,
|
|
2420
2669
|
maxWidth: 460,
|
|
@@ -2530,11 +2779,11 @@ window.__ModuleLoader__.load({
|
|
|
2530
2779
|
const staleText = stale && mark !== -1 ? t("recall.obsoleted") : void 0;
|
|
2531
2780
|
const selectable = props.rating && !stale;
|
|
2532
2781
|
const titleStyle = stale ? {
|
|
2533
|
-
...TITLE_STYLE,
|
|
2782
|
+
...TITLE_STYLE$1,
|
|
2534
2783
|
...STRIKE_STYLE
|
|
2535
|
-
} : TITLE_STYLE;
|
|
2784
|
+
} : TITLE_STYLE$1;
|
|
2536
2785
|
return (0, react.createElement)("li", {
|
|
2537
|
-
style: selected && props.rating ? ROW_SELECTED_STYLE : ROW_STYLE$
|
|
2786
|
+
style: selected && props.rating ? ROW_SELECTED_STYLE : ROW_STYLE$2,
|
|
2538
2787
|
onClick: selectable ? props.onSelect : void 0,
|
|
2539
2788
|
onMouseEnter: (e) => {
|
|
2540
2789
|
const el = e.currentTarget;
|
|
@@ -2563,7 +2812,7 @@ window.__ModuleLoader__.load({
|
|
|
2563
2812
|
confirmLabel: t("recall.obsoleteConfirm"),
|
|
2564
2813
|
whyLabel: t("recall.obsoleteWhy"),
|
|
2565
2814
|
onScore: props.onScore
|
|
2566
|
-
})), (0, react.createElement)("span", { style: META_STYLE }, `P${item.priority}`));
|
|
2815
|
+
})), (0, react.createElement)("span", { style: META_STYLE$1 }, `P${item.priority}`));
|
|
2567
2816
|
}
|
|
2568
2817
|
/** How long the `-1` button stays armed before reverting to its plain label. */
|
|
2569
2818
|
const ARM_WINDOW_MS = 5e3;
|
|
@@ -2669,10 +2918,10 @@ window.__ModuleLoader__.load({
|
|
|
2669
2918
|
const historyRows = view.items.filter((item) => item.superseded === true && item.retired !== true);
|
|
2670
2919
|
const hidden = historyRows.length;
|
|
2671
2920
|
if (rows.length === 0) return (0, react.createElement)("div", {
|
|
2672
|
-
style: SECTION_STYLE,
|
|
2921
|
+
style: SECTION_STYLE$2,
|
|
2673
2922
|
"data-dsh-brief-sidebar": "memory-recall-section"
|
|
2674
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
2675
|
-
style: EMPTY_STYLE,
|
|
2923
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$3 }, t("section.memoryRecall")), (0, react.createElement)("div", {
|
|
2924
|
+
style: EMPTY_STYLE$1,
|
|
2676
2925
|
...hidden > 0 ? { "data-dsh-brief-sidebar": "recall-superseded-note" } : {}
|
|
2677
2926
|
}, hidden > 0 ? expandCount(t("recall.supersededHidden"), hidden) : t("recall.empty")), historyRows.length > 0 && (0, react.createElement)(FoldToggle, {
|
|
2678
2927
|
label: expandCount(t("recall.historyGroup"), historyRows.length),
|
|
@@ -2681,7 +2930,7 @@ window.__ModuleLoader__.load({
|
|
|
2681
2930
|
onToggle: () => {
|
|
2682
2931
|
setHistoryOpen((current) => !current);
|
|
2683
2932
|
}
|
|
2684
|
-
}), historyOpen && (0, react.createElement)("ul", { style: LIST_STYLE }, historyRows.map((item) => (0, react.createElement)(RecallRow, {
|
|
2933
|
+
}), historyOpen && (0, react.createElement)("ul", { style: LIST_STYLE$1 }, historyRows.map((item) => (0, react.createElement)(RecallRow, {
|
|
2685
2934
|
key: item.id,
|
|
2686
2935
|
item,
|
|
2687
2936
|
selected: false,
|
|
@@ -2699,12 +2948,12 @@ window.__ModuleLoader__.load({
|
|
|
2699
2948
|
onLeave: () => {}
|
|
2700
2949
|
}))));
|
|
2701
2950
|
return (0, react.createElement)("div", {
|
|
2702
|
-
style: SECTION_STYLE,
|
|
2951
|
+
style: SECTION_STYLE$2,
|
|
2703
2952
|
"data-dsh-brief-sidebar": "memory-recall-section"
|
|
2704
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
2953
|
+
}, (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
2954
|
text: expandCount(t("recall.activeGroup"), rows.length),
|
|
2706
2955
|
marker: "recall-active-group"
|
|
2707
|
-
}), (0, react.createElement)("ul", { style: LIST_STYLE }, rows.map((item) => (0, react.createElement)(RecallRow, {
|
|
2956
|
+
}), (0, react.createElement)("ul", { style: LIST_STYLE$1 }, rows.map((item) => (0, react.createElement)(RecallRow, {
|
|
2708
2957
|
key: item.id,
|
|
2709
2958
|
item,
|
|
2710
2959
|
selected: selectedId === item.id,
|
|
@@ -2733,7 +2982,7 @@ window.__ModuleLoader__.load({
|
|
|
2733
2982
|
onToggle: () => {
|
|
2734
2983
|
setHistoryOpen((current) => !current);
|
|
2735
2984
|
}
|
|
2736
|
-
}), historyOpen && (0, react.createElement)("ul", { style: LIST_STYLE }, historyRows.map((item) => (0, react.createElement)(RecallRow, {
|
|
2985
|
+
}), historyOpen && (0, react.createElement)("ul", { style: LIST_STYLE$1 }, historyRows.map((item) => (0, react.createElement)(RecallRow, {
|
|
2737
2986
|
key: item.id,
|
|
2738
2987
|
item,
|
|
2739
2988
|
selected: false,
|
|
@@ -2760,16 +3009,361 @@ window.__ModuleLoader__.load({
|
|
|
2760
3009
|
})());
|
|
2761
3010
|
}
|
|
2762
3011
|
//#endregion
|
|
3012
|
+
//#region src/client/summary/MemorySlotsSection.tsx
|
|
3013
|
+
/**
|
|
3014
|
+
* The memory-slots section: the `memorySlots` projection drawn as the third
|
|
3015
|
+
* part of the summary tab (fixed last: 进展 → 产物 → 记忆).
|
|
3016
|
+
*
|
|
3017
|
+
* TWO gates, both fail-open to "hidden", so a host without dsh-prime-memory
|
|
3018
|
+
* paints nothing here:
|
|
3019
|
+
*
|
|
3020
|
+
* 1. **The switch.** This plugin's entry config (`configForms` → entry
|
|
3021
|
+
* `dsh-brief-sidebar`, field `showMemorySlots`, default OFF — the pinned
|
|
3022
|
+
* list moved out of the summary pane's way in favour of the session-recall
|
|
3023
|
+
* section). When the config face cannot answer (service absent, scope not
|
|
3024
|
+
* ready, field unset) the default applies — see `scope-face.ts`.
|
|
3025
|
+
* 2. **The capability.** The `memorySlots` projection must exist for the
|
|
3026
|
+
* session; `undefined` hides the section entirely, exactly like the
|
|
3027
|
+
* optional deliverables section.
|
|
3028
|
+
*
|
|
3029
|
+
* Colours come from `--dsw-alias-*` tokens only (skin contract, enforced by
|
|
3030
|
+
* `tests/purity.spec.ts`).
|
|
3031
|
+
*/
|
|
3032
|
+
const SECTION_STYLE$1 = {
|
|
3033
|
+
flexShrink: 0,
|
|
3034
|
+
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
3035
|
+
};
|
|
3036
|
+
const HEADER_STYLE$2 = {
|
|
3037
|
+
padding: "10px 12px 0",
|
|
3038
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
3039
|
+
fontSize: 12,
|
|
3040
|
+
fontWeight: 600
|
|
3041
|
+
};
|
|
3042
|
+
const LIST_STYLE = {
|
|
3043
|
+
listStyle: "none",
|
|
3044
|
+
margin: 0,
|
|
3045
|
+
padding: "6px 0",
|
|
3046
|
+
display: "flex",
|
|
3047
|
+
flexDirection: "column"
|
|
3048
|
+
};
|
|
3049
|
+
const ROW_STYLE$1 = {
|
|
3050
|
+
display: "flex",
|
|
3051
|
+
alignItems: "flex-start",
|
|
3052
|
+
gap: 8,
|
|
3053
|
+
padding: "6px 12px"
|
|
3054
|
+
};
|
|
3055
|
+
const TITLE_STYLE = {
|
|
3056
|
+
minWidth: 0,
|
|
3057
|
+
overflowWrap: "anywhere",
|
|
3058
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
3059
|
+
};
|
|
3060
|
+
const META_STYLE = {
|
|
3061
|
+
flexShrink: 0,
|
|
3062
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
3063
|
+
fontSize: 11,
|
|
3064
|
+
lineHeight: "18px",
|
|
3065
|
+
whiteSpace: "nowrap"
|
|
3066
|
+
};
|
|
3067
|
+
/** The kind pill: same form as the codeplan pill, tertiary emphasis. */
|
|
3068
|
+
const KIND_STYLE = {
|
|
3069
|
+
flexShrink: 0,
|
|
3070
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
3071
|
+
border: "1px solid var(--dsw-alias-label-tertiary)",
|
|
3072
|
+
borderRadius: 999,
|
|
3073
|
+
padding: "0 6px",
|
|
3074
|
+
fontSize: 11,
|
|
3075
|
+
lineHeight: "16px",
|
|
3076
|
+
whiteSpace: "nowrap"
|
|
3077
|
+
};
|
|
3078
|
+
/** The status pill: filled tint for an open slot, plain text otherwise. */
|
|
3079
|
+
const STATUS_OPEN_STYLE = {
|
|
3080
|
+
flexShrink: 0,
|
|
3081
|
+
color: "var(--dsw-alias-label-primary)",
|
|
3082
|
+
border: "1px solid var(--dsw-alias-label-secondary)",
|
|
3083
|
+
borderRadius: 999,
|
|
3084
|
+
padding: "0 6px",
|
|
3085
|
+
fontSize: 11,
|
|
3086
|
+
lineHeight: "16px",
|
|
3087
|
+
whiteSpace: "nowrap"
|
|
3088
|
+
};
|
|
3089
|
+
const STATUS_SETTLED_STYLE = {
|
|
3090
|
+
...STATUS_OPEN_STYLE,
|
|
3091
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
3092
|
+
border: "1px solid var(--dsw-alias-label-tertiary)"
|
|
3093
|
+
};
|
|
3094
|
+
const BODY_STYLE$1 = {
|
|
3095
|
+
margin: 0,
|
|
3096
|
+
minWidth: 0,
|
|
3097
|
+
overflowWrap: "anywhere",
|
|
3098
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
3099
|
+
fontSize: 12,
|
|
3100
|
+
lineHeight: "18px"
|
|
3101
|
+
};
|
|
3102
|
+
const REFS_STYLE = {
|
|
3103
|
+
...BODY_STYLE$1,
|
|
3104
|
+
fontSize: 11
|
|
3105
|
+
};
|
|
3106
|
+
const TOTAL_STYLE = {
|
|
3107
|
+
padding: "6px 12px 10px",
|
|
3108
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
3109
|
+
fontSize: 12
|
|
3110
|
+
};
|
|
3111
|
+
const EMPTY_STYLE = {
|
|
3112
|
+
margin: "0 auto",
|
|
3113
|
+
padding: 24,
|
|
3114
|
+
maxWidth: 460,
|
|
3115
|
+
textAlign: "center",
|
|
3116
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
3117
|
+
lineHeight: 1.7,
|
|
3118
|
+
fontSize: 12
|
|
3119
|
+
};
|
|
3120
|
+
/** Locale key per known slot status; unknown statuses fall back to the raw wire value. */
|
|
3121
|
+
const STATUS_KEY = {
|
|
3122
|
+
open: "slots.status.open",
|
|
3123
|
+
done: "slots.status.done",
|
|
3124
|
+
dropped: "slots.status.dropped",
|
|
3125
|
+
expired: "slots.status.expired"
|
|
3126
|
+
};
|
|
3127
|
+
/**
|
|
3128
|
+
* One slot row: kind pill, title (full body rides the tooltip), status pill,
|
|
3129
|
+
* priority; body line underneath when the wire view carries it; refs as
|
|
3130
|
+
* resolved 名称简述 (record_id → title) with the raw refs on the tooltip, or
|
|
3131
|
+
* raw refs when the host has not resolved them.
|
|
3132
|
+
*/
|
|
3133
|
+
function SlotRow(props) {
|
|
3134
|
+
const { slot, t } = props;
|
|
3135
|
+
const statusLabel = STATUS_KEY[slot.status] === void 0 ? slot.status : t(STATUS_KEY[slot.status]);
|
|
3136
|
+
const title = slot.body === void 0 ? slot.title : `${slot.title} — ${slot.body}`;
|
|
3137
|
+
const refsLine = slot.refViews !== void 0 ? slot.refViews.map((view) => view.title).join(" · ") : slot.refs !== void 0 ? slot.refs.join(" · ") : void 0;
|
|
3138
|
+
return (0, react.createElement)("li", { style: ROW_STYLE$1 }, (0, react.createElement)("span", { style: KIND_STYLE }, slot.kind), (0, react.createElement)("div", { style: {
|
|
3139
|
+
minWidth: 0,
|
|
3140
|
+
flex: "1 1 auto",
|
|
3141
|
+
display: "flex",
|
|
3142
|
+
flexDirection: "column",
|
|
3143
|
+
gap: 2
|
|
3144
|
+
} }, (0, react.createElement)("span", {
|
|
3145
|
+
style: TITLE_STYLE,
|
|
3146
|
+
title
|
|
3147
|
+
}, slot.title), slot.body !== void 0 && (0, react.createElement)("p", { style: BODY_STYLE$1 }, truncateBody(slot.body)), refsLine !== void 0 && (0, react.createElement)("p", {
|
|
3148
|
+
style: REFS_STYLE,
|
|
3149
|
+
title: slot.refs !== void 0 ? slot.refs.join("\n") : void 0
|
|
3150
|
+
}, 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));
|
|
3151
|
+
}
|
|
3152
|
+
/**
|
|
3153
|
+
* Subscribe to this plugin's entry config and resolve the slots switch.
|
|
3154
|
+
* Plumbing lives in `use-config-flag`; the default (off) lives in the resolver.
|
|
3155
|
+
*
|
|
3156
|
+
* @param ctx - the client root context handed to the tab body.
|
|
3157
|
+
* @returns whether the memory-slots section may render at all.
|
|
3158
|
+
*/
|
|
3159
|
+
function useShowMemorySlots(ctx) {
|
|
3160
|
+
return useConfigFlag(ctx, resolveShowMemorySlots);
|
|
3161
|
+
}
|
|
3162
|
+
/**
|
|
3163
|
+
* The memory-slots section. Both hooks run unconditionally (a conditional
|
|
3164
|
+
* hook order would break the moment the switch flips); the switch only
|
|
3165
|
+
* decides whether the resolved view paints.
|
|
3166
|
+
* @param props - translation, client context, session scope.
|
|
3167
|
+
*/
|
|
3168
|
+
function MemorySlotsSection(props) {
|
|
3169
|
+
const { t, ctx, scope } = props;
|
|
3170
|
+
const show = useShowMemorySlots(ctx);
|
|
3171
|
+
const view = readSlots(useProjectionValue(ctx, scope?.sessionId, MEMORY_SLOTS_KEY));
|
|
3172
|
+
if (show === false) return null;
|
|
3173
|
+
if (view === void 0) return null;
|
|
3174
|
+
if (view.slots.length === 0) return (0, react.createElement)("div", {
|
|
3175
|
+
style: SECTION_STYLE$1,
|
|
3176
|
+
"data-dsh-brief-sidebar": "memory-slots-section"
|
|
3177
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$2 }, t("section.memorySlots")), (0, react.createElement)("div", { style: EMPTY_STYLE }, (0, react.createElement)("div", { style: {
|
|
3178
|
+
marginBottom: 6,
|
|
3179
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
3180
|
+
} }, t("slots.empty")), (0, react.createElement)("div", null, t("slots.emptyHint"))));
|
|
3181
|
+
return (0, react.createElement)("div", {
|
|
3182
|
+
style: SECTION_STYLE$1,
|
|
3183
|
+
"data-dsh-brief-sidebar": "memory-slots-section"
|
|
3184
|
+
}, (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, {
|
|
3185
|
+
key: slot.id,
|
|
3186
|
+
slot,
|
|
3187
|
+
t
|
|
3188
|
+
}))), (0, react.createElement)("div", { style: TOTAL_STYLE }, expandCount(t("slots.openCount"), view.openCount)));
|
|
3189
|
+
}
|
|
3190
|
+
//#endregion
|
|
3191
|
+
//#region src/client/summary/ReferenceSection.tsx
|
|
3192
|
+
/**
|
|
3193
|
+
* The references section: 技能与记忆并列成两个可左右切换的页签。
|
|
3194
|
+
*
|
|
3195
|
+
* Why a tab strip instead of three stacked sections: the two lists answer the
|
|
3196
|
+
* same question — "what entered this session's context" — from two different
|
|
3197
|
+
* providers (this plugin's own skill fold, and prime-memory's recall/slots).
|
|
3198
|
+
* Stacked, they pushed the board and the produced files off screen on a narrow
|
|
3199
|
+
* sidebar; side by side, each gets the full width when the reader actually
|
|
3200
|
+
* wants it.
|
|
3201
|
+
*
|
|
3202
|
+
* Two decisions worth knowing when editing this file:
|
|
3203
|
+
*
|
|
3204
|
+
* 1. **The inactive pane stays mounted and is hidden with `display:none`.**
|
|
3205
|
+
* The recall pane carries real interaction state (selected row, pending
|
|
3206
|
+
* rating, the expanded history group); unmounting it would silently reset
|
|
3207
|
+
* what the human just did every time they glanced at the skills tab. The
|
|
3208
|
+
* cost is that a hidden pane keeps re-rendering on projection frames —
|
|
3209
|
+
* bounded, because the whole tab still renders nothing while `visible` is
|
|
3210
|
+
* false (see `SummaryTab`).
|
|
3211
|
+
* 2. **The parent repeats its children's gates.** A section that hides itself
|
|
3212
|
+
* leaves the tab strip pointing at an empty pane, so the availability
|
|
3213
|
+
* question is answered here with the SAME readers and resolvers the children
|
|
3214
|
+
* use. `tests/reference-section.spec.tsx` pins both sides against the same
|
|
3215
|
+
* fixtures — if a child gate ever drifts from this one, that test goes red
|
|
3216
|
+
* rather than the UI growing a dead tab.
|
|
3217
|
+
*
|
|
3218
|
+
* Colours come from `--dsw-alias-*` tokens only (skin contract).
|
|
3219
|
+
*/
|
|
3220
|
+
const SECTION_STYLE = {
|
|
3221
|
+
flexShrink: 0,
|
|
3222
|
+
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
3223
|
+
};
|
|
3224
|
+
const HEADER_STYLE$1 = {
|
|
3225
|
+
padding: "10px 12px 0",
|
|
3226
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
3227
|
+
fontSize: 12,
|
|
3228
|
+
fontWeight: 600
|
|
3229
|
+
};
|
|
3230
|
+
/** The tab strip: flush-left, same gutter as section headers. */
|
|
3231
|
+
const TABS_STYLE = {
|
|
3232
|
+
display: "flex",
|
|
3233
|
+
gap: 4,
|
|
3234
|
+
padding: "6px 12px 0"
|
|
3235
|
+
};
|
|
3236
|
+
/** One tab. Selected gets a hairline of secondary label; never a colour literal. */
|
|
3237
|
+
const TAB_STYLE = {
|
|
3238
|
+
background: "none",
|
|
3239
|
+
border: "none",
|
|
3240
|
+
borderBottom: "1px solid transparent",
|
|
3241
|
+
font: "inherit",
|
|
3242
|
+
fontSize: 12,
|
|
3243
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
3244
|
+
cursor: "pointer",
|
|
3245
|
+
padding: "4px 8px 6px"
|
|
3246
|
+
};
|
|
3247
|
+
const TAB_ACTIVE_STYLE = {
|
|
3248
|
+
...TAB_STYLE,
|
|
3249
|
+
borderBottom: "1px solid var(--dsw-alias-label-secondary)",
|
|
3250
|
+
color: "var(--dsw-alias-label-primary)",
|
|
3251
|
+
fontWeight: 600
|
|
3252
|
+
};
|
|
3253
|
+
const ORDER = [
|
|
3254
|
+
"skills",
|
|
3255
|
+
"memory",
|
|
3256
|
+
"search"
|
|
3257
|
+
];
|
|
3258
|
+
/**
|
|
3259
|
+
* The references section. All hooks run before any early return, so the tab
|
|
3260
|
+
* strip can disappear (no capability at all) without ever changing hook order.
|
|
3261
|
+
*
|
|
3262
|
+
* @param props - translation, client context, session scope, rating transport.
|
|
3263
|
+
*/
|
|
3264
|
+
function ReferenceSection(props) {
|
|
3265
|
+
const { t, ctx, scope, fetchImpl } = props;
|
|
3266
|
+
const [tab, setTab] = (0, react.useState)("skills");
|
|
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;
|
|
3269
|
+
const recallFrame = readRecall(useProjectionValue(ctx, scope?.sessionId, MEMORY_RECALL_KEY));
|
|
3270
|
+
const slotsFrame = readSlots(useProjectionValue(ctx, scope?.sessionId, MEMORY_SLOTS_KEY));
|
|
3271
|
+
const recallOn = useConfigFlag(ctx, resolveShowMemoryRecall) !== false;
|
|
3272
|
+
const slotsOn = useConfigFlag(ctx, resolveShowMemorySlots) === true;
|
|
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";
|
|
3280
|
+
const onTabsKeyDown = (0, react.useCallback)((event) => {
|
|
3281
|
+
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
|
|
3282
|
+
event.preventDefault();
|
|
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;
|
|
3290
|
+
const tabButton = (id, label) => (0, react.createElement)("button", {
|
|
3291
|
+
key: id,
|
|
3292
|
+
type: "button",
|
|
3293
|
+
role: "tab",
|
|
3294
|
+
id: `dsh-brief-reference-tab-${id}`,
|
|
3295
|
+
"aria-selected": active === id,
|
|
3296
|
+
"aria-controls": `dsh-brief-reference-pane-${id}`,
|
|
3297
|
+
style: active === id ? TAB_ACTIVE_STYLE : TAB_STYLE,
|
|
3298
|
+
"data-dsh-brief-sidebar": `reference-tab-${id}`,
|
|
3299
|
+
onClick: () => {
|
|
3300
|
+
setTab(id);
|
|
3301
|
+
}
|
|
3302
|
+
}, label);
|
|
3303
|
+
return (0, react.createElement)("div", {
|
|
3304
|
+
style: SECTION_STYLE,
|
|
3305
|
+
"data-dsh-brief-sidebar": "references-section"
|
|
3306
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$1 }, t("section.references")), (0, react.createElement)("div", {
|
|
3307
|
+
role: "tablist",
|
|
3308
|
+
"aria-label": t("section.references"),
|
|
3309
|
+
style: TABS_STYLE,
|
|
3310
|
+
onKeyDown: onTabsKeyDown,
|
|
3311
|
+
"data-dsh-brief-sidebar": "reference-tabs"
|
|
3312
|
+
}, skillsLive && tabButton("skills", t("references.tabSkills")), memoryLive && tabButton("memory", t("references.tabMemory")), searchLive && tabButton("search", t("references.tabSearch"))), skillsLive && (0, react.createElement)("div", {
|
|
3313
|
+
role: "tabpanel",
|
|
3314
|
+
id: "dsh-brief-reference-pane-skills",
|
|
3315
|
+
"aria-labelledby": "dsh-brief-reference-tab-skills",
|
|
3316
|
+
hidden: active !== "skills",
|
|
3317
|
+
style: active === "skills" ? void 0 : { display: "none" },
|
|
3318
|
+
"data-dsh-brief-sidebar": "reference-pane-skills"
|
|
3319
|
+
}, (0, react.createElement)(SkillUsageSection, {
|
|
3320
|
+
t,
|
|
3321
|
+
ctx,
|
|
3322
|
+
scope,
|
|
3323
|
+
hideHeader: true
|
|
3324
|
+
})), memoryLive && (0, react.createElement)("div", {
|
|
3325
|
+
role: "tabpanel",
|
|
3326
|
+
id: "dsh-brief-reference-pane-memory",
|
|
3327
|
+
"aria-labelledby": "dsh-brief-reference-tab-memory",
|
|
3328
|
+
hidden: active !== "memory",
|
|
3329
|
+
style: active === "memory" ? void 0 : { display: "none" },
|
|
3330
|
+
"data-dsh-brief-sidebar": "reference-pane-memory"
|
|
3331
|
+
}, (0, react.createElement)(MemoryRecallSection, {
|
|
3332
|
+
t,
|
|
3333
|
+
ctx,
|
|
3334
|
+
scope,
|
|
3335
|
+
fetchImpl
|
|
3336
|
+
}), (0, react.createElement)(MemorySlotsSection, {
|
|
3337
|
+
t,
|
|
3338
|
+
ctx,
|
|
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
|
|
3352
|
+
})));
|
|
3353
|
+
}
|
|
3354
|
+
//#endregion
|
|
2763
3355
|
//#region src/client/SummaryTab.tsx
|
|
2764
3356
|
/**
|
|
2765
3357
|
* The summary tab body: the session's at-a-glance state, composed of sections.
|
|
2766
3358
|
*
|
|
2767
3359
|
* Section order: 进展 (the todos board) → 产物 (produced files, codeplan
|
|
2768
|
-
* artifacts marked inline) →
|
|
2769
|
-
*
|
|
2770
|
-
*
|
|
2771
|
-
*
|
|
2772
|
-
*
|
|
3360
|
+
* artifacts marked inline) → 引用 (two switchable tabs: 技能 and 记忆).
|
|
3361
|
+
* The reference tabs replaced three stacked sections: which skills entered the
|
|
3362
|
+
* session, what prime-memory injected and compaction has not overwritten, and
|
|
3363
|
+
* the hand-pinned resident slots — all three answer "what is in this session's
|
|
3364
|
+
* context", and stacking them pushed the board off a narrow sidebar. Each pane
|
|
3365
|
+
* keeps its own data chain and its own gating contract; this component owns
|
|
3366
|
+
* only the shell.
|
|
2773
3367
|
*
|
|
2774
3368
|
* Two better-sidebar contracts are honoured:
|
|
2775
3369
|
*
|
|
@@ -2816,18 +3410,8 @@ window.__ModuleLoader__.load({
|
|
|
2816
3410
|
t,
|
|
2817
3411
|
ctx,
|
|
2818
3412
|
scope
|
|
2819
|
-
}), (0, react.createElement)(
|
|
2820
|
-
key: "
|
|
2821
|
-
t,
|
|
2822
|
-
ctx,
|
|
2823
|
-
scope
|
|
2824
|
-
}), (0, react.createElement)(MemoryRecallSection, {
|
|
2825
|
-
key: "memory-recall",
|
|
2826
|
-
t,
|
|
2827
|
-
ctx,
|
|
2828
|
-
scope
|
|
2829
|
-
}), (0, react.createElement)(MemorySlotsSection, {
|
|
2830
|
-
key: "memory-slots",
|
|
3413
|
+
}), (0, react.createElement)(ReferenceSection, {
|
|
3414
|
+
key: "references",
|
|
2831
3415
|
t,
|
|
2832
3416
|
ctx,
|
|
2833
3417
|
scope
|