dsh-brief-sidebar 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dsh.plugin.json +1 -1
- package/lib/client.js +404 -87
- package/lib/client.js.map +1 -1
- package/lib/index.mjs +12 -4
- package/lib/index.mjs.map +1 -1
- package/lib/types/client/locales.d.ts +5 -0
- package/lib/types/client/scope-face.d.ts +18 -6
- package/lib/types/client/summary/MemoryRecallSection.d.ts +19 -0
- package/lib/types/client/summary/MemorySlotsSection.d.ts +2 -6
- package/lib/types/client/summary/memory-recall.d.ts +50 -0
- package/lib/types/client/summary/use-config-flag.d.ts +10 -0
- package/lib/types/index.d.ts +15 -6
- package/package.json +1 -1
- package/src/client/SummaryTab.tsx +10 -7
- package/src/client/locales.ts +45 -0
- package/src/client/scope-face.ts +26 -7
- package/src/client/settings-card.tsx +52 -15
- package/src/client/summary/MemoryRecallSection.tsx +192 -0
- package/src/client/summary/MemorySlotsSection.tsx +8 -46
- package/src/client/summary/memory-recall.ts +91 -0
- package/src/client/summary/use-config-flag.ts +60 -0
- package/src/index.ts +15 -7
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$3 = {
|
|
235
235
|
flexShrink: 0,
|
|
236
236
|
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
237
237
|
};
|
|
238
|
-
const HEADER_STYLE$
|
|
238
|
+
const HEADER_STYLE$4 = {
|
|
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$3 = {
|
|
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$4 = {
|
|
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$4 }, (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$3,
|
|
320
320
|
"data-dsh-brief-sidebar": "progress-section"
|
|
321
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
321
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$4 }, 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$3,
|
|
327
327
|
"data-dsh-brief-sidebar": "progress-section"
|
|
328
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
328
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$4 }, 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$3,
|
|
335
335
|
"data-dsh-brief-sidebar": "progress-section"
|
|
336
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
336
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$4 }, t("section.progress")), (0, react.createElement)("div", { style: PROGRESS_STYLE }, progressLine(counts, t)), (0, react.createElement)("ul", { style: LIST_STYLE$3 }, todos.map((item, index) => (0, react.createElement)(TodoRow, {
|
|
337
337
|
key: `${index}:${item.content}`,
|
|
338
338
|
item,
|
|
339
339
|
t
|
|
@@ -561,10 +561,15 @@ window.__ModuleLoader__.load({
|
|
|
561
561
|
"slots.status.done": "已完成",
|
|
562
562
|
"slots.status.dropped": "已弃置",
|
|
563
563
|
"slots.status.expired": "已过期",
|
|
564
|
+
"section.memoryRecall": "本会话召回",
|
|
565
|
+
"recall.empty": "本会话还没有召回到记忆",
|
|
566
|
+
"recall.count": "注入 {count} 条 · 压缩后自动清空",
|
|
564
567
|
"settings.title": "概要侧栏",
|
|
565
568
|
"settings.desc": "右侧栏概要 tab 的分区开关(只读展示)。",
|
|
566
569
|
"settings.memorySlots": "记忆槽位分区",
|
|
567
570
|
"settings.memorySlotsHint": "开启后,概要 tab 在进展与产物之下显示 dsh-prime-memory 的激活记忆槽位(只读)。",
|
|
571
|
+
"settings.memoryRecall": "本会话召回分区",
|
|
572
|
+
"settings.memoryRecallHint": "开启后,概要 tab 显示本会话已注入、尚未被压缩覆盖的记忆(只读,非常驻)。",
|
|
568
573
|
"settings.unavailable": "配置面不可用,记忆分区按默认「开」处理。",
|
|
569
574
|
"settings.readonly": "当前配置只读,无法在此修改。"
|
|
570
575
|
},
|
|
@@ -592,10 +597,15 @@ window.__ModuleLoader__.load({
|
|
|
592
597
|
"slots.status.done": "Done",
|
|
593
598
|
"slots.status.dropped": "Dropped",
|
|
594
599
|
"slots.status.expired": "Expired",
|
|
600
|
+
"section.memoryRecall": "Session recall",
|
|
601
|
+
"recall.empty": "No memories recalled in this session yet",
|
|
602
|
+
"recall.count": "{count} injected · cleared on compaction",
|
|
595
603
|
"settings.title": "Brief sidebar",
|
|
596
604
|
"settings.desc": "Section switches for the summary tab in the right sidebar (read-only views).",
|
|
597
605
|
"settings.memorySlots": "Memory slots section",
|
|
598
606
|
"settings.memorySlotsHint": "When on, the summary tab lists the active memory slots from dsh-prime-memory beneath progress and produced files (read-only).",
|
|
607
|
+
"settings.memoryRecall": "Session recall section",
|
|
608
|
+
"settings.memoryRecallHint": "When on, the summary tab lists memories injected in this session that compaction has not overwritten (read-only, not resident).",
|
|
599
609
|
"settings.unavailable": "The config face is unavailable; the memory section falls back to its default (on).",
|
|
600
610
|
"settings.readonly": "This config is read-only here; edits are not available."
|
|
601
611
|
},
|
|
@@ -623,10 +633,15 @@ window.__ModuleLoader__.load({
|
|
|
623
633
|
"slots.status.done": "完了",
|
|
624
634
|
"slots.status.dropped": "破棄",
|
|
625
635
|
"slots.status.expired": "期限切れ",
|
|
636
|
+
"section.memoryRecall": "このセッションの想起",
|
|
637
|
+
"recall.empty": "このセッションではまだ記憶を想起していません",
|
|
638
|
+
"recall.count": "{count} 件注入 · 圧縮で自動消去",
|
|
626
639
|
"settings.title": "概要サイドバー",
|
|
627
640
|
"settings.desc": "右サイドバー概要タブのセクション表示切り替え(読み取り専用)。",
|
|
628
641
|
"settings.memorySlots": "メモリスロットセクション",
|
|
629
642
|
"settings.memorySlotsHint": "オンにすると、概要タブの進行状況と成果物の下に dsh-prime-memory のアクティブなメモリスロット(読み取り専用)が表示されます。",
|
|
643
|
+
"settings.memoryRecall": "セッション想起のセクション",
|
|
644
|
+
"settings.memoryRecallHint": "オンにすると、概要タブにこのセッションで注入され、まだ圧縮で失われていない記憶を表示します(読み取り専用、常駐しません)。",
|
|
630
645
|
"settings.unavailable": "設定画面を利用できません。メモリセクションは既定(オン)で動作します。",
|
|
631
646
|
"settings.readonly": "この設定はここでは読み取り専用のため変更できません。"
|
|
632
647
|
},
|
|
@@ -654,10 +669,15 @@ window.__ModuleLoader__.load({
|
|
|
654
669
|
"slots.status.done": "완료",
|
|
655
670
|
"slots.status.dropped": "폐기",
|
|
656
671
|
"slots.status.expired": "만료",
|
|
672
|
+
"section.memoryRecall": "이 세션 회상",
|
|
673
|
+
"recall.empty": "이 세션에서는 아직 기억을 회상하지 않았습니다",
|
|
674
|
+
"recall.count": "{count}개 주입 · 압축 시 자동 삭제",
|
|
657
675
|
"settings.title": "요약 사이드바",
|
|
658
676
|
"settings.desc": "오른쪽 사이드바 요약 탭의 섹션 표시 전환(읽기 전용).",
|
|
659
677
|
"settings.memorySlots": "메모리 슬롯 섹션",
|
|
660
678
|
"settings.memorySlotsHint": "켜면 요약 탭의 진행 상황과 산출물 아래에 dsh-prime-memory의 활성 메모리 슬롯(읽기 전용)이 표시됩니다.",
|
|
679
|
+
"settings.memoryRecall": "세션 회상 섹션",
|
|
680
|
+
"settings.memoryRecallHint": "켜면 개요 탭에 이 세션에 주입되고 아직 압축으로 사라지지 않은 기억이 표시됩니다(읽기 전용, 상주하지 않음).",
|
|
661
681
|
"settings.unavailable": "설정 화면을 사용할 수 없어 메모리 섹션은 기본값(켜짐)으로 동작합니다.",
|
|
662
682
|
"settings.readonly": "이 설정은 여기서 읽기 전용이므로 변경할 수 없습니다."
|
|
663
683
|
},
|
|
@@ -685,10 +705,15 @@ window.__ModuleLoader__.load({
|
|
|
685
705
|
"slots.status.done": "Terminé",
|
|
686
706
|
"slots.status.dropped": "Abandonné",
|
|
687
707
|
"slots.status.expired": "Expiré",
|
|
708
|
+
"section.memoryRecall": "Rappel de la session",
|
|
709
|
+
"recall.empty": "Aucun souvenir rappelé dans cette session",
|
|
710
|
+
"recall.count": "{count} injectés · effacés à la compression",
|
|
688
711
|
"settings.title": "Barre latérale Résumé",
|
|
689
712
|
"settings.desc": "Interrupteurs des sections de l’onglet Résumé de la barre latérale (lecture seule).",
|
|
690
713
|
"settings.memorySlots": "Section des emplacements mémoire",
|
|
691
714
|
"settings.memorySlotsHint": "Une fois activé, l’onglet Résumé liste les emplacements mémoire actifs de dsh-prime-memory sous l’avancement et les fichiers produits (lecture seule).",
|
|
715
|
+
"settings.memoryRecall": "Section rappel de session",
|
|
716
|
+
"settings.memoryRecallHint": "Affiche dans l’onglet Résumé les souvenirs injectés dans cette session et non encore effacés par la compression (lecture seule, non résidents).",
|
|
692
717
|
"settings.unavailable": "Le canal de configuration est indisponible ; la section mémoire revient à sa valeur par défaut (activée).",
|
|
693
718
|
"settings.readonly": "Cette configuration est en lecture seule ici ; aucune modification possible."
|
|
694
719
|
},
|
|
@@ -716,10 +741,15 @@ window.__ModuleLoader__.load({
|
|
|
716
741
|
"slots.status.done": "Erledigt",
|
|
717
742
|
"slots.status.dropped": "Verworfen",
|
|
718
743
|
"slots.status.expired": "Abgelaufen",
|
|
744
|
+
"section.memoryRecall": "Sitzungserinnerung",
|
|
745
|
+
"recall.empty": "In dieser Sitzung wurden noch keine Erinnerungen geholt",
|
|
746
|
+
"recall.count": "{count} eingefügt · beim Komprimieren geleert",
|
|
719
747
|
"settings.title": "Übersicht-Seitenleiste",
|
|
720
748
|
"settings.desc": "Schalter für die Abschnitte des Übersicht-Tabs in der rechten Seitenleiste (schreibgeschützt).",
|
|
721
749
|
"settings.memorySlots": "Speicherplätze-Abschnitt",
|
|
722
750
|
"settings.memorySlotsHint": "Ist der Schalter eingeschaltet, listet der Übersicht-Tab die aktiven Speicherplätze aus dsh-prime-memory unter Fortschritt und erzeugten Dateien auf (schreibgeschützt).",
|
|
751
|
+
"settings.memoryRecall": "Bereich Sitzungserinnerung",
|
|
752
|
+
"settings.memoryRecallHint": "Zeigt in der Übersichtsseite die in dieser Sitzung eingefügten, noch nicht komprimierten Erinnerungen (schreibgeschützt, nicht dauerhaft).",
|
|
723
753
|
"settings.unavailable": "Die Konfigurationsoberfläche ist nicht verfügbar; der Gedächtnis-Abschnitt fällt auf den Standard (ein) zurück.",
|
|
724
754
|
"settings.readonly": "Diese Konfiguration ist hier schreibgeschützt; Änderungen sind nicht möglich."
|
|
725
755
|
},
|
|
@@ -747,10 +777,15 @@ window.__ModuleLoader__.load({
|
|
|
747
777
|
"slots.status.done": "Completato",
|
|
748
778
|
"slots.status.dropped": "Scartato",
|
|
749
779
|
"slots.status.expired": "Scaduto",
|
|
780
|
+
"section.memoryRecall": "Richiamo della sessione",
|
|
781
|
+
"recall.empty": "Nessun ricordo richiamato in questa sessione",
|
|
782
|
+
"recall.count": "{count} inseriti · svuotati alla compressione",
|
|
750
783
|
"settings.title": "Barra laterale Riepilogo",
|
|
751
784
|
"settings.desc": "Interruttori delle sezioni della scheda Riepilogo nella barra laterale destra (sola lettura).",
|
|
752
785
|
"settings.memorySlots": "Sezione slot di memoria",
|
|
753
786
|
"settings.memorySlotsHint": "Quando attivo, la scheda Riepilogo elenca gli slot di memoria attivi di dsh-prime-memory sotto avanzamento e file prodotti (sola lettura).",
|
|
787
|
+
"settings.memoryRecall": "Sezione richiamo sessione",
|
|
788
|
+
"settings.memoryRecallHint": "Mostra nella pagina Riepilogo i ricordi inseriti in questa sessione e non ancora rimossi dalla compressione (sola lettura, non residenti).",
|
|
754
789
|
"settings.unavailable": "La superficie di configurazione non è disponibile; la sezione memoria torna al valore predefinito (attivo).",
|
|
755
790
|
"settings.readonly": "Questa configurazione è in sola lettura qui; non è possibile modificarla."
|
|
756
791
|
},
|
|
@@ -778,10 +813,15 @@ window.__ModuleLoader__.load({
|
|
|
778
813
|
"slots.status.done": "Завершён",
|
|
779
814
|
"slots.status.dropped": "Отброшен",
|
|
780
815
|
"slots.status.expired": "Истёк",
|
|
816
|
+
"section.memoryRecall": "Воспоминания сессии",
|
|
817
|
+
"recall.empty": "В этой сессии воспоминания ещё не вызывались",
|
|
818
|
+
"recall.count": "вставлено {count} · очищается при сжатии",
|
|
781
819
|
"settings.title": "Боковая панель «Сводка»",
|
|
782
820
|
"settings.desc": "Переключатели разделов вкладки «Сводка» на правой панели (только чтение).",
|
|
783
821
|
"settings.memorySlots": "Раздел слотов памяти",
|
|
784
822
|
"settings.memorySlotsHint": "Когда включено, вкладка «Сводка» показывает активные слоты памяти из dsh-prime-memory под ходом работы и созданными файлами (только чтение).",
|
|
823
|
+
"settings.memoryRecall": "Раздел Воспоминания сессии",
|
|
824
|
+
"settings.memoryRecallHint": "Показывает на странице Обзор воспоминания, вставленные в этой сессии и ещё не удалённые сжатием (только чтение, не постоянные).",
|
|
785
825
|
"settings.unavailable": "Панель конфигурации недоступна; раздел памяти работает по умолчанию (включён).",
|
|
786
826
|
"settings.readonly": "Эта конфигурация здесь только для чтения; изменить её нельзя."
|
|
787
827
|
},
|
|
@@ -809,10 +849,15 @@ window.__ModuleLoader__.load({
|
|
|
809
849
|
"slots.status.done": "Hecha",
|
|
810
850
|
"slots.status.dropped": "Descartada",
|
|
811
851
|
"slots.status.expired": "Caducada",
|
|
852
|
+
"section.memoryRecall": "Recuerdo de la sesión",
|
|
853
|
+
"recall.empty": "Aún no se ha recuperado ningún recuerdo en esta sesión",
|
|
854
|
+
"recall.count": "{count} insertados · se limpia al comprimir",
|
|
812
855
|
"settings.title": "Barra lateral Resumen",
|
|
813
856
|
"settings.desc": "Interruptores de las secciones de la pestaña Resumen en la barra lateral derecha (solo lectura).",
|
|
814
857
|
"settings.memorySlots": "Sección de ranuras de memoria",
|
|
815
858
|
"settings.memorySlotsHint": "Al activarla, la pestaña Resumen enumera las ranuras de memoria activas de dsh-prime-memory bajo el avance y los archivos generados (solo lectura).",
|
|
859
|
+
"settings.memoryRecall": "Sección de recuerdo de sesión",
|
|
860
|
+
"settings.memoryRecallHint": "Muestra en la pestaña Resumen los recuerdos insertados en esta sesión y aún no eliminados por la compresión (solo lectura, no residentes).",
|
|
816
861
|
"settings.unavailable": "La superficie de configuración no está disponible; la sección de memoria vuelve a su valor por defecto (activada).",
|
|
817
862
|
"settings.readonly": "Esta configuración es de solo lectura aquí; no se puede modificar."
|
|
818
863
|
}
|
|
@@ -846,24 +891,24 @@ window.__ModuleLoader__.load({
|
|
|
846
891
|
*
|
|
847
892
|
* Colours come from `--dsw-alias-*` tokens only (skin contract).
|
|
848
893
|
*/
|
|
849
|
-
const SECTION_STYLE$
|
|
894
|
+
const SECTION_STYLE$2 = {
|
|
850
895
|
flexShrink: 0,
|
|
851
896
|
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
852
897
|
};
|
|
853
|
-
const HEADER_STYLE$
|
|
898
|
+
const HEADER_STYLE$3 = {
|
|
854
899
|
padding: "10px 12px 0",
|
|
855
900
|
color: "var(--dsw-alias-label-secondary)",
|
|
856
901
|
fontSize: 12,
|
|
857
902
|
fontWeight: 600
|
|
858
903
|
};
|
|
859
|
-
const LIST_STYLE$
|
|
904
|
+
const LIST_STYLE$2 = {
|
|
860
905
|
listStyle: "none",
|
|
861
906
|
margin: 0,
|
|
862
907
|
padding: "6px 0",
|
|
863
908
|
display: "flex",
|
|
864
909
|
flexDirection: "column"
|
|
865
910
|
};
|
|
866
|
-
const ROW_STYLE$
|
|
911
|
+
const ROW_STYLE$3 = {
|
|
867
912
|
display: "flex",
|
|
868
913
|
alignItems: "flex-start",
|
|
869
914
|
gap: 8,
|
|
@@ -929,11 +974,11 @@ window.__ModuleLoader__.load({
|
|
|
929
974
|
const title = plan === null ? path : `${t("deliverable.codeplanTag")}: ${plan.task} · ${path}`;
|
|
930
975
|
const label = basename(path);
|
|
931
976
|
const content = plan === null ? null : (0, react.createElement)("span", { style: TAG_STYLE }, t("deliverable.codeplanTag"));
|
|
932
|
-
if (open === void 0) return (0, react.createElement)("li", { style: ROW_STYLE$
|
|
977
|
+
if (open === void 0) return (0, react.createElement)("li", { style: ROW_STYLE$3 }, content, (0, react.createElement)("span", {
|
|
933
978
|
style: PATH_STYLE,
|
|
934
979
|
title
|
|
935
980
|
}, label));
|
|
936
|
-
return (0, react.createElement)("li", { style: ROW_STYLE$
|
|
981
|
+
return (0, react.createElement)("li", { style: ROW_STYLE$3 }, content, (0, react.createElement)("button", {
|
|
937
982
|
style: BUTTON_STYLE,
|
|
938
983
|
title,
|
|
939
984
|
onClick: () => {
|
|
@@ -958,9 +1003,9 @@ window.__ModuleLoader__.load({
|
|
|
958
1003
|
const latestPaths = view.latest?.paths ?? [];
|
|
959
1004
|
const history = historyPaths(view.sessionPaths, latestPaths);
|
|
960
1005
|
if (latestPaths.length === 0 && view.sessionTotal === 0) return (0, react.createElement)("div", {
|
|
961
|
-
style: SECTION_STYLE$
|
|
1006
|
+
style: SECTION_STYLE$2,
|
|
962
1007
|
"data-dsh-brief-sidebar": "deliverables-section"
|
|
963
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
1008
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$3 }, t("section.deliverables")), (0, react.createElement)("div", { style: {
|
|
964
1009
|
margin: "0 auto",
|
|
965
1010
|
padding: 24,
|
|
966
1011
|
maxWidth: 460,
|
|
@@ -973,9 +1018,9 @@ window.__ModuleLoader__.load({
|
|
|
973
1018
|
color: "var(--dsw-alias-label-secondary)"
|
|
974
1019
|
} }, t("deliverables.empty")), (0, react.createElement)("div", null, t("deliverables.emptyHint"))));
|
|
975
1020
|
return (0, react.createElement)("div", {
|
|
976
|
-
style: SECTION_STYLE$
|
|
1021
|
+
style: SECTION_STYLE$2,
|
|
977
1022
|
"data-dsh-brief-sidebar": "deliverables-section"
|
|
978
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
1023
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$3 }, t("section.deliverables")), (0, react.createElement)("ul", { style: LIST_STYLE$2 }, latestPaths.map((path) => (0, react.createElement)(PathRow, {
|
|
979
1024
|
key: path,
|
|
980
1025
|
path,
|
|
981
1026
|
t,
|
|
@@ -1000,7 +1045,7 @@ window.__ModuleLoader__.load({
|
|
|
1000
1045
|
strokeWidth: 1.5,
|
|
1001
1046
|
strokeLinecap: "round",
|
|
1002
1047
|
strokeLinejoin: "round"
|
|
1003
|
-
}))), historyOpen && (0, react.createElement)("ul", { style: LIST_STYLE$
|
|
1048
|
+
}))), historyOpen && (0, react.createElement)("ul", { style: LIST_STYLE$2 }, history.map((path) => (0, react.createElement)(PathRow, {
|
|
1004
1049
|
key: path,
|
|
1005
1050
|
path,
|
|
1006
1051
|
t,
|
|
@@ -1048,7 +1093,7 @@ window.__ModuleLoader__.load({
|
|
|
1048
1093
|
function readSlots(value) {
|
|
1049
1094
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1050
1095
|
const raw = value;
|
|
1051
|
-
if (!isCounter(raw["rev"]) || !isCounter(raw["count"]) || !isCounter(raw["openCount"])) return void 0;
|
|
1096
|
+
if (!isCounter$1(raw["rev"]) || !isCounter$1(raw["count"]) || !isCounter$1(raw["openCount"])) return void 0;
|
|
1052
1097
|
if (!Array.isArray(raw["slots"])) return void 0;
|
|
1053
1098
|
const slots = [];
|
|
1054
1099
|
for (const entry of raw["slots"]) {
|
|
@@ -1063,7 +1108,7 @@ window.__ModuleLoader__.load({
|
|
|
1063
1108
|
};
|
|
1064
1109
|
}
|
|
1065
1110
|
/** Whether a value is a non-negative safe integer (the view's counter shape). */
|
|
1066
|
-
function isCounter(value) {
|
|
1111
|
+
function isCounter$1(value) {
|
|
1067
1112
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
1068
1113
|
}
|
|
1069
1114
|
/** Narrow one slot entry; `undefined` = malformed, dropped. */
|
|
@@ -1116,8 +1161,62 @@ window.__ModuleLoader__.load({
|
|
|
1116
1161
|
* explicit `false` is the only off.
|
|
1117
1162
|
*/
|
|
1118
1163
|
function resolveShowMemorySlots(snapshot) {
|
|
1164
|
+
if (snapshot === void 0 || snapshot.status !== "ready") return false;
|
|
1165
|
+
return snapshot.value?.showMemorySlots === true;
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Resolve the session-recall switch from one snapshot (pure; testable).
|
|
1169
|
+
* Any cannot-answer shape falls back to {@link DEFAULT_SHOW_MEMORY_RECALL};
|
|
1170
|
+
* an explicit `false` is the only off.
|
|
1171
|
+
*/
|
|
1172
|
+
function resolveShowMemoryRecall(snapshot) {
|
|
1119
1173
|
if (snapshot === void 0 || snapshot.status !== "ready") return true;
|
|
1120
|
-
return snapshot.value?.
|
|
1174
|
+
return snapshot.value?.showMemoryRecall !== false;
|
|
1175
|
+
}
|
|
1176
|
+
//#endregion
|
|
1177
|
+
//#region src/client/summary/use-config-flag.ts
|
|
1178
|
+
/**
|
|
1179
|
+
* The shared `configForms` subscription behind this plugin's section switches.
|
|
1180
|
+
*
|
|
1181
|
+
* Both memory sections (pinned slots / session recall) gate on one boolean of
|
|
1182
|
+
* this plugin's entry config. The plumbing — defensive scope resolution,
|
|
1183
|
+
* `useSyncExternalStore`, and a stable absent-snapshot — is identical, so it
|
|
1184
|
+
* lives here once; each section passes its own pure resolver, which is what
|
|
1185
|
+
* the unit tests pin.
|
|
1186
|
+
*
|
|
1187
|
+
* A host without `configForms` (or a scope still loading) degrades to the
|
|
1188
|
+
* resolver's default instead of throwing inside a render.
|
|
1189
|
+
*/
|
|
1190
|
+
/**
|
|
1191
|
+
* The snapshot served while no config scope is reachable — every resolver
|
|
1192
|
+
* maps it to its own default.
|
|
1193
|
+
*/
|
|
1194
|
+
const ABSENT_SNAPSHOT = {
|
|
1195
|
+
status: "unavailable",
|
|
1196
|
+
value: void 0,
|
|
1197
|
+
revision: void 0,
|
|
1198
|
+
writable: false
|
|
1199
|
+
};
|
|
1200
|
+
/** A no-op unsubscribe source, kept as one identity so the absent-scope
|
|
1201
|
+
* subscription never thrashes (a store contract requires a disposer). */
|
|
1202
|
+
const NO_SUBSCRIBE$1 = () => () => {};
|
|
1203
|
+
/**
|
|
1204
|
+
* Read one config switch, live.
|
|
1205
|
+
*
|
|
1206
|
+
* @param ctx - the client root context handed to the tab body.
|
|
1207
|
+
* @param resolve - pure snapshot → boolean resolver (owns the default).
|
|
1208
|
+
* @returns the resolved switch value.
|
|
1209
|
+
*/
|
|
1210
|
+
function useConfigFlag(ctx, resolve) {
|
|
1211
|
+
const scope = (0, react.useMemo)(() => {
|
|
1212
|
+
if (ctx === void 0) return void 0;
|
|
1213
|
+
const configForms = ctx.get("configForms");
|
|
1214
|
+
if (configForms === void 0 || typeof configForms.get !== "function") return void 0;
|
|
1215
|
+
return configForms.get("dsh-brief-sidebar");
|
|
1216
|
+
}, [ctx]);
|
|
1217
|
+
const subscribe = (0, react.useCallback)((listener) => scope?.subscribe(listener) ?? NO_SUBSCRIBE$1(), [scope]);
|
|
1218
|
+
const getSnapshot = (0, react.useCallback)(() => scope?.getSnapshot() ?? ABSENT_SNAPSHOT, [scope]);
|
|
1219
|
+
return resolve((0, react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot));
|
|
1121
1220
|
}
|
|
1122
1221
|
//#endregion
|
|
1123
1222
|
//#region src/client/summary/MemorySlotsSection.tsx
|
|
@@ -1129,9 +1228,10 @@ window.__ModuleLoader__.load({
|
|
|
1129
1228
|
* paints nothing here:
|
|
1130
1229
|
*
|
|
1131
1230
|
* 1. **The switch.** This plugin's entry config (`configForms` → entry
|
|
1132
|
-
* `dsh-brief-sidebar`, field `showMemorySlots`, default
|
|
1133
|
-
*
|
|
1134
|
-
*
|
|
1231
|
+
* `dsh-brief-sidebar`, field `showMemorySlots`, default OFF — the pinned
|
|
1232
|
+
* list moved out of the summary pane's way in favour of the session-recall
|
|
1233
|
+
* section). When the config face cannot answer (service absent, scope not
|
|
1234
|
+
* ready, field unset) the default applies — see `scope-face.ts`.
|
|
1135
1235
|
* 2. **The capability.** The `memorySlots` projection must exist for the
|
|
1136
1236
|
* session; `undefined` hides the section entirely, exactly like the
|
|
1137
1237
|
* optional deliverables section.
|
|
@@ -1139,35 +1239,35 @@ window.__ModuleLoader__.load({
|
|
|
1139
1239
|
* Colours come from `--dsw-alias-*` tokens only (skin contract, enforced by
|
|
1140
1240
|
* `tests/purity.spec.ts`).
|
|
1141
1241
|
*/
|
|
1142
|
-
const SECTION_STYLE = {
|
|
1242
|
+
const SECTION_STYLE$1 = {
|
|
1143
1243
|
flexShrink: 0,
|
|
1144
1244
|
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
1145
1245
|
};
|
|
1146
|
-
const HEADER_STYLE$
|
|
1246
|
+
const HEADER_STYLE$2 = {
|
|
1147
1247
|
padding: "10px 12px 0",
|
|
1148
1248
|
color: "var(--dsw-alias-label-secondary)",
|
|
1149
1249
|
fontSize: 12,
|
|
1150
1250
|
fontWeight: 600
|
|
1151
1251
|
};
|
|
1152
|
-
const LIST_STYLE = {
|
|
1252
|
+
const LIST_STYLE$1 = {
|
|
1153
1253
|
listStyle: "none",
|
|
1154
1254
|
margin: 0,
|
|
1155
1255
|
padding: "6px 0",
|
|
1156
1256
|
display: "flex",
|
|
1157
1257
|
flexDirection: "column"
|
|
1158
1258
|
};
|
|
1159
|
-
const ROW_STYLE$
|
|
1259
|
+
const ROW_STYLE$2 = {
|
|
1160
1260
|
display: "flex",
|
|
1161
1261
|
alignItems: "flex-start",
|
|
1162
1262
|
gap: 8,
|
|
1163
1263
|
padding: "6px 12px"
|
|
1164
1264
|
};
|
|
1165
|
-
const TITLE_STYLE = {
|
|
1265
|
+
const TITLE_STYLE$1 = {
|
|
1166
1266
|
minWidth: 0,
|
|
1167
1267
|
overflowWrap: "anywhere",
|
|
1168
1268
|
color: "var(--dsw-alias-label-secondary)"
|
|
1169
1269
|
};
|
|
1170
|
-
const META_STYLE = {
|
|
1270
|
+
const META_STYLE$1 = {
|
|
1171
1271
|
flexShrink: 0,
|
|
1172
1272
|
color: "var(--dsw-alias-label-tertiary)",
|
|
1173
1273
|
fontSize: 11,
|
|
@@ -1218,7 +1318,7 @@ window.__ModuleLoader__.load({
|
|
|
1218
1318
|
color: "var(--dsw-alias-label-tertiary)",
|
|
1219
1319
|
fontSize: 12
|
|
1220
1320
|
};
|
|
1221
|
-
const EMPTY_STYLE = {
|
|
1321
|
+
const EMPTY_STYLE$1 = {
|
|
1222
1322
|
margin: "0 auto",
|
|
1223
1323
|
padding: 24,
|
|
1224
1324
|
maxWidth: 460,
|
|
@@ -1245,57 +1345,29 @@ window.__ModuleLoader__.load({
|
|
|
1245
1345
|
const statusLabel = STATUS_KEY[slot.status] === void 0 ? slot.status : t(STATUS_KEY[slot.status]);
|
|
1246
1346
|
const title = slot.body === void 0 ? slot.title : `${slot.title} — ${slot.body}`;
|
|
1247
1347
|
const refsLine = slot.refViews !== void 0 ? slot.refViews.map((view) => view.title).join(" · ") : slot.refs !== void 0 ? slot.refs.join(" · ") : void 0;
|
|
1248
|
-
return (0, react.createElement)("li", { style: ROW_STYLE$
|
|
1348
|
+
return (0, react.createElement)("li", { style: ROW_STYLE$2 }, (0, react.createElement)("span", { style: KIND_STYLE }, slot.kind), (0, react.createElement)("div", { style: {
|
|
1249
1349
|
minWidth: 0,
|
|
1250
1350
|
flex: "1 1 auto",
|
|
1251
1351
|
display: "flex",
|
|
1252
1352
|
flexDirection: "column",
|
|
1253
1353
|
gap: 2
|
|
1254
1354
|
} }, (0, react.createElement)("span", {
|
|
1255
|
-
style: TITLE_STYLE,
|
|
1355
|
+
style: TITLE_STYLE$1,
|
|
1256
1356
|
title
|
|
1257
1357
|
}, slot.title), slot.body !== void 0 && (0, react.createElement)("p", { style: BODY_STYLE$1 }, truncateBody(slot.body)), refsLine !== void 0 && (0, react.createElement)("p", {
|
|
1258
1358
|
style: REFS_STYLE,
|
|
1259
1359
|
title: slot.refs !== void 0 ? slot.refs.join("\n") : void 0
|
|
1260
|
-
}, 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));
|
|
1360
|
+
}, 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));
|
|
1261
1361
|
}
|
|
1262
1362
|
/**
|
|
1263
|
-
*
|
|
1264
|
-
*
|
|
1265
|
-
* contract requires).
|
|
1266
|
-
*/
|
|
1267
|
-
const NO_SUBSCRIBE$1 = () => () => {};
|
|
1268
|
-
/**
|
|
1269
|
-
* The snapshot served while no config scope is reachable — resolves to the
|
|
1270
|
-
* switch default (on) in `resolveShowMemorySlots`.
|
|
1271
|
-
*/
|
|
1272
|
-
const ABSENT_SNAPSHOT = {
|
|
1273
|
-
status: "unavailable",
|
|
1274
|
-
value: void 0,
|
|
1275
|
-
revision: void 0,
|
|
1276
|
-
writable: false
|
|
1277
|
-
};
|
|
1278
|
-
/**
|
|
1279
|
-
* Subscribe to this plugin's entry config and resolve the section switch.
|
|
1280
|
-
*
|
|
1281
|
-
* The scope is resolved defensively (a host without `configForms` degrades to
|
|
1282
|
-
* the default instead of throwing inside a render) and subscribed with
|
|
1283
|
-
* `useSyncExternalStore`, so flipping the switch in settings updates the open
|
|
1284
|
-
* tab without a reload.
|
|
1363
|
+
* Subscribe to this plugin's entry config and resolve the slots switch.
|
|
1364
|
+
* Plumbing lives in `use-config-flag`; the default (off) lives in the resolver.
|
|
1285
1365
|
*
|
|
1286
1366
|
* @param ctx - the client root context handed to the tab body.
|
|
1287
1367
|
* @returns whether the memory-slots section may render at all.
|
|
1288
1368
|
*/
|
|
1289
1369
|
function useShowMemorySlots(ctx) {
|
|
1290
|
-
|
|
1291
|
-
if (ctx === void 0) return void 0;
|
|
1292
|
-
const configForms = ctx.get("configForms");
|
|
1293
|
-
if (configForms === void 0 || typeof configForms.get !== "function") return void 0;
|
|
1294
|
-
return configForms.get("dsh-brief-sidebar");
|
|
1295
|
-
}, [ctx]);
|
|
1296
|
-
const subscribe = (0, react.useCallback)((listener) => scope?.subscribe(listener) ?? NO_SUBSCRIBE$1(), [scope]);
|
|
1297
|
-
const getSnapshot = (0, react.useCallback)(() => scope?.getSnapshot() ?? ABSENT_SNAPSHOT, [scope]);
|
|
1298
|
-
return resolveShowMemorySlots((0, react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot));
|
|
1370
|
+
return useConfigFlag(ctx, resolveShowMemorySlots);
|
|
1299
1371
|
}
|
|
1300
1372
|
/**
|
|
1301
1373
|
* The memory-slots section. Both hooks run unconditionally (a conditional
|
|
@@ -1310,31 +1382,239 @@ window.__ModuleLoader__.load({
|
|
|
1310
1382
|
if (show === false) return null;
|
|
1311
1383
|
if (view === void 0) return null;
|
|
1312
1384
|
if (view.slots.length === 0) return (0, react.createElement)("div", {
|
|
1313
|
-
style: SECTION_STYLE,
|
|
1385
|
+
style: SECTION_STYLE$1,
|
|
1314
1386
|
"data-dsh-brief-sidebar": "memory-slots-section"
|
|
1315
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
1387
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$2 }, t("section.memorySlots")), (0, react.createElement)("div", { style: EMPTY_STYLE$1 }, (0, react.createElement)("div", { style: {
|
|
1316
1388
|
marginBottom: 6,
|
|
1317
1389
|
color: "var(--dsw-alias-label-secondary)"
|
|
1318
1390
|
} }, t("slots.empty")), (0, react.createElement)("div", null, t("slots.emptyHint"))));
|
|
1319
1391
|
return (0, react.createElement)("div", {
|
|
1320
|
-
style: SECTION_STYLE,
|
|
1392
|
+
style: SECTION_STYLE$1,
|
|
1321
1393
|
"data-dsh-brief-sidebar": "memory-slots-section"
|
|
1322
|
-
}, (0, react.createElement)("div", { style: HEADER_STYLE$
|
|
1394
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$2 }, t("section.memorySlots")), (0, react.createElement)("ul", { style: LIST_STYLE$1 }, view.slots.map((slot) => (0, react.createElement)(SlotRow, {
|
|
1323
1395
|
key: slot.id,
|
|
1324
1396
|
slot,
|
|
1325
1397
|
t
|
|
1326
1398
|
}))), (0, react.createElement)("div", { style: TOTAL_STYLE }, expandCount(t("slots.openCount"), view.openCount)));
|
|
1327
1399
|
}
|
|
1328
1400
|
//#endregion
|
|
1401
|
+
//#region src/client/summary/memory-recall.ts
|
|
1402
|
+
/**
|
|
1403
|
+
* The `memoryRecall` projection — reading it.
|
|
1404
|
+
*
|
|
1405
|
+
* The projection unit is registered by `dsh-prime-memory` (key `memoryRecall`,
|
|
1406
|
+
* wire view `{rev, sessionId, count, items[]}`): the memories this session has
|
|
1407
|
+
* actually injected and that compaction has not overwritten yet. Unlike
|
|
1408
|
+
* `memorySlots` (pinned, resident conventions written by hand), this set is
|
|
1409
|
+
* NOT resident — it is the at-a-glance answer to "what is the model seeing
|
|
1410
|
+
* right now", and it empties itself when the context is compacted.
|
|
1411
|
+
*
|
|
1412
|
+
* This plugin does NOT depend on prime-memory: with the unit unregistered the
|
|
1413
|
+
* client reads `undefined` from the face and the section hides. The module is
|
|
1414
|
+
* deliberately PURE — no React, no context, no IO — so the narrowing is
|
|
1415
|
+
* testable directly, exactly like `memory-slots.ts`.
|
|
1416
|
+
*/
|
|
1417
|
+
/** The projection key `dsh-prime-memory` registers (its canonical constant). */
|
|
1418
|
+
const MEMORY_RECALL_KEY = "memoryRecall";
|
|
1419
|
+
/** Whether a value is a non-negative safe integer (the view's counter shape). */
|
|
1420
|
+
function isCounter(value) {
|
|
1421
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
1422
|
+
}
|
|
1423
|
+
/** Narrow one item entry; `undefined` = malformed, dropped. */
|
|
1424
|
+
function readItem(value) {
|
|
1425
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1426
|
+
const raw = value;
|
|
1427
|
+
if (typeof raw["id"] !== "string" || raw["id"].length === 0) return void 0;
|
|
1428
|
+
if (typeof raw["type"] !== "string" || typeof raw["title"] !== "string") return void 0;
|
|
1429
|
+
if (typeof raw["priority"] !== "number") return void 0;
|
|
1430
|
+
const item = {
|
|
1431
|
+
id: raw["id"],
|
|
1432
|
+
type: raw["type"],
|
|
1433
|
+
title: raw["title"],
|
|
1434
|
+
priority: raw["priority"]
|
|
1435
|
+
};
|
|
1436
|
+
if (Array.isArray(raw["scenario"])) {
|
|
1437
|
+
const phrases = raw["scenario"].filter((s) => typeof s === "string" && s.length > 0);
|
|
1438
|
+
if (phrases.length > 0) item.scenario = phrases;
|
|
1439
|
+
}
|
|
1440
|
+
return item;
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Narrow a raw projection value into a recall view.
|
|
1444
|
+
*
|
|
1445
|
+
* `undefined` means the capability is ABSENT (prime-memory not in the
|
|
1446
|
+
* composition, or no frame carried the key) or the frame drifted beyond repair;
|
|
1447
|
+
* a view with `items: []` IS the honest empty state (prime-memory publishes as
|
|
1448
|
+
* soon as the unit registers). Malformed items are dropped, never thrown on.
|
|
1449
|
+
*
|
|
1450
|
+
* @param value - the raw `unknown` snapshot of the `memoryRecall` face.
|
|
1451
|
+
* @returns the validated view, or `undefined` when the capability is absent.
|
|
1452
|
+
*/
|
|
1453
|
+
function readRecall(value) {
|
|
1454
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
1455
|
+
const raw = value;
|
|
1456
|
+
if (!isCounter(raw["rev"]) || !isCounter(raw["count"])) return void 0;
|
|
1457
|
+
if (typeof raw["sessionId"] !== "string") return void 0;
|
|
1458
|
+
if (!Array.isArray(raw["items"])) return void 0;
|
|
1459
|
+
const items = [];
|
|
1460
|
+
for (const entry of raw["items"]) {
|
|
1461
|
+
const item = readItem(entry);
|
|
1462
|
+
if (item !== void 0) items.push(item);
|
|
1463
|
+
}
|
|
1464
|
+
return {
|
|
1465
|
+
rev: raw["rev"],
|
|
1466
|
+
sessionId: raw["sessionId"],
|
|
1467
|
+
count: raw["count"],
|
|
1468
|
+
items
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
//#endregion
|
|
1472
|
+
//#region src/client/summary/MemoryRecallSection.tsx
|
|
1473
|
+
/**
|
|
1474
|
+
* The session-recall section: what this session actually injected, drawn as a
|
|
1475
|
+
* part of the summary tab.
|
|
1476
|
+
*
|
|
1477
|
+
* Where `MemorySlotsSection` lists hand-pinned resident conventions (written
|
|
1478
|
+
* once, injected every turn), this one answers a different question: **which
|
|
1479
|
+
* memories is the model seeing right now**. The set is not resident — it is
|
|
1480
|
+
* relevance recall, and prime-memory empties it when the context is compacted
|
|
1481
|
+
* or cleared, so an entry disappearing here is honest, not a bug.
|
|
1482
|
+
*
|
|
1483
|
+
* TWO gates, both fail-open to "hidden", mirroring the slots section:
|
|
1484
|
+
*
|
|
1485
|
+
* 1. **The switch.** This plugin's entry config (`configForms` → field
|
|
1486
|
+
* `showMemoryRecall`, default on).
|
|
1487
|
+
* 2. **The capability.** The `memoryRecall` projection must exist for the
|
|
1488
|
+
* session; `undefined` hides the section entirely — a composition without
|
|
1489
|
+
* dsh-prime-memory paints nothing here.
|
|
1490
|
+
*
|
|
1491
|
+
* Rows are read-only by design: the write surface for memory is prime-memory's
|
|
1492
|
+
* own panel, and offering pin/activate affordances here would re-introduce the
|
|
1493
|
+
* "resident list" reading this section exists to replace.
|
|
1494
|
+
*
|
|
1495
|
+
* Colours come from `--dsw-alias-*` tokens only (skin contract, enforced by
|
|
1496
|
+
* `tests/purity.spec.ts`).
|
|
1497
|
+
*/
|
|
1498
|
+
const SECTION_STYLE = {
|
|
1499
|
+
flexShrink: 0,
|
|
1500
|
+
borderBottom: "1px solid var(--dsw-alias-border-secondary)"
|
|
1501
|
+
};
|
|
1502
|
+
const HEADER_STYLE$1 = {
|
|
1503
|
+
padding: "10px 12px 0",
|
|
1504
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
1505
|
+
fontSize: 12,
|
|
1506
|
+
fontWeight: 600
|
|
1507
|
+
};
|
|
1508
|
+
const LIST_STYLE = {
|
|
1509
|
+
listStyle: "none",
|
|
1510
|
+
margin: 0,
|
|
1511
|
+
padding: "6px 0",
|
|
1512
|
+
display: "flex",
|
|
1513
|
+
flexDirection: "column"
|
|
1514
|
+
};
|
|
1515
|
+
const ROW_STYLE$1 = {
|
|
1516
|
+
display: "flex",
|
|
1517
|
+
alignItems: "flex-start",
|
|
1518
|
+
gap: 8,
|
|
1519
|
+
padding: "6px 12px"
|
|
1520
|
+
};
|
|
1521
|
+
/** The type pill: same form as the slot kind pill, tertiary emphasis. */
|
|
1522
|
+
const TYPE_STYLE = {
|
|
1523
|
+
flexShrink: 0,
|
|
1524
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
1525
|
+
border: "1px solid var(--dsw-alias-label-tertiary)",
|
|
1526
|
+
borderRadius: 999,
|
|
1527
|
+
padding: "0 6px",
|
|
1528
|
+
fontSize: 11,
|
|
1529
|
+
lineHeight: "16px",
|
|
1530
|
+
whiteSpace: "nowrap"
|
|
1531
|
+
};
|
|
1532
|
+
const TITLE_STYLE = {
|
|
1533
|
+
minWidth: 0,
|
|
1534
|
+
overflowWrap: "anywhere",
|
|
1535
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
1536
|
+
};
|
|
1537
|
+
const SCENARIO_STYLE = {
|
|
1538
|
+
margin: 0,
|
|
1539
|
+
minWidth: 0,
|
|
1540
|
+
overflowWrap: "anywhere",
|
|
1541
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
1542
|
+
fontSize: 11,
|
|
1543
|
+
lineHeight: "16px"
|
|
1544
|
+
};
|
|
1545
|
+
const META_STYLE = {
|
|
1546
|
+
flexShrink: 0,
|
|
1547
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
1548
|
+
fontSize: 11,
|
|
1549
|
+
lineHeight: "18px",
|
|
1550
|
+
whiteSpace: "nowrap"
|
|
1551
|
+
};
|
|
1552
|
+
const FOOTER_STYLE = {
|
|
1553
|
+
padding: "6px 12px 10px",
|
|
1554
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
1555
|
+
fontSize: 12
|
|
1556
|
+
};
|
|
1557
|
+
const EMPTY_STYLE = {
|
|
1558
|
+
margin: "0 auto",
|
|
1559
|
+
padding: 24,
|
|
1560
|
+
maxWidth: 460,
|
|
1561
|
+
textAlign: "center",
|
|
1562
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
1563
|
+
lineHeight: 1.7,
|
|
1564
|
+
fontSize: 12
|
|
1565
|
+
};
|
|
1566
|
+
/**
|
|
1567
|
+
* One recalled memory: type pill, first line of the body (full text and the
|
|
1568
|
+
* "when should this surface" phrases ride the tooltip), its own priority.
|
|
1569
|
+
*/
|
|
1570
|
+
function RecallRow(props) {
|
|
1571
|
+
const { item } = props;
|
|
1572
|
+
const scenario = item.scenario === void 0 ? void 0 : item.scenario.join(" · ");
|
|
1573
|
+
return (0, react.createElement)("li", { style: ROW_STYLE$1 }, (0, react.createElement)("span", { style: TYPE_STYLE }, item.type), (0, react.createElement)("div", { style: {
|
|
1574
|
+
minWidth: 0,
|
|
1575
|
+
flex: "1 1 auto",
|
|
1576
|
+
display: "flex",
|
|
1577
|
+
flexDirection: "column",
|
|
1578
|
+
gap: 2
|
|
1579
|
+
} }, (0, react.createElement)("span", {
|
|
1580
|
+
style: TITLE_STYLE,
|
|
1581
|
+
title: scenario === void 0 ? item.title : `${item.title} — ${scenario}`
|
|
1582
|
+
}, item.title), scenario !== void 0 && (0, react.createElement)("p", { style: SCENARIO_STYLE }, scenario)), (0, react.createElement)("span", { style: META_STYLE }, `P${item.priority}`));
|
|
1583
|
+
}
|
|
1584
|
+
/**
|
|
1585
|
+
* The session-recall section. Both hooks run unconditionally (a conditional
|
|
1586
|
+
* hook order would break the moment the switch flips); the switch only decides
|
|
1587
|
+
* whether the resolved view paints.
|
|
1588
|
+
* @param props - translation, client context, session scope.
|
|
1589
|
+
*/
|
|
1590
|
+
function MemoryRecallSection(props) {
|
|
1591
|
+
const { t, ctx, scope } = props;
|
|
1592
|
+
const show = useConfigFlag(ctx, resolveShowMemoryRecall);
|
|
1593
|
+
const view = readRecall(useProjectionValue(ctx, scope?.sessionId, MEMORY_RECALL_KEY));
|
|
1594
|
+
if (show === false) return null;
|
|
1595
|
+
if (view === void 0) return null;
|
|
1596
|
+
if (view.items.length === 0) return (0, react.createElement)("div", {
|
|
1597
|
+
style: SECTION_STYLE,
|
|
1598
|
+
"data-dsh-brief-sidebar": "memory-recall-section"
|
|
1599
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$1 }, t("section.memoryRecall")), (0, react.createElement)("div", { style: EMPTY_STYLE }, t("recall.empty")));
|
|
1600
|
+
return (0, react.createElement)("div", {
|
|
1601
|
+
style: SECTION_STYLE,
|
|
1602
|
+
"data-dsh-brief-sidebar": "memory-recall-section"
|
|
1603
|
+
}, (0, react.createElement)("div", { style: HEADER_STYLE$1 }, t("section.memoryRecall")), (0, react.createElement)("ul", { style: LIST_STYLE }, view.items.map((item) => (0, react.createElement)(RecallRow, {
|
|
1604
|
+
key: item.id,
|
|
1605
|
+
item
|
|
1606
|
+
}))), (0, react.createElement)("div", { style: FOOTER_STYLE }, expandCount(t("recall.count"), view.count)));
|
|
1607
|
+
}
|
|
1608
|
+
//#endregion
|
|
1329
1609
|
//#region src/client/SummaryTab.tsx
|
|
1330
1610
|
/**
|
|
1331
1611
|
* The summary tab body: the session's at-a-glance state, composed of sections.
|
|
1332
1612
|
*
|
|
1333
1613
|
* Section order: 进展 (the todos board) → 产物 (produced files, codeplan
|
|
1334
|
-
* artifacts marked inline) →
|
|
1335
|
-
*
|
|
1336
|
-
* Each section owns its own data chain and its
|
|
1337
|
-
* component owns only the shell.
|
|
1614
|
+
* artifacts marked inline) → 本会话召回 (what prime-memory injected into this
|
|
1615
|
+
* session and compaction has not overwritten) → 记忆槽位 (hand-pinned resident
|
|
1616
|
+
* conventions, off by default). Each section owns its own data chain and its
|
|
1617
|
+
* own gating contract; this component owns only the shell.
|
|
1338
1618
|
*
|
|
1339
1619
|
* Two better-sidebar contracts are honoured:
|
|
1340
1620
|
*
|
|
@@ -1381,6 +1661,11 @@ window.__ModuleLoader__.load({
|
|
|
1381
1661
|
t,
|
|
1382
1662
|
ctx,
|
|
1383
1663
|
scope
|
|
1664
|
+
}), (0, react.createElement)(MemoryRecallSection, {
|
|
1665
|
+
key: "memory-recall",
|
|
1666
|
+
t,
|
|
1667
|
+
ctx,
|
|
1668
|
+
scope
|
|
1384
1669
|
}), (0, react.createElement)(MemorySlotsSection, {
|
|
1385
1670
|
key: "memory-slots",
|
|
1386
1671
|
t,
|
|
@@ -1628,6 +1913,25 @@ window.__ModuleLoader__.load({
|
|
|
1628
1913
|
cursor: "pointer"
|
|
1629
1914
|
};
|
|
1630
1915
|
/**
|
|
1916
|
+
* One switch row: label + hint on the left, checkbox on the right. Extracted
|
|
1917
|
+
* because the card now binds two sections (session recall / pinned slots).
|
|
1918
|
+
*/
|
|
1919
|
+
function FlagRow(props) {
|
|
1920
|
+
return (0, react.createElement)("div", { style: ROW_STYLE }, (0, react.createElement)("div", { style: ROW_TEXT_STYLE }, (0, react.createElement)("label", {
|
|
1921
|
+
htmlFor: props.id,
|
|
1922
|
+
style: LABEL_STYLE
|
|
1923
|
+
}, props.label), (0, react.createElement)("p", { style: HINT_STYLE }, props.hint)), (0, react.createElement)("input", {
|
|
1924
|
+
id: props.id,
|
|
1925
|
+
type: "checkbox",
|
|
1926
|
+
checked: props.checked,
|
|
1927
|
+
disabled: props.disabled,
|
|
1928
|
+
style: SWITCH_STYLE,
|
|
1929
|
+
onChange: (event) => {
|
|
1930
|
+
props.onToggle(event.currentTarget.checked);
|
|
1931
|
+
}
|
|
1932
|
+
}));
|
|
1933
|
+
}
|
|
1934
|
+
/**
|
|
1631
1935
|
* The settings card body. `scope` is optional on purpose: the inject factory
|
|
1632
1936
|
* always provides it, but a host whose `configForms` face degrades must not
|
|
1633
1937
|
* crash the settings page — the card renders the unavailable note and keeps
|
|
@@ -1639,7 +1943,8 @@ window.__ModuleLoader__.load({
|
|
|
1639
1943
|
const snapshot = (0, react.useSyncExternalStore)((0, react.useCallback)((listener) => scope?.subscribe(listener) ?? NO_SUBSCRIBE(), [scope]), (0, react.useCallback)(() => scope?.getSnapshot() ?? ABSENT, [scope]), (0, react.useCallback)(() => scope?.getSnapshot() ?? ABSENT, [scope]));
|
|
1640
1944
|
const unavailable = scope === void 0 || snapshot.status === "unavailable";
|
|
1641
1945
|
const readonly = unavailable || !snapshot.writable;
|
|
1642
|
-
const
|
|
1946
|
+
const slotsChecked = resolveShowMemorySlots(snapshot);
|
|
1947
|
+
const recallChecked = resolveShowMemoryRecall(snapshot);
|
|
1643
1948
|
return (0, react.createElement)("div", {
|
|
1644
1949
|
style: CARD_STYLE,
|
|
1645
1950
|
"data-dsh-brief-sidebar": "settings-card"
|
|
@@ -1663,17 +1968,29 @@ window.__ModuleLoader__.load({
|
|
|
1663
1968
|
strokeWidth: 1.5,
|
|
1664
1969
|
strokeLinecap: "round",
|
|
1665
1970
|
strokeLinejoin: "round"
|
|
1666
|
-
}))), open && (0, react.createElement)("div", { style: BODY_STYLE }, unavailable ? (0, react.createElement)("p", { style: HINT_STYLE }, t("settings.unavailable")) : (0, react.createElement)("div", { style:
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1971
|
+
}))), open && (0, react.createElement)("div", { style: BODY_STYLE }, unavailable ? (0, react.createElement)("p", { style: HINT_STYLE }, t("settings.unavailable")) : (0, react.createElement)("div", { style: {
|
|
1972
|
+
display: "flex",
|
|
1973
|
+
flexDirection: "column",
|
|
1974
|
+
gap: 12
|
|
1975
|
+
} }, (0, react.createElement)(FlagRow, {
|
|
1976
|
+
key: "recall",
|
|
1977
|
+
id: "dsh-brief-sidebar-show-memory-recall",
|
|
1978
|
+
label: t("settings.memoryRecall"),
|
|
1979
|
+
hint: t("settings.memoryRecallHint"),
|
|
1980
|
+
checked: recallChecked,
|
|
1981
|
+
disabled: readonly,
|
|
1982
|
+
onToggle: (next) => {
|
|
1983
|
+
scope?.set("showMemoryRecall", next);
|
|
1984
|
+
}
|
|
1985
|
+
}), (0, react.createElement)(FlagRow, {
|
|
1986
|
+
key: "slots",
|
|
1670
1987
|
id: "dsh-brief-sidebar-show-memory-slots",
|
|
1671
|
-
|
|
1672
|
-
|
|
1988
|
+
label: t("settings.memorySlots"),
|
|
1989
|
+
hint: t("settings.memorySlotsHint"),
|
|
1990
|
+
checked: slotsChecked,
|
|
1673
1991
|
disabled: readonly,
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
scope?.set("showMemorySlots", event.currentTarget.checked);
|
|
1992
|
+
onToggle: (next) => {
|
|
1993
|
+
scope?.set("showMemorySlots", next);
|
|
1677
1994
|
}
|
|
1678
1995
|
})), !unavailable && !snapshot.writable && (0, react.createElement)("p", { style: HINT_STYLE }, t("settings.readonly"))));
|
|
1679
1996
|
}
|