ocwatch 0.3.0 → 0.5.0
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 +6 -0
- package/package.json +1 -1
- package/src/client/dist/assets/index-BIu7r5_5.css +1 -0
- package/src/client/dist/assets/index-CzAaOuyw.js +50 -0
- package/src/client/dist/index.html +2 -2
- package/src/server/__tests__/helpers/testDb.ts +220 -0
- package/src/server/index.ts +20 -5
- package/src/server/logic/activityLogic.ts +260 -0
- package/src/server/logic/index.ts +2 -0
- package/src/server/logic/sessionLogic.ts +107 -0
- package/src/server/routes/parts.ts +9 -7
- package/src/server/routes/poll.ts +34 -45
- package/src/server/routes/projects.ts +4 -4
- package/src/server/routes/sessions.ts +107 -68
- package/src/server/routes/sse.ts +10 -4
- package/src/server/services/parsing.ts +211 -0
- package/src/server/services/pollService.ts +292 -114
- package/src/server/services/sessionService.ts +178 -106
- package/src/server/storage/db.ts +71 -0
- package/src/server/storage/index.ts +22 -0
- package/src/server/storage/queries.ts +325 -0
- package/src/server/utils/projectResolver.ts +2 -2
- package/src/server/utils/sessionStatus.ts +4 -70
- package/src/server/watcher.ts +187 -82
- package/src/shared/constants.ts +1 -0
- package/src/shared/types/index.ts +39 -5
- package/src/shared/utils/formatTime.ts +3 -1
- package/src/client/dist/assets/index-27vUxwIP.css +0 -1
- package/src/client/dist/assets/index-B1aj6-ff.js +0 -41
- package/src/server/storage/messageParser.ts +0 -169
- package/src/server/storage/partParser.ts +0 -532
- package/src/server/storage/sessionParser.ts +0 -180
- package/src/shared/utils/burstGrouping.ts +0 -99
package/src/server/watcher.ts
CHANGED
|
@@ -1,32 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Uses fs.watch() to detect changes and trigger cache invalidation
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { watch, type FSWatcher } from "node:fs";
|
|
7
|
-
import { join } from "node:path";
|
|
1
|
+
import { existsSync, watch, type FSWatcher } from "node:fs";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
8
3
|
import { EventEmitter } from "node:events";
|
|
9
|
-
import {
|
|
4
|
+
import { homedir } from "node:os";
|
|
10
5
|
|
|
11
6
|
export interface WatcherOptions {
|
|
7
|
+
dbPath?: string;
|
|
12
8
|
storagePath?: string;
|
|
13
9
|
projectPath?: string;
|
|
14
10
|
debounceMs?: number;
|
|
15
11
|
}
|
|
16
12
|
|
|
17
13
|
export class Watcher extends EventEmitter {
|
|
18
|
-
private
|
|
14
|
+
private dbWatcher: FSWatcher | null = null;
|
|
15
|
+
private boulderWatcher: FSWatcher | null = null;
|
|
19
16
|
private debounceTimer: Timer | null = null;
|
|
17
|
+
private rebindTimer: Timer | null = null;
|
|
20
18
|
private readonly debounceMs: number;
|
|
21
|
-
private readonly
|
|
19
|
+
private readonly dbPath: string;
|
|
20
|
+
private readonly walPath: string;
|
|
21
|
+
private readonly boulderPath: string;
|
|
22
|
+
private readonly boulderDirPath: string;
|
|
22
23
|
private readonly projectPath: string;
|
|
24
|
+
private dbWatcherTarget: string | null = null;
|
|
25
|
+
private boulderWatcherTarget: string | null = null;
|
|
23
26
|
private isRunning: boolean = false;
|
|
24
27
|
|
|
25
28
|
constructor(options: WatcherOptions = {}) {
|
|
26
29
|
super();
|
|
27
|
-
this.
|
|
30
|
+
this.dbPath = resolveDbPath(options);
|
|
31
|
+
this.walPath = `${this.dbPath}-wal`;
|
|
28
32
|
this.projectPath = options.projectPath || process.cwd();
|
|
29
|
-
this.
|
|
33
|
+
this.boulderPath = join(this.projectPath, ".sisyphus", "boulder.json");
|
|
34
|
+
this.boulderDirPath = join(this.projectPath, ".sisyphus");
|
|
35
|
+
this.debounceMs = options.debounceMs ?? 100;
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
start(): void {
|
|
@@ -36,58 +42,10 @@ export class Watcher extends EventEmitter {
|
|
|
36
42
|
|
|
37
43
|
this.isRunning = true;
|
|
38
44
|
|
|
39
|
-
const sessionDir = join(this.storagePath, "opencode", "storage", "session");
|
|
40
|
-
const messageDir = join(this.storagePath, "opencode", "storage", "message");
|
|
41
|
-
|
|
42
45
|
try {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
this.handleChange.bind(this)
|
|
47
|
-
);
|
|
48
|
-
this.watchers.push(sessionWatcher);
|
|
49
|
-
|
|
50
|
-
const messageWatcher = watch(
|
|
51
|
-
messageDir,
|
|
52
|
-
{ recursive: true },
|
|
53
|
-
this.handleChange.bind(this)
|
|
54
|
-
);
|
|
55
|
-
this.watchers.push(messageWatcher);
|
|
56
|
-
|
|
57
|
-
const partDir = join(this.storagePath, "opencode", "storage", "part");
|
|
58
|
-
try {
|
|
59
|
-
const partWatcher = watch(
|
|
60
|
-
partDir,
|
|
61
|
-
{ recursive: true },
|
|
62
|
-
this.handleChange.bind(this)
|
|
63
|
-
);
|
|
64
|
-
this.watchers.push(partWatcher);
|
|
65
|
-
} catch {
|
|
66
|
-
// part directory may not exist yet
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const boulderDir = join(this.projectPath, ".sisyphus");
|
|
70
|
-
try {
|
|
71
|
-
const boulderWatcher = watch(
|
|
72
|
-
boulderDir,
|
|
73
|
-
{ recursive: true },
|
|
74
|
-
this.handleChange.bind(this)
|
|
75
|
-
);
|
|
76
|
-
this.watchers.push(boulderWatcher);
|
|
77
|
-
} catch {
|
|
78
|
-
// boulder directory may not exist yet
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
try {
|
|
82
|
-
const projectWatcher = watch(
|
|
83
|
-
this.projectPath,
|
|
84
|
-
{ recursive: true },
|
|
85
|
-
this.handleProjectChange.bind(this)
|
|
86
|
-
);
|
|
87
|
-
this.watchers.push(projectWatcher);
|
|
88
|
-
} catch {
|
|
89
|
-
// project watcher may fail on unsupported environments
|
|
90
|
-
}
|
|
46
|
+
this.rebindDbWatcher();
|
|
47
|
+
this.rebindBoulderWatcher();
|
|
48
|
+
this.startRebindLoop();
|
|
91
49
|
|
|
92
50
|
this.emit("started");
|
|
93
51
|
} catch (error) {
|
|
@@ -96,15 +54,7 @@ export class Watcher extends EventEmitter {
|
|
|
96
54
|
}
|
|
97
55
|
}
|
|
98
56
|
|
|
99
|
-
private
|
|
100
|
-
if (!filename) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (!filename.endsWith(".json")) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
57
|
+
private emitDebouncedChange(eventType: string, filename: string): void {
|
|
108
58
|
if (this.debounceTimer) {
|
|
109
59
|
clearTimeout(this.debounceTimer);
|
|
110
60
|
}
|
|
@@ -115,20 +65,138 @@ export class Watcher extends EventEmitter {
|
|
|
115
65
|
}, this.debounceMs);
|
|
116
66
|
}
|
|
117
67
|
|
|
118
|
-
private
|
|
119
|
-
if (
|
|
68
|
+
private getPreferredDbWatchTarget(): string {
|
|
69
|
+
if (existsSync(this.walPath)) {
|
|
70
|
+
return this.walPath;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return this.dbPath;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private rebindDbWatcher(): void {
|
|
77
|
+
const preferredTarget = this.getPreferredDbWatchTarget();
|
|
78
|
+
if (this.dbWatcher && this.dbWatcherTarget === preferredTarget) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (this.dbWatcher) {
|
|
83
|
+
this.dbWatcher.close();
|
|
84
|
+
this.dbWatcher = null;
|
|
85
|
+
this.dbWatcherTarget = null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!existsSync(preferredTarget)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
this.dbWatcher = watch(preferredTarget, (eventType, filename) => {
|
|
94
|
+
this.handleDbEvent(eventType, filename);
|
|
95
|
+
});
|
|
96
|
+
this.dbWatcherTarget = preferredTarget;
|
|
97
|
+
} catch (error) {
|
|
98
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
99
|
+
if (code === "ENOENT") {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private handleDbEvent(eventType: string, _filename: string | null): void {
|
|
107
|
+
const watchedTarget = this.dbWatcherTarget;
|
|
108
|
+
if (!watchedTarget) {
|
|
120
109
|
return;
|
|
121
110
|
}
|
|
122
111
|
|
|
123
|
-
|
|
112
|
+
this.emitDebouncedChange(eventType, basename(watchedTarget));
|
|
113
|
+
|
|
114
|
+
if (eventType === "rename" || !existsSync(watchedTarget)) {
|
|
115
|
+
this.rebindDbWatcherSafe();
|
|
124
116
|
return;
|
|
125
117
|
}
|
|
126
118
|
|
|
127
|
-
if (
|
|
119
|
+
if (this.getPreferredDbWatchTarget() !== watchedTarget) {
|
|
120
|
+
this.rebindDbWatcherSafe();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private rebindBoulderWatcher(): void {
|
|
125
|
+
const preferredTarget = existsSync(this.boulderPath)
|
|
126
|
+
? this.boulderPath
|
|
127
|
+
: this.boulderDirPath;
|
|
128
|
+
|
|
129
|
+
if (this.boulderWatcher && this.boulderWatcherTarget === preferredTarget) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (this.boulderWatcher) {
|
|
134
|
+
this.boulderWatcher.close();
|
|
135
|
+
this.boulderWatcher = null;
|
|
136
|
+
this.boulderWatcherTarget = null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!existsSync(preferredTarget)) {
|
|
128
140
|
return;
|
|
129
141
|
}
|
|
130
142
|
|
|
131
|
-
this.
|
|
143
|
+
this.boulderWatcher = watch(preferredTarget, (eventType, filename) => {
|
|
144
|
+
this.handleBoulderEvent(eventType, filename);
|
|
145
|
+
});
|
|
146
|
+
this.boulderWatcherTarget = preferredTarget;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private handleBoulderEvent(eventType: string, filename: string | null): void {
|
|
150
|
+
if (this.boulderWatcherTarget === this.boulderDirPath) {
|
|
151
|
+
if (!filename || filename !== "boulder.json") {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this.emitDebouncedChange(eventType, ".sisyphus/boulder.json");
|
|
157
|
+
|
|
158
|
+
if (eventType === "rename") {
|
|
159
|
+
this.rebindBoulderWatcherSafe();
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const preferredTarget = existsSync(this.boulderPath)
|
|
164
|
+
? this.boulderPath
|
|
165
|
+
: this.boulderDirPath;
|
|
166
|
+
if (preferredTarget !== this.boulderWatcherTarget) {
|
|
167
|
+
this.rebindBoulderWatcherSafe();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private startRebindLoop(): void {
|
|
172
|
+
if (this.rebindTimer) {
|
|
173
|
+
clearInterval(this.rebindTimer);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
this.rebindTimer = setInterval(() => {
|
|
177
|
+
if (!this.isRunning) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.rebindDbWatcherSafe();
|
|
182
|
+
this.rebindBoulderWatcherSafe();
|
|
183
|
+
}, 1000);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private rebindDbWatcherSafe(): void {
|
|
187
|
+
try {
|
|
188
|
+
this.rebindDbWatcher();
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.warn('[watcher] Failed to rebind DB watcher:', error instanceof Error ? error.message : error);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private rebindBoulderWatcherSafe(): void {
|
|
195
|
+
try {
|
|
196
|
+
this.rebindBoulderWatcher();
|
|
197
|
+
} catch (error) {
|
|
198
|
+
console.warn('[watcher] Failed to rebind boulder watcher:', error instanceof Error ? error.message : error);
|
|
199
|
+
}
|
|
132
200
|
}
|
|
133
201
|
|
|
134
202
|
stop(): void {
|
|
@@ -141,20 +209,57 @@ export class Watcher extends EventEmitter {
|
|
|
141
209
|
this.debounceTimer = null;
|
|
142
210
|
}
|
|
143
211
|
|
|
144
|
-
|
|
145
|
-
|
|
212
|
+
if (this.rebindTimer) {
|
|
213
|
+
clearInterval(this.rebindTimer);
|
|
214
|
+
this.rebindTimer = null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (this.dbWatcher) {
|
|
218
|
+
this.dbWatcher.close();
|
|
219
|
+
this.dbWatcher = null;
|
|
220
|
+
this.dbWatcherTarget = null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (this.boulderWatcher) {
|
|
224
|
+
this.boulderWatcher.close();
|
|
225
|
+
this.boulderWatcher = null;
|
|
226
|
+
this.boulderWatcherTarget = null;
|
|
146
227
|
}
|
|
147
228
|
|
|
148
|
-
this.watchers = [];
|
|
149
229
|
this.isRunning = false;
|
|
150
230
|
this.emit("stopped");
|
|
151
231
|
}
|
|
152
232
|
|
|
233
|
+
close(): void {
|
|
234
|
+
this.stop();
|
|
235
|
+
}
|
|
236
|
+
|
|
153
237
|
getIsRunning(): boolean {
|
|
154
238
|
return this.isRunning;
|
|
155
239
|
}
|
|
156
240
|
}
|
|
157
241
|
|
|
158
|
-
|
|
159
|
-
|
|
242
|
+
function resolveDbPath(options: WatcherOptions): string {
|
|
243
|
+
if (options.dbPath) {
|
|
244
|
+
return normalizeDbPathInput(options.dbPath);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (options.storagePath) {
|
|
248
|
+
return join(options.storagePath, "opencode", "opencode.db");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const storageRoot = process.env.XDG_DATA_HOME || join(homedir(), ".local", "share");
|
|
252
|
+
return join(storageRoot, "opencode", "opencode.db");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function normalizeDbPathInput(dbPathOrStoragePath: string): string {
|
|
256
|
+
if (dbPathOrStoragePath.endsWith(".db")) {
|
|
257
|
+
return dbPathOrStoragePath;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return join(dbPathOrStoragePath, "opencode", "opencode.db");
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export function createWatcher(dbPath?: string, projectPath?: string): Watcher {
|
|
264
|
+
return new Watcher({ dbPath, projectPath });
|
|
160
265
|
}
|
package/src/shared/constants.ts
CHANGED
|
@@ -10,6 +10,7 @@ export const TWENTY_FOUR_HOURS_MS = 86400000 as const;
|
|
|
10
10
|
|
|
11
11
|
// API limits
|
|
12
12
|
export const MAX_SESSIONS_LIMIT = 20 as const;
|
|
13
|
+
/** Messages returned per session in API responses (client-facing limit) */
|
|
13
14
|
export const MAX_MESSAGES_LIMIT = 100 as const;
|
|
14
15
|
|
|
15
16
|
// Cache TTL
|
|
@@ -21,6 +21,36 @@ export interface SessionMetadata {
|
|
|
21
21
|
updatedAt: Date;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* SessionSummary is a lightweight session representation used in poll responses
|
|
26
|
+
* and as the summary field in SessionDetail
|
|
27
|
+
*/
|
|
28
|
+
export interface SessionSummary {
|
|
29
|
+
id: string;
|
|
30
|
+
projectID: string;
|
|
31
|
+
title: string;
|
|
32
|
+
status?: SessionStatus;
|
|
33
|
+
activityType?: SessionActivityType;
|
|
34
|
+
currentAction?: string | null;
|
|
35
|
+
agent?: string | null;
|
|
36
|
+
modelID?: string | null;
|
|
37
|
+
providerID?: string | null;
|
|
38
|
+
updatedAt: Date;
|
|
39
|
+
createdAt: Date;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* SessionDetail is the full detail response for a single session
|
|
44
|
+
* Contains summary, messages, activity tree, todos, and optional stats
|
|
45
|
+
*/
|
|
46
|
+
export interface SessionDetail {
|
|
47
|
+
session: SessionSummary;
|
|
48
|
+
messages: MessageMeta[];
|
|
49
|
+
activity: ActivitySession[];
|
|
50
|
+
todos: TodoItem[];
|
|
51
|
+
stats?: SessionStats;
|
|
52
|
+
}
|
|
53
|
+
|
|
24
54
|
/**
|
|
25
55
|
* MessageMeta represents a message in a session
|
|
26
56
|
*/
|
|
@@ -229,6 +259,13 @@ export interface PlanProgress {
|
|
|
229
259
|
tasks: Array<{ description: string; completed: boolean }>;
|
|
230
260
|
}
|
|
231
261
|
|
|
262
|
+
export interface TodoItem {
|
|
263
|
+
content: string;
|
|
264
|
+
status: string;
|
|
265
|
+
priority: string;
|
|
266
|
+
position: number;
|
|
267
|
+
}
|
|
268
|
+
|
|
232
269
|
/**
|
|
233
270
|
* ModelTokens represents token usage for a specific model
|
|
234
271
|
*/
|
|
@@ -316,13 +353,10 @@ export interface AgentPhase {
|
|
|
316
353
|
* Contains current session state, plan progress, and activity data
|
|
317
354
|
*/
|
|
318
355
|
export interface PollResponse {
|
|
319
|
-
sessions:
|
|
320
|
-
|
|
356
|
+
sessions: SessionSummary[];
|
|
357
|
+
activeSessionId: string | null;
|
|
321
358
|
planProgress: PlanProgress | null;
|
|
322
359
|
planName?: string;
|
|
323
|
-
messages: MessageMeta[];
|
|
324
|
-
activitySessions: ActivitySession[];
|
|
325
|
-
sessionStats?: SessionStats;
|
|
326
360
|
lastUpdate: number;
|
|
327
361
|
}
|
|
328
362
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/** "<1m", "3m", "2h", "3d", or locale date */
|
|
2
|
-
export function formatRelativeTime(date: Date | string): string {
|
|
2
|
+
export function formatRelativeTime(date: Date | string | undefined | null): string {
|
|
3
|
+
if (!date) return '';
|
|
3
4
|
const d = typeof date === 'string' ? new Date(date) : date;
|
|
5
|
+
if (isNaN(d.getTime())) return '';
|
|
4
6
|
const now = Date.now();
|
|
5
7
|
const diff = now - d.getTime();
|
|
6
8
|
const minutes = Math.floor(diff / 60000);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@keyframes slide-in-from-top{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes pulse-bg{0%{background-color:transparent}50%{background-color:#58a6ff1a}to{background-color:transparent}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.animate-slide-in-from-top{animation:slide-in-from-top .2s ease-out forwards}.animate-pulse-bg{animation:pulse-bg .3s ease-out forwards}.animate-fade-in{animation:fade-in .2s ease-out forwards}@keyframes badge-glow{0%,to{box-shadow:inset 0 1px #ffffff26,0 1px 2px #0003,0 0 4px var(--badge-color, rgba(88, 166, 255, .3))}50%{box-shadow:inset 0 1px #ffffff26,0 1px 2px #0003,0 0 16px var(--badge-color, rgba(88, 166, 255, .6))}}.animate-badge-glow{animation:badge-glow 2s ease-in-out infinite}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.animate-shimmer{background:linear-gradient(90deg,#161b22 25%,#30363d,#161b22 75%);background-size:200% 100%;animation:shimmer 1.5s infinite}@keyframes session-update-pulse{0%{background-color:#58a6ff14}to{background-color:transparent}}.animate-session-update{animation:session-update-pulse .4s ease-out forwards}@keyframes attention-glow{0%,to{box-shadow:inset 2px 0 #d2992299}50%{box-shadow:inset 2px 0 #d29922}}.animate-attention{animation:attention-glow 2s ease-in-out infinite}@keyframes waiting-user-row-pulse{0%,to{background-color:#d299220f}50%{background-color:#d299221f}}.animate-waiting-user-row{animation:waiting-user-row-pulse 2s ease-in-out infinite}@keyframes waiting-user-icon-pulse{0%,to{transform:scale(1);opacity:.8}50%{transform:scale(1.15);opacity:1}}.animate-waiting-user-icon{animation:waiting-user-icon-pulse 1.5s ease-in-out infinite}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}body{--tw-bg-opacity: 1;background-color:rgb(13 17 23 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(201 209 217 / var(--tw-text-opacity, 1));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.collapse{visibility:collapse}.static{position:static}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.bottom-4{bottom:1rem}.left-0{left:0}.right-0{right:0}.right-4{right:1rem}.top-full{top:100%}.z-10{z-index:10}.z-50{z-index:50}.-mx-2{margin-left:-.5rem;margin-right:-.5rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-auto{margin-top:auto}.flex{display:flex}.inline-flex{display:inline-flex}.hidden{display:none}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-80{height:20rem}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.max-h-24{max-height:6rem}.max-h-48{max-height:12rem}.max-h-64{max-height:16rem}.max-h-\[200px\]{max-height:200px}.max-h-\[320px\]{max-height:320px}.max-h-\[45\%\]{max-height:45%}.max-h-\[50vh\]{max-height:50vh}.min-h-0{min-height:0px}.w-1\.5{width:.375rem}.w-1\/2{width:50%}.w-1\/3{width:33.333333%}.w-1\/4{width:25%}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-2\/3{width:66.666667%}.w-24{width:6rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-3\/4{width:75%}.w-32{width:8rem}.w-4{width:1rem}.w-40{width:10rem}.w-5{width:1.25rem}.w-64{width:16rem}.w-\[280px\]{width:280px}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0px}.min-w-\[300px\]{min-width:300px}.min-w-min{min-width:-moz-min-content;min-width:min-content}.max-w-2xl{max-width:42rem}.max-w-\[180px\]{max-width:180px}.max-w-\[200px\]{max-width:200px}.max-w-\[400px\]{max-width:400px}.max-w-\[80px\]{max-width:80px}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-\[1\.4_1_13rem\]{flex:1.4 1 13rem}.flex-\[1_1_10rem\]{flex:1 1 10rem}.flex-\[2_1_16rem\]{flex:2 1 16rem}.flex-shrink-0,.shrink-0{flex-shrink:0}.origin-top-right{transform-origin:top right}.-translate-y-1{--tw-translate-y: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-110{--tw-scale-x: 1.1;--tw-scale-y: 1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-95{--tw-scale-x: .95;--tw-scale-y: .95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes ping{75%,to{transform:scale(2);opacity:0}}.animate-ping{animation:ping 1s cubic-bezier(0,0,.2,1) infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-border\/30>:not([hidden])~:not([hidden]){border-color:#30363d4d}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-hidden{overflow-y:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-xl{border-radius:.75rem}.rounded-r-full{border-top-right-radius:9999px;border-bottom-right-radius:9999px}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-accent{--tw-border-opacity: 1;border-color:rgb(88 166 255 / var(--tw-border-opacity, 1))}.border-border{--tw-border-opacity: 1;border-color:rgb(48 54 61 / var(--tw-border-opacity, 1))}.border-border\/20{border-color:#30363d33}.border-border\/30{border-color:#30363d4d}.border-border\/50{border-color:#30363d80}.border-error\/20{border-color:#f8514933}.border-red-500\/20{border-color:#ef444433}.border-red-500\/30{border-color:#ef44444d}.border-transparent{border-color:transparent}.border-warning\/20{border-color:#d2992233}.border-l-accent{--tw-border-opacity: 1;border-left-color:rgb(88 166 255 / var(--tw-border-opacity, 1))}.border-l-success{--tw-border-opacity: 1;border-left-color:rgb(35 134 54 / var(--tw-border-opacity, 1))}.border-l-transparent{border-left-color:transparent}.border-opacity-50{--tw-border-opacity: .5}.bg-\[\#0d1117\]{--tw-bg-opacity: 1;background-color:rgb(13 17 23 / var(--tw-bg-opacity, 1))}.bg-\[\#0d1117\]\/30{background-color:#0d11174d}.bg-accent{--tw-bg-opacity: 1;background-color:rgb(88 166 255 / var(--tw-bg-opacity, 1))}.bg-accent\/10{background-color:#58a6ff1a}.bg-accent\/5{background-color:#58a6ff0d}.bg-amber-500{--tw-bg-opacity: 1;background-color:rgb(245 158 11 / var(--tw-bg-opacity, 1))}.bg-background{--tw-bg-opacity: 1;background-color:rgb(13 17 23 / var(--tw-bg-opacity, 1))}.bg-border{--tw-bg-opacity: 1;background-color:rgb(48 54 61 / var(--tw-bg-opacity, 1))}.bg-error\/10{background-color:#f851491a}.bg-green-400{--tw-bg-opacity: 1;background-color:rgb(74 222 128 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-green-500\/5{background-color:#22c55e0d}.bg-purple-400\/50{background-color:#c084fc80}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-red-500\/10{background-color:#ef44441a}.bg-red-500\/5{background-color:#ef44440d}.bg-success{--tw-bg-opacity: 1;background-color:rgb(35 134 54 / var(--tw-bg-opacity, 1))}.bg-surface{--tw-bg-opacity: 1;background-color:rgb(22 27 34 / var(--tw-bg-opacity, 1))}.bg-surface\/20{background-color:#161b2233}.bg-surface\/30{background-color:#161b224d}.bg-surface\/50{background-color:#161b2280}.bg-transparent{background-color:transparent}.bg-warning\/10{background-color:#d299221a}.bg-white\/5{background-color:#ffffff0d}.bg-white\/\[0\.01\]{background-color:#ffffff03}.bg-opacity-20{--tw-bg-opacity: .2}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pl-6{padding-left:1.5rem}.pr-1{padding-right:.25rem}.pr-2{padding-right:.5rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pt-1\.5{padding-top:.375rem}.pt-2{padding-top:.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.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)}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-\[\#58a6ff\],.text-accent{--tw-text-opacity: 1;color:rgb(88 166 255 / var(--tw-text-opacity, 1))}.text-accent\/80{color:#58a6ffcc}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-amber-500{--tw-text-opacity: 1;color:rgb(245 158 11 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-border{--tw-text-opacity: 1;color:rgb(48 54 61 / var(--tw-text-opacity, 1))}.text-emerald-400{--tw-text-opacity: 1;color:rgb(52 211 153 / var(--tw-text-opacity, 1))}.text-error{--tw-text-opacity: 1;color:rgb(248 81 73 / var(--tw-text-opacity, 1))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-green-500\/60{color:#22c55e99}.text-orange-400{--tw-text-opacity: 1;color:rgb(251 146 60 / var(--tw-text-opacity, 1))}.text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-red-400\/80{color:#f87171cc}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-red-500\/60{color:#ef444499}.text-success{--tw-text-opacity: 1;color:rgb(35 134 54 / var(--tw-text-opacity, 1))}.text-text-primary{--tw-text-opacity: 1;color:rgb(201 209 217 / var(--tw-text-opacity, 1))}.text-text-secondary{--tw-text-opacity: 1;color:rgb(139 148 158 / var(--tw-text-opacity, 1))}.text-text-secondary\/50{color:#8b949e80}.text-text-secondary\/60{color:#8b949e99}.text-warning{--tw-text-opacity: 1;color:rgb(210 153 34 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.line-through{text-decoration-line:line-through}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.drop-shadow-\[0_0_6px_\#58a6ff\]{--tw-drop-shadow: drop-shadow(0 0 6px #58a6ff);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\:bg-accent\/10:hover{background-color:#58a6ff1a}.hover\:bg-accent\/90:hover{background-color:#58a6ffe6}.hover\:bg-background:hover{--tw-bg-opacity: 1;background-color:rgb(13 17 23 / var(--tw-bg-opacity, 1))}.hover\:bg-green-500\/10:hover{background-color:#22c55e1a}.hover\:bg-red-500\/10:hover{background-color:#ef44441a}.hover\:bg-surface:hover{--tw-bg-opacity: 1;background-color:rgb(22 27 34 / var(--tw-bg-opacity, 1))}.hover\:bg-white\/5:hover{background-color:#ffffff0d}.hover\:bg-white\/\[0\.02\]:hover{background-color:#ffffff05}.hover\:text-gray-300:hover{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.hover\:text-gray-400:hover{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.hover\:text-text-primary:hover{--tw-text-opacity: 1;color:rgb(201 209 217 / var(--tw-text-opacity, 1))}.hover\:text-text-secondary:hover{--tw-text-opacity: 1;color:rgb(139 148 158 / var(--tw-text-opacity, 1))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.focus\:bg-white\/5:focus{background-color:#ffffff0d}.group:hover .group-hover\:bg-purple-400{--tw-bg-opacity: 1;background-color:rgb(192 132 252 / var(--tw-bg-opacity, 1))}.group:hover .group-hover\:text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:text-text-primary{--tw-text-opacity: 1;color:rgb(201 209 217 / var(--tw-text-opacity, 1))}@media(min-width:640px){.sm\:inline-block{display:inline-block}}
|