@shortwind/cli 0.1.0-beta.13 → 0.1.0-beta.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.js","names":["runPreset"],"sources":["../src/commands/doctor.ts","../src/cli.ts","../src/bin.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { glob } from \"tinyglobby\";\nimport { buildRegistry, parseRecipeFile } from \"@shortwind/core\";\nimport type { Recipe, Registry } from \"@shortwind/core\";\nimport { findResidualRecipeTokens } from \"@shortwind/tailwind\";\nimport { installedFamilies, readConfig } from \"../project.js\";\nimport { DEFAULT_CONTENT, extractClassUsages } from \"./lint.js\";\n\nexport type DoctorVerdict =\n // No raw recipe tokens in any scanned output file.\n | \"clean\"\n // Raw tokens found AND every recipe referenced in source is among them —\n // nothing was expanded, so the adapter almost certainly never ran.\n | \"not-wired\"\n // Raw tokens found but other recipes did expand — the transform ran and\n // these specific tokens escaped it (dynamic className, opaque region, …).\n | \"leak\"\n // No build output directory found to scan.\n | \"no-output\";\n\nexport type DoctorFinding = { file: string; tokens: string[] };\n\nexport type DoctorOptions = {\n cwd: string;\n // Output directories to scan, relative to cwd. Defaults to whichever of\n // .next/, dist/, out/, build/ exist.\n dirs?: string[];\n // Source globs for the \"what does the project actually use\" scan; same\n // semantics and fallback chain as lint's content option.\n content?: string[];\n};\n\nexport type DoctorResult = {\n ok: boolean;\n verdict: DoctorVerdict;\n outputDirs: string[];\n scannedFiles: number;\n findings: DoctorFinding[];\n // Known recipes referenced from source files, as @-tokens, sorted.\n usedInSource: string[];\n};\n\nconst DEFAULT_OUTPUT_DIRS = [\".next\", \"dist\", \"out\", \"build\"];\n\n// Only formats a framework emits markup/scripts into. Sourcemaps embed the\n// original source (raw tokens are expected there) and are excluded by not\n// being listed; CSS never legitimately carries an @recipe token but Tailwind\n// output is full of @media/@container at-rules, so it stays out too.\nconst OUTPUT_GLOB = \"**/*.{html,htm,js,mjs,cjs,rsc}\";\n\n// Bundler caches (.next/cache, .vite cache dirs) store pre-transform source.\nconst OUTPUT_IGNORE = [\"**/node_modules/**\", \"**/cache/**\"];\n\nexport async function doctor(options: DoctorOptions): Promise<DoctorResult> {\n const cwd = path.resolve(options.cwd);\n const config = await readConfig(cwd);\n const recipesDir = path.join(cwd, config.recipesDir);\n const registry = loadRegistryLenient(recipesDir);\n\n const outputDirs = (options.dirs ?? DEFAULT_OUTPUT_DIRS).filter((d) =>\n existsSync(path.join(cwd, d)),\n );\n if (outputDirs.length === 0) {\n return {\n ok: false,\n verdict: \"no-output\",\n outputDirs: [],\n scannedFiles: 0,\n findings: [],\n usedInSource: [],\n };\n }\n\n const files = await glob(\n outputDirs.map((d) => path.posix.join(d.split(path.sep).join(\"/\"), OUTPUT_GLOB)),\n { cwd, absolute: true, onlyFiles: true, ignore: OUTPUT_IGNORE },\n );\n\n const findings: DoctorFinding[] = [];\n for (const file of files.sort()) {\n const code = await readFile(file, \"utf8\");\n const tokens = findResidualRecipeTokens(code, registry);\n if (tokens.length > 0) findings.push({ file, tokens });\n }\n\n const usedInSource = await scanSourceUsage(cwd, recipesDir, registry, options.content ?? config.content);\n\n let verdict: DoctorVerdict = \"clean\";\n if (findings.length > 0) {\n const raw = new Set(findings.flatMap((f) => f.tokens));\n verdict =\n usedInSource.length > 0 && usedInSource.every((t) => raw.has(t))\n ? \"not-wired\"\n : \"leak\";\n }\n\n return {\n ok: verdict === \"clean\",\n verdict,\n outputDirs,\n scannedFiles: files.length,\n findings,\n usedInSource,\n };\n}\n\n// Doctor's job is scanning build output, not validating recipes — a registry\n// that fails to resolve (cycle, unknown ref: lint's territory) must not stop\n// the leak scan. Fall back to a name-only registry so known-token matching\n// still works.\nfunction loadRegistryLenient(recipesDir: string): Registry {\n const allRecipes: Recipe[] = [];\n for (const family of installedFamilies(recipesDir)) {\n const source = readFileSync(path.join(recipesDir, `${family}.css`), \"utf8\");\n const parsed = parseRecipeFile(source, `${family}.css`);\n if (parsed.ok) allRecipes.push(...parsed.value.recipes);\n }\n const built = buildRegistry(allRecipes);\n if (built.ok) return built.value;\n return {\n flattened: Object.fromEntries(allRecipes.map((r) => [r.name, []])),\n families: {},\n };\n}\n\nasync function scanSourceUsage(\n cwd: string,\n recipesDir: string,\n registry: Registry,\n content: string[] | undefined,\n): Promise<string[]> {\n // Same project-relative recipes ignore as lint (see lint.ts for why the\n // absolute form is unsafe with tinyglobby).\n const recipesIgnore = path.posix.join(\n path.relative(cwd, recipesDir).split(path.sep).join(\"/\") || \".\",\n \"**\",\n );\n const files = await glob(content ?? DEFAULT_CONTENT, {\n cwd,\n absolute: true,\n onlyFiles: true,\n ignore: [\"**/node_modules/**\", \"**/dist/**\", \"**/.next/**\", recipesIgnore],\n });\n const used = new Set<string>();\n for (const file of files) {\n const source = await readFile(file, \"utf8\");\n for (const usage of extractClassUsages(source)) {\n for (const token of usage.tokens) {\n if (!token.value.startsWith(\"@\")) continue;\n if (Object.hasOwn(registry.flattened, token.value.slice(1))) used.add(token.value);\n }\n }\n }\n return [...used].sort();\n}\n","import path from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { cac } from \"cac\";\nimport { add } from \"./commands/add.js\";\nimport { build, BuildError } from \"./commands/build.js\";\nimport { dev } from \"./commands/dev.js\";\nimport { remove } from \"./commands/remove.js\";\nimport { preset as runPreset } from \"./commands/preset.js\";\nimport { ls, formatLsText } from \"./commands/ls.js\";\nimport {\n upgrade,\n UpgradeError,\n type TouchedContext,\n type UpgradeChoice,\n} from \"./commands/upgrade.js\";\nimport { verify, type VerifyIssue } from \"./commands/verify.js\";\nimport { doctor } from \"./commands/doctor.js\";\nimport { detectProject, type Bundler } from \"./detect.js\";\nimport { lint, formatFindingsText, ALL_RULES, type Rule } from \"./commands/lint.js\";\nimport { init, cliVersion, type InitOptions, DEFAULT_REGISTRY } from \"./init.js\";\nimport { bench, formatBenchTable } from \"./commands/bench.js\";\nimport { newFamily, NewFamilyError } from \"./commands/new.js\";\nimport { reseal } from \"./commands/reseal.js\";\n\nconst KNOWN_PRESETS = [\"starter\", \"app\", \"content\", \"all\", \"none\"];\nexport const DEFAULT_PRESET = \"starter\";\n\n// Decide the init preset without forcing a TTY: an explicit --preset wins,\n// --yes/-y takes the default, and only the bare interactive call prompts —\n// agents and CI run `init --yes` unattended (#68).\nexport async function resolveInitPreset(\n opts: { preset?: string; yes?: boolean },\n prompt: () => Promise<string>,\n): Promise<string> {\n if (opts.preset !== undefined) return opts.preset;\n if (opts.yes) return DEFAULT_PRESET;\n return prompt();\n}\n\nexport async function run(argv: string[] = process.argv): Promise<void> {\n const cli = cac(\"shortwind\");\n\n cli\n .command(\"init\", \"Bootstrap Shortwind in this project\")\n .option(\"--preset <name>\", \"Preset to install (starter|app|content|all|none)\")\n .option(\"-y, --yes\", `Skip prompts and use the default preset (${DEFAULT_PRESET})`)\n .option(\"--registry <url>\", \"Registry origin\", { default: DEFAULT_REGISTRY })\n .option(\"--cwd <dir>\", \"Working directory\", { default: process.cwd() })\n .action(async (opts: { preset?: string; yes?: boolean; registry?: string; cwd?: string }) => {\n const preset = await resolveInitPreset(opts, promptForPreset);\n const options: InitOptions = {\n cwd: opts.cwd ?? process.cwd(),\n preset,\n };\n if (opts.registry !== undefined) options.registry = opts.registry;\n const result = await init(options);\n printInitSummary(result);\n });\n\n cli\n .command(\"add <...families>\", \"Install one or more families\")\n .option(\"--as <name>\", \"Rename the family on install (requires a single family)\")\n .option(\"--all\", \"Install every family in the registry\")\n .option(\"--force\", \"Overwrite existing files\")\n .option(\"--registry <url>\", \"Registry origin\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(\n async (\n families: string[],\n opts: { as?: string; all?: boolean; force?: boolean; registry?: string; cwd?: string },\n ) => {\n const addOptions: Parameters<typeof add>[0] = {\n cwd: opts.cwd ?? process.cwd(),\n families,\n };\n if (opts.as !== undefined) addOptions.as = opts.as;\n if (opts.all) addOptions.all = true;\n if (opts.force) addOptions.force = true;\n if (opts.registry !== undefined) addOptions.registry = opts.registry;\n const result = await add(addOptions);\n for (const fam of result.added) p.log.success(`added ${fam}`);\n for (const fam of result.overwritten) p.log.success(`overwrote ${fam}`);\n for (const fam of result.skipped) p.log.warn(`${fam} already exists (use --force)`);\n for (const missing of result.missingDependencies) {\n p.log.warn(\n `${missing.family} references unknown recipes: ${missing.references.join(\", \")}`,\n );\n }\n },\n );\n\n cli\n .command(\"new <family>\", \"Scaffold a new custom recipe family file\")\n .option(\"--force\", \"Overwrite an existing family file\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (family: string, opts: { force?: boolean; cwd?: string }) => {\n try {\n const result = await newFamily({\n cwd: opts.cwd ?? process.cwd(),\n family,\n ...(opts.force ? { force: true } : {}),\n });\n p.log.success(`created ${result.familyPath}`);\n p.log.info(`regenerated ${result.skillPath} — edit the recipes, then \\`shortwind build\\` to refresh.`);\n } catch (err) {\n if (err instanceof NewFamilyError) {\n process.stderr.write(err.message + \"\\n\");\n process.exit(1);\n }\n throw err;\n }\n });\n\n cli\n .command(\"remove <...families>\", \"Remove installed families\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (families: string[], opts: { cwd?: string }) => {\n const result = await remove({ cwd: opts.cwd ?? process.cwd(), families });\n for (const fam of result.removed) p.log.success(`removed ${fam}`);\n for (const fam of result.notFound) p.log.warn(`${fam} is not installed`);\n for (const broken of result.brokenDependents) {\n p.log.warn(\n `${broken.dependent} references removed recipes: ${broken.references.join(\", \")}`,\n );\n }\n });\n\n cli\n .command(\"preset <name>\", \"Install every family in the named preset (additive)\")\n .option(\"--registry <url>\", \"Registry origin\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (name: string, opts: { registry?: string; cwd?: string }) => {\n const presetOptions: Parameters<typeof runPreset>[0] = {\n cwd: opts.cwd ?? process.cwd(),\n name,\n };\n if (opts.registry !== undefined) presetOptions.registry = opts.registry;\n const result = await runPreset(presetOptions);\n p.log.info(`preset ${name}: ${result.added.length} added, ${result.skipped.length} already present`);\n });\n\n cli\n .command(\"ls\", \"List installed and available families\")\n .option(\"--installed\", \"Only installed\")\n .option(\"--available\", \"Only available\")\n .option(\"--json\", \"Emit JSON\")\n .option(\"--registry <url>\", \"Registry origin\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(\n async (opts: {\n installed?: boolean;\n available?: boolean;\n json?: boolean;\n registry?: string;\n cwd?: string;\n }) => {\n const lsOptions: Parameters<typeof ls>[0] = {\n cwd: opts.cwd ?? process.cwd(),\n };\n if (opts.installed) lsOptions.installedOnly = true;\n if (opts.available) lsOptions.availableOnly = true;\n if (opts.registry !== undefined) lsOptions.registry = opts.registry;\n const result = await ls(lsOptions);\n if (opts.json) {\n process.stdout.write(JSON.stringify(result, null, 2) + \"\\n\");\n } else {\n process.stdout.write(formatLsText(result) + \"\\n\");\n }\n },\n );\n\n cli\n .command(\"build\", \"Regenerate SKILL.md from ./recipes/\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (opts: { cwd?: string }) => {\n try {\n const result = await build({ cwd: opts.cwd ?? process.cwd() });\n if (result.changed) p.log.success(`regenerated ${result.families.length} families`);\n else p.log.info(`up to date (${result.families.length} families)`);\n } catch (err) {\n if (err instanceof BuildError) {\n process.stderr.write(err.message + \"\\n\");\n process.exit(1);\n }\n throw err;\n }\n });\n\n cli\n .command(\"dev\", \"Watch ./recipes/ and regenerate SKILL.md on change\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .option(\"--once\", \"Run build once and exit\")\n .action(async (opts: { cwd?: string; once?: boolean }) => {\n if (opts.once) {\n try {\n await build({ cwd: opts.cwd ?? process.cwd() });\n } catch (err) {\n if (err instanceof BuildError) {\n process.stderr.write(err.message + \"\\n\");\n process.exit(1);\n }\n throw err;\n }\n return;\n }\n const controller = new AbortController();\n const onSigint = (): void => controller.abort();\n process.on(\"SIGINT\", onSigint);\n try {\n const { stop } = await dev({\n cwd: opts.cwd ?? process.cwd(),\n signal: controller.signal,\n onStatus: (status) => {\n if (status.kind === \"rebuilt\") {\n const msg = `✓ regenerated ${status.families.length} families${status.changed ? \"\" : \" (no changes)\"}`;\n process.stdout.write(msg + \"\\n\");\n } else if (status.kind === \"ready\") {\n process.stdout.write(`watching ${status.recipesDir}\\n`);\n } else {\n process.stderr.write(status.message + \"\\n\");\n }\n },\n });\n // Block until SIGINT (or otherwise aborted), then close the watcher\n // and exit cleanly — without this the listener stays attached and\n // controller.abort() never returns control to the CLI.\n await new Promise<void>((resolve) => {\n if (controller.signal.aborted) resolve();\n else controller.signal.addEventListener(\"abort\", () => resolve(), { once: true });\n });\n await stop();\n } finally {\n process.off(\"SIGINT\", onSigint);\n }\n process.exit(0);\n });\n\n cli\n .command(\"upgrade [...families]\", \"Pull registry updates into ./recipes\")\n .option(\"--check\", \"Read-only — print drift summary and exit nonzero on updates\")\n .option(\"--force\", \"Skip touched-detection; overwrite local edits\")\n .option(\"--registry <url>\", \"Registry origin\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(\n async (\n families: string[],\n opts: { check?: boolean; force?: boolean; registry?: string; cwd?: string },\n ) => {\n try {\n const upgradeOptions: Parameters<typeof upgrade>[0] = {\n cwd: opts.cwd ?? process.cwd(),\n families,\n };\n if (opts.check) upgradeOptions.check = true;\n if (opts.force) upgradeOptions.force = true;\n if (opts.registry !== undefined) upgradeOptions.registry = opts.registry;\n if (!opts.check && !opts.force) {\n upgradeOptions.resolver = makeInteractiveResolver();\n }\n const result = await upgrade(upgradeOptions);\n printUpgradeSummary(result.outcomes);\n if (opts.check) {\n if (result.hasUpdates || result.hasTouched) process.exit(1);\n }\n } catch (err) {\n if (err instanceof UpgradeError) {\n process.stderr.write(err.message + \"\\n\");\n process.exit(2);\n }\n throw err;\n }\n },\n );\n\n cli\n .command(\"reseal [...families]\", \"Re-sign recipes after intentional edits (updates header sha + lockfile)\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (families: string[], opts: { cwd?: string }) => {\n const result = await reseal({ cwd: opts.cwd ?? process.cwd(), families });\n for (const f of result.resealed) p.log.success(`resealed ${f}`);\n for (const f of result.unchanged) p.log.info(`${f} already sealed`);\n for (const f of result.notFound) p.log.warn(`${f} is not installed`);\n for (const f of result.noHeader) p.log.warn(`${f} has no fingerprint header`);\n if (result.resealed.length === 0 && result.unchanged.length > 0 && result.notFound.length === 0) {\n p.log.success(\"all recipes already sealed\");\n }\n });\n\n cli\n .command(\"verify\", \"Check installed recipes against fingerprint headers and lockfile\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (opts: { cwd?: string }) => {\n const result = await verify({ cwd: opts.cwd ?? process.cwd() });\n if (result.ok) {\n p.log.success(`verified ${result.checked.length} families`);\n return;\n }\n for (const issue of result.issues) {\n const desc = describeVerifyIssue(issue);\n process.stderr.write(`${issue.family}: ${desc}\\n`);\n }\n process.exit(1);\n });\n\n cli\n .command(\"doctor\", \"Scan build output for unexpanded @recipe tokens\")\n .option(\"--dir <path>\", \"Output directory to scan (repeatable; default: .next, dist, out, build)\")\n .option(\"--json\", \"Emit machine-readable JSON\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (opts: { dir?: string | string[]; json?: boolean; cwd?: string }) => {\n const cwd = opts.cwd ?? process.cwd();\n const doctorOptions: Parameters<typeof doctor>[0] = { cwd };\n if (opts.dir !== undefined) {\n doctorOptions.dirs = Array.isArray(opts.dir) ? opts.dir : [opts.dir];\n }\n const result = await doctor(doctorOptions);\n if (opts.json) {\n process.stdout.write(JSON.stringify(result, null, 2) + \"\\n\");\n } else {\n printDoctorReport(result, cwd);\n }\n if (result.verdict === \"no-output\") process.exit(2);\n if (!result.ok) process.exit(1);\n });\n\n cli\n .command(\"lint\", \"Static analysis over source files and recipes\")\n .option(\"--fix\", \"Apply auto-fixes where supported\")\n .option(\"--rule <rule>\", \"Only run the named rule (repeatable)\")\n .option(\"--content <glob>\", \"Source glob to scan for recipe usage (repeatable; overrides config)\")\n .option(\"--json\", \"Emit machine-readable JSON\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(\n async (opts: {\n fix?: boolean;\n rule?: string | string[];\n content?: string | string[];\n json?: boolean;\n cwd?: string;\n }) => {\n const requested = opts.rule === undefined ? [] : Array.isArray(opts.rule) ? opts.rule : [opts.rule];\n for (const r of requested) {\n if (!ALL_RULES.includes(r as Rule)) {\n process.stderr.write(`unknown rule: ${r}\\n`);\n process.stderr.write(`available: ${ALL_RULES.join(\", \")}\\n`);\n process.exit(2);\n }\n }\n const lintOptions: Parameters<typeof lint>[0] = { cwd: opts.cwd ?? process.cwd() };\n if (opts.fix) lintOptions.fix = true;\n if (requested.length > 0) lintOptions.rules = requested as Rule[];\n if (opts.content !== undefined) {\n lintOptions.content = Array.isArray(opts.content) ? opts.content : [opts.content];\n }\n const result = await lint(lintOptions);\n if (opts.json) {\n process.stdout.write(JSON.stringify(result, null, 2) + \"\\n\");\n } else {\n const text = formatFindingsText(result.findings);\n if (text) process.stdout.write(text + \"\\n\");\n for (const f of result.filesFixed) p.log.success(`fixed ${f}`);\n if (result.scannedFiles === 0) {\n p.log.warn(\n `content scan matched no source files — usage rules (recipe/unused) were skipped.\\n` +\n `Point lint at your sources with \"content\" in shortwind.config.json or --content <glob>.`,\n );\n }\n if (result.findings.length === 0) p.log.success(\"no findings\");\n }\n if (!result.ok) process.exit(1);\n },\n );\n\n cli\n .command(\"bench [path]\", \"Benchmark token savings in a corpus or target directory\")\n .option(\"--corpus\", \"Run benchmark on the built-in corpus\")\n .option(\"--json\", \"Emit machine-readable JSON\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (pathArg: string | undefined, opts: { corpus?: boolean; json?: boolean; cwd?: string }) => {\n const benchOptions: Parameters<typeof bench>[0] = { cwd: opts.cwd ?? process.cwd() };\n if (opts.corpus) benchOptions.corpus = true;\n if (pathArg !== undefined) benchOptions.path = pathArg;\n const result = await bench(benchOptions);\n if (opts.json) {\n process.stdout.write(JSON.stringify(result, null, 2) + \"\\n\");\n } else {\n const text = formatBenchTable(result);\n process.stdout.write(text + \"\\n\");\n }\n });\n\n cli.help();\n cli.version(cliVersion() ?? \"0.0.0\");\n // cac's parse() invokes the matched command's async action but does NOT await\n // it, so a rejection inside any async command (network failure in `add`, a\n // rethrow in `build`/`upgrade`, …) escapes bin.ts's `run().catch` and prints\n // a raw unhandled-rejection dump. Parse without running, then await the\n // command so its promise flows back to the caller's catch.\n cli.parse(argv, { run: false });\n await cli.runMatchedCommand();\n}\n\nfunction makeInteractiveResolver() {\n return async (ctx: TouchedContext): Promise<UpgradeChoice> => {\n process.stdout.write(\n pc.yellow(`\\n${ctx.family}: locally modified — incoming ${ctx.incoming.version}\\n`),\n );\n const choice = await p.select({\n message: `Resolve ${ctx.family}`,\n options: [\n { value: \"accept\", label: \"Accept new (discard local edits)\" },\n { value: \"keep\", label: \"Keep yours\" },\n { value: \"skip\", label: \"Skip for now\" },\n ],\n });\n if (p.isCancel(choice)) return \"skip\";\n return choice as UpgradeChoice;\n };\n}\n\nfunction printUpgradeSummary(outcomes: Awaited<ReturnType<typeof upgrade>>[\"outcomes\"]): void {\n for (const o of outcomes) {\n if (o.action === \"updated\") {\n process.stdout.write(pc.green(`✓ ${o.family} ${o.from} → ${o.to}\\n`));\n } else if (o.action === \"would-update\") {\n process.stdout.write(pc.cyan(`→ ${o.family} ${o.from} → ${o.to} (available)\\n`));\n } else if (o.action === \"would-review\") {\n process.stdout.write(pc.yellow(`! ${o.family} ${o.from} → ${o.to} (touched; needs review)\\n`));\n } else if (o.action === \"kept\" && o.reason === \"user-chose-keep\") {\n process.stdout.write(pc.dim(` ${o.family} kept local\\n`));\n } else if (o.action === \"skipped\") {\n process.stdout.write(pc.dim(` ${o.family} skipped (${o.reason})\\n`));\n }\n }\n}\n\n// The whole point of doctor (#84) is telling \"you forgot to wire the adapter\"\n// apart from \"the adapter ran and something leaked\" — both otherwise present\n// as a green build that ships raw @recipe text.\nfunction printDoctorReport(result: Awaited<ReturnType<typeof doctor>>, cwd: string): void {\n if (result.verdict === \"no-output\") {\n p.log.error(\n `no build output found (looked for .next/, dist/, out/, build/).\\n` +\n `Run your framework's build first, or point doctor at it with --dir <path>.`,\n );\n return;\n }\n if (result.verdict === \"clean\") {\n p.log.success(\n `no unexpanded recipe tokens in ${result.outputDirs.join(\", \")} (${result.scannedFiles} files scanned)`,\n );\n return;\n }\n for (const f of result.findings) {\n process.stderr.write(`${path.relative(cwd, f.file)}: ${f.tokens.join(\", \")}\\n`);\n }\n const tokenCount = new Set(result.findings.flatMap((f) => f.tokens)).size;\n if (result.verdict === \"not-wired\") {\n const bundler = detectProject(cwd).bundler;\n p.log.error(\n `found ${tokenCount} raw @recipe token${tokenCount === 1 ? \"\" : \"s\"} in build output and ` +\n `every recipe your source references is among them — it looks like no Shortwind ` +\n `transform ran during the build.\\n` +\n `Is the adapter wired? ${adapterHint(bundler)}\\n` +\n `Setup guide: ${setupGuideUrl(bundler)}`,\n );\n } else {\n p.log.error(\n `found ${tokenCount} raw @recipe token${tokenCount === 1 ? \"\" : \"s\"} in build output. ` +\n `The Shortwind transform ran (other recipes expanded), so these specific tokens ` +\n `escaped it — typically a className built from a variable/prop/template, or markup ` +\n `in a region the expander treats as opaque.\\n` +\n `See https://shortwind.dev/docs/dynamic-classes`,\n );\n }\n}\n\n// Slugs match site/src/content/docs/setup-<bundler>.md (#85).\nfunction setupGuideUrl(bundler: Bundler): string {\n return bundler === \"unknown\"\n ? \"https://shortwind.dev/docs/install\"\n : `https://shortwind.dev/docs/setup-${bundler}`;\n}\n\nfunction adapterHint(bundler: Bundler): string {\n switch (bundler) {\n case \"next\":\n return \"Next needs `export default withShortwind()(nextConfig)` in next.config — note the call is curried.\";\n case \"vite\":\n return \"Vite needs `shortwind()` in the vite.config plugins array: `plugins: [shortwind(), tailwindcss(), ...]`.\";\n case \"astro\":\n return \"Astro needs `shortwind()` in astro.config `integrations`.\";\n default:\n return \"Add the Shortwind plugin for your bundler (see the setup guide).\";\n }\n}\n\nfunction describeVerifyIssue(issue: VerifyIssue): string {\n switch (issue.kind) {\n case \"missing-header\":\n return \"no fingerprint header — recipe was hand-stripped\";\n case \"header-tampered\":\n return `header sha ${issue.recorded} but body hashes to ${issue.actual}`;\n case \"legacy-fingerprint\":\n return `sealed with an older fingerprint format (${issue.recorded}) — run \\`shortwind reseal\\` to upgrade it (the recipe body is unchanged)`;\n case \"lockfile-mismatch\":\n return `lockfile expects ${issue.locked} but body hashes to ${issue.actual}`;\n case \"missing-lock-entry\":\n return \"installed but not recorded in lockfile\";\n case \"missing-file\":\n return \"in lockfile but file is missing\";\n }\n}\n\nasync function promptForPreset(): Promise<string> {\n const choice = await p.select({\n message: \"Pick a preset\",\n options: KNOWN_PRESETS.map((name) => ({ value: name, label: name })),\n });\n if (p.isCancel(choice)) {\n p.cancel(\"Cancelled.\");\n process.exit(0);\n }\n return choice;\n}\n\nfunction installCmd(pm: Awaited<ReturnType<typeof init>>[\"packageManager\"]): string {\n switch (pm) {\n case \"bun\":\n return \"bun add -d\";\n case \"npm\":\n return \"npm install -D\";\n default:\n return `${pm} add -D`;\n }\n}\n\nfunction printInitSummary(result: Awaited<ReturnType<typeof init>>): void {\n p.note(\n [\n `preset: ${result.preset}`,\n `registry: ${result.registry}`,\n `package manager: ${result.packageManager}`,\n `families copied: ${result.installedFamilies.length}`,\n `families skipped: ${result.skippedFamilies.length}`,\n `adapters: ${result.installOk ? result.installedPackages.join(\", \") || \"none\" : \"install failed — see below\"}`,\n ``,\n `config: ${result.configPath}`,\n `vscode settings: ${result.vscodePath}`,\n `pre-commit: ${result.huskyPath ?? \"skipped (not a git repository)\"}`,\n `SKILL.md: ${result.skillPath}`,\n `theme: ${describeTheme(result)}`,\n `bundler config: ${describeBundlerConfig(result)}`,\n `agent guide: ${describeAgentsFile(result)}`,\n ].join(\"\\n\"),\n \"shortwind init\",\n );\n if (!result.installOk) {\n const v = cliVersion();\n const specs = result.installedPackages.map((p) => (v ? `${p}@${v}` : p)).join(\" \");\n p.log.warn(\n `Couldn't auto-install adapters (your recipes and config were still scaffolded).\\n` +\n `Finish by installing them yourself:\\n\\n` +\n ` ${installCmd(result.packageManager)} ${specs}\\n\\n` +\n `(${result.installError ?? \"unknown error\"})`,\n );\n }\n if (result.bundlerConfigAction === \"manual\" && result.bundlerConfigSnippet) {\n p.log.warn(`Add the plugin to your bundler config:\\n\\n${result.bundlerConfigSnippet}`);\n }\n p.log.info(`Setup guide for your stack: ${setupGuideUrl(result.bundler)}`);\n if (result.themeAction === \"supplemented\") {\n p.log.info(\n `Your theme (${result.themePath}) didn't define ${result.supplementedThemeTokens.length} design token${result.supplementedThemeTokens.length === 1 ? \"\" : \"s\"} the installed recipes use:\\n\\n` +\n ` ${result.supplementedThemeTokens.join(\", \")}\\n\\n` +\n `Appended them with neutral placeholder values (marked block at the end of the file) so recipes render on first run — tune them to your palette.\\n` +\n `Reference values: https://shortwind.dev/docs/install#theme-tokens`,\n );\n }\n if (result.missingThemeTokens.length > 0) {\n p.log.warn(\n `Your existing theme (${result.themePath}) does not define ${result.missingThemeTokens.length} design token${result.missingThemeTokens.length === 1 ? \"\" : \"s\"} the installed recipes use:\\n\\n` +\n ` ${result.missingThemeTokens.join(\", \")}\\n\\n` +\n `Recipes referencing them will render colorless until you add the tokens to your @theme.\\n` +\n `The default token block is documented at https://shortwind.dev/docs/install#theme-tokens`,\n );\n }\n p.outro(\n `Next: run \\`${devCmd(result.packageManager)}\\` and check a recipe renders. After a production build, \\`npx shortwind doctor\\` verifies nothing shipped unexpanded.`,\n );\n}\n\n// `npm dev` is not a thing — npm needs the `run` form; pnpm/yarn/bun accept\n// the bare script name.\nfunction devCmd(pm: Awaited<ReturnType<typeof init>>[\"packageManager\"]): string {\n return pm === \"npm\" ? \"npm run dev\" : `${pm} dev`;\n}\n\nfunction describeAgentsFile(result: Awaited<ReturnType<typeof init>>): string {\n switch (result.agentsFileAction) {\n case \"created\":\n return `wrote ${result.agentsFilePath}`;\n case \"appended\":\n return `added recipe pointer to ${result.agentsFilePath}`;\n case \"skipped\":\n return `pointer already in ${result.agentsFilePath}`;\n }\n}\n\nfunction describeBundlerConfig(result: Awaited<ReturnType<typeof init>>): string {\n switch (result.bundlerConfigAction) {\n case \"patched\":\n return `plugin added to ${result.bundlerConfigPath}`;\n case \"manual\":\n return \"needs a manual edit (see below)\";\n case \"skipped\":\n return result.bundlerConfigPath\n ? `already wired in ${result.bundlerConfigPath}`\n : \"skipped (no supported bundler)\";\n }\n}\n\nfunction describeTheme(result: Awaited<ReturnType<typeof init>>): string {\n switch (result.themeAction) {\n case \"injected\":\n return `tokens added to ${result.themePath}`;\n case \"created\":\n return `wrote ${result.themePath}`;\n case \"supplemented\":\n return `kept your theme; appended ${result.supplementedThemeTokens.length} missing tokens to ${result.themePath}`;\n case \"skipped\":\n return result.themePath\n ? `left existing theme in ${result.themePath} untouched`\n : \"skipped (no Tailwind v4 CSS entry — define color tokens yourself)\";\n }\n}\n","#!/usr/bin/env node\nimport { run } from \"./cli.js\";\n\nrun().catch((err) => {\n console.error(err instanceof Error ? err.stack ?? err.message : err);\n process.exit(1);\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"bin.js","names":["runPreset"],"sources":["../src/commands/doctor.ts","../src/cli.ts","../src/bin.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { glob } from \"tinyglobby\";\nimport { buildRegistry, parseRecipeFile } from \"@shortwind/core\";\nimport type { Recipe, Registry } from \"@shortwind/core\";\nimport { findResidualRecipeTokens } from \"@shortwind/tailwind\";\nimport { installedFamilies, readConfig } from \"../project.js\";\nimport { DEFAULT_CONTENT, extractClassUsages } from \"./lint.js\";\n\nexport type DoctorVerdict =\n // No raw recipe tokens in any scanned output file.\n | \"clean\"\n // Raw tokens found AND every recipe referenced in source is among them —\n // nothing was expanded, so the adapter almost certainly never ran.\n | \"not-wired\"\n // Raw tokens found but other recipes did expand — the transform ran and\n // these specific tokens escaped it (dynamic className, opaque region, …).\n | \"leak\"\n // No build output directory found to scan.\n | \"no-output\";\n\nexport type DoctorFinding = { file: string; tokens: string[] };\n\nexport type DoctorOptions = {\n cwd: string;\n // Output directories to scan, relative to cwd. Defaults to whichever of\n // .next/, dist/, out/, build/ exist.\n dirs?: string[];\n // Source globs for the \"what does the project actually use\" scan; same\n // semantics and fallback chain as lint's content option.\n content?: string[];\n};\n\nexport type DoctorResult = {\n ok: boolean;\n verdict: DoctorVerdict;\n outputDirs: string[];\n scannedFiles: number;\n findings: DoctorFinding[];\n // Known recipes referenced from source files, as @-tokens, sorted.\n usedInSource: string[];\n};\n\nconst DEFAULT_OUTPUT_DIRS = [\".next\", \"dist\", \"out\", \"build\"];\n\n// Only formats a framework emits markup/scripts into. Sourcemaps embed the\n// original source (raw tokens are expected there) and are excluded by not\n// being listed; CSS never legitimately carries an @recipe token but Tailwind\n// output is full of @media/@container at-rules, so it stays out too.\nconst OUTPUT_GLOB = \"**/*.{html,htm,js,mjs,cjs,rsc}\";\n\n// Bundler caches (.next/cache, .vite cache dirs) store pre-transform source.\nconst OUTPUT_IGNORE = [\"**/node_modules/**\", \"**/cache/**\"];\n\nexport async function doctor(options: DoctorOptions): Promise<DoctorResult> {\n const cwd = path.resolve(options.cwd);\n const config = await readConfig(cwd);\n const recipesDir = path.join(cwd, config.recipesDir);\n const registry = loadRegistryLenient(recipesDir);\n\n const outputDirs = (options.dirs ?? DEFAULT_OUTPUT_DIRS).filter((d) =>\n existsSync(path.join(cwd, d)),\n );\n if (outputDirs.length === 0) {\n return {\n ok: false,\n verdict: \"no-output\",\n outputDirs: [],\n scannedFiles: 0,\n findings: [],\n usedInSource: [],\n };\n }\n\n const files = await glob(\n outputDirs.map((d) => path.posix.join(d.split(path.sep).join(\"/\"), OUTPUT_GLOB)),\n { cwd, absolute: true, onlyFiles: true, ignore: OUTPUT_IGNORE },\n );\n\n const findings: DoctorFinding[] = [];\n for (const file of files.sort()) {\n const code = await readFile(file, \"utf8\");\n const tokens = findResidualRecipeTokens(code, registry);\n if (tokens.length > 0) findings.push({ file, tokens });\n }\n\n const usedInSource = await scanSourceUsage(cwd, recipesDir, registry, options.content ?? config.content);\n\n let verdict: DoctorVerdict = \"clean\";\n if (findings.length > 0) {\n const raw = new Set(findings.flatMap((f) => f.tokens));\n verdict =\n usedInSource.length > 0 && usedInSource.every((t) => raw.has(t))\n ? \"not-wired\"\n : \"leak\";\n }\n\n return {\n ok: verdict === \"clean\",\n verdict,\n outputDirs,\n scannedFiles: files.length,\n findings,\n usedInSource,\n };\n}\n\n// Doctor's job is scanning build output, not validating recipes — a registry\n// that fails to resolve (cycle, unknown ref: lint's territory) must not stop\n// the leak scan. Fall back to a name-only registry so known-token matching\n// still works.\nfunction loadRegistryLenient(recipesDir: string): Registry {\n const allRecipes: Recipe[] = [];\n for (const family of installedFamilies(recipesDir)) {\n const source = readFileSync(path.join(recipesDir, `${family}.css`), \"utf8\");\n const parsed = parseRecipeFile(source, `${family}.css`);\n if (parsed.ok) allRecipes.push(...parsed.value.recipes);\n }\n const built = buildRegistry(allRecipes);\n if (built.ok) return built.value;\n return {\n flattened: Object.fromEntries(allRecipes.map((r) => [r.name, []])),\n families: {},\n };\n}\n\nasync function scanSourceUsage(\n cwd: string,\n recipesDir: string,\n registry: Registry,\n content: string[] | undefined,\n): Promise<string[]> {\n // Same project-relative recipes ignore as lint (see lint.ts for why the\n // absolute form is unsafe with tinyglobby).\n const recipesIgnore = path.posix.join(\n path.relative(cwd, recipesDir).split(path.sep).join(\"/\") || \".\",\n \"**\",\n );\n const files = await glob(content ?? DEFAULT_CONTENT, {\n cwd,\n absolute: true,\n onlyFiles: true,\n ignore: [\"**/node_modules/**\", \"**/dist/**\", \"**/.next/**\", recipesIgnore],\n });\n const used = new Set<string>();\n for (const file of files) {\n const source = await readFile(file, \"utf8\");\n for (const usage of extractClassUsages(source)) {\n for (const token of usage.tokens) {\n if (!token.value.startsWith(\"@\")) continue;\n if (Object.hasOwn(registry.flattened, token.value.slice(1))) used.add(token.value);\n }\n }\n }\n return [...used].sort();\n}\n","import path from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { cac } from \"cac\";\nimport { add } from \"./commands/add.js\";\nimport { build, BuildError } from \"./commands/build.js\";\nimport { dev } from \"./commands/dev.js\";\nimport { remove } from \"./commands/remove.js\";\nimport { preset as runPreset } from \"./commands/preset.js\";\nimport { ls, formatLsText } from \"./commands/ls.js\";\nimport {\n upgrade,\n UpgradeError,\n type TouchedContext,\n type UpgradeChoice,\n} from \"./commands/upgrade.js\";\nimport { verify, type VerifyIssue } from \"./commands/verify.js\";\nimport { doctor } from \"./commands/doctor.js\";\nimport { detectProject, type Bundler } from \"./detect.js\";\nimport { lint, formatFindingsText, ALL_RULES, type Rule } from \"./commands/lint.js\";\nimport { init, cliVersion, type InitOptions, DEFAULT_REGISTRY } from \"./init.js\";\nimport { bench, formatBenchTable } from \"./commands/bench.js\";\nimport { newFamily, NewFamilyError } from \"./commands/new.js\";\nimport { reseal } from \"./commands/reseal.js\";\n\nconst KNOWN_PRESETS = [\"starter\", \"app\", \"content\", \"all\", \"none\"];\nexport const DEFAULT_PRESET = \"starter\";\n\n// Decide the init preset without forcing a TTY: an explicit --preset wins,\n// --yes/-y takes the default, and only the bare interactive call prompts —\n// agents and CI run `init --yes` unattended (#68).\nexport async function resolveInitPreset(\n opts: { preset?: string; yes?: boolean },\n prompt: () => Promise<string>,\n): Promise<string> {\n if (opts.preset !== undefined) return opts.preset;\n if (opts.yes) return DEFAULT_PRESET;\n return prompt();\n}\n\nexport async function run(argv: string[] = process.argv): Promise<void> {\n const cli = cac(\"shortwind\");\n\n cli\n .command(\"init\", \"Bootstrap Shortwind in this project\")\n .option(\"--preset <name>\", \"Preset to install (starter|app|content|all|none)\")\n .option(\"-y, --yes\", `Skip prompts and use the default preset (${DEFAULT_PRESET})`)\n .option(\"--registry <url>\", \"Registry origin\", { default: DEFAULT_REGISTRY })\n .option(\"--cwd <dir>\", \"Working directory\", { default: process.cwd() })\n .action(async (opts: { preset?: string; yes?: boolean; registry?: string; cwd?: string }) => {\n const preset = await resolveInitPreset(opts, promptForPreset);\n const options: InitOptions = {\n cwd: opts.cwd ?? process.cwd(),\n preset,\n };\n if (opts.registry !== undefined) options.registry = opts.registry;\n const result = await init(options);\n printInitSummary(result);\n });\n\n cli\n .command(\"add <...families>\", \"Install one or more families\")\n .option(\"--as <name>\", \"Rename the family on install (requires a single family)\")\n .option(\"--all\", \"Install every family in the registry\")\n .option(\"--force\", \"Overwrite existing files\")\n .option(\"--registry <url>\", \"Registry origin\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(\n async (\n families: string[],\n opts: { as?: string; all?: boolean; force?: boolean; registry?: string; cwd?: string },\n ) => {\n const addOptions: Parameters<typeof add>[0] = {\n cwd: opts.cwd ?? process.cwd(),\n families,\n };\n if (opts.as !== undefined) addOptions.as = opts.as;\n if (opts.all) addOptions.all = true;\n if (opts.force) addOptions.force = true;\n if (opts.registry !== undefined) addOptions.registry = opts.registry;\n const result = await add(addOptions);\n for (const fam of result.added) p.log.success(`added ${fam}`);\n for (const fam of result.overwritten) p.log.success(`overwrote ${fam}`);\n for (const fam of result.skipped) p.log.warn(`${fam} already exists (use --force)`);\n for (const missing of result.missingDependencies) {\n p.log.warn(\n `${missing.family} references unknown recipes: ${missing.references.join(\", \")}`,\n );\n }\n },\n );\n\n cli\n .command(\"new <family>\", \"Scaffold a new custom recipe family file\")\n .option(\"--force\", \"Overwrite an existing family file\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (family: string, opts: { force?: boolean; cwd?: string }) => {\n try {\n const result = await newFamily({\n cwd: opts.cwd ?? process.cwd(),\n family,\n ...(opts.force ? { force: true } : {}),\n });\n p.log.success(`created ${result.familyPath}`);\n p.log.info(`regenerated ${result.skillPath} — edit the recipes, then \\`shortwind build\\` to refresh.`);\n } catch (err) {\n if (err instanceof NewFamilyError) {\n process.stderr.write(err.message + \"\\n\");\n process.exit(1);\n }\n throw err;\n }\n });\n\n cli\n .command(\"remove <...families>\", \"Remove installed families\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (families: string[], opts: { cwd?: string }) => {\n const result = await remove({ cwd: opts.cwd ?? process.cwd(), families });\n for (const fam of result.removed) p.log.success(`removed ${fam}`);\n for (const fam of result.notFound) p.log.warn(`${fam} is not installed`);\n for (const broken of result.brokenDependents) {\n p.log.warn(\n `${broken.dependent} references removed recipes: ${broken.references.join(\", \")}`,\n );\n }\n });\n\n cli\n .command(\"preset <name>\", \"Install every family in the named preset (additive)\")\n .option(\"--registry <url>\", \"Registry origin\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (name: string, opts: { registry?: string; cwd?: string }) => {\n const presetOptions: Parameters<typeof runPreset>[0] = {\n cwd: opts.cwd ?? process.cwd(),\n name,\n };\n if (opts.registry !== undefined) presetOptions.registry = opts.registry;\n const result = await runPreset(presetOptions);\n p.log.info(`preset ${name}: ${result.added.length} added, ${result.skipped.length} already present`);\n });\n\n cli\n .command(\"ls\", \"List installed and available families\")\n .option(\"--installed\", \"Only installed\")\n .option(\"--available\", \"Only available\")\n .option(\"--json\", \"Emit JSON\")\n .option(\"--registry <url>\", \"Registry origin\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(\n async (opts: {\n installed?: boolean;\n available?: boolean;\n json?: boolean;\n registry?: string;\n cwd?: string;\n }) => {\n const lsOptions: Parameters<typeof ls>[0] = {\n cwd: opts.cwd ?? process.cwd(),\n };\n if (opts.installed) lsOptions.installedOnly = true;\n if (opts.available) lsOptions.availableOnly = true;\n if (opts.registry !== undefined) lsOptions.registry = opts.registry;\n const result = await ls(lsOptions);\n if (opts.json) {\n process.stdout.write(JSON.stringify(result, null, 2) + \"\\n\");\n } else {\n process.stdout.write(formatLsText(result) + \"\\n\");\n }\n },\n );\n\n cli\n .command(\"build\", \"Regenerate SKILL.md from ./recipes/\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (opts: { cwd?: string }) => {\n try {\n const result = await build({ cwd: opts.cwd ?? process.cwd() });\n if (result.changed) p.log.success(`regenerated ${result.families.length} families`);\n else p.log.info(`up to date (${result.families.length} families)`);\n } catch (err) {\n if (err instanceof BuildError) {\n process.stderr.write(err.message + \"\\n\");\n process.exit(1);\n }\n throw err;\n }\n });\n\n cli\n .command(\"dev\", \"Watch ./recipes/ and regenerate SKILL.md on change\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .option(\"--once\", \"Run build once and exit\")\n .action(async (opts: { cwd?: string; once?: boolean }) => {\n if (opts.once) {\n try {\n await build({ cwd: opts.cwd ?? process.cwd() });\n } catch (err) {\n if (err instanceof BuildError) {\n process.stderr.write(err.message + \"\\n\");\n process.exit(1);\n }\n throw err;\n }\n return;\n }\n const controller = new AbortController();\n const onSigint = (): void => controller.abort();\n process.on(\"SIGINT\", onSigint);\n try {\n const { stop } = await dev({\n cwd: opts.cwd ?? process.cwd(),\n signal: controller.signal,\n onStatus: (status) => {\n if (status.kind === \"rebuilt\") {\n const msg = `✓ regenerated ${status.families.length} families${status.changed ? \"\" : \" (no changes)\"}`;\n process.stdout.write(msg + \"\\n\");\n } else if (status.kind === \"ready\") {\n process.stdout.write(`watching ${status.recipesDir}\\n`);\n } else {\n process.stderr.write(status.message + \"\\n\");\n }\n },\n });\n // Block until SIGINT (or otherwise aborted), then close the watcher\n // and exit cleanly — without this the listener stays attached and\n // controller.abort() never returns control to the CLI.\n await new Promise<void>((resolve) => {\n if (controller.signal.aborted) resolve();\n else controller.signal.addEventListener(\"abort\", () => resolve(), { once: true });\n });\n await stop();\n } finally {\n process.off(\"SIGINT\", onSigint);\n }\n process.exit(0);\n });\n\n cli\n .command(\"upgrade [...families]\", \"Pull registry updates into ./recipes\")\n .option(\"--check\", \"Read-only — print drift summary and exit nonzero on updates\")\n .option(\"--force\", \"Skip touched-detection; overwrite local edits\")\n .option(\"--registry <url>\", \"Registry origin\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(\n async (\n families: string[],\n opts: { check?: boolean; force?: boolean; registry?: string; cwd?: string },\n ) => {\n try {\n const upgradeOptions: Parameters<typeof upgrade>[0] = {\n cwd: opts.cwd ?? process.cwd(),\n families,\n };\n if (opts.check) upgradeOptions.check = true;\n if (opts.force) upgradeOptions.force = true;\n if (opts.registry !== undefined) upgradeOptions.registry = opts.registry;\n if (!opts.check && !opts.force) {\n upgradeOptions.resolver = makeInteractiveResolver();\n }\n const result = await upgrade(upgradeOptions);\n printUpgradeSummary(result.outcomes);\n if (opts.check) {\n if (result.hasUpdates || result.hasTouched) process.exit(1);\n }\n } catch (err) {\n if (err instanceof UpgradeError) {\n process.stderr.write(err.message + \"\\n\");\n process.exit(2);\n }\n throw err;\n }\n },\n );\n\n cli\n .command(\"reseal [...families]\", \"Re-sign recipes after intentional edits (updates header sha + lockfile)\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (families: string[], opts: { cwd?: string }) => {\n const result = await reseal({ cwd: opts.cwd ?? process.cwd(), families });\n for (const f of result.resealed) p.log.success(`resealed ${f}`);\n for (const f of result.unchanged) p.log.info(`${f} already sealed`);\n for (const f of result.notFound) p.log.warn(`${f} is not installed`);\n for (const f of result.noHeader) p.log.warn(`${f} has no fingerprint header`);\n if (result.resealed.length === 0 && result.unchanged.length > 0 && result.notFound.length === 0) {\n p.log.success(\"all recipes already sealed\");\n }\n });\n\n cli\n .command(\"verify\", \"Check installed recipes against fingerprint headers and lockfile\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (opts: { cwd?: string }) => {\n const result = await verify({ cwd: opts.cwd ?? process.cwd() });\n if (result.ok) {\n p.log.success(`verified ${result.checked.length} families`);\n return;\n }\n for (const issue of result.issues) {\n const desc = describeVerifyIssue(issue);\n process.stderr.write(`${issue.family}: ${desc}\\n`);\n }\n process.exit(1);\n });\n\n cli\n .command(\"doctor\", \"Scan build output for unexpanded @recipe tokens\")\n .option(\"--dir <path>\", \"Output directory to scan (repeatable; default: .next, dist, out, build)\")\n .option(\"--json\", \"Emit machine-readable JSON\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (opts: { dir?: string | string[]; json?: boolean; cwd?: string }) => {\n const cwd = opts.cwd ?? process.cwd();\n const doctorOptions: Parameters<typeof doctor>[0] = { cwd };\n if (opts.dir !== undefined) {\n doctorOptions.dirs = Array.isArray(opts.dir) ? opts.dir : [opts.dir];\n }\n const result = await doctor(doctorOptions);\n if (opts.json) {\n process.stdout.write(JSON.stringify(result, null, 2) + \"\\n\");\n } else {\n printDoctorReport(result, cwd);\n }\n if (result.verdict === \"no-output\") process.exit(2);\n if (!result.ok) process.exit(1);\n });\n\n cli\n .command(\"lint\", \"Static analysis over source files and recipes\")\n .option(\"--fix\", \"Apply auto-fixes where supported\")\n .option(\"--rule <rule>\", \"Only run the named rule (repeatable)\")\n .option(\"--content <glob>\", \"Source glob to scan for recipe usage (repeatable; overrides config)\")\n .option(\"--json\", \"Emit machine-readable JSON\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(\n async (opts: {\n fix?: boolean;\n rule?: string | string[];\n content?: string | string[];\n json?: boolean;\n cwd?: string;\n }) => {\n const requested = opts.rule === undefined ? [] : Array.isArray(opts.rule) ? opts.rule : [opts.rule];\n for (const r of requested) {\n if (!ALL_RULES.includes(r as Rule)) {\n process.stderr.write(`unknown rule: ${r}\\n`);\n process.stderr.write(`available: ${ALL_RULES.join(\", \")}\\n`);\n process.exit(2);\n }\n }\n const lintOptions: Parameters<typeof lint>[0] = { cwd: opts.cwd ?? process.cwd() };\n if (opts.fix) lintOptions.fix = true;\n if (requested.length > 0) lintOptions.rules = requested as Rule[];\n if (opts.content !== undefined) {\n lintOptions.content = Array.isArray(opts.content) ? opts.content : [opts.content];\n }\n const result = await lint(lintOptions);\n if (opts.json) {\n process.stdout.write(JSON.stringify(result, null, 2) + \"\\n\");\n } else {\n const text = formatFindingsText(result.findings);\n if (text) process.stdout.write(text + \"\\n\");\n for (const f of result.filesFixed) p.log.success(`fixed ${f}`);\n if (result.scannedFiles === 0) {\n p.log.warn(\n `content scan matched no source files — usage rules (recipe/unused) were skipped.\\n` +\n `Point lint at your sources with \"content\" in shortwind.config.json or --content <glob>.`,\n );\n }\n if (result.findings.length === 0) p.log.success(\"no findings\");\n }\n if (!result.ok) process.exit(1);\n },\n );\n\n cli\n .command(\"bench [path]\", \"Benchmark token savings in a corpus or target directory\")\n .option(\"--corpus\", \"Run benchmark on the built-in corpus\")\n .option(\"--json\", \"Emit machine-readable JSON\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(async (pathArg: string | undefined, opts: { corpus?: boolean; json?: boolean; cwd?: string }) => {\n const benchOptions: Parameters<typeof bench>[0] = { cwd: opts.cwd ?? process.cwd() };\n if (opts.corpus) benchOptions.corpus = true;\n if (pathArg !== undefined) benchOptions.path = pathArg;\n const result = await bench(benchOptions);\n if (opts.json) {\n process.stdout.write(JSON.stringify(result, null, 2) + \"\\n\");\n } else {\n const text = formatBenchTable(result);\n process.stdout.write(text + \"\\n\");\n }\n });\n\n cli.help();\n cli.version(cliVersion() ?? \"0.0.0\");\n // cac's parse() invokes the matched command's async action but does NOT await\n // it, so a rejection inside any async command (network failure in `add`, a\n // rethrow in `build`/`upgrade`, …) escapes bin.ts's `run().catch` and prints\n // a raw unhandled-rejection dump. Parse without running, then await the\n // command so its promise flows back to the caller's catch.\n cli.parse(argv, { run: false });\n await cli.runMatchedCommand();\n}\n\nfunction makeInteractiveResolver() {\n return async (ctx: TouchedContext): Promise<UpgradeChoice> => {\n process.stdout.write(\n pc.yellow(`\\n${ctx.family}: locally modified — incoming ${ctx.incoming.version}\\n`),\n );\n const choice = await p.select({\n message: `Resolve ${ctx.family}`,\n options: [\n { value: \"accept\", label: \"Accept new (discard local edits)\" },\n { value: \"keep\", label: \"Keep yours\" },\n { value: \"skip\", label: \"Skip for now\" },\n ],\n });\n if (p.isCancel(choice)) return \"skip\";\n return choice as UpgradeChoice;\n };\n}\n\nfunction printUpgradeSummary(outcomes: Awaited<ReturnType<typeof upgrade>>[\"outcomes\"]): void {\n for (const o of outcomes) {\n if (o.action === \"updated\") {\n process.stdout.write(pc.green(`✓ ${o.family} ${o.from} → ${o.to}\\n`));\n } else if (o.action === \"would-update\") {\n process.stdout.write(pc.cyan(`→ ${o.family} ${o.from} → ${o.to} (available)\\n`));\n } else if (o.action === \"would-review\") {\n process.stdout.write(pc.yellow(`! ${o.family} ${o.from} → ${o.to} (touched; needs review)\\n`));\n } else if (o.action === \"kept\" && o.reason === \"user-chose-keep\") {\n process.stdout.write(pc.dim(` ${o.family} kept local\\n`));\n } else if (o.action === \"skipped\") {\n process.stdout.write(pc.dim(` ${o.family} skipped (${o.reason})\\n`));\n }\n }\n}\n\n// The whole point of doctor (#84) is telling \"you forgot to wire the adapter\"\n// apart from \"the adapter ran and something leaked\" — both otherwise present\n// as a green build that ships raw @recipe text.\nfunction printDoctorReport(result: Awaited<ReturnType<typeof doctor>>, cwd: string): void {\n if (result.verdict === \"no-output\") {\n p.log.error(\n `no build output found (looked for .next/, dist/, out/, build/).\\n` +\n `Run your framework's build first, or point doctor at it with --dir <path>.`,\n );\n return;\n }\n if (result.verdict === \"clean\") {\n p.log.success(\n `no unexpanded recipe tokens in ${result.outputDirs.join(\", \")} (${result.scannedFiles} files scanned)`,\n );\n return;\n }\n for (const f of result.findings) {\n process.stderr.write(`${path.relative(cwd, f.file)}: ${f.tokens.join(\", \")}\\n`);\n }\n const tokenCount = new Set(result.findings.flatMap((f) => f.tokens)).size;\n if (result.verdict === \"not-wired\") {\n const bundler = detectProject(cwd).bundler;\n p.log.error(\n `found ${tokenCount} raw @recipe token${tokenCount === 1 ? \"\" : \"s\"} in build output and ` +\n `every recipe your source references is among them — it looks like no Shortwind ` +\n `transform ran during the build.\\n` +\n `Is the adapter wired? ${adapterHint(bundler)}\\n` +\n `Setup guide: ${setupGuideUrl(bundler)}`,\n );\n } else {\n p.log.error(\n `found ${tokenCount} raw @recipe token${tokenCount === 1 ? \"\" : \"s\"} in build output. ` +\n `The Shortwind transform ran (other recipes expanded), so these specific tokens ` +\n `escaped it — typically a className built from a variable/prop/template, or markup ` +\n `in a region the expander treats as opaque.\\n` +\n `See https://shortwind.dev/docs/dynamic-classes`,\n );\n }\n}\n\n// Slugs match site/src/content/docs/setup-<bundler>.md (#85).\nfunction setupGuideUrl(bundler: Bundler): string {\n return bundler === \"unknown\"\n ? \"https://shortwind.dev/docs/install\"\n : `https://shortwind.dev/docs/setup-${bundler}`;\n}\n\nfunction adapterHint(bundler: Bundler): string {\n switch (bundler) {\n case \"next\":\n return \"Next needs `export default withShortwind()(nextConfig)` in next.config — note the call is curried.\";\n case \"vite\":\n return \"Vite needs `shortwind()` in the vite.config plugins array: `plugins: [shortwind(), tailwindcss(), ...]`.\";\n case \"astro\":\n return \"Astro needs `shortwind()` in astro.config `integrations`.\";\n default:\n return \"Add the Shortwind plugin for your bundler (see the setup guide).\";\n }\n}\n\nfunction describeVerifyIssue(issue: VerifyIssue): string {\n switch (issue.kind) {\n case \"missing-header\":\n return \"no fingerprint header — recipe was hand-stripped\";\n case \"header-tampered\":\n return `header sha ${issue.recorded} but body hashes to ${issue.actual}`;\n case \"legacy-fingerprint\":\n return `sealed with an older fingerprint format (${issue.recorded}) — run \\`shortwind reseal\\` to upgrade it (the recipe body is unchanged)`;\n case \"lockfile-mismatch\":\n return `lockfile expects ${issue.locked} but body hashes to ${issue.actual}`;\n case \"missing-lock-entry\":\n return \"installed but not recorded in lockfile\";\n case \"missing-file\":\n return \"in lockfile but file is missing\";\n }\n}\n\nasync function promptForPreset(): Promise<string> {\n const choice = await p.select({\n message: \"Pick a preset\",\n options: KNOWN_PRESETS.map((name) => ({ value: name, label: name })),\n });\n if (p.isCancel(choice)) {\n p.cancel(\"Cancelled.\");\n process.exit(0);\n }\n return choice;\n}\n\nfunction installCmd(pm: Awaited<ReturnType<typeof init>>[\"packageManager\"]): string {\n switch (pm) {\n case \"bun\":\n return \"bun add -d\";\n case \"npm\":\n return \"npm install -D\";\n default:\n return `${pm} add -D`;\n }\n}\n\nfunction printInitSummary(result: Awaited<ReturnType<typeof init>>): void {\n p.note(\n [\n `preset: ${result.preset}`,\n `registry: ${result.registry}`,\n `package manager: ${result.packageManager}`,\n `families copied: ${result.installedFamilies.length}`,\n `families skipped: ${result.skippedFamilies.length}`,\n `adapters: ${result.installOk ? result.installedPackages.join(\", \") || \"none\" : \"install failed — see below\"}`,\n ``,\n `config: ${result.configPath}`,\n `vscode settings: ${result.vscodePath}`,\n `pre-commit: ${result.huskyPath ?? \"skipped (not a git repository)\"}`,\n `SKILL.md: ${result.skillPath}`,\n `theme: ${describeTheme(result)}`,\n `tones: ${result.tonesAction === \"written\" ? `scaffolded default tones in ${result.tonesPath}` : \"skipped\"}`,\n `bundler config: ${describeBundlerConfig(result)}`,\n `agent guide: ${describeAgentsFile(result)}`,\n ].join(\"\\n\"),\n \"shortwind init\",\n );\n if (!result.installOk) {\n const v = cliVersion();\n const specs = result.installedPackages.map((p) => (v ? `${p}@${v}` : p)).join(\" \");\n p.log.warn(\n `Couldn't auto-install adapters (your recipes and config were still scaffolded).\\n` +\n `Finish by installing them yourself:\\n\\n` +\n ` ${installCmd(result.packageManager)} ${specs}\\n\\n` +\n `(${result.installError ?? \"unknown error\"})`,\n );\n }\n if (result.bundlerConfigAction === \"manual\" && result.bundlerConfigSnippet) {\n p.log.warn(`Add the plugin to your bundler config:\\n\\n${result.bundlerConfigSnippet}`);\n }\n p.log.info(`Setup guide for your stack: ${setupGuideUrl(result.bundler)}`);\n if (result.themeAction === \"supplemented\") {\n p.log.info(\n `Your theme (${result.themePath}) didn't define ${result.supplementedThemeTokens.length} design token${result.supplementedThemeTokens.length === 1 ? \"\" : \"s\"} the installed recipes use:\\n\\n` +\n ` ${result.supplementedThemeTokens.join(\", \")}\\n\\n` +\n `Appended them with neutral placeholder values (marked block at the end of the file) so recipes render on first run — tune them to your palette.\\n` +\n `Reference values: https://shortwind.dev/docs/install#theme-tokens`,\n );\n }\n if (result.missingThemeTokens.length > 0) {\n p.log.warn(\n `Your existing theme (${result.themePath}) does not define ${result.missingThemeTokens.length} design token${result.missingThemeTokens.length === 1 ? \"\" : \"s\"} the installed recipes use:\\n\\n` +\n ` ${result.missingThemeTokens.join(\", \")}\\n\\n` +\n `Recipes referencing them will render colorless until you add the tokens to your @theme.\\n` +\n `The default token block is documented at https://shortwind.dev/docs/install#theme-tokens`,\n );\n }\n p.outro(\n `Next: run \\`${devCmd(result.packageManager)}\\` and check a recipe renders. After a production build, \\`npx shortwind doctor\\` verifies nothing shipped unexpanded.`,\n );\n}\n\n// `npm dev` is not a thing — npm needs the `run` form; pnpm/yarn/bun accept\n// the bare script name.\nfunction devCmd(pm: Awaited<ReturnType<typeof init>>[\"packageManager\"]): string {\n return pm === \"npm\" ? \"npm run dev\" : `${pm} dev`;\n}\n\nfunction describeAgentsFile(result: Awaited<ReturnType<typeof init>>): string {\n switch (result.agentsFileAction) {\n case \"created\":\n return `wrote ${result.agentsFilePath}`;\n case \"appended\":\n return `added recipe pointer to ${result.agentsFilePath}`;\n case \"skipped\":\n return `pointer already in ${result.agentsFilePath}`;\n }\n}\n\nfunction describeBundlerConfig(result: Awaited<ReturnType<typeof init>>): string {\n switch (result.bundlerConfigAction) {\n case \"patched\":\n return `plugin added to ${result.bundlerConfigPath}`;\n case \"manual\":\n return \"needs a manual edit (see below)\";\n case \"skipped\":\n return result.bundlerConfigPath\n ? `already wired in ${result.bundlerConfigPath}`\n : \"skipped (no supported bundler)\";\n }\n}\n\nfunction describeTheme(result: Awaited<ReturnType<typeof init>>): string {\n switch (result.themeAction) {\n case \"injected\":\n return `tokens added to ${result.themePath}`;\n case \"created\":\n return `wrote ${result.themePath}`;\n case \"supplemented\":\n return `kept your theme; appended ${result.supplementedThemeTokens.length} missing tokens to ${result.themePath}`;\n case \"skipped\":\n return result.themePath\n ? `left existing theme in ${result.themePath} untouched`\n : \"skipped (no Tailwind v4 CSS entry — define color tokens yourself)\";\n }\n}\n","#!/usr/bin/env node\nimport { run } from \"./cli.js\";\n\nrun().catch((err) => {\n console.error(err instanceof Error ? err.stack ?? err.message : err);\n process.exit(1);\n});\n"],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ //#region src/catalog.generated.ts
2
+ const CATALOG_PRESETS = {
3
+ "starter": [
4
+ "card",
5
+ "button",
6
+ "layout",
7
+ "text",
8
+ "form"
9
+ ],
10
+ "app": [
11
+ "card",
12
+ "button",
13
+ "layout",
14
+ "text",
15
+ "form",
16
+ "badge",
17
+ "table",
18
+ "dialog",
19
+ "list",
20
+ "navigation",
21
+ "feedback",
22
+ "tooltip",
23
+ "menu",
24
+ "sheet",
25
+ "stat",
26
+ "segmented",
27
+ "switch"
28
+ ],
29
+ "content": [
30
+ "card",
31
+ "button",
32
+ "layout",
33
+ "text",
34
+ "form",
35
+ "badge",
36
+ "code",
37
+ "list",
38
+ "media",
39
+ "empty"
40
+ ],
41
+ "all": "*"
42
+ };
43
+ const CATALOG_FAMILIES = [
44
+ "badge",
45
+ "button",
46
+ "card",
47
+ "code",
48
+ "dialog",
49
+ "empty",
50
+ "feedback",
51
+ "form",
52
+ "icon",
53
+ "layout",
54
+ "list",
55
+ "media",
56
+ "menu",
57
+ "navigation",
58
+ "progress",
59
+ "segmented",
60
+ "sheet",
61
+ "skeleton",
62
+ "stat",
63
+ "surface",
64
+ "switch",
65
+ "table",
66
+ "text",
67
+ "tooltip"
68
+ ];
69
+ const CATALOG_RECIPES = {
70
+ "badge": "/* shortwind: badge@0.0.2 sha:07c6ab5a8deb77e3 */\n\n/* @guide\n @badge is the default. Its color is DATA-DRIVEN: it reads the --tone-bg /\n --tone-fg CSS variables, so set the tone from your data with a data-tone\n attribute instead of picking a different recipe name —\n `<span className=\"@badge\" data-tone={severity}>`. Define your tones once in\n CSS (init scaffolds neutral/success/warning/danger/info; add your own\n `[data-tone=\"sev1\"] { --tone-bg: …; --tone-fg: … }`). With no data-tone it\n falls back to the neutral muted look, identical to before.\n\n The static @badge-success/warning/danger/info variants still exist for when\n the tone is known at author time; reach for data-tone when it's chosen from\n data. @badge-outline is unfilled. @badge-base is a color-less shell for\n building custom tones — not for direct use.\n*/\n\n/* Badge shell — shape, sizing, focus ring. No bg/text/border color so\n variants can supply their own tone without conflicts. */\n@recipe badge-base {\n inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\n}\n\n/* Default badge — tone-aware. Reads --tone-bg/--tone-fg (set via data-tone),\n falling back to the neutral muted tokens so it renders unchanged when no\n tone is set. */\n@recipe badge {\n inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium bg-[var(--tone-bg,var(--muted))] text-[var(--tone-fg,var(--muted-foreground))]\n}\n\n/* Success tone badge. */\n@recipe badge-success {\n inline-flex items-center gap-1 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800 dark:bg-green-900 dark:text-green-200\n}\n\n/* Warning tone badge. */\n@recipe badge-warning {\n inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-0.5 text-xs font-medium text-amber-800 dark:bg-amber-900 dark:text-amber-200\n}\n\n/* Danger tone badge. */\n@recipe badge-danger {\n inline-flex items-center gap-1 rounded-full bg-destructive/15 px-2 py-0.5 text-xs font-medium text-destructive\n}\n\n/* Info tone badge. */\n@recipe badge-info {\n inline-flex items-center gap-1 rounded-full bg-primary/15 px-2 py-0.5 text-xs font-medium text-primary\n}\n\n/* Outline badge — no fill. */\n@recipe badge-outline {\n inline-flex items-center gap-1 rounded-full border border-border px-2 py-0.5 text-xs font-medium text-foreground\n}\n",
71
+ "button": "/* shortwind: button@0.0.2 sha:b0b08652cd757b90 */\n\n/* @guide\n Name order is @button-<intent>[-<size>]: intent first (primary/secondary/\n ghost/danger/outline), size second (sm/lg; omit for default). One intent per\n button — never combine @button-primary with @button-danger. @button-ghost is\n text-only, @button-outline is bordered with no fill, @button-icon is a square\n icon button. @button-base is the shared shell; don't use it on its own.\n The short @btn-<intent> forms are aliases that expand identically; the\n full-word @button-* names are canonical — prefer them.\n*/\n\n/* Shared button base — sizing, focus ring, disabled state. */\n@recipe btn-base {\n inline-flex items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring disabled:cursor-not-allowed disabled:opacity-50\n}\n\n/* Primary call-to-action button. */\n@recipe btn-primary {\n @btn-base bg-primary text-primary-foreground hover:bg-primary/90\n}\n\n/* Small primary button. */\n@recipe btn-primary-sm {\n @btn-primary px-3 py-1.5 text-xs\n}\n\n/* Large primary button. */\n@recipe btn-primary-lg {\n @btn-primary px-6 py-3 text-base\n}\n\n/* Secondary button — bordered surface tone. */\n@recipe btn-secondary {\n @btn-base border border-border bg-secondary text-secondary-foreground hover:bg-secondary/80\n}\n\n/* Small secondary button. */\n@recipe btn-secondary-sm {\n @btn-secondary px-3 py-1.5 text-xs\n}\n\n/* Large secondary button. */\n@recipe btn-secondary-lg {\n @btn-secondary px-6 py-3 text-base\n}\n\n/* Ghost button — text only, no background. */\n@recipe btn-ghost {\n @btn-base text-foreground hover:bg-muted\n}\n\n/* Small ghost button. */\n@recipe btn-ghost-sm {\n @btn-ghost px-3 py-1.5 text-xs\n}\n\n/* Large ghost button. */\n@recipe btn-ghost-lg {\n @btn-ghost px-6 py-3 text-base\n}\n\n/* Destructive button. */\n@recipe btn-danger {\n @btn-base bg-destructive text-destructive-foreground hover:bg-destructive/90\n}\n\n/* Outline button — bordered without fill. */\n@recipe btn-outline {\n @btn-base border border-primary text-primary hover:bg-primary/10\n}\n\n/* Square icon-only button. */\n@recipe btn-icon {\n inline-flex h-9 w-9 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring disabled:cursor-not-allowed disabled:opacity-50\n}\n\n/* Full-word canonical aliases — expand identically to the @btn-* short forms. */\n\n/* Shared button shell (alias of @btn-base). */\n@recipe button-base { @btn-base }\n\n/* Primary call-to-action button (alias of @btn-primary). */\n@recipe button-primary { @btn-primary }\n\n/* Small primary button (alias of @btn-primary-sm). */\n@recipe button-primary-sm { @btn-primary-sm }\n\n/* Large primary button (alias of @btn-primary-lg). */\n@recipe button-primary-lg { @btn-primary-lg }\n\n/* Secondary button (alias of @btn-secondary). */\n@recipe button-secondary { @btn-secondary }\n\n/* Small secondary button (alias of @btn-secondary-sm). */\n@recipe button-secondary-sm { @btn-secondary-sm }\n\n/* Large secondary button (alias of @btn-secondary-lg). */\n@recipe button-secondary-lg { @btn-secondary-lg }\n\n/* Ghost button (alias of @btn-ghost). */\n@recipe button-ghost { @btn-ghost }\n\n/* Small ghost button (alias of @btn-ghost-sm). */\n@recipe button-ghost-sm { @btn-ghost-sm }\n\n/* Large ghost button (alias of @btn-ghost-lg). */\n@recipe button-ghost-lg { @btn-ghost-lg }\n\n/* Destructive button (alias of @btn-danger). */\n@recipe button-danger { @btn-danger }\n\n/* Outline button (alias of @btn-outline). */\n@recipe button-outline { @btn-outline }\n\n/* Square icon-only button (alias of @btn-icon). */\n@recipe button-icon { @btn-icon }\n",
72
+ "card": "/* shortwind: card@0.0.1 sha:4813cef10cd21824 */\n\n/* @guide\n Pick exactly one container: @card (default), @card-elevated (raised shadow),\n @card-flat (no border), or @card-interactive (clickable hover/focus) — don't\n stack two container variants on one element. Lay out the inside with\n @card-header / @card-body / @card-footer.\n*/\n\n/* Default content card with border, padding, and surface color. */\n@recipe card {\n rounded-lg border border-border bg-card text-card-foreground p-4\n}\n\n/* Card with raised shadow for emphasis. */\n@recipe card-elevated {\n @card shadow-md\n}\n\n/* Card without border, on a muted surface. */\n@recipe card-flat {\n rounded-lg bg-muted text-foreground p-4\n}\n\n/* Clickable card with hover and focus-visible states. */\n@recipe card-interactive {\n @card cursor-pointer transition-shadow hover:shadow-md focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\n}\n\n/* Card header region with bottom divider. */\n@recipe card-header {\n mb-3 border-b border-border pb-3\n}\n\n/* Card body region. */\n@recipe card-body {\n py-1\n}\n\n/* Card footer with top divider and right-aligned actions. */\n@recipe card-footer {\n mt-3 flex items-center justify-end gap-2 border-t border-border pt-3\n}\n",
73
+ "code": "/* shortwind: code@0.0.1 sha:cc8d2b0bedb5b453 */\n\n/* @guide\n @code-inline for a code span inside prose, @code-block for a multi-line\n preformatted block, @kbd for a keyboard-shortcut hint. Pick by context, not\n by size.\n*/\n\n/* Inline code span. */\n@recipe code-inline {\n rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-foreground\n}\n\n/* Block of preformatted code. */\n@recipe code-block {\n overflow-x-auto rounded-md border border-border bg-muted p-4 font-mono text-sm leading-6 text-foreground\n}\n\n/* Keyboard shortcut hint. */\n@recipe kbd {\n inline-flex items-center rounded border border-border bg-muted px-1.5 py-0.5 font-mono text-xs text-foreground shadow-sm\n}\n",
74
+ "dialog": "/* shortwind: dialog@0.0.1 sha:b4dbf853e99fce75 */\n\n/* @guide\n A modal is three layers: @dialog-overlay (dimmed backdrop), @dialog (the\n centering wrapper), and @dialog-content (the panel). Structure the panel with\n @dialog-header and @dialog-footer. Don't put content styling on @dialog\n itself — it's only the positioner.\n*/\n\n/* Modal dialog wrapper — covers the viewport, centers content. */\n@recipe dialog {\n fixed inset-0 z-50 flex items-center justify-center p-4\n}\n\n/* Dimmed overlay behind the dialog. */\n@recipe dialog-overlay {\n fixed inset-0 z-40 bg-black/50\n}\n\n/* Dialog content panel. */\n@recipe dialog-content {\n relative z-50 w-full max-w-md rounded-lg border border-border bg-popover text-popover-foreground p-6 shadow-xl\n}\n\n/* Dialog header region with title. */\n@recipe dialog-header {\n mb-4 flex flex-col gap-1\n}\n\n/* Dialog footer with right-aligned actions. */\n@recipe dialog-footer {\n mt-6 flex items-center justify-end gap-2\n}\n",
75
+ "empty": "/* shortwind: empty@0.0.1 sha:003168e1b4a395c5 */\n\n/* @guide\n @empty is the container for a no-data state; fill it with @empty-icon,\n @empty-title, and @empty-description. These are slots for that pattern — use\n @heading-* / @body from the text family for ordinary copy.\n*/\n\n/* Empty-state container. */\n@recipe empty {\n flex flex-col items-center justify-center gap-3 rounded-md border border-dashed border-border p-8 text-center\n}\n\n/* Empty-state icon slot. */\n@recipe empty-icon {\n flex h-12 w-12 items-center justify-center rounded-full bg-muted text-muted-foreground\n}\n\n/* Empty-state title text. */\n@recipe empty-title {\n text-base font-semibold text-foreground\n}\n\n/* Empty-state supporting description. */\n@recipe empty-description {\n max-w-sm text-sm text-muted-foreground\n}\n",
76
+ "feedback": "/* shortwind: feedback@0.0.1 sha:818868f6af179bcf */\n\n/* @guide\n Inline messages use @alert (neutral) or a tone variant @alert-success/\n warning/danger/info — one tone each. @callout is a left-accent inline note,\n @toast is a floating notification, @banner spans the full viewport width.\n*/\n\n/* Default informational alert. */\n@recipe alert {\n flex items-start gap-3 rounded-md border border-border bg-card p-4 text-sm text-card-foreground\n}\n\n/* Success alert. */\n@recipe alert-success {\n flex items-start gap-3 rounded-md border border-green-200 bg-green-50 p-4 text-sm text-green-900 dark:border-green-900 dark:bg-green-950 dark:text-green-100\n}\n\n/* Warning alert. */\n@recipe alert-warning {\n flex items-start gap-3 rounded-md border border-amber-200 bg-amber-50 p-4 text-sm text-amber-900 dark:border-amber-900 dark:bg-amber-950 dark:text-amber-100\n}\n\n/* Danger alert. */\n@recipe alert-danger {\n flex items-start gap-3 rounded-md border border-destructive/30 bg-destructive/10 p-4 text-sm text-destructive\n}\n\n/* Informational alert. */\n@recipe alert-info {\n flex items-start gap-3 rounded-md border border-primary/30 bg-primary/10 p-4 text-sm text-primary\n}\n\n/* Inline callout — flush left edge accent. */\n@recipe callout {\n border-l-4 border-primary bg-primary/10 p-4 text-sm text-foreground\n}\n\n/* Floating toast notification. */\n@recipe toast {\n pointer-events-auto flex items-start gap-3 rounded-md border border-border bg-popover p-4 text-sm text-popover-foreground shadow-lg\n}\n\n/* Full-width banner spanning the viewport. */\n@recipe banner {\n w-full bg-primary px-4 py-2 text-center text-sm font-medium text-primary-foreground\n}\n",
77
+ "form": "/* shortwind: form@0.0.2 sha:e78b50acd247f5af */\n\n/* @guide\n Wrap each label+control+message in @field (use @field-error for the invalid\n state); group related fields with @fieldset. Controls are bare: @input,\n @textarea, @select, @checkbox, @radio, plus @input-error for invalid text\n and @input-shell for the transparent input shell. Helper text is\n @help. There is no @form-group (use @field), @form-input (use @input),\n @form-helper (use @help) or @form-checkbox (use @checkbox); the field label\n recipe is @label, in the text family.\n*/\n\n/* Text input field. */\n@recipe input {\n block w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-ring focus:outline-2 focus:outline-offset-2 focus:outline-ring disabled:cursor-not-allowed disabled:opacity-50\n}\n\n/* Dinachi-style input shell — transparent background, h-9, file/\n placeholder/selection/aria-invalid/focus-visible states baked in. */\n@recipe input-shell {\n flex h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs outline-none transition-[color,box-shadow] placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:border-destructive aria-invalid:ring-destructive/20 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40\n}\n\n/* Input in error state. */\n@recipe input-error {\n @input border-destructive focus:border-destructive focus:outline-destructive\n}\n\n/* Multi-line textarea. */\n@recipe textarea {\n block w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-ring focus:outline-2 focus:outline-offset-2 focus:outline-ring disabled:cursor-not-allowed disabled:opacity-50\n}\n\n/* Native select control. */\n@recipe select {\n block w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground focus:border-ring focus:outline-2 focus:outline-offset-2 focus:outline-ring disabled:cursor-not-allowed disabled:opacity-50\n}\n\n/* Checkbox input. */\n@recipe checkbox {\n h-4 w-4 rounded border-input text-primary focus:outline-2 focus:outline-offset-2 focus:outline-ring\n}\n\n/* Radio input. */\n@recipe radio {\n h-4 w-4 border-input text-primary focus:outline-2 focus:outline-offset-2 focus:outline-ring\n}\n\n/* Form field wrapper — label + input + help/error. */\n@recipe field {\n flex flex-col gap-1.5\n}\n\n/* Form field in error state. */\n@recipe field-error {\n flex flex-col gap-1.5\n}\n\n/* Grouped form section with optional legend. */\n@recipe fieldset {\n flex flex-col gap-4 rounded-md border border-border p-4\n}\n\n/* Field-level helper text. */\n@recipe help {\n text-xs text-muted-foreground\n}\n",
78
+ "icon": "/* shortwind: icon@0.0.1 sha:fab3b2d3a0c089fb */\n\n/* @guide\n Size an icon with @icon-sm/md/lg (16/20/24px) — these set width and height\n only; add @icon-muted for secondary color. They're for SVG/icon elements, not\n to be confused with @btn-icon (the icon button in the button family).\n*/\n\n/* Small icon — 16px. */\n@recipe icon-sm {\n h-4 w-4 shrink-0\n}\n\n/* Default icon size — 20px. */\n@recipe icon-md {\n h-5 w-5 shrink-0\n}\n\n/* Large icon — 24px. */\n@recipe icon-lg {\n h-6 w-6 shrink-0\n}\n\n/* Icon with muted color. */\n@recipe icon-muted {\n text-muted-foreground\n}\n",
79
+ "layout": "/* shortwind: layout@0.0.2 sha:48025daeec83041a */\n\n/* @guide\n Composition primitives. @stack-* stacks children vertically (flex-col);\n @row* lays them out horizontally (flex-row). Choose the gap with the size\n suffix (xs/sm/md/lg on stacks); bare @stack is the default and equals\n @stack-md. Use @grid-2/3/4 only for true multi-column grids, @center to\n center on both axes, @full to fill the parent. Common slips: there is no\n @flex-row (use @row) or @flex-col (use a @stack-*), and the grids are\n @grid-3, not @grid-cols-3.\n*/\n\n/* Bare stack — default gap, alias for @stack-md. */\n@recipe stack { @stack-md }\n\n/* Vertical stack with extra-small gap. */\n@recipe stack-xs {\n flex flex-col gap-1\n}\n\n/* Vertical stack with small gap. */\n@recipe stack-sm {\n flex flex-col gap-2\n}\n\n/* Vertical stack with medium gap. */\n@recipe stack-md {\n flex flex-col gap-4\n}\n\n/* Vertical stack with large gap. */\n@recipe stack-lg {\n flex flex-col gap-8\n}\n\n/* Horizontal row with default gap and centered items. */\n@recipe row {\n flex flex-row items-center gap-2\n}\n\n/* Horizontal row with space between children. */\n@recipe row-between {\n flex flex-row items-center justify-between gap-2\n}\n\n/* Horizontal row aligned to the end. */\n@recipe row-end {\n flex flex-row items-center justify-end gap-2\n}\n\n/* Two-column responsive grid. */\n@recipe grid-2 {\n grid grid-cols-1 gap-4 sm:grid-cols-2\n}\n\n/* Three-column responsive grid. */\n@recipe grid-3 {\n grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\n}\n\n/* Four-column responsive grid. */\n@recipe grid-4 {\n grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\n}\n\n/* Center content horizontally and vertically. */\n@recipe center {\n flex items-center justify-center\n}\n\n/* Fill the available width and height. */\n@recipe full {\n h-full w-full\n}\n",
80
+ "list": "/* shortwind: list@0.0.2 sha:6a7f93aa95067c44 */\n\n/* @guide\n @list wraps a stack of @list-item rows; use @list-bordered for divided rows.\n Definition lists are separate: @description-list with @description-term and\n @description-detail. The short @dl / @dt / @dd forms are aliases that expand\n identically; the spelled-out names are canonical — prefer them. For site\n navigation reach for the navigation family (@navigation), not @list.\n*/\n\n/* Vertical list with default gap. */\n@recipe list {\n flex flex-col gap-1\n}\n\n/* Single list item. */\n@recipe list-item {\n flex items-center gap-2 rounded-md px-3 py-2 text-sm text-foreground\n}\n\n/* List with dividing borders between items. */\n@recipe list-bordered {\n divide-y divide-border rounded-md border border-border\n}\n\n/* Definition list container. */\n@recipe dl {\n grid grid-cols-1 gap-2 sm:grid-cols-3 sm:gap-4\n}\n\n/* Definition term. */\n@recipe dt {\n text-sm font-medium text-muted-foreground\n}\n\n/* Definition description. */\n@recipe dd {\n text-sm text-foreground sm:col-span-2\n}\n\n/* Spelled-out canonical aliases — expand identically to @dl/@dt/@dd. */\n\n/* Definition list container (alias of @dl). */\n@recipe description-list { @dl }\n\n/* Definition term (alias of @dt). */\n@recipe description-term { @dt }\n\n/* Definition description (alias of @dd). */\n@recipe description-detail { @dd }\n",
81
+ "media": "/* shortwind: media@0.0.1 sha:dfc85633101b0637 */\n\n/* @guide\n @avatar (with @avatar-sm/lg) is a round user image; @thumb is a small square\n thumbnail. For responsive embeds use @aspect-square or @aspect-video. Avatars\n are circular by default — don't restyle the radius.\n*/\n\n/* User/profile avatar. */\n@recipe avatar {\n inline-flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-muted text-sm font-medium text-muted-foreground\n}\n\n/* Small avatar. */\n@recipe avatar-sm {\n @avatar h-6 w-6 text-xs\n}\n\n/* Large avatar. */\n@recipe avatar-lg {\n @avatar h-14 w-14 text-base\n}\n\n/* Small image thumbnail. */\n@recipe thumb {\n h-16 w-16 rounded-md object-cover\n}\n\n/* 1:1 aspect-ratio wrapper. */\n@recipe aspect-square {\n aspect-square w-full overflow-hidden rounded-md\n}\n\n/* 16:9 aspect-ratio wrapper. */\n@recipe aspect-video {\n aspect-video w-full overflow-hidden rounded-md\n}\n",
82
+ "menu": "/* shortwind: menu@0.0.1 sha:c95d3650d92a2fa6 */\n\n/* @guide\n A dropdown/actions menu (the \"…\" button's popover). @menu is the panel,\n @menu-item is a row, @menu-trigger is the control that opens it, and\n @menu-separator divides groups. The highlighted/active row is data-driven —\n set data-active on the item rather than swapping recipe names\n (`<button class=\"@menu-item\" data-active>`); data-disabled dims + disables a\n row. @menu only styles the panel; pair it with your own positioning.\n*/\n\n/* Dropdown menu panel. */\n@recipe menu {\n min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md\n}\n\n/* A single menu row. Highlight with data-active, dim with data-disabled. */\n@recipe menu-item {\n relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm text-foreground outline-none transition-colors hover:bg-muted focus-visible:bg-muted data-[active]:bg-muted data-[disabled]:pointer-events-none data-[disabled]:opacity-50\n}\n\n/* The control that opens the menu. */\n@recipe menu-trigger {\n inline-flex items-center justify-center gap-1 outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\n}\n\n/* Divider between menu groups. */\n@recipe menu-separator {\n -mx-1 my-1 h-px bg-border\n}\n",
83
+ "navigation": "/* shortwind: navigation@0.0.2 sha:2bb73370d69e0f65 */\n\n/* @guide\n @navigation is the container; links are @navigation-link with\n @navigation-link-active for the current page. Tabs mirror that pair: @tab and\n @tab-active. Use @breadcrumb for trail navigation. Active and inactive are\n separate recipes — swap the whole class rather than combining them. The short\n @nav / @nav-link forms are aliases that expand identically; the full-word\n @navigation-* names are canonical — prefer them.\n*/\n\n/* Top-level nav container. */\n@recipe nav {\n flex items-center gap-1\n}\n\n/* Inactive nav link with hover/focus states. */\n@recipe nav-link {\n inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\n}\n\n/* Active nav link. */\n@recipe nav-link-active {\n inline-flex items-center gap-2 rounded-md bg-muted px-3 py-1.5 text-sm font-medium text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\n}\n\n/* Breadcrumb trail container. */\n@recipe breadcrumb {\n flex items-center gap-1.5 text-sm text-muted-foreground\n}\n\n/* Inactive tab control. */\n@recipe tab {\n inline-flex items-center gap-2 border-b-2 border-transparent px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:border-border hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\n}\n\n/* Active tab control. */\n@recipe tab-active {\n inline-flex items-center gap-2 border-b-2 border-primary px-3 py-2 text-sm font-medium text-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\n}\n\n/* Full-word canonical aliases — expand identically to the @nav-* short forms. */\n\n/* Top-level nav container (alias of @nav). */\n@recipe navigation { @nav }\n\n/* Inactive nav link (alias of @nav-link). */\n@recipe navigation-link { @nav-link }\n\n/* Active nav link (alias of @nav-link-active). */\n@recipe navigation-link-active { @nav-link-active }\n",
84
+ "progress": "/* shortwind: progress@0.0.1 sha:37210e32d2cd555f */\n\n/* @guide\n A bar is two pieces: @progress-track (the background) wrapping @progress-bar\n (the fill). For an indeterminate state use @spinner instead — it's a\n standalone loader, not a bar.\n*/\n\n/* Progress bar track (background). */\n@recipe progress-track {\n h-2 w-full overflow-hidden rounded-full bg-muted\n}\n\n/* Progress bar fill. */\n@recipe progress-bar {\n h-full rounded-full bg-primary transition-all\n}\n\n/* Indeterminate loading spinner. */\n@recipe spinner {\n inline-block h-4 w-4 animate-spin rounded-full border-2 border-border border-t-primary\n}\n",
85
+ "segmented": "/* shortwind: segmented@0.0.1 sha:60bb982c1fc271f0 */\n\n/* @guide\n A segmented control / filter bar (one choice highlighted). @segmented is the\n track, @segmented-item is each segment. The selected segment is data-driven —\n set data-active on the chosen item (`<button class=\"@segmented-item\"\n data-active>`) rather than swapping recipe names. Use @tab/@tab-active from\n the navigation family for underlined tabs instead.\n*/\n\n/* Segmented-control track. */\n@recipe segmented {\n inline-flex items-center gap-1 rounded-md bg-muted p-1\n}\n\n/* A segment. Mark the selected one with data-active. */\n@recipe segmented-item {\n inline-flex items-center justify-center gap-1.5 rounded-sm px-3 py-1 text-sm font-medium text-muted-foreground transition-colors hover:text-foreground data-[active]:bg-background data-[active]:text-foreground data-[active]:shadow-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\n}\n",
86
+ "sheet": "/* shortwind: sheet@0.0.1 sha:2dd5109c9e3865fa */\n\n/* @guide\n A slide-over panel (drawer). Three layers mirror the dialog family:\n @sheet-overlay (dimmed backdrop), @sheet (the positioner that anchors the\n panel to a side), and @sheet-content (the panel itself). Structure the panel\n with @sheet-header and @sheet-footer. The edge is data-driven — set\n data-side=left/right on @sheet (`<div class=\"@sheet\" data-side=\"right\">`)\n rather than picking a different recipe. @drawer is an alias for @sheet.\n*/\n\n/* Dimmed backdrop behind the sheet. */\n@recipe sheet-overlay {\n fixed inset-0 z-40 bg-black/50\n}\n\n/* Positioner — anchors the panel to a side via data-side. */\n@recipe sheet {\n fixed inset-0 z-50 flex data-[side=right]:justify-end data-[side=left]:justify-start\n}\n\n/* The sliding panel. */\n@recipe sheet-content {\n flex h-full w-3/4 max-w-sm flex-col gap-4 border-border bg-popover p-6 text-popover-foreground shadow-xl\n}\n\n/* Sheet header region. */\n@recipe sheet-header {\n flex flex-col gap-1\n}\n\n/* Sheet footer with bottom-pinned, right-aligned actions. */\n@recipe sheet-footer {\n mt-auto flex items-center justify-end gap-2\n}\n\n/* Alias — @drawer reads as @sheet. */\n@recipe drawer { @sheet }\n",
87
+ "skeleton": "/* shortwind: skeleton@0.0.1 sha:cd2db5c1c48b16d3 */\n\n/* @guide\n Match the skeleton to the shape it stands in for: @skeleton (block),\n @skeleton-text (a text line), @skeleton-circle (avatar/icon). Size block and\n text skeletons with raw width/height utilities.\n*/\n\n/* Default rectangular skeleton placeholder. */\n@recipe skeleton {\n animate-pulse rounded-md bg-muted\n}\n\n/* Single-line text skeleton. */\n@recipe skeleton-text {\n h-4 w-full animate-pulse rounded bg-muted\n}\n\n/* Circular skeleton (avatar/icon). */\n@recipe skeleton-circle {\n h-10 w-10 animate-pulse rounded-full bg-muted\n}\n",
88
+ "stat": "/* shortwind: stat@0.0.1 sha:445f1f7abfebc0b1 */\n\n/* @guide\n A dashboard metric tile. @stat is the container; inside it @stat-label names\n the metric, @stat-value is the big number, and @stat-trend is the delta.\n Colour the trend with the tone system — set data-tone on @stat-trend\n (success for up, danger for down): `<span class=\"@stat-trend\"\n data-tone=\"success\">`. Same --tone-fg mechanism as @badge; define tones once\n in CSS (init scaffolds them).\n*/\n\n/* Metric tile container. */\n@recipe stat {\n flex flex-col gap-1 rounded-lg border border-border bg-card p-4 text-card-foreground\n}\n\n/* Metric name. */\n@recipe stat-label {\n text-sm font-medium text-muted-foreground\n}\n\n/* The metric's primary value. */\n@recipe stat-value {\n text-2xl font-semibold tracking-tight text-foreground tabular-nums\n}\n\n/* Trend delta — tone-aware; set data-tone (success up / danger down). */\n@recipe stat-trend {\n inline-flex items-center gap-1 text-xs font-medium text-[var(--tone-fg,var(--muted-foreground))]\n}\n",
89
+ "surface": "/* shortwind: surface@0.0.2 sha:31716e4cbe81f93f */\n\n/* @guide\n @surface / @surface-muted / @surface-accent set a background+foreground pair\n for a region — one per section. @wrapper (or @wrapper-tight for prose)\n centers and width-caps content; there is no @wrapper-lg, set a different cap\n with max-w-* yourself. (Note: @container is reserved for Tailwind's\n container-query utility, so the content wrapper is @wrapper.) @divider-h and\n @divider-v are hairline rules.\n*/\n\n/* Default page/section surface. */\n@recipe surface {\n bg-background text-foreground\n}\n\n/* Muted surface — secondary background. */\n@recipe surface-muted {\n bg-muted text-foreground\n}\n\n/* Accent surface — soft brand background. */\n@recipe surface-accent {\n bg-accent text-accent-foreground\n}\n\n/* Centered content wrapper with a max width. */\n@recipe wrapper {\n mx-auto w-full max-w-6xl px-4 sm:px-6 lg:px-8\n}\n\n/* Narrow content wrapper for prose. */\n@recipe wrapper-tight {\n mx-auto w-full max-w-3xl px-4 sm:px-6\n}\n\n/* Horizontal divider line. */\n@recipe divider-h {\n shrink-0 h-px w-full bg-border\n}\n\n/* Vertical divider line. */\n@recipe divider-v {\n shrink-0 h-full w-px bg-border\n}\n",
90
+ "switch": "/* shortwind: switch@0.0.1 sha:54fa33bb72839799 */\n\n/* @guide\n A toggle switch. @switch is the track, @switch-thumb is the sliding knob\n inside it. The on/off state is data-driven — set data-checked on BOTH the\n track and the thumb (`<button class=\"@switch\" data-checked><span\n class=\"@switch-thumb\" data-checked /></button>`). For a checkbox/radio reach\n for the form family instead.\n*/\n\n/* Toggle track. Add data-checked when on. */\n@recipe switch {\n peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent bg-input transition-colors data-[checked]:bg-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring disabled:cursor-not-allowed disabled:opacity-50\n}\n\n/* Sliding knob. Add data-checked to slide it on. */\n@recipe switch-thumb {\n pointer-events-none block h-4 w-4 translate-x-0 rounded-full bg-background shadow-sm transition-transform data-[checked]:translate-x-4\n}\n",
91
+ "table": "/* shortwind: table@0.0.1 sha:4f3295935b2a647c */\n\n/* @guide\n Wrap the table in @table-container for horizontal overflow, then put @table\n (or @table-zebra for striped rows) on the <table>. Cells are @th (header) and\n @td (body); add @tr-hover to a <tr> for row highlighting.\n*/\n\n/* Scroll container for a wide table — keeps overflow horizontal. */\n@recipe table-container {\n w-full overflow-x-auto rounded-md border border-border\n}\n\n/* Data table base. */\n@recipe table {\n w-full border-collapse text-left text-sm text-foreground\n}\n\n/* Table header cell. */\n@recipe th {\n border-b border-border px-3 py-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground\n}\n\n/* Table body cell. */\n@recipe td {\n border-b border-border px-3 py-2\n}\n\n/* Row hover state. */\n@recipe tr-hover {\n transition-colors hover:bg-muted\n}\n\n/* Table with zebra striping on alternating rows. */\n@recipe table-zebra {\n w-full border-collapse text-left text-sm text-foreground [&_tbody_tr:nth-child(odd)]:bg-muted\n}\n",
92
+ "text": "/* shortwind: text@0.0.2 sha:f39dc7b88afefc8f */\n\n/* @guide\n Headings are sized by weight, not HTML level: @heading-xl/lg/md/sm — there\n is no @h1..@h6. Body copy: @body (default), @lead (intro paragraphs), @muted\n (secondary), @caption (fine print), @eyebrow (uppercase kicker above a\n heading). Use @label for form labels and @link for inline links. Don't\n append a -text suffix: it's @body not @body-text, @muted not @muted-text,\n @link not @link-text.\n*/\n\n/* Top-level page heading. */\n@recipe heading-xl {\n text-4xl font-bold tracking-tight text-foreground\n}\n\n/* Large section heading. */\n@recipe heading-lg {\n text-2xl font-semibold tracking-tight text-foreground\n}\n\n/* Medium heading. */\n@recipe heading-md {\n text-xl font-semibold text-foreground\n}\n\n/* Small heading. */\n@recipe heading-sm {\n text-base font-semibold text-foreground\n}\n\n/* Default body text. */\n@recipe body {\n text-sm leading-6 text-foreground\n}\n\n/* Lead paragraph — larger body copy for hero/intro sections. */\n@recipe lead {\n text-lg leading-relaxed text-muted-foreground\n}\n\n/* Muted secondary text. */\n@recipe muted {\n text-sm text-muted-foreground\n}\n\n/* Form label text. */\n@recipe label {\n text-sm font-medium text-foreground\n}\n\n/* Caption — small supporting text. */\n@recipe caption {\n text-xs text-muted-foreground\n}\n\n/* Eyebrow — uppercase kicker above a heading. */\n@recipe eyebrow {\n font-mono text-xs font-medium uppercase tracking-[0.2em] text-muted-foreground\n}\n\n/* Inline link with hover/focus states. */\n@recipe link {\n text-primary underline-offset-2 hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\n}\n",
93
+ "tooltip": "/* shortwind: tooltip@0.0.1 sha:1be3af4165357dcd */\n\n/* @guide\n @tooltip is the floating label bubble — it styles appearance only, so pair it\n with your own positioning.\n*/\n\n/* Floating tooltip bubble. */\n@recipe tooltip {\n pointer-events-none z-50 rounded-md bg-foreground px-2 py-1 text-xs font-medium text-background shadow-md\n}\n"
94
+ };
95
+ //#endregion
96
+ export { CATALOG_FAMILIES, CATALOG_PRESETS, CATALOG_RECIPES };
97
+
98
+ //# sourceMappingURL=catalog.generated-fXJjJvJ2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.generated-fXJjJvJ2.js","names":[],"sources":["../src/catalog.generated.ts"],"sourcesContent":["// Generated by scripts/generate-catalog.ts — do not edit by hand.\n// A snapshot of the @shortwind/catalog build, embedded so the CLI can resolve\n// the default catalog with no network. Re-run `pnpm --filter @shortwind/cli gen:catalog`\n// after the catalog changes.\nimport type { Presets } from \"./registry-source.js\";\n\nexport const CATALOG_PRESETS: Presets = {\n \"starter\": [\"card\", \"button\", \"layout\", \"text\", \"form\"],\n \"app\": [\"card\", \"button\", \"layout\", \"text\", \"form\", \"badge\", \"table\", \"dialog\", \"list\", \"navigation\", \"feedback\", \"tooltip\", \"menu\", \"sheet\", \"stat\", \"segmented\", \"switch\"],\n \"content\": [\"card\", \"button\", \"layout\", \"text\", \"form\", \"badge\", \"code\", \"list\", \"media\", \"empty\"],\n \"all\": \"*\"\n};\n\nexport const CATALOG_FAMILIES: string[] = [\"badge\",\"button\",\"card\",\"code\",\"dialog\",\"empty\",\"feedback\",\"form\",\"icon\",\"layout\",\"list\",\"media\",\"menu\",\"navigation\",\"progress\",\"segmented\",\"sheet\",\"skeleton\",\"stat\",\"surface\",\"switch\",\"table\",\"text\",\"tooltip\"];\n\nexport const CATALOG_RECIPES: Record<string, string> = {\n \"badge\": \"/* shortwind: badge@0.0.2 sha:07c6ab5a8deb77e3 */\\n\\n/* @guide\\n @badge is the default. Its color is DATA-DRIVEN: it reads the --tone-bg /\\n --tone-fg CSS variables, so set the tone from your data with a data-tone\\n attribute instead of picking a different recipe name —\\n `<span className=\\\"@badge\\\" data-tone={severity}>`. Define your tones once in\\n CSS (init scaffolds neutral/success/warning/danger/info; add your own\\n `[data-tone=\\\"sev1\\\"] { --tone-bg: …; --tone-fg: … }`). With no data-tone it\\n falls back to the neutral muted look, identical to before.\\n\\n The static @badge-success/warning/danger/info variants still exist for when\\n the tone is known at author time; reach for data-tone when it's chosen from\\n data. @badge-outline is unfilled. @badge-base is a color-less shell for\\n building custom tones — not for direct use.\\n*/\\n\\n/* Badge shell — shape, sizing, focus ring. No bg/text/border color so\\n variants can supply their own tone without conflicts. */\\n@recipe badge-base {\\n inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\\n}\\n\\n/* Default badge — tone-aware. Reads --tone-bg/--tone-fg (set via data-tone),\\n falling back to the neutral muted tokens so it renders unchanged when no\\n tone is set. */\\n@recipe badge {\\n inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium bg-[var(--tone-bg,var(--muted))] text-[var(--tone-fg,var(--muted-foreground))]\\n}\\n\\n/* Success tone badge. */\\n@recipe badge-success {\\n inline-flex items-center gap-1 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800 dark:bg-green-900 dark:text-green-200\\n}\\n\\n/* Warning tone badge. */\\n@recipe badge-warning {\\n inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-0.5 text-xs font-medium text-amber-800 dark:bg-amber-900 dark:text-amber-200\\n}\\n\\n/* Danger tone badge. */\\n@recipe badge-danger {\\n inline-flex items-center gap-1 rounded-full bg-destructive/15 px-2 py-0.5 text-xs font-medium text-destructive\\n}\\n\\n/* Info tone badge. */\\n@recipe badge-info {\\n inline-flex items-center gap-1 rounded-full bg-primary/15 px-2 py-0.5 text-xs font-medium text-primary\\n}\\n\\n/* Outline badge — no fill. */\\n@recipe badge-outline {\\n inline-flex items-center gap-1 rounded-full border border-border px-2 py-0.5 text-xs font-medium text-foreground\\n}\\n\",\n \"button\": \"/* shortwind: button@0.0.2 sha:b0b08652cd757b90 */\\n\\n/* @guide\\n Name order is @button-<intent>[-<size>]: intent first (primary/secondary/\\n ghost/danger/outline), size second (sm/lg; omit for default). One intent per\\n button — never combine @button-primary with @button-danger. @button-ghost is\\n text-only, @button-outline is bordered with no fill, @button-icon is a square\\n icon button. @button-base is the shared shell; don't use it on its own.\\n The short @btn-<intent> forms are aliases that expand identically; the\\n full-word @button-* names are canonical — prefer them.\\n*/\\n\\n/* Shared button base — sizing, focus ring, disabled state. */\\n@recipe btn-base {\\n inline-flex items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring disabled:cursor-not-allowed disabled:opacity-50\\n}\\n\\n/* Primary call-to-action button. */\\n@recipe btn-primary {\\n @btn-base bg-primary text-primary-foreground hover:bg-primary/90\\n}\\n\\n/* Small primary button. */\\n@recipe btn-primary-sm {\\n @btn-primary px-3 py-1.5 text-xs\\n}\\n\\n/* Large primary button. */\\n@recipe btn-primary-lg {\\n @btn-primary px-6 py-3 text-base\\n}\\n\\n/* Secondary button — bordered surface tone. */\\n@recipe btn-secondary {\\n @btn-base border border-border bg-secondary text-secondary-foreground hover:bg-secondary/80\\n}\\n\\n/* Small secondary button. */\\n@recipe btn-secondary-sm {\\n @btn-secondary px-3 py-1.5 text-xs\\n}\\n\\n/* Large secondary button. */\\n@recipe btn-secondary-lg {\\n @btn-secondary px-6 py-3 text-base\\n}\\n\\n/* Ghost button — text only, no background. */\\n@recipe btn-ghost {\\n @btn-base text-foreground hover:bg-muted\\n}\\n\\n/* Small ghost button. */\\n@recipe btn-ghost-sm {\\n @btn-ghost px-3 py-1.5 text-xs\\n}\\n\\n/* Large ghost button. */\\n@recipe btn-ghost-lg {\\n @btn-ghost px-6 py-3 text-base\\n}\\n\\n/* Destructive button. */\\n@recipe btn-danger {\\n @btn-base bg-destructive text-destructive-foreground hover:bg-destructive/90\\n}\\n\\n/* Outline button — bordered without fill. */\\n@recipe btn-outline {\\n @btn-base border border-primary text-primary hover:bg-primary/10\\n}\\n\\n/* Square icon-only button. */\\n@recipe btn-icon {\\n inline-flex h-9 w-9 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring disabled:cursor-not-allowed disabled:opacity-50\\n}\\n\\n/* Full-word canonical aliases — expand identically to the @btn-* short forms. */\\n\\n/* Shared button shell (alias of @btn-base). */\\n@recipe button-base { @btn-base }\\n\\n/* Primary call-to-action button (alias of @btn-primary). */\\n@recipe button-primary { @btn-primary }\\n\\n/* Small primary button (alias of @btn-primary-sm). */\\n@recipe button-primary-sm { @btn-primary-sm }\\n\\n/* Large primary button (alias of @btn-primary-lg). */\\n@recipe button-primary-lg { @btn-primary-lg }\\n\\n/* Secondary button (alias of @btn-secondary). */\\n@recipe button-secondary { @btn-secondary }\\n\\n/* Small secondary button (alias of @btn-secondary-sm). */\\n@recipe button-secondary-sm { @btn-secondary-sm }\\n\\n/* Large secondary button (alias of @btn-secondary-lg). */\\n@recipe button-secondary-lg { @btn-secondary-lg }\\n\\n/* Ghost button (alias of @btn-ghost). */\\n@recipe button-ghost { @btn-ghost }\\n\\n/* Small ghost button (alias of @btn-ghost-sm). */\\n@recipe button-ghost-sm { @btn-ghost-sm }\\n\\n/* Large ghost button (alias of @btn-ghost-lg). */\\n@recipe button-ghost-lg { @btn-ghost-lg }\\n\\n/* Destructive button (alias of @btn-danger). */\\n@recipe button-danger { @btn-danger }\\n\\n/* Outline button (alias of @btn-outline). */\\n@recipe button-outline { @btn-outline }\\n\\n/* Square icon-only button (alias of @btn-icon). */\\n@recipe button-icon { @btn-icon }\\n\",\n \"card\": \"/* shortwind: card@0.0.1 sha:4813cef10cd21824 */\\n\\n/* @guide\\n Pick exactly one container: @card (default), @card-elevated (raised shadow),\\n @card-flat (no border), or @card-interactive (clickable hover/focus) — don't\\n stack two container variants on one element. Lay out the inside with\\n @card-header / @card-body / @card-footer.\\n*/\\n\\n/* Default content card with border, padding, and surface color. */\\n@recipe card {\\n rounded-lg border border-border bg-card text-card-foreground p-4\\n}\\n\\n/* Card with raised shadow for emphasis. */\\n@recipe card-elevated {\\n @card shadow-md\\n}\\n\\n/* Card without border, on a muted surface. */\\n@recipe card-flat {\\n rounded-lg bg-muted text-foreground p-4\\n}\\n\\n/* Clickable card with hover and focus-visible states. */\\n@recipe card-interactive {\\n @card cursor-pointer transition-shadow hover:shadow-md focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\\n}\\n\\n/* Card header region with bottom divider. */\\n@recipe card-header {\\n mb-3 border-b border-border pb-3\\n}\\n\\n/* Card body region. */\\n@recipe card-body {\\n py-1\\n}\\n\\n/* Card footer with top divider and right-aligned actions. */\\n@recipe card-footer {\\n mt-3 flex items-center justify-end gap-2 border-t border-border pt-3\\n}\\n\",\n \"code\": \"/* shortwind: code@0.0.1 sha:cc8d2b0bedb5b453 */\\n\\n/* @guide\\n @code-inline for a code span inside prose, @code-block for a multi-line\\n preformatted block, @kbd for a keyboard-shortcut hint. Pick by context, not\\n by size.\\n*/\\n\\n/* Inline code span. */\\n@recipe code-inline {\\n rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-foreground\\n}\\n\\n/* Block of preformatted code. */\\n@recipe code-block {\\n overflow-x-auto rounded-md border border-border bg-muted p-4 font-mono text-sm leading-6 text-foreground\\n}\\n\\n/* Keyboard shortcut hint. */\\n@recipe kbd {\\n inline-flex items-center rounded border border-border bg-muted px-1.5 py-0.5 font-mono text-xs text-foreground shadow-sm\\n}\\n\",\n \"dialog\": \"/* shortwind: dialog@0.0.1 sha:b4dbf853e99fce75 */\\n\\n/* @guide\\n A modal is three layers: @dialog-overlay (dimmed backdrop), @dialog (the\\n centering wrapper), and @dialog-content (the panel). Structure the panel with\\n @dialog-header and @dialog-footer. Don't put content styling on @dialog\\n itself — it's only the positioner.\\n*/\\n\\n/* Modal dialog wrapper — covers the viewport, centers content. */\\n@recipe dialog {\\n fixed inset-0 z-50 flex items-center justify-center p-4\\n}\\n\\n/* Dimmed overlay behind the dialog. */\\n@recipe dialog-overlay {\\n fixed inset-0 z-40 bg-black/50\\n}\\n\\n/* Dialog content panel. */\\n@recipe dialog-content {\\n relative z-50 w-full max-w-md rounded-lg border border-border bg-popover text-popover-foreground p-6 shadow-xl\\n}\\n\\n/* Dialog header region with title. */\\n@recipe dialog-header {\\n mb-4 flex flex-col gap-1\\n}\\n\\n/* Dialog footer with right-aligned actions. */\\n@recipe dialog-footer {\\n mt-6 flex items-center justify-end gap-2\\n}\\n\",\n \"empty\": \"/* shortwind: empty@0.0.1 sha:003168e1b4a395c5 */\\n\\n/* @guide\\n @empty is the container for a no-data state; fill it with @empty-icon,\\n @empty-title, and @empty-description. These are slots for that pattern — use\\n @heading-* / @body from the text family for ordinary copy.\\n*/\\n\\n/* Empty-state container. */\\n@recipe empty {\\n flex flex-col items-center justify-center gap-3 rounded-md border border-dashed border-border p-8 text-center\\n}\\n\\n/* Empty-state icon slot. */\\n@recipe empty-icon {\\n flex h-12 w-12 items-center justify-center rounded-full bg-muted text-muted-foreground\\n}\\n\\n/* Empty-state title text. */\\n@recipe empty-title {\\n text-base font-semibold text-foreground\\n}\\n\\n/* Empty-state supporting description. */\\n@recipe empty-description {\\n max-w-sm text-sm text-muted-foreground\\n}\\n\",\n \"feedback\": \"/* shortwind: feedback@0.0.1 sha:818868f6af179bcf */\\n\\n/* @guide\\n Inline messages use @alert (neutral) or a tone variant @alert-success/\\n warning/danger/info — one tone each. @callout is a left-accent inline note,\\n @toast is a floating notification, @banner spans the full viewport width.\\n*/\\n\\n/* Default informational alert. */\\n@recipe alert {\\n flex items-start gap-3 rounded-md border border-border bg-card p-4 text-sm text-card-foreground\\n}\\n\\n/* Success alert. */\\n@recipe alert-success {\\n flex items-start gap-3 rounded-md border border-green-200 bg-green-50 p-4 text-sm text-green-900 dark:border-green-900 dark:bg-green-950 dark:text-green-100\\n}\\n\\n/* Warning alert. */\\n@recipe alert-warning {\\n flex items-start gap-3 rounded-md border border-amber-200 bg-amber-50 p-4 text-sm text-amber-900 dark:border-amber-900 dark:bg-amber-950 dark:text-amber-100\\n}\\n\\n/* Danger alert. */\\n@recipe alert-danger {\\n flex items-start gap-3 rounded-md border border-destructive/30 bg-destructive/10 p-4 text-sm text-destructive\\n}\\n\\n/* Informational alert. */\\n@recipe alert-info {\\n flex items-start gap-3 rounded-md border border-primary/30 bg-primary/10 p-4 text-sm text-primary\\n}\\n\\n/* Inline callout — flush left edge accent. */\\n@recipe callout {\\n border-l-4 border-primary bg-primary/10 p-4 text-sm text-foreground\\n}\\n\\n/* Floating toast notification. */\\n@recipe toast {\\n pointer-events-auto flex items-start gap-3 rounded-md border border-border bg-popover p-4 text-sm text-popover-foreground shadow-lg\\n}\\n\\n/* Full-width banner spanning the viewport. */\\n@recipe banner {\\n w-full bg-primary px-4 py-2 text-center text-sm font-medium text-primary-foreground\\n}\\n\",\n \"form\": \"/* shortwind: form@0.0.2 sha:e78b50acd247f5af */\\n\\n/* @guide\\n Wrap each label+control+message in @field (use @field-error for the invalid\\n state); group related fields with @fieldset. Controls are bare: @input,\\n @textarea, @select, @checkbox, @radio, plus @input-error for invalid text\\n and @input-shell for the transparent input shell. Helper text is\\n @help. There is no @form-group (use @field), @form-input (use @input),\\n @form-helper (use @help) or @form-checkbox (use @checkbox); the field label\\n recipe is @label, in the text family.\\n*/\\n\\n/* Text input field. */\\n@recipe input {\\n block w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-ring focus:outline-2 focus:outline-offset-2 focus:outline-ring disabled:cursor-not-allowed disabled:opacity-50\\n}\\n\\n/* Dinachi-style input shell — transparent background, h-9, file/\\n placeholder/selection/aria-invalid/focus-visible states baked in. */\\n@recipe input-shell {\\n flex h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs outline-none transition-[color,box-shadow] placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:border-destructive aria-invalid:ring-destructive/20 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40\\n}\\n\\n/* Input in error state. */\\n@recipe input-error {\\n @input border-destructive focus:border-destructive focus:outline-destructive\\n}\\n\\n/* Multi-line textarea. */\\n@recipe textarea {\\n block w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-ring focus:outline-2 focus:outline-offset-2 focus:outline-ring disabled:cursor-not-allowed disabled:opacity-50\\n}\\n\\n/* Native select control. */\\n@recipe select {\\n block w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground focus:border-ring focus:outline-2 focus:outline-offset-2 focus:outline-ring disabled:cursor-not-allowed disabled:opacity-50\\n}\\n\\n/* Checkbox input. */\\n@recipe checkbox {\\n h-4 w-4 rounded border-input text-primary focus:outline-2 focus:outline-offset-2 focus:outline-ring\\n}\\n\\n/* Radio input. */\\n@recipe radio {\\n h-4 w-4 border-input text-primary focus:outline-2 focus:outline-offset-2 focus:outline-ring\\n}\\n\\n/* Form field wrapper — label + input + help/error. */\\n@recipe field {\\n flex flex-col gap-1.5\\n}\\n\\n/* Form field in error state. */\\n@recipe field-error {\\n flex flex-col gap-1.5\\n}\\n\\n/* Grouped form section with optional legend. */\\n@recipe fieldset {\\n flex flex-col gap-4 rounded-md border border-border p-4\\n}\\n\\n/* Field-level helper text. */\\n@recipe help {\\n text-xs text-muted-foreground\\n}\\n\",\n \"icon\": \"/* shortwind: icon@0.0.1 sha:fab3b2d3a0c089fb */\\n\\n/* @guide\\n Size an icon with @icon-sm/md/lg (16/20/24px) — these set width and height\\n only; add @icon-muted for secondary color. They're for SVG/icon elements, not\\n to be confused with @btn-icon (the icon button in the button family).\\n*/\\n\\n/* Small icon — 16px. */\\n@recipe icon-sm {\\n h-4 w-4 shrink-0\\n}\\n\\n/* Default icon size — 20px. */\\n@recipe icon-md {\\n h-5 w-5 shrink-0\\n}\\n\\n/* Large icon — 24px. */\\n@recipe icon-lg {\\n h-6 w-6 shrink-0\\n}\\n\\n/* Icon with muted color. */\\n@recipe icon-muted {\\n text-muted-foreground\\n}\\n\",\n \"layout\": \"/* shortwind: layout@0.0.2 sha:48025daeec83041a */\\n\\n/* @guide\\n Composition primitives. @stack-* stacks children vertically (flex-col);\\n @row* lays them out horizontally (flex-row). Choose the gap with the size\\n suffix (xs/sm/md/lg on stacks); bare @stack is the default and equals\\n @stack-md. Use @grid-2/3/4 only for true multi-column grids, @center to\\n center on both axes, @full to fill the parent. Common slips: there is no\\n @flex-row (use @row) or @flex-col (use a @stack-*), and the grids are\\n @grid-3, not @grid-cols-3.\\n*/\\n\\n/* Bare stack — default gap, alias for @stack-md. */\\n@recipe stack { @stack-md }\\n\\n/* Vertical stack with extra-small gap. */\\n@recipe stack-xs {\\n flex flex-col gap-1\\n}\\n\\n/* Vertical stack with small gap. */\\n@recipe stack-sm {\\n flex flex-col gap-2\\n}\\n\\n/* Vertical stack with medium gap. */\\n@recipe stack-md {\\n flex flex-col gap-4\\n}\\n\\n/* Vertical stack with large gap. */\\n@recipe stack-lg {\\n flex flex-col gap-8\\n}\\n\\n/* Horizontal row with default gap and centered items. */\\n@recipe row {\\n flex flex-row items-center gap-2\\n}\\n\\n/* Horizontal row with space between children. */\\n@recipe row-between {\\n flex flex-row items-center justify-between gap-2\\n}\\n\\n/* Horizontal row aligned to the end. */\\n@recipe row-end {\\n flex flex-row items-center justify-end gap-2\\n}\\n\\n/* Two-column responsive grid. */\\n@recipe grid-2 {\\n grid grid-cols-1 gap-4 sm:grid-cols-2\\n}\\n\\n/* Three-column responsive grid. */\\n@recipe grid-3 {\\n grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\\n}\\n\\n/* Four-column responsive grid. */\\n@recipe grid-4 {\\n grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\\n}\\n\\n/* Center content horizontally and vertically. */\\n@recipe center {\\n flex items-center justify-center\\n}\\n\\n/* Fill the available width and height. */\\n@recipe full {\\n h-full w-full\\n}\\n\",\n \"list\": \"/* shortwind: list@0.0.2 sha:6a7f93aa95067c44 */\\n\\n/* @guide\\n @list wraps a stack of @list-item rows; use @list-bordered for divided rows.\\n Definition lists are separate: @description-list with @description-term and\\n @description-detail. The short @dl / @dt / @dd forms are aliases that expand\\n identically; the spelled-out names are canonical — prefer them. For site\\n navigation reach for the navigation family (@navigation), not @list.\\n*/\\n\\n/* Vertical list with default gap. */\\n@recipe list {\\n flex flex-col gap-1\\n}\\n\\n/* Single list item. */\\n@recipe list-item {\\n flex items-center gap-2 rounded-md px-3 py-2 text-sm text-foreground\\n}\\n\\n/* List with dividing borders between items. */\\n@recipe list-bordered {\\n divide-y divide-border rounded-md border border-border\\n}\\n\\n/* Definition list container. */\\n@recipe dl {\\n grid grid-cols-1 gap-2 sm:grid-cols-3 sm:gap-4\\n}\\n\\n/* Definition term. */\\n@recipe dt {\\n text-sm font-medium text-muted-foreground\\n}\\n\\n/* Definition description. */\\n@recipe dd {\\n text-sm text-foreground sm:col-span-2\\n}\\n\\n/* Spelled-out canonical aliases — expand identically to @dl/@dt/@dd. */\\n\\n/* Definition list container (alias of @dl). */\\n@recipe description-list { @dl }\\n\\n/* Definition term (alias of @dt). */\\n@recipe description-term { @dt }\\n\\n/* Definition description (alias of @dd). */\\n@recipe description-detail { @dd }\\n\",\n \"media\": \"/* shortwind: media@0.0.1 sha:dfc85633101b0637 */\\n\\n/* @guide\\n @avatar (with @avatar-sm/lg) is a round user image; @thumb is a small square\\n thumbnail. For responsive embeds use @aspect-square or @aspect-video. Avatars\\n are circular by default — don't restyle the radius.\\n*/\\n\\n/* User/profile avatar. */\\n@recipe avatar {\\n inline-flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-muted text-sm font-medium text-muted-foreground\\n}\\n\\n/* Small avatar. */\\n@recipe avatar-sm {\\n @avatar h-6 w-6 text-xs\\n}\\n\\n/* Large avatar. */\\n@recipe avatar-lg {\\n @avatar h-14 w-14 text-base\\n}\\n\\n/* Small image thumbnail. */\\n@recipe thumb {\\n h-16 w-16 rounded-md object-cover\\n}\\n\\n/* 1:1 aspect-ratio wrapper. */\\n@recipe aspect-square {\\n aspect-square w-full overflow-hidden rounded-md\\n}\\n\\n/* 16:9 aspect-ratio wrapper. */\\n@recipe aspect-video {\\n aspect-video w-full overflow-hidden rounded-md\\n}\\n\",\n \"menu\": \"/* shortwind: menu@0.0.1 sha:c95d3650d92a2fa6 */\\n\\n/* @guide\\n A dropdown/actions menu (the \\\"…\\\" button's popover). @menu is the panel,\\n @menu-item is a row, @menu-trigger is the control that opens it, and\\n @menu-separator divides groups. The highlighted/active row is data-driven —\\n set data-active on the item rather than swapping recipe names\\n (`<button class=\\\"@menu-item\\\" data-active>`); data-disabled dims + disables a\\n row. @menu only styles the panel; pair it with your own positioning.\\n*/\\n\\n/* Dropdown menu panel. */\\n@recipe menu {\\n min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md\\n}\\n\\n/* A single menu row. Highlight with data-active, dim with data-disabled. */\\n@recipe menu-item {\\n relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm text-foreground outline-none transition-colors hover:bg-muted focus-visible:bg-muted data-[active]:bg-muted data-[disabled]:pointer-events-none data-[disabled]:opacity-50\\n}\\n\\n/* The control that opens the menu. */\\n@recipe menu-trigger {\\n inline-flex items-center justify-center gap-1 outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\\n}\\n\\n/* Divider between menu groups. */\\n@recipe menu-separator {\\n -mx-1 my-1 h-px bg-border\\n}\\n\",\n \"navigation\": \"/* shortwind: navigation@0.0.2 sha:2bb73370d69e0f65 */\\n\\n/* @guide\\n @navigation is the container; links are @navigation-link with\\n @navigation-link-active for the current page. Tabs mirror that pair: @tab and\\n @tab-active. Use @breadcrumb for trail navigation. Active and inactive are\\n separate recipes — swap the whole class rather than combining them. The short\\n @nav / @nav-link forms are aliases that expand identically; the full-word\\n @navigation-* names are canonical — prefer them.\\n*/\\n\\n/* Top-level nav container. */\\n@recipe nav {\\n flex items-center gap-1\\n}\\n\\n/* Inactive nav link with hover/focus states. */\\n@recipe nav-link {\\n inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\\n}\\n\\n/* Active nav link. */\\n@recipe nav-link-active {\\n inline-flex items-center gap-2 rounded-md bg-muted px-3 py-1.5 text-sm font-medium text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\\n}\\n\\n/* Breadcrumb trail container. */\\n@recipe breadcrumb {\\n flex items-center gap-1.5 text-sm text-muted-foreground\\n}\\n\\n/* Inactive tab control. */\\n@recipe tab {\\n inline-flex items-center gap-2 border-b-2 border-transparent px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:border-border hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\\n}\\n\\n/* Active tab control. */\\n@recipe tab-active {\\n inline-flex items-center gap-2 border-b-2 border-primary px-3 py-2 text-sm font-medium text-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\\n}\\n\\n/* Full-word canonical aliases — expand identically to the @nav-* short forms. */\\n\\n/* Top-level nav container (alias of @nav). */\\n@recipe navigation { @nav }\\n\\n/* Inactive nav link (alias of @nav-link). */\\n@recipe navigation-link { @nav-link }\\n\\n/* Active nav link (alias of @nav-link-active). */\\n@recipe navigation-link-active { @nav-link-active }\\n\",\n \"progress\": \"/* shortwind: progress@0.0.1 sha:37210e32d2cd555f */\\n\\n/* @guide\\n A bar is two pieces: @progress-track (the background) wrapping @progress-bar\\n (the fill). For an indeterminate state use @spinner instead — it's a\\n standalone loader, not a bar.\\n*/\\n\\n/* Progress bar track (background). */\\n@recipe progress-track {\\n h-2 w-full overflow-hidden rounded-full bg-muted\\n}\\n\\n/* Progress bar fill. */\\n@recipe progress-bar {\\n h-full rounded-full bg-primary transition-all\\n}\\n\\n/* Indeterminate loading spinner. */\\n@recipe spinner {\\n inline-block h-4 w-4 animate-spin rounded-full border-2 border-border border-t-primary\\n}\\n\",\n \"segmented\": \"/* shortwind: segmented@0.0.1 sha:60bb982c1fc271f0 */\\n\\n/* @guide\\n A segmented control / filter bar (one choice highlighted). @segmented is the\\n track, @segmented-item is each segment. The selected segment is data-driven —\\n set data-active on the chosen item (`<button class=\\\"@segmented-item\\\"\\n data-active>`) rather than swapping recipe names. Use @tab/@tab-active from\\n the navigation family for underlined tabs instead.\\n*/\\n\\n/* Segmented-control track. */\\n@recipe segmented {\\n inline-flex items-center gap-1 rounded-md bg-muted p-1\\n}\\n\\n/* A segment. Mark the selected one with data-active. */\\n@recipe segmented-item {\\n inline-flex items-center justify-center gap-1.5 rounded-sm px-3 py-1 text-sm font-medium text-muted-foreground transition-colors hover:text-foreground data-[active]:bg-background data-[active]:text-foreground data-[active]:shadow-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\\n}\\n\",\n \"sheet\": \"/* shortwind: sheet@0.0.1 sha:2dd5109c9e3865fa */\\n\\n/* @guide\\n A slide-over panel (drawer). Three layers mirror the dialog family:\\n @sheet-overlay (dimmed backdrop), @sheet (the positioner that anchors the\\n panel to a side), and @sheet-content (the panel itself). Structure the panel\\n with @sheet-header and @sheet-footer. The edge is data-driven — set\\n data-side=left/right on @sheet (`<div class=\\\"@sheet\\\" data-side=\\\"right\\\">`)\\n rather than picking a different recipe. @drawer is an alias for @sheet.\\n*/\\n\\n/* Dimmed backdrop behind the sheet. */\\n@recipe sheet-overlay {\\n fixed inset-0 z-40 bg-black/50\\n}\\n\\n/* Positioner — anchors the panel to a side via data-side. */\\n@recipe sheet {\\n fixed inset-0 z-50 flex data-[side=right]:justify-end data-[side=left]:justify-start\\n}\\n\\n/* The sliding panel. */\\n@recipe sheet-content {\\n flex h-full w-3/4 max-w-sm flex-col gap-4 border-border bg-popover p-6 text-popover-foreground shadow-xl\\n}\\n\\n/* Sheet header region. */\\n@recipe sheet-header {\\n flex flex-col gap-1\\n}\\n\\n/* Sheet footer with bottom-pinned, right-aligned actions. */\\n@recipe sheet-footer {\\n mt-auto flex items-center justify-end gap-2\\n}\\n\\n/* Alias — @drawer reads as @sheet. */\\n@recipe drawer { @sheet }\\n\",\n \"skeleton\": \"/* shortwind: skeleton@0.0.1 sha:cd2db5c1c48b16d3 */\\n\\n/* @guide\\n Match the skeleton to the shape it stands in for: @skeleton (block),\\n @skeleton-text (a text line), @skeleton-circle (avatar/icon). Size block and\\n text skeletons with raw width/height utilities.\\n*/\\n\\n/* Default rectangular skeleton placeholder. */\\n@recipe skeleton {\\n animate-pulse rounded-md bg-muted\\n}\\n\\n/* Single-line text skeleton. */\\n@recipe skeleton-text {\\n h-4 w-full animate-pulse rounded bg-muted\\n}\\n\\n/* Circular skeleton (avatar/icon). */\\n@recipe skeleton-circle {\\n h-10 w-10 animate-pulse rounded-full bg-muted\\n}\\n\",\n \"stat\": \"/* shortwind: stat@0.0.1 sha:445f1f7abfebc0b1 */\\n\\n/* @guide\\n A dashboard metric tile. @stat is the container; inside it @stat-label names\\n the metric, @stat-value is the big number, and @stat-trend is the delta.\\n Colour the trend with the tone system — set data-tone on @stat-trend\\n (success for up, danger for down): `<span class=\\\"@stat-trend\\\"\\n data-tone=\\\"success\\\">`. Same --tone-fg mechanism as @badge; define tones once\\n in CSS (init scaffolds them).\\n*/\\n\\n/* Metric tile container. */\\n@recipe stat {\\n flex flex-col gap-1 rounded-lg border border-border bg-card p-4 text-card-foreground\\n}\\n\\n/* Metric name. */\\n@recipe stat-label {\\n text-sm font-medium text-muted-foreground\\n}\\n\\n/* The metric's primary value. */\\n@recipe stat-value {\\n text-2xl font-semibold tracking-tight text-foreground tabular-nums\\n}\\n\\n/* Trend delta — tone-aware; set data-tone (success up / danger down). */\\n@recipe stat-trend {\\n inline-flex items-center gap-1 text-xs font-medium text-[var(--tone-fg,var(--muted-foreground))]\\n}\\n\",\n \"surface\": \"/* shortwind: surface@0.0.2 sha:31716e4cbe81f93f */\\n\\n/* @guide\\n @surface / @surface-muted / @surface-accent set a background+foreground pair\\n for a region — one per section. @wrapper (or @wrapper-tight for prose)\\n centers and width-caps content; there is no @wrapper-lg, set a different cap\\n with max-w-* yourself. (Note: @container is reserved for Tailwind's\\n container-query utility, so the content wrapper is @wrapper.) @divider-h and\\n @divider-v are hairline rules.\\n*/\\n\\n/* Default page/section surface. */\\n@recipe surface {\\n bg-background text-foreground\\n}\\n\\n/* Muted surface — secondary background. */\\n@recipe surface-muted {\\n bg-muted text-foreground\\n}\\n\\n/* Accent surface — soft brand background. */\\n@recipe surface-accent {\\n bg-accent text-accent-foreground\\n}\\n\\n/* Centered content wrapper with a max width. */\\n@recipe wrapper {\\n mx-auto w-full max-w-6xl px-4 sm:px-6 lg:px-8\\n}\\n\\n/* Narrow content wrapper for prose. */\\n@recipe wrapper-tight {\\n mx-auto w-full max-w-3xl px-4 sm:px-6\\n}\\n\\n/* Horizontal divider line. */\\n@recipe divider-h {\\n shrink-0 h-px w-full bg-border\\n}\\n\\n/* Vertical divider line. */\\n@recipe divider-v {\\n shrink-0 h-full w-px bg-border\\n}\\n\",\n \"switch\": \"/* shortwind: switch@0.0.1 sha:54fa33bb72839799 */\\n\\n/* @guide\\n A toggle switch. @switch is the track, @switch-thumb is the sliding knob\\n inside it. The on/off state is data-driven — set data-checked on BOTH the\\n track and the thumb (`<button class=\\\"@switch\\\" data-checked><span\\n class=\\\"@switch-thumb\\\" data-checked /></button>`). For a checkbox/radio reach\\n for the form family instead.\\n*/\\n\\n/* Toggle track. Add data-checked when on. */\\n@recipe switch {\\n peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent bg-input transition-colors data-[checked]:bg-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring disabled:cursor-not-allowed disabled:opacity-50\\n}\\n\\n/* Sliding knob. Add data-checked to slide it on. */\\n@recipe switch-thumb {\\n pointer-events-none block h-4 w-4 translate-x-0 rounded-full bg-background shadow-sm transition-transform data-[checked]:translate-x-4\\n}\\n\",\n \"table\": \"/* shortwind: table@0.0.1 sha:4f3295935b2a647c */\\n\\n/* @guide\\n Wrap the table in @table-container for horizontal overflow, then put @table\\n (or @table-zebra for striped rows) on the <table>. Cells are @th (header) and\\n @td (body); add @tr-hover to a <tr> for row highlighting.\\n*/\\n\\n/* Scroll container for a wide table — keeps overflow horizontal. */\\n@recipe table-container {\\n w-full overflow-x-auto rounded-md border border-border\\n}\\n\\n/* Data table base. */\\n@recipe table {\\n w-full border-collapse text-left text-sm text-foreground\\n}\\n\\n/* Table header cell. */\\n@recipe th {\\n border-b border-border px-3 py-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground\\n}\\n\\n/* Table body cell. */\\n@recipe td {\\n border-b border-border px-3 py-2\\n}\\n\\n/* Row hover state. */\\n@recipe tr-hover {\\n transition-colors hover:bg-muted\\n}\\n\\n/* Table with zebra striping on alternating rows. */\\n@recipe table-zebra {\\n w-full border-collapse text-left text-sm text-foreground [&_tbody_tr:nth-child(odd)]:bg-muted\\n}\\n\",\n \"text\": \"/* shortwind: text@0.0.2 sha:f39dc7b88afefc8f */\\n\\n/* @guide\\n Headings are sized by weight, not HTML level: @heading-xl/lg/md/sm — there\\n is no @h1..@h6. Body copy: @body (default), @lead (intro paragraphs), @muted\\n (secondary), @caption (fine print), @eyebrow (uppercase kicker above a\\n heading). Use @label for form labels and @link for inline links. Don't\\n append a -text suffix: it's @body not @body-text, @muted not @muted-text,\\n @link not @link-text.\\n*/\\n\\n/* Top-level page heading. */\\n@recipe heading-xl {\\n text-4xl font-bold tracking-tight text-foreground\\n}\\n\\n/* Large section heading. */\\n@recipe heading-lg {\\n text-2xl font-semibold tracking-tight text-foreground\\n}\\n\\n/* Medium heading. */\\n@recipe heading-md {\\n text-xl font-semibold text-foreground\\n}\\n\\n/* Small heading. */\\n@recipe heading-sm {\\n text-base font-semibold text-foreground\\n}\\n\\n/* Default body text. */\\n@recipe body {\\n text-sm leading-6 text-foreground\\n}\\n\\n/* Lead paragraph — larger body copy for hero/intro sections. */\\n@recipe lead {\\n text-lg leading-relaxed text-muted-foreground\\n}\\n\\n/* Muted secondary text. */\\n@recipe muted {\\n text-sm text-muted-foreground\\n}\\n\\n/* Form label text. */\\n@recipe label {\\n text-sm font-medium text-foreground\\n}\\n\\n/* Caption — small supporting text. */\\n@recipe caption {\\n text-xs text-muted-foreground\\n}\\n\\n/* Eyebrow — uppercase kicker above a heading. */\\n@recipe eyebrow {\\n font-mono text-xs font-medium uppercase tracking-[0.2em] text-muted-foreground\\n}\\n\\n/* Inline link with hover/focus states. */\\n@recipe link {\\n text-primary underline-offset-2 hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\\n}\\n\",\n \"tooltip\": \"/* shortwind: tooltip@0.0.1 sha:1be3af4165357dcd */\\n\\n/* @guide\\n @tooltip is the floating label bubble — it styles appearance only, so pair it\\n with your own positioning.\\n*/\\n\\n/* Floating tooltip bubble. */\\n@recipe tooltip {\\n pointer-events-none z-50 rounded-md bg-foreground px-2 py-1 text-xs font-medium text-background shadow-md\\n}\\n\",\n};\n"],"mappings":";AAMA,MAAa,kBAA2B;CACtC,WAAW;EAAC;EAAQ;EAAU;EAAU;EAAQ;EAAO;CACvD,OAAO;EAAC;EAAQ;EAAU;EAAU;EAAQ;EAAQ;EAAS;EAAS;EAAU;EAAQ;EAAc;EAAY;EAAW;EAAQ;EAAS;EAAQ;EAAa;EAAS;CAC5K,WAAW;EAAC;EAAQ;EAAU;EAAU;EAAQ;EAAQ;EAAS;EAAQ;EAAQ;EAAS;EAAQ;CAClG,OAAO;CACR;AAED,MAAa,mBAA6B;CAAC;CAAQ;CAAS;CAAO;CAAO;CAAS;CAAQ;CAAW;CAAO;CAAO;CAAS;CAAO;CAAQ;CAAO;CAAa;CAAW;CAAY;CAAQ;CAAW;CAAO;CAAU;CAAS;CAAQ;CAAO;CAAU;AAE7P,MAAa,kBAA0C;CACrD,SAAS;CACT,UAAU;CACV,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,SAAS;CACT,YAAY;CACZ,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,cAAc;CACd,YAAY;CACZ,aAAa;CACb,SAAS;CACT,YAAY;CACZ,QAAQ;CACR,WAAW;CACX,UAAU;CACV,SAAS;CACT,QAAQ;CACR,WAAW;CACZ"}
package/dist/index.d.ts CHANGED
@@ -47,6 +47,8 @@ type InitResult = {
47
47
  skillPath: string;
48
48
  themePath: string | null;
49
49
  themeAction: ThemeAction;
50
+ tonesPath: string | null;
51
+ tonesAction: "written" | "skipped";
50
52
  safelistCssPaths: string[];
51
53
  missingThemeTokens: string[];
52
54
  supplementedThemeTokens: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/detect.ts","../src/theme.ts","../src/bundler-config.ts","../src/agents-file.ts","../src/init.ts","../src/registry-source.ts","../src/lockfile.ts","../src/commands/add.ts","../src/commands/remove.ts","../src/commands/new.ts","../src/commands/reseal.ts","../src/commands/preset.ts","../src/commands/ls.ts","../src/commands/build.ts","../src/commands/dev.ts","../src/commands/upgrade.ts","../src/commands/verify.ts","../src/commands/bench.ts","../src/commands/lint.ts","../src/fingerprint.ts","../src/project.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/detect.ts","../src/theme.ts","../src/bundler-config.ts","../src/agents-file.ts","../src/init.ts","../src/registry-source.ts","../src/lockfile.ts","../src/commands/add.ts","../src/commands/remove.ts","../src/commands/new.ts","../src/commands/reseal.ts","../src/commands/preset.ts","../src/commands/ls.ts","../src/commands/build.ts","../src/commands/dev.ts","../src/commands/upgrade.ts","../src/commands/verify.ts","../src/commands/bench.ts","../src/commands/lint.ts","../src/fingerprint.ts","../src/project.ts"],"mappings":";;;KAKY,cAAA;AAAA,KACA,OAAA;AAAA,KAOA,SAAA;AAAA,KAEA,YAAA;EACV,cAAA,EAAgB,cAAA;EAChB,eAAA;EACA,aAAA;EACA,OAAA,EAAS,OAAA;EACT,SAAA,EAAW,SAAA;EACX,cAAA;AAAA;AAAA,iBAyBc,aAAA,CAAc,GAAA,WAAc,YAAA;;;KCuDhC,WAAA;;;KC3FA,iBAAA;;;KCyBA,gBAAA;;;cCGC,gBAAA;AAAA,KAED,WAAA;EACV,GAAA;EACA,MAAA;EACA,QAAA;EACA,eAAA,GAAkB,eAAA;AAAA;AAAA,KAGR,eAAA,IACV,EAAA,EAAI,cAAA,EACJ,QAAA,YACA,GAAA,aACG,OAAA;AAAA,KAEO,UAAA;EACV,cAAA,EAAgB,cAAA;EAGhB,OAAA,EAAS,UAAA,QAAkB,aAAA;EAC3B,MAAA;EACA,QAAA;EACA,QAAA;EACA,iBAAA;EACA,iBAAA;EACA,eAAA;EACA,UAAA;EACA,UAAA;EAEA,SAAA;EACA,SAAA;EACA,SAAA;EACA,WAAA,EAAa,WAAA;EAIb,SAAA;EACA,WAAA;EAGA,gBAAA;EAIA,kBAAA;EAGA,uBAAA;EACA,iBAAA;EACA,mBAAA,EAAqB,iBAAA;EACrB,oBAAA;EACA,cAAA;EACA,gBAAA,EAAkB,gBAAA;EAClB,SAAA;EACA,YAAA;AAAA;AAAA,iBAGoB,IAAA,CAAK,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,UAAA;;;KC3F9C,OAAA,GAAU,MAAA;AAAA,KAEV,cAAA;EACV,MAAA;EACA,WAAA,QAAmB,OAAA,CAAQ,OAAA;EAC3B,UAAA,GAAa,MAAA,aAAmB,OAAA;EAChC,eAAA,QAAuB,OAAA;AAAA;AAAA,iBAgBT,oBAAA,CAAqB,MAAA,WAAiB,cAAA;AAAA,iBAqLtC,qBAAA,CACd,MAAA,UACA,OAAA,EAAS,OAAA,EACT,WAAA;;;KC9MU,SAAA;EAAc,OAAA;EAAiB,GAAA;AAAA;AAAA,KAC/B,QAAA;EACV,OAAA;EACA,QAAA;EACA,QAAA,EAAU,MAAA,SAAe,SAAA;AAAA;AAAA,iBAUL,YAAA,CAAa,UAAA,WAAqB,OAAA,CAAQ,QAAA;AAAA,iBAoC1C,aAAA,CAAc,UAAA,UAAoB,IAAA,EAAM,QAAA,GAAW,OAAA;;;KCpC7D,UAAA;EACV,GAAA;EACA,QAAA;EACA,EAAA;EACA,GAAA;EACA,KAAA;EACA,QAAA;AAAA;AAAA,KAGU,SAAA;EACV,KAAA;EACA,OAAA;EACA,WAAA;EACA,mBAAA;IAAuB,MAAA;IAAgB,UAAA;EAAA;EACvC,QAAA,EAAU,QAAA;EACV,SAAA;AAAA;AAAA,iBAGoB,GAAA,CAAI,OAAA,EAAS,UAAA,GAAa,OAAA,CAAQ,SAAA;;;KCzB5C,aAAA;EACV,GAAA;EACA,QAAA;AAAA;AAAA,KAGU,YAAA;EACV,OAAA;EACA,QAAA;EACA,gBAAA;IAAoB,SAAA;IAAmB,UAAA;EAAA;EACvC,QAAA,EAAU,QAAA;EACV,SAAA;AAAA;AAAA,iBAGoB,MAAA,CAAO,OAAA,EAAS,aAAA,GAAgB,OAAA,CAAQ,YAAA;;;KClBlD,UAAA;EACV,GAAA;EACA,MAAA;EACA,KAAA;AAAA;AAAA,KAGU,SAAA;EACV,UAAA;EACA,SAAA;AAAA;AAAA,cAGW,cAAA,SAAuB,KAAA;cACtB,OAAA;AAAA;AAAA,iBAyBQ,SAAA,CAAU,OAAA,EAAS,UAAA,GAAa,OAAA,CAAQ,SAAA;;;KCpClD,aAAA;EACV,GAAA;EACA,QAAA;AAAA;AAAA,KAGU,YAAA;EACV,QAAA;EACA,SAAA;EACA,QAAA;EACA,QAAA;AAAA;AAAA,iBAQoB,MAAA,CAAO,OAAA,EAAS,aAAA,GAAgB,OAAA,CAAQ,YAAA;;;KCnBlD,aAAA;EACV,GAAA;EACA,IAAA;EACA,QAAA;AAAA;AAAA,KAGU,YAAA,GAAe,SAAA;EAAc,MAAA;AAAA;AAAA,iBAEnB,MAAA,CAAO,OAAA,EAAS,aAAA,GAAgB,OAAA,CAAQ,YAAA;;;KCRlD,SAAA;EACV,GAAA;EACA,QAAA;EACA,aAAA;EACA,aAAA;AAAA;AAAA,KAGU,QAAA;EACV,SAAA;IAAa,MAAA;IAAgB,OAAA;EAAA;EAC7B,SAAA;AAAA;AAAA,iBAGoB,EAAA,CAAG,OAAA,EAAS,SAAA,GAAY,OAAA,CAAQ,QAAA;AAAA,iBA2BtC,YAAA,CAAa,MAAA,EAAQ,QAAA;;;KChCzB,YAAA;EACV,GAAA;AAAA;AAAA,KAGU,WAAA;EACV,OAAA;EACA,QAAA;EACA,SAAA;EAIA,gBAAA;AAAA;AAAA,cAGW,UAAA,SAAmB,KAAA;EAAA,SACrB,WAAA,EAAa,UAAA;cAEV,WAAA,EAAa,UAAA;AAAA;AAAA,iBAWL,KAAA,CAAM,OAAA,EAAS,YAAA,GAAe,OAAA,CAAQ,WAAA;;;KCnChD,UAAA;EACV,GAAA;EACA,MAAA,GAAS,WAAA;EACT,QAAA,IAAY,MAAA,EAAQ,SAAA;EACpB,UAAA;EACA,mBAAA;AAAA;AAAA,KAGU,SAAA;EACN,IAAA;EAAe,UAAA;AAAA;EACf,IAAA;EAAiB,QAAA;EAAoB,OAAA;AAAA;EACrC,IAAA;EAAe,OAAA;AAAA;AAAA,iBAEC,GAAA,CAAI,OAAA,EAAS,UAAA,GAAa,OAAA;EAAU,IAAA,QAAY,OAAA;AAAA;;;KCJ1D,aAAA;AAAA,KAEA,cAAA;EACV,MAAA;EACA,KAAA;EACA,QAAA;IAAY,OAAA;IAAiB,GAAA;EAAA;EAC7B,QAAA;IAAY,OAAA;IAAiB,IAAA;EAAA;AAAA;AAAA,KAGnB,eAAA,IAAmB,GAAA,EAAK,cAAA,KAAmB,OAAA,CAAQ,aAAA;AAAA,KAEnD,cAAA;EACV,GAAA;EACA,QAAA;EACA,QAAA;EACA,KAAA;EACA,KAAA;EACA,QAAA,GAAW,eAAA;EACX,MAAA,GAAS,cAAA;AAAA;AAAA,KAGC,WAAA;AAAA,KAEA,aAAA;EACN,MAAA;EAAgB,MAAA;EAAmB,IAAA;EAAc,EAAA;EAAY,KAAA,EAAO,WAAA;AAAA;EACpE,MAAA;EAAgB,MAAA;EAAgB,MAAA;EAAyC,KAAA,EAAO,WAAA;AAAA;EAChF,MAAA;EAAgB,MAAA;EAAmB,MAAA;EAAgB,KAAA,EAAO,WAAA;AAAA;EAC1D,MAAA;EAAgB,MAAA;EAAwB,IAAA;EAAc,EAAA;EAAY,KAAA,EAAO,WAAA;AAAA;EACzE,MAAA;EAAgB,MAAA;EAAwB,IAAA;EAAc,EAAA;EAAY,KAAA,EAAO,WAAA;AAAA;AAAA,KAEnE,aAAA;EACV,QAAA,EAAU,aAAA;EACV,UAAA;EACA,UAAA;EACA,QAAA,EAAU,QAAA;EACV,SAAA;AAAA;AAAA,cAGW,YAAA,SAAqB,KAAA;EAAA,SACvB,MAAA;IAAU,MAAA;IAAgB,OAAA;EAAA;cACvB,MAAA;IAAU,MAAA;IAAgB,OAAA;EAAA;AAAA;AAAA,iBAOlB,OAAA,CAAQ,OAAA,EAAS,cAAA,GAAiB,OAAA,CAAQ,aAAA;;;KCvDpD,WAAA;EACN,MAAA;EAAgB,IAAA;EAAwB,IAAA;AAAA;EACxC,MAAA;EAAgB,IAAA;EAAyB,IAAA;EAAc,QAAA;EAAkB,MAAA;AAAA;EACzE,MAAA;EAAgB,IAAA;EAA4B,IAAA;EAAc,QAAA;AAAA;EAC1D,MAAA;EAAgB,IAAA;EAA2B,IAAA;EAAc,MAAA;EAAgB,MAAA;AAAA;EACzE,MAAA;EAAgB,IAAA;EAA4B,IAAA;AAAA;EAC5C,MAAA;EAAgB,IAAA;EAAsB,IAAA;AAAA;AAAA,KAEhC,aAAA;EACV,GAAA;AAAA;AAAA,KAGU,YAAA;EACV,EAAA;EACA,OAAA;EACA,MAAA,EAAQ,WAAA;AAAA;AAAA,iBAGY,MAAA,CAAO,OAAA,EAAS,aAAA,GAAgB,OAAA,CAAQ,YAAA;;;KCXlD,YAAA;EACV,GAAA;EACA,MAAA;EACA,IAAA;AAAA;AAAA,KAGU,eAAA;EACV,QAAA;EACA,kBAAA;EACA,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,iBAAA;AAAA;AAAA,KAGU,WAAA,GAAc,IAAA,CAAK,eAAA;AAAA,KAEnB,WAAA;EACV,KAAA,EAAO,eAAA;EACP,MAAA,EAAQ,WAAA;AAAA;AAAA,iBAOY,KAAA,CAAM,OAAA,EAAS,YAAA,GAAe,OAAA,CAAQ,WAAA;AAAA,iBA0I5C,gBAAA,CAAiB,MAAA,EAAQ,WAAA;;;cC5K5B,SAAA;AAAA,KAaD,IAAA,WAAe,SAAA;AAAA,KAEf,QAAA;AAAA,KAEA,OAAA;EACV,IAAA,EAAM,IAAA;EACN,QAAA,EAAU,QAAA;EACV,IAAA;EACA,IAAA;EACA,MAAA;EACA,OAAA;AAAA;AAAA,KAGU,WAAA;EACV,GAAA;EACA,KAAA,GAAQ,IAAA;EACR,GAAA;EACA,OAAA;AAAA;AAAA,KAGU,UAAA;EACV,EAAA;EACA,QAAA,EAAU,OAAA;EACV,UAAA;EAKA,YAAA;AAAA;AAAA,iBAYoB,IAAA,CAAK,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,UAAA;AAAA,KA6YrD,UAAA;EACH,UAAA;EAKA,UAAA;EACA,GAAA;EACA,MAAA,EAAQ,KAAA;IAAQ,KAAA;IAAe,IAAA;IAAc,MAAA;EAAA;EAI7C,aAAA,EAAe,KAAA;IAAQ,KAAA;IAAe,IAAA;IAAc,MAAA;EAAA;EAIpD,OAAA;AAAA;AAAA,iBAWc,kBAAA,CAAmB,MAAA,WAAiB,UAAA;AAAA,iBAyPpC,kBAAA,CAAmB,QAAA,EAAU,OAAA;;;KCjtBjC,YAAA;EACV,MAAA;EACA,OAAA;EACA,GAAA;AAAA;AAAA,iBAGc,aAAA,CAAc,MAAA,WAAiB,YAAA;AAAA,iBAgB/B,cAAA,CAAe,MAAA;AAAA,iBA2Bf,mBAAA,CAAoB,MAAA,UAAgB,MAAA;AAAA,iBAmBpC,eAAA,CAAgB,MAAA,UAAgB,OAAA,UAAiB,GAAA;AAAA,iBAIjD,gBAAA,CAAiB,MAAA,UAAgB,GAAA;AAAA,iBASjC,cAAA,CAAe,MAAA,UAAgB,MAAA,UAAgB,OAAA;;;;;;;;;;;iBCqE/C,oBAAA,CAAqB,MAAA,UAAgB,IAAA,UAAc,EAAA"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { A as createRegistrySource, D as init, F as extractHeader, I as rewriteHeaderSha, L as sealRecipeFile, M as detectProject, N as buildHeaderLine, O as readLockfile, P as computeBodySha, R as verifyFetchedFamily, T as DEFAULT_REGISTRY, _ as reseal, a as extractClassUsages, b as remove, c as verify, d as dev, f as BuildError, g as preset, h as ls, j as resolvePresetFamilies, k as writeLockfile, l as UpgradeError, m as formatLsText, n as formatBenchTable, o as formatFindingsText, p as build, r as ALL_RULES, s as lint, t as bench, u as upgrade, v as NewFamilyError, w as renameFamilyInSource, x as add, y as newFamily } from "./bench-B7SxNGiU.js";
1
+ import { A as createRegistrySource, D as init, F as extractHeader, I as rewriteHeaderSha, L as sealRecipeFile, M as detectProject, N as buildHeaderLine, O as readLockfile, P as computeBodySha, R as verifyFetchedFamily, T as DEFAULT_REGISTRY, _ as reseal, a as extractClassUsages, b as remove, c as verify, d as dev, f as BuildError, g as preset, h as ls, j as resolvePresetFamilies, k as writeLockfile, l as UpgradeError, m as formatLsText, n as formatBenchTable, o as formatFindingsText, p as build, r as ALL_RULES, s as lint, t as bench, u as upgrade, v as NewFamilyError, w as renameFamilyInSource, x as add, y as newFamily } from "./bench-DQtfv5aq.js";
2
2
  export { ALL_RULES, BuildError, DEFAULT_REGISTRY, NewFamilyError, UpgradeError, add, bench, build, buildHeaderLine, computeBodySha, createRegistrySource, detectProject, dev, extractClassUsages, extractHeader, formatBenchTable, formatFindingsText, formatLsText, init, lint, ls, newFamily, preset, readLockfile, remove, renameFamilyInSource, reseal, resolvePresetFamilies, rewriteHeaderSha, sealRecipeFile, upgrade, verify, verifyFetchedFamily, writeLockfile };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shortwind/cli",
3
- "version": "0.1.0-beta.13",
3
+ "version": "0.1.0-beta.14",
4
4
  "description": "Shortwind CLI — init, add, remove, upgrade, dev, build, lint, ls, preset. Provides the `shortwind` command.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,8 +19,8 @@
19
19
  "jsonc-parser": "^3.3.1",
20
20
  "picocolors": "^1.1.1",
21
21
  "tinyglobby": "^0.2.16",
22
- "@shortwind/core": "0.1.0-beta.13",
23
- "@shortwind/tailwind": "0.1.0-beta.13"
22
+ "@shortwind/core": "0.1.0-beta.14",
23
+ "@shortwind/tailwind": "0.1.0-beta.14"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/node": "^25.7.0",