@fapost/jig-ui 0.3.1-next.62 → 0.3.1-next.65

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/args.js CHANGED
@@ -1,13 +1,17 @@
1
1
  import { parseArgs } from 'node:util';
2
2
  export const DEFAULT_PORT = 4870;
3
+ const SUBCOMMANDS = ['start', 'stop', 'status', 'open', 'serve'];
4
+ /** Subcommands that listen on a port; the others only talk to the instance that does. */
5
+ const TAKES_PORT = new Set(['run', 'start', 'serve']);
3
6
  /** Parses the command line (without the node and script entries). Pure: it never exits or prints. */
4
7
  export function parseCliArgs(argv) {
8
+ let positionals;
5
9
  let values;
6
10
  try {
7
- ({ values } = parseArgs({
11
+ ({ values, positionals } = parseArgs({
8
12
  args: argv,
9
13
  strict: true,
10
- allowPositionals: false,
14
+ allowPositionals: true,
11
15
  options: {
12
16
  port: { type: 'string' },
13
17
  'no-open': { type: 'boolean' },
@@ -23,12 +27,35 @@ export function parseCliArgs(argv) {
23
27
  return { kind: 'help' };
24
28
  if (values.version)
25
29
  return { kind: 'version' };
26
- const open = !values['no-open'];
27
- if (values.port === undefined)
28
- return { kind: 'run', port: DEFAULT_PORT, open };
29
- const port = /^\d+$/.test(values.port) ? Number(values.port) : Number.NaN;
30
- if (!Number.isInteger(port) || port < 1 || port > 65535) {
31
- return { kind: 'error', message: `Invalid --port value: ${values.port}. Use a number from 1 to 65535.` };
30
+ const [name, ...rest] = positionals;
31
+ if (rest.length > 0)
32
+ return { kind: 'error', message: `Unexpected argument: ${rest[0]}` };
33
+ if (name !== undefined && !SUBCOMMANDS.includes(name)) {
34
+ return { kind: 'error', message: `Unknown command: ${name}. Run jig-ui --help.` };
35
+ }
36
+ const command = name ?? 'run';
37
+ if (values.port !== undefined && !TAKES_PORT.has(command)) {
38
+ return { kind: 'error', message: `--port does not apply to ${command}.` };
39
+ }
40
+ if (values['no-open'] && command !== 'run') {
41
+ return { kind: 'error', message: `--no-open does not apply to ${command}.` };
42
+ }
43
+ let port = DEFAULT_PORT;
44
+ if (values.port !== undefined) {
45
+ port = /^\d+$/.test(values.port) ? Number(values.port) : Number.NaN;
46
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
47
+ return { kind: 'error', message: `Invalid --port value: ${values.port}. Use a number from 1 to 65535.` };
48
+ }
49
+ }
50
+ switch (command) {
51
+ case 'start':
52
+ case 'serve':
53
+ return { kind: command, port };
54
+ case 'stop':
55
+ case 'status':
56
+ case 'open':
57
+ return { kind: command };
58
+ default:
59
+ return { kind: 'run', port, open: !values['no-open'] };
32
60
  }
33
- return { kind: 'run', port, open };
34
61
  }
package/dist/http/app.js CHANGED
@@ -79,9 +79,16 @@ function upgradeRefused(c, code) {
79
79
  * The API, chained so that its type carries every route's answers: the page derives its types
80
80
  * from `AppType` and never declares them a second time.
81
81
  */
82
- function apiRoutes(source, version) {
82
+ function apiRoutes(source, version, stop) {
83
83
  return (new Hono()
84
84
  .get('/api/instance', (c) => c.json({ version }, 200))
85
+ .post('/api/instance/stop', (c) => {
86
+ if (!stop)
87
+ return refuse(c, 404, 'not-found', 'This instance cannot be stopped from here.');
88
+ // After the reply is written, or the caller sees a cut connection instead of the 202.
89
+ setTimeout(stop, 50);
90
+ return c.json({ stopping: true }, 202);
91
+ })
85
92
  .get('/api/workspace', async (c) => c.json({ projects: await source.workspace(), machine: source.machine() }, 200))
86
93
  // Moves this machine's Jig to its newest release and answers once it has: a git fetch, not minutes.
87
94
  .post('/api/self-update', async (c) => {
@@ -283,7 +290,7 @@ function apiRoutes(source, version) {
283
290
  }
284
291
  })));
285
292
  }
286
- export function createApp({ port, token, source, version = readPackageVersion(), webRoot = DEFAULT_WEB_ROOT }) {
293
+ export function createApp({ port, token, source, version = readPackageVersion(), webRoot = DEFAULT_WEB_ROOT, stop }) {
287
294
  const app = new Hono();
288
295
  // Before the guard, so its refusals carry them too; HSTS means nothing on http://127.0.0.1.
289
296
  app.use(secureHeaders({ strictTransportSecurity: false }));
@@ -299,7 +306,7 @@ export function createApp({ port, token, source, version = readPackageVersion(),
299
306
  console.error(error);
300
307
  return refuse(c, 500, 'internal', 'Jig UI hit an error it did not expect; its terminal says what.');
301
308
  });
302
- app.route('/', apiRoutes(source, version));
309
+ app.route('/', apiRoutes(source, version, stop));
303
310
  app.all('/api/*', (c) => c.json({ error: 'not found' }, 404));
304
311
  if (existsSync(join(webRoot, 'index.html'))) {
305
312
  app.use('/*', serveStatic({ root: webRoot }));
package/dist/main.js CHANGED
@@ -8,16 +8,32 @@ import { openBrowser } from './open-browser.js';
8
8
  import { configDir } from './registry/config-dir.js';
9
9
  import { connectThrough, probeInstance, readInstance, removeInstance, writeInstance } from './registry/instance.js';
10
10
  import { mainCheckout } from './source/checkout.js';
11
+ import { instanceStatus, startInstance, stopInstance } from './service.js';
11
12
  import { localSource } from './source/local.js';
12
13
  const HOST = '127.0.0.1';
13
14
  const HELP = `Usage: jig-ui [options]
15
+ jig-ui start [--port <n>]
16
+ jig-ui stop | status | open
17
+
18
+ Run without a command, Jig UI runs in this terminal and opens the browser.
19
+
20
+ Commands:
21
+ start Start Jig UI in the background
22
+ stop Stop the running Jig UI
23
+ status Say whether Jig UI is running (exit code 3 when it is not)
24
+ open Open the running Jig UI in the browser
25
+ serve Run in this terminal without a browser or a folder: what start runs
14
26
 
15
27
  Options:
16
- --port <n> Port to listen on (default 4870)
17
- --no-open Print the link instead of opening the browser
28
+ --port <n> Port to listen on (default 4870); run, start and serve
29
+ --no-open Print the link instead of opening the browser; run only
18
30
  -v, --version Print the version and exit
19
31
  -h, --help Print this help and exit
20
32
  `;
33
+ /** The exit code of `status` and `open` when Jig UI is not running. */
34
+ const NOT_RUNNING = 3;
35
+ /** An instance whose port stays silent: maybe busy, so nothing is removed or started in its place. */
36
+ const silent = ({ port, pid }) => `Jig UI on port ${port} (pid ${pid}) does not answer. Try again; if it stays silent, end that process yourself.`;
21
37
  function fail(message) {
22
38
  console.error(message);
23
39
  process.exit(1);
@@ -71,36 +87,40 @@ async function winnerOfRace(dir) {
71
87
  }
72
88
  return undefined;
73
89
  }
74
- export async function main() {
75
- const command = parseCliArgs(process.argv.slice(2));
76
- if (command.kind === 'error')
77
- fail(command.message);
78
- if (command.kind === 'help') {
79
- process.stdout.write(HELP);
80
- return;
81
- }
82
- if (command.kind === 'version') {
83
- console.log(readPackageVersion());
84
- return;
85
- }
86
- const { port, open } = command;
87
- const dir = configDir(process.env, process.platform, homedir());
88
- const place = await here();
90
+ /**
91
+ * Runs the server in this process. `run` also connects the folder and opens the browser; `serve`
92
+ * (what `start` runs in the background) does neither and keeps quiet about Ctrl+C.
93
+ */
94
+ async function runServer(command, dir) {
95
+ const { port } = command;
96
+ const foreground = command.kind === 'run';
97
+ const open = foreground && command.open;
98
+ const place = foreground ? await here() : undefined;
89
99
  const running = await runningInstance(dir);
90
100
  if (running) {
91
101
  console.log(`Jig UI is already running on port ${running.port}.`);
92
102
  // ponytail: a --port equal to the default is indistinguishable from no flag; no note then.
93
103
  if (port !== running.port && port !== DEFAULT_PORT)
94
104
  console.log(`--port ${port} is ignored.`);
95
- await show(running, open, dir, await connectHere(running, place));
105
+ if (foreground)
106
+ await show(running, open, dir, await connectHere(running, place));
96
107
  return;
97
108
  }
98
109
  const version = readPackageVersion();
99
110
  const token = randomBytes(32).toString('base64url');
100
- const server = serve({ fetch: createApp({ port, token, source: localSource(dir), version }).fetch, hostname: HOST, port }, () => {
111
+ const shutdown = () => {
112
+ void removeInstance(dir, token).finally(() => {
113
+ server.close(() => process.exit(0));
114
+ if ('closeAllConnections' in server)
115
+ server.closeAllConnections();
116
+ });
117
+ };
118
+ const server = serve({ fetch: createApp({ port, token, source: localSource(dir), version, stop: shutdown }).fetch, hostname: HOST, port }, () => {
101
119
  void (async () => {
102
120
  await writeInstance(dir, { port, token, pid: process.pid, version });
103
121
  console.log(`Jig UI ${version} is running at http://${HOST}:${port}/`);
122
+ if (!foreground)
123
+ return;
104
124
  await show({ port, token }, open, dir, await connectHere({ port, token }, place));
105
125
  console.log('Press Ctrl+C to stop.');
106
126
  })().catch((error) => fail(`Could not start: ${error instanceof Error ? error.message : String(error)}`));
@@ -113,17 +133,104 @@ export async function main() {
113
133
  if (!winner)
114
134
  fail(`Port ${port} is already in use. Stop the other process or choose another port with --port.`);
115
135
  console.log(`Jig UI is already running on port ${winner.port}.`);
116
- await show(winner, open, dir, await connectHere(winner, place));
136
+ if (foreground)
137
+ await show(winner, open, dir, await connectHere(winner, place));
117
138
  process.exit(0);
118
139
  });
119
140
  });
120
- const shutdown = () => {
121
- void removeInstance(dir, token).finally(() => {
122
- server.close(() => process.exit(0));
123
- if ('closeAllConnections' in server)
124
- server.closeAllConnections();
125
- });
126
- };
127
141
  process.on('SIGINT', shutdown);
128
142
  process.on('SIGTERM', shutdown);
129
143
  }
144
+ async function startBackground(port, dir) {
145
+ const result = await startInstance(dir, port);
146
+ if (result.kind === 'failed') {
147
+ console.error(`Could not start Jig UI. The end of ${result.log}:\n${result.tail}`);
148
+ process.exit(1);
149
+ }
150
+ if (result.kind === 'not-answering')
151
+ fail(silent(result.info));
152
+ if (result.kind === 'already-running') {
153
+ console.log(`Jig UI is already running on port ${result.info.port}.`);
154
+ if (port !== result.info.port && port !== DEFAULT_PORT)
155
+ console.log(`--port ${port} is ignored.`);
156
+ console.log(`Open it at ${link(result.info)}`);
157
+ return;
158
+ }
159
+ console.log(`Jig UI ${result.info.version} is running at http://${HOST}:${result.info.port}/ in the background.`);
160
+ console.log(`Open it at ${link(result.info)}`);
161
+ console.log(`Log: ${result.log}`);
162
+ }
163
+ async function stopBackground(dir) {
164
+ const result = await stopInstance(dir);
165
+ switch (result.kind) {
166
+ case 'not-running':
167
+ console.log('Jig UI is not running.');
168
+ return;
169
+ case 'stale':
170
+ console.log(`Jig UI is not running: the file of an instance on port ${result.info.port} was left behind and is removed. No process was signalled.`);
171
+ return;
172
+ case 'stopped':
173
+ console.log(`Stopped Jig UI on port ${result.info.port}.`);
174
+ return;
175
+ case 'not-answering':
176
+ console.error(silent(result.info));
177
+ process.exitCode = 1;
178
+ return;
179
+ case 'timeout':
180
+ console.error(`Jig UI on port ${result.info.port} (pid ${result.info.pid}) did not stop in time.`);
181
+ process.exitCode = 1;
182
+ }
183
+ }
184
+ async function statusOf(dir) {
185
+ const status = await instanceStatus(dir);
186
+ if (status.kind === 'running') {
187
+ const { info } = status;
188
+ console.log(`Jig UI ${info.version} is running at http://${HOST}:${info.port}/ (pid ${info.pid}).`);
189
+ console.log(`Open it at ${link(info)}`);
190
+ return;
191
+ }
192
+ if (status.kind === 'not-answering') {
193
+ console.error(silent(status.info));
194
+ process.exitCode = 1;
195
+ return;
196
+ }
197
+ console.log(status.kind === 'stale'
198
+ ? `Jig UI is not running; the file of an instance on port ${status.info.port} was left behind. jig-ui stop removes it.`
199
+ : 'Jig UI is not running. Start it with jig-ui start.');
200
+ process.exitCode = NOT_RUNNING;
201
+ }
202
+ async function openRunning(dir) {
203
+ const status = await instanceStatus(dir);
204
+ if (status.kind === 'running')
205
+ return show(status.info, true, dir);
206
+ if (status.kind === 'not-answering')
207
+ fail(silent(status.info));
208
+ console.log('Jig UI is not running. Start it with jig-ui start.');
209
+ process.exitCode = NOT_RUNNING;
210
+ }
211
+ export async function main() {
212
+ const command = parseCliArgs(process.argv.slice(2));
213
+ if (command.kind === 'error')
214
+ fail(command.message);
215
+ if (command.kind === 'help') {
216
+ process.stdout.write(HELP);
217
+ return;
218
+ }
219
+ if (command.kind === 'version') {
220
+ console.log(readPackageVersion());
221
+ return;
222
+ }
223
+ const dir = configDir(process.env, process.platform, homedir());
224
+ switch (command.kind) {
225
+ case 'start':
226
+ return startBackground(command.port, dir);
227
+ case 'stop':
228
+ return stopBackground(dir);
229
+ case 'status':
230
+ return statusOf(dir);
231
+ case 'open':
232
+ return openRunning(dir);
233
+ default:
234
+ return runServer(command, dir);
235
+ }
236
+ }
@@ -35,21 +35,37 @@ export async function removeInstance(dir, token) {
35
35
  await rm(join(dir, OPEN_FILE), { force: true });
36
36
  await rm(join(dir, FILE), { force: true });
37
37
  }
38
+ const causeCode = (error) => error.cause?.code;
39
+ /** A connection refused outright: nothing listens on the port. */
40
+ const refused = (error) => causeCode(error) === 'ECONNREFUSED';
41
+ export async function askInstance(info, timeoutMs = 1000) {
42
+ for (let attempt = 0;; attempt++) {
43
+ try {
44
+ const res = await fetch(`http://127.0.0.1:${info.port}/api/instance`, {
45
+ headers: { cookie: `${cookieName(info.port)}=${info.token}` },
46
+ signal: AbortSignal.timeout(timeoutMs),
47
+ });
48
+ // An unread body holds the connection, and the next probe of a stop's wait would queue behind it.
49
+ await res.body?.cancel();
50
+ return res.ok ? 'up' : 'gone';
51
+ }
52
+ catch (error) {
53
+ if (refused(error))
54
+ return 'gone';
55
+ // A kept-alive connection to a server that has since stopped breaks (reset, or closed as it
56
+ // is used, as on Linux); only a timeout means silence, and a new connection tells the rest.
57
+ if (error.name !== 'TimeoutError' && attempt === 0)
58
+ continue;
59
+ return 'silent';
60
+ }
61
+ }
62
+ }
38
63
  /** True when an instance answers on its port with its token; a crash leaves the file behind. */
39
64
  export async function probeInstance(info, timeoutMs = 1000) {
40
- try {
41
- const res = await fetch(`http://127.0.0.1:${info.port}/api/instance`, {
42
- headers: { cookie: `${cookieName(info.port)}=${info.token}` },
43
- signal: AbortSignal.timeout(timeoutMs),
44
- });
45
- return res.ok;
46
- }
47
- catch {
48
- return false;
49
- }
65
+ return (await askInstance(info, timeoutMs)) === 'up';
50
66
  }
51
67
  // An instance before 0.2.0 answers a failure it did not foresee in plain text: neither of these.
52
- const Answer = z.union([
68
+ const Reply = z.union([
53
69
  z.object({ project: z.object({ id: z.string() }) }),
54
70
  z.object({ error: z.object({ code: z.string(), message: z.string() }) }),
55
71
  ]);
@@ -62,7 +78,7 @@ export async function connectThrough(info, path, timeoutMs = 15_000) {
62
78
  body: JSON.stringify({ path }),
63
79
  signal: AbortSignal.timeout(timeoutMs),
64
80
  });
65
- const body = Answer.safeParse(await res.json().catch(() => undefined)).data;
81
+ const body = Reply.safeParse(await res.json().catch(() => undefined)).data;
66
82
  if (body && 'project' in body)
67
83
  return { id: body.project.id, created: res.status === 201 };
68
84
  return body?.error ?? { code: 'unexpected', message: `Jig UI answered ${res.status}.` };
@@ -71,3 +87,19 @@ export async function connectThrough(info, path, timeoutMs = 15_000) {
71
87
  return { code: 'unreachable', message: error instanceof Error ? error.message : String(error) };
72
88
  }
73
89
  }
90
+ /** Asks the instance to shut down, as `connectThrough` asks it to connect: the token proves the instance is ours. */
91
+ export async function requestStop(info, timeoutMs = 5000) {
92
+ try {
93
+ const res = await fetch(`http://127.0.0.1:${info.port}/api/instance/stop`, {
94
+ method: 'POST',
95
+ headers: { cookie: `${cookieName(info.port)}=${info.token}` },
96
+ signal: AbortSignal.timeout(timeoutMs),
97
+ });
98
+ await res.body?.cancel();
99
+ return res.status === 404 ? 'unsupported' : 'stopping';
100
+ }
101
+ catch (error) {
102
+ // A timeout or reset after the request was sent may still be a server going down; a refusal is not.
103
+ return refused(error) ? 'unreachable' : 'stopping';
104
+ }
105
+ }
@@ -0,0 +1,130 @@
1
+ import { spawn as spawnProcess } from 'node:child_process';
2
+ import { mkdir, open, readFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { setTimeout as sleep } from 'node:timers/promises';
6
+ import { askInstance, readInstance, removeInstance, requestStop, } from './registry/instance.js';
7
+ /** The log of the background instance; overwritten at each start, but it grows while the instance runs (task server-log-rotation). */
8
+ export const LOG_FILE = 'server.log';
9
+ /** How long the instance gets to answer a probe; one that stays silent is busy, never stale. */
10
+ const PROBE_MS = 3000;
11
+ /** How long a start waits for the new instance, and a stop for the old one to go. */
12
+ const WAIT_MS = 10_000;
13
+ const POLL_MS = 100;
14
+ const TAIL_LINES = 20;
15
+ /** The program that runs the instance: this very CLI, next to this file in `dist` and in `server`. */
16
+ const CLI_PATH = fileURLToPath(new URL('./cli.js', import.meta.url));
17
+ export const realDeps = {
18
+ probe: askInstance,
19
+ requestStop: (info) => requestStop(info),
20
+ kill: (pid, signal) => process.kill(pid, signal),
21
+ spawn(command, args, { cwd, fd }) {
22
+ const child = spawnProcess(command, args, { detached: true, stdio: ['ignore', fd, fd], windowsHide: true, cwd });
23
+ const exit = new Promise((resolve) => {
24
+ // A process that cannot start never exits and writes no log: its error is all there is to say.
25
+ child.on('error', (error) => resolve({ code: -1, error: error.message }));
26
+ child.on('exit', (code) => resolve({ code }));
27
+ });
28
+ return { exit, unref: () => child.unref(), kill: () => child.kill() };
29
+ },
30
+ delay: (ms) => sleep(ms),
31
+ now: () => Date.now(),
32
+ };
33
+ /**
34
+ * Reads the instance file and asks the instance. A file whose port refuses, or answers as someone
35
+ * else, is stale; one whose port stays silent is `not-answering`: maybe alive and busy. It deletes nothing.
36
+ */
37
+ export async function instanceStatus(dir, deps = realDeps) {
38
+ const info = await readInstance(dir);
39
+ if (!info)
40
+ return { kind: 'not-running' };
41
+ const answer = await deps.probe(info, PROBE_MS);
42
+ return { kind: answer === 'up' ? 'running' : answer === 'gone' ? 'stale' : 'not-answering', info };
43
+ }
44
+ async function logTail(log) {
45
+ try {
46
+ return (await readFile(log, 'utf8')).trimEnd().split('\n').slice(-TAIL_LINES).join('\n');
47
+ }
48
+ catch {
49
+ return '';
50
+ }
51
+ }
52
+ /**
53
+ * Starts the instance in the background and waits until it answers. The child is the CLI itself
54
+ * as `serve`: detached from this terminal, its output in the log, its working directory the
55
+ * config directory, so it holds no folder of the person's.
56
+ */
57
+ export async function startInstance(dir, port, deps = realDeps) {
58
+ const status = await instanceStatus(dir, deps);
59
+ if (status.kind === 'running')
60
+ return { kind: 'already-running', info: status.info };
61
+ // A second server would only lose the port to it, and the log would be overwritten for nothing.
62
+ if (status.kind === 'not-answering')
63
+ return status;
64
+ await mkdir(dir, { recursive: true, mode: 0o700 });
65
+ const log = join(dir, LOG_FILE);
66
+ const handle = await open(log, 'w', 0o600);
67
+ let child;
68
+ try {
69
+ child = deps.spawn(process.execPath, [CLI_PATH, 'serve', '--port', String(port)], { cwd: dir, fd: handle.fd });
70
+ }
71
+ finally {
72
+ // The child has its own copy of the descriptor.
73
+ await handle.close();
74
+ }
75
+ child.unref();
76
+ let ended;
77
+ void child.exit.then((value) => {
78
+ ended = value;
79
+ });
80
+ const deadline = deps.now() + WAIT_MS;
81
+ while (deps.now() < deadline) {
82
+ await deps.delay(POLL_MS);
83
+ const info = await readInstance(dir);
84
+ if (info && (await deps.probe(info, PROBE_MS)) === 'up')
85
+ return { kind: 'started', info, log };
86
+ // A child that exited with 0 found another instance already there: the next poll sees it.
87
+ if (ended && ended.code !== 0)
88
+ break;
89
+ }
90
+ // Too slow is a failure too: end the child rather than let it come up after we said it did not.
91
+ if (!ended)
92
+ child.kill();
93
+ return { kind: 'failed', log, tail: ended?.error ?? (await logTail(log)) };
94
+ }
95
+ /**
96
+ * Stops the instance the file names. An instance that answers is asked over HTTP, with its token:
97
+ * that works on every system and cannot hit a process that merely reused the pid. A stale file
98
+ * only goes away, and no signal is sent, because its pid may belong to anything by now. A silent
99
+ * port changes nothing: removing the file of a busy instance would leave it running unseen.
100
+ */
101
+ export async function stopInstance(dir, deps = realDeps) {
102
+ const status = await instanceStatus(dir, deps);
103
+ if (status.kind === 'not-running' || status.kind === 'not-answering')
104
+ return status;
105
+ const { info } = status;
106
+ if (status.kind === 'stale') {
107
+ await removeInstance(dir, info.token);
108
+ return status;
109
+ }
110
+ const requested = await deps.requestStop(info);
111
+ // An instance from before the endpoint existed: the probe just proved that its pid is the one that answers.
112
+ if (requested === 'unsupported') {
113
+ try {
114
+ deps.kill(info.pid, 'SIGTERM');
115
+ }
116
+ catch {
117
+ // Gone between the probe and the signal: the wait below sees it.
118
+ }
119
+ }
120
+ const deadline = deps.now() + WAIT_MS;
121
+ while (deps.now() < deadline) {
122
+ if ((await deps.probe(info, PROBE_MS)) === 'gone') {
123
+ // The server removes its files itself; this covers a hard exit.
124
+ await removeInstance(dir, info.token);
125
+ return { kind: 'stopped', info };
126
+ }
127
+ await deps.delay(POLL_MS);
128
+ }
129
+ return { kind: 'timeout', info };
130
+ }
@@ -1 +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 c,I as ee,J as l,K as te,L as u,M as d,N as f,O as ne,P as re,Q as p,R as ie,S as m,T as h,U as g,V as _,W as v,X as y,Y as b,Z as x,_ as S,a as C,at as w,b as ae,c as oe,d as T,et as E,f as se,g as D,h as O,i as ce,it as le,j as ue,k as de,l as fe,m as pe,n as me,nt as k,o as he,p as A,q as j,r as M,rt as N,s as P,t as ge,tt as F,u as _e,v as ve,w as ye,x as I,y as be,z as xe}from"./index-U-0MJGK-.js";var Se=b({__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(e){let t=e,n=[`horizontal`,`vertical`];function r(e){return n.includes(e)}let i=_(()=>r(t.orientation)?t.orientation:`horizontal`),a=_(()=>i.value===`vertical`?t.orientation:void 0),o=_(()=>t.decorative?{role:`none`}:{"aria-orientation":a.value,role:`separator`});return(t,n)=>(p(),g(k(re),x({as:e.as,"as-child":e.asChild,"data-orientation":i.value},o.value),{default:F(()=>[E(t.$slots,`default`)]),_:3},16,[`as`,`as-child`,`data-orientation`]))}}),Ce=b({__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)=>(p(),g(Se,le(y(t)),{default:F(()=>[E(e.$slots,`default`)]),_:3},16))}}),we=b({__name:`CardContent`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let t=e;return(e,n)=>(p(),s(`div`,{"data-slot":`card-content`,class:N(k(o)(`px-4 group-data-[size=sm]/card:px-3`,t.class))},[E(e.$slots,`default`)],2))}}),Te=b({__name:`CardDescription`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let t=e;return(e,n)=>(p(),s(`div`,{"data-slot":`card-description`,class:N(k(o)(`text-xs/relaxed text-muted-foreground`,t.class))},[E(e.$slots,`default`)],2))}}),Ee=b({__name:`CardHeader`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let t=e;return(e,n)=>(p(),s(`div`,{"data-slot":`card-header`,class:N(k(o)(`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`,t.class))},[E(e.$slots,`default`)],2))}}),De=b({__name:`CardTitle`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let t=e;return(e,n)=>(p(),s(`div`,{"data-slot":`card-title`,class:N(k(o)(`cn-font-heading text-sm font-medium group-data-[size=sm]/card:text-sm`,t.class))},[E(e.$slots,`default`)],2))}}),L=b({__name:`Separator`,props:{orientation:{default:`horizontal`},decorative:{type:Boolean,default:!0},asChild:{type:Boolean},as:{},class:{type:[Boolean,null,String,Object,Array]}},setup(e){let n=e,r=t(n,`class`);return(e,t)=>(p(),g(k(Ce),x({"data-slot":`separator`},k(r),{class:k(o)(`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`]))}}),Oe=[`aria-label`],ke={class:`flex items-baseline gap-3 border-b bg-muted/50 px-4 py-2`},Ae={class:`font-semibold tracking-wider uppercase`},je={key:0,class:`text-muted-foreground`},Me={class:`grid grid-cols-1 divide-y lg:grid-cols-2 lg:divide-x lg:divide-y-0`},R=b({__name:`CanvasSection`,props:{title:{},note:{}},setup(t){return(r,i)=>(p(),s(`section`,{"aria-label":t.title,class:`border-b`},[c(`h2`,ke,[c(`span`,Ae,w(t.title),1),t.note?(p(),s(`span`,je,w(t.note),1)):v(``,!0)]),c(`div`,Me,[(p(),s(n,null,e([`light`,`dark`],e=>c(`div`,{key:e,class:N([e,`flex flex-col gap-4 bg-background p-4 text-foreground`])},[E(r.$slots,`default`)],2)),64))])],8,Oe))}}),z=(e,t)=>({id:e,class:`T2`,status:`active`,goal:null,paused:!1,pauseRequest:null,pauseRequestable:!0,resumable:!1,resumesTo:null,started:!0,gate:null,autopilot:null,pullRequest:null,priority:null,queue:null,...t}),B=(e,t)=>({url:`https://github.com/fapost-lab/jig-ui/pull/18`,state:e,ci:t,fetchedAt:`2026-10-18T09:40:00Z`}),V=[z(`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.`}),z(`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.`}),z(`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.`}),z(`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.`}),z(`task-details`,{status:`ready`,pullRequest:B(`open`,`green`),goal:`Open a task's details from its card.`}),z(`refusal-fixture`,{class:`T0`,status:`ready`,pullRequest:B(`open`,`red`),goal:`Replace the refusal text in task-list-states.json with one Jig prints.`}),z(`docs-board`,{class:`T1`,status:`ready`,pullRequest:B(`open`,`pending`),goal:`Describe the five columns on the projects page of the docs.`}),z(`live-updates`,{status:`ready`,pullRequest:B(`merged`,`none`),goal:`Follow connected projects without a reload.`}),z(`no-pr-yet`,{class:`T1`,status:`ready`,goal:`Ready, nothing pushed.`}),z(`jig-init-hook`,{class:`T3`,paused:!0,pauseRequestable:!1,goal:`Register a project in an installed Jig UI from jig init.`}),z(`file-backlog-task`,{class:null,started:!1,goal:`File a minimal backlog task from the page through jig task new.`}),z(`a-task-with-a-rather-long-identifier-that-wraps`,{goal:null}),z(`conflict-display`,{pauseRequest:{requestedAt:`2026-10-22T09:00:00Z`},pauseRequestable:!1,goal:`Show what changed when a write was based on a stale read.`}),z(`resume-from-page`,{class:`T3`,paused:!0,pauseRequestable:!1,resumable:!0,resumesTo:`in-progress`,goal:`A drop out of Paused, or the card menu, resumes the task through jig task resume.`})],H=[{id:`needs-you`,title:`Needs you`,cards:V.slice(0,3)},{id:`in-progress`,title:`In progress`,cards:[V[3],V[12],V[11]]},{id:`ready`,title:`Ready`,cards:V.slice(4,9)},{id:`backlog`,title:`Backlog`,cards:[V[10]]},{id:`paused`,title:`Paused`,cards:[V[9],V[13]]}],U=H.map(e=>({...e,cards:[]})),Ne=[{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}],Pe=[{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}],W=[{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,versions: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`,versions:null},{kind:`housekeeping`,subject:{type:`project`,id:null},tasks:[],title:`Housekeeping never ran`,detail:null,todo:`Run jig housekeeping.`,url:`https://example.com/docs`,versions:null}],Fe=[{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`}],Ie={state:`ready`,issue:null,baseBranch:`main`,createdAt:`2026-10-17`,updatedAt:`2026-10-18`,knowledgeConsolidated:!1,worktree:null,gateBy:null,run:null,housekeepingFlags:[],attention:[]},G=[{...Ie,...V[2],gate:`approved`,gateBy:`human`,branch:`task/pause-request`,worktree:{uncommitted:4},issue:`#42`,pullRequest:B(`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:[W[0]],actions:Fe,column:{id:`needs-you`,title:`Needs you`}},{...Ie,...V[10],branch:null,actions:[{id:`start`,command:`jig task start file-backlog-task`}],column:{id:`backlog`,title:`Backlog`}}],K=(e,t={})=>({id:e,state:`ready`,jigVersion:`0.29.0`,counts:{active:11,paused:1,ready:3},attention:[],housekeepingStale:!1,upgrade:null,...t}),q=[K(`jig-ui`,{attention:W,housekeepingStale:!0}),K(`nextjs-store`,{counts:{active:0,paused:0,ready:0}}),K(`legacy-shop`,{jigVersion:`0.34.0`,counts:{active:2,paused:0,ready:0},attention:[{kind:`upgrade-available`,subject:{type:`project`,id:null},tasks:[],title:`Jig in this project is older than on this machine`,detail:`this project runs 0.34.0; this machine has 0.35.0`,todo:`jig upgrade`,url:null,versions:{from:`0.34.0`,to:`0.35.0`}}]}),{id:`old-jig`,state:`unsupported`,jigVersion:`0.26.0`,minimum:`0.29.0`,upgrade:null},{id:`new-jig`,state:`incompatible`,jigVersion:`1.0.0`,upgrade:null},{id:`busy`,state:`unreadable`,code:`locked`,upgrade:null},{id:`moved`,state:`unavailable`,reason:`missing`,upgrade:null}],J={mode:`copy`,from:`0.34.0`,to:`0.35.0`,outcome:`planned`,git:null,manualSteps:[],wouldStop:[]},Le=[{state:`reading`},{state:`planned`,plan:J},{state:`planned`,plan:{...J,manualSteps:[`Restart your agent sessions: the skills changed.`]}},{state:`planned`,plan:{...J,wouldStop:["the working tree has uncommitted changes; commit them, stash them (`git stash`), or finish the task they belong to, then run `jig upgrade` again"]}},{state:`refused`,message:`Jig refused the upgrade.`}],Y={level:`pr`,branch:`jig/upgrade-0.35.0`,commit:`279390b`,pushed:!0,pullRequest:`https://github.com/fapost-lab/jig-ui/pull/49`,merged:!1},X=e=>({state:`done`,at:`2026-10-27T08:00:00Z`,upgrade:{...J,outcome:`upgraded`,git:Y,...e}}),Re=[{state:`running`,since:new Date(Date.now()-12e4).toISOString(),to:`0.35.0`},X({git:{...Y,merged:!0}}),X({manualSteps:[`Restart your agent sessions: the skills changed.`]}),X({outcome:`staged`,git:{...Y,level:`none`,commit:null,pushed:!1,pullRequest:null}}),X({outcome:`nothing-to-do`,git:null}),{state:`refused`,at:`2026-10-27T08:00:00Z`,code:`upgrade-blocked`,reasons:[`task up-x is started and its branch task/up-x is checked out here (record .ai/runtime/working/up-x); the upgrade would take this checkout off it`]},{state:`refused`,at:`2026-10-27T08:00:00Z`,code:`failed`,reasons:[]}],Z=[{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}],Q=[{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:[{...Z[0],text:`package and release`,task:null}]},{phase:1,title:`Read-only UI`,done:1,total:5,filed:2,fog:1,items:Z}],queue:null},{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:[Z[3]]}],queue:{position:100,priority:1}},{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:[],queue:null},{id:`half-written`,title:null,missingFiles:[`spec.md`,`roadmap.md`],release:null,epic:null,progress:null,phases:[],queue:null}],$=[Q[1],{...Q[1],id:`refunds`,title:`Refunds`,queue:{position:200,priority:null}},{...Q[1],id:`exports`,title:`Exports`,queue:{position:300,priority:4}},{...Q[1],id:`payouts`,title:`Payouts`,queue:null}],ze={class:`flex gap-4`},Be={class:`w-24 text-muted-foreground`},Ve={class:`flex items-center gap-2`},He={class:`flex flex-wrap gap-2`},Ue={class:`flex max-w-sm flex-col gap-2`},We={class:`flex h-5 items-center gap-2`},Ge={class:`flex gap-2`},Ke={class:`flex flex-wrap items-center gap-6`},qe={class:`flex items-center gap-1`},Je={class:`flex flex-wrap items-center gap-6`},Ye={class:`flex items-center gap-1`},Xe={class:`grid grid-cols-2 gap-2 xl:grid-cols-3`},Ze={class:`flex h-[28rem] flex-col`},Qe={class:`flex h-40 flex-col`},$e={class:`flex h-[28rem] flex-col`},et={class:`flex h-64 flex-col`},tt={class:`flex h-64 flex-col`},nt=b({__name:`CanvasView`,setup(t){let o=[`default`,`secondary`,`outline`,`ghost`,`destructive`,`link`],re=[`xs`,`sm`,`default`,`lg`],_=[`icon-xs`,`icon-sm`,`icon`,`icon-lg`],v=[`default`,`secondary`,`outline`,`ghost`,`destructive`,`link`],y=[`attention`,`good`,`bad`,`quiet`];return(t,b)=>(p(),s(`div`,null,[b[14]||=c(`div`,{class:`border-b px-4 py-3`},[c(`h1`,{class:`font-semibold tracking-wider uppercase`},`Canvas`),c(`p`,{class:`text-muted-foreground`},`Every component of the page in every variant and state; the left panel light, the right dark.`)],-1),l(R,{title:`Tones`,note:`lib/tones.ts — the only colours with a meaning`},{default:F(()=>[(p(),s(n,null,e(y,e=>c(`p`,{key:e,class:N(k(ie)[e])},w(e)+` as a rule`,3)),64)),c(`p`,ze,[(p(),s(n,null,e(y,e=>c(`span`,{key:e,class:N(k(xe)[e])},w(e),3)),64))])]),_:1}),b[15]||=c(`h2`,{class:`border-b px-4 py-2 font-bold tracking-widest uppercase`},`components/ui`,-1),l(R,{title:`Button`,note:`variant × size; icon sizes; disabled`},{default:F(()=>[(p(),s(n,null,e(o,t=>c(`div`,{key:t,class:`flex flex-wrap items-center gap-2`},[c(`span`,Be,w(t),1),(p(),s(n,null,e(re,e=>l(k(f),{key:e,variant:t,size:e},{default:F(()=>[j(w(e),1)]),_:2},1032,[`variant`,`size`])),64)),l(k(f),{variant:t,disabled:``},{default:F(()=>[...b[0]||=[j(`disabled`,-1)]]),_:1},8,[`variant`])])),64)),c(`div`,Ve,[b[2]||=c(`span`,{class:`w-24 text-muted-foreground`},`icon`,-1),(p(),s(n,null,e(_,e=>l(k(f),{key:e,variant:`outline`,size:e,"aria-label":e},{default:F(()=>[l(k(ee))]),_:1},8,[`size`,`aria-label`])),64)),l(k(f),{variant:`outline`,size:`sm`},{default:F(()=>[l(k(ee),{"data-icon":`inline-start`}),b[1]||=j(`with icon`,-1)]),_:1})])]),_:1}),l(R,{title:`Badge`},{default:F(()=>[c(`div`,He,[(p(),s(n,null,e(v,e=>l(k(de),{key:e,variant:e},{default:F(()=>[j(w(e),1)]),_:2},1032,[`variant`])),64))])]),_:1}),l(R,{title:`Card`},{default:F(()=>[l(k(ne),{class:`max-w-sm`},{default:F(()=>[l(k(Ee),null,{default:F(()=>[l(k(De),null,{default:F(()=>[...b[3]||=[j(`Card title`,-1)]]),_:1}),l(k(Te),null,{default:F(()=>[...b[4]||=[j(`Card description, muted.`,-1)]]),_:1})]),_:1}),l(k(we),null,{default:F(()=>[...b[5]||=[j(`Card content.`,-1)]]),_:1})]),_:1})]),_:1}),l(R,{title:`Input and Label`,note:`empty, filled, invalid, disabled`},{default:F(()=>[c(`div`,Ue,[l(k(se),null,{default:F(()=>[...b[6]||=[j(`Project path`,-1)]]),_:1}),l(k(A),{placeholder:`/path/to/project`}),l(k(A),{"model-value":`/Users/me/project`}),l(k(A),{"model-value":`/not/a/project`,"aria-invalid":`true`}),l(k(A),{"model-value":`disabled`,disabled:``})])]),_:1}),l(R,{title:`Separator`},{default:F(()=>[l(k(L)),c(`div`,We,[b[7]||=j(`left`,-1),l(k(L),{orientation:`vertical`}),b[8]||=j(`right`,-1)])]),_:1}),l(R,{title:`ToggleGroup`,note:`the Board / List switch`},{default:F(()=>[l(k(he),{type:`single`,variant:`outline`,size:`sm`,"model-value":`board`,"aria-label":`View`},{default:F(()=>[l(k(C),{value:`board`},{default:F(()=>[...b[9]||=[j(`Board`,-1)]]),_:1}),l(k(C),{value:`list`},{default:F(()=>[...b[10]||=[j(`List`,-1)]]),_:1})]),_:1})]),_:1}),l(R,{title:`Toggle`,note:`the board's finished tasks: off, on`},{default:F(()=>[c(`div`,Ge,[l(k(P),{variant:`outline`,size:`sm`,"model-value":!1},{default:F(()=>[...b[11]||=[j(`3 finished tasks`,-1)]]),_:1}),l(k(P),{variant:`outline`,size:`sm`,"model-value":!0},{default:F(()=>[...b[12]||=[j(`3 finished tasks`,-1)]]),_:1})])]),_:1}),l(R,{title:`Collapsible`,note:`open and folded`},{default:F(()=>[(p(),s(n,null,e([!0,!1],e=>l(k(ae),{key:String(e),"default-open":e},{default:F(()=>[l(k(ve),{class:`w-full border-b bg-muted/50 px-3 py-1.5 text-left`},{default:F(()=>[j(`Trigger (`+w(e?`open`:`folded`)+`)`,1)]),_:2},1024),l(k(be),{class:`px-3 py-2`},{default:F(()=>[...b[13]||=[j(`Content`,-1)]]),_:1})]),_:2},1032,[`default-open`])),64))]),_:1}),b[16]||=c(`h2`,{class:`border-b px-4 py-2 font-bold tracking-widest uppercase`},`components`,-1),l(R,{title:`ProjectTabs`,note:`on the workspace; on a project; empty`},{default:F(()=>[l(u,{projects:k(q),version:`0.1.0`},{default:F(()=>[l(d)]),_:1},8,[`projects`]),l(u,{projects:k(q),current:`jig-ui`,version:`0.1.0`},null,8,[`projects`]),l(u,{projects:[]})]),_:1}),l(R,{title:`ThemeToggle`,note:`switches the whole page; the icon is the theme it switches to`},{default:F(()=>[c(`div`,null,[l(d)])]),_:1}),l(R,{title:`ProjectCard`,note:`ready with attention and stale housekeeping; nothing needs you; its Jig older than the machine's, Upgrade… offered; each state it cannot be read in`},{default:F(()=>[(p(!0),s(n,null,e(k(q),e=>(p(),g(ge,{key:e.id,project:e},null,8,[`project`]))),128))]),_:1}),l(R,{title:`UpgradePanel`,note:`on a card: reading the dry run; the plan, with steps by hand; a stop; a refusal; under way; merged, with steps by hand, staged, nothing to do; not started; failed`},{default:F(()=>[(p(!0),s(n,null,e(k(Le),(e,t)=>(p(),g(me,{key:`plan-${t}`,project:`legacy-shop`,run:null,plan:e},null,8,[`plan`]))),128)),(p(!0),s(n,null,e(k(Re),(e,t)=>(p(),g(me,{key:`run-${t}`,project:`legacy-shop`,run:e,plan:void 0},null,8,[`run`]))),128))]),_:1}),l(R,{title:`NewTaskForm`,note:`empty, on its way, the id taken`},{default:F(()=>[l(T,{busy:!1}),l(T,{busy:!0,task:`fix-login`,goal:`Log in with the token after a restart.`}),l(T,{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}),l(R,{title:`ConnectForm`,note:`idle, busy, refused`},{default:F(()=>[l(M,{busy:!1}),l(M,{busy:!0,"model-value":`/Users/me/project`}),l(M,{busy:!1,"model-value":`/Users/me/project/.claude/worktrees/x`,refusal:`That is a worktree. Connect the main checkout instead.`})]),_:1}),l(R,{title:`AttentionItem`,note:`no link; a pull request; another link`},{default:F(()=>[(p(!0),s(n,null,e(k(W),(e,t)=>(p(),g(ce,{key:t,item:e},null,8,[`item`]))),128))]),_:1}),l(R,{title:`ColumnHeader`,note:`each column, and one this page does not know`},{default:F(()=>[(p(!0),s(n,null,e(k(Pe),e=>(p(),g(ue,{id:e.id,key:e.id,title:e.title,count:e.count},null,8,[`id`,`title`,`count`]))),128))]),_:1}),l(R,{title:`QueueMenu`,note:`press to open: To top, Up, Down, To bottom, Priority, the pause — first queued, in the middle, not queued, last, paused under a Jig that cannot resume it (no pause to ask, Resume off), paused and resumable, a pause asked, while its change is on its way`},{default:F(()=>[c(`div`,Ke,[(p(!0),s(n,null,e([k(V)[0],k(V)[1],k(V)[3],k(V)[11],k(V)[9],k(V)[13],k(V)[12]],e=>(p(),s(`span`,{key:e.id,class:`flex items-center gap-1`},[j(w(e.id),1),l(i,{task:e,cards:k(V)},null,8,[`task`,`cards`])]))),128)),c(`span`,qe,[j(w(k(V)[1].id),1),l(i,{task:k(V)[1],cards:k(V),disabled:``},null,8,[`task`,`cards`])])])]),_:1}),l(R,{title:`EpicMenu`,note:`press to open: To top, Up, Down, To bottom, Priority — first queued (no Up), in the middle (Up and Down), last queued (no Down: the next one is not queued), not queued (Up joins the queued ones), while its change is on its way`},{default:F(()=>[c(`div`,Je,[(p(!0),s(n,null,e(k($),e=>(p(),s(`span`,{key:e.id,class:`flex items-center gap-1`},[j(w(e.id),1),l(O,{spec:e,specs:k($)},null,8,[`spec`,`specs`])]))),128)),c(`span`,Ye,[j(w(k($)[1].id),1),l(O,{spec:k($)[1],specs:k($),disabled:``},null,8,[`spec`,`specs`])])])]),_:1}),l(R,{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:F(()=>[c(`div`,Xe,[(p(!0),s(n,null,e(k(V),e=>(p(),g(m,{key:e.id,project:`jig-ui`,task:e,open:!1},null,8,[`task`]))),128)),l(m,{project:`jig-ui`,task:k(V)[1],open:!1,cards:k(V)},null,8,[`task`,`cards`]),l(m,{project:`jig-ui`,task:k(V)[0],open:!1,cards:k(V),busy:``},null,8,[`task`,`cards`]),l(m,{project:`jig-ui`,task:k(V)[3],open:!1,cards:k(V),changed:``},null,8,[`task`,`cards`]),l(m,{project:`jig-ui`,task:k(V)[2],open:!0,details:k(G)[0]},null,8,[`task`,`details`]),l(m,{project:`jig-ui`,task:k(V)[0],open:!0},null,8,[`task`]),l(m,{project:`jig-ui`,task:k(V)[1],open:!0,details:`unavailable`},null,8,[`task`])])]),_:1}),l(R,{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:F(()=>[c(`div`,null,[(p(!0),s(n,null,e(k(V),(e,t)=>(p(),g(S,{key:e.id,task:e,selected:t===2},null,8,[`task`,`selected`]))),128)),l(S,{task:k(V)[1],selected:!1,cards:k(V)},null,8,[`task`,`cards`]),l(S,{task:k(V)[0],selected:!1,cards:k(V),busy:``},null,8,[`task`,`cards`]),l(S,{task:k(V)[3],selected:!1,cards:k(V),changed:``},null,8,[`task`,`cards`])])]),_:1}),l(R,{title:`BoardColumns`,note:`cards in every column, one open with its details, two changed since a refused change; no live task`},{default:F(()=>[c(`div`,Ze,[l(I,{project:`jig-ui`,columns:k(H),selected:k(V)[2].id,details:k(G)[0],pending:k(V)[0].id,changed:[k(V)[1].id,k(V)[3].id]},null,8,[`columns`,`selected`,`details`,`pending`,`changed`])]),c(`div`,Qe,[l(I,{project:`jig-ui`,columns:k(U)},null,8,[`columns`])])]),_:1}),l(R,{title:`BoardList`,note:`a row picked with its details; nothing picked; no live task`},{default:F(()=>[c(`div`,$e,[l(D,{project:`jig-ui`,columns:k(H),selected:k(V)[2].id,details:k(G)[0]},null,8,[`columns`,`selected`,`details`])]),c(`div`,et,[l(D,{project:`jig-ui`,columns:k(H)},null,8,[`columns`])]),c(`div`,tt,[l(D,{project:`jig-ui`,columns:k(U)},null,8,[`columns`])])]),_:1}),l(R,{title:`FinishedList`,note:`closed with its pull request merged; abandoned; one without a goal`},{default:F(()=>[l(pe,{tasks:k(Ne),class:`border`},null,8,[`tasks`])]),_:1}),l(R,{title:`TaskFacts`,note:`everything a task can carry; a task not started`},{default:F(()=>[(p(!0),s(n,null,e(k(G),e=>(p(),g(ye,{key:e.id,details:e,class:`border-b pb-3`},null,8,[`details`]))),128))]),_:1}),l(R,{title:`TaskBrief`,note:`facts, actions and the link to the full page`},{default:F(()=>[l(r,{project:`jig-ui`,task:k(G)[1].id,details:k(G)[1]},null,8,[`task`,`details`])]),_:1}),l(R,{title:`ActionList`,note:`Jig's offers, one unknown id; none`},{default:F(()=>[l(h,{actions:k(Fe)},null,8,[`actions`]),l(h,{actions:[]})]),_:1}),l(R,{title:`CopyCommand`,note:`short; long enough to be cut`},{default:F(()=>[l(a,{command:`jig task pause design-system`}),l(a,{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}),l(R,{title:`ProgressBar`,note:`none, part, all, an empty roadmap`},{default:F(()=>[(p(),s(n,null,e([[0,4],[3,9],[5,5],[0,0]],([e,t])=>l(_e,{key:`${e}/${t}`,done:e,total:t},null,8,[`done`,`total`])),64))]),_:1}),l(R,{title:`SpecItemRow`,note:`done with a live task; open with dependencies; a task no longer live; no task; fog`},{default:F(()=>[c(`ul`,null,[(p(!0),s(n,null,e(k(Z),(e,t)=>(p(),g(fe,{key:t,item:e,project:`jig-ui`,live:t<2},null,8,[`item`,`live`]))),128))])]),_:1}),l(R,{title:`SpecCard`,note:`no epic, a finished phase folded; an open epic in the queue at P1, with its queue menu; a lost epic branch, not queued, with its change on its way; missing files`},{default:F(()=>[(p(!0),s(n,null,e(k(Q),e=>(p(),g(oe,{key:e.id,spec:e,project:`jig-ui`,live:[`design-system`,`specs-and-epics`],busy:e.id===`lost-epic`},te({_:2},[e.epic?.state===`open`?{name:`menu`,fn:F(()=>[l(O,{spec:e,specs:k(Q),disabled:e.id===`lost-epic`},null,8,[`spec`,`specs`,`disabled`])]),key:`0`}:void 0]),1032,[`spec`,`busy`]))),128))]),_:1})]))}});export{nt as default};
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 c,I as ee,J as l,K as te,L as u,M as d,N as f,O as ne,P as re,Q as p,R as ie,S as m,T as h,U as g,V as _,W as v,X as y,Y as b,Z as x,_ as S,a as C,at as w,b as ae,c as oe,d as T,et as E,f as se,g as D,h as O,i as ce,it as le,j as ue,k as de,l as fe,m as pe,n as me,nt as k,o as he,p as A,q as j,r as M,rt as N,s as P,t as ge,tt as F,u as _e,v as ve,w as ye,x as I,y as be,z as xe}from"./index-DVjQs1Wr.js";var Se=b({__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(e){let t=e,n=[`horizontal`,`vertical`];function r(e){return n.includes(e)}let i=_(()=>r(t.orientation)?t.orientation:`horizontal`),a=_(()=>i.value===`vertical`?t.orientation:void 0),o=_(()=>t.decorative?{role:`none`}:{"aria-orientation":a.value,role:`separator`});return(t,n)=>(p(),g(k(re),x({as:e.as,"as-child":e.asChild,"data-orientation":i.value},o.value),{default:F(()=>[E(t.$slots,`default`)]),_:3},16,[`as`,`as-child`,`data-orientation`]))}}),Ce=b({__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)=>(p(),g(Se,le(y(t)),{default:F(()=>[E(e.$slots,`default`)]),_:3},16))}}),we=b({__name:`CardContent`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let t=e;return(e,n)=>(p(),s(`div`,{"data-slot":`card-content`,class:N(k(o)(`px-4 group-data-[size=sm]/card:px-3`,t.class))},[E(e.$slots,`default`)],2))}}),Te=b({__name:`CardDescription`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let t=e;return(e,n)=>(p(),s(`div`,{"data-slot":`card-description`,class:N(k(o)(`text-xs/relaxed text-muted-foreground`,t.class))},[E(e.$slots,`default`)],2))}}),Ee=b({__name:`CardHeader`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let t=e;return(e,n)=>(p(),s(`div`,{"data-slot":`card-header`,class:N(k(o)(`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`,t.class))},[E(e.$slots,`default`)],2))}}),De=b({__name:`CardTitle`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let t=e;return(e,n)=>(p(),s(`div`,{"data-slot":`card-title`,class:N(k(o)(`cn-font-heading text-sm font-medium group-data-[size=sm]/card:text-sm`,t.class))},[E(e.$slots,`default`)],2))}}),L=b({__name:`Separator`,props:{orientation:{default:`horizontal`},decorative:{type:Boolean,default:!0},asChild:{type:Boolean},as:{},class:{type:[Boolean,null,String,Object,Array]}},setup(e){let n=e,r=t(n,`class`);return(e,t)=>(p(),g(k(Ce),x({"data-slot":`separator`},k(r),{class:k(o)(`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`]))}}),Oe=[`aria-label`],ke={class:`flex items-baseline gap-3 border-b bg-muted/50 px-4 py-2`},Ae={class:`font-semibold tracking-wider uppercase`},je={key:0,class:`text-muted-foreground`},Me={class:`grid grid-cols-1 divide-y lg:grid-cols-2 lg:divide-x lg:divide-y-0`},R=b({__name:`CanvasSection`,props:{title:{},note:{}},setup(t){return(r,i)=>(p(),s(`section`,{"aria-label":t.title,class:`border-b`},[c(`h2`,ke,[c(`span`,Ae,w(t.title),1),t.note?(p(),s(`span`,je,w(t.note),1)):v(``,!0)]),c(`div`,Me,[(p(),s(n,null,e([`light`,`dark`],e=>c(`div`,{key:e,class:N([e,`flex flex-col gap-4 bg-background p-4 text-foreground`])},[E(r.$slots,`default`)],2)),64))])],8,Oe))}}),z=(e,t)=>({id:e,class:`T2`,status:`active`,goal:null,paused:!1,pauseRequest:null,pauseRequestable:!0,resumable:!1,resumesTo:null,started:!0,gate:null,autopilot:null,pullRequest:null,priority:null,queue:null,...t}),B=(e,t)=>({url:`https://github.com/fapost-lab/jig-ui/pull/18`,state:e,ci:t,fetchedAt:`2026-10-18T09:40:00Z`}),V=[z(`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.`}),z(`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.`}),z(`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.`}),z(`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.`}),z(`task-details`,{status:`ready`,pullRequest:B(`open`,`green`),goal:`Open a task's details from its card.`}),z(`refusal-fixture`,{class:`T0`,status:`ready`,pullRequest:B(`open`,`red`),goal:`Replace the refusal text in task-list-states.json with one Jig prints.`}),z(`docs-board`,{class:`T1`,status:`ready`,pullRequest:B(`open`,`pending`),goal:`Describe the five columns on the projects page of the docs.`}),z(`live-updates`,{status:`ready`,pullRequest:B(`merged`,`none`),goal:`Follow connected projects without a reload.`}),z(`no-pr-yet`,{class:`T1`,status:`ready`,goal:`Ready, nothing pushed.`}),z(`jig-init-hook`,{class:`T3`,paused:!0,pauseRequestable:!1,goal:`Register a project in an installed Jig UI from jig init.`}),z(`file-backlog-task`,{class:null,started:!1,goal:`File a minimal backlog task from the page through jig task new.`}),z(`a-task-with-a-rather-long-identifier-that-wraps`,{goal:null}),z(`conflict-display`,{pauseRequest:{requestedAt:`2026-10-22T09:00:00Z`},pauseRequestable:!1,goal:`Show what changed when a write was based on a stale read.`}),z(`resume-from-page`,{class:`T3`,paused:!0,pauseRequestable:!1,resumable:!0,resumesTo:`in-progress`,goal:`A drop out of Paused, or the card menu, resumes the task through jig task resume.`})],H=[{id:`needs-you`,title:`Needs you`,cards:V.slice(0,3)},{id:`in-progress`,title:`In progress`,cards:[V[3],V[12],V[11]]},{id:`ready`,title:`Ready`,cards:V.slice(4,9)},{id:`backlog`,title:`Backlog`,cards:[V[10]]},{id:`paused`,title:`Paused`,cards:[V[9],V[13]]}],U=H.map(e=>({...e,cards:[]})),Ne=[{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}],Pe=[{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}],W=[{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,versions: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`,versions:null},{kind:`housekeeping`,subject:{type:`project`,id:null},tasks:[],title:`Housekeeping never ran`,detail:null,todo:`Run jig housekeeping.`,url:`https://example.com/docs`,versions:null}],Fe=[{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`}],Ie={state:`ready`,issue:null,baseBranch:`main`,createdAt:`2026-10-17`,updatedAt:`2026-10-18`,knowledgeConsolidated:!1,worktree:null,gateBy:null,run:null,housekeepingFlags:[],attention:[]},G=[{...Ie,...V[2],gate:`approved`,gateBy:`human`,branch:`task/pause-request`,worktree:{uncommitted:4},issue:`#42`,pullRequest:B(`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:[W[0]],actions:Fe,column:{id:`needs-you`,title:`Needs you`}},{...Ie,...V[10],branch:null,actions:[{id:`start`,command:`jig task start file-backlog-task`}],column:{id:`backlog`,title:`Backlog`}}],K=(e,t={})=>({id:e,state:`ready`,jigVersion:`0.29.0`,counts:{active:11,paused:1,ready:3},attention:[],housekeepingStale:!1,upgrade:null,...t}),q=[K(`jig-ui`,{attention:W,housekeepingStale:!0}),K(`nextjs-store`,{counts:{active:0,paused:0,ready:0}}),K(`legacy-shop`,{jigVersion:`0.34.0`,counts:{active:2,paused:0,ready:0},attention:[{kind:`upgrade-available`,subject:{type:`project`,id:null},tasks:[],title:`Jig in this project is older than on this machine`,detail:`this project runs 0.34.0; this machine has 0.35.0`,todo:`jig upgrade`,url:null,versions:{from:`0.34.0`,to:`0.35.0`}}]}),{id:`old-jig`,state:`unsupported`,jigVersion:`0.26.0`,minimum:`0.29.0`,upgrade:null},{id:`new-jig`,state:`incompatible`,jigVersion:`1.0.0`,upgrade:null},{id:`busy`,state:`unreadable`,code:`locked`,upgrade:null},{id:`moved`,state:`unavailable`,reason:`missing`,upgrade:null}],J={mode:`copy`,from:`0.34.0`,to:`0.35.0`,outcome:`planned`,git:null,manualSteps:[],wouldStop:[]},Le=[{state:`reading`},{state:`planned`,plan:J},{state:`planned`,plan:{...J,manualSteps:[`Restart your agent sessions: the skills changed.`]}},{state:`planned`,plan:{...J,wouldStop:["the working tree has uncommitted changes; commit them, stash them (`git stash`), or finish the task they belong to, then run `jig upgrade` again"]}},{state:`refused`,message:`Jig refused the upgrade.`}],Y={level:`pr`,branch:`jig/upgrade-0.35.0`,commit:`279390b`,pushed:!0,pullRequest:`https://github.com/fapost-lab/jig-ui/pull/49`,merged:!1},X=e=>({state:`done`,at:`2026-10-27T08:00:00Z`,upgrade:{...J,outcome:`upgraded`,git:Y,...e}}),Re=[{state:`running`,since:new Date(Date.now()-12e4).toISOString(),to:`0.35.0`},X({git:{...Y,merged:!0}}),X({manualSteps:[`Restart your agent sessions: the skills changed.`]}),X({outcome:`staged`,git:{...Y,level:`none`,commit:null,pushed:!1,pullRequest:null}}),X({outcome:`nothing-to-do`,git:null}),{state:`refused`,at:`2026-10-27T08:00:00Z`,code:`upgrade-blocked`,reasons:[`task up-x is started and its branch task/up-x is checked out here (record .ai/runtime/working/up-x); the upgrade would take this checkout off it`]},{state:`refused`,at:`2026-10-27T08:00:00Z`,code:`failed`,reasons:[]}],Z=[{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}],Q=[{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:[{...Z[0],text:`package and release`,task:null}]},{phase:1,title:`Read-only UI`,done:1,total:5,filed:2,fog:1,items:Z}],queue:null},{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:[Z[3]]}],queue:{position:100,priority:1}},{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:[],queue:null},{id:`half-written`,title:null,missingFiles:[`spec.md`,`roadmap.md`],release:null,epic:null,progress:null,phases:[],queue:null}],$=[Q[1],{...Q[1],id:`refunds`,title:`Refunds`,queue:{position:200,priority:null}},{...Q[1],id:`exports`,title:`Exports`,queue:{position:300,priority:4}},{...Q[1],id:`payouts`,title:`Payouts`,queue:null}],ze={class:`flex gap-4`},Be={class:`w-24 text-muted-foreground`},Ve={class:`flex items-center gap-2`},He={class:`flex flex-wrap gap-2`},Ue={class:`flex max-w-sm flex-col gap-2`},We={class:`flex h-5 items-center gap-2`},Ge={class:`flex gap-2`},Ke={class:`flex flex-wrap items-center gap-6`},qe={class:`flex items-center gap-1`},Je={class:`flex flex-wrap items-center gap-6`},Ye={class:`flex items-center gap-1`},Xe={class:`grid grid-cols-2 gap-2 xl:grid-cols-3`},Ze={class:`flex h-[28rem] flex-col`},Qe={class:`flex h-40 flex-col`},$e={class:`flex h-[28rem] flex-col`},et={class:`flex h-64 flex-col`},tt={class:`flex h-64 flex-col`},nt=b({__name:`CanvasView`,setup(t){let o=[`default`,`secondary`,`outline`,`ghost`,`destructive`,`link`],re=[`xs`,`sm`,`default`,`lg`],_=[`icon-xs`,`icon-sm`,`icon`,`icon-lg`],v=[`default`,`secondary`,`outline`,`ghost`,`destructive`,`link`],y=[`attention`,`good`,`bad`,`quiet`];return(t,b)=>(p(),s(`div`,null,[b[14]||=c(`div`,{class:`border-b px-4 py-3`},[c(`h1`,{class:`font-semibold tracking-wider uppercase`},`Canvas`),c(`p`,{class:`text-muted-foreground`},`Every component of the page in every variant and state; the left panel light, the right dark.`)],-1),l(R,{title:`Tones`,note:`lib/tones.ts — the only colours with a meaning`},{default:F(()=>[(p(),s(n,null,e(y,e=>c(`p`,{key:e,class:N(k(ie)[e])},w(e)+` as a rule`,3)),64)),c(`p`,ze,[(p(),s(n,null,e(y,e=>c(`span`,{key:e,class:N(k(xe)[e])},w(e),3)),64))])]),_:1}),b[15]||=c(`h2`,{class:`border-b px-4 py-2 font-bold tracking-widest uppercase`},`components/ui`,-1),l(R,{title:`Button`,note:`variant × size; icon sizes; disabled`},{default:F(()=>[(p(),s(n,null,e(o,t=>c(`div`,{key:t,class:`flex flex-wrap items-center gap-2`},[c(`span`,Be,w(t),1),(p(),s(n,null,e(re,e=>l(k(f),{key:e,variant:t,size:e},{default:F(()=>[j(w(e),1)]),_:2},1032,[`variant`,`size`])),64)),l(k(f),{variant:t,disabled:``},{default:F(()=>[...b[0]||=[j(`disabled`,-1)]]),_:1},8,[`variant`])])),64)),c(`div`,Ve,[b[2]||=c(`span`,{class:`w-24 text-muted-foreground`},`icon`,-1),(p(),s(n,null,e(_,e=>l(k(f),{key:e,variant:`outline`,size:e,"aria-label":e},{default:F(()=>[l(k(ee))]),_:1},8,[`size`,`aria-label`])),64)),l(k(f),{variant:`outline`,size:`sm`},{default:F(()=>[l(k(ee),{"data-icon":`inline-start`}),b[1]||=j(`with icon`,-1)]),_:1})])]),_:1}),l(R,{title:`Badge`},{default:F(()=>[c(`div`,He,[(p(),s(n,null,e(v,e=>l(k(de),{key:e,variant:e},{default:F(()=>[j(w(e),1)]),_:2},1032,[`variant`])),64))])]),_:1}),l(R,{title:`Card`},{default:F(()=>[l(k(ne),{class:`max-w-sm`},{default:F(()=>[l(k(Ee),null,{default:F(()=>[l(k(De),null,{default:F(()=>[...b[3]||=[j(`Card title`,-1)]]),_:1}),l(k(Te),null,{default:F(()=>[...b[4]||=[j(`Card description, muted.`,-1)]]),_:1})]),_:1}),l(k(we),null,{default:F(()=>[...b[5]||=[j(`Card content.`,-1)]]),_:1})]),_:1})]),_:1}),l(R,{title:`Input and Label`,note:`empty, filled, invalid, disabled`},{default:F(()=>[c(`div`,Ue,[l(k(se),null,{default:F(()=>[...b[6]||=[j(`Project path`,-1)]]),_:1}),l(k(A),{placeholder:`/path/to/project`}),l(k(A),{"model-value":`/Users/me/project`}),l(k(A),{"model-value":`/not/a/project`,"aria-invalid":`true`}),l(k(A),{"model-value":`disabled`,disabled:``})])]),_:1}),l(R,{title:`Separator`},{default:F(()=>[l(k(L)),c(`div`,We,[b[7]||=j(`left`,-1),l(k(L),{orientation:`vertical`}),b[8]||=j(`right`,-1)])]),_:1}),l(R,{title:`ToggleGroup`,note:`the Board / List switch`},{default:F(()=>[l(k(he),{type:`single`,variant:`outline`,size:`sm`,"model-value":`board`,"aria-label":`View`},{default:F(()=>[l(k(C),{value:`board`},{default:F(()=>[...b[9]||=[j(`Board`,-1)]]),_:1}),l(k(C),{value:`list`},{default:F(()=>[...b[10]||=[j(`List`,-1)]]),_:1})]),_:1})]),_:1}),l(R,{title:`Toggle`,note:`the board's finished tasks: off, on`},{default:F(()=>[c(`div`,Ge,[l(k(P),{variant:`outline`,size:`sm`,"model-value":!1},{default:F(()=>[...b[11]||=[j(`3 finished tasks`,-1)]]),_:1}),l(k(P),{variant:`outline`,size:`sm`,"model-value":!0},{default:F(()=>[...b[12]||=[j(`3 finished tasks`,-1)]]),_:1})])]),_:1}),l(R,{title:`Collapsible`,note:`open and folded`},{default:F(()=>[(p(),s(n,null,e([!0,!1],e=>l(k(ae),{key:String(e),"default-open":e},{default:F(()=>[l(k(ve),{class:`w-full border-b bg-muted/50 px-3 py-1.5 text-left`},{default:F(()=>[j(`Trigger (`+w(e?`open`:`folded`)+`)`,1)]),_:2},1024),l(k(be),{class:`px-3 py-2`},{default:F(()=>[...b[13]||=[j(`Content`,-1)]]),_:1})]),_:2},1032,[`default-open`])),64))]),_:1}),b[16]||=c(`h2`,{class:`border-b px-4 py-2 font-bold tracking-widest uppercase`},`components`,-1),l(R,{title:`ProjectTabs`,note:`on the workspace; on a project; empty`},{default:F(()=>[l(u,{projects:k(q),version:`0.1.0`},{default:F(()=>[l(d)]),_:1},8,[`projects`]),l(u,{projects:k(q),current:`jig-ui`,version:`0.1.0`},null,8,[`projects`]),l(u,{projects:[]})]),_:1}),l(R,{title:`ThemeToggle`,note:`switches the whole page; the icon is the theme it switches to`},{default:F(()=>[c(`div`,null,[l(d)])]),_:1}),l(R,{title:`ProjectCard`,note:`ready with attention and stale housekeeping; nothing needs you; its Jig older than the machine's, Upgrade… offered; each state it cannot be read in`},{default:F(()=>[(p(!0),s(n,null,e(k(q),e=>(p(),g(ge,{key:e.id,project:e},null,8,[`project`]))),128))]),_:1}),l(R,{title:`UpgradePanel`,note:`on a card: reading the dry run; the plan, with steps by hand; a stop; a refusal; under way; merged, with steps by hand, staged, nothing to do; not started; failed`},{default:F(()=>[(p(!0),s(n,null,e(k(Le),(e,t)=>(p(),g(me,{key:`plan-${t}`,project:`legacy-shop`,run:null,plan:e},null,8,[`plan`]))),128)),(p(!0),s(n,null,e(k(Re),(e,t)=>(p(),g(me,{key:`run-${t}`,project:`legacy-shop`,run:e,plan:void 0},null,8,[`run`]))),128))]),_:1}),l(R,{title:`NewTaskForm`,note:`empty, on its way, the id taken`},{default:F(()=>[l(T,{busy:!1}),l(T,{busy:!0,task:`fix-login`,goal:`Log in with the token after a restart.`}),l(T,{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}),l(R,{title:`ConnectForm`,note:`idle, busy, refused`},{default:F(()=>[l(M,{busy:!1}),l(M,{busy:!0,"model-value":`/Users/me/project`}),l(M,{busy:!1,"model-value":`/Users/me/project/.claude/worktrees/x`,refusal:`That is a worktree. Connect the main checkout instead.`})]),_:1}),l(R,{title:`AttentionItem`,note:`no link; a pull request; another link`},{default:F(()=>[(p(!0),s(n,null,e(k(W),(e,t)=>(p(),g(ce,{key:t,item:e},null,8,[`item`]))),128))]),_:1}),l(R,{title:`ColumnHeader`,note:`each column, and one this page does not know`},{default:F(()=>[(p(!0),s(n,null,e(k(Pe),e=>(p(),g(ue,{id:e.id,key:e.id,title:e.title,count:e.count},null,8,[`id`,`title`,`count`]))),128))]),_:1}),l(R,{title:`QueueMenu`,note:`press to open: To top, Up, Down, To bottom, Priority, the pause — first queued, in the middle, not queued, last, paused under a Jig that cannot resume it (no pause to ask, Resume off), paused and resumable, a pause asked, while its change is on its way`},{default:F(()=>[c(`div`,Ke,[(p(!0),s(n,null,e([k(V)[0],k(V)[1],k(V)[3],k(V)[11],k(V)[9],k(V)[13],k(V)[12]],e=>(p(),s(`span`,{key:e.id,class:`flex items-center gap-1`},[j(w(e.id),1),l(i,{task:e,cards:k(V)},null,8,[`task`,`cards`])]))),128)),c(`span`,qe,[j(w(k(V)[1].id),1),l(i,{task:k(V)[1],cards:k(V),disabled:``},null,8,[`task`,`cards`])])])]),_:1}),l(R,{title:`EpicMenu`,note:`press to open: To top, Up, Down, To bottom, Priority — first queued (no Up), in the middle (Up and Down), last queued (no Down: the next one is not queued), not queued (Up joins the queued ones), while its change is on its way`},{default:F(()=>[c(`div`,Je,[(p(!0),s(n,null,e(k($),e=>(p(),s(`span`,{key:e.id,class:`flex items-center gap-1`},[j(w(e.id),1),l(O,{spec:e,specs:k($)},null,8,[`spec`,`specs`])]))),128)),c(`span`,Ye,[j(w(k($)[1].id),1),l(O,{spec:k($)[1],specs:k($),disabled:``},null,8,[`spec`,`specs`])])])]),_:1}),l(R,{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:F(()=>[c(`div`,Xe,[(p(!0),s(n,null,e(k(V),e=>(p(),g(m,{key:e.id,project:`jig-ui`,task:e,open:!1},null,8,[`task`]))),128)),l(m,{project:`jig-ui`,task:k(V)[1],open:!1,cards:k(V)},null,8,[`task`,`cards`]),l(m,{project:`jig-ui`,task:k(V)[0],open:!1,cards:k(V),busy:``},null,8,[`task`,`cards`]),l(m,{project:`jig-ui`,task:k(V)[3],open:!1,cards:k(V),changed:``},null,8,[`task`,`cards`]),l(m,{project:`jig-ui`,task:k(V)[2],open:!0,details:k(G)[0]},null,8,[`task`,`details`]),l(m,{project:`jig-ui`,task:k(V)[0],open:!0},null,8,[`task`]),l(m,{project:`jig-ui`,task:k(V)[1],open:!0,details:`unavailable`},null,8,[`task`])])]),_:1}),l(R,{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:F(()=>[c(`div`,null,[(p(!0),s(n,null,e(k(V),(e,t)=>(p(),g(S,{key:e.id,task:e,selected:t===2},null,8,[`task`,`selected`]))),128)),l(S,{task:k(V)[1],selected:!1,cards:k(V)},null,8,[`task`,`cards`]),l(S,{task:k(V)[0],selected:!1,cards:k(V),busy:``},null,8,[`task`,`cards`]),l(S,{task:k(V)[3],selected:!1,cards:k(V),changed:``},null,8,[`task`,`cards`])])]),_:1}),l(R,{title:`BoardColumns`,note:`cards in every column, one open with its details, two changed since a refused change; no live task`},{default:F(()=>[c(`div`,Ze,[l(I,{project:`jig-ui`,columns:k(H),selected:k(V)[2].id,details:k(G)[0],pending:k(V)[0].id,changed:[k(V)[1].id,k(V)[3].id]},null,8,[`columns`,`selected`,`details`,`pending`,`changed`])]),c(`div`,Qe,[l(I,{project:`jig-ui`,columns:k(U)},null,8,[`columns`])])]),_:1}),l(R,{title:`BoardList`,note:`a row picked with its details; nothing picked; no live task`},{default:F(()=>[c(`div`,$e,[l(D,{project:`jig-ui`,columns:k(H),selected:k(V)[2].id,details:k(G)[0]},null,8,[`columns`,`selected`,`details`])]),c(`div`,et,[l(D,{project:`jig-ui`,columns:k(H)},null,8,[`columns`])]),c(`div`,tt,[l(D,{project:`jig-ui`,columns:k(U)},null,8,[`columns`])])]),_:1}),l(R,{title:`FinishedList`,note:`closed with its pull request merged; abandoned; one without a goal`},{default:F(()=>[l(pe,{tasks:k(Ne),class:`border`},null,8,[`tasks`])]),_:1}),l(R,{title:`TaskFacts`,note:`everything a task can carry; a task not started`},{default:F(()=>[(p(!0),s(n,null,e(k(G),e=>(p(),g(ye,{key:e.id,details:e,class:`border-b pb-3`},null,8,[`details`]))),128))]),_:1}),l(R,{title:`TaskBrief`,note:`facts, actions and the link to the full page`},{default:F(()=>[l(r,{project:`jig-ui`,task:k(G)[1].id,details:k(G)[1]},null,8,[`task`,`details`])]),_:1}),l(R,{title:`ActionList`,note:`Jig's offers, one unknown id; none`},{default:F(()=>[l(h,{actions:k(Fe)},null,8,[`actions`]),l(h,{actions:[]})]),_:1}),l(R,{title:`CopyCommand`,note:`short; long enough to be cut`},{default:F(()=>[l(a,{command:`jig task pause design-system`}),l(a,{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}),l(R,{title:`ProgressBar`,note:`none, part, all, an empty roadmap`},{default:F(()=>[(p(),s(n,null,e([[0,4],[3,9],[5,5],[0,0]],([e,t])=>l(_e,{key:`${e}/${t}`,done:e,total:t},null,8,[`done`,`total`])),64))]),_:1}),l(R,{title:`SpecItemRow`,note:`done with a live task; open with dependencies; a task no longer live; no task; fog`},{default:F(()=>[c(`ul`,null,[(p(!0),s(n,null,e(k(Z),(e,t)=>(p(),g(fe,{key:t,item:e,project:`jig-ui`,live:t<2},null,8,[`item`,`live`]))),128))])]),_:1}),l(R,{title:`SpecCard`,note:`no epic, a finished phase folded; an open epic in the queue at P1, with its queue menu; a lost epic branch, not queued, with its change on its way; missing files`},{default:F(()=>[(p(!0),s(n,null,e(k(Q),e=>(p(),g(oe,{key:e.id,spec:e,project:`jig-ui`,live:[`design-system`,`specs-and-epics`],busy:e.id===`lost-epic`},te({_:2},[e.epic?.state===`open`?{name:`menu`,fn:F(()=>[l(O,{spec:e,specs:k(Q),disabled:e.id===`lost-epic`},null,8,[`spec`,`specs`,`disabled`])]),key:`0`}:void 0]),1032,[`spec`,`busy`]))),128))]),_:1})]))}});export{nt as default};