@beaket/ui 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +228 -66
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4,8 +4,8 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/commands/add.ts
7
- import path3 from "path";
8
- import pc from "picocolors";
7
+ import path4 from "path";
8
+ import pc2 from "picocolors";
9
9
 
10
10
  // src/utils/config.ts
11
11
  import fs from "fs-extra";
@@ -117,17 +117,165 @@ async function fetchComponent(componentDef) {
117
117
  return files;
118
118
  }
119
119
 
120
+ // src/utils/theme.ts
121
+ import fs3 from "fs-extra";
122
+ import path3 from "path";
123
+ import pc from "picocolors";
124
+ import prompts2 from "prompts";
125
+ var THEME_START = "/* beaket:theme:start */";
126
+ var THEME_END = "/* beaket:theme:end */";
127
+ var LEGACY_MARKER = "Beaket UI Design System";
128
+ var LEGACY_END_MARKER = "@keyframes navigation-progress";
129
+ function wrapThemeCss(css) {
130
+ return `${THEME_START}
131
+ ${css}${THEME_END}
132
+ `;
133
+ }
134
+ function findLegacyBlockEnd(css, searchFrom) {
135
+ const keyframeIdx = css.indexOf(LEGACY_END_MARKER, searchFrom);
136
+ if (keyframeIdx === -1) {
137
+ return css.length;
138
+ }
139
+ let depth = 0;
140
+ let inBlock = false;
141
+ for (let i = keyframeIdx; i < css.length; i++) {
142
+ if (css[i] === "{") {
143
+ depth++;
144
+ inBlock = true;
145
+ } else if (css[i] === "}") {
146
+ depth--;
147
+ if (inBlock && depth === 0) {
148
+ const end = css[i + 1] === "\n" ? i + 2 : i + 1;
149
+ return end;
150
+ }
151
+ }
152
+ }
153
+ return css.length;
154
+ }
155
+ function replaceThemeInCss(existingCss, newThemeCss) {
156
+ const wrapped = wrapThemeCss(newThemeCss);
157
+ const startIdx = existingCss.indexOf(THEME_START);
158
+ const endIdx = existingCss.indexOf(THEME_END);
159
+ if (startIdx !== -1 && endIdx !== -1 && startIdx < endIdx) {
160
+ const before = existingCss.substring(0, startIdx);
161
+ const afterEnd = endIdx + THEME_END.length;
162
+ const after = existingCss[afterEnd] === "\n" ? existingCss.substring(afterEnd + 1) : existingCss.substring(afterEnd);
163
+ return { css: before + wrapped + after, replaced: true };
164
+ }
165
+ const legacyIdx = existingCss.indexOf(LEGACY_MARKER);
166
+ if (legacyIdx !== -1) {
167
+ const commentStart = existingCss.lastIndexOf("/*", legacyIdx);
168
+ if (commentStart !== -1) {
169
+ const blockEnd = findLegacyBlockEnd(existingCss, commentStart);
170
+ let cutPoint = commentStart;
171
+ if (commentStart > 0 && existingCss[commentStart - 1] === "\n") {
172
+ cutPoint = commentStart - 1;
173
+ }
174
+ const before = existingCss.substring(0, cutPoint);
175
+ const after = existingCss.substring(blockEnd);
176
+ return { css: before + "\n" + wrapped + after, replaced: true };
177
+ }
178
+ }
179
+ const separator = existingCss.endsWith("\n") ? "\n" : "\n\n";
180
+ return { css: existingCss + separator + wrapped, replaced: false };
181
+ }
182
+ function extractThemeBlock(cssContent) {
183
+ const startIdx = cssContent.indexOf(THEME_START);
184
+ const endIdx = cssContent.indexOf(THEME_END);
185
+ if (startIdx !== -1 && endIdx !== -1 && startIdx < endIdx) {
186
+ const contentStart = startIdx + THEME_START.length;
187
+ const adjustedStart = cssContent[contentStart] === "\n" ? contentStart + 1 : contentStart;
188
+ return cssContent.substring(adjustedStart, endIdx);
189
+ }
190
+ const legacyIdx = cssContent.indexOf(LEGACY_MARKER);
191
+ if (legacyIdx !== -1) {
192
+ const commentStart = cssContent.lastIndexOf("/*", legacyIdx);
193
+ if (commentStart !== -1) {
194
+ const blockEnd = findLegacyBlockEnd(cssContent, commentStart);
195
+ return cssContent.substring(commentStart, blockEnd);
196
+ }
197
+ }
198
+ return null;
199
+ }
200
+ async function syncTheme(config, themeCssMap, options = {}) {
201
+ const themeName = config.theme || "porcelain";
202
+ const themeCss = themeCssMap[themeName];
203
+ if (!themeCss) {
204
+ console.log(pc.yellow("!"), `Unknown theme "${themeName}". Skipping theme sync.`);
205
+ return false;
206
+ }
207
+ if (!config.css) {
208
+ console.log(pc.yellow("!"), "No CSS path in config. Skipping theme sync.");
209
+ console.log(" Run", pc.cyan("npx @beaket/ui init"), "to set CSS path.");
210
+ return false;
211
+ }
212
+ const cssPath = path3.join(process.cwd(), config.css);
213
+ if (!await fs3.pathExists(cssPath)) {
214
+ console.log(pc.yellow("!"), `CSS file not found: ${config.css}. Skipping theme sync.`);
215
+ return false;
216
+ }
217
+ const existingCss = await fs3.readFile(cssPath, "utf-8");
218
+ const existingTheme = extractThemeBlock(existingCss);
219
+ if (existingTheme !== null && existingTheme.trim() === themeCss.trim()) {
220
+ console.log(pc.green("\u2714"), "Theme tokens are up to date.");
221
+ return false;
222
+ }
223
+ if (existingTheme === null) {
224
+ const { css: css2 } = replaceThemeInCss(existingCss, themeCss);
225
+ await fs3.writeFile(cssPath, css2);
226
+ console.log(pc.green("\u2714"), `Added ${themeName} theme tokens to ${config.css}`);
227
+ return true;
228
+ }
229
+ if (!options.overwrite) {
230
+ const { confirm } = await prompts2({
231
+ type: "confirm",
232
+ name: "confirm",
233
+ message: `Theme tokens in ${config.css} are outdated. Update to latest ${themeName}?`,
234
+ initial: true
235
+ });
236
+ if (!confirm) {
237
+ console.log(pc.yellow("\u2139"), "Skipped theme update.");
238
+ return false;
239
+ }
240
+ }
241
+ const { css } = replaceThemeInCss(existingCss, themeCss);
242
+ await fs3.writeFile(cssPath, css);
243
+ console.log(pc.green("\u2714"), `Updated ${themeName} theme tokens in ${config.css}`);
244
+ return true;
245
+ }
246
+
247
+ // ../../src/themes/eucalyptus.css
248
+ var eucalyptus_default = "/*\n * Beaket UI Design System - Eucalyptus Theme\n * Enterprise titanium. Navy ink, blue-gray surfaces, structured shadows.\n * Cold precision for office and productivity applications.\n */\n\n@theme {\n /* Neutral palette */\n --color-graphite: #0a1025;\n --color-ink: #162036;\n --color-branch: #1c2a42;\n --color-iron: #243250;\n --color-slate: #2f3f58;\n --color-zinc: #384d68;\n --color-steel: #3d5170;\n --color-muted: #5a6d88;\n --color-aluminum: #8295ae;\n --color-chrome: #c0cddb;\n --color-silver: #cdd8e4;\n --color-platinum: #dce3ed;\n --color-frost: #eff2f8;\n --color-paper: #f8f9fc;\n --color-inverse: var(--color-paper);\n\n /* Signal colors \u2014 deep cobalt, formal crimson, jade, brass */\n --color-signal-blue: #1535c0;\n --color-signal-red: #b81c38;\n --color-signal-red-hover: #9e1830;\n --color-signal-red-active: #841428;\n --color-signal-red-text: #9e1830;\n --color-signal-green: #0a7860;\n --color-signal-green-hover: #086450;\n --color-signal-green-active: #065040;\n --color-signal-amber: #b87500;\n --color-signal-amber-hover: #9c6300;\n --color-signal-amber-active: #805200;\n --color-signal-amber-text: #9c6300;\n --color-signal-purple: #5b22c8;\n --color-signal-cyan: #0078a8;\n\n /* Surface layers (visual depth) */\n --color-surface-0: #e6ebf2;\n --color-surface-1: #f8f9fc;\n --color-surface-2: #ffffff;\n\n /* Shadows \u2014 blue-gray, structured */\n --shadow-offset: 2px 2px 0px 0px var(--color-chrome);\n --shadow-offset-dark: 2px 2px 0px 0px var(--color-aluminum);\n --shadow-offset-hover: 3px 3px 0px 0px var(--color-chrome);\n --shadow-offset-active: 1px 1px 0px 0px var(--color-chrome);\n\n /* Animations */\n --animate-navigation-progress: navigation-progress 1s ease-in-out infinite;\n}\n\n/*\n * Dark mode \u2014 mission control.\n * Command center at night. Navy-blue darkness with structured panels.\n * Navy-tinted neutrals. Signals glow with focused intensity.\n */\n@media (prefers-color-scheme: dark) {\n :root {\n /* Neutral palette \u2014 navy-tinted for professional dark authority */\n --color-graphite: #e8ecf4;\n --color-ink: #dce2ec;\n --color-branch: #c4cede;\n --color-iron: #b0bace;\n --color-slate: #98a2b8;\n --color-zinc: #808aa2;\n --color-steel: #687288;\n --color-muted: #546070;\n --color-aluminum: #3c4858;\n --color-chrome: #283444;\n --color-silver: #1e2836;\n --color-platinum: #141a2a;\n --color-frost: #0c1020;\n --color-paper: #060a14;\n\n /* Signal colors \u2014 professional, brightened for dark canvas */\n --color-signal-blue: #3060e0;\n --color-signal-red: #d43450;\n --color-signal-red-hover: #bc2e46;\n --color-signal-red-active: #a4283c;\n --color-signal-red-text: #e84c64;\n --color-signal-green: #14a878;\n --color-signal-green-hover: #108e64;\n --color-signal-green-active: #0c7450;\n --color-signal-amber: #d89420;\n --color-signal-amber-hover: #bc801c;\n --color-signal-amber-active: #a06c18;\n --color-signal-amber-text: #e8a830;\n --color-signal-purple: #9454e8;\n --color-signal-cyan: #2094c8;\n\n /* Surface layers \u2014 elevation = lighter */\n --color-surface-0: #0a0e1a;\n --color-surface-1: #101422;\n --color-surface-2: #18202e;\n }\n}\n\n@keyframes navigation-progress {\n 0% {\n transform: translateX(-100%);\n }\n 100% {\n transform: translateX(400%);\n }\n}\n";
249
+
250
+ // ../../src/themes/marigold.css
251
+ var marigold_default = "/*\n * Beaket UI Design System - Marigold Theme\n * A neon sign snapping on. Screen-printed posters in a design studio.\n * Light: pure white canvas, ink-black heavy shadows, maximum saturation.\n * Dark: neon signs at night \u2014 vivid electric colors on near-black canvas.\n */\n\n@theme {\n /* Neutral palette */\n --color-graphite: #0a0a0a;\n --color-ink: #121212;\n --color-branch: #1a1a1a;\n --color-iron: #262626;\n --color-slate: #3a3a3a;\n --color-zinc: #4e4e4e;\n --color-steel: #5a5a5a;\n --color-muted: #6e6e6e;\n --color-aluminum: #949494;\n --color-chrome: #c0c0c0;\n --color-silver: #cfcfcf;\n --color-platinum: #e0e0e0;\n --color-frost: #f0f0f0;\n --color-paper: #ffffff;\n --color-inverse: var(--color-paper);\n\n /* Signal colors \u2014 LOUD, max saturation, screen-print ink */\n --color-signal-blue: #0044ee;\n --color-signal-red: #e82010;\n --color-signal-red-hover: #cc1c0e;\n --color-signal-red-active: #b0180c;\n --color-signal-red-text: #cc1c0e;\n --color-signal-green: #008050;\n --color-signal-green-hover: #006a42;\n --color-signal-green-active: #005535;\n --color-signal-amber: #f07800;\n --color-signal-amber-hover: #d06800;\n --color-signal-amber-active: #b05800;\n --color-signal-amber-text: #cc6600;\n --color-signal-purple: #8b22ff;\n --color-signal-cyan: #00b4cc;\n\n /* Surface layers (visual depth) */\n --color-surface-0: #ebebeb;\n --color-surface-1: #f8f8f8;\n --color-surface-2: #ffffff;\n\n /* Shadows \u2014 ink-black, heavy, graphic */\n --shadow-offset: 3px 3px 0px 0px var(--color-ink);\n --shadow-offset-dark: 3px 3px 0px 0px var(--color-graphite);\n --shadow-offset-hover: 4px 4px 0px 0px var(--color-ink);\n --shadow-offset-active: 1px 1px 0px 0px var(--color-ink);\n\n /* Animations */\n --animate-navigation-progress: navigation-progress 1s ease-in-out infinite;\n}\n\n/*\n * Dark mode \u2014 neon signs at night.\n * Neutrals flip (paper\u2192black, ink\u2192white). Shadows auto-flip via token\n * references (white edge on black = neon glow effect). Signal colors\n * brightened to neon-max for dark canvas contrast.\n */\n@media (prefers-color-scheme: dark) {\n :root {\n /* Neutral palette \u2014 pure achromatic for maximum neon contrast */\n --color-graphite: #f5f5f5;\n --color-ink: #ececec;\n --color-branch: #e0e0e0;\n --color-iron: #d0d0d0;\n --color-slate: #bfbfbf;\n --color-zinc: #acacac;\n --color-steel: #999999;\n --color-muted: #858585;\n --color-aluminum: #666666;\n --color-chrome: #404040;\n --color-silver: #303030;\n --color-platinum: #222222;\n --color-frost: #161616;\n --color-paper: #0a0a0a;\n\n /* Signal colors \u2014 neon-bright for dark canvas */\n --color-signal-blue: #4488ff;\n --color-signal-red: #ff2a1a;\n --color-signal-red-hover: #e52518;\n --color-signal-red-active: #cc2012;\n --color-signal-red-text: #ff4838;\n --color-signal-green: #00cc66;\n --color-signal-green-hover: #00b058;\n --color-signal-green-active: #009448;\n --color-signal-amber: #ff8800;\n --color-signal-amber-hover: #e07800;\n --color-signal-amber-active: #c06800;\n --color-signal-amber-text: #ffa030;\n --color-signal-purple: #bb55ff;\n --color-signal-cyan: #00ccdd;\n\n /* Surface layers \u2014 elevation = lighter */\n --color-surface-0: #141414;\n --color-surface-1: #1a1a1a;\n --color-surface-2: #222222;\n }\n}\n\n@keyframes navigation-progress {\n 0% {\n transform: translateX(-100%);\n }\n 100% {\n transform: translateX(400%);\n }\n}\n";
252
+
253
+ // ../../src/themes/porcelain.css
254
+ var porcelain_default = "/*\n * Beaket UI Design System - Porcelain Theme\n * Industrial precision. Etched on control panels in black synthetic lacquer.\n * Pure white, cold blue-black ink, ghostly shadows, teal accent.\n */\n\n@theme {\n /* Neutral palette */\n --color-graphite: #030508;\n --color-ink: #080b10;\n --color-branch: #05070c;\n --color-iron: #282b2f;\n --color-slate: #3e4145;\n --color-zinc: #53565b;\n --color-steel: #686b6f;\n --color-muted: #7a7d81;\n --color-aluminum: #a0a3a7;\n --color-chrome: #c0c4ca;\n --color-silver: #d5d8dc;\n --color-platinum: #e8eaec;\n --color-frost: #f3f4f6;\n --color-paper: #ffffff;\n --color-inverse: var(--color-paper);\n\n /* Signal colors \u2014 cold, precise, teal accent */\n --color-signal-blue: #0c6bae;\n --color-signal-red: #c41028;\n --color-signal-red-hover: #a80d22;\n --color-signal-red-active: #8c0b1c;\n --color-signal-red-text: #a80d22;\n --color-signal-green: #0a7653;\n --color-signal-green-hover: #086248;\n --color-signal-green-active: #064e3a;\n --color-signal-amber: #e09800;\n --color-signal-amber-hover: #c48400;\n --color-signal-amber-active: #a87000;\n --color-signal-amber-text: #b87c00;\n --color-signal-purple: #6122aa;\n --color-signal-cyan: #007e8c;\n\n /* Surface layers (visual depth) */\n --color-surface-0: #eff0f1;\n --color-surface-1: #f8f8f9;\n --color-surface-2: #ffffff;\n\n /* Shadows \u2014 ghostly, precise */\n --shadow-offset: 1px 1px 0px 0px var(--color-chrome);\n --shadow-offset-dark: 1px 1px 0px 0px var(--color-aluminum);\n --shadow-offset-hover: 2px 2px 0px 0px var(--color-chrome);\n --shadow-offset-active: 0px 0px 0px 0px var(--color-chrome);\n\n /* Animations */\n --animate-navigation-progress: navigation-progress 1s ease-in-out infinite;\n}\n\n/*\n * Dark mode \u2014 X-ray lightbox.\n * Cold blue-steel instruments in a dark examination room.\n * Neutrals tinted cold blue-black. Ghostly shadows become spectral.\n */\n@media (prefers-color-scheme: dark) {\n :root {\n /* Neutral palette \u2014 cold blue-tinted for clinical dark precision */\n --color-graphite: #e6eaee;\n --color-ink: #dce0e6;\n --color-branch: #c8d0da;\n --color-iron: #b4bcc6;\n --color-slate: #9ca4b2;\n --color-zinc: #848c9e;\n --color-steel: #6c7486;\n --color-muted: #586070;\n --color-aluminum: #3e4454;\n --color-chrome: #2a303e;\n --color-silver: #1e242e;\n --color-platinum: #161a22;\n --color-frost: #0e1016;\n --color-paper: #06080c;\n\n /* Signal colors \u2014 cold, brightened for dark canvas */\n --color-signal-blue: #1a8ed8;\n --color-signal-red: #e03040;\n --color-signal-red-hover: #c82a38;\n --color-signal-red-active: #b02430;\n --color-signal-red-text: #f04852;\n --color-signal-green: #10a66e;\n --color-signal-green-hover: #0e8e5e;\n --color-signal-green-active: #0c764e;\n --color-signal-amber: #e89a28;\n --color-signal-amber-hover: #cc8620;\n --color-signal-amber-active: #b07218;\n --color-signal-amber-text: #f0a020;\n --color-signal-purple: #7e3ec4;\n --color-signal-cyan: #1aa0b8;\n\n /* Surface layers \u2014 elevation = lighter */\n --color-surface-0: #0c0e15;\n --color-surface-1: #12141d;\n --color-surface-2: #1a1c27;\n }\n}\n\n@keyframes navigation-progress {\n 0% {\n transform: translateX(-100%);\n }\n 100% {\n transform: translateX(400%);\n }\n}\n";
255
+
256
+ // ../../src/themes/tobacco.css
257
+ var tobacco_default = "/*\n * Beaket UI Design System - Tobacco Theme\n * Golden hour in a library. Pampas warmth, terracotta accent.\n * Warm gray-cream surface, brown shadows, earthy signals.\n */\n\n@theme {\n /* Neutral palette */\n --color-graphite: #111110;\n --color-ink: #1a1a18;\n --color-branch: #222120;\n --color-iron: #312f2c;\n --color-slate: #46443e;\n --color-zinc: #585650;\n --color-steel: #5e5d54;\n --color-muted: #6b6a60;\n --color-aluminum: #9c9a90;\n --color-chrome: #d0cec5;\n --color-silver: #dddbd3;\n --color-platinum: #e8e7e0;\n --color-frost: #edece6;\n --color-paper: #f4f3ee;\n --color-inverse: var(--color-paper);\n\n /* Signal colors \u2014 warm, earthy: old ink, fired brick, forest, ochre */\n --color-signal-blue: #35388a;\n --color-signal-red: #b03525;\n --color-signal-red-hover: #962d1f;\n --color-signal-red-active: #7c2519;\n --color-signal-red-text: #962d1f;\n --color-signal-green: #3a7040;\n --color-signal-green-hover: #305c35;\n --color-signal-green-active: #26482a;\n --color-signal-amber: #a57218;\n --color-signal-amber-hover: #8c6014;\n --color-signal-amber-active: #734f10;\n --color-signal-amber-text: #8c6014;\n --color-signal-purple: #7c336e;\n --color-signal-cyan: #187566;\n\n /* Surface layers (visual depth) */\n --color-surface-0: #e8e7e0;\n --color-surface-1: #f4f3ee;\n --color-surface-2: #faf9f5;\n\n /* Shadows \u2014 warm brown, grounded */\n --shadow-offset: 2px 2px 0px 0px var(--color-iron);\n --shadow-offset-dark: 2px 2px 0px 0px var(--color-slate);\n --shadow-offset-hover: 3px 3px 0px 0px var(--color-iron);\n --shadow-offset-active: 1px 1px 0px 0px var(--color-iron);\n\n /* Animations */\n --animate-navigation-progress: navigation-progress 1s ease-in-out infinite;\n}\n\n/*\n * Dark mode \u2014 whiskey bar at midnight.\n * Late-night library, amber reading lamps, worn leather.\n * Warm sepia-tinted neutrals. Signals glow like stained glass.\n */\n@media (prefers-color-scheme: dark) {\n :root {\n /* Neutral palette \u2014 warm sepia-tinted for intimate darkness */\n --color-graphite: #eceae0;\n --color-ink: #e2e0d6;\n --color-branch: #d4d0c6;\n --color-iron: #c0bcb2;\n --color-slate: #a8a49a;\n --color-zinc: #908c82;\n --color-steel: #787468;\n --color-muted: #646054;\n --color-aluminum: #4a4840;\n --color-chrome: #343230;\n --color-silver: #282624;\n --color-platinum: #1e1c1a;\n --color-frost: #141312;\n --color-paper: #0c0b0a;\n\n /* Signal colors \u2014 warm stained glass glow */\n --color-signal-blue: #5068c0;\n --color-signal-red: #d04838;\n --color-signal-red-hover: #b84030;\n --color-signal-red-active: #a03828;\n --color-signal-red-text: #e06050;\n --color-signal-green: #48a850;\n --color-signal-green-hover: #3c8e44;\n --color-signal-green-active: #307438;\n --color-signal-amber: #d09828;\n --color-signal-amber-hover: #b48420;\n --color-signal-amber-active: #987018;\n --color-signal-amber-text: #e0a838;\n --color-signal-purple: #a05088;\n --color-signal-cyan: #28a890;\n\n /* Surface layers \u2014 elevation = lighter */\n --color-surface-0: #121110;\n --color-surface-1: #1a1918;\n --color-surface-2: #222120;\n }\n}\n\n@keyframes navigation-progress {\n 0% {\n transform: translateX(-100%);\n }\n 100% {\n transform: translateX(400%);\n }\n}\n";
258
+
259
+ // src/utils/themes.ts
260
+ var THEME_CSS = {
261
+ porcelain: porcelain_default,
262
+ tobacco: tobacco_default,
263
+ marigold: marigold_default,
264
+ eucalyptus: eucalyptus_default
265
+ };
266
+ var VALID_THEMES = Object.keys(THEME_CSS);
267
+
120
268
  // src/commands/add.ts
121
269
  async function add(componentNames, options) {
122
270
  console.log();
123
271
  const config = await getConfig();
124
272
  if (!config) {
125
- console.log(pc.red("Error:"), "beaket.ui.json not found.");
126
- console.log("Run", pc.cyan("npx @beaket/ui init"), "first.");
273
+ console.log(pc2.red("Error:"), "beaket.ui.json not found.");
274
+ console.log("Run", pc2.cyan("npx @beaket/ui init"), "first.");
127
275
  process.exit(1);
128
276
  }
129
277
  const registry = await fetchRegistry();
130
- console.log(pc.green("\u2714"), "Checking registry.");
278
+ console.log(pc2.green("\u2714"), "Checking registry.");
131
279
  const notFound = [];
132
280
  const componentDefs = componentNames.map((name) => {
133
281
  const def = registry.components.find((c) => c.name === name);
@@ -135,7 +283,7 @@ async function add(componentNames, options) {
135
283
  return def;
136
284
  });
137
285
  if (notFound.length > 0) {
138
- console.log(pc.red("Error:"), `Component(s) not found: ${notFound.join(", ")}`);
286
+ console.log(pc2.red("Error:"), `Component(s) not found: ${notFound.join(", ")}`);
139
287
  console.log();
140
288
  console.log("Available components:");
141
289
  registry.components.forEach((c) => {
@@ -153,9 +301,9 @@ async function add(componentNames, options) {
153
301
  }
154
302
  if (allDependencies.size > 0) {
155
303
  await installDependencies([...allDependencies]);
156
- console.log(pc.green("\u2714"), "Installing dependencies.");
304
+ console.log(pc2.green("\u2714"), "Installing dependencies.");
157
305
  }
158
- const componentsDir = path3.join(process.cwd(), config.components);
306
+ const componentsDir = path4.join(process.cwd(), config.components);
159
307
  const allWritten = [];
160
308
  const allSkipped = [];
161
309
  for (const def of componentDefs) {
@@ -167,7 +315,7 @@ async function add(componentNames, options) {
167
315
  }
168
316
  if (allSkipped.length > 0) {
169
317
  console.log(
170
- pc.yellow("\u2139"),
318
+ pc2.yellow("\u2139"),
171
319
  `Skipped ${allSkipped.length} file(s): (use --overwrite to overwrite)`
172
320
  );
173
321
  allSkipped.forEach((f) => console.log(` - ${f}`));
@@ -178,47 +326,26 @@ async function add(componentNames, options) {
178
326
  }
179
327
  console.log();
180
328
  console.log("Added:");
181
- allWritten.forEach((f) => console.log(pc.cyan(` ${f}`)));
329
+ allWritten.forEach((f) => console.log(pc2.cyan(` ${f}`)));
330
+ if (config.css) {
331
+ console.log();
332
+ await syncTheme(config, THEME_CSS, { overwrite: options.overwrite });
333
+ }
182
334
  console.log();
183
335
  }
184
336
 
185
337
  // src/commands/init.ts
186
- import fs3 from "fs-extra";
187
- import path4 from "path";
188
- import pc2 from "picocolors";
189
- import prompts2 from "prompts";
190
-
191
- // ../../src/css-variables.css
192
- var css_variables_default = "/*\n * Beaket UI Design System - Core Variables (Porcelain)\n * This file is the single source of truth for CSS variables.\n * Used by: styles.css (via @import) and CLI init command (via build script)\n *\n * BREAKING CHANGE: Colors now use --color-* prefix (Tailwind v4 convention).\n * Components use clean utilities: bg-paper, text-ink, border-chrome\n */\n\n@theme {\n /* Neutral palette */\n --color-graphite: #030509;\n --color-ink: #080b12;\n --color-branch: #05070d;\n --color-iron: #282b30;\n --color-slate: #3e4146;\n --color-zinc: #53565c;\n --color-steel: #686b70;\n --color-muted: #7a7d82;\n --color-aluminum: #a0a3a8;\n --color-chrome: #c0c5cc;\n --color-silver: #d5d8dd;\n --color-platinum: #e8eaed;\n --color-frost: #f3f4f6;\n --color-paper: #ffffff;\n --color-inverse: var(--color-paper);\n\n /* Signal colors \u2014 cold, precise, teal accent */\n --color-signal-blue: #0c6daa;\n --color-signal-red: #c41028;\n --color-signal-red-hover: #a80d22;\n --color-signal-red-active: #8c0b1c;\n --color-signal-red-text: #a80d22;\n --color-signal-green: #0a7855;\n --color-signal-green-hover: #08644a;\n --color-signal-green-active: #06503c;\n --color-signal-amber: #b58a00;\n --color-signal-amber-hover: #9a7500;\n --color-signal-amber-active: #7f6000;\n --color-signal-purple: #6122aa;\n --color-signal-cyan: #007e8c;\n\n /* Surface layers (visual depth) */\n --color-surface-0: #eff0f2;\n --color-surface-1: #f8f8fa;\n --color-surface-2: #ffffff;\n\n /* Shadows \u2014 ghostly, precise */\n --shadow-offset: 1px 1px 0px 0px var(--color-chrome);\n --shadow-offset-dark: 1px 1px 0px 0px var(--color-aluminum);\n --shadow-offset-hover: 2px 2px 0px 0px var(--color-chrome);\n --shadow-offset-active: 0px 0px 0px 0px var(--color-chrome);\n\n /* Animations */\n --animate-navigation-progress: navigation-progress 1s ease-in-out infinite;\n}\n\n@keyframes navigation-progress {\n 0% {\n transform: translateX(-100%);\n }\n 100% {\n transform: translateX(400%);\n }\n}\n";
193
-
194
- // ../../src/themes/eucalyptus.css
195
- var eucalyptus_default = "/*\n * Beaket UI Design System - Eucalyptus Theme\n * Enterprise titanium. Navy ink, blue-gray surfaces, structured shadows.\n * Cold precision for office and productivity applications.\n */\n\n@theme {\n /* Neutral palette */\n --color-graphite: #0a1025;\n --color-ink: #162036;\n --color-branch: #1c2a42;\n --color-iron: #243250;\n --color-slate: #2f3f58;\n --color-zinc: #384d68;\n --color-steel: #3d5170;\n --color-muted: #5a6d88;\n --color-aluminum: #8295ae;\n --color-chrome: #c0cddb;\n --color-silver: #cdd8e4;\n --color-platinum: #dce3ed;\n --color-frost: #eff2f8;\n --color-paper: #f8f9fc;\n --color-inverse: var(--color-paper);\n\n /* Signal colors \u2014 deep cobalt, formal crimson, jade, brass */\n --color-signal-blue: #1535c0;\n --color-signal-red: #b81c38;\n --color-signal-red-hover: #9e1830;\n --color-signal-red-active: #841428;\n --color-signal-red-text: #9e1830;\n --color-signal-green: #0a7860;\n --color-signal-green-hover: #086450;\n --color-signal-green-active: #065040;\n --color-signal-amber: #b87500;\n --color-signal-amber-hover: #9c6300;\n --color-signal-amber-active: #805200;\n --color-signal-purple: #5b22c8;\n --color-signal-cyan: #0078a8;\n\n /* Surface layers (visual depth) */\n --color-surface-0: #e6ebf2;\n --color-surface-1: #f8f9fc;\n --color-surface-2: #ffffff;\n\n /* Shadows \u2014 blue-gray, structured */\n --shadow-offset: 2px 2px 0px 0px var(--color-chrome);\n --shadow-offset-dark: 2px 2px 0px 0px var(--color-aluminum);\n --shadow-offset-hover: 3px 3px 0px 0px var(--color-chrome);\n --shadow-offset-active: 1px 1px 0px 0px var(--color-chrome);\n\n /* Animations */\n --animate-navigation-progress: navigation-progress 1s ease-in-out infinite;\n}\n\n/*\n * Dark mode \u2014 mission control.\n * Command center at night. Navy-blue darkness with structured panels.\n * Navy-tinted neutrals. Signals glow with focused intensity.\n */\n@media (prefers-color-scheme: dark) {\n :root {\n /* Neutral palette \u2014 navy-tinted for professional dark authority */\n --color-graphite: #e8ecf4;\n --color-ink: #dce2ec;\n --color-branch: #c4cede;\n --color-iron: #b0bace;\n --color-slate: #98a2b8;\n --color-zinc: #808aa2;\n --color-steel: #687288;\n --color-muted: #546070;\n --color-aluminum: #3c4858;\n --color-chrome: #283444;\n --color-silver: #1e2836;\n --color-platinum: #141a2a;\n --color-frost: #0c1020;\n --color-paper: #060a14;\n\n /* Signal colors \u2014 professional, brightened for dark canvas */\n --color-signal-blue: #3060e0;\n --color-signal-red: #d43450;\n --color-signal-red-hover: #bc2e46;\n --color-signal-red-active: #a4283c;\n --color-signal-red-text: #e84c64;\n --color-signal-green: #14a878;\n --color-signal-green-hover: #108e64;\n --color-signal-green-active: #0c7450;\n --color-signal-amber: #d89420;\n --color-signal-amber-hover: #bc801c;\n --color-signal-amber-active: #a06c18;\n --color-signal-purple: #9454e8;\n --color-signal-cyan: #2094c8;\n\n /* Surface layers \u2014 elevation = lighter */\n --color-surface-0: #0a0e1a;\n --color-surface-1: #101422;\n --color-surface-2: #18202e;\n }\n}\n\n@keyframes navigation-progress {\n 0% {\n transform: translateX(-100%);\n }\n 100% {\n transform: translateX(400%);\n }\n}\n";
196
-
197
- // ../../src/themes/marigold.css
198
- var marigold_default = "/*\n * Beaket UI Design System - Marigold Theme\n * A neon sign snapping on. Screen-printed posters in a design studio.\n * Light: pure white canvas, ink-black heavy shadows, maximum saturation.\n * Dark: neon signs at night \u2014 vivid electric colors on near-black canvas.\n */\n\n@theme {\n /* Neutral palette */\n --color-graphite: #0a0a0a;\n --color-ink: #121212;\n --color-branch: #1a1a1a;\n --color-iron: #262626;\n --color-slate: #3a3a3a;\n --color-zinc: #4e4e4e;\n --color-steel: #5a5a5a;\n --color-muted: #6e6e6e;\n --color-aluminum: #949494;\n --color-chrome: #c0c0c0;\n --color-silver: #cfcfcf;\n --color-platinum: #e0e0e0;\n --color-frost: #f0f0f0;\n --color-paper: #ffffff;\n --color-inverse: var(--color-paper);\n\n /* Signal colors \u2014 LOUD, max saturation, screen-print ink */\n --color-signal-blue: #0044ee;\n --color-signal-red: #e82010;\n --color-signal-red-hover: #cc1c0e;\n --color-signal-red-active: #b0180c;\n --color-signal-red-text: #cc1c0e;\n --color-signal-green: #008050;\n --color-signal-green-hover: #006a42;\n --color-signal-green-active: #005535;\n --color-signal-amber: #f07800;\n --color-signal-amber-hover: #d06800;\n --color-signal-amber-active: #b05800;\n --color-signal-purple: #8b22ff;\n --color-signal-cyan: #00b4cc;\n\n /* Surface layers (visual depth) */\n --color-surface-0: #ebebeb;\n --color-surface-1: #f8f8f8;\n --color-surface-2: #ffffff;\n\n /* Shadows \u2014 ink-black, heavy, graphic */\n --shadow-offset: 3px 3px 0px 0px var(--color-ink);\n --shadow-offset-dark: 3px 3px 0px 0px var(--color-graphite);\n --shadow-offset-hover: 4px 4px 0px 0px var(--color-ink);\n --shadow-offset-active: 1px 1px 0px 0px var(--color-ink);\n\n /* Animations */\n --animate-navigation-progress: navigation-progress 1s ease-in-out infinite;\n}\n\n/*\n * Dark mode \u2014 neon signs at night.\n * Neutrals flip (paper\u2192black, ink\u2192white). Shadows auto-flip via token\n * references (white edge on black = neon glow effect). Signal colors\n * brightened to neon-max for dark canvas contrast.\n */\n@media (prefers-color-scheme: dark) {\n :root {\n /* Neutral palette \u2014 pure achromatic for maximum neon contrast */\n --color-graphite: #f5f5f5;\n --color-ink: #ececec;\n --color-branch: #e0e0e0;\n --color-iron: #d0d0d0;\n --color-slate: #bfbfbf;\n --color-zinc: #acacac;\n --color-steel: #999999;\n --color-muted: #858585;\n --color-aluminum: #666666;\n --color-chrome: #404040;\n --color-silver: #303030;\n --color-platinum: #222222;\n --color-frost: #161616;\n --color-paper: #0a0a0a;\n\n /* Signal colors \u2014 neon-bright for dark canvas */\n --color-signal-blue: #4488ff;\n --color-signal-red: #ff2a1a;\n --color-signal-red-hover: #e52518;\n --color-signal-red-active: #cc2012;\n --color-signal-red-text: #ff4838;\n --color-signal-green: #00cc66;\n --color-signal-green-hover: #00b058;\n --color-signal-green-active: #009448;\n --color-signal-amber: #ff8800;\n --color-signal-amber-hover: #e07800;\n --color-signal-amber-active: #c06800;\n --color-signal-purple: #bb55ff;\n --color-signal-cyan: #00ccdd;\n\n /* Surface layers \u2014 elevation = lighter */\n --color-surface-0: #141414;\n --color-surface-1: #1a1a1a;\n --color-surface-2: #222222;\n }\n}\n\n@keyframes navigation-progress {\n 0% {\n transform: translateX(-100%);\n }\n 100% {\n transform: translateX(400%);\n }\n}\n";
199
-
200
- // ../../src/themes/porcelain.css
201
- var porcelain_default = "/*\n * Beaket UI Design System - Porcelain Theme\n * Industrial precision. Etched on control panels in black synthetic lacquer.\n * Pure white, cold blue-black ink, ghostly shadows, teal accent.\n */\n\n@theme {\n /* Neutral palette */\n --color-graphite: #030508;\n --color-ink: #080b10;\n --color-branch: #05070c;\n --color-iron: #282b2f;\n --color-slate: #3e4145;\n --color-zinc: #53565b;\n --color-steel: #686b6f;\n --color-muted: #7a7d81;\n --color-aluminum: #a0a3a7;\n --color-chrome: #c0c4ca;\n --color-silver: #d5d8dc;\n --color-platinum: #e8eaec;\n --color-frost: #f3f4f6;\n --color-paper: #ffffff;\n --color-inverse: var(--color-paper);\n\n /* Signal colors \u2014 cold, precise, teal accent */\n --color-signal-blue: #0c6bae;\n --color-signal-red: #c41028;\n --color-signal-red-hover: #a80d22;\n --color-signal-red-active: #8c0b1c;\n --color-signal-red-text: #a80d22;\n --color-signal-green: #0a7653;\n --color-signal-green-hover: #086248;\n --color-signal-green-active: #064e3a;\n --color-signal-amber: #b58a00;\n --color-signal-amber-hover: #9a7500;\n --color-signal-amber-active: #7f6000;\n --color-signal-purple: #6122aa;\n --color-signal-cyan: #007e8c;\n\n /* Surface layers (visual depth) */\n --color-surface-0: #eff0f1;\n --color-surface-1: #f8f8f9;\n --color-surface-2: #ffffff;\n\n /* Shadows \u2014 ghostly, precise */\n --shadow-offset: 1px 1px 0px 0px var(--color-chrome);\n --shadow-offset-dark: 1px 1px 0px 0px var(--color-aluminum);\n --shadow-offset-hover: 2px 2px 0px 0px var(--color-chrome);\n --shadow-offset-active: 0px 0px 0px 0px var(--color-chrome);\n\n /* Animations */\n --animate-navigation-progress: navigation-progress 1s ease-in-out infinite;\n}\n\n/*\n * Dark mode \u2014 X-ray lightbox.\n * Cold blue-steel instruments in a dark examination room.\n * Neutrals tinted cold blue-black. Ghostly shadows become spectral.\n */\n@media (prefers-color-scheme: dark) {\n :root {\n /* Neutral palette \u2014 cold blue-tinted for clinical dark precision */\n --color-graphite: #e6eaee;\n --color-ink: #dce0e6;\n --color-branch: #c8d0da;\n --color-iron: #b4bcc6;\n --color-slate: #9ca4b2;\n --color-zinc: #848c9e;\n --color-steel: #6c7486;\n --color-muted: #586070;\n --color-aluminum: #3e4454;\n --color-chrome: #2a303e;\n --color-silver: #1e242e;\n --color-platinum: #161a22;\n --color-frost: #0e1016;\n --color-paper: #06080c;\n\n /* Signal colors \u2014 cold, brightened for dark canvas */\n --color-signal-blue: #1a8ed8;\n --color-signal-red: #e03040;\n --color-signal-red-hover: #c82a38;\n --color-signal-red-active: #b02430;\n --color-signal-red-text: #f04852;\n --color-signal-green: #10a66e;\n --color-signal-green-hover: #0e8e5e;\n --color-signal-green-active: #0c764e;\n --color-signal-amber: #d4a010;\n --color-signal-amber-hover: #b88c0e;\n --color-signal-amber-active: #9c780c;\n --color-signal-purple: #7e3ec4;\n --color-signal-cyan: #1aa0b8;\n\n /* Surface layers \u2014 elevation = lighter */\n --color-surface-0: #0c0e15;\n --color-surface-1: #12141d;\n --color-surface-2: #1a1c27;\n }\n}\n\n@keyframes navigation-progress {\n 0% {\n transform: translateX(-100%);\n }\n 100% {\n transform: translateX(400%);\n }\n}\n";
202
-
203
- // ../../src/themes/tobacco.css
204
- var tobacco_default = "/*\n * Beaket UI Design System - Tobacco Theme\n * Golden hour in a library. Pampas warmth, terracotta accent.\n * Warm gray-cream surface, brown shadows, earthy signals.\n */\n\n@theme {\n /* Neutral palette */\n --color-graphite: #111110;\n --color-ink: #1a1a18;\n --color-branch: #222120;\n --color-iron: #312f2c;\n --color-slate: #46443e;\n --color-zinc: #585650;\n --color-steel: #5e5d54;\n --color-muted: #6b6a60;\n --color-aluminum: #9c9a90;\n --color-chrome: #d0cec5;\n --color-silver: #dddbd3;\n --color-platinum: #e8e7e0;\n --color-frost: #edece6;\n --color-paper: #f4f3ee;\n --color-inverse: var(--color-paper);\n\n /* Signal colors \u2014 warm, earthy: old ink, fired brick, forest, ochre */\n --color-signal-blue: #35388a;\n --color-signal-red: #b03525;\n --color-signal-red-hover: #962d1f;\n --color-signal-red-active: #7c2519;\n --color-signal-red-text: #962d1f;\n --color-signal-green: #3a7040;\n --color-signal-green-hover: #305c35;\n --color-signal-green-active: #26482a;\n --color-signal-amber: #a57218;\n --color-signal-amber-hover: #8c6014;\n --color-signal-amber-active: #734f10;\n --color-signal-purple: #7c336e;\n --color-signal-cyan: #187566;\n\n /* Surface layers (visual depth) */\n --color-surface-0: #e8e7e0;\n --color-surface-1: #f4f3ee;\n --color-surface-2: #faf9f5;\n\n /* Shadows \u2014 warm brown, grounded */\n --shadow-offset: 2px 2px 0px 0px var(--color-iron);\n --shadow-offset-dark: 2px 2px 0px 0px var(--color-slate);\n --shadow-offset-hover: 3px 3px 0px 0px var(--color-iron);\n --shadow-offset-active: 1px 1px 0px 0px var(--color-iron);\n\n /* Animations */\n --animate-navigation-progress: navigation-progress 1s ease-in-out infinite;\n}\n\n/*\n * Dark mode \u2014 whiskey bar at midnight.\n * Late-night library, amber reading lamps, worn leather.\n * Warm sepia-tinted neutrals. Signals glow like stained glass.\n */\n@media (prefers-color-scheme: dark) {\n :root {\n /* Neutral palette \u2014 warm sepia-tinted for intimate darkness */\n --color-graphite: #eceae0;\n --color-ink: #e2e0d6;\n --color-branch: #d4d0c6;\n --color-iron: #c0bcb2;\n --color-slate: #a8a49a;\n --color-zinc: #908c82;\n --color-steel: #787468;\n --color-muted: #646054;\n --color-aluminum: #4a4840;\n --color-chrome: #343230;\n --color-silver: #282624;\n --color-platinum: #1e1c1a;\n --color-frost: #141312;\n --color-paper: #0c0b0a;\n\n /* Signal colors \u2014 warm stained glass glow */\n --color-signal-blue: #5068c0;\n --color-signal-red: #d04838;\n --color-signal-red-hover: #b84030;\n --color-signal-red-active: #a03828;\n --color-signal-red-text: #e06050;\n --color-signal-green: #48a850;\n --color-signal-green-hover: #3c8e44;\n --color-signal-green-active: #307438;\n --color-signal-amber: #d09828;\n --color-signal-amber-hover: #b48420;\n --color-signal-amber-active: #987018;\n --color-signal-purple: #a05088;\n --color-signal-cyan: #28a890;\n\n /* Surface layers \u2014 elevation = lighter */\n --color-surface-0: #121110;\n --color-surface-1: #1a1918;\n --color-surface-2: #222120;\n }\n}\n\n@keyframes navigation-progress {\n 0% {\n transform: translateX(-100%);\n }\n 100% {\n transform: translateX(400%);\n }\n}\n";
205
-
206
- // src/commands/init.ts
207
- var THEME_CSS = {
208
- porcelain: porcelain_default,
209
- tobacco: tobacco_default,
210
- marigold: marigold_default,
211
- eucalyptus: eucalyptus_default,
212
- default: css_variables_default
213
- };
214
- var VALID_THEMES = Object.keys(THEME_CSS);
338
+ import fs4 from "fs-extra";
339
+ import path5 from "path";
340
+ import pc3 from "picocolors";
341
+ import prompts3 from "prompts";
215
342
  async function detectAliasPath() {
216
343
  const cwd = process.cwd();
217
344
  for (const configFile of ["tsconfig.json", "tsconfig.app.json"]) {
218
- const configPath = path4.join(cwd, configFile);
219
- if (await fs3.pathExists(configPath)) {
345
+ const configPath = path5.join(cwd, configFile);
346
+ if (await fs4.pathExists(configPath)) {
220
347
  try {
221
- const content = await fs3.readFile(configPath, "utf-8");
348
+ const content = await fs4.readFile(configPath, "utf-8");
222
349
  const tsconfig = JSON.parse(content);
223
350
  const paths = tsconfig.compilerOptions?.paths;
224
351
  if (paths?.["@/*"]) {
@@ -230,10 +357,10 @@ async function detectAliasPath() {
230
357
  }
231
358
  }
232
359
  }
233
- const pkgPath = path4.join(cwd, "package.json");
234
- if (await fs3.pathExists(pkgPath)) {
360
+ const pkgPath = path5.join(cwd, "package.json");
361
+ if (await fs4.pathExists(pkgPath)) {
235
362
  try {
236
- const content = await fs3.readFile(pkgPath, "utf-8");
363
+ const content = await fs4.readFile(pkgPath, "utf-8");
237
364
  const pkg = JSON.parse(content);
238
365
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
239
366
  if (deps.next) {
@@ -246,10 +373,10 @@ async function detectAliasPath() {
246
373
  }
247
374
  async function detectCssPath() {
248
375
  const cwd = process.cwd();
249
- const pkgPath = path4.join(cwd, "package.json");
250
- if (await fs3.pathExists(pkgPath)) {
376
+ const pkgPath = path5.join(cwd, "package.json");
377
+ if (await fs4.pathExists(pkgPath)) {
251
378
  try {
252
- const content = await fs3.readFile(pkgPath, "utf-8");
379
+ const content = await fs4.readFile(pkgPath, "utf-8");
253
380
  const pkg = JSON.parse(content);
254
381
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
255
382
  if (deps.next) {
@@ -262,11 +389,11 @@ async function detectCssPath() {
262
389
  }
263
390
  async function init(options) {
264
391
  console.log();
265
- console.log(pc2.bold("Initializing Beaket UI..."));
392
+ console.log(pc3.bold("Initializing Beaket UI..."));
266
393
  console.log();
267
394
  if (options.theme && !VALID_THEMES.includes(options.theme)) {
268
395
  console.log(
269
- pc2.red("Error:"),
396
+ pc3.red("Error:"),
270
397
  `Invalid theme "${options.theme}". Choose from: ${VALID_THEMES.join(", ")}`
271
398
  );
272
399
  process.exit(1);
@@ -281,7 +408,7 @@ async function init(options) {
281
408
  theme: options.theme || "porcelain"
282
409
  };
283
410
  } else {
284
- const answers = await prompts2([
411
+ const answers = await prompts3([
285
412
  {
286
413
  type: "text",
287
414
  name: "components",
@@ -308,7 +435,7 @@ async function init(options) {
308
435
  }
309
436
  ]);
310
437
  if (!answers.components) {
311
- console.log(pc2.red("Cancelled."));
438
+ console.log(pc3.red("Cancelled."));
312
439
  process.exit(1);
313
440
  }
314
441
  response = {
@@ -320,40 +447,75 @@ async function init(options) {
320
447
  const config = {
321
448
  $schema: "https://beaket.dev/schema.json",
322
449
  components: response.components,
450
+ css: response.css || void 0,
323
451
  theme: response.theme
324
452
  };
325
453
  await writeConfig(config);
326
- console.log(pc2.green("\u2714"), "Created beaket.ui.json");
454
+ console.log(pc3.green("\u2714"), "Created beaket.ui.json");
327
455
  const selectedCss = THEME_CSS[response.theme];
456
+ if (!selectedCss) {
457
+ console.log(pc3.red("Error:"), `Unknown theme "${response.theme}".`);
458
+ process.exit(1);
459
+ }
328
460
  if (response.css) {
329
- const cssPath = path4.join(process.cwd(), response.css);
330
- if (await fs3.pathExists(cssPath)) {
331
- const cssContent = await fs3.readFile(cssPath, "utf-8");
332
- if (!cssContent.includes("Beaket UI Design System")) {
333
- await fs3.writeFile(cssPath, cssContent + selectedCss);
334
- console.log(pc2.green("\u2714"), `Added CSS variables to ${response.css}`);
335
- console.log(pc2.green("\u2714"), `Using ${response.theme} theme`);
461
+ const cssPath = path5.join(process.cwd(), response.css);
462
+ if (await fs4.pathExists(cssPath)) {
463
+ const cssContent = await fs4.readFile(cssPath, "utf-8");
464
+ if (!cssContent.includes("Beaket UI Design System") && !cssContent.includes("beaket:theme:start")) {
465
+ const { css } = replaceThemeInCss(cssContent, selectedCss);
466
+ await fs4.writeFile(cssPath, css);
467
+ console.log(pc3.green("\u2714"), `Added CSS variables to ${response.css}`);
468
+ console.log(pc3.green("\u2714"), `Using ${response.theme} theme`);
336
469
  } else {
337
- console.log(pc2.yellow("\u2139"), "CSS variables already exist");
470
+ console.log(pc3.yellow("\u2139"), "CSS variables already exist");
338
471
  }
339
472
  } else {
340
- console.log(pc2.yellow("!"), `CSS file not found: ${response.css}`);
473
+ console.log(pc3.yellow("!"), `CSS file not found: ${response.css}`);
341
474
  console.log(" Add CSS variables manually:");
342
- console.log(pc2.cyan(" https://beaket.github.io/ui/installation"));
475
+ console.log(pc3.cyan(" https://beaket.github.io/ui/installation"));
343
476
  }
344
477
  }
345
478
  console.log();
346
- console.log(pc2.green("Done!"), "Beaket UI is ready.");
479
+ console.log(pc3.green("Done!"), "Beaket UI is ready.");
347
480
  console.log();
348
481
  console.log("Add components:");
349
- console.log(pc2.cyan(" npx @beaket/ui add button"));
482
+ console.log(pc3.cyan(" npx @beaket/ui add button"));
483
+ console.log();
484
+ }
485
+
486
+ // src/commands/theme.ts
487
+ import pc4 from "picocolors";
488
+ async function theme(options) {
489
+ console.log();
490
+ const config = await getConfig();
491
+ if (!config) {
492
+ console.log(pc4.red("Error:"), "beaket.ui.json not found.");
493
+ console.log("Run", pc4.cyan("npx @beaket/ui init"), "first.");
494
+ process.exit(1);
495
+ }
496
+ if (options.theme) {
497
+ if (!VALID_THEMES.includes(options.theme)) {
498
+ console.log(
499
+ pc4.red("Error:"),
500
+ `Invalid theme "${options.theme}". Choose from: ${VALID_THEMES.join(", ")}`
501
+ );
502
+ process.exit(1);
503
+ }
504
+ config.theme = options.theme;
505
+ }
506
+ await syncTheme(config, THEME_CSS, { overwrite: true });
507
+ if (options.theme) {
508
+ await writeConfig(config);
509
+ console.log(pc4.green("\u2714"), `Switched to ${options.theme} theme.`);
510
+ }
350
511
  console.log();
351
512
  }
352
513
 
353
514
  // src/index.ts
354
- var version = "2.1.0";
515
+ var version = "2.2.0";
355
516
  var program = new Command();
356
517
  program.name("@beaket/ui").description("CLI for adding Beaket UI components to your project").version(version);
357
518
  program.command("init").description("Initialize Beaket UI in your project").option("-y, --yes", "Use defaults without prompting").option("--theme <preset>", "Theme: porcelain, tobacco, marigold, or eucalyptus").action(init);
358
519
  program.command("add").description("Add components to your project").argument("<components...>", "Component names to add").option("-o, --overwrite", "Overwrite existing files").action(add);
520
+ program.command("theme").description("Sync theme CSS tokens to your project").option("--theme <preset>", "Switch theme: porcelain, tobacco, marigold, or eucalyptus").action(theme);
359
521
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beaket/ui",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "CLI tool for adding Beaket UI components to your project",
5
5
  "type": "module",
6
6
  "bin": {