cascivo 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ import{c as e}from"./config-DT-Cs7bC.mjs";function t(e,t){let n=e.find(e=>e.startsWith(`--${t}=`));if(n)return n.slice(t.length+3);let r=e.indexOf(`--${t}`),i=r===-1?void 0:e[r+1];return i&&!i.startsWith(`--`)?i:void 0}function n(e,t=[]){let n=[];for(let r=0;r<e.length;r++){let i=e[r];if(i.startsWith(`-`)){let n=i.replace(/^--?/,``).split(`=`)[0],a=e[r+1];t.includes(n)&&!i.includes(`=`)&&a!==void 0&&!a.startsWith(`-`)&&r++;continue}n.push(i)}return n}function r(n){let r=t(n,`package-manager`)??t(n,`pm`);return r===void 0?{pm:void 0}:e(r)?{pm:r}:{error:`Unknown package manager "${r}". Use one of: pnpm, yarn, npm, bun.`}}export{n,r,t};
2
+ //# sourceMappingURL=args-CtmltI1S.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args-CtmltI1S.mjs","names":[],"sources":["../src/utils/args.ts"],"sourcesContent":["import { isPackageManager, type PackageManager } from './config.js'\n\n/**\n * Read `--key value` or `--key=value` from an argv slice. Ignores a following\n * token that is itself a flag (so `--tokens --dry-run` yields undefined, not\n * `--dry-run`).\n */\nexport function flagValue(args: string[], key: string): string | undefined {\n const eq = args.find((a) => a.startsWith(`--${key}=`))\n if (eq) return eq.slice(key.length + 3)\n const idx = args.indexOf(`--${key}`)\n const next = idx !== -1 ? args[idx + 1] : undefined\n return next && !next.startsWith('--') ? next : undefined\n}\n\n/**\n * Extract positional (non-flag) arguments from an argv slice. Any token starting\n * with `-` is dropped, as is the value token immediately following a flag named\n * in `valueFlags` (e.g. `pnpm` in `--pm pnpm`), so component names never pick up\n * a flag or its value.\n */\nexport function positionalArgs(args: string[], valueFlags: string[] = []): string[] {\n const out: string[] = []\n for (let i = 0; i < args.length; i++) {\n const token = args[i]!\n if (token.startsWith('-')) {\n const key = token.replace(/^--?/, '').split('=')[0]!\n const next = args[i + 1]\n if (\n valueFlags.includes(key) &&\n !token.includes('=') &&\n next !== undefined &&\n !next.startsWith('-')\n ) {\n i++ // skip the flag's value token\n }\n continue\n }\n out.push(token)\n }\n return out\n}\n\n/**\n * Resolve the `--package-manager` / `--pm` flag to a validated PackageManager.\n * Absent flag → `{ pm: undefined }` (fall back to auto-detection). Unknown value\n * → `{ error }` so the caller can print it and exit non-zero.\n */\nexport function resolvePackageManagerFlag(\n args: string[],\n): { pm: PackageManager | undefined } | { error: string } {\n const raw = flagValue(args, 'package-manager') ?? flagValue(args, 'pm')\n if (raw === undefined) return { pm: undefined }\n if (isPackageManager(raw)) return { pm: raw }\n return { error: `Unknown package manager \"${raw}\". Use one of: pnpm, yarn, npm, bun.` }\n}\n"],"mappings":"0CAOA,SAAgB,EAAU,EAAgB,EAAiC,CACzE,IAAM,EAAK,EAAK,KAAM,GAAM,EAAE,WAAW,KAAK,EAAI,EAAE,CAAC,EACrD,GAAI,EAAI,OAAO,EAAG,MAAM,EAAI,OAAS,CAAC,EACtC,IAAM,EAAM,EAAK,QAAQ,KAAK,GAAK,EAC7B,EAAO,IAAQ,GAAqB,IAAA,GAAhB,EAAK,EAAM,GACrC,OAAO,GAAQ,CAAC,EAAK,WAAW,IAAI,EAAI,EAAO,IAAA,EACjD,CAQA,SAAgB,EAAe,EAAgB,EAAuB,CAAC,EAAa,CAClF,IAAM,EAAgB,CAAC,EACvB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,CACpC,IAAM,EAAQ,EAAK,GACnB,GAAI,EAAM,WAAW,GAAG,EAAG,CACzB,IAAM,EAAM,EAAM,QAAQ,OAAQ,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,GAC3C,EAAO,EAAK,EAAI,GAEpB,EAAW,SAAS,CAAG,GACvB,CAAC,EAAM,SAAS,GAAG,GACnB,IAAS,IAAA,IACT,CAAC,EAAK,WAAW,GAAG,GAEpB,IAEF,QACF,CACA,EAAI,KAAK,CAAK,CAChB,CACA,OAAO,CACT,CAOA,SAAgB,EACd,EACwD,CACxD,IAAM,EAAM,EAAU,EAAM,iBAAiB,GAAK,EAAU,EAAM,IAAI,EAGtE,OAFI,IAAQ,IAAA,GAAkB,CAAE,GAAI,IAAA,EAAU,EAC1C,EAAiB,CAAG,EAAU,CAAE,GAAI,CAAI,EACrC,CAAE,MAAO,4BAA4B,EAAI,qCAAsC,CACxF"}
@@ -0,0 +1,4 @@
1
+ import{n as e,t}from"./args-CtmltI1S.mjs";import{stdin as n}from"node:process";import{existsSync as r,mkdirSync as i,readFileSync as a,statSync as o,writeFileSync as s}from"node:fs";import{dirname as c,join as l}from"node:path";import{tmpdir as u}from"node:os";const d=`https://www.caniemail.com/api/data.json`,f=l(u(),`cascivo-caniemail.json`);async function p(){try{return await import(`@cascivo/email`)}catch{throw Error(`cascivo email lint needs @cascivo/email, which is not installed here.
2
+ pnpm add @cascivo/email`)}}async function m(){let e=[];for await(let t of n)e.push(t);return Buffer.concat(e).toString(`utf8`)}function h(e,t){let n=e=>{throw Error(`The Can I email matrix from ${t} is not usable: ${e}`)};(typeof e!=`object`||!e)&&n(`not a JSON object`);let r=e;Array.isArray(r.data)||n("no `data` array");let i=r.data;i.length===0&&n("`data` is empty");let a=i.map((e,t)=>{if(typeof e!=`object`||!e)return n(`data[${t}] is not an object`);let r=e;return typeof r.slug==`string`?typeof r.stats!=`object`||r.stats===null?n(`data[${t}] (${r.slug}) has no \`stats\``):{slug:r.slug,title:typeof r.title==`string`?r.title:r.slug,category:typeof r.category==`string`?r.category:`unknown`,...typeof r.keywords==`string`?{keywords:r.keywords}:{},stats:r.stats}:n(`data[${t}] has no \`slug\``)});return{api_version:typeof r.api_version==`string`?r.api_version:`unknown`,last_update_date:typeof r.last_update_date==`string`?r.last_update_date:`unknown`,data:a}}async function g(e){let n=t(e,`data`);if(n!==void 0){if(!r(n))throw Error(`No such file: ${n}`);return h(JSON.parse(a(n,`utf8`)),n)}if(!e.includes(`--refresh`)&&r(f)&&Date.now()-o(f).mtimeMs<864e5)return h(JSON.parse(a(f,`utf8`)),f);let l=await fetch(d).catch(e=>{throw Error(`Could not fetch the Can I email matrix from ${d}: ${e instanceof Error?e.message:String(e)}\n Offline? Download it once and pass --data <file>.`)});if(!l.ok)throw Error(`Could not fetch ${d}: ${l.status} ${l.statusText}`);let u=await l.text();return i(c(f),{recursive:!0}),s(f,u),h(JSON.parse(u),d)}function _(e,t){let n=[t];for(let t of e)n.push(` ${t.level===`blocked`?`✗`:`!`} ${t.slug} ${t.source}\n ${t.clients.join(`, `)}`);return n.join(`
3
+ `)}async function v(t){let n=[...e(t,[`data`]),...t.includes(`-`)?[`-`]:[]];if(n.length===0)throw Error("cascivo email lint needs a file, or `-` to read rendered HTML from stdin.");let{lint:r,indexFeatures:i,CASCIVO_ALLOW:o}=await p(),s=i(await g(t)),c=t.includes(`--no-allowlist`)?{}:o,l=0,u=0;for(let e of n){let t=r(e===`-`?await m():a(e,`utf8`),s,{allow:c}),n=t.filter(e=>e.level===`blocked`),i=t.filter(e=>e.level===`caveat`);l+=n.length,u+=i.length;let o=e===`-`?`(stdin)`:e;if(n.length===0&&i.length===0){console.log(`✓ ${o}`);continue}console.log(_([...n,...i],`${n.length===0?`✓`:`✗`} ${o}`))}let d=`\n${n.length} file${n.length===1?``:`s`}, ${l} blocked, ${u} caveat${u===1?``:`s`}.`;console.log(d),l>0&&(process.exitCode=1)}async function y(e){let[t,...n]=e;if(t===`lint`){await v(n);return}console.error(t===void 0?"cascivo email needs a subcommand. Try `cascivo email lint --help`.":`Unknown email subcommand: ${t}`),process.exitCode=1}export{y as email};
4
+ //# sourceMappingURL=email-CwQcZ-mB.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-CwQcZ-mB.mjs","names":[],"sources":["../src/commands/email.ts"],"sourcesContent":["/**\n * `cascivo email lint` — the conformance check, without the boilerplate.\n *\n * `lint()` is the most compelling thing in `@cascivo/email` and it cannot run out of the\n * box: it needs the Can I email support matrix, which the package deliberately does not\n * bundle (~483 KB of test data in every adopter's `node_modules`). The README says where to\n * fetch it, and every adopter then writes the same dozen lines — fetch, cache, index, lint,\n * decide an exit code. This is those lines, once.\n *\n * It lints **rendered HTML**, not templates. Rendering a `.tsx` would mean a compiler this\n * CLI has no business carrying, and every project already renders in its own build; piping\n * the output here costs nothing and keeps the command honest about what it checks — the\n * finished document, which is the only thing a client ever sees.\n */\nimport { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { tmpdir } from 'node:os'\nimport { stdin } from 'node:process'\nimport type { CanIEmailData, Feature, Finding } from '@cascivo/email'\nimport { flagValue, positionalArgs } from '../utils/args.js'\n\n/** Upstream, and the URL the README and the preview both name. MIT, hteumeuleu/caniemail. */\nconst SOURCE = 'https://www.caniemail.com/api/data.json'\n\n/**\n * Where a fetched copy is kept.\n *\n * In the temp directory rather than the project: it is a cache of somebody else's data, it\n * is large, and a stale copy is a correctness problem rather than a disk one — so it lives\n * somewhere that gets cleaned up, and `--refresh` exists for when it should not be trusted.\n */\nconst CACHE = join(tmpdir(), 'cascivo-caniemail.json')\n\n/** A day. Long enough that a CI run does not re-download; short enough to notice a retest. */\nconst MAX_AGE_MS = 24 * 60 * 60 * 1000\n\n/**\n * Load `@cascivo/email` from the caller's project.\n *\n * An optional peer, resolved at call time. Making it a real dependency would put the whole\n * email package into the download for `cascivo add button`; anyone who wants this command\n * has it installed already, and the failure below says so if they do not.\n *\n * `@cascivo/email` is a devDependency here purely so these types resolve — nothing in the\n * published bundle imports it except this line.\n */\nasync function loadEmail(): Promise<typeof import('@cascivo/email')> {\n try {\n return await import('@cascivo/email')\n } catch {\n throw new Error(\n 'cascivo email lint needs @cascivo/email, which is not installed here.\\n' +\n ' pnpm add @cascivo/email',\n )\n }\n}\n\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = []\n for await (const chunk of stdin) chunks.push(chunk as Buffer)\n return Buffer.concat(chunks).toString('utf8')\n}\n\n/**\n * Turn the downloaded payload into a `CanIEmailData`, or say why it is not one.\n *\n * `res.json()` hands back `any`, so `as CanIEmailData` would type-check and prove nothing —\n * and this payload is the definition of one you did not produce: third-party, fetched over\n * the network, and cached to disk where anything could edit it. A bad file would otherwise\n * surface as an unreadable `TypeError` from inside `indexFeatures`.\n *\n * Structural failures throw; a feature missing the optional fields is coerced rather than\n * rejected, so an upstream addition cannot break the command.\n */\nexport function parseMatrix(raw: unknown, source: string): CanIEmailData {\n const bad = (why: string): never => {\n throw new Error(`The Can I email matrix from ${source} is not usable: ${why}`)\n }\n if (typeof raw !== 'object' || raw === null) bad('not a JSON object')\n const root = raw as Record<string, unknown>\n if (!Array.isArray(root['data'])) bad('no `data` array')\n const entries = root['data'] as unknown[]\n if (entries.length === 0) bad('`data` is empty')\n\n const data: Feature[] = entries.map((entry, i) => {\n if (typeof entry !== 'object' || entry === null) return bad(`data[${i}] is not an object`)\n const feature = entry as Record<string, unknown>\n if (typeof feature['slug'] !== 'string') return bad(`data[${i}] has no \\`slug\\``)\n if (typeof feature['stats'] !== 'object' || feature['stats'] === null) {\n return bad(`data[${i}] (${feature['slug']}) has no \\`stats\\``)\n }\n return {\n slug: feature['slug'],\n title: typeof feature['title'] === 'string' ? feature['title'] : feature['slug'],\n category: typeof feature['category'] === 'string' ? feature['category'] : 'unknown',\n ...(typeof feature['keywords'] === 'string' ? { keywords: feature['keywords'] } : {}),\n stats: feature['stats'] as Feature['stats'],\n }\n })\n\n return {\n api_version: typeof root['api_version'] === 'string' ? root['api_version'] : 'unknown',\n last_update_date:\n typeof root['last_update_date'] === 'string' ? root['last_update_date'] : 'unknown',\n data,\n }\n}\n\n/** The matrix, from `--data`, from a fresh-enough cache, or from upstream. */\nasync function loadMatrix(args: string[]): Promise<CanIEmailData> {\n const supplied = flagValue(args, 'data')\n if (supplied !== undefined) {\n if (!existsSync(supplied)) throw new Error(`No such file: ${supplied}`)\n return parseMatrix(JSON.parse(readFileSync(supplied, 'utf8')), supplied)\n }\n\n const refresh = args.includes('--refresh')\n if (!refresh && existsSync(CACHE) && Date.now() - statSync(CACHE).mtimeMs < MAX_AGE_MS) {\n return parseMatrix(JSON.parse(readFileSync(CACHE, 'utf8')), CACHE)\n }\n\n const response = await fetch(SOURCE).catch((error: unknown) => {\n throw new Error(\n `Could not fetch the Can I email matrix from ${SOURCE}: ` +\n `${error instanceof Error ? error.message : String(error)}\\n` +\n ' Offline? Download it once and pass --data <file>.',\n )\n })\n if (!response.ok) {\n throw new Error(`Could not fetch ${SOURCE}: ${response.status} ${response.statusText}`)\n }\n const text = await response.text()\n // Cache before parsing so a malformed payload is inspectable rather than just gone.\n mkdirSync(dirname(CACHE), { recursive: true })\n writeFileSync(CACHE, text)\n return parseMatrix(JSON.parse(text), SOURCE)\n}\n\nfunction format(findings: Finding[], label: string): string {\n const lines = [label]\n for (const finding of findings) {\n lines.push(\n ` ${finding.level === 'blocked' ? '✗' : '!'} ${finding.slug} ${finding.source}` +\n `\\n ${finding.clients.join(', ')}`,\n )\n }\n return lines.join('\\n')\n}\n\n/**\n * Lint one or more rendered emails.\n *\n * Exits non-zero on a blocked finding and only on a blocked finding: a caveat is partial\n * support an author wants to know about, not a reason to fail a build.\n */\nexport async function emailLint(args: string[]): Promise<void> {\n // `positionalArgs` rather than a filter on `-`: a plain filter also keeps the *value* of\n // `--data <file>`, so the matrix itself got linted as if it were an email. The symptom was\n // spectacular — 28 blocked findings against a clean template, because the scanner read\n // slugs like `html-dfn` out of the JSON and took them for tags.\n //\n // A bare `-` is stdin by long convention and looks like a flag to any such parser, so it\n // is added back by name.\n const files = [...positionalArgs(args, ['data']), ...(args.includes('-') ? ['-'] : [])]\n if (files.length === 0) {\n throw new Error('cascivo email lint needs a file, or `-` to read rendered HTML from stdin.')\n }\n\n const { lint, indexFeatures, CASCIVO_ALLOW } = await loadEmail()\n const features = indexFeatures(await loadMatrix(args))\n const allow = args.includes('--no-allowlist') ? {} : CASCIVO_ALLOW\n\n let blockedTotal = 0\n let caveatTotal = 0\n\n for (const file of files) {\n const html = file === '-' ? await readStdin() : readFileSync(file, 'utf8')\n const findings = lint(html, features, { allow })\n const blocked = findings.filter((f) => f.level === 'blocked')\n const caveats = findings.filter((f) => f.level === 'caveat')\n blockedTotal += blocked.length\n caveatTotal += caveats.length\n\n const name = file === '-' ? '(stdin)' : file\n if (blocked.length === 0 && caveats.length === 0) {\n console.log(`✓ ${name}`)\n continue\n }\n console.log(format([...blocked, ...caveats], `${blocked.length === 0 ? '✓' : '✗'} ${name}`))\n }\n\n const summary =\n `\\n${files.length} file${files.length === 1 ? '' : 's'}, ` +\n `${blockedTotal} blocked, ${caveatTotal} caveat${caveatTotal === 1 ? '' : 's'}.`\n console.log(summary)\n if (blockedTotal > 0) process.exitCode = 1\n}\n\n/** Dispatch `cascivo email <subcommand>`. */\nexport async function email(args: string[]): Promise<void> {\n const [subcommand, ...rest] = args\n if (subcommand === 'lint') {\n await emailLint(rest)\n return\n }\n console.error(\n subcommand === undefined\n ? 'cascivo email needs a subcommand. Try `cascivo email lint --help`.'\n : `Unknown email subcommand: ${subcommand}`,\n )\n process.exitCode = 1\n}\n"],"mappings":"qQAsBA,MAAM,EAAS,0CAST,EAAQ,EAAK,EAAO,EAAG,wBAAwB,EAerD,eAAe,GAAsD,CACnE,GAAI,CACF,OAAO,MAAM,OAAO,iBACtB,MAAQ,CACN,MAAU,MACR;0BAEF,CACF,CACF,CAEA,eAAe,GAA6B,CAC1C,IAAM,EAAmB,CAAC,EAC1B,UAAW,IAAM,KAAS,EAAO,EAAO,KAAK,CAAe,EAC5D,OAAO,OAAO,OAAO,CAAM,CAAC,CAAC,SAAS,MAAM,CAC9C,CAaA,SAAgB,EAAY,EAAc,EAA+B,CACvE,IAAM,EAAO,GAAuB,CAClC,MAAU,MAAM,+BAA+B,EAAO,kBAAkB,GAAK,CAC/E,GACI,OAAO,GAAQ,WAAY,IAAc,EAAI,mBAAmB,EACpE,IAAM,EAAO,EACR,MAAM,QAAQ,EAAK,IAAO,GAAG,EAAI,iBAAiB,EACvD,IAAM,EAAU,EAAK,KACjB,EAAQ,SAAW,GAAG,EAAI,iBAAiB,EAE/C,IAAM,EAAkB,EAAQ,KAAK,EAAO,IAAM,CAChD,GAAI,OAAO,GAAU,WAAY,EAAgB,OAAO,EAAI,QAAQ,EAAE,mBAAmB,EACzF,IAAM,EAAU,EAKhB,OAJI,OAAO,EAAQ,MAAY,SAC3B,OAAO,EAAQ,OAAa,UAAY,EAAQ,QAAa,KACxD,EAAI,QAAQ,EAAE,KAAK,EAAQ,KAAQ,mBAAmB,EAExD,CACL,KAAM,EAAQ,KACd,MAAO,OAAO,EAAQ,OAAa,SAAW,EAAQ,MAAW,EAAQ,KACzE,SAAU,OAAO,EAAQ,UAAgB,SAAW,EAAQ,SAAc,UAC1E,GAAI,OAAO,EAAQ,UAAgB,SAAW,CAAE,SAAU,EAAQ,QAAY,EAAI,CAAC,EACnF,MAAO,EAAQ,KACjB,EAVgD,EAAI,QAAQ,EAAE,kBAAkB,CAWlF,CAAC,EAED,MAAO,CACL,YAAa,OAAO,EAAK,aAAmB,SAAW,EAAK,YAAiB,UAC7E,iBACE,OAAO,EAAK,kBAAwB,SAAW,EAAK,iBAAsB,UAC5E,MACF,CACF,CAGA,eAAe,EAAW,EAAwC,CAChE,IAAM,EAAW,EAAU,EAAM,MAAM,EACvC,GAAI,IAAa,IAAA,GAAW,CAC1B,GAAI,CAAC,EAAW,CAAQ,EAAG,MAAU,MAAM,iBAAiB,GAAU,EACtE,OAAO,EAAY,KAAK,MAAM,EAAa,EAAU,MAAM,CAAC,EAAG,CAAQ,CACzE,CAGA,GAAI,CADY,EAAK,SAAS,WACnB,GAAK,EAAW,CAAK,GAAK,KAAK,IAAI,EAAI,EAAS,CAAK,CAAC,CAAC,QAAU,MAC1E,OAAO,EAAY,KAAK,MAAM,EAAa,EAAO,MAAM,CAAC,EAAG,CAAK,EAGnE,IAAM,EAAW,MAAM,MAAM,CAAM,CAAC,CAAC,MAAO,GAAmB,CAC7D,MAAU,MACR,+CAA+C,EAAO,IACjD,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EAAE,sDAE9D,CACF,CAAC,EACD,GAAI,CAAC,EAAS,GACZ,MAAU,MAAM,mBAAmB,EAAO,IAAI,EAAS,OAAO,GAAG,EAAS,YAAY,EAExF,IAAM,EAAO,MAAM,EAAS,KAAK,EAIjC,OAFA,EAAU,EAAQ,CAAK,EAAG,CAAE,UAAW,EAAK,CAAC,EAC7C,EAAc,EAAO,CAAI,EAClB,EAAY,KAAK,MAAM,CAAI,EAAG,CAAM,CAC7C,CAEA,SAAS,EAAO,EAAqB,EAAuB,CAC1D,IAAM,EAAQ,CAAC,CAAK,EACpB,IAAK,IAAM,KAAW,EACpB,EAAM,KACJ,KAAK,EAAQ,QAAU,UAAY,IAAM,IAAI,GAAG,EAAQ,KAAK,IAAI,EAAQ,OAAA,UAC5D,EAAQ,QAAQ,KAAK,IAAI,GACxC,EAEF,OAAO,EAAM,KAAK;CAAI,CACxB,CAQA,eAAsB,EAAU,EAA+B,CAQ7D,IAAM,EAAQ,CAAC,GAAG,EAAe,EAAM,CAAC,MAAM,CAAC,EAAG,GAAI,EAAK,SAAS,GAAG,EAAI,CAAC,GAAG,EAAI,CAAC,CAAE,EACtF,GAAI,EAAM,SAAW,EACnB,MAAU,MAAM,2EAA2E,EAG7F,GAAM,CAAE,OAAM,gBAAe,iBAAkB,MAAM,EAAU,EACzD,EAAW,EAAc,MAAM,EAAW,CAAI,CAAC,EAC/C,EAAQ,EAAK,SAAS,gBAAgB,EAAI,CAAC,EAAI,EAEjD,EAAe,EACf,EAAc,EAElB,IAAK,IAAM,KAAQ,EAAO,CAExB,IAAM,EAAW,EADJ,IAAS,IAAM,MAAM,EAAU,EAAI,EAAa,EAAM,MAAM,EAC7C,EAAU,CAAE,OAAM,CAAC,EACzC,EAAU,EAAS,OAAQ,GAAM,EAAE,QAAU,SAAS,EACtD,EAAU,EAAS,OAAQ,GAAM,EAAE,QAAU,QAAQ,EAC3D,GAAgB,EAAQ,OACxB,GAAe,EAAQ,OAEvB,IAAM,EAAO,IAAS,IAAM,UAAY,EACxC,GAAI,EAAQ,SAAW,GAAK,EAAQ,SAAW,EAAG,CAChD,QAAQ,IAAI,KAAK,GAAM,EACvB,QACF,CACA,QAAQ,IAAI,EAAO,CAAC,GAAG,EAAS,GAAG,CAAO,EAAG,GAAG,EAAQ,SAAW,EAAI,IAAM,IAAI,GAAG,GAAM,CAAC,CAC7F,CAEA,IAAM,EACJ,KAAK,EAAM,OAAO,OAAO,EAAM,SAAW,EAAI,GAAK,IAAI,IACpD,EAAa,YAAY,EAAY,SAAS,IAAgB,EAAI,GAAK,IAAI,GAChF,QAAQ,IAAI,CAAO,EACf,EAAe,IAAG,QAAQ,SAAW,EAC3C,CAGA,eAAsB,EAAM,EAA+B,CACzD,GAAM,CAAC,EAAY,GAAG,GAAQ,EAC9B,GAAI,IAAe,OAAQ,CACzB,MAAM,EAAU,CAAI,EACpB,MACF,CACA,QAAQ,MACN,IAAe,IAAA,GACX,qEACA,6BAA6B,GACnC,EACA,QAAQ,SAAW,CACrB"}
@@ -237,6 +237,7 @@
237
237
  { "name": "--cascivo-chart-grid", "resolvedDefault": "oklch(0.928 0.006 264)" },
238
238
  { "name": "--cascivo-chart-axis", "resolvedDefault": "oklch(0.5 0.016 264)" },
239
239
  { "name": "--cascivo-chart-fill-opacity", "resolvedDefault": "0.25" },
240
+ { "name": "--cascivo-chart-bar-radius", "resolvedDefault": "2px" },
240
241
  { "name": "--cascivo-color-accent-text", "resolvedDefault": "oklch(0.52 0.2 250)" },
241
242
  { "name": "--cascivo-color-accent-text-hover", "resolvedDefault": "oklch(0.45 0.2 250)" },
242
243
  {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/utils/config.ts","../src/index.ts"],"mappings":";AA2BA;AAAA,cAfa,MAAA;AAAA,KAeD,SAAA,WAAoB,MAAM;AAAA,KAE1B,uBAAA;EAEN,GAAA;EAAa,OAAA,GAAU,MAAA;EAAwB,MAAA,GAAS,MAAM;AAAA;AAAA,UAEnD,aAAA;EAFX;EAIJ,QAAA;EAJ2B;EAM3B,SAAA;EAN4D;EAQ5D,KAAA,EAAO,SAAA;EAR2D;EAUlE,UAAA,GAAa,MAAA,SAAe,uBAAA;EARA;EAU5B,KAAA;AAAA;;;cCvBW,OAAA;AAAA,iBA4JS,GAAA,CAAI,IAAA,aAAiB,OAAO"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/utils/config.ts","../src/index.ts"],"mappings":";AA2BA;AAAA,cAfa,MAAA;AAAA,KAeD,SAAA,WAAoB,MAAM;AAAA,KAE1B,uBAAA;EAEN,GAAA;EAAa,OAAA,GAAU,MAAA;EAAwB,MAAA,GAAS,MAAM;AAAA;AAAA,UAEnD,aAAA;EAFX;EAIJ,QAAA;EAJ2B;EAM3B,SAAA;EAN4D;EAQ5D,KAAA,EAAO,SAAA;EAR2D;EAUlE,UAAA,GAAa,MAAA,SAAe,uBAAA;EARA;EAU5B,KAAA;AAAA;;;cCvBW,OAAA;AAAA,iBAwLS,GAAA,CAAI,IAAA,aAAiB,OAAO"}
package/dist/index.mjs CHANGED
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
- import{a as e,c as t,l as n,n as r,o as i,r as a,s as o,t as s,u as c}from"./config-DT-Cs7bC.mjs";import{i as l,n as u,r as d,t as f}from"./fs-B7O-fNID.mjs";import{r as p,t as m}from"./http-Dx1cFJ5S.mjs";import{a as h,c as g,i as _,l as v,n as y,o as b,r as ee,s as x,t as te}from"./lock-BwLjTQmQ.mjs";import{n as S,r as ne,t as re}from"./peer-versions-DIPdOfWj.mjs";import{createRequire as C}from"node:module";import{argv as w,stdin as T,stdout as E}from"node:process";import{pathToFileURL as ie}from"node:url";import{existsSync as D,readFileSync as O,readdirSync as k,writeFileSync as A}from"node:fs";import{basename as ae,dirname as oe,join as j,resolve as M}from"node:path";import{spawnSync as se}from"node:child_process";import{readFile as ce}from"node:fs/promises";import{asTemplateMeta as le,buildRegistry as ue,isTemplateItem as N,parseItem as P,parseLegacyRegistry as de,validateIndex as fe,validateItem as pe,validateTemplate as me}from"@cascivo/registry";import{createInterface as F}from"node:readline/promises";function I(t,n=process.cwd(),r={}){if(t.length===0)return!0;let a=r.pm??e(n),[s,c]=i(a,t,{dev:r.dev??!1,...r.floors?{floors:r.floors}:{},...r.pin?{pin:r.pin}:{}});return console.log(`Installing ${t.join(`, `)} with ${s}…`),se(s,c,{cwd:n,stdio:`inherit`,shell:process.platform===`win32`}).status===0?!0:(console.error(`Failed to install: ${t.join(`, `)}`),console.error(`Install them yourself with: ${o(a,t,{dev:r.dev??!1})}`),!1)}const he=/^([^/]+)\/([^/]+)\/([^#]+)(?:#(.+))?$/;function L(e,t=process.env){return e.replace(/\$\{([^}]+)\}/g,(e,n)=>{let r=t[n];if(r===void 0)throw Error(`Environment variable ${n} is not set`);return r})}function ge(e,t=process.env){if(e.startsWith(`http://`)||e.startsWith(`https://`)||e.startsWith(`./`)||e.startsWith(`/`))return{kind:`url`,name:e,raw:e};if(e.startsWith(`@`)){let t=e.indexOf(`/`,1);if(t===-1)throw Error(`Invalid namespace address: ${e} (expected @ns/name)`);return{kind:`namespace`,namespace:e.slice(0,t),name:e.slice(t+1),raw:e}}let n=e.includes(`#`)?e.slice(0,e.indexOf(`#`)):e,r=he.exec(e);if(r&&!n.includes(`.`)&&n.split(`/`).length>=3){let[,t,n,i,a]=r;return{kind:`github`,name:`${t}/${n}/${i}`,ref:a??`main`,raw:e}}return{kind:`bare`,name:e,raw:e}}function _e(e,t,n,r){let i=typeof n==`string`?n:n.url;if(!i.includes(`{name}`))throw Error(`Registry namespace "${e}" URL template must contain {name}: ${i}`);return L(i.replace(`{name}`,t),r)}async function ve(e,t,n=process.env,r){switch(e.kind){case`url`:return{url:e.name};case`bare`:return{url:`${t.registry.replace(/\/registry\.json$/,``).replace(/\/r$/,``)}/r/${e.name}.json`};case`github`:{let t=e.name.split(`/`),n=t[0],r=t[1],i=t.slice(2).join(`/`);return{url:`https://raw.githubusercontent.com/${n}/${r}/${e.ref??`main`}/r/${i}.json`}}case`namespace`:{let i=e.namespace,a=e.name;if(t.registries?.[i]){let e=t.registries[i],r=_e(i,a,e,n),o=typeof e==`string`?void 0:e.headers,s=typeof e==`string`?void 0:e.params,c=o?Object.fromEntries(Object.entries(o).map(([e,t])=>[e,L(t,n)])):void 0,l=s?Object.fromEntries(Object.entries(s).map(([e,t])=>[e,L(t,n)])):void 0,u={url:r};return c&&(u.headers=c),l&&(u.params=l),u}if(r){let e=await r(i);if(e)return{url:_e(i,a,e,n)}}if(i===`@cascivo`||i===`@cascade`)return{url:`${s}/r/${a}.json`};throw Error(`Unknown namespace "${i}". Add it to registries in cascivo.config.ts or check the directory at cascivo.com`)}}}async function ye(e,t,n=process.env,r){let i=new Set,a=[...e],o=[];for(;a.length>0;){let e=a.shift(),s=ge(e,n),c=s.raw;if(i.has(c))continue;i.add(c);let{url:l,headers:u,params:d}=await ve(s,t,n,r),f={};u&&(f.headers=u),d&&(f.params=d);let p=P(await m(l,f),l),h={spec:e,addr:s,itemUrl:l,item:p,registryBase:l.replace(/\/[^/]+\.json$/,``)};u&&(h.headers=u),d&&(h.params=d),o.push(h);for(let e of p.registryDependencies??[])a.push(e)}return{items:o}}const be=`${s}/r/registries.json`;function xe(e,t){if(typeof e!=`object`||!e)return null;let n=e.registries;if(!Array.isArray(n))return null;for(let e of n){if(typeof e!=`object`||!e)continue;let n=e;if(n.namespace!==t)continue;let r=n.registryUrl;return typeof r!=`string`||!/^https?:\/\//.test(r)?null:r}return null}async function Se(e,t){try{let n;if(t){let e=await t(be,{headers:{Accept:`application/json`}});if(!e.ok)throw Error(`HTTP ${e.status}`);n=await e.json()}else n=await m(be);return xe(n,e)}catch(e){let t=e instanceof Error?e.message:String(e);return console.warn(`Could not reach cascivo.com directory: ${t}`),null}}var Ce=c({LAYOUT_ALIASES:()=>R,NAME_NOTES:()=>z,add:()=>U,isThirdParty:()=>V,resolveBareClosure:()=>B,resolveTemplateTarget:()=>De});const we=new Set([`react`,`react-dom`]);function Te(e){try{let t=JSON.parse(O(j(e,`package.json`),`utf8`));return{...t.dependencies,...t.devDependencies}}catch{return{}}}function Ee(e,t){let n=Te(e);n[`@cascivo/themes`]!==void 0||n[`@cascivo/tokens`]!==void 0||(console.log(`
3
- Styling: cascivo components read --cascivo-* tokens, which this project does not import yet.`),console.log(` ${o(t,[`@cascivo/themes`])}`),console.log(` import '@cascivo/themes/all' // once, in your entry file`),console.log(` <html data-theme="light"> // or any container`))}function De(e,t){return f(e,M(e,t))}const R={box:`layout/flex`,hstack:`layout/flex`,vstack:`layout/flex`,gap:`spacer`},z={stack:`Note: cascivo's Stack overlaps children into a card-pile (z-axis), not a vertical spacing layout. For vertical spacing use Flex direction="vertical" (cascivo add flex; aliases: vstack, hstack).`};function B(e,t){let n=t.map(e=>({name:e,requested:!0})),r=new Set,i=[],a=[];for(;n.length>0;){let{name:t,requested:o}=n.shift(),s=g(e,R[t.toLowerCase()]??t);if(!s){a.push(t);continue}if(r.has(s.name))continue;r.add(s.name);let c=v(s,t);i.push({entry:s,requested:o,...c?{viaAlias:c}:{}});for(let e of s.registryDependencies??[])n.push({name:e,requested:!1})}return{resolved:i,missing:a}}const Oe=/^https:\/\/(cascivo\.com|raw\.githubusercontent\.com\/cascivo\/cascivo)\//;function V(e){return!Oe.test(e)}function H(e){return e.startsWith(`@`)||e.startsWith(`http://`)||e.startsWith(`https://`)||e.startsWith(`./`)||e.startsWith(`/`)||e.split(`/`).length>=3}async function U(t,n,r={}){if(t.length===0){console.error(`Usage: cascivo add <component...>`);return}let i=r.cwd??process.cwd(),a=r.pm??e(i),s=t.filter(H),c=t.filter(e=>!H(e)),u=null;if(c.length>0){u=await b(n.registry);let e=new Set(u.templates.map(e=>e.toLowerCase())),t=c.filter(t=>e.has(t.toLowerCase()));t.length>0&&(c=c.filter(t=>!e.has(t.toLowerCase())),s.push(...t.map(e=>`@cascivo/${e.toLowerCase()}`)))}let f=await y(i)??te();if(s.length>0){let e=await ye(s,n,process.env,Se);if(r.dryRun){console.log(`Dry run — would install:`);for(let t of e.items){let e=N(t.item)?` [template]`:``;console.log(` ${t.spec} (${t.item.name}@${t.item.version})${e} from ${t.itemUrl}`);for(let e of t.item.files){let n=N(t.item)&&e.target?` → ${e.target}`:``;console.log(` ${e.url}${n}`)}t.item.registryDependencies?.length&&console.log(` components: ${t.item.registryDependencies.join(`, `)}`)}return}for(let{item:t,itemUrl:o,registryBase:s}of e.items){V(o)&&console.log(`\nReview before use — files from non-directory registry: ${s}\n`+(t.files??[]).map(e=>` ${e.url}`).join(`
4
- `));let e;try{e=await ke(t,n,i,{pm:a,...r.noInstall?{noInstall:!0}:{}})}catch(e){console.error(`Failed to install ${t.name}: ${e instanceof Error?e.message:e}`),process.exitCode=1;continue}f=_(f,t.name,{registry:s,version:t.version,installedAt:new Date().toISOString().slice(0,10),files:e})}if(await h(f,i),c.length===0){Ee(i,a);return}}if(c.length===0)return;u??=await b(n.registry);let m=new Set,v=n.registry.replace(/\/registry\.json$/,``).replace(/\/r$/,``);for(let e of c){let t=z[e.toLowerCase()];t&&console.log(t);let n=R[e.toLowerCase()];if(!n)continue;let r=g(u,n);r&&console.log(`"${e}" is not a cascivo component — installing ${r.name} (its ${e} primitive).`)}let{resolved:S,missing:ne}=B(u,c);for(let e of ne)console.error(`Component "${e}" not found in registry. Run "cascivo list".`);for(let{entry:e,requested:t,viaAlias:n}of S)!t||!n||console.log(`\n"${n}" is cascivo's "${e.name}" — installing that.`);let C=new Set;for(let{entry:e,requested:t}of S)!t||!e.install||C.has(e.install)||(C.add(e.install),console.log(`\n"${e.name}" ships in the ${e.install} npm package — no source is copied into your project. Updates come from your package manager, not \`cascivo update\`.`));for(let{entry:e,requested:t}of S){if(!t||!e.deprecated)continue;let{replacement:n,since:r,note:i}=e.deprecated;console.warn(`\n⚠ "${e.name}" is deprecated since ${r}. Use "${n}" instead:\n cascivo add ${n}\n`+(i?` ${i}\n`:``)+` Adding "${e.name}" anyway — it still works.\n`)}let w={};for(let{entry:e}of S)for(let[t,n]of Object.entries(e.peerVersions??{}))w[t]=n;let T=new Set,E=[];for(let{entry:e,requested:t}of S){if(e.install){let t=e.install;T.add(t),console.log(`"${e.name}" is distributed as the npm package ${t} — a runtime dependency, not copied source (updates come via your package manager).`),E.push(` import { ${e.meta.name} } from '${t}'`),e.styles&&E.push(` import '${e.styles}' // once, in your entry file`);continue}let r=e.name.includes(`/`)?e.name.split(`/`).pop():e.name,a={},o;try{o=await Promise.all(e.files.map(async e=>({url:e,content:await p(e)})))}catch(t){console.error(`Failed to install ${e.name}: ${t instanceof Error?t.message:t}`),process.exitCode=1;continue}for(let{url:e,content:t}of o){let o=d(n.outputDir,r,x(e),i);await l(o,t),a[o]=ee(t)}for(let t of e.dependencies)we.has(t)||m.add(t);f=_(f,e.name,{registry:v,version:e.version,installedAt:new Date().toISOString().slice(0,10),files:a});let s=t?``:` (dependency)`;console.log(`Added ${e.name} to ${n.outputDir}/${r}/${s}`)}if(T.size>0){let e=Te(i),t=[...T].filter(t=>e[t]===void 0);r.dryRun?console.log(`Dry run — would install npm package(s): ${[...T].join(`, `)}`):t.length>0&&(r.noInstall?console.log(`\nSkipped install (--no-install). Install with:\n ${o(a,t)}`):I(t,i,{pm:a})||(process.exitCode=1)),E.length>0&&console.log(`\nImport it:\n${E.join(`
5
- `)}`)}if(m.size>0&&!r.dryRun&&(r.noInstall?console.log(`\nSkipped install (--no-install). Install with:\n ${o(a,[...m])}`):I([...m],i,{pm:a})),Object.keys(w).length>0){let e=await re(i,w);for(let t of e)console.error(`\nWarning: ${t.pkg} ${t.installed?`${t.installed} is`:`is not`} installed, but the copied component source needs ${t.pkg} ${t.required}. Run: ${o(a,[`${t.pkg}@latest`])}`),process.exitCode=1}await h(f,i),Ee(i,a)}async function ke(t,n,r,i={}){let a={},s=new Set,c=N(t),u=t.name.includes(`/`)?t.name.split(`/`).pop():t.name,f=await Promise.all((t.files??[]).map(async e=>({file:e,content:await p(e.url)})));for(let{file:e,content:t}of f){let i=c&&e.target?De(r,e.target):d(n.outputDir,u,x(e.url),r);await l(i,t),a[i]=ee(t)}for(let e of t.dependencies??[])we.has(e)||s.add(e);if(s.size>0&&(i.noInstall?console.log(`\nSkipped install (--no-install). Install with:\n ${o(i.pm??e(r),[...s])}`):I([...s],r,i.pm?{pm:i.pm}:{})),c){let e=t.files?.filter(e=>e.target).map(e=>e.target)??[];console.log(`Installed template ${t.name} (${e.length} files)`)}else console.log(`Added ${t.name} to ${n.outputDir}/${u}/`);return a}function W(e,t){let n=e.find(e=>e.startsWith(`--${t}=`));if(n)return n.slice(t.length+3);let r=e.indexOf(`--${t}`),i=r===-1?void 0:e[r+1];return i&&!i.startsWith(`--`)?i:void 0}function Ae(e,t=[]){let n=[];for(let r=0;r<e.length;r++){let i=e[r];if(i.startsWith(`-`)){let n=i.replace(/^--?/,``).split(`=`)[0],a=e[r+1];t.includes(n)&&!i.includes(`=`)&&a!==void 0&&!a.startsWith(`-`)&&r++;continue}n.push(i)}return n}function G(e){let n=W(e,`package-manager`)??W(e,`pm`);return n===void 0?{pm:void 0}:t(n)?{pm:n}:{error:`Unknown package manager "${n}". Use one of: pnpm, yarn, npm, bun.`}}const K={"@cascivo/react":`1.2.0`,"@cascivo/themes":`1.0.0`,"@cascivo/charts":`1.2.0`,"@cascivo/icons":`1.1.0`,"@cascivo/eslint-config":`0.4.0`};function je(e){return e===`yarn`?`yarn`:`${e} install`}function Me(e,t){return e===`npm`?`npm run ${t}`:`${e} ${t}`}function Ne(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``)||`section`}function Pe(e){let t=e.trim().split(/[^a-zA-Z0-9]+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(``)||`Section`;return/^[0-9]/.test(t)?`Section${t}`:t}function Fe(e){let t=e.trim().split(/[^a-zA-Z0-9]+/).filter(e=>e!==``&&!/^\d+$/.test(e)&&!/^v\d+$/i.test(e)),n=[];for(let e of t){if(n.length>=3||n.length>0&&n.join(` `).length+1+e.length>24)break;n.push(e.charAt(0).toUpperCase()+e.slice(1))}return n.join(` `)||`App`}function Ie(e){return e.trim().toLowerCase().replace(/[^a-z0-9._-]+/g,`-`).replace(/^-+|-+$/g,``)||`cascivo-app`}function Le(e){let t=new Set,n=new Set,r=[];for(let i of e){let e=i.trim();if(!e)continue;let a=Ne(e),o=Pe(e),s=2;for(;t.has(a)||n.has(o);)a=`${Ne(e)}-${s}`,o=`${Pe(e)}${s}`,s++;t.add(a),n.add(o),r.push({key:a,label:e,component:o})}return r.length>0?r:[{key:`home`,label:`Home`,component:`Home`}]}function Re(e){let t={name:Ie(e.name),private:!0,version:`0.0.0`,type:`module`,scripts:{dev:`vite`,build:`tsc && vite build`,preview:`vite preview`,typecheck:`tsc --noEmit`,lint:`eslint .`,format:`prettier --write .`,"format:check":`prettier --check .`},dependencies:{"@cascivo/react":K[`@cascivo/react`],"@cascivo/themes":K[`@cascivo/themes`],"@preact/signals-react":`>=3.0.0`,react:`^19.0.0`,"react-dom":`^19.0.0`},devDependencies:{"@cascivo/eslint-config":K[`@cascivo/eslint-config`],"@eslint/js":`^9.0.0`,"@types/react":`^19.0.0`,"@types/react-dom":`^19.0.0`,"@vitejs/plugin-react":`^5.0.0`,eslint:`^9.0.0`,"eslint-plugin-react-hooks":`^7.0.0`,prettier:`^3.0.0`,typescript:`^5.7.0`,"typescript-eslint":`^8.0.0`,vite:`^7.0.0`}};return JSON.stringify(t,null,2)+`
6
- `}function ze(){return JSON.stringify({compilerOptions:{target:`ES2022`,useDefineForClassFields:!0,lib:[`ES2022`,`DOM`,`DOM.Iterable`],module:`ESNext`,skipLibCheck:!0,moduleResolution:`bundler`,allowImportingTsExtensions:!0,resolveJsonModule:!0,isolatedModules:!0,moduleDetection:`force`,noEmit:!0,jsx:`react-jsx`,strict:!0,noUnusedLocals:!0,noUnusedParameters:!0,noFallthroughCasesInSwitch:!0},include:[`src`]},null,2)+`
7
- `}function Be(){return`import react from '@vitejs/plugin-react'
2
+ import{a as e,l as t,n,o as r,r as i,s as a,t as o,u as s}from"./config-DT-Cs7bC.mjs";import{i as c,n as l,r as u,t as d}from"./fs-B7O-fNID.mjs";import{r as f,t as p}from"./http-Dx1cFJ5S.mjs";import{a as m,c as h,i as g,l as _,n as v,o as y,r as b,s as x,t as ee}from"./lock-BwLjTQmQ.mjs";import{n as S,r as C,t as te}from"./peer-versions-DIPdOfWj.mjs";import{n as w,r as T,t as E}from"./args-CtmltI1S.mjs";import{createRequire as D}from"node:module";import{argv as O,stdin as k,stdout as A}from"node:process";import{pathToFileURL as ne}from"node:url";import{existsSync as j,readFileSync as M,readdirSync as N,writeFileSync as P}from"node:fs";import{basename as re,dirname as ie,join as F,resolve as I}from"node:path";import{spawnSync as ae}from"node:child_process";import{readFile as oe}from"node:fs/promises";import{asTemplateMeta as se,buildRegistry as ce,isTemplateItem as L,parseItem as R,parseLegacyRegistry as le,validateIndex as ue,validateItem as de,validateTemplate as fe}from"@cascivo/registry";import{createInterface as z}from"node:readline/promises";function B(t,n=process.cwd(),i={}){if(t.length===0)return!0;let o=i.pm??e(n),[s,c]=r(o,t,{dev:i.dev??!1,...i.floors?{floors:i.floors}:{},...i.pin?{pin:i.pin}:{}});return console.log(`Installing ${t.join(`, `)} with ${s}…`),ae(s,c,{cwd:n,stdio:`inherit`,shell:process.platform===`win32`}).status===0?!0:(console.error(`Failed to install: ${t.join(`, `)}`),console.error(`Install them yourself with: ${a(o,t,{dev:i.dev??!1})}`),!1)}const pe=/^([^/]+)\/([^/]+)\/([^#]+)(?:#(.+))?$/;function V(e,t=process.env){return e.replace(/\$\{([^}]+)\}/g,(e,n)=>{let r=t[n];if(r===void 0)throw Error(`Environment variable ${n} is not set`);return r})}function H(e,t=process.env){if(e.startsWith(`http://`)||e.startsWith(`https://`)||e.startsWith(`./`)||e.startsWith(`/`))return{kind:`url`,name:e,raw:e};if(e.startsWith(`@`)){let t=e.indexOf(`/`,1);if(t===-1)throw Error(`Invalid namespace address: ${e} (expected @ns/name)`);return{kind:`namespace`,namespace:e.slice(0,t),name:e.slice(t+1),raw:e}}let n=e.includes(`#`)?e.slice(0,e.indexOf(`#`)):e,r=pe.exec(e);if(r&&!n.includes(`.`)&&n.split(`/`).length>=3){let[,t,n,i,a]=r;return{kind:`github`,name:`${t}/${n}/${i}`,ref:a??`main`,raw:e}}return{kind:`bare`,name:e,raw:e}}function U(e,t,n,r){let i=typeof n==`string`?n:n.url;if(!i.includes(`{name}`))throw Error(`Registry namespace "${e}" URL template must contain {name}: ${i}`);return V(i.replace(`{name}`,t),r)}async function W(e,t,n=process.env,r){switch(e.kind){case`url`:return{url:e.name};case`bare`:return{url:`${t.registry.replace(/\/registry\.json$/,``).replace(/\/r$/,``)}/r/${e.name}.json`};case`github`:{let t=e.name.split(`/`),n=t[0],r=t[1],i=t.slice(2).join(`/`);return{url:`https://raw.githubusercontent.com/${n}/${r}/${e.ref??`main`}/r/${i}.json`}}case`namespace`:{let i=e.namespace,a=e.name;if(t.registries?.[i]){let e=t.registries[i],r=U(i,a,e,n),o=typeof e==`string`?void 0:e.headers,s=typeof e==`string`?void 0:e.params,c=o?Object.fromEntries(Object.entries(o).map(([e,t])=>[e,V(t,n)])):void 0,l=s?Object.fromEntries(Object.entries(s).map(([e,t])=>[e,V(t,n)])):void 0,u={url:r};return c&&(u.headers=c),l&&(u.params=l),u}if(r){let e=await r(i);if(e)return{url:U(i,a,e,n)}}if(i===`@cascivo`||i===`@cascade`)return{url:`${o}/r/${a}.json`};throw Error(`Unknown namespace "${i}". Add it to registries in cascivo.config.ts or check the directory at cascivo.com`)}}}async function me(e,t,n=process.env,r){let i=new Set,a=[...e],o=[];for(;a.length>0;){let e=a.shift(),s=H(e,n),c=s.raw;if(i.has(c))continue;i.add(c);let{url:l,headers:u,params:d}=await W(s,t,n,r),f={};u&&(f.headers=u),d&&(f.params=d);let m=R(await p(l,f),l),h={spec:e,addr:s,itemUrl:l,item:m,registryBase:l.replace(/\/[^/]+\.json$/,``)};u&&(h.headers=u),d&&(h.params=d),o.push(h);for(let e of m.registryDependencies??[])a.push(e)}return{items:o}}const he=`${o}/r/registries.json`;function ge(e,t){if(typeof e!=`object`||!e)return null;let n=e.registries;if(!Array.isArray(n))return null;for(let e of n){if(typeof e!=`object`||!e)continue;let n=e;if(n.namespace!==t)continue;let r=n.registryUrl;return typeof r!=`string`||!/^https?:\/\//.test(r)?null:r}return null}async function _e(e,t){try{let n;if(t){let e=await t(he,{headers:{Accept:`application/json`}});if(!e.ok)throw Error(`HTTP ${e.status}`);n=await e.json()}else n=await p(he);return ge(n,e)}catch(e){let t=e instanceof Error?e.message:String(e);return console.warn(`Could not reach cascivo.com directory: ${t}`),null}}var ve=s({LAYOUT_ALIASES:()=>G,NAME_NOTES:()=>Ce,add:()=>Oe,isThirdParty:()=>Ee,resolveBareClosure:()=>we,resolveTemplateTarget:()=>Se});const ye=new Set([`react`,`react-dom`]);function be(e){try{let t=JSON.parse(M(F(e,`package.json`),`utf8`));return{...t.dependencies,...t.devDependencies}}catch{return{}}}function xe(e,t){let n=be(e);n[`@cascivo/themes`]!==void 0||n[`@cascivo/tokens`]!==void 0||(console.log(`
3
+ Styling: cascivo components read --cascivo-* tokens, which this project does not import yet.`),console.log(` ${a(t,[`@cascivo/themes`])}`),console.log(` import '@cascivo/themes/all' // once, in your entry file`),console.log(` <html data-theme="light"> // or any container`))}function Se(e,t){return d(e,I(e,t))}const G={box:`layout/flex`,hstack:`layout/flex`,vstack:`layout/flex`,gap:`spacer`},Ce={stack:`Note: cascivo's Stack overlaps children into a card-pile (z-axis), not a vertical spacing layout. For vertical spacing use Flex direction="vertical" (cascivo add flex; aliases: vstack, hstack).`};function we(e,t){let n=t.map(e=>({name:e,requested:!0})),r=new Set,i=[],a=[];for(;n.length>0;){let{name:t,requested:o}=n.shift(),s=h(e,G[t.toLowerCase()]??t);if(!s){a.push(t);continue}if(r.has(s.name))continue;r.add(s.name);let c=_(s,t);i.push({entry:s,requested:o,...c?{viaAlias:c}:{}});for(let e of s.registryDependencies??[])n.push({name:e,requested:!1})}return{resolved:i,missing:a}}const Te=/^https:\/\/(cascivo\.com|raw\.githubusercontent\.com\/cascivo\/cascivo)\//;function Ee(e){return!Te.test(e)}function De(e){return e.startsWith(`@`)||e.startsWith(`http://`)||e.startsWith(`https://`)||e.startsWith(`./`)||e.startsWith(`/`)||e.split(`/`).length>=3}async function Oe(t,n,r={}){if(t.length===0){console.error(`Usage: cascivo add <component...>`);return}let i=r.cwd??process.cwd(),o=r.pm??e(i),s=t.filter(De),l=t.filter(e=>!De(e)),d=null;if(l.length>0){d=await y(n.registry);let e=new Set(d.templates.map(e=>e.toLowerCase())),t=l.filter(t=>e.has(t.toLowerCase()));t.length>0&&(l=l.filter(t=>!e.has(t.toLowerCase())),s.push(...t.map(e=>`@cascivo/${e.toLowerCase()}`)))}let p=await v(i)??ee();if(s.length>0){let e=await me(s,n,process.env,_e);if(r.dryRun){console.log(`Dry run — would install:`);for(let t of e.items){let e=L(t.item)?` [template]`:``;console.log(` ${t.spec} (${t.item.name}@${t.item.version})${e} from ${t.itemUrl}`);for(let e of t.item.files){let n=L(t.item)&&e.target?` → ${e.target}`:``;console.log(` ${e.url}${n}`)}t.item.registryDependencies?.length&&console.log(` components: ${t.item.registryDependencies.join(`, `)}`)}return}for(let{item:t,itemUrl:a,registryBase:s}of e.items){Ee(a)&&console.log(`\nReview before use — files from non-directory registry: ${s}\n`+(t.files??[]).map(e=>` ${e.url}`).join(`
4
+ `));let e;try{e=await ke(t,n,i,{pm:o,...r.noInstall?{noInstall:!0}:{}})}catch(e){console.error(`Failed to install ${t.name}: ${e instanceof Error?e.message:e}`),process.exitCode=1;continue}p=g(p,t.name,{registry:s,version:t.version,installedAt:new Date().toISOString().slice(0,10),files:e})}if(await m(p,i),l.length===0){xe(i,o);return}}if(l.length===0)return;d??=await y(n.registry);let _=new Set,S=n.registry.replace(/\/registry\.json$/,``).replace(/\/r$/,``);for(let e of l){let t=Ce[e.toLowerCase()];t&&console.log(t);let n=G[e.toLowerCase()];if(!n)continue;let r=h(d,n);r&&console.log(`"${e}" is not a cascivo component — installing ${r.name} (its ${e} primitive).`)}let{resolved:C,missing:w}=we(d,l);for(let e of w)console.error(`Component "${e}" not found in registry. Run "cascivo list".`);for(let{entry:e,requested:t,viaAlias:n}of C)!t||!n||console.log(`\n"${n}" is cascivo's "${e.name}" — installing that.`);let T=new Set;for(let{entry:e,requested:t}of C)!t||!e.install||T.has(e.install)||(T.add(e.install),console.log(`\n"${e.name}" ships in the ${e.install} npm package — no source is copied into your project. Updates come from your package manager, not \`cascivo update\`.`));for(let{entry:e,requested:t}of C){if(!t||!e.deprecated)continue;let{replacement:n,since:r,note:i}=e.deprecated;console.warn(`\n⚠ "${e.name}" is deprecated since ${r}. Use "${n}" instead:\n cascivo add ${n}\n`+(i?` ${i}\n`:``)+` Adding "${e.name}" anyway — it still works.\n`)}let E={};for(let{entry:e}of C)for(let[t,n]of Object.entries(e.peerVersions??{}))E[t]=n;let D=new Set,O=[];for(let{entry:e,requested:t}of C){if(e.install){let t=e.install;D.add(t),console.log(`"${e.name}" is distributed as the npm package ${t} — a runtime dependency, not copied source (updates come via your package manager).`),O.push(` import { ${e.meta.name} } from '${t}'`),e.styles&&O.push(` import '${e.styles}' // once, in your entry file`);continue}let r=e.name.includes(`/`)?e.name.split(`/`).pop():e.name,a={},o;try{o=await Promise.all(e.files.map(async e=>({url:e,content:await f(e)})))}catch(t){console.error(`Failed to install ${e.name}: ${t instanceof Error?t.message:t}`),process.exitCode=1;continue}for(let{url:e,content:t}of o){let o=u(n.outputDir,r,x(e),i);await c(o,t),a[o]=b(t)}for(let t of e.dependencies)ye.has(t)||_.add(t);p=g(p,e.name,{registry:S,version:e.version,installedAt:new Date().toISOString().slice(0,10),files:a});let s=t?``:` (dependency)`;console.log(`Added ${e.name} to ${n.outputDir}/${r}/${s}`)}if(D.size>0){let e=be(i),t=[...D].filter(t=>e[t]===void 0);r.dryRun?console.log(`Dry run — would install npm package(s): ${[...D].join(`, `)}`):t.length>0&&(r.noInstall?console.log(`\nSkipped install (--no-install). Install with:\n ${a(o,t)}`):B(t,i,{pm:o})||(process.exitCode=1)),O.length>0&&console.log(`\nImport it:\n${O.join(`
5
+ `)}`)}if(_.size>0&&!r.dryRun&&(r.noInstall?console.log(`\nSkipped install (--no-install). Install with:\n ${a(o,[..._])}`):B([..._],i,{pm:o})),Object.keys(E).length>0){let e=await te(i,E);for(let t of e)console.error(`\nWarning: ${t.pkg} ${t.installed?`${t.installed} is`:`is not`} installed, but the copied component source needs ${t.pkg} ${t.required}. Run: ${a(o,[`${t.pkg}@latest`])}`),process.exitCode=1}await m(p,i),xe(i,o)}async function ke(t,n,r,i={}){let o={},s=new Set,l=L(t),d=t.name.includes(`/`)?t.name.split(`/`).pop():t.name,p=await Promise.all((t.files??[]).map(async e=>({file:e,content:await f(e.url)})));for(let{file:e,content:t}of p){let i=l&&e.target?Se(r,e.target):u(n.outputDir,d,x(e.url),r);await c(i,t),o[i]=b(t)}for(let e of t.dependencies??[])ye.has(e)||s.add(e);if(s.size>0&&(i.noInstall?console.log(`\nSkipped install (--no-install). Install with:\n ${a(i.pm??e(r),[...s])}`):B([...s],r,i.pm?{pm:i.pm}:{})),l){let e=t.files?.filter(e=>e.target).map(e=>e.target)??[];console.log(`Installed template ${t.name} (${e.length} files)`)}else console.log(`Added ${t.name} to ${n.outputDir}/${d}/`);return o}const K={"@cascivo/react":`1.2.0`,"@cascivo/themes":`1.0.0`,"@cascivo/charts":`1.2.0`,"@cascivo/icons":`1.1.0`,"@cascivo/eslint-config":`0.4.0`};function Ae(e){return e===`yarn`?`yarn`:`${e} install`}function je(e,t){return e===`npm`?`npm run ${t}`:`${e} ${t}`}function Me(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``)||`section`}function Ne(e){let t=e.trim().split(/[^a-zA-Z0-9]+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(``)||`Section`;return/^[0-9]/.test(t)?`Section${t}`:t}function Pe(e){let t=e.trim().split(/[^a-zA-Z0-9]+/).filter(e=>e!==``&&!/^\d+$/.test(e)&&!/^v\d+$/i.test(e)),n=[];for(let e of t){if(n.length>=3||n.length>0&&n.join(` `).length+1+e.length>24)break;n.push(e.charAt(0).toUpperCase()+e.slice(1))}return n.join(` `)||`App`}function Fe(e){return e.trim().toLowerCase().replace(/[^a-z0-9._-]+/g,`-`).replace(/^-+|-+$/g,``)||`cascivo-app`}function Ie(e){let t=new Set,n=new Set,r=[];for(let i of e){let e=i.trim();if(!e)continue;let a=Me(e),o=Ne(e),s=2;for(;t.has(a)||n.has(o);)a=`${Me(e)}-${s}`,o=`${Ne(e)}${s}`,s++;t.add(a),n.add(o),r.push({key:a,label:e,component:o})}return r.length>0?r:[{key:`home`,label:`Home`,component:`Home`}]}function Le(e){let t={name:Fe(e.name),private:!0,version:`0.0.0`,type:`module`,scripts:{dev:`vite`,build:`tsc && vite build`,preview:`vite preview`,typecheck:`tsc --noEmit`,lint:`eslint .`,format:`prettier --write .`,"format:check":`prettier --check .`},dependencies:{"@cascivo/react":K[`@cascivo/react`],"@cascivo/themes":K[`@cascivo/themes`],"@preact/signals-react":`>=3.0.0`,react:`^19.0.0`,"react-dom":`^19.0.0`},devDependencies:{"@cascivo/eslint-config":K[`@cascivo/eslint-config`],"@eslint/js":`^9.0.0`,"@types/react":`^19.0.0`,"@types/react-dom":`^19.0.0`,"@vitejs/plugin-react":`^5.0.0`,eslint:`^9.0.0`,"eslint-plugin-react-hooks":`^7.0.0`,prettier:`^3.0.0`,typescript:`^5.7.0`,"typescript-eslint":`^8.0.0`,vite:`^7.0.0`}};return JSON.stringify(t,null,2)+`
6
+ `}function Re(){return JSON.stringify({compilerOptions:{target:`ES2022`,useDefineForClassFields:!0,lib:[`ES2022`,`DOM`,`DOM.Iterable`],module:`ESNext`,skipLibCheck:!0,moduleResolution:`bundler`,allowImportingTsExtensions:!0,resolveJsonModule:!0,isolatedModules:!0,moduleDetection:`force`,noEmit:!0,jsx:`react-jsx`,strict:!0,noUnusedLocals:!0,noUnusedParameters:!0,noFallthroughCasesInSwitch:!0},include:[`src`]},null,2)+`
7
+ `}function ze(){return`import react from '@vitejs/plugin-react'
8
8
  import { defineConfig } from 'vite'
9
9
 
10
10
  export default defineConfig({
11
11
  plugins: [react()],
12
12
  })
13
- `}function Ve(e){return`<!doctype html>
13
+ `}function Be(e){return`<!doctype html>
14
14
  <html lang="en" data-theme="${e.theme}">
15
15
  <head>
16
16
  <meta charset="UTF-8" />
17
17
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
18
- <title>${Fe(e.name)}</title>
18
+ <title>${Pe(e.name)}</title>
19
19
  <style>
20
20
  @layer vendor, cascivo.reset, cascivo.base, cascivo.tokens, cascivo.component,
21
21
  cascivo.platform, cascivo.theme, cascivo.blocks, cascivo.example, cascivo.override;
@@ -49,7 +49,7 @@ export default defineConfig({
49
49
  <script type="module" src="/src/main.tsx"><\/script>
50
50
  </body>
51
51
  </html>
52
- `}function He(){return`import React from 'react'
52
+ `}function Ve(){return`import React from 'react'
53
53
  import ReactDOM from 'react-dom/client'
54
54
  import App from './App'
55
55
 
@@ -61,8 +61,8 @@ if (root) {
61
61
  </React.StrictMode>,
62
62
  )
63
63
  }
64
- `}function Ue(){return`/// <reference types="vite/client" />
65
- `}function We(e){let t=e.map(e=>`import { ${e.component} } from './sections/${e.component}'`).join(`
64
+ `}function He(){return`/// <reference types="vite/client" />
65
+ `}function Ue(e){let t=e.map(e=>`import { ${e.component} } from './sections/${e.component}'`).join(`
66
66
  `),n=e.map(e=>`'${e.key}'`).join(` | `),r=e.map(e=>` {
67
67
  label: '${e.label.replace(/'/g,`\\'`)}',
68
68
  active: section.value === '${e.key}',
@@ -94,7 +94,7 @@ ${i}
94
94
  </Shell>
95
95
  )
96
96
  }
97
- `}function Ge(e){return`'use client'
97
+ `}function We(e){return`'use client'
98
98
  import { AppShell, ShellHeader, SideNav, type SideNavItem } from '@cascivo/react'
99
99
  import type { ReactNode } from 'react'
100
100
 
@@ -125,14 +125,14 @@ export interface ShellProps {
125
125
  export function Shell({ navItems, children }: ShellProps) {
126
126
  return (
127
127
  <AppShell
128
- header={<ShellHeader brand={{ name: '${Fe(e.name).replace(/'/g,`\\'`)}' }} />}
128
+ header={<ShellHeader brand={{ name: '${Pe(e.name).replace(/'/g,`\\'`)}' }} />}
129
129
  nav={<SideNav items={navItems} />}
130
130
  >
131
131
  {children}
132
132
  </AppShell>
133
133
  )
134
134
  }
135
- `}function Ke(e){return`import { Card, CardContent, CardHeader, CardTitle, Flex, Heading, Text } from '@cascivo/react'
135
+ `}function Ge(e){return`import { Card, CardContent, CardHeader, CardTitle, Flex, Heading, Text } from '@cascivo/react'
136
136
 
137
137
  export function ${e.component}() {
138
138
  return (
@@ -158,7 +158,7 @@ export function ${e.component}() {
158
158
  </Flex>
159
159
  )
160
160
  }
161
- `}function qe(){return`import js from '@eslint/js'
161
+ `}function Ke(){return`import js from '@eslint/js'
162
162
  import tseslint from 'typescript-eslint'
163
163
  import reactHooks from 'eslint-plugin-react-hooks'
164
164
  import cascivo from '@cascivo/eslint-config'
@@ -180,17 +180,17 @@ export default [
180
180
  // see https://cascivo.com/docs/using-with-strict-eslint.md
181
181
  ...cascivo,
182
182
  ]
183
- `}function Je(){return JSON.stringify({semi:!1,singleQuote:!0,printWidth:100},null,2)+`
184
- `}function Ye(){return`dist
183
+ `}function qe(){return JSON.stringify({semi:!1,singleQuote:!0,printWidth:100},null,2)+`
184
+ `}function Je(){return`dist
185
185
  node_modules
186
186
  # Vendored cascivo source is formatted upstream — reformatting it makes every
187
187
  # \`cascivo add\` update a merge conflict.
188
188
  src/components/ui/
189
- `}function Xe(){return`node_modules
189
+ `}function Ye(){return`node_modules
190
190
  dist
191
191
  *.local
192
192
  .DS_Store
193
- `}function Ze(e){let t=e.pm??`npm`;return`# ${e.name}
193
+ `}function Xe(e){let t=e.pm??`npm`;return`# ${e.name}
194
194
 
195
195
  A [cascivo](https://cascivo.com) app — Vite + React + TypeScript, pre-wired with
196
196
  the cascivo app shell, side navigation, and the \`${e.theme}\` theme.
@@ -198,8 +198,8 @@ the cascivo app shell, side navigation, and the \`${e.theme}\` theme.
198
198
  ## Develop
199
199
 
200
200
  \`\`\`sh
201
- ${je(t)}
202
- ${Me(t,`dev`)}
201
+ ${Ae(t)}
202
+ ${je(t,`dev`)}
203
203
  \`\`\`
204
204
 
205
205
  ## Structure
@@ -232,7 +232,7 @@ setLinkComponent(({ href, ...rest }: LinkComponentProps) => <Link to={href ?? '#
232
232
  That one call makes \`SideNav\`, \`ShellHeader\` and \`Breadcrumb\` render real router links.
233
233
  Links you write in page content use \`<Link asChild>\` instead — two kinds of link, two
234
234
  mechanisms. Full recipe: https://cascivo.com/docs/using-with-a-router.md
235
- `}function Qe(e){return`# Agent instructions — ${e.name}
235
+ `}function Ze(e){return`# Agent instructions — ${e.name}
236
236
 
237
237
  This is a [cascivo](https://cascivo.com) app. When generating or editing CSS, follow
238
238
  the **layer contract** — cascivo styles live in cascade layers, and layer order beats
@@ -299,13 +299,13 @@ The vocabulary types are on a subpath: \`import type { Tone } from '@cascivo/rea
299
299
  add \`@cascivo/core\` to this app's dependencies — it is transitive here.
300
300
 
301
301
  More: cascivo's machine-readable guide is at https://cascivo.com/llms.txt.
302
- `}function $e(e){let t=Le(e.sections);return[{path:`package.json`,contents:Re(e)},{path:`tsconfig.json`,contents:ze()},{path:`vite.config.ts`,contents:Be()},{path:`index.html`,contents:Ve(e)},{path:`eslint.config.js`,contents:qe()},{path:`.prettierrc`,contents:Je()},{path:`.prettierignore`,contents:Ye()},{path:`.gitignore`,contents:Xe()},{path:`README.md`,contents:Ze(e)},{path:`AGENTS.md`,contents:Qe(e)},{path:`src/main.tsx`,contents:He()},{path:`src/vite-env.d.ts`,contents:Ue()},{path:`src/App.tsx`,contents:We(t)},{path:`src/Shell.tsx`,contents:Ge(e)},...t.map(e=>({path:`src/sections/${e.component}.tsx`,contents:Ke(e)}))]}const et=[`Dashboard`,`Reports`,`Settings`];async function tt(t,n=process.cwd()){let r=t.includes(`--yes`)||t.includes(`-y`),i=Ae(t,[`pm`,`package-manager`,`theme`,`sections`,`template`])[0],o=W(t,`theme`),s=W(t,`sections`),c=G(t);if(`error`in c){console.error(c.error),process.exitCode=1;return}let u=e(n,{preferLockfileOverUserAgent:!0,...c.pm?{override:c.pm}:{}}),d=!r&&T.isTTY?F({input:T,output:E}):null;try{let e=i;!e&&d&&(e=(await d.question(`Project name? [my-cascivo-app]: `)).trim()),e||=`my-cascivo-app`;let r=(o??``).toLowerCase();!a.includes(r)&&d&&(r=(await d.question(`Theme? (${a.join(`/`)}) [light]: `)).trim().toLowerCase());let c=a.includes(r)?r:`light`,f=s;!f&&d&&(f=(await d.question(`Nav sections? (comma-separated) [${et.join(`, `)}]: `)).trim());let p=(f??``).split(`,`).map(e=>e.trim()).filter(Boolean),m={name:e,theme:c,sections:p.length>0?p:et,pm:u},h=j(n,e);if(D(h)&&k(h).length>0){console.error(`Target directory "${e}" already exists and is not empty.`),process.exitCode=1;return}let g=$e(m);for(let e of g)await l(j(h,e.path),e.contents);console.log(`\nCreated ${e} with the ${c} theme (${g.length} files).`);let _=W(t,`template`);if(_){let{add:e}=await Promise.resolve().then(()=>Ce),{loadConfig:t}=await import(`./config-DT-Cs7bC.mjs`).then(e=>e.i);console.log(`\nInstalling template "${_}"…`),await e([_],await t(),{cwd:h,pm:u})}console.log(`
303
- Next steps:`),console.log(` cd ${e}`),console.log(` ${je(u)}`),console.log(` ${Me(u,`dev`)}`),console.log(`
302
+ `}function Qe(e){let t=Ie(e.sections);return[{path:`package.json`,contents:Le(e)},{path:`tsconfig.json`,contents:Re()},{path:`vite.config.ts`,contents:ze()},{path:`index.html`,contents:Be(e)},{path:`eslint.config.js`,contents:Ke()},{path:`.prettierrc`,contents:qe()},{path:`.prettierignore`,contents:Je()},{path:`.gitignore`,contents:Ye()},{path:`README.md`,contents:Xe(e)},{path:`AGENTS.md`,contents:Ze(e)},{path:`src/main.tsx`,contents:Ve()},{path:`src/vite-env.d.ts`,contents:He()},{path:`src/App.tsx`,contents:Ue(t)},{path:`src/Shell.tsx`,contents:We(e)},...t.map(e=>({path:`src/sections/${e.component}.tsx`,contents:Ge(e)}))]}const $e=[`Dashboard`,`Reports`,`Settings`];async function et(t,n=process.cwd()){let r=t.includes(`--yes`)||t.includes(`-y`),a=w(t,[`pm`,`package-manager`,`theme`,`sections`,`template`])[0],o=E(t,`theme`),s=E(t,`sections`),l=T(t);if(`error`in l){console.error(l.error),process.exitCode=1;return}let u=e(n,{preferLockfileOverUserAgent:!0,...l.pm?{override:l.pm}:{}}),d=!r&&k.isTTY?z({input:k,output:A}):null;try{let e=a;!e&&d&&(e=(await d.question(`Project name? [my-cascivo-app]: `)).trim()),e||=`my-cascivo-app`;let r=(o??``).toLowerCase();!i.includes(r)&&d&&(r=(await d.question(`Theme? (${i.join(`/`)}) [light]: `)).trim().toLowerCase());let l=i.includes(r)?r:`light`,f=s;!f&&d&&(f=(await d.question(`Nav sections? (comma-separated) [${$e.join(`, `)}]: `)).trim());let p=(f??``).split(`,`).map(e=>e.trim()).filter(Boolean),m={name:e,theme:l,sections:p.length>0?p:$e,pm:u},h=F(n,e);if(j(h)&&N(h).length>0){console.error(`Target directory "${e}" already exists and is not empty.`),process.exitCode=1;return}let g=Qe(m);for(let e of g)await c(F(h,e.path),e.contents);console.log(`\nCreated ${e} with the ${l} theme (${g.length} files).`);let _=E(t,`template`);if(_){let{add:e}=await Promise.resolve().then(()=>ve),{loadConfig:t}=await import(`./config-DT-Cs7bC.mjs`).then(e=>e.i);console.log(`\nInstalling template "${_}"…`),await e([_],await t(),{cwd:h,pm:u})}console.log(`
303
+ Next steps:`),console.log(` cd ${e}`),console.log(` ${Ae(u)}`),console.log(` ${je(u,`dev`)}`),console.log(`
304
304
  Good to know:`),console.log(` No cascivo.config.ts is written — this app uses the prebuilt @cascivo/react`),console.log(" packages and never copies source. `cascivo add <component>` writes the"),console.log(` config itself the first time you vendor a component.`),console.log(`
305
- Adding a router? Keep src/Shell.tsx, delete src/App.tsx + src/sections/,`),console.log(` and register your Link once with setLinkComponent — see`),console.log(` https://cascivo.com/docs/using-with-a-router.md`)}finally{d?.close()}}var nt=c({checkDuplicateCore:()=>pt,checkFormatterIgnore:()=>_t,checkProjectDependencies:()=>dt,checkSignalsCompat:()=>mt,checkSsrConfig:()=>vt,detectInstallPath:()=>ct,runDoctor:()=>xt,stripCommentsAndStrings:()=>bt});const rt=[`@cascivo/core`,`@cascivo/tokens`,`@cascivo/themes`,`@preact/signals-react`],it=[`@cascivo/react`,`@cascivo/themes`,`@preact/signals-react`],at={"@cascivo/core":`AI-RULES.md: never add @cascivo/core to a prebuilt-path app — it is transitive, and everything is re-exported from @cascivo/react`,"@cascivo/tokens":`GETTING-STARTED.md: @cascivo/tokens comes with @cascivo/themes as a direct dependency — never install it by hand`},ot=[`@cascivo/i18n`,`@cascivo/charts`],st=[`cascivo.config.ts`,`cascivo.config.js`,`cascivo.config.mjs`];function ct(e){let t={},n=!0;try{let n=JSON.parse(O(j(e,`package.json`),`utf8`));t={...n.dependencies,...n.devDependencies}}catch{n=!1}if(!n)return`unknown`;let r=t[`@cascivo/react`]!==void 0,i=lt(e);return r&&i?`hybrid`:r?`prebuilt`:i?`copied`:`unknown`}function lt(e){for(let t of ut(e)){let n=j(e,t);if(D(n))try{if(k(n).some(e=>e.endsWith(`.tsx`)))return!0}catch{}}return!1}function ut(e){let t=new Set([`src/components/ui`]);for(let n of st){let r=j(e,n);if(D(r))try{let e=/outputDir\s*:\s*['"]([^'"]+)['"]/.exec(O(r,`utf8`))?.[1];e!==void 0&&t.add(e)}catch{}}return[...t]}function dt(t){let n={};try{let e=JSON.parse(O(j(t,`package.json`),`utf8`));n={...e.dependencies,...e.devDependencies}}catch{return[]}let r=ct(t);if(r===`unknown`)return[];let i=e(t),a=[],s=r===`prebuilt`?it:rt;for(let e of s)n[e]===void 0&&a.push({package:e,required:!0,hint:`detected ${r} install path — ${o(i,[e])}`});if(r===`prebuilt`)for(let[e,t]of Object.entries(at))n[e]!==void 0&&a.push({package:e,required:!0,kind:`forbidden`,hint:t});for(let e of ot)n[e]===void 0&&a.push({package:e,required:!1,hint:o(i,[e])});return a}const ft=[`@cascivo/react`,`@cascivo/charts`,`@cascivo/flow`,`@cascivo/editor`];async function pt(e){let t=await S(e,`@cascivo/core`),n=[];for(let r of ft){let i=j(e,`node_modules`,r);if(!D(i))continue;let a=await S(i,`@cascivo/core`);a!==null&&a!==t&&n.push(`${r} → ${a}`)}return n.length===0?null:{root:t,nested:n,hint:`Align the @cascivo/* versions (they are released together — see breaking-changes.json) and reinstall. Two copies of @cascivo/core means two signal registries: writes through one are invisible to components subscribed through the other, with no error.`}}async function mt(t){let[n,r]=await Promise.all([S(t,`@preact/signals-react`),S(t,`react`)]);if(n===null)return null;let i;try{i=ne(n,`3.0.0`)<0}catch{return null}if(!i)return null;let a=o(e(t),[`@preact/signals-react@^3`]),s=null;if(r!==null){let e=/^(\d+)\./.exec(r.trim());s=e?Number(e[1]):null}return s!==null&&s>=19?{severity:`error`,detail:`@preact/signals-react ${n} cannot run on React ${r} — React 19 removed an internal signals 2.x imports, so it fails at module load. Upgrade to signals-react 3.x.`,hint:a}:{severity:`warning`,detail:`@preact/signals-react ${n} is below the required 3.x floor. It works on React 18 today but breaks the moment you move to React 19; upgrade now.`,hint:a}}const ht=[`@tanstack/react-start`,`vite-ssr`,`@remix-run/dev`],gt=[`vite.config.ts`,`vite.config.js`,`vite.config.mjs`,`vite.config.mts`];function _t(e,t){let n=[{configs:[`.prettierrc`,`.prettierrc.json`,`.prettierrc.js`,`prettier.config.js`],ignore:`.prettierignore`},{configs:[`.oxfmtrc`,`.oxfmtrc.json`],ignore:`.oxfmtignore`}],r=t.replace(/\/+$/,``);for(let{configs:t,ignore:i}of n){if(!t.some(t=>D(j(e,t))))continue;let n=``;try{n=O(j(e,i),`utf8`)}catch{}if(!n.split(`
306
- `).some(e=>e.trim().replace(/\/+$/,``)===r))return`"${r}/" is not excluded from your formatter. Running it will rewrite code you own, and \`cascivo update\` will then report drift on files you never edited. Add "${r}/" to ${i}.`}return null}function vt(e){let t={};try{let n=JSON.parse(O(j(e,`package.json`),`utf8`));t={...n.dependencies,...n.devDependencies}}catch{return null}let n=ht.find(e=>t[e]!==void 0);if(n===void 0)return null;for(let t of gt){let n=j(e,t);if(!D(n))continue;let r=O(n,`utf8`);if(/noExternal/.test(r)&&/@cascivo/.test(r)||/cascivoSsr/.test(r))return null}return`${n} is a Vite SSR framework, but no vite config marks the cascivo packages ssr.noExternal — an unconfigured SSR build crashes with \`Unknown file extension ".css"\`. Add \`ssr: { noExternal: [/^@cascivo\\//] }\` (or the cascivoSsr() plugin from @cascivo/vite-plugin). Recipe: https://cascivo.com/docs/using-with-vite-ssr.md`}const yt=[`useState`,`useEffect`,`useLayoutEffect`,`useContext`,`useReducer`];function bt(e){let t=``,n=0,r=e.length;for(;n<r;){let i=e[n],a=e[n+1];if(i===`/`&&a===`/`)for(;n<r&&e[n]!==`
307
- `;)n++;else if(i===`/`&&a===`*`){for(n+=2;n<r&&!(e[n]===`*`&&e[n+1]===`/`);)n++;n+=2}else if(i===`'`||i===`"`){for(n++;n<r&&e[n]!==i;)e[n]===`\\`&&n++,n++;n++,t+=i+i}else if(i==="`"){for(n++;n<r&&e[n]!=="`";)e[n]===`\\`&&n++,n++;n++,t+="``"}else t+=i,n++}return t}async function xt(e){let t=[],n=j(e,`packages`,`components`,`src`);if(!D(n))return{violations:[],passed:!0};let r=k(n,{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name),i=j(e,`packages`,`react`,`src`,`index.ts`),a=D(i)?O(i,`utf8`):``;for(let e of r){let r=j(n,e,`${e}.tsx`);if(!D(r))continue;let i=O(r,`utf8`),o=bt(i);for(let n of yt)RegExp(`\\b${n}\\b`).test(o)&&t.push({file:r,rule:`no-react-hooks`,detail:`Banned hook '${n}' in ${e}.tsx`});/aria-label="[A-Z]/.test(i)&&t.push({file:r,rule:`no-hardcoded-strings`,detail:`Hardcoded aria-label in ${e}.tsx — use t(builtin.*)`}),RegExp(`from.*components/src/${e}`).test(a)||t.push({file:r,rule:`missing-react-export`,detail:`${e} not exported from @cascivo/react`})}return{violations:t,passed:t.length===0}}function St(e){return typeof e==`object`&&!!e&&`$t`in e}function Ct(e){return typeof e==`string`?`'${e.replace(/'/g,`\\'`)}'`:String(e)}function wt(e){return typeof e==`string`?`"${e.replace(/"/g,`\\"`)}"`:typeof e==`number`||typeof e==`boolean`?`{${e}}`:e===null?`{null}`:St(e)?`{"${e.$t}"} {/* i18n: ${e.$t} */}`:`{${JSON.stringify(e)}}`}function Tt(e,t,n,r){return e===void 0?``:typeof e==`string`?e:St(e)?`{/* i18n: ${e.$t} */}`:Array.isArray(e)?e.map(e=>Et(e,t+` `,n,r)).join(`
308
- `):``}function Et(e,t,n,r){let i=e.component;r.add(i);let a=[];if(e.props)for(let[t,n]of Object.entries(e.props))a.push(`${t}=${wt(n)}`);if(e.bind)for(let[t,n]of Object.entries(e.bind))n.startsWith(`$state.`)?a.push(`${t}={${n.slice(7)}.value}`):a.push(`${t}={data.${n.replace(/^\$data\./,``)}}`);if(e.events)for(let[t,n]of Object.entries(e.events))if(n.startsWith(`$state.set.`)){let e=n.slice(11);a.push(`${t}={(e) => (${e}.value = coerceValue(e))}`)}else if(n.startsWith(`$state.toggle.`)){let e=n.slice(14);a.push(`${t}={() => (${e}.value = !${e}.value)}`)}else a.push(`${t}={actions.${n.replace(/^\$actions\./,``)}}`);let o=a.length>0?` `+a.join(` `):``,s=e.children===void 0?void 0:Tt(e.children,t,[],r);return s!==void 0&&s!==``?`${t}<${i}${o}>\n${t} ${s}\n${t}</${i}>`:`${t}<${i}${o} />`}function Dt(e){let t=[],n=[];function r(e){if(e.bind)for(let n of Object.values(e.bind)){if(n.startsWith(`$state.`))continue;let e=n.replace(/^\$data\./,``);t.includes(e)||t.push(e)}if(e.events)for(let t of Object.values(e.events)){if(t.startsWith(`$state.`))continue;let e=t.replace(/^\$actions\./,``);n.includes(e)||n.push(e)}Array.isArray(e.children)&&e.children.forEach(r)}return e.forEach(r),{boundProps:t,actionProps:n}}function Ot(e){let t=e=>e.events&&Object.values(e.events).some(e=>e.startsWith(`$state.set.`))?!0:Array.isArray(e.children)?e.children.some(t):!1;return e.some(t)}function kt(e,t,n){let r=new Set,i=Object.values(e.view.regions).flat(),{boundProps:a,actionProps:o}=Dt(i),s=Object.entries(e.state??{}),c=s.length>0,l=Ot(i),u=Object.entries(e.view.regions).map(([e,t])=>` <div className="region-${e}">\n${t.map(e=>Et(e,` `,[],r)).join(`
305
+ Adding a router? Keep src/Shell.tsx, delete src/App.tsx + src/sections/,`),console.log(` and register your Link once with setLinkComponent — see`),console.log(` https://cascivo.com/docs/using-with-a-router.md`)}finally{d?.close()}}var tt=s({checkDuplicateCore:()=>ft,checkFormatterIgnore:()=>gt,checkProjectDependencies:()=>ut,checkSignalsCompat:()=>pt,checkSsrConfig:()=>_t,detectInstallPath:()=>st,runDoctor:()=>bt,stripCommentsAndStrings:()=>yt});const nt=[`@cascivo/core`,`@cascivo/tokens`,`@cascivo/themes`,`@preact/signals-react`],rt=[`@cascivo/react`,`@cascivo/themes`,`@preact/signals-react`],it={"@cascivo/core":`AI-RULES.md: never add @cascivo/core to a prebuilt-path app — it is transitive, and everything is re-exported from @cascivo/react`,"@cascivo/tokens":`GETTING-STARTED.md: @cascivo/tokens comes with @cascivo/themes as a direct dependency — never install it by hand`},at=[`@cascivo/i18n`,`@cascivo/charts`],ot=[`cascivo.config.ts`,`cascivo.config.js`,`cascivo.config.mjs`];function st(e){let t={},n=!0;try{let n=JSON.parse(M(F(e,`package.json`),`utf8`));t={...n.dependencies,...n.devDependencies}}catch{n=!1}if(!n)return`unknown`;let r=t[`@cascivo/react`]!==void 0,i=ct(e);return r&&i?`hybrid`:r?`prebuilt`:i?`copied`:`unknown`}function ct(e){for(let t of lt(e)){let n=F(e,t);if(j(n))try{if(N(n).some(e=>e.endsWith(`.tsx`)))return!0}catch{}}return!1}function lt(e){let t=new Set([`src/components/ui`]);for(let n of ot){let r=F(e,n);if(j(r))try{let e=/outputDir\s*:\s*['"]([^'"]+)['"]/.exec(M(r,`utf8`))?.[1];e!==void 0&&t.add(e)}catch{}}return[...t]}function ut(t){let n={};try{let e=JSON.parse(M(F(t,`package.json`),`utf8`));n={...e.dependencies,...e.devDependencies}}catch{return[]}let r=st(t);if(r===`unknown`)return[];let i=e(t),o=[],s=r===`prebuilt`?rt:nt;for(let e of s)n[e]===void 0&&o.push({package:e,required:!0,hint:`detected ${r} install path — ${a(i,[e])}`});if(r===`prebuilt`)for(let[e,t]of Object.entries(it))n[e]!==void 0&&o.push({package:e,required:!0,kind:`forbidden`,hint:t});for(let e of at)n[e]===void 0&&o.push({package:e,required:!1,hint:a(i,[e])});return o}const dt=[`@cascivo/react`,`@cascivo/charts`,`@cascivo/flow`,`@cascivo/editor`];async function ft(e){let t=await S(e,`@cascivo/core`),n=[];for(let r of dt){let i=F(e,`node_modules`,r);if(!j(i))continue;let a=await S(i,`@cascivo/core`);a!==null&&a!==t&&n.push(`${r} → ${a}`)}return n.length===0?null:{root:t,nested:n,hint:`Align the @cascivo/* versions (they are released together — see breaking-changes.json) and reinstall. Two copies of @cascivo/core means two signal registries: writes through one are invisible to components subscribed through the other, with no error.`}}async function pt(t){let[n,r]=await Promise.all([S(t,`@preact/signals-react`),S(t,`react`)]);if(n===null)return null;let i;try{i=C(n,`3.0.0`)<0}catch{return null}if(!i)return null;let o=a(e(t),[`@preact/signals-react@^3`]),s=null;if(r!==null){let e=/^(\d+)\./.exec(r.trim());s=e?Number(e[1]):null}return s!==null&&s>=19?{severity:`error`,detail:`@preact/signals-react ${n} cannot run on React ${r} — React 19 removed an internal signals 2.x imports, so it fails at module load. Upgrade to signals-react 3.x.`,hint:o}:{severity:`warning`,detail:`@preact/signals-react ${n} is below the required 3.x floor. It works on React 18 today but breaks the moment you move to React 19; upgrade now.`,hint:o}}const mt=[`@tanstack/react-start`,`vite-ssr`,`@remix-run/dev`],ht=[`vite.config.ts`,`vite.config.js`,`vite.config.mjs`,`vite.config.mts`];function gt(e,t){let n=[{configs:[`.prettierrc`,`.prettierrc.json`,`.prettierrc.js`,`prettier.config.js`],ignore:`.prettierignore`},{configs:[`.oxfmtrc`,`.oxfmtrc.json`],ignore:`.oxfmtignore`}],r=t.replace(/\/+$/,``);for(let{configs:t,ignore:i}of n){if(!t.some(t=>j(F(e,t))))continue;let n=``;try{n=M(F(e,i),`utf8`)}catch{}if(!n.split(`
306
+ `).some(e=>e.trim().replace(/\/+$/,``)===r))return`"${r}/" is not excluded from your formatter. Running it will rewrite code you own, and \`cascivo update\` will then report drift on files you never edited. Add "${r}/" to ${i}.`}return null}function _t(e){let t={};try{let n=JSON.parse(M(F(e,`package.json`),`utf8`));t={...n.dependencies,...n.devDependencies}}catch{return null}let n=mt.find(e=>t[e]!==void 0);if(n===void 0)return null;for(let t of ht){let n=F(e,t);if(!j(n))continue;let r=M(n,`utf8`);if(/noExternal/.test(r)&&/@cascivo/.test(r)||/cascivoSsr/.test(r))return null}return`${n} is a Vite SSR framework, but no vite config marks the cascivo packages ssr.noExternal — an unconfigured SSR build crashes with \`Unknown file extension ".css"\`. Add \`ssr: { noExternal: [/^@cascivo\\//] }\` (or the cascivoSsr() plugin from @cascivo/vite-plugin). Recipe: https://cascivo.com/docs/using-with-vite-ssr.md`}const vt=[`useState`,`useEffect`,`useLayoutEffect`,`useContext`,`useReducer`];function yt(e){let t=``,n=0,r=e.length;for(;n<r;){let i=e[n],a=e[n+1];if(i===`/`&&a===`/`)for(;n<r&&e[n]!==`
307
+ `;)n++;else if(i===`/`&&a===`*`){for(n+=2;n<r&&!(e[n]===`*`&&e[n+1]===`/`);)n++;n+=2}else if(i===`'`||i===`"`){for(n++;n<r&&e[n]!==i;)e[n]===`\\`&&n++,n++;n++,t+=i+i}else if(i==="`"){for(n++;n<r&&e[n]!=="`";)e[n]===`\\`&&n++,n++;n++,t+="``"}else t+=i,n++}return t}async function bt(e){let t=[],n=F(e,`packages`,`components`,`src`);if(!j(n))return{violations:[],passed:!0};let r=N(n,{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name),i=F(e,`packages`,`react`,`src`,`index.ts`),a=j(i)?M(i,`utf8`):``;for(let e of r){let r=F(n,e,`${e}.tsx`);if(!j(r))continue;let i=M(r,`utf8`),o=yt(i);for(let n of vt)RegExp(`\\b${n}\\b`).test(o)&&t.push({file:r,rule:`no-react-hooks`,detail:`Banned hook '${n}' in ${e}.tsx`});/aria-label="[A-Z]/.test(i)&&t.push({file:r,rule:`no-hardcoded-strings`,detail:`Hardcoded aria-label in ${e}.tsx — use t(builtin.*)`}),RegExp(`from.*components/src/${e}`).test(a)||t.push({file:r,rule:`missing-react-export`,detail:`${e} not exported from @cascivo/react`})}return{violations:t,passed:t.length===0}}function xt(e){return typeof e==`object`&&!!e&&`$t`in e}function St(e){return typeof e==`string`?`'${e.replace(/'/g,`\\'`)}'`:String(e)}function Ct(e){return typeof e==`string`?`"${e.replace(/"/g,`\\"`)}"`:typeof e==`number`||typeof e==`boolean`?`{${e}}`:e===null?`{null}`:xt(e)?`{"${e.$t}"} {/* i18n: ${e.$t} */}`:`{${JSON.stringify(e)}}`}function wt(e,t,n,r){return e===void 0?``:typeof e==`string`?e:xt(e)?`{/* i18n: ${e.$t} */}`:Array.isArray(e)?e.map(e=>Tt(e,t+` `,n,r)).join(`
308
+ `):``}function Tt(e,t,n,r){let i=e.component;r.add(i);let a=[];if(e.props)for(let[t,n]of Object.entries(e.props))a.push(`${t}=${Ct(n)}`);if(e.bind)for(let[t,n]of Object.entries(e.bind))n.startsWith(`$state.`)?a.push(`${t}={${n.slice(7)}.value}`):a.push(`${t}={data.${n.replace(/^\$data\./,``)}}`);if(e.events)for(let[t,n]of Object.entries(e.events))if(n.startsWith(`$state.set.`)){let e=n.slice(11);a.push(`${t}={(e) => (${e}.value = coerceValue(e))}`)}else if(n.startsWith(`$state.toggle.`)){let e=n.slice(14);a.push(`${t}={() => (${e}.value = !${e}.value)}`)}else a.push(`${t}={actions.${n.replace(/^\$actions\./,``)}}`);let o=a.length>0?` `+a.join(` `):``,s=e.children===void 0?void 0:wt(e.children,t,[],r);return s!==void 0&&s!==``?`${t}<${i}${o}>\n${t} ${s}\n${t}</${i}>`:`${t}<${i}${o} />`}function Et(e){let t=[],n=[];function r(e){if(e.bind)for(let n of Object.values(e.bind)){if(n.startsWith(`$state.`))continue;let e=n.replace(/^\$data\./,``);t.includes(e)||t.push(e)}if(e.events)for(let t of Object.values(e.events)){if(t.startsWith(`$state.`))continue;let e=t.replace(/^\$actions\./,``);n.includes(e)||n.push(e)}Array.isArray(e.children)&&e.children.forEach(r)}return e.forEach(r),{boundProps:t,actionProps:n}}function Dt(e){let t=e=>e.events&&Object.values(e.events).some(e=>e.startsWith(`$state.set.`))?!0:Array.isArray(e.children)?e.children.some(t):!1;return e.some(t)}function Ot(e,t,n){let r=new Set,i=Object.values(e.view.regions).flat(),{boundProps:a,actionProps:o}=Et(i),s=Object.entries(e.state??{}),c=s.length>0,l=Dt(i),u=Object.entries(e.view.regions).map(([e,t])=>` <div className="region-${e}">\n${t.map(e=>Tt(e,` `,[],r)).join(`
309
309
  `)}\n </div>`).join(`
310
310
  `),d=[...r].sort().map(e=>`import { ${e} } from '${n}/${e.toLowerCase()}/${e.toLowerCase()}'`).join(`
311
311
  `),f=a.length>0,p=o.length>0,m=[f?`data: {\n${a.map(e=>` ${e.replace(/\./g,`_`)}: unknown // TODO: type your data`).join(`
@@ -320,7 +320,7 @@ function coerceValue(e: unknown) {
320
320
  return e
321
321
  }
322
322
  `:``,v=c?` useSignals()
323
- `+s.map(([e,t])=>` const ${e} = useSignal(${Ct(t)})`).join(`
323
+ `+s.map(([e,t])=>` const ${e} = useSignal(${St(t)})`).join(`
324
324
  `)+`
325
325
  `:``;return`import React from 'react'${g}
326
326
  ${d}
@@ -334,34 +334,34 @@ ${u}
334
334
  </div>
335
335
  )
336
336
  }
337
- `}async function At(e,t){let n=e.find((t,n)=>e[n-1]===`--out`),r=e.find(e=>!e.startsWith(`--`)),i=e.find((t,n)=>e[n-1]===`--components-dir`);if(!r){console.error(`Usage: cascivo generate <config.json> [--out output.tsx] [--components-dir ./src/components/ui]`),process.exitCode=1;return}let{readFileSync:a}=await import(`node:fs`),o=a(r,`utf-8`),s=kt(JSON.parse(o),new Map,i??t.outputDir??`./src/components/ui`),c=n??j(oe(r),`${ae(r,`.json`)}.tsx`);A(c,s,`utf-8`),console.log(`Generated ${c}`);try{let{spawnSync:e}=await import(`node:child_process`);e(`pnpm`,[`exec`,`vp`,`fmt`,c],{encoding:`utf8`,stdio:`inherit`}).status!==0&&e(`npx`,[`--yes`,`prettier`,`--write`,c],{encoding:`utf8`,stdio:`inherit`}).status!==0&&console.log(`(Note: formatter not found — output may need manual formatting)`)}catch{}}const q=[`@cascivo/core`,`@cascivo/tokens`,`@cascivo/themes`,`@preact/signals-react`],J=[`cascivo`];async function jt(){let e=F({input:T,output:E});try{let t=(await e.question(`Theme? (${a.join(`/`)}) [light]: `)).trim().toLowerCase();return a.includes(t)?t:`light`}finally{e.close()}}function Mt(e){return`import type { CascadeConfig } from 'cascivo'
337
+ `}async function kt(e,t){let n=e.find((t,n)=>e[n-1]===`--out`),r=e.find(e=>!e.startsWith(`--`)),i=e.find((t,n)=>e[n-1]===`--components-dir`);if(!r){console.error(`Usage: cascivo generate <config.json> [--out output.tsx] [--components-dir ./src/components/ui]`),process.exitCode=1;return}let{readFileSync:a}=await import(`node:fs`),o=a(r,`utf-8`),s=Ot(JSON.parse(o),new Map,i??t.outputDir??`./src/components/ui`),c=n??F(ie(r),`${re(r,`.json`)}.tsx`);P(c,s,`utf-8`),console.log(`Generated ${c}`);try{let{spawnSync:e}=await import(`node:child_process`);e(`pnpm`,[`exec`,`vp`,`fmt`,c],{encoding:`utf8`,stdio:`inherit`}).status!==0&&e(`npx`,[`--yes`,`prettier`,`--write`,c],{encoding:`utf8`,stdio:`inherit`}).status!==0&&console.log(`(Note: formatter not found — output may need manual formatting)`)}catch{}}const q=[`@cascivo/core`,`@cascivo/tokens`,`@cascivo/themes`,`@preact/signals-react`],J=[`cascivo`];async function At(){let e=z({input:k,output:A});try{let t=(await e.question(`Theme? (${i.join(`/`)}) [light]: `)).trim().toLowerCase();return i.includes(t)?t:`light`}finally{e.close()}}function jt(e){return`import type { CascadeConfig } from 'cascivo'
338
338
 
339
339
  const config: CascadeConfig = {
340
- registry: '${r.registry}',
341
- outputDir: '${r.outputDir}',
340
+ registry: '${n.registry}',
341
+ outputDir: '${n.outputDir}',
342
342
  theme: '${e}',
343
343
  }
344
344
 
345
345
  export default config
346
- `}const Nt=[`eslint.config.js`,`eslint.config.mjs`,`eslint.config.cjs`,`eslint.config.ts`,`.eslintrc`,`.eslintrc.js`,`.eslintrc.cjs`,`.eslintrc.json`,`.eslintrc.yml`,`.eslintrc.yaml`];function Pt(e){Nt.some(t=>D(j(e,t)))&&(console.log(`
347
- ESLint: copied cascivo source may trip a strict host config on stylistic rules.`),console.log(` Scope them off your components dir — see docs/USING-WITH-STRICT-ESLINT.md`))}function Ft(e,t,n){let r=j(e,`package.json`);if(!D(r))return[];let i;try{i=JSON.parse(O(r,`utf8`))}catch{return[]}let a=n.dev?`devDependencies`:`dependencies`,o=i[a]??={},s=[];for(let e of t)o[e]===void 0&&(o[e]=`latest`,s.push(e));return s.length===0?[]:(A(r,`${JSON.stringify(i,null,2)}\n`,`utf8`),s)}const It=[{config:[`.prettierrc`,`.prettierrc.json`,`.prettierrc.js`,`prettier.config.js`],ignore:`.prettierignore`},{config:[`.oxfmtrc`,`.oxfmtrc.json`],ignore:`.oxfmtignore`}];function Lt(e,t){let n=`${t.replace(/\/+$/,``)}/`;for(let{config:t,ignore:r}of It){if(!(t.some(t=>D(j(e,t)))||Rt(e,r)))continue;let i=j(e,r),a=D(i)?O(i,`utf8`):``;if(a.split(`
346
+ `}const Mt=[`eslint.config.js`,`eslint.config.mjs`,`eslint.config.cjs`,`eslint.config.ts`,`.eslintrc`,`.eslintrc.js`,`.eslintrc.cjs`,`.eslintrc.json`,`.eslintrc.yml`,`.eslintrc.yaml`];function Nt(e){Mt.some(t=>j(F(e,t)))&&(console.log(`
347
+ ESLint: copied cascivo source may trip a strict host config on stylistic rules.`),console.log(` Scope them off your components dir — see docs/USING-WITH-STRICT-ESLINT.md`))}function Pt(e,t,n){let r=F(e,`package.json`);if(!j(r))return[];let i;try{i=JSON.parse(M(r,`utf8`))}catch{return[]}let a=n.dev?`devDependencies`:`dependencies`,o=i[a]??={},s=[];for(let e of t)o[e]===void 0&&(o[e]=`latest`,s.push(e));return s.length===0?[]:(P(r,`${JSON.stringify(i,null,2)}\n`,`utf8`),s)}const Ft=[{config:[`.prettierrc`,`.prettierrc.json`,`.prettierrc.js`,`prettier.config.js`],ignore:`.prettierignore`},{config:[`.oxfmtrc`,`.oxfmtrc.json`],ignore:`.oxfmtignore`}];function It(e,t){let n=`${t.replace(/\/+$/,``)}/`;for(let{config:t,ignore:r}of Ft){if(!(t.some(t=>j(F(e,t)))||Lt(e,r)))continue;let i=F(e,r),a=j(i)?M(i,`utf8`):``;if(a.split(`
348
348
  `).some(e=>e.trim()===n))continue;let o=`# cascivo: vendored component source — you own it, so do not reformat it
349
- `;A(i,a===``?o+n+`
349
+ `;P(i,a===``?o+n+`
350
350
  `:`${a.replace(/\n*$/,`
351
- `)}\n${o}${n}\n`,`utf8`),console.log(`\nAdded "${n}" to ${r} (so your formatter does not rewrite copied source).`)}}function Rt(e,t){if(t!==`.prettierignore`)return!1;let n=j(e,`package.json`);if(!D(n))return!1;try{return`prettier`in JSON.parse(O(n,`utf8`))}catch{return!1}}function zt(){console.log(`
352
- Dependencies`),console.log(` runtime: ${q.join(` `)}`),console.log(` dev: ${J.join(` `)} (types for cascivo.config.ts)`),console.log(" on demand: @cascivo/i18n, @cascivo/charts — added by `cascivo add` when a component or chart needs them")}async function Bt(t=[],n=process.cwd()){let i=t.includes(`--yes`)||t.includes(`-y`),s=t.includes(`--no-install`),c=W(t,`theme`)?.toLowerCase();if(c!==void 0&&!a.includes(c)){console.error(`Unknown theme "${c}". Available: ${a.join(`, `)}`),process.exitCode=1;return}let u=G(t);if(`error`in u){console.error(u.error),process.exitCode=1;return}let d=e(n,u.pm?{override:u.pm}:{}),f=c===void 0&&!i&&T.isTTY,p=c??(f?await jt():`light`);if(await l(j(n,`cascivo.config.ts`),Mt(p)),console.log(`\nCreated cascivo.config.ts (theme: ${p})`),s)console.log(`
353
- Skipped install (--no-install). Install the dependencies yourself:`),console.log(` ${o(d,q)}`),console.log(` ${o(d,J,{dev:!0})}`);else{let e=I(q,n,{pm:d}),t=I(J,n,{pm:d,dev:!0});if(!e||!t){let r=[...e?[]:Ft(n,q,{dev:!1}),...t?[]:Ft(n,J,{dev:!0})];console.error(`
354
- Install failed — cascivo.config.ts was written but the packages are not installed.`),r.length>0?(console.error(`Wrote ${r.length} dependency entries to package.json: ${r.join(`, `)}`),console.error(`Recover with:\n ${d} install`)):console.error(`Recover with:\n ${o(d,q)}\n ${o(d,J,{dev:!0})}`),process.exitCode=1}}console.log(`
351
+ `)}\n${o}${n}\n`,`utf8`),console.log(`\nAdded "${n}" to ${r} (so your formatter does not rewrite copied source).`)}}function Lt(e,t){if(t!==`.prettierignore`)return!1;let n=F(e,`package.json`);if(!j(n))return!1;try{return`prettier`in JSON.parse(M(n,`utf8`))}catch{return!1}}function Rt(){console.log(`
352
+ Dependencies`),console.log(` runtime: ${q.join(` `)}`),console.log(` dev: ${J.join(` `)} (types for cascivo.config.ts)`),console.log(" on demand: @cascivo/i18n, @cascivo/charts — added by `cascivo add` when a component or chart needs them")}async function zt(t=[],r=process.cwd()){let o=t.includes(`--yes`)||t.includes(`-y`),s=t.includes(`--no-install`),l=E(t,`theme`)?.toLowerCase();if(l!==void 0&&!i.includes(l)){console.error(`Unknown theme "${l}". Available: ${i.join(`, `)}`),process.exitCode=1;return}let u=T(t);if(`error`in u){console.error(u.error),process.exitCode=1;return}let d=e(r,u.pm?{override:u.pm}:{}),f=l===void 0&&!o&&k.isTTY,p=l??(f?await At():`light`);if(await c(F(r,`cascivo.config.ts`),jt(p)),console.log(`\nCreated cascivo.config.ts (theme: ${p})`),s)console.log(`
353
+ Skipped install (--no-install). Install the dependencies yourself:`),console.log(` ${a(d,q)}`),console.log(` ${a(d,J,{dev:!0})}`);else{let e=B(q,r,{pm:d}),t=B(J,r,{pm:d,dev:!0});if(!e||!t){let n=[...e?[]:Pt(r,q,{dev:!1}),...t?[]:Pt(r,J,{dev:!0})];console.error(`
354
+ Install failed — cascivo.config.ts was written but the packages are not installed.`),n.length>0?(console.error(`Wrote ${n.length} dependency entries to package.json: ${n.join(`, `)}`),console.error(`Recover with:\n ${d} install`)):console.error(`Recover with:\n ${a(d,q)}\n ${a(d,J,{dev:!0})}`),process.exitCode=1}}console.log(`
355
355
  Stylesheets — import these once, in this order, in your entry file:`),console.log(` import '@cascivo/tokens' // primitive tokens — every --cascivo-* value`),console.log(` import '@cascivo/themes/${p}.css'${` `.repeat(Math.max(1,16-p.length))}// the ${p} theme's semantic values`),console.log(" // …then your component CSS (`cascivo add` writes .module.css beside each component)"),console.log(`
356
356
  Then set the theme on your root element:`),console.log(` <html data-theme="${p}">`),console.log(`
357
357
  Switching themes at runtime? Use a bundle instead of the single theme:`),console.log(` import '@cascivo/themes/light-dark.css' // light + dark — the common case`),console.log(` import '@cascivo/themes/all.css' // all twelve themes`),console.log(` import { ThemeProvider } from '@cascivo/core'`),console.log(`
358
358
  In YOUR components, when you read a signal during render:`),console.log(` import { useSignals } from '@cascivo/core'`),console.log(` function MyComponent() {`),console.log(` useSignals() // ← first statement, or the component never re-renders`),console.log(` return <span>{count.value}</span>`),console.log(` }`),console.log(`
359
- Adding a chart later? Charts ship as an npm package with their own stylesheet:`),console.log(" import '@cascivo/charts/styles.css' // `cascivo add <chart>` reminds you"),zt(),Lt(n,r.outputDir),Pt(n),console.log(`
359
+ Adding a chart later? Charts ship as an npm package with their own stylesheet:`),console.log(" import '@cascivo/charts/styles.css' // `cascivo add <chart>` reminds you"),Rt(),It(r,n.outputDir),Nt(r),console.log(`
360
360
  Packages version independently (changesets), so a low number means fewer`),console.log(`releases, not less finished. Pin exact versions, and before upgrading run:`),console.log(` cascivo doctor --drift # reads breaking-changes.json, reports API drift`),console.log(`
361
- Add components with: cascivo add <name>`)}const Vt={component:`Components`,layout:`Layouts (copy-paste)`,block:`Blocks (copy-paste)`,section:`Sections (copy-paste)`,chart:`Charts (npm: @cascivo/charts)`,flow:`Flow (npm: @cascivo/flow)`,editor:`Editor (npm: @cascivo/editor)`};function Ht(e){return e.deprecated?` ⚠ deprecated → ${e.deprecated.replacement}`:``}function Ut(e){let t=e.map(e=>[e.name,e.category,e.description+Ht(e)]),n=[`Name`,`Category`,`Description`],r=n.map((e,n)=>Math.max(e.length,...t.map(e=>(e[n]??``).length))),i=e=>e.map((e,t)=>e.padEnd(r[t]??e.length)).join(` `).trimEnd(),a=r.map(e=>`-`.repeat(e)).join(` `);return[i(n),a,...t.map(i)].join(`
362
- `)}function Wt(e){if(e.length===0)return`No components found.`;if(!e.some(e=>e.type!==void 0))return Ut(e);let t=new Map;for(let n of e){let e=n.type??`component`;t.has(e)||t.set(e,[]),t.get(e).push(n)}let n=[];for(let[e,r]of t){let t=Vt[e]??e;n.push(`${t}\n${Ut(r)}`)}return n.join(`
361
+ Add components with: cascivo add <name>`)}const Bt={component:`Components`,layout:`Layouts (copy-paste)`,block:`Blocks (copy-paste)`,section:`Sections (copy-paste)`,chart:`Charts (npm: @cascivo/charts)`,flow:`Flow (npm: @cascivo/flow)`,editor:`Editor (npm: @cascivo/editor)`};function Vt(e){return e.deprecated?` ⚠ deprecated → ${e.deprecated.replacement}`:``}function Ht(e){let t=e.map(e=>[e.name,e.category,e.description+Vt(e)]),n=[`Name`,`Category`,`Description`],r=n.map((e,n)=>Math.max(e.length,...t.map(e=>(e[n]??``).length))),i=e=>e.map((e,t)=>e.padEnd(r[t]??e.length)).join(` `).trimEnd(),a=r.map(e=>`-`.repeat(e)).join(` `);return[i(n),a,...t.map(i)].join(`
362
+ `)}function Ut(e){if(e.length===0)return`No components found.`;if(!e.some(e=>e.type!==void 0))return Ht(e);let t=new Map;for(let n of e){let e=n.type??`component`;t.has(e)||t.set(e,[]),t.get(e).push(n)}let n=[];for(let[e,r]of t){let t=Bt[e]??e;n.push(`${t}\n${Ht(r)}`)}return n.join(`
363
363
 
364
- `)}async function Gt(e,t={}){let n=(await b(e.registry)).components;t.installed&&(n=n.filter(t=>{let n=t.name.includes(`/`)?t.name.split(`/`).pop():t.name;return D(j(process.cwd(),e.outputDir,n))})),console.log(Wt(n)),console.log("\nTip: install any entry by its bare name — `cascivo add flex` resolves to `layout/flex`. Layout primitives (Flex, Grid, AutoGrid, Columns, Spacer, Center) replace inline-style layout; see docs/cookbooks/layout-and-spacing.md.")}function Kt(e){let t=[],n=[],r=new Set(e.items.map(e=>e.name));for(let i of e.items)if(N(i)){for(let e of me(i))t.push(`template "${i.name}": ${e}`);for(let e of i.registryDependencies??[])!(e.includes(`/`)||e.startsWith(`@`)||e.startsWith(`http`))&&!r.has(e)&&n.push(`template "${i.name}": component "${e}" is not in this index — it must resolve from another registry at install time`)}return{errors:t,warnings:n}}async function qt(e){let t=`cascade-registry.json`,n=`public/r`;for(let r=0;r<e.length;r++)e[r]===`--in`&&e[r+1]?(t=e[r+1],r++):e[r]===`--out`&&e[r+1]&&(n=e[r+1],r++);let r=M(t);if(!D(r)){console.error(`cascade registry build: file not found: ${r}`),process.exitCode=1;return}let i=JSON.parse(await ce(r,`utf8`));oe(r);let a=typeof i==`object`&&i&&i.schemaVersion===2?i:de(i),o=fe(a);if(o.warnings.length>0)for(let e of o.warnings)console.warn(`warn: ${e}`);if(!o.ok){for(let e of o.errors)console.error(`error: ${e}`);process.exitCode=1;return}let s=Kt(a);for(let e of s.warnings)console.warn(`warn: ${e}`);if(s.errors.length>0){for(let e of s.errors)console.error(`error: ${e}`);process.exitCode=1;return}let c=M(n);await ue(a,c),console.log(`cascade registry build: wrote static output to ${c}`)}function Jt(e,t){let n=t.toLowerCase();return e.name.toLowerCase().includes(n)||e.description.toLowerCase().includes(n)||e.tags.some(e=>e.toLowerCase().includes(n))}async function Yt(e,t){let n=e.indexOf(`--registry`),r=n===-1?void 0:e[n+1],i=e.filter((t,n)=>t!==`--registry`&&e[n-1]!==`--registry`).join(` `).trim();if(!i){console.error(`Usage: cascivo search <query> [--registry @ns]`);return}let a=[];if(!r){let e=await b(t.registry);for(let t of e.components)Jt(t,i)&&a.push({name:t.name,version:t.version,description:t.description})}let o=t.registries??{};for(let[e,t]of Object.entries(o))if(!(r&&e!==r))try{let n=await m((typeof t==`string`?t:t.url).replace(/{name}[^$]*$/,``).replace(/\/$/,``)+`/registry.json`),r=typeof n==`object`&&n&&Array.isArray(n.items)?n.items??[]:[];for(let t of r){if(!pe(t).ok)continue;let n=t;Jt(n,i)&&a.push({name:`${e}/${n.name}`,version:n.version,description:n.description,registry:e})}}catch{console.warn(` [warn] Could not search ${e} registry`)}if(a.length===0){console.log(`No results found.`);return}for(let e of a){let t=e.registry?` (${e.registry})`:``;console.log(`${e.name}@${e.version}${t} — ${e.description}`)}}const Y={baseMode:`light`,accentHue:250,accentChroma:.2,radiusBase:.375,fontFamily:`system`,presetId:`light`};function Xt(e){if(!e)return null;try{let t=e.replace(/-/g,`+`).replace(/_/g,`/`),n=atob(t),r=JSON.parse(n);return{baseMode:r.baseMode===`dark`?`dark`:`light`,accentHue:typeof r.accentHue==`number`?r.accentHue:Y.accentHue,accentChroma:typeof r.accentChroma==`number`?r.accentChroma:Y.accentChroma,radiusBase:typeof r.radiusBase==`number`?r.radiusBase:Y.radiusBase,fontFamily:r.fontFamily??Y.fontFamily,presetId:r.presetId??null}}catch{return null}}const Zt={geometric:`'Futura', 'Century Gothic', 'Trebuchet MS', ui-sans-serif, sans-serif`,humanist:`'Gill Sans', 'Trebuchet MS', 'Calibri', ui-sans-serif, sans-serif`,transitional:`'Optima', 'Candara', 'Palatino Linotype', ui-sans-serif, sans-serif`,mono:`ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace`};function Qt(e,t={}){let{previewMode:n=!1,themeName:r=`create-custom`}=t,i=e.baseMode===`light`,a=e.accentHue,o=e.accentChroma,s=(e,t=o)=>`oklch(${e} ${t.toFixed(3)} ${a})`,c=[`/* Add to your project CSS after importing the base theme: */`,`/* @import '@cascivo/themes/${e.baseMode}.css'; */`,`/* @import './theme.css'; */`,``,`@layer cascivo.theme {`,` [data-theme="${r}"] {`];return n&&(c.push(i?`
364
+ `)}async function Wt(e,t={}){let n=(await y(e.registry)).components;t.installed&&(n=n.filter(t=>{let n=t.name.includes(`/`)?t.name.split(`/`).pop():t.name;return j(F(process.cwd(),e.outputDir,n))})),console.log(Ut(n)),console.log("\nTip: install any entry by its bare name — `cascivo add flex` resolves to `layout/flex`. Layout primitives (Flex, Grid, AutoGrid, Columns, Spacer, Center) replace inline-style layout; see docs/cookbooks/layout-and-spacing.md.")}function Gt(e){let t=[],n=[],r=new Set(e.items.map(e=>e.name));for(let i of e.items)if(L(i)){for(let e of fe(i))t.push(`template "${i.name}": ${e}`);for(let e of i.registryDependencies??[])!(e.includes(`/`)||e.startsWith(`@`)||e.startsWith(`http`))&&!r.has(e)&&n.push(`template "${i.name}": component "${e}" is not in this index — it must resolve from another registry at install time`)}return{errors:t,warnings:n}}async function Kt(e){let t=`cascade-registry.json`,n=`public/r`;for(let r=0;r<e.length;r++)e[r]===`--in`&&e[r+1]?(t=e[r+1],r++):e[r]===`--out`&&e[r+1]&&(n=e[r+1],r++);let r=I(t);if(!j(r)){console.error(`cascade registry build: file not found: ${r}`),process.exitCode=1;return}let i=JSON.parse(await oe(r,`utf8`));ie(r);let a=typeof i==`object`&&i&&i.schemaVersion===2?i:le(i),o=ue(a);if(o.warnings.length>0)for(let e of o.warnings)console.warn(`warn: ${e}`);if(!o.ok){for(let e of o.errors)console.error(`error: ${e}`);process.exitCode=1;return}let s=Gt(a);for(let e of s.warnings)console.warn(`warn: ${e}`);if(s.errors.length>0){for(let e of s.errors)console.error(`error: ${e}`);process.exitCode=1;return}let c=I(n);await ce(a,c),console.log(`cascade registry build: wrote static output to ${c}`)}function qt(e,t){let n=t.toLowerCase();return e.name.toLowerCase().includes(n)||e.description.toLowerCase().includes(n)||e.tags.some(e=>e.toLowerCase().includes(n))}async function Jt(e,t){let n=e.indexOf(`--registry`),r=n===-1?void 0:e[n+1],i=e.filter((t,n)=>t!==`--registry`&&e[n-1]!==`--registry`).join(` `).trim();if(!i){console.error(`Usage: cascivo search <query> [--registry @ns]`);return}let a=[];if(!r){let e=await y(t.registry);for(let t of e.components)qt(t,i)&&a.push({name:t.name,version:t.version,description:t.description})}let o=t.registries??{};for(let[e,t]of Object.entries(o))if(!(r&&e!==r))try{let n=await p((typeof t==`string`?t:t.url).replace(/{name}[^$]*$/,``).replace(/\/$/,``)+`/registry.json`),r=typeof n==`object`&&n&&Array.isArray(n.items)?n.items??[]:[];for(let t of r){if(!de(t).ok)continue;let n=t;qt(n,i)&&a.push({name:`${e}/${n.name}`,version:n.version,description:n.description,registry:e})}}catch{console.warn(` [warn] Could not search ${e} registry`)}if(a.length===0){console.log(`No results found.`);return}for(let e of a){let t=e.registry?` (${e.registry})`:``;console.log(`${e.name}@${e.version}${t} — ${e.description}`)}}const Y={baseMode:`light`,accentHue:250,accentChroma:.2,radiusBase:.375,fontFamily:`system`,presetId:`light`};function Yt(e){if(!e)return null;try{let t=e.replace(/-/g,`+`).replace(/_/g,`/`),n=atob(t),r=JSON.parse(n);return{baseMode:r.baseMode===`dark`?`dark`:`light`,accentHue:typeof r.accentHue==`number`?r.accentHue:Y.accentHue,accentChroma:typeof r.accentChroma==`number`?r.accentChroma:Y.accentChroma,radiusBase:typeof r.radiusBase==`number`?r.radiusBase:Y.radiusBase,fontFamily:r.fontFamily??Y.fontFamily,presetId:r.presetId??null}}catch{return null}}const X={geometric:`'Futura', 'Century Gothic', 'Trebuchet MS', ui-sans-serif, sans-serif`,humanist:`'Gill Sans', 'Trebuchet MS', 'Calibri', ui-sans-serif, sans-serif`,transitional:`'Optima', 'Candara', 'Palatino Linotype', ui-sans-serif, sans-serif`,mono:`ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace`};function Xt(e,t={}){let{previewMode:n=!1,themeName:r=`create-custom`}=t,i=e.baseMode===`light`,a=e.accentHue,o=e.accentChroma,s=(e,t=o)=>`oklch(${e} ${t.toFixed(3)} ${a})`,c=[`/* Add to your project CSS after importing the base theme: */`,`/* @import '@cascivo/themes/${e.baseMode}.css'; */`,`/* @import './theme.css'; */`,``,`@layer cascivo.theme {`,` [data-theme="${r}"] {`];return n&&(c.push(i?`
365
365
  color-scheme: light;
366
366
  --cascivo-color-background: oklch(1 0 0);
367
367
  --cascivo-color-surface: oklch(0.985 0.002 264);
@@ -457,20 +457,20 @@ Add components with: cascivo add <name>`)}const Vt={component:`Components`,layou
457
457
  --cascivo-ring-width: 2px;
458
458
  --cascivo-ring-offset: 0px;
459
459
  --cascivo-ring-color: color-mix(in oklch, var(--cascivo-color-accent) 65%, transparent);
460
- --cascivo-focus-ring: 0 0 0 var(--cascivo-ring-width) var(--cascivo-ring-color);`),c.push(``)),c.push(` /* ── Accent ─────────────────────────────────── */`,` --cascivo-color-accent: ${s(i?.52:.65)};`,` --cascivo-color-accent-foreground: ${i?`oklch(1 0 0)`:`oklch(0.145 0.005 ${a})`};`,` --cascivo-color-accent-hover: ${s(i?.45:.707)};`,` --cascivo-color-accent-active: ${s(i?.373:.808,Math.min(o,.155))};`,` --cascivo-color-accent-subtle: ${s(i?.97:.623,.025)};`,` --cascivo-color-accent-muted: ${s(i?.932:.623,i?.055:o*.2)};`,``,` /* ── Radius ─────────────────────────────────── */`,` --cascivo-radius-base: ${e.radiusBase}rem;`,` --cascivo-radius-control: var(--cascivo-radius-base);`,` --cascivo-radius-surface: calc(var(--cascivo-radius-base) * 1.66);`,` --cascivo-radius-indicator: calc(var(--cascivo-radius-base) / 2);`,` --cascivo-radius-full: 9999px;`,` --cascivo-radius-component: var(--cascivo-radius-base);`,` --cascivo-radius-button: var(--cascivo-radius-base);`,` --cascivo-radius-input: var(--cascivo-radius-base);`,` --cascivo-radius-card: calc(var(--cascivo-radius-base) * 1.66);`,` --cascivo-radius-badge: var(--cascivo-radius-full);`,` --cascivo-radius-modal: calc(var(--cascivo-radius-base) * 2);`),e.fontFamily!==`system`&&(c.push(``),c.push(` /* ── Font ───────────────────────────────────── */`),c.push(` --cascivo-font-sans: ${Zt[e.fontFamily]??Zt.humanist};`)),c.push(` }`),c.push(`}`),c.join(`
461
- `)}const $t=/^[a-z][a-z0-9-]*$/;function X(e,t){let n=e.find(e=>e.startsWith(`--${t}=`));if(n)return n.slice(t.length+3);let r=e.indexOf(`--${t}`),i=r===-1?void 0:e[r+1];return i&&!i.startsWith(`--`)?i:void 0}async function en(e,t){let n=e.find(e=>!e.startsWith(`--`)),r=X(e,`from`),i=e.includes(`--dry-run`);if(!n||!r){console.error(`Usage: cascivo theme create <name> --from <hash> [--out <path>]`),console.error(`Get <hash> from the theme builder at https://cascivo.com/create (Copy CLI command).`);return}if(!$t.test(n)){console.error(`Invalid theme name "${n}". Use lowercase letters, digits, and dashes.`);return}let a=Xt(r);if(!a){console.error(`Could not decode --from. Copy the command again from the theme builder.`);return}let o=Qt(a,{themeName:n});if(i){console.log(o);return}await l(M(t,X(e,`out`)??`${n}.theme.css`),o),console.log(`\n✓ Wrote ${n}.theme.css`),console.log(`
462
- Import the base theme and your overlay in your entry CSS:`),console.log(` @import '@cascivo/themes/${a.baseMode}.css';`),console.log(` @import './${n}.theme.css';`),console.log(`Then set it on a container:`),console.log(` <div data-theme="${n}">…</div>`)}async function tn(e){let[t,n]=e;if(t===`create`){await en(e.slice(1),process.cwd());return}if(t!==`add`||!n){console.error(`Usage: cascivo theme add <${a.join(`|`)}>`),console.error(` cascivo theme create <name> --from <hash>`);return}if(!a.includes(n)){console.error(`Unknown theme "${n}". Available: ${a.join(`, `)}`);return}I([`@cascivo/themes`]),console.log(`
463
- Import the theme in your root CSS or entry file:`),console.log(` import '@cascivo/themes/${n}.css'`),console.log(`Then set it on a container:`),console.log(` <div data-theme="${n}">…</div>`)}function nn(e,t){let n=e.split(`
460
+ --cascivo-focus-ring: 0 0 0 var(--cascivo-ring-width) var(--cascivo-ring-color);`),c.push(``)),c.push(` /* ── Accent ─────────────────────────────────── */`,` --cascivo-color-accent: ${s(i?.52:.65)};`,` --cascivo-color-accent-foreground: ${i?`oklch(1 0 0)`:`oklch(0.145 0.005 ${a})`};`,` --cascivo-color-accent-hover: ${s(i?.45:.707)};`,` --cascivo-color-accent-active: ${s(i?.373:.808,Math.min(o,.155))};`,` --cascivo-color-accent-subtle: ${s(i?.97:.623,.025)};`,` --cascivo-color-accent-muted: ${s(i?.932:.623,i?.055:o*.2)};`,``,` /* ── Radius ─────────────────────────────────── */`,` --cascivo-radius-base: ${e.radiusBase}rem;`,` --cascivo-radius-control: var(--cascivo-radius-base);`,` --cascivo-radius-surface: calc(var(--cascivo-radius-base) * 1.66);`,` --cascivo-radius-indicator: calc(var(--cascivo-radius-base) / 2);`,` --cascivo-radius-full: 9999px;`,` --cascivo-radius-component: var(--cascivo-radius-base);`,` --cascivo-radius-button: var(--cascivo-radius-base);`,` --cascivo-radius-input: var(--cascivo-radius-base);`,` --cascivo-radius-card: calc(var(--cascivo-radius-base) * 1.66);`,` --cascivo-radius-badge: var(--cascivo-radius-full);`,` --cascivo-radius-modal: calc(var(--cascivo-radius-base) * 2);`),e.fontFamily!==`system`&&(c.push(``),c.push(` /* ── Font ───────────────────────────────────── */`),c.push(` --cascivo-font-sans: ${X[e.fontFamily]??X.humanist};`)),c.push(` }`),c.push(`}`),c.join(`
461
+ `)}const Zt=/^[a-z][a-z0-9-]*$/;function Qt(e,t){let n=e.find(e=>e.startsWith(`--${t}=`));if(n)return n.slice(t.length+3);let r=e.indexOf(`--${t}`),i=r===-1?void 0:e[r+1];return i&&!i.startsWith(`--`)?i:void 0}async function $t(e,t){let n=e.find(e=>!e.startsWith(`--`)),r=Qt(e,`from`),i=e.includes(`--dry-run`);if(!n||!r){console.error(`Usage: cascivo theme create <name> --from <hash> [--out <path>]`),console.error(`Get <hash> from the theme builder at https://cascivo.com/create (Copy CLI command).`);return}if(!Zt.test(n)){console.error(`Invalid theme name "${n}". Use lowercase letters, digits, and dashes.`);return}let a=Yt(r);if(!a){console.error(`Could not decode --from. Copy the command again from the theme builder.`);return}let o=Xt(a,{themeName:n});if(i){console.log(o);return}await c(I(t,Qt(e,`out`)??`${n}.theme.css`),o),console.log(`\n✓ Wrote ${n}.theme.css`),console.log(`
462
+ Import the base theme and your overlay in your entry CSS:`),console.log(` @import '@cascivo/themes/${a.baseMode}.css';`),console.log(` @import './${n}.theme.css';`),console.log(`Then set it on a container:`),console.log(` <div data-theme="${n}">…</div>`)}async function en(e){let[t,n]=e;if(t===`create`){await $t(e.slice(1),process.cwd());return}if(t!==`add`||!n){console.error(`Usage: cascivo theme add <${i.join(`|`)}>`),console.error(` cascivo theme create <name> --from <hash>`);return}if(!i.includes(n)){console.error(`Unknown theme "${n}". Available: ${i.join(`, `)}`);return}B([`@cascivo/themes`]),console.log(`
463
+ Import the theme in your root CSS or entry file:`),console.log(` import '@cascivo/themes/${n}.css'`),console.log(`Then set it on a container:`),console.log(` <div data-theme="${n}">…</div>`)}function tn(e,t){let n=e.split(`
464
464
  `),r=t.split(`
465
- `),i=n.length,a=r.length,o=Array.from({length:i+1},()=>Array.from({length:a+1},()=>0));for(let e=i-1;e>=0;e--)for(let t=a-1;t>=0;t--)o[e][t]=n[e]===r[t]?o[e+1][t+1]+1:Math.max(o[e+1][t],o[e][t+1]);let s=[],c=0,l=0;for(;c<i&&l<a;)n[c]===r[l]?(s.push(` ${n[c]}`),c++,l++):o[c+1][l]>=o[c][l+1]?(s.push(`- ${n[c]}`),c++):(s.push(`+ ${r[l]}`),l++);for(;c<i;)s.push(`- ${n[c++]}`);for(;l<a;)s.push(`+ ${r[l++]}`);return s}function rn(e,t){return e!==t}function an(e,t){let n=e.length,r=t.length,i=Array.from({length:n+1},()=>Array.from({length:r+1},()=>0));for(let a=1;a<=n;a++)for(let n=1;n<=r;n++)i[a][n]=e[a-1]===t[n-1]?i[a-1][n-1]+1:Math.max(i[a-1][n],i[a][n-1]);return i}function Z(e,t){let n=an(e,t),r=[],i=e.length,a=t.length,o=[],s=[],c=[];function l(){o.length>0||s.length>0?(c.length>0&&(r.unshift({type:`common`,lines:[...c]}),c.length=0),r.unshift({type:o.length>0?`left`:`right`,lines:o.length>0?[...o]:[...s]}),o.length=0,s.length=0):c.length>0&&(r.unshift({type:`common`,lines:[...c]}),c.length=0)}for(;i>0||a>0;)i>0&&a>0&&e[i-1]===t[a-1]?((o.length>0||s.length>0)&&l(),c.unshift(e[i-1]),i--,a--):a>0&&(i===0||n[i][a-1]>=n[i-1][a])?(l(),s.unshift(t[a-1]),a--):(l(),o.unshift(e[i-1]),i--);return l(),r}function on(e,t,n){let r=e.split(`
465
+ `),i=n.length,a=r.length,o=Array.from({length:i+1},()=>Array.from({length:a+1},()=>0));for(let e=i-1;e>=0;e--)for(let t=a-1;t>=0;t--)o[e][t]=n[e]===r[t]?o[e+1][t+1]+1:Math.max(o[e+1][t],o[e][t+1]);let s=[],c=0,l=0;for(;c<i&&l<a;)n[c]===r[l]?(s.push(` ${n[c]}`),c++,l++):o[c+1][l]>=o[c][l+1]?(s.push(`- ${n[c]}`),c++):(s.push(`+ ${r[l]}`),l++);for(;c<i;)s.push(`- ${n[c++]}`);for(;l<a;)s.push(`+ ${r[l++]}`);return s}function nn(e,t){return e!==t}function rn(e,t){let n=e.length,r=t.length,i=Array.from({length:n+1},()=>Array.from({length:r+1},()=>0));for(let a=1;a<=n;a++)for(let n=1;n<=r;n++)i[a][n]=e[a-1]===t[n-1]?i[a-1][n-1]+1:Math.max(i[a-1][n],i[a][n-1]);return i}function Z(e,t){let n=rn(e,t),r=[],i=e.length,a=t.length,o=[],s=[],c=[];function l(){o.length>0||s.length>0?(c.length>0&&(r.unshift({type:`common`,lines:[...c]}),c.length=0),r.unshift({type:o.length>0?`left`:`right`,lines:o.length>0?[...o]:[...s]}),o.length=0,s.length=0):c.length>0&&(r.unshift({type:`common`,lines:[...c]}),c.length=0)}for(;i>0||a>0;)i>0&&a>0&&e[i-1]===t[a-1]?((o.length>0||s.length>0)&&l(),c.unshift(e[i-1]),i--,a--):a>0&&(i===0||n[i][a-1]>=n[i-1][a])?(l(),s.unshift(t[a-1]),a--):(l(),o.unshift(e[i-1]),i--);return l(),r}function an(e,t,n){let r=e.split(`
466
466
  `),i=t.split(`
467
467
  `),a=n.split(`
468
468
  `),o=Z(r,i),s=new Map,c=0;for(let e of o)e.type===`left`?s.set(`del:${c}`,e.lines):(e.type===`right`&&s.set(`ins:${c}`,e.lines),c+=e.lines.length);let l=[],u=0;if(r.length,i.length,a.length,t===e)return{text:n,conflicts:0};if(n===e)return{text:t,conflicts:0};let d=Z(r,i),f=Z(r,a),p=0,m=0,h=0,g=[],_=[];for(let e of d)e.type===`common`?(m+=e.lines.length,p+=e.lines.length):e.type===`right`?(g.push({baseStart:p,baseEnd:p,lines:e.lines}),m+=e.lines.length):(g.push({baseStart:p,baseEnd:p+e.lines.length,lines:[]}),p+=e.lines.length);p=0;for(let e of f)e.type===`common`?(h+=e.lines.length,p+=e.lines.length):e.type===`right`?(_.push({baseStart:p,baseEnd:p,lines:e.lines}),h+=e.lines.length):(_.push({baseStart:p,baseEnd:p+e.lines.length,lines:[]}),p+=e.lines.length);let v=new Map;for(let e of g)v.set(e.baseStart,{end:e.baseEnd,lines:e.lines});let y=new Map;for(let e of _)y.set(e.baseStart,{end:e.baseEnd,lines:e.lines});let b=0;for(;b<=r.length;){let e=v.get(b),t=y.get(b);if(e&&t){let n=Math.max(e.end,b),r=Math.max(t.end,b),i=e.lines,a=t.lines;JSON.stringify(i)===JSON.stringify(a)?l.push(...i):(l.push(`<<<<<<< local`),l.push(...i),l.push(`=======`),l.push(...a),l.push(`>>>>>>> upstream`),u++),b=Math.max(n,r,b+1)}else e?(l.push(...e.lines),b=Math.max(e.end,b+1)):t?(l.push(...t.lines),b=Math.max(t.end,b+1)):(b<r.length&&l.push(r[b]),b++)}return{text:l.join(`
469
- `),conflicts:u}}async function sn(e){let t=F({input:T,output:E});try{let n=(await t.question(`${e} (y/N): `)).trim().toLowerCase();return n===`y`||n===`yes`}finally{t.close()}}async function cn(e,t,n={}){if(n.check)return dn(t);if(!e){console.error(`Usage: cascivo update <component>`);return}let r=(await y())?.items[e],i=g(await b(t.registry),e);if(!i){console.error(`Component "${e}" not found in registry. Run "cascivo list".`);return}if(r){let a=r.version,o=`${r.registry}/r/${e}@${a}.json`,s=null;try{s=P(await m(o),o)}catch{console.warn(`Warning: could not fetch base version ${a} from ${o}. Falling back to two-way diff.`)}s?await ln(e,i,s,t,n):await un(e,i,t,n)}else console.warn(`Warning: "${e}" has no lock entry. Falling back to two-way diff update (pre-v11 install).`),await un(e,i,t,n)}async function ln(e,t,n,r,i){if(!t)return;let a=e.includes(`/`)?e.split(`/`).pop():e,o=[],s=[];for(let e of t.files){let t=x(e),i=n.files.find(e=>x(e.url)===t),c=d(r.outputDir,a,t),l=await p(e),u=D(c)?await ce(c,`utf8`):``,f=i?await p(i.url):``;if(f===``&&u===``){o.push({file:t,result:`clean`}),s.push({dest:c,content:l,conflicts:0});continue}let{text:m,conflicts:h}=on(f,u,l);if(h>0)o.push({file:t,result:`conflict`});else if(m===u){o.push({file:t,result:`unchanged`});continue}else o.push({file:t,result:`clean`});s.push({dest:c,content:m,conflicts:h})}if(s.length===0){console.log(`${e} is already up to date.`);return}for(let e of o){let t=e.result===`conflict`?`⚠`:e.result===`clean`?`✓`:`·`;console.log(` ${t} ${e.file} (${e.result})`)}let c=s.filter(e=>e.conflicts>0);if((i.yes?c.length===0:await sn(`\nApply ${s.length} change(s) to ${e}?`))||i.yes){let n=await y(),r=n;for(let{dest:e,content:t,conflicts:n}of s){if(i.yes&&n>0){console.log(` skipped (conflicts): ${e}`);continue}await l(e,t)}if(n&&r){let i=c.length>0;r=_(r,e,{...n.items[e],version:t.version,...i?{conflicted:!0}:{}}),await h(r)}console.log(`Updated ${e}.`),c.length>0&&console.log(`${c.length} file(s) have conflict markers — resolve them manually.`)}else console.log(`Aborted. No files changed.`)}async function un(e,t,n,r){if(!t)return;let i=[];for(let e of t.files){let r;try{r=await p(e)}catch(e){console.error(e instanceof Error?e.message:String(e));return}let a=d(n.outputDir,t.name,x(e)),o=await u(a);o===null?(console.log(`New file: ${a}`),i.push({path:a,content:r})):rn(o,r)&&(console.log(`\n--- ${a}`),console.log(nn(o,r).join(`
470
- `)),i.push({path:a,content:r}))}if(i.length===0){console.log(`${e} is already up to date.`);return}if(r.yes||await sn(`\nApply ${i.length} change(s) to ${e}?`)){for(let{path:e,content:t}of i)await l(e,t);console.log(`Updated ${e}.`)}else console.log(`Aborted. No files changed.`)}async function dn(e){let t=await y();if(!t||Object.keys(t.items).length===0){console.log(`No installed components found in cascivo.lock.`);return}let n=await b(e.registry),r=0;for(let[e,i]of Object.entries(t.items)){let t=g(n,e);if(t)if(t.fileHashes&&Object.keys(t.fileHashes).length>0){let n=new Map(Object.entries(i.files).map(([e,t])=>[e.split(`/`).pop(),t])),a=Object.entries(t.fileHashes).filter(([e,t])=>n.get(e)!==t);a.length>0&&(console.log(`${e}: ${a.length} file(s) changed upstream (${i.version} → ${t.version})`),r++)}else t.version!==i.version&&(console.log(`${e}: ${i.version} → ${t.version}`),r++)}r>0?(console.log(`${r} component(s) outdated.`),process.exitCode=1):console.log(`All components up to date.`)}function fn(e){let t=[];if(t.push(`\n${e.name} v${e.version}`),t.push(`Type: ${e.type}${e.category?` / ${e.category}`:``}`),t.push(e.description),e.author&&t.push(`Author: ${e.author}`),e.license&&t.push(`License: ${e.license}`),e.homepage&&t.push(`Homepage: ${e.homepage}`),e.install?(t.push(`Distribution: npm package — pnpm add ${e.install}`),e.styles&&t.push(`Stylesheet: import '${e.styles}' (required)`)):N(e)||t.push(`Distribution: copy-paste — npx cascivo add ${e.name}`),N(e))try{let n=le(e.meta);if(t.push(`\nTemplate: ${n.intent}`),t.push(`Framework: ${n.framework}`),n.demoUrl&&t.push(`Demo: ${n.demoUrl}`),n.pages?.length){t.push(`Pages:`);for(let e of n.pages)t.push(` ${e.name} → ${e.target}`)}}catch{}if(e.changelog?.length){t.push(`
469
+ `),conflicts:u}}async function on(e){let t=z({input:k,output:A});try{let n=(await t.question(`${e} (y/N): `)).trim().toLowerCase();return n===`y`||n===`yes`}finally{t.close()}}async function sn(e,t,n={}){if(n.check)return un(t);if(!e){console.error(`Usage: cascivo update <component>`);return}let r=(await v())?.items[e],i=h(await y(t.registry),e);if(!i){console.error(`Component "${e}" not found in registry. Run "cascivo list".`);return}if(r){let a=r.version,o=`${r.registry}/r/${e}@${a}.json`,s=null;try{s=R(await p(o),o)}catch{console.warn(`Warning: could not fetch base version ${a} from ${o}. Falling back to two-way diff.`)}s?await cn(e,i,s,t,n):await ln(e,i,t,n)}else console.warn(`Warning: "${e}" has no lock entry. Falling back to two-way diff update (pre-v11 install).`),await ln(e,i,t,n)}async function cn(e,t,n,r,i){if(!t)return;let a=e.includes(`/`)?e.split(`/`).pop():e,o=[],s=[];for(let e of t.files){let t=x(e),i=n.files.find(e=>x(e.url)===t),c=u(r.outputDir,a,t),l=await f(e),d=j(c)?await oe(c,`utf8`):``,p=i?await f(i.url):``;if(p===``&&d===``){o.push({file:t,result:`clean`}),s.push({dest:c,content:l,conflicts:0});continue}let{text:m,conflicts:h}=an(p,d,l);if(h>0)o.push({file:t,result:`conflict`});else if(m===d){o.push({file:t,result:`unchanged`});continue}else o.push({file:t,result:`clean`});s.push({dest:c,content:m,conflicts:h})}if(s.length===0){console.log(`${e} is already up to date.`);return}for(let e of o){let t=e.result===`conflict`?`⚠`:e.result===`clean`?`✓`:`·`;console.log(` ${t} ${e.file} (${e.result})`)}let l=s.filter(e=>e.conflicts>0);if((i.yes?l.length===0:await on(`\nApply ${s.length} change(s) to ${e}?`))||i.yes){let n=await v(),r=n;for(let{dest:e,content:t,conflicts:n}of s){if(i.yes&&n>0){console.log(` skipped (conflicts): ${e}`);continue}await c(e,t)}if(n&&r){let i=l.length>0;r=g(r,e,{...n.items[e],version:t.version,...i?{conflicted:!0}:{}}),await m(r)}console.log(`Updated ${e}.`),l.length>0&&console.log(`${l.length} file(s) have conflict markers — resolve them manually.`)}else console.log(`Aborted. No files changed.`)}async function ln(e,t,n,r){if(!t)return;let i=[];for(let e of t.files){let r;try{r=await f(e)}catch(e){console.error(e instanceof Error?e.message:String(e));return}let a=u(n.outputDir,t.name,x(e)),o=await l(a);o===null?(console.log(`New file: ${a}`),i.push({path:a,content:r})):nn(o,r)&&(console.log(`\n--- ${a}`),console.log(tn(o,r).join(`
470
+ `)),i.push({path:a,content:r}))}if(i.length===0){console.log(`${e} is already up to date.`);return}if(r.yes||await on(`\nApply ${i.length} change(s) to ${e}?`)){for(let{path:e,content:t}of i)await c(e,t);console.log(`Updated ${e}.`)}else console.log(`Aborted. No files changed.`)}async function un(e){let t=await v();if(!t||Object.keys(t.items).length===0){console.log(`No installed components found in cascivo.lock.`);return}let n=await y(e.registry),r=0;for(let[e,i]of Object.entries(t.items)){let t=h(n,e);if(t)if(t.fileHashes&&Object.keys(t.fileHashes).length>0){let n=new Map(Object.entries(i.files).map(([e,t])=>[e.split(`/`).pop(),t])),a=Object.entries(t.fileHashes).filter(([e,t])=>n.get(e)!==t);a.length>0&&(console.log(`${e}: ${a.length} file(s) changed upstream (${i.version} → ${t.version})`),r++)}else t.version!==i.version&&(console.log(`${e}: ${i.version} → ${t.version}`),r++)}r>0?(console.log(`${r} component(s) outdated.`),process.exitCode=1):console.log(`All components up to date.`)}function dn(e){let t=[];if(t.push(`\n${e.name} v${e.version}`),t.push(`Type: ${e.type}${e.category?` / ${e.category}`:``}`),t.push(e.description),e.author&&t.push(`Author: ${e.author}`),e.license&&t.push(`License: ${e.license}`),e.homepage&&t.push(`Homepage: ${e.homepage}`),e.install?(t.push(`Distribution: npm package — pnpm add ${e.install}`),e.styles&&t.push(`Stylesheet: import '${e.styles}' (required)`)):L(e)||t.push(`Distribution: copy-paste — npx cascivo add ${e.name}`),L(e))try{let n=se(e.meta);if(t.push(`\nTemplate: ${n.intent}`),t.push(`Framework: ${n.framework}`),n.demoUrl&&t.push(`Demo: ${n.demoUrl}`),n.pages?.length){t.push(`Pages:`);for(let e of n.pages)t.push(` ${e.name} → ${e.target}`)}}catch{}if(e.changelog?.length){t.push(`
471
471
  Changelog:`);for(let n of e.changelog.slice(0,5))t.push(` ${n.version}: ${n.note}`)}if(e.files?.length){t.push(`
472
- Files:`);for(let n of e.files)t.push(` ${n.url}${n.target?` → ${n.target}`:``}`)}if(e.dependencies?.length&&t.push(`\nDependencies: ${e.dependencies.join(`, `)}`),e.registryDependencies?.length){let n=N(e)?`Components`:`Registry dependencies`;t.push(`${n}: ${e.registryDependencies.join(`, `)}`)}return t.join(`
473
- `)}async function pn(e,t){let n=e[0];if(!n){console.error(`Usage: cascivo view <component-spec>`);return}let{url:r,headers:i,params:a}=await ve(ge(n),t),o={};i&&(o.headers=i),a&&(o.params=a);let s=P(await m(r,o),r);console.log(fn(s))}const Q=C(import.meta.url)(`../package.json`).version,mn=`cascivo ${Q} — the CSS-native, signal-driven, AI-first design system
472
+ Files:`);for(let n of e.files)t.push(` ${n.url}${n.target?` → ${n.target}`:``}`)}if(e.dependencies?.length&&t.push(`\nDependencies: ${e.dependencies.join(`, `)}`),e.registryDependencies?.length){let n=L(e)?`Components`:`Registry dependencies`;t.push(`${n}: ${e.registryDependencies.join(`, `)}`)}return t.join(`
473
+ `)}async function fn(e,t){let n=e[0];if(!n){console.error(`Usage: cascivo view <component-spec>`);return}let{url:r,headers:i,params:a}=await W(H(n),t),o={};i&&(o.headers=i),a&&(o.params=a);let s=R(await p(r,o),r);console.log(dn(s))}const Q=D(import.meta.url)(`../package.json`).version,pn=`cascivo ${Q} — the CSS-native, signal-driven, AI-first design system
474
474
 
475
475
  Usage: cascivo <command> [options]
476
476
 
@@ -491,8 +491,34 @@ Commands:
491
491
  registry build Build a static registry from a cascivo-registry.json file
492
492
  template init <name> Scaffold a new template (source + manifest + registry entry)
493
493
  tokens import <file> Import external design tokens as cascivo overrides
494
+ email lint <file...> Check rendered email HTML against the Can I email matrix
494
495
 
495
- Run "cascivo <command> --help" for details.`,$=a.join(` | `),hn={create:`Usage: cascivo create [name] [options]
496
+ Run "cascivo <command> --help" for details.`,$=i.join(` | `),mn={email:`Usage: cascivo email lint <file...> [options]
497
+
498
+ Check rendered email HTML against the Can I email support matrix — the same
499
+ conformance lint @cascivo/email exposes as \`lint()\`, with the setup done for you.
500
+
501
+ It lints rendered HTML, not templates: render in your own build and pass the
502
+ output, or pipe it in.
503
+
504
+ Arguments:
505
+ <file...> One or more rendered .html files, or \`-\` for stdin
506
+
507
+ Options:
508
+ --data <file> Use a local copy of the matrix instead of fetching
509
+ --refresh Re-fetch the matrix even if the cache is fresh
510
+ --no-allowlist Report findings CASCIVO_ALLOW waives, too
511
+
512
+ The matrix is fetched from https://www.caniemail.com/api/data.json and cached for
513
+ a day. It is not bundled with @cascivo/email because it is ~483 KB of test data.
514
+
515
+ Exits non-zero when a floor client genuinely cannot support something. A caveat is
516
+ partial support worth knowing about and never fails the command.
517
+
518
+ Examples:
519
+ cascivo email lint dist/emails/*.html
520
+ node render.js | cascivo email lint -
521
+ cascivo email lint dist/welcome.html --data caniemail.json`,create:`Usage: cascivo create [name] [options]
496
522
 
497
523
  Scaffold a new ready-to-run app — Vite + React + TypeScript, pre-wired with the
498
524
  cascivo app shell, side navigation, header, and a theme.
@@ -598,5 +624,5 @@ Options:
598
624
  --repo <owner/repo> Repository the template will be hosted in`,tokens:`Usage: cascivo tokens import <file>
599
625
 
600
626
  Import external design tokens (W3C design-tokens JSON) as cascivo token
601
- overrides.`};async function gn(e){let[t,...r]=e;if(t!==void 0&&(r.includes(`--help`)||r.includes(`-h`))){let e=hn[t];if(e!==void 0){console.log(e);return}}switch(t){case`create`:await tt(r);break;case`init`:await Bt(r);break;case`add`:{let e=G(r);if(`error`in e){console.error(e.error),process.exitCode=1;break}await U(Ae(r,[`pm`,`package-manager`]),await n(),{dryRun:r.includes(`--dry-run`),yes:r.includes(`--yes`),noInstall:r.includes(`--no-install`),...e.pm?{pm:e.pm}:{}});break}case`list`:await Gt(await n(),{installed:r.includes(`--installed`)});break;case`update`:await cn(r.filter(e=>!e.startsWith(`--`))[0],await n(),{check:r.includes(`--check`),yes:r.includes(`--yes`)});break;case`search`:await Yt(r,await n());break;case`view`:await pn(r,await n());break;case`theme`:await tn(r);break;case`eject`:{let{eject:e}=await import(`./eject-B94ujtUo.mjs`),t=e=>{let t=r.find(t=>t.startsWith(`--${e}=`));if(t)return t.slice(e.length+3);let n=r.indexOf(`--${e}`),i=n===-1?void 0:r[n+1];return i&&!i.startsWith(`--`)?i:void 0},i=t(`tokens`);await e(r,await n(),{...i?{tokens:i.split(`,`).map(e=>e.trim()).filter(Boolean)}:{},...t(`scope`)?{scope:t(`scope`)}:{},...t(`out`)?{out:t(`out`)}:{},dryRun:r.includes(`--dry-run`)});break}case`generate`:await At(r,await n());break;case`registry`:r[0]===`build`?await qt(r.slice(1)):(console.error(`Unknown registry subcommand: ${r[0]}`),process.exitCode=1);break;case`template`:if(r[0]===`init`){let{templateInit:e}=await import(`./template-init-BFlRHOxK.mjs`);await e(r.slice(1))}else console.error(`Unknown template subcommand: ${r[0]}`),process.exitCode=1;break;case`doctor`:{let e=r.includes(`--ci`);if(r.includes(`--drift`)){let{runDoctorDrift:e}=await import(`./drift-BCtn9Ua_.mjs`);await e(await n())}else{let t=process.cwd(),r=await xt(t),{checkDuplicateCore:i,checkFormatterIgnore:a,checkProjectDependencies:o,checkSignalsCompat:s,checkSsrConfig:c,detectInstallPath:l}=await Promise.resolve().then(()=>nt),u=l(t)!==`unknown`,d=u?o(t):[],f=d.filter(e=>e.required),p=u?await s(t):null,m=p?.severity===`error`,h=u?c(t):null,g=u?await i(t):null,_=u?a(t,(await n()).outputDir):null,{runDoctorDrift:v}=await import(`./drift-BCtn9Ua_.mjs`),y=u?await v(await n(),t):{ran:!1,reason:`no cascivo install detected`,issues:0};if(r.passed&&d.length===0&&p===null&&h===null&&g===null&&_===null&&y.issues===0)console.log(y.ran?`No violations found.`:`No violations found (drift: not checked — ${y.reason}).`);else{for(let e of r.violations)console.error(`[${e.rule}] ${e.detail}\n ${e.file}`);for(let e of f)console.error(e.kind===`forbidden`?`[forbidden-dependency] ${e.package} must not be a direct dependency here. Remove it — ${e.hint}`:`[missing-dependency] ${e.package} is not in package.json. Install: ${e.hint}`);p&&(m?console.error:console.log)(`[${m?`signals-incompatible`:`signals-outdated`}] ${p.detail} Upgrade: ${p.hint}`),h&&console.log(`[ssr-config] ${h}`),_&&console.log(`[formatter-drift] ${_}`),g&&console.error(`[duplicate-core] More than one @cascivo/core is installed (root: ${g.root??`none`}; ${g.nested.join(`; `)}). `+g.hint);for(let e of d.filter(e=>!e.required))console.log(`[optional] ${e.package} is not installed; add it when a component or chart needs it: ${e.hint}`);e&&(r.violations.length>0||f.length>0||m||g!==null||y.issues>0)&&(process.exitCode=1)}}break}case`audit`:{let{audit:e}=await import(`./audit-DqWSsQGC.mjs`);await e(r,await n());break}case`tokens`:if(r[0]===`import`){let{tokensImport:e}=await import(`./tokens-VSJC9GgT.mjs`);await e(r.slice(1))}else console.error(`Unknown tokens subcommand: ${r[0]}`),process.exitCode=1;break;case void 0:case`--help`:case`-h`:case`help`:console.log(mn);break;case`--version`:case`-v`:console.log(Q);break;default:console.error(`Unknown command: ${t}\n`),console.log(mn),process.exitCode=1}}w[1]!==void 0&&import.meta.url===ie(w[1]).href&&gn(w.slice(2)).catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});export{Q as VERSION,gn as run};
627
+ overrides.`};async function hn(e){let[n,...r]=e;if(n!==void 0&&(r.includes(`--help`)||r.includes(`-h`))){let e=mn[n];if(e!==void 0){console.log(e);return}}switch(n){case`create`:await et(r);break;case`init`:await zt(r);break;case`add`:{let e=T(r);if(`error`in e){console.error(e.error),process.exitCode=1;break}await Oe(w(r,[`pm`,`package-manager`]),await t(),{dryRun:r.includes(`--dry-run`),yes:r.includes(`--yes`),noInstall:r.includes(`--no-install`),...e.pm?{pm:e.pm}:{}});break}case`list`:await Wt(await t(),{installed:r.includes(`--installed`)});break;case`update`:await sn(r.filter(e=>!e.startsWith(`--`))[0],await t(),{check:r.includes(`--check`),yes:r.includes(`--yes`)});break;case`search`:await Jt(r,await t());break;case`view`:await fn(r,await t());break;case`theme`:await en(r);break;case`eject`:{let{eject:e}=await import(`./eject-B94ujtUo.mjs`),n=e=>{let t=r.find(t=>t.startsWith(`--${e}=`));if(t)return t.slice(e.length+3);let n=r.indexOf(`--${e}`),i=n===-1?void 0:r[n+1];return i&&!i.startsWith(`--`)?i:void 0},i=n(`tokens`);await e(r,await t(),{...i?{tokens:i.split(`,`).map(e=>e.trim()).filter(Boolean)}:{},...n(`scope`)?{scope:n(`scope`)}:{},...n(`out`)?{out:n(`out`)}:{},dryRun:r.includes(`--dry-run`)});break}case`generate`:await kt(r,await t());break;case`registry`:r[0]===`build`?await Kt(r.slice(1)):(console.error(`Unknown registry subcommand: ${r[0]}`),process.exitCode=1);break;case`template`:if(r[0]===`init`){let{templateInit:e}=await import(`./template-init-BFlRHOxK.mjs`);await e(r.slice(1))}else console.error(`Unknown template subcommand: ${r[0]}`),process.exitCode=1;break;case`doctor`:{let e=r.includes(`--ci`);if(r.includes(`--drift`)){let{runDoctorDrift:e}=await import(`./drift-BCtn9Ua_.mjs`);await e(await t())}else{let n=process.cwd(),r=await bt(n),{checkDuplicateCore:i,checkFormatterIgnore:a,checkProjectDependencies:o,checkSignalsCompat:s,checkSsrConfig:c,detectInstallPath:l}=await Promise.resolve().then(()=>tt),u=l(n)!==`unknown`,d=u?o(n):[],f=d.filter(e=>e.required),p=u?await s(n):null,m=p?.severity===`error`,h=u?c(n):null,g=u?await i(n):null,_=u?a(n,(await t()).outputDir):null,{runDoctorDrift:v}=await import(`./drift-BCtn9Ua_.mjs`),y=u?await v(await t(),n):{ran:!1,reason:`no cascivo install detected`,issues:0};if(r.passed&&d.length===0&&p===null&&h===null&&g===null&&_===null&&y.issues===0)console.log(y.ran?`No violations found.`:`No violations found (drift: not checked — ${y.reason}).`);else{for(let e of r.violations)console.error(`[${e.rule}] ${e.detail}\n ${e.file}`);for(let e of f)console.error(e.kind===`forbidden`?`[forbidden-dependency] ${e.package} must not be a direct dependency here. Remove it — ${e.hint}`:`[missing-dependency] ${e.package} is not in package.json. Install: ${e.hint}`);p&&(m?console.error:console.log)(`[${m?`signals-incompatible`:`signals-outdated`}] ${p.detail} Upgrade: ${p.hint}`),h&&console.log(`[ssr-config] ${h}`),_&&console.log(`[formatter-drift] ${_}`),g&&console.error(`[duplicate-core] More than one @cascivo/core is installed (root: ${g.root??`none`}; ${g.nested.join(`; `)}). `+g.hint);for(let e of d.filter(e=>!e.required))console.log(`[optional] ${e.package} is not installed; add it when a component or chart needs it: ${e.hint}`);e&&(r.violations.length>0||f.length>0||m||g!==null||y.issues>0)&&(process.exitCode=1)}}break}case`audit`:{let{audit:e}=await import(`./audit-DqWSsQGC.mjs`);await e(r,await t());break}case`email`:{let{email:e}=await import(`./email-CwQcZ-mB.mjs`);await e(r);break}case`tokens`:if(r[0]===`import`){let{tokensImport:e}=await import(`./tokens-VSJC9GgT.mjs`);await e(r.slice(1))}else console.error(`Unknown tokens subcommand: ${r[0]}`),process.exitCode=1;break;case void 0:case`--help`:case`-h`:case`help`:console.log(pn);break;case`--version`:case`-v`:console.log(Q);break;default:console.error(`Unknown command: ${n}\n`),console.log(pn),process.exitCode=1}}O[1]!==void 0&&import.meta.url===ne(O[1]).href&&hn(O.slice(2)).catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});export{Q as VERSION,hn as run};
602
628
  //# sourceMappingURL=index.mjs.map