@fapost/jig-ui 0.1.0-next.14 → 0.1.0-next.16

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/http/app.js CHANGED
@@ -60,6 +60,12 @@ export function createApp({ port, token, source, version = readPackageVersion(),
60
60
  return refuse(c, 404, 'not-found', 'This project has no live task with this id.');
61
61
  return c.json({ task: { state: 'ready', ...read.task, column: placeOf(read.task) } });
62
62
  });
63
+ app.get('/api/projects/:id/specs', async (c) => {
64
+ const read = await source.specs(c.req.param('id'));
65
+ if (read === undefined)
66
+ return refuse(c, 404, 'not-connected', 'No connected project has this id.');
67
+ return c.json({ specs: read });
68
+ });
63
69
  app.post('/api/projects', async (c) => {
64
70
  const body = await c.req.json().catch(() => undefined);
65
71
  const path = absolute(body?.path);
@@ -188,6 +188,59 @@ export async function readTasks(root, platform = process.platform) {
188
188
  throw new JigShapeError();
189
189
  return { revision, tasks: data.tasks.map(summary), hidden: data.hidden };
190
190
  }
191
+ const isCount = (v) => Number.isInteger(v) && v >= 0;
192
+ const isProgress = (v) => isObject(v) && isCount(v.done) && isCount(v.total) && isCount(v.filed) && isCount(v.fog);
193
+ const isItem = (v) => isObject(v) && isString(v.text) && typeof v.checked === 'boolean' && isNullableString(v.task) && typeof v.fog === 'boolean';
194
+ const isPhase = (v) => isObject(v) &&
195
+ isProgress(v) &&
196
+ (v.phase === null || isCount(v.phase)) &&
197
+ isNullableString(v.title) &&
198
+ Array.isArray(v.items) &&
199
+ v.items.every(isItem);
200
+ const isEpic = (v) => v === null ||
201
+ (isObject(v) &&
202
+ isString(v.branch) &&
203
+ (v.state === 'open' || v.state === 'finished') &&
204
+ typeof v.checked_out === 'boolean' &&
205
+ typeof v.missing === 'boolean' &&
206
+ isNullableString(v.ref));
207
+ const isSpecRow = (v) => isObject(v) &&
208
+ isString(v.id) &&
209
+ isNullableString(v.title) &&
210
+ Array.isArray(v.missing_files) &&
211
+ v.missing_files.every(isString) &&
212
+ isNullableString(v.release) &&
213
+ isEpic(v.epic) &&
214
+ (v.progress === null || isProgress(v.progress)) &&
215
+ Array.isArray(v.phases) &&
216
+ v.phases.every(isPhase);
217
+ const progress = ({ done, total, filed, fog }) => ({ done, total, filed, fog });
218
+ /** The project's specs (`jig spec list --json`), in Jig's order. */
219
+ export async function readSpecs(root, platform = process.platform) {
220
+ const { data } = await readJson(root, ['spec', 'list'], platform);
221
+ if (!Array.isArray(data.specs) || !data.specs.every(isSpecRow))
222
+ throw new JigShapeError();
223
+ return data.specs.map((s) => ({
224
+ id: s.id,
225
+ title: s.title,
226
+ missingFiles: [...s.missing_files],
227
+ release: s.release,
228
+ epic: s.epic && {
229
+ branch: s.epic.branch,
230
+ state: s.epic.state,
231
+ checkedOut: s.epic.checked_out,
232
+ missing: s.epic.missing,
233
+ fetched: s.epic.ref?.startsWith('refs/remotes/') === true,
234
+ },
235
+ progress: s.progress && progress(s.progress),
236
+ phases: s.phases.map((p) => ({
237
+ ...progress(p),
238
+ phase: p.phase,
239
+ title: p.title,
240
+ items: p.items.map(({ text, checked, task, fog }) => ({ text, checked, task, fog })),
241
+ })),
242
+ }));
243
+ }
191
244
  /** True when `version` is at least the minimum Jig UI reads. */
192
245
  export function supports(version, minimum = MIN_JIG_VERSION) {
193
246
  const a = version.split('.').map(Number);
@@ -1,5 +1,5 @@
1
1
  import { existsSync } from 'node:fs';
2
- import { JigRefusal, JigShapeError, jigVersion, MIN_JIG_VERSION, readStatus, readTasks, supports, watchDir, writtenByReads, } from '../jig/client.js';
2
+ import { JigRefusal, JigShapeError, jigVersion, MIN_JIG_VERSION, readSpecs, readStatus, readTasks, 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';
@@ -92,12 +92,12 @@ async function reading({ id, path }, read) {
92
92
  }
93
93
  async function readProject(project) {
94
94
  const read = await reading(project, async (root, jigVersion) => {
95
- const [status, list] = await Promise.all([readStatus(root), readTasks(root)]);
96
- return { status, list, jigVersion };
95
+ const [status, list, specs] = await Promise.all([readStatus(root), readTasks(root), readSpecs(root)]);
96
+ return { status, list, specs, jigVersion };
97
97
  });
98
98
  if (!('list' in read))
99
- return { view: read, tasks: read, details: [], revision: null };
100
- const { status, list, jigVersion } = read;
99
+ return { view: read, tasks: read, details: [], specs: read, revision: null };
100
+ const { status, list, specs, jigVersion } = read;
101
101
  // Jig's live list holds active and ready tasks only.
102
102
  const live = list.tasks.filter((t) => t.status === 'active' || t.status === 'ready');
103
103
  return {
@@ -111,6 +111,7 @@ async function readProject(project) {
111
111
  },
112
112
  tasks: { state: 'ready', tasks: live.map(card), finished: list.hidden },
113
113
  details: live.map((t) => details(t, status.attention)),
114
+ specs: { state: 'ready', specs },
114
115
  revision: list.revision,
115
116
  };
116
117
  }
@@ -125,6 +126,8 @@ export function localSource(configDir, { rescanMs = 60_000, quietMs, maxWaitMs }
125
126
  const entries = new Map();
126
127
  const listeners = new Set();
127
128
  let rescan;
129
+ /** Counts the times following started; a read that finishes after a stop belongs to none. */
130
+ let session = 0;
128
131
  const following = () => rescan !== undefined;
129
132
  const entryOf = (project) => {
130
133
  let entry = entries.get(project.id);
@@ -140,6 +143,7 @@ export function localSource(configDir, { rescanMs = 60_000, quietMs, maxWaitMs }
140
143
  }
141
144
  entry.held = false;
142
145
  entry.shown = read;
146
+ entry.readIn = following() ? session : undefined;
143
147
  }
144
148
  /** Reads the project again — one read at a time — and signals when what it shows changed. */
145
149
  function refresh(entry) {
@@ -166,7 +170,7 @@ export function localSource(configDir, { rescanMs = 60_000, quietMs, maxWaitMs }
166
170
  /** What the project shows: from the watch while following, read through Jig otherwise. */
167
171
  async function shown(project) {
168
172
  const entry = entryOf(project);
169
- if (!(following() && entry.shown))
173
+ if (!(following() && entry.shown && entry.readIn === session))
170
174
  await (entry.reading ?? refresh(entry));
171
175
  return entry.shown;
172
176
  }
@@ -203,6 +207,7 @@ export function localSource(configDir, { rescanMs = 60_000, quietMs, maxWaitMs }
203
207
  }
204
208
  }
205
209
  function start() {
210
+ session++;
206
211
  rescan = setInterval(() => void sweep(), rescanMs);
207
212
  rescan.unref();
208
213
  void sweep();
@@ -246,6 +251,10 @@ export function localSource(configDir, { rescanMs = 60_000, quietMs, maxWaitMs }
246
251
  return read.tasks;
247
252
  return { state: 'ready', task: read.details.find((t) => t.id === taskId) ?? null };
248
253
  },
254
+ async specs(id) {
255
+ const project = (await readProjects(configDir)).find((p) => p.id === id);
256
+ return project && (await shown(project)).specs;
257
+ },
249
258
  async disconnect(id) {
250
259
  if (!(await removeProject(configDir, id)))
251
260
  return false;
@@ -21,11 +21,17 @@ export function watchTree(dir, { ignore, onChange, quietMs = 1_000, maxWaitMs =
21
21
  watcher?.close();
22
22
  watcher = undefined;
23
23
  };
24
- // Linux's recursive watch does not throw for a missing directory, macOS's does: ask first.
24
+ // Node 24.16-24.21 on Linux do not throw for a missing directory (24.0-24.15, 24.22 and macOS do): ask first.
25
25
  if (!existsSync(dir))
26
26
  return { open: false, close };
27
27
  try {
28
28
  watcher = watch(dir, { recursive: true }, (_event, file) => {
29
+ // Linux keeps watching a removed directory's inode and never says it is gone: close, so the owner watches it again.
30
+ if (!existsSync(dir)) {
31
+ close();
32
+ onChange();
33
+ return;
34
+ }
29
35
  // Some systems name no file; that is still a change.
30
36
  if (file !== null && ignore(file.replaceAll('\\', '/')))
31
37
  return;
@@ -0,0 +1 @@
1
+ import{A as e,B as t,C as n,D as r,E as i,F as a,G as o,H as s,I as c,J as l,K as u,L as d,M as f,N as p,O as m,P as h,R as g,S as _,T as v,U as y,V as ee,W as b,X as x,Y as te,_ as ne,a as re,b as ie,c as ae,d as oe,f as S,g as C,h as w,i as T,j as se,k as E,l as D,m as ce,n as O,o as k,p as le,q as A,r as ue,s as de,t as fe,u as pe,v as j,w as M,x as N,y as P,z as F}from"./index-CYfTN3Dj.js";var I=t({__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 c=p(()=>r(t.orientation)?t.orientation:`horizontal`),l=p(()=>c.value===`vertical`?t.orientation:void 0),d=p(()=>t.decorative?{role:`none`}:{"aria-orientation":l.value,role:`separator`});return(t,n)=>(y(),a(A(i),s({as:e.as,"as-child":e.asChild,"data-orientation":c.value},d.value),{default:u(()=>[o(t.$slots,`default`)]),_:3},16,[`as`,`as-child`,`data-orientation`]))}}),L=t({__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)=>(y(),a(I,te(ee(t)),{default:u(()=>[o(e.$slots,`default`)]),_:3},16))}}),R=t({__name:`CardContent`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let t=e;return(e,n)=>(y(),d(`div`,{"data-slot":`card-content`,class:l(A(r)(`px-4 group-data-[size=sm]/card:px-3`,t.class))},[o(e.$slots,`default`)],2))}}),z=t({__name:`CardDescription`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let t=e;return(e,n)=>(y(),d(`div`,{"data-slot":`card-description`,class:l(A(r)(`text-xs/relaxed text-muted-foreground`,t.class))},[o(e.$slots,`default`)],2))}}),B=t({__name:`CardHeader`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let t=e;return(e,n)=>(y(),d(`div`,{"data-slot":`card-header`,class:l(A(r)(`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))},[o(e.$slots,`default`)],2))}}),me=t({__name:`CardTitle`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let t=e;return(e,n)=>(y(),d(`div`,{"data-slot":`card-title`,class:l(A(r)(`cn-font-heading text-sm font-medium group-data-[size=sm]/card:text-sm`,t.class))},[o(e.$slots,`default`)],2))}}),V=t({__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 t=e,n=S(t,`class`);return(e,i)=>(y(),a(A(L),s({"data-slot":`separator`},A(n),{class:A(r)(`shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch`,t.class)}),null,16,[`class`]))}}),he=[`aria-label`],ge={class:`flex items-baseline gap-3 border-b bg-muted/50 px-4 py-2`},_e={class:`font-semibold tracking-wider uppercase`},ve={key:0,class:`text-muted-foreground`},ye={class:`grid grid-cols-1 divide-y lg:grid-cols-2 lg:divide-x lg:divide-y-0`},H=t({__name:`CanvasSection`,props:{title:{},note:{}},setup(e){return(t,n)=>(y(),d(`section`,{"aria-label":e.title,class:`border-b`},[h(`h2`,ge,[h(`span`,_e,x(e.title),1),e.note?(y(),d(`span`,ve,x(e.note),1)):c(``,!0)]),h(`div`,ye,[(y(),d(f,null,b([`light`,`dark`],e=>h(`div`,{key:e,class:l([e,`flex flex-col gap-4 bg-background p-4 text-foreground`])},[o(t.$slots,`default`)],2)),64))])],8,he))}}),U=(e,t)=>({id:e,class:`T2`,status:`active`,goal:null,paused:!1,started:!0,gate:null,autopilot:null,pullRequest:null,...t}),W=(e,t)=>({url:`https://github.com/fapost-lab/jig-ui/pull/18`,state:e,ci:t,fetchedAt:`2026-10-18T09:40:00Z`}),G=[U(`design-system`,{class:`T3`,gate:`waiting`,goal:`shadcn-vue on Tailwind v4 with light and dark themes; #/canvas with every component in every variant; the three screens rebuilt on it.`}),U(`registry-schema`,{gate:`changed`,goal:`Version the registry file so a newer Jig UI can read an older one.`}),U(`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.`}),U(`specs-and-epics`,{autopilot:`on`,goal:`Show each spec with its phases, progress and epic branch beside the board, from Jig's spec list.`}),U(`task-details`,{status:`ready`,pullRequest:W(`open`,`green`),goal:`Open a task's details from its card.`}),U(`refusal-fixture`,{class:`T0`,status:`ready`,pullRequest:W(`open`,`red`),goal:`Replace the refusal text in task-list-states.json with one Jig prints.`}),U(`docs-board`,{class:`T1`,status:`ready`,pullRequest:W(`open`,`pending`),goal:`Describe the five columns on the projects page of the docs.`}),U(`live-updates`,{status:`ready`,pullRequest:W(`merged`,`none`),goal:`Follow connected projects without a reload.`}),U(`no-pr-yet`,{class:`T1`,status:`ready`,goal:`Ready, nothing pushed.`}),U(`jig-init-hook`,{class:`T3`,paused:!0,goal:`Register a project in an installed Jig UI from jig init.`}),U(`file-backlog-task`,{class:null,started:!1,goal:`File a minimal backlog task from the page through jig task new.`}),U(`a-task-with-a-rather-long-identifier-that-wraps`,{goal:null})],be=[{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}],K=[{kind:`gate`,subject:{type:`task`,id:`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`},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},title:`Housekeeping never ran`,detail:null,todo:`Run jig housekeeping.`,url:`https://example.com/docs`}],q=[{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:`something-new`,command:`jig task something-new design-system`}],J={state:`ready`,issue:null,baseBranch:`main`,createdAt:`2026-10-17`,updatedAt:`2026-10-18`,knowledgeConsolidated:!1,worktree:null,gateBy:null,run:null,housekeepingFlags:[],attention:[]},Y=[{...J,...G[2],gate:`approved`,gateBy:`human`,branch:`task/pause-request`,worktree:{uncommitted:4},issue:`#42`,pullRequest:W(`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:[K[0]],actions:q,column:{id:`needs-you`,title:`Needs you`}},{...J,...G[10],branch:null,actions:[{id:`start`,command:`jig task start file-backlog-task`}],column:{id:`backlog`,title:`Backlog`}}],X=(e,t={})=>({id:e,state:`ready`,jigVersion:`0.29.0`,counts:{active:11,paused:1,ready:3},attention:[],housekeepingStale:!1,...t}),Z=[X(`jig-ui`,{attention:K,housekeepingStale:!0}),X(`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`}],Q=[{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}],xe=[{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:[{...Q[0],text:`package and release`,task:null}]},{phase:1,title:`Read-only UI`,done:1,total:5,filed:2,fog:1,items:Q}]},{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:[Q[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:[]}],Se={class:`flex gap-4`},Ce={class:`w-24 text-muted-foreground`},$={class:`flex items-center gap-2`},we={class:`flex flex-wrap gap-2`},Te={class:`flex max-w-sm flex-col gap-2`},Ee={class:`flex h-5 items-center gap-2`},De={class:`grid grid-cols-2 gap-2 xl:grid-cols-3`},Oe=t({__name:`CanvasView`,setup(t){let r=[`default`,`secondary`,`outline`,`ghost`,`destructive`,`link`],i=[`xs`,`sm`,`default`,`lg`],o=[`icon-xs`,`icon-sm`,`icon`,`icon-lg`],s=[`default`,`secondary`,`outline`,`ghost`,`destructive`,`link`],c=[`attention`,`good`,`bad`,`quiet`];return(t,p)=>(y(),d(`div`,null,[p[12]||=h(`div`,{class:`border-b px-4 py-3`},[h(`h1`,{class:`font-semibold tracking-wider uppercase`},`Canvas`),h(`p`,{class:`text-muted-foreground`},`Every component of the page in every variant and state; the left panel light, the right dark.`)],-1),F(H,{title:`Tones`,note:`lib/tones.ts — the only colours with a meaning`},{default:u(()=>[(y(),d(f,null,b(c,t=>h(`p`,{key:t,class:l(A(e)[t])},x(t)+` as a rule`,3)),64)),h(`p`,Se,[(y(),d(f,null,b(c,e=>h(`span`,{key:e,class:l(A(se)[e])},x(e),3)),64))])]),_:1}),p[13]||=h(`h2`,{class:`border-b px-4 py-2 font-bold tracking-widest uppercase`},`components/ui`,-1),F(H,{title:`Button`,note:`variant × size; icon sizes; disabled`},{default:u(()=>[(y(),d(f,null,b(r,e=>h(`div`,{key:e,class:`flex flex-wrap items-center gap-2`},[h(`span`,Ce,x(e),1),(y(),d(f,null,b(i,t=>F(A(v),{key:t,variant:e,size:t},{default:u(()=>[g(x(t),1)]),_:2},1032,[`variant`,`size`])),64)),F(A(v),{variant:e,disabled:``},{default:u(()=>[...p[0]||=[g(`disabled`,-1)]]),_:1},8,[`variant`])])),64)),h(`div`,$,[p[2]||=h(`span`,{class:`w-24 text-muted-foreground`},`icon`,-1),(y(),d(f,null,b(o,e=>F(A(v),{key:e,variant:`outline`,size:e,"aria-label":e},{default:u(()=>[F(A(m))]),_:1},8,[`size`,`aria-label`])),64)),F(A(v),{variant:`outline`,size:`sm`},{default:u(()=>[F(A(m),{"data-icon":`inline-start`}),p[1]||=g(`with icon`,-1)]),_:1})])]),_:1}),F(H,{title:`Badge`},{default:u(()=>[h(`div`,we,[(y(),d(f,null,b(s,e=>F(A(oe),{key:e,variant:e},{default:u(()=>[g(x(e),1)]),_:2},1032,[`variant`])),64))])]),_:1}),F(H,{title:`Card`},{default:u(()=>[F(A(pe),{class:`max-w-sm`},{default:u(()=>[F(A(B),null,{default:u(()=>[F(A(me),null,{default:u(()=>[...p[3]||=[g(`Card title`,-1)]]),_:1}),F(A(z),null,{default:u(()=>[...p[4]||=[g(`Card description, muted.`,-1)]]),_:1})]),_:1}),F(A(R),null,{default:u(()=>[...p[5]||=[g(`Card content.`,-1)]]),_:1})]),_:1})]),_:1}),F(H,{title:`Input and Label`,note:`empty, filled, invalid, disabled`},{default:u(()=>[h(`div`,Te,[F(A(ue),null,{default:u(()=>[...p[6]||=[g(`Project path`,-1)]]),_:1}),F(A(T),{placeholder:`/path/to/project`}),F(A(T),{"model-value":`/Users/me/project`}),F(A(T),{"model-value":`/not/a/project`,"aria-invalid":`true`}),F(A(T),{"model-value":`disabled`,disabled:``})])]),_:1}),F(H,{title:`Separator`},{default:u(()=>[F(A(V)),h(`div`,Ee,[p[7]||=g(`left`,-1),F(A(V),{orientation:`vertical`}),p[8]||=g(`right`,-1)])]),_:1}),F(H,{title:`ToggleGroup`,note:`the Board / List switch`},{default:u(()=>[F(A(de),{type:`single`,variant:`outline`,size:`sm`,"model-value":`board`,"aria-label":`View`},{default:u(()=>[F(A(k),{value:`board`},{default:u(()=>[...p[9]||=[g(`Board`,-1)]]),_:1}),F(A(k),{value:`list`},{default:u(()=>[...p[10]||=[g(`List`,-1)]]),_:1})]),_:1})]),_:1}),F(H,{title:`Collapsible`,note:`open and folded`},{default:u(()=>[(y(),d(f,null,b([!0,!1],e=>F(A(_),{key:String(e),"default-open":e},{default:u(()=>[F(A(ie),{class:`w-full border-b bg-muted/50 px-3 py-1.5 text-left`},{default:u(()=>[g(`Trigger (`+x(e?`open`:`folded`)+`)`,1)]),_:2},1024),F(A(N),{class:`px-3 py-2`},{default:u(()=>[...p[11]||=[g(`Content`,-1)]]),_:1})]),_:2},1032,[`default-open`])),64))]),_:1}),p[14]||=h(`h2`,{class:`border-b px-4 py-2 font-bold tracking-widest uppercase`},`components`,-1),F(H,{title:`ProjectTabs`,note:`on the workspace; on a project; empty`},{default:u(()=>[F(E,{projects:A(Z),version:`0.1.0`},{default:u(()=>[F(M)]),_:1},8,[`projects`]),F(E,{projects:A(Z),current:`jig-ui`,version:`0.1.0`},null,8,[`projects`]),F(E,{projects:[]})]),_:1}),F(H,{title:`ThemeToggle`,note:`switches the whole page; the icon is the theme it switches to`},{default:u(()=>[h(`div`,null,[F(M)])]),_:1}),F(H,{title:`ProjectCard`,note:`ready with attention and stale housekeeping; nothing needs you; each state it cannot be read in`},{default:u(()=>[(y(!0),d(f,null,b(A(Z),e=>(y(),a(fe,{key:e.id,project:e},null,8,[`project`]))),128))]),_:1}),F(H,{title:`ConnectForm`,note:`idle, busy, refused`},{default:u(()=>[F(O,{busy:!1}),F(O,{busy:!0,"model-value":`/Users/me/project`}),F(O,{busy:!1,"model-value":`/Users/me/project/.claude/worktrees/x`,refusal:`That is a worktree. Connect the main checkout instead.`})]),_:1}),F(H,{title:`AttentionItem`,note:`no link; a pull request; another link`},{default:u(()=>[(y(!0),d(f,null,b(A(K),(e,t)=>(y(),a(re,{key:t,item:e},null,8,[`item`]))),128))]),_:1}),F(H,{title:`ColumnHeader`,note:`each column, and one this page does not know`},{default:u(()=>[(y(!0),d(f,null,b(A(be),e=>(y(),a(n,{id:e.id,key:e.id,title:e.title,count:e.count},null,8,[`id`,`title`,`count`]))),128))]),_:1}),F(H,{title:`TaskCard`,note:`every note a card carries; open with details, while reading, unreadable`},{default:u(()=>[h(`div`,De,[(y(!0),d(f,null,b(A(G),e=>(y(),a(D,{key:e.id,project:`jig-ui`,task:e,open:!1},null,8,[`task`]))),128)),F(D,{project:`jig-ui`,task:A(G)[2],open:!0,details:A(Y)[0]},null,8,[`task`,`details`]),F(D,{project:`jig-ui`,task:A(G)[0],open:!0},null,8,[`task`]),F(D,{project:`jig-ui`,task:A(G)[1],open:!0,details:`unavailable`},null,8,[`task`])])]),_:1}),F(H,{title:`TaskRow`,note:`each note; selected`},{default:u(()=>[h(`div`,null,[(y(!0),d(f,null,b(A(G),(e,t)=>(y(),a(ae,{key:e.id,task:e,selected:t===2},null,8,[`task`,`selected`]))),128))])]),_:1}),F(H,{title:`TaskFacts`,note:`everything a task can carry; a task not started`},{default:u(()=>[(y(!0),d(f,null,b(A(Y),e=>(y(),a(ce,{key:e.id,details:e,class:`border-b pb-3`},null,8,[`details`]))),128))]),_:1}),F(H,{title:`TaskBrief`,note:`facts, actions and the link to the full page`},{default:u(()=>[F(le,{project:`jig-ui`,task:A(Y)[1].id,details:A(Y)[1]},null,8,[`task`,`details`])]),_:1}),F(H,{title:`ActionList`,note:`Jig's offers, one unknown id; none`},{default:u(()=>[F(w,{actions:A(q)},null,8,[`actions`]),F(w,{actions:[]})]),_:1}),F(H,{title:`CopyCommand`,note:`short; long enough to be cut`},{default:u(()=>[F(C,{command:`jig task pause design-system`}),F(C,{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}),F(H,{title:`ProgressBar`,note:`none, part, all, an empty roadmap`},{default:u(()=>[(y(),d(f,null,b([[0,4],[3,9],[5,5],[0,0]],([e,t])=>F(P,{key:`${e}/${t}`,done:e,total:t},null,8,[`done`,`total`])),64))]),_:1}),F(H,{title:`SpecItemRow`,note:`done with a live task; open with dependencies; a task no longer live; no task; fog`},{default:u(()=>[h(`ul`,null,[(y(!0),d(f,null,b(A(Q),(e,t)=>(y(),a(j,{key:t,item:e,project:`jig-ui`,live:t<2},null,8,[`item`,`live`]))),128))])]),_:1}),F(H,{title:`SpecCard`,note:`no epic, a finished phase folded; an open epic as of the last fetch; a lost epic branch; missing files`},{default:u(()=>[(y(!0),d(f,null,b(A(xe),e=>(y(),a(ne,{key:e.id,spec:e,project:`jig-ui`,live:[`design-system`,`specs-and-epics`]},null,8,[`spec`]))),128))]),_:1})]))}});export{Oe as default};