digdesign 0.2.0 → 0.4.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 +839 -72
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -20,6 +20,8 @@ function defaultConfig() {
20
20
  tsx: true,
21
21
  tailwind: {
22
22
  css: "src/app/globals.css",
23
+ theme: "src/app/dig-theme.css",
24
+ brand: "src/app/brand.css",
23
25
  baseColor: "neutral",
24
26
  cssVariables: true
25
27
  },
@@ -53,6 +55,60 @@ async function writeConfig(config, cwd = process.cwd()) {
53
55
  );
54
56
  }
55
57
 
58
+ // src/lib/errors.ts
59
+ var DigError = class extends Error {
60
+ constructor(message, options) {
61
+ super(message, options);
62
+ this.name = "DigError";
63
+ }
64
+ };
65
+
66
+ // src/lib/prompt.ts
67
+ import * as readline from "readline/promises";
68
+ import { stdin, stdout } from "process";
69
+ async function askLine(question) {
70
+ const rl = readline.createInterface({ input: stdin, output: stdout });
71
+ try {
72
+ return await rl.question(question);
73
+ } catch {
74
+ console.log();
75
+ return "";
76
+ } finally {
77
+ rl.close();
78
+ }
79
+ }
80
+ async function confirm(question, defaultValue) {
81
+ const suffix = defaultValue ? "(Y/n)" : "(y/N)";
82
+ const raw = await askLine(`${question} ${suffix} `);
83
+ const answer = raw.trim().toLowerCase();
84
+ if (!answer) return defaultValue;
85
+ return answer === "y" || answer === "yes";
86
+ }
87
+ async function ask(question, defaultValue) {
88
+ const raw = await askLine(`${question} (${defaultValue}) `);
89
+ return raw.trim() || defaultValue;
90
+ }
91
+ async function multiSelect(question, options) {
92
+ if (options.length === 0) return [];
93
+ const width = String(options.length).length;
94
+ for (const [index, option] of options.entries()) {
95
+ const number = String(index + 1).padStart(width);
96
+ console.log(` ${number}) ${option.label}`);
97
+ }
98
+ const raw = await askLine(`${question} `);
99
+ return parseSelection(raw, options);
100
+ }
101
+ function parseSelection(raw, options) {
102
+ const picked = /* @__PURE__ */ new Set();
103
+ for (const token of raw.split(/[\s,،]+/).filter(Boolean)) {
104
+ const index = Number(token) - 1;
105
+ if (Number.isInteger(index) && index >= 0 && index < options.length) {
106
+ picked.add(options[index].value);
107
+ }
108
+ }
109
+ return [...picked];
110
+ }
111
+
56
112
  // src/lib/hash.ts
57
113
  import { createHash } from "crypto";
58
114
  function hashContent(content) {
@@ -125,31 +181,62 @@ function installArgs(pm, deps, dev) {
125
181
  const devFlag = dev ? pm === "npm" ? "--save-dev" : "-D" : null;
126
182
  return devFlag ? [verb, devFlag, ...deps] : [verb, ...deps];
127
183
  }
184
+ var PEER_CONFLICT_FLAG = {
185
+ npm: "--legacy-peer-deps",
186
+ pnpm: "--strict-peer-dependencies=false"
187
+ };
188
+ function installFailure(pm, args, code, stderr) {
189
+ const command = `${pm} ${args.join(" ")}`;
190
+ const lines = [`\u0646\u0635\u0628\u0650 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u200C\u0647\u0627 \u0634\u06A9\u0633\u062A \u062E\u0648\u0631\u062F (${command}\u060C \u06A9\u062F ${code ?? "?"}).`];
191
+ if (/ERESOLVE|peer dep|ERR_PNPM_PEER_DEP_ISSUES/i.test(stderr)) {
192
+ const flag = PEER_CONFLICT_FLAG[pm];
193
+ lines.push(
194
+ " \u0639\u0644\u062A: \u062A\u0639\u0627\u0631\u0636\u0650 peer dependency \u062F\u0631 \u062F\u0631\u062E\u062A\u0650 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u0650 \u062E\u0648\u062F\u0650 \u067E\u0631\u0648\u0698\u0647\u060C \u0646\u0647 \u062F\u0631 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627\u06CC \u062F\u06CC\u06AF."
195
+ );
196
+ lines.push(" \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0646\u0648\u0634\u062A\u0647 \u0634\u062F\u0646\u062F\u061B \u0641\u0642\u0637 \u0646\u0635\u0628\u0650 \u067E\u06A9\u06CC\u062C\u200C\u0647\u0627 \u0645\u0627\u0646\u062F\u0647.");
197
+ lines.push(
198
+ flag ? ` \u062F\u0633\u062A\u06CC \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F: ${command} ${flag}` : ` \u0627\u0648\u0644 \u062F\u0631\u062E\u062A\u0650 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC \u0631\u0627 \u062F\u0631\u0633\u062A \u06A9\u0646\u06CC\u062F\u060C \u0628\u0639\u062F: ${command}`
199
+ );
200
+ } else {
201
+ lines.push(" \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0646\u0648\u0634\u062A\u0647 \u0634\u062F\u0646\u062F\u061B \u0641\u0642\u0637 \u0646\u0635\u0628\u0650 \u067E\u06A9\u06CC\u062C\u200C\u0647\u0627 \u0645\u0627\u0646\u062F\u0647.");
202
+ lines.push(` \u062E\u0631\u0648\u062C\u06CC\u0650 \u06A9\u0627\u0645\u0644\u0650 ${pm} \u0628\u0627\u0644\u0627\u062A\u0631 \u0686\u0627\u067E \u0634\u062F\u0647. \u0628\u0631\u0627\u06CC \u062A\u0644\u0627\u0634 \u062F\u0648\u0628\u0627\u0631\u0647: ${command}`);
203
+ }
204
+ lines.push(" \u0628\u0631\u0627\u06CC \u0631\u062F \u06A9\u0631\u062F\u0646\u0650 \u0646\u0635\u0628\u0650 \u062E\u0648\u062F\u06A9\u0627\u0631 \u0627\u0632 \u0627\u06CC\u0646 \u0628\u0647 \u0628\u0639\u062F: dig add \u2026 --no-install");
205
+ return new DigError(lines.join("\n"));
206
+ }
128
207
  function installDependencies(pm, deps, options = {}) {
129
208
  if (!deps.length) return Promise.resolve();
130
209
  const args = installArgs(pm, deps, Boolean(options.dev));
131
210
  return new Promise((resolve, reject) => {
132
211
  const child = spawn(pm, args, {
133
212
  cwd: options.cwd ?? process.cwd(),
134
- stdio: "inherit",
213
+ // stderr پایپ می‌شود تا هم مثل قبل بی‌کم‌وکاست به کاربر برسد، هم بتوانیم
214
+ // علتِ شکست را از رویش تشخیص بدهیم. stdout همچنان inherit است چون
215
+ // نوارِ پیشرفتِ پکیج‌منیجر باید زنده بماند.
216
+ stdio: ["inherit", "inherit", "pipe"],
135
217
  shell: process.platform === "win32"
136
218
  });
219
+ let stderr = "";
220
+ child.stderr?.on("data", (chunk) => {
221
+ const text = chunk.toString();
222
+ stderr += text;
223
+ process.stderr.write(text);
224
+ });
137
225
  child.on("exit", (code) => {
138
226
  if (code === 0) resolve();
139
- else reject(new Error(`${pm} ${args.join(" ")} \u0628\u0627 \u06A9\u062F ${code} \u0634\u06A9\u0633\u062A \u062E\u0648\u0631\u062F.`));
227
+ else reject(installFailure(pm, args, code, stderr));
228
+ });
229
+ child.on("error", (cause) => {
230
+ reject(
231
+ new DigError(
232
+ `\xAB${pm}\xBB \u0627\u062C\u0631\u0627 \u0646\u0634\u062F. \u0645\u0637\u0645\u0626\u0646 \u0634\u0648\u06CC\u062F \u0646\u0635\u0628 \u0627\u0633\u062A \u0648 \u062F\u0631 PATH \u0642\u0631\u0627\u0631 \u062F\u0627\u0631\u062F.`,
233
+ { cause }
234
+ )
235
+ );
140
236
  });
141
- child.on("error", reject);
142
237
  });
143
238
  }
144
239
 
145
- // src/lib/errors.ts
146
- var DigError = class extends Error {
147
- constructor(message, options) {
148
- super(message, options);
149
- this.name = "DigError";
150
- }
151
- };
152
-
153
240
  // src/lib/registry.ts
154
241
  function resolveUrl(nameOrUrl, registryUrl) {
155
242
  if (/^https?:\/\//.test(nameOrUrl)) return nameOrUrl;
@@ -188,15 +275,23 @@ async function fetchFromRegistry(url, subject) {
188
275
  }
189
276
  throw new DigError(`\u062F\u0631\u06CC\u0627\u0641\u062A ${subject} \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062F (${response.status}): ${url}`);
190
277
  }
278
+ function normalizeType(type) {
279
+ return type.startsWith("registry:") ? `dig:${type.slice("registry:".length)}` : type;
280
+ }
191
281
  async function fetchRegistryIndex(registryUrl) {
192
282
  const url = `${registryUrl}/r/registry.json`;
193
283
  const response = await fetchFromRegistry(url, "\u0641\u0647\u0631\u0633\u062A\u0650 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC");
194
- return await response.json();
284
+ const index = await response.json();
285
+ for (const item of index.items) item.type = normalizeType(item.type);
286
+ return index;
195
287
  }
196
288
  async function fetchRegistryItem(nameOrUrl, registryUrl) {
197
289
  const url = resolveUrl(nameOrUrl, registryUrl);
198
290
  const response = await fetchFromRegistry(url, `\xAB${nameOrUrl}\xBB`);
199
- return await response.json();
291
+ const item = await response.json();
292
+ item.type = normalizeType(item.type);
293
+ for (const file of item.files ?? []) file.type = normalizeType(file.type);
294
+ return item;
200
295
  }
201
296
  async function resolveRegistryTree(names, registryUrl) {
202
297
  const resolved = /* @__PURE__ */ new Map();
@@ -284,7 +379,13 @@ import path5 from "path";
284
379
  function toPosixPath(relativePath) {
285
380
  return relativePath.split(path5.sep).join("/");
286
381
  }
287
- function resolveTargetPath(filePath, aliases) {
382
+ function targetPathOptions(config) {
383
+ return { tailwindCss: config.tailwind.css, themeCss: config.tailwind.theme };
384
+ }
385
+ function resolveTargetPath(filePath, aliases, options = {}) {
386
+ if (/(?:^|\/)app\/globals\.css$/.test(filePath)) {
387
+ return options.themeCss ?? options.tailwindCss ?? "src/app/globals.css";
388
+ }
288
389
  const match = filePath.match(/(?:^|\/)(components|lib|hooks)\/(.*)$/);
289
390
  const cleaned = match ? `${match[1]}/${match[2]}` : filePath;
290
391
  if (cleaned.startsWith("components/ui/")) {
@@ -311,11 +412,78 @@ async function pathExists(target) {
311
412
  return false;
312
413
  }
313
414
  }
415
+ async function pickComponents(registryUrl) {
416
+ if (!process.stdin.isTTY) {
417
+ throw new DigError(
418
+ "\u0646\u0627\u0645 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0631\u0627 \u0628\u062F\u0647\u06CC\u062F: dig add button\n \u0628\u0631\u0627\u06CC \u062F\u06CC\u062F\u0646\u0650 \u0641\u0647\u0631\u0633\u062A: dig list"
419
+ );
420
+ }
421
+ const index = await fetchRegistryIndex(registryUrl);
422
+ const choices = index.items.filter((item) => item.type === "dig:ui").map((item) => ({
423
+ value: item.name,
424
+ label: item.title ? `${item.name} \u2014 ${item.title}` : item.name
425
+ }));
426
+ console.log(`
427
+ ${choices.length} \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u062F\u0631 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC:
428
+ `);
429
+ const picked = await multiSelect(
430
+ "\n\u0634\u0645\u0627\u0631\u0647\u200C\u0647\u0627 \u0631\u0627 \u0628\u0627 \u0641\u0627\u0635\u0644\u0647 \u0628\u062F\u0647\u06CC\u062F (\u062E\u0627\u0644\u06CC = \u0644\u063A\u0648):",
431
+ choices
432
+ );
433
+ if (picked.length === 0) {
434
+ console.log("\u0686\u06CC\u0632\u06CC \u0627\u0646\u062A\u062E\u0627\u0628 \u0646\u0634\u062F.");
435
+ return [];
436
+ }
437
+ return picked;
438
+ }
314
439
  async function addCommand(components, options) {
315
440
  const cwd = process.cwd();
316
441
  const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
442
+ const names = components.length ? components : await pickComponents(config.registryUrl);
443
+ if (names.length === 0) return;
317
444
  console.log(`\u062F\u0631\u06CC\u0627\u0641\u062A \u0627\u0632 ${config.registryUrl} \u2026`);
318
- const items = await resolveRegistryTree(components, config.registryUrl);
445
+ const items = await resolveRegistryTree(names, config.registryUrl);
446
+ let overwrite = Boolean(options.overwrite);
447
+ if (!overwrite) {
448
+ const existing = [];
449
+ for (const item of items.values()) {
450
+ for (const file of item.files) {
451
+ const target = path6.join(cwd, resolveTargetPath(file.path, config.aliases, targetPathOptions(config)));
452
+ if (await pathExists(target)) existing.push(path6.relative(cwd, target));
453
+ }
454
+ }
455
+ if (existing.length) {
456
+ if (options.yes) {
457
+ overwrite = true;
458
+ } else if (process.stdin.isTTY) {
459
+ console.log(`
460
+ ${existing.length} \u0641\u0627\u06CC\u0644 \u0627\u0632 \u0642\u0628\u0644 \u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F:`);
461
+ for (const file of existing.slice(0, 10)) console.log(` ${file}`);
462
+ if (existing.length > 10) console.log(` \u2026 \u0648 ${existing.length - 10} \u0641\u0627\u06CC\u0644 \u062F\u06CC\u06AF\u0631`);
463
+ overwrite = await confirm("\u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u0634\u0648\u0646\u062F\u061F", false);
464
+ }
465
+ }
466
+ }
467
+ if (options.dryRun) {
468
+ const npmDeps = /* @__PURE__ */ new Set();
469
+ console.log("\n\u067E\u06CC\u0634\u200C\u0646\u0645\u0627\u06CC\u0634 (\u0647\u06CC\u0686 \u0641\u0627\u06CC\u0644\u06CC \u0646\u0648\u0634\u062A\u0647 \u0646\u0645\u06CC\u200C\u0634\u0648\u062F):\n");
470
+ for (const item of items.values()) {
471
+ for (const dep of item.dependencies ?? []) npmDeps.add(dep);
472
+ for (const file of item.files) {
473
+ const target = path6.join(cwd, resolveTargetPath(file.path, config.aliases, targetPathOptions(config)));
474
+ const exists2 = await pathExists(target);
475
+ const mark = exists2 ? overwrite ? "~" : "-" : "+";
476
+ const note = exists2 ? overwrite ? " (\u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC)" : " (\u0631\u062F \u0645\u06CC\u200C\u0634\u0648\u062F\u060C \u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F)" : "";
477
+ console.log(`${mark} ${path6.relative(cwd, target)}${note}`);
478
+ }
479
+ }
480
+ console.log(`
481
+ ${items.size} \u0622\u06CC\u062A\u0645 \u0627\u0632 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC.`);
482
+ if (npmDeps.size) {
483
+ console.log(`\u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u200C\u0647\u0627\u06CC npm: ${[...npmDeps].join(" ")}`);
484
+ }
485
+ return;
486
+ }
319
487
  const dependencies = /* @__PURE__ */ new Set();
320
488
  const devDependencies = /* @__PURE__ */ new Set();
321
489
  let written = 0;
@@ -329,8 +497,8 @@ async function addCommand(components, options) {
329
497
  const writtenFiles = {};
330
498
  const writtenContents = {};
331
499
  for (const file of item.files) {
332
- const target = path6.join(cwd, resolveTargetPath(file.path, config.aliases));
333
- if (!options.overwrite && await pathExists(target)) {
500
+ const target = path6.join(cwd, resolveTargetPath(file.path, config.aliases, targetPathOptions(config)));
501
+ if (!overwrite && await pathExists(target)) {
334
502
  console.log(`- \u0631\u062F \u0634\u062F (\u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F): ${path6.relative(cwd, target)}`);
335
503
  skipped++;
336
504
  continue;
@@ -384,6 +552,107 @@ ${written} \u0641\u0627\u06CC\u0644 \u0646\u0648\u0634\u062A\u0647 \u0634\u062F\
384
552
  }
385
553
  }
386
554
 
555
+ // src/lib/search.ts
556
+ var ARABIC_TO_PERSIAN = {
557
+ "\u064A": "\u06CC",
558
+ // ي → ی
559
+ "\u0649": "\u06CC",
560
+ // ى → ی
561
+ "\u0643": "\u06A9",
562
+ // ك → ک
563
+ "\u0629": "\u0647",
564
+ // ة → ه
565
+ "\u0623": "\u0627",
566
+ // أ → ا
567
+ "\u0625": "\u0627",
568
+ // إ → ا
569
+ "\u0622": "\u0627",
570
+ // آ → ا
571
+ "\u0624": "\u0648"
572
+ // ؤ → و
573
+ };
574
+ var DIACRITICS = /[ً-ْٰ]/g;
575
+ var INVISIBLE = /[​-‏؜]/g;
576
+ function toSearchKey(value) {
577
+ return value.replace(/[يىكةأإآؤ]/g, (ch) => ARABIC_TO_PERSIAN[ch]).replace(DIACRITICS, "").replace(INVISIBLE, "").toLowerCase().replace(/[\s‌]+/g, "");
578
+ }
579
+ function scoreItem(item, query) {
580
+ const q = toSearchKey(query);
581
+ if (!q) return 0;
582
+ const name = toSearchKey(item.name);
583
+ const title = toSearchKey(item.title ?? "");
584
+ const description = toSearchKey(item.description ?? "");
585
+ if (name === q || title === q) return 100;
586
+ if (name.startsWith(q) || title.startsWith(q)) return 80;
587
+ if (name.includes(q) || title.includes(q)) return 60;
588
+ if (description.includes(q)) return 30;
589
+ return 0;
590
+ }
591
+ function searchItems(items, query) {
592
+ return items.map((item) => ({ item, score: scoreItem(item, query) })).filter((entry) => entry.score > 0).sort(
593
+ (a, b) => b.score - a.score || a.item.name.localeCompare(b.item.name)
594
+ ).map((entry) => entry.item);
595
+ }
596
+
597
+ // src/commands/list.ts
598
+ var TYPE_LABEL = {
599
+ "dig:ui": "\u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A",
600
+ "dig:lib": "\u0627\u0628\u0632\u0627\u0631",
601
+ "dig:theme": "\u062A\u0645"
602
+ };
603
+ async function loadItems(cwd) {
604
+ const config = await configExists(cwd) ? await readConfig(cwd) : defaultConfig();
605
+ const index = await fetchRegistryIndex(config.registryUrl);
606
+ return index.items;
607
+ }
608
+ function matchesType(item, type) {
609
+ if (!type) return true;
610
+ const wanted = type.startsWith("dig:") ? type : `dig:${type}`;
611
+ return item.type === wanted;
612
+ }
613
+ function printTable(items) {
614
+ if (items.length === 0) {
615
+ console.log("\u0686\u06CC\u0632\u06CC \u067E\u06CC\u062F\u0627 \u0646\u0634\u062F.");
616
+ return;
617
+ }
618
+ const width = Math.max(...items.map((item) => item.name.length));
619
+ const labelWidth = Math.max(
620
+ ...items.map((item) => (TYPE_LABEL[item.type] ?? item.type).length)
621
+ );
622
+ for (const item of items) {
623
+ const label = (TYPE_LABEL[item.type] ?? item.type).padEnd(labelWidth);
624
+ const title = item.title ? ` \xB7 ${item.title}` : "";
625
+ console.log(`${item.name.padEnd(width)} ${label}${title}`);
626
+ }
627
+ console.log(`
628
+ ${items.length} \u0622\u06CC\u062A\u0645. \u0628\u0631\u0627\u06CC \u0646\u0635\u0628: dig add <\u0646\u0627\u0645>`);
629
+ }
630
+ async function listCommand(options) {
631
+ const items = (await loadItems(process.cwd())).filter(
632
+ (item) => matchesType(item, options.type)
633
+ );
634
+ if (options.json) {
635
+ console.log(JSON.stringify(items, null, 2));
636
+ return;
637
+ }
638
+ printTable(items);
639
+ }
640
+ async function searchCommand(query, options) {
641
+ const items = searchItems(await loadItems(process.cwd()), query);
642
+ if (options.json) {
643
+ console.log(JSON.stringify(items, null, 2));
644
+ return;
645
+ }
646
+ if (items.length === 0) {
647
+ console.log(
648
+ `\u0686\u06CC\u0632\u06CC \u0628\u0631\u0627\u06CC \xAB${query}\xBB \u067E\u06CC\u062F\u0627 \u0646\u0634\u062F.
649
+ \u0641\u0647\u0631\u0633\u062A \u06A9\u0627\u0645\u0644: dig list`
650
+ );
651
+ return;
652
+ }
653
+ printTable(items);
654
+ }
655
+
387
656
  // src/lib/diff.ts
388
657
  import { readFile as readFile4 } from "fs/promises";
389
658
  import path7 from "path";
@@ -423,8 +692,8 @@ async function readLocalHash(target) {
423
692
  throw error;
424
693
  }
425
694
  }
426
- async function computeFileDrift(cwd, aliases, lockEntry, file) {
427
- const target = path7.join(cwd, resolveTargetPath(file.path, aliases));
695
+ async function computeFileDrift(cwd, aliases, lockEntry, file, paths) {
696
+ const target = path7.join(cwd, resolveTargetPath(file.path, aliases, paths));
428
697
  const relPath = toPosixPath(path7.relative(cwd, target));
429
698
  const registryHash = file.hash ?? hashContent(file.content);
430
699
  const installedHash = lockEntry?.files[relPath];
@@ -438,9 +707,9 @@ async function computeFileDrift(cwd, aliases, lockEntry, file) {
438
707
  };
439
708
  }
440
709
  async function computeComponentDrift(params) {
441
- const { cwd, aliases, registryUrl, lockEntry, registryItem } = params;
710
+ const { cwd, aliases, registryUrl, lockEntry, registryItem, paths } = params;
442
711
  const files = await Promise.all(
443
- registryItem.files.map((file) => computeFileDrift(cwd, aliases, lockEntry, file))
712
+ registryItem.files.map((file) => computeFileDrift(cwd, aliases, lockEntry, file, paths))
444
713
  );
445
714
  return {
446
715
  name: registryItem.name,
@@ -588,7 +857,7 @@ function describeThreeWayConflict(baseContent, localContent, registryContent) {
588
857
  return `\u0646\u0633\u0628\u062A \u0628\u0647 \u0646\u0633\u062E\u0647\u0654 \u0646\u0635\u0628\u200C\u0634\u062F\u0647 (\u067E\u0627\u06CC\u0647): \u0634\u0645\u0627 ${local.added + local.removed} \u062E\u0637 \u062A\u063A\u06CC\u06CC\u0631 \u062F\u0627\u062F\u0647\u200C\u0627\u06CC\u062F (+${local.added}/-${local.removed})\u060C \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC ${registry.added + registry.removed} \u062E\u0637 \u062A\u063A\u06CC\u06CC\u0631 \u062F\u0627\u062F\u0647 (+${registry.added}/-${registry.removed}). \u062F\u06CC\u06AF \u062E\u0648\u062F\u06A9\u0627\u0631 \u0627\u062F\u063A\u0627\u0645 \u0646\u0645\u06CC\u200C\u06A9\u0646\u062F \u2014 \u062F\u0648 \u062F\u06CC\u0641\u0650 \u0628\u0627\u0644\u0627 \u0631\u0627 \u062F\u0633\u062A\u06CC \u0628\u0631\u0631\u0633\u06CC \u06A9\u0646\u06CC\u062F.`;
589
858
  }
590
859
  async function computeFileDiffDetails(params) {
591
- const { cwd, aliases, name, snapshotVersion, file, registryFile } = params;
860
+ const { cwd, aliases, name, snapshotVersion, file, registryFile, paths } = params;
592
861
  if (file.status === "up_to_date") return void 0;
593
862
  if (file.status === "new_upstream") {
594
863
  return {
@@ -604,7 +873,7 @@ async function computeFileDiffDetails(params) {
604
873
  note: file.registryHash === file.installedHash ? "\u0641\u0627\u06CC\u0644 \u0628\u0647\u200C\u0635\u0648\u0631\u062A \u0645\u062D\u0644\u06CC \u062D\u0630\u0641 \u0634\u062F\u0647\u061B \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0627\u0632 \u0632\u0645\u0627\u0646 \u0646\u0635\u0628 \u062A\u063A\u06CC\u06CC\u0631\u06CC \u0646\u06A9\u0631\u062F\u0647." : "\u0641\u0627\u06CC\u0644 \u0628\u0647\u200C\u0635\u0648\u0631\u062A \u0645\u062D\u0644\u06CC \u062D\u0630\u0641 \u0634\u062F\u0647 \u0648 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0647\u0645 \u062A\u063A\u06CC\u06CC\u0631 \u06A9\u0631\u062F\u0647 \u2014 \u0645\u0642\u0627\u06CC\u0633\u0647\u200C\u0627\u06CC \u0628\u0627 \u0641\u0627\u06CC\u0644 \u062D\u0630\u0641\u200C\u0634\u062F\u0647 \u0645\u0645\u06A9\u0646 \u0646\u06CC\u0633\u062A."
605
874
  };
606
875
  }
607
- const target = path8.join(cwd, resolveTargetPath(registryFile.path, aliases));
876
+ const target = path8.join(cwd, resolveTargetPath(registryFile.path, aliases, paths));
608
877
  const localContent = await readTextFileSafely(target);
609
878
  if (localContent === "binary") {
610
879
  return {
@@ -699,6 +968,7 @@ async function diffCommand(components, options) {
699
968
  const drift = await computeComponentDrift({
700
969
  cwd,
701
970
  aliases: config.aliases,
971
+ paths: targetPathOptions(config),
702
972
  registryUrl: config.registryUrl,
703
973
  lockEntry,
704
974
  registryItem
@@ -710,6 +980,7 @@ async function diffCommand(components, options) {
710
980
  (file, index) => file.status === "up_to_date" ? Promise.resolve(void 0) : computeFileDiffDetails({
711
981
  cwd,
712
982
  aliases: config.aliases,
983
+ paths: targetPathOptions(config),
713
984
  name,
714
985
  snapshotVersion: lockEntry?.snapshot?.version,
715
986
  file,
@@ -1393,7 +1664,7 @@ async function generateIntelligenceExport(params) {
1393
1664
  } catch {
1394
1665
  continue;
1395
1666
  }
1396
- if (item.type !== "registry:ui") continue;
1667
+ if (item.type !== "dig:ui") continue;
1397
1668
  const scan = await scanRepositoryForComponent({ projectRoot: cwd, component: name });
1398
1669
  installedComponents.push({
1399
1670
  name,
@@ -1612,7 +1883,7 @@ async function healthCommand(options) {
1612
1883
  } catch {
1613
1884
  continue;
1614
1885
  }
1615
- if (item.type !== "registry:ui") continue;
1886
+ if (item.type !== "dig:ui") continue;
1616
1887
  const scan = await scanRepositoryForComponent({ projectRoot: cwd, component: name });
1617
1888
  installedComponents.push({
1618
1889
  name,
@@ -1686,28 +1957,497 @@ async function impactCommand(name, options) {
1686
1957
  }
1687
1958
 
1688
1959
  // src/commands/init.ts
1960
+ import { readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
1961
+ import path12 from "path";
1962
+
1963
+ // src/commands/theme.ts
1964
+ import { access as access5, mkdir as mkdir4, readFile as readFile7, writeFile as writeFile5 } from "fs/promises";
1965
+ import path11 from "path";
1966
+
1967
+ // src/lib/brand-ramp.ts
1968
+ var RAMP_STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
1969
+ var REFERENCE_L = {
1970
+ 50: 96.6,
1971
+ 100: 93.1,
1972
+ 200: 87.6,
1973
+ 300: 79.7,
1974
+ 400: 69,
1975
+ 500: 60.4,
1976
+ 600: 52.9,
1977
+ 700: 47.3,
1978
+ 800: 41.1,
1979
+ 900: 36.9,
1980
+ 950: 26.9
1981
+ };
1982
+ var CHROMA_SHAPE = {
1983
+ 50: 0.07,
1984
+ 100: 0.13,
1985
+ 200: 0.25,
1986
+ 300: 0.44,
1987
+ 400: 0.69,
1988
+ 500: 0.89,
1989
+ 600: 1,
1990
+ 700: 0.92,
1991
+ 800: 0.74,
1992
+ 900: 0.55,
1993
+ 950: 0.34
1994
+ };
1995
+ function parseHex(hex) {
1996
+ const clean = hex.trim().replace(/^#/, "");
1997
+ const full = clean.length === 3 ? [...clean].map((ch) => ch + ch).join("") : clean;
1998
+ if (!/^[0-9a-fA-F]{6}$/.test(full)) {
1999
+ throw new DigError(`\xAB${hex}\xBB \u0631\u0646\u06AF\u0650 \u0647\u06AF\u0632\u0650 \u0645\u0639\u062A\u0628\u0631 \u0646\u06CC\u0633\u062A\u061B \u0645\u062B\u0644\u0627\u064B #2C4C5B \u0628\u062F\u0647\u06CC\u062F.`);
2000
+ }
2001
+ return [0, 2, 4].map((i) => parseInt(full.slice(i, i + 2), 16) / 255);
2002
+ }
2003
+ var toLinear = (v) => v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
2004
+ var fromLinear = (v) => v <= 31308e-7 ? 12.92 * v : 1.055 * v ** (1 / 2.4) - 0.055;
2005
+ function hexToOklch(hex) {
2006
+ const [r, g, b] = parseHex(hex).map(toLinear);
2007
+ const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
2008
+ const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
2009
+ const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
2010
+ const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s;
2011
+ const A = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s;
2012
+ const B = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s;
2013
+ const c = Math.sqrt(A * A + B * B);
2014
+ const h = (Math.atan2(B, A) * 180 / Math.PI + 360) % 360;
2015
+ return { l: L * 100, c, h };
2016
+ }
2017
+ function oklchToSrgb({ l, c, h }) {
2018
+ const L = l / 100;
2019
+ const rad = h * Math.PI / 180;
2020
+ const A = c * Math.cos(rad);
2021
+ const B = c * Math.sin(rad);
2022
+ const l_ = (L + 0.3963377774 * A + 0.2158037573 * B) ** 3;
2023
+ const m_ = (L - 0.1055613458 * A - 0.0638541728 * B) ** 3;
2024
+ const s_ = (L - 0.0894841775 * A - 1.291485548 * B) ** 3;
2025
+ return [
2026
+ fromLinear(4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_),
2027
+ fromLinear(-1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_),
2028
+ fromLinear(-0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_)
2029
+ ];
2030
+ }
2031
+ var inGamut = (rgb) => rgb.every((v) => v >= -5e-4 && v <= 1.0005);
2032
+ function fitGamut(color) {
2033
+ let { c } = color;
2034
+ while (c > 0 && !inGamut(oklchToSrgb({ ...color, c }))) c -= 2e-3;
2035
+ return { ...color, c: Math.max(0, c) };
2036
+ }
2037
+ function relativeLuminance(rgb) {
2038
+ const [r, g, b] = rgb.map((v) => toLinear(Math.min(1, Math.max(0, v))));
2039
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
2040
+ }
2041
+ function contrastWithWhite(color) {
2042
+ return 1.05 / (relativeLuminance(oklchToSrgb(color)) + 0.05);
2043
+ }
2044
+ function formatOklch({ l, c, h }) {
2045
+ return `oklch(${l.toFixed(1)}% ${c.toFixed(3)} ${h.toFixed(1)})`;
2046
+ }
2047
+ function buildBrandRamp(hex) {
2048
+ const base = hexToOklch(hex);
2049
+ const top = 97.5;
2050
+ const bottom = Math.min(26, Math.max(8, base.l * 0.55));
2051
+ const ref600 = REFERENCE_L[600];
2052
+ const steps = {};
2053
+ for (const step of RAMP_STEPS) {
2054
+ if (step === 600) {
2055
+ steps[step] = base;
2056
+ continue;
2057
+ }
2058
+ const ref = REFERENCE_L[step];
2059
+ const l = ref > ref600 ? base.l + (ref - ref600) / (REFERENCE_L[50] - ref600) * (top - base.l) : base.l - (ref600 - ref) / (ref600 - REFERENCE_L[950]) * (base.l - bottom);
2060
+ steps[step] = fitGamut({ l, c: base.c * CHROMA_SHAPE[step], h: base.h });
2061
+ }
2062
+ return { steps, primaryContrast: contrastWithWhite(base) };
2063
+ }
2064
+
2065
+ // src/lib/theme-files.ts
1689
2066
  import path10 from "path";
2067
+ function importPath(fromFile, toFile) {
2068
+ const rel = toPosixPath(path10.relative(path10.dirname(fromFile), toFile));
2069
+ return rel.startsWith(".") ? rel : `./${rel}`;
2070
+ }
2071
+ function globalsCss(paths, extraStatements = []) {
2072
+ const lines = [
2073
+ "/* \u0641\u0642\u0637 import. \u062A\u0645 \u062F\u0631 dig-theme.css (\u0645\u0627\u0644\u0650 \u062F\u06CC\u06AF) \u0648 \u0628\u0631\u0646\u062F \u062F\u0631 brand.css (\u0645\u0627\u0644\u0650",
2074
+ " \u067E\u0631\u0648\u0698\u0647) \u0627\u0633\u062A\u061B \u062A\u0631\u062A\u06CC\u0628 \u0645\u0647\u0645 \u0627\u0633\u062A: \u0628\u0631\u0646\u062F \u0628\u0639\u062F \u0627\u0632 \u062A\u0645 \u0645\u06CC\u200C\u0622\u06CC\u062F \u062A\u0627 \u0628\u0631\u0646\u062F\u0647 \u0628\u0627\u0634\u062F. */",
2075
+ `@import "${importPath(paths.css, paths.theme)}";`,
2076
+ ...extraStatements,
2077
+ `@import "${importPath(paths.css, paths.brand)}";`,
2078
+ ""
2079
+ ];
2080
+ return lines.join("\n");
2081
+ }
2082
+ var BRAND_HEADER = `/* ==================================================================== *
2083
+ * \u0628\u0631\u0646\u062F\u0650 \u067E\u0631\u0648\u0698\u0647
2084
+ *
2085
+ * \u0627\u06CC\u0646 \u0641\u0627\u06CC\u0644 \u0645\u0627\u0644\u0650 \u067E\u0631\u0648\u0698\u0647 \u0627\u0633\u062A \u0648 \u062F\u06CC\u06AF \u0647\u06CC\u0686\u200C\u0648\u0642\u062A \u0622\u0646 \u0631\u0627 \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u0646\u0645\u06CC\u200C\u06A9\u0646\u062F\u061B \u062A\u0645\u0650 \u062F\u06CC\u06AF \u062F\u0631
2086
+ * dig-theme.css \u0627\u0633\u062A \u0648 \u0628\u0627 \xABdig update theme\xBB \u0628\u0647\u200C\u0631\u0648\u0632 \u0645\u06CC\u200C\u0634\u0648\u062F.
2087
+ *
2088
+ * \u0686\u0647 \u0686\u06CC\u0632\u06CC \u0627\u06CC\u0646\u062C\u0627 \u0645\u06CC\u200C\u0622\u06CC\u062F:
2089
+ * \u06F1) \u0631\u0645\u067E\u0650 \u0628\u0631\u0646\u062F (--brand-*\u060C \u0644\u0627\u06CC\u0647\u0654 \u06F2)\u061B Shade \u06F6\u06F0\u06F0 \u0647\u0645\u0627\u0646 --primary \u0627\u0633\u062A.
2090
+ * \u06F2) \u0644\u0627\u06CC\u0647\u0654 \u0645\u062D\u0635\u0648\u0644: \u0641\u0642\u0637 \u062A\u0648\u06A9\u0646\u200C\u0647\u0627\u06CC \u0645\u0639\u0646\u0627\u06CC\u06CC\u200C\u0627\u06CC \u06A9\u0647 \u0628\u0627 \u067E\u06CC\u0634\u200C\u0641\u0631\u0636\u0650 \u062F\u06CC\u06AF \u0641\u0631\u0642 \u062F\u0627\u0631\u0646\u062F
2091
+ * (--background\u060C --foreground\u060C --radius\u060C --font-anchor\u060C \u0633\u0627\u06CC\u0647\u200C\u0647\u0627\u060C ...).
2092
+ * \u06F3) \u062A\u0648\u06A9\u0646\u200C\u0647\u0627\u06CC \u062A\u0627\u0632\u0647\u0654 \u062E\u0648\u062F\u0650 \u0645\u062D\u0635\u0648\u0644 \u062F\u0627\u062E\u0644 @theme inline.
2093
+ *
2094
+ * \u0631\u0645\u067E \u0631\u0627 \u0645\u06CC\u200C\u0634\u0648\u062F \u062F\u0648\u0628\u0627\u0631\u0647 \u0627\u0632 \u06CC\u06A9 \u0631\u0646\u06AF \u0633\u0627\u062E\u062A: npx digdesign theme brand "#2C4C5B"
2095
+ * ==================================================================== */
2096
+ `;
2097
+ function brandRampBlock(ramp, hex) {
2098
+ const lines = RAMP_STEPS.map((step) => ` --brand-${step}: ${formatOklch(ramp.steps[step])};`);
2099
+ return `/* dig:brand-ramp-start \u2014 \u0633\u0627\u062E\u062A\u0647\u200C\u0634\u062F\u0647 \u0627\u0632 ${hex.toUpperCase()}\u061B \xABdig theme brand\xBB \u0647\u0645\u06CC\u0646 \u0628\u0644\u0648\u06A9 \u0631\u0627 \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u0645\u06CC\u200C\u06A9\u0646\u062F */
2100
+ :root {
2101
+ ${lines.join("\n")}
2102
+ }
2103
+ /* dig:brand-ramp-end */`;
2104
+ }
2105
+ function brandCss(options) {
2106
+ const parts = [BRAND_HEADER];
2107
+ if (options.ramp && options.hex) parts.push(brandRampBlock(options.ramp, options.hex));
2108
+ if (options.body?.trim()) parts.push(options.body.trim());
2109
+ if (!options.ramp && !options.body?.trim()) {
2110
+ parts.push(`/* \u0646\u0645\u0648\u0646\u0647:
2111
+ :root {
2112
+ --brand-600: #2c4c5b;
2113
+ --radius: 0.75rem;
2114
+ }
2115
+ */`);
2116
+ }
2117
+ return `${parts.join("\n\n")}
2118
+ `;
2119
+ }
2120
+ function replaceBrandRamp(existing, ramp, hex) {
2121
+ const block = brandRampBlock(ramp, hex);
2122
+ const pattern = /\/\* dig:brand-ramp-start[\s\S]*?\/\* dig:brand-ramp-end \*\//;
2123
+ if (pattern.test(existing)) return existing.replace(pattern, block);
2124
+ const headerEnd = existing.indexOf("* ==================================================================== */");
2125
+ if (existing.startsWith("/* ====") && headerEnd !== -1) {
2126
+ const cut = existing.indexOf("\n", headerEnd) + 1;
2127
+ return `${existing.slice(0, cut)}
2128
+ ${block}
2129
+ ${existing.slice(cut)}`;
2130
+ }
2131
+ return `${block}
1690
2132
 
1691
- // src/lib/prompt.ts
1692
- import * as readline from "readline/promises";
1693
- import { stdin, stdout } from "process";
1694
- async function confirm(question, defaultValue) {
1695
- const rl = readline.createInterface({ input: stdin, output: stdout });
1696
- const suffix = defaultValue ? "(Y/n)" : "(y/N)";
1697
- const raw = await rl.question(`${question} ${suffix} `);
1698
- rl.close();
1699
- const answer = raw.trim().toLowerCase();
1700
- if (!answer) return defaultValue;
1701
- return answer === "y" || answer === "yes";
2133
+ ${existing}`;
2134
+ }
2135
+ function stripComments(css) {
2136
+ return css.replace(/\/\*[\s\S]*?\*\//g, "");
2137
+ }
2138
+ function parseCss(css) {
2139
+ const source = stripComments(css);
2140
+ const declarations = [];
2141
+ const statements = [];
2142
+ const stack = [];
2143
+ let buffer = "";
2144
+ let depthParen = 0;
2145
+ const flush = (terminator) => {
2146
+ const text = buffer.trim();
2147
+ buffer = "";
2148
+ if (!text) return;
2149
+ if (text.startsWith("@")) {
2150
+ statements.push({ context: [...stack], text: `${text};` });
2151
+ return;
2152
+ }
2153
+ const colon = text.indexOf(":");
2154
+ if (colon === -1) return;
2155
+ declarations.push({
2156
+ context: [...stack],
2157
+ property: text.slice(0, colon).trim(),
2158
+ value: text.slice(colon + 1).trim().replace(/\s+/g, " ")
2159
+ });
2160
+ void terminator;
2161
+ };
2162
+ for (const ch of source) {
2163
+ if (ch === "(") depthParen++;
2164
+ if (ch === ")") depthParen--;
2165
+ if (depthParen > 0) {
2166
+ buffer += ch;
2167
+ continue;
2168
+ }
2169
+ if (ch === "{") {
2170
+ stack.push(buffer.trim().replace(/\s+/g, " "));
2171
+ buffer = "";
2172
+ } else if (ch === ";") {
2173
+ flush(";");
2174
+ } else if (ch === "}") {
2175
+ flush("}");
2176
+ stack.pop();
2177
+ } else {
2178
+ buffer += ch;
2179
+ }
2180
+ }
2181
+ return { declarations, statements };
1702
2182
  }
1703
- async function ask(question, defaultValue) {
1704
- const rl = readline.createInterface({ input: stdin, output: stdout });
1705
- const raw = await rl.question(`${question} (${defaultValue}) `);
1706
- rl.close();
1707
- return raw.trim() || defaultValue;
2183
+ var key = (context, name) => `${context.join(" > ")}|${name}`;
2184
+ function splitTheme(projectCss, digThemeCss) {
2185
+ const project = parseCss(projectCss);
2186
+ const dig = parseCss(digThemeCss);
2187
+ const digValues = new Map(dig.declarations.map((d) => [key(d.context, d.property), d.value]));
2188
+ const digStatements = new Set(dig.statements.map((s) => key(s.context, s.text)));
2189
+ const overrides = /* @__PURE__ */ new Map();
2190
+ let changed = 0;
2191
+ let added = 0;
2192
+ for (const decl of project.declarations) {
2193
+ const k = key(decl.context, decl.property);
2194
+ const digValue = digValues.get(k);
2195
+ if (digValue === decl.value) {
2196
+ overrides.delete(k);
2197
+ continue;
2198
+ }
2199
+ overrides.set(k, decl);
2200
+ }
2201
+ for (const [k] of overrides) {
2202
+ if (digValues.has(k)) changed++;
2203
+ else added++;
2204
+ }
2205
+ const groups = /* @__PURE__ */ new Map();
2206
+ for (const decl of overrides.values()) {
2207
+ const ctx = decl.context.join(" > ");
2208
+ if (!groups.has(ctx)) groups.set(ctx, []);
2209
+ groups.get(ctx).push(decl);
2210
+ }
2211
+ const blocks = [];
2212
+ for (const [, decls] of groups) {
2213
+ const context = decls[0].context;
2214
+ const indent = (level) => " ".repeat(level);
2215
+ const lines = [];
2216
+ context.forEach((selector, level) => lines.push(`${indent(level)}${selector} {`));
2217
+ for (const d of decls) lines.push(`${indent(context.length)}${d.property}: ${d.value};`);
2218
+ for (let level = context.length - 1; level >= 0; level--) lines.push(`${indent(level)}}`);
2219
+ blocks.push(lines.join("\n"));
2220
+ }
2221
+ const extraStatements = project.statements.filter((s) => s.context.length === 0 && !digStatements.has(key(s.context, s.text))).map((s) => s.text);
2222
+ return { body: blocks.join("\n\n"), extraStatements, changed, added };
2223
+ }
2224
+
2225
+ // src/commands/theme.ts
2226
+ var THEME_ITEM = "theme";
2227
+ async function readIfExists(target) {
2228
+ try {
2229
+ return await readFile7(target, "utf8");
2230
+ } catch {
2231
+ return null;
2232
+ }
2233
+ }
2234
+ async function exists(target) {
2235
+ try {
2236
+ await access5(target);
2237
+ return true;
2238
+ } catch {
2239
+ return false;
2240
+ }
2241
+ }
2242
+ function themePaths(config) {
2243
+ const dir = path11.posix.dirname(toPosixPath(config.tailwind.css));
2244
+ return {
2245
+ css: config.tailwind.css,
2246
+ theme: config.tailwind.theme ?? `${dir}/dig-theme.css`,
2247
+ brand: config.tailwind.brand ?? `${dir}/brand.css`
2248
+ };
2249
+ }
2250
+ function isBoilerplateCss(content) {
2251
+ if (content === null) return true;
2252
+ const withoutComments = content.replace(/\/\*[\s\S]*?\*\//g, "");
2253
+ return withoutComments.split("\n").filter((line) => line.trim() && !line.trim().startsWith("@import")).length === 0;
2254
+ }
2255
+ async function fetchTheme(config) {
2256
+ const item = await fetchRegistryItem(THEME_ITEM, config.registryUrl);
2257
+ const file = item.files[0];
2258
+ if (!file) throw new DigError("\u0622\u06CC\u062A\u0645\u0650 \xABtheme\xBB \u062F\u0631 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0641\u0627\u06CC\u0644\u06CC \u0646\u062F\u0627\u0631\u062F.");
2259
+ return { ...item, content: file.content };
2260
+ }
2261
+ async function recordTheme(cwd, config, themeRel, item) {
2262
+ const lock = await readLock(cwd);
2263
+ const version = item.version ?? "0.0.0";
2264
+ const installedAt = (/* @__PURE__ */ new Date()).toISOString();
2265
+ const relPath = toPosixPath(themeRel);
2266
+ const snapshot = await writeComponentSnapshot(cwd, THEME_ITEM, version, { [relPath]: item.content });
2267
+ lock.components[THEME_ITEM] = mergeComponentEntry(lock.components[THEME_ITEM], {
2268
+ version: item.version,
2269
+ registryUrl: config.registryUrl,
2270
+ installedAt,
2271
+ writtenFiles: { [relPath]: item.files[0].hash ?? hashContent(item.content) },
2272
+ snapshot: "path" in snapshot ? { version, path: snapshot.path, installedAt } : void 0
2273
+ });
2274
+ await writeLock(lock, cwd);
2275
+ }
2276
+ async function writeText(cwd, rel, content) {
2277
+ const target = path11.join(cwd, rel);
2278
+ await mkdir4(path11.dirname(target), { recursive: true });
2279
+ await writeFile5(target, content, "utf8");
2280
+ }
2281
+ async function installTheme(cwd, config, options) {
2282
+ const paths = themePaths(config);
2283
+ const item = await fetchTheme(config);
2284
+ const ramp = options.brand ? buildBrandRamp(options.brand) : void 0;
2285
+ await writeText(cwd, paths.theme, item.content);
2286
+ console.log(`\u2713 \u062A\u0645\u0650 \u062F\u06CC\u06AF \u062F\u0631 ${paths.theme} \u0646\u0648\u0634\u062A\u0647 \u0634\u062F (\u0645\u0627\u0644\u0650 \u062F\u06CC\u06AF\u061B \u0628\u0627 \xABdig update theme\xBB \u0628\u0647\u200C\u0631\u0648\u0632 \u0645\u06CC\u200C\u0634\u0648\u062F).`);
2287
+ const existingBrand = await readIfExists(path11.join(cwd, paths.brand));
2288
+ if (existingBrand === null) {
2289
+ await writeText(cwd, paths.brand, brandCss({ ramp, hex: options.brand }));
2290
+ console.log(`\u2713 ${paths.brand} \u0633\u0627\u062E\u062A\u0647 \u0634\u062F (\u0645\u0627\u0644\u0650 \u067E\u0631\u0648\u0698\u0647\u061B \u062F\u06CC\u06AF \u0647\u0631\u06AF\u0632 \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC\u200C\u0627\u0634 \u0646\u0645\u06CC\u200C\u06A9\u0646\u062F).`);
2291
+ } else if (ramp && options.brand) {
2292
+ await writeText(cwd, paths.brand, replaceBrandRamp(existingBrand, ramp, options.brand));
2293
+ console.log(`\u2713 \u0631\u0645\u067E\u0650 \u0628\u0631\u0646\u062F \u062F\u0631 ${paths.brand} \u0627\u0632 ${options.brand} \u0628\u0627\u0632\u0633\u0627\u0632\u06CC \u0634\u062F\u061B \u0628\u0642\u06CC\u0647\u0654 \u0641\u0627\u06CC\u0644 \u062F\u0633\u062A \u0646\u062E\u0648\u0631\u062F.`);
2294
+ } else {
2295
+ console.log(`- ${paths.brand} \u0627\u0632 \u0642\u0628\u0644 \u0647\u0633\u062A\u061B \u062F\u0633\u062A \u0646\u062E\u0648\u0631\u062F.`);
2296
+ }
2297
+ if (ramp) reportContrast(ramp.primaryContrast);
2298
+ const cssTarget = path11.join(cwd, paths.css);
2299
+ const existingCss = await readIfExists(cssTarget);
2300
+ if (!isBoilerplateCss(existingCss) && existingCss !== null) {
2301
+ await writeFile5(`${cssTarget}.backup`, existingCss, "utf8");
2302
+ console.log(` \u0646\u0633\u062E\u0647\u0654 \u0642\u0628\u0644\u06CC\u0650 ${paths.css} \u062F\u0631 ${paths.css}.backup \u0646\u06AF\u0647 \u062F\u0627\u0634\u062A\u0647 \u0634\u062F.`);
2303
+ console.log(` \u0627\u06AF\u0631 \u0622\u0646 \u0641\u0627\u06CC\u0644 \u062A\u0645\u0650 \u0642\u062F\u06CC\u0645\u06CC\u0650 \u062F\u06CC\u06AF \u0628\u0648\u062F\u060C \u0628\u0647\u200C\u062C\u0627\u06CC init \u0627\u0632 \xABdig theme split\xBB \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u06A9\u0646\u06CC\u062F.`);
2304
+ }
2305
+ await writeText(cwd, paths.css, globalsCss(paths));
2306
+ console.log(`\u2713 ${paths.css} \u0641\u0642\u0637 import \u0634\u062F: \u062A\u0645\u060C \u0633\u067E\u0633 \u0628\u0631\u0646\u062F.`);
2307
+ await recordTheme(cwd, config, paths.theme, item);
2308
+ return item;
2309
+ }
2310
+ function reportContrast(contrast) {
2311
+ const ratio = contrast.toFixed(2);
2312
+ if (contrast >= 4.5) {
2313
+ console.log(` \u06A9\u0646\u062A\u0631\u0627\u0633\u062A\u0650 \u0645\u062A\u0646\u0650 \u0633\u0641\u06CC\u062F \u0631\u0648\u06CC \u0631\u0646\u06AF\u0650 \u0627\u0635\u0644\u06CC: ${ratio} (AA \u0631\u0627 \u0631\u062F \u0645\u06CC\u200C\u06A9\u0646\u062F).`);
2314
+ } else {
2315
+ console.log(
2316
+ ` \u26A0 \u06A9\u0646\u062A\u0631\u0627\u0633\u062A\u0650 \u0645\u062A\u0646\u0650 \u0633\u0641\u06CC\u062F \u0631\u0648\u06CC \u0631\u0646\u06AF\u0650 \u0627\u0635\u0644\u06CC ${ratio} \u0627\u0633\u062A\u060C \u06A9\u0645\u062A\u0631 \u0627\u0632 \u06F4.\u06F5 (AA). \u0645\u062A\u0646\u0650 \u062F\u06A9\u0645\u0647\u200C\u0647\u0627\u06CC \u0627\u0635\u0644\u06CC \u062E\u0648\u0627\u0646\u0627 \u0646\u06CC\u0633\u062A\u061B \u0631\u0646\u06AF\u06CC \u062A\u06CC\u0631\u0647\u200C\u062A\u0631 \u0628\u062F\u0647\u06CC\u062F \u06CC\u0627 --primary-foreground \u0631\u0627 \u062F\u0631 brand.css \u062A\u06CC\u0631\u0647 \u06A9\u0646\u06CC\u062F.`
2317
+ );
2318
+ }
2319
+ }
2320
+ async function requireConfig(cwd) {
2321
+ if (!await configExists(cwd)) {
2322
+ throw new DigError("dig.json \u067E\u06CC\u062F\u0627 \u0646\u0634\u062F\u061B \u0627\u0648\u0644 \xABnpx digdesign init\xBB \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F.");
2323
+ }
2324
+ return readConfig(cwd);
2325
+ }
2326
+ async function themeSplitCommand(options) {
2327
+ const cwd = process.cwd();
2328
+ const config = await requireConfig(cwd);
2329
+ if (config.tailwind.theme) {
2330
+ console.log(`\u067E\u0631\u0648\u0698\u0647 \u0627\u0632 \u0642\u0628\u0644 \u0633\u0627\u062E\u062A\u0627\u0631\u0650 \u062F\u0648\u0641\u0627\u06CC\u0644\u06CC \u062F\u0627\u0631\u062F (${config.tailwind.theme})\u061B \u06A9\u0627\u0631\u06CC \u0644\u0627\u0632\u0645 \u0646\u06CC\u0633\u062A.`);
2331
+ return;
2332
+ }
2333
+ const paths = themePaths(config);
2334
+ const current = await readIfExists(path11.join(cwd, paths.css));
2335
+ if (current === null || isBoilerplateCss(current)) {
2336
+ throw new DigError(`${paths.css} \u062A\u0645\u06CC \u0646\u062F\u0627\u0631\u062F\u061B \u0628\u0631\u0627\u06CC \u0646\u0635\u0628\u0650 \u062A\u0627\u0632\u0647 \xABdig init\xBB \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F.`);
2337
+ }
2338
+ if (await exists(path11.join(cwd, paths.brand))) {
2339
+ throw new DigError(`${paths.brand} \u0627\u0632 \u0642\u0628\u0644 \u0648\u062C\u0648\u062F \u062F\u0627\u0631\u062F\u061B \u0628\u0631\u0627\u06CC \u062C\u0644\u0648\u06AF\u06CC\u0631\u06CC \u0627\u0632 \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC\u060C \u0627\u0648\u0644 \u062C\u0627\u0628\u0647\u200C\u062C\u0627\u06CC\u0634 \u06A9\u0646\u06CC\u062F.`);
2340
+ }
2341
+ console.log(`\u062F\u0631\u06CC\u0627\u0641\u062A\u0650 \u062A\u0645\u0650 \u062F\u06CC\u06AF \u0627\u0632 ${config.registryUrl} \u2026`);
2342
+ const item = await fetchTheme(config);
2343
+ const split = splitTheme(current, item.content);
2344
+ console.log(`
2345
+ ${split.changed} \u062A\u0648\u06A9\u0646\u0650 \u0639\u0648\u0636\u200C\u0634\u062F\u0647 \u0648 ${split.added} \u062A\u0648\u06A9\u0646\u0650 \u062A\u0627\u0632\u0647 \u067E\u06CC\u062F\u0627 \u0634\u062F \u2192 ${paths.brand}`);
2346
+ if (split.extraStatements.length) {
2347
+ console.log(`${split.extraStatements.length} \u062F\u0633\u062A\u0648\u0631\u0650 \u0633\u0637\u062D\u0650 \u0628\u0627\u0644\u0627 \u062F\u0631 ${paths.css} \u0645\u06CC\u200C\u0645\u0627\u0646\u062F:`);
2348
+ for (const statement of split.extraStatements) console.log(` ${statement}`);
2349
+ }
2350
+ if (options.dryRun) {
2351
+ console.log(`
2352
+ \u067E\u06CC\u0634\u200C\u0646\u0645\u0627\u06CC\u0634\u0650 ${paths.brand} (\u0686\u06CC\u0632\u06CC \u0646\u0648\u0634\u062A\u0647 \u0646\u0634\u062F):
2353
+ `);
2354
+ console.log(split.body || "(\u062E\u0627\u0644\u06CC: \u067E\u0631\u0648\u0698\u0647 \u0686\u06CC\u0632\u06CC \u062F\u0631 \u062A\u0645 \u0639\u0648\u0636 \u0646\u06A9\u0631\u062F\u0647)");
2355
+ return;
2356
+ }
2357
+ await writeFile5(path11.join(cwd, `${paths.css}.backup`), current, "utf8");
2358
+ await writeText(cwd, paths.theme, item.content);
2359
+ await writeText(cwd, paths.brand, brandCss({ body: split.body }));
2360
+ await writeText(cwd, paths.css, globalsCss(paths, split.extraStatements));
2361
+ await writeConfig(
2362
+ { ...config, tailwind: { ...config.tailwind, theme: paths.theme, brand: paths.brand } },
2363
+ cwd
2364
+ );
2365
+ await recordTheme(cwd, config, paths.theme, item);
2366
+ console.log(`
2367
+ \u2713 ${paths.theme} (\u062A\u0645\u0650 \u062F\u06CC\u06AF)\u060C ${paths.brand} (\u0628\u0631\u0646\u062F) \u0648 ${paths.css} (\u0641\u0642\u0637 import) \u0646\u0648\u0634\u062A\u0647 \u0634\u062F.`);
2368
+ console.log(` \u0646\u0633\u062E\u0647\u0654 \u0642\u0628\u0644\u06CC: ${paths.css}.backup`);
2369
+ console.log(
2370
+ ` \u062A\u0641\u0627\u0648\u062A\u200C\u0647\u0627 \u0628\u0627 \u0622\u062E\u0631\u06CC\u0646 \u062A\u0645\u0650 \u062F\u06CC\u06AF \u0645\u0642\u0627\u06CC\u0633\u0647 \u0634\u062F\u0646\u062F\u061B \u0627\u06AF\u0631 \u067E\u0631\u0648\u0698\u0647 \u0646\u0633\u062E\u0647\u0654 \u0642\u062F\u06CC\u0645\u06CC\u200C\u062A\u0631\u06CC \u0627\u0632 \u062A\u0645 \u062F\u0627\u0634\u062A\u060C \u0645\u0645\u06A9\u0646 \u0627\u0633\u062A \u0686\u0646\u062F \u0645\u0642\u062F\u0627\u0631\u0650 \u0642\u062F\u06CC\u0645\u06CC\u0650 \u062F\u06CC\u06AF \u0647\u0645 \u0628\u0647 ${paths.brand} \u0631\u0641\u062A\u0647 \u0628\u0627\u0634\u062F. \u06CC\u06A9 \u0646\u06AF\u0627\u0647 \u0628\u0647 \u0622\u0646 \u0628\u06CC\u0646\u062F\u0627\u0632\u06CC\u062F.`
2371
+ );
2372
+ }
2373
+ async function themeBrandCommand(hex) {
2374
+ const cwd = process.cwd();
2375
+ const config = await requireConfig(cwd);
2376
+ if (!config.tailwind.theme) {
2377
+ throw new DigError("\u0627\u06CC\u0646 \u067E\u0631\u0648\u0698\u0647 \u0647\u0646\u0648\u0632 brand.css \u0646\u062F\u0627\u0631\u062F\u061B \u0627\u0648\u0644 \xABdig theme split\xBB \u0631\u0627 \u0627\u062C\u0631\u0627 \u06A9\u0646\u06CC\u062F.");
2378
+ }
2379
+ const paths = themePaths(config);
2380
+ const ramp = buildBrandRamp(hex);
2381
+ const existing = await readIfExists(path11.join(cwd, paths.brand)) ?? brandCss({});
2382
+ const next = replaceBrandRamp(existing, ramp, hex);
2383
+ await writeText(cwd, paths.brand, next);
2384
+ console.log(`\u2713 \u0631\u0645\u067E\u0650 \u0628\u0631\u0646\u062F \u062F\u0631 ${paths.brand} \u0627\u0632 ${hex} \u0633\u0627\u062E\u062A\u0647 \u0634\u062F (Shade \u06F6\u06F0\u06F0 = \u0631\u0646\u06AF\u0650 \u062F\u0627\u062F\u0647\u200C\u0634\u062F\u0647).`);
2385
+ reportContrast(ramp.primaryContrast);
2386
+ const outside = next.replace(/\/\* dig:brand-ramp-start[\s\S]*?\/\* dig:brand-ramp-end \*\//, "");
2387
+ const shadowing = outside.match(/--brand-\d+\s*:/g);
2388
+ if (shadowing) {
2389
+ console.log(
2390
+ ` \u26A0 ${shadowing.length} \u0627\u0639\u0644\u0627\u0646\u0650 --brand-* \u0628\u06CC\u0631\u0648\u0646 \u0627\u0632 \u0628\u0644\u0648\u06A9\u0650 \u0631\u0645\u067E \u062F\u0631 ${paths.brand} \u0647\u0633\u062A \u0648 \u0628\u0631 \u0622\u0646 \u0628\u0631\u0646\u062F\u0647 \u0645\u06CC\u200C\u0634\u0648\u062F\u061B \u0627\u06AF\u0631 \u0631\u0645\u067E\u0650 \u062A\u0627\u0632\u0647 \u0631\u0627 \u0645\u06CC\u200C\u062E\u0648\u0627\u0647\u06CC\u062F\u060C \u0622\u0646\u200C\u0647\u0627 \u0631\u0627 \u067E\u0627\u06A9 \u06A9\u0646\u06CC\u062F.`
2391
+ );
2392
+ }
1708
2393
  }
1709
2394
 
1710
2395
  // src/commands/init.ts
2396
+ async function readIfExists2(target) {
2397
+ try {
2398
+ return await readFile8(target, "utf8");
2399
+ } catch {
2400
+ return null;
2401
+ }
2402
+ }
2403
+ async function writeTheme(config, cwd, options) {
2404
+ const paths = themePaths(config);
2405
+ const existing = await readIfExists2(path12.join(cwd, paths.css));
2406
+ if (!isBoilerplateCss(existing) && !options.yes) {
2407
+ console.log(`
2408
+ ${paths.css} \u0627\u0632 \u0642\u0628\u0644 \u0645\u062D\u062A\u0648\u0627 \u062F\u0627\u0631\u062F.`);
2409
+ console.log(` \u0627\u06AF\u0631 \u062A\u0645\u0650 \u0642\u062F\u06CC\u0645\u06CC\u0650 \u062F\u06CC\u06AF \u0627\u0633\u062A\u060C \u0628\u0647\u200C\u062C\u0627\u06CC \u0627\u06CC\u0646 \u0627\u0632 \xABdig theme split\xBB \u0627\u0633\u062A\u0641\u0627\u062F\u0647 \u06A9\u0646\u06CC\u062F \u062A\u0627 \u0628\u0631\u0646\u062F\u062A\u0627\u0646 \u062D\u0641\u0638 \u0634\u0648\u062F.`);
2410
+ const overwrite = await confirm(
2411
+ "\u0628\u0627 \u0633\u0627\u062E\u062A\u0627\u0631\u0650 \u062A\u0627\u0632\u0647 (\u0641\u0642\u0637 import) \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u0634\u0648\u062F\u061F (\u0646\u0633\u062E\u0647\u0654 \u0641\u0639\u0644\u06CC \u06A9\u0646\u0627\u0631\u0634 \u0628\u0647\u200C\u0639\u0646\u0648\u0627\u0646 .backup \u0645\u06CC\u200C\u0645\u0627\u0646\u062F)",
2412
+ false
2413
+ );
2414
+ if (!overwrite) {
2415
+ console.log(`- \u062A\u0645 \u0646\u0648\u0634\u062A\u0647 \u0646\u0634\u062F. \u0628\u0639\u062F\u0627\u064B: dig init \u06CC\u0627 dig theme split`);
2416
+ return "skipped";
2417
+ }
2418
+ }
2419
+ const item = await installTheme(cwd, config, { brand: options.brand });
2420
+ if (item.dependencies?.length) {
2421
+ const pm = await detectPackageManager(cwd);
2422
+ console.log(`
2423
+ \u0646\u0635\u0628 \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u200C\u0647\u0627\u06CC \u062A\u0645 \u0628\u0627 ${pm} \u2026`);
2424
+ await installDependencies(pm, item.dependencies, { cwd });
2425
+ }
2426
+ return "written";
2427
+ }
2428
+ async function applyRtl(cwd) {
2429
+ for (const candidate of ["src/app/layout.tsx", "app/layout.tsx"]) {
2430
+ const target = path12.join(cwd, candidate);
2431
+ const source = await readIfExists2(target);
2432
+ if (source === null) continue;
2433
+ if (/<html[^>]*\sdir=/.test(source)) {
2434
+ console.log(`- ${candidate} \u0627\u0632 \u0642\u0628\u0644 dir \u062F\u0627\u0631\u062F\u061B \u062F\u0633\u062A \u0646\u062E\u0648\u0631\u062F.`);
2435
+ return;
2436
+ }
2437
+ const match = source.match(/<html\b([^>]*)>/);
2438
+ if (!match) break;
2439
+ const updated = source.replace(
2440
+ match[0],
2441
+ `<html${match[1]} dir="rtl">`.replace(/\s+dir="rtl">$/, ' dir="rtl">')
2442
+ );
2443
+ await writeFile6(target, updated, "utf8");
2444
+ console.log(`\u2713 dir="rtl" \u0628\u0647 ${candidate} \u0627\u0636\u0627\u0641\u0647 \u0634\u062F.`);
2445
+ return;
2446
+ }
2447
+ console.log(
2448
+ '- \u0686\u06CC\u062F\u0645\u0627\u0646\u0650 \u0631\u06CC\u0634\u0647 \u067E\u06CC\u062F\u0627 \u0646\u0634\u062F. \u062E\u0648\u062F\u062A\u0627\u0646 dir="rtl" \u0631\u0627 \u0631\u0648\u06CC \u062A\u06AF <html> \u0628\u06AF\u0630\u0627\u0631\u06CC\u062F.'
2449
+ );
2450
+ }
1711
2451
  async function initCommand(options) {
1712
2452
  const cwd = process.cwd();
1713
2453
  const base = defaultConfig();
@@ -1721,21 +2461,34 @@ async function initCommand(options) {
1721
2461
  const config = options.yes ? base : {
1722
2462
  ...base,
1723
2463
  rtl: await confirm("\u067E\u0631\u0648\u0698\u0647 \u0631\u0627\u0633\u062A\u200C\u0628\u0647\u200C\u0686\u067E (RTL) \u0627\u0633\u062A\u061F", base.rtl),
1724
- tailwind: {
1725
- ...base.tailwind,
1726
- css: await ask(
1727
- "\u0645\u0633\u06CC\u0631 \u0641\u0627\u06CC\u0644 CSS \u0627\u0635\u0644\u06CC \u062A\u06CC\u0644\u0648\u06CC\u0646\u062F\u061F",
1728
- base.tailwind.css
1729
- )
1730
- }
2464
+ tailwind: await (async () => {
2465
+ const css = await ask("\u0645\u0633\u06CC\u0631 \u0641\u0627\u06CC\u0644 CSS \u0627\u0635\u0644\u06CC \u062A\u06CC\u0644\u0648\u06CC\u0646\u062F\u061F", base.tailwind.css);
2466
+ const dir = path12.posix.dirname(css.split(path12.sep).join("/"));
2467
+ return { ...base.tailwind, css, theme: `${dir}/dig-theme.css`, brand: `${dir}/brand.css` };
2468
+ })()
1731
2469
  };
1732
2470
  await writeConfig(config, cwd);
1733
- console.log(`\u2713 ${path10.join(cwd, "dig.json")} \u0646\u0648\u0634\u062A\u0647 \u0634\u062F.`);
2471
+ console.log(`\u2713 ${path12.join(cwd, "dig.json")} \u0646\u0648\u0634\u062A\u0647 \u0634\u062F.`);
2472
+ const theme2 = await writeTheme(config, cwd, options);
2473
+ if (config.rtl) {
2474
+ await applyRtl(cwd);
2475
+ }
2476
+ console.log("\n\u0622\u0645\u0627\u062F\u0647 \u0627\u0633\u062A. \u0642\u062F\u0645 \u0628\u0639\u062F\u06CC:");
2477
+ console.log(" dig add button");
2478
+ if (theme2 === "written") {
2479
+ console.log("\n\u0628\u0631\u0646\u062F \u0648 \u0634\u062E\u0635\u06CC\u200C\u0633\u0627\u0632\u06CC \u0641\u0642\u0637 \u062F\u0631 brand.css\u061B \u062A\u0645\u0650 \u062F\u06CC\u06AF \u0628\u0627 \xABdig update theme\xBB \u0628\u0647\u200C\u0631\u0648\u0632 \u0645\u06CC\u200C\u0634\u0648\u062F.");
2480
+ }
2481
+ if (theme2 === "skipped") {
2482
+ console.log(
2483
+ `
2484
+ \u26A0 \u0628\u062F\u0648\u0646 \u062A\u0645\u060C \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627 \u0646\u0635\u0628 \u0645\u06CC\u200C\u0634\u0648\u0646\u062F \u0648\u0644\u06CC \u0628\u06CC\u200C\u0638\u0627\u0647\u0631 \u0645\u06CC\u200C\u0645\u0627\u0646\u0646\u062F (\u0631\u0646\u06AF\u060C \u0634\u0639\u0627\u0639 \u0648 \u0633\u0627\u06CC\u0647 \u0627\u0632 \u062A\u0648\u06A9\u0646 \u0645\u06CC\u200C\u0622\u06CC\u0646\u062F).`
2485
+ );
2486
+ }
1734
2487
  }
1735
2488
 
1736
2489
  // src/lib/migration-apply.ts
1737
- import { readFile as readFile7, writeFile as writeFile5 } from "fs/promises";
1738
- import path11 from "path";
2490
+ import { readFile as readFile9, writeFile as writeFile7 } from "fs/promises";
2491
+ import path13 from "path";
1739
2492
 
1740
2493
  // src/lib/migration-strategy.ts
1741
2494
  var TextReplaceStrategy = class {
@@ -1812,7 +2565,7 @@ function looksBinary2(buffer) {
1812
2565
  async function readTextFileSafely2(target) {
1813
2566
  let buffer;
1814
2567
  try {
1815
- buffer = await readFile7(target);
2568
+ buffer = await readFile9(target);
1816
2569
  } catch (error) {
1817
2570
  if (error.code === "ENOENT") return void 0;
1818
2571
  throw error;
@@ -1834,7 +2587,7 @@ function summarizeMigration(migration) {
1834
2587
  return void 0;
1835
2588
  }
1836
2589
  async function planOrApplyMigrations(params) {
1837
- const { cwd, aliases, registryUrl, lock, registryItem, dryRun } = params;
2590
+ const { cwd, aliases, registryUrl, lock, registryItem, dryRun, paths } = params;
1838
2591
  const name = registryItem.name;
1839
2592
  const lockEntry = lock.components[name];
1840
2593
  if (!lockEntry) {
@@ -1857,8 +2610,8 @@ async function planOrApplyMigrations(params) {
1857
2610
  };
1858
2611
  }
1859
2612
  const fileTargets = registryItem.files.map((file) => {
1860
- const target = path11.join(cwd, resolveTargetPath(file.path, aliases));
1861
- return { relPath: toPosixPath(path11.relative(cwd, target)), target };
2613
+ const target = path13.join(cwd, resolveTargetPath(file.path, aliases, paths));
2614
+ return { relPath: toPosixPath(path13.relative(cwd, target)), target };
1862
2615
  });
1863
2616
  const currentContents = /* @__PURE__ */ new Map();
1864
2617
  for (const ft of fileTargets) {
@@ -1924,12 +2677,12 @@ async function planOrApplyMigrations(params) {
1924
2677
  const written = [];
1925
2678
  try {
1926
2679
  for (const item of pending) {
1927
- await writeFile5(item.target, item.newContent, "utf8");
2680
+ await writeFile7(item.target, item.newContent, "utf8");
1928
2681
  written.push(item);
1929
2682
  }
1930
2683
  } catch (error) {
1931
2684
  for (const item of written) {
1932
- await writeFile5(item.target, item.originalContent, "utf8");
2685
+ await writeFile7(item.target, item.originalContent, "utf8");
1933
2686
  }
1934
2687
  return {
1935
2688
  name,
@@ -2066,6 +2819,7 @@ async function runApplyOrDryRun(components, options, dryRun) {
2066
2819
  const result = await planOrApplyMigrations({
2067
2820
  cwd,
2068
2821
  aliases: config.aliases,
2822
+ paths: targetPathOptions(config),
2069
2823
  registryUrl: config.registryUrl,
2070
2824
  lock,
2071
2825
  registryItem,
@@ -2223,7 +2977,7 @@ ${message}`);
2223
2977
  }
2224
2978
  const summary = intelligence.components.find((c) => c.name === component);
2225
2979
  if (!summary) {
2226
- const message = `\xAB${component}\xBB \u062F\u0631 \u062F\u0627\u062F\u0647\u0654 \u0647\u0648\u0634\u0645\u0646\u062F\u06CC\u0650 \u0641\u0639\u0644\u06CC \u0646\u06CC\u0633\u062A (\u0634\u0627\u06CC\u062F \u0627\u0632 \u0646\u0648\u0639\u0650 registry:lib \u0628\u0627\u0634\u062F\u060C \u0646\u0647 UI).`;
2980
+ const message = `\xAB${component}\xBB \u062F\u0631 \u062F\u0627\u062F\u0647\u0654 \u0647\u0648\u0634\u0645\u0646\u062F\u06CC\u0650 \u0641\u0639\u0644\u06CC \u0646\u06CC\u0633\u062A (\u0634\u0627\u06CC\u062F \u0627\u0632 \u0646\u0648\u0639\u0650 dig:lib \u0628\u0627\u0634\u062F\u060C \u0646\u0647 UI).`;
2227
2981
  if (options.json) {
2228
2982
  console.log(JSON.stringify({ component, status: "no-data", message }, null, 2));
2229
2983
  } else {
@@ -2540,8 +3294,8 @@ ${entry.migrationHint}`);
2540
3294
  }
2541
3295
 
2542
3296
  // src/lib/update.ts
2543
- import { mkdir as mkdir4, readFile as readFile8, rm as rm2, writeFile as writeFile6 } from "fs/promises";
2544
- import path12 from "path";
3297
+ import { mkdir as mkdir5, readFile as readFile10, rm as rm2, writeFile as writeFile8 } from "fs/promises";
3298
+ import path14 from "path";
2545
3299
  function safeFileIndexes(drift) {
2546
3300
  const indexes = [];
2547
3301
  drift.files.forEach((file, index) => {
@@ -2550,7 +3304,7 @@ function safeFileIndexes(drift) {
2550
3304
  });
2551
3305
  return indexes;
2552
3306
  }
2553
- async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun) {
3307
+ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun, paths) {
2554
3308
  const actions = new Array(drift.files.length).fill("skipped");
2555
3309
  const safeIndexes = safeFileIndexes(drift);
2556
3310
  if (dryRun) {
@@ -2561,7 +3315,7 @@ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun) {
2561
3315
  for (const index of safeIndexes) {
2562
3316
  const file = drift.files[index];
2563
3317
  const registryFile = registryItem.files[index];
2564
- const target = path12.join(cwd, resolveTargetPath(registryFile.path, aliases));
3318
+ const target = path14.join(cwd, resolveTargetPath(registryFile.path, aliases, paths));
2565
3319
  const isNewFile = file.status === "new_upstream";
2566
3320
  pending.push({
2567
3321
  index,
@@ -2569,14 +3323,14 @@ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun) {
2569
3323
  newContent: registryFile.content,
2570
3324
  newHash: registryFile.hash ?? hashContent(registryFile.content),
2571
3325
  isNewFile,
2572
- originalContent: isNewFile ? void 0 : await readFile8(target, "utf8")
3326
+ originalContent: isNewFile ? void 0 : await readFile10(target, "utf8")
2573
3327
  });
2574
3328
  }
2575
3329
  const written = [];
2576
3330
  try {
2577
3331
  for (const item of pending) {
2578
- if (item.isNewFile) await mkdir4(path12.dirname(item.target), { recursive: true });
2579
- await writeFile6(item.target, item.newContent, "utf8");
3332
+ if (item.isNewFile) await mkdir5(path14.dirname(item.target), { recursive: true });
3333
+ await writeFile8(item.target, item.newContent, "utf8");
2580
3334
  written.push(item);
2581
3335
  }
2582
3336
  } catch (error) {
@@ -2584,7 +3338,7 @@ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun) {
2584
3338
  if (item.isNewFile) {
2585
3339
  await rm2(item.target, { force: true });
2586
3340
  } else {
2587
- await writeFile6(item.target, item.originalContent, "utf8");
3341
+ await writeFile8(item.target, item.originalContent, "utf8");
2588
3342
  }
2589
3343
  }
2590
3344
  return { actions, writtenHashes: {}, rollbackError: error.message };
@@ -2609,7 +3363,7 @@ async function applySafeFiles(cwd, aliases, drift, registryItem, dryRun) {
2609
3363
  return { actions, writtenHashes, snapshotPath: snapshotResult.path };
2610
3364
  }
2611
3365
  async function updateComponent(params) {
2612
- const { cwd, aliases, registryUrl, lock, registryItem, dryRun = false } = params;
3366
+ const { cwd, aliases, registryUrl, lock, registryItem, dryRun = false, paths } = params;
2613
3367
  const name = registryItem.name;
2614
3368
  const lockEntry = lock.components[name];
2615
3369
  if (!lockEntry) {
@@ -2620,9 +3374,10 @@ async function updateComponent(params) {
2620
3374
  aliases,
2621
3375
  registryUrl,
2622
3376
  lockEntry,
2623
- registryItem
3377
+ registryItem,
3378
+ paths
2624
3379
  });
2625
- const { actions, writtenHashes, rollbackError, snapshotPath, snapshotError } = await applySafeFiles(cwd, aliases, drift, registryItem, dryRun);
3380
+ const { actions, writtenHashes, rollbackError, snapshotPath, snapshotError } = await applySafeFiles(cwd, aliases, drift, registryItem, dryRun, paths);
2626
3381
  if (rollbackError) {
2627
3382
  return {
2628
3383
  name,
@@ -2686,6 +3441,7 @@ async function updateCommand(components, options) {
2686
3441
  const result = await updateComponent({
2687
3442
  cwd,
2688
3443
  aliases: config.aliases,
3444
+ paths: targetPathOptions(config),
2689
3445
  registryUrl: config.registryUrl,
2690
3446
  lock,
2691
3447
  registryItem,
@@ -2817,6 +3573,7 @@ ${result.name}`);
2817
3573
  const details = await computeFileDiffDetails({
2818
3574
  cwd,
2819
3575
  aliases: config.aliases,
3576
+ paths: targetPathOptions(config),
2820
3577
  name: result.name,
2821
3578
  snapshotVersion,
2822
3579
  file,
@@ -2872,11 +3629,21 @@ ${result.name}`);
2872
3629
  }
2873
3630
  }
2874
3631
 
3632
+ // src/lib/version.ts
3633
+ var CLI_VERSION = "0.4.0";
3634
+
2875
3635
  // src/index.ts
2876
3636
  var program = new Command();
2877
- program.name("dig").description("CLI \u0631\u0633\u0645\u06CC \u062F\u06CC\u0632\u0627\u06CC\u0646 \u0633\u06CC\u0633\u062A\u0645 \u062F\u06CC\u06AF").version("0.1.1");
2878
- program.command("init").description("\u0631\u0627\u0647\u200C\u0627\u0646\u062F\u0627\u0632\u06CC \u062F\u06CC\u06AF \u062F\u0631 \u067E\u0631\u0648\u0698\u0647 (\u0633\u0627\u062E\u062A dig.json)").option("--yes", "\u067E\u0627\u0633\u062E \u067E\u06CC\u0634\u200C\u0641\u0631\u0636 \u0628\u0647 \u0647\u0645\u0647\u0654 \u0633\u0648\u0627\u0644\u0627\u062A", false).action(initCommand);
2879
- program.command("add").description("\u0627\u0641\u0632\u0648\u062F\u0646 \u06CC\u06A9 \u06CC\u0627 \u0686\u0646\u062F \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0627\u0632 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u062F\u06CC\u06AF").argument("<components...>", "\u0646\u0627\u0645 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627 (\u0645\u062B\u0644 button) \u06CC\u0627 \u0622\u062F\u0631\u0633 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC").option("--overwrite", "\u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0645\u0648\u062C\u0648\u062F", false).option("--no-install", "\u0641\u0642\u0637 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627 \u0631\u0627 \u0628\u0646\u0648\u06CC\u0633\u060C \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u200C\u0647\u0627 \u0631\u0627 \u0646\u0635\u0628 \u0646\u06A9\u0646").action(addCommand);
3637
+ program.name("dig").description("CLI \u0631\u0633\u0645\u06CC \u062F\u06CC\u0632\u0627\u06CC\u0646 \u0633\u06CC\u0633\u062A\u0645 \u062F\u06CC\u06AF").version(CLI_VERSION);
3638
+ program.command("init").description(
3639
+ '\u0631\u0627\u0647\u200C\u0627\u0646\u062F\u0627\u0632\u06CC \u062F\u06CC\u06AF \u062F\u0631 \u067E\u0631\u0648\u0698\u0647: dig.json\u060C \u062A\u0645 \u0648 \u062A\u0648\u06A9\u0646\u200C\u0647\u0627\u060C \u0648 dir="rtl"'
3640
+ ).option("--yes", "\u067E\u0627\u0633\u062E \u067E\u06CC\u0634\u200C\u0641\u0631\u0636 \u0628\u0647 \u0647\u0645\u0647\u0654 \u0633\u0648\u0627\u0644\u0627\u062A", false).option("--brand <hex>", "\u0633\u0627\u062E\u062A\u0646\u0650 \u0631\u0645\u067E\u0650 \u0628\u0631\u0646\u062F \u0627\u0632 \u06CC\u06A9 \u0631\u0646\u06AF (\u0631\u0648\u06CC Shade \u06F6\u06F0\u06F0 \u0645\u06CC\u200C\u0646\u0634\u06CC\u0646\u062F)\u060C \u0645\u062B\u0644 #2C4C5B").action(initCommand);
3641
+ var theme = program.command("theme").description("\u062A\u0645 \u0648 \u0628\u0631\u0646\u062F: \u062C\u062F\u0627 \u06A9\u0631\u062F\u0646\u0650 \u062A\u0645\u0650 \u062F\u06CC\u06AF \u0627\u0632 \u0628\u0631\u0646\u062F\u0650 \u067E\u0631\u0648\u0698\u0647");
3642
+ theme.command("split").description("\u067E\u0631\u0648\u0698\u0647\u0654 \u0642\u062F\u06CC\u0645\u06CC \u0631\u0627 \u0628\u0647 \u0633\u0627\u062E\u062A\u0627\u0631\u0650 \u062F\u0648\u0641\u0627\u06CC\u0644\u06CC \u0645\u06CC\u200C\u0628\u0631\u062F (dig-theme.css \u0645\u0627\u0644\u0650 \u062F\u06CC\u06AF\u060C brand.css \u0645\u0627\u0644\u0650 \u067E\u0631\u0648\u0698\u0647)").option("--dry-run", "\u0641\u0642\u0637 \u0646\u0634\u0627\u0646 \u0628\u062F\u0647 \u0686\u0647 \u0686\u06CC\u0632\u06CC \u0628\u0647 brand.css \u0645\u06CC\u200C\u0631\u0648\u062F\u060C \u0686\u06CC\u0632\u06CC \u0646\u0646\u0648\u06CC\u0633", false).action(themeSplitCommand);
3643
+ theme.command("brand").description("\u0631\u0645\u067E\u0650 \u0628\u0631\u0646\u062F \u0631\u0627 \u062F\u0631 brand.css \u0627\u0632 \u06CC\u06A9 \u0631\u0646\u06AF \u0645\u06CC\u200C\u0633\u0627\u0632\u062F\u061B \u0628\u0642\u06CC\u0647\u0654 \u0641\u0627\u06CC\u0644 \u062F\u0633\u062A \u0646\u0645\u06CC\u200C\u062E\u0648\u0631\u062F").argument("<hex>", "\u0631\u0646\u06AF\u0650 \u0627\u0635\u0644\u06CC\u0650 \u0628\u0631\u0646\u062F\u060C \u0645\u062B\u0644 #2C4C5B").action(themeBrandCommand);
3644
+ program.command("add").description("\u0627\u0641\u0632\u0648\u062F\u0646 \u06CC\u06A9 \u06CC\u0627 \u0686\u0646\u062F \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u0627\u0632 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u062F\u06CC\u06AF").argument("[components...]", "\u0646\u0627\u0645 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627 (\u0645\u062B\u0644 button) \u06CC\u0627 \u0622\u062F\u0631\u0633 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC\u061B \u0628\u062F\u0648\u0646 \u0622\u0631\u06AF\u0648\u0645\u0627\u0646 \u0641\u0647\u0631\u0633\u062A \u0631\u0627 \u0646\u0634\u0627\u0646 \u0645\u06CC\u200C\u062F\u0647\u062F").option("--overwrite", "\u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0645\u0648\u062C\u0648\u062F", false).option("-y, --yes", "\u0628\u062F\u0648\u0646 \u067E\u0631\u0633\u0634\u060C \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0645\u0648\u062C\u0648\u062F \u0631\u0627 \u0628\u0627\u0632\u0646\u0648\u06CC\u0633\u06CC \u06A9\u0646", false).option("--dry-run", "\u0641\u0642\u0637 \u0646\u0634\u0627\u0646 \u0628\u062F\u0647 \u0686\u0647 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC\u06CC \u0646\u0648\u0634\u062A\u0647 \u0645\u06CC\u200C\u0634\u0648\u062F\u060C \u0686\u06CC\u0632\u06CC \u0646\u0646\u0648\u06CC\u0633", false).option("--no-install", "\u0641\u0642\u0637 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627 \u0631\u0627 \u0628\u0646\u0648\u06CC\u0633\u060C \u0648\u0627\u0628\u0633\u062A\u06AF\u06CC\u200C\u0647\u0627 \u0631\u0627 \u0646\u0635\u0628 \u0646\u06A9\u0646").action(addCommand);
3645
+ program.command("list").description("\u0641\u0647\u0631\u0633\u062A \u0622\u06CC\u062A\u0645\u200C\u0647\u0627\u06CC \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u062F\u06CC\u06AF").option("--type <type>", "\u0641\u06CC\u0644\u062A\u0631 \u0646\u0648\u0639: ui\u060C lib \u06CC\u0627 theme").option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).action(listCommand);
3646
+ program.command("search").description("\u062C\u0633\u062A\u062C\u0648\u06CC \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A \u062F\u0631 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC\u060C \u0628\u0627 \u0646\u0627\u0645 \u0641\u0627\u0631\u0633\u06CC \u06CC\u0627 \u0627\u0646\u06AF\u0644\u06CC\u0633\u06CC").argument("<query>", "\u0639\u0628\u0627\u0631\u062A \u062C\u0633\u062A\u062C\u0648").option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).action(searchCommand);
2880
3647
  program.command("diff").description(
2881
3648
  "\u0645\u0642\u0627\u06CC\u0633\u0647\u0654 \u0641\u0642\u0637\u200C\u062E\u0648\u0627\u0646\u062F\u0646\u06CC\u0650 \u0646\u0633\u062E\u0647\u0654 \u0646\u0635\u0628\u200C\u0634\u062F\u0647 \u0628\u0627 \u0631\u062C\u06CC\u0633\u062A\u0631\u06CC \u0648 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0645\u062D\u0644\u06CC (\u0628\u062F\u0648\u0646 \u062A\u063A\u06CC\u06CC\u0631 \u0641\u0627\u06CC\u0644)"
2882
3649
  ).argument("<components...>", "\u0646\u0627\u0645 \u06A9\u0627\u0645\u067E\u0648\u0646\u0646\u062A\u200C\u0647\u0627 (\u0645\u062B\u0644 button)").option("--json", "\u062E\u0631\u0648\u062C\u06CC JSON \u0628\u0631\u0627\u06CC \u0627\u0633\u06A9\u0631\u06CC\u067E\u062A/\u0627\u0628\u0632\u0627\u0631", false).option(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "digdesign",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "CLI رسمی برای نصب کامپوننت‌های دیزاین سیستم دیگ",
5
5
  "type": "module",
6
6
  "license": "MIT",