conductor-remote 1.52.2 → 1.53.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/dist/assets/index-CNdG7lFR.css +1 -0
- package/dist/assets/index-DeKtykCg.js +44 -0
- package/dist/index.html +2 -2
- package/dist/sw.js +1 -1
- package/dist-node/src/attachments.js +1 -1
- package/dist-node/src/firstprompt.js +48 -6
- package/dist-node/src/routes.js +4 -0
- package/dist-node/src/server.js +70 -3
- package/dist-node/src/shared.js +53 -0
- package/dist-node/src/staged-attachments.js +96 -0
- package/package.json +1 -1
- package/dist/assets/index-Bcai01v3.css +0 -1
- package/dist/assets/index-CJWy7b5t.js +0 -44
package/dist/index.html
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
<title>Conductor Remote</title>
|
|
14
14
|
<!-- Runs before the module bundle so it can catch a stale shell that fails to boot. -->
|
|
15
15
|
<script src="/self-heal.js"></script>
|
|
16
|
-
<script type="module" crossorigin src="/assets/index-
|
|
17
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
16
|
+
<script type="module" crossorigin src="/assets/index-DeKtykCg.js"></script>
|
|
17
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CNdG7lFR.css">
|
|
18
18
|
<link rel="manifest" href="/manifest.webmanifest"></head>
|
|
19
19
|
<body>
|
|
20
20
|
<div id="root"></div>
|
package/dist/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let l={};const t=e=>i(e,o),c={module:{uri:o},exports:l,require:t};s[o]=Promise.all(n.map(e=>c[e]||t(e))).then(e=>(r(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";importScripts("/push-sw.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"self-heal.js",revision:"49bd63adb25a09341f8d2610e8bd3c76"},{url:"push-sw.js",revision:"e1e682e2e5e88fa03b7808ae9db8e098"},{url:"index.html",revision:"
|
|
1
|
+
if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let l={};const t=e=>i(e,o),c={module:{uri:o},exports:l,require:t};s[o]=Promise.all(n.map(e=>c[e]||t(e))).then(e=>(r(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";importScripts("/push-sw.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"self-heal.js",revision:"49bd63adb25a09341f8d2610e8bd3c76"},{url:"push-sw.js",revision:"e1e682e2e5e88fa03b7808ae9db8e098"},{url:"index.html",revision:"202ce98b69f7471208f90362c8f3f4bd"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-DeKtykCg.js",revision:null},{url:"assets/index-CNdG7lFR.css",revision:null},{url:"apple-touch-icon.png",revision:"2b9301416b880d45d4bb655f2600d1f2"},{url:"icon-192.png",revision:"c5e01ac58768627e18ee7b8b6a9239ef"},{url:"icon-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon-maskable-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon.svg",revision:"c1aee186821798733dd477e69a0ef243"},{url:"manifest.webmanifest",revision:"cf88fbc5755108a7fe0616fa160a8a15"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api\//]}))});
|
|
@@ -84,5 +84,5 @@ export function writeAttachment(worktree, name, body) {
|
|
|
84
84
|
const absPath = path.join(dir, safe);
|
|
85
85
|
fs.writeFileSync(absPath, body);
|
|
86
86
|
const relPath = path.join(ATTACHMENT_DIR, id, safe);
|
|
87
|
-
return { name: safe, relPath, absPath, bytes: Buffer.byteLength(body), token: attachmentToken(safe, relPath) };
|
|
87
|
+
return { id, name: safe, relPath, absPath, bytes: Buffer.byteLength(body), token: attachmentToken(safe, relPath) };
|
|
88
88
|
}
|
|
@@ -89,10 +89,18 @@ export class FirstPromptQueue {
|
|
|
89
89
|
* prompt lands (`null`) or is given up on (the failed entry) — awaited by API
|
|
90
90
|
* callers that asked to block, ignored by the phone.
|
|
91
91
|
*/
|
|
92
|
-
enqueue(workspaceId, text, sendImmediately = true) {
|
|
92
|
+
enqueue(workspaceId, text, sendImmediately = true, attachmentIds = []) {
|
|
93
93
|
this.entries = [
|
|
94
94
|
...this.entries.filter(e => e.workspaceId !== workspaceId),
|
|
95
|
-
{
|
|
95
|
+
{
|
|
96
|
+
workspaceId,
|
|
97
|
+
text,
|
|
98
|
+
...(attachmentIds.length ? { attachmentIds } : {}),
|
|
99
|
+
status: 'waiting',
|
|
100
|
+
attempts: 0,
|
|
101
|
+
createdAt: Date.now(),
|
|
102
|
+
sendImmediately
|
|
103
|
+
}
|
|
96
104
|
];
|
|
97
105
|
this.save();
|
|
98
106
|
const settled = new Promise(resolve => {
|
|
@@ -105,10 +113,12 @@ export class FirstPromptQueue {
|
|
|
105
113
|
}
|
|
106
114
|
/** Drop an entry — dismissed from the phone, or superseded by a send the user made themselves. */
|
|
107
115
|
forget(workspaceId) {
|
|
108
|
-
|
|
116
|
+
const entry = this.entries.find(e => e.workspaceId === workspaceId);
|
|
117
|
+
if (!entry)
|
|
109
118
|
return false;
|
|
110
|
-
this.entries = this.entries.filter(e => e
|
|
119
|
+
this.entries = this.entries.filter(e => e !== entry);
|
|
111
120
|
this.save();
|
|
121
|
+
this.discard(entry);
|
|
112
122
|
this.settle(workspaceId, null);
|
|
113
123
|
return true;
|
|
114
124
|
}
|
|
@@ -159,6 +169,19 @@ export class FirstPromptQueue {
|
|
|
159
169
|
if (!sendable && Date.now() - entry.createdAt > MAX_AGE_MS) {
|
|
160
170
|
return this.fail(entry, 'the workspace never finished setting up');
|
|
161
171
|
}
|
|
172
|
+
if (entry.attachmentIds?.length) {
|
|
173
|
+
if (!target?.worktree)
|
|
174
|
+
return;
|
|
175
|
+
if (!this.deps.materialize)
|
|
176
|
+
return this.fail(entry, 'the relay cannot place the attached files');
|
|
177
|
+
const attachmentIds = entry.attachmentIds;
|
|
178
|
+
const materialized = await this.deps.materialize(entry.workspaceId, target.worktree, attachmentIds);
|
|
179
|
+
if (!materialized.ok)
|
|
180
|
+
return this.fail(entry, materialized.error ?? 'the attached files could not be copied');
|
|
181
|
+
entry.attachmentIds = [];
|
|
182
|
+
this.save();
|
|
183
|
+
this.discardIds(attachmentIds);
|
|
184
|
+
}
|
|
162
185
|
if (!target?.sessionId)
|
|
163
186
|
return;
|
|
164
187
|
// It already went — the user sent it from the Mac, where the deep link left it
|
|
@@ -216,6 +239,7 @@ export class FirstPromptQueue {
|
|
|
216
239
|
delivered(entry) {
|
|
217
240
|
this.entries = this.entries.filter(e => e !== entry);
|
|
218
241
|
this.save();
|
|
242
|
+
this.discard(entry);
|
|
219
243
|
this.settle(entry.workspaceId, null);
|
|
220
244
|
}
|
|
221
245
|
/** Given up on: kept, so the phone can show the text and the reason. */
|
|
@@ -225,6 +249,17 @@ export class FirstPromptQueue {
|
|
|
225
249
|
this.save();
|
|
226
250
|
this.settle(entry.workspaceId, entry);
|
|
227
251
|
}
|
|
252
|
+
discard(entry) {
|
|
253
|
+
if (entry.attachmentIds?.length)
|
|
254
|
+
this.discardIds(entry.attachmentIds);
|
|
255
|
+
}
|
|
256
|
+
discardIds(attachmentIds) {
|
|
257
|
+
if (!attachmentIds.length || !this.deps.discard)
|
|
258
|
+
return;
|
|
259
|
+
void Promise.resolve(this.deps.discard(attachmentIds)).catch(err => {
|
|
260
|
+
console.warn(`[relay] could not discard staged attachments: ${err instanceof Error ? err.message : err}`);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
228
263
|
settle(workspaceId, result) {
|
|
229
264
|
const list = this.waiters.get(workspaceId);
|
|
230
265
|
if (!list)
|
|
@@ -245,9 +280,16 @@ export class FirstPromptQueue {
|
|
|
245
280
|
const parsed = JSON.parse(raw);
|
|
246
281
|
if (!Array.isArray(parsed))
|
|
247
282
|
return [];
|
|
248
|
-
return parsed
|
|
283
|
+
return parsed
|
|
284
|
+
.filter(e => typeof e?.workspaceId === 'string' &&
|
|
249
285
|
typeof e.text === 'string' &&
|
|
250
|
-
Date.now() - (e.createdAt ?? 0) < KEEP_FAILED_MS)
|
|
286
|
+
Date.now() - (e.createdAt ?? 0) < KEEP_FAILED_MS)
|
|
287
|
+
.map(e => ({
|
|
288
|
+
...e,
|
|
289
|
+
attachmentIds: Array.isArray(e.attachmentIds)
|
|
290
|
+
? e.attachmentIds.filter((id) => typeof id === 'string')
|
|
291
|
+
: undefined
|
|
292
|
+
}));
|
|
251
293
|
}
|
|
252
294
|
catch (err) {
|
|
253
295
|
console.warn(`[relay] ignoring unreadable ${this.file}: ${err instanceof Error ? err.message : err}`);
|
package/dist-node/src/routes.js
CHANGED
|
@@ -49,6 +49,10 @@ export const routes = {
|
|
|
49
49
|
localImage: param('GET', '/api/local-images/:path'),
|
|
50
50
|
/** A source-file preview linked from agent Markdown. */
|
|
51
51
|
filePreview: param('GET', '/api/files/:reference'),
|
|
52
|
+
/** Temporarily hold a file while the phone creates the workspace it will belong to. */
|
|
53
|
+
stageAttachment: flat('POST', '/api/attachments'),
|
|
54
|
+
/** Drop a staged file the user removed before creating its workspace. */
|
|
55
|
+
discardStagedAttachment: param('DELETE', '/api/attachments/:attachmentId'),
|
|
52
56
|
logs: flat('GET', '/api/logs'),
|
|
53
57
|
settings: flat('GET', '/api/settings'),
|
|
54
58
|
updateSettings: flat('PATCH', '/api/settings'),
|
package/dist-node/src/server.js
CHANGED
|
@@ -24,6 +24,7 @@ import { isRoute, routeParam, routes } from "./routes.js";
|
|
|
24
24
|
import { foldHits, queryTokens, SearchIndex } from "./search.js";
|
|
25
25
|
import { SendOnce } from "./sendonce.js";
|
|
26
26
|
import { readSettings, writeSettings } from "./settings.js";
|
|
27
|
+
import { discardStagedAttachment, materializeStagedAttachments, stageAttachment, stagedAttachments } from "./staged-attachments.js";
|
|
27
28
|
import { driftWarningLines, tailscaleBin } from "./tailscale.js";
|
|
28
29
|
import { renderTranscript } from "./transcript.js";
|
|
29
30
|
import { autoJoinHotspotMode, currentSsid, looksLikeHotspot, preferredNetworks } from "./wifi.js";
|
|
@@ -35,6 +36,7 @@ const cfg = loadConfig();
|
|
|
35
36
|
const db = new ConductorDb(cfg.dbPath);
|
|
36
37
|
const reads = new Reads(db, cfg.workspacesRoot);
|
|
37
38
|
const actuator = pickActuator(cfg.writeStrategy);
|
|
39
|
+
const STAGED_ATTACHMENTS_DIR = path.join(stateDir(), 'attachment-staging');
|
|
38
40
|
// Full-text index over the chat prose, in the relay's own sidecar DB — never in
|
|
39
41
|
// Conductor's (see src/search.ts). It backfills in the background and is disposable:
|
|
40
42
|
// deleting the file rebuilds it on the next start.
|
|
@@ -293,7 +295,8 @@ const firstPrompts = new FirstPromptQueue(path.join(stateDir(), 'first-prompts.j
|
|
|
293
295
|
return {
|
|
294
296
|
phase: ws.state === 'ready' ? 'ready' : 'setting_up',
|
|
295
297
|
sessionId: session?.id ?? null,
|
|
296
|
-
alreadySent: !!session?.last_user_message_at
|
|
298
|
+
alreadySent: !!session?.last_user_message_at,
|
|
299
|
+
worktree: ws.worktree
|
|
297
300
|
};
|
|
298
301
|
},
|
|
299
302
|
// The queue fires on its own schedule, so it must never make a human tap wait.
|
|
@@ -304,6 +307,19 @@ const firstPrompts = new FirstPromptQueue(path.join(stateDir(), 'first-prompts.j
|
|
|
304
307
|
const result = await deliverPrompt(ws, sessionId, text);
|
|
305
308
|
return { ok: result.ok, error: result.error, blocked: lockBlocked(result.error) };
|
|
306
309
|
}),
|
|
310
|
+
materialize: async (_workspaceId, worktree, attachmentIds) => {
|
|
311
|
+
try {
|
|
312
|
+
materializeStagedAttachments(STAGED_ATTACHMENTS_DIR, worktree, attachmentIds);
|
|
313
|
+
return { ok: true };
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
return { ok: false, error: err instanceof Error ? err.message : 'the attached files could not be copied' };
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
discard: attachmentIds => {
|
|
320
|
+
for (const id of attachmentIds)
|
|
321
|
+
discardStagedAttachment(STAGED_ATTACHMENTS_DIR, id);
|
|
322
|
+
},
|
|
307
323
|
// A locked Mac holds first prompts whole — no attempts spent, no aging — instead
|
|
308
324
|
// of burning all three sends into a lock screen nobody is there to see.
|
|
309
325
|
gate: async () => (await screenLocked()) !== true
|
|
@@ -895,12 +911,37 @@ const server = http.createServer(async (req, res) => {
|
|
|
895
911
|
});
|
|
896
912
|
return json(req, res, result.ok ? 200 : 502, result);
|
|
897
913
|
}
|
|
914
|
+
// POST /api/attachments — hold one phone file until its new workspace has a worktree.
|
|
915
|
+
if (isRoute(routes.stageAttachment, req.method, pathname)) {
|
|
916
|
+
const name = attachmentHeaderName(req);
|
|
917
|
+
if (!name)
|
|
918
|
+
return json(req, res, 400, { error: 'missing attachment name' });
|
|
919
|
+
const bytes = await readAttachmentBody(req);
|
|
920
|
+
if (!bytes.length)
|
|
921
|
+
return json(req, res, 400, { error: 'empty attachment' });
|
|
922
|
+
const attachment = stageAttachment(STAGED_ATTACHMENTS_DIR, name, bytes);
|
|
923
|
+
return json(req, res, 201, { ok: true, attachment });
|
|
924
|
+
}
|
|
925
|
+
// DELETE /api/attachments/:id — a file removed from the new-workspace sheet.
|
|
926
|
+
const stagedAttachment = routeParam(routes.discardStagedAttachment, req.method, pathname);
|
|
927
|
+
if (stagedAttachment)
|
|
928
|
+
return json(req, res, discardStagedAttachment(STAGED_ATTACHMENTS_DIR, stagedAttachment) ? 200 : 404, {
|
|
929
|
+
ok: true
|
|
930
|
+
});
|
|
898
931
|
// POST /api/workspaces { repo, prompt, send? } — create a workspace via Conductor's deep link
|
|
899
932
|
if (isRoute(routes.createWorkspace, req.method, pathname)) {
|
|
900
933
|
const body = JSON.parse((await readBody(req)) || '{}');
|
|
934
|
+
const attachmentIds = body.attachmentIds ?? [];
|
|
935
|
+
if (!Array.isArray(attachmentIds) || attachmentIds.some(id => typeof id !== 'string'))
|
|
936
|
+
return json(req, res, 400, { error: 'attachment ids must be a list of strings' });
|
|
937
|
+
const attachments = stagedAttachments(STAGED_ATTACHMENTS_DIR, attachmentIds);
|
|
938
|
+
if (!attachments)
|
|
939
|
+
return json(req, res, 409, { error: 'an attached file is no longer available; add it again' });
|
|
901
940
|
// The prompt is optional — a bare `path=` opens an empty workspace, like
|
|
902
941
|
// Conductor's own New workspace — but *something* has to say where it goes.
|
|
903
|
-
const prompt = (body.prompt ?? '').trim()
|
|
942
|
+
const prompt = [...attachments.map(attachment => attachment.token), (body.prompt ?? '').trim()]
|
|
943
|
+
.filter(Boolean)
|
|
944
|
+
.join('\n');
|
|
904
945
|
if (!prompt && !body.repo)
|
|
905
946
|
return json(req, res, 400, { error: 'need a repo or a prompt' });
|
|
906
947
|
// Resolve the repo to a real path: an unmatched `path` would silently land
|
|
@@ -934,7 +975,9 @@ const server = http.createServer(async (req, res) => {
|
|
|
934
975
|
// its own schedule and the phone watches it in /api/state; `send:true` opts API
|
|
935
976
|
// callers into waiting.
|
|
936
977
|
// Whatever happens, the prompt is already pre-filled in Conductor's composer.
|
|
937
|
-
const settled = prompt
|
|
978
|
+
const settled = prompt
|
|
979
|
+
? firstPrompts.enqueue(created.id, prompt, body.sendImmediately !== false, attachmentIds)
|
|
980
|
+
: null;
|
|
938
981
|
const failed = settled && body.send === true ? await settled : null;
|
|
939
982
|
settled?.catch(() => undefined); // fire-and-forget: it reports failure, it never rejects
|
|
940
983
|
return json(req, res, 200, {
|
|
@@ -1202,6 +1245,30 @@ const server = http.createServer(async (req, res) => {
|
|
|
1202
1245
|
console.info(`[relay] send to ${ws.branch ?? ws.id} already delivered for this tap — answering, not resending`);
|
|
1203
1246
|
}
|
|
1204
1247
|
const answer = await sendOnce.run(body.clientId, async () => {
|
|
1248
|
+
// A failed first-prompt entry offers the same Retry button as an ordinary
|
|
1249
|
+
// prompt. If staging had been the failure, put its files in place before
|
|
1250
|
+
// that retry reaches the attachment tokens.
|
|
1251
|
+
const first = firstPrompts.get(ws.id);
|
|
1252
|
+
if (first?.attachmentIds?.length && first.text === text) {
|
|
1253
|
+
if (!ws.worktree)
|
|
1254
|
+
return {
|
|
1255
|
+
status: 409,
|
|
1256
|
+
body: { ok: false, strategy: actuator.name, error: 'worktree path unresolved' }
|
|
1257
|
+
};
|
|
1258
|
+
try {
|
|
1259
|
+
materializeStagedAttachments(STAGED_ATTACHMENTS_DIR, ws.worktree, first.attachmentIds);
|
|
1260
|
+
}
|
|
1261
|
+
catch (err) {
|
|
1262
|
+
return {
|
|
1263
|
+
status: 409,
|
|
1264
|
+
body: {
|
|
1265
|
+
ok: false,
|
|
1266
|
+
strategy: actuator.name,
|
|
1267
|
+
error: err instanceof Error ? err.message : 'the attached files could not be copied'
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1205
1272
|
if (agent) {
|
|
1206
1273
|
const applied = await applyAgentPatch(ws, sessionId, agent);
|
|
1207
1274
|
if (!applied.ok) {
|
package/dist-node/src/shared.js
CHANGED
|
@@ -65,3 +65,56 @@ export function modelPickerLabel(label) {
|
|
|
65
65
|
*/
|
|
66
66
|
export const HIT_OPEN = '\u0001';
|
|
67
67
|
export const HIT_CLOSE = '\u0002';
|
|
68
|
+
const ATTACHMENT_PREFIX = '.context/attachments/';
|
|
69
|
+
/**
|
|
70
|
+
* Read Conductor attachment tokens from prompt text.
|
|
71
|
+
*
|
|
72
|
+
* `encodeURIComponent` intentionally leaves parentheses alone. Looking for the first
|
|
73
|
+
* closing parenthesis would therefore break an ordinary file such as `diagram (old).png`.
|
|
74
|
+
* Match a candidate only once its decoded path has Conductor's attachment layout and
|
|
75
|
+
* its basename equals the visible name.
|
|
76
|
+
*/
|
|
77
|
+
export function attachmentTokens(text) {
|
|
78
|
+
const tokens = [];
|
|
79
|
+
let offset = 0;
|
|
80
|
+
while (offset < text.length) {
|
|
81
|
+
const start = text.indexOf('@⟦', offset);
|
|
82
|
+
if (start < 0)
|
|
83
|
+
break;
|
|
84
|
+
const labelEnd = text.indexOf('⟧(', start + 2);
|
|
85
|
+
if (labelEnd < 0 || /[\r\n]/.test(text.slice(start, labelEnd))) {
|
|
86
|
+
offset = start + 2;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const name = text.slice(start + 2, labelEnd);
|
|
90
|
+
const pathStart = labelEnd + 2;
|
|
91
|
+
let close = pathStart;
|
|
92
|
+
let found = false;
|
|
93
|
+
while (!found) {
|
|
94
|
+
close = text.indexOf(')', close);
|
|
95
|
+
if (close < 0)
|
|
96
|
+
break;
|
|
97
|
+
const encoded = text.slice(pathStart, close);
|
|
98
|
+
try {
|
|
99
|
+
const filePath = decodeURIComponent(encoded);
|
|
100
|
+
const suffix = filePath.startsWith(ATTACHMENT_PREFIX) ? filePath.slice(ATTACHMENT_PREFIX.length) : '';
|
|
101
|
+
const slash = suffix.indexOf('/');
|
|
102
|
+
const id = slash < 0 ? '' : suffix.slice(0, slash);
|
|
103
|
+
const fileName = slash < 0 ? '' : suffix.slice(slash + 1);
|
|
104
|
+
if (/^[A-Za-z0-9]{6}$/.test(id) && fileName === name && !fileName.includes('/')) {
|
|
105
|
+
tokens.push({ start, end: close + 1, name, path: filePath });
|
|
106
|
+
offset = close + 1;
|
|
107
|
+
found = true;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Try a later parenthesis. It can be part of an otherwise valid file name.
|
|
113
|
+
}
|
|
114
|
+
close += 1;
|
|
115
|
+
}
|
|
116
|
+
if (!found)
|
|
117
|
+
offset = start + 2;
|
|
118
|
+
}
|
|
119
|
+
return tokens;
|
|
120
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Files picked before a new workspace exists.
|
|
3
|
+
*
|
|
4
|
+
* Conductor's attachment files belong inside a worktree, but its deep link creates
|
|
5
|
+
* that worktree after the phone has supplied the first prompt. Keep the bytes in the
|
|
6
|
+
* relay state directory for that gap. The first-prompt queue moves them into the
|
|
7
|
+
* worktree before it can send the token that refers to them.
|
|
8
|
+
*/
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { ATTACHMENT_DIR, attachmentName, attachmentToken, writeAttachment } from "./attachments.js";
|
|
12
|
+
const STAGE_ID = /^[A-Za-z0-9]{6}$/;
|
|
13
|
+
function attachmentDirectory(root, stageId) {
|
|
14
|
+
if (!STAGE_ID.test(stageId))
|
|
15
|
+
return null;
|
|
16
|
+
return path.join(root, ATTACHMENT_DIR, stageId);
|
|
17
|
+
}
|
|
18
|
+
/** Write a phone file outside a worktree until the new workspace has one. */
|
|
19
|
+
export function stageAttachment(root, name, body) {
|
|
20
|
+
const written = writeAttachment(root, name, body);
|
|
21
|
+
return { stageId: written.id, name: written.name, path: written.relPath, bytes: written.bytes, token: written.token };
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Read staged files back from disk. The directory is the authority, so a relay restart
|
|
25
|
+
* between selecting files and creating the workspace keeps the files usable.
|
|
26
|
+
*/
|
|
27
|
+
export function stagedAttachments(root, stageIds) {
|
|
28
|
+
if (new Set(stageIds).size !== stageIds.length)
|
|
29
|
+
return null;
|
|
30
|
+
const staged = [];
|
|
31
|
+
for (const stageId of stageIds) {
|
|
32
|
+
const dir = attachmentDirectory(root, stageId);
|
|
33
|
+
if (!dir)
|
|
34
|
+
return null;
|
|
35
|
+
let entries;
|
|
36
|
+
try {
|
|
37
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const files = entries.filter(entry => entry.isFile());
|
|
43
|
+
if (files.length !== 1 || files[0].name !== attachmentName(files[0].name))
|
|
44
|
+
return null;
|
|
45
|
+
const file = files[0];
|
|
46
|
+
try {
|
|
47
|
+
const info = fs.statSync(path.join(dir, file.name));
|
|
48
|
+
if (!info.isFile())
|
|
49
|
+
return null;
|
|
50
|
+
const relPath = path.join(ATTACHMENT_DIR, stageId, file.name);
|
|
51
|
+
staged.push({
|
|
52
|
+
stageId,
|
|
53
|
+
name: file.name,
|
|
54
|
+
path: relPath,
|
|
55
|
+
bytes: info.size,
|
|
56
|
+
token: attachmentToken(file.name, relPath)
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return staged;
|
|
64
|
+
}
|
|
65
|
+
/** Copy every staged file to its token path. The queue removes the staged copies after it persists this step. */
|
|
66
|
+
export function materializeStagedAttachments(root, worktree, stageIds) {
|
|
67
|
+
const staged = stagedAttachments(root, stageIds);
|
|
68
|
+
if (!staged)
|
|
69
|
+
throw new Error('an attached file is no longer available');
|
|
70
|
+
for (const attachment of staged) {
|
|
71
|
+
const source = path.join(root, attachment.path);
|
|
72
|
+
const destination = path.join(worktree, attachment.path);
|
|
73
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
74
|
+
try {
|
|
75
|
+
fs.copyFileSync(source, destination, fs.constants.COPYFILE_EXCL);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
if (!(err instanceof Error) || !('code' in err) || err.code !== 'EEXIST')
|
|
79
|
+
throw err;
|
|
80
|
+
// A relay restart can resume midway through a multi-file copy. An identical file
|
|
81
|
+
// is the successful half of that earlier run, not a collision to overwrite.
|
|
82
|
+
const sourceBytes = fs.readFileSync(source);
|
|
83
|
+
const destinationBytes = fs.readFileSync(destination);
|
|
84
|
+
if (!sourceBytes.equals(destinationBytes))
|
|
85
|
+
throw new Error(`an attachment already exists at ${attachment.path}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Discard a file the user removed before creating its workspace. */
|
|
90
|
+
export function discardStagedAttachment(root, stageId) {
|
|
91
|
+
const dir = attachmentDirectory(root, stageId);
|
|
92
|
+
if (!dir || !fs.existsSync(dir))
|
|
93
|
+
return false;
|
|
94
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
95
|
+
return true;
|
|
96
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "conductor-remote",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.53.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"packageManager": "yarn@4.15.0",
|
|
6
6
|
"description": "Phone control panel for local Conductor agents. Reads ride SQLite + git; prompts ride Conductor's own dispatch path.",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-outline-style:solid;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, -apple-system, "SF Pro Text", "Segoe UI", Roboto, sans-serif;--font-mono:ui-monospace, "SF Mono", "JetBrains Mono", "Fira Code", Menlo, monospace;--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wide:.025em;--leading-tight:1.25;--leading-snug:1.375;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--radius-3xl:1.5rem;--ease-out:cubic-bezier(0, 0, .2, 1);--animate-spin:spin 1s linear infinite;--blur-xl:24px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-bg:#0a0b0e;--color-surface:#14161b;--color-surface-2:#1b1e26;--color-border:#262a33;--color-border-soft:#1e222a;--color-text:#e7e9ee;--color-muted:#8b909d;--color-faint:#5c616d;--color-accent:#8b7dff;--color-accent-soft:#2a2650;--color-working:#f5a623;--color-idle:#3ecf8e;--color-done:#5b9dff;--color-pr-merged:#ac47ff;--color-pr-draft:#a4a3a2;--color-pr-conflicts:#fd9a00;--color-pr-mergeable:#49de80;--color-add:#3ecf8e;--color-del:#ff6b6b}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{-webkit-tap-highlight-color:transparent}html,body,#root{height:var(--app-height,100dvh)}html,body{overflow:hidden}@media(display-mode:standalone){html,body,#root{height:var(--app-height,100vh)}}html[data-standalone],html[data-standalone] body,html[data-standalone] #root{height:var(--app-height,100vh)}.app-height{height:var(--app-height,100dvh)}@media(display-mode:standalone){.app-height{height:var(--app-height,100vh)}}html[data-standalone] .app-height{height:var(--app-height,100vh)}@media(min-width:48rem){.app-height,html[data-standalone] .app-height{height:auto}}html{background:var(--color-bg)}body{background:var(--color-bg);color:var(--color-text);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;overscroll-behavior:none;margin:0}button{font:inherit;color:inherit;cursor:pointer}::-webkit-scrollbar{width:0;height:0}}@layer components{.card{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border);background-color:var(--color-surface);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3.5);text-align:left;transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));display:flex}.card:active{background-color:var(--color-surface-2);scale:.985}.pill{padding-inline:calc(var(--spacing) * 3.5);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-muted);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px}.pill-active{background-color:var(--color-surface-2);color:var(--color-text)}.ctl{padding-inline:calc(var(--spacing) * 2);padding-block:var(--spacing);--tw-font-weight:var(--font-weight-medium);font-size:13px;font-weight:var(--font-weight-medium);color:var(--color-muted);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px}.ctl-on{background-color:var(--color-surface-2);color:var(--color-text)}.ctl-staged{background-color:var(--color-accent-soft);color:var(--color-accent)}.dot{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5);background-color:var(--color-faint);border-radius:3.40282e38px;flex-shrink:0}.dot-spinner{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5);animation:var(--animate-spin);border-style:var(--tw-border-style);--spin-color:var(--color-working);border-width:2px;border-color:var(--spin-color);border-radius:3.40282e38px;flex-shrink:0}@supports (color:color-mix(in lab,red,red)){.dot-spinner{border-color:color-mix(in srgb,var(--spin-color) 30%,transparent)}}.dot-spinner{border-top-color:var(--spin-color);border-right-color:var(--spin-color)}.dot-idle{background:var(--color-idle)}.dot-done{background:var(--color-done)}.typing-dot{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5);background-color:var(--color-muted);border-radius:3.40282e38px;animation:1.2s ease-in-out infinite typing}.typing-dot:nth-child(2){animation-delay:.15s}.typing-dot:nth-child(3){animation-delay:.3s}.md>:first-child{margin-top:0}.md>:last-child{margin-bottom:0}.md p{margin:.5em 0}.md h1,.md h2,.md h3,.md h4,.md h5,.md h6{margin:.9em 0 .4em;font-weight:600;line-height:1.3}.md h1{font-size:1.25em}.md h2{font-size:1.15em}.md h3{font-size:1.05em}.md ul,.md ol{margin:.5em 0;padding-left:1.4em}.md ul{list-style:outside}.md ol{list-style:decimal}.md li,.md li>ul,.md li>ol{margin:.2em 0}.md code{background:#1b1e26cc;border-radius:.3rem}@supports (color:color-mix(in lab,red,red)){.md code{background:color-mix(in srgb,var(--color-surface-2) 80%,transparent)}}.md code{font-family:var(--font-mono);padding:.1em .35em;font-size:.85em}.md pre{border:1px solid var(--color-border-soft);background:var(--color-bg);border-radius:.6rem;margin:.5em 0;padding:.6em .75em;overflow-x:auto}.md pre code{background:0 0;padding:0;font-size:12px;line-height:1.5}.md a{color:var(--color-accent);text-underline-offset:2px;text-decoration:underline}.md blockquote{border-left:2px solid var(--color-border);color:var(--color-muted);margin:.5em 0;padding-left:.75em}.md hr{border:0;border-top:1px solid var(--color-border-soft);margin:.75em 0}.md img{border-radius:.6rem;max-width:100%;height:auto;margin:.5em 0;display:block}.md table{border-collapse:collapse;max-width:100%;margin:.5em 0;font-size:.9em;display:block;overflow-x:auto}.md th,.md td{border:1px solid var(--color-border);text-align:left;padding:.3em .6em}.md th{background:var(--color-surface-2);font-weight:600}.md strong{font-weight:600}.md input[type=checkbox]{margin-right:.4em}}@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-1{inset:var(--spacing)}.inset-x-0{inset-inline:0}.inset-y-0{inset-block:0}.top-0{top:0}.top-0\.5{top:calc(var(--spacing) * .5)}.top-1\.5{top:calc(var(--spacing) * 1.5)}.top-1\/2{top:50%}.top-full{top:100%}.-right-0\.5{right:calc(var(--spacing) * -.5)}.right-1{right:var(--spacing)}.right-1\.5{right:calc(var(--spacing) * 1.5)}.right-2{right:calc(var(--spacing) * 2)}.-bottom-0\.5{bottom:calc(var(--spacing) * -.5)}.bottom-0{bottom:0}.bottom-full{bottom:100%}.left-0{left:0}.left-0\.5{left:calc(var(--spacing) * .5)}.left-3{left:calc(var(--spacing) * 3)}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[60\]{z-index:60}.m-0{margin:0}.mx-auto{margin-inline:auto}.-mt-1{margin-top:calc(var(--spacing) * -1)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-2\.5{margin-top:calc(var(--spacing) * 2.5)}.mt-4{margin-top:calc(var(--spacing) * 4)}.-mr-1{margin-right:calc(var(--spacing) * -1)}.-mr-2{margin-right:calc(var(--spacing) * -2)}.mr-1{margin-right:var(--spacing)}.mr-1\.5{margin-right:calc(var(--spacing) * 1.5)}.mb-1{margin-bottom:var(--spacing)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.-ml-1{margin-left:calc(var(--spacing) * -1)}.ml-1{margin-left:var(--spacing)}.ml-1\.5{margin-left:calc(var(--spacing) * 1.5)}.ml-auto{margin-left:auto}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-4{-webkit-line-clamp:4;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.table{display:table}.size-1\.5{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5)}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-2\.5{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.size-7{width:calc(var(--spacing) * 7);height:calc(var(--spacing) * 7)}.size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.size-11{width:calc(var(--spacing) * 11);height:calc(var(--spacing) * 11)}.size-20{width:calc(var(--spacing) * 20);height:calc(var(--spacing) * 20)}.size-60{width:calc(var(--spacing) * 60);height:calc(var(--spacing) * 60)}.size-\[18px\]{width:18px;height:18px}.size-full{width:100%;height:100%}.h-4{height:calc(var(--spacing) * 4)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-12{height:calc(var(--spacing) * 12)}.h-full{height:100%}.max-h-40{max-height:calc(var(--spacing) * 40)}.max-h-48{max-height:calc(var(--spacing) * 48)}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-72{max-height:calc(var(--spacing) * 72)}.max-h-\[70\%\]{max-height:70%}.max-h-\[85dvh\]{max-height:85dvh}.min-h-0{min-height:0}.min-h-14{min-height:calc(var(--spacing) * 14)}.w-1{width:var(--spacing)}.w-5{width:calc(var(--spacing) * 5)}.w-11{width:calc(var(--spacing) * 11)}.w-12{width:calc(var(--spacing) * 12)}.w-44{width:calc(var(--spacing) * 44)}.w-56{width:calc(var(--spacing) * 56)}.w-60{width:calc(var(--spacing) * 60)}.w-64{width:calc(var(--spacing) * 64)}.w-\[85\%\]{width:85%}.w-full{width:100%}.w-px{width:1px}.max-w-36{max-width:calc(var(--spacing) * 36)}.max-w-40{max-width:calc(var(--spacing) * 40)}.max-w-64{max-width:calc(var(--spacing) * 64)}.max-w-80{max-width:calc(var(--spacing) * 80)}.max-w-\[55vw\]{max-width:55vw}.max-w-\[70vw\]{max-width:70vw}.max-w-\[85\%\]{max-width:85%}.max-w-\[85vw\]{max-width:85vw}.max-w-\[92\%\]{max-width:92%}.max-w-full{max-width:100%}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:0}.min-w-5{min-width:calc(var(--spacing) * 5)}.min-w-max{min-width:max-content}.flex-1{flex:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-0{--tw-translate-x:0;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-5{--tw-translate-x:calc(var(--spacing) * 5);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-0{--tw-translate-y:0;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-3{--tw-translate-y:calc(var(--spacing) * 3);translate:var(--tw-translate-x) var(--tw-translate-y)}.-rotate-90{rotate:-90deg}.rotate-180{rotate:180deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-spin{animation:var(--animate-spin)}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.resize{resize:both}.resize-none{resize:none}.scrollbar-thin{scrollbar-width:thin}.list-none{list-style-type:none}.grid-cols-\[auto_1fr\]{grid-template-columns:auto 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-start{justify-content:flex-start}.gap-0{gap:0}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}.self-start{align-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.overscroll-contain{overscroll-behavior:contain}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-3xl{border-radius:var(--radius-3xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-t-2xl{border-top-left-radius:var(--radius-2xl);border-top-right-radius:var(--radius-2xl)}.rounded-t-3xl{border-top-left-radius:var(--radius-3xl);border-top-right-radius:var(--radius-3xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-x{border-inline-style:var(--tw-border-style);border-inline-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-accent{border-color:var(--color-accent)}.border-accent\/50{border-color:#8b7dff80}@supports (color:color-mix(in lab,red,red)){.border-accent\/50{border-color:color-mix(in oklab,var(--color-accent) 50%,transparent)}}.border-border{border-color:var(--color-border)}.border-border-soft{border-color:var(--color-border-soft)}.border-del\/30{border-color:#ff6b6b4d}@supports (color:color-mix(in lab,red,red)){.border-del\/30{border-color:color-mix(in oklab,var(--color-del) 30%,transparent)}}.border-del\/40{border-color:#ff6b6b66}@supports (color:color-mix(in lab,red,red)){.border-del\/40{border-color:color-mix(in oklab,var(--color-del) 40%,transparent)}}.border-faint{border-color:var(--color-faint)}.border-white\/80{border-color:#fffc}@supports (color:color-mix(in lab,red,red)){.border-white\/80{border-color:color-mix(in oklab,var(--color-white) 80%,transparent)}}.border-t-accent{border-top-color:var(--color-accent)}.border-t-text{border-top-color:var(--color-text)}.border-l-accent{border-left-color:var(--color-accent)}.bg-accent{background-color:var(--color-accent)}.bg-accent-soft{background-color:var(--color-accent-soft)}.bg-accent-soft\/90{background-color:#2a2650e6}@supports (color:color-mix(in lab,red,red)){.bg-accent-soft\/90{background-color:color-mix(in oklab,var(--color-accent-soft) 90%,transparent)}}.bg-accent\/10{background-color:#8b7dff1a}@supports (color:color-mix(in lab,red,red)){.bg-accent\/10{background-color:color-mix(in oklab,var(--color-accent) 10%,transparent)}}.bg-accent\/25{background-color:#8b7dff40}@supports (color:color-mix(in lab,red,red)){.bg-accent\/25{background-color:color-mix(in oklab,var(--color-accent) 25%,transparent)}}.bg-add{background-color:var(--color-add)}.bg-add\/10{background-color:#3ecf8e1a}@supports (color:color-mix(in lab,red,red)){.bg-add\/10{background-color:color-mix(in oklab,var(--color-add) 10%,transparent)}}.bg-bg{background-color:var(--color-bg)}.bg-bg\/80{background-color:#0a0b0ecc}@supports (color:color-mix(in lab,red,red)){.bg-bg\/80{background-color:color-mix(in oklab,var(--color-bg) 80%,transparent)}}.bg-black{background-color:var(--color-black)}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black) 60%,transparent)}}.bg-black\/75{background-color:#000000bf}@supports (color:color-mix(in lab,red,red)){.bg-black\/75{background-color:color-mix(in oklab,var(--color-black) 75%,transparent)}}.bg-border-soft{background-color:var(--color-border-soft)}.bg-del{background-color:var(--color-del)}.bg-del\/5{background-color:#ff6b6b0d}@supports (color:color-mix(in lab,red,red)){.bg-del\/5{background-color:color-mix(in oklab,var(--color-del) 5%,transparent)}}.bg-del\/10{background-color:#ff6b6b1a}@supports (color:color-mix(in lab,red,red)){.bg-del\/10{background-color:color-mix(in oklab,var(--color-del) 10%,transparent)}}.bg-del\/15{background-color:#ff6b6b26}@supports (color:color-mix(in lab,red,red)){.bg-del\/15{background-color:color-mix(in oklab,var(--color-del) 15%,transparent)}}.bg-pr-merged\/10{background-color:#ac47ff1a}@supports (color:color-mix(in lab,red,red)){.bg-pr-merged\/10{background-color:color-mix(in oklab,var(--color-pr-merged) 10%,transparent)}}.bg-pr-merged\/15{background-color:#ac47ff26}@supports (color:color-mix(in lab,red,red)){.bg-pr-merged\/15{background-color:color-mix(in oklab,var(--color-pr-merged) 15%,transparent)}}.bg-surface{background-color:var(--color-surface)}.bg-surface-2{background-color:var(--color-surface-2)}.bg-surface-2\/60{background-color:#1b1e2699}@supports (color:color-mix(in lab,red,red)){.bg-surface-2\/60{background-color:color-mix(in oklab,var(--color-surface-2) 60%,transparent)}}.bg-surface\/40{background-color:#14161b66}@supports (color:color-mix(in lab,red,red)){.bg-surface\/40{background-color:color-mix(in oklab,var(--color-surface) 40%,transparent)}}.bg-surface\/60{background-color:#14161b99}@supports (color:color-mix(in lab,red,red)){.bg-surface\/60{background-color:color-mix(in oklab,var(--color-surface) 60%,transparent)}}.bg-surface\/70{background-color:#14161bb3}@supports (color:color-mix(in lab,red,red)){.bg-surface\/70{background-color:color-mix(in oklab,var(--color-surface) 70%,transparent)}}.bg-surface\/95{background-color:#14161bf2}@supports (color:color-mix(in lab,red,red)){.bg-surface\/95{background-color:color-mix(in oklab,var(--color-surface) 95%,transparent)}}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-working{background-color:var(--color-working)}.bg-working\/10{background-color:#f5a6231a}@supports (color:color-mix(in lab,red,red)){.bg-working\/10{background-color:color-mix(in oklab,var(--color-working) 10%,transparent)}}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-1{padding:var(--spacing)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.px-0\.5{padding-inline:calc(var(--spacing) * .5)}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-16{padding-block:calc(var(--spacing) * 16)}.py-px{padding-block:1px}.pt-0\.5{padding-top:calc(var(--spacing) * .5)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-2\.5{padding-top:calc(var(--spacing) * 2.5)}.pr-1{padding-right:var(--spacing)}.pr-9{padding-right:calc(var(--spacing) * 9)}.pb-1{padding-bottom:var(--spacing)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-2\.5{padding-bottom:calc(var(--spacing) * 2.5)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-2\.5{padding-left:calc(var(--spacing) * 2.5)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-8{padding-left:calc(var(--spacing) * 8)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11\.5px\]{font-size:11.5px}.text-\[11px\]{font-size:11px}.text-\[12\.5px\]{font-size:12.5px}.text-\[12px\]{font-size:12px}.text-\[13\.5px\]{font-size:13.5px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.leading-\[1\.5\]{--tw-leading:1.5;line-height:1.5}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.\[overflow-wrap\:anywhere\]{overflow-wrap:anywhere}.break-words{overflow-wrap:break-word}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent{color:var(--color-accent)}.text-add{color:var(--color-add)}.text-bg{color:var(--color-bg)}.text-black{color:var(--color-black)}.text-del{color:var(--color-del)}.text-del\/80{color:#ff6b6bcc}@supports (color:color-mix(in lab,red,red)){.text-del\/80{color:color-mix(in oklab,var(--color-del) 80%,transparent)}}.text-faint{color:var(--color-faint)}.text-muted{color:var(--color-muted)}.text-pr-merged{color:var(--color-pr-merged)}.text-text{color:var(--color-text)}.text-white{color:var(--color-white)}.text-working{color:var(--color-working)}.uppercase{text-transform:uppercase}.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,)}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.\[color-scheme\:dark\]{color-scheme:dark}.opacity-0{opacity:0}.opacity-40{opacity:.4}.opacity-60{opacity:.6}.opacity-100{opacity:1}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_0_0_100vmax_rgba\(0\,0\,0\,0\.5\)\]{--tw-shadow:0 0 0 100vmax var(--tw-shadow-color,#00000080);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-black\/40{--tw-shadow-color:#0006}@supports (color:color-mix(in lab,red,red)){.shadow-black\/40{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--color-black) 40%, transparent) var(--tw-shadow-alpha), transparent)}}.shadow-black\/60{--tw-shadow-color:#0009}@supports (color:color-mix(in lab,red,red)){.shadow-black\/60{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--color-black) 60%, transparent) var(--tw-shadow-alpha), transparent)}}.ring-surface{--tw-ring-color:var(--color-surface)}.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,)}.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,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-xl{--tw-backdrop-blur:blur(var(--blur-xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.group-open\/steps\:invisible:is(:where(.group\/steps):is([open],:popover-open,:open) *){visibility:hidden}.group-open\/steps\:rotate-90:is(:where(.group\/steps):is([open],:popover-open,:open) *),.group-open\/think\:rotate-90:is(:where(.group\/think):is([open],:popover-open,:open) *){rotate:90deg}.peer-focus-visible\:outline-2:is(:where(.peer):focus-visible~*){outline-style:var(--tw-outline-style);outline-width:2px}.peer-focus-visible\:outline-offset-2:is(:where(.peer):focus-visible~*){outline-offset:2px}.peer-focus-visible\:outline-accent:is(:where(.peer):focus-visible~*){outline-color:var(--color-accent)}.placeholder\:font-sans::placeholder{font-family:var(--font-sans)}.placeholder\:text-faint::placeholder{color:var(--color-faint)}@media(hover:hover){.hover\:underline:hover{text-decoration-line:underline}}.focus\:border-accent\/50:focus{border-color:#8b7dff80}@supports (color:color-mix(in lab,red,red)){.focus\:border-accent\/50:focus{border-color:color-mix(in oklab,var(--color-accent) 50%,transparent)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:-outline-offset-2:focus-visible{outline-offset:-2px}.focus-visible\:outline-accent:focus-visible{outline-color:var(--color-accent)}.active\:scale-90:active{--tw-scale-x:90%;--tw-scale-y:90%;--tw-scale-z:90%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:scale-95:active{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:scale-\[0\.985\]:active{scale:.985}.active\:bg-black\/70:active{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.active\:bg-black\/70:active{background-color:color-mix(in oklab,var(--color-black) 70%,transparent)}}.active\:bg-surface:active{background-color:var(--color-surface)}.active\:bg-surface-2:active{background-color:var(--color-surface-2)}.active\:text-text:active{color:var(--color-text)}.active\:opacity-80:active{opacity:.8}.disabled\:bg-surface-2:disabled{background-color:var(--color-surface-2)}.disabled\:text-faint:disabled{color:var(--color-faint)}.disabled\:text-faint\/40:disabled{color:#5c616d66}@supports (color:color-mix(in lab,red,red)){.disabled\:text-faint\/40:disabled{color:color-mix(in oklab,var(--color-faint) 40%,transparent)}}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.has-\[textarea\:focus\]\:border-accent\/60:has(:is(textarea:focus)){border-color:#8b7dff99}@supports (color:color-mix(in lab,red,red)){.has-\[textarea\:focus\]\:border-accent\/60:has(:is(textarea:focus)){border-color:color-mix(in oklab,var(--color-accent) 60%,transparent)}}@media(prefers-reduced-motion:reduce){.motion-reduce\:transition-none{transition-property:none}}@media(min-width:400px){.min-\[400px\]\:block{display:block}}@media(min-width:48rem){.md\:static{position:static}.md\:inset-0{inset:0}.md\:inset-6{inset:calc(var(--spacing) * 6)}.md\:inset-x-auto{inset-inline:auto}.md\:top-16{top:calc(var(--spacing) * 16)}.md\:left-1\/2{left:50%}.md\:z-auto{z-index:auto}.md\:m-auto{margin:auto}.md\:mb-6{margin-bottom:calc(var(--spacing) * 6)}.md\:block{display:block}.md\:hidden{display:none}.md\:h-fit{height:fit-content}.md\:max-h-\[70vh\]{max-height:70vh}.md\:w-72{width:calc(var(--spacing) * 72)}.md\:w-\[36rem\]{width:36rem}.md\:max-w-\[92vw\]{max-width:92vw}.md\:max-w-none{max-width:none}.md\:shrink-0{flex-shrink:0}.md\:-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.md\:translate-x-0{--tw-translate-x:0;translate:var(--tw-translate-x) var(--tw-translate-y)}.md\:rounded-2xl{border-radius:var(--radius-2xl)}.md\:rounded-3xl{border-radius:var(--radius-3xl)}.md\:border{border-style:var(--tw-border-style);border-width:1px}.md\:border-border{border-color:var(--color-border)}.md\:border-border-soft{border-color:var(--color-border-soft)}.md\:shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.md\:transition-none{transition-property:none}}@media(min-width:64rem){.lg\:static{position:static}.lg\:z-auto{z-index:auto}.lg\:w-80{width:calc(var(--spacing) * 80)}.lg\:w-\[380px\]{width:380px}.lg\:shrink-0{flex-shrink:0}.lg\:border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.lg\:border-border-soft{border-color:var(--color-border-soft)}}@media(min-width:80rem){.xl\:w-\[460px\]{width:460px}}.\[\&\:\:-webkit-details-marker\]\:hidden::-webkit-details-marker{display:none}}@keyframes typing{0%,60%,to{opacity:.35;transform:none}30%{opacity:1;transform:translateY(-3px)}}@keyframes fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}.fade-in{animation:.18s ease-out fade-in}@media(prefers-reduced-motion:reduce){.fade-in{opacity:1;animation:none}.typing-dot{opacity:.6;animation:none}}.pt-safe{padding-top:max(env(safe-area-inset-top),.5rem)}.pb-safe{padding-bottom:max(env(safe-area-inset-bottom),.5rem)}html[data-keyboard] .pb-safe{padding-bottom:.5rem}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@keyframes spin{to{transform:rotate(360deg)}}
|