@fapost/jig-ui 0.2.0 → 0.3.0-next.38
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/board/columns.js +14 -2
- package/dist/http/app.js +95 -1
- package/dist/jig/client.js +93 -12
- package/dist/source/local.js +71 -13
- package/dist/web/assets/CanvasView-C5ctZB7z.js +1 -0
- package/dist/web/assets/index-BAP4tdVQ.css +2 -0
- package/dist/web/assets/index-DNFgC6Vp.js +6 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/CanvasView-4RWri2ak.js +0 -1
- package/dist/web/assets/index-D1V3u6PF.css +0 -2
- package/dist/web/assets/index-DOnV4WX5.js +0 -6
package/dist/board/columns.js
CHANGED
|
@@ -23,7 +23,19 @@ export function placeOf(task) {
|
|
|
23
23
|
const id = columnOf(task);
|
|
24
24
|
return { id, title: COLUMNS.find((c) => c.id === id)?.title ?? id };
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
26
|
+
/** Jig's queue order: the top level first, then each open epic's by its name, by position there; a task not queued after them all. */
|
|
27
|
+
function byQueue(a, b) {
|
|
28
|
+
if (!a.queue || !b.queue)
|
|
29
|
+
return a.queue ? -1 : b.queue ? 1 : 0;
|
|
30
|
+
const [x, y] = [a.queue.level, b.queue.level];
|
|
31
|
+
if (x !== y)
|
|
32
|
+
return x === null ? -1 : y === null ? 1 : x < y ? -1 : 1;
|
|
33
|
+
return a.queue.position - b.queue.position;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The board: every column, in a fixed order, its cards in Jig's queue order and the tasks not queued after them in the
|
|
37
|
+
* order Jig listed them (the sort is stable). Computed on every read, never stored.
|
|
38
|
+
*/
|
|
27
39
|
export function columns(tasks) {
|
|
28
|
-
return COLUMNS.map((c) => ({ ...c, cards: tasks.filter((t) => columnOf(t) === c.id) }));
|
|
40
|
+
return COLUMNS.map((c) => ({ ...c, cards: tasks.filter((t) => columnOf(t) === c.id).sort(byQueue) }));
|
|
29
41
|
}
|
package/dist/http/app.js
CHANGED
|
@@ -32,6 +32,41 @@ function absolute(path) {
|
|
|
32
32
|
const expanded = path.trim().replace(/^~(?=$|[\\/])/, homedir());
|
|
33
33
|
return isAbsolute(expanded) ? expanded : undefined;
|
|
34
34
|
}
|
|
35
|
+
const isId = (v) => typeof v === 'string' && v !== '';
|
|
36
|
+
const isRevision = (v) => Number.isSafeInteger(v) && v >= 0;
|
|
37
|
+
const field = (body, key) => typeof body === 'object' && body !== null ? body[key] : undefined;
|
|
38
|
+
/** Where a drop puts a task: after another, or to the top or bottom of its level; `undefined` for anything else. */
|
|
39
|
+
function place(v) {
|
|
40
|
+
if (v === 'top' || v === 'bottom')
|
|
41
|
+
return v;
|
|
42
|
+
const after = field(v, 'after');
|
|
43
|
+
return isId(after) ? { after } : undefined;
|
|
44
|
+
}
|
|
45
|
+
/** A priority as the page sends it: 0 to 5, or `null` for none. */
|
|
46
|
+
const isPriority = (v) => v === null || (Number.isInteger(v) && v >= 0 && v <= 5);
|
|
47
|
+
/** A new task's id as Jig takes one (`jig_valid_id`): never empty, never led by `.` or `-`, so never a flag. */
|
|
48
|
+
const isNewId = (v) => typeof v === 'string' && /^[A-Za-z0-9_][A-Za-z0-9._-]*$/.test(v);
|
|
49
|
+
/** A goal as `jig task new --goal` takes one: a line of text, at most 500 characters; Jig refuses the rest as `usage`. */
|
|
50
|
+
const isGoal = (v) => typeof v === 'string' && v.trim() !== '' && v.length <= 500 && !/[\r\n]/.test(v);
|
|
51
|
+
/** The body of a write that names only a task: the task and the revision it was read at, `undefined` where invalid. */
|
|
52
|
+
function taskAt(body) {
|
|
53
|
+
const [task, revision] = [field(body, 'task'), field(body, 'revision')];
|
|
54
|
+
return { task: isId(task) ? task : undefined, revision: isRevision(revision) ? revision : undefined };
|
|
55
|
+
}
|
|
56
|
+
/** What a write came to, in the API's form. A refusal names no path: only Jig's code goes on. */
|
|
57
|
+
function written(c, w) {
|
|
58
|
+
if (w === undefined)
|
|
59
|
+
return refuse(c, 404, 'not-connected', 'No connected project has this id.');
|
|
60
|
+
if (w.state === 'written')
|
|
61
|
+
return c.json({ written: true }, 200);
|
|
62
|
+
if (w.state !== 'refused')
|
|
63
|
+
return c.json({ project: w }, 200);
|
|
64
|
+
if (w.code === 'stale-revision')
|
|
65
|
+
return refuse(c, 409, w.code, 'The project changed since the board was read.');
|
|
66
|
+
if (w.code === 'not-found')
|
|
67
|
+
return refuse(c, 404, w.code, 'This project has no live task with this id.');
|
|
68
|
+
return refuse(c, 422, w.code, 'Jig refused the change.');
|
|
69
|
+
}
|
|
35
70
|
/**
|
|
36
71
|
* The API, chained so that its type carries every route's answers: the page derives its types
|
|
37
72
|
* from `AppType` and never declares them a second time.
|
|
@@ -47,7 +82,8 @@ function apiRoutes(source, version) {
|
|
|
47
82
|
return refuse(c, 404, 'not-connected', 'No connected project has this id.');
|
|
48
83
|
if (read.state !== 'ready')
|
|
49
84
|
return c.json({ board: read }, 200);
|
|
50
|
-
|
|
85
|
+
const { state, finished, revision } = read;
|
|
86
|
+
return c.json({ board: { id, state, columns: columns(read.tasks), finished, revision } }, 200);
|
|
51
87
|
})
|
|
52
88
|
.get('/api/projects/:id/tasks/:task', async (c) => {
|
|
53
89
|
const read = await source.task(c.req.param('id'), c.req.param('task'));
|
|
@@ -64,6 +100,64 @@ function apiRoutes(source, version) {
|
|
|
64
100
|
if (read === undefined)
|
|
65
101
|
return refuse(c, 404, 'not-connected', 'No connected project has this id.');
|
|
66
102
|
return c.json({ specs: read }, 200);
|
|
103
|
+
})
|
|
104
|
+
// A write carries the revision of the board it was based on; Jig refuses it if anything was written since.
|
|
105
|
+
.post('/api/projects/:id/queue/move', validator('json', (body) => {
|
|
106
|
+
const [task, revision] = [field(body, 'task'), field(body, 'revision')];
|
|
107
|
+
return {
|
|
108
|
+
task: isId(task) ? task : undefined,
|
|
109
|
+
place: place(field(body, 'place')),
|
|
110
|
+
revision: isRevision(revision) ? revision : undefined,
|
|
111
|
+
};
|
|
112
|
+
}), async (c) => {
|
|
113
|
+
const { task, place, revision } = c.req.valid('json');
|
|
114
|
+
if (task === undefined || place === undefined || revision === undefined) {
|
|
115
|
+
return refuse(c, 422, 'usage', 'Give a task, where it goes and the revision it was read at.');
|
|
116
|
+
}
|
|
117
|
+
return written(c, await source.move(c.req.param('id'), task, place, revision));
|
|
118
|
+
})
|
|
119
|
+
.post('/api/projects/:id/queue/priority', validator('json', (body) => {
|
|
120
|
+
const [task, priority, revision] = [field(body, 'task'), field(body, 'priority'), field(body, 'revision')];
|
|
121
|
+
return {
|
|
122
|
+
task: isId(task) ? task : undefined,
|
|
123
|
+
// `undefined` for anything but 0 to 5 or null: a priority left out is no priority of none
|
|
124
|
+
priority: isPriority(priority) ? priority : undefined,
|
|
125
|
+
revision: isRevision(revision) ? revision : undefined,
|
|
126
|
+
};
|
|
127
|
+
}), async (c) => {
|
|
128
|
+
const { task, priority, revision } = c.req.valid('json');
|
|
129
|
+
if (task === undefined || priority === undefined || revision === undefined) {
|
|
130
|
+
return refuse(c, 422, 'usage', 'Give a task, a priority from 0 to 5 or null, and the revision it was read at.');
|
|
131
|
+
}
|
|
132
|
+
return written(c, await source.prioritise(c.req.param('id'), task, priority, revision));
|
|
133
|
+
})
|
|
134
|
+
// A pause is asked of the task, not made: Jig honours it at the task's next stage boundary.
|
|
135
|
+
.post('/api/projects/:id/queue/pause', validator('json', taskAt), async (c) => {
|
|
136
|
+
const { task, revision } = c.req.valid('json');
|
|
137
|
+
if (task === undefined || revision === undefined)
|
|
138
|
+
return refuse(c, 422, 'usage', 'Give a task and the revision it was read at.');
|
|
139
|
+
return written(c, await source.requestPause(c.req.param('id'), task, revision));
|
|
140
|
+
})
|
|
141
|
+
.post('/api/projects/:id/queue/pause/cancel', validator('json', taskAt), async (c) => {
|
|
142
|
+
const { task, revision } = c.req.valid('json');
|
|
143
|
+
if (task === undefined || revision === undefined)
|
|
144
|
+
return refuse(c, 422, 'usage', 'Give a task and the revision it was read at.');
|
|
145
|
+
return written(c, await source.cancelPause(c.req.param('id'), task, revision));
|
|
146
|
+
})
|
|
147
|
+
// A new task carries no revision: it writes nothing that exists (adr-20261022-filing-a-task-carries-no-revision).
|
|
148
|
+
.post('/api/projects/:id/tasks', validator('json', (body) => {
|
|
149
|
+
const [task, goal] = [field(body, 'task'), field(body, 'goal')];
|
|
150
|
+
return { task: isNewId(task) ? task : undefined, goal: isGoal(goal) ? goal : undefined };
|
|
151
|
+
}), async (c) => {
|
|
152
|
+
const { task, goal } = c.req.valid('json');
|
|
153
|
+
if (task === undefined || goal === undefined) {
|
|
154
|
+
return refuse(c, 422, 'usage', 'Give an id of letters, digits, ".", "_" or "-", not led by "." or "-", and a goal of one line.');
|
|
155
|
+
}
|
|
156
|
+
const w = await source.fileTask(c.req.param('id'), task, goal);
|
|
157
|
+
if (w?.state === 'refused' && w.code === 'already-exists') {
|
|
158
|
+
return refuse(c, 409, w.code, 'The project has a task with this id, maybe a finished one.');
|
|
159
|
+
}
|
|
160
|
+
return written(c, w);
|
|
67
161
|
})
|
|
68
162
|
.post('/api/projects', validator('json', (body) => ({
|
|
69
163
|
path: absolute(body?.path),
|
package/dist/jig/client.js
CHANGED
|
@@ -4,9 +4,9 @@ import { join, resolve } from 'node:path';
|
|
|
4
4
|
import { promisify } from 'node:util';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
const run = promisify(execFile);
|
|
7
|
-
/** The oldest Jig
|
|
8
|
-
export const MIN_JIG_VERSION = '0.
|
|
9
|
-
const SCRIPT =
|
|
7
|
+
/** The oldest Jig Jig UI reads and writes: 0.29.0 is the first that prints JSON, 0.31.0 the first with a queue. */
|
|
8
|
+
export const MIN_JIG_VERSION = '0.31.0';
|
|
9
|
+
const SCRIPT = '.ai/scripts/jig';
|
|
10
10
|
const TIMEOUT_MS = 10_000;
|
|
11
11
|
/** The directory whose changes may change what Jig reports: the main checkout's `.ai/`. */
|
|
12
12
|
export const watchDir = (root) => join(root, '.ai');
|
|
@@ -33,9 +33,23 @@ async function bash(platform) {
|
|
|
33
33
|
});
|
|
34
34
|
return gitBash;
|
|
35
35
|
}
|
|
36
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Runs the project's own Jig in its main checkout, without a shell. Git bash on Windows parses its own command line:
|
|
38
|
+
* it globs an argument Node left unquoted (a one-word goal `out*` became `out.json`), and with `MSYS=noglob` it reads
|
|
39
|
+
* `\"` as a backslash and a quote instead (CI, task `file-backlog-task`). There the arguments go in the environment,
|
|
40
|
+
* and a fixed script hands them to Jig, so nothing parses a person's text. The script sources Jig (which finds itself
|
|
41
|
+
* by `BASH_SOURCE`) rather than `exec` it: Git bash's `exec` starts a second bash and waits for it, and every run took
|
|
42
|
+
* half as long again (CI, task `conflict-display`).
|
|
43
|
+
*/
|
|
37
44
|
async function jig(root, args, platform) {
|
|
38
|
-
const
|
|
45
|
+
const argv = [SCRIPT, ...args];
|
|
46
|
+
const { stdout } = platform === 'win32'
|
|
47
|
+
? await run(await bash(platform), ['-c', `. ${argv.map((_, i) => `"$JIG_UI_ARG_${i}"`).join(' ')}`], {
|
|
48
|
+
cwd: root,
|
|
49
|
+
timeout: TIMEOUT_MS,
|
|
50
|
+
env: { ...process.env, ...Object.fromEntries(argv.map((arg, i) => [`JIG_UI_ARG_${i}`, arg])) },
|
|
51
|
+
})
|
|
52
|
+
: await run(await bash(platform), argv, { cwd: root, timeout: TIMEOUT_MS });
|
|
39
53
|
return stdout;
|
|
40
54
|
}
|
|
41
55
|
/**
|
|
@@ -58,7 +72,7 @@ export class JigRefusal extends Error {
|
|
|
58
72
|
this.code = code;
|
|
59
73
|
}
|
|
60
74
|
}
|
|
61
|
-
const CODES = new Set(['usage', 'not-a-repository', 'not-initialised', 'failed']);
|
|
75
|
+
const CODES = new Set(['usage', 'not-a-repository', 'not-initialised', 'stale-revision', 'not-found', 'already-exists', 'failed']);
|
|
62
76
|
/** Jig answered in a shape this Jig UI does not read: a Jig newer than the contract its tests pin. */
|
|
63
77
|
export class JigShapeError extends Error {
|
|
64
78
|
constructor() {
|
|
@@ -67,7 +81,7 @@ export class JigShapeError extends Error {
|
|
|
67
81
|
}
|
|
68
82
|
const isObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
69
83
|
/**
|
|
70
|
-
* Runs a `--json`
|
|
84
|
+
* Runs a `--json` command and returns its `data` and the clone's revision — for a write, the one it was checked against. Jig's refusal throws
|
|
71
85
|
* JigRefusal, as does output that is no JSON at all (a crash); an envelope of another shape throws
|
|
72
86
|
* JigShapeError.
|
|
73
87
|
*/
|
|
@@ -131,7 +145,20 @@ export async function readStatus(root, platform = process.platform) {
|
|
|
131
145
|
return { revision, attention, housekeepingStale: housekeeping?.stale === true };
|
|
132
146
|
}
|
|
133
147
|
/** Jig's action ids this Jig UI knows; Jig may add ids without raising `schema`, and a consumer ignores those. */
|
|
134
|
-
const ACTIONS = new Set([
|
|
148
|
+
const ACTIONS = new Set([
|
|
149
|
+
'start',
|
|
150
|
+
'pause',
|
|
151
|
+
'resume',
|
|
152
|
+
'request-pause',
|
|
153
|
+
'cancel-pause-request',
|
|
154
|
+
'approve-gate',
|
|
155
|
+
'answer-stop',
|
|
156
|
+
'set-class',
|
|
157
|
+
'ship',
|
|
158
|
+
'close',
|
|
159
|
+
'abandon',
|
|
160
|
+
'reopen',
|
|
161
|
+
]);
|
|
135
162
|
/** What a person may do next with a task, as Jig decides it from the task's state. */
|
|
136
163
|
const TaskAction = z.object({
|
|
137
164
|
id: text,
|
|
@@ -141,7 +168,7 @@ const TaskAction = z.object({
|
|
|
141
168
|
/** Jig's refusal, word for word, when not offered; it may name a path. */
|
|
142
169
|
reason: maybe,
|
|
143
170
|
});
|
|
144
|
-
/** A task as `jig task list --json` reports it, as far as Jig UI reads it; the statuses are Jig's (schemas/state.md). */
|
|
171
|
+
/** A task as `jig task list --all --json` reports it, as far as Jig UI reads it; the statuses are Jig's (schemas/state.md). */
|
|
145
172
|
const TaskRow = z
|
|
146
173
|
.object({
|
|
147
174
|
id: text,
|
|
@@ -151,6 +178,8 @@ const TaskRow = z
|
|
|
151
178
|
goal: maybe,
|
|
152
179
|
knowledge_consolidated: z.boolean(),
|
|
153
180
|
paused: z.boolean(),
|
|
181
|
+
/** A pause asked of the task and not yet honoured; its reason is a person's text and not read. */
|
|
182
|
+
pause_request: z.object({ requested_at: text }).nullable(),
|
|
154
183
|
created_at: maybe,
|
|
155
184
|
updated_at: maybe,
|
|
156
185
|
/** `null` while the task has not been started. */
|
|
@@ -174,6 +203,7 @@ const TaskRow = z
|
|
|
174
203
|
goal: t.goal,
|
|
175
204
|
knowledgeConsolidated: t.knowledge_consolidated,
|
|
176
205
|
paused: t.paused,
|
|
206
|
+
pauseRequest: t.pause_request && { requestedAt: t.pause_request.requested_at },
|
|
177
207
|
createdAt: t.created_at,
|
|
178
208
|
updatedAt: t.updated_at,
|
|
179
209
|
branch: t.branch,
|
|
@@ -193,10 +223,10 @@ const TaskRow = z
|
|
|
193
223
|
housekeepingFlags: t.housekeeping_flags,
|
|
194
224
|
actions: t.actions.filter((a) => ACTIONS.has(a.id)),
|
|
195
225
|
}));
|
|
196
|
-
const TaskList = z.object({ tasks: z.array(TaskRow)
|
|
197
|
-
/** The project's
|
|
226
|
+
const TaskList = z.object({ tasks: z.array(TaskRow) });
|
|
227
|
+
/** The project's tasks, the finished ones too while Jig keeps their workspace (`jig task list --all --json`). */
|
|
198
228
|
export async function readTasks(root, platform = process.platform) {
|
|
199
|
-
const { revision, data } = await readJson(root, ['task', 'list'], platform);
|
|
229
|
+
const { revision, data } = await readJson(root, ['task', 'list', '--all'], platform);
|
|
200
230
|
return { revision, ...parsed(TaskList, data) };
|
|
201
231
|
}
|
|
202
232
|
/** How far a roadmap or one of its phases is: items checked, all items, items with a task filed, fog items. */
|
|
@@ -243,6 +273,38 @@ export async function readSpecs(root, platform = process.platform) {
|
|
|
243
273
|
const { data } = await readJson(root, ['spec', 'list'], platform);
|
|
244
274
|
return parsed(SpecList, data).specs;
|
|
245
275
|
}
|
|
276
|
+
/** A task in Jig's queue: its level (`null` the top, `epic:<spec>` an open epic's), its place there, its priority. */
|
|
277
|
+
const QueueItem = z.object({
|
|
278
|
+
kind: text,
|
|
279
|
+
id: text,
|
|
280
|
+
level: maybe,
|
|
281
|
+
/** Sparse; compared only within one level. */
|
|
282
|
+
position: z.int(),
|
|
283
|
+
/** 0, the most urgent, to 5; `null` unset. A label: the order is `position`'s. */
|
|
284
|
+
priority: z.int().min(0).max(5).nullable(),
|
|
285
|
+
});
|
|
286
|
+
const Queue = z.object({ items: z.array(QueueItem) });
|
|
287
|
+
/** The tasks in the project's queue (`jig queue list --json`), in Jig's order; a task not queued is not there, nor is an epic. */
|
|
288
|
+
export async function readQueue(root, platform = process.platform) {
|
|
289
|
+
const { revision, data } = await readJson(root, ['queue', 'list'], platform);
|
|
290
|
+
const items = parsed(Queue, data)
|
|
291
|
+
.items.filter((i) => i.kind === 'task')
|
|
292
|
+
.map(({ id, level, position, priority }) => ({ id, level, position, priority }));
|
|
293
|
+
return { revision, items };
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Moves a task in the queue (`jig queue move`), adding it when it is not there; refused as `stale-revision`
|
|
297
|
+
* when anything was written in the clone since `revision`. Ids go to Jig as `task:<id>`, never as a flag.
|
|
298
|
+
*/
|
|
299
|
+
export async function moveInQueue(root, task, place, revision, platform = process.platform) {
|
|
300
|
+
const where = place === 'top' ? ['--top'] : place === 'bottom' ? ['--bottom'] : ['--after', `task:${place.after}`];
|
|
301
|
+
await readJson(root, ['queue', 'move', `task:${task}`, ...where, '--revision', String(revision)], platform);
|
|
302
|
+
}
|
|
303
|
+
/** Sets a task's priority in the queue (`jig queue set`), `null` for none; refused as `moveInQueue` is. */
|
|
304
|
+
export async function setPriority(root, task, priority, revision, platform = process.platform) {
|
|
305
|
+
const value = priority === null ? 'none' : String(priority);
|
|
306
|
+
await readJson(root, ['queue', 'set', `task:${task}`, '--priority', value, '--revision', String(revision)], platform);
|
|
307
|
+
}
|
|
246
308
|
/** True when `version` is at least the minimum Jig UI reads. */
|
|
247
309
|
export function supports(version, minimum = MIN_JIG_VERSION) {
|
|
248
310
|
const a = version.split('.').map(Number);
|
|
@@ -253,3 +315,22 @@ export function supports(version, minimum = MIN_JIG_VERSION) {
|
|
|
253
315
|
}
|
|
254
316
|
return true;
|
|
255
317
|
}
|
|
318
|
+
/**
|
|
319
|
+
* Asks a task to pause at its next stage boundary (`jig queue request … pause`); refused as `already-exists` when a
|
|
320
|
+
* pause is already asked, `failed` when the task is paused or finished, and as `moveInQueue` is.
|
|
321
|
+
*/
|
|
322
|
+
export async function requestPause(root, task, revision, platform = process.platform) {
|
|
323
|
+
await readJson(root, ['queue', 'request', `task:${task}`, 'pause', '--revision', String(revision)], platform);
|
|
324
|
+
}
|
|
325
|
+
/** Takes back a pause asked of a task (`jig queue cancel … pause`); refused as `not-found` when none is asked. */
|
|
326
|
+
export async function cancelPause(root, task, revision, platform = process.platform) {
|
|
327
|
+
await readJson(root, ['queue', 'cancel', `task:${task}`, 'pause', '--revision', String(revision)], platform);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Files a task with only its goal (`jig task new <id> --goal <text>`); refused as `already-exists` when the id is
|
|
331
|
+
* taken — by a finished task too — and `usage` for an id or goal Jig does not take. No revision: it writes nothing
|
|
332
|
+
* that exists (adr-20261022-filing-a-task-carries-no-revision).
|
|
333
|
+
*/
|
|
334
|
+
export async function fileTask(root, id, goal, platform = process.platform) {
|
|
335
|
+
await readJson(root, ['task', 'new', id, '--goal', goal], platform);
|
|
336
|
+
}
|
package/dist/source/local.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import { JigRefusal, JigShapeError, jigVersion, MIN_JIG_VERSION, readSpecs, readStatus, readTasks, supports, watchDir, writtenByReads, } from '../jig/client.js';
|
|
2
|
+
import { cancelPause, fileTask, JigRefusal, JigShapeError, jigVersion, MIN_JIG_VERSION, moveInQueue, readQueue, readSpecs, readStatus, readTasks, requestPause, setPriority, supports, watchDir, writtenByReads, } from '../jig/client.js';
|
|
3
3
|
import { addProject, readProjects, removeProject } from '../registry/projects.js';
|
|
4
4
|
import { watchTree } from '../watch/watch.js';
|
|
5
5
|
import { mainCheckout } from './checkout.js';
|
|
@@ -33,21 +33,33 @@ function count(tasks) {
|
|
|
33
33
|
ready: tasks.filter((t) => t.status === 'ready' && !t.paused).length,
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
-
/** A
|
|
37
|
-
const
|
|
36
|
+
/** A finished task as the board's list of them shows it. */
|
|
37
|
+
const gone = (t) => ({
|
|
38
|
+
id: t.id,
|
|
39
|
+
status: t.status,
|
|
40
|
+
goal: t.goal,
|
|
41
|
+
updatedAt: t.updatedAt,
|
|
42
|
+
pullRequest: t.pullRequest && { url: t.pullRequest.url, state: t.pullRequest.state },
|
|
43
|
+
});
|
|
44
|
+
/** A live task as a board's card shows it, with its place in the queue if it has one. */
|
|
45
|
+
const card = (t, q) => ({
|
|
38
46
|
id: t.id,
|
|
39
47
|
class: t.class,
|
|
40
48
|
status: t.status,
|
|
41
49
|
goal: t.goal,
|
|
42
50
|
paused: t.paused,
|
|
51
|
+
pauseRequest: t.pauseRequest,
|
|
52
|
+
pauseRequestable: t.actions.some((a) => a.id === 'request-pause' && a.offered),
|
|
43
53
|
started: t.branch !== null,
|
|
44
54
|
gate: t.gate?.state ?? null,
|
|
45
55
|
autopilot: t.autopilot?.state ?? null,
|
|
46
56
|
pullRequest: t.pullRequest,
|
|
57
|
+
priority: q?.priority ?? null,
|
|
58
|
+
queue: q ? { level: q.level, position: q.position } : null,
|
|
47
59
|
});
|
|
48
60
|
/** A live task in full; where its worktree is was never read, and an action Jig refuses is left out. */
|
|
49
|
-
const details = (t, attention) => ({
|
|
50
|
-
...card(t),
|
|
61
|
+
const details = (t, q, attention) => ({
|
|
62
|
+
...card(t, q),
|
|
51
63
|
issue: t.issue,
|
|
52
64
|
branch: t.branch,
|
|
53
65
|
baseBranch: t.baseBranch,
|
|
@@ -92,27 +104,31 @@ async function reading({ id, path }, read) {
|
|
|
92
104
|
}
|
|
93
105
|
async function readProject(project) {
|
|
94
106
|
const read = await reading(project, async (root, jigVersion) => {
|
|
95
|
-
const [status, list, specs] = await Promise.all([readStatus(root), readTasks(root), readSpecs(root)]);
|
|
96
|
-
return { status, list, specs, jigVersion };
|
|
107
|
+
const [status, list, queue, specs] = await Promise.all([readStatus(root), readTasks(root), readQueue(root), readSpecs(root)]);
|
|
108
|
+
return { status, list, queue, specs, jigVersion };
|
|
97
109
|
});
|
|
98
110
|
if (!('list' in read))
|
|
99
111
|
return { view: read, tasks: read, details: [], specs: read, revision: null };
|
|
100
|
-
const { status, list, specs, jigVersion } = read;
|
|
101
|
-
//
|
|
112
|
+
const { status, list, queue, specs, jigVersion } = read;
|
|
113
|
+
// Live: active and ready; the rest are finished.
|
|
102
114
|
const live = list.tasks.filter((t) => t.status === 'active' || t.status === 'ready');
|
|
115
|
+
const finished = list.tasks.filter((t) => t.status === 'consolidated' || t.status === 'abandoned');
|
|
116
|
+
const queued = new Map(queue.items.map((q) => [q.id, q]));
|
|
117
|
+
// The oldest of the reads the board is made of: a write against it is refused if anything was written after any of them.
|
|
118
|
+
const revision = Math.min(status.revision, list.revision, queue.revision);
|
|
103
119
|
return {
|
|
104
120
|
view: {
|
|
105
121
|
id: project.id,
|
|
106
122
|
state: 'ready',
|
|
107
123
|
jigVersion,
|
|
108
|
-
counts: count(
|
|
124
|
+
counts: count(live),
|
|
109
125
|
attention: status.attention,
|
|
110
126
|
housekeepingStale: status.housekeepingStale,
|
|
111
127
|
},
|
|
112
|
-
tasks: { state: 'ready', tasks: live.map(card), finished:
|
|
113
|
-
details: live.map((t) => details(t, status.attention)),
|
|
128
|
+
tasks: { state: 'ready', tasks: live.map((t) => card(t, queued.get(t.id))), finished: finished.map(gone), revision },
|
|
129
|
+
details: live.map((t) => details(t, queued.get(t.id), status.attention)),
|
|
114
130
|
specs: { state: 'ready', specs },
|
|
115
|
-
revision
|
|
131
|
+
revision,
|
|
116
132
|
};
|
|
117
133
|
}
|
|
118
134
|
/** A read that may have caught Jig mid-write: worth one more try before it is believed. */
|
|
@@ -241,6 +257,31 @@ export function localSource(configDir, { rescanMs = 60_000, quietMs, maxWaitMs }
|
|
|
241
257
|
entry.held = false;
|
|
242
258
|
}
|
|
243
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Runs a write in a project once every task it names is live in what the project shows — so nothing but an id
|
|
262
|
+
* Jig itself listed reaches Jig's arguments; a new task's id names none, and the HTTP boundary checks it against
|
|
263
|
+
* Jig's grammar instead — then reads the project again, refused or not: the page reads next.
|
|
264
|
+
*/
|
|
265
|
+
async function writing(id, taskIds, write) {
|
|
266
|
+
const project = (await readProjects(configDir)).find((p) => p.id === id);
|
|
267
|
+
if (!project)
|
|
268
|
+
return undefined;
|
|
269
|
+
const { tasks } = await shown(project);
|
|
270
|
+
if (tasks.state !== 'ready')
|
|
271
|
+
return tasks;
|
|
272
|
+
if (!taskIds.every((t) => tasks.tasks.some((live) => live.id === t)))
|
|
273
|
+
return { state: 'refused', code: 'not-found' };
|
|
274
|
+
let written = { state: 'written' };
|
|
275
|
+
try {
|
|
276
|
+
await write(project.path);
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
// Only the code goes on: Jig's message may name a path.
|
|
280
|
+
written = { state: 'refused', code: error instanceof JigRefusal ? error.code : 'failed' };
|
|
281
|
+
}
|
|
282
|
+
await refresh(entryOf(project));
|
|
283
|
+
return written;
|
|
284
|
+
}
|
|
244
285
|
return {
|
|
245
286
|
async workspace() {
|
|
246
287
|
return Promise.all((await readProjects(configDir)).map(async (p) => (await shown(p)).view));
|
|
@@ -276,6 +317,23 @@ export function localSource(configDir, { rescanMs = 60_000, quietMs, maxWaitMs }
|
|
|
276
317
|
const project = (await readProjects(configDir)).find((p) => p.id === id);
|
|
277
318
|
return project && (await shown(project)).specs;
|
|
278
319
|
},
|
|
320
|
+
move(id, taskId, place, revision) {
|
|
321
|
+
const named = typeof place === 'object' ? [taskId, place.after] : [taskId];
|
|
322
|
+
return writing(id, named, (root) => moveInQueue(root, taskId, place, revision));
|
|
323
|
+
},
|
|
324
|
+
prioritise(id, taskId, priority, revision) {
|
|
325
|
+
return writing(id, [taskId], (root) => setPriority(root, taskId, priority, revision));
|
|
326
|
+
},
|
|
327
|
+
requestPause(id, taskId, revision) {
|
|
328
|
+
return writing(id, [taskId], (root) => requestPause(root, taskId, revision));
|
|
329
|
+
},
|
|
330
|
+
cancelPause(id, taskId, revision) {
|
|
331
|
+
return writing(id, [taskId], (root) => cancelPause(root, taskId, revision));
|
|
332
|
+
},
|
|
333
|
+
fileTask(id, taskId, goal) {
|
|
334
|
+
// A new id names no live task: Jig itself refuses a taken one.
|
|
335
|
+
return writing(id, [], (root) => fileTask(root, taskId, goal));
|
|
336
|
+
},
|
|
279
337
|
async disconnect(id) {
|
|
280
338
|
if (!(await removeProject(configDir, id)))
|
|
281
339
|
return false;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{$ as e,A as t,B as n,C as r,D as i,E as a,F as o,G as s,H as ee,I as te,J as c,K as l,L as u,M as d,N as f,O as p,P as m,Q as h,R as g,S as ne,T as _,U as v,V as y,W as b,X as x,Y as S,Z as C,_ as re,a as ie,b as w,c as ae,d as oe,et as T,f as E,g as se,h as D,i as O,j as k,k as ce,l as le,m as A,n as j,nt as M,o as N,p as ue,q as de,r as fe,s as pe,t as me,tt as he,u as P,v as ge,w as F,x as I,y as L,z as R}from"./index-DNFgC6Vp.js";var _e=l({__name:`BaseSeparator`,props:{orientation:{type:String,required:!1,default:`horizontal`},decorative:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},setup(t){let n=t,r=[`horizontal`,`vertical`];function i(e){return r.includes(e)}let a=R(()=>i(n.orientation)?n.orientation:`horizontal`),o=R(()=>a.value===`vertical`?n.orientation:void 0),s=R(()=>n.decorative?{role:`none`}:{"aria-orientation":o.value,role:`separator`});return(n,r)=>(S(),y(e(d),c({as:t.as,"as-child":t.asChild,"data-orientation":a.value},s.value),{default:h(()=>[C(n.$slots,`default`)]),_:3},16,[`as`,`as-child`,`data-orientation`]))}}),ve=l({__name:`Separator`,props:{orientation:{type:String,required:!1,default:`horizontal`},decorative:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},setup(e){let t=e;return(e,n)=>(S(),y(_e,he(de(t)),{default:h(()=>[C(e.$slots,`default`)]),_:3},16))}}),ye=l({__name:`CardContent`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(t){let n=t;return(t,r)=>(S(),v(`div`,{"data-slot":`card-content`,class:T(e(f)(`px-4 group-data-[size=sm]/card:px-3`,n.class))},[C(t.$slots,`default`)],2))}}),be=l({__name:`CardDescription`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(t){let n=t;return(t,r)=>(S(),v(`div`,{"data-slot":`card-description`,class:T(e(f)(`text-xs/relaxed text-muted-foreground`,n.class))},[C(t.$slots,`default`)],2))}}),xe=l({__name:`CardHeader`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(t){let n=t;return(t,r)=>(S(),v(`div`,{"data-slot":`card-header`,class:T(e(f)(`group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-none px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3`,n.class))},[C(t.$slots,`default`)],2))}}),Se=l({__name:`CardTitle`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(t){let n=t;return(t,r)=>(S(),v(`div`,{"data-slot":`card-title`,class:T(e(f)(`cn-font-heading text-sm font-medium group-data-[size=sm]/card:text-sm`,n.class))},[C(t.$slots,`default`)],2))}}),z=l({__name:`Separator`,props:{orientation:{default:`horizontal`},decorative:{type:Boolean,default:!0},asChild:{type:Boolean},as:{},class:{type:[Boolean,null,String,Object,Array]}},setup(t){let n=t,r=p(n,`class`);return(t,i)=>(S(),y(e(ve),c({"data-slot":`separator`},e(r),{class:e(f)(`shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch`,n.class)}),null,16,[`class`]))}}),Ce=[`aria-label`],we={class:`flex items-baseline gap-3 border-b bg-muted/50 px-4 py-2`},Te={class:`font-semibold tracking-wider uppercase`},Ee={key:0,class:`text-muted-foreground`},B={class:`grid grid-cols-1 divide-y lg:grid-cols-2 lg:divide-x lg:divide-y-0`},V=l({__name:`CanvasSection`,props:{title:{},note:{}},setup(e){return(t,r)=>(S(),v(`section`,{"aria-label":e.title,class:`border-b`},[n(`h2`,we,[n(`span`,Te,M(e.title),1),e.note?(S(),v(`span`,Ee,M(e.note),1)):ee(``,!0)]),n(`div`,B,[(S(),v(g,null,x([`light`,`dark`],e=>n(`div`,{key:e,class:T([e,`flex flex-col gap-4 bg-background p-4 text-foreground`])},[C(t.$slots,`default`)],2)),64))])],8,Ce))}}),H=(e,t)=>({id:e,class:`T2`,status:`active`,goal:null,paused:!1,pauseRequest:null,pauseRequestable:!0,started:!0,gate:null,autopilot:null,pullRequest:null,priority:null,queue:null,...t}),U=(e,t)=>({url:`https://github.com/fapost-lab/jig-ui/pull/18`,state:e,ci:t,fetchedAt:`2026-10-18T09:40:00Z`}),W=[H(`design-system`,{class:`T3`,gate:`waiting`,priority:0,queue:{level:null,position:100},goal:`shadcn-vue on Tailwind v4 with light and dark themes; #/canvas with every component in every variant; the three screens rebuilt on it.`}),H(`registry-schema`,{gate:`changed`,priority:3,queue:{level:null,position:200},goal:`Version the registry file so a newer Jig UI can read an older one.`}),H(`pause-request`,{class:`T3`,autopilot:`stopped`,goal:`A drop on the paused column writes a sticky request through Jig; the card shows it with its age and a cancel.`}),H(`specs-and-epics`,{autopilot:`on`,queue:{level:null,position:300},goal:`Show each spec with its phases, progress and epic branch beside the board, from Jig's spec list.`}),H(`task-details`,{status:`ready`,pullRequest:U(`open`,`green`),goal:`Open a task's details from its card.`}),H(`refusal-fixture`,{class:`T0`,status:`ready`,pullRequest:U(`open`,`red`),goal:`Replace the refusal text in task-list-states.json with one Jig prints.`}),H(`docs-board`,{class:`T1`,status:`ready`,pullRequest:U(`open`,`pending`),goal:`Describe the five columns on the projects page of the docs.`}),H(`live-updates`,{status:`ready`,pullRequest:U(`merged`,`none`),goal:`Follow connected projects without a reload.`}),H(`no-pr-yet`,{class:`T1`,status:`ready`,goal:`Ready, nothing pushed.`}),H(`jig-init-hook`,{class:`T3`,paused:!0,pauseRequestable:!1,goal:`Register a project in an installed Jig UI from jig init.`}),H(`file-backlog-task`,{class:null,started:!1,goal:`File a minimal backlog task from the page through jig task new.`}),H(`a-task-with-a-rather-long-identifier-that-wraps`,{goal:null}),H(`conflict-display`,{pauseRequest:{requestedAt:`2026-10-22T09:00:00Z`},pauseRequestable:!1,goal:`Show what changed when a write was based on a stale read.`})],G=[{id:`needs-you`,title:`Needs you`,cards:W.slice(0,3)},{id:`in-progress`,title:`In progress`,cards:[W[3],W[12],W[11]]},{id:`ready`,title:`Ready`,cards:W.slice(4,9)},{id:`backlog`,title:`Backlog`,cards:[W[10]]},{id:`paused`,title:`Paused`,cards:[W[9]]}],K=G.map(e=>({...e,cards:[]})),De=[{id:`file-backlog-task`,status:`consolidated`,goal:`File a minimal backlog task from the page.`,updatedAt:`2026-10-25`,pullRequest:{url:`https://github.com/fapost-lab/jig-ui/pull/40`,state:`merged`}},{id:`pr-template`,status:`abandoned`,goal:`A pull request template.`,updatedAt:`2026-10-17`,pullRequest:null},{id:`old-spike`,status:`abandoned`,goal:null,updatedAt:`2026-10-12`,pullRequest:null}],Oe=[{id:`needs-you`,title:`Needs you`,count:3},{id:`in-progress`,title:`In progress`,count:1},{id:`ready`,title:`Ready`,count:5},{id:`backlog`,title:`Backlog`,count:1},{id:`paused`,title:`Paused`,count:1},{id:`unknown`,title:`A column this page does not know`,count:0}],q=[{kind:`gate`,subject:{type:`task`,id:`design-system`},tasks:[`design-system`],title:`Design waits at its gate`,detail:null,todo:`Review the design and approve it: jig task gate design-system approved`,url:null},{kind:`pull-request-ci-failed`,subject:{type:`task`,id:`refusal-fixture`},tasks:[`refusal-fixture`],title:`CI failed`,detail:`pull request #19`,todo:`Look at the failing check.`,url:`https://github.com/fapost-lab/jig-ui/pull/19`},{kind:`housekeeping`,subject:{type:`project`,id:null},tasks:[],title:`Housekeeping never ran`,detail:null,todo:`Run jig housekeeping.`,url:`https://example.com/docs`}],J=[{id:`approve-gate`,command:`jig task gate design-system approved`},{id:`answer-stop`,command:`jig task autopilot pause-request resume --answer <text>`},{id:`pause`,command:`jig task pause design-system`},{id:`request-pause`,command:`jig queue request task:design-system pause`},{id:`something-new`,command:`jig task something-new design-system`}],Y={state:`ready`,issue:null,baseBranch:`main`,createdAt:`2026-10-17`,updatedAt:`2026-10-18`,knowledgeConsolidated:!1,worktree:null,gateBy:null,run:null,housekeepingFlags:[],attention:[]},X=[{...Y,...W[2],gate:`approved`,gateBy:`human`,branch:`task/pause-request`,worktree:{uncommitted:4},issue:`#42`,pullRequest:U(`open`,`red`),run:{mode:`attended`,stage:`implement`,stageAt:`2026-10-18T09:00:00Z`,stopReason:`Which column takes a paused task that already has an open pull request?`,stopAt:`2026-10-18T09:12:00Z`,phase:null},housekeepingFlags:[`needs-consolidation`],attention:[q[0]],actions:J,column:{id:`needs-you`,title:`Needs you`}},{...Y,...W[10],branch:null,actions:[{id:`start`,command:`jig task start file-backlog-task`}],column:{id:`backlog`,title:`Backlog`}}],Z=(e,t={})=>({id:e,state:`ready`,jigVersion:`0.29.0`,counts:{active:11,paused:1,ready:3},attention:[],housekeepingStale:!1,...t}),Q=[Z(`jig-ui`,{attention:q,housekeepingStale:!0}),Z(`nextjs-store`,{counts:{active:0,paused:0,ready:0}}),{id:`old-jig`,state:`unsupported`,jigVersion:`0.26.0`,minimum:`0.29.0`},{id:`new-jig`,state:`incompatible`,jigVersion:`1.0.0`},{id:`busy`,state:`unreadable`,code:`locked`},{id:`moved`,state:`unavailable`,reason:`missing`}],$=[{text:"`design-system` — design system and canvas",checked:!0,task:`design-system`,fog:!1},{text:"`specs-and-epics` — specs and epics (after: project board — `jig spec list`)",checked:!1,task:`specs-and-epics`,fog:!1},{text:"`old-task` — a task no longer live",checked:!1,task:`old-task`,fog:!1},{text:`invoices from orders, no task filed yet`,checked:!1,task:null,fog:!1},{text:"fog: registration by `jig init`",checked:!1,task:null,fog:!0}],ke=[{id:`jig-ui`,title:`Jig UI — a local control center for Jig projects`,missingFiles:[],release:null,epic:null,progress:{done:2,total:6,filed:2,fog:1},phases:[{phase:0,title:`Package and release`,done:1,total:1,filed:0,fog:0,items:[{...$[0],text:`package and release`,task:null}]},{phase:1,title:`Read-only UI`,done:1,total:5,filed:2,fog:1,items:$}]},{id:`billing`,title:`Billing in one place`,missingFiles:[],release:`minor`,epic:{branch:`epic/billing`,state:`open`,checkedOut:!1,missing:!1,fetched:!0},progress:{done:0,total:1,filed:0,fog:0},phases:[{phase:null,title:null,done:0,total:1,filed:0,fog:0,items:[$[3]]}]},{id:`lost-epic`,title:`An epic whose branch is gone`,missingFiles:[],release:`major`,epic:{branch:`epic/lost`,state:`open`,checkedOut:!0,missing:!0,fetched:!1},progress:null,phases:[]},{id:`half-written`,title:null,missingFiles:[`spec.md`,`roadmap.md`],release:null,epic:null,progress:null,phases:[]}],Ae={class:`flex gap-4`},je={class:`w-24 text-muted-foreground`},Me={class:`flex items-center gap-2`},Ne={class:`flex flex-wrap gap-2`},Pe={class:`flex max-w-sm flex-col gap-2`},Fe={class:`flex h-5 items-center gap-2`},Ie={class:`flex gap-2`},Le={class:`flex flex-wrap items-center gap-6`},Re={class:`flex items-center gap-1`},ze={class:`grid grid-cols-2 gap-2 xl:grid-cols-3`},Be={class:`flex h-[28rem] flex-col`},Ve={class:`flex h-40 flex-col`},He={class:`flex h-[28rem] flex-col`},Ue={class:`flex h-64 flex-col`},We={class:`flex h-64 flex-col`},Ge=l({__name:`CanvasView`,setup(ee){let c=[`default`,`secondary`,`outline`,`ghost`,`destructive`,`link`],l=[`xs`,`sm`,`default`,`lg`],d=[`icon-xs`,`icon-sm`,`icon`,`icon-lg`],f=[`default`,`secondary`,`outline`,`ghost`,`destructive`,`link`],p=[`attention`,`good`,`bad`,`quiet`];return(ee,C)=>(S(),v(`div`,null,[C[14]||=n(`div`,{class:`border-b px-4 py-3`},[n(`h1`,{class:`font-semibold tracking-wider uppercase`},`Canvas`),n(`p`,{class:`text-muted-foreground`},`Every component of the page in every variant and state; the left panel light, the right dark.`)],-1),s(V,{title:`Tones`,note:`lib/tones.ts — the only colours with a meaning`},{default:h(()=>[(S(),v(g,null,x(p,t=>n(`p`,{key:t,class:T(e(te)[t])},M(t)+` as a rule`,3)),64)),n(`p`,Ae,[(S(),v(g,null,x(p,t=>n(`span`,{key:t,class:T(e(u)[t])},M(t),3)),64))])]),_:1}),C[15]||=n(`h2`,{class:`border-b px-4 py-2 font-bold tracking-widest uppercase`},`components/ui`,-1),s(V,{title:`Button`,note:`variant × size; icon sizes; disabled`},{default:h(()=>[(S(),v(g,null,x(c,t=>n(`div`,{key:t,class:`flex flex-wrap items-center gap-2`},[n(`span`,je,M(t),1),(S(),v(g,null,x(l,n=>s(e(k),{key:n,variant:t,size:n},{default:h(()=>[b(M(n),1)]),_:2},1032,[`variant`,`size`])),64)),s(e(k),{variant:t,disabled:``},{default:h(()=>[...C[0]||=[b(`disabled`,-1)]]),_:1},8,[`variant`])])),64)),n(`div`,Me,[C[2]||=n(`span`,{class:`w-24 text-muted-foreground`},`icon`,-1),(S(),v(g,null,x(d,t=>s(e(k),{key:t,variant:`outline`,size:t,"aria-label":t},{default:h(()=>[s(e(m))]),_:1},8,[`size`,`aria-label`])),64)),s(e(k),{variant:`outline`,size:`sm`},{default:h(()=>[s(e(m),{"data-icon":`inline-start`}),C[1]||=b(`with icon`,-1)]),_:1})])]),_:1}),s(V,{title:`Badge`},{default:h(()=>[n(`div`,Ne,[(S(),v(g,null,x(f,t=>s(e(i),{key:t,variant:t},{default:h(()=>[b(M(t),1)]),_:2},1032,[`variant`])),64))])]),_:1}),s(V,{title:`Card`},{default:h(()=>[s(e(a),{class:`max-w-sm`},{default:h(()=>[s(e(xe),null,{default:h(()=>[s(e(Se),null,{default:h(()=>[...C[3]||=[b(`Card title`,-1)]]),_:1}),s(e(be),null,{default:h(()=>[...C[4]||=[b(`Card description, muted.`,-1)]]),_:1})]),_:1}),s(e(ye),null,{default:h(()=>[...C[5]||=[b(`Card content.`,-1)]]),_:1})]),_:1})]),_:1}),s(V,{title:`Input and Label`,note:`empty, filled, invalid, disabled`},{default:h(()=>[n(`div`,Pe,[s(e(oe),null,{default:h(()=>[...C[6]||=[b(`Project path`,-1)]]),_:1}),s(e(E),{placeholder:`/path/to/project`}),s(e(E),{"model-value":`/Users/me/project`}),s(e(E),{"model-value":`/not/a/project`,"aria-invalid":`true`}),s(e(E),{"model-value":`disabled`,disabled:``})])]),_:1}),s(V,{title:`Separator`},{default:h(()=>[s(e(z)),n(`div`,Fe,[C[7]||=b(`left`,-1),s(e(z),{orientation:`vertical`}),C[8]||=b(`right`,-1)])]),_:1}),s(V,{title:`ToggleGroup`,note:`the Board / List switch`},{default:h(()=>[s(e(ie),{type:`single`,variant:`outline`,size:`sm`,"model-value":`board`,"aria-label":`View`},{default:h(()=>[s(e(O),{value:`board`},{default:h(()=>[...C[9]||=[b(`Board`,-1)]]),_:1}),s(e(O),{value:`list`},{default:h(()=>[...C[10]||=[b(`List`,-1)]]),_:1})]),_:1})]),_:1}),s(V,{title:`Toggle`,note:`the board's finished tasks: off, on`},{default:h(()=>[n(`div`,Ie,[s(e(N),{variant:`outline`,size:`sm`,"model-value":!1},{default:h(()=>[...C[11]||=[b(`3 finished tasks`,-1)]]),_:1}),s(e(N),{variant:`outline`,size:`sm`,"model-value":!0},{default:h(()=>[...C[12]||=[b(`3 finished tasks`,-1)]]),_:1})])]),_:1}),s(V,{title:`Collapsible`,note:`open and folded`},{default:h(()=>[(S(),v(g,null,x([!0,!1],t=>s(e(ge),{key:String(t),"default-open":t},{default:h(()=>[s(e(se),{class:`w-full border-b bg-muted/50 px-3 py-1.5 text-left`},{default:h(()=>[b(`Trigger (`+M(t?`open`:`folded`)+`)`,1)]),_:2},1024),s(e(re),{class:`px-3 py-2`},{default:h(()=>[...C[13]||=[b(`Content`,-1)]]),_:1})]),_:2},1032,[`default-open`])),64))]),_:1}),C[16]||=n(`h2`,{class:`border-b px-4 py-2 font-bold tracking-widest uppercase`},`components`,-1),s(V,{title:`ProjectTabs`,note:`on the workspace; on a project; empty`},{default:h(()=>[s(o,{projects:e(Q),version:`0.1.0`},{default:h(()=>[s(t)]),_:1},8,[`projects`]),s(o,{projects:e(Q),current:`jig-ui`,version:`0.1.0`},null,8,[`projects`]),s(o,{projects:[]})]),_:1}),s(V,{title:`ThemeToggle`,note:`switches the whole page; the icon is the theme it switches to`},{default:h(()=>[n(`div`,null,[s(t)])]),_:1}),s(V,{title:`ProjectCard`,note:`ready with attention and stale housekeeping; nothing needs you; each state it cannot be read in`},{default:h(()=>[(S(!0),v(g,null,x(e(Q),e=>(S(),y(me,{key:e.id,project:e},null,8,[`project`]))),128))]),_:1}),s(V,{title:`NewTaskForm`,note:`empty, on its way, the id taken`},{default:h(()=>[s(P,{busy:!1}),s(P,{busy:!0,task:`fix-login`,goal:`Log in with the token after a restart.`}),s(P,{busy:!1,task:`queue-reorder`,goal:`Order the work.`,refusal:`The project has a task called queue-reorder already, maybe a finished one. Choose another id.`,taken:``})]),_:1}),s(V,{title:`ConnectForm`,note:`idle, busy, refused`},{default:h(()=>[s(j,{busy:!1}),s(j,{busy:!0,"model-value":`/Users/me/project`}),s(j,{busy:!1,"model-value":`/Users/me/project/.claude/worktrees/x`,refusal:`That is a worktree. Connect the main checkout instead.`})]),_:1}),s(V,{title:`AttentionItem`,note:`no link; a pull request; another link`},{default:h(()=>[(S(!0),v(g,null,x(e(q),(e,t)=>(S(),y(fe,{key:t,item:e},null,8,[`item`]))),128))]),_:1}),s(V,{title:`ColumnHeader`,note:`each column, and one this page does not know`},{default:h(()=>[(S(!0),v(g,null,x(e(Oe),e=>(S(),y(ce,{id:e.id,key:e.id,title:e.title,count:e.count},null,8,[`id`,`title`,`count`]))),128))]),_:1}),s(V,{title:`QueueMenu`,note:`press to open: To top, Up, Down, To bottom, Priority, the pause — first queued, in the middle, not queued, last, paused (no pause to ask), a pause asked, while its change is on its way`},{default:h(()=>[n(`div`,Le,[(S(!0),v(g,null,x([e(W)[0],e(W)[1],e(W)[3],e(W)[11],e(W)[9],e(W)[12]],t=>(S(),v(`span`,{key:t.id,class:`flex items-center gap-1`},[b(M(t.id),1),s(_,{task:t,cards:e(W)},null,8,[`task`,`cards`])]))),128)),n(`span`,Re,[b(M(e(W)[1].id),1),s(_,{task:e(W)[1],cards:e(W),disabled:``},null,8,[`task`,`cards`])])])]),_:1}),s(V,{title:`TaskCard`,note:`every note a card carries, a priority among them; with its queue menu; while its change is on its way; changed since a refused change; open with details, while reading, unreadable`},{default:h(()=>[n(`div`,ze,[(S(!0),v(g,null,x(e(W),e=>(S(),y(w,{key:e.id,project:`jig-ui`,task:e,open:!1},null,8,[`task`]))),128)),s(w,{project:`jig-ui`,task:e(W)[1],open:!1,cards:e(W)},null,8,[`task`,`cards`]),s(w,{project:`jig-ui`,task:e(W)[0],open:!1,cards:e(W),busy:``},null,8,[`task`,`cards`]),s(w,{project:`jig-ui`,task:e(W)[3],open:!1,cards:e(W),changed:``},null,8,[`task`,`cards`]),s(w,{project:`jig-ui`,task:e(W)[2],open:!0,details:e(X)[0]},null,8,[`task`,`details`]),s(w,{project:`jig-ui`,task:e(W)[0],open:!0},null,8,[`task`]),s(w,{project:`jig-ui`,task:e(W)[1],open:!0,details:`unavailable`},null,8,[`task`])])]),_:1}),s(V,{title:`TaskRow`,note:`each note and priority; selected; with its queue menu; while its change is on its way; changed since a refused change`},{default:h(()=>[n(`div`,null,[(S(!0),v(g,null,x(e(W),(e,t)=>(S(),y(D,{key:e.id,task:e,selected:t===2},null,8,[`task`,`selected`]))),128)),s(D,{task:e(W)[1],selected:!1,cards:e(W)},null,8,[`task`,`cards`]),s(D,{task:e(W)[0],selected:!1,cards:e(W),busy:``},null,8,[`task`,`cards`]),s(D,{task:e(W)[3],selected:!1,cards:e(W),changed:``},null,8,[`task`,`cards`])])]),_:1}),s(V,{title:`BoardColumns`,note:`cards in every column, one open with its details, two changed since a refused change; no live task`},{default:h(()=>[n(`div`,Be,[s(L,{project:`jig-ui`,columns:e(G),selected:e(W)[2].id,details:e(X)[0],pending:e(W)[0].id,changed:[e(W)[1].id,e(W)[3].id]},null,8,[`columns`,`selected`,`details`,`pending`,`changed`])]),n(`div`,Ve,[s(L,{project:`jig-ui`,columns:e(K)},null,8,[`columns`])])]),_:1}),s(V,{title:`BoardList`,note:`a row picked with its details; nothing picked; no live task`},{default:h(()=>[n(`div`,He,[s(A,{project:`jig-ui`,columns:e(G),selected:e(W)[2].id,details:e(X)[0]},null,8,[`columns`,`selected`,`details`])]),n(`div`,Ue,[s(A,{project:`jig-ui`,columns:e(G)},null,8,[`columns`])]),n(`div`,We,[s(A,{project:`jig-ui`,columns:e(K)},null,8,[`columns`])])]),_:1}),s(V,{title:`FinishedList`,note:`closed with its pull request merged; abandoned; one without a goal`},{default:h(()=>[s(ue,{tasks:e(De),class:`border`},null,8,[`tasks`])]),_:1}),s(V,{title:`TaskFacts`,note:`everything a task can carry; a task not started`},{default:h(()=>[(S(!0),v(g,null,x(e(X),e=>(S(),y(ne,{key:e.id,details:e,class:`border-b pb-3`},null,8,[`details`]))),128))]),_:1}),s(V,{title:`TaskBrief`,note:`facts, actions and the link to the full page`},{default:h(()=>[s(I,{project:`jig-ui`,task:e(X)[1].id,details:e(X)[1]},null,8,[`task`,`details`])]),_:1}),s(V,{title:`ActionList`,note:`Jig's offers, one unknown id; none`},{default:h(()=>[s(r,{actions:e(J)},null,8,[`actions`]),s(r,{actions:[]})]),_:1}),s(V,{title:`CopyCommand`,note:`short; long enough to be cut`},{default:h(()=>[s(F,{command:`jig task pause design-system`}),s(F,{command:`jig task autopilot a-task-with-a-rather-long-identifier-that-wraps resume --answer 'a long answer to the question'`,class:`max-w-xs`})]),_:1}),s(V,{title:`ProgressBar`,note:`none, part, all, an empty roadmap`},{default:h(()=>[(S(),v(g,null,x([[0,4],[3,9],[5,5],[0,0]],([e,t])=>s(le,{key:`${e}/${t}`,done:e,total:t},null,8,[`done`,`total`])),64))]),_:1}),s(V,{title:`SpecItemRow`,note:`done with a live task; open with dependencies; a task no longer live; no task; fog`},{default:h(()=>[n(`ul`,null,[(S(!0),v(g,null,x(e($),(e,t)=>(S(),y(ae,{key:t,item:e,project:`jig-ui`,live:t<2},null,8,[`item`,`live`]))),128))])]),_:1}),s(V,{title:`SpecCard`,note:`no epic, a finished phase folded; an open epic as of the last fetch; a lost epic branch; missing files`},{default:h(()=>[(S(!0),v(g,null,x(e(ke),e=>(S(),y(pe,{key:e.id,spec:e,project:`jig-ui`,live:[`design-system`,`specs-and-epics`]},null,8,[`spec`]))),128))]),_:1})]))}});export{Ge as default};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-divide-x-reverse:0;--tw-border-style:solid;--tw-divide-y-reverse:0;--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-outline-style:solid;--tw-duration:initial;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:"JetBrains Mono Variable", ui-monospace, monospace;--color-red-600:oklch(57.7% .245 27.325);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-700:oklch(55.5% .163 48.998);--color-emerald-300:oklch(84.5% .143 164.978);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-700:oklch(50.8% .118 165.612);--color-sky-500:oklch(68.5% .169 237.323);--color-slate-400:oklch(70.4% .04 256.788);--color-zinc-400:oklch(70.5% .015 286.067);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-3xl:48rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wider:.05em;--tracking-widest:.1em;--leading-relaxed:1.625;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:"JetBrains Mono Variable", ui-monospace, monospace}}@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,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, 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;-webkit-text-decoration:inherit;-webkit-text-decoration: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:where(:not(iframe)){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}*{border-color:var(--border);outline-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){*{outline-color:color-mix(in oklab, var(--ring) 50%, transparent)}}body{background-color:var(--background);font-family:JetBrains Mono Variable,ui-monospace,monospace;font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));color:var(--foreground)}}@layer components;@layer utilities{.\@container\/card-header{container:card-header/inline-size}.\@container{container-type:inline-size}.pointer-events-none{pointer-events:none}.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}.relative{position:relative}.sticky{position:sticky}.inset-x-0{inset-inline:0}.-top-1\.5{top:calc(var(--spacing) * -1.5)}.top-0{top:0}.right-2{right:calc(var(--spacing) * 2)}.-bottom-1\.5{bottom:calc(var(--spacing) * -1.5)}.bottom-0{bottom:0}.z-10{z-index:10}.z-50{z-index:50}.col-start-2{grid-column-start:2}.row-span-2{grid-row:span 2/span 2}.row-start-1{grid-row-start:1}.-mx-1{margin-inline:calc(var(--spacing) * -1)}.mx-auto{margin-inline:auto}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mb-1{margin-bottom:var(--spacing)}.ml-auto{margin-left:auto}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.size-6{width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.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)}.h-0\.5{height:calc(var(--spacing) * .5)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-3{height:calc(var(--spacing) * 3)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-40{height:calc(var(--spacing) * 40)}.h-64{height:calc(var(--spacing) * 64)}.h-\[28rem\]{height:28rem}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-\(--reka-dropdown-menu-content-available-height\){max-height:var(--reka-dropdown-menu-content-available-height)}.max-h-48{max-height:calc(var(--spacing) * 48)}.min-h-0{min-height:0}.w-\(--reka-dropdown-menu-trigger-width\){width:var(--reka-dropdown-menu-trigger-width)}.w-1{width:var(--spacing)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-20{width:calc(var(--spacing) * 20)}.w-24{width:calc(var(--spacing) * 24)}.w-40{width:calc(var(--spacing) * 40)}.w-44{width:calc(var(--spacing) * 44)}.w-56{width:calc(var(--spacing) * 56)}.w-96{width:calc(var(--spacing) * 96)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-\(--reka-dropdown-menu-content-available-width\){max-width:var(--reka-dropdown-menu-content-available-width)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-sm{max-width:var(--container-sm)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:0}.min-w-7{min-width:calc(var(--spacing) * 7)}.min-w-8{min-width:calc(var(--spacing) * 8)}.min-w-9{min-width:calc(var(--spacing) * 9)}.min-w-32{min-width:calc(var(--spacing) * 32)}.min-w-64{min-width:calc(var(--spacing) * 64)}.min-w-\[96px\]{min-width:96px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.origin-\(--reka-dropdown-menu-content-transform-origin\){transform-origin:var(--reka-dropdown-menu-content-transform-origin)}.cursor-default{cursor:default}.auto-rows-min{grid-auto-rows:min-content}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\[repeat\(5\,minmax\(14rem\,1fr\)\)\]{grid-template-columns:repeat(5,minmax(14rem,1fr))}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-center{justify-content:center}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}.gap-\[--spacing\(var\(--gap\)\)\]{gap:calc(var(--spacing) * var(--gap))}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-x-4{column-gap:calc(var(--spacing) * 4)}.gap-y-1{row-gap:var(--spacing)}:where(.divide-x>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(1px * var(--tw-divide-x-reverse));border-inline-end-width:calc(1px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}.self-start{align-self:flex-start}.justify-self-end{justify-self:flex-end}.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}.rounded-none{border-radius:0}.border{border-style:var(--tw-border-style);border-width:1px}.border-y{border-block-style:var(--tw-border-style);border-block-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-amber-500{border-color:var(--color-amber-500)}.border-border{border-color:var(--border)}.border-destructive{border-color:var(--destructive)}.border-emerald-500{border-color:var(--color-emerald-500)}.border-input{border-color:var(--input)}.border-transparent{border-color:#0000}.bg-amber-500{background-color:var(--color-amber-500)}.bg-background{background-color:var(--background)}.bg-border{background-color:var(--border)}.bg-card{background-color:var(--card)}.bg-destructive\/10{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.bg-destructive\/10{background-color:color-mix(in oklab, var(--destructive) 10%, transparent)}}.bg-emerald-500{background-color:var(--color-emerald-500)}.bg-foreground{background-color:var(--foreground)}.bg-muted,.bg-muted\/50{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.bg-muted\/50{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}.bg-popover{background-color:var(--popover)}.bg-primary{background-color:var(--primary)}.bg-red-600{background-color:var(--color-red-600)}.bg-secondary{background-color:var(--secondary)}.bg-sky-500{background-color:var(--color-sky-500)}.bg-slate-400{background-color:var(--color-slate-400)}.bg-transparent{background-color:#0000}.bg-zinc-400{background-color:var(--color-zinc-400)}.bg-clip-padding{background-clip:padding-box}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.px-0\.5{padding-inline:calc(var(--spacing) * .5)}.px-1{padding-inline:var(--spacing)}.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-4{padding-inline:calc(var(--spacing) * 4)}.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-4{padding-block:calc(var(--spacing) * 4)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pl-2{padding-left:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-left{text-align:left}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-xs\/relaxed{font-size:var(--text-xs);line-height:var(--leading-relaxed)}.leading-none{--tw-leading:1;line-height:1}.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-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.text-amber-700{color:var(--color-amber-700)}.text-background{color:var(--background)}.text-black{color:var(--color-black)}.text-card-foreground{color:var(--card-foreground)}.text-destructive{color:var(--destructive)}.text-emerald-700{color:var(--color-emerald-700)}.text-foreground{color:var(--foreground)}.text-muted-foreground{color:var(--muted-foreground)}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-secondary-foreground{color:var(--secondary-foreground)}.text-white{color:var(--color-white)}.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}.underline-offset-4{text-underline-offset:4px}.opacity-60{opacity:.6}.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-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px 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-1{--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-foreground,.ring-foreground\/10{--tw-ring-color:var(--foreground)}@supports (color:color-mix(in lab, red, red)){.ring-foreground\/10{--tw-ring-color:color-mix(in oklab, var(--foreground) 10%, transparent)}}.outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.transition-all{transition-property:all;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))}.duration-100{--tw-duration:.1s;transition-duration:.1s}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.paused{animation-play-state:paused}.running{animation-play-state:running}.group-focus\/dropdown-menu-item\:text-accent-foreground:is(:where(.group\/dropdown-menu-item):focus *){color:var(--accent-foreground)}.group-data-\[disabled\=true\]\:pointer-events-none:is(:where(.group)[data-disabled=true] *){pointer-events:none}.group-data-\[disabled\=true\]\:opacity-50:is(:where(.group)[data-disabled=true] *){opacity:.5}.group-data-\[size\=sm\]\/card\:p-3:is(:where(.group\/card)[data-size=sm] *){padding:calc(var(--spacing) * 3)}.group-data-\[size\=sm\]\/card\:px-3:is(:where(.group\/card)[data-size=sm] *){padding-inline:calc(var(--spacing) * 3)}.group-data-\[size\=sm\]\/card\:text-sm:is(:where(.group\/card)[data-size=sm] *){font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.group-data-\[spacing\=0\]\/toggle-group\:rounded-none:is(:where(.group\/toggle-group)[data-spacing="0"] *){border-radius:0}.group-data-\[spacing\=0\]\/toggle-group\:px-2:is(:where(.group\/toggle-group)[data-spacing="0"] *){padding-inline:calc(var(--spacing) * 2)}.peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled~*){cursor:not-allowed}.peer-disabled\:opacity-50:is(:where(.peer):disabled~*){opacity:.5}.file\:inline-flex::file-selector-button{display:inline-flex}.file\:h-6::file-selector-button{height:calc(var(--spacing) * 6)}.file\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\:bg-transparent::file-selector-button{background-color:#0000}.file\:text-xs::file-selector-button{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.file\:font-medium::file-selector-button{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.file\:text-foreground::file-selector-button{color:var(--foreground)}.placeholder\:text-muted-foreground::placeholder{color:var(--muted-foreground)}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media (hover:hover){.hover\:bg-destructive\/20:hover{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.hover\:bg-destructive\/20:hover{background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.hover\:bg-muted:hover,.hover\:bg-muted\/50:hover{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/50:hover{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}.hover\:bg-secondary\/80:hover{background-color:var(--secondary)}@supports (color:color-mix(in lab, red, red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab, var(--secondary) 80%, transparent)}}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:text-muted-foreground:hover{color:var(--muted-foreground)}.hover\:underline:hover{text-decoration-line:underline}}.focus\:z-10:focus{z-index:10}.focus\:bg-accent:focus{background-color:var(--accent)}.focus\:text-accent-foreground:focus,:is(.focus\:\*\*\:text-accent-foreground:focus *),:is(.not-data-\[variant\=destructive\]\:focus\:\*\*\:text-accent-foreground:not([data-variant=destructive]):focus *){color:var(--accent-foreground)}.focus-visible\:z-10:focus-visible{z-index:10}.focus-visible\:border-destructive\/40:focus-visible{border-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:border-destructive\/40:focus-visible{border-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.focus-visible\:border-ring:focus-visible{border-color:var(--ring)}.focus-visible\:ring-1:focus-visible{--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)}.focus-visible\:ring-3:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-destructive\/20:focus-visible{--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:var(--ring)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:color-mix(in oklab, var(--ring) 50%, transparent)}}.active\:not-aria-\[haspopup\]\:translate-y-px:active:not([aria-haspopup]){--tw-translate-y:1px;translate:var(--tw-translate-x) var(--tw-translate-y)}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-input\/50:disabled{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.disabled\:bg-input\/50:disabled{background-color:color-mix(in oklab, var(--input) 50%, transparent)}}.disabled\:opacity-50:disabled{opacity:.5}.has-data-\[icon\=inline-end\]\:pr-1\.5:has([data-icon=inline-end]){padding-right:calc(var(--spacing) * 1.5)}.has-data-\[icon\=inline-end\]\:pr-2:has([data-icon=inline-end]){padding-right:calc(var(--spacing) * 2)}.group-data-\[spacing\=0\]\/toggle-group\:has-data-\[icon\=inline-end\]\:pr-1\.5:is(:where(.group\/toggle-group)[data-spacing="0"] *):has([data-icon=inline-end]){padding-right:calc(var(--spacing) * 1.5)}.has-data-\[icon\=inline-start\]\:pl-1\.5:has([data-icon=inline-start]){padding-left:calc(var(--spacing) * 1.5)}.has-data-\[icon\=inline-start\]\:pl-2:has([data-icon=inline-start]){padding-left:calc(var(--spacing) * 2)}.group-data-\[spacing\=0\]\/toggle-group\:has-data-\[icon\=inline-start\]\:pl-1\.5:is(:where(.group\/toggle-group)[data-spacing="0"] *):has([data-icon=inline-start]){padding-left:calc(var(--spacing) * 1.5)}.has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has([data-slot=card-action]){grid-template-columns:1fr auto}.has-data-\[slot\=card-description\]\:grid-rows-\[auto_auto\]:has([data-slot=card-description]){grid-template-rows:auto auto}.has-data-\[slot\=card-footer\]\:pb-0:has([data-slot=card-footer]){padding-bottom:0}.has-\[\>img\:first-child\]\:pt-0:has(>img:first-child){padding-top:0}.aria-expanded\:bg-muted[aria-expanded=true]{background-color:var(--muted)}.aria-expanded\:bg-secondary[aria-expanded=true]{background-color:var(--secondary)}.aria-expanded\:text-foreground[aria-expanded=true]{color:var(--foreground)}.aria-expanded\:text-secondary-foreground[aria-expanded=true]{color:var(--secondary-foreground)}.aria-invalid\:border-destructive[aria-invalid=true]{border-color:var(--destructive)}.aria-invalid\:ring-1[aria-invalid=true]{--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)}.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.aria-invalid\:ring-destructive\/20[aria-invalid=true]{--tw-ring-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.aria-pressed\:bg-muted[aria-pressed=true]{background-color:var(--muted)}.data-closed\:animate-out[data-closed]{animation:exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-closed\:fade-out-0[data-closed]{--tw-exit-opacity:0}.data-closed\:zoom-out-95[data-closed]{--tw-exit-scale:.95}.data-disabled\:pointer-events-none[data-disabled]{pointer-events:none}.data-disabled\:opacity-50[data-disabled]{opacity:.5}.data-inset\:pl-7[data-inset]{padding-left:calc(var(--spacing) * 7)}.data-open\:animate-in[data-open]{animation:enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none)}.data-open\:bg-accent[data-open]{background-color:var(--accent)}.data-open\:text-accent-foreground[data-open]{color:var(--accent-foreground)}.data-open\:fade-in-0[data-open]{--tw-enter-opacity:0}.data-open\:zoom-in-95[data-open]{--tw-enter-scale:.95}.data-vertical\:flex-col[data-vertical]{flex-direction:column}.data-vertical\:items-stretch[data-vertical]{align-items:stretch}.data-\[orientation\=horizontal\]\:h-px[data-orientation=horizontal]{height:1px}.data-\[orientation\=horizontal\]\:w-full[data-orientation=horizontal]{width:100%}.data-\[orientation\=vertical\]\:w-px[data-orientation=vertical]{width:1px}.data-\[orientation\=vertical\]\:self-stretch[data-orientation=vertical]{align-self:stretch}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:calc(2*var(--spacing)*-1)}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:calc(2*var(--spacing))}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:calc(2*var(--spacing)*-1)}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:calc(2*var(--spacing))}.data-\[size\=sm\]\:gap-2[data-size=sm]{gap:calc(var(--spacing) * 2)}.data-\[size\=sm\]\:rounded-none[data-size=sm]{border-radius:0}.data-\[size\=sm\]\:py-3[data-size=sm]{padding-block:calc(var(--spacing) * 3)}.data-\[size\=sm\]\:has-data-\[slot\=card-footer\]\:pb-0[data-size=sm]:has([data-slot=card-footer]){padding-bottom:0}.group-data-horizontal\/toggle-group\:data-\[spacing\=0\]\:first\:rounded-none:is(:where(.group\/toggle-group)[data-horizontal] *)[data-spacing="0"]:first-child,.group-data-vertical\/toggle-group\:data-\[spacing\=0\]\:first\:rounded-none:is(:where(.group\/toggle-group)[data-vertical] *)[data-spacing="0"]:first-child,.group-data-horizontal\/toggle-group\:data-\[spacing\=0\]\:last\:rounded-none:is(:where(.group\/toggle-group)[data-horizontal] *)[data-spacing="0"]:last-child,.group-data-vertical\/toggle-group\:data-\[spacing\=0\]\:last\:rounded-none:is(:where(.group\/toggle-group)[data-vertical] *)[data-spacing="0"]:last-child{border-radius:0}.data-\[state\=closed\]\:overflow-hidden[data-state=closed]{overflow:hidden}.data-\[state\=on\]\:bg-muted[data-state=on]{background-color:var(--muted)}.data-\[variant\=destructive\]\:text-destructive[data-variant=destructive]{color:var(--destructive)}.data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant=destructive]:focus{background-color:color-mix(in oklab, var(--destructive) 10%, transparent)}}.data-\[variant\=destructive\]\:focus\:text-destructive[data-variant=destructive]:focus{color:var(--destructive)}.group-data-horizontal\/toggle-group\:data-\[spacing\=0\]\:data-\[variant\=outline\]\:border-l-0:is(:where(.group\/toggle-group)[data-horizontal] *)[data-spacing="0"][data-variant=outline]{border-left-style:var(--tw-border-style);border-left-width:0}.group-data-vertical\/toggle-group\:data-\[spacing\=0\]\:data-\[variant\=outline\]\:border-t-0:is(:where(.group\/toggle-group)[data-vertical] *)[data-spacing="0"][data-variant=outline]{border-top-style:var(--tw-border-style);border-top-width:0}.group-data-horizontal\/toggle-group\:data-\[spacing\=0\]\:data-\[variant\=outline\]\:first\:border-l:is(:where(.group\/toggle-group)[data-horizontal] *)[data-spacing="0"][data-variant=outline]:first-child{border-left-style:var(--tw-border-style);border-left-width:1px}.group-data-vertical\/toggle-group\:data-\[spacing\=0\]\:data-\[variant\=outline\]\:first\:border-t:is(:where(.group\/toggle-group)[data-vertical] *)[data-spacing="0"][data-variant=outline]:first-child{border-top-style:var(--tw-border-style);border-top-width:1px}@media (width>=48rem){.md\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}}@media (width>=64rem){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}:where(.lg\:divide-x>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(1px * var(--tw-divide-x-reverse));border-inline-end-width:calc(1px * calc(1 - var(--tw-divide-x-reverse)))}:where(.lg\:divide-y-0>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(0px * var(--tw-divide-y-reverse));border-bottom-width:calc(0px * calc(1 - var(--tw-divide-y-reverse)))}}@media (width>=80rem){.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@container (width>=24rem){.\@sm\:mb-0{margin-bottom:0}.\@sm\:grid-cols-\[max-content_1fr\]{grid-template-columns:max-content 1fr}.\@sm\:gap-x-4{column-gap:calc(var(--spacing) * 4)}.\@sm\:gap-y-1{row-gap:var(--spacing)}}.dark\:border-input:is(.dark *):not(.light *){border-color:var(--input)}.dark\:bg-destructive\/20:is(.dark *):not(.light *){background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:bg-destructive\/20:is(.dark *):not(.light *){background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.dark\:bg-input\/30:is(.dark *):not(.light *){background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.dark\:bg-input\/30:is(.dark *):not(.light *){background-color:color-mix(in oklab, var(--input) 30%, transparent)}}.dark\:text-amber-300:is(.dark *):not(.light *){color:var(--color-amber-300)}.dark\:text-emerald-300:is(.dark *):not(.light *){color:var(--color-emerald-300)}@media (hover:hover){.dark\:hover\:bg-destructive\/30:is(.dark *):not(.light *):hover{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:hover\:bg-destructive\/30:is(.dark *):not(.light *):hover{background-color:color-mix(in oklab, var(--destructive) 30%, transparent)}}.dark\:hover\:bg-input\/50:is(.dark *):not(.light *):hover{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.dark\:hover\:bg-input\/50:is(.dark *):not(.light *):hover{background-color:color-mix(in oklab, var(--input) 50%, transparent)}}.dark\:hover\:bg-muted\/50:is(.dark *):not(.light *):hover{background-color:var(--muted)}@supports (color:color-mix(in lab, red, red)){.dark\:hover\:bg-muted\/50:is(.dark *):not(.light *):hover{background-color:color-mix(in oklab, var(--muted) 50%, transparent)}}}.dark\:focus-visible\:ring-destructive\/40:is(.dark *):not(.light *):focus-visible{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:focus-visible\:ring-destructive\/40:is(.dark *):not(.light *):focus-visible{--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.dark\:disabled\:bg-input\/80:is(.dark *):not(.light *):disabled{background-color:var(--input)}@supports (color:color-mix(in lab, red, red)){.dark\:disabled\:bg-input\/80:is(.dark *):not(.light *):disabled{background-color:color-mix(in oklab, var(--input) 80%, transparent)}}.dark\:aria-invalid\:border-destructive\/50:is(.dark *):not(.light *)[aria-invalid=true]{border-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:aria-invalid\:border-destructive\/50:is(.dark *):not(.light *)[aria-invalid=true]{border-color:color-mix(in oklab, var(--destructive) 50%, transparent)}}.dark\:aria-invalid\:ring-destructive\/40:is(.dark *):not(.light *)[aria-invalid=true]{--tw-ring-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:aria-invalid\:ring-destructive\/40:is(.dark *):not(.light *)[aria-invalid=true]{--tw-ring-color:color-mix(in oklab, var(--destructive) 40%, transparent)}}.dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is(.dark *):not(.light *)[data-variant=destructive]:focus{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is(.dark *):not(.light *)[data-variant=destructive]:focus{background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_svg\:not\(\[class\*\=size-\]\)\]\:size-3 svg:not([class*=size-]){width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.\[\&_svg\:not\(\[class\*\=size-\]\)\]\:size-3\.5 svg:not([class*=size-]){width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.\[\&_svg\:not\(\[class\*\=size-\]\)\]\:size-4 svg:not([class*=size-]){width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\.border-b\]\:pb-4.border-b{padding-bottom:calc(var(--spacing) * 4)}.group-data-\[size\=sm\]\/card\:\[\.border-b\]\:pb-3:is(:where(.group\/card)[data-size=sm] *).border-b{padding-bottom:calc(var(--spacing) * 3)}@media (hover:hover){.\[a\]\:hover\:bg-destructive\/20:is(a):hover{background-color:var(--destructive)}@supports (color:color-mix(in lab, red, red)){.\[a\]\:hover\:bg-destructive\/20:is(a):hover{background-color:color-mix(in oklab, var(--destructive) 20%, transparent)}}.\[a\]\:hover\:bg-muted:is(a):hover{background-color:var(--muted)}.\[a\]\:hover\:bg-primary\/80:is(a):hover{background-color:var(--primary)}@supports (color:color-mix(in lab, red, red)){.\[a\]\:hover\:bg-primary\/80:is(a):hover{background-color:color-mix(in oklab, var(--primary) 80%, transparent)}}.\[a\]\:hover\:bg-secondary\/80:is(a):hover{background-color:var(--secondary)}@supports (color:color-mix(in lab, red, red)){.\[a\]\:hover\:bg-secondary\/80:is(a):hover{background-color:color-mix(in oklab, var(--secondary) 80%, transparent)}}.\[a\]\:hover\:text-muted-foreground:is(a):hover{color:var(--muted-foreground)}}:is(.\*\:\[img\:first-child\]\:rounded-none>*):is(img:first-child),:is(.\*\:\[img\:last-child\]\:rounded-none>*):is(img:last-child){border-radius:0}:is(.data-\[variant\=destructive\]\:\*\:\[svg\]\:text-destructive[data-variant=destructive]>*):is(svg){color:var(--destructive)}.\[\&\>svg\]\:pointer-events-none>svg{pointer-events:none}.\[\&\>svg\]\:size-3\!>svg{width:calc(var(--spacing) * 3)!important;height:calc(var(--spacing) * 3)!important}}@property --tw-animation-delay{syntax:"*";inherits:false;initial-value:0s}@property --tw-animation-direction{syntax:"*";inherits:false;initial-value:normal}@property --tw-animation-duration{syntax:"*";inherits:false}@property --tw-animation-fill-mode{syntax:"*";inherits:false;initial-value:none}@property --tw-animation-iteration-count{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-translate-y{syntax:"*";inherits:false;initial-value:0}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(data:font/woff2;base64,d09GMgABAAAAAAfsABQAAAAAEAwAAAeCAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhwbHhwoP0hWQVJbBmA/U1RBVIFiJyYAdC9qEQgKhGSEAAsgADCGCAE2AiQDOgQgBYlMB4EUDAcbLQ4onoexrSC/2ZyLAa8p8VHB8/x3Vue+V0hVJalMJg2nx/TCrQXxBeqLjQG7FyM1WEa/X1tEXN7cFz9EJEMmMUz3RihWSSKeQCbcIou0izz/C8v+fq3VfajEa9gDD11CImXS7qL/RJFVzC1qiB6KmKeD6TZdQ6IRGv78dL6uSVVCfgni5mzu7kcgQBgAEAQTQRCoL++STTYybkJxNfQxAAIAGu8OdEB9teW2jh4BpgDqFjAeSEByW3zFP0CBBgNMsMCGEDjgggdhiEAUAeIIED7ABTDUEnkIE9Q9ahFgKttcVhApo4ACB4qobHaccgDfEjFO6aaWUhjMLt2SyIvHKoDqoA4CSUwEIYQCEjhAO9R1G6keDeDZGjNo+AhxOjCEGTr1WeIF3kYBiLAOKvkJSMiKX0VdAyQt3SDJClCkxJCHkCzfqyVTriJZLcolS32JZHUekq2TYNkYtCtjYHMQXSxGjXDz2t/yLWXzDzxz+o3zFwDEaN23F+13pyMdQAEaSKAR9vcGq4A4MTSKCElGW+M7UcY7xqkggITb28ZJhlqc9q2twYKTt0NjixBgYvO9BIihEBLYuOFXQzfIQ7dXGUEEEgFDooBfAzqiQbpJrhiWSuKJCRFKYbHCyJKI2G5GiZbNAvgAu5pc3vwx4G+g3aDkhklABiSz0BICXrYghtYhx/cdJ+44rY2oZ0aMNRFz3VZjb6W33F3gzltqtOCV8tTHSpOeXuItfvr5lCdfzFpqtEitvqdcdGGFd28ZqqC0tPbeChGXgrIlnhSWu/eUso4uKWFLugyDzQJhflY4659+WjQ++6x72WUMv9G8mw6QJl7BVxX5fe/kpUsOvnZwee9uQ0cGXYd0o89XB2748sDSnt8d2VphdOTTgceDVvOds0v9P/s7HPq15aGun/6Vllb56f1dl0t1LejqrNkpdRZsG8TOnM5vkBG5oiVyVGnS8LHps5cfNWJs6qKPfaNSxiQNBUm3cKNWROr0GSur7Za31k1vieq7LH11VF+jXdRIasRKflc7jkobm1Z9te1IyZA0pDkhLR98+H37Zf1c/8at+dB7x+7GfVyTfJMPiYztsnl59Y5l4j+0n1RXlpHnF3Tq7HecmNF/CJodEMAikruxiyJaGLvHOdAfoA+oDvpjBm2b91cHGRZMU9n25xEU0A8fgEEAdKI3Q1iDtc034sug5YVMkE2jsE+BIkwSoQ3gxXMqz9tELp48bd0cFKOKS7xYjEuXBnZP5ia7DyiO/X/YI+PQSbt2uSdqAkWL9nQbV1XB94/+uPfdZz8dnXYFBYrcTl2SIR/ybxJNJPz/Gupb0JaZeens2ekC7EKr8t+Ls/P5VJPYJdHKyqfg2nqU6bhlidzcddQV/7MmecTzJ5VPcKXkNKSEogHjYFx6QZ7rQ+FSe8njaiNuOnXS8H2ScQ619c2mC3VTtauL0rRbXd/CkSOP37FY9Zkjz8+GibYUMOEWF+RdrFS8Ecv1SHOpPUPZGEIpjPvFyU5cXKjd6OXqorTqy9GwRd++HVufPGnVsW+aO3vggKZ18jR9sXaTC1PWTEsVUaK0FkNySbTQDqlm2PfDjZcu4aalnSLKjnOoYQ0nUlqqXcGpPu/4VgV/xU2pAqW4BW3qzhQ8/hFKhV2qE3+BKAtDqBXjfgnVdH4y0wg5tbVNRenNdTWOrenWLcupQdmsbq5b+18piTe/xRdp1xbILxNPJGInm2z6hoB21Lal0i+ePTtd7B45+3XhFJ329evskXm7qurUVREotqSluSo/L29d3qDhI4YOQqWhI4YNvBNfsMHeXKemXrxQfKeuPOGRVayA3JtkJKEgbPp+dXUDluddutRYLFoXGXWX6N3WFaGLbQtRSitVYNacTNSdy7AaG/HSaUEANcBoGXNdcZvZsOqQ1icBDv21/gzAoYPHH/WDW0qNR3QTYKEAEHig6o13NXbND06CQPlRtYjGNnSktRc09k1mAMDvAlDKfQjgy6fssInlfzmNAjKkDxoxHOBLdVRAIVt9j4qo+hA1w9T1aNBNTUOTTNUHLbqokE+UAfJXCIGw/IxCSL5GRUJeR40rL/UxTm4Q08H6MbCs70ObuNyIIXrINHQYInF06UUlevTjbQzTh5upiDMzMMogUtEnjPs/Y7jAHCJeB0GBHh04tC6FiB6ZFB1oArUSIoFoqhzCeAN6lHwm0T4C3VVPWvjpSMXReuWesMEcoqrmgtNBGd2noWeV0hNAz9rFeShNJxHGsPa3HXeKTk8b55hahySYHaYKKFFLpCfN8rsoaJn01CR04Gkc+5k7KVTCmClX8Q10HCrUEkVlSX+XO33oQR9609tJ516H497WSobWs5Up6TLaS10/dessIskgJSLiDlWvHVUywpkQ7hdPZqGyiEF0uVQerVcPamT1A3eKXdyI1vG9OoflrSXihZ1qqGE3nhmAgiIbRCQgPLEPtOM3UQwTLYaYYomNlpA44opnjV6jkD6id80OOrzf6BzmMD6eEa1zKyeYG1fzfEf16V6jw9XYOaar1/b2kP/IYX8oR2mcFvv2GtBV3JXgd437AQAA)format("woff2-variations");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2)format("woff2-variations");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2)format("woff2-variations");unicode-range:U+370-377,U+37A-37F,U+384-38A,U+38C,U+38E-3A1,U+3A3-3FF}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2)format("woff2-variations");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:JetBrains Mono Variable;font-style:normal;font-display:swap;font-weight:100 800;src:url(/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root,.light{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--radius:.625rem;--background:oklch(100% 0 0);--foreground:oklch(14.5% 0 0);--card:oklch(100% 0 0);--card-foreground:oklch(14.5% 0 0);--popover:oklch(100% 0 0);--popover-foreground:oklch(14.5% 0 0);--primary:oklch(20.5% 0 0);--primary-foreground:oklch(98.5% 0 0);--secondary:oklch(97% 0 0);--secondary-foreground:oklch(20.5% 0 0);--muted:oklch(97% 0 0);--muted-foreground:oklch(55.6% 0 0);--accent:oklch(97% 0 0);--accent-foreground:oklch(20.5% 0 0);--destructive:oklch(57.7% .245 27.325);--border:oklch(92.2% 0 0);--input:oklch(92.2% 0 0);--ring:oklch(70.8% 0 0);--chart-1:oklch(64.6% .222 41.116);--chart-2:oklch(60% .118 184.704);--chart-3:oklch(39.8% .07 227.392);--chart-4:oklch(82.8% .189 84.429);--chart-5:oklch(76.9% .188 70.08);--sidebar:oklch(98.5% 0 0);--sidebar-foreground:oklch(14.5% 0 0);--sidebar-primary:oklch(20.5% 0 0);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(97% 0 0);--sidebar-accent-foreground:oklch(20.5% 0 0);--sidebar-border:oklch(92.2% 0 0);--sidebar-ring:oklch(70.8% 0 0)}.dark{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--background:oklch(14.5% 0 0);--foreground:oklch(98.5% 0 0);--card:oklch(20.5% 0 0);--card-foreground:oklch(98.5% 0 0);--popover:oklch(20.5% 0 0);--popover-foreground:oklch(98.5% 0 0);--primary:oklch(92.2% 0 0);--primary-foreground:oklch(20.5% 0 0);--secondary:oklch(26.9% 0 0);--secondary-foreground:oklch(98.5% 0 0);--muted:oklch(26.9% 0 0);--muted-foreground:oklch(70.8% 0 0);--accent:oklch(26.9% 0 0);--accent-foreground:oklch(98.5% 0 0);--destructive:oklch(70.4% .191 22.216);--border:oklch(100% 0 0/.1);--input:oklch(100% 0 0/.15);--ring:oklch(55.6% 0 0);--chart-1:oklch(48.8% .243 264.376);--chart-2:oklch(69.6% .17 162.48);--chart-3:oklch(76.9% .188 70.08);--chart-4:oklch(62.7% .265 303.9);--chart-5:oklch(64.5% .246 16.439);--sidebar:oklch(20.5% 0 0);--sidebar-foreground:oklch(98.5% 0 0);--sidebar-primary:oklch(48.8% .243 264.376);--sidebar-primary-foreground:oklch(98.5% 0 0);--sidebar-accent:oklch(26.9% 0 0);--sidebar-accent-foreground:oklch(98.5% 0 0);--sidebar-border:oklch(100% 0 0/.1);--sidebar-ring:oklch(55.6% 0 0)}@property --tw-divide-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-leading{syntax:"*";inherits:false}@property --tw-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-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-duration{syntax:"*";inherits:false}@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}@keyframes enter{0%{opacity:var(--tw-enter-opacity,1);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0)scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1))rotate(var(--tw-enter-rotate,0));filter:blur(var(--tw-enter-blur,0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity,1);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0)scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1))rotate(var(--tw-exit-rotate,0));filter:blur(var(--tw-exit-blur,0))}}
|