🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@illodev/workfile

Package Overview
Dependencies
Maintainers
1
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@illodev/workfile - npm Package Compare versions

Comparing version
0.8.1
to
0.9.0
+100
dist/src/modules/cards/changed.d.ts
/**
* The cards a branch touched, and what running their declared checks decided.
*
* T-0189, the `ci` tier of ADR-0016 — the only tier with a witness. `local` is a
* command that ran on the author's machine and is still self-reported; this is
* the same commands run somewhere the author does not control, recorded with the
* run that ran them.
*
* ## What it will and will not close
*
* A criterion bound to a command is machine-owned: `card ac --check` refuses it
* and only the runner writes it. A narrative criterion is not, and nothing here
* can judge one — "the recut demo video reads correctly" is not a thing a runner
* has an opinion about. So the rule is mechanical and it is the whole of the
* safety here:
*
* **A card is closed by CI only when every one of its criteria is bound.**
*
* A card with one narrative criterion gets its bound boxes written and stays
* open, which is not a failure — it is the run doing the part it can witness and
* declining the part it cannot. A card with none of its criteria bound is not
* touched at all: it declares no commands, so there is nothing to run.
*
* ## Why the close happens here and not in the job that pushes
*
* Every Workfile command loads the workspace, and loading the workspace
* `import()`s `project.config.mjs` from the checkout. On a pull request that is
* code the pull request wrote — see ADR-0019. So the job that runs card commands
* must hold nothing, and the job that holds a write token must not run this. The
* generated workflow splits them: this produces the finished card files and a
* report, and a second job with no repository code in it commits the result.
* `ci.ts` is where that split is written down.
*/
import type { VerifyRunReport } from "./runner.js";
/** What happened to one card in the run. */
export interface ChangedCardResult {
id: string;
file: string;
/**
* `verified` — every declared command passed.
* `failed` — at least one decided against a criterion it owns.
* `undecided` — a command reached no verdict: killed at the timeout, or
* never started because this machine has no such command.
* `skipped` — the card declares no commands, so there was nothing to run.
*/
outcome: "verified" | "failed" | "undecided" | "skipped";
/** Absent for `skipped`, which never reached the runner. */
report?: VerifyRunReport;
/** Whether every criterion is bound, which is what CI may close. */
fullyBound: boolean;
/** Set when this run moved the card to `done`. */
closed?: {
commit: string | null;
run: string | null;
};
/** Why a card that passed was nevertheless left open. */
heldOpen?: string;
}
export interface ChangedCardsReport {
/** The ref the diff was taken against. */
base: string;
/**
* False when git could not answer, in which case `cards` is empty and means
* nothing. A caller that reports this as "no cards to verify" is reporting
* the opposite of what happened.
*/
resolved: boolean;
/** Card files the branch touched, whether or not they declare commands. */
touched: string[];
cards: ChangedCardResult[];
/** True when nothing failed and nothing was left undecided. */
ok: boolean;
}
/**
* Run the declared checks of every card this branch touched.
*
* `close` is opt-in, because writing `verified` is what the caller may not be
* entitled to do — and because a run that only reports is the useful half on a
* fork, where the write can never land anyway.
*/
export declare function verifyChangedCards(workspace: any, { base, actor, close, run, commit, now }: {
base: string;
actor?: string | null;
/** Move a fully-bound card that passed to `done`, with `method: ci`. */
close?: boolean;
/** The run that witnessed it, recorded on the card. */
run?: string | null;
/**
* The commit the checks ran against.
*
* Absent, not null: `commitForClose` reads `undefined` as "resolve HEAD
* yourself" and any other value — including `null` — as the answer. So
* threading a `null` through from an unset CLI flag would record a card
* closed at no commit, which is exactly the field criterion 2 of T-0189
* asks for. Worth supplying explicitly all the same on a pull request,
* where HEAD is a merge commit that exists on no branch.
*/
commit?: string;
now?: string | number | Date;
}): Promise<ChangedCardsReport>;
/**
* The cards a branch touched, and what running their declared checks decided.
*
* T-0189, the `ci` tier of ADR-0016 — the only tier with a witness. `local` is a
* command that ran on the author's machine and is still self-reported; this is
* the same commands run somewhere the author does not control, recorded with the
* run that ran them.
*
* ## What it will and will not close
*
* A criterion bound to a command is machine-owned: `card ac --check` refuses it
* and only the runner writes it. A narrative criterion is not, and nothing here
* can judge one — "the recut demo video reads correctly" is not a thing a runner
* has an opinion about. So the rule is mechanical and it is the whole of the
* safety here:
*
* **A card is closed by CI only when every one of its criteria is bound.**
*
* A card with one narrative criterion gets its bound boxes written and stays
* open, which is not a failure — it is the run doing the part it can witness and
* declining the part it cannot. A card with none of its criteria bound is not
* touched at all: it declares no commands, so there is nothing to run.
*
* ## Why the close happens here and not in the job that pushes
*
* Every Workfile command loads the workspace, and loading the workspace
* `import()`s `project.config.mjs` from the checkout. On a pull request that is
* code the pull request wrote — see ADR-0019. So the job that runs card commands
* must hold nothing, and the job that holds a write token must not run this. The
* generated workflow splits them: this produces the finished card files and a
* report, and a second job with no repository code in it commits the result.
* `ci.ts` is where that split is written down.
*/
import { NotFoundError, ValidationError } from "../../core/errors.js";
import { normalizeRepoPath } from "../../core/glob.js";
import { ensureWritable } from "../../core/guards.js";
import { criterionOwners, parseAcceptance } from "./acceptance.js";
import { loadCards } from "./cards.js";
import { changedPaths } from "./git.js";
import { releaseCard } from "./mutations.js";
import { runCardVerification } from "./runner.js";
/**
* Card ids, from the paths a diff reported.
*
* Composed from the configured directories rather than parsed out of the
* filename. A card's name is derived from its title and `card renumber` exists,
* so a path is not an id — and the two places cards live are declared values a
* project may move. An archived card answers too: a branch that archived one
* touched it.
*
* Matched by full path rather than by basename, because the archive holds files
* whose names collide with live ones by design.
*/
function idsForPaths(workspace, cards, paths) {
const live = normalizeRepoPath(workspace.config.cards.path);
const archive = normalizeRepoPath(workspace.config.cards.archivePath);
const byPath = new Map();
for (const card of cards) {
const directory = card.archived ? archive : live;
byPath.set(`${directory}/${normalizeRepoPath(card.file)}`, {
id: card.id,
file: card.file
});
}
const found = [];
const seen = new Set();
for (const path of paths) {
const hit = byPath.get(normalizeRepoPath(path));
if (!hit || seen.has(hit.id))
continue;
seen.add(hit.id);
found.push(hit);
}
return found.sort((left, right) => left.id.localeCompare(right.id));
}
/**
* Run the declared checks of every card this branch touched.
*
* `close` is opt-in, because writing `verified` is what the caller may not be
* entitled to do — and because a run that only reports is the useful half on a
* fork, where the write can never land anyway.
*/
export async function verifyChangedCards(workspace, { base, actor = null, close = false, run = null, commit, now }) {
// Before the diff rather than after: a read-only workspace can record
// nothing these commands prove, and a run that spawns a test suite and then
// finds that out has already spent the expensive part.
ensureWritable(workspace);
if (!base) {
throw new ValidationError("CARD_VERIFY_NO_BASE", "A base ref is required to know which cards this branch touched. " +
"Pass `--base main`, or the pull request's base branch in CI.");
}
const paths = await changedPaths(workspace.root, base);
if (paths === null) {
// Reported, never treated as an empty diff. The two are opposite claims
// and only one of them is safe to act on.
return { base, resolved: false, touched: [], cards: [], ok: false };
}
// Archived cards come back from this too, which is wanted: a branch that
// archived a card touched it, and the diff will say so.
const { cards } = await loadCards(workspace);
const touched = idsForPaths(workspace, cards, paths);
const results = [];
for (const { id, file } of touched) {
const card = cards.find((candidate) => candidate.id === id);
const reading = parseAcceptance(card?.body || "");
const owners = criterionOwners(reading, card?.verify);
const fullyBound = reading.items.length > 0 && owners.size === reading.items.length;
if (!card?.verify?.length) {
results.push({ id, file, outcome: "skipped", fullyBound });
continue;
}
let report;
try {
report = await runCardVerification(workspace, id, { actor, now });
}
catch (error) {
// A card that declares entries the allowlist refuses, or whose
// bindings are stale, raises rather than returning a report. That is
// a fact about the card and belongs in the report as one, not as a
// crash that abandons every card after it in the list.
if (error instanceof ValidationError || error instanceof NotFoundError) {
results.push({
id,
file,
outcome: "failed",
fullyBound,
heldOpen: error.message
});
continue;
}
throw error;
}
const decided = report.entries.filter((entry) => entry.outcome === "passed" || entry.outcome === "failed");
const outcome = report.ok
? "verified"
: decided.length === report.entries.length
? "failed"
: "undecided";
const result = { id, file, outcome, report, fullyBound };
if (outcome === "verified" && close) {
if (card?.status === "done") {
// Already closed, so there is nothing to record and the door
// would refuse: a card that is done keeps the verification the
// write that closed it recorded. Re-running the checks on a
// branch that touches a closed card is ordinary — a second push
// to the same pull request does it — so this is a normal state
// and not a failure.
result.heldOpen = "already done; the run that closed it keeps the record";
}
else if (!fullyBound) {
// The honest half-answer: the boxes this run owns are written,
// and the ones a person judges are left to the person.
result.heldOpen =
`${reading.items.length - owners.size} of ${reading.items.length} ` +
"criteria are not bound to a command, so this run cannot say " +
"they are met";
}
else {
try {
await releaseCard(workspace, id, {
status: "done",
actor,
method: "ci",
run,
commit,
now
});
result.closed = { commit: commit ?? null, run };
}
catch (error) {
// A refusal is a fact about this card — an area whose policy
// does not accept `ci`, a transition its status does not
// allow — and it must not abandon every card after it in the
// list. One card's policy is not the run's verdict.
if (error instanceof ValidationError) {
result.heldOpen = error.message;
}
else {
throw error;
}
}
}
}
results.push(result);
}
return {
base,
resolved: true,
touched: touched.map((entry) => entry.file),
cards: results,
ok: results.every((entry) => entry.outcome === "verified" || entry.outcome === "skipped")
};
}
/**
* Whether a record's filename still describes the record, for every kind.
*
* `diagnoseCards` had this rule and nothing else did. Memory records, managed
* documents and changelog fragments all derive their filenames from their titles
* the same way, so retitling one through `memory patch` left a file named after a
* title the record no longer has and nothing reported it — found by doing it:
* LRN-0033 was retitled and sat under its old name with `doctor` reporting 0
* errors and 0 warnings (T-0223).
*
* Written here, in the layer that holds every kind at once, for the same reason
* duplicate identity is answered here: a per-module rule is four copies of one
* sentence, and the module that owns a kind cannot see the others. The card rule
* moved out of `diagnoseCards` rather than being left beside this one.
*
* ## What is deliberately out of scope, and why
*
* **An indexed document.** Its filename is somebody's `README.md`, outside the
* protocol directory and read-only through the protocol by definition. Renaming
* it would be this tool editing a repository's own tree to match a title it does
* not own.
*
* **A released changelog fragment.** The protocol already refuses to retitle one:
* `changelog patch` answers `CHANGE_FRAGMENT_RELEASED` and tells the caller to
* write a new fragment instead. So this exclusion is not the primary guard — it
* covers the fragment whose title was edited by hand, or edited before the
* release moved it, where reporting drift would ask a reader to churn a published
* release directory to fix a slug.
*
* **A release.** Its filename comes from the version, not the title, so the
* comparison this rule makes does not apply to it at all.
*
* **A record renamed by hand to something legitimate.** There is no way to tell
* that from drift, and nothing here tries: the rule reports what the title would
* produce today and `doctor --fix` renames only when asked. A project that keeps
* a deliberate name gets one warning per record and can accept it into the
* baseline, which is what the baseline is for.
*/
/**
* What this record's file would be called if it were created now, or `null` when
* the rule does not apply to it.
*
* The four derivations differ in their length cap — 50 for a card, 60 for a
* document, 70 for the other two — and that is load-bearing rather than
* historical accident to be tidied: unifying them would rename every existing
* record whose title crosses the new bound, in one sweep, on the next `--fix`.
*/
export declare function expectedRecordFileName(record: any): string | null;
export interface StaleFilename {
record: any;
module: string;
current: string;
expected: string;
}
/**
* Every record whose filename has drifted from its title.
*
* A file whose name does not even start with its id is skipped: that is a
* different fault with a different repair — `filename-mismatch` for a card, and
* renumbering rather than renaming fixes it.
*/
export declare function staleFilenames(records: any[]): StaleFilename[];
/** The diagnostic, worded once so all four kinds read alike. */
export declare function staleFilenameIssue(entry: StaleFilename): {
severity: "warning";
module: string;
code: string;
id: any;
file: any;
message: string;
details: {
current: string;
expected: string;
};
};
/**
* Whether a record's filename still describes the record, for every kind.
*
* `diagnoseCards` had this rule and nothing else did. Memory records, managed
* documents and changelog fragments all derive their filenames from their titles
* the same way, so retitling one through `memory patch` left a file named after a
* title the record no longer has and nothing reported it — found by doing it:
* LRN-0033 was retitled and sat under its old name with `doctor` reporting 0
* errors and 0 warnings (T-0223).
*
* Written here, in the layer that holds every kind at once, for the same reason
* duplicate identity is answered here: a per-module rule is four copies of one
* sentence, and the module that owns a kind cannot see the others. The card rule
* moved out of `diagnoseCards` rather than being left beside this one.
*
* ## What is deliberately out of scope, and why
*
* **An indexed document.** Its filename is somebody's `README.md`, outside the
* protocol directory and read-only through the protocol by definition. Renaming
* it would be this tool editing a repository's own tree to match a title it does
* not own.
*
* **A released changelog fragment.** The protocol already refuses to retitle one:
* `changelog patch` answers `CHANGE_FRAGMENT_RELEASED` and tells the caller to
* write a new fragment instead. So this exclusion is not the primary guard — it
* covers the fragment whose title was edited by hand, or edited before the
* release moved it, where reporting drift would ask a reader to churn a published
* release directory to fix a slug.
*
* **A release.** Its filename comes from the version, not the title, so the
* comparison this rule makes does not apply to it at all.
*
* **A record renamed by hand to something legitimate.** There is no way to tell
* that from drift, and nothing here tries: the rule reports what the title would
* produce today and `doctor --fix` renames only when asked. A project that keeps
* a deliberate name gets one warning per record and can accept it into the
* baseline, which is what the baseline is for.
*/
import { fragmentFileName } from "../changelog/changelog.js";
import { cardFileName } from "../cards/slug.js";
import { documentFileName } from "../docs/docs.js";
import { memoryFileName } from "../memory/memory.js";
/** The module each kind's findings are attributed to (T-0218). */
const MODULE_FOR_KIND = {
card: "cards",
memory: "memory",
doc: "docs",
change: "changelog"
};
/**
* What this record's file would be called if it were created now, or `null` when
* the rule does not apply to it.
*
* The four derivations differ in their length cap — 50 for a card, 60 for a
* document, 70 for the other two — and that is load-bearing rather than
* historical accident to be tidied: unifying them would rename every existing
* record whose title crosses the new bound, in one sweep, on the next `--fix`.
*/
export function expectedRecordFileName(record) {
if (!record?.id || !record?.title)
return null;
switch (record.kind) {
case "card":
return cardFileName(record.id, record.title);
case "memory":
return memoryFileName(record.id, record.title);
case "doc":
return record.managed ? documentFileName(record.id, record.title) : null;
case "change":
// Unreleased only. `released` is the flag the record carries; the
// path check is the belt to its braces, because a fragment moved
// into a release directory by hand is still published history.
return record.released || !/\/unreleased\//.test(String(record.path || ""))
? null
: fragmentFileName(record.id, record.title);
default:
return null;
}
}
/** The basename of a repository-relative path, without importing `node:path`. */
function basenameOf(path) {
const normalized = String(path || "").replace(/\\/g, "/");
return normalized.slice(normalized.lastIndexOf("/") + 1);
}
/**
* Every record whose filename has drifted from its title.
*
* A file whose name does not even start with its id is skipped: that is a
* different fault with a different repair — `filename-mismatch` for a card, and
* renumbering rather than renaming fixes it.
*/
export function staleFilenames(records) {
const stale = [];
for (const record of records || []) {
const expected = expectedRecordFileName(record);
if (!expected)
continue;
const current = basenameOf(record.path);
if (!current || current === expected)
continue;
if (!current.startsWith(`${record.id}-`))
continue;
stale.push({
record,
module: MODULE_FOR_KIND[record.kind] || "doctor",
current,
expected
});
}
return stale;
}
/** The diagnostic, worded once so all four kinds read alike. */
export function staleFilenameIssue(entry) {
return {
severity: "warning",
module: entry.module,
code: "filename-stale",
id: entry.record.id,
file: entry.record.path,
message: "Filename no longer matches the title; `doctor --fix` renames it to " +
entry.expected,
details: { current: entry.current, expected: entry.expected }
};
}
import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{Et as r,Tt as i,pt as a}from"./ui-primitives-DRENhlck.js";import{i as o,n as s,o as c,s as l,u}from"./theme-CcOVK72d.js";import{A as d,H as f,L as p,M as m,N as h,O as g,R as _,T as v,U as y,j as b,n as x,r as S,rt as C,t as w,tt as T}from"./index-CyDRMkuZ.js";var E=e(t(),1),D=n(),O=[];function k(e,t){let[n,r]=(0,E.useState)(t);(0,E.useEffect)(()=>{r(t)},[e.length,t]);let i=(0,E.useCallback)(()=>r(n=>Math.min(n+t,e.length)),[e.length,t]);return[n>=e.length?e:e.slice(0,n),n<e.length,i]}function A({onVisible:e,remaining:t}){let n=(0,E.useRef)(null);return(0,E.useEffect)(()=>{let t=n.current;if(!t)return;let r=new IntersectionObserver(t=>{t.some(e=>e.isIntersecting)&&e()},{rootMargin:`600px 0px`});return r.observe(t),()=>r.disconnect()},[e,t]),(0,D.jsxs)(`span`,{ref:n,className:`px-0.5 py-1 font-mono text-[11px] text-muted-foreground`,children:[`+`,t,` more`]})}function j({task:e,epicId:t,onOpen:n,onDragStart:r,onCarry:i,carrying:a}){let o=e.claimed_at?Date.parse(e.claimed_at.includes(`T`)?e.claimed_at:`${e.claimed_at}T00:00:00`):NaN,l=Number.isNaN(o)?null:Math.max(0,Math.floor((Date.now()-o)/864e5)),d=[t&&t!==e.id?`epic ${t}`:``,e.effort?`effort ${e.effort}`:``,e.claimed_by?`claimed by ${e.claimed_by}${l==null?``:` · ${l}d`}`:``].filter(Boolean);return(0,D.jsxs)(`article`,{className:u(`flex cursor-pointer flex-col gap-1.5 rounded-lg border bg-background px-3 py-2.5 shadow-xs outline-none transition-[color,border-color,box-shadow] hover:border-ring focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50`,a&&`border-ring ring-2 ring-ring`),tabIndex:0,draggable:!!r,"aria-grabbed":i?!!a:void 0,title:d.length?d.join(` · `):void 0,onClick:()=>n(e.id),onKeyDown:t=>{t.key===`Enter`?(t.preventDefault(),n(e.id)):t.key===` `&&i?(t.preventDefault(),i()):t.key===` `&&(t.preventDefault(),n(e.id))},onDragStart:r,children:[(0,D.jsxs)(`span`,{className:`flex items-center`,children:[(0,D.jsx)(`span`,{className:`font-mono text-[11px] text-foreground/70`,children:e.id}),(0,D.jsx)(`span`,{className:`flex-1`}),(0,D.jsx)(`span`,{className:`font-mono text-[10px] font-medium`,style:{color:s(e.priority)},children:e.priority})]}),(0,D.jsx)(`span`,{className:`text-[12.5px] leading-snug font-medium`,role:`heading`,"aria-level":3,children:e.title}),(0,D.jsxs)(`span`,{className:`flex items-center gap-1.5 font-mono text-[10px] text-muted-foreground`,children:[(0,D.jsx)(`span`,{children:e.area}),(0,D.jsx)(`span`,{children:`·`}),(0,D.jsx)(`span`,{children:e.type}),e.claimed_by?(0,D.jsxs)(`span`,{className:`ml-auto inline-flex min-w-0 items-center gap-[5px]`,style:{color:c(`doing`)},children:[(0,D.jsx)(`span`,{className:`size-[5px] flex-none rounded-full bg-current`,"aria-hidden":`true`}),(0,D.jsx)(`span`,{className:`max-w-[90px] truncate`,children:e.claimed_by})]}):null]}),Array.isArray(e.scope)&&e.scope.length?(0,D.jsxs)(`span`,{className:`mt-0.5 truncate border-t border-dashed pt-1.5 font-mono text-[10.5px] text-muted-foreground`,children:[`scope `,e.scope.join(` · `)]}):null]})}function M({status:e,cards:t,epicIds:n,collapsed:o,onToggleCollapsed:s,onOpen:p,onMove:_,onCarry:x,carryingId:S,isDropTarget:w,onDragEnterColumn:T,onDragLeaveColumn:E}){let O=v(),[M,N,P]=k(t,25),F=c(e),I={onDragOver:t=>{t.preventDefault(),t.dataTransfer.dropEffect=`move`,T?.(e)},onDragLeave:t=>{t.currentTarget.contains(t.relatedTarget)||E?.(e)},onDrop:t=>{t.preventDefault(),E?.(e);let n=t.dataTransfer.getData(`text/plain`);n&&_(n,e).catch(()=>void 0)}};return o?(0,D.jsxs)(y,{role:`region`,"aria-label":`${e}, ${t.length} cards, collapsed`,className:u(`relative w-11 flex-none gap-0 overflow-hidden rounded-lg py-0 shadow-xs`,w&&`border-primary`),...I,children:[(0,D.jsx)(f,{edge:`top`,color:F}),(0,D.jsxs)(`button`,{type:`button`,"aria-expanded":!1,"aria-label":`Expand the ${e} column`,title:`${e} · ${t.length}`,className:u(`flex h-full w-full cursor-pointer flex-col items-center gap-2.5 px-1 pt-4 pb-3 transition-colors hover:bg-accent/50`,w&&`bg-accent/50`),onClick:s,children:[(0,D.jsx)(r,{"aria-hidden":`true`,className:`size-3.5 shrink-0 text-muted-foreground`}),(0,D.jsx)(`span`,{className:`min-h-0 flex-1 truncate font-mono text-[11px] uppercase tracking-[0.06em] [writing-mode:vertical-rl]`,style:{color:F},children:e}),(0,D.jsx)(C,{variant:`secondary`,className:`h-5 shrink-0 rounded-md px-[7px] font-mono text-[11px] font-normal`,children:t.length})]})]}):(0,D.jsxs)(y,{role:`region`,"aria-label":`${e}, ${t.length} cards`,className:u(`relative w-[268px] flex-none gap-0 overflow-hidden rounded-lg py-0 shadow-xs`,w&&`border-primary`),...I,children:[(0,D.jsx)(f,{edge:`top`,color:F}),(0,D.jsxs)(`header`,{className:`flex flex-none items-center gap-2 px-3 pb-2.5 pt-4`,children:[(0,D.jsx)(`span`,{className:`flex-1 font-mono text-[11px] uppercase tracking-[0.06em]`,style:{color:F},children:e}),(0,D.jsx)(C,{variant:`secondary`,className:`h-5 rounded-md px-[7px] font-mono text-[11px] font-normal`,children:t.length}),s?(0,D.jsx)(l,{type:`button`,variant:`ghost`,size:`icon-xs`,"aria-expanded":!0,"aria-label":`Collapse the ${e} column`,title:`Collapse column`,className:`-mr-1 text-muted-foreground`,onClick:s,children:(0,D.jsx)(i,{"aria-hidden":`true`})}):null]}),(0,D.jsxs)(`div`,{className:u(`scroll-fade flex flex-1 flex-col gap-2 overflow-y-auto p-2.5`,w&&`bg-accent/50`),children:[t.length===0?(0,D.jsx)(g,{className:`flex-1 gap-2 rounded-lg border border-dashed p-4`,children:(0,D.jsxs)(b,{className:`gap-1`,children:[(0,D.jsx)(m,{variant:`icon`,className:`mb-0 size-8 [&_svg:not([class*='size-'])]:size-4`,children:(0,D.jsx)(a,{"aria-hidden":`true`})}),(0,D.jsx)(h,{className:`text-[12.5px] font-medium`,children:`No cards`}),(0,D.jsx)(d,{className:`text-[11.5px]`,children:`Nothing in this state.`})]})}):M.map(e=>(0,D.jsx)(j,{task:e,epicId:n.get(e.id),onOpen:p,onCarry:x&&!O?()=>x(e):void 0,carrying:S===e.id,onDragStart:O?void 0:t=>{t.dataTransfer.effectAllowed=`move`,t.dataTransfer.setData(`text/plain`,e.id)}},e.id)),N&&(0,D.jsx)(A,{onVisible:P,remaining:t.length-M.length})]})]})}function N({tasks:e,epicIds:t,showClosed:n,onOpen:r,onMove:i}){let[a,o]=(0,E.useState)(null),[s,c]=(0,E.useState)(null),[l,u]=(0,E.useState)(``),[d,f]=(0,E.useState)(()=>{try{let e=localStorage.getItem(`workfile-flow-collapsed`);return new Set(e?JSON.parse(e):[])}catch{return new Set}}),p=(0,E.useCallback)(e=>{f(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),localStorage.setItem(`workfile-flow-collapsed`,JSON.stringify([...n])),n})},[]),m=(0,E.useMemo)(()=>[`backlog`,`next`,`doing`,`review`,`blocked`,`deferred`,...n?[`done`,`discarded`]:[]],[n]),h=(0,E.useMemo)(()=>{let t=new Map;for(let n of e){let e=t.get(n.status);e?e.push(n):t.set(n.status,[n])}return t},[e]),g=e=>{o({id:e.id,status:e.status}),u(`${e.id} picked up from ${e.status}. Use the arrow keys to choose a column, space to drop, escape to cancel.`)},_=e=>{if(!a)return;let t=m.indexOf(a.status),n=m[Math.min(m.length-1,Math.max(0,t+e))];!n||n===a.status||(o({...a,status:n}),u(`${a.id} over ${n}.`))},v=async()=>{if(!a)return;let t=a;o(null);let n=e.find(e=>e.id===t.id);n&&n.status!==t.status?(await i(t.id,t.status),u(`${t.id} moved to ${t.status}.`)):u(`${t.id} put back.`)};return(0,D.jsxs)(`div`,{className:`flex min-h-0 flex-1 gap-3 overflow-x-auto p-3.5`,onDragEnd:()=>c(null),onKeyDown:e=>{a&&(e.key===`Escape`?(e.preventDefault(),o(null),u(`Move cancelled.`)):e.key===`ArrowRight`?(e.preventDefault(),_(1)):e.key===`ArrowLeft`?(e.preventDefault(),_(-1)):(e.key===` `||e.key===`Enter`)&&(e.preventDefault(),v()))},children:[(0,D.jsx)(`p`,{className:`sr-only`,role:`status`,"aria-live":`polite`,children:l}),m.map(e=>(0,D.jsx)(M,{status:e,cards:h.get(e)??O,epicIds:t,collapsed:d.has(e),onToggleCollapsed:()=>p(e),onOpen:r,onMove:i,onCarry:g,carryingId:a?.id??null,isDropTarget:a?.status===e||s===e,onDragEnterColumn:c,onDragLeaveColumn:e=>c(t=>t===e?null:t)},e))]})}function P({tasks:e,allTasks:t,epicIds:n,onOpen:r}){let i=(0,E.useMemo)(()=>new Map(t.map(e=>[e.id,e])),[t]),a=(0,E.useMemo)(()=>{let t=new Map;for(let r of e){let e=n.get(r.id)||(r.type===`epic`?r.id:`__none`);t.has(e)||t.set(e,[]),r.id!==e&&t.get(e)?.push(r)}return[...t].sort(([e],[t])=>e===`__none`?1:t===`__none`?-1:e.localeCompare(t,void 0,{numeric:!0}))},[n,e]);return a.length?(0,D.jsx)(`div`,{className:`flex-1 overflow-y-auto p-3.5`,children:(0,D.jsx)(`div`,{className:`flex flex-col gap-2.5`,children:a.map(([e,t])=>{let n=i.get(e),a=t.length,o=t.filter(e=>e.status===`done`||e.status===`discarded`).length,s=t.filter(e=>e.status===`doing`).length,l=a-o-s,d=e=>a?`${e/a*100}%`:`0%`,f=[{label:`${o} done`,color:c(`done`)},{label:`${s} doing`,color:c(`doing`)},{label:`${l} open`,color:null}],p=(0,D.jsxs)(D.Fragment,{children:[(0,D.jsxs)(`span`,{className:`flex min-w-0 items-center gap-2.5`,children:[(0,D.jsx)(`span`,{className:`font-mono text-[11.5px] text-foreground/70`,children:e===`__none`?`—`:e}),(0,D.jsx)(`span`,{className:`min-w-0 flex-1 text-sm font-semibold tracking-[-0.01em] text-pretty`,children:n?.title||`Without epic`}),n?(0,D.jsx)(`span`,{className:`font-mono text-[11px]`,style:{color:c(n.status)},children:n.status}):null,(0,D.jsxs)(`span`,{className:`font-mono text-[11.5px] text-muted-foreground`,children:[o,`/`,a]})]}),(0,D.jsx)(`span`,{className:`flex h-2 w-full overflow-hidden rounded-full bg-muted`,"aria-hidden":`true`,children:a>0?(0,D.jsxs)(D.Fragment,{children:[(0,D.jsx)(`span`,{className:`h-full`,style:{width:d(o),background:c(`done`)}}),(0,D.jsx)(`span`,{className:`h-full`,style:{width:d(s),background:c(`doing`)}})]}):null}),(0,D.jsxs)(`span`,{className:`flex flex-wrap items-center gap-3.5 font-mono text-[10.5px] text-muted-foreground`,children:[f.map(e=>(0,D.jsxs)(`span`,{className:`inline-flex items-center gap-[5px]`,children:[(0,D.jsx)(`span`,{className:u(`size-1.5 rounded-[2px]`,!e.color&&`bg-muted-foreground`),style:e.color?{background:e.color}:void 0,"aria-hidden":`true`}),e.label]},e.label)),(0,D.jsx)(`span`,{className:`ml-auto`,children:n?.area??``})]})]});return n?(0,D.jsx)(`button`,{type:`button`,className:`flex w-full cursor-pointer flex-col gap-2.5 rounded-xl border bg-card px-4 py-3.5 text-left text-card-foreground shadow-xs outline-none transition-[color,border-color,box-shadow] hover:border-ring focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50`,onClick:()=>r(e),children:p},e):(0,D.jsx)(y,{className:`gap-2.5 rounded-xl px-4 py-3.5 shadow-xs`,children:p},e)})})}):(0,D.jsx)(g,{className:`flex-1 p-6`,children:(0,D.jsxs)(b,{children:[(0,D.jsx)(h,{className:`text-sm`,children:`No epics`}),(0,D.jsx)(d,{className:`text-[11.5px]`,children:`No cards match the current filters.`})]})})}var F=300,I=30;function L({task:e,span:t,mode:n,epicId:r,pct:i,labelWidth:a,onOpen:o}){let s=c(e.status);return(0,D.jsxs)(`button`,{type:`button`,onClick:()=>o(e.id),title:`${S(e,n,t)} · ${e.status}${r&&r!==e.id?` · epic ${r}`:``}`,className:`flex w-full cursor-pointer items-center border-b bg-transparent p-0 text-left transition-colors hover:bg-muted`,children:[(0,D.jsxs)(`span`,{className:`flex min-w-0 items-center gap-2 border-r px-3.5`,style:{width:a,flex:`0 0 ${a}px`,height:`var(--row-h)`},children:[(0,D.jsx)(`span`,{className:`flex-none whitespace-nowrap font-mono text-[11px] text-foreground/70`,children:e.id}),(0,D.jsx)(`span`,{className:`min-w-0 truncate text-[12.5px]`,children:e.title})]}),(0,D.jsx)(`span`,{className:`relative block flex-1`,style:{height:`var(--row-h)`},children:t.point?(0,D.jsx)(`span`,{style:{position:`absolute`,top:`50%`,width:9,height:9,transform:`translate(-50%, -50%) rotate(45deg)`,borderRadius:2,background:s,display:`block`,left:`${i(t.from)}%`}}):(0,D.jsx)(`span`,{style:{position:`absolute`,top:`50%`,transform:`translateY(-50%)`,height:12,minWidth:6,borderRadius:3,background:s,display:`block`,left:`${i(t.from)}%`,width:`${Math.max(i(t.to)-i(t.from),.8)}%`}})})]})}function R({tasks:e,epicIds:t,axes:n={},mode:r,counts:i,onModeChange:a,onOpen:s}){let c=T()?168:F,u=c+460,[f,m]=(0,E.useState)(()=>{try{return localStorage.getItem(`workfile-timeline-group`)||`none`}catch{return`none`}}),v=(0,E.useCallback)(e=>{m(e);try{localStorage.setItem(`workfile-timeline-group`,e)}catch{}},[]),y=(0,E.useMemo)(()=>[`none`,`epic`,`area`,...Object.keys(n)],[n]),S=y.includes(f)?f:`none`,C=(0,E.useCallback)(e=>{if(S===`epic`)return t.get(e.id)||``;if(S===`area`)return e.area||``;let n=e[S];return typeof n==`string`?n:``},[t,S]),O=(0,E.useMemo)(()=>{let t=new Map;for(let n of e){let e=x(n,r);e&&t.set(n.id,e)}return t},[r,e]),k=(0,E.useMemo)(()=>{let t=(e,t)=>O.get(e.id).from-O.get(t.id).from||e.id.localeCompare(t.id),n=e.filter(e=>O.has(e.id)).sort(t);return S===`none`?n:[...n].sort((e,n)=>{let r=C(e),i=C(n);return!r==!i?r.localeCompare(i)||t(e,n):r?-1:1})},[C,S,O,e]),A=(0,E.useMemo)(()=>{if(S===`none`)return k.map(e=>({task:e,label:null}));let e=[],t=null;for(let n of k){let r=C(n);r!==t&&(t=r,e.push({task:null,label:r||`no ${S}`})),e.push({task:n,label:null})}return e},[C,S,k]),j=(0,E.useMemo)(()=>new Map(A.flatMap((e,t)=>e.task?[[e.task.id,t]]:[])),[A]),M=(0,E.useMemo)(()=>{let e=[];for(let t of k)for(let n of t.depends||[])j.has(n)&&e.push({from:n,to:t.id});return e},[j,k]),N=(0,E.useMemo)(()=>w(k.map(e=>O.get(e.id)),Date.now()),[k,O]),P=i[r===`plan`?`actual`:`plan`];return!k.length||!N?(0,D.jsx)(g,{className:`flex-1 p-6`,children:(0,D.jsxs)(b,{children:[(0,D.jsx)(h,{className:`text-sm`,children:r===`plan`?`Nothing scheduled`:`Nothing recorded`}),(0,D.jsx)(d,{className:`text-[11.5px]`,children:r===`plan`?`Add a start or due date to a card.`:`Cards record a trail as they are claimed and moved.`}),P>0?(0,D.jsx)(d,{className:`text-[11.5px]`,children:(0,D.jsx)(l,{variant:`outline`,size:`sm`,className:`mt-2 text-[12.5px] font-medium`,onClick:()=>a(r===`plan`?`actual`:`plan`),children:r===`plan`?`show what actually happened · ${P} cards`:`show the schedule · ${P} cards`})}):null]})}):(0,D.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col`,children:[(0,D.jsxs)(p,{gutter:`3.5`,className:`shrink-0 border-b bg-card py-2`,children:[(0,D.jsxs)(`span`,{className:`shrink-0 font-mono text-[11px] whitespace-nowrap text-muted-foreground`,children:[k.length,` `,r===`actual`?`recorded`:`scheduled`,` ·`,` `,M.length,` dependenc`,M.length===1?`y`:`ies`]}),(0,D.jsxs)(`span`,{className:`ml-auto flex shrink-0 items-center gap-2`,children:[(0,D.jsx)(_,{label:`dates`,value:r,allLabel:null,align:`end`,options:[{value:`plan`},{value:`actual`}],onChange:e=>a(e)}),(0,D.jsx)(_,{label:`group`,value:S,allLabel:null,align:`end`,options:y.map(e=>({value:e})),onChange:v})]})]}),(0,D.jsx)(`div`,{className:`min-h-0 flex-1 overflow-auto`,children:(0,D.jsxs)(`div`,{className:`relative min-h-full`,style:{minWidth:u},children:[(0,D.jsxs)(`div`,{"aria-hidden":`true`,className:`sticky top-0 z-[2] flex items-stretch border-b bg-card`,style:{height:I},children:[(0,D.jsx)(`span`,{className:`flex items-center border-r px-3.5 text-[10px] uppercase tracking-[0.08em] text-muted-foreground`,style:{width:c,flex:`0 0 ${c}px`},children:`card`}),(0,D.jsx)(`span`,{className:`relative flex-1`,children:N.ticks.map((e,t)=>(0,D.jsx)(`span`,{className:`text-[10px] uppercase tracking-[0.08em] text-muted-foreground`,style:{position:`absolute`,top:`50%`,transform:`translateY(-50%)`,left:`${e.left}%`,width:`${(N.ticks[t+1]?.left??100)-e.left}%`,overflow:`hidden`,paddingLeft:8,whiteSpace:`nowrap`},children:e.label},e.key))})]}),(0,D.jsxs)(`div`,{"aria-hidden":`true`,className:`pointer-events-none absolute`,style:{top:I,bottom:0,left:c,right:0},children:[N.ticks.map(e=>(0,D.jsx)(`span`,{className:`absolute inset-y-0 w-px bg-border`,style:{left:`${e.left}%`}},e.key)),N.today!=null&&(0,D.jsx)(`span`,{className:`absolute inset-y-0 w-px`,style:{background:o(`error`),opacity:.55,left:`${N.today}%`}})]}),(0,D.jsxs)(`div`,{className:`relative`,children:[M.length>0&&(0,D.jsx)(`svg`,{"aria-hidden":`true`,preserveAspectRatio:`none`,viewBox:`0 0 100 ${A.length}`,className:`pointer-events-none absolute top-0 h-full`,style:{left:c,width:`calc(100% - ${c}px)`},children:M.map(e=>{let t=O.get(e.from),n=O.get(e.to);if(!t||!n)return null;let r=N.pct(t.to),i=N.pct(n.from),a=j.get(e.from)+.5,s=j.get(e.to)+.5;return(0,D.jsx)(`path`,{d:`M ${r} ${a} C ${(r+i)/2} ${a}, ${(r+i)/2} ${s}, ${i} ${s}`,vectorEffect:`non-scaling-stroke`,style:i<r?{fill:`none`,stroke:o(`error`),strokeWidth:1.5,strokeDasharray:`3 2`}:{fill:`none`,stroke:`var(--muted-foreground)`,strokeWidth:1.5,opacity:.4}},`${e.from}-${e.to}`)})}),A.map((e,n)=>e.task?(0,D.jsx)(L,{task:e.task,span:O.get(e.task.id),mode:r,epicId:t.get(e.task.id),pct:N.pct,labelWidth:c,onOpen:s},e.task.id):(0,D.jsx)(`div`,{className:`border-b bg-muted/40`,children:(0,D.jsx)(`span`,{className:`flex items-center px-3.5 text-[10px] uppercase tracking-[0.08em] text-muted-foreground`,style:{height:`var(--row-h)`},children:e.label})},`group-${n}-${e.label}`))]})]})})]})}export{P as EpicsView,N as FlowBoard,R as TimelineView};
import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{U as r,_t as i,kt as a,nt as o,q as s,vt as c}from"./ui-primitives-DRENhlck.js";import{r as l,s as u,u as d}from"./theme-CcOVK72d.js";import{A as ee,B as te,C as ne,D as f,E as p,F as re,J as m,L as ie,N as ae,O as oe,S as se,T as ce,V as le,_ as h,at as g,b as _,c as ue,d as de,et as v,f as fe,g as pe,h as y,it as b,j as me,l as he,m as ge,p as _e,rt as x,tt as ve,v as ye,w as S,x as be,y as C,z as xe}from"./index-CyDRMkuZ.js";import{t as Se}from"./layout-QiuZ_k5v.js";var w=e(t(),1),T=n(),E=`doc-h`,Ce=[`current`,`draft`,`superseded`,`archived`],D=`text-[10px] font-medium tracking-[0.07em] uppercase text-muted-foreground`;function we({document:e,selected:t,onSelect:n}){return(0,T.jsx)(f,{asChild:!0,size:`sm`,className:d(`w-full cursor-pointer flex-col items-start gap-0.5 px-2 py-1.5 text-left hover:bg-accent`,t&&`bg-accent`),children:(0,T.jsxs)(`button`,{type:`button`,"aria-current":t?`true`:void 0,onClick:n,children:[(0,T.jsxs)(`span`,{className:`flex w-full items-center gap-1.5`,children:[(0,T.jsx)(`span`,{className:`flex-1 truncate text-xs font-medium`,children:e.title}),(0,T.jsx)(`span`,{className:d(`font-mono text-[10px]`,!e.managed&&`text-muted-foreground`),style:e.managed?{color:l(e.status)}:void 0,children:e.managed?e.status:`indexed`})]}),(0,T.jsx)(`span`,{className:`w-full truncate font-mono text-[10px] text-muted-foreground`,children:e.path})]})})}function Te({entries:e,activeId:t,onJump:n}){let r=Math.min(...e.map(e=>e.level));return(0,T.jsxs)(`aside`,{"aria-label":`Document outline`,className:`hidden w-[228px] shrink-0 overflow-y-auto border-l px-3 py-6.5 xl:block`,children:[(0,T.jsx)(`span`,{className:d(D,`px-2`),children:`on this page`}),(0,T.jsx)(`nav`,{className:`mt-2 flex flex-col gap-px`,children:e.map(e=>{let i=e.id===t;return(0,T.jsx)(`button`,{type:`button`,"aria-current":i?`true`:void 0,className:d(`cursor-pointer rounded-md px-2 py-1 text-left text-xs leading-snug transition-colors hover:bg-accent`,i?`bg-accent font-medium text-foreground`:`text-muted-foreground`),style:{paddingLeft:`${8+Math.min(e.level-r,3)*12}px`},onClick:()=>n(e.id),children:e.text},e.id)})})]})}function O({label:e,value:t}){return(0,T.jsxs)(h,{className:`w-auto min-w-[120px] gap-0.5 rounded-lg border bg-card px-3 py-2 shadow-xs`,children:[(0,T.jsx)(`span`,{className:D,children:e}),(0,T.jsx)(`span`,{className:`text-[13px] font-medium`,children:t})]})}function k({label:e,links:t,onOpen:n}){return t.length?(0,T.jsxs)(`section`,{className:`flex flex-col gap-1.5`,children:[(0,T.jsx)(`span`,{className:D,children:e}),t.map((e,t)=>{let r=!e.exists&&!e.title;return(0,T.jsx)(f,{asChild:!0,variant:`outline`,size:`sm`,className:d(`w-full cursor-pointer gap-2 px-2.5 py-2 text-left hover:bg-accent`,r&&`cursor-default opacity-55 hover:bg-transparent`),children:(0,T.jsxs)(`button`,{type:`button`,disabled:r,onClick:()=>n(e.id),children:[(0,T.jsx)(`span`,{className:`min-w-[78px] shrink-0 font-mono text-[11px] font-medium`,children:e.id}),(0,T.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-xs text-muted-foreground`,children:e.title||(e.exists===!1?`Missing record`:e.id)}),(e.relations??[e.relation]).filter(Boolean).map(e=>(0,T.jsx)(x,{variant:`secondary`,className:`font-mono text-[10px]`,children:e},e))]})},`${e.id}-${t}`)})]}):null}var A=(0,T.jsx)(`span`,{"aria-hidden":`true`,className:`text-muted-foreground`,children:`·`});function j({id:e,onSelect:t,onOpen:n}){let[r,i]=(0,w.useState)(null),[a,o]=(0,w.useState)(``),s=(0,w.useRef)(null),d=(0,w.useMemo)(()=>r?be(r.body||``,E):[],[r]);return(0,w.useEffect)(()=>{let t=!0;return i(null),o(``),m.record(e).then(e=>{t&&i(e.record)}).catch(e=>{t&&o(e.message)}),()=>{t=!1}},[e]),a?(0,T.jsx)(`div`,{className:`px-4 py-3 text-xs text-muted-foreground`,children:a}):r?(0,T.jsxs)(`div`,{ref:s,className:`flex min-h-0 flex-1 flex-col overflow-y-auto px-4 py-3`,children:[(0,T.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 font-mono text-[11px] text-muted-foreground`,children:[(0,T.jsx)(`span`,{children:r.id}),A,(0,T.jsx)(`span`,{children:r.documentKind}),A,(0,T.jsx)(`span`,{style:{color:l(r.status)},children:r.status}),A,(0,T.jsx)(`span`,{children:r.managed?`managed`:`indexed`}),(0,T.jsxs)(u,{type:`button`,variant:`ghost`,size:`sm`,className:`ml-auto px-2`,onClick:()=>n(e),children:[(0,T.jsx)(c,{"aria-hidden":`true`,className:`size-3`}),`Open in Docs`]})]}),(0,T.jsx)(`h2`,{className:`mt-1 text-sm font-medium`,children:r.title}),(0,T.jsx)(`p`,{className:`font-mono text-[11px] text-muted-foreground`,children:r.path}),r.freshness?.length?(0,T.jsx)(b,{className:`mt-3`,children:(0,T.jsx)(g,{children:r.freshness.map(e=>e.message).join(` `)})}):null,(0,T.jsxs)(`div`,{className:`mt-3 flex min-w-0 items-start gap-1`,children:[(0,T.jsx)(`div`,{className:`min-w-0 flex-1 [&>.typeset]:[--typeset-leading:1.6] [&>.typeset]:[--typeset-size:0.8125rem] [&>.typeset>:not(.typeset-scroll)]:max-w-[72ch]`,children:(0,T.jsx)(_,{source:r.body||`_This document is empty._`,onOpen:t,headingPrefix:E})}),(0,T.jsx)(ge,{entries:d,container:s})]})]}):(0,T.jsxs)(`div`,{className:`flex items-center gap-2 px-4 py-3 text-sm text-muted-foreground`,children:[(0,T.jsx)(p,{}),` Reading `,e,`…`]})}function M({selectedId:e,onSelect:t,onOpenCard:n,search:c,onSearchChange:f,filters:ge,onFiltersChange:x}){let j=ce(),[M,N]=(0,w.useState)([]),[P,F]=(0,w.useState)(!0),[I,Ee]=(0,w.useState)(``),{managedOnly:L}=ge,[R,De]=(0,w.useState)(!1),[z,B]=(0,w.useState)(null),[V,H]=(0,w.useState)(!1),[U,W]=(0,w.useState)(``),[G,Oe]=(0,w.useState)(null),[ke,K]=(0,w.useState)(0);le(e=>{te(e,`/docs/`,`docs/`)&&K(e=>e+1)}),(0,w.useEffect)(()=>{let e=!1;F(!0);let t=window.setTimeout(()=>{m.docs(c.trim()).then(t=>{e||(N(t.records),Ee(``))}).catch(t=>{e||Ee(t instanceof Error?t.message:String(t))}).finally(()=>{e||F(!1)})},c?180:0);return()=>{e=!0,window.clearTimeout(t)}},[c,ke]),(0,w.useEffect)(()=>{if(!z||G)return;let e=!1;return m.tasks().then(t=>{e||Oe(t.schema.docs)}).catch(()=>{}),()=>{e=!0}},[z,G]);let q=(0,w.useMemo)(()=>L?M.filter(e=>e.managed):M,[M,L]),J=(0,w.useMemo)(()=>{let e=q.filter(e=>e.managed),t=q.filter(e=>!e.managed);return[{key:`managed`,label:`.project/docs · managed`,docs:e},{key:`indexed`,label:`indexed · read only`,docs:t}].filter(e=>e.docs.length>0)},[q]),Ae=(0,w.useMemo)(()=>J.flatMap(e=>e.docs.map(e=>e.id)),[J]),je=ve(),Y=q.find(t=>t.id===e)||(je?void 0:q[0]),Me=(0,w.useRef)(null),[Ne,X]=(0,w.useState)(``),Z=(0,w.useMemo)(()=>Y&&!R?be(Y.body,E):[],[Y,R]),Q=Z.length>1;(0,w.useEffect)(()=>{X(``);let e=Me.current;if(!e||!Q)return;let t=new Map,n=new IntersectionObserver(e=>{for(let n of e)t.set(n.target.id,n.isIntersecting);let n=Z.find(e=>t.get(e.id));n&&X(n.id)},{root:e,rootMargin:`0px 0px -66% 0px`,threshold:0});for(let e of Z){let t=document.getElementById(e.id);t&&n.observe(t)}return()=>n.disconnect()},[Z,Q]);let Pe=e=>{document.getElementById(e)?.scrollIntoView({block:`start`,behavior:`smooth`}),X(e)},$=e=>{let r=M.find(t=>t.id===e);r?t(r.id):n(e)},Fe=(0,w.useMemo)(()=>{let e=new Set(G?.kinds??[]);for(let t of M)t.managed&&e.add(t.documentKind);return z&&e.add(z.kind),[...e].sort()},[G,M,z]),Ie=(0,w.useMemo)(()=>{let e=new Set(G?.statuses??Ce);for(let t of M)t.managed&&e.add(t.status);return z&&e.add(z.status),[...e].sort()},[G,M,z]);function Le(e){W(``),B({id:e.id,title:e.title,kind:e.documentKind,status:e.status,owners:(e.owners??[]).join(`, `),reviewed:e.reviewed??``})}async function Re(){if(!z)return;let e=M.find(e=>e.id===z.id);if(!e){W(`This document no longer exists in the workspace.`);return}let t=z.owners.split(`,`).map(e=>e.trim()).filter(Boolean),n={},r=z.title.trim();if(r&&r!==e.title&&(n.title=r),z.kind!==e.documentKind&&(n.kind=z.kind),z.status!==e.status&&(n.status=z.status),t.join(`
`)!==(e.owners??[]).join(`
`)&&(n.owners=t),(z.reviewed||``)!==(e.reviewed??``)&&(n.reviewed=z.reviewed||null),!Object.keys(n).length){B(null);return}H(!0),W(``);try{let t=await m.patchDocument(e.id,n,e.revision);N(n=>n.map(n=>n.id===e.id?t.record:n)),B(null)}catch(e){let t=e;t.code?.endsWith(`WRITE_CONFLICT`)?(K(e=>e+1),W(`The document changed on disk; the list was refreshed. Save again to apply your changes to the latest revision.`)):W(t.message||String(e))}finally{H(!1)}}return(0,T.jsxs)(`div`,{className:`flex min-h-0 flex-1`,children:[(0,T.jsxs)(`aside`,{"aria-label":`Documents`,className:d(`min-h-0 w-full shrink-0 flex-col border-r px-2 py-3 lg:flex lg:w-[290px]`,Y?`hidden`:`flex`),children:[(0,T.jsx)(ie,{className:`pb-2.5`,before:(0,T.jsx)(re,{scope:`records`,value:c,label:`Search documentation`,onChange:f}),children:(0,T.jsx)(xe,{label:`managed`,on:L,onLabel:`only`,offLabel:`all`,onChange:e=>x({managedOnly:e})})}),(0,T.jsx)(`div`,{"aria-busy":P||void 0,className:`min-h-0 flex-1 overflow-y-auto`,children:P?(0,T.jsxs)(`span`,{className:`flex items-center gap-2 px-2 py-1.5 font-mono text-[10.5px] text-muted-foreground`,children:[(0,T.jsx)(p,{className:`size-3`}),`Loading documents…`]}):I?(0,T.jsx)(b,{variant:`destructive`,className:`mt-1.5`,children:(0,T.jsx)(g,{children:I})}):J.length?J.map(e=>(0,T.jsxs)(`div`,{className:`flex flex-col gap-px pb-3.5`,children:[(0,T.jsxs)(`span`,{className:`flex items-center gap-2 px-2 py-1.5 font-mono text-[10.5px] text-muted-foreground`,children:[(0,T.jsx)(`span`,{className:`text-foreground/80`,children:e.label}),(0,T.jsx)(`span`,{children:e.docs.length})]}),e.docs.map(e=>(0,T.jsx)(we,{document:e,selected:Y?.id===e.id,onSelect:()=>t(e.id,Ae)},e.id))]},e.key)):(0,T.jsx)(oe,{className:`gap-2 p-4 md:p-4`,children:(0,T.jsxs)(me,{children:[(0,T.jsx)(ae,{className:`text-sm`,children:`No documents found.`}),(0,T.jsx)(ee,{className:`text-xs`,children:L?`Try another search, or include indexed files.`:`Try another search.`})]})})})]}),(0,T.jsx)(`section`,{ref:Me,className:d(`min-w-0 flex-1 overflow-y-auto px-6 py-6.5 sm:px-8.5`,Y?`block`:`hidden lg:block`),children:(0,T.jsx)(`div`,{className:Se,children:Y?(0,T.jsxs)(T.Fragment,{children:[(0,T.jsxs)(u,{type:`button`,variant:`ghost`,size:`sm`,className:`-ml-2 mb-2 lg:hidden`,onClick:()=>t(null),children:[(0,T.jsx)(a,{"aria-hidden":`true`}),`All documents`]}),(0,T.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 font-mono text-[11px] text-muted-foreground`,children:[(0,T.jsx)(`span`,{children:Y.id}),A,(0,T.jsx)(`span`,{children:Y.documentKind}),A,(0,T.jsx)(`span`,{style:{color:l(Y.status)},children:Y.status}),A,(0,T.jsx)(`span`,{children:Y.managed?`managed`:`indexed`}),(0,T.jsx)(`span`,{className:`flex-1`}),Y.managed?(0,T.jsxs)(T.Fragment,{children:[(0,T.jsxs)(u,{type:`button`,variant:`outline`,size:`sm`,disabled:j,title:j?S:void 0,onClick:()=>De(e=>!e),children:[R?(0,T.jsx)(i,{"aria-hidden":`true`}):(0,T.jsx)(o,{"aria-hidden":`true`}),R?`Preview`:`Edit`]}),(0,T.jsxs)(u,{type:`button`,variant:`outline`,size:`sm`,disabled:j,title:j?S:void 0,onClick:()=>Le(Y),children:[(0,T.jsx)(s,{"aria-hidden":`true`}),`Metadata`]})]}):null,(0,T.jsx)(ne,{noun:`document`})]}),(0,T.jsx)(`h2`,{className:`mt-3 mb-1.5 text-2xl font-semibold tracking-tight`,children:Y.title}),(0,T.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground [overflow-wrap:anywhere]`,children:Y.path}),(0,T.jsxs)(`div`,{className:`mt-4.5 flex flex-wrap gap-2`,children:[(0,T.jsx)(O,{label:`kind`,value:Y.documentKind}),(0,T.jsx)(O,{label:`status`,value:Y.status}),(0,T.jsx)(O,{label:`reviewed`,value:Y.reviewed||`—`}),(0,T.jsx)(O,{label:`owners`,value:Y.owners?.join(`, `)||`—`}),(0,T.jsx)(O,{label:`backlinks`,value:String(Y.incomingTotal??Y.incoming.length)}),Y.updated?(0,T.jsx)(O,{label:`updated`,value:Y.updated}):null]}),Y.freshness.length>0?(0,T.jsxs)(b,{role:`status`,className:`mt-4.5 max-w-2xl`,children:[(0,T.jsx)(r,{"aria-hidden":`true`,className:`text-sev-warning`}),(0,T.jsx)(g,{children:Y.freshness.map(e=>(0,T.jsx)(`span`,{children:e.message},`${e.code}-${e.message}`))})]}):null,(0,T.jsx)(`div`,{className:`mt-6.5`,children:R&&Y.managed?(0,T.jsx)(se,{value:Y.body,revision:Y.revision,onSave:async(e,t)=>{let n=await m.patchDocument(Y.id,{body:e},t);N(e=>e.map(e=>e.id===Y.id?n.record:e))}},Y.id):Y.body.trim()?(0,T.jsx)(_,{source:Y.body,headingPrefix:E,onOpen:$}):(0,T.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:Y.managed?`This document is empty. Use Edit to write its first version.`:`This file has no body to render.`})}),Y.outgoing.length||Y.incoming.length||Y.scope?.length?(0,T.jsxs)(`div`,{className:`mt-7 flex max-w-[70ch] flex-col gap-4.5`,children:[(0,T.jsx)(k,{label:`links to`,links:Y.outgoing,onOpen:$}),(0,T.jsx)(k,{label:(Y.incomingTotal??Y.incoming.length)>Y.incoming.length?`backlinks (${Y.incoming.length} of ${Y.incomingTotal})`:`backlinks`,links:Y.incoming,onOpen:$}),Y.scope?.length?(0,T.jsxs)(`section`,{className:`flex flex-col gap-1.5`,children:[(0,T.jsx)(`span`,{className:D,children:`scope`}),Y.scope.map(e=>(0,T.jsx)(`span`,{className:`font-mono text-[10.5px] text-muted-foreground [overflow-wrap:anywhere]`,children:e},e))]}):null]}):null]}):(0,T.jsx)(`div`,{className:`flex h-full items-center justify-center text-xs text-muted-foreground`,children:P?`Loading documents…`:`Select a document from the list to read it.`})})}),Q?(0,T.jsx)(Te,{entries:Z,activeId:Ne,onJump:Pe}):null,(0,T.jsx)(ue,{open:z!==null,onOpenChange:e=>{!e&&!V&&B(null)},children:(0,T.jsxs)(he,{"aria-describedby":void 0,children:[(0,T.jsx)(fe,{children:(0,T.jsx)(_e,{children:`Edit metadata${z?` — ${z.id}`:``}`})}),z?(0,T.jsxs)(ye,{className:`gap-4`,children:[(0,T.jsxs)(h,{children:[(0,T.jsx)(C,{htmlFor:`docs-meta-title`,children:`title`}),(0,T.jsx)(v,{id:`docs-meta-title`,value:z.title,onChange:e=>B({...z,title:e.target.value})})]}),(0,T.jsxs)(`div`,{className:`grid grid-cols-2 gap-2.5`,children:[(0,T.jsxs)(h,{className:`[&_[data-slot=native-select-wrapper]]:w-full`,children:[(0,T.jsx)(C,{htmlFor:`docs-meta-kind`,children:`kind`}),(0,T.jsx)(y,{id:`docs-meta-kind`,value:z.kind,onChange:e=>B({...z,kind:e.target.value}),children:Fe.map(e=>(0,T.jsx)(pe,{value:e,children:e},e))})]}),(0,T.jsxs)(h,{className:`[&_[data-slot=native-select-wrapper]]:w-full`,children:[(0,T.jsx)(C,{htmlFor:`docs-meta-status`,children:`status`}),(0,T.jsx)(y,{id:`docs-meta-status`,value:z.status,onChange:e=>B({...z,status:e.target.value}),children:Ie.map(e=>(0,T.jsx)(pe,{value:e,children:e},e))})]})]}),(0,T.jsxs)(`div`,{className:`grid grid-cols-2 gap-2.5`,children:[(0,T.jsxs)(h,{children:[(0,T.jsx)(C,{htmlFor:`docs-meta-owners`,children:`owners`}),(0,T.jsx)(v,{id:`docs-meta-owners`,value:z.owners,placeholder:`comma-separated`,onChange:e=>B({...z,owners:e.target.value})})]}),(0,T.jsxs)(h,{children:[(0,T.jsx)(C,{htmlFor:`docs-meta-reviewed`,children:`reviewed`}),(0,T.jsx)(v,{id:`docs-meta-reviewed`,type:`date`,value:z.reviewed,onChange:e=>B({...z,reviewed:e.target.value})})]})]}),U?(0,T.jsx)(b,{variant:`destructive`,children:(0,T.jsx)(g,{children:U})}):null]}):null,(0,T.jsxs)(de,{children:[(0,T.jsx)(u,{type:`button`,variant:`outline`,disabled:V,onClick:()=>B(null),children:`Cancel`}),(0,T.jsx)(u,{type:`button`,disabled:V,onClick:()=>void Re(),children:V?`Saving…`:`Save`})]})]})})]})}export{j as DocPanel,M as DocsView};
import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{D as r,E as i,Ft as a,It as o,jt as s,q as ee,wt as te}from"./ui-primitives-DRENhlck.js";import{n as c,o as l,s as u,u as d}from"./theme-CcOVK72d.js";import{$ as ne,A as f,N as re,O as ie,P as ae,Q as oe,X as se,Y as ce,Z as le,a as p,g as m,h,i as ue,j as de,o as g,s as fe}from"./index-CyDRMkuZ.js";import{t as _}from"./progress-CzHcd1lF.js";var v=e(t(),1),y=n();function b({className:e,...t}){return(0,y.jsx)(i,{"data-slot":`checkbox`,className:d(`peer size-4 shrink-0 rounded-[4px] border border-input shadow-xs transition-shadow outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:bg-input/30 dark:aria-invalid:ring-destructive/40 dark:data-[state=checked]:bg-primary`,e),...t,children:(0,y.jsx)(r,{"data-slot":`checkbox-indicator`,className:`grid place-content-center text-current transition-none`,children:(0,y.jsx)(s,{className:`size-3.5`})})})}function pe({className:e,...t}){return(0,y.jsx)(`div`,{"data-slot":`table-container`,className:`relative w-full overflow-x-auto`,children:(0,y.jsx)(`table`,{"data-slot":`table`,className:d(`w-full caption-bottom text-sm`,e),...t})})}function me({className:e,...t}){return(0,y.jsx)(`thead`,{"data-slot":`table-header`,className:d(`[&_tr]:border-b`,e),...t})}function he({className:e,...t}){return(0,y.jsx)(`tbody`,{"data-slot":`table-body`,className:d(`[&_tr:last-child]:border-0`,e),...t})}function x({className:e,...t}){return(0,y.jsx)(`tr`,{"data-slot":`table-row`,className:d(`border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted`,e),...t})}function S({className:e,...t}){return(0,y.jsx)(`th`,{"data-slot":`table-head`,className:d(`h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]`,e),...t})}function C({className:e,...t}){return(0,y.jsx)(`td`,{"data-slot":`table-cell`,className:d(`p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]`,e),...t})}var w=[[`id`,`id`],[`title`,`title · claim`],[`status`,`status`],[`priority`,`prio`],[`type`,`type`],[`area`,`area`],[`epic`,`links`],[`updated`,`updated`]],T=new Map(p.map((e,t)=>[e,t])),E=new Map(g.map((e,t)=>[e,t])),D=w.length+1;function O(e,t){let n=new Map;for(let r of e){let e=r[t];typeof e==`string`&&n.set(e,(n.get(e)||0)+1)}return n}function k({title:e,values:t,counts:n,selected:r,color:i,onSelect:a}){let o=t.filter(e=>n.has(e));if(!o.length)return null;let s=Math.max(...o.map(e=>n.get(e)||0));return(0,y.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,y.jsx)(`span`,{className:`px-1.5 font-mono text-[10px] tracking-widest uppercase text-muted-foreground`,children:e}),o.map(e=>{let t=n.get(e)||0,o=r===e;return(0,y.jsxs)(`button`,{type:`button`,"aria-pressed":o,onClick:()=>a(o?``:e),className:d(`flex w-full cursor-pointer flex-col gap-1 rounded-md px-1.5 py-1 text-left transition-colors hover:bg-accent/50`,o&&`bg-accent`),children:[(0,y.jsxs)(`span`,{className:`flex w-full items-center gap-1.5`,children:[(0,y.jsx)(`span`,{className:d(`min-w-0 flex-1 truncate text-xs`,o?`font-medium text-foreground`:`text-muted-foreground`),children:e}),(0,y.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:t})]}),(0,y.jsx)(_,{value:s?Math.round(t/s*100):0,className:d(`h-[5px] bg-muted [&>div]:bg-current`,!i&&`text-primary`),style:i?{color:i(e)}:void 0})]},e)})]})}function A({label:e,value:t,options:n,color:r,withDot:i,onChange:a}){return(0,y.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,style:{color:r},children:[i?(0,y.jsx)(`span`,{className:`size-1.5 shrink-0 rounded-full bg-current`,"aria-hidden":`true`}):null,(0,y.jsx)(h,{"aria-label":e,value:t,onChange:e=>a(e.target.value),className:`h-[22px] cursor-pointer border-transparent bg-transparent px-1 py-0 pr-8 font-mono text-[11px] text-inherit shadow-none dark:bg-transparent dark:hover:bg-transparent`,children:n.map(e=>(0,y.jsx)(m,{value:e,children:e},e))})]})}var ge=(0,v.memo)(function({task:e,epicId:t,checked:n,isOpen:r,onToggle:i,onOpen:a,onPatch:o}){let s=(e.depends?.length??0)+ +!!e.parent;return(0,y.jsxs)(x,{className:`h-[var(--row-h)] cursor-pointer`,"data-state":r?`selected`:void 0,tabIndex:0,onClick:()=>a(e.id),onKeyDown:t=>{t.key===`Enter`&&a(e.id)},children:[(0,y.jsx)(C,{className:d(`w-7 border-l-2 border-l-transparent`,r&&`border-l-primary`),onClick:e=>e.stopPropagation(),children:(0,y.jsx)(b,{"aria-label":`Select ${e.id}`,checked:n,onCheckedChange:()=>i(e.id)})}),(0,y.jsx)(C,{className:`font-mono text-xs text-muted-foreground`,children:e.id}),(0,y.jsx)(C,{className:`max-w-[520px]`,children:(0,y.jsxs)(`span`,{className:`flex min-w-0 items-baseline gap-2`,children:[(0,y.jsx)(`span`,{className:`min-w-0 truncate font-medium`,children:e.title}),e.claimed_by?(0,y.jsxs)(`span`,{className:`font-mono text-[10px] whitespace-nowrap text-muted-foreground/60`,children:[`· `,e.claimed_by]}):null]})}),(0,y.jsx)(C,{onClick:e=>e.stopPropagation(),children:(0,y.jsx)(A,{label:`Status for ${e.id}`,value:e.status,options:g,color:l(e.status),withDot:!0,onChange:t=>void o(e.id,{status:t}).catch(()=>void 0)})}),(0,y.jsx)(C,{onClick:e=>e.stopPropagation(),children:(0,y.jsx)(A,{label:`Priority for ${e.id}`,value:e.priority,options:p,color:c(e.priority),onChange:t=>void o(e.id,{priority:t}).catch(()=>void 0)})}),(0,y.jsx)(C,{className:`font-mono text-[11px] text-muted-foreground`,children:e.type}),(0,y.jsx)(C,{className:`font-mono text-[11px] text-muted-foreground`,children:e.area}),(0,y.jsxs)(C,{className:`font-mono text-[11px] text-muted-foreground/60`,children:[t?(0,y.jsx)(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),a(t)},className:d(`cursor-pointer text-primary hover:underline`,s>0&&`mr-1.5`),children:t}):null,s>0?`${s} ↔`:t?null:`—`]}),(0,y.jsx)(C,{className:`font-mono text-[11px] text-muted-foreground/60`,children:e.updated||`—`})]})});function j({tasks:e,allTasks:t,areas:n,filters:r,setFilters:i,epicIds:s,onOpen:d,onPatch:_,onBulkPatch:C}){let[A,j]=(0,v.useState)(()=>new Set),[_e,ve]=(0,v.useState)(null),[M,ye]=(0,v.useState)(`id`),[N,P]=(0,v.useState)(`desc`),[F,I]=(0,v.useState)(``),[L,R]=(0,v.useState)(``),[z,B]=(0,v.useState)(``),V=(0,v.useRef)(null),[H,be]=(0,v.useState)({start:0,end:40}),[U,xe]=(0,v.useState)(40),W=(0,v.useDeferredValue)(r),G=(0,v.useMemo)(()=>{let e=e=>ue(t,{...W,...e});return{status:O(e({status:``}),`status`),type:O(e({type:``,showIdeas:!0}),`type`),priority:O(e({priority:``}),`priority`),area:O(e({area:``}),`area`)}},[t,W]),K=(0,v.useMemo)(()=>{let t=[...e];return t.sort((e,t)=>{let n=0;return n=M===`priority`?(T.get(e.priority)||0)-(T.get(t.priority)||0):M===`status`?(E.get(e.status)||0)-(E.get(t.status)||0):M===`epic`?(s.get(e.id)||``).localeCompare(s.get(t.id)||``):String(e[M]||``).localeCompare(String(t[M]||``),void 0,{numeric:!0}),N===`asc`?n:-n}),t},[s,N,M,e]),q=(0,v.useRef)(0),Se=K.length>0,J=(0,v.useCallback)(()=>{let e=V.current;if(!e)return;let t=parseFloat(getComputedStyle(document.documentElement).getPropertyValue(`--row-h`))||40;xe(t);let n=Math.max(0,Math.floor(e.scrollTop/t)-10),r=Math.ceil(e.clientHeight/t);be({start:n,end:Math.min(q.current,n+r+20)})},[]);(0,v.useEffect)(()=>{let e=V.current;if(!e)return;e.addEventListener(`scroll`,J,{passive:!0}),window.addEventListener(`resize`,J);let t=new MutationObserver(J);return t.observe(document.documentElement,{attributes:!0,attributeFilter:[`data-density`]}),()=>{e.removeEventListener(`scroll`,J),window.removeEventListener(`resize`,J),t.disconnect()}},[J,Se]),(0,v.useEffect)(()=>{q.current=K.length,J()},[J,K.length]);let Ce=[r.search,r.status,r.area,r.type,r.priority,r.milestone,r.showIdeas,r.showClosed,M,N].join(`|`);(0,v.useEffect)(()=>{V.current&&(V.current.scrollTop=0),J()},[Ce,J]);let we=(0,v.useCallback)(e=>{j(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),Te=(0,v.useCallback)(e=>{ve(e),d(e)},[d]),Y=(0,v.useMemo)(()=>K.map(e=>e.id),[K]),X=Y.length>0&&Y.every(e=>A.has(e)),Ee=Y.some(e=>A.has(e)),De=K.slice(H.start,H.end),Z=!!(F||L||z);function Oe(e){M===e?P(e=>e===`asc`?`desc`:`asc`):(ye(e),P(e===`id`?`desc`:`asc`))}async function ke(){let e={};if(F&&(e.status=F),L&&(e.priority=L),z&&(e.area=z),!(!Z||A.size===0))try{await C([...A],e),j(new Set),I(``),R(``),B(``)}catch{}}let Q=(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(k,{title:`status`,values:g,counts:G.status,selected:r.status,color:l,onSelect:e=>i(t=>({...t,status:e}))}),(0,y.jsx)(k,{title:`priority`,values:p,counts:G.priority,selected:r.priority,color:c,onSelect:e=>i(t=>({...t,priority:e}))}),(0,y.jsx)(k,{title:`area`,values:n,counts:G.area,selected:r.area,onSelect:e=>i(t=>({...t,area:e}))}),(0,y.jsx)(k,{title:`type`,values:fe,counts:G.type,selected:r.type,onSelect:e=>i(t=>({...t,type:e}))})]}),$=[r.status,r.priority,r.area,r.type].filter(Boolean).length;return(0,y.jsxs)(`div`,{className:`flex min-h-0 flex-1`,children:[(0,y.jsx)(`aside`,{"aria-label":`Backlog facets`,className:`hidden w-[204px] flex-none flex-col gap-5 overflow-y-auto border-r px-3.5 py-4 lg:flex`,children:Q}),(0,y.jsxs)(`div`,{className:`flex min-h-0 min-w-0 flex-1 flex-col`,children:[(0,y.jsxs)(`div`,{className:`flex flex-none items-center gap-2 px-3.5 pt-2.5 lg:hidden`,children:[(0,y.jsxs)(ce,{children:[(0,y.jsx)(ne,{asChild:!0,children:(0,y.jsxs)(u,{variant:`outline`,size:`sm`,children:[(0,y.jsx)(ee,{className:`size-3.5`}),`Facets`,$?(0,y.jsx)(`span`,{className:`font-mono text-[10px] text-muted-foreground`,children:$}):null]})}),(0,y.jsxs)(se,{side:`left`,className:`w-[280px] gap-0 sm:max-w-[280px]`,children:[(0,y.jsx)(le,{className:`pb-2`,children:(0,y.jsx)(oe,{className:`font-mono text-[11px] tracking-wide uppercase`,children:`Facets`})}),(0,y.jsx)(`div`,{className:`flex flex-col gap-5 overflow-y-auto px-4 pb-4`,children:Q})]})]}),(0,y.jsxs)(`span`,{className:`font-mono text-[10.5px] text-muted-foreground/70`,children:[K.length.toLocaleString(),` row`,K.length===1?``:`s`,` · scroll sideways for every column`]})]}),A.size>0&&(0,y.jsxs)(`div`,{role:`region`,"aria-label":`Bulk actions`,className:`mx-3.5 mt-2.5 mb-2.5 flex flex-none flex-wrap items-center gap-2 rounded-md border bg-muted/50 px-3 py-2`,children:[(0,y.jsxs)(`span`,{className:`font-mono text-[11px]`,children:[A.size,` selected`]}),(0,y.jsxs)(h,{"aria-label":`Set status`,value:F,onChange:e=>I(e.target.value),size:`sm`,children:[(0,y.jsx)(m,{value:``,children:`status…`}),g.map(e=>(0,y.jsx)(m,{value:e,children:e},e))]}),(0,y.jsxs)(h,{"aria-label":`Set priority`,value:L,onChange:e=>R(e.target.value),size:`sm`,children:[(0,y.jsx)(m,{value:``,children:`priority…`}),p.map(e=>(0,y.jsx)(m,{value:e,children:e},e))]}),(0,y.jsxs)(h,{"aria-label":`Set area`,value:z,onChange:e=>B(e.target.value),size:`sm`,children:[(0,y.jsx)(m,{value:``,children:`area…`}),n.map(e=>(0,y.jsx)(m,{value:e,children:e},e))]}),(0,y.jsxs)(ae,{children:[(0,y.jsx)(u,{size:`sm`,disabled:!Z,onClick:()=>void ke(),children:`Apply`}),(0,y.jsx)(u,{size:`sm`,variant:`outline`,onClick:()=>j(new Set),children:`Clear`})]})]}),K.length===0?(0,y.jsx)(ie,{children:(0,y.jsxs)(de,{children:[(0,y.jsx)(re,{children:`No cards match`}),(0,y.jsx)(f,{children:`Adjust filters or clear the search`})]})}):(0,y.jsx)(`div`,{ref:V,className:`min-w-0 flex-1 overflow-auto [&>[data-slot=table-container]]:overflow-visible`,children:(0,y.jsxs)(pe,{className:`text-[13px]`,children:[(0,y.jsx)(me,{children:(0,y.jsxs)(x,{className:`hover:bg-transparent`,children:[(0,y.jsx)(S,{className:`sticky top-0 z-10 w-7 bg-background`,children:(0,y.jsx)(b,{"aria-label":`Select all matching cards`,checked:X?!0:Ee?`indeterminate`:!1,onCheckedChange:()=>j(e=>{let t=new Set(e);return X?Y.forEach(e=>t.delete(e)):Y.forEach(e=>t.add(e)),t})})}),w.map(([e,t])=>(0,y.jsx)(S,{"aria-sort":M===e?N===`asc`?`ascending`:`descending`:`none`,className:`sticky top-0 z-10 bg-background`,children:(0,y.jsxs)(u,{variant:`ghost`,size:`sm`,onClick:()=>Oe(e),className:`-ml-2 px-2 text-muted-foreground`,children:[t,M===e?N===`asc`?(0,y.jsx)(a,{className:`size-3`}):(0,y.jsx)(o,{className:`size-3`}):(0,y.jsx)(te,{className:`size-3 opacity-50`})]})},e))]})}),(0,y.jsxs)(he,{children:[H.start>0&&(0,y.jsx)(`tr`,{"aria-hidden":`true`,children:(0,y.jsx)(`td`,{colSpan:D,style:{height:H.start*U}})}),De.map(e=>(0,y.jsx)(ge,{task:e,epicId:s.get(e.id)||``,checked:A.has(e.id),isOpen:_e===e.id,onToggle:we,onOpen:Te,onPatch:_},e.id)),H.end<K.length&&(0,y.jsx)(`tr`,{"aria-hidden":`true`,children:(0,y.jsx)(`td`,{colSpan:D,style:{height:(K.length-H.end)*U}})})]})]})})]})]})}export{j as Explorer};
import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{Ct as r}from"./ui-primitives-DRENhlck.js";import{i,o as a,s as o}from"./theme-CcOVK72d.js";import{A as s,E as c,H as l,J as u,M as d,N as f,O as p,U as m,V as h,at as g,it as _,j as v,rt as y}from"./index-CyDRMkuZ.js";var b=e(t(),1),x=n(),S=[{level:`error`,label:`errors`,hint:`must be fixed for a consistent workspace`,zeroHint:`the doctor does not block the release`},{level:`warning`,label:`warnings`,hint:`worth a look, nothing is broken yet`,zeroHint:`nothing worth flagging`},{level:`info`,label:`infos`,hint:`informational, no action required`,zeroHint:`no notices from the doctor`}];function C(e){return e.replace(/[-_.]+/g,` `)}var w={error:0,warning:1,info:2};function T({onOpen:e}){let[t,n]=(0,b.useState)(null),[T,E]=(0,b.useState)(``),[D,O]=(0,b.useState)(``),[k,A]=(0,b.useState)(0);h(()=>A(e=>e+1)),(0,b.useEffect)(()=>{let e=!0;return u.health().then(t=>{e&&n(t)}).catch(t=>{e&&E(t instanceof Error?t.message:String(t))}),()=>{e=!1}},[k]);let j=(0,b.useMemo)(()=>{if(!t)return[];let e=D?t.issues.filter(e=>e.severity===D):t.issues,n=new Map;for(let t of e){let e=n.get(t.code);e?e.push(t):n.set(t.code,[t])}return[...n.entries()].sort(([e,[t]],[n,[r]])=>{let i=w[t.severity]-w[r.severity];return i===0?e.localeCompare(n):i})},[t,D]);if(T)return(0,x.jsx)(`div`,{className:`p-3.5`,children:(0,x.jsx)(_,{variant:`destructive`,children:(0,x.jsx)(g,{children:T})})});if(!t)return(0,x.jsxs)(`div`,{className:`flex items-center gap-2 p-3.5`,"aria-busy":`true`,children:[(0,x.jsx)(c,{className:`size-3 text-muted-foreground`}),(0,x.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:`running workfile doctor…`})]});let M=[[`cards`,t.modules?.cards??t.cards],[`docs`,t.modules?.docs],[`memory`,t.modules?.memory],[`changelog`,t.modules?.changelog]].filter(([,e])=>e!=null).map(([e,t])=>`${t.toLocaleString()} ${e}`).join(`, `),N=new Intl.DateTimeFormat(void 0,{dateStyle:`medium`,timeStyle:`short`}).format(new Date(t.generatedAt));return(0,x.jsxs)(`div`,{className:`flex-1 overflow-y-auto p-3.5`,children:[(0,x.jsx)(`div`,{className:`mb-2.5 flex gap-1.5`,children:S.map(({level:e,label:n})=>(0,x.jsxs)(o,{type:`button`,variant:`outline`,size:`sm`,"aria-pressed":D===e,className:`aria-pressed:border-ring aria-pressed:bg-accent`,onClick:()=>O(t=>t===e?``:e),children:[n,(0,x.jsx)(y,{variant:`secondary`,className:`px-1.5 font-mono text-[10.5px]`,children:t.counts[e]})]},e))}),(0,x.jsx)(`div`,{className:`flex flex-wrap gap-2.5`,children:S.map(({level:e,label:n,hint:r,zeroHint:o})=>{let s=t.counts[e],c=e===`error`&&s===0?a(`done`):i(e);return(0,x.jsxs)(m,{className:`relative min-w-[13rem] flex-1 gap-1 py-3 pl-5 pr-3.5`,children:[(0,x.jsx)(l,{edge:`left`,color:c}),(0,x.jsxs)(`span`,{className:`flex items-baseline gap-2`,children:[(0,x.jsx)(`span`,{className:`text-[26px] font-semibold tracking-tight`,style:{color:c},children:s}),(0,x.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:n})]}),(0,x.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:s===0?o:r})]},e)})}),(0,x.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-2.5 gap-y-1 px-0.5 pt-4 pb-2`,children:[(0,x.jsxs)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:[`grouped by code · `,M,` · checked `,N]}),(0,x.jsx)(`span`,{className:`ml-auto font-mono text-[10.5px] text-muted-foreground/70`,children:`workfile doctor --json`})]}),j.length===0?(0,x.jsx)(p,{className:`gap-2 p-10`,children:(0,x.jsxs)(v,{children:[(0,x.jsx)(d,{children:(0,x.jsx)(r,{"aria-hidden":`true`,size:20,style:{color:a(`done`)}})}),(0,x.jsx)(f,{className:`text-sm`,children:`All clear`}),(0,x.jsxs)(s,{className:`text-[12.5px]`,children:[`No `,D||`integrity`,` issues found.`]})]})}):(0,x.jsx)(`div`,{className:`flex flex-col gap-2`,children:j.map(([t,n])=>(0,x.jsxs)(m,{className:`gap-0 overflow-hidden py-0`,children:[(0,x.jsxs)(`div`,{className:`flex items-center gap-2 border-b px-3 py-1.5`,children:[(0,x.jsx)(`span`,{"aria-hidden":`true`,className:`size-[7px] rounded-full bg-current`,style:{color:i(n[0].severity)}}),(0,x.jsx)(`span`,{className:`font-mono text-[11.5px]`,children:t}),(0,x.jsx)(`span`,{className:`flex-1 text-[12.5px] text-muted-foreground`,children:C(t)}),(0,x.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground/70`,children:n.length})]}),n.map((t,n)=>(0,x.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-2.5 gap-y-1 border-b px-3 py-[7px] last:border-0`,children:[t.id?(0,x.jsx)(o,{type:`button`,variant:`link`,className:`h-auto w-[82px] flex-[0_0_82px] justify-start p-0 font-mono text-[11px] font-normal`,onClick:()=>e(t.id),children:t.id}):(0,x.jsx)(`span`,{className:`w-[82px] flex-[0_0_82px] font-mono text-[11px] text-muted-foreground/70`,children:`—`}),(0,x.jsx)(`span`,{className:`min-w-[12rem] flex-1 text-[12.5px] text-muted-foreground`,children:t.message}),t.file?(0,x.jsx)(`span`,{className:`max-w-full truncate font-mono text-[10.5px] text-muted-foreground/70 sm:max-w-80`,title:t.file,children:t.file}):null]},`${t.id||t.file}-${n}`))]},t))})]})}export{T as HealthView};
import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{B as r,kt as i,tt as a}from"./ui-primitives-DRENhlck.js";import{i as o,o as s,r as c,s as l,u}from"./theme-CcOVK72d.js";import{A as d,B as f,C as p,D as m,F as ee,G as h,I as g,J as _,K as te,L as ne,O as re,P as ie,R as ae,T as oe,U as se,V as ce,W as v,_ as y,at as b,b as x,c as S,d as C,et as w,f as T,g as E,h as D,it as O,l as k,p as A,q as j,rt as le,u as M,w as ue,y as N}from"./index-CyDRMkuZ.js";import{t as de}from"./layout-QiuZ_k5v.js";var P=e(t(),1),F=n(),I=`text-[10px] font-medium tracking-widest uppercase text-muted-foreground`;function L(e){switch(e){case`added`:return s(`done`);case`changed`:return s(`doing`);case`fixed`:return s(`review`);case`removed`:return s(`blocked`);case`security`:return o(`error`);default:return s(`backlog`)}}function fe(e,t){let n=null;for(let t of e){let e=/^v?(\d+)\.(\d+)\.(\d+)/.exec(t.version);if(!e)continue;let r=[Number(e[1]),Number(e[2]),Number(e[3])];(n?r[0]-n[0]||r[1]-n[1]||r[2]-n[2]:1)>0&&(n=r)}return n?t.some(e=>[`added`,`removed`,`deprecated`].includes(e.type))?`${n[0]}.${n[1]+1}.0`:`${n[0]}.${n[1]}.${n[2]+1}`:`0.1.0`}function R(e){return e instanceof Error?e.message:String(e)}function z({record:e,selected:t,onSelect:n}){let r=e.kind===`release`?`release`:e.type,i=e.kind===`release`?`var(--primary)`:L(e.type),a=e.kind===`release`?`${e.fragments.length} fragment${e.fragments.length===1?``:`s`} · ${e.date}`:e.area;return(0,F.jsx)(m,{asChild:!0,variant:`outline`,size:`sm`,children:(0,F.jsxs)(`button`,{type:`button`,"aria-current":t?`true`:void 0,onClick:n,className:u(`flex-col flex-nowrap items-stretch gap-1 px-2.5 py-2 text-left shadow-xs`,t?`border-ring bg-accent`:`bg-card hover:border-ring`),children:[(0,F.jsxs)(`span`,{className:`flex items-baseline gap-2`,children:[(0,F.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:e.id}),(0,F.jsx)(`span`,{className:`font-mono text-[10px]`,style:{color:i},children:r}),(0,F.jsx)(`span`,{className:`flex-1`}),(0,F.jsx)(`span`,{className:`max-w-[170px] truncate font-mono text-[10px] text-muted-foreground/70`,children:a})]}),(0,F.jsx)(`span`,{className:`text-sm leading-snug font-normal`,children:e.title})]})})}function B({label:e,records:t,selectedId:n,onSelect:r}){return t.length?(0,F.jsxs)(`div`,{role:`group`,"aria-label":e,className:`flex flex-col gap-1.5 pt-4`,children:[(0,F.jsxs)(`span`,{className:I,children:[e,` · `,t.length]}),t.map(e=>(0,F.jsx)(z,{record:e,selected:e.id===n,onSelect:()=>r(e.id)},e.id))]}):null}function V({id:e,title:t,relation:n,disabled:r,onOpen:i}){return(0,F.jsx)(m,{asChild:!0,variant:`outline`,size:`sm`,children:(0,F.jsxs)(`button`,{type:`button`,disabled:r,onClick:i,className:`flex-nowrap gap-2 bg-card px-2.5 py-1.5 text-left shadow-xs hover:border-ring disabled:pointer-events-none disabled:opacity-55`,children:[(0,F.jsx)(`span`,{className:`shrink-0 font-mono text-[11px] text-muted-foreground`,children:e}),(0,F.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-[12.5px]`,children:t}),n?(0,F.jsx)(le,{variant:`outline`,className:`shrink-0 font-mono text-[10px] font-normal text-muted-foreground`,children:n}):null]})})}function H({label:e,links:t,onOpen:n}){return t.length?(0,F.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[(0,F.jsx)(`span`,{className:I,children:e}),t.map(t=>(0,F.jsx)(V,{id:t.id,title:t.title,relation:t.relation,disabled:t.disabled,onOpen:()=>n(t.id)},`${e}-${t.id}`))]}):null}function pe(e){return e.map(e=>({id:e.id,title:e.title||`Missing record`,relation:e.relation,disabled:!e.exists&&!e.title}))}function me({schema:e,areas:t,onClose:n,onCreated:r}){let[i,a]=(0,P.useState)({title:``,type:e.defaults.type,area:t[0]||`general`,visibility:e.defaults.visibility,body:``}),[o,s]=(0,P.useState)(!1),[c,u]=(0,P.useState)(``),d=(e,t)=>a(n=>({...n,[e]:t})),f=async()=>{s(!0);try{r((await _.createChange(i)).record)}catch(e){u(R(e))}finally{s(!1)}};return(0,F.jsx)(S,{open:!0,onOpenChange:e=>{e||n()},children:(0,F.jsxs)(k,{onOpenAutoFocus:e=>e.preventDefault(),children:[(0,F.jsxs)(T,{children:[(0,F.jsx)(A,{children:`New change fragment`}),(0,F.jsx)(M,{children:`Record one user- or operator-meaningful change.`})]}),(0,F.jsxs)(y,{children:[(0,F.jsx)(N,{htmlFor:`new-fragment-title`,children:`Title`}),(0,F.jsx)(w,{id:`new-fragment-title`,autoFocus:!0,required:!0,maxLength:120,value:i.title,onChange:e=>d(`title`,e.target.value)})]}),(0,F.jsxs)(`div`,{className:`grid grid-cols-3 gap-2.5`,children:[(0,F.jsxs)(y,{children:[(0,F.jsx)(N,{htmlFor:`new-fragment-type`,children:`Type`}),(0,F.jsx)(D,{id:`new-fragment-type`,value:i.type,onChange:e=>d(`type`,e.target.value),children:e.types.map(e=>(0,F.jsx)(E,{value:e,children:e},e))})]}),(0,F.jsxs)(y,{children:[(0,F.jsx)(N,{htmlFor:`new-fragment-area`,children:`Area`}),(0,F.jsx)(D,{id:`new-fragment-area`,value:i.area,onChange:e=>d(`area`,e.target.value),children:t.map(e=>(0,F.jsx)(E,{value:e,children:e},e))})]}),(0,F.jsxs)(y,{children:[(0,F.jsx)(N,{htmlFor:`new-fragment-visibility`,children:`Visibility`}),(0,F.jsx)(D,{id:`new-fragment-visibility`,value:i.visibility,onChange:e=>d(`visibility`,e.target.value),children:e.visibilities.map(e=>(0,F.jsx)(E,{value:e,children:e},e))})]})]}),(0,F.jsxs)(y,{children:[(0,F.jsx)(N,{htmlFor:`new-fragment-details`,children:`Details`}),(0,F.jsx)(g,{id:`new-fragment-details`,rows:5,value:i.body,onChange:e=>d(`body`,e.target.value)})]}),c?(0,F.jsx)(O,{variant:`destructive`,"aria-live":`polite`,children:(0,F.jsx)(b,{children:c})}):null,(0,F.jsxs)(C,{children:[(0,F.jsx)(l,{type:`button`,variant:`outline`,onClick:n,children:`Cancel`}),(0,F.jsx)(l,{type:`button`,disabled:o||!i.title.trim(),onClick:()=>void f(),children:o?`Saving…`:`Create fragment`})]})]})})}function he({preview:e,suggestedVersion:t,onClose:n,onReleased:r}){let[i,a]=(0,P.useState)(t),[o,s]=(0,P.useState)(``),[c,u]=(0,P.useState)(!1),[d,f]=(0,P.useState)(``),p=async()=>{u(!0);try{await _.createRelease({version:i,title:o||void 0,fragmentIds:e.fragments.map(e=>e.id)}),r()}catch(e){f(R(e))}finally{u(!1)}};return(0,F.jsx)(S,{open:!0,onOpenChange:e=>{e||n()},children:(0,F.jsxs)(k,{className:`flex max-h-[85vh] flex-col sm:max-w-[640px]`,children:[(0,F.jsxs)(T,{children:[(0,F.jsx)(A,{children:`Release preparation`}),(0,F.jsxs)(M,{children:[e.fragments.length,` unreleased fragment`,e.fragments.length===1?``:`s`,` selected.`]})]}),(0,F.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto`,children:[(0,F.jsxs)(`div`,{className:`grid grid-cols-[150px_1fr] gap-2.5`,children:[(0,F.jsxs)(y,{children:[(0,F.jsx)(N,{htmlFor:`release-version`,children:`Version`}),(0,F.jsx)(w,{id:`release-version`,className:`font-mono`,placeholder:`2.4.0`,value:i,onChange:e=>a(e.target.value)})]}),(0,F.jsxs)(y,{children:[(0,F.jsx)(N,{htmlFor:`release-title`,children:`Release title`}),(0,F.jsx)(w,{id:`release-title`,placeholder:`Optional curated title`,value:o,onChange:e=>s(e.target.value)})]})]}),e.groups.map(e=>(0,F.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,F.jsxs)(`span`,{className:I,style:{color:L(e.type)},children:[e.type,` · `,e.fragments.length]}),e.fragments.map(e=>(0,F.jsxs)(`span`,{className:`flex items-baseline gap-2 text-[12.5px]`,children:[(0,F.jsx)(`span`,{className:`shrink-0 font-mono text-[11px] text-muted-foreground`,children:e.id}),(0,F.jsx)(`span`,{className:`min-w-0 truncate`,children:e.title}),(0,F.jsx)(`span`,{className:`flex-1`}),(0,F.jsx)(`span`,{className:`font-mono text-[10px] text-muted-foreground/70`,children:e.area})]},e.id))]},e.type)),(0,F.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,F.jsx)(`span`,{className:I,children:`release notes preview`}),(0,F.jsx)(`div`,{className:`max-h-[220px] overflow-y-auto rounded-md border bg-background px-3 py-1`,children:(0,F.jsx)(x,{source:e.markdown||`No release notes to render.`})})]}),d?(0,F.jsx)(O,{variant:`destructive`,"aria-live":`polite`,children:(0,F.jsx)(b,{children:d})}):null]}),(0,F.jsxs)(C,{children:[(0,F.jsx)(l,{type:`button`,variant:`outline`,onClick:n,children:`Cancel`}),(0,F.jsx)(l,{type:`button`,disabled:c||!i.trim()||!e.fragments.length,onClick:()=>void p(),children:c?`Releasing…`:`Create release`})]})]})})}function ge({record:e,schema:t,areas:n,onSaved:r}){let[i,a]=(0,P.useState)({title:e.title,type:e.type,area:e.area,visibility:e.visibility}),[s,c]=(0,P.useState)(!1),[u,d]=(0,P.useState)(``),f=(e,t)=>a(n=>({...n,[e]:t})),p={};for(let t of[`title`,`type`,`area`,`visibility`])i[t]!==e[t]&&(p[t]=i[t]);let m=Object.keys(p).length>0,ee=n.includes(e.area)?n:[e.area,...n],g=async()=>{c(!0);try{let t=await _.patchChange(e.id,p,e.revision);d(``),r(t.record)}catch(e){d(R(e))}finally{c(!1)}};return(0,F.jsxs)(se,{className:`gap-2.5 rounded-lg py-3 shadow-xs`,children:[(0,F.jsx)(te,{className:`px-3`,children:(0,F.jsx)(j,{className:I,children:`edit fragment`})}),(0,F.jsxs)(v,{className:`flex flex-col gap-2.5 px-3`,children:[(0,F.jsxs)(y,{children:[(0,F.jsx)(N,{htmlFor:`edit-fragment-title`,children:`Title`}),(0,F.jsx)(w,{id:`edit-fragment-title`,maxLength:120,value:i.title,onChange:e=>f(`title`,e.target.value)})]}),(0,F.jsxs)(`div`,{className:`grid grid-cols-3 gap-2.5`,children:[(0,F.jsxs)(y,{children:[(0,F.jsx)(N,{htmlFor:`edit-fragment-type`,children:`Type`}),(0,F.jsx)(D,{id:`edit-fragment-type`,value:i.type,onChange:e=>f(`type`,e.target.value),children:t.types.map(e=>(0,F.jsx)(E,{value:e,children:e},e))})]}),(0,F.jsxs)(y,{children:[(0,F.jsx)(N,{htmlFor:`edit-fragment-area`,children:`Area`}),(0,F.jsx)(D,{id:`edit-fragment-area`,value:i.area,onChange:e=>f(`area`,e.target.value),children:ee.map(e=>(0,F.jsx)(E,{value:e,children:e},e))})]}),(0,F.jsxs)(y,{children:[(0,F.jsx)(N,{htmlFor:`edit-fragment-visibility`,children:`Visibility`}),(0,F.jsx)(D,{id:`edit-fragment-visibility`,value:i.visibility,onChange:e=>f(`visibility`,e.target.value),children:t.visibilities.map(e=>(0,F.jsx)(E,{value:e,children:e},e))})]})]})]}),(0,F.jsxs)(h,{className:`gap-2.5 px-3`,children:[u?(0,F.jsx)(`span`,{className:`flex-1 text-xs`,style:{color:o(`error`)},"aria-live":`polite`,children:u}):(0,F.jsx)(`span`,{className:`flex-1`}),(0,F.jsx)(l,{type:`button`,variant:`outline`,size:`sm`,disabled:s||!m||!i.title.trim(),onClick:()=>void g(),children:s?`Saving…`:`Save changes`})]})]})}function U({selectedId:e,onSelect:t,onOpenRecord:n,schema:s,areas:m,search:h,onSearchChange:g,filters:te,onFiltersChange:v}){let y=oe(),[S,C]=(0,P.useState)([]),{state:w,visibility:T}=te,[E,D]=(0,P.useState)(!0),[k,A]=(0,P.useState)(``),[j,M]=(0,P.useState)(``),[N,I]=(0,P.useState)(!1),[z,V]=(0,P.useState)(null),[U,_e]=(0,P.useState)(`public`),[W,G]=(0,P.useState)({content:``,error:``,loading:!0}),[ve,ye]=(0,P.useState)(0),be=()=>ye(e=>e+1);ce(e=>{f(e,`/changelog/`)&&be()}),(0,P.useEffect)(()=>{let e=!1,t=async()=>{D(!0);try{let t=await _.changelog(h.trim(),{state:w||void 0,visibility:T||void 0});if(e)return;C(t.records),A(``)}catch(t){e||A(R(t))}finally{e||D(!1)}},n=window.setTimeout(()=>void t(),h?180:0);return()=>{e=!0,window.clearTimeout(n)}},[h,w,T,ve]),(0,P.useEffect)(()=>{let e=!1;return G(e=>({...e,loading:!0})),_.renderedChangelog(U).then(t=>{e||G({content:t.content,error:``,loading:!1})}).catch(t=>{e||G({content:``,error:R(t),loading:!1})}),()=>{e=!0}},[U,ve]);let K=(0,P.useMemo)(()=>[...S].sort((e,t)=>{if(e.kind!==t.kind)return e.kind===`change`?-1:1;if(e.kind===`release`&&t.kind===`release`){let n=t.date.localeCompare(e.date);return n===0?t.id.localeCompare(e.id):n}return String(t.updated||``).localeCompare(String(e.updated||``))}),[S]),q=(0,P.useMemo)(()=>new Map(S.map(e=>[e.id,e])),[S]),J=(0,P.useMemo)(()=>K.filter(e=>e.kind===`change`&&!e.released),[K]),Y=(0,P.useMemo)(()=>K.filter(e=>e.kind===`change`&&e.released),[K]),X=(0,P.useMemo)(()=>K.filter(e=>e.kind===`release`),[K]),xe=(0,P.useMemo)(()=>fe(X,J),[X,J]),Z=(0,P.useMemo)(()=>[...J,...X,...Y].map(e=>e.id),[Y,X,J]),Q=e?q.get(e):void 0,$=e=>{if(q.has(e)){t(e);return}if(/^(CHG|REL)-/.test(e)){v({state:``,visibility:``}),t(e);return}n(e)},Se=()=>{M(``),_.releasePreview().then(V).catch(e=>M(R(e)))},Ce=Q?.issues.some(e=>e.severity===`error`)?`destructive`:`default`,we=(0,F.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,disabled:y,title:y?ue:void 0,onClick:()=>I(!0),children:[(0,F.jsx)(a,{"aria-hidden":`true`}),`New fragment`]});return(0,F.jsxs)(`div`,{className:`flex min-h-0 flex-1`,children:[(0,F.jsxs)(`div`,{className:u(`w-full shrink-0 flex-col border-r lg:flex lg:w-[400px]`,Q?`hidden`:`flex`),children:[(0,F.jsxs)(`div`,{className:`flex flex-col gap-2.5 p-3.5 pb-0`,children:[(0,F.jsxs)(se,{className:`flex-row items-center gap-2.5 border-primary bg-primary/10 p-3`,children:[(0,F.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,F.jsxs)(`span`,{className:`text-[13px] font-semibold`,children:[J.length,` unpublished fragment`,J.length===1?``:`s`]}),(0,F.jsxs)(`span`,{className:`font-mono text-[10.5px] text-muted-foreground`,children:[`next: `,xe,` ·`,` `,s.releaseStrategy]})]}),(0,F.jsx)(l,{type:`button`,size:`sm`,className:`whitespace-nowrap`,disabled:y,title:y?ue:void 0,onClick:Se,children:`Prepare release`})]}),j?(0,F.jsx)(O,{variant:`destructive`,"aria-live":`polite`,children:(0,F.jsx)(b,{children:j})}):null,(0,F.jsxs)(ne,{before:(0,F.jsx)(ee,{scope:`records`,value:h,label:`Search history`,onChange:g}),children:[(0,F.jsx)(ae,{label:`state`,value:w,options:[{value:`unreleased`},{value:`released`}],onChange:e=>v({state:e})}),(0,F.jsx)(ae,{label:`visibility`,value:T,options:s.visibilities.map(e=>({value:e})),onChange:e=>v({visibility:e})})]})]}),(0,F.jsx)(`div`,{className:`min-h-0 flex-1 overflow-y-auto px-3.5 pb-6 [mask-image:linear-gradient(to_bottom,black_calc(100%-24px),transparent)]`,children:E?(0,F.jsx)(`div`,{"aria-busy":`true`,className:`flex flex-col gap-2 pt-4`,children:Array.from({length:6},(e,t)=>(0,F.jsx)(`div`,{className:`h-[52px] animate-pulse rounded-md bg-muted`},t))}):k?(0,F.jsx)(O,{variant:`destructive`,className:`mt-4`,"aria-live":`polite`,children:(0,F.jsx)(b,{children:k})}):K.length?(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(B,{label:`unpublished`,records:J,selectedId:e,onSelect:e=>t(e,Z)}),(0,F.jsx)(B,{label:`releases`,records:X,selectedId:e,onSelect:e=>t(e,Z)}),(0,F.jsx)(B,{label:`published fragments`,records:Y,selectedId:e,onSelect:e=>t(e,Z)})]}):(0,F.jsx)(re,{className:`mt-4 gap-1 p-4 md:p-4`,children:(0,F.jsx)(d,{className:`text-xs`,children:`No history records match the filters.`})})})]}),(0,F.jsx)(`div`,{className:u(`min-w-0 flex-1 overflow-y-auto px-6 py-5 sm:px-8.5`,Q?`block`:`hidden lg:block`),children:(0,F.jsx)(`div`,{className:de,children:Q?(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(l,{type:`button`,variant:`ghost`,size:`sm`,className:`-ml-2 mb-2 lg:hidden`,onClick:()=>t(``),children:[(0,F.jsx)(i,{"aria-hidden":`true`}),`All history`]}),(0,F.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-2 gap-y-1 font-mono text-[11px]`,children:[(0,F.jsx)(`span`,{className:`whitespace-nowrap text-primary`,children:Q.id}),(0,F.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,F.jsx)(`span`,{className:`text-muted-foreground/70`,children:Q.kind}),(0,F.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),Q.kind===`change`?(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`span`,{style:{color:L(Q.type)},children:Q.type}),(0,F.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,F.jsx)(`span`,{className:`text-muted-foreground`,children:Q.area}),(0,F.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,F.jsx)(`span`,{className:`text-muted-foreground`,children:Q.visibility}),(0,F.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,F.jsx)(`span`,{style:{color:c(Q.released?`released`:`unreleased`)},children:Q.released?`released`:`unreleased`}),Q.updated?(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,F.jsx)(`span`,{className:`text-muted-foreground/70`,children:Q.updated})]}):null]}):(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`span`,{className:`text-primary`,children:Q.version}),(0,F.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,F.jsx)(`span`,{className:`text-muted-foreground`,children:Q.date}),Q.commit?(0,F.jsxs)(F.Fragment,{children:[(0,F.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,F.jsx)(`span`,{className:`text-muted-foreground/70`,children:Q.commit})]}):null,(0,F.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,F.jsxs)(`span`,{className:`text-muted-foreground/70`,children:[Q.fragments.length,` fragment`,Q.fragments.length===1?``:`s`]})]}),(0,F.jsxs)(`span`,{className:`ml-auto flex shrink-0 items-center gap-1`,children:[(0,F.jsx)(p,{noun:Q.kind===`release`?`release`:`fragment`}),we,(0,F.jsx)(l,{type:`button`,variant:`ghost`,size:`icon-sm`,"aria-label":`Close record`,title:`Back to the derived changelog`,onClick:()=>t(``),children:(0,F.jsx)(r,{"aria-hidden":`true`})})]})]}),(0,F.jsx)(`h2`,{className:`mt-2.5 mb-1 text-[26px] leading-tight font-semibold tracking-tight [text-wrap:pretty]`,children:Q.title}),(0,F.jsx)(`div`,{className:`font-mono text-[10.5px] break-all text-muted-foreground/70`,children:Q.path}),Q.issues.length>0?(0,F.jsx)(O,{variant:Ce,className:`mt-3.5`,children:(0,F.jsx)(b,{className:`w-full gap-1`,children:Q.issues.map(e=>(0,F.jsxs)(`span`,{className:`flex items-baseline gap-2`,children:[(0,F.jsx)(`span`,{className:`shrink-0 font-mono text-[10.5px]`,style:{color:o(e.severity)},children:e.severity}),(0,F.jsx)(`span`,{children:e.message})]},`${e.code}-${e.message}`))})}):null,(0,F.jsx)(`div`,{className:`mt-4.5`,children:(0,F.jsx)(x,{source:Q.body||`No additional notes.`,onOpen:$})}),(0,F.jsxs)(`div`,{className:`mt-5.5 flex flex-col gap-3.5`,children:[Q.kind===`change`?(0,F.jsx)(H,{label:`shipped in`,links:(Q.releaseIds||[]).map(e=>({id:e,title:q.get(e)?.title||`Open release`,relation:`release`})),onOpen:$}):(0,F.jsx)(H,{label:`fragments · ${Q.fragments.length}`,links:Q.fragments.map(e=>{let t=q.get(e);return{id:e,title:t?.title||`Open fragment`,relation:t?.kind===`change`?t.type:void 0}}),onOpen:$}),(0,F.jsx)(H,{label:`links to`,links:pe(Q.outgoing),onOpen:$}),(0,F.jsx)(H,{label:`backlinks`,links:pe(Q.incoming),onOpen:$})]}),Q.kind===`change`&&!y?(0,F.jsx)(`div`,{className:`mt-5.5`,children:(0,F.jsx)(ge,{record:Q,schema:s,areas:m,onSaved:e=>C(t=>t.map(t=>t.id===e.id?e:t))},`${Q.id}:${Q.revision}`)}):null]}):(0,F.jsxs)(F.Fragment,{children:[(0,F.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-2.5 gap-y-2 border-b pb-3`,children:[(0,F.jsx)(`span`,{className:`text-[13px] font-semibold`,children:`Derived changelog`}),(0,F.jsxs)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:[`visibility `,U,` · CHANGELOG.md`]}),(0,F.jsxs)(`span`,{className:`ml-auto flex flex-wrap items-center gap-2.5`,children:[(0,F.jsx)(ie,{children:s.visibilities.map(e=>(0,F.jsx)(l,{type:`button`,size:`sm`,variant:U===e?`default`:`outline`,"aria-pressed":U===e,onClick:()=>_e(e),children:e},e))}),(0,F.jsx)(le,{variant:`outline`,className:`rounded-md font-mono text-[10.5px] font-normal whitespace-nowrap text-muted-foreground`,children:`render --write`}),we]})]}),W.error?(0,F.jsx)(O,{variant:`destructive`,className:`mt-4`,"aria-live":`polite`,children:(0,F.jsx)(b,{children:W.error})}):(0,F.jsx)(`pre`,{className:`mt-4 font-mono text-xs leading-[1.75] whitespace-pre-wrap text-muted-foreground`,"aria-busy":W.loading||void 0,children:W.loading&&!W.content?`Rendering…`:W.content||`Nothing to render yet — create the first change fragment.`})]})})}),N?(0,F.jsx)(me,{schema:s,areas:m,onClose:()=>I(!1),onCreated:e=>{I(!1),C(t=>[e,...t]),t(e.id)}}):null,z?(0,F.jsx)(he,{preview:z,suggestedVersion:xe,onClose:()=>V(null),onReleased:()=>{V(null),be()}}):null]})}export{U as HistoryView};

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{et as r,mt as i,nt as a,tt as o}from"./ui-primitives-DRENhlck.js";import{i as s,r as c,s as l,u}from"./theme-CcOVK72d.js";import{A as d,B as f,C as p,D as m,E as h,F as g,I as _,J as v,K as y,L as b,O as x,R as S,T as C,U as ee,V as w,W as T,_ as E,at as D,b as O,c as k,d as A,et as j,f as M,g as N,h as P,it as F,l as I,p as L,rt as R,w as z,y as B}from"./index-CyDRMkuZ.js";var V=e(t(),1),H=n(),U=[`low`,`medium`,`high`],W=[`critical`,`high`,`medium`,`low`],te=`[mask-image:linear-gradient(to_bottom,black_calc(100%_-_24px),transparent)]`;function G(e){return e&&e[0].toUpperCase()+e.slice(1)}function K(e,t){return`${e} ${t}${e===1?``:`s`}`}function q(e){return{category:e===`learnings`||e===`decisions`,confidence:e===`learnings`,severity:e===`incidents`,expires:e===`context`,review_after:e===`context`}}function J(e){let t=[];switch(e.collection){case`learnings`:t.push(e.confidence,e.category,e.occurrences==null?null:`${e.occurrences}×`);break;case`decisions`:e.superseded_by?.length?t.push(`superseded by ${e.superseded_by.join(`, `)}`):e.supersedes?.length?t.push(`supersedes ${e.supersedes.join(`, `)}`):t.push(e.category);break;case`incidents`:t.push(e.severity,e.corrective_actions?.length?K(e.corrective_actions.length,`corrective action`):null);break;case`conventions`:t.push(e.owners?.length?e.owners.join(`, `):`no owner`);break;case`context`:t.push(e.expires?`expires ${e.expires}`:null,e.review_after?`review after ${e.review_after}`:null);break;default:t.push(e.category,e.severity)}return t.filter(Boolean).join(` · `)}function Y({id:e,label:t,children:n}){return(0,H.jsxs)(E,{className:`gap-1.5 [&_[data-slot=native-select-wrapper]]:w-full`,children:[(0,H.jsx)(B,{htmlFor:e,children:t}),n]})}function ne({record:e,selected:t,onSelect:n}){let r=J(e),i=e.lifecycleIssues?.length||0;return(0,H.jsx)(m,{asChild:!0,variant:`outline`,size:`sm`,className:`w-full flex-none flex-col items-stretch gap-1 rounded-lg bg-background px-2.5 py-2 text-left shadow-xs hover:border-ring aria-[current=true]:border-ring aria-[current=true]:bg-accent`,children:(0,H.jsxs)(`button`,{type:`button`,"aria-current":t?`true`:void 0,onClick:n,children:[(0,H.jsxs)(`span`,{className:`flex items-center justify-between gap-2`,children:[(0,H.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:e.id}),(0,H.jsxs)(R,{variant:`outline`,className:`h-[18px] gap-1 rounded-md px-1.5 font-mono text-[10px] font-medium`,children:[(0,H.jsx)(`span`,{className:`size-[5px] shrink-0 rounded-full`,style:{backgroundColor:c(e.status)},"aria-hidden":`true`}),e.status]})]}),(0,H.jsx)(`span`,{className:`text-[13px] font-medium leading-snug`,children:e.title}),r||i?(0,H.jsxs)(`span`,{className:`font-mono text-[10.5px] text-muted-foreground`,children:[r,r&&i?` · `:null,i?(0,H.jsx)(`span`,{style:{color:s(`warning`)},children:K(i,`lifecycle warning`)}):null]}):null]})})}function X({issues:e,kind:t}){return e.length?(0,H.jsx)(H.Fragment,{children:e.map(e=>(0,H.jsx)(F,{variant:e.severity===`error`?`destructive`:`default`,className:`px-3 py-2`,children:(0,H.jsxs)(D,{className:`flex flex-wrap items-baseline gap-x-2 gap-y-0.5`,children:[(0,H.jsx)(`span`,{className:`font-mono text-[10.5px]`,style:{color:s(e.severity)},children:t===`lifecycle`?`lifecycle`:e.severity}),(0,H.jsx)(`span`,{children:e.message})]})},`${t}-${e.code}-${e.message}`))}):null}function Z({label:e,links:t,onOpen:n}){return t.length?(0,H.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,H.jsx)(`span`,{className:`text-[10px] font-medium uppercase tracking-wide text-muted-foreground`,children:e}),t.map(t=>{let r=!t.exists&&!t.title;return(0,H.jsx)(m,{asChild:!0,variant:`outline`,size:`sm`,className:`gap-2 rounded-lg px-2.5 py-2 text-left hover:border-ring disabled:pointer-events-none disabled:opacity-50`,children:(0,H.jsxs)(`button`,{type:`button`,disabled:r,onClick:()=>n(t.id),children:[(0,H.jsx)(`span`,{className:`w-[78px] shrink-0 truncate font-mono text-[11px] font-medium`,children:t.id}),(0,H.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-xs text-muted-foreground`,children:t.title||`Missing record`}),(t.relations??[t.relation||t.kind]).filter(Boolean).map(e=>(0,H.jsx)(R,{variant:`secondary`,className:`h-[18px] rounded-md px-1.5 font-mono text-[10px] font-medium`,children:e},e))]})},`${e}-${t.id}`)})]}):null}function Q({message:e}){return e?(0,H.jsx)(F,{variant:`destructive`,className:`px-3 py-2`,children:(0,H.jsx)(D,{children:e})}):null}function re({schema:e,initialCollection:t,onClose:n,onCreated:r}){let i=e.collections.find(e=>e.id===t)||e.collections[0],[a,o]=(0,V.useState)({collection:i?.id||`learnings`,status:i?.statuses[0]||`active`,title:``,category:``,confidence:``,severity:``,expires:``,body:``}),[s,c]=(0,V.useState)(!1),[u,d]=(0,V.useState)(``),f=e.collections.find(e=>e.id===a.collection),p=q(a.collection),m=(e,t)=>o(n=>({...n,[e]:t})),g=t=>{let n=e.collections.find(e=>e.id===t);o(e=>({...e,collection:t,status:n?.statuses[0]||`active`}))},y=async()=>{c(!0);try{r((await v.createMemory({collection:a.collection,title:a.title,status:a.status,body:a.body,category:a.category||void 0,confidence:a.confidence||void 0,severity:a.severity||void 0,expires:a.expires||void 0})).record)}catch(e){d(e instanceof Error?e.message:String(e))}finally{c(!1)}};return(0,H.jsx)(k,{open:!0,onOpenChange:e=>{e||n()},children:(0,H.jsxs)(I,{className:`sm:max-w-[520px]`,"aria-describedby":void 0,children:[(0,H.jsx)(M,{children:(0,H.jsxs)(L,{children:[`New `,f?.singular||`record`]})}),(0,H.jsxs)(`div`,{className:`-m-1 flex max-h-[65vh] flex-col gap-3 overflow-y-auto p-1`,children:[(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-2.5`,children:[(0,H.jsx)(Y,{id:`memory-create-collection`,label:`Collection`,children:(0,H.jsx)(P,{id:`memory-create-collection`,value:a.collection,onChange:e=>g(e.target.value),children:e.collections.map(e=>(0,H.jsx)(N,{value:e.id,children:e.id},e.id))})}),(0,H.jsx)(Y,{id:`memory-create-status`,label:`Status`,children:(0,H.jsx)(P,{id:`memory-create-status`,value:a.status,onChange:e=>m(`status`,e.target.value),children:(f?.statuses||[]).map(e=>(0,H.jsx)(N,{value:e,children:e},e))})})]}),(0,H.jsx)(Y,{id:`memory-create-title`,label:`Title`,children:(0,H.jsx)(j,{id:`memory-create-title`,autoFocus:!0,required:!0,maxLength:120,value:a.title,onChange:e=>m(`title`,e.target.value)})}),p.category||p.confidence||p.severity||p.expires?(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-2.5`,children:[p.category?(0,H.jsx)(Y,{id:`memory-create-category`,label:`Category`,children:(0,H.jsx)(j,{id:`memory-create-category`,value:a.category,onChange:e=>m(`category`,e.target.value)})}):null,p.confidence?(0,H.jsx)(Y,{id:`memory-create-confidence`,label:`Confidence`,children:(0,H.jsxs)(P,{id:`memory-create-confidence`,value:a.confidence,onChange:e=>m(`confidence`,e.target.value),children:[(0,H.jsx)(N,{value:``,children:`not set`}),U.map(e=>(0,H.jsx)(N,{value:e,children:e},e))]})}):null,p.severity?(0,H.jsx)(Y,{id:`memory-create-severity`,label:`Severity`,children:(0,H.jsxs)(P,{id:`memory-create-severity`,value:a.severity,onChange:e=>m(`severity`,e.target.value),children:[(0,H.jsx)(N,{value:``,children:`not set`}),W.map(e=>(0,H.jsx)(N,{value:e,children:e},e))]})}):null,p.expires?(0,H.jsx)(Y,{id:`memory-create-expires`,label:`Expires`,children:(0,H.jsx)(j,{id:`memory-create-expires`,type:`date`,value:a.expires,onChange:e=>m(`expires`,e.target.value)})}):null]}):null,(0,H.jsx)(Y,{id:`memory-create-body`,label:`Details`,children:(0,H.jsx)(_,{id:`memory-create-body`,rows:8,value:a.body,onChange:e=>m(`body`,e.target.value)})}),(0,H.jsx)(Q,{message:u})]}),(0,H.jsxs)(A,{children:[(0,H.jsx)(l,{type:`button`,variant:`outline`,onClick:n,children:`Cancel`}),(0,H.jsx)(l,{type:`button`,disabled:s||!a.title.trim(),onClick:()=>void y(),children:s?(0,H.jsxs)(H.Fragment,{children:[(0,H.jsx)(h,{"aria-hidden":`true`}),`Saving…`]}):`Create record`})]})]})})}function ie({record:e,statuses:t,onClose:n,onUpdated:r}){let i=q(e.collection),[a,o]=(0,V.useState)({title:e.title,status:e.status,category:e.category||``,confidence:e.confidence||``,severity:e.severity||``,expires:e.expires||``,review_after:e.review_after||``,body:e.body}),[s,c]=(0,V.useState)(!1),[u,d]=(0,V.useState)(``),f=(e,t)=>o(n=>({...n,[e]:t})),p=async()=>{let t={};a.title.trim()&&a.title!==e.title&&(t.title=a.title),a.status!==e.status&&(t.status=a.status),a.body!==e.body&&(t.body=a.body);for(let n of[`category`,`confidence`,`severity`,`expires`,`review_after`])a[n]!==(e[n]||``)&&(t[n]=a[n]||null);if(!Object.keys(t).length){n();return}c(!0);try{r((await v.patchMemory(e.id,t,e.revision)).record),n()}catch(e){d(e instanceof Error?e.message:String(e))}finally{c(!1)}};return(0,H.jsx)(k,{open:!0,onOpenChange:e=>{e||n()},children:(0,H.jsxs)(I,{className:`sm:max-w-[520px]`,"aria-describedby":void 0,children:[(0,H.jsx)(M,{children:(0,H.jsxs)(L,{children:[`Edit `,e.id]})}),(0,H.jsxs)(`div`,{className:`-m-1 flex max-h-[65vh] flex-col gap-3 overflow-y-auto p-1`,children:[(0,H.jsx)(Y,{id:`memory-edit-title`,label:`Title`,children:(0,H.jsx)(j,{id:`memory-edit-title`,autoFocus:!0,required:!0,maxLength:120,value:a.title,onChange:e=>f(`title`,e.target.value)})}),(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-2.5`,children:[(0,H.jsx)(Y,{id:`memory-edit-status`,label:`Status`,children:(0,H.jsx)(P,{id:`memory-edit-status`,value:a.status,onChange:e=>f(`status`,e.target.value),children:(t.includes(a.status)?t:[a.status,...t]).map(e=>(0,H.jsx)(N,{value:e,children:e},e))})}),i.category?(0,H.jsx)(Y,{id:`memory-edit-category`,label:`Category`,children:(0,H.jsx)(j,{id:`memory-edit-category`,value:a.category,onChange:e=>f(`category`,e.target.value)})}):null,i.confidence?(0,H.jsx)(Y,{id:`memory-edit-confidence`,label:`Confidence`,children:(0,H.jsxs)(P,{id:`memory-edit-confidence`,value:a.confidence,onChange:e=>f(`confidence`,e.target.value),children:[(0,H.jsx)(N,{value:``,children:`not set`}),U.map(e=>(0,H.jsx)(N,{value:e,children:e},e))]})}):null,i.severity?(0,H.jsx)(Y,{id:`memory-edit-severity`,label:`Severity`,children:(0,H.jsxs)(P,{id:`memory-edit-severity`,value:a.severity,onChange:e=>f(`severity`,e.target.value),children:[(0,H.jsx)(N,{value:``,children:`not set`}),W.map(e=>(0,H.jsx)(N,{value:e,children:e},e))]})}):null,i.expires?(0,H.jsx)(Y,{id:`memory-edit-expires`,label:`Expires`,children:(0,H.jsx)(j,{id:`memory-edit-expires`,type:`date`,value:a.expires,onChange:e=>f(`expires`,e.target.value)})}):null,i.review_after?(0,H.jsx)(Y,{id:`memory-edit-review-after`,label:`Review after`,children:(0,H.jsx)(j,{id:`memory-edit-review-after`,type:`date`,value:a.review_after,onChange:e=>f(`review_after`,e.target.value)})}):null]}),(0,H.jsx)(Y,{id:`memory-edit-body`,label:`Details`,children:(0,H.jsx)(_,{id:`memory-edit-body`,rows:10,value:a.body,onChange:e=>f(`body`,e.target.value)})}),(0,H.jsx)(Q,{message:u})]}),(0,H.jsxs)(A,{children:[(0,H.jsx)(l,{type:`button`,variant:`outline`,onClick:n,children:`Cancel`}),(0,H.jsx)(l,{type:`button`,disabled:s||!a.title.trim(),onClick:()=>void p(),children:s?(0,H.jsxs)(H.Fragment,{children:[(0,H.jsx)(h,{"aria-hidden":`true`}),`Saving…`]}):`Save changes`})]})]})})}function ae({record:e,mode:t,onClose:n,onUpdated:r}){let[i,a]=(0,V.useState)(``),[o,s]=(0,V.useState)(!1),[c,u]=(0,V.useState)(``),d=async()=>{s(!0);try{r((t===`graduate`?await v.graduateMemory(e.id,i.split(`,`).map(e=>e.trim()).filter(Boolean),e.revision):await v.supersedeMemory(e.id,i.trim(),e.revision)).record),n()}catch(e){u(e instanceof Error?e.message:String(e))}finally{s(!1)}};return(0,H.jsx)(k,{open:!0,onOpenChange:e=>{e||n()},children:(0,H.jsxs)(I,{className:`sm:max-w-[420px]`,"aria-describedby":void 0,children:[(0,H.jsx)(M,{children:(0,H.jsxs)(L,{children:[t===`graduate`?`Graduate`:`Supersede`,` `,e.id]})}),(0,H.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,H.jsx)(Y,{id:`memory-lifecycle-target`,label:t===`graduate`?`Target IDs`:`Replacement ID`,children:(0,H.jsx)(j,{id:`memory-lifecycle-target`,autoFocus:!0,placeholder:t===`graduate`?`CONV-0001, DOC-0004`:`ADR-0009`,value:i,onChange:e=>a(e.target.value)})}),(0,H.jsx)(Q,{message:c})]}),(0,H.jsxs)(A,{children:[(0,H.jsx)(l,{type:`button`,variant:`outline`,onClick:n,children:`Cancel`}),(0,H.jsx)(l,{type:`button`,disabled:o||!i.trim(),onClick:()=>void d(),children:o?(0,H.jsxs)(H.Fragment,{children:[(0,H.jsx)(h,{"aria-hidden":`true`}),`Saving…`]}):`Apply`})]})]})})}function oe({record:e,statuses:t,onOpenRelation:n,onOpenRecord:o,onUpdated:u,onDialogOpenChange:d}){let f=C(),[m,h]=(0,V.useState)(!1),[g,_]=(0,V.useState)(``),v=m||!!g;(0,V.useEffect)(()=>{d?.(v)},[v,d]);let y=e.collection===`learnings`&&e.status!==`graduated`,b=[`learnings`,`decisions`,`conventions`].includes(e.collection),x=[[`status`,e.status,c(e.status)]];return e.category&&x.push([`category`,e.category]),e.confidence&&x.push([`confidence`,e.confidence]),e.severity&&x.push([`severity`,e.severity,s(e.severity)]),e.occurrences!=null&&x.push([`occurrences`,String(e.occurrences)]),e.expires&&x.push([`expires`,e.expires]),e.review_after&&x.push([`review after`,e.review_after]),e.started_at&&x.push([`started`,e.started_at]),e.resolved_at&&x.push([`resolved`,e.resolved_at]),e.graduated_to?.length&&x.push([`graduated to`,e.graduated_to.join(`, `)]),e.superseded_by?.length&&x.push([`superseded by`,e.superseded_by.join(`, `)]),e.owners?.length&&x.push([`owners`,e.owners.join(`, `)]),x.push([`updated`,e.updated||`—`]),(0,H.jsxs)(`aside`,{"aria-label":`Memory record`,className:`flex min-h-0 flex-col overflow-hidden border-l bg-background`,children:[(0,H.jsxs)(`div`,{className:`flex h-11 shrink-0 items-center gap-2 border-b px-3.5`,children:[(0,H.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:e.id}),(0,H.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground/60`,children:`·`}),(0,H.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:e.collection}),(0,H.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground/60`,children:`·`}),(0,H.jsx)(`span`,{className:`font-mono text-[11px]`,style:{color:c(e.status)},children:e.status}),(0,H.jsx)(`span`,{className:`flex-1`}),(0,H.jsx)(p,{})]}),(0,H.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col gap-3.5 overflow-y-auto p-4`,children:[(0,H.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,H.jsx)(`h2`,{className:`m-0 text-[17px] font-semibold leading-[1.3] tracking-[-0.01em] [text-wrap:pretty]`,children:e.title}),e.path?(0,H.jsx)(`span`,{className:`break-all font-mono text-[10.5px] text-muted-foreground`,children:e.path}):null]}),(0,H.jsx)(`div`,{className:`grid grid-cols-2 gap-x-3 gap-y-2`,children:x.map(([e,t,n])=>(0,H.jsxs)(`span`,{className:`flex flex-col gap-0.5`,children:[(0,H.jsx)(`span`,{className:`text-[10px] font-medium uppercase tracking-wide text-muted-foreground`,children:e}),(0,H.jsx)(`span`,{className:`text-sm`,style:n?{color:n}:void 0,children:t})]},e))}),(0,H.jsx)(X,{issues:e.issues,kind:`validation`}),(0,H.jsx)(X,{issues:e.lifecycleIssues||[],kind:`lifecycle`}),(0,H.jsx)(O,{className:`[--typeset-size:0.875rem]`,source:e.body||`No details recorded.`,onOpen:o}),(0,H.jsx)(Z,{label:`Links to`,links:e.outgoing,onOpen:n}),(0,H.jsx)(Z,{label:`Backlinks`,links:e.incoming,onOpen:n}),(0,H.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,H.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,disabled:f,title:f?z:void 0,onClick:()=>h(!0),children:[(0,H.jsx)(a,{"aria-hidden":`true`}),`Edit`]}),y?(0,H.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,disabled:f,title:f?z:void 0,onClick:()=>_(`graduate`),children:[(0,H.jsx)(i,{"aria-hidden":`true`}),`Graduate`]}):null,b?(0,H.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,disabled:f,title:f?z:void 0,onClick:()=>_(`supersede`),children:[(0,H.jsx)(r,{"aria-hidden":`true`}),`Supersede`]}):null]})]}),m?(0,H.jsx)(ie,{record:e,statuses:t,onClose:()=>h(!1),onUpdated:u}):null,g?(0,H.jsx)(ae,{record:e,mode:g,onClose:()=>_(``),onUpdated:u}):null]})}function $(e,t){return e.find(e=>e.id===t)?.statuses||[]}function se({id:e,schema:t,onSelect:n,onOpenRecord:r,onDialogOpenChange:i,onChanged:a}){let[o,s]=(0,V.useState)(null),[c,l]=(0,V.useState)(``);return(0,V.useEffect)(()=>{let t=!0;return s(null),l(``),v.record(e).then(e=>{t&&s(e.record)}).catch(e=>{t&&l(e.message)}),()=>{t=!1}},[e]),c?(0,H.jsx)(`div`,{className:`px-4 py-3 text-xs text-muted-foreground`,children:c}):o?(0,H.jsx)(oe,{record:o,statuses:$(t.collections,o.collection),onOpenRelation:n,onOpenRecord:r,onUpdated:e=>{s(e),a?.()},onDialogOpenChange:i},o.id):(0,H.jsxs)(`div`,{className:`flex items-center gap-2 px-4 py-3 text-sm text-muted-foreground`,children:[(0,H.jsx)(h,{}),` Reading `,e,`…`]})}function ce({selectedId:e,onSelect:t,onOpenRecord:n,schema:r,search:i,onSearchChange:a,filters:s,onFiltersChange:p}){let m=C(),[_,E]=(0,V.useState)([]),{collection:O,status:k}=s,[A,j]=(0,V.useState)(!0),[M,N]=(0,V.useState)(``),[P,I]=(0,V.useState)(null),L=(0,V.useRef)(0),B=(0,V.useCallback)((e,n)=>{L.current=performance.now(),t(e,n)},[t]),[U,W]=(0,V.useState)(0);w(e=>{f(e,`/memory/`)&&W(e=>e+1)}),(0,V.useEffect)(()=>{let e=async()=>{j(!0);try{let e=await v.memory(i.trim(),{collection:O||void 0,status:k||void 0});E(e.records),N(``)}catch(e){N(e instanceof Error?e.message:String(e))}finally{j(!1)}},t=window.setTimeout(()=>void e(),i?180:0);return()=>window.clearTimeout(t)},[i,O,k,U]);let q=(0,V.useMemo)(()=>[..._].sort((e,t)=>String(t.updated||``).localeCompare(String(e.updated||``))||e.title.localeCompare(t.title)),[_]),J=(0,V.useMemo)(()=>{let e=r.collections.filter(e=>!O||e.id===O).map(e=>({schema:e,records:q.filter(t=>t.collection===e.id)})),t=new Set(r.collections.map(e=>e.id)),n=q.filter(e=>!t.has(e.collection));return n.length&&e.push({schema:{id:`other`,singular:`record`,idPrefix:`?`,statuses:[]},records:n}),e},[r.collections,q,O]),Y=(0,V.useMemo)(()=>J.flatMap(e=>e.records.map(e=>e.id)),[J]);q.find(t=>t.id===e);let X=O?$(r.collections,O):[...new Set(r.collections.flatMap(e=>e.statuses))];return(0,H.jsxs)(H.Fragment,{children:[(0,H.jsxs)(b,{gutter:`3.5`,className:`pt-3.5`,before:(0,H.jsx)(g,{scope:`records`,value:i,label:`Search workfile memory`,onChange:a}),after:(0,H.jsx)(`span`,{className:`flex shrink-0 items-center gap-1.5 whitespace-nowrap font-mono text-[11px] text-muted-foreground`,children:A?(0,H.jsxs)(H.Fragment,{children:[(0,H.jsx)(h,{"aria-hidden":`true`,className:`size-3`}),`loading…`]}):K(_.length,`record`)}),children:[(0,H.jsx)(S,{label:`collection`,value:O,options:r.collections.map(e=>({value:e.id})),onChange:e=>p({collection:e,status:``})}),(0,H.jsx)(S,{label:`status`,value:k,options:X.map(e=>({value:e,color:c(e)})),onChange:e=>p({status:e})})]}),M?(0,H.jsx)(F,{variant:`destructive`,className:`mx-3.5 mt-3 w-auto px-3 py-2`,children:(0,H.jsxs)(D,{children:[`Memory could not be loaded: `,M]})}):null,(0,H.jsx)(`div`,{className:`flex min-h-0 flex-1 gap-3 overflow-hidden p-3.5`,children:(0,H.jsx)(`div`,{className:`flex min-h-0 flex-1 gap-3 overflow-x-auto`,children:J.map(t=>(0,H.jsxs)(ee,{className:`w-[272px] flex-none gap-0 overflow-hidden rounded-xl py-0 [--card-spacing:--spacing(2)]`,children:[(0,H.jsxs)(y,{className:`flex flex-row items-center gap-2 border-b px-3 py-2`,children:[(0,H.jsx)(`span`,{className:`font-mono text-[11px] font-medium text-primary`,children:t.schema.idPrefix}),(0,H.jsx)(`span`,{className:`flex-1 text-[12.5px] font-semibold`,children:G(t.schema.singular)}),(0,H.jsx)(R,{variant:`secondary`,className:`h-5 px-1.5 font-mono text-[11px] font-normal`,children:t.records.length}),t.schema.id===`other`?null:(0,H.jsx)(l,{type:`button`,variant:`ghost`,size:`icon-xs`,"aria-label":`New ${t.schema.singular}`,disabled:m,title:m?z:void 0,onClick:()=>I(t.schema.id),children:(0,H.jsx)(o,{"aria-hidden":`true`})})]}),(0,H.jsxs)(T,{className:u(`flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto p-2.5`,te),children:[t.records.map(t=>(0,H.jsx)(ne,{record:t,selected:t.id===e,onSelect:()=>B(t.id,Y)},t.id)),!t.records.length&&!A?(0,H.jsx)(x,{className:`gap-1 border border-dashed p-4 md:p-6`,children:(0,H.jsx)(d,{className:`font-mono text-xs`,children:`no records`})}):null]})]},t.schema.id))})}),P===null?null:(0,H.jsx)(re,{schema:r,initialCollection:P,onClose:()=>I(null),onCreated:e=>{I(null),E(t=>[e,...t]),t(e.id)}})]})}export{se as MemoryPanel,ce as MemoryView};
import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{$ as r,Mt as i,it as a,lt as o}from"./ui-primitives-DRENhlck.js";import{n as s,o as c,s as l,u}from"./theme-CcOVK72d.js";import{A as d,M as f,N as p,O as m,T as h,a as g,b as _,j as v,k as y,nt as b}from"./index-CyDRMkuZ.js";import{t as x}from"./layout-QiuZ_k5v.js";import{t as S}from"./progress-CzHcd1lF.js";var C=e(t(),1),w=n(),T=[{key:`N`,label:`Move to next`,status:`next`},{key:`D`,label:`Defer`,status:`deferred`},{key:`X`,label:`Discard`,status:`discarded`}];function E({tasks:e,repoRoot:t,repoUrl:n,onPatch:E,onOpen:D}){let O=h(),[k,A]=(0,C.useState)(()=>new Set),[j,M]=(0,C.useState)(0),N=(0,C.useMemo)(()=>e.filter(e=>!k.has(e.id)),[k,e]),P=N[j]||N[0],F=e.length;(0,C.useEffect)(()=>{j>=N.length&&M(Math.max(0,N.length-1))},[j,N.length]);let I=(0,C.useCallback)(async(e,t=!0)=>{if(P){try{await E(P.id,e)}catch{return}t&&(A(e=>new Set(e).add(P.id)),M(e=>Math.min(e,Math.max(0,N.length-2))))}},[E,N.length,P]);(0,C.useEffect)(()=>{if(O)return;let e=e=>{if(e.ctrlKey||e.metaKey||e.altKey)return;let t=e.target;if([`INPUT`,`SELECT`,`TEXTAREA`].includes(t.tagName)||t.isContentEditable)return;let n=e.key.toUpperCase();if(n===`J`||e.key===`ArrowDown`)e.preventDefault(),M(e=>Math.min(N.length-1,e+1));else if(n===`K`||e.key===`ArrowUp`)e.preventDefault(),M(e=>Math.max(0,e-1));else if(/^[1-4]$/.test(n))e.preventDefault(),I({priority:g[Number(n)-1]},!1);else{let t=T.find(e=>e.key===n);t&&(e.preventDefault(),I({status:t.status}))}};return document.addEventListener(`keydown`,e),()=>document.removeEventListener(`keydown`,e)},[I,N.length,O]);let L=e=>n?`${n.replace(/\/+$/,``)}/blob/main/${e}`:`vscode://file${t}/${e}`;return O?(0,w.jsx)(m,{className:`gap-3 p-10`,children:(0,w.jsxs)(v,{children:[(0,w.jsx)(f,{children:(0,w.jsx)(o,{"aria-hidden":`true`,size:20})}),(0,w.jsx)(p,{className:`text-sm`,children:`Triage needs a writable workspace`}),(0,w.jsx)(d,{className:`text-[12.5px]`,children:`This board is served read-only, and every action here assigns a status or a priority.`})]})}):P?(0,w.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col overflow-y-auto`,children:[(0,w.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-2.5 gap-y-2 border-b bg-card px-3.5 py-2.5`,children:[(0,w.jsxs)(`div`,{className:`flex min-w-0 flex-1 basis-64 items-center gap-2.5`,children:[(0,w.jsx)(S,{value:F?k.size/F*100:0,className:`min-w-16 max-w-[340px] flex-1`}),(0,w.jsxs)(`span`,{className:`shrink-0 font-mono text-[11px] text-muted-foreground`,children:[k.size,` of `,F,` processed`]})]}),(0,w.jsxs)(`div`,{className:`ml-auto flex shrink-0 items-center gap-2.5`,children:[(0,w.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,disabled:j===0,onClick:()=>M(e=>Math.max(0,e-1)),children:[(0,w.jsx)(b,{className:`max-sm:hidden`,children:`K`}),`Previous`]}),(0,w.jsxs)(`span`,{className:`font-mono text-[11px] text-muted-foreground tabular-nums`,children:[j+1,` / `,N.length]}),(0,w.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,disabled:j>=N.length-1,onClick:()=>M(e=>Math.min(N.length-1,e+1)),children:[(0,w.jsx)(b,{className:`max-sm:hidden`,children:`J`}),`Next`]}),(0,w.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,title:`Open full card`,"aria-label":`Open full card`,className:`max-sm:size-7 max-sm:px-0`,onClick:()=>D(P.id),children:[(0,w.jsx)(a,{"aria-hidden":`true`}),(0,w.jsx)(`span`,{className:`max-sm:hidden`,children:`Open full card`})]})]})]}),(0,w.jsxs)(`div`,{className:u(x,`px-6 py-7 sm:px-8`),children:[(0,w.jsxs)(`div`,{className:`flex items-center gap-2 font-mono text-[11px] text-muted-foreground`,children:[(0,w.jsx)(`span`,{children:P.id}),(0,w.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,w.jsx)(`span`,{style:{color:c(P.status)},children:P.status}),(0,w.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,w.jsx)(`span`,{children:P.area}),(0,w.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,w.jsx)(`span`,{children:P.type})]}),(0,w.jsx)(`h2`,{className:`mt-3 mb-1 text-[26px] leading-[1.2] font-semibold tracking-tight [text-wrap:pretty]`,children:P.title}),P.file?(0,w.jsx)(`a`,{className:`font-mono text-[11px] text-muted-foreground/70 underline underline-offset-[3px]`,href:L(P.file),target:n?`_blank`:void 0,rel:n?`noreferrer`:void 0,children:P.file}):null,P.source?(0,w.jsxs)(`span`,{className:`mt-[3px] block font-mono text-[11px] text-muted-foreground/70`,children:[`source`,` `,(0,w.jsx)(`a`,{className:`font-mono underline underline-offset-[3px]`,href:L(P.source),target:n?`_blank`:void 0,rel:n?`noreferrer`:void 0,children:P.source})]}):null,(0,w.jsx)(`div`,{className:`mt-[22px]`,children:(0,w.jsx)(_,{source:P.body,onOpen:D})}),(0,w.jsxs)(`div`,{className:`mt-[30px] flex flex-wrap gap-2 border-t pt-[18px]`,children:[g.map((e,t)=>(0,w.jsxs)(l,{type:`button`,variant:`outline`,size:`lg`,"aria-pressed":P.priority===e,style:P.priority===e?{borderColor:s(e)}:void 0,onClick:()=>void I({priority:e},!1),children:[(0,w.jsx)(b,{children:t+1}),(0,w.jsx)(`span`,{style:{color:s(e)},children:e})]},e)),T.map(e=>(0,w.jsxs)(l,{type:`button`,variant:`outline`,size:`lg`,onClick:()=>void I({status:e.status}),children:[(0,w.jsx)(b,{children:e.key}),(0,w.jsx)(`span`,{style:{color:c(e.status)},children:e.label})]},e.key))]}),(0,w.jsx)(`span`,{className:`mt-3.5 block text-xs text-muted-foreground`,children:`Every action writes the card's frontmatter to disk immediately. Shortcuts work while focus is outside a form.`})]})]}):(0,w.jsxs)(m,{className:`gap-3 p-10`,children:[(0,w.jsxs)(v,{children:[(0,w.jsx)(f,{children:(0,w.jsx)(i,{"aria-hidden":`true`,size:20,style:{color:c(`done`)}})}),(0,w.jsx)(p,{className:`text-sm`,children:`Queue clear`}),(0,w.jsxs)(d,{className:`text-[12.5px]`,children:[`You processed `,k.size.toLocaleString(),` cards.`]})]}),(0,w.jsx)(y,{children:(0,w.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>{A(new Set),M(0)},children:[(0,w.jsx)(r,{"aria-hidden":`true`}),`Start again`]})})]})}export{E as TriageView};
import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{bt as r,ut as i}from"./ui-primitives-DRENhlck.js";import{r as a,s as o,u as s}from"./theme-CcOVK72d.js";import{J as c,L as l,rt as u}from"./index-CyDRMkuZ.js";var d=e(t(),1),f=[{id:`parent`,label:`parent`,declared:!0},{id:`depends`,label:`depends`,declared:!0},{id:`origin`,label:`origin`,declared:!0},{id:`supersedes`,label:`supersedes`,declared:!0},{id:`superseded_by`,label:`superseded by`,declared:!0},{id:`graduated_to`,label:`graduated to`,declared:!0},{id:`corrective_actions`,label:`corrective`,declared:!0},{id:`cards`,label:`cards`,declared:!0},{id:`decisions`,label:`decisions`,declared:!0},{id:`fragments`,label:`fragments`,declared:!0},{id:`related`,label:`related`,declared:!0},{id:`source`,label:`source`,declared:!0},{id:`wikilink`,label:`wiki link`,declared:!1},{id:`markdown`,label:`md link`,declared:!1},{id:`mention`,label:`mention`,declared:!1}],p=new Set(f.filter(e=>e.declared).map(e=>e.id)),m=[{id:`card`,label:`Cards`},{id:`memory`,label:`Memory`},{id:`doc`,label:`Docs`},{id:`change`,label:`Changes`},{id:`release`,label:`Releases`}],h=f.map(e=>e.id).filter(e=>e!==`mention`),g=[`card`,`memory`,`doc`];function _(e,t){return e.kind!==`card`||!(t.status&&e.status!==t.status||t.area&&e.area!==t.area||t.type&&e.recordType!==t.type||t.priority&&e.priority!==t.priority||t.milestone&&e.milestone!==t.milestone)}function ee(e,t){let n=Object.values(t.record??{}).some(Boolean),r=e.filter(e=>t.kinds.has(e.kind)&&_(e,t.record??{}));if(n){let e=new Set(r.filter(e=>e.kind===`card`).map(e=>e.id)),n=new Set;for(let i of v(r,t.relations).links)e.has(i.from)&&n.add(i.to),e.has(i.to)&&n.add(i.from);r=r.filter(e=>e.kind===`card`||n.has(e.id))}let{links:i,degree:a}=v(r,t.relations),o=t.hideIsolated?r.filter(e=>a.get(e.id)):r;return{records:o,links:i,degree:a,isolated:r.length-o.length}}function v(e,t){let n=new Set(e.map(e=>e.id)),r=[],i=new Map;for(let a of e)for(let e of a.edges){if(!n.has(e.to)||e.to===a.id)continue;let o=e.rel.filter(e=>t.has(e));o.length&&(r.push({from:a.id,to:e.to,relations:o,declared:o.some(e=>p.has(e))}),i.set(a.id,(i.get(a.id)||0)+1),i.set(e.to,(i.get(e.to)||0)+1))}return{links:r,degree:i}}function y(e,t){let n=e*2.399963,r=18*Math.sqrt(e)+(t>200?40:0);return{x:Math.cos(n)*r,y:Math.sin(n)*r}}var b=9e3,x=.012,S=130,C=6e-4,w=.82;function te(e,t,n){for(let t=0;t<e.length;t+=1){let r=e[t];for(let i=t+1;i<e.length;i+=1){let a=e[i],o=r.x-a.x,s=r.y-a.y,c=o*o+s*s;c<1&&(o=(t-i)*.5,s=.5,c=o*o+s*s);let l=Math.sqrt(c),u=b*n/c,d=o/l*u,f=s/l*u;r.vx+=d,r.vy+=f,a.vx-=d,a.vy-=f}}let r=new Map(e.map(e=>[e.id,e]));for(let e of t){let t=r.get(e.from),i=r.get(e.to);if(!t||!i)continue;let a=i.x-t.x,o=i.y-t.y,s=Math.sqrt(a*a+o*o)||1,c=(s-S)*x*n,l=a/s*c,u=o/s*c;t.vx+=l,t.vy+=u,i.vx-=l,i.vy-=u}for(let t of e)t.vx-=t.x*C*n,t.vy-=t.y*C*n,t.vx*=w,t.vy*=w,t.x+=t.vx,t.y+=t.vy}function T(e,t,n){let r=new Map(e.map(e=>[e.id,e]));return t.map((e,i)=>{let a=r.get(e.id)??y(i,t.length);return{id:e.id,x:a.x,y:a.y,vx:0,vy:0,record:e,degree:n.get(e.id)||0}})}function E(e,t,n,r){let i=n-e,a=r-t,o=Math.sqrt(i*i+a*a)||1,s=Math.min(o*.18,60);return`M ${e} ${t} Q ${(e+n)/2-a/o*s} ${(t+r)/2+i/o*s} ${n} ${r}`}var D=.08;function O(e,t,n,r){let i=Math.min(4,Math.max(D,e.k*r));return{k:i,x:t-(t-e.x)/e.k*i,y:n-(n-e.y)/e.k*i}}function k(e,t,n){return{x:t-e.x,y:n-e.y}}function A(e){let t=1/0,n=1/0,r=-1/0,i=-1/0;for(let a of e)t=Math.min(t,a.x),n=Math.min(n,a.y),r=Math.max(r,a.x),i=Math.max(i,a.y);return{minX:t,minY:n,maxX:r,maxY:i}}var j=n(),M=`workfile-workflow-filters`;function N(){let e={relations:[...h],kinds:[...g],hideIsolated:!0};try{let t=localStorage.getItem(M);if(!t)return e;let n=JSON.parse(t);return{relations:Array.isArray(n.relations)?n.relations:e.relations,kinds:Array.isArray(n.kinds)?n.kinds:e.kinds,hideIsolated:typeof n.hideIsolated==`boolean`?n.hideIsolated:e.hideIsolated}}catch{return e}}function P({on:e,onClick:t,children:n,dashed:r}){return(0,j.jsx)(`button`,{type:`button`,"aria-pressed":e,onClick:t,className:s(`shrink-0 rounded-full border px-2 py-0.5 text-[11px] whitespace-nowrap transition-colors`,e?`border-ring bg-accent text-foreground`:`border-border text-muted-foreground hover:bg-accent/50`,r&&`border-dashed`),children:n})}function F({selectedId:e,onSelect:t,filters:n}){let[p,h]=(0,d.useState)(null),[g,_]=(0,d.useState)(null),v=(0,d.useRef)(N()),[y,b]=(0,d.useState)(()=>new Set(v.current.relations)),[x,S]=(0,d.useState)(()=>new Set(v.current.kinds)),[C,w]=(0,d.useState)(v.current.hideIsolated),[D,F]=(0,d.useState)(null),I=(0,d.useRef)(0),[L,R]=(0,d.useState)({x:0,y:0,k:1}),[,z]=(0,d.useState)(0),B=(0,d.useRef)({nodes:[],links:[],alpha:0}),V=(0,d.useRef)(null),H=(0,d.useRef)(!1);(0,d.useEffect)(()=>{let e=!0;return c.graph().then(t=>{e&&h(t.records)}).catch(t=>{e&&_(t.message)}),()=>{e=!1}},[]),(0,d.useEffect)(()=>{localStorage.setItem(M,JSON.stringify({relations:[...y],kinds:[...x],hideIsolated:C}))},[y,x,C]);let U=(0,d.useMemo)(()=>ee(p??[],{relations:y,kinds:x,hideIsolated:C,record:n}),[p,x,y,C,n]),W=(0,d.useMemo)(()=>Object.entries(n).filter(([,e])=>e).map(([e,t])=>`${e} ${t}`),[n]);(0,d.useEffect)(()=>{B.current.nodes=T(B.current.nodes,U.records,U.degree),B.current.links=U.links,B.current.alpha=1,z(e=>e+1)},[U]);let G=(0,d.useCallback)(()=>{let e=B.current.nodes,t=V.current;if(!e.length||!t)return;let n=t.getBoundingClientRect(),{minX:r,minY:i,maxX:a,maxY:o}=A(e),s=Math.min(3,Math.max(.15,Math.min(n.width/(a-r+160),n.height/(o-i+160))));R({k:s,x:n.width/2-(r+a)/2*s,y:n.height/2-(i+o)/2*s})},[]);(0,d.useEffect)(()=>{let e=0,t=()=>{let n=B.current;n.alpha>.02&&n.nodes.length&&(te(n.nodes,n.links,n.alpha),n.alpha*=.97,H.current||G(),z(e=>e+1)),e=requestAnimationFrame(t)};return e=requestAnimationFrame(t),()=>cancelAnimationFrame(e)},[G]);let ne=e=>{e.preventDefault();let t=V.current?.getBoundingClientRect();if(!t)return;let n=e.clientX-t.left,r=e.clientY-t.top;H.current=!0;let i=e.deltaY<0?1.12:.89;R(e=>O(e,n,r,i))},K=(0,d.useRef)(null),re=e=>{H.current=!0,K.current={x:e.clientX-L.x,y:e.clientY-L.y},e.target.setPointerCapture?.(e.pointerId)},ie=e=>{let t=K.current;if(!t)return;let n=k(t,e.clientX,e.clientY);R(e=>({...e,...n}))},q=()=>{K.current=null},J=(e,t,n)=>{let r=new Set(e);r.has(n)?r.delete(n):r.add(n),t(r)},Y=B.current.nodes,X=(0,d.useMemo)(()=>new Map(Y.map(e=>[e.id,e])),[Y,L]),Z=D??e,Q=Z?X.get(Z):void 0,$=(0,d.useMemo)(()=>{if(!Z)return null;let e=new Set([Z]);for(let t of U.links)t.from===Z&&e.add(t.to),t.to===Z&&e.add(t.from);return e},[Z,U.links]);return g?(0,j.jsxs)(`div`,{className:`p-6 text-sm text-muted-foreground`,children:[`The graph could not be read: `,g]}):(0,j.jsxs)(`div`,{className:`flex h-full min-h-0 flex-col`,children:[(0,j.jsxs)(l,{gutter:`3`,className:`shrink-0 border-b py-2`,after:(0,j.jsxs)(j.Fragment,{children:[(0,j.jsxs)(`span`,{className:`hidden text-[11px] whitespace-nowrap text-muted-foreground sm:inline`,children:[U.records.length,` nodes · `,U.links.length,` `,`edges`]}),(0,j.jsxs)(o,{type:`button`,variant:`outline`,size:`sm`,className:`shrink-0 px-2`,onClick:()=>{H.current=!1,G()},children:[(0,j.jsx)(r,{"aria-hidden":`true`,className:`size-3`}),`Fit`]})]}),children:[(0,j.jsx)(`div`,{className:`flex shrink-0 items-center gap-1`,children:m.map(e=>(0,j.jsx)(P,{on:x.has(e.id),onClick:()=>J(x,S,e.id),children:e.label},e.id))}),(0,j.jsx)(`span`,{className:`h-4 w-px shrink-0 bg-border`,"aria-hidden":`true`}),(0,j.jsx)(`div`,{className:`flex shrink-0 items-center gap-1`,children:f.map(e=>(0,j.jsx)(P,{on:y.has(e.id),dashed:!e.declared,onClick:()=>J(y,b,e.id),children:e.label},e.id))}),(0,j.jsx)(`span`,{className:`h-4 w-px shrink-0 bg-border`,"aria-hidden":`true`}),(0,j.jsx)(P,{on:C,onClick:()=>w(!C),children:`hide isolated`})]}),(0,j.jsxs)(`div`,{className:`relative min-h-0 flex-1 overflow-hidden`,children:[p?null:(0,j.jsxs)(`div`,{className:`flex h-full items-center justify-center gap-2 text-sm text-muted-foreground`,children:[(0,j.jsx)(i,{"aria-hidden":`true`,className:`size-4 animate-spin`}),`Reading the graph…`]}),p?.length&&!U.records.length?(0,j.jsx)(`div`,{className:`absolute inset-0 flex flex-col items-center justify-center gap-1.5 px-6 text-center text-sm text-muted-foreground`,children:U.isolated?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsxs)(`span`,{children:[U.isolated,` `,U.isolated===1?`record matches`:`records match`,`, and`,` `,U.isolated===1?`it is`:`none is`,` `,`connected to anything else here.`]}),(0,j.jsx)(o,{type:`button`,variant:`outline`,size:`sm`,className:`px-2`,onClick:()=>w(!1),children:`Show unconnected records`})]}):(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(`span`,{children:`No records match these filters.`}),(0,j.jsx)(`span`,{className:`text-xs`,children:W.length?`${W.join(`, `)} above, and ${x.size} of ${m.length} kinds here.`:`${x.size} of ${m.length} kinds and ${y.size} of ${f.length} relationships.`})]})}):null,(0,j.jsxs)(`svg`,{ref:V,role:`presentation`,className:`size-full cursor-grab touch-none active:cursor-grabbing`,onWheel:ne,onPointerDown:re,onPointerMove:ie,onPointerUp:q,onPointerLeave:q,children:[(0,j.jsx)(`defs`,{children:(0,j.jsx)(`marker`,{id:`workflow-arrow`,viewBox:`0 0 8 8`,refX:`7`,refY:`4`,markerWidth:`5`,markerHeight:`5`,orient:`auto-start-reverse`,children:(0,j.jsx)(`path`,{d:`M 0 1 L 7 4 L 0 7 z`,className:`fill-muted-foreground`})})}),(0,j.jsxs)(`g`,{transform:`translate(${L.x} ${L.y}) scale(${L.k})`,children:[U.links.map(e=>{let t=X.get(e.from),n=X.get(e.to);if(!t||!n)return null;let r=$&&!($.has(e.from)&&$.has(e.to));return(0,j.jsx)(`path`,{d:E(t.x,t.y,n.x,n.y),fill:`none`,markerEnd:`url(#workflow-arrow)`,className:s(`stroke-muted-foreground transition-opacity`,r?`opacity-10`:`opacity-45`),strokeWidth:1.2/L.k,strokeDasharray:e.declared?void 0:`${4/L.k} ${3/L.k}`,children:(0,j.jsx)(`title`,{children:`${e.from} → ${e.to}: ${e.relations.join(`, `)}`})},`${e.from}->${e.to}`)}),Y.map(n=>{let r=$&&!$.has(n.id),i=n.id===e,o=Math.min(16,6+Math.sqrt(n.degree)*2);return(0,j.jsxs)(`g`,{transform:`translate(${n.x} ${n.y})`,className:s(`cursor-pointer transition-opacity`,r&&`opacity-20`),onPointerEnter:()=>F(n.id),onPointerLeave:()=>F(null),onClick:e=>{e.stopPropagation(),I.current=performance.now(),t(n.id)},children:[(0,j.jsx)(`circle`,{r:o,style:{fill:a(n.record.status||`backlog`)},className:s(i?`stroke-foreground`:`stroke-background`),strokeWidth:(i?3:1.5)/L.k}),(0,j.jsx)(`title`,{children:`${n.id} — ${n.record.title}`}),L.k>.55||i||r===!1?(0,j.jsx)(`text`,{y:o+11/L.k,textAnchor:`middle`,className:`pointer-events-none fill-foreground`,style:{fontSize:`${11/L.k}px`},children:n.id}):null]},n.id)})]})]}),Q?(0,j.jsxs)(`div`,{className:`pointer-events-none absolute bottom-3 left-3 max-w-[min(30rem,70%)] rounded-md border bg-background/95 px-3 py-2 shadow-sm`,children:[(0,j.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,j.jsx)(`span`,{className:`font-mono text-[11px] font-medium`,children:Z}),(0,j.jsx)(u,{variant:`secondary`,className:`px-1.5 py-0 text-[10px] font-normal`,children:Q.record.recordType})]}),(0,j.jsx)(`p`,{className:`truncate text-xs text-muted-foreground`,children:Q.record.title})]}):null]})]})}export{F as WorkflowView};
+1
-1

@@ -21,3 +21,3 @@ export declare const SCHEMA_VERSION = 2;

*/
export declare const CARD_RESERVED_KEYS: readonly ["archived", "area", "body", "claimed_at", "claimed_by", "created", "depends", "due", "effort", "file", "id", "milestone", "origin", "parent", "priority", "related", "revision", "scope", "source", "start", "status", "tags", "title", "type", "updated", "verified", "verify"];
export declare const CARD_RESERVED_KEYS: readonly ["archived", "area", "body", "claimed_at", "claimed_by", "created", "depends", "due", "effort", "file", "id", "milestone", "origin", "parent", "priority", "raised", "related", "revision", "scope", "source", "start", "status", "tags", "title", "type", "updated", "verified", "verify"];
/** What an axis name may look like: a plain, greppable frontmatter key. */

@@ -24,0 +24,0 @@ export declare const AXIS_NAME_RE: RegExp;

@@ -60,2 +60,3 @@ export const SCHEMA_VERSION = 2;

"priority",
"raised",
"related",

@@ -62,0 +63,0 @@ "revision",

@@ -54,1 +54,15 @@ /**

export declare function resolveActor(options?: ResolveActorOptions): ResolvedActor;
/**
* The readable tail of a session id.
*
* A UUID's first block is already distinct enough to separate the handful of
* sessions that can share one checkout, and it stays short enough that the
* actor is still a name rather than a token.
*
* Exported because it is not only how an actor is *written*: it is also how a
* claim is read back. `claimSession` in `modules/cards/claims.ts` recovers the
* session from a `claimed_by` written by an earlier process, and a second copy
* of this normalization there would let the two drift apart silently — the
* comparison would start answering "different session" for one session.
*/
export declare function sessionDiscriminator(sessionId: string | undefined): string | undefined;

@@ -122,4 +122,10 @@ /**

* actor is still a name rather than a token.
*
* Exported because it is not only how an actor is *written*: it is also how a
* claim is read back. `claimSession` in `modules/cards/claims.ts` recovers the
* session from a `claimed_by` written by an earlier process, and a second copy
* of this normalization there would let the two drift apart silently — the
* comparison would start answering "different session" for one session.
*/
function sessionDiscriminator(sessionId) {
export function sessionDiscriminator(sessionId) {
if (!sessionId)

@@ -126,0 +132,0 @@ return undefined;

@@ -94,2 +94,19 @@ import { ValidationError } from "./errors.js";

const BLOCK_ITEM = /^(\s+)-\s?(.*)$/;
/**
* Trailing newlines removed without a regex.
*
* `replace(/\n+$/, "")` retries the anchored `+` from every start position, so a
* value that ends in anything other than a newline costs O(N²) — and this one is
* applied to a *body*, which nothing caps. A card title is refused past 80
* characters, so the same shape in the slug helpers is quadratic over a bounded
* input; a body read from `--body-file` is bounded by the disk. Found while
* writing the rule for T-0224, not by CodeQL, which reported only the copy whose
* taint it could follow.
*/
function stripTrailingNewlines(value) {
let end = value.length;
while (end > 0 && value[end - 1] === "\n")
end -= 1;
return end === value.length ? value : value.slice(0, end);
}
const BLOCK_SCALAR = /^([|>])([+-]?\d*)\s*$/;

@@ -261,3 +278,3 @@ /** ` - id: gate-test` — the line that opens one record in a `records` block. */

style === "literal"
? text.join("\n").replace(/\n+$/, "")
? stripTrailingNewlines(text.join("\n"))
: text.join(" ").replace(/\s+/g, " ").trim();

@@ -264,0 +281,0 @@ }

/** Convert the small glob subset used by Workfile into a RegExp.
* Supports `*`, `?`, and `**`; paths are always matched with `/` separators. */
export declare function globToRegExp(pattern: any): any;
/**
* Trailing separators removed, without a regex.
*
* `replace(/\/+$/, "")` is the obvious spelling and CodeQL is right about it:
* the engine retries the anchored `+` from every start position, so a value of N
* slashes costs O(N²). Nothing here is attacker-controlled — the values are
* config entries and repository paths a maintainer writes — but the loop is
* shorter than the argument for keeping the regex, and it was written five times
* across this package before this existed.
*/
export declare function stripTrailingSlashes(value: string): string;
export declare function normalizeRepoPath(value: any): string;

@@ -5,0 +16,0 @@ export declare function matchesAnyGlob(path: any, patterns: any): any;

@@ -56,2 +56,18 @@ import { readdir, stat } from "node:fs/promises";

}
/**
* Trailing separators removed, without a regex.
*
* `replace(/\/+$/, "")` is the obvious spelling and CodeQL is right about it:
* the engine retries the anchored `+` from every start position, so a value of N
* slashes costs O(N²). Nothing here is attacker-controlled — the values are
* config entries and repository paths a maintainer writes — but the loop is
* shorter than the argument for keeping the regex, and it was written five times
* across this package before this existed.
*/
export function stripTrailingSlashes(value) {
let end = value.length;
while (end > 0 && value[end - 1] === "/")
end -= 1;
return end === value.length ? value : value.slice(0, end);
}
export function normalizeRepoPath(value) {

@@ -58,0 +74,0 @@ return String(value).split(sep).join("/").replace(/^\.\//, "");

@@ -39,3 +39,3 @@ export type { AgentTarget, BaseProjectRecord, CardEffort, CardChanges, CardMutationOptions, CardPriority, CardRecord, CardStatus, CardType, ChangeRecord, ChangeVisibility, CiTarget, CreateCardInput, CreateChangeInput, CreateDocumentInput, CreateMemoryInput, CreateReleaseInput, DeepPartial, DocumentLayout, DoctorReport, DocumentRecord, EffectiveProjectSchema, HybridSearchOptions, MemoryCollection, MemoryRecord, ProjectAgentsConfig, ProjectCardsConfig, ProjectChangelogConfig, ProjectCiConfig, ProjectConfig, ProjectConfigInput, ProjectDiagnostic, ProjectDocsConfig, ProjectIntegration, ProjectIndex, ProjectMcpConfig, ProjectMemoryConfig, ProjectRecord, ProjectRecordLink, ProjectSearchConfig, ProjectSearchOptions, ProjectSearchResult, ProjectStorageConfig, ProjectUiConfig, ProjectWorkspace, ProjectWorkspacePaths, RecordMutationResult, ReleaseRecord, ReleaseStrategy, RevisionOptions, SemanticSearchMatch, SemanticSearchProvider, SemanticSearchRecord, WorkspaceVersion } from "./types.js";

export { runDoctor } from "./modules/health/doctor.js";
export { healDuplicateCardIds, healDuplicateRecordIds, renumberCard, renumberRecord, reslugStaleCardFiles } from "./modules/health/renumber.js";
export { healDuplicateCardIds, healDuplicateRecordIds, renumberCard, renumberRecord, reslugStaleCardFiles, reslugStaleRecordFiles } from "./modules/health/renumber.js";
export { HEALABLE_KINDS, byCodeUnit, classifyDuplicates, duplicateIssueMessage } from "./modules/health/duplicates.js";

@@ -42,0 +42,0 @@ export type { DuplicateClassification, DuplicateRefusal } from "./modules/health/duplicates.js";

@@ -33,3 +33,3 @@ export { defineProject } from "./config/define-project.js";

export { runDoctor } from "./modules/health/doctor.js";
export { healDuplicateCardIds, healDuplicateRecordIds, renumberCard, renumberRecord, reslugStaleCardFiles } from "./modules/health/renumber.js";
export { healDuplicateCardIds, healDuplicateRecordIds, renumberCard, renumberRecord, reslugStaleCardFiles, reslugStaleRecordFiles } from "./modules/health/renumber.js";
export { HEALABLE_KINDS, byCodeUnit, classifyDuplicates, duplicateIssueMessage } from "./modules/health/duplicates.js";

@@ -36,0 +36,0 @@ export { baselineMissing, diffAgainstBaseline, issueKey, readDoctorBaseline, writeDoctorBaseline } from "./modules/health/baseline.js";

@@ -134,4 +134,5 @@ import { readFile } from "node:fs/promises";

3. Relate it through \`parent\`, \`depends\`, \`source\` or record IDs.
4. Use \`idea\` only for unvalidated proposals; use a committed work type when a decision already exists.
5. Do not change owner priorities without explicit authorization.`,
4. Set \`raised\`: \`reported\` when a person asked for it, \`derived\` when you inferred it from the repository. Work you found is \`derived\` — say so rather than leaving it blank, because the difference is unrecoverable once the session ends and a reported card is a commitment to somebody where a derived one is a proposal.
5. Use \`idea\` only for unvalidated proposals; use a committed work type when a decision already exists.
6. Do not change owner priorities without explicit authorization.`,
"record-knowledge": `# Record knowledge

@@ -138,0 +139,0 @@

@@ -16,2 +16,12 @@ import { access, readdir, readFile } from "node:fs/promises";

import { CARD_EFFORTS, CARD_PRIORITIES, CARD_STATUSES, CARD_TYPES } from "../../config/defaults.js";
/**
* The first day `raised` could be answered, so the rule below can be quiet about
* every card filed before it.
*
* A date rather than a config value: a project does not get to choose when this
* field became available in the package it installed, and a knob here would only
* be used to switch the rule off — which `doctor --severity` and the baseline
* already do, per project, with a record of the decision.
*/
const RAISED_EXPECTED_FROM = "2026-08-08";
export const CARD_LIST_KEYS = new Set([

@@ -253,14 +263,30 @@ "tags",

}
else if (card.id &&
card.title &&
basename(card.file || "") !== cardFileName(card.id, card.title)) {
// Creating a card derives the filename from the title; retitling it
// never revisited that, so a file could sit for months named after a
// title the card no longer has. The filename is the handle people
// and agents grep by, and a stale one misdirects long after anyone
// remembers the rename. A warning rather than an error: the record
// is intact and only its label has drifted, and renaming on every
// title edit would churn history and break open editor buffers —
// so the repair is `doctor --fix`, when the reader asks for it.
issues.push(issue("warning", "filename-stale", card, `Filename no longer matches the title; \`doctor --fix\` renames it to ${cardFileName(card.id, card.title)}`));
// The stale-filename rule used to sit here, and it only ever covered
// cards. It moved to `health/filenames.ts`, which is the layer that
// holds every kind: memory records, managed documents and unreleased
// changelog fragments derive their names from their titles identically
// and had no rule at all (T-0223). The same argument
// `duplicate-record-id` makes — a module sees one kind, and this
// question is about all of them.
/**
* A card that does not say whether a person asked for it.
*
* `warning`, not `error`, because it is a fact about how the card was
* filed and not a defect in the record — and because the repair is a
* judgement only the filer can make.
*
* Bounded by date, and that is the part the card did not settle. Every
* card written before the field existed carries none, and this repository
* alone holds 223 of them; reporting all of them would drown the doctor
* on the day the field shipped and teach everyone to ignore the rule.
* Backfilling is not available either — guessing which of them were
* reported would reproduce the exact error that prompted T-0210. So the
* rule speaks about cards filed from the day it could be answered, and
* says nothing about the ones that could not.
*/
if (!card.raised &&
String(card.created || "") >= RAISED_EXPECTED_FROM &&
!card.archived) {
issues.push(issue("warning", "raised-missing", card, "Does not say whether a person reported it or it was derived; " +
"set `raised: reported` or `raised: derived`"));
}

@@ -267,0 +293,0 @@ if ((card.title || "").length > 80) {

@@ -35,3 +35,3 @@ /** Default window within which a session is considered still present. */

/** The board's view of one card, or null when it holds no claim. */
export declare function claimBoardEntry(card: any): {
export declare function claimBoardEntry(card: any, sessions?: any[]): {
id: any;

@@ -42,2 +42,17 @@ title: any;

claimedAt: any;
/**
* The session this claim belongs to, resolved here because here is where
* the session files are in hand (T-0219).
*
* The board carried `claimedBy` and nothing else, so the scope guard —
* which reads only this file — could recover a session from the actor's
* tail and no other way. A `claimed_by` written from an explicit
* `--actor` has no tail, so two agents sharing one saw a string equal to
* their own and the guard stayed silent. That is the residual ADR-0020
* left open, and LRN-0030 records it.
*
* `null` when there is none to find, which the guard has to treat as
* "unproven" rather than as "the same process".
*/
session: string;
scope: any;

@@ -105,2 +120,46 @@ };

/**
* The session a claim was made from, however it can be recovered.
*
* Two places carry it and neither is always present. A live session file knows
* its own id; a `claimed_by` written by any process that resolved its own actor
* carries the discriminator in its tail, which outlives the session file and
* survives into git. Normalized through `sessionDiscriminator` so a full id from
* a session file and an eight-character tail from an actor compare equal.
*/
export declare function claimSession(claim: {
by?: string | null;
sessionId?: string | null;
}): string | null;
/**
* What tells two claims apart, or nothing if they are one process.
*
* The rule this replaces compared `claimed_by`, and that reads as a session only
* by accident: `resolveActor` appends a session discriminator, so two agents
* *usually* differ. Two plain terminals resolve to the same `user@host` and were
* dropped as one person; so were two agents that were handed the same `--actor`.
* Both are two processes about to overwrite each other.
*
* So the question is not "same actor" but "provably the same process", and the
* answer names its own evidence, because the three cases are not equally strong:
*
* - `sessions-differ` — two sessions, seen. Two processes.
* - `actors-differ` — no session either side, different actors. Two people.
* - `unproven` — no session either side and the same actor. One person holding
* two overlapping cards and two terminals racing each other are the same
* record; nothing in the workspace distinguishes them.
*
* `unproven` is reported rather than dropped, and that is the decision T-0206
* had to make. Silence is the bug — it is what let two terminals collide with
* no trace. But a consumer that interrupts somebody must be able to tell a
* verdict from a guess, which is what the label is for: the popover can show it,
* and the scope guard does not prompt on it (see `plugins/workfile/runtime/hooks.mjs`).
*/
export declare function claimSeparation(a: {
by?: string | null;
sessionId?: string | null;
}, b: {
by?: string | null;
sessionId?: string | null;
}): "sessions-differ" | "actors-differ" | "unproven" | null;
/**
* Everything that is happening in the workspace right now.

@@ -107,0 +166,0 @@ *

import { readdir, readFile, rm } from "node:fs/promises";
import { join } from "node:path";
import { sessionDiscriminator } from "../../core/actor.js";
import { writeFileAtomic } from "../../core/filesystem.js";

@@ -129,3 +130,3 @@ import { withFileLock } from "../../core/locks.js";

/** The board's view of one card, or null when it holds no claim. */
export function claimBoardEntry(card) {
export function claimBoardEntry(card, sessions = []) {
if (!card?.claimed_by)

@@ -139,2 +140,17 @@ return null;

claimedAt: card.claimed_at,
/**
* The session this claim belongs to, resolved here because here is where
* the session files are in hand (T-0219).
*
* The board carried `claimedBy` and nothing else, so the scope guard —
* which reads only this file — could recover a session from the actor's
* tail and no other way. A `claimed_by` written from an explicit
* `--actor` has no tail, so two agents sharing one saw a string equal to
* their own and the guard stayed silent. That is the residual ADR-0020
* left open, and LRN-0030 records it.
*
* `null` when there is none to find, which the guard has to treat as
* "unproven" rather than as "the same process".
*/
session: sessionForClaim(card, sessions),
scope: Array.isArray(card.scope)

@@ -147,2 +163,18 @@ ? card.scope

}
/**
* The session behind a claim: the one that named this card, else the one
* belonging to this actor, else whatever the actor's tail carries.
*
* The first two are the same two-step `claimState` and the activity snapshot
* take, in that order and for the reason T-0206 established — a session that
* names the card beats one that merely shares an actor, because two agents can
* share an actor.
*/
function sessionForClaim(card, sessions) {
const match = sessions.find((candidate) => candidate.cardId === card.id) ||
sessions.find((candidate) => candidate.actor === card.claimed_by);
return (sessionDiscriminator(match?.sessionId) ??
claimSession({ by: card.claimed_by }) ??
null);
}
export async function readClaimBoard(workspace) {

@@ -177,3 +209,3 @@ try {

const claims = (board.claims || []).filter((claim) => claim.id !== card.id);
const entry = claimBoardEntry(card);
const entry = claimBoardEntry(card, await readAgentSessions(workspace, { now }));
if (entry)

@@ -186,3 +218,5 @@ claims.push(entry);

export async function rebuildClaimBoard(workspace, cards, { now = new Date() } = {}) {
return writeBoard(workspace, cards.map(claimBoardEntry).filter(Boolean), now);
// Read once for the whole sweep rather than per card.
const sessions = await readAgentSessions(workspace, { now });
return writeBoard(workspace, cards.map((card) => claimBoardEntry(card, sessions)).filter(Boolean), now);
}

@@ -219,3 +253,8 @@ /**

const ageMs = Number.isFinite(claimedAt) ? now.getTime() - claimedAt : null;
const session = sessions.find((candidate) => candidate.cardId === card.id || candidate.actor === card.claimed_by);
// The card's own session wins over any session merely sharing its actor.
// As one `find` over an `||` this returned whichever session came first, so
// two cards held by one actor string could both be attributed to the same
// session — which is exactly the evidence the conflict rule below reads.
const session = sessions.find((candidate) => candidate.cardId === card.id) ||
sessions.find((candidate) => candidate.actor === card.claimed_by);
const base = {

@@ -242,2 +281,51 @@ by: card.claimed_by,

/**
* The session a claim was made from, however it can be recovered.
*
* Two places carry it and neither is always present. A live session file knows
* its own id; a `claimed_by` written by any process that resolved its own actor
* carries the discriminator in its tail, which outlives the session file and
* survives into git. Normalized through `sessionDiscriminator` so a full id from
* a session file and an eight-character tail from an actor compare equal.
*/
export function claimSession(claim) {
const fromSession = sessionDiscriminator(claim.sessionId || undefined);
if (fromSession)
return fromSession;
const tail = /#([A-Za-z0-9]+)$/.exec(String(claim.by || ""));
return tail ? sessionDiscriminator(tail[1]) || null : null;
}
/**
* What tells two claims apart, or nothing if they are one process.
*
* The rule this replaces compared `claimed_by`, and that reads as a session only
* by accident: `resolveActor` appends a session discriminator, so two agents
* *usually* differ. Two plain terminals resolve to the same `user@host` and were
* dropped as one person; so were two agents that were handed the same `--actor`.
* Both are two processes about to overwrite each other.
*
* So the question is not "same actor" but "provably the same process", and the
* answer names its own evidence, because the three cases are not equally strong:
*
* - `sessions-differ` — two sessions, seen. Two processes.
* - `actors-differ` — no session either side, different actors. Two people.
* - `unproven` — no session either side and the same actor. One person holding
* two overlapping cards and two terminals racing each other are the same
* record; nothing in the workspace distinguishes them.
*
* `unproven` is reported rather than dropped, and that is the decision T-0206
* had to make. Silence is the bug — it is what let two terminals collide with
* no trace. But a consumer that interrupts somebody must be able to tell a
* verdict from a guess, which is what the label is for: the popover can show it,
* and the scope guard does not prompt on it (see `plugins/workfile/runtime/hooks.mjs`).
*/
export function claimSeparation(a, b) {
const left = claimSession(a);
const right = claimSession(b);
if (left && right)
return left === right ? null : "sessions-differ";
if (left || right)
return "sessions-differ";
return a.by === b.by ? "unproven" : "actors-differ";
}
/**
* Everything that is happening in the workspace right now.

@@ -270,3 +358,4 @@ *

const b = claims[right];
if (a.claim.by === b.claim.by)
const basis = claimSeparation(a.claim, b.claim);
if (!basis)
continue;

@@ -277,3 +366,3 @@ const shared = a.scope.filter((path) => b.scope.some((other) => path === other ||

if (shared.length) {
conflicts.push({ cards: [a.id, b.id], paths: shared });
conflicts.push({ cards: [a.id, b.id], paths: shared, basis });
}

@@ -280,0 +369,0 @@ }

/**
* The repository, asked two questions and nothing else.
* The repository, asked three questions and nothing else.
*
* A card that records the commit it was verified at needs to know what HEAD is,
* and `doctor` needs to know whether that commit is still reachable. Both are
* git questions, and this is the first subprocess anything under `src/` spawns —
* so the shape of it is worth stating rather than inferring.
* `doctor` needs to know whether that commit is still reachable, and a CI run
* that verifies the cards a branch touched needs to know which ones those are.
* All three are git questions, and this is the first subprocess anything under
* `src/` spawns — so the shape of it is worth stating rather than inferring.
*

@@ -57,1 +58,21 @@ * **Git is optional.** Nothing else in this package requires a repository, and

export declare function isAncestorOfHead(root: string, commit: string): Promise<"yes" | "no" | "unknown">;
/**
* The paths this branch touched, against a base ref.
*
* `base...HEAD` with three dots, which diffs from the merge base rather than
* from the tip of the base branch — the same thing a pull request shows. Two
* dots would report every file the base moved on since, so a branch that merely
* fell behind would look like it had touched cards it never opened, and CI would
* run their commands and write to them.
*
* `null` is "cannot answer", and every caller has to treat it as such rather
* than as "nothing changed". The distinction is the whole safety of the thing
* this feeds: a shallow CI checkout has no merge base, and reading that as an
* empty list would report a run that verified nothing as a run that found
* nothing to verify. Those are opposite claims about the same silence.
*
* The ref is checked against `SAFE_REF` before it becomes an argument. Nothing
* here goes through a shell, so this is not about metacharacters: it is about a
* value out of the environment beginning with `-` and being read as an option.
*/
export declare function changedPaths(root: string, base: string): Promise<string[] | null>;
/**
* The repository, asked two questions and nothing else.
* The repository, asked three questions and nothing else.
*
* A card that records the commit it was verified at needs to know what HEAD is,
* and `doctor` needs to know whether that commit is still reachable. Both are
* git questions, and this is the first subprocess anything under `src/` spawns —
* so the shape of it is worth stating rather than inferring.
* `doctor` needs to know whether that commit is still reachable, and a CI run
* that verifies the cards a branch touched needs to know which ones those are.
* All three are git questions, and this is the first subprocess anything under
* `src/` spawns — so the shape of it is worth stating rather than inferring.
*

@@ -137,1 +138,40 @@ * **Git is optional.** Nothing else in this package requires a repository, and

}
/** A ref as this module will pass one to git, which is deliberately narrow. */
const SAFE_REF = /^[0-9A-Za-z._\/-]{1,255}$/;
/**
* The paths this branch touched, against a base ref.
*
* `base...HEAD` with three dots, which diffs from the merge base rather than
* from the tip of the base branch — the same thing a pull request shows. Two
* dots would report every file the base moved on since, so a branch that merely
* fell behind would look like it had touched cards it never opened, and CI would
* run their commands and write to them.
*
* `null` is "cannot answer", and every caller has to treat it as such rather
* than as "nothing changed". The distinction is the whole safety of the thing
* this feeds: a shallow CI checkout has no merge base, and reading that as an
* empty list would report a run that verified nothing as a run that found
* nothing to verify. Those are opposite claims about the same silence.
*
* The ref is checked against `SAFE_REF` before it becomes an argument. Nothing
* here goes through a shell, so this is not about metacharacters: it is about a
* value out of the environment beginning with `-` and being read as an option.
*/
export async function changedPaths(root, base) {
if (!root || !SAFE_REF.test(String(base)))
return null;
// Resolved first, so a base ref this clone does not have is reported as
// "cannot answer" rather than as a diff against something else.
const resolved = await git(root, ["rev-parse", "--verify", `${base}^{commit}`]);
if (!resolved.ok)
return null;
const result = await git(root, [
"diff",
"--name-only",
"--diff-filter=d",
`${base}...HEAD`
]);
if (!result.ok)
return null;
return result.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
}

@@ -7,4 +7,6 @@ export { CRITERION_DIGEST, acceptanceSummary, applyAcceptance, criterionDigest, criterionOwners, normalizeCriterion, parseAcceptance, staleBindings, unreadableCriteria, verifyEntries } from "./acceptance.js";

export { runCardVerification } from "./runner.js";
export { verifyChangedCards } from "./changed.js";
export type { ChangedCardResult, ChangedCardsReport } from "./changed.js";
export type { VerifyEntryResult, VerifyOutcome, VerifyRunReport } from "./runner.js";
export { LIVE_WINDOW_MS, ORPHAN_WINDOW_MS, buildActivitySnapshot, claimBoardChanged, claimBoardEntry, claimState, readActiveLocks, readClaimBoard, rebuildClaimBoard, pruneAgentSessions, readAgentSessions, recordAgentSignal, updateClaimBoard } from "./claims.js";
export { LIVE_WINDOW_MS, ORPHAN_WINDOW_MS, buildActivitySnapshot, claimBoardChanged, claimBoardEntry, claimSeparation, claimSession, claimState, readActiveLocks, readClaimBoard, rebuildClaimBoard, pruneAgentSessions, readAgentSessions, recordAgentSignal, updateClaimBoard } from "./claims.js";
export { REQUESTABLE_VERIFICATION_METHODS, VERIFICATION_METHODS, VERIFIED_DIGEST, VERIFIED_FIELDS, criteriaDigest, resolveVerification, verifiedCommit, verifiedProblems } from "./verification.js";

@@ -11,0 +13,0 @@ export type { VerifiedBlock, VerificationIntent } from "./verification.js";

@@ -9,3 +9,4 @@ export { CRITERION_DIGEST, acceptanceSummary, applyAcceptance, criterionDigest, criterionOwners, normalizeCriterion, parseAcceptance, staleBindings, unreadableCriteria, verifyEntries } from "./acceptance.js";

export { runCardVerification } from "./runner.js";
export { LIVE_WINDOW_MS, ORPHAN_WINDOW_MS, buildActivitySnapshot, claimBoardChanged, claimBoardEntry, claimState, readActiveLocks, readClaimBoard, rebuildClaimBoard, pruneAgentSessions, readAgentSessions, recordAgentSignal, updateClaimBoard } from "./claims.js";
export { verifyChangedCards } from "./changed.js";
export { LIVE_WINDOW_MS, ORPHAN_WINDOW_MS, buildActivitySnapshot, claimBoardChanged, claimBoardEntry, claimSeparation, claimSession, claimState, readActiveLocks, readClaimBoard, rebuildClaimBoard, pruneAgentSessions, readAgentSessions, recordAgentSignal, updateClaimBoard } from "./claims.js";
export { REQUESTABLE_VERIFICATION_METHODS, VERIFICATION_METHODS, VERIFIED_DIGEST, VERIFIED_FIELDS, criteriaDigest, resolveVerification, verifiedCommit, verifiedProblems } from "./verification.js";

@@ -12,0 +13,0 @@ export { COMMIT_SHA, headCommit, isAncestorOfHead, isShallowRepository } from "./git.js";

@@ -14,2 +14,22 @@ /**

/**
* How a card came to be on the board, which nothing recorded.
*
* Asked where one of eight cards came from, the record could not answer: the
* commit message that filed them was read, the grouping of its paragraphs was
* taken as evidence that the card was the agent's own, and it was wrong — it was
* item six of a list the owner had written out. The fields that look like they
* should carry this are both something else. `origin` takes record ids, which is
* the provenance of *discovered* work: what were you doing when you found this.
* `source` takes a repository-relative path and is checked on disk, so a report
* made in conversation has nothing to put there.
*
* Two values, and the smallness is the decision. A person reported it, or it was
* derived from reading the code. More than two and nobody picks correctly — and
* the distinction that actually changes behaviour is exactly this one: a reported
* card is a commitment to somebody, and a derived card is a proposal that costs
* nothing to discard. Six months of the two mixed together is a backlog nobody
* can prioritise (T-0210).
*/
export declare const CARD_RAISED_VALUES: readonly string[];
/**
* The axes this workspace declares, as `[name, vocabulary]` pairs.

@@ -16,0 +36,0 @@ *

import { ARGV_CONTROL_CHARACTER_RE, CARD_EFFORTS, CARD_PRIORITIES, CARD_STATUSES, CARD_TYPES, VERIFICATION_POLICY_DEFAULT_AREA, VERIFY_TIMEOUT_SECONDS_DEFAULT } from "../../config/defaults.js";
import { stripTrailingSlashes } from "../../core/glob.js";
import { ValidationError } from "../../core/errors.js";

@@ -34,4 +35,25 @@ import { CRITERION_DIGEST, parseAcceptance, staleBindings, verifyEntries } from "./acceptance.js";

"origin",
"raised",
"verify"
]);
/**
* How a card came to be on the board, which nothing recorded.
*
* Asked where one of eight cards came from, the record could not answer: the
* commit message that filed them was read, the grouping of its paragraphs was
* taken as evidence that the card was the agent's own, and it was wrong — it was
* item six of a list the owner had written out. The fields that look like they
* should carry this are both something else. `origin` takes record ids, which is
* the provenance of *discovered* work: what were you doing when you found this.
* `source` takes a repository-relative path and is checked on disk, so a report
* made in conversation has nothing to put there.
*
* Two values, and the smallness is the decision. A person reported it, or it was
* derived from reading the code. More than two and nobody picks correctly — and
* the distinction that actually changes behaviour is exactly this one: a reported
* card is a commitment to somebody, and a derived card is a proposal that costs
* nothing to discard. Six months of the two mixed together is a backlog nobody
* can prioritise (T-0210).
*/
export const CARD_RAISED_VALUES = Object.freeze(["reported", "derived"]);
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;

@@ -445,2 +467,20 @@ const TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;

}
// T-0161. The third relationship field, which had a `doctor` rule and no
// write-time guard — so `card create --title X --origin T-0001` allocating
// `T-0001` reported success and left the repository in a state `doctor`
// calls an error. The pre-commit hook then refuses the next commit, for a
// card written minutes earlier by a command that said it worked.
//
// Existence is deliberately not checked here, unlike `parent` and
// `depends`. An origin may legitimately name a record that does not exist
// yet — a card can come out of a decision still being written — which is
// why `missing-origin` stays a `doctor` rule and this is not.
for (const origin of candidate.origin || []) {
if (origin === currentId || origin === candidate.id) {
fail("CARD_SELF_ORIGIN", "A card cannot originate from itself.");
}
}
if (candidate.raised && !CARD_RAISED_VALUES.includes(candidate.raised)) {
fail("CARD_RAISED_INVALID", `raised must be one of ${CARD_RAISED_VALUES.join(", ")}.`);
}
const hasActor = Boolean(candidate.claimed_by);

@@ -461,6 +501,3 @@ const hasTimestamp = Boolean(candidate.claimed_at);

function normalizeScopePath(value) {
return String(value || "")
.replaceAll("\\", "/")
.replace(/^\.\//, "")
.replace(/\/+$/, "");
return stripTrailingSlashes(String(value || "").replaceAll("\\", "/").replace(/^\.\//, ""));
}

@@ -467,0 +504,0 @@ export function scopesOverlap(left = [], right = []) {

export declare const CHANGE_LIST_KEYS: Set<string>;
export declare const CHANGE_REQUIRED_KEYS: readonly string[];
export declare const RELEASE_REQUIRED_KEYS: readonly string[];
/** The filename a changelog fragment with this id and title would get today. */
export declare function fragmentFileName(id: any, title: any): string;
export declare function loadChangelog(workspace: any): Promise<{

@@ -5,0 +7,0 @@ fragments: any[];

@@ -39,2 +39,6 @@ import { randomUUID } from "node:crypto";

const SEMVER_RE = /^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
/** The filename a changelog fragment with this id and title would get today. */
export function fragmentFileName(id, title) {
return `${id}-${slugify(title)}.md`;
}
function slugify(value, fallback = "change") {

@@ -41,0 +45,0 @@ return (String(value)

@@ -0,1 +1,2 @@

import { stripTrailingSlashes } from "../../core/glob.js";
import { chmod, readFile } from "node:fs/promises";

@@ -29,11 +30,63 @@ import { resolve } from "node:path";

* `project.config.mjs` out of the checkout — so on a pull request this job
* executes code the pull request wrote, before it reads a single card. That is
* the ordinary cost of building a pull request rather than a defect, and it is
* why the useful controls here are about what the job *holds* rather than about
* what it runs. The three targets differ sharply on that, and each says what it
* can enforce and what it cannot.
* executes code the pull request wrote, before it reads a single card. Two hops:
* `doctor` then calls every `healthCheck` that module declared. That is the
* ordinary cost of building a pull request rather than a defect, and it is why
* the useful controls here are about what the job *holds* rather than about what
* it runs. The three targets differ sharply on that, and each says what it can
* enforce and what it cannot.
*
* Each generated file states both hops, because a reader who only knows the
* first will price this wrongly: "it imports a config file" sounds like reading
* settings, and it is not. See ADR-0019 and LRN-0028.
*/
const EXECUTES_REPOSITORY_CODE = [
"Both commands load the workspace, which `import()`s project.config.mjs from",
"the checkout: this job runs that file's module body, and `doctor` then calls",
"every healthCheck and search provider it declares. On a pull request that is",
"code the pull request wrote. Nothing in Workfile sandboxes it — containment",
"is whatever this job holds, described below."
];
function executesRepositoryCode() {
return EXECUTES_REPOSITORY_CODE.map((line) => `# ${line}`).join("\n");
}
/**
* Why the card runner and the write token are in different jobs.
*
* T-0189. A card may bind a criterion to a command, and running that command is
* the only thing that can check it. So one job here executes commands a pull
* request declared, and it must therefore hold nothing at all — `permissions:
* {}`, no credentials left in `.git/config`, no secrets a fork could reach.
*
* But the evidence has to be written back, and writing to the repository needs
* `contents: write`. Putting that scope on the job that runs card commands would
* hand a token to a process a pull request configured, which is the whole thing
* ADR-0019 exists to say out loud. So the run and the write are two jobs: the
* first produces a patch bounded to the protocol directory, the second applies
* it and holds no repository code at all — it never invokes Workfile, because
* every Workfile command `import()`s `project.config.mjs` from the checkout.
*
* On a fork the second job cannot write whatever this file says: GitHub issues a
* read-only token for `pull_request` from a fork, so the push fails and nothing
* is recorded. That is a fail-closed enforced by the platform rather than by our
* condition, and the condition below is documentation of it.
*/
const TWO_JOB_SPLIT = [
"The job that runs card-declared commands holds nothing, and the job that",
"holds a write token runs no repository code. They cannot be one job: a",
"criterion bound to a command can only be checked by running it, and a",
"process a pull request configured must not be handed a token. See T-0189.",
"On a fork the write token is read-only whatever this file says, so nothing",
"is recorded there — GitHub enforces that, not the condition below."
];
function twoJobSplit(indent = "# ") {
return TWO_JOB_SPLIT.map((line) => `${indent}${line}`).join("\n");
}
function githubBody(workspace) {
const node = String(workspace.config.ci.nodeVersion || "22");
const protocolRoot = stripTrailingSlashes(String(workspace.config.storage.root || ".project")
.replace(/\\/g, "/")
.replace(/^\.\//, ""));
return `# Generated by @illodev/workfile ${PACKAGE_VERSION}
#
${executesRepositoryCode()}
name: Workfile

@@ -71,2 +124,102 @@

run: npx --yes @illodev/workfile@${PACKAGE_VERSION} agents check --json
${twoJobSplit(" # ")}
cards:
# Only on a pull request. "The cards this branch touched" is a diff against a
# base, and a push to a default branch has none — running here would answer
# for whatever ref happened to resolve.
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 20
# Nothing. This job runs commands the pull request declared.
permissions: {}
steps:
- uses: actions/checkout@v4
with:
# The merge base is what a card diff is taken from, and a shallow
# checkout has none. \`changedPaths\` reports that as "cannot answer"
# rather than as an empty diff, so a shallow clone here would fail the
# job rather than silently verify nothing — but it would still fail.
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: "${node}"
# Values arrive through \`env:\`, never interpolated into the script. A
# \`\${{ }}\` inside a \`run:\` block is expanded before the shell sees it, so a
# branch name is code there; as an environment variable it is data.
- name: Verify the cards this branch touched
env:
BASE_REF: \${{ github.base_ref }}
HEAD_SHA: \${{ github.event.pull_request.head.sha }}
RUN_URL: \${{ github.server_url }}/\${{ github.repository }}/actions/runs/\${{ github.run_id }}
run: |
npx --yes @illodev/workfile@${PACKAGE_VERSION} card verify --changed \\
--base "origin/$BASE_REF" \\
--close --run "$RUN_URL" --commit "$HEAD_SHA" \\
--json > workfile-cards.json
# Bounded to the protocol directory at the point it is produced, so the
# job that applies it is not the only thing standing between a card
# command and the rest of the repository.
- name: Collect what the run wrote
if: always()
run: git diff -- ${protocolRoot} > workfile-cards.patch || true
- uses: actions/upload-artifact@v4
if: always()
with:
name: workfile-cards
path: |
workfile-cards.json
workfile-cards.patch
if-no-files-found: ignore
record:
needs: cards
# Same-repository pull requests only. A fork gets a read-only token whatever
# this says, so the push there fails rather than being refused by us; this
# condition keeps the job from starting in order to say so.
if: >-
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: write
steps:
# The head branch, not the merge commit: a commit is being pushed to it.
- uses: actions/checkout@v4
with:
ref: \${{ github.event.pull_request.head.ref }}
- uses: actions/download-artifact@v4
with:
name: workfile-cards
# This job runs no Workfile command, deliberately. Every one of them
# \`import()\`s project.config.mjs from the checkout, which is the code this
# job exists not to execute while holding a write token.
- name: Refuse a patch that reaches outside the protocol directory
run: |
test -s workfile-cards.patch || exit 0
git apply --check workfile-cards.patch
if git apply --numstat workfile-cards.patch | cut -f3 |
grep -qv '^${protocolRoot}/'; then
echo "::error::refusing a patch that reaches outside ${protocolRoot}/"
exit 1
fi
# No loop, for two independent reasons and the first one is the load
# bearing one: a push made with GITHUB_TOKEN does not start a workflow
# run, so this does not re-enter. Observed as a run created in
# \`action_required\` that never executes. And if it did run, it would find
# the cards already recorded, produce an empty patch and commit nothing —
# which is what makes the first reason safe to rely on rather than
# load-bearing on its own.
- name: Commit the evidence
run: |
test -s workfile-cards.patch || exit 0
git apply workfile-cards.patch
git add -- ${protocolRoot}
git diff --cached --quiet && exit 0
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git commit -m "Record card verification from CI"
git push
`;

@@ -78,2 +231,4 @@ }

#
${executesRepositoryCode()}
#
# GitLab has no per-job permission scope. This job sees every CI/CD variable

@@ -87,2 +242,16 @@ # that is not marked protected, and the branch rule below fires on any branch

# \`include: { local: .gitlab/workfile.yml }\` there or no pipeline runs.
#
# It also does not run the commands cards declare, and that is the same fact read
# once more. On GitHub those run in a job holding \`permissions: {}\`, with a
# second job doing the write — see T-0189. There is no scope to put them behind
# here: this job sees every unprotected variable in the project, so running a
# command a merge request declared would run it beside the credentials. Enabling
# it is a decision only the maintainer can make, and it needs the variables
# protected or absent first:
#
# - npx --yes @illodev/workfile@${PACKAGE_VERSION} card verify --changed
# --base "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
#
# Without \`--close\` it reports and writes nothing back, which is the half that
# needs no token at all.
project_protocol:

@@ -105,2 +274,4 @@ image: node:${node}-slim

#
${executesRepositoryCode()}
#
# There is no permission model to configure here: this script inherits the

@@ -111,2 +282,7 @@ # entire environment of whatever invokes it — a credential block on a build

# secrets" is something the caller has to arrange and this file can only state.
#
# Which is also why it does not run the commands cards declare. On GitHub those
# get a job that holds nothing and a separate one that writes — T-0189. Here
# there is nothing to hold them behind, so \`card verify --changed --base REF\`
# is left for a caller who knows what this script inherits.
set -eu

@@ -113,0 +289,0 @@

export declare const DOC_LIST_KEYS: Set<string>;
export declare const DOC_REQUIRED_KEYS: readonly string[];
/** The filename a managed document with this id and title would get today. */
export declare function documentFileName(id: any, title: any): string;
export declare function loadManagedDocuments(workspace: any): Promise<{

@@ -4,0 +6,0 @@ documents: any[];

@@ -31,2 +31,6 @@ import { createHash } from "node:crypto";

const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
/** The filename a managed document with this id and title would get today. */
export function documentFileName(id, title) {
return `${id}-${slugify(title)}.md`;
}
function slugify(title) {

@@ -33,0 +37,0 @@ return (String(title)

@@ -0,1 +1,2 @@

import { stripTrailingSlashes } from "../../core/glob.js";
import { stat } from "node:fs/promises";

@@ -164,3 +165,3 @@ import { posix, resolve } from "node:path";

const routeRoots = (workspace.config.docs.routeRoots || [])
.map((entry) => String(entry).replace(/^\.\//, "").replace(/\/+$/, ""))
.map((entry) => stripTrailingSlashes(String(entry).replace(/^\.\//, "")))
.filter(Boolean);

@@ -167,0 +168,0 @@ const ids = new Map();

@@ -8,2 +8,3 @@ import { join } from "node:path";

import { classifyDuplicates, duplicateIssueMessage } from "./duplicates.js";
import { staleFilenameIssue, staleFilenames } from "./filenames.js";
import { exists } from "../../core/fs-utils.js";

@@ -67,5 +68,15 @@ import { lockIsStale } from "../../core/locks.js";

}));
/**
* Each reporter's findings, tagged with who produced them.
*
* The module is named here because here is the only place that knows: the
* core reporters return `{ counts, ok, issues }` and no module — only the
* integration registry returns one, which is what T-0218 assumed of all of
* them. Tagged rather than mutated, so the shared report objects hanging off
* the index are left as they are for the routes that also serve them.
*/
const reports = [];
const from = (module, report) => reports.push({ module, issues: report.issues || [] });
if (workspace.config.cards.enabled) {
reports.push(await diagnoseCards({
from("cards", await diagnoseCards({
cards: index.records.filter((record) => record.kind === "card"),

@@ -88,12 +99,12 @@ unreadable: index.unreadable.cards,

if (workspace.config.docs.enabled)
reports.push(index.reports.docs);
from("docs", index.reports.docs);
if (workspace.config.changelog.enabled)
reports.push(index.reports.changelog);
from("changelog", index.reports.changelog);
if (workspace.config.memory.enabled)
reports.push(index.reports.memory);
from("memory", index.reports.memory);
if (workspace.config.agents.enabled) {
reports.push(await checkAgentInstructions(workspace));
from("agents", await checkAgentInstructions(workspace));
}
if (workspace.config.ci.enabled && workspace.config.ci.targets.length) {
reports.push(await checkCiTemplates(workspace));
from("ci", await checkCiTemplates(workspace));
}

@@ -109,6 +120,26 @@ const integrationRegistry = options.integrationRegistry ||

// beside it that names nothing to run.
// Every kind's stale filenames, answered here for the same reason duplicate
// identity is: a module sees one kind, and this question is about all of them
// (T-0223). Attributed to the module that owns each record so the field
// T-0218 added stays honest.
for (const entry of staleFilenames(index.records)) {
reports.push({ module: entry.module, issues: [staleFilenameIssue(entry)] });
}
const duplicates = classifyDuplicates(index);
const claimed = new Set(duplicates.map((duplicate) => duplicate.id));
// The module rides along with each issue, which it did not: every reporter
// returns `{ module, issues }` and this flatten threw the module away, so
// what reached the reader was a flat list where nothing said where a finding
// came from. Mostly invisible, because a core `code` implies its module to
// anyone who knows the codebase — and not invisible at all for integrations,
// which are the one source that is not ours. A well-formed diagnostic
// returned by a repository's own `healthCheck` read exactly like one Workfile
// produced (T-0218).
//
// A field, and `code` deliberately untouched. Namespacing the code would be
// clearer and would change `issueIdentity`, which is what a baseline is
// matched by — so every baseline accepted with `--accept-baseline` would go
// stale at once, for a cosmetic gain.
const issues = reports
.flatMap((report) => report.issues)
.flatMap((report) => report.issues.map((issue) => issue.module ? issue : { ...issue, module: report.module }))
.filter((issue) => issue.code !== "duplicate-record-id" ||

@@ -120,2 +151,3 @@ !claimed.has(String(issue.id || "")));

severity: "warning",
module: "doctor",
code: "search-provider-unresolved",

@@ -146,2 +178,3 @@ message: `search.provider is "${workspace.config.search.provider}", but no declared integration with that id offers semantic search. Search runs lexical-only.`,

severity: "warning",
module: "doctor",
code: "verification-policy-area-unknown",

@@ -157,2 +190,3 @@ message: `cards.verification.methods names ${orphanedPolicy.join(", ")}, ` +

severity: "error",
module: "doctor",
code: "duplicate-record-id",

@@ -175,2 +209,3 @@ id: duplicate.id,

severity: "warning",
module: "doctor",
code: "stale-write-lock",

@@ -190,2 +225,3 @@ id: stale.owner?.metadata?.recordId,

severity: "info",
module: "doctor",
code: "legacy-planning-not-migrated",

@@ -192,0 +228,0 @@ file: ".planning/backlog/tasks",

@@ -101,2 +101,34 @@ /**

*/
/**
* Renames every record whose filename no longer matches its title.
*
* The card-only version is below and delegates to this. Driven off the index
* rather than off four loaders, because the index already holds every kind with
* the one thing this needs: the repository-relative path, whose directory is
* where the file goes and whose basename is what it is called. That is what
* makes an archived card, a memory collection and an unreleased fragment the
* same case here.
*
* Nothing rewrites references. Records are linked by id, and the id half of a
* filename does not move — only the slug does.
*
* The activity line is appended for cards alone, because cards are the only kind
* that carries a trail. A rename with no trail entry is not silent: it is a
* `git mv` in a diff, which for the other three kinds is the whole record of it.
*
* Which kinds are in scope, and why the others are not, is stated once in
* `filenames.ts` — the same function that decides what to report.
*/
export declare function reslugStaleRecordFiles(workspace: any, { actor, now, kinds }?: any): Promise<{
moves: {
id: string;
from: string;
to: string;
}[];
skipped: {
id: string;
file: string;
reason: string;
}[];
}>;
export declare function reslugStaleCardFiles(workspace: any, { actor, now }?: any): Promise<{

@@ -103,0 +135,0 @@ moves: {

@@ -17,2 +17,3 @@ import { readFile, rm } from "node:fs/promises";

import { classifyDuplicates } from "./duplicates.js";
import { staleFilenames } from "./filenames.js";
function escapeRegExp(value) {

@@ -409,47 +410,73 @@ return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");

*/
export async function reslugStaleCardFiles(workspace, { actor = null, now } = {}) {
/**
* Renames every record whose filename no longer matches its title.
*
* The card-only version is below and delegates to this. Driven off the index
* rather than off four loaders, because the index already holds every kind with
* the one thing this needs: the repository-relative path, whose directory is
* where the file goes and whose basename is what it is called. That is what
* makes an archived card, a memory collection and an unreleased fragment the
* same case here.
*
* Nothing rewrites references. Records are linked by id, and the id half of a
* filename does not move — only the slug does.
*
* The activity line is appended for cards alone, because cards are the only kind
* that carries a trail. A rename with no trail entry is not silent: it is a
* `git mv` in a diff, which for the other three kinds is the whole record of it.
*
* Which kinds are in scope, and why the others are not, is stated once in
* `filenames.ts` — the same function that decides what to report.
*/
export async function reslugStaleRecordFiles(workspace, { actor = null, now, kinds = null } = {}) {
ensureWritable(workspace);
const loaded = await loadCards(workspace);
const index = await buildProjectIndex(workspace);
const wanted = kinds ? new Set(kinds) : null;
const moves = [];
const skipped = [];
const taken = new Set(loaded.cards.map((card) => card.file));
for (const card of loaded.cards) {
if (!card.id || !card.title)
// Every path the workspace already holds, so a rename cannot land on one.
// Read once and kept current as moves happen, which is what makes two records
// wanting the same slug a skip rather than a lost file.
const taken = new Set(index.records.map((record) => normalizeRepoPath(record.path || "")));
for (const entry of staleFilenames(index.records)) {
const record = entry.record;
if (wanted && !wanted.has(record.kind))
continue;
if (!card.file?.startsWith(`${card.id}-`))
const from = normalizeRepoPath(record.path);
const directory = dirname(from);
const to = `${directory}/${entry.expected}`;
if (taken.has(to)) {
skipped.push({ id: record.id, file: entry.current, reason: "name-taken" });
continue;
const target = cardFileName(card.id, card.title);
if (target === card.file)
continue;
if (taken.has(target)) {
skipped.push({ id: card.id, file: card.file, reason: "name-taken" });
continue;
}
const directory = card.archived
? workspace.paths.cardArchive
: workspace.paths.cards;
const content = await readFile(join(directory, card.file), "utf8");
const trailed = workspace.config.cards.activityTrail !== false
? appendActivityLine(content, activityEntry(actor, `renamed file to ${target}`, now))
const absoluteFrom = join(workspace.root, from);
const content = await readFile(absoluteFrom, "utf8");
const written = record.kind === "card" && workspace.config.cards.activityTrail !== false
? appendActivityLine(content, activityEntry(actor, `renamed file to ${entry.expected}`, now))
: content;
try {
await createFileExclusive(join(directory, target), trailed);
await createFileExclusive(join(workspace.root, to), written);
}
catch (error) {
// `taken` was read before the loop started, so the name can be
// claimed underneath us — by another process, or by an earlier
// move in this very pass. The contract above is to skip a
// collision, and it applies whichever way the collision is
// reported: this used to escape as an internal error instead.
// `taken` was read before the loop, so a name can be claimed
// underneath us — by another process, or by an earlier move in this
// very pass. Skipping a collision is the contract whichever way the
// collision arrives.
if (!isCreateContention(error))
throw error;
skipped.push({ id: card.id, file: card.file, reason: "name-taken" });
skipped.push({ id: record.id, file: entry.current, reason: "name-taken" });
continue;
}
await rm(join(directory, card.file), { force: true });
taken.delete(card.file);
taken.add(target);
moves.push({ id: card.id, from: card.file, to: target });
await rm(absoluteFrom, { force: true });
taken.delete(from);
taken.add(to);
moves.push({ id: record.id, from: entry.current, to: entry.expected });
}
return { moves, skipped };
}
export async function reslugStaleCardFiles(workspace, { actor = null, now } = {}) {
// Kept as the name the CLI and the exported surface already use. The rule and
// the repair are one implementation now — leaving a card-only copy beside it
// is how the other three kinds came to have no rule at all.
return reslugStaleRecordFiles(workspace, { actor, now, kinds: ["card"] });
}

@@ -0,1 +1,2 @@

import { stripTrailingSlashes } from "../../core/glob.js";
import { readFile } from "node:fs/promises";

@@ -269,3 +270,3 @@ import { mkdir, readdir } from "node:fs/promises";

// that moved it would otherwise commit its persisted index.
const gitignoreAfter = addGitignoreEntry(gitignoreBefore, `${config.storage.cache.replace(/\/+$/, "")}/`);
const gitignoreAfter = addGitignoreEntry(gitignoreBefore, `${stripTrailingSlashes(config.storage.cache)}/`);
actions.push(fileAction(gitignorePath, gitignoreAfter, gitignoreBefore === gitignoreAfter

@@ -272,0 +273,0 @@ ? "unchanged"

@@ -1,2 +0,2 @@

import type { ProjectIndex, ProjectIntegration, ProjectWorkspace, SemanticSearchProvider } from "../../types.js";
import type { ProjectDiagnostic, ProjectIndex, ProjectIntegration, ProjectWorkspace, SemanticSearchProvider } from "../../types.js";
export declare function defineProjectIntegration(definition: ProjectIntegration): Readonly<ProjectIntegration>;

@@ -7,7 +7,20 @@ export interface ProjectIntegrationRegistry {

semanticSearchProvider(preferredId?: string): SemanticSearchProvider | null;
/**
* `module`, not `integration`: the shape every other `doctor` report has,
* which is what the returned value has always actually carried.
*/
healthReports(workspace: ProjectWorkspace, index: ProjectIndex): Promise<Array<{
integration: string;
issues: unknown[];
module: string;
issues: ProjectDiagnostic[];
}>>;
}
export declare function createIntegrationRegistry(integrations?: ProjectIntegration[]): Readonly<ProjectIntegrationRegistry>;
export interface IntegrationRegistryOptions {
/**
* Override the bound on a declared `healthCheck`. Exists so the bound is
* testable in milliseconds rather than only at its ten-second default —
* `runDoctor` does not pass it, and an untested timeout is a timeout that
* regresses quietly.
*/
healthCheckTimeoutMs?: number;
}
export declare function createIntegrationRegistry(integrations?: ProjectIntegration[], options?: IntegrationRegistryOptions): Readonly<ProjectIntegrationRegistry>;
import { ValidationError } from "../../core/errors.js";
/**
* How long a declared `healthCheck` may take before `doctor` answers without it.
*
* Generous on purpose: a health check that reaches a model or a socket is the
* kind worth declaring, and this is not a performance budget. It exists so that
* a hook which never settles produces a named finding in ten seconds instead of
* a CI job that dies at its own timeout with nothing to read.
*
* The bound is real for an awaited hang and worthless against a synchronous
* spin: a hook runs on `doctor`'s own event loop, so `while (true) {}` starves
* the timer too. Bounding that would mean running the hook in a worker, which is
* a different feature — see ADR-0019.
*/
const HEALTH_CHECK_TIMEOUT_MS = 10_000;
const HEALTH_CHECK_TIMED_OUT = Symbol("health-check-timed-out");
const DIAGNOSTIC_SEVERITIES = new Set(["error", "warning", "info"]);
function validId(value) {
return /^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$/.test(String(value || ""));
}
function describe(value) {
if (value === null)
return "null";
if (Array.isArray(value))
return "array";
return typeof value;
}
/**
* Split what a `healthCheck` returned into diagnostics `doctor` can count and
* entries it cannot.
*
* `runDoctor` derives `counts` and `ok` from `issue.severity` and sorts on it,
* so an entry carrying anything else does not merely look wrong: it lands in no
* bucket, leaves `ok` true, and makes the comparator sort on NaN. That is the
* failure this guards — an integration cannot hand back a value that decides
* whether the repository passes.
*/
function partitionDiagnostics(raw) {
const issues = [];
const rejected = [];
raw.forEach((entry, position) => {
const at = `[${position}]`;
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
rejected.push(`${at} is ${describe(entry)}, not a diagnostic object`);
return;
}
const diagnostic = entry;
const problems = [];
if (!DIAGNOSTIC_SEVERITIES.has(String(diagnostic.severity))) {
problems.push(`severity ${JSON.stringify(diagnostic.severity)} is not error, warning or info`);
}
if (typeof diagnostic.code !== "string" || !diagnostic.code) {
problems.push("code is not a non-empty string");
}
if (typeof diagnostic.message !== "string" || !diagnostic.message) {
problems.push("message is not a non-empty string");
}
if (problems.length) {
rejected.push(`${at} ${problems.join("; ")}`);
return;
}
issues.push(diagnostic);
});
return { issues, rejected };
}
/**
* Call one declared `healthCheck` and turn whatever it does into diagnostics.
*
* The repository declaring the hook already runs its own code on every command
* — `loadWorkspace` `import()`s `project.config.mjs` — so this is not a
* sandbox and does not pretend to be one. What it does is stop a hook from
* speaking for `doctor`: a throw, a hang or a malformed diagnostic becomes a
* finding *about the integration*, attributed to it by id, instead of taking
* down the one command the generated CI workflow exists to run.
*
* Each failure is an error rather than a warning because `doctor` is a gate. A
* declared check that could not answer is not a pass, and there is no way to
* tell what a malformed entry was trying to say.
*/
async function healthCheckDiagnostics(integration, context, timeoutMs) {
const details = { integration: integration.id };
let timer;
let report;
try {
const settled = Promise.resolve(integration.healthCheck(context));
// A hook that rejects after the race is already decided still needs a
// handler here, or Node takes the process down for an unhandled
// rejection well after `doctor` has printed its report.
settled.catch(() => { });
report = await Promise.race([
settled,
// Deliberately not `unref`ed. An unreferenced timer lets Node exit
// once the hung hook is the only thing left, so `workfile doctor`
// would die silently having printed no report at all — the failure
// this bound exists to replace. `clearTimeout` in the `finally` is
// what keeps a fast hook from holding the process for ten seconds.
new Promise((resolve) => {
timer = setTimeout(() => resolve(HEALTH_CHECK_TIMED_OUT), timeoutMs);
})
]);
}
catch (error) {
return [
{
severity: "error",
code: "integration-health-check-failed",
message: `Integration ${integration.id} declares a healthCheck that threw: ` +
`${error?.message || String(error)}. Its findings are missing from this report.`,
details: {
...details,
error: error?.message || String(error)
}
}
];
}
finally {
clearTimeout(timer);
}
if (report === HEALTH_CHECK_TIMED_OUT) {
return [
{
severity: "error",
code: "integration-health-check-timeout",
message: `Integration ${integration.id} declares a healthCheck that did not settle within ` +
`${timeoutMs}ms. Its findings are missing from this report.`,
details: { ...details, timeoutMs }
}
];
}
// Nothing to say is a valid answer, and stays indistinguishable from an
// integration that declares no hook at all.
if (!report)
return null;
const raw = Array.isArray(report)
? report
: report.issues;
if (!Array.isArray(raw)) {
return [
{
severity: "error",
code: "integration-health-check-invalid",
message: `Integration ${integration.id} declares a healthCheck that returned ` +
`${describe(report)}, not an array of diagnostics or an object with an \`issues\` array.`,
details: { ...details, returned: describe(report) }
}
];
}
const { issues, rejected } = partitionDiagnostics(raw);
if (rejected.length) {
issues.push({
severity: "error",
code: "integration-health-check-invalid",
message: `Integration ${integration.id} returned ${rejected.length} of ${raw.length} ` +
`diagnostics that could not be counted, so they were dropped: ${rejected.join(", ")}.`,
details: { ...details, rejected, returned: raw.length }
});
}
return issues;
}
export function defineProjectIntegration(definition) {

@@ -26,3 +181,4 @@ if (!definition || typeof definition !== "object" || Array.isArray(definition)) {

}
export function createIntegrationRegistry(integrations = []) {
export function createIntegrationRegistry(integrations = [], options = {}) {
const healthCheckTimeoutMs = options.healthCheckTimeoutMs ?? HEALTH_CHECK_TIMEOUT_MS;
const ordered = [];

@@ -64,8 +220,8 @@ const byId = new Map();

continue;
const report = await integration.healthCheck({ workspace, index });
if (!report)
const issues = await healthCheckDiagnostics(integration, { workspace, index }, healthCheckTimeoutMs);
if (!issues)
continue;
reports.push({
module: `integration:${integration.id}`,
issues: Array.isArray(report) ? report : report.issues || []
issues
});

@@ -72,0 +228,0 @@ }

@@ -158,4 +158,14 @@ import { readFile } from "node:fs/promises";

}
// `resultTruncated`, not `truncated`. This marker is the transport
// saying what the byte ceiling did; `truncated` is whatever the tool
// itself means by it, and one of them means something already:
// `project_agent_context` returns `truncated: boolean` for relations
// dropped to respect `limit`, and this write landed on top of it. A
// caller checking `=== true` then got an object, which is truthy, so the
// check survived by accident; a caller reading `truncated.records` on any
// other tool got `true` from that one and read `.records` off a boolean.
// Two different facts — the limit dropped relations, the ceiling dropped
// rows — and indistinguishable once merged (T-0147).
if (truncated)
payload = { ...payload, truncated };
payload = { ...payload, resultTruncated: truncated };
}

@@ -162,0 +172,0 @@ const bytes = measure(payload);

export declare const MEMORY_LIST_KEYS: Set<string>;
export declare const MEMORY_REQUIRED_KEYS: readonly string[];
/**
* The filename a memory record with this id and title would be created with
* today. Exported so the stale-filename rule can be written once, in the layer
* that holds every kind — see `health/filenames.ts`.
*
* The 70-character cap is not shared with the other kinds and must not be: cards
* cap at 50 and documents at 60, and unifying them would rename every existing
* record whose title is long enough to cross the new bound.
*/
export declare function memoryFileName(id: any, title: any): string;
export declare function loadMemory(workspace: any): Promise<{

@@ -4,0 +14,0 @@ records: any[];

@@ -35,2 +35,14 @@ import { readFile, rm } from "node:fs/promises";

const SEVERITIES = new Set(["critical", "high", "medium", "low"]);
/**
* The filename a memory record with this id and title would be created with
* today. Exported so the stale-filename rule can be written once, in the layer
* that holds every kind — see `health/filenames.ts`.
*
* The 70-character cap is not shared with the other kinds and must not be: cards
* cap at 50 and documents at 60, and unifying them would rename every existing
* record whose title is long enough to cross the new bound.
*/
export function memoryFileName(id, title) {
return `${id}-${slugify(title)}.md`;
}
function slugify(value) {

@@ -37,0 +49,0 @@ return (String(value)

@@ -86,2 +86,5 @@ #!/usr/bin/env node

}
// Read once for the whole sweep. This runs at session start, not on the hot
// `PreToolUse` path, so the cost is paid where there is room for it.
const sessions = await readSessions(root);
const claims = [];

@@ -100,2 +103,14 @@ for (const name of names) {

claimedAt: fields.claimed_at,
// The same two steps `claimBoardEntry` takes, over the same files: a
// session that names this card beats one that merely shares an actor,
// because two agents can share an actor. Falls back to the tail the
// actor carries, and to `null` when there is none — which the guard
// reads as unproven rather than as one process.
session:
discriminatorOf(
(
sessions.find((entry) => entry.cardId === fields.id) ||
sessions.find((entry) => entry.actor === fields.claimed_by)
)?.sessionId
) || discriminatorOf(/#([A-Za-z0-9]+)$/.exec(fields.claimed_by)?.[1]),
scope: Array.isArray(fields.scope)

@@ -111,5 +126,19 @@ ? fields.scope

/**
* Trailing separators removed without a regex, mirroring
* `stripTrailingSlashes` in `core/glob.ts` — which this file cannot import, see
* the header. `replace(/\/+$/, "")` retries the anchored `+` from every start
* position, so N slashes cost O(N²); CodeQL flags the package's copy of that
* spelling and is right to. The scope here comes off a card, and a card in a
* repository taking pull requests can arrive from a fork.
*/
const withoutTrailingSlashes = (value) => {
let end = value.length;
while (end > 0 && value[end - 1] === "/") end -= 1;
return end === value.length ? value : value.slice(0, end);
};
function scopeCovers(scope, repoPath) {
return scope.some((entry) => {
const normalized = entry.replace(/\/+$/, "");
const normalized = withoutTrailingSlashes(entry);
if (!normalized) return false;

@@ -152,2 +181,13 @@ if (normalized.includes("*")) {

*/
/**
* `sessionDiscriminator` from `core/actor.ts`, duplicated for the reason the
* header gives: this file imports nothing from the package. Pinned against it by
* `test/claude-surface.test.ts`, because a board written by the CLI and read by
* this hook has to agree on what a session id normalises to.
*/
const discriminatorOf = (value) => {
const cleaned = String(value || "").replace(/[^A-Za-z0-9]/g, "");
return cleaned ? cleaned.slice(0, 8).toLowerCase() : null;
};
const actorFor = (input) => {

@@ -174,2 +214,39 @@ const configured = (process.env.WORKFILE_ACTOR || "").trim();

/**
* Whether a claim belongs to some process other than this one.
*
* The rule is `claimSeparation` in `modules/cards/claims.ts`: two claims are one
* process only when provably one session, and an actor is not a session. Here it
* collapses back to comparing the strings, and that is worth stating rather than
* leaving to look like a coincidence — `actorFor` writes the session
* discriminator into the tail, so for every pairing this guard can see, actor
* equality *is* session equality:
*
* - both tails present and equal, or both absent with the same actor → one
* process, or `unproven` and deliberately not prompted on. A configured
* `WORKFILE_ACTOR` is somebody declaring an identity, and interrupting them
* about their own claim is how a guard rail gets switched off.
* - tails differing, or one present and one absent → two processes.
* - no tails and different actors → two people.
*
* So this stays a string comparison, and the pinning test in
* `test/claude-surface.test.ts` drives both derivations over every case rather
* than trusting the paragraph above. What the guard cannot see is a session that
* exists only in a session file — `claimed_by` written from an explicit
* `--actor` carries no tail — and the snapshot can. That residual is LRN-0030.
*/
function separatesFromMe(claim, mine, mySession) {
const theirs = claim.session || null;
// Two sessions, seen. The strongest answer, and the one the board could not
// give before T-0219 put `session` on the entry.
if (theirs && mySession) return theirs !== mySession;
// One side has a session and the other does not, so they are not the same
// process — the same call `claimSeparation` makes.
if (theirs || mySession) return true;
// Neither has one. Different actors are two people; the same actor is
// `unproven`, and the guard stays quiet on a guess rather than interrupting
// somebody about their own card.
return claim.claimedBy !== mine;
}
const SESSIONS = `${CACHE}/sessions`;

@@ -224,2 +301,27 @@

*/
/**
* Every session file this workspace holds.
*
* Mirrors `readAgentSessions` minus the liveness arithmetic, which `buildBoard`
* does not need: it is resolving which session a claim belongs to, not whether
* that session is still breathing. A half-written file is skipped rather than
* failing the sweep, the same rule the package side takes.
*/
async function readSessions(root) {
const directory = join(root, SESSIONS);
let names;
try {
names = await readdir(directory);
} catch {
return [];
}
const sessions = [];
for (const name of names) {
if (!name.endsWith(".json")) continue;
const session = await readJson(join(directory, name), null);
if (session?.sessionId) sessions.push(session);
}
return sessions;
}
async function pruneSessions(root, olderThanMs = 86_400_000) {

@@ -246,6 +348,12 @@ const directory = join(root, SESSIONS);

const root = projectDir(input);
const board = await buildBoard(root);
await mkdir(join(root, CACHE), { recursive: true });
await pruneSessions(root);
// This session's own signal is written *before* the board is built, and the
// order is load-bearing now that an entry carries a session (T-0219). Built
// first, a claim this very session already holds resolved to no session — its
// file did not exist yet — and the guard then saw a claim with none against a
// caller with one, called them two processes, and asked the session about its
// own card. Which is precisely the failure the guard exists not to have.
await signal(root, input);
const board = await buildBoard(root);
await writeFile(

@@ -375,6 +483,11 @@ join(root, CACHE, "board.json"),

const mine = actorFor(input);
// Read from the payload, not from the board: this is who *this* process is,
// and no file is opened for it. Which is the whole reason the other side's
// session is resolved when the board is written rather than here — a
// `PreToolUse` fires before every matching tool call, p95 under 30 ms.
const mySession = discriminatorOf(sessionId(input));
const conflict = board.claims.find(
(claim) =>
claim.status === "doing" &&
claim.claimedBy !== mine &&
separatesFromMe(claim, mine, mySession) &&
claim.scope.length &&

@@ -381,0 +494,0 @@ scopeCovers(claim.scope, repoPath)

@@ -11,1 +11,15 @@ /**

export declare function discoverWorkspaceRoot(cwd?: string): Promise<string>;
/**
* Whether this exact directory is a workspace, without walking anywhere.
*
* The same two markers the walk above looks for, extracted so the rule is
* written once. `--root` needed it: `loadWorkspace({ root })` took the directory
* as given and checked nothing, so `doctor --root packages/workfile` reported
* six missing-instruction issues, exited 0, and indexed that package's `docs/`
* as the workspace's documents — a clean, empty, believable answer from a
* directory that is not a workspace at all (T-0160).
*
* Deliberately not a walk. `--root` is an assertion by the caller, and quietly
* resolving it to a parent would be a second surprise rather than a fix.
*/
export declare function isWorkspaceRoot(directory: string): Promise<boolean>;

@@ -16,6 +16,4 @@ import { dirname, join, parse, resolve } from "node:path";

while (true) {
if (await exists(join(current, "project.config.mjs")))
if (await isWorkspaceRoot(current))
return current;
if (await exists(join(current, ".project", "VERSION")))
return current;
if (current === root)

@@ -26,1 +24,20 @@ return null;

}
/**
* Whether this exact directory is a workspace, without walking anywhere.
*
* The same two markers the walk above looks for, extracted so the rule is
* written once. `--root` needed it: `loadWorkspace({ root })` took the directory
* as given and checked nothing, so `doctor --root packages/workfile` reported
* six missing-instruction issues, exited 0, and indexed that package's `docs/`
* as the workspace's documents — a clean, empty, believable answer from a
* directory that is not a workspace at all (T-0160).
*
* Deliberately not a walk. `--root` is an assertion by the caller, and quietly
* resolving it to a parent would be a second surprise rather than a fix.
*/
export async function isWorkspaceRoot(directory) {
const current = resolve(directory);
if (await exists(join(current, "project.config.mjs")))
return true;
return exists(join(current, ".project", "VERSION"));
}

@@ -10,3 +10,3 @@ import { readFile } from "node:fs/promises";

import { verifyTimeoutSeconds } from "../modules/cards/validation.js";
import { discoverWorkspaceRoot } from "./discover.js";
import { discoverWorkspaceRoot, isWorkspaceRoot } from "./discover.js";
import { exists } from "../core/fs-utils.js";

@@ -185,8 +185,21 @@ import { cliInvocation, detectPackageManager } from "../core/package-manager.js";

const cwd = resolve(options.cwd || process.cwd());
const discovered = options.root
? resolve(options.root)
: await discoverWorkspaceRoot(cwd);
const explicit = options.root ? resolve(options.root) : null;
const discovered = explicit ?? (await discoverWorkspaceRoot(cwd));
if (!discovered && !options.allowMissing) {
throw new ConfigError("WORKSPACE_NOT_FOUND", `No project workspace found in ${cwd} or any parent directory. Run \`workfile init\` to create one, or pass --root.`, { cwd });
}
// An explicit root gets the same marker check the walk performs, which it
// never had: it was taken as given, so a mistyped or stale `--root` inside a
// monorepo — one directory too deep is the ordinary case — answered from an
// empty workspace and reported nothing wrong. `allowMissing` is the way
// through, and it is what `--allow-new` already means: accept a directory
// that is not yet a workspace. `init` is its one caller.
//
// Before anything is read or written, so a directory that fails this gets no
// cache, no lock and no index.
if (explicit && !options.allowMissing && !(await isWorkspaceRoot(explicit))) {
throw new ConfigError("WORKSPACE_NOT_FOUND", `${explicit} is not a workspace: it has no project.config.mjs and no ` +
".project/VERSION. Run `workfile init --root <dir>` to create one, " +
"or pass --allow-new to accept a directory that is not one yet.", { root: explicit });
}
const root = discovered || cwd;

@@ -193,0 +206,0 @@ const configPath = options.configPath

@@ -13,3 +13,3 @@ <!doctype html>

/>
<script type="module" crossorigin src="/static/index-BcEUSS3r.js"></script>
<script type="module" crossorigin src="/static/index-CyDRMkuZ.js"></script>
<link rel="modulepreload" crossorigin href="/static/rolldown-runtime-CbXtAM7H.js">

@@ -19,3 +19,3 @@ <link rel="modulepreload" crossorigin href="/static/react-Buq45Vzz.js">

<link rel="modulepreload" crossorigin href="/static/theme-CcOVK72d.js">
<link rel="stylesheet" crossorigin href="/static/index-Bb1zRGE2.css">
<link rel="stylesheet" crossorigin href="/static/index-CakELuEw.css">
</head>

@@ -22,0 +22,0 @@ <body>

@@ -158,3 +158,3 @@ # CLI reference

[`@illodev/workfile-search-local`](https://github.com/illodev/workfile/tree/main/packages/search-local#readme):
on-device embeddings via transformers.js, cached by content hash, fully
on-device embeddings via onnxruntime-web, cached by content hash, fully
offline after the first model download.

@@ -214,2 +214,3 @@

workfile card create --title TITLE [--area AREA] [--type TYPE] [--priority PRIORITY]
workfile card create --title TITLE --raised reported|derived
[--parent ID] [--source PATH] [--tags a,b] [--scope PATH,PATH]

@@ -237,2 +238,4 @@ [--depends ID,ID] [--related ID,ID] [--origin ID,ID]

workfile card verify ID [--only gate] [--actor ACTOR] # run the declared commands
workfile card verify --changed --base main # every card this branch touched
workfile card verify --changed --base main --close --run URL --commit SHA
```

@@ -718,2 +721,41 @@

### What the generated GitHub workflow does, and what it will not do
Three jobs. `doctor` validates the protocol. `cards` runs the commands the cards
this branch touched declare, and `record` writes the result back.
Those last two are deliberately not one job. A criterion bound to a command can
only be checked by running it, so `cards` executes commands a pull request
declared — and therefore holds `permissions: {}`, with no credentials left in
`.git/config`. Writing evidence needs `contents: write`, so `record` holds it and
runs no repository code at all: not even Workfile, because every Workfile command
`import()`s `project.config.mjs` from the checkout. It applies a patch bounded to
the protocol directory and pushes.
**A fork records nothing.** GitHub issues a read-only token for `pull_request`
from a fork, so the push cannot land whatever the workflow says; `record` also
declines to start there, in order to say so rather than fail at the last step.
**CI closes a card only when every one of its criteria is bound to a command.** A
narrative criterion is not something a runner has an opinion about, so a card
that carries one gets its bound boxes written and stays open, with the reason
reported. That is the whole safety of the write-back: `card ac --check` refuses a
bound criterion and only the runner writes it, so the boxes CI touches are boxes
no person was going to check either way.
**Only on a pull request.** "The cards this branch touched" is a diff against a
base and a push to a default branch has none. The checkout needs
`fetch-depth: 0`, because the diff is taken from the merge base and a shallow
clone has none — reported as *cannot answer* rather than as an empty diff, which
would turn "nothing was verified" into "there was nothing to verify".
`--base` is required and has no default. Guessing it wrong means running the
declared commands of cards the branch never opened, and writing to them.
**GitLab and the generic script run no card commands.** GitLab has no per-job
permission scope, so the job sees every unprotected variable in the project and
there is nowhere to put a command a merge request declared; the generic script
inherits the whole environment of whatever invokes it. Both files carry the
invocation commented out with what a maintainer would have to arrange first.
## Legacy migration

@@ -720,0 +762,0 @@

@@ -37,5 +37,15 @@ # MCP server

one-line summary rather than a second copy of the payload. When a result would
exceed `maxToolResultBytes` it is truncated with a `truncated` marker instead of
failing the call, because a get-by-id has no query to narrow.
exceed `maxToolResultBytes` the server degrades it rather than failing the call,
because a get-by-id has no query to narrow — and says so with
**`resultTruncated`**: `{ records: <rows dropped> }`, or
`{ bodyBytes: <original size> }` when a single record's body was clipped.
That marker is the transport speaking, and it is deliberately not called
`truncated`. A tool may declare a `truncated` of its own meaning something else
entirely: `project_agent_context` returns `truncated: boolean` for relations
dropped to respect `limit`, and the two used to be one key — so a large bundle
replaced the boolean with an object, a caller checking `=== true` survived by
accident because an object is truthy, and a caller reading `truncated.records` on
any other tool got `true` from that one.
## Claude Code integration

@@ -212,5 +222,7 @@

- **Every tool declares an `outputSchema`** matching the `structuredContent` it
returns. None of them is a closed object: a payload over `maxToolResultBytes`
gains a `truncated` marker, and a schema that forbade it would invalidate the
server's own degradation path.
returns, including `resultTruncated` — declared rather than merely allowed, so
a caller reads it from the schema instead of meeting it the first time a
payload gets large. None of them is a closed object either: the degradation
path adds a field, and a schema that forbade it would invalidate the server's
own answer.

@@ -217,0 +229,0 @@ `project_card_release` is the one place where an enum is narrower than the

@@ -145,2 +145,29 @@ # The interface

grows a box of its own or a wording of its own.
- **A filter that is not in the URL is a filter that dies on reload.** Every
one of them is state the shell owns and `ui/src/query.ts` serialises — the
card axes flat (`?status=`, `?area=`, …), the record collections' axes
namespaced by view (`?docs-managed=1`, `?history-state=`,
`?memory-collection=`, `?memory-status=`). The prefix is a rule and not a
case-by-case choice: the obvious name for Memory's is `status`, which the
card filter already owns, and the loser of a clash like that filters by
nothing without saying so. A record view therefore takes its filters as a
prop and reports changes as a patch, so its coupled pairs — picking a Memory
collection clears the status that belonged to it — reach the address bar in
one write. Same suite: it fails on a view that takes one back into a
`useState`, and on a parameter that collides with a card axis.
- **A record opened from a list can be read as a sequence.** Every panel that
reads a record — the card inspector, the memory panel, the generic record
panel, and the readers Docs and History own themselves — renders
`ui/src/record-cursor.tsx`, and the rule for where previous and next go is
`recordNeighbours` in `navigation.ts`, beside the other navigation rules. The
list is whatever the view was showing, in the order it was showing it, so it
narrows when the filters do; each view publishes its own as the second
argument to `onSelect`. **Absent, not guessed, where there is no list:** a
`[[LRN-0004]]` in a body, a `related` row, the command palette, and a node of
the Workflow graph all open a record with nothing behind it, and a force
layout is not an order. At the ends of a real list the control renders with
one half disabled, which is how a reader tells "no next" from "there was
never a sequence here". It is a context rather than a prop for the reason
`read-only.tsx` gives: the panels sit in three different places, and all
three have to reach it.
- **The filter bar is one container, and it decides what may scroll away.**

@@ -147,0 +174,0 @@ `ui/src/components/FilterBar.tsx` owns the whole bar in every view that has

{
"name": "@illodev/workfile",
"version": "0.8.1",
"version": "0.9.0",
"type": "module",

@@ -5,0 +5,0 @@ "mcpName": "io.github.illodev/workfile",

import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{Et as r,Tt as i,pt as a}from"./ui-primitives-DRENhlck.js";import{i as o,n as s,o as c,s as l,u}from"./theme-CcOVK72d.js";import{A as d,D as f,H as p,I as m,L as h,M as g,V as _,et as v,j as y,k as b,n as x,nt as S,r as C,t as w,w as T}from"./index-BcEUSS3r.js";var E=e(t(),1),D=n(),O=[];function k(e,t){let[n,r]=(0,E.useState)(t);(0,E.useEffect)(()=>{r(t)},[e.length,t]);let i=(0,E.useCallback)(()=>r(n=>Math.min(n+t,e.length)),[e.length,t]);return[n>=e.length?e:e.slice(0,n),n<e.length,i]}function A({onVisible:e,remaining:t}){let n=(0,E.useRef)(null);return(0,E.useEffect)(()=>{let t=n.current;if(!t)return;let r=new IntersectionObserver(t=>{t.some(e=>e.isIntersecting)&&e()},{rootMargin:`600px 0px`});return r.observe(t),()=>r.disconnect()},[e,t]),(0,D.jsxs)(`span`,{ref:n,className:`px-0.5 py-1 font-mono text-[11px] text-muted-foreground`,children:[`+`,t,` more`]})}function j({task:e,epicId:t,onOpen:n,onDragStart:r,onCarry:i,carrying:a}){let o=e.claimed_at?Date.parse(e.claimed_at.includes(`T`)?e.claimed_at:`${e.claimed_at}T00:00:00`):NaN,l=Number.isNaN(o)?null:Math.max(0,Math.floor((Date.now()-o)/864e5)),d=[t&&t!==e.id?`epic ${t}`:``,e.effort?`effort ${e.effort}`:``,e.claimed_by?`claimed by ${e.claimed_by}${l==null?``:` · ${l}d`}`:``].filter(Boolean);return(0,D.jsxs)(`article`,{className:u(`flex cursor-pointer flex-col gap-1.5 rounded-lg border bg-background px-3 py-2.5 shadow-xs outline-none transition-[color,border-color,box-shadow] hover:border-ring focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50`,a&&`border-ring ring-2 ring-ring`),tabIndex:0,draggable:!!r,"aria-grabbed":i?!!a:void 0,title:d.length?d.join(` · `):void 0,onClick:()=>n(e.id),onKeyDown:t=>{t.key===`Enter`?(t.preventDefault(),n(e.id)):t.key===` `&&i?(t.preventDefault(),i()):t.key===` `&&(t.preventDefault(),n(e.id))},onDragStart:r,children:[(0,D.jsxs)(`span`,{className:`flex items-center`,children:[(0,D.jsx)(`span`,{className:`font-mono text-[11px] text-foreground/70`,children:e.id}),(0,D.jsx)(`span`,{className:`flex-1`}),(0,D.jsx)(`span`,{className:`font-mono text-[10px] font-medium`,style:{color:s(e.priority)},children:e.priority})]}),(0,D.jsx)(`span`,{className:`text-[12.5px] leading-snug font-medium`,role:`heading`,"aria-level":3,children:e.title}),(0,D.jsxs)(`span`,{className:`flex items-center gap-1.5 font-mono text-[10px] text-muted-foreground`,children:[(0,D.jsx)(`span`,{children:e.area}),(0,D.jsx)(`span`,{children:`·`}),(0,D.jsx)(`span`,{children:e.type}),e.claimed_by?(0,D.jsxs)(`span`,{className:`ml-auto inline-flex min-w-0 items-center gap-[5px]`,style:{color:c(`doing`)},children:[(0,D.jsx)(`span`,{className:`size-[5px] flex-none rounded-full bg-current`,"aria-hidden":`true`}),(0,D.jsx)(`span`,{className:`max-w-[90px] truncate`,children:e.claimed_by})]}):null]}),Array.isArray(e.scope)&&e.scope.length?(0,D.jsxs)(`span`,{className:`mt-0.5 truncate border-t border-dashed pt-1.5 font-mono text-[10.5px] text-muted-foreground`,children:[`scope `,e.scope.join(` · `)]}):null]})}function M({status:e,cards:t,epicIds:n,collapsed:o,onToggleCollapsed:s,onOpen:m,onMove:h,onCarry:v,carryingId:x,isDropTarget:C,onDragEnterColumn:w,onDragLeaveColumn:E}){let O=T(),[M,N,P]=k(t,25),F=c(e),I={onDragOver:t=>{t.preventDefault(),t.dataTransfer.dropEffect=`move`,w?.(e)},onDragLeave:t=>{t.currentTarget.contains(t.relatedTarget)||E?.(e)},onDrop:t=>{t.preventDefault(),E?.(e);let n=t.dataTransfer.getData(`text/plain`);n&&h(n,e).catch(()=>void 0)}};return o?(0,D.jsxs)(p,{role:`region`,"aria-label":`${e}, ${t.length} cards, collapsed`,className:u(`relative w-11 flex-none gap-0 overflow-hidden rounded-lg py-0 shadow-xs`,C&&`border-primary`),...I,children:[(0,D.jsx)(_,{edge:`top`,color:F}),(0,D.jsxs)(`button`,{type:`button`,"aria-expanded":!1,"aria-label":`Expand the ${e} column`,title:`${e} · ${t.length}`,className:u(`flex h-full w-full cursor-pointer flex-col items-center gap-2.5 px-1 pt-4 pb-3 transition-colors hover:bg-accent/50`,C&&`bg-accent/50`),onClick:s,children:[(0,D.jsx)(r,{"aria-hidden":`true`,className:`size-3.5 shrink-0 text-muted-foreground`}),(0,D.jsx)(`span`,{className:`min-h-0 flex-1 truncate font-mono text-[11px] uppercase tracking-[0.06em] [writing-mode:vertical-rl]`,style:{color:F},children:e}),(0,D.jsx)(S,{variant:`secondary`,className:`h-5 shrink-0 rounded-md px-[7px] font-mono text-[11px] font-normal`,children:t.length})]})]}):(0,D.jsxs)(p,{role:`region`,"aria-label":`${e}, ${t.length} cards`,className:u(`relative w-[268px] flex-none gap-0 overflow-hidden rounded-lg py-0 shadow-xs`,C&&`border-primary`),...I,children:[(0,D.jsx)(_,{edge:`top`,color:F}),(0,D.jsxs)(`header`,{className:`flex flex-none items-center gap-2 px-3 pb-2.5 pt-4`,children:[(0,D.jsx)(`span`,{className:`flex-1 font-mono text-[11px] uppercase tracking-[0.06em]`,style:{color:F},children:e}),(0,D.jsx)(S,{variant:`secondary`,className:`h-5 rounded-md px-[7px] font-mono text-[11px] font-normal`,children:t.length}),s?(0,D.jsx)(l,{type:`button`,variant:`ghost`,size:`icon-xs`,"aria-expanded":!0,"aria-label":`Collapse the ${e} column`,title:`Collapse column`,className:`-mr-1 text-muted-foreground`,onClick:s,children:(0,D.jsx)(i,{"aria-hidden":`true`})}):null]}),(0,D.jsxs)(`div`,{className:u(`scroll-fade flex flex-1 flex-col gap-2 overflow-y-auto p-2.5`,C&&`bg-accent/50`),children:[t.length===0?(0,D.jsx)(f,{className:`flex-1 gap-2 rounded-lg border border-dashed p-4`,children:(0,D.jsxs)(d,{className:`gap-1`,children:[(0,D.jsx)(y,{variant:`icon`,className:`mb-0 size-8 [&_svg:not([class*='size-'])]:size-4`,children:(0,D.jsx)(a,{"aria-hidden":`true`})}),(0,D.jsx)(g,{className:`text-[12.5px] font-medium`,children:`No cards`}),(0,D.jsx)(b,{className:`text-[11.5px]`,children:`Nothing in this state.`})]})}):M.map(e=>(0,D.jsx)(j,{task:e,epicId:n.get(e.id),onOpen:m,onCarry:v&&!O?()=>v(e):void 0,carrying:x===e.id,onDragStart:O?void 0:t=>{t.dataTransfer.effectAllowed=`move`,t.dataTransfer.setData(`text/plain`,e.id)}},e.id)),N&&(0,D.jsx)(A,{onVisible:P,remaining:t.length-M.length})]})]})}function N({tasks:e,epicIds:t,showClosed:n,onOpen:r,onMove:i}){let[a,o]=(0,E.useState)(null),[s,c]=(0,E.useState)(null),[l,u]=(0,E.useState)(``),[d,f]=(0,E.useState)(()=>{try{let e=localStorage.getItem(`workfile-flow-collapsed`);return new Set(e?JSON.parse(e):[])}catch{return new Set}}),p=(0,E.useCallback)(e=>{f(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),localStorage.setItem(`workfile-flow-collapsed`,JSON.stringify([...n])),n})},[]),m=(0,E.useMemo)(()=>[`backlog`,`next`,`doing`,`review`,`blocked`,`deferred`,...n?[`done`,`discarded`]:[]],[n]),h=(0,E.useMemo)(()=>{let t=new Map;for(let n of e){let e=t.get(n.status);e?e.push(n):t.set(n.status,[n])}return t},[e]),g=e=>{o({id:e.id,status:e.status}),u(`${e.id} picked up from ${e.status}. Use the arrow keys to choose a column, space to drop, escape to cancel.`)},_=e=>{if(!a)return;let t=m.indexOf(a.status),n=m[Math.min(m.length-1,Math.max(0,t+e))];!n||n===a.status||(o({...a,status:n}),u(`${a.id} over ${n}.`))},v=async()=>{if(!a)return;let t=a;o(null);let n=e.find(e=>e.id===t.id);n&&n.status!==t.status?(await i(t.id,t.status),u(`${t.id} moved to ${t.status}.`)):u(`${t.id} put back.`)};return(0,D.jsxs)(`div`,{className:`flex min-h-0 flex-1 gap-3 overflow-x-auto p-3.5`,onDragEnd:()=>c(null),onKeyDown:e=>{a&&(e.key===`Escape`?(e.preventDefault(),o(null),u(`Move cancelled.`)):e.key===`ArrowRight`?(e.preventDefault(),_(1)):e.key===`ArrowLeft`?(e.preventDefault(),_(-1)):(e.key===` `||e.key===`Enter`)&&(e.preventDefault(),v()))},children:[(0,D.jsx)(`p`,{className:`sr-only`,role:`status`,"aria-live":`polite`,children:l}),m.map(e=>(0,D.jsx)(M,{status:e,cards:h.get(e)??O,epicIds:t,collapsed:d.has(e),onToggleCollapsed:()=>p(e),onOpen:r,onMove:i,onCarry:g,carryingId:a?.id??null,isDropTarget:a?.status===e||s===e,onDragEnterColumn:c,onDragLeaveColumn:e=>c(t=>t===e?null:t)},e))]})}function P({tasks:e,allTasks:t,epicIds:n,onOpen:r}){let i=(0,E.useMemo)(()=>new Map(t.map(e=>[e.id,e])),[t]),a=(0,E.useMemo)(()=>{let t=new Map;for(let r of e){let e=n.get(r.id)||(r.type===`epic`?r.id:`__none`);t.has(e)||t.set(e,[]),r.id!==e&&t.get(e)?.push(r)}return[...t].sort(([e],[t])=>e===`__none`?1:t===`__none`?-1:e.localeCompare(t,void 0,{numeric:!0}))},[n,e]);return a.length?(0,D.jsx)(`div`,{className:`flex-1 overflow-y-auto p-3.5`,children:(0,D.jsx)(`div`,{className:`flex flex-col gap-2.5`,children:a.map(([e,t])=>{let n=i.get(e),a=t.length,o=t.filter(e=>e.status===`done`||e.status===`discarded`).length,s=t.filter(e=>e.status===`doing`).length,l=a-o-s,d=e=>a?`${e/a*100}%`:`0%`,f=[{label:`${o} done`,color:c(`done`)},{label:`${s} doing`,color:c(`doing`)},{label:`${l} open`,color:null}],m=(0,D.jsxs)(D.Fragment,{children:[(0,D.jsxs)(`span`,{className:`flex min-w-0 items-center gap-2.5`,children:[(0,D.jsx)(`span`,{className:`font-mono text-[11.5px] text-foreground/70`,children:e===`__none`?`—`:e}),(0,D.jsx)(`span`,{className:`min-w-0 flex-1 text-sm font-semibold tracking-[-0.01em] text-pretty`,children:n?.title||`Without epic`}),n?(0,D.jsx)(`span`,{className:`font-mono text-[11px]`,style:{color:c(n.status)},children:n.status}):null,(0,D.jsxs)(`span`,{className:`font-mono text-[11.5px] text-muted-foreground`,children:[o,`/`,a]})]}),(0,D.jsx)(`span`,{className:`flex h-2 w-full overflow-hidden rounded-full bg-muted`,"aria-hidden":`true`,children:a>0?(0,D.jsxs)(D.Fragment,{children:[(0,D.jsx)(`span`,{className:`h-full`,style:{width:d(o),background:c(`done`)}}),(0,D.jsx)(`span`,{className:`h-full`,style:{width:d(s),background:c(`doing`)}})]}):null}),(0,D.jsxs)(`span`,{className:`flex flex-wrap items-center gap-3.5 font-mono text-[10.5px] text-muted-foreground`,children:[f.map(e=>(0,D.jsxs)(`span`,{className:`inline-flex items-center gap-[5px]`,children:[(0,D.jsx)(`span`,{className:u(`size-1.5 rounded-[2px]`,!e.color&&`bg-muted-foreground`),style:e.color?{background:e.color}:void 0,"aria-hidden":`true`}),e.label]},e.label)),(0,D.jsx)(`span`,{className:`ml-auto`,children:n?.area??``})]})]});return n?(0,D.jsx)(`button`,{type:`button`,className:`flex w-full cursor-pointer flex-col gap-2.5 rounded-xl border bg-card px-4 py-3.5 text-left text-card-foreground shadow-xs outline-none transition-[color,border-color,box-shadow] hover:border-ring focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50`,onClick:()=>r(e),children:m},e):(0,D.jsx)(p,{className:`gap-2.5 rounded-xl px-4 py-3.5 shadow-xs`,children:m},e)})})}):(0,D.jsx)(f,{className:`flex-1 p-6`,children:(0,D.jsxs)(d,{children:[(0,D.jsx)(g,{className:`text-sm`,children:`No epics`}),(0,D.jsx)(b,{className:`text-[11.5px]`,children:`No cards match the current filters.`})]})})}var F=300,I=30;function L({task:e,span:t,mode:n,epicId:r,pct:i,labelWidth:a,onOpen:o}){let s=c(e.status);return(0,D.jsxs)(`button`,{type:`button`,onClick:()=>o(e.id),title:`${C(e,n,t)} · ${e.status}${r&&r!==e.id?` · epic ${r}`:``}`,className:`flex w-full cursor-pointer items-center border-b bg-transparent p-0 text-left transition-colors hover:bg-muted`,children:[(0,D.jsxs)(`span`,{className:`flex min-w-0 items-center gap-2 border-r px-3.5`,style:{width:a,flex:`0 0 ${a}px`,height:`var(--row-h)`},children:[(0,D.jsx)(`span`,{className:`flex-none whitespace-nowrap font-mono text-[11px] text-foreground/70`,children:e.id}),(0,D.jsx)(`span`,{className:`min-w-0 truncate text-[12.5px]`,children:e.title})]}),(0,D.jsx)(`span`,{className:`relative block flex-1`,style:{height:`var(--row-h)`},children:t.point?(0,D.jsx)(`span`,{style:{position:`absolute`,top:`50%`,width:9,height:9,transform:`translate(-50%, -50%) rotate(45deg)`,borderRadius:2,background:s,display:`block`,left:`${i(t.from)}%`}}):(0,D.jsx)(`span`,{style:{position:`absolute`,top:`50%`,transform:`translateY(-50%)`,height:12,minWidth:6,borderRadius:3,background:s,display:`block`,left:`${i(t.from)}%`,width:`${Math.max(i(t.to)-i(t.from),.8)}%`}})})]})}function R({tasks:e,epicIds:t,axes:n={},mode:r,counts:i,onModeChange:a,onOpen:s}){let c=v()?168:F,u=c+460,[p,_]=(0,E.useState)(()=>{try{return localStorage.getItem(`workfile-timeline-group`)||`none`}catch{return`none`}}),y=(0,E.useCallback)(e=>{_(e);try{localStorage.setItem(`workfile-timeline-group`,e)}catch{}},[]),S=(0,E.useMemo)(()=>[`none`,`epic`,`area`,...Object.keys(n)],[n]),C=S.includes(p)?p:`none`,T=(0,E.useCallback)(e=>{if(C===`epic`)return t.get(e.id)||``;if(C===`area`)return e.area||``;let n=e[C];return typeof n==`string`?n:``},[t,C]),O=(0,E.useMemo)(()=>{let t=new Map;for(let n of e){let e=x(n,r);e&&t.set(n.id,e)}return t},[r,e]),k=(0,E.useMemo)(()=>{let t=(e,t)=>O.get(e.id).from-O.get(t.id).from||e.id.localeCompare(t.id),n=e.filter(e=>O.has(e.id)).sort(t);return C===`none`?n:[...n].sort((e,n)=>{let r=T(e),i=T(n);return!r==!i?r.localeCompare(i)||t(e,n):r?-1:1})},[T,C,O,e]),A=(0,E.useMemo)(()=>{if(C===`none`)return k.map(e=>({task:e,label:null}));let e=[],t=null;for(let n of k){let r=T(n);r!==t&&(t=r,e.push({task:null,label:r||`no ${C}`})),e.push({task:n,label:null})}return e},[T,C,k]),j=(0,E.useMemo)(()=>new Map(A.flatMap((e,t)=>e.task?[[e.task.id,t]]:[])),[A]),M=(0,E.useMemo)(()=>{let e=[];for(let t of k)for(let n of t.depends||[])j.has(n)&&e.push({from:n,to:t.id});return e},[j,k]),N=(0,E.useMemo)(()=>w(k.map(e=>O.get(e.id)),Date.now()),[k,O]),P=i[r===`plan`?`actual`:`plan`];return!k.length||!N?(0,D.jsx)(f,{className:`flex-1 p-6`,children:(0,D.jsxs)(d,{children:[(0,D.jsx)(g,{className:`text-sm`,children:r===`plan`?`Nothing scheduled`:`Nothing recorded`}),(0,D.jsx)(b,{className:`text-[11.5px]`,children:r===`plan`?`Add a start or due date to a card.`:`Cards record a trail as they are claimed and moved.`}),P>0?(0,D.jsx)(b,{className:`text-[11.5px]`,children:(0,D.jsx)(l,{variant:`outline`,size:`sm`,className:`mt-2 text-[12.5px] font-medium`,onClick:()=>a(r===`plan`?`actual`:`plan`),children:r===`plan`?`show what actually happened · ${P} cards`:`show the schedule · ${P} cards`})}):null]})}):(0,D.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col`,children:[(0,D.jsxs)(m,{gutter:`3.5`,className:`shrink-0 border-b bg-card py-2`,children:[(0,D.jsxs)(`span`,{className:`shrink-0 font-mono text-[11px] whitespace-nowrap text-muted-foreground`,children:[k.length,` `,r===`actual`?`recorded`:`scheduled`,` ·`,` `,M.length,` dependenc`,M.length===1?`y`:`ies`]}),(0,D.jsxs)(`span`,{className:`ml-auto flex shrink-0 items-center gap-2`,children:[(0,D.jsx)(h,{label:`dates`,value:r,allLabel:null,align:`end`,options:[{value:`plan`},{value:`actual`}],onChange:e=>a(e)}),(0,D.jsx)(h,{label:`group`,value:C,allLabel:null,align:`end`,options:S.map(e=>({value:e})),onChange:y})]})]}),(0,D.jsx)(`div`,{className:`min-h-0 flex-1 overflow-auto`,children:(0,D.jsxs)(`div`,{className:`relative min-h-full`,style:{minWidth:u},children:[(0,D.jsxs)(`div`,{"aria-hidden":`true`,className:`sticky top-0 z-[2] flex items-stretch border-b bg-card`,style:{height:I},children:[(0,D.jsx)(`span`,{className:`flex items-center border-r px-3.5 text-[10px] uppercase tracking-[0.08em] text-muted-foreground`,style:{width:c,flex:`0 0 ${c}px`},children:`card`}),(0,D.jsx)(`span`,{className:`relative flex-1`,children:N.ticks.map((e,t)=>(0,D.jsx)(`span`,{className:`text-[10px] uppercase tracking-[0.08em] text-muted-foreground`,style:{position:`absolute`,top:`50%`,transform:`translateY(-50%)`,left:`${e.left}%`,width:`${(N.ticks[t+1]?.left??100)-e.left}%`,overflow:`hidden`,paddingLeft:8,whiteSpace:`nowrap`},children:e.label},e.key))})]}),(0,D.jsxs)(`div`,{"aria-hidden":`true`,className:`pointer-events-none absolute`,style:{top:I,bottom:0,left:c,right:0},children:[N.ticks.map(e=>(0,D.jsx)(`span`,{className:`absolute inset-y-0 w-px bg-border`,style:{left:`${e.left}%`}},e.key)),N.today!=null&&(0,D.jsx)(`span`,{className:`absolute inset-y-0 w-px`,style:{background:o(`error`),opacity:.55,left:`${N.today}%`}})]}),(0,D.jsxs)(`div`,{className:`relative`,children:[M.length>0&&(0,D.jsx)(`svg`,{"aria-hidden":`true`,preserveAspectRatio:`none`,viewBox:`0 0 100 ${A.length}`,className:`pointer-events-none absolute top-0 h-full`,style:{left:c,width:`calc(100% - ${c}px)`},children:M.map(e=>{let t=O.get(e.from),n=O.get(e.to);if(!t||!n)return null;let r=N.pct(t.to),i=N.pct(n.from),a=j.get(e.from)+.5,s=j.get(e.to)+.5;return(0,D.jsx)(`path`,{d:`M ${r} ${a} C ${(r+i)/2} ${a}, ${(r+i)/2} ${s}, ${i} ${s}`,vectorEffect:`non-scaling-stroke`,style:i<r?{fill:`none`,stroke:o(`error`),strokeWidth:1.5,strokeDasharray:`3 2`}:{fill:`none`,stroke:`var(--muted-foreground)`,strokeWidth:1.5,opacity:.4}},`${e.from}-${e.to}`)})}),A.map((e,n)=>e.task?(0,D.jsx)(L,{task:e.task,span:O.get(e.task.id),mode:r,epicId:t.get(e.task.id),pct:N.pct,labelWidth:c,onOpen:s},e.task.id):(0,D.jsx)(`div`,{className:`border-b bg-muted/40`,children:(0,D.jsx)(`span`,{className:`flex items-center px-3.5 text-[10px] uppercase tracking-[0.08em] text-muted-foreground`,style:{height:`var(--row-h)`},children:e.label})},`group-${n}-${e.label}`))]})]})})]})}export{P as EpicsView,N as FlowBoard,R as TimelineView};
import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{U as r,_t as i,kt as a,nt as o,q as s,vt as c}from"./ui-primitives-DRENhlck.js";import{r as l,s as u,u as d}from"./theme-CcOVK72d.js";import{$ as f,A as ee,B as te,C as p,D as ne,E as m,I as re,M as ie,P as ae,R as oe,S as se,T as h,_ as g,b as _,c as ce,d as le,et as ue,f as de,g as v,h as y,it as b,k as fe,l as pe,m as x,nt as S,p as me,q as C,rt as w,v as he,w as ge,x as _e,y as T,z as ve}from"./index-BcEUSS3r.js";import{t as ye}from"./layout-QiuZ_k5v.js";var E=e(t(),1),D=n(),O=`doc-h`,be=[`current`,`draft`,`superseded`,`archived`],k=`text-[10px] font-medium tracking-[0.07em] uppercase text-muted-foreground`;function xe({document:e,selected:t,onSelect:n}){return(0,D.jsx)(m,{asChild:!0,size:`sm`,className:d(`w-full cursor-pointer flex-col items-start gap-0.5 px-2 py-1.5 text-left hover:bg-accent`,t&&`bg-accent`),children:(0,D.jsxs)(`button`,{type:`button`,"aria-current":t?`true`:void 0,onClick:n,children:[(0,D.jsxs)(`span`,{className:`flex w-full items-center gap-1.5`,children:[(0,D.jsx)(`span`,{className:`flex-1 truncate text-xs font-medium`,children:e.title}),(0,D.jsx)(`span`,{className:d(`font-mono text-[10px]`,!e.managed&&`text-muted-foreground`),style:e.managed?{color:l(e.status)}:void 0,children:e.managed?e.status:`indexed`})]}),(0,D.jsx)(`span`,{className:`w-full truncate font-mono text-[10px] text-muted-foreground`,children:e.path})]})})}function Se({entries:e,activeId:t,onJump:n}){let r=Math.min(...e.map(e=>e.level));return(0,D.jsxs)(`aside`,{"aria-label":`Document outline`,className:`hidden w-[228px] shrink-0 overflow-y-auto border-l px-3 py-6.5 xl:block`,children:[(0,D.jsx)(`span`,{className:d(k,`px-2`),children:`on this page`}),(0,D.jsx)(`nav`,{className:`mt-2 flex flex-col gap-px`,children:e.map(e=>{let i=e.id===t;return(0,D.jsx)(`button`,{type:`button`,"aria-current":i?`true`:void 0,className:d(`cursor-pointer rounded-md px-2 py-1 text-left text-xs leading-snug transition-colors hover:bg-accent`,i?`bg-accent font-medium text-foreground`:`text-muted-foreground`),style:{paddingLeft:`${8+Math.min(e.level-r,3)*12}px`},onClick:()=>n(e.id),children:e.text},e.id)})})]})}function A({label:e,value:t}){return(0,D.jsxs)(g,{className:`w-auto min-w-[120px] gap-0.5 rounded-lg border bg-card px-3 py-2 shadow-xs`,children:[(0,D.jsx)(`span`,{className:k,children:e}),(0,D.jsx)(`span`,{className:`text-[13px] font-medium`,children:t})]})}function j({label:e,links:t,onOpen:n}){return t.length?(0,D.jsxs)(`section`,{className:`flex flex-col gap-1.5`,children:[(0,D.jsx)(`span`,{className:k,children:e}),t.map((e,t)=>{let r=!e.exists&&!e.title;return(0,D.jsx)(m,{asChild:!0,variant:`outline`,size:`sm`,className:d(`w-full cursor-pointer gap-2 px-2.5 py-2 text-left hover:bg-accent`,r&&`cursor-default opacity-55 hover:bg-transparent`),children:(0,D.jsxs)(`button`,{type:`button`,disabled:r,onClick:()=>n(e.id),children:[(0,D.jsx)(`span`,{className:`min-w-[78px] shrink-0 font-mono text-[11px] font-medium`,children:e.id}),(0,D.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-xs text-muted-foreground`,children:e.title||(e.exists===!1?`Missing record`:e.id)}),(e.relations??[e.relation]).filter(Boolean).map(e=>(0,D.jsx)(S,{variant:`secondary`,className:`font-mono text-[10px]`,children:e},e))]})},`${e.id}-${t}`)})]}):null}var M=(0,D.jsx)(`span`,{"aria-hidden":`true`,className:`text-muted-foreground`,children:`·`});function N({id:e,onSelect:t,onOpen:n}){let[r,i]=(0,E.useState)(null),[a,o]=(0,E.useState)(``),s=(0,E.useRef)(null),d=(0,E.useMemo)(()=>r?_e(r.body||``,O):[],[r]);return(0,E.useEffect)(()=>{let t=!0;return i(null),o(``),C.record(e).then(e=>{t&&i(e.record)}).catch(e=>{t&&o(e.message)}),()=>{t=!1}},[e]),a?(0,D.jsx)(`div`,{className:`px-4 py-3 text-xs text-muted-foreground`,children:a}):r?(0,D.jsxs)(`div`,{ref:s,className:`flex min-h-0 flex-1 flex-col overflow-y-auto px-4 py-3`,children:[(0,D.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 font-mono text-[11px] text-muted-foreground`,children:[(0,D.jsx)(`span`,{children:r.id}),M,(0,D.jsx)(`span`,{children:r.documentKind}),M,(0,D.jsx)(`span`,{style:{color:l(r.status)},children:r.status}),M,(0,D.jsx)(`span`,{children:r.managed?`managed`:`indexed`}),(0,D.jsxs)(u,{type:`button`,variant:`ghost`,size:`sm`,className:`ml-auto px-2`,onClick:()=>n(e),children:[(0,D.jsx)(c,{"aria-hidden":`true`,className:`size-3`}),`Open in Docs`]})]}),(0,D.jsx)(`h2`,{className:`mt-1 text-sm font-medium`,children:r.title}),(0,D.jsx)(`p`,{className:`font-mono text-[11px] text-muted-foreground`,children:r.path}),r.freshness?.length?(0,D.jsx)(w,{className:`mt-3`,children:(0,D.jsx)(b,{children:r.freshness.map(e=>e.message).join(` `)})}):null,(0,D.jsxs)(`div`,{className:`mt-3 flex min-w-0 items-start gap-1`,children:[(0,D.jsx)(`div`,{className:`min-w-0 flex-1 [&>.typeset]:[--typeset-leading:1.6] [&>.typeset]:[--typeset-size:0.8125rem] [&>.typeset>:not(.typeset-scroll)]:max-w-[72ch]`,children:(0,D.jsx)(_,{source:r.body||`_This document is empty._`,onOpen:t,headingPrefix:O})}),(0,D.jsx)(x,{entries:d,container:s})]})]}):(0,D.jsxs)(`div`,{className:`flex items-center gap-2 px-4 py-3 text-sm text-muted-foreground`,children:[(0,D.jsx)(h,{}),` Reading `,e,`…`]})}function P({selectedId:e,onSelect:t,onOpenCard:n,search:c,onSearchChange:m}){let x=ge(),[S,N]=(0,E.useState)([]),[P,F]=(0,E.useState)(!0),[I,L]=(0,E.useState)(``),[R,Ce]=(0,E.useState)(!1),[z,we]=(0,E.useState)(!1),[B,V]=(0,E.useState)(null),[H,U]=(0,E.useState)(!1),[W,G]=(0,E.useState)(``),[K,Te]=(0,E.useState)(null),[Ee,q]=(0,E.useState)(0);te(e=>{ve(e,`/docs/`,`docs/`)&&q(e=>e+1)}),(0,E.useEffect)(()=>{let e=!1;F(!0);let t=window.setTimeout(()=>{C.docs(c.trim()).then(t=>{e||(N(t.records),L(``))}).catch(t=>{e||L(t instanceof Error?t.message:String(t))}).finally(()=>{e||F(!1)})},c?180:0);return()=>{e=!0,window.clearTimeout(t)}},[c,Ee]),(0,E.useEffect)(()=>{if(!B||K)return;let e=!1;return C.tasks().then(t=>{e||Te(t.schema.docs)}).catch(()=>{}),()=>{e=!0}},[B,K]);let J=(0,E.useMemo)(()=>R?S.filter(e=>e.managed):S,[S,R]),De=(0,E.useMemo)(()=>{let e=J.filter(e=>e.managed),t=J.filter(e=>!e.managed);return[{key:`managed`,label:`.project/docs · managed`,docs:e},{key:`indexed`,label:`indexed · read only`,docs:t}].filter(e=>e.docs.length>0)},[J]),Oe=ue(),Y=J.find(t=>t.id===e)||(Oe?void 0:J[0]),ke=(0,E.useRef)(null),[Ae,X]=(0,E.useState)(``),Z=(0,E.useMemo)(()=>Y&&!z?_e(Y.body,O):[],[Y,z]),Q=Z.length>1;(0,E.useEffect)(()=>{X(``);let e=ke.current;if(!e||!Q)return;let t=new Map,n=new IntersectionObserver(e=>{for(let n of e)t.set(n.target.id,n.isIntersecting);let n=Z.find(e=>t.get(e.id));n&&X(n.id)},{root:e,rootMargin:`0px 0px -66% 0px`,threshold:0});for(let e of Z){let t=document.getElementById(e.id);t&&n.observe(t)}return()=>n.disconnect()},[Z,Q]);let je=e=>{document.getElementById(e)?.scrollIntoView({block:`start`,behavior:`smooth`}),X(e)},$=e=>{let r=S.find(t=>t.id===e);r?t(r.id):n(e)},Me=(0,E.useMemo)(()=>{let e=new Set(K?.kinds??[]);for(let t of S)t.managed&&e.add(t.documentKind);return B&&e.add(B.kind),[...e].sort()},[K,S,B]),Ne=(0,E.useMemo)(()=>{let e=new Set(K?.statuses??be);for(let t of S)t.managed&&e.add(t.status);return B&&e.add(B.status),[...e].sort()},[K,S,B]);function Pe(e){G(``),V({id:e.id,title:e.title,kind:e.documentKind,status:e.status,owners:(e.owners??[]).join(`, `),reviewed:e.reviewed??``})}async function Fe(){if(!B)return;let e=S.find(e=>e.id===B.id);if(!e){G(`This document no longer exists in the workspace.`);return}let t=B.owners.split(`,`).map(e=>e.trim()).filter(Boolean),n={},r=B.title.trim();if(r&&r!==e.title&&(n.title=r),B.kind!==e.documentKind&&(n.kind=B.kind),B.status!==e.status&&(n.status=B.status),t.join(`
`)!==(e.owners??[]).join(`
`)&&(n.owners=t),(B.reviewed||``)!==(e.reviewed??``)&&(n.reviewed=B.reviewed||null),!Object.keys(n).length){V(null);return}U(!0),G(``);try{let t=await C.patchDocument(e.id,n,e.revision);N(n=>n.map(n=>n.id===e.id?t.record:n)),V(null)}catch(e){let t=e;t.code?.endsWith(`WRITE_CONFLICT`)?(q(e=>e+1),G(`The document changed on disk; the list was refreshed. Save again to apply your changes to the latest revision.`)):G(t.message||String(e))}finally{U(!1)}}return(0,D.jsxs)(`div`,{className:`flex min-h-0 flex-1`,children:[(0,D.jsxs)(`aside`,{"aria-label":`Documents`,className:d(`min-h-0 w-full shrink-0 flex-col border-r px-2 py-3 lg:flex lg:w-[290px]`,Y?`hidden`:`flex`),children:[(0,D.jsx)(re,{className:`pb-2.5`,before:(0,D.jsx)(ae,{scope:`records`,value:c,label:`Search documentation`,onChange:m}),children:(0,D.jsx)(oe,{label:`managed`,on:R,onLabel:`only`,offLabel:`all`,onChange:Ce})}),(0,D.jsx)(`div`,{"aria-busy":P||void 0,className:`min-h-0 flex-1 overflow-y-auto`,children:P?(0,D.jsxs)(`span`,{className:`flex items-center gap-2 px-2 py-1.5 font-mono text-[10.5px] text-muted-foreground`,children:[(0,D.jsx)(h,{className:`size-3`}),`Loading documents…`]}):I?(0,D.jsx)(w,{variant:`destructive`,className:`mt-1.5`,children:(0,D.jsx)(b,{children:I})}):De.length?De.map(e=>(0,D.jsxs)(`div`,{className:`flex flex-col gap-px pb-3.5`,children:[(0,D.jsxs)(`span`,{className:`flex items-center gap-2 px-2 py-1.5 font-mono text-[10.5px] text-muted-foreground`,children:[(0,D.jsx)(`span`,{className:`text-foreground/80`,children:e.label}),(0,D.jsx)(`span`,{children:e.docs.length})]}),e.docs.map(e=>(0,D.jsx)(xe,{document:e,selected:Y?.id===e.id,onSelect:()=>t(e.id)},e.id))]},e.key)):(0,D.jsx)(ne,{className:`gap-2 p-4 md:p-4`,children:(0,D.jsxs)(ee,{children:[(0,D.jsx)(ie,{className:`text-sm`,children:`No documents found.`}),(0,D.jsx)(fe,{className:`text-xs`,children:R?`Try another search, or include indexed files.`:`Try another search.`})]})})})]}),(0,D.jsx)(`section`,{ref:ke,className:d(`min-w-0 flex-1 overflow-y-auto px-6 py-6.5 sm:px-8.5`,Y?`block`:`hidden lg:block`),children:(0,D.jsx)(`div`,{className:ye,children:Y?(0,D.jsxs)(D.Fragment,{children:[(0,D.jsxs)(u,{type:`button`,variant:`ghost`,size:`sm`,className:`-ml-2 mb-2 lg:hidden`,onClick:()=>t(null),children:[(0,D.jsx)(a,{"aria-hidden":`true`}),`All documents`]}),(0,D.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 font-mono text-[11px] text-muted-foreground`,children:[(0,D.jsx)(`span`,{children:Y.id}),M,(0,D.jsx)(`span`,{children:Y.documentKind}),M,(0,D.jsx)(`span`,{style:{color:l(Y.status)},children:Y.status}),M,(0,D.jsx)(`span`,{children:Y.managed?`managed`:`indexed`}),(0,D.jsx)(`span`,{className:`flex-1`}),Y.managed?(0,D.jsxs)(D.Fragment,{children:[(0,D.jsxs)(u,{type:`button`,variant:`outline`,size:`sm`,disabled:x,title:x?p:void 0,onClick:()=>we(e=>!e),children:[z?(0,D.jsx)(i,{"aria-hidden":`true`}):(0,D.jsx)(o,{"aria-hidden":`true`}),z?`Preview`:`Edit`]}),(0,D.jsxs)(u,{type:`button`,variant:`outline`,size:`sm`,disabled:x,title:x?p:void 0,onClick:()=>Pe(Y),children:[(0,D.jsx)(s,{"aria-hidden":`true`}),`Metadata`]})]}):null]}),(0,D.jsx)(`h2`,{className:`mt-3 mb-1.5 text-2xl font-semibold tracking-tight`,children:Y.title}),(0,D.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground [overflow-wrap:anywhere]`,children:Y.path}),(0,D.jsxs)(`div`,{className:`mt-4.5 flex flex-wrap gap-2`,children:[(0,D.jsx)(A,{label:`kind`,value:Y.documentKind}),(0,D.jsx)(A,{label:`status`,value:Y.status}),(0,D.jsx)(A,{label:`reviewed`,value:Y.reviewed||`—`}),(0,D.jsx)(A,{label:`owners`,value:Y.owners?.join(`, `)||`—`}),(0,D.jsx)(A,{label:`backlinks`,value:String(Y.incomingTotal??Y.incoming.length)}),Y.updated?(0,D.jsx)(A,{label:`updated`,value:Y.updated}):null]}),Y.freshness.length>0?(0,D.jsxs)(w,{role:`status`,className:`mt-4.5 max-w-2xl`,children:[(0,D.jsx)(r,{"aria-hidden":`true`,className:`text-sev-warning`}),(0,D.jsx)(b,{children:Y.freshness.map(e=>(0,D.jsx)(`span`,{children:e.message},`${e.code}-${e.message}`))})]}):null,(0,D.jsx)(`div`,{className:`mt-6.5`,children:z&&Y.managed?(0,D.jsx)(se,{value:Y.body,revision:Y.revision,onSave:async(e,t)=>{let n=await C.patchDocument(Y.id,{body:e},t);N(e=>e.map(e=>e.id===Y.id?n.record:e))}},Y.id):Y.body.trim()?(0,D.jsx)(_,{source:Y.body,headingPrefix:O,onOpen:$}):(0,D.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:Y.managed?`This document is empty. Use Edit to write its first version.`:`This file has no body to render.`})}),Y.outgoing.length||Y.incoming.length||Y.scope?.length?(0,D.jsxs)(`div`,{className:`mt-7 flex max-w-[70ch] flex-col gap-4.5`,children:[(0,D.jsx)(j,{label:`links to`,links:Y.outgoing,onOpen:$}),(0,D.jsx)(j,{label:(Y.incomingTotal??Y.incoming.length)>Y.incoming.length?`backlinks (${Y.incoming.length} of ${Y.incomingTotal})`:`backlinks`,links:Y.incoming,onOpen:$}),Y.scope?.length?(0,D.jsxs)(`section`,{className:`flex flex-col gap-1.5`,children:[(0,D.jsx)(`span`,{className:k,children:`scope`}),Y.scope.map(e=>(0,D.jsx)(`span`,{className:`font-mono text-[10.5px] text-muted-foreground [overflow-wrap:anywhere]`,children:e},e))]}):null]}):null]}):(0,D.jsx)(`div`,{className:`flex h-full items-center justify-center text-xs text-muted-foreground`,children:P?`Loading documents…`:`Select a document from the list to read it.`})})}),Q?(0,D.jsx)(Se,{entries:Z,activeId:Ae,onJump:je}):null,(0,D.jsx)(ce,{open:B!==null,onOpenChange:e=>{!e&&!H&&V(null)},children:(0,D.jsxs)(pe,{"aria-describedby":void 0,children:[(0,D.jsx)(de,{children:(0,D.jsx)(me,{children:`Edit metadata${B?` — ${B.id}`:``}`})}),B?(0,D.jsxs)(he,{className:`gap-4`,children:[(0,D.jsxs)(g,{children:[(0,D.jsx)(T,{htmlFor:`docs-meta-title`,children:`title`}),(0,D.jsx)(f,{id:`docs-meta-title`,value:B.title,onChange:e=>V({...B,title:e.target.value})})]}),(0,D.jsxs)(`div`,{className:`grid grid-cols-2 gap-2.5`,children:[(0,D.jsxs)(g,{className:`[&_[data-slot=native-select-wrapper]]:w-full`,children:[(0,D.jsx)(T,{htmlFor:`docs-meta-kind`,children:`kind`}),(0,D.jsx)(y,{id:`docs-meta-kind`,value:B.kind,onChange:e=>V({...B,kind:e.target.value}),children:Me.map(e=>(0,D.jsx)(v,{value:e,children:e},e))})]}),(0,D.jsxs)(g,{className:`[&_[data-slot=native-select-wrapper]]:w-full`,children:[(0,D.jsx)(T,{htmlFor:`docs-meta-status`,children:`status`}),(0,D.jsx)(y,{id:`docs-meta-status`,value:B.status,onChange:e=>V({...B,status:e.target.value}),children:Ne.map(e=>(0,D.jsx)(v,{value:e,children:e},e))})]})]}),(0,D.jsxs)(`div`,{className:`grid grid-cols-2 gap-2.5`,children:[(0,D.jsxs)(g,{children:[(0,D.jsx)(T,{htmlFor:`docs-meta-owners`,children:`owners`}),(0,D.jsx)(f,{id:`docs-meta-owners`,value:B.owners,placeholder:`comma-separated`,onChange:e=>V({...B,owners:e.target.value})})]}),(0,D.jsxs)(g,{children:[(0,D.jsx)(T,{htmlFor:`docs-meta-reviewed`,children:`reviewed`}),(0,D.jsx)(f,{id:`docs-meta-reviewed`,type:`date`,value:B.reviewed,onChange:e=>V({...B,reviewed:e.target.value})})]})]}),W?(0,D.jsx)(w,{variant:`destructive`,children:(0,D.jsx)(b,{children:W})}):null]}):null,(0,D.jsxs)(le,{children:[(0,D.jsx)(u,{type:`button`,variant:`outline`,disabled:H,onClick:()=>V(null),children:`Cancel`}),(0,D.jsx)(u,{type:`button`,disabled:H,onClick:()=>void Fe(),children:H?`Saving…`:`Save`})]})]})})]})}export{N as DocPanel,P as DocsView};
import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{D as r,E as i,Ft as a,It as o,jt as s,q as ee,wt as te}from"./ui-primitives-DRENhlck.js";import{n as c,o as l,s as u,u as d}from"./theme-CcOVK72d.js";import{A as ne,D as f,J as re,M as ie,N as ae,Q as oe,X as se,Y as ce,Z as le,a as p,g as m,h,i as ue,k as de,o as g,s as fe}from"./index-BcEUSS3r.js";import{t as _}from"./progress-CzHcd1lF.js";var v=e(t(),1),y=n();function b({className:e,...t}){return(0,y.jsx)(i,{"data-slot":`checkbox`,className:d(`peer size-4 shrink-0 rounded-[4px] border border-input shadow-xs transition-shadow outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:bg-input/30 dark:aria-invalid:ring-destructive/40 dark:data-[state=checked]:bg-primary`,e),...t,children:(0,y.jsx)(r,{"data-slot":`checkbox-indicator`,className:`grid place-content-center text-current transition-none`,children:(0,y.jsx)(s,{className:`size-3.5`})})})}function pe({className:e,...t}){return(0,y.jsx)(`div`,{"data-slot":`table-container`,className:`relative w-full overflow-x-auto`,children:(0,y.jsx)(`table`,{"data-slot":`table`,className:d(`w-full caption-bottom text-sm`,e),...t})})}function me({className:e,...t}){return(0,y.jsx)(`thead`,{"data-slot":`table-header`,className:d(`[&_tr]:border-b`,e),...t})}function he({className:e,...t}){return(0,y.jsx)(`tbody`,{"data-slot":`table-body`,className:d(`[&_tr:last-child]:border-0`,e),...t})}function x({className:e,...t}){return(0,y.jsx)(`tr`,{"data-slot":`table-row`,className:d(`border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted`,e),...t})}function S({className:e,...t}){return(0,y.jsx)(`th`,{"data-slot":`table-head`,className:d(`h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]`,e),...t})}function C({className:e,...t}){return(0,y.jsx)(`td`,{"data-slot":`table-cell`,className:d(`p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]`,e),...t})}var w=[[`id`,`id`],[`title`,`title · claim`],[`status`,`status`],[`priority`,`prio`],[`type`,`type`],[`area`,`area`],[`epic`,`links`],[`updated`,`updated`]],T=new Map(p.map((e,t)=>[e,t])),E=new Map(g.map((e,t)=>[e,t])),D=w.length+1;function O(e,t){let n=new Map;for(let r of e){let e=r[t];typeof e==`string`&&n.set(e,(n.get(e)||0)+1)}return n}function k({title:e,values:t,counts:n,selected:r,color:i,onSelect:a}){let o=t.filter(e=>n.has(e));if(!o.length)return null;let s=Math.max(...o.map(e=>n.get(e)||0));return(0,y.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,y.jsx)(`span`,{className:`px-1.5 font-mono text-[10px] tracking-widest uppercase text-muted-foreground`,children:e}),o.map(e=>{let t=n.get(e)||0,o=r===e;return(0,y.jsxs)(`button`,{type:`button`,"aria-pressed":o,onClick:()=>a(o?``:e),className:d(`flex w-full cursor-pointer flex-col gap-1 rounded-md px-1.5 py-1 text-left transition-colors hover:bg-accent/50`,o&&`bg-accent`),children:[(0,y.jsxs)(`span`,{className:`flex w-full items-center gap-1.5`,children:[(0,y.jsx)(`span`,{className:d(`min-w-0 flex-1 truncate text-xs`,o?`font-medium text-foreground`:`text-muted-foreground`),children:e}),(0,y.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:t})]}),(0,y.jsx)(_,{value:s?Math.round(t/s*100):0,className:d(`h-[5px] bg-muted [&>div]:bg-current`,!i&&`text-primary`),style:i?{color:i(e)}:void 0})]},e)})]})}function A({label:e,value:t,options:n,color:r,withDot:i,onChange:a}){return(0,y.jsxs)(`span`,{className:`inline-flex items-center gap-1.5`,style:{color:r},children:[i?(0,y.jsx)(`span`,{className:`size-1.5 shrink-0 rounded-full bg-current`,"aria-hidden":`true`}):null,(0,y.jsx)(h,{"aria-label":e,value:t,onChange:e=>a(e.target.value),className:`h-[22px] cursor-pointer border-transparent bg-transparent px-1 py-0 pr-8 font-mono text-[11px] text-inherit shadow-none dark:bg-transparent dark:hover:bg-transparent`,children:n.map(e=>(0,y.jsx)(m,{value:e,children:e},e))})]})}var ge=(0,v.memo)(function({task:e,epicId:t,checked:n,isOpen:r,onToggle:i,onOpen:a,onPatch:o}){let s=(e.depends?.length??0)+ +!!e.parent;return(0,y.jsxs)(x,{className:`h-[var(--row-h)] cursor-pointer`,"data-state":r?`selected`:void 0,tabIndex:0,onClick:()=>a(e.id),onKeyDown:t=>{t.key===`Enter`&&a(e.id)},children:[(0,y.jsx)(C,{className:d(`w-7 border-l-2 border-l-transparent`,r&&`border-l-primary`),onClick:e=>e.stopPropagation(),children:(0,y.jsx)(b,{"aria-label":`Select ${e.id}`,checked:n,onCheckedChange:()=>i(e.id)})}),(0,y.jsx)(C,{className:`font-mono text-xs text-muted-foreground`,children:e.id}),(0,y.jsx)(C,{className:`max-w-[520px]`,children:(0,y.jsxs)(`span`,{className:`flex min-w-0 items-baseline gap-2`,children:[(0,y.jsx)(`span`,{className:`min-w-0 truncate font-medium`,children:e.title}),e.claimed_by?(0,y.jsxs)(`span`,{className:`font-mono text-[10px] whitespace-nowrap text-muted-foreground/60`,children:[`· `,e.claimed_by]}):null]})}),(0,y.jsx)(C,{onClick:e=>e.stopPropagation(),children:(0,y.jsx)(A,{label:`Status for ${e.id}`,value:e.status,options:g,color:l(e.status),withDot:!0,onChange:t=>void o(e.id,{status:t}).catch(()=>void 0)})}),(0,y.jsx)(C,{onClick:e=>e.stopPropagation(),children:(0,y.jsx)(A,{label:`Priority for ${e.id}`,value:e.priority,options:p,color:c(e.priority),onChange:t=>void o(e.id,{priority:t}).catch(()=>void 0)})}),(0,y.jsx)(C,{className:`font-mono text-[11px] text-muted-foreground`,children:e.type}),(0,y.jsx)(C,{className:`font-mono text-[11px] text-muted-foreground`,children:e.area}),(0,y.jsxs)(C,{className:`font-mono text-[11px] text-muted-foreground/60`,children:[t?(0,y.jsx)(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),a(t)},className:d(`cursor-pointer text-primary hover:underline`,s>0&&`mr-1.5`),children:t}):null,s>0?`${s} ↔`:t?null:`—`]}),(0,y.jsx)(C,{className:`font-mono text-[11px] text-muted-foreground/60`,children:e.updated||`—`})]})});function j({tasks:e,allTasks:t,areas:n,filters:r,setFilters:i,epicIds:s,onOpen:d,onPatch:_,onBulkPatch:C}){let[A,j]=(0,v.useState)(()=>new Set),[_e,ve]=(0,v.useState)(null),[M,ye]=(0,v.useState)(`id`),[N,P]=(0,v.useState)(`desc`),[F,I]=(0,v.useState)(``),[L,R]=(0,v.useState)(``),[z,B]=(0,v.useState)(``),V=(0,v.useRef)(null),[H,be]=(0,v.useState)({start:0,end:40}),[U,xe]=(0,v.useState)(40),W=(0,v.useDeferredValue)(r),G=(0,v.useMemo)(()=>{let e=e=>ue(t,{...W,...e});return{status:O(e({status:``}),`status`),type:O(e({type:``,showIdeas:!0}),`type`),priority:O(e({priority:``}),`priority`),area:O(e({area:``}),`area`)}},[t,W]),K=(0,v.useMemo)(()=>{let t=[...e];return t.sort((e,t)=>{let n=0;return n=M===`priority`?(T.get(e.priority)||0)-(T.get(t.priority)||0):M===`status`?(E.get(e.status)||0)-(E.get(t.status)||0):M===`epic`?(s.get(e.id)||``).localeCompare(s.get(t.id)||``):String(e[M]||``).localeCompare(String(t[M]||``),void 0,{numeric:!0}),N===`asc`?n:-n}),t},[s,N,M,e]),q=(0,v.useRef)(0),Se=K.length>0,J=(0,v.useCallback)(()=>{let e=V.current;if(!e)return;let t=parseFloat(getComputedStyle(document.documentElement).getPropertyValue(`--row-h`))||40;xe(t);let n=Math.max(0,Math.floor(e.scrollTop/t)-10),r=Math.ceil(e.clientHeight/t);be({start:n,end:Math.min(q.current,n+r+20)})},[]);(0,v.useEffect)(()=>{let e=V.current;if(!e)return;e.addEventListener(`scroll`,J,{passive:!0}),window.addEventListener(`resize`,J);let t=new MutationObserver(J);return t.observe(document.documentElement,{attributes:!0,attributeFilter:[`data-density`]}),()=>{e.removeEventListener(`scroll`,J),window.removeEventListener(`resize`,J),t.disconnect()}},[J,Se]),(0,v.useEffect)(()=>{q.current=K.length,J()},[J,K.length]);let Ce=[r.search,r.status,r.area,r.type,r.priority,r.milestone,r.showIdeas,r.showClosed,M,N].join(`|`);(0,v.useEffect)(()=>{V.current&&(V.current.scrollTop=0),J()},[Ce,J]);let we=(0,v.useCallback)(e=>{j(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),Te=(0,v.useCallback)(e=>{ve(e),d(e)},[d]),Y=(0,v.useMemo)(()=>K.map(e=>e.id),[K]),X=Y.length>0&&Y.every(e=>A.has(e)),Ee=Y.some(e=>A.has(e)),De=K.slice(H.start,H.end),Z=!!(F||L||z);function Oe(e){M===e?P(e=>e===`asc`?`desc`:`asc`):(ye(e),P(e===`id`?`desc`:`asc`))}async function ke(){let e={};if(F&&(e.status=F),L&&(e.priority=L),z&&(e.area=z),!(!Z||A.size===0))try{await C([...A],e),j(new Set),I(``),R(``),B(``)}catch{}}let Q=(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(k,{title:`status`,values:g,counts:G.status,selected:r.status,color:l,onSelect:e=>i(t=>({...t,status:e}))}),(0,y.jsx)(k,{title:`priority`,values:p,counts:G.priority,selected:r.priority,color:c,onSelect:e=>i(t=>({...t,priority:e}))}),(0,y.jsx)(k,{title:`area`,values:n,counts:G.area,selected:r.area,onSelect:e=>i(t=>({...t,area:e}))}),(0,y.jsx)(k,{title:`type`,values:fe,counts:G.type,selected:r.type,onSelect:e=>i(t=>({...t,type:e}))})]}),$=[r.status,r.priority,r.area,r.type].filter(Boolean).length;return(0,y.jsxs)(`div`,{className:`flex min-h-0 flex-1`,children:[(0,y.jsx)(`aside`,{"aria-label":`Backlog facets`,className:`hidden w-[204px] flex-none flex-col gap-5 overflow-y-auto border-r px-3.5 py-4 lg:flex`,children:Q}),(0,y.jsxs)(`div`,{className:`flex min-h-0 min-w-0 flex-1 flex-col`,children:[(0,y.jsxs)(`div`,{className:`flex flex-none items-center gap-2 px-3.5 pt-2.5 lg:hidden`,children:[(0,y.jsxs)(re,{children:[(0,y.jsx)(oe,{asChild:!0,children:(0,y.jsxs)(u,{variant:`outline`,size:`sm`,children:[(0,y.jsx)(ee,{className:`size-3.5`}),`Facets`,$?(0,y.jsx)(`span`,{className:`font-mono text-[10px] text-muted-foreground`,children:$}):null]})}),(0,y.jsxs)(ce,{side:`left`,className:`w-[280px] gap-0 sm:max-w-[280px]`,children:[(0,y.jsx)(se,{className:`pb-2`,children:(0,y.jsx)(le,{className:`font-mono text-[11px] tracking-wide uppercase`,children:`Facets`})}),(0,y.jsx)(`div`,{className:`flex flex-col gap-5 overflow-y-auto px-4 pb-4`,children:Q})]})]}),(0,y.jsxs)(`span`,{className:`font-mono text-[10.5px] text-muted-foreground/70`,children:[K.length.toLocaleString(),` row`,K.length===1?``:`s`,` · scroll sideways for every column`]})]}),A.size>0&&(0,y.jsxs)(`div`,{role:`region`,"aria-label":`Bulk actions`,className:`mx-3.5 mt-2.5 mb-2.5 flex flex-none flex-wrap items-center gap-2 rounded-md border bg-muted/50 px-3 py-2`,children:[(0,y.jsxs)(`span`,{className:`font-mono text-[11px]`,children:[A.size,` selected`]}),(0,y.jsxs)(h,{"aria-label":`Set status`,value:F,onChange:e=>I(e.target.value),size:`sm`,children:[(0,y.jsx)(m,{value:``,children:`status…`}),g.map(e=>(0,y.jsx)(m,{value:e,children:e},e))]}),(0,y.jsxs)(h,{"aria-label":`Set priority`,value:L,onChange:e=>R(e.target.value),size:`sm`,children:[(0,y.jsx)(m,{value:``,children:`priority…`}),p.map(e=>(0,y.jsx)(m,{value:e,children:e},e))]}),(0,y.jsxs)(h,{"aria-label":`Set area`,value:z,onChange:e=>B(e.target.value),size:`sm`,children:[(0,y.jsx)(m,{value:``,children:`area…`}),n.map(e=>(0,y.jsx)(m,{value:e,children:e},e))]}),(0,y.jsxs)(ae,{children:[(0,y.jsx)(u,{size:`sm`,disabled:!Z,onClick:()=>void ke(),children:`Apply`}),(0,y.jsx)(u,{size:`sm`,variant:`outline`,onClick:()=>j(new Set),children:`Clear`})]})]}),K.length===0?(0,y.jsx)(f,{children:(0,y.jsxs)(ne,{children:[(0,y.jsx)(ie,{children:`No cards match`}),(0,y.jsx)(de,{children:`Adjust filters or clear the search`})]})}):(0,y.jsx)(`div`,{ref:V,className:`min-w-0 flex-1 overflow-auto [&>[data-slot=table-container]]:overflow-visible`,children:(0,y.jsxs)(pe,{className:`text-[13px]`,children:[(0,y.jsx)(me,{children:(0,y.jsxs)(x,{className:`hover:bg-transparent`,children:[(0,y.jsx)(S,{className:`sticky top-0 z-10 w-7 bg-background`,children:(0,y.jsx)(b,{"aria-label":`Select all matching cards`,checked:X?!0:Ee?`indeterminate`:!1,onCheckedChange:()=>j(e=>{let t=new Set(e);return X?Y.forEach(e=>t.delete(e)):Y.forEach(e=>t.add(e)),t})})}),w.map(([e,t])=>(0,y.jsx)(S,{"aria-sort":M===e?N===`asc`?`ascending`:`descending`:`none`,className:`sticky top-0 z-10 bg-background`,children:(0,y.jsxs)(u,{variant:`ghost`,size:`sm`,onClick:()=>Oe(e),className:`-ml-2 px-2 text-muted-foreground`,children:[t,M===e?N===`asc`?(0,y.jsx)(a,{className:`size-3`}):(0,y.jsx)(o,{className:`size-3`}):(0,y.jsx)(te,{className:`size-3 opacity-50`})]})},e))]})}),(0,y.jsxs)(he,{children:[H.start>0&&(0,y.jsx)(`tr`,{"aria-hidden":`true`,children:(0,y.jsx)(`td`,{colSpan:D,style:{height:H.start*U}})}),De.map(e=>(0,y.jsx)(ge,{task:e,epicId:s.get(e.id)||``,checked:A.has(e.id),isOpen:_e===e.id,onToggle:we,onOpen:Te,onPatch:_},e.id)),H.end<K.length&&(0,y.jsx)(`tr`,{"aria-hidden":`true`,children:(0,y.jsx)(`td`,{colSpan:D,style:{height:(K.length-H.end)*U}})})]})]})})]})]})}export{j as Explorer};
import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{Ct as r}from"./ui-primitives-DRENhlck.js";import{i,o as a,s as o}from"./theme-CcOVK72d.js";import{A as s,B as c,D as l,H as u,M as d,T as f,V as p,it as m,j as h,k as g,nt as _,q as v,rt as y}from"./index-BcEUSS3r.js";var b=e(t(),1),x=n(),S=[{level:`error`,label:`errors`,hint:`must be fixed for a consistent workspace`,zeroHint:`the doctor does not block the release`},{level:`warning`,label:`warnings`,hint:`worth a look, nothing is broken yet`,zeroHint:`nothing worth flagging`},{level:`info`,label:`infos`,hint:`informational, no action required`,zeroHint:`no notices from the doctor`}];function C(e){return e.replace(/[-_.]+/g,` `)}var w={error:0,warning:1,info:2};function T({onOpen:e}){let[t,n]=(0,b.useState)(null),[T,E]=(0,b.useState)(``),[D,O]=(0,b.useState)(``),[k,A]=(0,b.useState)(0);c(()=>A(e=>e+1)),(0,b.useEffect)(()=>{let e=!0;return v.health().then(t=>{e&&n(t)}).catch(t=>{e&&E(t instanceof Error?t.message:String(t))}),()=>{e=!1}},[k]);let j=(0,b.useMemo)(()=>{if(!t)return[];let e=D?t.issues.filter(e=>e.severity===D):t.issues,n=new Map;for(let t of e){let e=n.get(t.code);e?e.push(t):n.set(t.code,[t])}return[...n.entries()].sort(([e,[t]],[n,[r]])=>{let i=w[t.severity]-w[r.severity];return i===0?e.localeCompare(n):i})},[t,D]);if(T)return(0,x.jsx)(`div`,{className:`p-3.5`,children:(0,x.jsx)(y,{variant:`destructive`,children:(0,x.jsx)(m,{children:T})})});if(!t)return(0,x.jsxs)(`div`,{className:`flex items-center gap-2 p-3.5`,"aria-busy":`true`,children:[(0,x.jsx)(f,{className:`size-3 text-muted-foreground`}),(0,x.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:`running workfile doctor…`})]});let M=[[`cards`,t.modules?.cards??t.cards],[`docs`,t.modules?.docs],[`memory`,t.modules?.memory],[`changelog`,t.modules?.changelog]].filter(([,e])=>e!=null).map(([e,t])=>`${t.toLocaleString()} ${e}`).join(`, `),N=new Intl.DateTimeFormat(void 0,{dateStyle:`medium`,timeStyle:`short`}).format(new Date(t.generatedAt));return(0,x.jsxs)(`div`,{className:`flex-1 overflow-y-auto p-3.5`,children:[(0,x.jsx)(`div`,{className:`mb-2.5 flex gap-1.5`,children:S.map(({level:e,label:n})=>(0,x.jsxs)(o,{type:`button`,variant:`outline`,size:`sm`,"aria-pressed":D===e,className:`aria-pressed:border-ring aria-pressed:bg-accent`,onClick:()=>O(t=>t===e?``:e),children:[n,(0,x.jsx)(_,{variant:`secondary`,className:`px-1.5 font-mono text-[10.5px]`,children:t.counts[e]})]},e))}),(0,x.jsx)(`div`,{className:`flex flex-wrap gap-2.5`,children:S.map(({level:e,label:n,hint:r,zeroHint:o})=>{let s=t.counts[e],c=e===`error`&&s===0?a(`done`):i(e);return(0,x.jsxs)(u,{className:`relative min-w-[13rem] flex-1 gap-1 py-3 pl-5 pr-3.5`,children:[(0,x.jsx)(p,{edge:`left`,color:c}),(0,x.jsxs)(`span`,{className:`flex items-baseline gap-2`,children:[(0,x.jsx)(`span`,{className:`text-[26px] font-semibold tracking-tight`,style:{color:c},children:s}),(0,x.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:n})]}),(0,x.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:s===0?o:r})]},e)})}),(0,x.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-2.5 gap-y-1 px-0.5 pt-4 pb-2`,children:[(0,x.jsxs)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:[`grouped by code · `,M,` · checked `,N]}),(0,x.jsx)(`span`,{className:`ml-auto font-mono text-[10.5px] text-muted-foreground/70`,children:`workfile doctor --json`})]}),j.length===0?(0,x.jsx)(l,{className:`gap-2 p-10`,children:(0,x.jsxs)(s,{children:[(0,x.jsx)(h,{children:(0,x.jsx)(r,{"aria-hidden":`true`,size:20,style:{color:a(`done`)}})}),(0,x.jsx)(d,{className:`text-sm`,children:`All clear`}),(0,x.jsxs)(g,{className:`text-[12.5px]`,children:[`No `,D||`integrity`,` issues found.`]})]})}):(0,x.jsx)(`div`,{className:`flex flex-col gap-2`,children:j.map(([t,n])=>(0,x.jsxs)(u,{className:`gap-0 overflow-hidden py-0`,children:[(0,x.jsxs)(`div`,{className:`flex items-center gap-2 border-b px-3 py-1.5`,children:[(0,x.jsx)(`span`,{"aria-hidden":`true`,className:`size-[7px] rounded-full bg-current`,style:{color:i(n[0].severity)}}),(0,x.jsx)(`span`,{className:`font-mono text-[11.5px]`,children:t}),(0,x.jsx)(`span`,{className:`flex-1 text-[12.5px] text-muted-foreground`,children:C(t)}),(0,x.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground/70`,children:n.length})]}),n.map((t,n)=>(0,x.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-2.5 gap-y-1 border-b px-3 py-[7px] last:border-0`,children:[t.id?(0,x.jsx)(o,{type:`button`,variant:`link`,className:`h-auto w-[82px] flex-[0_0_82px] justify-start p-0 font-mono text-[11px] font-normal`,onClick:()=>e(t.id),children:t.id}):(0,x.jsx)(`span`,{className:`w-[82px] flex-[0_0_82px] font-mono text-[11px] text-muted-foreground/70`,children:`—`}),(0,x.jsx)(`span`,{className:`min-w-[12rem] flex-1 text-[12.5px] text-muted-foreground`,children:t.message}),t.file?(0,x.jsx)(`span`,{className:`max-w-full truncate font-mono text-[10.5px] text-muted-foreground/70 sm:max-w-80`,title:t.file,children:t.file}):null]},`${t.id||t.file}-${n}`))]},t))})]})}export{T as HealthView};
import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{B as r,kt as i,tt as a}from"./ui-primitives-DRENhlck.js";import{i as o,o as s,r as c,s as l,u}from"./theme-CcOVK72d.js";import{$ as d,B as f,C as p,D as m,E as h,F as g,G as _,H as ee,I as v,K as y,L as te,N as ne,P as re,U as b,W as x,_ as S,b as ie,c as C,d as w,f as T,g as E,h as D,it as O,k as ae,l as k,nt as A,p as j,q as M,rt as N,u as P,w as oe,y as F,z as se}from"./index-BcEUSS3r.js";import{t as ce}from"./layout-QiuZ_k5v.js";var I=e(t(),1),L=n(),R=`text-[10px] font-medium tracking-widest uppercase text-muted-foreground`;function z(e){switch(e){case`added`:return s(`done`);case`changed`:return s(`doing`);case`fixed`:return s(`review`);case`removed`:return s(`blocked`);case`security`:return o(`error`);default:return s(`backlog`)}}function le(e,t){let n=null;for(let t of e){let e=/^v?(\d+)\.(\d+)\.(\d+)/.exec(t.version);if(!e)continue;let r=[Number(e[1]),Number(e[2]),Number(e[3])];(n?r[0]-n[0]||r[1]-n[1]||r[2]-n[2]:1)>0&&(n=r)}return n?t.some(e=>[`added`,`removed`,`deprecated`].includes(e.type))?`${n[0]}.${n[1]+1}.0`:`${n[0]}.${n[1]}.${n[2]+1}`:`0.1.0`}function B(e){return e instanceof Error?e.message:String(e)}function V({record:e,selected:t,onSelect:n}){let r=e.kind===`release`?`release`:e.type,i=e.kind===`release`?`var(--primary)`:z(e.type),a=e.kind===`release`?`${e.fragments.length} fragment${e.fragments.length===1?``:`s`} · ${e.date}`:e.area;return(0,L.jsx)(h,{asChild:!0,variant:`outline`,size:`sm`,children:(0,L.jsxs)(`button`,{type:`button`,"aria-current":t?`true`:void 0,onClick:n,className:u(`flex-col flex-nowrap items-stretch gap-1 px-2.5 py-2 text-left shadow-xs`,t?`border-ring bg-accent`:`bg-card hover:border-ring`),children:[(0,L.jsxs)(`span`,{className:`flex items-baseline gap-2`,children:[(0,L.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:e.id}),(0,L.jsx)(`span`,{className:`font-mono text-[10px]`,style:{color:i},children:r}),(0,L.jsx)(`span`,{className:`flex-1`}),(0,L.jsx)(`span`,{className:`max-w-[170px] truncate font-mono text-[10px] text-muted-foreground/70`,children:a})]}),(0,L.jsx)(`span`,{className:`text-sm leading-snug font-normal`,children:e.title})]})})}function H({label:e,records:t,selectedId:n,onSelect:r}){return t.length?(0,L.jsxs)(`div`,{role:`group`,"aria-label":e,className:`flex flex-col gap-1.5 pt-4`,children:[(0,L.jsxs)(`span`,{className:R,children:[e,` · `,t.length]}),t.map(e=>(0,L.jsx)(V,{record:e,selected:e.id===n,onSelect:()=>r(e.id)},e.id))]}):null}function U({id:e,title:t,relation:n,disabled:r,onOpen:i}){return(0,L.jsx)(h,{asChild:!0,variant:`outline`,size:`sm`,children:(0,L.jsxs)(`button`,{type:`button`,disabled:r,onClick:i,className:`flex-nowrap gap-2 bg-card px-2.5 py-1.5 text-left shadow-xs hover:border-ring disabled:pointer-events-none disabled:opacity-55`,children:[(0,L.jsx)(`span`,{className:`shrink-0 font-mono text-[11px] text-muted-foreground`,children:e}),(0,L.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-[12.5px]`,children:t}),n?(0,L.jsx)(A,{variant:`outline`,className:`shrink-0 font-mono text-[10px] font-normal text-muted-foreground`,children:n}):null]})})}function W({label:e,links:t,onOpen:n}){return t.length?(0,L.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[(0,L.jsx)(`span`,{className:R,children:e}),t.map(t=>(0,L.jsx)(U,{id:t.id,title:t.title,relation:t.relation,disabled:t.disabled,onOpen:()=>n(t.id)},`${e}-${t.id}`))]}):null}function ue(e){return e.map(e=>({id:e.id,title:e.title||`Missing record`,relation:e.relation,disabled:!e.exists&&!e.title}))}function de({schema:e,areas:t,onClose:n,onCreated:r}){let[i,a]=(0,I.useState)({title:``,type:e.defaults.type,area:t[0]||`general`,visibility:e.defaults.visibility,body:``}),[o,s]=(0,I.useState)(!1),[c,u]=(0,I.useState)(``),f=(e,t)=>a(n=>({...n,[e]:t})),p=async()=>{s(!0);try{r((await M.createChange(i)).record)}catch(e){u(B(e))}finally{s(!1)}};return(0,L.jsx)(C,{open:!0,onOpenChange:e=>{e||n()},children:(0,L.jsxs)(k,{onOpenAutoFocus:e=>e.preventDefault(),children:[(0,L.jsxs)(T,{children:[(0,L.jsx)(j,{children:`New change fragment`}),(0,L.jsx)(P,{children:`Record one user- or operator-meaningful change.`})]}),(0,L.jsxs)(S,{children:[(0,L.jsx)(F,{htmlFor:`new-fragment-title`,children:`Title`}),(0,L.jsx)(d,{id:`new-fragment-title`,autoFocus:!0,required:!0,maxLength:120,value:i.title,onChange:e=>f(`title`,e.target.value)})]}),(0,L.jsxs)(`div`,{className:`grid grid-cols-3 gap-2.5`,children:[(0,L.jsxs)(S,{children:[(0,L.jsx)(F,{htmlFor:`new-fragment-type`,children:`Type`}),(0,L.jsx)(D,{id:`new-fragment-type`,value:i.type,onChange:e=>f(`type`,e.target.value),children:e.types.map(e=>(0,L.jsx)(E,{value:e,children:e},e))})]}),(0,L.jsxs)(S,{children:[(0,L.jsx)(F,{htmlFor:`new-fragment-area`,children:`Area`}),(0,L.jsx)(D,{id:`new-fragment-area`,value:i.area,onChange:e=>f(`area`,e.target.value),children:t.map(e=>(0,L.jsx)(E,{value:e,children:e},e))})]}),(0,L.jsxs)(S,{children:[(0,L.jsx)(F,{htmlFor:`new-fragment-visibility`,children:`Visibility`}),(0,L.jsx)(D,{id:`new-fragment-visibility`,value:i.visibility,onChange:e=>f(`visibility`,e.target.value),children:e.visibilities.map(e=>(0,L.jsx)(E,{value:e,children:e},e))})]})]}),(0,L.jsxs)(S,{children:[(0,L.jsx)(F,{htmlFor:`new-fragment-details`,children:`Details`}),(0,L.jsx)(g,{id:`new-fragment-details`,rows:5,value:i.body,onChange:e=>f(`body`,e.target.value)})]}),c?(0,L.jsx)(N,{variant:`destructive`,"aria-live":`polite`,children:(0,L.jsx)(O,{children:c})}):null,(0,L.jsxs)(w,{children:[(0,L.jsx)(l,{type:`button`,variant:`outline`,onClick:n,children:`Cancel`}),(0,L.jsx)(l,{type:`button`,disabled:o||!i.title.trim(),onClick:()=>void p(),children:o?`Saving…`:`Create fragment`})]})]})})}function fe({preview:e,suggestedVersion:t,onClose:n,onReleased:r}){let[i,a]=(0,I.useState)(t),[o,s]=(0,I.useState)(``),[c,u]=(0,I.useState)(!1),[f,p]=(0,I.useState)(``),m=async()=>{u(!0);try{await M.createRelease({version:i,title:o||void 0,fragmentIds:e.fragments.map(e=>e.id)}),r()}catch(e){p(B(e))}finally{u(!1)}};return(0,L.jsx)(C,{open:!0,onOpenChange:e=>{e||n()},children:(0,L.jsxs)(k,{className:`flex max-h-[85vh] flex-col sm:max-w-[640px]`,children:[(0,L.jsxs)(T,{children:[(0,L.jsx)(j,{children:`Release preparation`}),(0,L.jsxs)(P,{children:[e.fragments.length,` unreleased fragment`,e.fragments.length===1?``:`s`,` selected.`]})]}),(0,L.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto`,children:[(0,L.jsxs)(`div`,{className:`grid grid-cols-[150px_1fr] gap-2.5`,children:[(0,L.jsxs)(S,{children:[(0,L.jsx)(F,{htmlFor:`release-version`,children:`Version`}),(0,L.jsx)(d,{id:`release-version`,className:`font-mono`,placeholder:`2.4.0`,value:i,onChange:e=>a(e.target.value)})]}),(0,L.jsxs)(S,{children:[(0,L.jsx)(F,{htmlFor:`release-title`,children:`Release title`}),(0,L.jsx)(d,{id:`release-title`,placeholder:`Optional curated title`,value:o,onChange:e=>s(e.target.value)})]})]}),e.groups.map(e=>(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsxs)(`span`,{className:R,style:{color:z(e.type)},children:[e.type,` · `,e.fragments.length]}),e.fragments.map(e=>(0,L.jsxs)(`span`,{className:`flex items-baseline gap-2 text-[12.5px]`,children:[(0,L.jsx)(`span`,{className:`shrink-0 font-mono text-[11px] text-muted-foreground`,children:e.id}),(0,L.jsx)(`span`,{className:`min-w-0 truncate`,children:e.title}),(0,L.jsx)(`span`,{className:`flex-1`}),(0,L.jsx)(`span`,{className:`font-mono text-[10px] text-muted-foreground/70`,children:e.area})]},e.id))]},e.type)),(0,L.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,L.jsx)(`span`,{className:R,children:`release notes preview`}),(0,L.jsx)(`div`,{className:`max-h-[220px] overflow-y-auto rounded-md border bg-background px-3 py-1`,children:(0,L.jsx)(ie,{source:e.markdown||`No release notes to render.`})})]}),f?(0,L.jsx)(N,{variant:`destructive`,"aria-live":`polite`,children:(0,L.jsx)(O,{children:f})}):null]}),(0,L.jsxs)(w,{children:[(0,L.jsx)(l,{type:`button`,variant:`outline`,onClick:n,children:`Cancel`}),(0,L.jsx)(l,{type:`button`,disabled:c||!i.trim()||!e.fragments.length,onClick:()=>void m(),children:c?`Releasing…`:`Create release`})]})]})})}function pe({record:e,schema:t,areas:n,onSaved:r}){let[i,a]=(0,I.useState)({title:e.title,type:e.type,area:e.area,visibility:e.visibility}),[s,c]=(0,I.useState)(!1),[u,f]=(0,I.useState)(``),p=(e,t)=>a(n=>({...n,[e]:t})),m={};for(let t of[`title`,`type`,`area`,`visibility`])i[t]!==e[t]&&(m[t]=i[t]);let h=Object.keys(m).length>0,g=n.includes(e.area)?n:[e.area,...n],v=async()=>{c(!0);try{let t=await M.patchChange(e.id,m,e.revision);f(``),r(t.record)}catch(e){f(B(e))}finally{c(!1)}};return(0,L.jsxs)(ee,{className:`gap-2.5 rounded-lg py-3 shadow-xs`,children:[(0,L.jsx)(_,{className:`px-3`,children:(0,L.jsx)(y,{className:R,children:`edit fragment`})}),(0,L.jsxs)(b,{className:`flex flex-col gap-2.5 px-3`,children:[(0,L.jsxs)(S,{children:[(0,L.jsx)(F,{htmlFor:`edit-fragment-title`,children:`Title`}),(0,L.jsx)(d,{id:`edit-fragment-title`,maxLength:120,value:i.title,onChange:e=>p(`title`,e.target.value)})]}),(0,L.jsxs)(`div`,{className:`grid grid-cols-3 gap-2.5`,children:[(0,L.jsxs)(S,{children:[(0,L.jsx)(F,{htmlFor:`edit-fragment-type`,children:`Type`}),(0,L.jsx)(D,{id:`edit-fragment-type`,value:i.type,onChange:e=>p(`type`,e.target.value),children:t.types.map(e=>(0,L.jsx)(E,{value:e,children:e},e))})]}),(0,L.jsxs)(S,{children:[(0,L.jsx)(F,{htmlFor:`edit-fragment-area`,children:`Area`}),(0,L.jsx)(D,{id:`edit-fragment-area`,value:i.area,onChange:e=>p(`area`,e.target.value),children:g.map(e=>(0,L.jsx)(E,{value:e,children:e},e))})]}),(0,L.jsxs)(S,{children:[(0,L.jsx)(F,{htmlFor:`edit-fragment-visibility`,children:`Visibility`}),(0,L.jsx)(D,{id:`edit-fragment-visibility`,value:i.visibility,onChange:e=>p(`visibility`,e.target.value),children:t.visibilities.map(e=>(0,L.jsx)(E,{value:e,children:e},e))})]})]})]}),(0,L.jsxs)(x,{className:`gap-2.5 px-3`,children:[u?(0,L.jsx)(`span`,{className:`flex-1 text-xs`,style:{color:o(`error`)},"aria-live":`polite`,children:u}):(0,L.jsx)(`span`,{className:`flex-1`}),(0,L.jsx)(l,{type:`button`,variant:`outline`,size:`sm`,disabled:s||!h||!i.title.trim(),onClick:()=>void v(),children:s?`Saving…`:`Save changes`})]})]})}function G({selectedId:e,onSelect:t,onOpenRecord:n,schema:s,areas:d,search:h,onSearchChange:g}){let _=oe(),[y,b]=(0,I.useState)([]),[x,S]=(0,I.useState)(``),[C,w]=(0,I.useState)(``),[T,E]=(0,I.useState)(!0),[D,k]=(0,I.useState)(``),[j,P]=(0,I.useState)(``),[F,R]=(0,I.useState)(!1),[V,U]=(0,I.useState)(null),[G,me]=(0,I.useState)(`public`),[K,q]=(0,I.useState)({content:``,error:``,loading:!0}),[he,ge]=(0,I.useState)(0),_e=()=>ge(e=>e+1);f(e=>{se(e,`/changelog/`)&&_e()}),(0,I.useEffect)(()=>{let e=!1,t=async()=>{E(!0);try{let t=await M.changelog(h.trim(),{state:x||void 0,visibility:C||void 0});if(e)return;b(t.records),k(``)}catch(t){e||k(B(t))}finally{e||E(!1)}},n=window.setTimeout(()=>void t(),h?180:0);return()=>{e=!0,window.clearTimeout(n)}},[h,x,C,he]),(0,I.useEffect)(()=>{let e=!1;return q(e=>({...e,loading:!0})),M.renderedChangelog(G).then(t=>{e||q({content:t.content,error:``,loading:!1})}).catch(t=>{e||q({content:``,error:B(t),loading:!1})}),()=>{e=!0}},[G,he]);let J=(0,I.useMemo)(()=>[...y].sort((e,t)=>{if(e.kind!==t.kind)return e.kind===`change`?-1:1;if(e.kind===`release`&&t.kind===`release`){let n=t.date.localeCompare(e.date);return n===0?t.id.localeCompare(e.id):n}return String(t.updated||``).localeCompare(String(e.updated||``))}),[y]),Y=(0,I.useMemo)(()=>new Map(y.map(e=>[e.id,e])),[y]),X=(0,I.useMemo)(()=>J.filter(e=>e.kind===`change`&&!e.released),[J]),ve=(0,I.useMemo)(()=>J.filter(e=>e.kind===`change`&&e.released),[J]),Z=(0,I.useMemo)(()=>J.filter(e=>e.kind===`release`),[J]),ye=(0,I.useMemo)(()=>le(Z,X),[Z,X]),Q=e?Y.get(e):void 0,$=e=>{if(Y.has(e)){t(e);return}if(/^(CHG|REL)-/.test(e)){S(``),w(``),t(e);return}n(e)},be=()=>{P(``),M.releasePreview().then(U).catch(e=>P(B(e)))},xe=Q?.issues.some(e=>e.severity===`error`)?`destructive`:`default`,Se=(0,L.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,disabled:_,title:_?p:void 0,onClick:()=>R(!0),children:[(0,L.jsx)(a,{"aria-hidden":`true`}),`New fragment`]});return(0,L.jsxs)(`div`,{className:`flex min-h-0 flex-1`,children:[(0,L.jsxs)(`div`,{className:u(`w-full shrink-0 flex-col border-r lg:flex lg:w-[400px]`,Q?`hidden`:`flex`),children:[(0,L.jsxs)(`div`,{className:`flex flex-col gap-2.5 p-3.5 pb-0`,children:[(0,L.jsxs)(ee,{className:`flex-row items-center gap-2.5 border-primary bg-primary/10 p-3`,children:[(0,L.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,L.jsxs)(`span`,{className:`text-[13px] font-semibold`,children:[X.length,` unpublished fragment`,X.length===1?``:`s`]}),(0,L.jsxs)(`span`,{className:`font-mono text-[10.5px] text-muted-foreground`,children:[`next: `,ye,` ·`,` `,s.releaseStrategy]})]}),(0,L.jsx)(l,{type:`button`,size:`sm`,className:`whitespace-nowrap`,disabled:_,title:_?p:void 0,onClick:be,children:`Prepare release`})]}),j?(0,L.jsx)(N,{variant:`destructive`,"aria-live":`polite`,children:(0,L.jsx)(O,{children:j})}):null,(0,L.jsxs)(v,{before:(0,L.jsx)(re,{scope:`records`,value:h,label:`Search history`,onChange:g}),children:[(0,L.jsx)(te,{label:`state`,value:x,options:[{value:`unreleased`},{value:`released`}],onChange:S}),(0,L.jsx)(te,{label:`visibility`,value:C,options:s.visibilities.map(e=>({value:e})),onChange:w})]})]}),(0,L.jsx)(`div`,{className:`min-h-0 flex-1 overflow-y-auto px-3.5 pb-6 [mask-image:linear-gradient(to_bottom,black_calc(100%-24px),transparent)]`,children:T?(0,L.jsx)(`div`,{"aria-busy":`true`,className:`flex flex-col gap-2 pt-4`,children:Array.from({length:6},(e,t)=>(0,L.jsx)(`div`,{className:`h-[52px] animate-pulse rounded-md bg-muted`},t))}):D?(0,L.jsx)(N,{variant:`destructive`,className:`mt-4`,"aria-live":`polite`,children:(0,L.jsx)(O,{children:D})}):J.length?(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(H,{label:`unpublished`,records:X,selectedId:e,onSelect:t}),(0,L.jsx)(H,{label:`releases`,records:Z,selectedId:e,onSelect:t}),(0,L.jsx)(H,{label:`published fragments`,records:ve,selectedId:e,onSelect:t})]}):(0,L.jsx)(m,{className:`mt-4 gap-1 p-4 md:p-4`,children:(0,L.jsx)(ae,{className:`text-xs`,children:`No history records match the filters.`})})})]}),(0,L.jsx)(`div`,{className:u(`min-w-0 flex-1 overflow-y-auto px-6 py-5 sm:px-8.5`,Q?`block`:`hidden lg:block`),children:(0,L.jsx)(`div`,{className:ce,children:Q?(0,L.jsxs)(L.Fragment,{children:[(0,L.jsxs)(l,{type:`button`,variant:`ghost`,size:`sm`,className:`-ml-2 mb-2 lg:hidden`,onClick:()=>t(``),children:[(0,L.jsx)(i,{"aria-hidden":`true`}),`All history`]}),(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-2 gap-y-1 font-mono text-[11px]`,children:[(0,L.jsx)(`span`,{className:`whitespace-nowrap text-primary`,children:Q.id}),(0,L.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,L.jsx)(`span`,{className:`text-muted-foreground/70`,children:Q.kind}),(0,L.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),Q.kind===`change`?(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(`span`,{style:{color:z(Q.type)},children:Q.type}),(0,L.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,L.jsx)(`span`,{className:`text-muted-foreground`,children:Q.area}),(0,L.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,L.jsx)(`span`,{className:`text-muted-foreground`,children:Q.visibility}),(0,L.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,L.jsx)(`span`,{style:{color:c(Q.released?`released`:`unreleased`)},children:Q.released?`released`:`unreleased`}),Q.updated?(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,L.jsx)(`span`,{className:`text-muted-foreground/70`,children:Q.updated})]}):null]}):(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(`span`,{className:`text-primary`,children:Q.version}),(0,L.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,L.jsx)(`span`,{className:`text-muted-foreground`,children:Q.date}),Q.commit?(0,L.jsxs)(L.Fragment,{children:[(0,L.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,L.jsx)(`span`,{className:`text-muted-foreground/70`,children:Q.commit})]}):null,(0,L.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,L.jsxs)(`span`,{className:`text-muted-foreground/70`,children:[Q.fragments.length,` fragment`,Q.fragments.length===1?``:`s`]})]}),(0,L.jsxs)(`span`,{className:`ml-auto flex shrink-0 items-center gap-1`,children:[Se,(0,L.jsx)(l,{type:`button`,variant:`ghost`,size:`icon-sm`,"aria-label":`Close record`,title:`Back to the derived changelog`,onClick:()=>t(``),children:(0,L.jsx)(r,{"aria-hidden":`true`})})]})]}),(0,L.jsx)(`h2`,{className:`mt-2.5 mb-1 text-[26px] leading-tight font-semibold tracking-tight [text-wrap:pretty]`,children:Q.title}),(0,L.jsx)(`div`,{className:`font-mono text-[10.5px] break-all text-muted-foreground/70`,children:Q.path}),Q.issues.length>0?(0,L.jsx)(N,{variant:xe,className:`mt-3.5`,children:(0,L.jsx)(O,{className:`w-full gap-1`,children:Q.issues.map(e=>(0,L.jsxs)(`span`,{className:`flex items-baseline gap-2`,children:[(0,L.jsx)(`span`,{className:`shrink-0 font-mono text-[10.5px]`,style:{color:o(e.severity)},children:e.severity}),(0,L.jsx)(`span`,{children:e.message})]},`${e.code}-${e.message}`))})}):null,(0,L.jsx)(`div`,{className:`mt-4.5`,children:(0,L.jsx)(ie,{source:Q.body||`No additional notes.`,onOpen:$})}),(0,L.jsxs)(`div`,{className:`mt-5.5 flex flex-col gap-3.5`,children:[Q.kind===`change`?(0,L.jsx)(W,{label:`shipped in`,links:(Q.releaseIds||[]).map(e=>({id:e,title:Y.get(e)?.title||`Open release`,relation:`release`})),onOpen:$}):(0,L.jsx)(W,{label:`fragments · ${Q.fragments.length}`,links:Q.fragments.map(e=>{let t=Y.get(e);return{id:e,title:t?.title||`Open fragment`,relation:t?.kind===`change`?t.type:void 0}}),onOpen:$}),(0,L.jsx)(W,{label:`links to`,links:ue(Q.outgoing),onOpen:$}),(0,L.jsx)(W,{label:`backlinks`,links:ue(Q.incoming),onOpen:$})]}),Q.kind===`change`&&!_?(0,L.jsx)(`div`,{className:`mt-5.5`,children:(0,L.jsx)(pe,{record:Q,schema:s,areas:d,onSaved:e=>b(t=>t.map(t=>t.id===e.id?e:t))},`${Q.id}:${Q.revision}`)}):null]}):(0,L.jsxs)(L.Fragment,{children:[(0,L.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-2.5 gap-y-2 border-b pb-3`,children:[(0,L.jsx)(`span`,{className:`text-[13px] font-semibold`,children:`Derived changelog`}),(0,L.jsxs)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:[`visibility `,G,` · CHANGELOG.md`]}),(0,L.jsxs)(`span`,{className:`ml-auto flex flex-wrap items-center gap-2.5`,children:[(0,L.jsx)(ne,{children:s.visibilities.map(e=>(0,L.jsx)(l,{type:`button`,size:`sm`,variant:G===e?`default`:`outline`,"aria-pressed":G===e,onClick:()=>me(e),children:e},e))}),(0,L.jsx)(A,{variant:`outline`,className:`rounded-md font-mono text-[10.5px] font-normal whitespace-nowrap text-muted-foreground`,children:`render --write`}),Se]})]}),K.error?(0,L.jsx)(N,{variant:`destructive`,className:`mt-4`,"aria-live":`polite`,children:(0,L.jsx)(O,{children:K.error})}):(0,L.jsx)(`pre`,{className:`mt-4 font-mono text-xs leading-[1.75] whitespace-pre-wrap text-muted-foreground`,"aria-busy":K.loading||void 0,children:K.loading&&!K.content?`Rendering…`:K.content||`Nothing to render yet — create the first change fragment.`})]})})}),F?(0,L.jsx)(de,{schema:s,areas:d,onClose:()=>R(!1),onCreated:e=>{R(!1),b(t=>[e,...t]),t(e.id)}}):null,V?(0,L.jsx)(fe,{preview:V,suggestedVersion:ye,onClose:()=>U(null),onReleased:()=>{U(null),_e()}}):null]})}export{G as HistoryView};

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{et as r,mt as i,nt as a,tt as o}from"./ui-primitives-DRENhlck.js";import{i as s,r as c,s as l,u}from"./theme-CcOVK72d.js";import{$ as d,B as f,C as p,D as m,E as h,F as g,G as _,H as v,I as y,L as b,P as x,T as S,U as C,_ as w,b as T,c as E,d as D,f as O,g as k,h as A,it as j,k as M,l as N,nt as P,p as F,q as I,rt as L,w as R,y as z,z as B}from"./index-BcEUSS3r.js";var V=e(t(),1),H=n(),U=[`low`,`medium`,`high`],W=[`critical`,`high`,`medium`,`low`],G=`[mask-image:linear-gradient(to_bottom,black_calc(100%_-_24px),transparent)]`;function ee(e){return e&&e[0].toUpperCase()+e.slice(1)}function K(e,t){return`${e} ${t}${e===1?``:`s`}`}function q(e){return{category:e===`learnings`||e===`decisions`,confidence:e===`learnings`,severity:e===`incidents`,expires:e===`context`,review_after:e===`context`}}function J(e){let t=[];switch(e.collection){case`learnings`:t.push(e.confidence,e.category,e.occurrences==null?null:`${e.occurrences}×`);break;case`decisions`:e.superseded_by?.length?t.push(`superseded by ${e.superseded_by.join(`, `)}`):e.supersedes?.length?t.push(`supersedes ${e.supersedes.join(`, `)}`):t.push(e.category);break;case`incidents`:t.push(e.severity,e.corrective_actions?.length?K(e.corrective_actions.length,`corrective action`):null);break;case`conventions`:t.push(e.owners?.length?e.owners.join(`, `):`no owner`);break;case`context`:t.push(e.expires?`expires ${e.expires}`:null,e.review_after?`review after ${e.review_after}`:null);break;default:t.push(e.category,e.severity)}return t.filter(Boolean).join(` · `)}function Y({id:e,label:t,children:n}){return(0,H.jsxs)(w,{className:`gap-1.5 [&_[data-slot=native-select-wrapper]]:w-full`,children:[(0,H.jsx)(z,{htmlFor:e,children:t}),n]})}function te({record:e,selected:t,onSelect:n}){let r=J(e),i=e.lifecycleIssues?.length||0;return(0,H.jsx)(h,{asChild:!0,variant:`outline`,size:`sm`,className:`w-full flex-none flex-col items-stretch gap-1 rounded-lg bg-background px-2.5 py-2 text-left shadow-xs hover:border-ring aria-[current=true]:border-ring aria-[current=true]:bg-accent`,children:(0,H.jsxs)(`button`,{type:`button`,"aria-current":t?`true`:void 0,onClick:n,children:[(0,H.jsxs)(`span`,{className:`flex items-center justify-between gap-2`,children:[(0,H.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:e.id}),(0,H.jsxs)(P,{variant:`outline`,className:`h-[18px] gap-1 rounded-md px-1.5 font-mono text-[10px] font-medium`,children:[(0,H.jsx)(`span`,{className:`size-[5px] shrink-0 rounded-full`,style:{backgroundColor:c(e.status)},"aria-hidden":`true`}),e.status]})]}),(0,H.jsx)(`span`,{className:`text-[13px] font-medium leading-snug`,children:e.title}),r||i?(0,H.jsxs)(`span`,{className:`font-mono text-[10.5px] text-muted-foreground`,children:[r,r&&i?` · `:null,i?(0,H.jsx)(`span`,{style:{color:s(`warning`)},children:K(i,`lifecycle warning`)}):null]}):null]})})}function X({issues:e,kind:t}){return e.length?(0,H.jsx)(H.Fragment,{children:e.map(e=>(0,H.jsx)(L,{variant:e.severity===`error`?`destructive`:`default`,className:`px-3 py-2`,children:(0,H.jsxs)(j,{className:`flex flex-wrap items-baseline gap-x-2 gap-y-0.5`,children:[(0,H.jsx)(`span`,{className:`font-mono text-[10.5px]`,style:{color:s(e.severity)},children:t===`lifecycle`?`lifecycle`:e.severity}),(0,H.jsx)(`span`,{children:e.message})]})},`${t}-${e.code}-${e.message}`))}):null}function Z({label:e,links:t,onOpen:n}){return t.length?(0,H.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,H.jsx)(`span`,{className:`text-[10px] font-medium uppercase tracking-wide text-muted-foreground`,children:e}),t.map(t=>{let r=!t.exists&&!t.title;return(0,H.jsx)(h,{asChild:!0,variant:`outline`,size:`sm`,className:`gap-2 rounded-lg px-2.5 py-2 text-left hover:border-ring disabled:pointer-events-none disabled:opacity-50`,children:(0,H.jsxs)(`button`,{type:`button`,disabled:r,onClick:()=>n(t.id),children:[(0,H.jsx)(`span`,{className:`w-[78px] shrink-0 truncate font-mono text-[11px] font-medium`,children:t.id}),(0,H.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-xs text-muted-foreground`,children:t.title||`Missing record`}),(t.relations??[t.relation||t.kind]).filter(Boolean).map(e=>(0,H.jsx)(P,{variant:`secondary`,className:`h-[18px] rounded-md px-1.5 font-mono text-[10px] font-medium`,children:e},e))]})},`${e}-${t.id}`)})]}):null}function Q({message:e}){return e?(0,H.jsx)(L,{variant:`destructive`,className:`px-3 py-2`,children:(0,H.jsx)(j,{children:e})}):null}function ne({schema:e,initialCollection:t,onClose:n,onCreated:r}){let i=e.collections.find(e=>e.id===t)||e.collections[0],[a,o]=(0,V.useState)({collection:i?.id||`learnings`,status:i?.statuses[0]||`active`,title:``,category:``,confidence:``,severity:``,expires:``,body:``}),[s,c]=(0,V.useState)(!1),[u,f]=(0,V.useState)(``),p=e.collections.find(e=>e.id===a.collection),m=q(a.collection),h=(e,t)=>o(n=>({...n,[e]:t})),_=t=>{let n=e.collections.find(e=>e.id===t);o(e=>({...e,collection:t,status:n?.statuses[0]||`active`}))},v=async()=>{c(!0);try{r((await I.createMemory({collection:a.collection,title:a.title,status:a.status,body:a.body,category:a.category||void 0,confidence:a.confidence||void 0,severity:a.severity||void 0,expires:a.expires||void 0})).record)}catch(e){f(e instanceof Error?e.message:String(e))}finally{c(!1)}};return(0,H.jsx)(E,{open:!0,onOpenChange:e=>{e||n()},children:(0,H.jsxs)(N,{className:`sm:max-w-[520px]`,"aria-describedby":void 0,children:[(0,H.jsx)(O,{children:(0,H.jsxs)(F,{children:[`New `,p?.singular||`record`]})}),(0,H.jsxs)(`div`,{className:`-m-1 flex max-h-[65vh] flex-col gap-3 overflow-y-auto p-1`,children:[(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-2.5`,children:[(0,H.jsx)(Y,{id:`memory-create-collection`,label:`Collection`,children:(0,H.jsx)(A,{id:`memory-create-collection`,value:a.collection,onChange:e=>_(e.target.value),children:e.collections.map(e=>(0,H.jsx)(k,{value:e.id,children:e.id},e.id))})}),(0,H.jsx)(Y,{id:`memory-create-status`,label:`Status`,children:(0,H.jsx)(A,{id:`memory-create-status`,value:a.status,onChange:e=>h(`status`,e.target.value),children:(p?.statuses||[]).map(e=>(0,H.jsx)(k,{value:e,children:e},e))})})]}),(0,H.jsx)(Y,{id:`memory-create-title`,label:`Title`,children:(0,H.jsx)(d,{id:`memory-create-title`,autoFocus:!0,required:!0,maxLength:120,value:a.title,onChange:e=>h(`title`,e.target.value)})}),m.category||m.confidence||m.severity||m.expires?(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-2.5`,children:[m.category?(0,H.jsx)(Y,{id:`memory-create-category`,label:`Category`,children:(0,H.jsx)(d,{id:`memory-create-category`,value:a.category,onChange:e=>h(`category`,e.target.value)})}):null,m.confidence?(0,H.jsx)(Y,{id:`memory-create-confidence`,label:`Confidence`,children:(0,H.jsxs)(A,{id:`memory-create-confidence`,value:a.confidence,onChange:e=>h(`confidence`,e.target.value),children:[(0,H.jsx)(k,{value:``,children:`not set`}),U.map(e=>(0,H.jsx)(k,{value:e,children:e},e))]})}):null,m.severity?(0,H.jsx)(Y,{id:`memory-create-severity`,label:`Severity`,children:(0,H.jsxs)(A,{id:`memory-create-severity`,value:a.severity,onChange:e=>h(`severity`,e.target.value),children:[(0,H.jsx)(k,{value:``,children:`not set`}),W.map(e=>(0,H.jsx)(k,{value:e,children:e},e))]})}):null,m.expires?(0,H.jsx)(Y,{id:`memory-create-expires`,label:`Expires`,children:(0,H.jsx)(d,{id:`memory-create-expires`,type:`date`,value:a.expires,onChange:e=>h(`expires`,e.target.value)})}):null]}):null,(0,H.jsx)(Y,{id:`memory-create-body`,label:`Details`,children:(0,H.jsx)(g,{id:`memory-create-body`,rows:8,value:a.body,onChange:e=>h(`body`,e.target.value)})}),(0,H.jsx)(Q,{message:u})]}),(0,H.jsxs)(D,{children:[(0,H.jsx)(l,{type:`button`,variant:`outline`,onClick:n,children:`Cancel`}),(0,H.jsx)(l,{type:`button`,disabled:s||!a.title.trim(),onClick:()=>void v(),children:s?(0,H.jsxs)(H.Fragment,{children:[(0,H.jsx)(S,{"aria-hidden":`true`}),`Saving…`]}):`Create record`})]})]})})}function re({record:e,statuses:t,onClose:n,onUpdated:r}){let i=q(e.collection),[a,o]=(0,V.useState)({title:e.title,status:e.status,category:e.category||``,confidence:e.confidence||``,severity:e.severity||``,expires:e.expires||``,review_after:e.review_after||``,body:e.body}),[s,c]=(0,V.useState)(!1),[u,f]=(0,V.useState)(``),p=(e,t)=>o(n=>({...n,[e]:t})),m=async()=>{let t={};a.title.trim()&&a.title!==e.title&&(t.title=a.title),a.status!==e.status&&(t.status=a.status),a.body!==e.body&&(t.body=a.body);for(let n of[`category`,`confidence`,`severity`,`expires`,`review_after`])a[n]!==(e[n]||``)&&(t[n]=a[n]||null);if(!Object.keys(t).length){n();return}c(!0);try{r((await I.patchMemory(e.id,t,e.revision)).record),n()}catch(e){f(e instanceof Error?e.message:String(e))}finally{c(!1)}};return(0,H.jsx)(E,{open:!0,onOpenChange:e=>{e||n()},children:(0,H.jsxs)(N,{className:`sm:max-w-[520px]`,"aria-describedby":void 0,children:[(0,H.jsx)(O,{children:(0,H.jsxs)(F,{children:[`Edit `,e.id]})}),(0,H.jsxs)(`div`,{className:`-m-1 flex max-h-[65vh] flex-col gap-3 overflow-y-auto p-1`,children:[(0,H.jsx)(Y,{id:`memory-edit-title`,label:`Title`,children:(0,H.jsx)(d,{id:`memory-edit-title`,autoFocus:!0,required:!0,maxLength:120,value:a.title,onChange:e=>p(`title`,e.target.value)})}),(0,H.jsxs)(`div`,{className:`grid grid-cols-2 gap-2.5`,children:[(0,H.jsx)(Y,{id:`memory-edit-status`,label:`Status`,children:(0,H.jsx)(A,{id:`memory-edit-status`,value:a.status,onChange:e=>p(`status`,e.target.value),children:(t.includes(a.status)?t:[a.status,...t]).map(e=>(0,H.jsx)(k,{value:e,children:e},e))})}),i.category?(0,H.jsx)(Y,{id:`memory-edit-category`,label:`Category`,children:(0,H.jsx)(d,{id:`memory-edit-category`,value:a.category,onChange:e=>p(`category`,e.target.value)})}):null,i.confidence?(0,H.jsx)(Y,{id:`memory-edit-confidence`,label:`Confidence`,children:(0,H.jsxs)(A,{id:`memory-edit-confidence`,value:a.confidence,onChange:e=>p(`confidence`,e.target.value),children:[(0,H.jsx)(k,{value:``,children:`not set`}),U.map(e=>(0,H.jsx)(k,{value:e,children:e},e))]})}):null,i.severity?(0,H.jsx)(Y,{id:`memory-edit-severity`,label:`Severity`,children:(0,H.jsxs)(A,{id:`memory-edit-severity`,value:a.severity,onChange:e=>p(`severity`,e.target.value),children:[(0,H.jsx)(k,{value:``,children:`not set`}),W.map(e=>(0,H.jsx)(k,{value:e,children:e},e))]})}):null,i.expires?(0,H.jsx)(Y,{id:`memory-edit-expires`,label:`Expires`,children:(0,H.jsx)(d,{id:`memory-edit-expires`,type:`date`,value:a.expires,onChange:e=>p(`expires`,e.target.value)})}):null,i.review_after?(0,H.jsx)(Y,{id:`memory-edit-review-after`,label:`Review after`,children:(0,H.jsx)(d,{id:`memory-edit-review-after`,type:`date`,value:a.review_after,onChange:e=>p(`review_after`,e.target.value)})}):null]}),(0,H.jsx)(Y,{id:`memory-edit-body`,label:`Details`,children:(0,H.jsx)(g,{id:`memory-edit-body`,rows:10,value:a.body,onChange:e=>p(`body`,e.target.value)})}),(0,H.jsx)(Q,{message:u})]}),(0,H.jsxs)(D,{children:[(0,H.jsx)(l,{type:`button`,variant:`outline`,onClick:n,children:`Cancel`}),(0,H.jsx)(l,{type:`button`,disabled:s||!a.title.trim(),onClick:()=>void m(),children:s?(0,H.jsxs)(H.Fragment,{children:[(0,H.jsx)(S,{"aria-hidden":`true`}),`Saving…`]}):`Save changes`})]})]})})}function ie({record:e,mode:t,onClose:n,onUpdated:r}){let[i,a]=(0,V.useState)(``),[o,s]=(0,V.useState)(!1),[c,u]=(0,V.useState)(``),f=async()=>{s(!0);try{r((t===`graduate`?await I.graduateMemory(e.id,i.split(`,`).map(e=>e.trim()).filter(Boolean),e.revision):await I.supersedeMemory(e.id,i.trim(),e.revision)).record),n()}catch(e){u(e instanceof Error?e.message:String(e))}finally{s(!1)}};return(0,H.jsx)(E,{open:!0,onOpenChange:e=>{e||n()},children:(0,H.jsxs)(N,{className:`sm:max-w-[420px]`,"aria-describedby":void 0,children:[(0,H.jsx)(O,{children:(0,H.jsxs)(F,{children:[t===`graduate`?`Graduate`:`Supersede`,` `,e.id]})}),(0,H.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,H.jsx)(Y,{id:`memory-lifecycle-target`,label:t===`graduate`?`Target IDs`:`Replacement ID`,children:(0,H.jsx)(d,{id:`memory-lifecycle-target`,autoFocus:!0,placeholder:t===`graduate`?`CONV-0001, DOC-0004`:`ADR-0009`,value:i,onChange:e=>a(e.target.value)})}),(0,H.jsx)(Q,{message:c})]}),(0,H.jsxs)(D,{children:[(0,H.jsx)(l,{type:`button`,variant:`outline`,onClick:n,children:`Cancel`}),(0,H.jsx)(l,{type:`button`,disabled:o||!i.trim(),onClick:()=>void f(),children:o?(0,H.jsxs)(H.Fragment,{children:[(0,H.jsx)(S,{"aria-hidden":`true`}),`Saving…`]}):`Apply`})]})]})})}function ae({record:e,statuses:t,onOpenRelation:n,onOpenRecord:o,onUpdated:u,onDialogOpenChange:d}){let f=R(),[m,h]=(0,V.useState)(!1),[g,_]=(0,V.useState)(``),v=m||!!g;(0,V.useEffect)(()=>{d?.(v)},[v,d]);let y=e.collection===`learnings`&&e.status!==`graduated`,b=[`learnings`,`decisions`,`conventions`].includes(e.collection),x=[[`status`,e.status,c(e.status)]];return e.category&&x.push([`category`,e.category]),e.confidence&&x.push([`confidence`,e.confidence]),e.severity&&x.push([`severity`,e.severity,s(e.severity)]),e.occurrences!=null&&x.push([`occurrences`,String(e.occurrences)]),e.expires&&x.push([`expires`,e.expires]),e.review_after&&x.push([`review after`,e.review_after]),e.started_at&&x.push([`started`,e.started_at]),e.resolved_at&&x.push([`resolved`,e.resolved_at]),e.graduated_to?.length&&x.push([`graduated to`,e.graduated_to.join(`, `)]),e.superseded_by?.length&&x.push([`superseded by`,e.superseded_by.join(`, `)]),e.owners?.length&&x.push([`owners`,e.owners.join(`, `)]),x.push([`updated`,e.updated||`—`]),(0,H.jsxs)(`aside`,{"aria-label":`Memory record`,className:`flex min-h-0 flex-col overflow-hidden border-l bg-background`,children:[(0,H.jsxs)(`div`,{className:`flex h-11 shrink-0 items-center gap-2 border-b px-3.5`,children:[(0,H.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:e.id}),(0,H.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground/60`,children:`·`}),(0,H.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground`,children:e.collection}),(0,H.jsx)(`span`,{className:`font-mono text-[11px] text-muted-foreground/60`,children:`·`}),(0,H.jsx)(`span`,{className:`font-mono text-[11px]`,style:{color:c(e.status)},children:e.status})]}),(0,H.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col gap-3.5 overflow-y-auto p-4`,children:[(0,H.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,H.jsx)(`h2`,{className:`m-0 text-[17px] font-semibold leading-[1.3] tracking-[-0.01em] [text-wrap:pretty]`,children:e.title}),e.path?(0,H.jsx)(`span`,{className:`break-all font-mono text-[10.5px] text-muted-foreground`,children:e.path}):null]}),(0,H.jsx)(`div`,{className:`grid grid-cols-2 gap-x-3 gap-y-2`,children:x.map(([e,t,n])=>(0,H.jsxs)(`span`,{className:`flex flex-col gap-0.5`,children:[(0,H.jsx)(`span`,{className:`text-[10px] font-medium uppercase tracking-wide text-muted-foreground`,children:e}),(0,H.jsx)(`span`,{className:`text-sm`,style:n?{color:n}:void 0,children:t})]},e))}),(0,H.jsx)(X,{issues:e.issues,kind:`validation`}),(0,H.jsx)(X,{issues:e.lifecycleIssues||[],kind:`lifecycle`}),(0,H.jsx)(T,{className:`[--typeset-size:0.875rem]`,source:e.body||`No details recorded.`,onOpen:o}),(0,H.jsx)(Z,{label:`Links to`,links:e.outgoing,onOpen:n}),(0,H.jsx)(Z,{label:`Backlinks`,links:e.incoming,onOpen:n}),(0,H.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,H.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,disabled:f,title:f?p:void 0,onClick:()=>h(!0),children:[(0,H.jsx)(a,{"aria-hidden":`true`}),`Edit`]}),y?(0,H.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,disabled:f,title:f?p:void 0,onClick:()=>_(`graduate`),children:[(0,H.jsx)(i,{"aria-hidden":`true`}),`Graduate`]}):null,b?(0,H.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,disabled:f,title:f?p:void 0,onClick:()=>_(`supersede`),children:[(0,H.jsx)(r,{"aria-hidden":`true`}),`Supersede`]}):null]})]}),m?(0,H.jsx)(re,{record:e,statuses:t,onClose:()=>h(!1),onUpdated:u}):null,g?(0,H.jsx)(ie,{record:e,mode:g,onClose:()=>_(``),onUpdated:u}):null]})}function $(e,t){return e.find(e=>e.id===t)?.statuses||[]}function oe({id:e,schema:t,onSelect:n,onOpenRecord:r,onDialogOpenChange:i,onChanged:a}){let[o,s]=(0,V.useState)(null),[c,l]=(0,V.useState)(``);return(0,V.useEffect)(()=>{let t=!0;return s(null),l(``),I.record(e).then(e=>{t&&s(e.record)}).catch(e=>{t&&l(e.message)}),()=>{t=!1}},[e]),c?(0,H.jsx)(`div`,{className:`px-4 py-3 text-xs text-muted-foreground`,children:c}):o?(0,H.jsx)(ae,{record:o,statuses:$(t.collections,o.collection),onOpenRelation:n,onOpenRecord:r,onUpdated:e=>{s(e),a?.()},onDialogOpenChange:i},o.id):(0,H.jsxs)(`div`,{className:`flex items-center gap-2 px-4 py-3 text-sm text-muted-foreground`,children:[(0,H.jsx)(S,{}),` Reading `,e,`…`]})}function se({selectedId:e,onSelect:t,onOpenRecord:n,schema:r,search:i,onSearchChange:a}){let s=R(),[d,h]=(0,V.useState)([]),[g,w]=(0,V.useState)(``),[T,E]=(0,V.useState)(``),[D,O]=(0,V.useState)(!0),[k,A]=(0,V.useState)(``),[N,F]=(0,V.useState)(null),z=(0,V.useRef)(0),U=(0,V.useCallback)(e=>{z.current=performance.now(),t(e)},[t]),[W,q]=(0,V.useState)(0);f(e=>{B(e,`/memory/`)&&q(e=>e+1)}),(0,V.useEffect)(()=>{let e=async()=>{O(!0);try{let e=await I.memory(i.trim(),{collection:g||void 0,status:T||void 0});h(e.records),A(``)}catch(e){A(e instanceof Error?e.message:String(e))}finally{O(!1)}},t=window.setTimeout(()=>void e(),i?180:0);return()=>window.clearTimeout(t)},[i,g,T,W]);let J=(0,V.useMemo)(()=>[...d].sort((e,t)=>String(t.updated||``).localeCompare(String(e.updated||``))||e.title.localeCompare(t.title)),[d]),Y=(0,V.useMemo)(()=>{let e=r.collections.filter(e=>!g||e.id===g).map(e=>({schema:e,records:J.filter(t=>t.collection===e.id)})),t=new Set(r.collections.map(e=>e.id)),n=J.filter(e=>!t.has(e.collection));return n.length&&e.push({schema:{id:`other`,singular:`record`,idPrefix:`?`,statuses:[]},records:n}),e},[r.collections,J,g]);J.find(t=>t.id===e);let X=g?$(r.collections,g):[...new Set(r.collections.flatMap(e=>e.statuses))];return(0,H.jsxs)(H.Fragment,{children:[(0,H.jsxs)(y,{gutter:`3.5`,className:`pt-3.5`,before:(0,H.jsx)(x,{scope:`records`,value:i,label:`Search workfile memory`,onChange:a}),after:(0,H.jsx)(`span`,{className:`flex shrink-0 items-center gap-1.5 whitespace-nowrap font-mono text-[11px] text-muted-foreground`,children:D?(0,H.jsxs)(H.Fragment,{children:[(0,H.jsx)(S,{"aria-hidden":`true`,className:`size-3`}),`loading…`]}):K(d.length,`record`)}),children:[(0,H.jsx)(b,{label:`collection`,value:g,options:r.collections.map(e=>({value:e.id})),onChange:e=>{w(e),E(``)}}),(0,H.jsx)(b,{label:`status`,value:T,options:X.map(e=>({value:e,color:c(e)})),onChange:E})]}),k?(0,H.jsx)(L,{variant:`destructive`,className:`mx-3.5 mt-3 w-auto px-3 py-2`,children:(0,H.jsxs)(j,{children:[`Memory could not be loaded: `,k]})}):null,(0,H.jsx)(`div`,{className:`flex min-h-0 flex-1 gap-3 overflow-hidden p-3.5`,children:(0,H.jsx)(`div`,{className:`flex min-h-0 flex-1 gap-3 overflow-x-auto`,children:Y.map(t=>(0,H.jsxs)(v,{className:`w-[272px] flex-none gap-0 overflow-hidden rounded-xl py-0 [--card-spacing:--spacing(2)]`,children:[(0,H.jsxs)(_,{className:`flex flex-row items-center gap-2 border-b px-3 py-2`,children:[(0,H.jsx)(`span`,{className:`font-mono text-[11px] font-medium text-primary`,children:t.schema.idPrefix}),(0,H.jsx)(`span`,{className:`flex-1 text-[12.5px] font-semibold`,children:ee(t.schema.singular)}),(0,H.jsx)(P,{variant:`secondary`,className:`h-5 px-1.5 font-mono text-[11px] font-normal`,children:t.records.length}),t.schema.id===`other`?null:(0,H.jsx)(l,{type:`button`,variant:`ghost`,size:`icon-xs`,"aria-label":`New ${t.schema.singular}`,disabled:s,title:s?p:void 0,onClick:()=>F(t.schema.id),children:(0,H.jsx)(o,{"aria-hidden":`true`})})]}),(0,H.jsxs)(C,{className:u(`flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto p-2.5`,G),children:[t.records.map(t=>(0,H.jsx)(te,{record:t,selected:t.id===e,onSelect:()=>U(t.id)},t.id)),!t.records.length&&!D?(0,H.jsx)(m,{className:`gap-1 border border-dashed p-4 md:p-6`,children:(0,H.jsx)(M,{className:`font-mono text-xs`,children:`no records`})}):null]})]},t.schema.id))})}),N===null?null:(0,H.jsx)(ne,{schema:r,initialCollection:N,onClose:()=>F(null),onCreated:e=>{F(null),h(t=>[e,...t]),t(e.id)}})]})}export{oe as MemoryPanel,se as MemoryView};
import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{$ as r,Mt as i,it as a,lt as o}from"./ui-primitives-DRENhlck.js";import{n as s,o as c,s as l,u}from"./theme-CcOVK72d.js";import{A as d,D as f,M as p,O as m,a as h,b as g,j as _,k as v,tt as y,w as b}from"./index-BcEUSS3r.js";import{t as x}from"./layout-QiuZ_k5v.js";import{t as S}from"./progress-CzHcd1lF.js";var C=e(t(),1),w=n(),T=[{key:`N`,label:`Move to next`,status:`next`},{key:`D`,label:`Defer`,status:`deferred`},{key:`X`,label:`Discard`,status:`discarded`}];function E({tasks:e,repoRoot:t,repoUrl:n,onPatch:E,onOpen:D}){let O=b(),[k,A]=(0,C.useState)(()=>new Set),[j,M]=(0,C.useState)(0),N=(0,C.useMemo)(()=>e.filter(e=>!k.has(e.id)),[k,e]),P=N[j]||N[0],F=e.length;(0,C.useEffect)(()=>{j>=N.length&&M(Math.max(0,N.length-1))},[j,N.length]);let I=(0,C.useCallback)(async(e,t=!0)=>{if(P){try{await E(P.id,e)}catch{return}t&&(A(e=>new Set(e).add(P.id)),M(e=>Math.min(e,Math.max(0,N.length-2))))}},[E,N.length,P]);(0,C.useEffect)(()=>{if(O)return;let e=e=>{if(e.ctrlKey||e.metaKey||e.altKey)return;let t=e.target;if([`INPUT`,`SELECT`,`TEXTAREA`].includes(t.tagName)||t.isContentEditable)return;let n=e.key.toUpperCase();if(n===`J`||e.key===`ArrowDown`)e.preventDefault(),M(e=>Math.min(N.length-1,e+1));else if(n===`K`||e.key===`ArrowUp`)e.preventDefault(),M(e=>Math.max(0,e-1));else if(/^[1-4]$/.test(n))e.preventDefault(),I({priority:h[Number(n)-1]},!1);else{let t=T.find(e=>e.key===n);t&&(e.preventDefault(),I({status:t.status}))}};return document.addEventListener(`keydown`,e),()=>document.removeEventListener(`keydown`,e)},[I,N.length,O]);let L=e=>n?`${n.replace(/\/+$/,``)}/blob/main/${e}`:`vscode://file${t}/${e}`;return O?(0,w.jsx)(f,{className:`gap-3 p-10`,children:(0,w.jsxs)(d,{children:[(0,w.jsx)(_,{children:(0,w.jsx)(o,{"aria-hidden":`true`,size:20})}),(0,w.jsx)(p,{className:`text-sm`,children:`Triage needs a writable workspace`}),(0,w.jsx)(v,{className:`text-[12.5px]`,children:`This board is served read-only, and every action here assigns a status or a priority.`})]})}):P?(0,w.jsxs)(`div`,{className:`flex min-h-0 flex-1 flex-col overflow-y-auto`,children:[(0,w.jsxs)(`div`,{className:`flex flex-wrap items-center gap-x-2.5 gap-y-2 border-b bg-card px-3.5 py-2.5`,children:[(0,w.jsxs)(`div`,{className:`flex min-w-0 flex-1 basis-64 items-center gap-2.5`,children:[(0,w.jsx)(S,{value:F?k.size/F*100:0,className:`min-w-16 max-w-[340px] flex-1`}),(0,w.jsxs)(`span`,{className:`shrink-0 font-mono text-[11px] text-muted-foreground`,children:[k.size,` of `,F,` processed`]})]}),(0,w.jsxs)(`div`,{className:`ml-auto flex shrink-0 items-center gap-2.5`,children:[(0,w.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,disabled:j===0,onClick:()=>M(e=>Math.max(0,e-1)),children:[(0,w.jsx)(y,{className:`max-sm:hidden`,children:`K`}),`Previous`]}),(0,w.jsxs)(`span`,{className:`font-mono text-[11px] text-muted-foreground tabular-nums`,children:[j+1,` / `,N.length]}),(0,w.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,disabled:j>=N.length-1,onClick:()=>M(e=>Math.min(N.length-1,e+1)),children:[(0,w.jsx)(y,{className:`max-sm:hidden`,children:`J`}),`Next`]}),(0,w.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,title:`Open full card`,"aria-label":`Open full card`,className:`max-sm:size-7 max-sm:px-0`,onClick:()=>D(P.id),children:[(0,w.jsx)(a,{"aria-hidden":`true`}),(0,w.jsx)(`span`,{className:`max-sm:hidden`,children:`Open full card`})]})]})]}),(0,w.jsxs)(`div`,{className:u(x,`px-6 py-7 sm:px-8`),children:[(0,w.jsxs)(`div`,{className:`flex items-center gap-2 font-mono text-[11px] text-muted-foreground`,children:[(0,w.jsx)(`span`,{children:P.id}),(0,w.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,w.jsx)(`span`,{style:{color:c(P.status)},children:P.status}),(0,w.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,w.jsx)(`span`,{children:P.area}),(0,w.jsx)(`span`,{className:`text-muted-foreground/60`,children:`·`}),(0,w.jsx)(`span`,{children:P.type})]}),(0,w.jsx)(`h2`,{className:`mt-3 mb-1 text-[26px] leading-[1.2] font-semibold tracking-tight [text-wrap:pretty]`,children:P.title}),P.file?(0,w.jsx)(`a`,{className:`font-mono text-[11px] text-muted-foreground/70 underline underline-offset-[3px]`,href:L(P.file),target:n?`_blank`:void 0,rel:n?`noreferrer`:void 0,children:P.file}):null,P.source?(0,w.jsxs)(`span`,{className:`mt-[3px] block font-mono text-[11px] text-muted-foreground/70`,children:[`source`,` `,(0,w.jsx)(`a`,{className:`font-mono underline underline-offset-[3px]`,href:L(P.source),target:n?`_blank`:void 0,rel:n?`noreferrer`:void 0,children:P.source})]}):null,(0,w.jsx)(`div`,{className:`mt-[22px]`,children:(0,w.jsx)(g,{source:P.body,onOpen:D})}),(0,w.jsxs)(`div`,{className:`mt-[30px] flex flex-wrap gap-2 border-t pt-[18px]`,children:[h.map((e,t)=>(0,w.jsxs)(l,{type:`button`,variant:`outline`,size:`lg`,"aria-pressed":P.priority===e,style:P.priority===e?{borderColor:s(e)}:void 0,onClick:()=>void I({priority:e},!1),children:[(0,w.jsx)(y,{children:t+1}),(0,w.jsx)(`span`,{style:{color:s(e)},children:e})]},e)),T.map(e=>(0,w.jsxs)(l,{type:`button`,variant:`outline`,size:`lg`,onClick:()=>void I({status:e.status}),children:[(0,w.jsx)(y,{children:e.key}),(0,w.jsx)(`span`,{style:{color:c(e.status)},children:e.label})]},e.key))]}),(0,w.jsx)(`span`,{className:`mt-3.5 block text-xs text-muted-foreground`,children:`Every action writes the card's frontmatter to disk immediately. Shortcuts work while focus is outside a form.`})]})]}):(0,w.jsxs)(f,{className:`gap-3 p-10`,children:[(0,w.jsxs)(d,{children:[(0,w.jsx)(_,{children:(0,w.jsx)(i,{"aria-hidden":`true`,size:20,style:{color:c(`done`)}})}),(0,w.jsx)(p,{className:`text-sm`,children:`Queue clear`}),(0,w.jsxs)(v,{className:`text-[12.5px]`,children:[`You processed `,k.size.toLocaleString(),` cards.`]})]}),(0,w.jsx)(m,{children:(0,w.jsxs)(l,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>{A(new Set),M(0)},children:[(0,w.jsx)(r,{"aria-hidden":`true`}),`Start again`]})})]})}export{E as TriageView};
import{n as e}from"./rolldown-runtime-CbXtAM7H.js";import{i as t,t as n}from"./react-Buq45Vzz.js";import{bt as r,ut as i}from"./ui-primitives-DRENhlck.js";import{r as a,s as o,u as s}from"./theme-CcOVK72d.js";import{I as c,nt as l,q as u}from"./index-BcEUSS3r.js";var d=e(t(),1),f=[{id:`parent`,label:`parent`,declared:!0},{id:`depends`,label:`depends`,declared:!0},{id:`origin`,label:`origin`,declared:!0},{id:`supersedes`,label:`supersedes`,declared:!0},{id:`superseded_by`,label:`superseded by`,declared:!0},{id:`graduated_to`,label:`graduated to`,declared:!0},{id:`corrective_actions`,label:`corrective`,declared:!0},{id:`cards`,label:`cards`,declared:!0},{id:`decisions`,label:`decisions`,declared:!0},{id:`fragments`,label:`fragments`,declared:!0},{id:`related`,label:`related`,declared:!0},{id:`source`,label:`source`,declared:!0},{id:`wikilink`,label:`wiki link`,declared:!1},{id:`markdown`,label:`md link`,declared:!1},{id:`mention`,label:`mention`,declared:!1}],p=new Set(f.filter(e=>e.declared).map(e=>e.id)),m=[{id:`card`,label:`Cards`},{id:`memory`,label:`Memory`},{id:`doc`,label:`Docs`},{id:`change`,label:`Changes`},{id:`release`,label:`Releases`}],h=f.map(e=>e.id).filter(e=>e!==`mention`),g=[`card`,`memory`,`doc`];function _(e,t){return e.kind!==`card`||!(t.status&&e.status!==t.status||t.area&&e.area!==t.area||t.type&&e.recordType!==t.type||t.priority&&e.priority!==t.priority||t.milestone&&e.milestone!==t.milestone)}function ee(e,t){let n=Object.values(t.record??{}).some(Boolean),r=e.filter(e=>t.kinds.has(e.kind)&&_(e,t.record??{}));if(n){let e=new Set(r.filter(e=>e.kind===`card`).map(e=>e.id)),n=new Set;for(let i of v(r,t.relations).links)e.has(i.from)&&n.add(i.to),e.has(i.to)&&n.add(i.from);r=r.filter(e=>e.kind===`card`||n.has(e.id))}let{links:i,degree:a}=v(r,t.relations),o=t.hideIsolated?r.filter(e=>a.get(e.id)):r;return{records:o,links:i,degree:a,isolated:r.length-o.length}}function v(e,t){let n=new Set(e.map(e=>e.id)),r=[],i=new Map;for(let a of e)for(let e of a.edges){if(!n.has(e.to)||e.to===a.id)continue;let o=e.rel.filter(e=>t.has(e));o.length&&(r.push({from:a.id,to:e.to,relations:o,declared:o.some(e=>p.has(e))}),i.set(a.id,(i.get(a.id)||0)+1),i.set(e.to,(i.get(e.to)||0)+1))}return{links:r,degree:i}}function y(e,t){let n=e*2.399963,r=18*Math.sqrt(e)+(t>200?40:0);return{x:Math.cos(n)*r,y:Math.sin(n)*r}}var b=9e3,x=.012,S=130,C=6e-4,w=.82;function te(e,t,n){for(let t=0;t<e.length;t+=1){let r=e[t];for(let i=t+1;i<e.length;i+=1){let a=e[i],o=r.x-a.x,s=r.y-a.y,c=o*o+s*s;c<1&&(o=(t-i)*.5,s=.5,c=o*o+s*s);let l=Math.sqrt(c),u=b*n/c,d=o/l*u,f=s/l*u;r.vx+=d,r.vy+=f,a.vx-=d,a.vy-=f}}let r=new Map(e.map(e=>[e.id,e]));for(let e of t){let t=r.get(e.from),i=r.get(e.to);if(!t||!i)continue;let a=i.x-t.x,o=i.y-t.y,s=Math.sqrt(a*a+o*o)||1,c=(s-S)*x*n,l=a/s*c,u=o/s*c;t.vx+=l,t.vy+=u,i.vx-=l,i.vy-=u}for(let t of e)t.vx-=t.x*C*n,t.vy-=t.y*C*n,t.vx*=w,t.vy*=w,t.x+=t.vx,t.y+=t.vy}function T(e,t,n){let r=new Map(e.map(e=>[e.id,e]));return t.map((e,i)=>{let a=r.get(e.id)??y(i,t.length);return{id:e.id,x:a.x,y:a.y,vx:0,vy:0,record:e,degree:n.get(e.id)||0}})}function E(e,t,n,r){let i=n-e,a=r-t,o=Math.sqrt(i*i+a*a)||1,s=Math.min(o*.18,60);return`M ${e} ${t} Q ${(e+n)/2-a/o*s} ${(t+r)/2+i/o*s} ${n} ${r}`}var D=.08;function O(e,t,n,r){let i=Math.min(4,Math.max(D,e.k*r));return{k:i,x:t-(t-e.x)/e.k*i,y:n-(n-e.y)/e.k*i}}function k(e,t,n){return{x:t-e.x,y:n-e.y}}function A(e){let t=1/0,n=1/0,r=-1/0,i=-1/0;for(let a of e)t=Math.min(t,a.x),n=Math.min(n,a.y),r=Math.max(r,a.x),i=Math.max(i,a.y);return{minX:t,minY:n,maxX:r,maxY:i}}var j=n(),M=`workfile-workflow-filters`;function N(){let e={relations:[...h],kinds:[...g],hideIsolated:!0};try{let t=localStorage.getItem(M);if(!t)return e;let n=JSON.parse(t);return{relations:Array.isArray(n.relations)?n.relations:e.relations,kinds:Array.isArray(n.kinds)?n.kinds:e.kinds,hideIsolated:typeof n.hideIsolated==`boolean`?n.hideIsolated:e.hideIsolated}}catch{return e}}function P({on:e,onClick:t,children:n,dashed:r}){return(0,j.jsx)(`button`,{type:`button`,"aria-pressed":e,onClick:t,className:s(`shrink-0 rounded-full border px-2 py-0.5 text-[11px] whitespace-nowrap transition-colors`,e?`border-ring bg-accent text-foreground`:`border-border text-muted-foreground hover:bg-accent/50`,r&&`border-dashed`),children:n})}function F({selectedId:e,onSelect:t,filters:n}){let[p,h]=(0,d.useState)(null),[g,_]=(0,d.useState)(null),v=(0,d.useRef)(N()),[y,b]=(0,d.useState)(()=>new Set(v.current.relations)),[x,S]=(0,d.useState)(()=>new Set(v.current.kinds)),[C,w]=(0,d.useState)(v.current.hideIsolated),[D,F]=(0,d.useState)(null),I=(0,d.useRef)(0),[L,R]=(0,d.useState)({x:0,y:0,k:1}),[,z]=(0,d.useState)(0),B=(0,d.useRef)({nodes:[],links:[],alpha:0}),V=(0,d.useRef)(null),H=(0,d.useRef)(!1);(0,d.useEffect)(()=>{let e=!0;return u.graph().then(t=>{e&&h(t.records)}).catch(t=>{e&&_(t.message)}),()=>{e=!1}},[]),(0,d.useEffect)(()=>{localStorage.setItem(M,JSON.stringify({relations:[...y],kinds:[...x],hideIsolated:C}))},[y,x,C]);let U=(0,d.useMemo)(()=>ee(p??[],{relations:y,kinds:x,hideIsolated:C,record:n}),[p,x,y,C,n]),W=(0,d.useMemo)(()=>Object.entries(n).filter(([,e])=>e).map(([e,t])=>`${e} ${t}`),[n]);(0,d.useEffect)(()=>{B.current.nodes=T(B.current.nodes,U.records,U.degree),B.current.links=U.links,B.current.alpha=1,z(e=>e+1)},[U]);let G=(0,d.useCallback)(()=>{let e=B.current.nodes,t=V.current;if(!e.length||!t)return;let n=t.getBoundingClientRect(),{minX:r,minY:i,maxX:a,maxY:o}=A(e),s=Math.min(3,Math.max(.15,Math.min(n.width/(a-r+160),n.height/(o-i+160))));R({k:s,x:n.width/2-(r+a)/2*s,y:n.height/2-(i+o)/2*s})},[]);(0,d.useEffect)(()=>{let e=0,t=()=>{let n=B.current;n.alpha>.02&&n.nodes.length&&(te(n.nodes,n.links,n.alpha),n.alpha*=.97,H.current||G(),z(e=>e+1)),e=requestAnimationFrame(t)};return e=requestAnimationFrame(t),()=>cancelAnimationFrame(e)},[G]);let ne=e=>{e.preventDefault();let t=V.current?.getBoundingClientRect();if(!t)return;let n=e.clientX-t.left,r=e.clientY-t.top;H.current=!0;let i=e.deltaY<0?1.12:.89;R(e=>O(e,n,r,i))},K=(0,d.useRef)(null),re=e=>{H.current=!0,K.current={x:e.clientX-L.x,y:e.clientY-L.y},e.target.setPointerCapture?.(e.pointerId)},ie=e=>{let t=K.current;if(!t)return;let n=k(t,e.clientX,e.clientY);R(e=>({...e,...n}))},q=()=>{K.current=null},J=(e,t,n)=>{let r=new Set(e);r.has(n)?r.delete(n):r.add(n),t(r)},Y=B.current.nodes,X=(0,d.useMemo)(()=>new Map(Y.map(e=>[e.id,e])),[Y,L]),Z=D??e,Q=Z?X.get(Z):void 0,$=(0,d.useMemo)(()=>{if(!Z)return null;let e=new Set([Z]);for(let t of U.links)t.from===Z&&e.add(t.to),t.to===Z&&e.add(t.from);return e},[Z,U.links]);return g?(0,j.jsxs)(`div`,{className:`p-6 text-sm text-muted-foreground`,children:[`The graph could not be read: `,g]}):(0,j.jsxs)(`div`,{className:`flex h-full min-h-0 flex-col`,children:[(0,j.jsxs)(c,{gutter:`3`,className:`shrink-0 border-b py-2`,after:(0,j.jsxs)(j.Fragment,{children:[(0,j.jsxs)(`span`,{className:`hidden text-[11px] whitespace-nowrap text-muted-foreground sm:inline`,children:[U.records.length,` nodes · `,U.links.length,` `,`edges`]}),(0,j.jsxs)(o,{type:`button`,variant:`outline`,size:`sm`,className:`shrink-0 px-2`,onClick:()=>{H.current=!1,G()},children:[(0,j.jsx)(r,{"aria-hidden":`true`,className:`size-3`}),`Fit`]})]}),children:[(0,j.jsx)(`div`,{className:`flex shrink-0 items-center gap-1`,children:m.map(e=>(0,j.jsx)(P,{on:x.has(e.id),onClick:()=>J(x,S,e.id),children:e.label},e.id))}),(0,j.jsx)(`span`,{className:`h-4 w-px shrink-0 bg-border`,"aria-hidden":`true`}),(0,j.jsx)(`div`,{className:`flex shrink-0 items-center gap-1`,children:f.map(e=>(0,j.jsx)(P,{on:y.has(e.id),dashed:!e.declared,onClick:()=>J(y,b,e.id),children:e.label},e.id))}),(0,j.jsx)(`span`,{className:`h-4 w-px shrink-0 bg-border`,"aria-hidden":`true`}),(0,j.jsx)(P,{on:C,onClick:()=>w(!C),children:`hide isolated`})]}),(0,j.jsxs)(`div`,{className:`relative min-h-0 flex-1 overflow-hidden`,children:[p?null:(0,j.jsxs)(`div`,{className:`flex h-full items-center justify-center gap-2 text-sm text-muted-foreground`,children:[(0,j.jsx)(i,{"aria-hidden":`true`,className:`size-4 animate-spin`}),`Reading the graph…`]}),p?.length&&!U.records.length?(0,j.jsx)(`div`,{className:`absolute inset-0 flex flex-col items-center justify-center gap-1.5 px-6 text-center text-sm text-muted-foreground`,children:U.isolated?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsxs)(`span`,{children:[U.isolated,` `,U.isolated===1?`record matches`:`records match`,`, and`,` `,U.isolated===1?`it is`:`none is`,` `,`connected to anything else here.`]}),(0,j.jsx)(o,{type:`button`,variant:`outline`,size:`sm`,className:`px-2`,onClick:()=>w(!1),children:`Show unconnected records`})]}):(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(`span`,{children:`No records match these filters.`}),(0,j.jsx)(`span`,{className:`text-xs`,children:W.length?`${W.join(`, `)} above, and ${x.size} of ${m.length} kinds here.`:`${x.size} of ${m.length} kinds and ${y.size} of ${f.length} relationships.`})]})}):null,(0,j.jsxs)(`svg`,{ref:V,role:`presentation`,className:`size-full cursor-grab touch-none active:cursor-grabbing`,onWheel:ne,onPointerDown:re,onPointerMove:ie,onPointerUp:q,onPointerLeave:q,children:[(0,j.jsx)(`defs`,{children:(0,j.jsx)(`marker`,{id:`workflow-arrow`,viewBox:`0 0 8 8`,refX:`7`,refY:`4`,markerWidth:`5`,markerHeight:`5`,orient:`auto-start-reverse`,children:(0,j.jsx)(`path`,{d:`M 0 1 L 7 4 L 0 7 z`,className:`fill-muted-foreground`})})}),(0,j.jsxs)(`g`,{transform:`translate(${L.x} ${L.y}) scale(${L.k})`,children:[U.links.map(e=>{let t=X.get(e.from),n=X.get(e.to);if(!t||!n)return null;let r=$&&!($.has(e.from)&&$.has(e.to));return(0,j.jsx)(`path`,{d:E(t.x,t.y,n.x,n.y),fill:`none`,markerEnd:`url(#workflow-arrow)`,className:s(`stroke-muted-foreground transition-opacity`,r?`opacity-10`:`opacity-45`),strokeWidth:1.2/L.k,strokeDasharray:e.declared?void 0:`${4/L.k} ${3/L.k}`,children:(0,j.jsx)(`title`,{children:`${e.from} → ${e.to}: ${e.relations.join(`, `)}`})},`${e.from}->${e.to}`)}),Y.map(n=>{let r=$&&!$.has(n.id),i=n.id===e,o=Math.min(16,6+Math.sqrt(n.degree)*2);return(0,j.jsxs)(`g`,{transform:`translate(${n.x} ${n.y})`,className:s(`cursor-pointer transition-opacity`,r&&`opacity-20`),onPointerEnter:()=>F(n.id),onPointerLeave:()=>F(null),onClick:e=>{e.stopPropagation(),I.current=performance.now(),t(n.id)},children:[(0,j.jsx)(`circle`,{r:o,style:{fill:a(n.record.status||`backlog`)},className:s(i?`stroke-foreground`:`stroke-background`),strokeWidth:(i?3:1.5)/L.k}),(0,j.jsx)(`title`,{children:`${n.id} — ${n.record.title}`}),L.k>.55||i||r===!1?(0,j.jsx)(`text`,{y:o+11/L.k,textAnchor:`middle`,className:`pointer-events-none fill-foreground`,style:{fontSize:`${11/L.k}px`},children:n.id}):null]},n.id)})]})]}),Q?(0,j.jsxs)(`div`,{className:`pointer-events-none absolute bottom-3 left-3 max-w-[min(30rem,70%)] rounded-md border bg-background/95 px-3 py-2 shadow-sm`,children:[(0,j.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,j.jsx)(`span`,{className:`font-mono text-[11px] font-medium`,children:Z}),(0,j.jsx)(l,{variant:`secondary`,className:`px-1.5 py-0 text-[10px] font-normal`,children:Q.record.recordType})]}),(0,j.jsx)(`p`,{className:`truncate text-xs text-muted-foreground`,children:Q.record.title})]}):null]})]})}export{F as WorkflowView};

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display