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.
- package/README.md +11 -8
- package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +50 -0
- package/dist/extensions/dashboard-server/api-contracts/rag-metrics.js +1 -0
- package/dist/extensions/dashboard-server/api-contracts/rag-settings.js +3 -3
- package/dist/extensions/dashboard-server/api-contracts/wiki.js +1 -0
- package/dist/extensions/dashboard-server/routes-rag-metrics.js +60 -0
- package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +167 -0
- package/dist/extensions/dashboard-server/routes-rag-settings.js +136 -77
- package/dist/extensions/dashboard-server/routes-wiki-helpers.js +97 -0
- package/dist/extensions/dashboard-server/routes-wiki.js +330 -0
- package/dist/extensions/dashboard-server/routes.js +2 -0
- package/dist/extensions/dashboard-server/server.js +17 -3
- package/dist/extensions/mega-events/context-handler/afterCompact.js +35 -2
- package/dist/extensions/mega-pipeline/recall.js +31 -0
- package/dist/extensions/mega-turn-store.js +2 -1
- package/dist/src/config/turns.js +10 -0
- package/dist/src/config.js +2 -0
- package/dist/src/recall/async.js +10 -1
- package/dist/src/recall/format.js +10 -1
- package/dist/src/recall/hydeTelemetry.js +22 -0
- package/dist/src/recall/sync.js +28 -2
- package/dist/src/store/turns/hydeStore.js +32 -0
- package/dist/src/store/turns/index.js +1 -0
- package/dist/src/store/turns/schema.js +102 -1
- package/dist/src/store/turns/sqlite-store/rows.js +23 -0
- package/dist/src/store/turns/sqlite-store/write.js +7 -3
- package/dist/src/topics/durability.js +187 -0
- package/dist/src/topics/index.js +2 -1
- package/dist/src/topics/store.js +28 -9
- package/dist/src/wiki/curation-helpers.js +71 -0
- package/dist/src/wiki/curation.js +147 -0
- package/dist/src/wiki/incremental.js +126 -0
- package/dist/src/wiki/index.js +5 -0
- package/extensions/dashboard-client/dist/assets/AreaChart-KAmp_6bL.js +70 -0
- package/extensions/dashboard-client/dist/assets/{SessionsTab-C30AwyoF.js.map → AreaChart-KAmp_6bL.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/CacheTab-Bk-gm-R0.js +2 -0
- package/extensions/dashboard-client/dist/assets/CacheTab-Bk-gm-R0.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/EventsTab-Cyylo0of.js +2 -0
- package/extensions/dashboard-client/dist/assets/EventsTab-Cyylo0of.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/HealthTab-D-IzmV29.js +2 -0
- package/extensions/dashboard-client/dist/assets/HealthTab-D-IzmV29.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-CrlkCFVj.js +2 -0
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-CrlkCFVj.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-DHUdZ7RR.js +2 -0
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-DHUdZ7RR.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/MetricsTab-B7I4HWVg.js +2 -0
- package/extensions/dashboard-client/dist/assets/MetricsTab-B7I4HWVg.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/OverviewTab-K6j4ZI7u.js +2 -0
- package/extensions/dashboard-client/dist/assets/OverviewTab-K6j4ZI7u.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/ReposTab-BSRWhn56.js +2 -0
- package/extensions/dashboard-client/dist/assets/ReposTab-BSRWhn56.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/SessionsTab-BXdizHN1.js +2 -0
- package/extensions/dashboard-client/dist/assets/SessionsTab-BXdizHN1.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-CAgyfqY3.js +2 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-CAgyfqY3.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-DwgCzPD9.js +2 -0
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-DwgCzPD9.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/TurnsTab-CVKcStBp.js +5 -0
- package/extensions/dashboard-client/dist/assets/TurnsTab-CVKcStBp.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/WikiTab-BNohxTbs.js +2 -0
- package/extensions/dashboard-client/dist/assets/WikiTab-BNohxTbs.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/button-B4A-_dCA.js +2 -0
- package/extensions/dashboard-client/dist/assets/button-B4A-_dCA.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/card-Cjn7Ifoe.js +2 -0
- package/extensions/dashboard-client/dist/assets/card-Cjn7Ifoe.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/index-CQjCLvv7.js +137 -0
- package/extensions/dashboard-client/dist/assets/index-CQjCLvv7.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/index-CUA6EDRF.css +1 -0
- package/extensions/dashboard-client/dist/assets/switch-CcdXX0Zm.js +2 -0
- package/extensions/dashboard-client/dist/assets/switch-CcdXX0Zm.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/toggle-KdBnFLu3.js +2 -0
- package/extensions/dashboard-client/dist/assets/toggle-KdBnFLu3.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{useSSE-p1pOI_-P.js → useSSE-B6t44vzc.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-p1pOI_-P.js.map → useSSE-B6t44vzc.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +2 -2
- package/extensions/dashboard-client/package-lock.json +1995 -191
- package/extensions/dashboard-client/package.json +12 -1
- package/extensions/dashboard-client/postcss.config.js +6 -0
- package/extensions/dashboard-client/src/App.tsx +127 -88
- package/extensions/dashboard-client/src/api/client.ts +109 -10
- package/extensions/dashboard-client/src/components/ActiveReposTable.tsx +15 -7
- package/extensions/dashboard-client/src/components/ActiveSessionsTable.tsx +67 -55
- package/extensions/dashboard-client/src/components/CacheHitRateTrendCard.tsx +17 -13
- package/extensions/dashboard-client/src/components/CacheHitsCard.tsx +35 -30
- package/extensions/dashboard-client/src/components/CacheStatusPerModel.tsx +33 -28
- package/extensions/dashboard-client/src/components/EventCategoryFilter.tsx +5 -6
- package/extensions/dashboard-client/src/components/EventStream.tsx +17 -13
- package/extensions/dashboard-client/src/components/HydeDetailPanel.tsx +173 -0
- package/extensions/dashboard-client/src/components/LegendCard.tsx +16 -9
- package/extensions/dashboard-client/src/components/PerfCards.tsx +28 -20
- package/extensions/dashboard-client/src/components/PerfChart.tsx +9 -6
- package/extensions/dashboard-client/src/components/ProviderCacheCard.tsx +15 -7
- package/extensions/dashboard-client/src/components/RagDashboard.tsx +253 -0
- package/extensions/dashboard-client/src/components/RepoAllSessionsCard.tsx +15 -10
- package/extensions/dashboard-client/src/components/RepoContextStack.tsx +24 -16
- package/extensions/dashboard-client/src/components/RepoDetailModal.tsx +52 -49
- package/extensions/dashboard-client/src/components/RepoTable.tsx +26 -17
- package/extensions/dashboard-client/src/components/SessionContextGauges.tsx +11 -14
- package/extensions/dashboard-client/src/components/SessionInfo.tsx +20 -15
- package/extensions/dashboard-client/src/components/SessionsMemoryChart.tsx +8 -5
- package/extensions/dashboard-client/src/components/SummaryTiles.tsx +26 -17
- package/extensions/dashboard-client/src/components/TabBar.tsx +1 -3
- package/extensions/dashboard-client/src/components/layout/AppShell.tsx +42 -0
- package/extensions/dashboard-client/src/components/layout/BottomBar.tsx +88 -0
- package/extensions/dashboard-client/src/components/layout/Sidebar.tsx +83 -0
- package/extensions/dashboard-client/src/components/ui/badge.tsx +30 -0
- package/extensions/dashboard-client/src/components/ui/button.tsx +44 -0
- package/extensions/dashboard-client/src/components/ui/card.tsx +32 -0
- package/extensions/dashboard-client/src/components/ui/select.tsx +64 -0
- package/extensions/dashboard-client/src/components/ui/switch.tsx +26 -0
- package/extensions/dashboard-client/src/components/ui/tabs.tsx +36 -0
- package/extensions/dashboard-client/src/components/ui/toggle.tsx +28 -0
- package/extensions/dashboard-client/src/components/ui/tooltip.tsx +27 -0
- package/extensions/dashboard-client/src/config.ts +10 -0
- package/extensions/dashboard-client/src/main.tsx +2 -2
- package/extensions/dashboard-client/src/memory-map-layout.ts +1 -2
- package/extensions/dashboard-client/src/styles/index.css +79 -0
- package/extensions/dashboard-client/src/tabs/CacheTab.tsx +33 -26
- package/extensions/dashboard-client/src/tabs/EventsTab.tsx +22 -7
- package/extensions/dashboard-client/src/tabs/HealthTab.tsx +77 -46
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/ActionsCard.tsx +60 -50
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/DbStatsCard.tsx +61 -35
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/DebugBundleCard.tsx +34 -23
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/HealthMitigationCard.tsx +62 -43
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/SchemaHealthCard.tsx +53 -32
- package/extensions/dashboard-client/src/tabs/MaintenanceTab.tsx +2 -2
- package/extensions/dashboard-client/src/tabs/MemoryMapTab/MemoryMapView.tsx +29 -46
- package/extensions/dashboard-client/src/tabs/MemoryMapTab/RaptorTreeView.tsx +10 -34
- package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +14 -34
- package/extensions/dashboard-client/src/tabs/MetricsTab.tsx +5 -2
- package/extensions/dashboard-client/src/tabs/OverviewTab.tsx +11 -5
- package/extensions/dashboard-client/src/tabs/ReposTab.tsx +5 -5
- package/extensions/dashboard-client/src/tabs/SessionsTab.tsx +47 -33
- package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderSetup.tsx +6 -2
- package/extensions/dashboard-client/src/tabs/SetupTab/RagHealthCard.tsx +78 -0
- package/extensions/dashboard-client/src/tabs/SetupTab/SettingsPanel.tsx +198 -0
- package/extensions/dashboard-client/src/tabs/SetupTab/SettingsSection.tsx +50 -0
- package/extensions/dashboard-client/src/tabs/SetupTab.tsx +21 -59
- package/extensions/dashboard-client/src/tabs/TopicsTab.tsx +75 -40
- package/extensions/dashboard-client/src/tabs/TurnsTab.tsx +150 -112
- package/extensions/dashboard-client/src/tabs/WikiTab/TopicEvolutionGraph.tsx +274 -0
- package/extensions/dashboard-client/src/tabs/WikiTab/TopicEvolutionView.tsx +71 -0
- package/extensions/dashboard-client/src/tabs/WikiTab/TopicTimeline.tsx +103 -0
- package/extensions/dashboard-client/src/tabs/WikiTab/WikiPage.tsx +213 -0
- package/extensions/dashboard-client/src/tabs/WikiTab/WikiPageControls.tsx +188 -0
- package/extensions/dashboard-client/src/tabs/WikiTab.tsx +222 -0
- package/extensions/dashboard-client/src/tabs/registry.ts +57 -0
- package/extensions/dashboard-client/src/utils/cn.ts +7 -0
- package/extensions/dashboard-client/tailwind.config.js +46 -0
- package/extensions/dashboard-server/api-contracts/core.ts +59 -0
- package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +79 -0
- package/extensions/dashboard-server/api-contracts/index.ts +48 -2
- package/extensions/dashboard-server/api-contracts/rag-metrics.ts +22 -0
- package/extensions/dashboard-server/api-contracts/rag-settings.ts +30 -20
- package/extensions/dashboard-server/api-contracts/wiki.ts +138 -0
- package/extensions/dashboard-server/routes-rag-metrics.ts +82 -0
- package/extensions/dashboard-server/routes-rag-settings-helpers.ts +314 -0
- package/extensions/dashboard-server/routes-rag-settings.ts +147 -97
- package/extensions/dashboard-server/routes-wiki-helpers.ts +126 -0
- package/extensions/dashboard-server/routes-wiki.ts +376 -0
- package/extensions/dashboard-server/routes.ts +2 -0
- package/extensions/dashboard-server/server.ts +18 -2
- package/extensions/mega-events/context-handler/afterCompact.ts +44 -1
- package/extensions/mega-pipeline/recall.ts +31 -0
- package/extensions/mega-turn-store.ts +13 -0
- package/package.json +1 -1
- package/src/config/turns.ts +17 -0
- package/src/config.ts +3 -0
- package/src/recall/async.ts +10 -1
- package/src/recall/format.ts +15 -2
- package/src/recall/hydeTelemetry.ts +44 -0
- package/src/recall/sync.ts +34 -2
- package/src/recall/types.ts +36 -0
- package/src/store/turns/hydeStore.ts +82 -0
- package/src/store/turns/index.ts +4 -0
- package/src/store/turns/schema.ts +126 -1
- package/src/store/turns/sqlite-store/rows.ts +25 -0
- package/src/store/turns/sqlite-store/write.ts +20 -2
- package/src/store/turns/types.ts +33 -0
- package/src/topics/durability.ts +231 -0
- package/src/topics/index.ts +7 -1
- package/src/topics/store.ts +35 -9
- package/src/wiki/curation-helpers.ts +140 -0
- package/src/wiki/curation.ts +217 -0
- package/src/wiki/incremental.ts +164 -0
- package/src/wiki/index.ts +13 -0
- package/extensions/dashboard-client/dist/assets/CacheTab-Cqnc7lMg.js +0 -2
- package/extensions/dashboard-client/dist/assets/CacheTab-Cqnc7lMg.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/EventsTab-tYk3VFAp.js +0 -2
- package/extensions/dashboard-client/dist/assets/EventsTab-tYk3VFAp.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/HealthTab-D2_kjgxw.js +0 -2
- package/extensions/dashboard-client/dist/assets/HealthTab-D2_kjgxw.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-Bc-uPYrz.js +0 -2
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-Bc-uPYrz.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-D1V9g_NC.js +0 -2
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-D1V9g_NC.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/MetricsTab-DT_XfL96.js +0 -2
- package/extensions/dashboard-client/dist/assets/MetricsTab-DT_XfL96.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/OverviewTab-BY6KZCme.js +0 -2
- package/extensions/dashboard-client/dist/assets/OverviewTab-BY6KZCme.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/ReposTab-CeS-tpDM.js +0 -2
- package/extensions/dashboard-client/dist/assets/ReposTab-CeS-tpDM.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/SessionsTab-C30AwyoF.js +0 -70
- package/extensions/dashboard-client/dist/assets/SetupTab-BJUHOVK2.js +0 -2
- package/extensions/dashboard-client/dist/assets/SetupTab-BJUHOVK2.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-p55K1tFP.js +0 -2
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-p55K1tFP.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/TopicsTab-D8FqyAbK.js +0 -2
- package/extensions/dashboard-client/dist/assets/TopicsTab-D8FqyAbK.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/TurnsTab-ju-nXDys.js +0 -2
- package/extensions/dashboard-client/dist/assets/TurnsTab-ju-nXDys.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-BEj1Zahy.js +0 -42
- package/extensions/dashboard-client/dist/assets/index-BEj1Zahy.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-CUNn22iV.css +0 -1
- package/extensions/dashboard-client/src/styles/metrics-extra.css +0 -69
- package/extensions/dashboard-client/src/styles/turns.css +0 -142
- 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
|
+
}
|