dsh-brief-sidebar 0.5.0-beta.1 → 0.5.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-brief-sidebar",
3
- "version": "0.5.0-beta.1",
3
+ "version": "0.5.0-beta.3",
4
4
  "main": "./lib/index.mjs",
5
5
  "description": "会话简报侧栏页(brief):把会话的 todo 列表(`todos` 投影的进展看板)、产物列表(`dshSummaryDeliverables` 投影的实时产出文件)与记忆槽位(`memorySlots` 投影,来自 dsh-prime-memory,开关可关)一起渲染成右侧栏的一个「概要」tab,并遮蔽 DSH 官方那条 composer 上方的 todo 面板。只读展示,不做写回。",
6
6
  "engines": {
package/lib/client.js CHANGED
@@ -496,6 +496,17 @@ window.__ModuleLoader__.load({
496
496
  const slash = normalizedPath.lastIndexOf("/");
497
497
  return slash < 0 ? normalizedPath : normalizedPath.slice(slash + 1);
498
498
  }
499
+ /**
500
+ * 历史文件 = 全会话去重路径 − 本回合活跃路径。
501
+ *
502
+ * 分隔符归一化后比较(`a\b` 与 `a/b` 视为同一路径),保持 sessionPaths 自带的
503
+ * "最近在前"顺序;活跃路径一个都不出现——分层契约:活跃的已在上方列表里,
504
+ * 折叠区只收"更早改过的文件"。
505
+ */
506
+ function historyPaths(sessionPaths, latestPaths) {
507
+ const active = new Set(latestPaths.map(normalized));
508
+ return sessionPaths.filter((path) => !active.has(normalized(path)));
509
+ }
499
510
  //#endregion
500
511
  //#region src/client/locales.ts
501
512
  /**
@@ -540,7 +551,7 @@ window.__ModuleLoader__.load({
540
551
  "status.completed": "已完成",
541
552
  "deliverables.empty": "本回合暂无改动文件",
542
553
  "deliverables.emptyHint": "会话里成功写入或修改文件后,它们会实时出现在这里。",
543
- "deliverables.sessionTotal": "本会话共 {count} 个文件",
554
+ "deliverables.history": "更早改动的文件 · {count}",
544
555
  "deliverable.codeplanTag": "规划",
545
556
  "section.memorySlots": "记忆",
546
557
  "slots.empty": "当前没有激活的记忆槽位",
@@ -571,7 +582,7 @@ window.__ModuleLoader__.load({
571
582
  "status.completed": "Done",
572
583
  "deliverables.empty": "No files changed in this turn",
573
584
  "deliverables.emptyHint": "Files written or edited successfully in the session appear here live.",
574
- "deliverables.sessionTotal": "{count} files in this session",
585
+ "deliverables.history": "Earlier changes · {count}",
575
586
  "deliverable.codeplanTag": "Plan",
576
587
  "section.memorySlots": "Memory",
577
588
  "slots.empty": "No active memory slots",
@@ -602,7 +613,7 @@ window.__ModuleLoader__.load({
602
613
  "status.completed": "完了",
603
614
  "deliverables.empty": "このターンで変更されたファイルはありません",
604
615
  "deliverables.emptyHint": "セッションでファイルの書き込み・変更が成功すると、ここにリアルタイムで表示されます。",
605
- "deliverables.sessionTotal": "このセッションのファイル数: {count}",
616
+ "deliverables.history": "以前の変更 · {count}",
606
617
  "deliverable.codeplanTag": "計画",
607
618
  "section.memorySlots": "メモリ",
608
619
  "slots.empty": "アクティブなメモリスロットはありません",
@@ -633,7 +644,7 @@ window.__ModuleLoader__.load({
633
644
  "status.completed": "완료",
634
645
  "deliverables.empty": "이번 턴에 변경된 파일이 없습니다",
635
646
  "deliverables.emptyHint": "세션에서 파일을 성공적으로 작성하거나 수정하면 여기에 실시간으로 표시됩니다.",
636
- "deliverables.sessionTotal": "이 세션의 파일 {count}개",
647
+ "deliverables.history": "이전 변경 사항 · {count}",
637
648
  "deliverable.codeplanTag": "계획",
638
649
  "section.memorySlots": "메모리",
639
650
  "slots.empty": "활성화된 메모리 슬롯이 없습니다",
@@ -664,7 +675,7 @@ window.__ModuleLoader__.load({
664
675
  "status.completed": "Terminé",
665
676
  "deliverables.empty": "Aucun fichier modifié à ce tour",
666
677
  "deliverables.emptyHint": "Les fichiers écrits ou modifiés dans la session apparaissent ici en direct.",
667
- "deliverables.sessionTotal": "{count} fichiers dans cette session",
678
+ "deliverables.history": "Modifications antérieures · {count}",
668
679
  "deliverable.codeplanTag": "Plan",
669
680
  "section.memorySlots": "Mémoire",
670
681
  "slots.empty": "Aucun emplacement mémoire actif",
@@ -695,7 +706,7 @@ window.__ModuleLoader__.load({
695
706
  "status.completed": "Fertig",
696
707
  "deliverables.empty": "Keine Dateien in diesem Durchgang geändert",
697
708
  "deliverables.emptyHint": "In der Sitzung erfolgreich geschriebene oder geänderte Dateien erscheinen hier live.",
698
- "deliverables.sessionTotal": "{count} Dateien in dieser Sitzung",
709
+ "deliverables.history": "Frühere Änderungen · {count}",
699
710
  "deliverable.codeplanTag": "Plan",
700
711
  "section.memorySlots": "Gedächtnis",
701
712
  "slots.empty": "Keine aktiven Speicherplätze",
@@ -726,7 +737,7 @@ window.__ModuleLoader__.load({
726
737
  "status.completed": "Completato",
727
738
  "deliverables.empty": "Nessun file modificato in questo turno",
728
739
  "deliverables.emptyHint": "I file scritti o modificati nella sessione appaiono qui in tempo reale.",
729
- "deliverables.sessionTotal": "{count} file in questa sessione",
740
+ "deliverables.history": "Modifiche precedenti · {count}",
730
741
  "deliverable.codeplanTag": "Piano",
731
742
  "section.memorySlots": "Memoria",
732
743
  "slots.empty": "Nessuno slot di memoria attivo",
@@ -757,7 +768,7 @@ window.__ModuleLoader__.load({
757
768
  "status.completed": "Готово",
758
769
  "deliverables.empty": "За этот ход файлы не менялись",
759
770
  "deliverables.emptyHint": "Успешно записанные или изменённые файлы появляются здесь в реальном времени.",
760
- "deliverables.sessionTotal": "Файлов в этой сессии: {count}",
771
+ "deliverables.history": "Более ранние изменения · {count}",
761
772
  "deliverable.codeplanTag": "План",
762
773
  "section.memorySlots": "Память",
763
774
  "slots.empty": "Активных слотов памяти нет",
@@ -788,7 +799,7 @@ window.__ModuleLoader__.load({
788
799
  "status.completed": "Hecho",
789
800
  "deliverables.empty": "Ningún archivo modificado en este turno",
790
801
  "deliverables.emptyHint": "Los archivos escritos o editados en la sesión aparecen aquí en directo.",
791
- "deliverables.sessionTotal": "{count} archivos en esta sesión",
802
+ "deliverables.history": "Cambios anteriores · {count}",
792
803
  "deliverable.codeplanTag": "Plan",
793
804
  "section.memorySlots": "Memoria",
794
805
  "slots.empty": "No hay ranuras de memoria activas",
@@ -858,11 +869,26 @@ window.__ModuleLoader__.load({
858
869
  gap: 8,
859
870
  padding: "6px 12px"
860
871
  };
861
- const TOTAL_STYLE$1 = {
862
- padding: "6px 12px 10px",
872
+ /** 折叠区开关行:与文件行同宽,tertiary 强调,整行可点。 */
873
+ const HISTORY_TOGGLE_STYLE = {
874
+ background: "none",
875
+ border: "none",
876
+ font: "inherit",
877
+ fontSize: 12,
863
878
  color: "var(--dsw-alias-label-tertiary)",
864
- fontSize: 12
879
+ cursor: "pointer",
880
+ display: "flex",
881
+ alignItems: "center",
882
+ gap: 4,
883
+ padding: "6px 12px",
884
+ textAlign: "left",
885
+ width: "calc(100% - 24px)"
865
886
  };
887
+ const CHEVRON_STYLE$1 = (open) => ({
888
+ flexShrink: 0,
889
+ transition: "transform 0.16s",
890
+ transform: open ? "rotate(180deg)" : "none"
891
+ });
866
892
  const PATH_STYLE = {
867
893
  minWidth: 0,
868
894
  overflowWrap: "anywhere",
@@ -918,14 +944,19 @@ window.__ModuleLoader__.load({
918
944
  /**
919
945
  * The deliverables section. The subscription is unconditional (a plain
920
946
  * listener, not IO).
947
+ *
948
+ * 分层:活跃文件(本回合 latest)直接列表;全会话更早改动折叠在开关行后面
949
+ * (与活跃文件按归一化路径去重),展开后是与活跃区相同的可点击文件行。
921
950
  * @param props - translation, client context, session scope.
922
951
  */
923
952
  function DeliverablesSection(props) {
924
953
  const { t, ctx, scope } = props;
954
+ const [historyOpen, setHistoryOpen] = (0, react.useState)(false);
925
955
  const view = readDeliverables(useProjectionValue(ctx, scope?.sessionId, DELIVERABLES_KEY));
926
956
  if (view === void 0) return null;
927
957
  const open = sidebarFileOpener(ctx, scope?.sessionId);
928
958
  const latestPaths = view.latest?.paths ?? [];
959
+ const history = historyPaths(view.sessionPaths, latestPaths);
929
960
  if (latestPaths.length === 0 && view.sessionTotal === 0) return (0, react.createElement)("div", {
930
961
  style: SECTION_STYLE$1,
931
962
  "data-dsh-brief-sidebar": "deliverables-section"
@@ -949,7 +980,32 @@ window.__ModuleLoader__.load({
949
980
  path,
950
981
  t,
951
982
  open
952
- }))), (0, react.createElement)("div", { style: TOTAL_STYLE$1 }, expandCount(t("deliverables.sessionTotal"), view.sessionTotal)));
983
+ }))), history.length > 0 && (0, react.createElement)("button", {
984
+ type: "button",
985
+ "aria-expanded": historyOpen,
986
+ style: HISTORY_TOGGLE_STYLE,
987
+ onClick: () => {
988
+ setHistoryOpen((current) => !current);
989
+ }
990
+ }, expandCount(t("deliverables.history"), history.length), (0, react.createElement)("svg", {
991
+ width: 10,
992
+ height: 10,
993
+ viewBox: "0 0 16 16",
994
+ "aria-hidden": true,
995
+ style: CHEVRON_STYLE$1(historyOpen)
996
+ }, (0, react.createElement)("path", {
997
+ d: "M4 6l4 4 4-4",
998
+ fill: "none",
999
+ stroke: "currentColor",
1000
+ strokeWidth: 1.5,
1001
+ strokeLinecap: "round",
1002
+ strokeLinejoin: "round"
1003
+ }))), historyOpen && (0, react.createElement)("ul", { style: LIST_STYLE$1 }, history.map((path) => (0, react.createElement)(PathRow, {
1004
+ key: path,
1005
+ path,
1006
+ t,
1007
+ open
1008
+ }))));
953
1009
  }
954
1010
  //#endregion
955
1011
  //#region src/client/summary/memory-slots.ts
@@ -1030,6 +1086,20 @@ window.__ModuleLoader__.load({
1030
1086
  const refs = raw["refs"].filter((ref) => typeof ref === "string" && ref.length > 0);
1031
1087
  if (refs.length > 0) slot.refs = refs;
1032
1088
  }
1089
+ if (Array.isArray(raw["refViews"])) {
1090
+ const views = [];
1091
+ for (const entry of raw["refViews"]) {
1092
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) continue;
1093
+ const view = entry;
1094
+ if (typeof view["ref"] !== "string" || view["ref"].length === 0) continue;
1095
+ if (typeof view["title"] !== "string" || view["title"].length === 0) continue;
1096
+ views.push({
1097
+ ref: view["ref"],
1098
+ title: view["title"]
1099
+ });
1100
+ }
1101
+ if (views.length > 0) slot.refViews = views;
1102
+ }
1033
1103
  return slot;
1034
1104
  }
1035
1105
  /**
@@ -1166,12 +1236,15 @@ window.__ModuleLoader__.load({
1166
1236
  };
1167
1237
  /**
1168
1238
  * One slot row: kind pill, title (full body rides the tooltip), status pill,
1169
- * priority; body and refs lines underneath when the wire view carries them.
1239
+ * priority; body line underneath when the wire view carries it; refs as
1240
+ * resolved 名称简述 (record_id → title) with the raw refs on the tooltip, or
1241
+ * raw refs when the host has not resolved them.
1170
1242
  */
1171
1243
  function SlotRow(props) {
1172
1244
  const { slot, t } = props;
1173
1245
  const statusLabel = STATUS_KEY[slot.status] === void 0 ? slot.status : t(STATUS_KEY[slot.status]);
1174
1246
  const title = slot.body === void 0 ? slot.title : `${slot.title} — ${slot.body}`;
1247
+ const refsLine = slot.refViews !== void 0 ? slot.refViews.map((view) => view.title).join(" · ") : slot.refs !== void 0 ? slot.refs.join(" · ") : void 0;
1175
1248
  return (0, react.createElement)("li", { style: ROW_STYLE$1 }, (0, react.createElement)("span", { style: KIND_STYLE }, slot.kind), (0, react.createElement)("div", { style: {
1176
1249
  minWidth: 0,
1177
1250
  flex: "1 1 auto",
@@ -1181,7 +1254,10 @@ window.__ModuleLoader__.load({
1181
1254
  } }, (0, react.createElement)("span", {
1182
1255
  style: TITLE_STYLE,
1183
1256
  title
1184
- }, slot.title), slot.body !== void 0 && (0, react.createElement)("p", { style: BODY_STYLE$1 }, truncateBody(slot.body)), slot.refs !== void 0 && (0, react.createElement)("p", { style: REFS_STYLE }, slot.refs.join(" · "))), (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));
1257
+ }, 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
+ style: REFS_STYLE,
1259
+ 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));
1185
1261
  }
1186
1262
  /**
1187
1263
  * A no-op unsubscribe source, kept as one identity so the absent-scope