pi-mega-compact 0.13.4 → 0.13.6

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.
Files changed (217) hide show
  1. package/README.md +11 -8
  2. package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +50 -0
  3. package/dist/extensions/dashboard-server/api-contracts/rag-metrics.js +1 -0
  4. package/dist/extensions/dashboard-server/api-contracts/rag-settings.js +3 -3
  5. package/dist/extensions/dashboard-server/api-contracts/wiki.js +1 -0
  6. package/dist/extensions/dashboard-server/routes-rag-metrics.js +60 -0
  7. package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +167 -0
  8. package/dist/extensions/dashboard-server/routes-rag-settings.js +136 -77
  9. package/dist/extensions/dashboard-server/routes-wiki-helpers.js +97 -0
  10. package/dist/extensions/dashboard-server/routes-wiki.js +330 -0
  11. package/dist/extensions/dashboard-server/routes.js +2 -0
  12. package/dist/extensions/dashboard-server/server.js +17 -3
  13. package/dist/extensions/mega-events/context-handler/afterCompact.js +35 -2
  14. package/dist/extensions/mega-pipeline/recall.js +31 -0
  15. package/dist/extensions/mega-turn-store.js +2 -1
  16. package/dist/src/config/turns.js +10 -0
  17. package/dist/src/config.js +2 -0
  18. package/dist/src/recall/async.js +10 -1
  19. package/dist/src/recall/format.js +10 -1
  20. package/dist/src/recall/hydeTelemetry.js +22 -0
  21. package/dist/src/recall/sync.js +28 -2
  22. package/dist/src/store/turns/hydeStore.js +32 -0
  23. package/dist/src/store/turns/index.js +1 -0
  24. package/dist/src/store/turns/schema.js +102 -1
  25. package/dist/src/store/turns/sqlite-store/rows.js +23 -0
  26. package/dist/src/store/turns/sqlite-store/write.js +7 -3
  27. package/dist/src/topics/durability.js +187 -0
  28. package/dist/src/topics/index.js +2 -1
  29. package/dist/src/topics/store.js +28 -9
  30. package/dist/src/wiki/curation-helpers.js +71 -0
  31. package/dist/src/wiki/curation.js +147 -0
  32. package/dist/src/wiki/incremental.js +126 -0
  33. package/dist/src/wiki/index.js +5 -0
  34. package/extensions/dashboard-client/dist/assets/AreaChart-KAmp_6bL.js +70 -0
  35. package/extensions/dashboard-client/dist/assets/{SessionsTab-C30AwyoF.js.map → AreaChart-KAmp_6bL.js.map} +1 -1
  36. package/extensions/dashboard-client/dist/assets/CacheTab-Bk-gm-R0.js +2 -0
  37. package/extensions/dashboard-client/dist/assets/CacheTab-Bk-gm-R0.js.map +1 -0
  38. package/extensions/dashboard-client/dist/assets/EventsTab-Cyylo0of.js +2 -0
  39. package/extensions/dashboard-client/dist/assets/EventsTab-Cyylo0of.js.map +1 -0
  40. package/extensions/dashboard-client/dist/assets/HealthTab-D-IzmV29.js +2 -0
  41. package/extensions/dashboard-client/dist/assets/HealthTab-D-IzmV29.js.map +1 -0
  42. package/extensions/dashboard-client/dist/assets/MaintenanceTab-CrlkCFVj.js +2 -0
  43. package/extensions/dashboard-client/dist/assets/MaintenanceTab-CrlkCFVj.js.map +1 -0
  44. package/extensions/dashboard-client/dist/assets/MemoryMapTab-DHUdZ7RR.js +2 -0
  45. package/extensions/dashboard-client/dist/assets/MemoryMapTab-DHUdZ7RR.js.map +1 -0
  46. package/extensions/dashboard-client/dist/assets/MetricsTab-B7I4HWVg.js +2 -0
  47. package/extensions/dashboard-client/dist/assets/MetricsTab-B7I4HWVg.js.map +1 -0
  48. package/extensions/dashboard-client/dist/assets/OverviewTab-K6j4ZI7u.js +2 -0
  49. package/extensions/dashboard-client/dist/assets/OverviewTab-K6j4ZI7u.js.map +1 -0
  50. package/extensions/dashboard-client/dist/assets/ReposTab-BSRWhn56.js +2 -0
  51. package/extensions/dashboard-client/dist/assets/ReposTab-BSRWhn56.js.map +1 -0
  52. package/extensions/dashboard-client/dist/assets/SessionsTab-BXdizHN1.js +2 -0
  53. package/extensions/dashboard-client/dist/assets/SessionsTab-BXdizHN1.js.map +1 -0
  54. package/extensions/dashboard-client/dist/assets/SetupTab-CAgyfqY3.js +2 -0
  55. package/extensions/dashboard-client/dist/assets/SetupTab-CAgyfqY3.js.map +1 -0
  56. package/extensions/dashboard-client/dist/assets/TimeSavedCard-DwgCzPD9.js +2 -0
  57. package/extensions/dashboard-client/dist/assets/TimeSavedCard-DwgCzPD9.js.map +1 -0
  58. package/extensions/dashboard-client/dist/assets/TurnsTab-CVKcStBp.js +5 -0
  59. package/extensions/dashboard-client/dist/assets/TurnsTab-CVKcStBp.js.map +1 -0
  60. package/extensions/dashboard-client/dist/assets/WikiTab-BNohxTbs.js +2 -0
  61. package/extensions/dashboard-client/dist/assets/WikiTab-BNohxTbs.js.map +1 -0
  62. package/extensions/dashboard-client/dist/assets/button-B4A-_dCA.js +2 -0
  63. package/extensions/dashboard-client/dist/assets/button-B4A-_dCA.js.map +1 -0
  64. package/extensions/dashboard-client/dist/assets/card-Cjn7Ifoe.js +2 -0
  65. package/extensions/dashboard-client/dist/assets/card-Cjn7Ifoe.js.map +1 -0
  66. package/extensions/dashboard-client/dist/assets/index-CQjCLvv7.js +137 -0
  67. package/extensions/dashboard-client/dist/assets/index-CQjCLvv7.js.map +1 -0
  68. package/extensions/dashboard-client/dist/assets/index-CUA6EDRF.css +1 -0
  69. package/extensions/dashboard-client/dist/assets/switch-CcdXX0Zm.js +2 -0
  70. package/extensions/dashboard-client/dist/assets/switch-CcdXX0Zm.js.map +1 -0
  71. package/extensions/dashboard-client/dist/assets/toggle-KdBnFLu3.js +2 -0
  72. package/extensions/dashboard-client/dist/assets/toggle-KdBnFLu3.js.map +1 -0
  73. package/extensions/dashboard-client/dist/assets/{useSSE-p1pOI_-P.js → useSSE-B6t44vzc.js} +2 -2
  74. package/extensions/dashboard-client/dist/assets/{useSSE-p1pOI_-P.js.map → useSSE-B6t44vzc.js.map} +1 -1
  75. package/extensions/dashboard-client/dist/index.html +2 -2
  76. package/extensions/dashboard-client/package-lock.json +1995 -191
  77. package/extensions/dashboard-client/package.json +12 -1
  78. package/extensions/dashboard-client/postcss.config.js +6 -0
  79. package/extensions/dashboard-client/src/App.tsx +127 -88
  80. package/extensions/dashboard-client/src/api/client.ts +109 -10
  81. package/extensions/dashboard-client/src/components/ActiveReposTable.tsx +15 -7
  82. package/extensions/dashboard-client/src/components/ActiveSessionsTable.tsx +67 -55
  83. package/extensions/dashboard-client/src/components/CacheHitRateTrendCard.tsx +17 -13
  84. package/extensions/dashboard-client/src/components/CacheHitsCard.tsx +35 -30
  85. package/extensions/dashboard-client/src/components/CacheStatusPerModel.tsx +33 -28
  86. package/extensions/dashboard-client/src/components/EventCategoryFilter.tsx +5 -6
  87. package/extensions/dashboard-client/src/components/EventStream.tsx +17 -13
  88. package/extensions/dashboard-client/src/components/HydeDetailPanel.tsx +173 -0
  89. package/extensions/dashboard-client/src/components/LegendCard.tsx +16 -9
  90. package/extensions/dashboard-client/src/components/PerfCards.tsx +28 -20
  91. package/extensions/dashboard-client/src/components/PerfChart.tsx +9 -6
  92. package/extensions/dashboard-client/src/components/ProviderCacheCard.tsx +15 -7
  93. package/extensions/dashboard-client/src/components/RagDashboard.tsx +253 -0
  94. package/extensions/dashboard-client/src/components/RepoAllSessionsCard.tsx +15 -10
  95. package/extensions/dashboard-client/src/components/RepoContextStack.tsx +24 -16
  96. package/extensions/dashboard-client/src/components/RepoDetailModal.tsx +52 -49
  97. package/extensions/dashboard-client/src/components/RepoTable.tsx +26 -17
  98. package/extensions/dashboard-client/src/components/SessionContextGauges.tsx +11 -14
  99. package/extensions/dashboard-client/src/components/SessionInfo.tsx +20 -15
  100. package/extensions/dashboard-client/src/components/SessionsMemoryChart.tsx +8 -5
  101. package/extensions/dashboard-client/src/components/SummaryTiles.tsx +26 -17
  102. package/extensions/dashboard-client/src/components/TabBar.tsx +1 -3
  103. package/extensions/dashboard-client/src/components/layout/AppShell.tsx +42 -0
  104. package/extensions/dashboard-client/src/components/layout/BottomBar.tsx +88 -0
  105. package/extensions/dashboard-client/src/components/layout/Sidebar.tsx +83 -0
  106. package/extensions/dashboard-client/src/components/ui/badge.tsx +30 -0
  107. package/extensions/dashboard-client/src/components/ui/button.tsx +44 -0
  108. package/extensions/dashboard-client/src/components/ui/card.tsx +32 -0
  109. package/extensions/dashboard-client/src/components/ui/select.tsx +64 -0
  110. package/extensions/dashboard-client/src/components/ui/switch.tsx +26 -0
  111. package/extensions/dashboard-client/src/components/ui/tabs.tsx +36 -0
  112. package/extensions/dashboard-client/src/components/ui/toggle.tsx +28 -0
  113. package/extensions/dashboard-client/src/components/ui/tooltip.tsx +27 -0
  114. package/extensions/dashboard-client/src/config.ts +10 -0
  115. package/extensions/dashboard-client/src/main.tsx +2 -2
  116. package/extensions/dashboard-client/src/memory-map-layout.ts +1 -2
  117. package/extensions/dashboard-client/src/styles/index.css +79 -0
  118. package/extensions/dashboard-client/src/tabs/CacheTab.tsx +33 -26
  119. package/extensions/dashboard-client/src/tabs/EventsTab.tsx +22 -7
  120. package/extensions/dashboard-client/src/tabs/HealthTab.tsx +77 -46
  121. package/extensions/dashboard-client/src/tabs/MaintenanceTab/ActionsCard.tsx +60 -50
  122. package/extensions/dashboard-client/src/tabs/MaintenanceTab/DbStatsCard.tsx +61 -35
  123. package/extensions/dashboard-client/src/tabs/MaintenanceTab/DebugBundleCard.tsx +34 -23
  124. package/extensions/dashboard-client/src/tabs/MaintenanceTab/HealthMitigationCard.tsx +62 -43
  125. package/extensions/dashboard-client/src/tabs/MaintenanceTab/SchemaHealthCard.tsx +53 -32
  126. package/extensions/dashboard-client/src/tabs/MaintenanceTab.tsx +2 -2
  127. package/extensions/dashboard-client/src/tabs/MemoryMapTab/MemoryMapView.tsx +29 -46
  128. package/extensions/dashboard-client/src/tabs/MemoryMapTab/RaptorTreeView.tsx +10 -34
  129. package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +14 -34
  130. package/extensions/dashboard-client/src/tabs/MetricsTab.tsx +5 -2
  131. package/extensions/dashboard-client/src/tabs/OverviewTab.tsx +11 -5
  132. package/extensions/dashboard-client/src/tabs/ReposTab.tsx +5 -5
  133. package/extensions/dashboard-client/src/tabs/SessionsTab.tsx +47 -33
  134. package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderSetup.tsx +6 -2
  135. package/extensions/dashboard-client/src/tabs/SetupTab/RagHealthCard.tsx +78 -0
  136. package/extensions/dashboard-client/src/tabs/SetupTab/SettingsPanel.tsx +198 -0
  137. package/extensions/dashboard-client/src/tabs/SetupTab/SettingsSection.tsx +50 -0
  138. package/extensions/dashboard-client/src/tabs/SetupTab.tsx +21 -59
  139. package/extensions/dashboard-client/src/tabs/TopicsTab.tsx +75 -40
  140. package/extensions/dashboard-client/src/tabs/TurnsTab.tsx +150 -112
  141. package/extensions/dashboard-client/src/tabs/WikiTab/TopicEvolutionGraph.tsx +274 -0
  142. package/extensions/dashboard-client/src/tabs/WikiTab/TopicEvolutionView.tsx +71 -0
  143. package/extensions/dashboard-client/src/tabs/WikiTab/TopicTimeline.tsx +103 -0
  144. package/extensions/dashboard-client/src/tabs/WikiTab/WikiPage.tsx +213 -0
  145. package/extensions/dashboard-client/src/tabs/WikiTab/WikiPageControls.tsx +188 -0
  146. package/extensions/dashboard-client/src/tabs/WikiTab.tsx +222 -0
  147. package/extensions/dashboard-client/src/tabs/registry.ts +57 -0
  148. package/extensions/dashboard-client/src/utils/cn.ts +7 -0
  149. package/extensions/dashboard-client/tailwind.config.js +46 -0
  150. package/extensions/dashboard-server/api-contracts/core.ts +59 -0
  151. package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +79 -0
  152. package/extensions/dashboard-server/api-contracts/index.ts +48 -2
  153. package/extensions/dashboard-server/api-contracts/rag-metrics.ts +22 -0
  154. package/extensions/dashboard-server/api-contracts/rag-settings.ts +30 -20
  155. package/extensions/dashboard-server/api-contracts/wiki.ts +138 -0
  156. package/extensions/dashboard-server/routes-rag-metrics.ts +82 -0
  157. package/extensions/dashboard-server/routes-rag-settings-helpers.ts +314 -0
  158. package/extensions/dashboard-server/routes-rag-settings.ts +147 -97
  159. package/extensions/dashboard-server/routes-wiki-helpers.ts +126 -0
  160. package/extensions/dashboard-server/routes-wiki.ts +376 -0
  161. package/extensions/dashboard-server/routes.ts +2 -0
  162. package/extensions/dashboard-server/server.ts +18 -2
  163. package/extensions/mega-events/context-handler/afterCompact.ts +44 -1
  164. package/extensions/mega-pipeline/recall.ts +31 -0
  165. package/extensions/mega-turn-store.ts +13 -0
  166. package/package.json +1 -1
  167. package/src/config/turns.ts +17 -0
  168. package/src/config.ts +3 -0
  169. package/src/recall/async.ts +10 -1
  170. package/src/recall/format.ts +15 -2
  171. package/src/recall/hydeTelemetry.ts +44 -0
  172. package/src/recall/sync.ts +34 -2
  173. package/src/recall/types.ts +36 -0
  174. package/src/store/turns/hydeStore.ts +82 -0
  175. package/src/store/turns/index.ts +4 -0
  176. package/src/store/turns/schema.ts +126 -1
  177. package/src/store/turns/sqlite-store/rows.ts +25 -0
  178. package/src/store/turns/sqlite-store/write.ts +20 -2
  179. package/src/store/turns/types.ts +33 -0
  180. package/src/topics/durability.ts +231 -0
  181. package/src/topics/index.ts +7 -1
  182. package/src/topics/store.ts +35 -9
  183. package/src/wiki/curation-helpers.ts +140 -0
  184. package/src/wiki/curation.ts +217 -0
  185. package/src/wiki/incremental.ts +164 -0
  186. package/src/wiki/index.ts +13 -0
  187. package/extensions/dashboard-client/dist/assets/CacheTab-Cqnc7lMg.js +0 -2
  188. package/extensions/dashboard-client/dist/assets/CacheTab-Cqnc7lMg.js.map +0 -1
  189. package/extensions/dashboard-client/dist/assets/EventsTab-tYk3VFAp.js +0 -2
  190. package/extensions/dashboard-client/dist/assets/EventsTab-tYk3VFAp.js.map +0 -1
  191. package/extensions/dashboard-client/dist/assets/HealthTab-D2_kjgxw.js +0 -2
  192. package/extensions/dashboard-client/dist/assets/HealthTab-D2_kjgxw.js.map +0 -1
  193. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Bc-uPYrz.js +0 -2
  194. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Bc-uPYrz.js.map +0 -1
  195. package/extensions/dashboard-client/dist/assets/MemoryMapTab-D1V9g_NC.js +0 -2
  196. package/extensions/dashboard-client/dist/assets/MemoryMapTab-D1V9g_NC.js.map +0 -1
  197. package/extensions/dashboard-client/dist/assets/MetricsTab-DT_XfL96.js +0 -2
  198. package/extensions/dashboard-client/dist/assets/MetricsTab-DT_XfL96.js.map +0 -1
  199. package/extensions/dashboard-client/dist/assets/OverviewTab-BY6KZCme.js +0 -2
  200. package/extensions/dashboard-client/dist/assets/OverviewTab-BY6KZCme.js.map +0 -1
  201. package/extensions/dashboard-client/dist/assets/ReposTab-CeS-tpDM.js +0 -2
  202. package/extensions/dashboard-client/dist/assets/ReposTab-CeS-tpDM.js.map +0 -1
  203. package/extensions/dashboard-client/dist/assets/SessionsTab-C30AwyoF.js +0 -70
  204. package/extensions/dashboard-client/dist/assets/SetupTab-BJUHOVK2.js +0 -2
  205. package/extensions/dashboard-client/dist/assets/SetupTab-BJUHOVK2.js.map +0 -1
  206. package/extensions/dashboard-client/dist/assets/TimeSavedCard-p55K1tFP.js +0 -2
  207. package/extensions/dashboard-client/dist/assets/TimeSavedCard-p55K1tFP.js.map +0 -1
  208. package/extensions/dashboard-client/dist/assets/TopicsTab-D8FqyAbK.js +0 -2
  209. package/extensions/dashboard-client/dist/assets/TopicsTab-D8FqyAbK.js.map +0 -1
  210. package/extensions/dashboard-client/dist/assets/TurnsTab-ju-nXDys.js +0 -2
  211. package/extensions/dashboard-client/dist/assets/TurnsTab-ju-nXDys.js.map +0 -1
  212. package/extensions/dashboard-client/dist/assets/index-BEj1Zahy.js +0 -42
  213. package/extensions/dashboard-client/dist/assets/index-BEj1Zahy.js.map +0 -1
  214. package/extensions/dashboard-client/dist/assets/index-CUNn22iV.css +0 -1
  215. package/extensions/dashboard-client/src/styles/metrics-extra.css +0 -69
  216. package/extensions/dashboard-client/src/styles/turns.css +0 -142
  217. package/extensions/dashboard-client/src/tabs/SetupTab/RagSettingsCard.tsx +0 -160
@@ -0,0 +1,103 @@
1
+ /**
2
+ * dashboard-client/src/tabs/WikiTab/TopicTimeline.tsx — per-topic timeline.
3
+ *
4
+ * Renders topic memory-addition buckets as a lightweight SVG bar strip (no
5
+ * chart library needed). X = time bucket, bar height = memories added.
6
+ * Fetches GET /api/wiki/topic/:topicId/timeline.
7
+ *
8
+ * Styling: Tailwind + shadcn (Card). No legacy CSS classes.
9
+ */
10
+
11
+ import type React from "react";
12
+ import { useCallback } from "react";
13
+ import { useApi } from "../../hooks/useApi";
14
+ import { fetchTopicTimeline } from "../../api/client";
15
+ import type { TopicTimelineResponse } from "@contracts";
16
+
17
+ const W = 720;
18
+ const H = 56;
19
+ const BAR_W = 10;
20
+ const GAP = 3;
21
+
22
+ interface Props {
23
+ topicId: string;
24
+ }
25
+
26
+ function fmtBucket(ts: number): string {
27
+ if (!ts) return "—";
28
+ return new Date(ts).toLocaleDateString();
29
+ }
30
+
31
+ export default function TopicTimeline({ topicId }: Props): React.ReactElement {
32
+ const { data, error } = useApi<TopicTimelineResponse>(
33
+ useCallback(() => fetchTopicTimeline(topicId), [topicId]),
34
+ {},
35
+ );
36
+
37
+ if (error) {
38
+ return (
39
+ <div className="rounded-lg border border-border bg-bg-card p-4 text-sm text-muted-foreground">
40
+ Timeline unavailable.
41
+ </div>
42
+ );
43
+ }
44
+ if (!data) {
45
+ return (
46
+ <div className="rounded-lg border border-border bg-bg-card p-4 text-sm text-muted-foreground">
47
+ Loading timeline…
48
+ </div>
49
+ );
50
+ }
51
+
52
+ const { buckets, total } = data;
53
+ const max = Math.max(...buckets.map((b) => b.count), 1);
54
+ const totalWidth = buckets.length * (BAR_W + GAP);
55
+ const scale = Math.min(1, (W - 24) / Math.max(totalWidth, 1));
56
+
57
+ return (
58
+ <div className="rounded-lg border border-border bg-bg-card p-4">
59
+ <h3 className="mb-2 font-heading text-sm font-semibold text-muted-foreground">
60
+ Memory timeline{" "}
61
+ <span className="font-normal text-xs">
62
+ ({total} memory{total !== 1 ? "ies" : "y"} · daily buckets)
63
+ </span>
64
+ </h3>
65
+ {buckets.length === 0 ? (
66
+ <p className="text-sm text-muted-foreground">No timeline data yet.</p>
67
+ ) : (
68
+ <>
69
+ <svg
70
+ width="100%"
71
+ height={H}
72
+ viewBox={`0 0 ${Math.max(W, totalWidth * scale)} ${H}`}
73
+ role="img"
74
+ aria-label="Memory additions over time"
75
+ >
76
+ {buckets.map((b, i) => {
77
+ const x = i * (BAR_W + GAP) * scale;
78
+ const h = Math.max(3, (b.count / max) * (H - 20));
79
+ return (
80
+ <rect
81
+ key={`${b.bucket}-${i}`}
82
+ x={x}
83
+ y={H - 6 - h}
84
+ width={Math.max(2, BAR_W * scale)}
85
+ height={h}
86
+ rx={2}
87
+ fill="hsl(var(--primary))"
88
+ opacity={0.85}
89
+ >
90
+ <title>{`${fmtBucket(b.bucket)}: ${b.count}`}</title>
91
+ </rect>
92
+ );
93
+ })}
94
+ </svg>
95
+ <p className="mt-1 text-xs text-muted-foreground">
96
+ First display: {fmtBucket(buckets[0].bucket)} · last:
97
+ {fmtBucket(buckets[buckets.length - 1].bucket)}
98
+ </p>
99
+ </>
100
+ )}
101
+ </div>
102
+ );
103
+ }
@@ -0,0 +1,213 @@
1
+ /**
2
+ * dashboard-client/src/tabs/WikiTab/WikiPage.tsx — single wiki topic page.
3
+ *
4
+ * Renders the extractive summary, key member memories, the member provenance
5
+ * table (memory → session/assigned-at/method), related topics (resolved from
6
+ * the wiki index), and the per-topic memory timeline. Mutations
7
+ * (rename/merge/split) delegate to WikiPageControls; on mutation success the
8
+ * page + index are refetched.
9
+ *
10
+ * Styling: Tailwind + shadcn (Card, Badge, Button) — no legacy CSS.
11
+ */
12
+
13
+ import type React from "react";
14
+ import { useCallback, useMemo, useState } from "react";
15
+ import { useApi } from "../../hooks/useApi";
16
+ import { fetchWikiTopic, fetchWikiIndex } from "../../api/client";
17
+ import type { WikiPageResponse, WikiIndexResponse } from "@contracts";
18
+ import { Badge } from "../../components/ui/badge";
19
+ import { Button } from "../../components/ui/button";
20
+ import { Card, CardContent } from "../../components/ui/card";
21
+ import WikiPageControls from "./WikiPageControls";
22
+ import TopicTimeline from "./TopicTimeline";
23
+
24
+ interface WikiPageProps {
25
+ topicId: string;
26
+ onBack: () => void;
27
+ }
28
+
29
+ function fmtTs(ms: number): string {
30
+ if (!ms) return "—";
31
+ return new Date(ms).toLocaleString();
32
+ }
33
+
34
+ const METHOD_LABELS: Record<string, string> = {
35
+ "kmeans+tfidf": "kmeans",
36
+ merge: "merge",
37
+ split: "split",
38
+ manual: "manual",
39
+ };
40
+
41
+ export default function WikiPage({
42
+ topicId,
43
+ onBack,
44
+ }: WikiPageProps): React.ReactElement {
45
+ const [dirty, setDirty] = useState(0);
46
+ const bump = useCallback(() => setDirty((d) => d + 1), []);
47
+
48
+ const { data: page, error, loading } = useApi<WikiPageResponse>(
49
+ useCallback(() => fetchWikiTopic(topicId), [topicId, dirty]),
50
+ {},
51
+ );
52
+ const { data: index } = useApi<WikiIndexResponse>(
53
+ useCallback(() => fetchWikiIndex(), []),
54
+ {},
55
+ );
56
+
57
+ const labelOf = useMemo(() => {
58
+ const map = new Map<string, string>();
59
+ for (const t of index?.topics ?? []) map.set(t.id, t.label);
60
+ return map;
61
+ }, [index]);
62
+
63
+ if (error && !page) {
64
+ return (
65
+ <div className="rounded-lg border border-border bg-bg-card p-6 text-sm text-danger">
66
+ Error loading topic: {error.message}
67
+ </div>
68
+ );
69
+ }
70
+ if (loading && !page) {
71
+ return (
72
+ <div className="rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground">
73
+ Loading topic…
74
+ </div>
75
+ );
76
+ }
77
+ if (!page) {
78
+ return (
79
+ <div className="rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground">
80
+ No topic data available.
81
+ </div>
82
+ );
83
+ }
84
+
85
+ const otherTopics = (index?.topics ?? [])
86
+ .filter((t) => t.id !== topicId)
87
+ .map((t) => ({ id: t.id, label: t.label }));
88
+
89
+ return (
90
+ <div className="flex flex-col gap-4">
91
+ <div className="flex items-center justify-between">
92
+ <div className="flex items-center gap-2">
93
+ <Button variant="outline" size="sm" onClick={onBack}>
94
+ ← Wiki
95
+ </Button>
96
+ <h2 className="font-heading text-lg font-semibold">
97
+ {page.topic.label}
98
+ </h2>
99
+ {page.topic.edited && <Badge variant="outline">edited</Badge>}
100
+ <Badge variant="default">
101
+ {page.provenance.length} memories
102
+ </Badge>
103
+ </div>
104
+ <WikiPageControls
105
+ topicId={topicId}
106
+ others={otherTopics}
107
+ members={page.provenance}
108
+ onMutated={bump}
109
+ />
110
+ </div>
111
+
112
+ {page.summary && (
113
+ <Card>
114
+ <CardContent>
115
+ <h3 className="mb-1 font-heading text-sm font-semibold text-muted-foreground">
116
+ Summary
117
+ </h3>
118
+ <p className="text-sm leading-relaxed text-foreground/90">
119
+ {page.summary}
120
+ </p>
121
+ </CardContent>
122
+ </Card>
123
+ )}
124
+
125
+ {page.keyMemories.length > 0 && (
126
+ <Card>
127
+ <CardContent>
128
+ <h3 className="mb-2 font-heading text-sm font-semibold text-muted-foreground">
129
+ Key memories
130
+ </h3>
131
+ <ul className="flex flex-col gap-2">
132
+ {page.keyMemories.map((m) => (
133
+ <li
134
+ key={m.memoryId}
135
+ className="rounded-md border border-border/60 bg-bg-elevated/40 p-3 text-sm"
136
+ >
137
+ <p className="line-clamp-3 text-foreground/90">{m.content}</p>
138
+ <p className="mt-1 text-xs text-muted-foreground">
139
+ {m.memoryId} · {fmtTs(m.timestamp)}
140
+ </p>
141
+ </li>
142
+ ))}
143
+ </ul>
144
+ </CardContent>
145
+ </Card>
146
+ )}
147
+
148
+ <TopicTimeline topicId={topicId} />
149
+
150
+ {page.provenance.length > 0 && (
151
+ <Card>
152
+ <CardContent>
153
+ <h3 className="mb-2 font-heading text-sm font-semibold text-muted-foreground">
154
+ Provenance
155
+ </h3>
156
+ <div className="max-h-72 overflow-auto rounded-md border border-border/60">
157
+ <table className="w-full text-sm">
158
+ <thead className="sticky top-0 bg-bg-card">
159
+ <tr className="border-b border-border text-left text-xs uppercase text-muted-foreground">
160
+ <th className="px-3 py-2 font-medium">Memory</th>
161
+ <th className="px-3 py-2 font-medium">Session</th>
162
+ <th className="px-3 py-2 font-medium">Assigned</th>
163
+ <th className="px-3 py-2 font-medium">Method</th>
164
+ </tr>
165
+ </thead>
166
+ <tbody>
167
+ {page.provenance.map((p) => (
168
+ <tr
169
+ key={p.memoryId}
170
+ className="border-b border-border/40 last:border-0"
171
+ >
172
+ <td className="px-3 py-1.5 font-mono text-xs">
173
+ {p.memoryId}
174
+ </td>
175
+ <td className="px-3 py-1.5 text-xs text-muted-foreground">
176
+ {p.sessionId || "unknown"}
177
+ </td>
178
+ <td className="px-3 py-1.5 text-xs text-muted-foreground">
179
+ {fmtTs(p.assignedAt)}
180
+ </td>
181
+ <td className="px-3 py-1.5">
182
+ <Badge variant="outline">
183
+ {METHOD_LABELS[p.method] ?? p.method}
184
+ </Badge>
185
+ </td>
186
+ </tr>
187
+ ))}
188
+ </tbody>
189
+ </table>
190
+ </div>
191
+ </CardContent>
192
+ </Card>
193
+ )}
194
+
195
+ {page.relatedTopicIds.length > 0 && (
196
+ <Card>
197
+ <CardContent>
198
+ <h3 className="mb-2 font-heading text-sm font-semibold text-muted-foreground">
199
+ Related topics
200
+ </h3>
201
+ <div className="flex flex-wrap gap-2">
202
+ {page.relatedTopicIds.map((id) => (
203
+ <Badge key={id} variant="accent">
204
+ {labelOf.get(id) ?? id}
205
+ </Badge>
206
+ ))}
207
+ </div>
208
+ </CardContent>
209
+ </Card>
210
+ )}
211
+ </div>
212
+ );
213
+ }
@@ -0,0 +1,188 @@
1
+ /**
2
+ * dashboard-client/src/tabs/WikiTab/WikiPageControls.tsx — rename/merge/split.
3
+ *
4
+ * Controlled dialogs for the three curation mutations. Non-destructive: on
5
+ * success the parent refetches and paints the returned CurationResult; on
6
+ * failure the pre-mutation snapshot stays (no local partial apply).
7
+ *
8
+ * Styling: Tailwind + shadcn (Button). No legacy CSS classes.
9
+ */
10
+
11
+ import type React from "react";
12
+ import { useState } from "react";
13
+ import { renameTopic, mergeTopics, splitTopic } from "../../api/client";
14
+ import type { MemoryProvenance } from "@contracts";
15
+ import { Button } from "../../components/ui/button";
16
+
17
+ interface ControlsProps {
18
+ topicId: string;
19
+ others: Array<{ id: string; label: string }>;
20
+ members: MemoryProvenance[];
21
+ onMutated: () => void;
22
+ }
23
+
24
+ type Mode = "rename" | "merge" | "split" | null;
25
+
26
+ function memberSnippet(id: string, method: string): string {
27
+ const base = id.length > 14 ? id.slice(0, 14) + "…" : id;
28
+ return method ? `${base} (${method})` : base;
29
+ }
30
+
31
+ export default function WikiPageControls({
32
+ topicId,
33
+ others,
34
+ members,
35
+ onMutated,
36
+ }: ControlsProps): React.ReactElement {
37
+ const [mode, setMode] = useState<Mode>(null);
38
+ const [label, setLabel] = useState("");
39
+ const [target, setTarget] = useState("");
40
+ const [picked, setPicked] = useState<string[]>([]);
41
+ const [busy, setBusy] = useState(false);
42
+ const [error, setError] = useState<string | null>(null);
43
+
44
+ function open(m: Exclude<Mode, null>): void {
45
+ setMode(m);
46
+ setError(null);
47
+ setLabel("");
48
+ setTarget("");
49
+ setPicked([]);
50
+ }
51
+
52
+ async function submit(): Promise<void> {
53
+ setBusy(true);
54
+ setError(null);
55
+ try {
56
+ if (mode === "rename") {
57
+ await renameTopic(topicId, label);
58
+ } else if (mode === "merge") {
59
+ if (!target) {
60
+ setError("Pick a target topic.");
61
+ setBusy(false);
62
+ return;
63
+ }
64
+ await mergeTopics(topicId, target);
65
+ } else if (mode === "split") {
66
+ if (picked.length === 0) {
67
+ setError("Pick at least one memory.");
68
+ setBusy(false);
69
+ return;
70
+ }
71
+ await splitTopic(topicId, picked);
72
+ }
73
+ setMode(null);
74
+ onMutated();
75
+ } catch (e) {
76
+ setError(e instanceof Error ? e.message : String(e));
77
+ } finally {
78
+ setBusy(false);
79
+ }
80
+ }
81
+
82
+ return (
83
+ <>
84
+ <div className="flex gap-2">
85
+ <Button variant="outline" size="sm" onClick={() => open("rename")}>
86
+ Rename
87
+ </Button>
88
+ <Button variant="outline" size="sm" onClick={() => open("merge")}>
89
+ Merge into…
90
+ </Button>
91
+ <Button variant="outline" size="sm" onClick={() => open("split")}>
92
+ Split
93
+ </Button>
94
+ </div>
95
+
96
+ {mode && (
97
+ <div
98
+ className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4"
99
+ onClick={() => setMode(null)}
100
+ >
101
+ <div
102
+ className="w-full max-w-md rounded-lg border border-border bg-bg-card p-5 shadow-xl"
103
+ role="dialog"
104
+ aria-modal="true"
105
+ aria-label={mode}
106
+ onClick={(e) => e.stopPropagation()}
107
+ >
108
+ <h3 className="mb-3 font-heading text-base font-semibold text-foreground">
109
+ {mode === "rename"
110
+ ? "Rename topic"
111
+ : mode === "merge"
112
+ ? "Merge into…"
113
+ : "Split topic"}
114
+ </h3>
115
+
116
+ {mode === "rename" && (
117
+ <input
118
+ type="text"
119
+ value={label}
120
+ placeholder="New label (empty = back to auto)"
121
+ aria-label="New topic label"
122
+ autoFocus
123
+ onChange={(e) => setLabel(e.target.value)}
124
+ className="w-full rounded-md border border-border bg-bg-elevated/50 px-3 py-2 text-sm outline-none transition-colors focus:border-primary"
125
+ />
126
+ )}
127
+
128
+ {mode === "merge" && (
129
+ <select
130
+ value={target}
131
+ aria-label="Target topic"
132
+ onChange={(e) => setTarget(e.target.value)}
133
+ className="w-full rounded-md border border-border bg-bg-elevated/50 px-3 py-2 text-sm outline-none transition-colors focus:border-primary"
134
+ >
135
+ <option value="">Pick a target topic…</option>
136
+ {others.map((o) => (
137
+ <option key={o.id} value={o.id}>
138
+ {o.label}
139
+ </option>
140
+ ))}
141
+ </select>
142
+ )}
143
+
144
+ {mode === "split" && (
145
+ <div className="max-h-56 overflow-auto rounded-md border border-border/60">
146
+ {members.map((m) => (
147
+ <label
148
+ key={m.memoryId}
149
+ className="flex cursor-pointer items-center gap-2 border-b border-border/40 px-3 py-2 text-sm last:border-0 hover:bg-bg-elevated/40"
150
+ >
151
+ <input
152
+ type="checkbox"
153
+ checked={picked.includes(m.memoryId)}
154
+ onChange={(e) => {
155
+ setPicked((prev) =>
156
+ e.target.checked
157
+ ? [...prev, m.memoryId]
158
+ : prev.filter((x) => x !== m.memoryId),
159
+ );
160
+ }}
161
+ className="accent-primary"
162
+ />
163
+ <span className="font-mono text-xs text-muted-foreground">
164
+ {memberSnippet(m.memoryId, m.method)}
165
+ </span>
166
+ </label>
167
+ ))}
168
+ </div>
169
+ )}
170
+
171
+ {error && (
172
+ <p className="mt-3 text-sm text-danger">{error}</p>
173
+ )}
174
+
175
+ <div className="mt-4 flex justify-end gap-2">
176
+ <Button variant="ghost" size="sm" onClick={() => setMode(null)}>
177
+ Cancel
178
+ </Button>
179
+ <Button size="sm" onClick={submit} disabled={busy}>
180
+ {busy ? "…" : "Confirm"}
181
+ </Button>
182
+ </div>
183
+ </div>
184
+ </div>
185
+ )}
186
+ </>
187
+ );
188
+ }