@townco/debugger 0.1.32 → 0.1.34
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/package.json +14 -14
- package/src/App.tsx +1 -0
- package/src/analysis/analyzer.ts +1 -2
- package/src/analysis/comparison-analyzer.ts +528 -0
- package/src/analysis/comparison-schema.ts +151 -0
- package/src/analysis/comparison-types.ts +194 -0
- package/src/analysis-db.ts +13 -6
- package/src/comparison-db.ts +75 -3
- package/src/components/AnalyzeAllButton.tsx +6 -2
- package/src/components/ComparisonAnalysisDialog.tsx +591 -0
- package/src/components/DebuggerHeader.tsx +0 -1
- package/src/components/LogList.tsx +9 -0
- package/src/components/SessionTraceList.tsx +9 -0
- package/src/components/SpanDetailsPanel.tsx +20 -1
- package/src/components/SpanTimeline.tsx +31 -4
- package/src/components/SpanTree.tsx +10 -1
- package/src/components/TurnMetadataPanel.tsx +0 -1
- package/src/components/UnifiedTimeline.tsx +24 -35
- package/src/components/ui/button.tsx +1 -1
- package/src/components/ui/card.tsx +1 -1
- package/src/components/ui/checkbox.tsx +1 -0
- package/src/components/ui/input.tsx +1 -1
- package/src/components/ui/label.tsx +1 -1
- package/src/components/ui/select.tsx +1 -1
- package/src/components/ui/textarea.tsx +1 -1
- package/src/frontend.tsx +2 -0
- package/src/lib/metrics.test.ts +2 -0
- package/src/lib/turnExtractor.ts +28 -0
- package/src/pages/ComparisonView.tsx +584 -92
- package/src/pages/FindSessions.tsx +3 -1
- package/src/pages/TownHall.tsx +30 -14
- package/src/server.ts +177 -7
- package/src/types.ts +4 -0
- package/styles/globals.css +120 -0
- package/tsconfig.json +2 -2
|
@@ -114,7 +114,9 @@ export function FindSessions() {
|
|
|
114
114
|
</CardHeader>
|
|
115
115
|
<CardContent className="space-y-4">
|
|
116
116
|
<div className="space-y-2">
|
|
117
|
-
<label className="text-sm font-medium">
|
|
117
|
+
<label htmlFor="session-select" className="text-sm font-medium">
|
|
118
|
+
Select Session
|
|
119
|
+
</label>
|
|
118
120
|
<Select
|
|
119
121
|
value={selectedSessionId}
|
|
120
122
|
onValueChange={setSelectedSessionId}
|
package/src/pages/TownHall.tsx
CHANGED
|
@@ -19,13 +19,6 @@ import type {
|
|
|
19
19
|
Session,
|
|
20
20
|
} from "../types";
|
|
21
21
|
|
|
22
|
-
function formatDuration(startNano: number, endNano: number): string {
|
|
23
|
-
const ms = (endNano - startNano) / 1_000_000;
|
|
24
|
-
if (ms < 1000) return `${ms.toFixed(0)}ms`;
|
|
25
|
-
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
|
|
26
|
-
return `${(ms / 60000).toFixed(1)}m`;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
22
|
function formatRelativeTime(nanoseconds: number): string {
|
|
30
23
|
const ms = Date.now() - nanoseconds / 1_000_000;
|
|
31
24
|
const seconds = Math.floor(ms / 1000);
|
|
@@ -47,9 +40,9 @@ interface SessionWithMessage extends Session {
|
|
|
47
40
|
export function TownHall() {
|
|
48
41
|
const [sessions, setSessions] = useState<SessionWithMessage[]>([]);
|
|
49
42
|
const [comparisonRuns, setComparisonRuns] = useState<ComparisonRun[]>([]);
|
|
50
|
-
const [
|
|
51
|
-
|
|
52
|
-
);
|
|
43
|
+
const [_comparisonSessionIds, setComparisonSessionIds] = useState<
|
|
44
|
+
Set<string>
|
|
45
|
+
>(new Set());
|
|
53
46
|
const [loading, setLoading] = useState(true);
|
|
54
47
|
const [error, setError] = useState<string | null>(null);
|
|
55
48
|
|
|
@@ -64,6 +57,7 @@ export function TownHall() {
|
|
|
64
57
|
const [variantModel, setVariantModel] = useState<string | null>(null);
|
|
65
58
|
const [variantSystemPrompt, setVariantSystemPrompt] = useState<string>("");
|
|
66
59
|
const [variantTools, setVariantTools] = useState<string[]>([]);
|
|
60
|
+
const [hypothesis, setHypothesis] = useState<string>("");
|
|
67
61
|
|
|
68
62
|
// Running comparison state
|
|
69
63
|
const [runningComparison, setRunningComparison] = useState<string | null>(
|
|
@@ -175,13 +169,14 @@ export function TownHall() {
|
|
|
175
169
|
variantSystemPrompt !== "" &&
|
|
176
170
|
variantSystemPrompt !== agentConfig?.systemPrompt
|
|
177
171
|
);
|
|
178
|
-
case "tools":
|
|
172
|
+
case "tools": {
|
|
179
173
|
const currentToolNames =
|
|
180
174
|
agentConfig?.tools?.map((t) => t.name).sort() || [];
|
|
181
175
|
const variantToolNames = [...variantTools].sort();
|
|
182
176
|
return (
|
|
183
177
|
JSON.stringify(currentToolNames) !== JSON.stringify(variantToolNames)
|
|
184
178
|
);
|
|
179
|
+
}
|
|
185
180
|
default:
|
|
186
181
|
return false;
|
|
187
182
|
}
|
|
@@ -224,6 +219,8 @@ export function TownHall() {
|
|
|
224
219
|
// Include tools if selected
|
|
225
220
|
...(selectedDimensions.has("tools") &&
|
|
226
221
|
variantTools.length > 0 && { variantTools }),
|
|
222
|
+
// Include hypothesis if provided
|
|
223
|
+
...(hypothesis && { hypothesis }),
|
|
227
224
|
};
|
|
228
225
|
|
|
229
226
|
// Save config
|
|
@@ -477,6 +474,25 @@ export function TownHall() {
|
|
|
477
474
|
)}
|
|
478
475
|
</div>
|
|
479
476
|
)}
|
|
477
|
+
|
|
478
|
+
{/* Hypothesis input - shown when any dimension is selected */}
|
|
479
|
+
{selectedDimensions.size > 0 && (
|
|
480
|
+
<div className="space-y-1 pt-2 border-t">
|
|
481
|
+
<Label className="text-xs text-muted-foreground">
|
|
482
|
+
Hypothesis (optional)
|
|
483
|
+
</Label>
|
|
484
|
+
<Textarea
|
|
485
|
+
value={hypothesis}
|
|
486
|
+
onChange={(e) => setHypothesis(e.target.value)}
|
|
487
|
+
className="min-h-[60px] text-xs"
|
|
488
|
+
placeholder="I expect this change to... (e.g., 'reduce token usage by caching file reads' or 'improve answer accuracy by adding web search')"
|
|
489
|
+
/>
|
|
490
|
+
<p className="text-xs text-muted-foreground">
|
|
491
|
+
Describe what you expect the change to achieve. This helps
|
|
492
|
+
the analysis evaluate if your hypothesis was correct.
|
|
493
|
+
</p>
|
|
494
|
+
</div>
|
|
495
|
+
)}
|
|
480
496
|
</CardContent>
|
|
481
497
|
</Card>
|
|
482
498
|
{/* Status indicator - inline below card */}
|
|
@@ -527,9 +543,9 @@ export function TownHall() {
|
|
|
527
543
|
variant="outline"
|
|
528
544
|
size="sm"
|
|
529
545
|
className="h-7 px-2 text-xs"
|
|
530
|
-
onClick={() =>
|
|
531
|
-
|
|
532
|
-
}
|
|
546
|
+
onClick={() => {
|
|
547
|
+
window.location.href = `/sessions/${session.session_id}`;
|
|
548
|
+
}}
|
|
533
549
|
>
|
|
534
550
|
View
|
|
535
551
|
</Button>
|
package/src/server.ts
CHANGED
|
@@ -12,7 +12,6 @@ import type {
|
|
|
12
12
|
ComparisonConfig,
|
|
13
13
|
ConversationTrace,
|
|
14
14
|
SessionMetrics,
|
|
15
|
-
Span,
|
|
16
15
|
} from "./types";
|
|
17
16
|
|
|
18
17
|
export const DEFAULT_DEBUGGER_PORT = 4000;
|
|
@@ -113,6 +112,7 @@ export function startDebuggerServer(
|
|
|
113
112
|
// Start debugger UI server
|
|
114
113
|
const server = serve({
|
|
115
114
|
port,
|
|
115
|
+
idleTimeout: 120, // 2 minutes for long-running LLM analysis requests
|
|
116
116
|
routes: {
|
|
117
117
|
"/api/config": {
|
|
118
118
|
GET() {
|
|
@@ -140,8 +140,12 @@ export function startDebuggerServer(
|
|
|
140
140
|
const url = new URL(req.url);
|
|
141
141
|
const limit = Number.parseInt(
|
|
142
142
|
url.searchParams.get("limit") || "1000",
|
|
143
|
+
10,
|
|
144
|
+
);
|
|
145
|
+
const offset = Number.parseInt(
|
|
146
|
+
url.searchParams.get("offset") || "0",
|
|
147
|
+
10,
|
|
143
148
|
);
|
|
144
|
-
const offset = Number.parseInt(url.searchParams.get("offset") || "0");
|
|
145
149
|
const sessions = db.listSessions(limit, offset);
|
|
146
150
|
return Response.json(sessions);
|
|
147
151
|
},
|
|
@@ -150,8 +154,14 @@ export function startDebuggerServer(
|
|
|
150
154
|
"/api/traces": {
|
|
151
155
|
GET(req) {
|
|
152
156
|
const url = new URL(req.url);
|
|
153
|
-
const limit = Number.parseInt(
|
|
154
|
-
|
|
157
|
+
const limit = Number.parseInt(
|
|
158
|
+
url.searchParams.get("limit") || "50",
|
|
159
|
+
10,
|
|
160
|
+
);
|
|
161
|
+
const offset = Number.parseInt(
|
|
162
|
+
url.searchParams.get("offset") || "0",
|
|
163
|
+
10,
|
|
164
|
+
);
|
|
155
165
|
const sessionId = url.searchParams.get("sessionId") || undefined;
|
|
156
166
|
const traces = db.listTraces(limit, offset, sessionId);
|
|
157
167
|
return Response.json(traces);
|
|
@@ -309,8 +319,14 @@ export function startDebuggerServer(
|
|
|
309
319
|
"/api/comparison-runs": {
|
|
310
320
|
GET(req) {
|
|
311
321
|
const url = new URL(req.url);
|
|
312
|
-
const limit = Number.parseInt(
|
|
313
|
-
|
|
322
|
+
const limit = Number.parseInt(
|
|
323
|
+
url.searchParams.get("limit") || "50",
|
|
324
|
+
10,
|
|
325
|
+
);
|
|
326
|
+
const offset = Number.parseInt(
|
|
327
|
+
url.searchParams.get("offset") || "0",
|
|
328
|
+
10,
|
|
329
|
+
);
|
|
314
330
|
const sourceSessionId = url.searchParams.get("sourceSessionId");
|
|
315
331
|
|
|
316
332
|
if (sourceSessionId) {
|
|
@@ -481,7 +497,7 @@ export function startDebuggerServer(
|
|
|
481
497
|
});
|
|
482
498
|
|
|
483
499
|
return Response.json({ success: true });
|
|
484
|
-
} catch (
|
|
500
|
+
} catch (_error) {
|
|
485
501
|
return Response.json(
|
|
486
502
|
{ error: "Failed to update comparison run" },
|
|
487
503
|
{ status: 500 },
|
|
@@ -799,9 +815,11 @@ export function startDebuggerServer(
|
|
|
799
815
|
// List all analyses
|
|
800
816
|
const limit = Number.parseInt(
|
|
801
817
|
url.searchParams.get("limit") || "50",
|
|
818
|
+
10,
|
|
802
819
|
);
|
|
803
820
|
const offset = Number.parseInt(
|
|
804
821
|
url.searchParams.get("offset") || "0",
|
|
822
|
+
10,
|
|
805
823
|
);
|
|
806
824
|
|
|
807
825
|
const analyses = analysisDb.listAnalyses(limit, offset);
|
|
@@ -828,6 +846,7 @@ export function startDebuggerServer(
|
|
|
828
846
|
const url = new URL(req.url);
|
|
829
847
|
const limit = Number.parseInt(
|
|
830
848
|
url.searchParams.get("limit") || "10",
|
|
849
|
+
10,
|
|
831
850
|
);
|
|
832
851
|
|
|
833
852
|
// Get embedding for this session
|
|
@@ -862,6 +881,157 @@ export function startDebuggerServer(
|
|
|
862
881
|
},
|
|
863
882
|
},
|
|
864
883
|
|
|
884
|
+
// Comparison analysis endpoints
|
|
885
|
+
"/api/analyze-comparison/:runId": {
|
|
886
|
+
async POST(req) {
|
|
887
|
+
const runId = req.params.runId;
|
|
888
|
+
|
|
889
|
+
try {
|
|
890
|
+
// Import analyzer dynamically
|
|
891
|
+
const { analyzeComparison } = await import(
|
|
892
|
+
"./analysis/comparison-analyzer.js"
|
|
893
|
+
);
|
|
894
|
+
|
|
895
|
+
// Get the comparison run
|
|
896
|
+
const run = comparisonDb.getRun(runId);
|
|
897
|
+
if (!run) {
|
|
898
|
+
return Response.json(
|
|
899
|
+
{ error: "Comparison run not found" },
|
|
900
|
+
{ status: 404 },
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// Get the comparison config
|
|
905
|
+
const config = comparisonDb.getConfig(run.configId);
|
|
906
|
+
if (!config) {
|
|
907
|
+
return Response.json(
|
|
908
|
+
{ error: "Comparison config not found" },
|
|
909
|
+
{ status: 404 },
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// Verify all sessions exist
|
|
914
|
+
if (!run.controlSessionId || !run.variantSessionId) {
|
|
915
|
+
return Response.json(
|
|
916
|
+
{ error: "Comparison run is incomplete - missing session IDs" },
|
|
917
|
+
{ status: 400 },
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// Fetch all three sessions from agent server
|
|
922
|
+
const [originalRes, controlRes, variantRes] = await Promise.all([
|
|
923
|
+
fetch(`${agentServerUrl}/sessions/${run.sourceSessionId}`),
|
|
924
|
+
fetch(`${agentServerUrl}/sessions/${run.controlSessionId}`),
|
|
925
|
+
fetch(`${agentServerUrl}/sessions/${run.variantSessionId}`),
|
|
926
|
+
]);
|
|
927
|
+
|
|
928
|
+
if (!originalRes.ok || !controlRes.ok || !variantRes.ok) {
|
|
929
|
+
return Response.json(
|
|
930
|
+
{ error: "Failed to fetch one or more sessions" },
|
|
931
|
+
{ status: 500 },
|
|
932
|
+
);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
const [originalSession, controlSession, variantSession] =
|
|
936
|
+
await Promise.all([
|
|
937
|
+
originalRes.json(),
|
|
938
|
+
controlRes.json(),
|
|
939
|
+
variantRes.json(),
|
|
940
|
+
]);
|
|
941
|
+
|
|
942
|
+
// Get agent config for original tools and system prompt
|
|
943
|
+
const agentConfig = await fetchAgentConfig();
|
|
944
|
+
|
|
945
|
+
// Get metrics for each session
|
|
946
|
+
const getMetrics = (sessionId: string) => {
|
|
947
|
+
const spans = db.getSpansBySessionAttribute(sessionId);
|
|
948
|
+
const traces = db.listTraces(100, 0, sessionId);
|
|
949
|
+
return extractSessionMetrics(
|
|
950
|
+
traces,
|
|
951
|
+
spans,
|
|
952
|
+
agentConfig?.model || "unknown",
|
|
953
|
+
);
|
|
954
|
+
};
|
|
955
|
+
|
|
956
|
+
const originalMetrics = getMetrics(run.sourceSessionId);
|
|
957
|
+
const controlMetrics = getMetrics(run.controlSessionId);
|
|
958
|
+
const variantMetrics = getMetrics(run.variantSessionId);
|
|
959
|
+
|
|
960
|
+
// Run the comparison analysis
|
|
961
|
+
const analysis = await analyzeComparison({
|
|
962
|
+
runId,
|
|
963
|
+
hypothesis: config.hypothesis || "",
|
|
964
|
+
config,
|
|
965
|
+
originalSession,
|
|
966
|
+
controlSession,
|
|
967
|
+
variantSession,
|
|
968
|
+
originalMetrics,
|
|
969
|
+
controlMetrics,
|
|
970
|
+
variantMetrics,
|
|
971
|
+
originalSystemPrompt: agentConfig?.systemPrompt || undefined,
|
|
972
|
+
originalTools: agentConfig?.tools?.map((t) => t.name) || [],
|
|
973
|
+
});
|
|
974
|
+
|
|
975
|
+
// Save to database
|
|
976
|
+
comparisonDb.saveComparisonAnalysis(runId, analysis);
|
|
977
|
+
|
|
978
|
+
return Response.json(analysis);
|
|
979
|
+
} catch (error) {
|
|
980
|
+
console.error("Comparison analysis error:", error);
|
|
981
|
+
return Response.json(
|
|
982
|
+
{
|
|
983
|
+
error:
|
|
984
|
+
error instanceof Error
|
|
985
|
+
? error.message
|
|
986
|
+
: "Comparison analysis failed",
|
|
987
|
+
},
|
|
988
|
+
{ status: 500 },
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
},
|
|
992
|
+
},
|
|
993
|
+
|
|
994
|
+
"/api/comparison-analysis/:runId": {
|
|
995
|
+
async GET(req) {
|
|
996
|
+
try {
|
|
997
|
+
const runId = req.params.runId;
|
|
998
|
+
const analysis = comparisonDb.getComparisonAnalysis(runId);
|
|
999
|
+
|
|
1000
|
+
if (!analysis) {
|
|
1001
|
+
return Response.json(
|
|
1002
|
+
{ error: "Comparison analysis not found" },
|
|
1003
|
+
{ status: 404 },
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
return Response.json(analysis);
|
|
1008
|
+
} catch (error) {
|
|
1009
|
+
console.error("Error fetching comparison analysis:", error);
|
|
1010
|
+
return Response.json(
|
|
1011
|
+
{
|
|
1012
|
+
error:
|
|
1013
|
+
error instanceof Error
|
|
1014
|
+
? error.message
|
|
1015
|
+
: "Failed to fetch comparison analysis",
|
|
1016
|
+
},
|
|
1017
|
+
{ status: 500 },
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
},
|
|
1021
|
+
},
|
|
1022
|
+
|
|
1023
|
+
"/api/comparison-analysis/:runId/exists": {
|
|
1024
|
+
async GET(req) {
|
|
1025
|
+
try {
|
|
1026
|
+
const runId = req.params.runId;
|
|
1027
|
+
const exists = comparisonDb.hasComparisonAnalysis(runId);
|
|
1028
|
+
return Response.json({ exists });
|
|
1029
|
+
} catch (_error) {
|
|
1030
|
+
return Response.json({ exists: false });
|
|
1031
|
+
}
|
|
1032
|
+
},
|
|
1033
|
+
},
|
|
1034
|
+
|
|
865
1035
|
// Serve index.html for all unmatched routes (SPA routing)
|
|
866
1036
|
"/*": index,
|
|
867
1037
|
},
|
package/src/types.ts
CHANGED
|
@@ -54,6 +54,8 @@ export interface AgentMessage {
|
|
|
54
54
|
timestamp: number; // end_time_unix_nano of the chat span
|
|
55
55
|
type: "chat" | "tool_call";
|
|
56
56
|
toolName?: string; // Only for tool_call type
|
|
57
|
+
toolInput?: unknown; // Only for tool_call type - the input/args to the tool
|
|
58
|
+
toolOutput?: unknown; // Only for tool_call type - the result from the tool
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
export interface TraceDetail extends TraceDetailRaw {
|
|
@@ -90,6 +92,7 @@ export interface ComparisonConfig {
|
|
|
90
92
|
variantModel?: string | undefined;
|
|
91
93
|
variantSystemPrompt?: string | undefined;
|
|
92
94
|
variantTools?: string[] | undefined; // JSON array of tool names
|
|
95
|
+
hypothesis?: string | undefined; // User's expected outcome: "I expect this change to..."
|
|
93
96
|
createdAt: string;
|
|
94
97
|
updatedAt: string;
|
|
95
98
|
}
|
|
@@ -101,6 +104,7 @@ export interface ComparisonConfigRow {
|
|
|
101
104
|
variant_model: string | null;
|
|
102
105
|
variant_system_prompt: string | null;
|
|
103
106
|
variant_tools: string | null; // JSON string
|
|
107
|
+
hypothesis: string | null;
|
|
104
108
|
created_at: string;
|
|
105
109
|
updated_at: string;
|
|
106
110
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@import "tw-animate-css";
|
|
3
|
+
|
|
4
|
+
@custom-variant dark (&:is(.dark *));
|
|
5
|
+
|
|
6
|
+
@theme inline {
|
|
7
|
+
--color-background: var(--background);
|
|
8
|
+
--color-foreground: var(--foreground);
|
|
9
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
10
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
11
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
12
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
13
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
14
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
15
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
16
|
+
--color-sidebar: var(--sidebar);
|
|
17
|
+
--color-chart-5: var(--chart-5);
|
|
18
|
+
--color-chart-4: var(--chart-4);
|
|
19
|
+
--color-chart-3: var(--chart-3);
|
|
20
|
+
--color-chart-2: var(--chart-2);
|
|
21
|
+
--color-chart-1: var(--chart-1);
|
|
22
|
+
--color-ring: var(--ring);
|
|
23
|
+
--color-input: var(--input);
|
|
24
|
+
--color-border: var(--border);
|
|
25
|
+
--color-destructive: var(--destructive);
|
|
26
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
27
|
+
--color-accent: var(--accent);
|
|
28
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
29
|
+
--color-muted: var(--muted);
|
|
30
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
31
|
+
--color-secondary: var(--secondary);
|
|
32
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
33
|
+
--color-primary: var(--primary);
|
|
34
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
35
|
+
--color-popover: var(--popover);
|
|
36
|
+
--color-card-foreground: var(--card-foreground);
|
|
37
|
+
--color-card: var(--card);
|
|
38
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
39
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
40
|
+
--radius-lg: var(--radius);
|
|
41
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
:root {
|
|
45
|
+
--radius: 0.625rem;
|
|
46
|
+
--background: oklch(1 0 0);
|
|
47
|
+
--foreground: oklch(0.145 0 0);
|
|
48
|
+
--card: oklch(1 0 0);
|
|
49
|
+
--card-foreground: oklch(0.145 0 0);
|
|
50
|
+
--popover: oklch(1 0 0);
|
|
51
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
52
|
+
--primary: oklch(0.205 0 0);
|
|
53
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
54
|
+
--secondary: oklch(0.97 0 0);
|
|
55
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
56
|
+
--muted: oklch(0.97 0 0);
|
|
57
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
58
|
+
--accent: oklch(0.97 0 0);
|
|
59
|
+
--accent-foreground: oklch(0.205 0 0);
|
|
60
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
61
|
+
--border: oklch(0.922 0 0);
|
|
62
|
+
--input: oklch(0.922 0 0);
|
|
63
|
+
--ring: oklch(0.708 0 0);
|
|
64
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
65
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
66
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
67
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
68
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
69
|
+
--sidebar: oklch(0.985 0 0);
|
|
70
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
71
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
72
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
73
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
74
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
75
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
76
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.dark {
|
|
80
|
+
--background: oklch(0.145 0 0);
|
|
81
|
+
--foreground: oklch(0.985 0 0);
|
|
82
|
+
--card: oklch(0.205 0 0);
|
|
83
|
+
--card-foreground: oklch(0.985 0 0);
|
|
84
|
+
--popover: oklch(0.205 0 0);
|
|
85
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
86
|
+
--primary: oklch(0.922 0 0);
|
|
87
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
88
|
+
--secondary: oklch(0.269 0 0);
|
|
89
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
90
|
+
--muted: oklch(0.269 0 0);
|
|
91
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
92
|
+
--accent: oklch(0.269 0 0);
|
|
93
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
94
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
95
|
+
--border: oklch(1 0 0 / 10%);
|
|
96
|
+
--input: oklch(1 0 0 / 15%);
|
|
97
|
+
--ring: oklch(0.556 0 0);
|
|
98
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
99
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
100
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
101
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
102
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
103
|
+
--sidebar: oklch(0.205 0 0);
|
|
104
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
105
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
106
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
107
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
108
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
109
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
110
|
+
--sidebar-ring: oklch(0.556 0 0);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@layer base {
|
|
114
|
+
* {
|
|
115
|
+
@apply border-border outline-ring/50;
|
|
116
|
+
}
|
|
117
|
+
body {
|
|
118
|
+
@apply bg-background text-foreground;
|
|
119
|
+
}
|
|
120
|
+
}
|