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
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import type React from "react";
|
|
11
11
|
import { useState, useMemo } from "react";
|
|
12
12
|
import type { IndexesIndexRow } from "@contracts";
|
|
13
|
+
import { Card, CardContent } from "../components/ui/card";
|
|
13
14
|
|
|
14
15
|
export interface RepoTableProps {
|
|
15
16
|
repos: IndexesIndexRow[];
|
|
@@ -99,30 +100,32 @@ export function RepoTable({
|
|
|
99
100
|
};
|
|
100
101
|
|
|
101
102
|
return (
|
|
102
|
-
<
|
|
103
|
+
<Card>
|
|
104
|
+
<CardContent>
|
|
103
105
|
<input
|
|
104
106
|
type="search"
|
|
105
|
-
className="
|
|
107
|
+
className="mb-3 w-full rounded-md border border-border bg-bg-elevated/50 px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/60"
|
|
106
108
|
placeholder="Search repos…"
|
|
107
109
|
value={search}
|
|
108
110
|
onChange={(e) => setSearch(e.target.value)}
|
|
109
111
|
aria-label="Search repos"
|
|
110
112
|
/>
|
|
111
|
-
<
|
|
113
|
+
<div className="overflow-x-auto">
|
|
114
|
+
<table className="w-full border-collapse text-sm">
|
|
112
115
|
<thead>
|
|
113
116
|
<tr>
|
|
114
117
|
{COLUMNS.map((col) => (
|
|
115
|
-
<th key={col.key}>
|
|
118
|
+
<th key={col.key} className="border-b border-border px-3 py-2 text-left">
|
|
116
119
|
<button
|
|
117
120
|
type="button"
|
|
118
|
-
className={`
|
|
121
|
+
className={`font-medium text-muted-foreground transition-colors hover:text-foreground ${
|
|
122
|
+
sortKey === col.key ? "text-neon" : ""
|
|
123
|
+
}`}
|
|
119
124
|
onClick={() => handleSort(col.key)}
|
|
120
125
|
>
|
|
121
126
|
{col.label}
|
|
122
127
|
{sortKey === col.key && (
|
|
123
|
-
<span
|
|
124
|
-
{sortDir === "asc" ? " ▲" : " ▼"}
|
|
125
|
-
</span>
|
|
128
|
+
<span>{sortDir === "asc" ? " ▲" : " ▼"}</span>
|
|
126
129
|
)}
|
|
127
130
|
</button>
|
|
128
131
|
</th>
|
|
@@ -132,7 +135,7 @@ export function RepoTable({
|
|
|
132
135
|
<tbody>
|
|
133
136
|
{filtered.length === 0 && (
|
|
134
137
|
<tr>
|
|
135
|
-
<td colSpan={COLUMNS.length} className="
|
|
138
|
+
<td colSpan={COLUMNS.length} className="px-3 py-4 text-muted-foreground">
|
|
136
139
|
{repos.length === 0
|
|
137
140
|
? "No repositories registered yet."
|
|
138
141
|
: "No repos match your search."}
|
|
@@ -140,19 +143,25 @@ export function RepoTable({
|
|
|
140
143
|
</tr>
|
|
141
144
|
)}
|
|
142
145
|
{filtered.map((repo) => (
|
|
143
|
-
<tr
|
|
144
|
-
|
|
146
|
+
<tr
|
|
147
|
+
key={repo.stateDir}
|
|
148
|
+
onClick={() => onSelect(repo)}
|
|
149
|
+
className="cursor-pointer border-b border-border/50 transition-colors hover:bg-bg-elevated/40"
|
|
150
|
+
>
|
|
151
|
+
<td className="px-3 py-2 font-medium" title={repo.repoRoot}>
|
|
145
152
|
{repo.displayName}
|
|
146
153
|
</td>
|
|
147
|
-
<td className="
|
|
148
|
-
<td className="
|
|
149
|
-
<td className="
|
|
150
|
-
<td className="
|
|
151
|
-
<td className="
|
|
154
|
+
<td className="px-3 py-2 text-muted-foreground">{repo.modelName ?? "\u2014"}</td>
|
|
155
|
+
<td className="px-3 py-2 text-right">{repo.checkpointCount.toLocaleString()}</td>
|
|
156
|
+
<td className="px-3 py-2 text-right">{repo.tokensSaved.toLocaleString()}</td>
|
|
157
|
+
<td className="px-3 py-2 text-right">{fmtBytesTop(repo.compressedOriginalBytes)}</td>
|
|
158
|
+
<td className="px-3 py-2 text-right">{formatTs(repo.lastCompactedAt)}</td>
|
|
152
159
|
</tr>
|
|
153
160
|
))}
|
|
154
161
|
</tbody>
|
|
155
162
|
</table>
|
|
156
|
-
|
|
163
|
+
</div>
|
|
164
|
+
</CardContent>
|
|
165
|
+
</Card>
|
|
157
166
|
);
|
|
158
167
|
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
import type React from "react";
|
|
21
21
|
import type { ActiveSession, SessionsResponse } from "@contracts";
|
|
22
|
+
import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card";
|
|
22
23
|
|
|
23
24
|
export interface SessionContextGaugesProps {
|
|
24
25
|
/** Active sessions from /api/sessions (may be null while loading). */
|
|
@@ -153,29 +154,25 @@ export function SessionContextGauges({
|
|
|
153
154
|
const showLoading = loading && !sessions;
|
|
154
155
|
|
|
155
156
|
return (
|
|
156
|
-
<
|
|
157
|
-
<
|
|
157
|
+
<Card className="electric-hover">
|
|
158
|
+
<CardHeader>
|
|
159
|
+
<CardTitle>Context per Session (live)</CardTitle>
|
|
160
|
+
</CardHeader>
|
|
161
|
+
<CardContent>
|
|
158
162
|
{hasError ? (
|
|
159
|
-
<div className="
|
|
163
|
+
<div className="text-sm text-muted-foreground">
|
|
160
164
|
Error loading sessions: {error!.message}
|
|
161
165
|
</div>
|
|
162
166
|
) : showLoading ? (
|
|
163
|
-
<div className="
|
|
164
|
-
) : gauges.length <= 1 && others.length === 0 ? (
|
|
165
|
-
// Launcher-only: just render the one self gauge (no "no sessions"
|
|
166
|
-
// message — the launcher always has a gauge).
|
|
167
|
-
<div className="session-gauges-grid">
|
|
168
|
-
{gauges.map((g) => (
|
|
169
|
-
<SessionGauge key={g.key} data={g} />
|
|
170
|
-
))}
|
|
171
|
-
</div>
|
|
167
|
+
<div className="text-sm text-muted-foreground">Loading sessions…</div>
|
|
172
168
|
) : (
|
|
173
|
-
<div className="
|
|
169
|
+
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
|
174
170
|
{gauges.map((g) => (
|
|
175
171
|
<SessionGauge key={g.key} data={g} />
|
|
176
172
|
))}
|
|
177
173
|
</div>
|
|
178
174
|
)}
|
|
179
|
-
|
|
175
|
+
</CardContent>
|
|
176
|
+
</Card>
|
|
180
177
|
);
|
|
181
178
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type React from "react";
|
|
8
|
+
import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card";
|
|
8
9
|
|
|
9
10
|
export interface SessionInfoProps {
|
|
10
11
|
/** Number of active crew agents. */
|
|
@@ -21,20 +22,24 @@ export function SessionInfo(
|
|
|
21
22
|
activeAgents > 0 ? `▶ ${activeAgents} running` : "idle";
|
|
22
23
|
|
|
23
24
|
return (
|
|
24
|
-
<
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
25
|
+
<Card className="electric-hover">
|
|
26
|
+
<CardHeader>
|
|
27
|
+
<CardTitle>Crew / Agents</CardTitle>
|
|
28
|
+
</CardHeader>
|
|
29
|
+
<CardContent>
|
|
30
|
+
<div className="flex items-baseline justify-between border-b border-border py-1 text-sm">
|
|
31
|
+
<span className="text-xs text-muted-foreground">Active Agents</span>
|
|
32
|
+
<span className="font-semibold">{activeAgents}</span>
|
|
33
|
+
</div>
|
|
34
|
+
<div className="flex items-baseline justify-between border-b border-border py-1 text-sm">
|
|
35
|
+
<span className="text-xs text-muted-foreground">Current Turn</span>
|
|
36
|
+
<span className="font-semibold">{currentTurn}</span>
|
|
37
|
+
</div>
|
|
38
|
+
<div className="flex items-baseline justify-between py-1 text-sm">
|
|
39
|
+
<span className="text-xs text-muted-foreground">Status</span>
|
|
40
|
+
<span className="font-semibold">{status}</span>
|
|
41
|
+
</div>
|
|
42
|
+
</CardContent>
|
|
43
|
+
</Card>
|
|
39
44
|
);
|
|
40
45
|
}
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
Legend,
|
|
26
26
|
} from "recharts";
|
|
27
27
|
import type { SessionSeries } from "@contracts";
|
|
28
|
+
import { Card, CardContent } from "../components/ui/card";
|
|
28
29
|
|
|
29
30
|
export interface SessionsMemoryChartProps {
|
|
30
31
|
/** Per-session series with stable colors (from /api/sessions/timeseries). */
|
|
@@ -110,14 +111,15 @@ export function SessionsMemoryChart({
|
|
|
110
111
|
|
|
111
112
|
if (series.length === 0) {
|
|
112
113
|
return (
|
|
113
|
-
<
|
|
114
|
-
No token samples in this window yet
|
|
115
|
-
</
|
|
114
|
+
<Card>
|
|
115
|
+
<CardContent>No token samples in this window yet.</CardContent>
|
|
116
|
+
</Card>
|
|
116
117
|
);
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
return (
|
|
120
|
-
<
|
|
121
|
+
<Card>
|
|
122
|
+
<CardContent>
|
|
121
123
|
<ResponsiveContainer width="100%" height={360}>
|
|
122
124
|
<AreaChart
|
|
123
125
|
data={data}
|
|
@@ -194,6 +196,7 @@ export function SessionsMemoryChart({
|
|
|
194
196
|
/>
|
|
195
197
|
</AreaChart>
|
|
196
198
|
</ResponsiveContainer>
|
|
197
|
-
|
|
199
|
+
</CardContent>
|
|
200
|
+
</Card>
|
|
198
201
|
);
|
|
199
202
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type React from "react";
|
|
9
|
+
import { Card, CardContent } from "../components/ui/card";
|
|
9
10
|
|
|
10
11
|
export interface SummaryTilesProps {
|
|
11
12
|
/** Total repos in the registry. */
|
|
@@ -39,23 +40,31 @@ export function SummaryTiles({
|
|
|
39
40
|
compressedOriginalBytes,
|
|
40
41
|
}: SummaryTilesProps): React.ReactElement {
|
|
41
42
|
return (
|
|
42
|
-
<div className="
|
|
43
|
-
<
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
</
|
|
55
|
-
<
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
43
|
+
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4">
|
|
44
|
+
<Card className="electric-hover">
|
|
45
|
+
<CardContent>
|
|
46
|
+
<span className="tile-label">Repositories</span>
|
|
47
|
+
<div className="text-xl font-semibold">{totalRepos.toLocaleString()}</div>
|
|
48
|
+
</CardContent>
|
|
49
|
+
</Card>
|
|
50
|
+
<Card className="electric-hover">
|
|
51
|
+
<CardContent>
|
|
52
|
+
<span className="tile-label">Total Checkpoints</span>
|
|
53
|
+
<div className="text-xl font-semibold">{totalCheckpoints.toLocaleString()}</div>
|
|
54
|
+
</CardContent>
|
|
55
|
+
</Card>
|
|
56
|
+
<Card className="electric-hover">
|
|
57
|
+
<CardContent>
|
|
58
|
+
<span className="tile-label">Total Tokens Saved</span>
|
|
59
|
+
<div className="text-xl font-semibold">{fmt(totalTokensSaved)}</div>
|
|
60
|
+
</CardContent>
|
|
61
|
+
</Card>
|
|
62
|
+
<Card className="electric-hover">
|
|
63
|
+
<CardContent>
|
|
64
|
+
<span className="tile-label">Compressed-Original</span>
|
|
65
|
+
<div className="text-xl font-semibold">{fmtBytesTop(compressedOriginalBytes)}</div>
|
|
66
|
+
</CardContent>
|
|
67
|
+
</Card>
|
|
59
68
|
</div>
|
|
60
69
|
);
|
|
61
70
|
}
|
|
@@ -11,7 +11,7 @@ import type { TabId } from "../App";
|
|
|
11
11
|
export interface TabBarProps {
|
|
12
12
|
primaryTabs: Array<{ id: TabId; label: string }>;
|
|
13
13
|
advancedTabs: Array<{ id: TabId; label: string }>;
|
|
14
|
-
advancedTabIds:
|
|
14
|
+
advancedTabIds: ReadonlySet<TabId>;
|
|
15
15
|
active: TabId;
|
|
16
16
|
onTabChange: (id: TabId) => void;
|
|
17
17
|
}
|
|
@@ -27,8 +27,6 @@ export function TabBar({
|
|
|
27
27
|
() => advancedTabIds.has(active), // start open if an advanced tab is active
|
|
28
28
|
);
|
|
29
29
|
|
|
30
|
-
const isAdvancedActive = advancedTabIds.has(active);
|
|
31
|
-
|
|
32
30
|
return (
|
|
33
31
|
<nav className="tab-bar" role="tablist">
|
|
34
32
|
{primaryTabs.map((tab) => (
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { SnapshotResponse } from "@contracts";
|
|
3
|
+
import { Sidebar } from "./Sidebar";
|
|
4
|
+
import { BottomBar } from "./BottomBar";
|
|
5
|
+
import type { TabId } from "../../tabs/registry";
|
|
6
|
+
|
|
7
|
+
interface AppShellProps {
|
|
8
|
+
active: TabId;
|
|
9
|
+
onTabChange: (id: TabId) => void;
|
|
10
|
+
snapshot: SnapshotResponse | null;
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function AppShell({ active, onTabChange, snapshot, children }: AppShellProps) {
|
|
15
|
+
const tier = snapshot?.tier ?? "unknown";
|
|
16
|
+
const version = snapshot?.model?.name ?? "";
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className="min-h-screen bg-background text-foreground">
|
|
20
|
+
<div className="mx-auto flex max-w-[1600px] gap-6 px-4 py-4">
|
|
21
|
+
<Sidebar active={active} onTabChange={onTabChange} />
|
|
22
|
+
<div className="flex min-w-0 flex-1 flex-col">
|
|
23
|
+
<header className="mb-4 flex items-center gap-3">
|
|
24
|
+
<h1 className="font-heading text-lg font-semibold">
|
|
25
|
+
<span className="gradient-text">mega-compact dashboard</span>
|
|
26
|
+
</h1>
|
|
27
|
+
<span className="rounded-md border border-border bg-bg-elevated px-2 py-0.5 text-xs text-muted">
|
|
28
|
+
{tier}
|
|
29
|
+
</span>
|
|
30
|
+
{version && (
|
|
31
|
+
<span className="rounded-md border border-border bg-bg-elevated px-2 py-0.5 text-xs text-muted">
|
|
32
|
+
{version}
|
|
33
|
+
</span>
|
|
34
|
+
)}
|
|
35
|
+
</header>
|
|
36
|
+
<main className="flex-1 pb-20 lg:pb-4">{children}</main>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
<BottomBar active={active} onTabChange={onTabChange} />
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { MoreHorizontal } from "lucide-react";
|
|
3
|
+
import { cn } from "../../utils/cn";
|
|
4
|
+
import {
|
|
5
|
+
PRIMARY_TABS,
|
|
6
|
+
ADVANCED_TABS,
|
|
7
|
+
type TabId,
|
|
8
|
+
} from "../../tabs/registry";
|
|
9
|
+
|
|
10
|
+
interface BottomBarProps {
|
|
11
|
+
active: TabId;
|
|
12
|
+
onTabChange: (id: TabId) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function BottomBar({ active, onTabChange }: BottomBarProps) {
|
|
16
|
+
const [moreOpen, setMoreOpen] = useState(false);
|
|
17
|
+
const primary = PRIMARY_TABS.slice(0, 4);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<nav className="glass-panel fixed inset-x-3 bottom-3 z-40 flex items-center justify-around rounded-xl p-2 lg:hidden">
|
|
21
|
+
{primary.map((tab) => {
|
|
22
|
+
const Icon = tab.icon;
|
|
23
|
+
const isActive = active === tab.id;
|
|
24
|
+
return (
|
|
25
|
+
<button
|
|
26
|
+
key={tab.id}
|
|
27
|
+
type="button"
|
|
28
|
+
onClick={() => onTabChange(tab.id)}
|
|
29
|
+
className={cn(
|
|
30
|
+
"flex flex-col items-center gap-1 rounded-lg px-3 py-1.5 text-[11px] font-medium",
|
|
31
|
+
isActive
|
|
32
|
+
? "text-neon glow-primary"
|
|
33
|
+
: "text-muted hover:text-foreground",
|
|
34
|
+
)}
|
|
35
|
+
aria-current={isActive ? "page" : undefined}
|
|
36
|
+
>
|
|
37
|
+
<Icon className="h-5 w-5" />
|
|
38
|
+
{tab.label}
|
|
39
|
+
</button>
|
|
40
|
+
);
|
|
41
|
+
})}
|
|
42
|
+
|
|
43
|
+
<button
|
|
44
|
+
type="button"
|
|
45
|
+
onClick={() => setMoreOpen((o) => !o)}
|
|
46
|
+
aria-expanded={moreOpen}
|
|
47
|
+
aria-controls="bottombar-more-section"
|
|
48
|
+
className={cn(
|
|
49
|
+
"flex flex-col items-center gap-1 rounded-lg px-3 py-1.5 text-[11px] font-medium",
|
|
50
|
+
moreOpen ? "text-neon" : "text-muted hover:text-foreground",
|
|
51
|
+
)}
|
|
52
|
+
>
|
|
53
|
+
<MoreHorizontal className="h-5 w-5" />
|
|
54
|
+
More
|
|
55
|
+
</button>
|
|
56
|
+
|
|
57
|
+
{moreOpen && (
|
|
58
|
+
<div
|
|
59
|
+
id="bottombar-more-section"
|
|
60
|
+
className="absolute bottom-16 right-2 w-56 rounded-xl border border-border bg-bg-elevated p-2 shadow-panel"
|
|
61
|
+
>
|
|
62
|
+
{[...PRIMARY_TABS.slice(4), ...ADVANCED_TABS].map((tab) => {
|
|
63
|
+
const Icon = tab.icon;
|
|
64
|
+
return (
|
|
65
|
+
<button
|
|
66
|
+
key={tab.id}
|
|
67
|
+
type="button"
|
|
68
|
+
onClick={() => {
|
|
69
|
+
onTabChange(tab.id);
|
|
70
|
+
setMoreOpen(false);
|
|
71
|
+
}}
|
|
72
|
+
className={cn(
|
|
73
|
+
"flex w-full items-center gap-3 rounded-lg px-3 py-2 text-left text-sm",
|
|
74
|
+
active === tab.id
|
|
75
|
+
? "bg-primary/15 text-neon"
|
|
76
|
+
: "text-muted hover:bg-bg-elevated hover:text-foreground",
|
|
77
|
+
)}
|
|
78
|
+
>
|
|
79
|
+
<Icon className="h-4 w-4 shrink-0" />
|
|
80
|
+
{tab.label}
|
|
81
|
+
</button>
|
|
82
|
+
);
|
|
83
|
+
})}
|
|
84
|
+
</div>
|
|
85
|
+
)}
|
|
86
|
+
</nav>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { ChevronDown, Sparkles } from "lucide-react";
|
|
3
|
+
import { cn } from "../../utils/cn";
|
|
4
|
+
import {
|
|
5
|
+
PRIMARY_TABS,
|
|
6
|
+
ADVANCED_TABS,
|
|
7
|
+
ADVANCED_TAB_IDS,
|
|
8
|
+
type TabId,
|
|
9
|
+
} from "../../tabs/registry";
|
|
10
|
+
import { Badge } from "../ui/badge";
|
|
11
|
+
|
|
12
|
+
interface SidebarProps {
|
|
13
|
+
active: TabId;
|
|
14
|
+
onTabChange: (id: TabId) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function Sidebar({ active, onTabChange }: SidebarProps) {
|
|
18
|
+
const [advancedOpen, setAdvancedOpen] = useState<boolean>(
|
|
19
|
+
() => ADVANCED_TAB_IDS.has(active),
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
// Keep the advanced section expanded whenever the active tab is advanced,
|
|
23
|
+
// regardless of how the navigation happened (tab click, cross-device recalc).
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (ADVANCED_TAB_IDS.has(active)) setAdvancedOpen(true);
|
|
26
|
+
}, [active]);
|
|
27
|
+
|
|
28
|
+
const Tile = ({ tab }: { tab: (typeof PRIMARY_TABS)[number] }) => {
|
|
29
|
+
const Icon = tab.icon;
|
|
30
|
+
const isActive = active === tab.id;
|
|
31
|
+
return (
|
|
32
|
+
<button
|
|
33
|
+
type="button"
|
|
34
|
+
onClick={() => onTabChange(tab.id)}
|
|
35
|
+
className={cn(
|
|
36
|
+
"flex w-full items-center gap-3 rounded-lg px-3 py-2 text-left text-sm font-medium transition-all",
|
|
37
|
+
isActive
|
|
38
|
+
? "bg-primary/15 text-neon glow-primary border border-primary/40"
|
|
39
|
+
: "border border-transparent text-muted hover:bg-bg-elevated hover:text-foreground",
|
|
40
|
+
)}
|
|
41
|
+
aria-current={isActive ? "page" : undefined}
|
|
42
|
+
>
|
|
43
|
+
<Icon className="h-4 w-4 shrink-0" />
|
|
44
|
+
<span className="truncate">{tab.label}</span>
|
|
45
|
+
{isActive && <Badge variant="default" className="ml-auto">●</Badge>}
|
|
46
|
+
</button>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<aside className="glass-panel hidden w-64 shrink-0 flex-col gap-1 p-3 lg:flex">
|
|
52
|
+
<div className="mb-2 flex items-center gap-2 px-2">
|
|
53
|
+
<Sparkles className="h-4 w-4 text-neon" />
|
|
54
|
+
<span className="font-heading text-sm uppercase tracking-widest text-muted">
|
|
55
|
+
Tabs
|
|
56
|
+
</span>
|
|
57
|
+
</div>
|
|
58
|
+
{PRIMARY_TABS.map((tab) => (
|
|
59
|
+
<Tile key={tab.id} tab={tab} />
|
|
60
|
+
))}
|
|
61
|
+
|
|
62
|
+
<button
|
|
63
|
+
type="button"
|
|
64
|
+
onClick={() => setAdvancedOpen((o) => !o)}
|
|
65
|
+
className="mt-2 flex w-full items-center justify-between rounded-lg px-3 py-2 text-left text-sm font-semibold text-muted hover:text-foreground"
|
|
66
|
+
aria-expanded={advancedOpen}
|
|
67
|
+
aria-controls="sidebar-advanced-section"
|
|
68
|
+
>
|
|
69
|
+
Advanced
|
|
70
|
+
<ChevronDown
|
|
71
|
+
className={cn("h-4 w-4 transition-transform", advancedOpen && "rotate-180")}
|
|
72
|
+
/>
|
|
73
|
+
</button>
|
|
74
|
+
{advancedOpen && (
|
|
75
|
+
<div id="sidebar-advanced-section" className="mt-1 flex flex-col gap-1">
|
|
76
|
+
{ADVANCED_TABS.map((tab) => (
|
|
77
|
+
<Tile key={tab.id} tab={tab} />
|
|
78
|
+
))}
|
|
79
|
+
</div>
|
|
80
|
+
)}
|
|
81
|
+
</aside>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "../../utils/cn";
|
|
4
|
+
|
|
5
|
+
const badgeVariants = cva(
|
|
6
|
+
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "border-primary/40 bg-primary/15 text-neon",
|
|
11
|
+
success: "border-success/40 bg-success/15 text-success",
|
|
12
|
+
warning: "border-warning/40 bg-warning/15 text-warning",
|
|
13
|
+
danger: "border-danger/40 bg-danger/15 text-danger",
|
|
14
|
+
outline: "border-border text-foreground",
|
|
15
|
+
accent: "border-accent/40 bg-accent/15 text-accent",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: { variant: "default" },
|
|
19
|
+
},
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export interface BadgeProps
|
|
23
|
+
extends React.HTMLAttributes<HTMLSpanElement>,
|
|
24
|
+
VariantProps<typeof badgeVariants> {}
|
|
25
|
+
|
|
26
|
+
function Badge({ className, variant, ...props }: BadgeProps) {
|
|
27
|
+
return <span className={cn(badgeVariants({ variant }), className)} {...props} />;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { Badge, badgeVariants };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "../../utils/cn";
|
|
4
|
+
|
|
5
|
+
const buttonVariants = cva(
|
|
6
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90 glow-primary",
|
|
11
|
+
outline: "border border-border bg-transparent hover:bg-bg-elevated hover:text-foreground",
|
|
12
|
+
ghost: "hover:bg-bg-elevated hover:text-foreground",
|
|
13
|
+
glass: "glass-panel electric-hover",
|
|
14
|
+
},
|
|
15
|
+
size: {
|
|
16
|
+
default: "h-9 px-4 py-2",
|
|
17
|
+
sm: "h-8 rounded-md px-3 text-xs",
|
|
18
|
+
lg: "h-10 rounded-md px-8",
|
|
19
|
+
icon: "h-9 w-9",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
variant: "default",
|
|
24
|
+
size: "default",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export interface ButtonProps
|
|
30
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
31
|
+
VariantProps<typeof buttonVariants> {}
|
|
32
|
+
|
|
33
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
34
|
+
({ className, variant, size, ...props }, ref) => (
|
|
35
|
+
<button
|
|
36
|
+
ref={ref}
|
|
37
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
38
|
+
{...props}
|
|
39
|
+
/>
|
|
40
|
+
),
|
|
41
|
+
);
|
|
42
|
+
Button.displayName = "Button";
|
|
43
|
+
|
|
44
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../../utils/cn";
|
|
3
|
+
|
|
4
|
+
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
5
|
+
({ className, ...props }, ref) => (
|
|
6
|
+
<div ref={ref} className={cn("glass-panel p-4", className)} {...props} />
|
|
7
|
+
),
|
|
8
|
+
);
|
|
9
|
+
Card.displayName = "Card";
|
|
10
|
+
|
|
11
|
+
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
12
|
+
({ className, ...props }, ref) => (
|
|
13
|
+
<div ref={ref} className={cn("mb-3 flex flex-col space-y-1.5", className)} {...props} />
|
|
14
|
+
),
|
|
15
|
+
);
|
|
16
|
+
CardHeader.displayName = "CardHeader";
|
|
17
|
+
|
|
18
|
+
const CardTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
|
19
|
+
({ className, ...props }, ref) => (
|
|
20
|
+
<h3 ref={ref} className={cn("font-heading text-sm font-semibold leading-none tracking-tight", className)} {...props} />
|
|
21
|
+
),
|
|
22
|
+
);
|
|
23
|
+
CardTitle.displayName = "CardTitle";
|
|
24
|
+
|
|
25
|
+
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
26
|
+
({ className, ...props }, ref) => (
|
|
27
|
+
<div ref={ref} className={cn("text-sm", className)} {...props} />
|
|
28
|
+
),
|
|
29
|
+
);
|
|
30
|
+
CardContent.displayName = "CardContent";
|
|
31
|
+
|
|
32
|
+
export { Card, CardHeader, CardTitle, CardContent };
|