@phaseo/devtools-viewer 0.2.1 → 0.2.2

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.
@@ -0,0 +1,273 @@
1
+ import { Hono } from "hono";
2
+ import { serve } from "@hono/node-server";
3
+ import { serveStatic } from "@hono/node-server/serve-static";
4
+ import { DevToolsWriter } from "../core.js";
5
+ import * as path from "path";
6
+ import * as fs from "fs";
7
+ import { fileURLToPath } from "url";
8
+ const app = new Hono();
9
+ // Configuration
10
+ const DEFAULT_DEVTOOLS_DIR = ".phaseo-devtools";
11
+ const BASE_PORT = parseInt(process.env.PORT || "4983", 10);
12
+ const getArgValue = (flag) => {
13
+ const idx = process.argv.indexOf(flag);
14
+ if (idx === -1)
15
+ return undefined;
16
+ return process.argv[idx + 1];
17
+ };
18
+ const portArg = getArgValue("--port");
19
+ const resolvedPort = portArg ? parseInt(portArg, 10) : BASE_PORT;
20
+ const dirArg = getArgValue("--dir");
21
+ const SERVER_DIR = path.dirname(fileURLToPath(import.meta.url));
22
+ const PUBLIC_DIR = path.resolve(SERVER_DIR, "../../public");
23
+ const resolveDevtoolsDir = () => dirArg ||
24
+ process.env.PHASEO_DEVTOOLS_DIR ||
25
+ DEFAULT_DEVTOOLS_DIR;
26
+ let devtoolsDir = resolveDevtoolsDir();
27
+ let writer = new DevToolsWriter(devtoolsDir);
28
+ function getGatewayRequestId(entry) {
29
+ const metadataRequestId = entry.metadata?.request_id;
30
+ if (typeof metadataRequestId === "string" && metadataRequestId.trim().length > 0) {
31
+ return metadataRequestId;
32
+ }
33
+ const response = entry.response;
34
+ if (!response || typeof response !== "object") {
35
+ return undefined;
36
+ }
37
+ const responseRecord = response;
38
+ const metadata = responseRecord.metadata;
39
+ if (metadata && typeof metadata === "object") {
40
+ const gatewayId = metadata.phaseo_request_id;
41
+ if (typeof gatewayId === "string" && gatewayId.trim().length > 0) {
42
+ return gatewayId;
43
+ }
44
+ }
45
+ const requestId = responseRecord.request_id;
46
+ if (typeof requestId === "string" && requestId.trim().length > 0) {
47
+ return requestId;
48
+ }
49
+ return undefined;
50
+ }
51
+ function matchesGenerationLookupId(entry, lookupId) {
52
+ return entry.id === lookupId || getGatewayRequestId(entry) === lookupId;
53
+ }
54
+ /**
55
+ * API Routes
56
+ */
57
+ // Get all generations with optional filtering
58
+ app.get("/api/generations", (c) => {
59
+ try {
60
+ const entries = writer.readEntries();
61
+ // Query parameters for filtering
62
+ const type = c.req.query("type");
63
+ const model = c.req.query("model");
64
+ const hasError = c.req.query("hasError");
65
+ const limit = parseInt(c.req.query("limit") || "100", 10);
66
+ const offset = parseInt(c.req.query("offset") || "0", 10);
67
+ let filtered = entries;
68
+ if (type) {
69
+ filtered = filtered.filter((e) => e.type === type);
70
+ }
71
+ if (model) {
72
+ filtered = filtered.filter((e) => e.metadata.model === model);
73
+ }
74
+ if (hasError === "true") {
75
+ filtered = filtered.filter((e) => e.error !== null);
76
+ }
77
+ else if (hasError === "false") {
78
+ filtered = filtered.filter((e) => e.error === null);
79
+ }
80
+ // Sort by timestamp (most recent first)
81
+ filtered.sort((a, b) => b.timestamp - a.timestamp);
82
+ // Pagination
83
+ const paginated = filtered.slice(offset, offset + limit);
84
+ return c.json({
85
+ generations: paginated,
86
+ total: filtered.length,
87
+ offset,
88
+ limit
89
+ });
90
+ }
91
+ catch (error) {
92
+ console.error("Error reading generations:", error);
93
+ return c.json({ error: "Failed to read generations" }, 500);
94
+ }
95
+ });
96
+ // Get a specific generation by ID
97
+ app.get("/api/generations/:id", (c) => {
98
+ try {
99
+ const lookupId = c.req.param("id");
100
+ const entries = writer.readEntries();
101
+ const entry = entries.find((e) => matchesGenerationLookupId(e, lookupId));
102
+ if (!entry) {
103
+ return c.json({ error: "Generation not found" }, 404);
104
+ }
105
+ return c.json(entry);
106
+ }
107
+ catch (error) {
108
+ console.error("Error reading generation:", error);
109
+ return c.json({ error: "Failed to read generation" }, 500);
110
+ }
111
+ });
112
+ // Get aggregate statistics
113
+ app.get("/api/stats", (c) => {
114
+ try {
115
+ const entries = writer.readEntries();
116
+ const stats = {
117
+ total_requests: entries.length,
118
+ total_errors: entries.filter((e) => e.error !== null).length,
119
+ total_cost: entries.reduce((sum, e) => sum + (e.metadata.cost?.total_cost || 0), 0),
120
+ total_tokens: entries.reduce((sum, e) => sum + (e.metadata.usage?.total_tokens || 0), 0),
121
+ total_duration_ms: entries.reduce((sum, e) => sum + e.duration_ms, 0),
122
+ by_endpoint: {},
123
+ by_model: {}
124
+ };
125
+ // Aggregate by endpoint type
126
+ entries.forEach((entry) => {
127
+ const type = entry.type;
128
+ if (!stats.by_endpoint[type]) {
129
+ stats.by_endpoint[type] = {
130
+ count: 0,
131
+ errors: 0,
132
+ avg_duration_ms: 0,
133
+ total_cost: 0
134
+ };
135
+ }
136
+ stats.by_endpoint[type].count++;
137
+ if (entry.error)
138
+ stats.by_endpoint[type].errors++;
139
+ stats.by_endpoint[type].total_cost += entry.metadata.cost?.total_cost || 0;
140
+ });
141
+ // Calculate average duration for each endpoint
142
+ Object.keys(stats.by_endpoint).forEach((type) => {
143
+ const typeEntries = entries.filter((e) => e.type === type);
144
+ const totalDuration = typeEntries.reduce((sum, e) => sum + e.duration_ms, 0);
145
+ stats.by_endpoint[type].avg_duration_ms = totalDuration / typeEntries.length;
146
+ });
147
+ // Aggregate by model
148
+ entries.forEach((entry) => {
149
+ const model = entry.metadata.model;
150
+ if (!model)
151
+ return;
152
+ if (!stats.by_model[model]) {
153
+ stats.by_model[model] = {
154
+ count: 0,
155
+ tokens: 0,
156
+ cost: 0
157
+ };
158
+ }
159
+ stats.by_model[model].count++;
160
+ stats.by_model[model].tokens += entry.metadata.usage?.total_tokens || 0;
161
+ stats.by_model[model].cost += entry.metadata.cost?.total_cost || 0;
162
+ });
163
+ return c.json(stats);
164
+ }
165
+ catch (error) {
166
+ console.error("Error calculating stats:", error);
167
+ return c.json({ error: "Failed to calculate stats" }, 500);
168
+ }
169
+ });
170
+ // Get session metadata
171
+ app.get("/api/metadata", (c) => {
172
+ try {
173
+ const metadata = writer.readSessionMetadata();
174
+ return c.json(metadata || {});
175
+ }
176
+ catch (error) {
177
+ console.error("Error reading metadata:", error);
178
+ return c.json({ error: "Failed to read metadata" }, 500);
179
+ }
180
+ });
181
+ // Export data
182
+ app.get("/api/export", (c) => {
183
+ const format = c.req.query("format") || "json";
184
+ try {
185
+ const entries = writer.readEntries();
186
+ if (format === "json") {
187
+ c.header("Content-Type", "application/json");
188
+ c.header("Content-Disposition", 'attachment; filename="devtools-export.json"');
189
+ return c.json(entries);
190
+ }
191
+ if (format === "jsonl") {
192
+ const jsonl = entries.map((e) => JSON.stringify(e)).join("\n");
193
+ c.header("Content-Type", "application/x-ndjson");
194
+ c.header("Content-Disposition", 'attachment; filename="devtools-export.jsonl"');
195
+ return c.text(jsonl);
196
+ }
197
+ return c.json({ error: "Unsupported format. Use 'json' or 'jsonl'" }, 400);
198
+ }
199
+ catch (error) {
200
+ console.error("Error exporting data:", error);
201
+ return c.json({ error: "Failed to export data" }, 500);
202
+ }
203
+ });
204
+ // Serve binary assets (images, audio, video)
205
+ app.get("/devtools-assets/*", async (c) => {
206
+ const assetPath = c.req.path.replace("/devtools-assets/", "");
207
+ const fullPath = path.join(devtoolsDir, "assets", assetPath);
208
+ if (!fs.existsSync(fullPath)) {
209
+ return c.json({ error: "Asset not found" }, 404);
210
+ }
211
+ const buffer = fs.readFileSync(fullPath);
212
+ const ext = path.extname(fullPath).toLowerCase();
213
+ // Set appropriate content type
214
+ const contentTypes = {
215
+ ".png": "image/png",
216
+ ".jpg": "image/jpeg",
217
+ ".jpeg": "image/jpeg",
218
+ ".gif": "image/gif",
219
+ ".webp": "image/webp",
220
+ ".mp3": "audio/mpeg",
221
+ ".wav": "audio/wav",
222
+ ".m4a": "audio/mp4",
223
+ ".mp4": "video/mp4",
224
+ ".avi": "video/x-msvideo"
225
+ };
226
+ c.header("Content-Type", contentTypes[ext] || "application/octet-stream");
227
+ return c.body(buffer);
228
+ });
229
+ // Serve React frontend
230
+ app.use("/*", serveStatic({ root: PUBLIC_DIR }));
231
+ // Fallback to index.html for client-side routing
232
+ app.get("*", serveStatic({ path: path.join(PUBLIC_DIR, "index.html") }));
233
+ /**
234
+ * Start server
235
+ */
236
+ export function startServer(port = resolvedPort) {
237
+ console.log(" █████╗ ██╗ ███████╗████████╗ █████╗ ████████╗███████╗ ██████╗ ███████╗██╗ ██╗████████╗ ██████╗ ██████╗ ██╗ ███████╗");
238
+ console.log("██╔══██╗██║ ██╔════╝╚══██╔══╝██╔══██╗╚══██╔══╝██╔════╝ ██╔══██╗██╔════╝██║ ██║╚══██╔══╝██╔═══██╗██╔═══██╗██║ ██╔════╝");
239
+ console.log("███████║██║ ███████╗ ██║ ███████║ ██║ ███████╗ ██║ ██║█████╗ ██║ ██║ ██║ ██║ ██║██║ ██║██║ ███████╗");
240
+ console.log("██╔══██║██║ ╚════██║ ██║ ██╔══██║ ██║ ╚════██║ ██║ ██║██╔══╝ ╚██╗ ██╔╝ ██║ ██║ ██║██║ ██║██║ ╚════██║");
241
+ console.log("██║ ██║██║ ███████║ ██║ ██║ ██║ ██║ ███████║ ██████╔╝███████╗ ╚████╔╝ ██║ ╚██████╔╝╚██████╔╝███████╗███████║");
242
+ console.log("╚═╝ ╚═╝╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═════╝ ╚══════╝ ╚═══╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝");
243
+ console.log("");
244
+ devtoolsDir = resolveDevtoolsDir();
245
+ writer = new DevToolsWriter(devtoolsDir);
246
+ console.log("Phaseo Devtools Viewer starting...");
247
+ console.log(`Watching directory: ${path.resolve(devtoolsDir)}`);
248
+ console.log(`Server running at http://localhost:${port}`);
249
+ console.log(`View Devtools at http://localhost:${port}`);
250
+ console.log("Monitor requests, responses, costs, and usage in real time.");
251
+ if (!fs.existsSync(path.join(PUBLIC_DIR, "index.html"))) {
252
+ console.warn("UI build not found. Run `pnpm --filter @phaseo/devtools-viewer build` or use `pnpm --filter @phaseo/devtools-viewer dev` for the UI dev server.");
253
+ }
254
+ serve({
255
+ fetch: app.fetch,
256
+ port
257
+ });
258
+ }
259
+ // Start server if this file is run directly
260
+ const moduleUrl = new URL(import.meta.url);
261
+ moduleUrl.search = "";
262
+ moduleUrl.hash = "";
263
+ const modulePath = fileURLToPath(moduleUrl);
264
+ const argvPaths = process.argv.map((arg) => path.resolve(arg));
265
+ const isDirectRun = argvPaths.includes(path.resolve(modulePath)) ||
266
+ argvPaths.some((arg) => arg.endsWith(path.normalize("src/server/index.ts")) ||
267
+ arg.endsWith(path.normalize("dist/server/index.js")));
268
+ const shouldStart = isDirectRun || process.argv.includes("--start");
269
+ if (shouldStart) {
270
+ startServer(resolvedPort);
271
+ }
272
+ export { app };
273
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,28 +1,70 @@
1
1
  {
2
2
  "name": "@phaseo/devtools-viewer",
3
- "version": "0.2.1",
4
- "description": "Phaseo DevTools viewer transition package.",
3
+ "description": "Web-based viewer for Phaseo DevTools telemetry",
4
+ "version": "0.2.2",
5
5
  "type": "module",
6
+ "private": false,
6
7
  "license": "MIT",
8
+ "engines": {
9
+ "node": ">=22.12.0"
10
+ },
7
11
  "bin": {
8
- "phaseo-devtools": "bin/phaseo-devtools.js",
9
- "ai-stats-devtools": "bin/phaseo-devtools.js"
12
+ "phaseo-devtools": "./dist/cli/index.js"
10
13
  },
11
- "main": "./dist/index.js",
12
- "types": "./dist/index.d.ts",
14
+ "main": "./dist/server/index.js",
15
+ "types": "./dist/server/index.d.ts",
13
16
  "files": [
14
- "bin",
15
17
  "dist",
16
- "README.md"
18
+ "public",
19
+ "README.md",
20
+ "LICENSE"
17
21
  ],
18
22
  "dependencies": {
19
- "@ai-stats/devtools-viewer": "0.2.1"
23
+ "@hono/node-server": "^2.0.4",
24
+ "@radix-ui/react-select": "^2.2.6",
25
+ "@tanstack/react-query": "^5.100.14",
26
+ "clsx": "^2.1.1",
27
+ "hono": "^4.12.27",
28
+ "lucide-react": "^1.17.0",
29
+ "react": "^19.2.7",
30
+ "react-dom": "^19.2.7",
31
+ "recharts": "^3.8.1",
32
+ "tailwind-merge": "^3.3.1"
33
+ },
34
+ "devDependencies": {
35
+ "@tailwindcss/postcss": "^4.3.2",
36
+ "@types/node": "^25.6.0",
37
+ "@types/react": "^19.2.17",
38
+ "@types/react-dom": "^19.0.3",
39
+ "@vitejs/plugin-react": "^6.0.3",
40
+ "autoprefixer": "^10.5.2",
41
+ "concurrently": "^10.0.3",
42
+ "postcss": "^8.5.14",
43
+ "rimraf": "^6.0.0",
44
+ "tailwindcss": "^4.3.2",
45
+ "tsx": "^4.7.0",
46
+ "typescript": "^6.0.3",
47
+ "vite": "^8.0.16",
48
+ "vitest": "^4.1.7",
49
+ "wait-on": "^9.0.10"
20
50
  },
21
51
  "repository": {
22
52
  "type": "git",
23
- "url": "git+https://github.com/phaseoteam/Phaseo.git"
53
+ "url": "https://github.com/phaseoteam/Phaseo"
24
54
  },
25
55
  "publishConfig": {
26
56
  "access": "public"
57
+ },
58
+ "scripts": {
59
+ "clean": "rimraf dist public",
60
+ "build:server": "tsc -p tsconfig.server.json",
61
+ "build:ui": "vite build",
62
+ "build": "pnpm run build:ui && pnpm run build:server",
63
+ "dev:server": "pnpm exec tsx watch src/server/index.ts -- --start --port 4984 --dir ../../../.phaseo-devtools",
64
+ "dev:ui": "pnpm exec wait-on tcp:4984 && vite --port 4983",
65
+ "dev": "node dev-start.mjs",
66
+ "dev:old": "concurrently \"pnpm run dev:server\" \"pnpm run dev:ui\"",
67
+ "start": "node dist/cli/index.js start",
68
+ "test": "vitest"
27
69
  }
28
- }
70
+ }
@@ -0,0 +1,2 @@
1
+ /*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-pan-x:initial;--tw-pan-y:initial;--tw-pinch-zoom:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-x-reverse:0;--tw-border-style:solid;--tw-divide-y-reverse:0;--tw-leading:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}.\@container{container-type:inline-size}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.not-sr-only{clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:static;overflow:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.isolate{isolation:isolate}.isolation-auto{isolation:auto}.z-10{z-index:10}.z-50{z-index:50}.container{width:100%}.-mt-px{margin-top:-1px}.block{display:block}.contents{display:contents}.flex{display:flex}.flow-root{display:flow-root}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.inline-grid{display:inline-grid}.inline-table{display:inline-table}.list-item{display:list-item}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.table-column{display:table-column}.table-column-group{display:table-column-group}.table-footer-group{display:table-footer-group}.table-header-group{display:table-header-group}.table-row{display:table-row}.table-row-group{display:table-row-group}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-\[24rem\]{max-height:24rem}.max-h-\[28rem\]{max-height:28rem}.min-h-\[72px\]{min-height:72px}.min-h-\[400px\]{min-height:400px}.w-\[72px\]{width:72px}.w-full{width:100%}.max-w-\[18rem\]{max-width:18rem}.min-w-\[2\.5rem\]{min-width:2.5rem}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.border-collapse{border-collapse:collapse}.translate-none{translate:none}.scale-3d{scale:var(--tw-scale-x) var(--tw-scale-y) var(--tw-scale-z)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.touch-pinch-zoom{--tw-pinch-zoom:pinch-zoom;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.resize{resize:both}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-\[minmax\(0\,220px\)_minmax\(0\,1fr\)_72px\]{grid-template-columns:minmax(0,220px) minmax(0,1fr) 72px}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}:where(.space-y-reverse>:not(:last-child)){--tw-space-y-reverse:1}:where(.space-x-reverse>:not(:last-child)){--tw-space-x-reverse:1}:where(.divide-x>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(1px * var(--tw-divide-x-reverse));border-inline-end-width:calc(1px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-y-reverse>:not(:last-child)){--tw-divide-y-reverse:1}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-x{border-inline-style:var(--tw-border-style);border-inline-width:1px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-s{border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px}.border-e{border-inline-end-style:var(--tw-border-style);border-inline-end-width:1px}.border-bs{border-block-start-style:var(--tw-border-style);border-block-start-width:1px}.border-be{border-block-end-style:var(--tw-border-style);border-block-end-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-l-transparent{border-left-color:#0000}.bg-repeat{background-repeat:repeat}.mask-no-clip{-webkit-mask-clip:no-clip;mask-clip:no-clip}.mask-repeat{-webkit-mask-repeat:repeat;mask-repeat:repeat}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.leading-none{--tw-leading:1;line-height:1}.tracking-\[0\.3em\]{--tw-tracking:.3em;letter-spacing:.3em}.tracking-\[0\.14em\]{--tw-tracking:.14em;letter-spacing:.14em}.tracking-\[0\.16em\]{--tw-tracking:.16em;letter-spacing:.16em}.tracking-\[0\.24em\]{--tw-tracking:.24em;letter-spacing:.24em}.text-wrap{text-wrap:wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.text-clip{text-overflow:clip}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.italic{font-style:italic}.not-italic{font-style:normal}.diagonal-fractions{--tw-numeric-fraction:diagonal-fractions;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.lining-nums{--tw-numeric-figure:lining-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.oldstyle-nums{--tw-numeric-figure:oldstyle-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.proportional-nums{--tw-numeric-spacing:proportional-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.slashed-zero{--tw-slashed-zero:slashed-zero;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.stacked-fractions{--tw-numeric-fraction:stacked-fractions;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.normal-nums{font-variant-numeric:normal}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.overline{text-decoration-line:overline}.underline{text-decoration-line:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.subpixel-antialiased{-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.inset-ring{--tw-inset-ring-shadow:inset 0 0 0 1px var(--tw-inset-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.backdrop-grayscale{--tw-backdrop-grayscale:grayscale(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-invert{--tw-backdrop-invert:invert(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-sepia{--tw-backdrop-sepia:sepia(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}:where(.divide-x-reverse>:not(:last-child)){--tw-divide-x-reverse:1}.ring-inset{--tw-ring-inset:inset}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media (hover:hover){.hover\:underline:hover{text-decoration-line:underline}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}@media (prefers-color-scheme:dark){.dark\:block{display:block}.dark\:hidden{display:none}}.\[\&\>span\]\:line-clamp-1>span{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}:root{--radius:.625rem;--background:oklch(100% 0 0);--foreground:oklch(14.479% .00002 271.152);--card:oklch(100% 0 0);--card-foreground:oklch(14.5% 0 0);--popover:oklch(100% 0 0);--popover-foreground:oklch(14.5% 0 0);--primary:oklch(20.5% 0 0);--primary-foreground:oklch(98.5% 0 0);--secondary:oklch(97% 0 0);--secondary-foreground:oklch(20.5% 0 0);--muted:oklch(97.015% .00011 271.152);--muted-foreground:oklch(55.6% 0 0);--accent:oklch(97% 0 0);--accent-foreground:oklch(20.5% 0 0);--destructive:oklch(57.7% .245 27.325);--destructive-foreground:oklch(98.5% 0 0);--border:oklch(92.2% 0 0);--input:oklch(92.2% 0 0);--ring:oklch(70.8% 0 0);--sidebar:oklch(100% 0 0);--sidebar-foreground:oklch(14.5% 0 0);--sidebar-primary:oklch(20.5% 0 0);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(97% 0 0);--sidebar-accent-foreground:oklch(20.5% 0 0);--sidebar-border:oklch(92.2% 0 0);--sidebar-ring:oklch(70.8% 0 0)}.dark{--background:#09090b;--foreground:#fafafa;--card:#09090b;--card-foreground:#fafafa;--popover:#09090b;--popover-foreground:#fafafa;--primary:#f4f4f5;--primary-foreground:#18181b;--secondary:#18181b;--secondary-foreground:#f4f4f5;--muted:#18181b;--muted-foreground:#a1a1aa;--accent:#27272a;--accent-foreground:#fafafa;--destructive:#ef4444;--destructive-foreground:#fafafa;--border:#27272a;--input:#27272a;--ring:#d4d4d8;--sidebar:#09090b;--sidebar-foreground:#fafafa;--sidebar-primary:#f4f4f5;--sidebar-primary-foreground:#18181b;--sidebar-accent:#18181b;--sidebar-accent-foreground:#fafafa;--sidebar-border:#27272a;--sidebar-ring:#d4d4d8}body{color:var(--foreground);background-color:var(--background);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;margin:0;padding:0;font-family:Segoe UI,SF Pro Text,Inter,Helvetica Neue,Arial,sans-serif;transition:background-color .3s,color .3s}code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}*{box-sizing:border-box}*,:before,:after{border-color:var(--border)}.dark{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:var(--muted);border-radius:4px}::-webkit-scrollbar-thumb{background:var(--muted-foreground);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:var(--foreground)}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.animate-slide-in{animation:.2s ease-out slideIn}:focus-visible,button:focus-visible{outline:2px solid var(--primary);outline-offset:2px}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-pan-x{syntax:"*";inherits:false}@property --tw-pan-y{syntax:"*";inherits:false}@property --tw-pinch-zoom{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-leading{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}