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
|
@@ -12,6 +12,9 @@ import type {
|
|
|
12
12
|
MaintenanceActionResult,
|
|
13
13
|
} from "@contracts";
|
|
14
14
|
import { postMaintenanceAction } from "../../api/client";
|
|
15
|
+
import { Badge } from "../../components/ui/badge";
|
|
16
|
+
import { Button } from "../../components/ui/button";
|
|
17
|
+
import { Card, CardHeader, CardTitle } from "../../components/ui/card";
|
|
15
18
|
|
|
16
19
|
// ---------------------------------------------------------------------------
|
|
17
20
|
// Action config
|
|
@@ -120,59 +123,66 @@ export function ActionsCard(): React.ReactElement {
|
|
|
120
123
|
);
|
|
121
124
|
|
|
122
125
|
return (
|
|
123
|
-
<
|
|
124
|
-
<
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
</
|
|
139
|
-
<
|
|
140
|
-
{lastResult && (
|
|
141
|
-
<div
|
|
142
|
-
className={`action-result ${lastResult.success ? "text-ok" : "text-error"}`}
|
|
143
|
-
>
|
|
144
|
-
{lastResult.summary}
|
|
145
|
-
</div>
|
|
146
|
-
)}
|
|
126
|
+
<Card>
|
|
127
|
+
<CardHeader>
|
|
128
|
+
<CardTitle>Actions</CardTitle>
|
|
129
|
+
</CardHeader>
|
|
130
|
+
<div className="grid grid-cols-1 gap-3 lg:grid-cols-2">
|
|
131
|
+
{ACTIONS.map((def) => {
|
|
132
|
+
const isRunning = running === def.key;
|
|
133
|
+
const lastResult = results.find((r) => r.operation === def.key);
|
|
134
|
+
return (
|
|
135
|
+
<div
|
|
136
|
+
key={def.key}
|
|
137
|
+
className="flex items-start justify-between gap-3 rounded-lg border border-border/60 p-3"
|
|
138
|
+
>
|
|
139
|
+
<div className="min-w-0">
|
|
140
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
141
|
+
<span className="font-semibold">{def.label}</span>
|
|
142
|
+
{def.dangerous && <Badge variant="danger">DESTRUCTIVE</Badge>}
|
|
147
143
|
</div>
|
|
148
|
-
<div className="
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
min={1}
|
|
153
|
-
max={365}
|
|
154
|
-
value={days}
|
|
155
|
-
onChange={(e) => setDays(e.target.value)}
|
|
156
|
-
className="days-input"
|
|
157
|
-
disabled={isRunning}
|
|
158
|
-
/>
|
|
159
|
-
)}
|
|
160
|
-
<button
|
|
161
|
-
className={`btn ${def.dangerous ? "btn-danger" : "btn-primary"}`}
|
|
162
|
-
disabled={running !== null}
|
|
163
|
-
onClick={() => {
|
|
164
|
-
if (def.confirm && !window.confirm(def.confirm)) return;
|
|
165
|
-
handleAction(def);
|
|
166
|
-
}}
|
|
144
|
+
<div className="mt-1 text-xs text-muted-foreground">{def.desc}</div>
|
|
145
|
+
{lastResult && (
|
|
146
|
+
<div
|
|
147
|
+
className={`mt-1 text-xs ${lastResult.success ? "text-emerald-400" : "text-red-400"}`}
|
|
167
148
|
>
|
|
168
|
-
{
|
|
169
|
-
</
|
|
170
|
-
|
|
149
|
+
{lastResult.summary}
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
|
+
</div>
|
|
153
|
+
<div className="flex shrink-0 items-center gap-2">
|
|
154
|
+
{def.daysOld && (
|
|
155
|
+
<input
|
|
156
|
+
type="number"
|
|
157
|
+
min={1}
|
|
158
|
+
max={365}
|
|
159
|
+
value={days}
|
|
160
|
+
onChange={(e) => setDays(e.target.value)}
|
|
161
|
+
className="w-16 rounded-md border border-border bg-bg-elevated/50 px-2 py-1 text-sm outline-none focus:border-primary"
|
|
162
|
+
disabled={isRunning}
|
|
163
|
+
/>
|
|
164
|
+
)}
|
|
165
|
+
<Button
|
|
166
|
+
type="button"
|
|
167
|
+
variant={def.dangerous ? "ghost" : "default"}
|
|
168
|
+
className={
|
|
169
|
+
def.dangerous
|
|
170
|
+
? "border border-red-500/50 text-red-400 hover:bg-red-500/10 hover:text-red-300"
|
|
171
|
+
: undefined
|
|
172
|
+
}
|
|
173
|
+
disabled={running !== null}
|
|
174
|
+
onClick={() => {
|
|
175
|
+
if (def.confirm && !window.confirm(def.confirm)) return;
|
|
176
|
+
handleAction(def);
|
|
177
|
+
}}
|
|
178
|
+
>
|
|
179
|
+
{isRunning ? "Running…" : "Run"}
|
|
180
|
+
</Button>
|
|
171
181
|
</div>
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
})}
|
|
175
185
|
</div>
|
|
176
|
-
</
|
|
186
|
+
</Card>
|
|
177
187
|
);
|
|
178
188
|
}
|
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type React from "react";
|
|
8
8
|
import type { DbStatsResponse } from "@contracts";
|
|
9
|
+
import {
|
|
10
|
+
Card,
|
|
11
|
+
CardHeader,
|
|
12
|
+
CardTitle,
|
|
13
|
+
CardContent,
|
|
14
|
+
} from "../../components/ui/card";
|
|
9
15
|
|
|
10
16
|
// ---------------------------------------------------------------------------
|
|
11
17
|
// Format helpers (local — matches html.ts fmtBytes)
|
|
@@ -34,37 +40,47 @@ function FileSizesCard({
|
|
|
34
40
|
}): React.ReactElement {
|
|
35
41
|
const total = files.dbBytes + files.walBytes + files.shmBytes;
|
|
36
42
|
return (
|
|
37
|
-
<div className="
|
|
38
|
-
<div className="
|
|
39
|
-
<table className="
|
|
43
|
+
<div className="mt-3">
|
|
44
|
+
<div className="text-xs font-semibold text-muted-foreground">File Sizes</div>
|
|
45
|
+
<table className="mt-1 w-full border-collapse text-sm">
|
|
40
46
|
<tbody>
|
|
41
|
-
<tr>
|
|
42
|
-
<td>sqlite.db</td>
|
|
43
|
-
<td className="
|
|
47
|
+
<tr className="border-b border-border/50">
|
|
48
|
+
<td className="px-2 py-1">sqlite.db</td>
|
|
49
|
+
<td className="px-2 py-1 text-right font-semibold">
|
|
50
|
+
{fmtBytes(files.dbBytes)}
|
|
51
|
+
</td>
|
|
44
52
|
</tr>
|
|
45
53
|
{files.walBytes > 0 && (
|
|
46
|
-
<tr>
|
|
47
|
-
<td>.wal</td>
|
|
48
|
-
<td className="
|
|
54
|
+
<tr className="border-b border-border/50">
|
|
55
|
+
<td className="px-2 py-1">.wal</td>
|
|
56
|
+
<td className="px-2 py-1 text-right font-semibold">
|
|
57
|
+
{fmtBytes(files.walBytes)}
|
|
58
|
+
</td>
|
|
49
59
|
</tr>
|
|
50
60
|
)}
|
|
51
61
|
{files.shmBytes > 0 && (
|
|
52
|
-
<tr>
|
|
53
|
-
<td>.shm</td>
|
|
54
|
-
<td className="
|
|
62
|
+
<tr className="border-b border-border/50">
|
|
63
|
+
<td className="px-2 py-1">.shm</td>
|
|
64
|
+
<td className="px-2 py-1 text-right font-semibold">
|
|
65
|
+
{fmtBytes(files.shmBytes)}
|
|
66
|
+
</td>
|
|
55
67
|
</tr>
|
|
56
68
|
)}
|
|
57
|
-
<tr>
|
|
58
|
-
<td>Total</td>
|
|
59
|
-
<td className="
|
|
69
|
+
<tr className="border-b border-border/50">
|
|
70
|
+
<td className="px-2 py-1">Total</td>
|
|
71
|
+
<td className="px-2 py-1 text-right font-semibold">
|
|
72
|
+
{fmtBytes(total)}
|
|
73
|
+
</td>
|
|
60
74
|
</tr>
|
|
61
|
-
<tr>
|
|
62
|
-
<td>Page size</td>
|
|
63
|
-
<td className="
|
|
75
|
+
<tr className="border-b border-border/50">
|
|
76
|
+
<td className="px-2 py-1">Page size</td>
|
|
77
|
+
<td className="px-2 py-1 text-right font-semibold">
|
|
78
|
+
{fmtBytes(pageSize)}
|
|
79
|
+
</td>
|
|
64
80
|
</tr>
|
|
65
81
|
<tr>
|
|
66
|
-
<td>Pages</td>
|
|
67
|
-
<td className="
|
|
82
|
+
<td className="px-2 py-1">Pages</td>
|
|
83
|
+
<td className="px-2 py-1 text-right font-semibold">
|
|
68
84
|
{pageCount.toLocaleString()} ({freelistPages.toLocaleString()}{" "}
|
|
69
85
|
free)
|
|
70
86
|
</td>
|
|
@@ -82,22 +98,26 @@ function TableRowCountsCard({
|
|
|
82
98
|
}): React.ReactElement {
|
|
83
99
|
const totalRows = tables.reduce((s, t) => s + Math.max(0, t.rowCount), 0);
|
|
84
100
|
return (
|
|
85
|
-
<div className="
|
|
86
|
-
<div className="
|
|
101
|
+
<div className="mt-3">
|
|
102
|
+
<div className="text-xs font-semibold text-muted-foreground">
|
|
87
103
|
Table Row Counts ({totalRows.toLocaleString()} total)
|
|
88
104
|
</div>
|
|
89
|
-
<table className="
|
|
105
|
+
<table className="mt-1 w-full border-collapse text-sm">
|
|
90
106
|
<thead>
|
|
91
107
|
<tr>
|
|
92
|
-
<th>
|
|
93
|
-
|
|
108
|
+
<th className="border-b border-border px-2 py-1 text-left font-medium text-muted-foreground">
|
|
109
|
+
Table
|
|
110
|
+
</th>
|
|
111
|
+
<th className="border-b border-border px-2 py-1 text-right font-medium text-muted-foreground">
|
|
112
|
+
Rows
|
|
113
|
+
</th>
|
|
94
114
|
</tr>
|
|
95
115
|
</thead>
|
|
96
116
|
<tbody>
|
|
97
117
|
{tables.map((t) => (
|
|
98
|
-
<tr key={t.table}>
|
|
99
|
-
<td>{t.table}</td>
|
|
100
|
-
<td className="
|
|
118
|
+
<tr key={t.table} className="border-b border-border/50">
|
|
119
|
+
<td className="px-2 py-1">{t.table}</td>
|
|
120
|
+
<td className="px-2 py-1 text-right">
|
|
101
121
|
{t.rowCount >= 0 ? t.rowCount.toLocaleString() : "—"}
|
|
102
122
|
</td>
|
|
103
123
|
</tr>
|
|
@@ -118,11 +138,17 @@ export function DbStatsCard({
|
|
|
118
138
|
error: Error | null;
|
|
119
139
|
}): React.ReactElement {
|
|
120
140
|
return (
|
|
121
|
-
<
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
141
|
+
<Card>
|
|
142
|
+
<CardHeader>
|
|
143
|
+
<CardTitle>DB Stats</CardTitle>
|
|
144
|
+
</CardHeader>
|
|
145
|
+
<CardContent>
|
|
146
|
+
{loading && !data && (
|
|
147
|
+
<span className="text-sm text-muted-foreground">Loading…</span>
|
|
148
|
+
)}
|
|
149
|
+
{error && !data && (
|
|
150
|
+
<span className="text-sm text-red-400">{error.message}</span>
|
|
151
|
+
)}
|
|
126
152
|
{data && (
|
|
127
153
|
<>
|
|
128
154
|
<FileSizesCard
|
|
@@ -134,7 +160,7 @@ export function DbStatsCard({
|
|
|
134
160
|
<TableRowCountsCard tables={data.tables} />
|
|
135
161
|
</>
|
|
136
162
|
)}
|
|
137
|
-
</
|
|
138
|
-
</
|
|
163
|
+
</CardContent>
|
|
164
|
+
</Card>
|
|
139
165
|
);
|
|
140
166
|
}
|
|
@@ -9,6 +9,13 @@ import type React from "react";
|
|
|
9
9
|
import { useState, useCallback } from "react";
|
|
10
10
|
import type { DebugBundleResponse } from "@contracts";
|
|
11
11
|
import { fetchDebugBundle } from "../../api/client";
|
|
12
|
+
import { Button } from "../../components/ui/button";
|
|
13
|
+
import {
|
|
14
|
+
Card,
|
|
15
|
+
CardHeader,
|
|
16
|
+
CardTitle,
|
|
17
|
+
CardContent,
|
|
18
|
+
} from "../../components/ui/card";
|
|
12
19
|
|
|
13
20
|
// ---------------------------------------------------------------------------
|
|
14
21
|
// Debug bundle card — gather diagnostic info for bug reports
|
|
@@ -55,55 +62,59 @@ export function DebugBundleCard(): React.ReactElement {
|
|
|
55
62
|
const criticalCount = bundle?.criticalEvents.length ?? 0;
|
|
56
63
|
|
|
57
64
|
return (
|
|
58
|
-
<
|
|
59
|
-
<
|
|
60
|
-
Debug Bundle
|
|
65
|
+
<Card>
|
|
66
|
+
<CardHeader className="flex-row items-center justify-between space-y-0">
|
|
67
|
+
<CardTitle>Debug Bundle</CardTitle>
|
|
61
68
|
{criticalCount > 0 && (
|
|
62
|
-
<span className="text-
|
|
69
|
+
<span className="text-xs text-amber-400">
|
|
63
70
|
{criticalCount} critical event{criticalCount !== 1 ? "s" : ""} found
|
|
64
71
|
</span>
|
|
65
72
|
)}
|
|
66
|
-
</
|
|
67
|
-
<
|
|
68
|
-
<p className="text-muted"
|
|
73
|
+
</CardHeader>
|
|
74
|
+
<CardContent>
|
|
75
|
+
<p className="mb-3 text-sm text-muted-foreground">
|
|
69
76
|
Gather a diagnostic bundle (recent events, config flags, schema health,
|
|
70
77
|
store stats) to attach to a bug report. Critical/compaction events are
|
|
71
78
|
highlighted.
|
|
72
79
|
</p>
|
|
73
|
-
<button onClick={gather} disabled={loading}
|
|
80
|
+
<Button type="button" variant="glass" onClick={gather} disabled={loading}>
|
|
74
81
|
{loading ? "Gathering…" : "Gather Debug Logs"}
|
|
75
|
-
</
|
|
76
|
-
{error && <div className="text-
|
|
82
|
+
</Button>
|
|
83
|
+
{error && <div className="mt-2 text-sm text-red-400">{error}</div>}
|
|
77
84
|
{bundle && showJson && (
|
|
78
85
|
<>
|
|
79
|
-
<div
|
|
80
|
-
<button
|
|
86
|
+
<div className="mb-2 mt-3 flex gap-2">
|
|
87
|
+
<Button type="button" variant="outline" size="sm" onClick={copyToClipboard}>
|
|
81
88
|
Copy to clipboard
|
|
82
|
-
</
|
|
83
|
-
<button onClick={downloadJson}>
|
|
84
|
-
|
|
89
|
+
</Button>
|
|
90
|
+
<Button type="button" variant="outline" size="sm" onClick={downloadJson}>
|
|
91
|
+
Download JSON
|
|
92
|
+
</Button>
|
|
93
|
+
<Button
|
|
94
|
+
type="button"
|
|
95
|
+
variant="outline"
|
|
96
|
+
size="sm"
|
|
85
97
|
onClick={() => setShowJson((v) => !v)}
|
|
86
|
-
style={{ marginLeft: "0.5em" }}
|
|
87
98
|
>
|
|
88
99
|
{showJson ? "Hide" : "Show"}
|
|
89
|
-
</
|
|
100
|
+
</Button>
|
|
90
101
|
</div>
|
|
91
102
|
{criticalCount > 0 && (
|
|
92
|
-
<div className="
|
|
93
|
-
<div className="
|
|
103
|
+
<div className="mt-3">
|
|
104
|
+
<div className="text-xs font-semibold text-amber-400">
|
|
94
105
|
Critical events ({criticalCount})
|
|
95
106
|
</div>
|
|
96
|
-
<pre className="
|
|
107
|
+
<pre className="max-h-48 overflow-auto rounded-md border border-border/60 bg-bg-elevated/40 p-3 font-mono text-xs">
|
|
97
108
|
{JSON.stringify(bundle.criticalEvents, null, 2)}
|
|
98
109
|
</pre>
|
|
99
110
|
</div>
|
|
100
111
|
)}
|
|
101
|
-
<pre className="
|
|
112
|
+
<pre className="mt-3 max-h-96 overflow-auto rounded-md border border-border/60 bg-bg-elevated/40 p-3 font-mono text-xs">
|
|
102
113
|
{JSON.stringify(bundle, null, 2)}
|
|
103
114
|
</pre>
|
|
104
115
|
</>
|
|
105
116
|
)}
|
|
106
|
-
</
|
|
107
|
-
</
|
|
117
|
+
</CardContent>
|
|
118
|
+
</Card>
|
|
108
119
|
);
|
|
109
120
|
}
|
|
@@ -7,6 +7,13 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type React from "react";
|
|
9
9
|
import { useState, useCallback, useEffect } from "react";
|
|
10
|
+
import { Switch } from "../../components/ui/switch";
|
|
11
|
+
import {
|
|
12
|
+
Card,
|
|
13
|
+
CardHeader,
|
|
14
|
+
CardTitle,
|
|
15
|
+
CardContent,
|
|
16
|
+
} from "../../components/ui/card";
|
|
10
17
|
|
|
11
18
|
// ---------------------------------------------------------------------------
|
|
12
19
|
// Health Mitigation card — toggle auto-mitigation for degraded context
|
|
@@ -48,51 +55,63 @@ export function HealthMitigationCard(): React.ReactElement {
|
|
|
48
55
|
}, [mitigate]);
|
|
49
56
|
|
|
50
57
|
return (
|
|
51
|
-
<
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
<Card>
|
|
59
|
+
<CardHeader>
|
|
60
|
+
<CardTitle>Context Health Mitigation</CardTitle>
|
|
61
|
+
</CardHeader>
|
|
62
|
+
<CardContent>
|
|
63
|
+
{loading ? (
|
|
64
|
+
<p className="text-sm text-muted-foreground">Loading…</p>
|
|
65
|
+
) : (
|
|
66
|
+
<>
|
|
67
|
+
<div className="flex items-center gap-2">
|
|
68
|
+
<Switch
|
|
61
69
|
checked={mitigate}
|
|
62
|
-
|
|
70
|
+
onCheckedChange={toggle}
|
|
63
71
|
disabled={saving}
|
|
72
|
+
aria-label="Context health auto-mitigation"
|
|
64
73
|
/>
|
|
65
|
-
<span
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
74
|
+
<span className="text-sm">
|
|
75
|
+
Auto-mitigation {mitigate ? "ON" : "OFF"}
|
|
76
|
+
</span>
|
|
77
|
+
</div>
|
|
78
|
+
<div className="mt-3 space-y-2 text-sm">
|
|
79
|
+
<p>
|
|
80
|
+
When <strong>ON</strong>, the extension automatically intervenes
|
|
81
|
+
when context health degrades:
|
|
82
|
+
</p>
|
|
83
|
+
<ul className="list-disc space-y-1 pl-5">
|
|
84
|
+
<li>
|
|
85
|
+
<strong>Forced compaction</strong> — when the composite health
|
|
86
|
+
score drops below 0.4 (severe drift or garbled output), a
|
|
87
|
+
compaction is triggered to flush the degraded context and
|
|
88
|
+
rebuild from clean checkpoints, even if the context window
|
|
89
|
+
isn't full.
|
|
90
|
+
</li>
|
|
91
|
+
<li>
|
|
92
|
+
<strong>Prefix break</strong> — when KV cache poison is
|
|
93
|
+
detected (score < 0.3), the cached prefix is invalidated to
|
|
94
|
+
force a cache miss on the next turn, bypassing the corrupted KV
|
|
95
|
+
state.
|
|
96
|
+
</li>
|
|
97
|
+
</ul>
|
|
98
|
+
<p className="rounded-md border border-amber-500/30 bg-amber-500/5 p-3 text-amber-400">
|
|
99
|
+
WARNING: both interventions have costs — forced compaction discards
|
|
100
|
+
live context, and prefix breaks kill cache savings. Only enable
|
|
101
|
+
when you're seeing degraded output (garbled text,
|
|
102
|
+
hallucinations, loss of coherence) and the Health tab confirms low
|
|
103
|
+
scores.
|
|
104
|
+
</p>
|
|
105
|
+
<p className="text-muted-foreground">
|
|
106
|
+
Also settable via env var:{" "}
|
|
107
|
+
<code>MEGACOMPACT_CONTEXT_HEALTH_MITIGATE=1</code>. The dashboard
|
|
108
|
+
toggle persists to the store and takes precedence.
|
|
109
|
+
</p>
|
|
110
|
+
{error && <p className="text-sm text-red-400">Error: {error}</p>}
|
|
111
|
+
</div>
|
|
112
|
+
</>
|
|
113
|
+
)}
|
|
114
|
+
</CardContent>
|
|
115
|
+
</Card>
|
|
97
116
|
);
|
|
98
117
|
}
|
|
@@ -7,6 +7,13 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type React from "react";
|
|
9
9
|
import type { SchemaHealthResponse } from "@contracts";
|
|
10
|
+
import { Badge } from "../../components/ui/badge";
|
|
11
|
+
import {
|
|
12
|
+
Card,
|
|
13
|
+
CardHeader,
|
|
14
|
+
CardTitle,
|
|
15
|
+
CardContent,
|
|
16
|
+
} from "../../components/ui/card";
|
|
10
17
|
|
|
11
18
|
// ---------------------------------------------------------------------------
|
|
12
19
|
// Schema Health card
|
|
@@ -19,26 +26,34 @@ function ColumnAuditCard({
|
|
|
19
26
|
}): React.ReactElement {
|
|
20
27
|
const missing = columns.filter((c) => !c.present);
|
|
21
28
|
return (
|
|
22
|
-
<div className="
|
|
23
|
-
<div className="
|
|
29
|
+
<div className="mt-3">
|
|
30
|
+
<div className="text-xs font-semibold text-muted-foreground">
|
|
24
31
|
Column Audit ({columns.length} columns, {missing.length} missing)
|
|
25
32
|
</div>
|
|
26
|
-
<table className="
|
|
33
|
+
<table className="mt-1 w-full border-collapse text-sm">
|
|
27
34
|
<thead>
|
|
28
35
|
<tr>
|
|
29
|
-
<th>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
<th>
|
|
36
|
+
<th className="border-b border-border px-2 py-1 text-left font-medium text-muted-foreground">
|
|
37
|
+
Table
|
|
38
|
+
</th>
|
|
39
|
+
<th className="border-b border-border px-2 py-1 text-left font-medium text-muted-foreground">
|
|
40
|
+
Column
|
|
41
|
+
</th>
|
|
42
|
+
<th className="border-b border-border px-2 py-1 text-left font-medium text-muted-foreground">
|
|
43
|
+
Decl
|
|
44
|
+
</th>
|
|
45
|
+
<th className="border-b border-border px-2 py-1 text-left font-medium text-muted-foreground">
|
|
46
|
+
Status
|
|
47
|
+
</th>
|
|
33
48
|
</tr>
|
|
34
49
|
</thead>
|
|
35
50
|
<tbody>
|
|
36
51
|
{columns.map((c) => (
|
|
37
|
-
<tr key={`${c.table}.${c.column}`}>
|
|
38
|
-
<td>{c.table}</td>
|
|
39
|
-
<td>{c.column}</td>
|
|
40
|
-
<td className="
|
|
41
|
-
<td className={c.present ? "text-
|
|
52
|
+
<tr key={`${c.table}.${c.column}`} className="border-b border-border/50">
|
|
53
|
+
<td className="px-2 py-1">{c.table}</td>
|
|
54
|
+
<td className="px-2 py-1">{c.column}</td>
|
|
55
|
+
<td className="px-2 py-1 font-mono">{c.expectedDecl}</td>
|
|
56
|
+
<td className={`px-2 py-1 ${c.present ? "text-emerald-400" : "text-red-400"}`}>
|
|
42
57
|
{c.present ? "✓" : "MISSING"}
|
|
43
58
|
</td>
|
|
44
59
|
</tr>
|
|
@@ -59,29 +74,35 @@ export function SchemaHealthCard({
|
|
|
59
74
|
error: Error | null;
|
|
60
75
|
}): React.ReactElement {
|
|
61
76
|
return (
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
Schema Health
|
|
77
|
+
<Card>
|
|
78
|
+
<CardHeader className="flex-row items-center justify-between space-y-0">
|
|
79
|
+
<CardTitle>Schema Health</CardTitle>
|
|
65
80
|
{data && (
|
|
66
|
-
<
|
|
81
|
+
<Badge variant={data.healthy ? "success" : "danger"}>
|
|
67
82
|
{data.healthy ? "HEALTHY" : "DEGRADED"}
|
|
68
|
-
</
|
|
83
|
+
</Badge>
|
|
84
|
+
)}
|
|
85
|
+
</CardHeader>
|
|
86
|
+
<CardContent>
|
|
87
|
+
{loading && !data && (
|
|
88
|
+
<span className="text-sm text-muted-foreground">Loading…</span>
|
|
89
|
+
)}
|
|
90
|
+
{error && !data && (
|
|
91
|
+
<span className="text-sm text-red-400">{error.message}</span>
|
|
69
92
|
)}
|
|
70
|
-
</div>
|
|
71
|
-
<div className="card-body">
|
|
72
|
-
{loading && !data && <span className="text-muted">Loading…</span>}
|
|
73
|
-
{error && !data && <span className="text-error">{error.message}</span>}
|
|
74
93
|
{data && (
|
|
75
94
|
<>
|
|
76
|
-
<div className="
|
|
77
|
-
<span className="
|
|
78
|
-
<span className="
|
|
95
|
+
<div className="flex items-baseline justify-between border-b border-border py-1 text-sm">
|
|
96
|
+
<span className="text-muted-foreground">Schema version</span>
|
|
97
|
+
<span className="font-semibold">{data.schemaVersion}</span>
|
|
79
98
|
</div>
|
|
80
|
-
<div className="
|
|
81
|
-
<span className="
|
|
99
|
+
<div className="flex items-baseline justify-between border-b border-border py-1 text-sm">
|
|
100
|
+
<span className="text-muted-foreground">Integrity</span>
|
|
82
101
|
<span
|
|
83
102
|
className={
|
|
84
|
-
data.integrity[0] === "ok"
|
|
103
|
+
data.integrity[0] === "ok"
|
|
104
|
+
? "text-emerald-400"
|
|
105
|
+
: "text-red-400"
|
|
85
106
|
}
|
|
86
107
|
>
|
|
87
108
|
{data.integrity.join("; ")}
|
|
@@ -89,12 +110,12 @@ export function SchemaHealthCard({
|
|
|
89
110
|
</div>
|
|
90
111
|
<ColumnAuditCard columns={data.columns} />
|
|
91
112
|
{data.fkCheck.length > 0 && (
|
|
92
|
-
<div className="
|
|
93
|
-
<div className="
|
|
113
|
+
<div className="mt-3">
|
|
114
|
+
<div className="text-xs font-semibold text-amber-400">
|
|
94
115
|
FK Check ({data.fkCheck.length})
|
|
95
116
|
</div>
|
|
96
117
|
{data.fkCheck.map((line, i) => (
|
|
97
|
-
<div key={i} className="text-
|
|
118
|
+
<div key={i} className="font-mono text-xs text-amber-400">
|
|
98
119
|
{line}
|
|
99
120
|
</div>
|
|
100
121
|
))}
|
|
@@ -102,7 +123,7 @@ export function SchemaHealthCard({
|
|
|
102
123
|
)}
|
|
103
124
|
</>
|
|
104
125
|
)}
|
|
105
|
-
</
|
|
106
|
-
</
|
|
126
|
+
</CardContent>
|
|
127
|
+
</Card>
|
|
107
128
|
);
|
|
108
129
|
}
|
|
@@ -46,8 +46,8 @@ export default function MaintenanceTab(): React.ReactElement {
|
|
|
46
46
|
);
|
|
47
47
|
|
|
48
48
|
return (
|
|
49
|
-
<div className="
|
|
50
|
-
<div className="
|
|
49
|
+
<div className="flex flex-col gap-4">
|
|
50
|
+
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3">
|
|
51
51
|
<SchemaHealthCard
|
|
52
52
|
data={schemaHealth}
|
|
53
53
|
loading={healthLoading}
|