claude-code-session-manager 0.8.1 → 0.8.3
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 +66 -11
- package/dist/assets/{cssMode-DKTELvb6.js → cssMode-DyaNC2Cs.js} +1 -1
- package/dist/assets/{editor.main-Dx55Am4z.js → editor.main-BhSGi_Jw.js} +3 -3
- package/dist/assets/{freemarker2-CBdvn_u-.js → freemarker2-DZH3si5v.js} +1 -1
- package/dist/assets/{handlebars-B67ay2ue.js → handlebars-DvzTd6uL.js} +1 -1
- package/dist/assets/{html-002uK0_M.js → html-C5GmopAN.js} +1 -1
- package/dist/assets/{htmlMode-DsT8oVY_.js → htmlMode-DwnrHwx1.js} +1 -1
- package/dist/assets/index-BGshD4Pw.js +2976 -0
- package/dist/assets/index-DCK87t79.css +32 -0
- package/dist/assets/{javascript-Cfg-gFlu.js → javascript-JqHrxiCa.js} +1 -1
- package/dist/assets/{jsonMode-CCIKxANa.js → jsonMode-8rZcy09i.js} +1 -1
- package/dist/assets/{liquid-DewgYvox.js → liquid-ClpD_v7G.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-BcMPMUo0.js → lspLanguageFeatures-u0WgQBQz.js} +1 -1
- package/dist/assets/{mdx-BGrrIvjV.js → mdx-DtViUgdm.js} +1 -1
- package/dist/assets/{python-CVhAv32T.js → python-CaAvhRGm.js} +1 -1
- package/dist/assets/{razor-DteXtrPO.js → razor-saGNVU7l.js} +1 -1
- package/dist/assets/{tsMode-DKeWRYvl.js → tsMode-HZwWTCj8.js} +1 -1
- package/dist/assets/{typescript-Dl1KPrAp.js → typescript-BInV4PNE.js} +1 -1
- package/dist/assets/{xml-DdyOGE0N.js → xml-tgO806YR.js} +1 -1
- package/dist/assets/{yaml-BwFXDW6t.js → yaml-CHApZArv.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/main/config.cjs +93 -19
- package/src/main/index.cjs +163 -31
- package/src/main/ipcSchemas.cjs +59 -2
- package/src/main/lib/cleanEnv.cjs +20 -0
- package/src/main/lib/credentials.cjs +184 -0
- package/src/main/lib/schedulerConfig.cjs +10 -0
- package/src/main/logs.cjs +1 -1
- package/src/main/otelSettings.cjs +1 -1
- package/src/main/pty.cjs +53 -6
- package/src/main/scheduler.cjs +518 -147
- package/src/main/transcripts.cjs +26 -21
- package/src/main/usage.cjs +76 -25
- package/src/main/voiceSettings.cjs +1 -1
- package/src/main/watchers.cjs +69 -11
- package/src/preload/api.d.ts +51 -11
- package/src/preload/index.cjs +13 -0
- package/dist/assets/index-DsC4vT8M.css +0 -32
- package/dist/assets/index-E14-spyd.js +0 -2972
package/src/main/transcripts.cjs
CHANGED
|
@@ -84,40 +84,45 @@ function classifyLine(obj) {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
-
* Read new bytes from
|
|
88
|
-
*
|
|
87
|
+
* Read new bytes from sub.filePath into sub.offset/pending/inode in place.
|
|
88
|
+
* Resets offset+pending when the file inode changes (rename+replace rotation).
|
|
89
|
+
* Returns parsed line strings ready for JSON.parse.
|
|
89
90
|
*/
|
|
90
|
-
async function readDelta(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
async function readDelta(sub) {
|
|
92
|
+
const stat = await fsp.stat(sub.filePath).catch(() => null);
|
|
93
|
+
if (!stat) return [];
|
|
94
|
+
// Inode changed → file was replaced underfoot; restart from the top.
|
|
95
|
+
if (sub.inode !== undefined && stat.ino !== sub.inode) {
|
|
96
|
+
sub.offset = 0;
|
|
97
|
+
sub.pending = '';
|
|
96
98
|
}
|
|
97
|
-
if (stat.size < offset) {
|
|
99
|
+
if (stat.size < sub.offset) {
|
|
98
100
|
// File was truncated/rotated — start over.
|
|
99
|
-
offset = 0;
|
|
100
|
-
pending = '';
|
|
101
|
+
sub.offset = 0;
|
|
102
|
+
sub.pending = '';
|
|
103
|
+
}
|
|
104
|
+
if (stat.size === sub.offset) {
|
|
105
|
+
sub.inode = stat.ino;
|
|
106
|
+
return [];
|
|
101
107
|
}
|
|
102
|
-
|
|
103
|
-
const fd = await fsp.open(filePath, 'r');
|
|
108
|
+
const fd = await fsp.open(sub.filePath, 'r');
|
|
104
109
|
try {
|
|
105
|
-
const length = stat.size - offset;
|
|
110
|
+
const length = stat.size - sub.offset;
|
|
106
111
|
const buf = Buffer.alloc(length);
|
|
107
|
-
await fd.read(buf, 0, length, offset);
|
|
108
|
-
const text = pending + buf.toString('utf8');
|
|
112
|
+
await fd.read(buf, 0, length, sub.offset);
|
|
113
|
+
const text = sub.pending + buf.toString('utf8');
|
|
109
114
|
const parts = text.split('\n');
|
|
110
|
-
|
|
111
|
-
|
|
115
|
+
sub.pending = parts.pop() ?? '';
|
|
116
|
+
sub.offset = stat.size;
|
|
117
|
+
sub.inode = stat.ino;
|
|
118
|
+
return parts.filter(Boolean);
|
|
112
119
|
} finally {
|
|
113
120
|
await fd.close();
|
|
114
121
|
}
|
|
115
122
|
}
|
|
116
123
|
|
|
117
124
|
async function flush(sub, { emit = true } = {}) {
|
|
118
|
-
const
|
|
119
|
-
sub.offset = newOffset;
|
|
120
|
-
sub.pending = pending;
|
|
125
|
+
const lines = await readDelta(sub);
|
|
121
126
|
for (const line of lines) {
|
|
122
127
|
let obj;
|
|
123
128
|
try {
|
package/src/main/usage.cjs
CHANGED
|
@@ -20,48 +20,99 @@ const fsp = require('node:fs/promises');
|
|
|
20
20
|
const path = require('node:path');
|
|
21
21
|
const os = require('node:os');
|
|
22
22
|
const { ipcMain } = require('electron');
|
|
23
|
+
const { refreshIfNeeded, expiresAtMs } = require('./lib/credentials.cjs');
|
|
23
24
|
|
|
24
|
-
const CREDS_PATH = path.join(os.homedir(), '.claude', '.credentials.json');
|
|
25
25
|
const USAGE_URL = 'https://api.anthropic.com/api/oauth/usage';
|
|
26
|
+
const CACHE_PATH = path.join(os.homedir(), '.claude', 'session-manager', 'billing-cache.json');
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
let cache = null;
|
|
29
|
+
let hydrationPromise = null;
|
|
30
|
+
|
|
31
|
+
async function hydrateCache() {
|
|
32
|
+
try {
|
|
33
|
+
cache = JSON.parse(await fsp.readFile(CACHE_PATH, 'utf8'));
|
|
34
|
+
} catch {
|
|
35
|
+
cache = null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function persistCache(c) {
|
|
40
|
+
await fsp.mkdir(path.dirname(CACHE_PATH), { recursive: true });
|
|
41
|
+
const tmp = `${CACHE_PATH}.${process.pid}.${Date.now()}.tmp`;
|
|
42
|
+
await fsp.writeFile(tmp, JSON.stringify(c), { encoding: 'utf8', mode: 0o600 });
|
|
43
|
+
await fsp.rename(tmp, CACHE_PATH);
|
|
33
44
|
}
|
|
34
45
|
|
|
35
46
|
async function fetchUsage() {
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
// Check expiry and attempt proactive refresh before touching the network.
|
|
48
|
+
const refresh = await refreshIfNeeded();
|
|
49
|
+
if (refresh.kind === 'auth') {
|
|
50
|
+
return { kind: 'auth', message: refresh.message, httpStatus: 401, expiredAt: refresh.expiredAt ?? null };
|
|
51
|
+
}
|
|
52
|
+
if (refresh.kind === 'config') return refresh;
|
|
53
|
+
// 'ok' or 'unsupported' — creds present and not yet expired
|
|
54
|
+
const creds = refresh.creds;
|
|
55
|
+
|
|
56
|
+
let r;
|
|
57
|
+
try {
|
|
58
|
+
r = await fetch(USAGE_URL, {
|
|
59
|
+
headers: {
|
|
60
|
+
Authorization: `Bearer ${creds.accessToken}`,
|
|
61
|
+
'anthropic-beta': 'oauth-2025-04-20',
|
|
62
|
+
'User-Agent': 'claude-code-session-manager',
|
|
63
|
+
},
|
|
64
|
+
signal: AbortSignal.timeout(10_000),
|
|
65
|
+
});
|
|
66
|
+
} catch (e) {
|
|
67
|
+
return { kind: 'transient', message: e.message || String(e), httpStatus: null };
|
|
68
|
+
}
|
|
69
|
+
if (r.status === 401 || r.status === 403) {
|
|
70
|
+
const body = await r.text().catch(() => '');
|
|
71
|
+
const ms = expiresAtMs(creds);
|
|
72
|
+
return { kind: 'auth', message: body.slice(0, 200) || `HTTP ${r.status}`, httpStatus: r.status, expiredAt: ms };
|
|
73
|
+
}
|
|
74
|
+
if (r.status === 408 || r.status === 429 || r.status >= 500) {
|
|
75
|
+
const body = await r.text().catch(() => '');
|
|
76
|
+
return { kind: 'transient', message: body.slice(0, 200) || `HTTP ${r.status}`, httpStatus: r.status };
|
|
77
|
+
}
|
|
44
78
|
if (!r.ok) {
|
|
45
79
|
const body = await r.text().catch(() => '');
|
|
46
|
-
|
|
80
|
+
return { kind: 'transient', message: body.slice(0, 200) || `HTTP ${r.status}`, httpStatus: r.status };
|
|
47
81
|
}
|
|
48
82
|
const usage = await r.json();
|
|
49
83
|
return {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
84
|
+
kind: 'ok',
|
|
85
|
+
data: {
|
|
86
|
+
usage,
|
|
87
|
+
subscriptionType: creds.subscriptionType ?? null,
|
|
88
|
+
rateLimitTier: creds.rateLimitTier ?? null,
|
|
89
|
+
credentialsExpiresAt: creds.expiresAt ?? null,
|
|
90
|
+
fetchedAt: Date.now(),
|
|
91
|
+
},
|
|
55
92
|
};
|
|
56
93
|
}
|
|
57
94
|
|
|
58
95
|
function registerBillingHandlers() {
|
|
96
|
+
hydrationPromise = hydrateCache();
|
|
97
|
+
|
|
59
98
|
ipcMain.handle('billing:fetch', async () => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
99
|
+
if (hydrationPromise) { await hydrationPromise; hydrationPromise = null; }
|
|
100
|
+
|
|
101
|
+
const r = await fetchUsage();
|
|
102
|
+
if (r.kind === 'ok') {
|
|
103
|
+
cache = { data: r.data, fetchedAt: Date.now(), sourceCredsExpiresAt: r.data.credentialsExpiresAt };
|
|
104
|
+
persistCache(cache).catch(() => {});
|
|
105
|
+
return { kind: 'ok', data: r.data };
|
|
106
|
+
}
|
|
107
|
+
if (r.kind === 'auth') {
|
|
108
|
+
if (cache) return { kind: 'auth', message: r.message, httpStatus: r.httpStatus, expiredAt: r.expiredAt, cached: cache.data, staleSince: cache.fetchedAt };
|
|
109
|
+
return r;
|
|
110
|
+
}
|
|
111
|
+
if (r.kind === 'transient') {
|
|
112
|
+
if (cache) return { kind: 'ok-stale', data: cache.data, staleSince: cache.fetchedAt, lastError: r.message };
|
|
113
|
+
return r;
|
|
64
114
|
}
|
|
115
|
+
return r; // config
|
|
65
116
|
});
|
|
66
117
|
}
|
|
67
118
|
|
|
@@ -109,7 +109,7 @@ async function writeMerged(patch) {
|
|
|
109
109
|
const next = { ...existing, ...patch };
|
|
110
110
|
const body = JSON.stringify(next, null, 2) + '\n';
|
|
111
111
|
const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
|
|
112
|
-
await fsp.writeFile(tmp, body, 'utf8',
|
|
112
|
+
await fsp.writeFile(tmp, body, { encoding: 'utf8', mode: 0o600 });
|
|
113
113
|
// chmod tmp explicitly because some platforms ignore the mode arg on
|
|
114
114
|
// writeFile when the file pre-exists. Then rename for atomicity.
|
|
115
115
|
try { await fsp.chmod(tmp, 0o600); } catch { /* */ }
|
package/src/main/watchers.cjs
CHANGED
|
@@ -10,8 +10,40 @@
|
|
|
10
10
|
|
|
11
11
|
const { ipcMain } = require('electron');
|
|
12
12
|
const { spawn } = require('node:child_process');
|
|
13
|
-
const readline = require('node:readline');
|
|
14
13
|
const crypto = require('node:crypto');
|
|
14
|
+
const path = require('node:path');
|
|
15
|
+
const os = require('node:os');
|
|
16
|
+
const fs = require('node:fs');
|
|
17
|
+
const { cleanChildEnv } = require('./lib/cleanEnv.cjs');
|
|
18
|
+
|
|
19
|
+
// Splits a readable stream into lines capped at maxLineBytes. Prevents OOM
|
|
20
|
+
// from commands that produce no newlines (e.g. cat /dev/urandom | base64).
|
|
21
|
+
function lineSplit(stream, onLine, maxLineBytes = 4096) {
|
|
22
|
+
let buf = Buffer.alloc(0);
|
|
23
|
+
stream.on('data', (chunk) => {
|
|
24
|
+
buf = Buffer.concat([buf, chunk]);
|
|
25
|
+
while (true) {
|
|
26
|
+
const nl = buf.indexOf(0x0a);
|
|
27
|
+
if (nl === -1) {
|
|
28
|
+
if (buf.length > maxLineBytes) {
|
|
29
|
+
onLine(buf.slice(0, maxLineBytes).toString('utf8') + '…[truncated]');
|
|
30
|
+
buf = Buffer.alloc(0);
|
|
31
|
+
}
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const line = buf.slice(0, nl).toString('utf8');
|
|
35
|
+
buf = buf.slice(nl + 1);
|
|
36
|
+
onLine(line.length > maxLineBytes ? line.slice(0, maxLineBytes) + '…[truncated]' : line);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
stream.on('end', () => {
|
|
40
|
+
if (buf.length > 0) {
|
|
41
|
+
onLine(buf.length > maxLineBytes
|
|
42
|
+
? buf.slice(0, maxLineBytes).toString('utf8') + '…[truncated]'
|
|
43
|
+
: buf.toString('utf8'));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
15
47
|
|
|
16
48
|
class WatcherManager {
|
|
17
49
|
constructor() {
|
|
@@ -25,13 +57,27 @@ class WatcherManager {
|
|
|
25
57
|
}
|
|
26
58
|
|
|
27
59
|
add({ tabId, label, command, cwd }) {
|
|
60
|
+
const resolvedCwd = cwd || process.cwd();
|
|
61
|
+
|
|
62
|
+
// Require cwd's realpath to be inside homedir.
|
|
63
|
+
const home = os.homedir();
|
|
64
|
+
let realCwd;
|
|
65
|
+
try {
|
|
66
|
+
realCwd = fs.realpathSync(resolvedCwd);
|
|
67
|
+
} catch {
|
|
68
|
+
realCwd = path.resolve(resolvedCwd);
|
|
69
|
+
}
|
|
70
|
+
if (realCwd !== home && !realCwd.startsWith(home + path.sep)) {
|
|
71
|
+
throw new Error(`watcher cwd outside home directory: ${realCwd}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
28
74
|
const watcherId = crypto.randomUUID();
|
|
29
75
|
const trimmedLabel = (label && label.trim()) || command.slice(0, 40);
|
|
30
76
|
const child = spawn(command, [], {
|
|
31
|
-
cwd:
|
|
77
|
+
cwd: resolvedCwd,
|
|
32
78
|
shell: true,
|
|
33
79
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
34
|
-
env:
|
|
80
|
+
env: cleanChildEnv(),
|
|
35
81
|
});
|
|
36
82
|
|
|
37
83
|
const entry = {
|
|
@@ -39,7 +85,7 @@ class WatcherManager {
|
|
|
39
85
|
tabId,
|
|
40
86
|
label: trimmedLabel,
|
|
41
87
|
command,
|
|
42
|
-
cwd:
|
|
88
|
+
cwd: resolvedCwd,
|
|
43
89
|
child,
|
|
44
90
|
startedAt: Date.now(),
|
|
45
91
|
lineCount: 0,
|
|
@@ -59,18 +105,16 @@ class WatcherManager {
|
|
|
59
105
|
}
|
|
60
106
|
};
|
|
61
107
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const stderrRl = readline.createInterface({ input: child.stderr });
|
|
65
|
-
stderrRl.on('line', emitLine);
|
|
108
|
+
lineSplit(child.stdout, emitLine);
|
|
109
|
+
lineSplit(child.stderr, emitLine);
|
|
66
110
|
|
|
67
111
|
child.on('error', (err) => {
|
|
68
112
|
emitLine(`[watcher error] ${err?.message ?? String(err)}`);
|
|
113
|
+
// Remove from map so list() doesn't return ghosts after a spawn failure.
|
|
114
|
+
this.watchers.delete(watcherId);
|
|
69
115
|
});
|
|
70
116
|
|
|
71
117
|
child.on('close', (code, signal) => {
|
|
72
|
-
stdoutRl.close();
|
|
73
|
-
stderrRl.close();
|
|
74
118
|
// Only emit close if still registered (remove() suppresses by deleting first).
|
|
75
119
|
if (this.watchers.has(watcherId)) {
|
|
76
120
|
emitLine(`[exited code=${code ?? 'null'}${signal ? ` signal=${signal}` : ''}]`);
|
|
@@ -135,6 +179,12 @@ class WatcherManager {
|
|
|
135
179
|
|
|
136
180
|
const manager = new WatcherManager();
|
|
137
181
|
|
|
182
|
+
// Top-level export so index.cjs can call watchers.attachWindow(mainWindow)
|
|
183
|
+
// without going through the manager instance directly.
|
|
184
|
+
function attachWindow(window) {
|
|
185
|
+
manager.attachWindow(window);
|
|
186
|
+
}
|
|
187
|
+
|
|
138
188
|
function registerWatcherHandlers() {
|
|
139
189
|
const { z } = require('zod');
|
|
140
190
|
const addSchema = z.object({
|
|
@@ -145,10 +195,18 @@ function registerWatcherHandlers() {
|
|
|
145
195
|
});
|
|
146
196
|
const listSchema = z.object({ tabId: z.string().min(1).max(128) });
|
|
147
197
|
const removeSchema = z.object({ watcherId: z.string().min(1).max(128) });
|
|
198
|
+
const killTabSchema = z.object({ tabId: z.string().min(1).max(128) });
|
|
148
199
|
|
|
149
200
|
ipcMain.handle('watchers:add', (_e, payload) => manager.add(addSchema.parse(payload)));
|
|
150
201
|
ipcMain.handle('watchers:list', (_e, payload) => manager.list(listSchema.parse(payload)));
|
|
151
202
|
ipcMain.handle('watchers:remove', (_e, payload) => manager.remove(removeSchema.parse(payload)));
|
|
203
|
+
ipcMain.handle('watchers:kill-tab', (_e, payload) => {
|
|
204
|
+
const { tabId } = killTabSchema.parse(payload);
|
|
205
|
+
for (const [id, w] of manager.watchers) {
|
|
206
|
+
if (w.tabId === tabId) manager.remove({ watcherId: id });
|
|
207
|
+
}
|
|
208
|
+
return { ok: true };
|
|
209
|
+
});
|
|
152
210
|
}
|
|
153
211
|
|
|
154
|
-
module.exports = { manager, registerWatcherHandlers };
|
|
212
|
+
module.exports = { manager, attachWindow, registerWatcherHandlers };
|
package/src/preload/api.d.ts
CHANGED
|
@@ -10,6 +10,11 @@ export interface PtyExit {
|
|
|
10
10
|
signal?: number;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
export interface WriteErrorEvent {
|
|
14
|
+
tabId: string;
|
|
15
|
+
reason: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
export interface ReadJsonResult {
|
|
14
19
|
exists: boolean;
|
|
15
20
|
raw: string;
|
|
@@ -84,6 +89,8 @@ export interface PersistedTab {
|
|
|
84
89
|
export interface LoadedSessions {
|
|
85
90
|
tabs: PersistedTab[];
|
|
86
91
|
activeTabId: string | null;
|
|
92
|
+
/** True when the main process rotated all session IDs on boot (e.g. force-fresh). */
|
|
93
|
+
freshStart?: boolean;
|
|
87
94
|
}
|
|
88
95
|
|
|
89
96
|
export interface UsageWindow {
|
|
@@ -107,18 +114,21 @@ export interface UsageSnapshot {
|
|
|
107
114
|
[key: string]: unknown;
|
|
108
115
|
}
|
|
109
116
|
|
|
110
|
-
export interface
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
credentialsExpiresAt: number | null;
|
|
117
|
-
fetchedAt: number;
|
|
118
|
-
};
|
|
119
|
-
error?: string;
|
|
117
|
+
export interface BillingData {
|
|
118
|
+
usage: UsageSnapshot;
|
|
119
|
+
subscriptionType: string | null;
|
|
120
|
+
rateLimitTier: string | null;
|
|
121
|
+
credentialsExpiresAt: string | null;
|
|
122
|
+
fetchedAt: number;
|
|
120
123
|
}
|
|
121
124
|
|
|
125
|
+
export type BillingFetchResult =
|
|
126
|
+
| { kind: 'ok'; data: BillingData }
|
|
127
|
+
| { kind: 'ok-stale'; data: BillingData; staleSince: number; lastError: string }
|
|
128
|
+
| { kind: 'auth'; message: string; httpStatus: number; expiredAt?: number | null; cached?: BillingData; staleSince?: number }
|
|
129
|
+
| { kind: 'transient'; message: string; httpStatus: number | null }
|
|
130
|
+
| { kind: 'config'; message: string };
|
|
131
|
+
|
|
122
132
|
export interface VoiceHotkeyConfig {
|
|
123
133
|
accelerator: string;
|
|
124
134
|
mode: 'hold' | 'toggle';
|
|
@@ -259,7 +269,7 @@ export interface SchedulePaths {
|
|
|
259
269
|
queue: string;
|
|
260
270
|
}
|
|
261
271
|
|
|
262
|
-
export type SchedulePauseReason = 'rate_limit';
|
|
272
|
+
export type SchedulePauseReason = 'rate_limit' | 'auth' | 'network' | 'manual' | 'reset_failure';
|
|
263
273
|
|
|
264
274
|
export interface SchedulePauseInfo {
|
|
265
275
|
reason: SchedulePauseReason;
|
|
@@ -270,6 +280,27 @@ export interface SchedulePauseInfo {
|
|
|
270
280
|
resumeAt: string | null;
|
|
271
281
|
}
|
|
272
282
|
|
|
283
|
+
export interface ScheduleHealthSnapshot {
|
|
284
|
+
bootedAt: number;
|
|
285
|
+
lastPollAt: number | null;
|
|
286
|
+
lastPollOk: boolean;
|
|
287
|
+
consecutiveFailures: number;
|
|
288
|
+
backoffNextAt: number | null;
|
|
289
|
+
nextResetCached: string | null;
|
|
290
|
+
pausedSince: number | null;
|
|
291
|
+
pauseReason: SchedulePauseReason | null;
|
|
292
|
+
runningJobs: { slug: string; startedAt: number; pid: number }[];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export interface PrdListItem {
|
|
296
|
+
slug: string;
|
|
297
|
+
parallelGroup: number;
|
|
298
|
+
title: string;
|
|
299
|
+
cwd: string;
|
|
300
|
+
estimateMinutes: number | null;
|
|
301
|
+
mtimeMs: number;
|
|
302
|
+
}
|
|
303
|
+
|
|
273
304
|
export interface ScheduleStateSnapshot {
|
|
274
305
|
config: ScheduleConfig;
|
|
275
306
|
jobs: ScheduleJob[];
|
|
@@ -331,6 +362,10 @@ export interface SessionManagerAPI {
|
|
|
331
362
|
isE2E: () => Promise<boolean>;
|
|
332
363
|
onNewSession: (handler: () => void) => () => void;
|
|
333
364
|
onRebootSession: (handler: () => void) => () => void;
|
|
365
|
+
openInEditor: (cwd: string, editor?: string | null) => Promise<{ ok: boolean; editor?: string; error?: string }>;
|
|
366
|
+
openInFinder: (cwd: string) => Promise<{ ok: boolean; error?: string }>;
|
|
367
|
+
openInTerminal: (cwd: string) => Promise<{ ok: boolean; terminal?: string; error?: string }>;
|
|
368
|
+
archiveProject: (encoded: string) => Promise<{ ok: boolean; error?: string }>;
|
|
334
369
|
};
|
|
335
370
|
pty: {
|
|
336
371
|
spawn: (payload: { tabId: string; cwd: string; cols?: number; rows?: number }) => Promise<SpawnResult>;
|
|
@@ -339,6 +374,7 @@ export interface SessionManagerAPI {
|
|
|
339
374
|
kill: (tabId: string) => void;
|
|
340
375
|
onData: (tabId: string, handler: (data: string) => void) => () => void;
|
|
341
376
|
onExit: (tabId: string, handler: (info: PtyExit) => void) => () => void;
|
|
377
|
+
onWriteError: (handler: (ev: WriteErrorEvent) => void) => () => void;
|
|
342
378
|
};
|
|
343
379
|
transcripts: {
|
|
344
380
|
subscribe: (payload: { tabId: string; cwd: string; sessionUuid: string }) => Promise<SubscribeResult>;
|
|
@@ -393,6 +429,7 @@ export interface SessionManagerAPI {
|
|
|
393
429
|
add: (payload: { tabId: string; label?: string; command: string; cwd?: string | null }) => Promise<WatcherAddResult>;
|
|
394
430
|
list: (tabId: string) => Promise<WatcherInfo[]>;
|
|
395
431
|
remove: (watcherId: string) => Promise<{ ok: boolean }>;
|
|
432
|
+
killTab: (tabId: string) => Promise<{ ok: boolean }>;
|
|
396
433
|
onLine: (handler: (ev: WatcherLineEvent) => void) => () => void;
|
|
397
434
|
onClosed: (handler: (ev: WatcherClosedEvent) => void) => () => void;
|
|
398
435
|
};
|
|
@@ -412,6 +449,9 @@ export interface SessionManagerAPI {
|
|
|
412
449
|
openFolder: () => Promise<{ ok: boolean }>;
|
|
413
450
|
readPrd: (slug: string) => Promise<{ ok: boolean; text?: string; error?: string }>;
|
|
414
451
|
readLog: (runId: string, slug: string) => Promise<{ ok: boolean; text?: string; error?: string }>;
|
|
452
|
+
writePrd: (slug: string, body: string) => Promise<{ ok: boolean; bytesWritten: number }>;
|
|
453
|
+
listPrds: () => Promise<PrdListItem[]>;
|
|
454
|
+
health: () => Promise<ScheduleHealthSnapshot>;
|
|
415
455
|
onState: (handler: (snapshot: ScheduleStateSnapshot) => void) => () => void;
|
|
416
456
|
};
|
|
417
457
|
}
|
package/src/preload/index.cjs
CHANGED
|
@@ -9,6 +9,10 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
9
9
|
pickDirectory: () => ipcRenderer.invoke('app:pick-directory'),
|
|
10
10
|
gitBranch: (cwd) => ipcRenderer.invoke('app:git-branch', { cwd }),
|
|
11
11
|
rebootApp: () => ipcRenderer.send('app:reboot-app'),
|
|
12
|
+
openInEditor: (cwd, editor) => ipcRenderer.invoke('app:open-in-editor', { cwd, editor }),
|
|
13
|
+
openInFinder: (cwd) => ipcRenderer.invoke('app:open-in-finder', { cwd }),
|
|
14
|
+
openInTerminal: (cwd) => ipcRenderer.invoke('app:open-in-terminal', { cwd }),
|
|
15
|
+
archiveProject: (encoded) => ipcRenderer.invoke('app:archive-project', { encoded }),
|
|
12
16
|
testFireHook: (args) => ipcRenderer.invoke('app:test-fire-hook', args),
|
|
13
17
|
// F7: lets the renderer suppress the wizard auto-trigger under SM_E2E=1.
|
|
14
18
|
isE2E: () => ipcRenderer.invoke('app:is-e2e'),
|
|
@@ -40,6 +44,11 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
40
44
|
ipcRenderer.on(channel, listener);
|
|
41
45
|
return () => ipcRenderer.removeListener(channel, listener);
|
|
42
46
|
},
|
|
47
|
+
onWriteError: (handler) => {
|
|
48
|
+
const listener = (_e, payload) => handler(payload);
|
|
49
|
+
ipcRenderer.on('pty:write-error', listener);
|
|
50
|
+
return () => ipcRenderer.removeListener('pty:write-error', listener);
|
|
51
|
+
},
|
|
43
52
|
},
|
|
44
53
|
transcripts: {
|
|
45
54
|
subscribe: (payload) => ipcRenderer.invoke('transcript:subscribe', payload),
|
|
@@ -111,6 +120,7 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
111
120
|
add: (payload) => ipcRenderer.invoke('watchers:add', payload),
|
|
112
121
|
list: (tabId) => ipcRenderer.invoke('watchers:list', { tabId }),
|
|
113
122
|
remove: (watcherId) => ipcRenderer.invoke('watchers:remove', { watcherId }),
|
|
123
|
+
killTab: (tabId) => ipcRenderer.invoke('watchers:kill-tab', { tabId }),
|
|
114
124
|
onLine: (handler) => {
|
|
115
125
|
const listener = (_e, payload) => handler(payload);
|
|
116
126
|
ipcRenderer.on('watcher:line', listener);
|
|
@@ -138,6 +148,9 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
138
148
|
openFolder: () => ipcRenderer.invoke('schedule:open-folder'),
|
|
139
149
|
readPrd: (slug) => ipcRenderer.invoke('schedule:read-prd', { slug }),
|
|
140
150
|
readLog: (runId, slug) => ipcRenderer.invoke('schedule:read-log', { runId, slug }),
|
|
151
|
+
writePrd: (slug, body) => ipcRenderer.invoke('schedule:write-prd', { slug, body }),
|
|
152
|
+
listPrds: () => ipcRenderer.invoke('schedule:list-prds'),
|
|
153
|
+
health: () => ipcRenderer.invoke('schedule:health'),
|
|
141
154
|
onState: (handler) => {
|
|
142
155
|
const listener = (_e, payload) => handler(payload);
|
|
143
156
|
ipcRenderer.on('schedule:state', listener);
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
|
|
3
|
-
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
|
4
|
-
* https://github.com/chjj/term.js
|
|
5
|
-
* @license MIT
|
|
6
|
-
*
|
|
7
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
-
* in the Software without restriction, including without limitation the rights
|
|
10
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
-
* furnished to do so, subject to the following conditions:
|
|
13
|
-
*
|
|
14
|
-
* The above copyright notice and this permission notice shall be included in
|
|
15
|
-
* all copies or substantial portions of the Software.
|
|
16
|
-
*
|
|
17
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
23
|
-
* THE SOFTWARE.
|
|
24
|
-
*
|
|
25
|
-
* Originally forked from (with the author's permission):
|
|
26
|
-
* Fabrice Bellard's javascript vt100 for jslinux:
|
|
27
|
-
* http://bellard.org/jslinux/
|
|
28
|
-
* Copyright (c) 2011 Fabrice Bellard
|
|
29
|
-
* The original design remains. The terminal itself
|
|
30
|
-
* has been extended to include xterm CSI codes, among
|
|
31
|
-
* other features.
|
|
32
|
-
*/.xterm{cursor:text;position:relative;-moz-user-select:none;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::-moz-selection{color:transparent}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{-webkit-user-select:text;-moz-user-select:text;user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{-webkit-text-decoration:double underline;text-decoration:double underline}.xterm-underline-3{-webkit-text-decoration:wavy underline;text-decoration:wavy underline}.xterm-underline-4{-webkit-text-decoration:dotted underline;text-decoration:dotted underline}.xterm-underline-5{-webkit-text-decoration:dashed underline;text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{-webkit-text-decoration:overline double underline;text-decoration:overline double underline}.xterm-overline.xterm-underline-3{-webkit-text-decoration:overline wavy underline;text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{-webkit-text-decoration:overline dotted underline;text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{-webkit-text-decoration:overline dashed underline;text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}*,: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:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,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}.\!container{width:100%!important}.container{width:100%}@media(min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media(min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media(min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media(min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media(min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.-bottom-\[5px\]{bottom:-5px}.-bottom-\[7px\]{bottom:-7px}.-top-20{top:-5rem}.-top-28{top:-7rem}.-top-48{top:-12rem}.bottom-0{bottom:0}.bottom-24{bottom:6rem}.bottom-3{bottom:.75rem}.bottom-4{bottom:1rem}.bottom-\[88px\]{bottom:88px}.left-0{left:0}.left-1\/2{left:50%}.left-4{left:1rem}.left-\[72\%\]{left:72%}.right-0{right:0}.right-2{right:.5rem}.right-4{right:1rem}.top-0{top:0}.top-0\.5{top:.125rem}.top-10{top:2.5rem}.top-4{top:1rem}.top-full{top:100%}.z-10{z-index:10}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-3{margin-left:.75rem;margin-right:.75rem}.mx-4{margin-left:1rem;margin-right:1rem}.-mb-px{margin-bottom:-1px}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-0\.5{height:.125rem}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-2{height:.5rem}.h-24{height:6rem}.h-3{height:.75rem}.h-4{height:1rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-9{height:2.25rem}.h-full{height:100%}.h-px{height:1px}.max-h-24{max-height:6rem}.max-h-40{max-height:10rem}.max-h-64{max-height:16rem}.max-h-72{max-height:18rem}.max-h-\[60vh\]{max-height:60vh}.min-h-0{min-height:0px}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-10{width:2.5rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-20{width:5rem}.w-24{width:6rem}.w-3{width:.75rem}.w-32{width:8rem}.w-4{width:1rem}.w-48{width:12rem}.w-56{width:14rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-\[420px\]{width:420px}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-\[12rem\]{max-width:12rem}.max-w-\[8rem\]{max-width:8rem}.max-w-md{max-width:28rem}.max-w-xl{max-width:36rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x: -50%;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-x-0\.5{--tw-translate-x: .125rem;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-x-3\.5{--tw-translate-x: .875rem;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-45{--tw-rotate: 45deg;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))}.transform{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 pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-col-resize{cursor:col-resize}.cursor-grab{cursor:grab}.cursor-grabbing{cursor:grabbing}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-wait{cursor:wait}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-y{resize:vertical}.resize{resize:both}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-\[auto_1fr\]{grid-template-columns:auto 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-end{justify-content:flex-end}.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-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-y-1{row-gap:.25rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.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))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * 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-line>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(36 42 51 / var(--tw-divide-opacity, 1))}.divide-line\/60>:not([hidden])~:not([hidden]){border-color:#242a3399}.self-start{align-self:flex-start}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-xl{border-radius:.75rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-0{border-bottom-width:0px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-t-2{border-top-width:2px}.border-accent{--tw-border-opacity: 1;border-color:rgb(217 119 87 / var(--tw-border-opacity, 1))}.border-accent-muted{--tw-border-opacity: 1;border-color:rgb(138 74 51 / var(--tw-border-opacity, 1))}.border-accent\/30{border-color:#d977574d}.border-accent\/40{border-color:#d9775766}.border-accent\/50{border-color:#d9775780}.border-blue-900\/40{border-color:#1e3a8a66}.border-emerald-900\/40{border-color:#064e3b66}.border-line{--tw-border-opacity: 1;border-color:rgb(36 42 51 / var(--tw-border-opacity, 1))}.border-line\/50{border-color:#242a3380}.border-line\/60{border-color:#242a3399}.border-purple-900\/40{border-color:#581c8766}.border-red-500\/70{border-color:#ef4444b3}.border-red-800{--tw-border-opacity: 1;border-color:rgb(153 27 27 / var(--tw-border-opacity, 1))}.border-red-900\/40{border-color:#7f1d1d66}.border-transparent{border-color:transparent}.border-yellow-600\/50{border-color:#ca8a0480}.border-yellow-600\/60{border-color:#ca8a0499}.border-yellow-900\/40{border-color:#713f1266}.bg-accent{--tw-bg-opacity: 1;background-color:rgb(217 119 87 / var(--tw-bg-opacity, 1))}.bg-accent\/15{background-color:#d9775726}.bg-accent\/5{background-color:#d977570d}.bg-amber-400{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / var(--tw-bg-opacity, 1))}.bg-amber-500\/10{background-color:#f59e0b1a}.bg-amber-500\/5{background-color:#f59e0b0d}.bg-bg{--tw-bg-opacity: 1;background-color:rgb(11 13 16 / var(--tw-bg-opacity, 1))}.bg-bg-elev{--tw-bg-opacity: 1;background-color:rgb(18 21 26 / var(--tw-bg-opacity, 1))}.bg-bg-elev\/40{background-color:#12151a66}.bg-bg-elev\/60{background-color:#12151a99}.bg-bg-elev\/80{background-color:#12151acc}.bg-bg-hi{--tw-bg-opacity: 1;background-color:rgb(26 31 39 / var(--tw-bg-opacity, 1))}.bg-bg-hi\/30{background-color:#1a1f274d}.bg-bg\/50{background-color:#0b0d1080}.bg-black\/60{background-color:#0009}.bg-blue-950\/30{background-color:#1725544d}.bg-emerald-400{--tw-bg-opacity: 1;background-color:rgb(52 211 153 / var(--tw-bg-opacity, 1))}.bg-emerald-950\/30{background-color:#022c224d}.bg-fg{--tw-bg-opacity: 1;background-color:rgb(230 232 236 / var(--tw-bg-opacity, 1))}.bg-fg-dim{--tw-bg-opacity: 1;background-color:rgb(138 147 160 / var(--tw-bg-opacity, 1))}.bg-fg-faint{--tw-bg-opacity: 1;background-color:rgb(84 92 104 / var(--tw-bg-opacity, 1))}.bg-fg-faint\/40{background-color:#545c6866}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-green-900\/30{background-color:#14532d4d}.bg-line\/60{background-color:#242a3399}.bg-purple-950\/30{background-color:#3b07644d}.bg-red-400{--tw-bg-opacity: 1;background-color:rgb(248 113 113 / var(--tw-bg-opacity, 1))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-red-900\/30{background-color:#7f1d1d4d}.bg-red-950\/20{background-color:#450a0a33}.bg-red-950\/30{background-color:#450a0a4d}.bg-red-950\/60{background-color:#450a0a99}.bg-yellow-400{--tw-bg-opacity: 1;background-color:rgb(250 204 21 / var(--tw-bg-opacity, 1))}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.bg-yellow-950\/20{background-color:#42200633}.bg-yellow-950\/30{background-color:#4220064d}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.from-bg{--tw-gradient-from: #0b0d10 var(--tw-gradient-from-position);--tw-gradient-to: rgb(11 13 16 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.via-bg{--tw-gradient-to: rgb(11 13 16 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), #0b0d10 var(--tw-gradient-via-position), var(--tw-gradient-to)}.to-bg-elev{--tw-gradient-to: #12151a var(--tw-gradient-to-position)}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.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}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-top:0;padding-bottom:0}.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-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.pb-1{padding-bottom:.25rem}.pb-2{padding-bottom:.5rem}.pb-3{padding-bottom:.75rem}.pl-4{padding-left:1rem}.pr-2{padding-right:.5rem}.pt-1{padding-top:.25rem}.pt-2{padding-top:.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,monospace}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[9px\]{font-size:9px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.normal-case{text-transform:none}.italic{font-style:italic}.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-5{line-height:1.25rem}.leading-relaxed{line-height:1.625}.tracking-normal{letter-spacing:0em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-accent{--tw-text-opacity: 1;color:rgb(217 119 87 / var(--tw-text-opacity, 1))}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-bg{--tw-text-opacity: 1;color:rgb(11 13 16 / var(--tw-text-opacity, 1))}.text-blue-400\/80{color:#60a5facc}.text-emerald-200{--tw-text-opacity: 1;color:rgb(167 243 208 / var(--tw-text-opacity, 1))}.text-fg{--tw-text-opacity: 1;color:rgb(230 232 236 / var(--tw-text-opacity, 1))}.text-fg-dim{--tw-text-opacity: 1;color:rgb(138 147 160 / var(--tw-text-opacity, 1))}.text-fg-faint{--tw-text-opacity: 1;color:rgb(84 92 104 / var(--tw-text-opacity, 1))}.text-green-300{--tw-text-opacity: 1;color:rgb(134 239 172 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-purple-400\/80{color:#c084fccc}.text-red-200{--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity, 1))}.text-red-300{--tw-text-opacity: 1;color:rgb(252 165 165 / 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-yellow-200{--tw-text-opacity: 1;color:rgb(254 240 138 / var(--tw-text-opacity, 1))}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}.text-yellow-500\/70{color:#eab308b3}.text-yellow-500\/80{color:#eab308cc}.text-yellow-500\/90{color:#eab308e6}.underline{text-decoration-line:underline}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.underline-offset-2{text-underline-offset:2px}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.placeholder-fg-faint::-moz-placeholder{--tw-placeholder-opacity: 1;color:rgb(84 92 104 / var(--tw-placeholder-opacity, 1))}.placeholder-fg-faint::placeholder{--tw-placeholder-opacity: 1;color:rgb(84 92 104 / var(--tw-placeholder-opacity, 1))}.accent-accent{accent-color:#d97757}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-75{opacity:.75}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px 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-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-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px 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}.ring{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-red-400{--tw-ring-opacity: 1;--tw-ring-color: rgb(248 113 113 / var(--tw-ring-opacity, 1))}.ring-red-500{--tw-ring-opacity: 1;--tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity, 1))}.blur{--tw-blur: blur(8px);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)}.drop-shadow-2xl{--tw-drop-shadow: drop-shadow(0 25px 25px rgb(0 0 0 / .15));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)}.invert{--tw-invert: invert(100%);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)!important}.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-\[width\]{transition-property:width;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-opacity{transition-property:opacity;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-75{transition-duration:75ms}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}html,body,#root{height:100%;margin:0;overflow:hidden}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{background:#0b0d10}::-webkit-scrollbar-thumb{background:#242a33;border-radius:5px}::-webkit-scrollbar-thumb:hover{background:#2f3742}.placeholder\:text-fg-faint::-moz-placeholder{--tw-text-opacity: 1;color:rgb(84 92 104 / var(--tw-text-opacity, 1))}.placeholder\:text-fg-faint::placeholder{--tw-text-opacity: 1;color:rgb(84 92 104 / var(--tw-text-opacity, 1))}.hover\:border-accent:hover{--tw-border-opacity: 1;border-color:rgb(217 119 87 / var(--tw-border-opacity, 1))}.hover\:border-fg-faint:hover{--tw-border-opacity: 1;border-color:rgb(84 92 104 / var(--tw-border-opacity, 1))}.hover\:bg-accent:hover{--tw-bg-opacity: 1;background-color:rgb(217 119 87 / var(--tw-bg-opacity, 1))}.hover\:bg-accent\/25:hover{background-color:#d9775740}.hover\:bg-accent\/40:hover{background-color:#d9775766}.hover\:bg-bg:hover{--tw-bg-opacity: 1;background-color:rgb(11 13 16 / var(--tw-bg-opacity, 1))}.hover\:bg-bg-elev:hover{--tw-bg-opacity: 1;background-color:rgb(18 21 26 / var(--tw-bg-opacity, 1))}.hover\:bg-bg-elev\/20:hover{background-color:#12151a33}.hover\:bg-bg-elev\/50:hover{background-color:#12151a80}.hover\:bg-bg-hi:hover{--tw-bg-opacity: 1;background-color:rgb(26 31 39 / var(--tw-bg-opacity, 1))}.hover\:bg-bg-hi\/50:hover{background-color:#1a1f2780}.hover\:bg-transparent:hover{background-color:transparent}.hover\:bg-yellow-600:hover{--tw-bg-opacity: 1;background-color:rgb(202 138 4 / var(--tw-bg-opacity, 1))}.hover\:text-bg:hover{--tw-text-opacity: 1;color:rgb(11 13 16 / var(--tw-text-opacity, 1))}.hover\:text-fg:hover{--tw-text-opacity: 1;color:rgb(230 232 236 / var(--tw-text-opacity, 1))}.hover\:text-fg-dim:hover{--tw-text-opacity: 1;color:rgb(138 147 160 / var(--tw-text-opacity, 1))}.hover\:text-red-400:hover{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-90:hover{opacity:.9}.focus\:border-fg-faint:focus{--tw-border-opacity: 1;border-color:rgb(84 92 104 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.active\:bg-accent\/60:active{background-color:#d9775799}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:opacity-100{opacity:1}
|