@axis-ds/cli 1.6.1 → 1.7.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.
package/dist/index.js CHANGED
@@ -1,10 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  MonetClient
4
- } from "./chunk-ZVVACWEW.js";
4
+ } from "./chunk-LVMDBOXD.js";
5
5
  import {
6
6
  V0Client
7
- } from "./chunk-NNFSJISD.js";
7
+ } from "./chunk-2PNCUPCG.js";
8
+ import {
9
+ NonInteractiveError,
10
+ ask,
11
+ failNonInteractive,
12
+ isInteractive,
13
+ noteDefault
14
+ } from "./chunk-HFMMK6R4.js";
8
15
  import {
9
16
  registerSyncCommand
10
17
  } from "./chunk-7CDXEBEE.js";
@@ -1106,17 +1113,59 @@ function toSearchItem(c) {
1106
1113
  url: `/library/${c.category}/${c.slug}`
1107
1114
  };
1108
1115
  }
1116
+ var sourceTypeOf = (item) => typeof item.source === "string" ? item.source : item.source.type;
1117
+ function resolveRouteSlugs(items) {
1118
+ const groups = /* @__PURE__ */ new Map();
1119
+ for (const item of items) {
1120
+ const key = `${item.category}/${item.slug}`;
1121
+ if (!groups.has(key)) groups.set(key, []);
1122
+ groups.get(key).push(item);
1123
+ }
1124
+ const result = /* @__PURE__ */ new Map();
1125
+ for (const [key, group] of groups) {
1126
+ if (group.length === 1) {
1127
+ result.set(group[0].id, group[0].slug);
1128
+ continue;
1129
+ }
1130
+ const axis = group.filter((i) => sourceTypeOf(i) === "axis");
1131
+ if (axis.length > 1) {
1132
+ throw new Error(`\uC0C1\uC138 \uC8FC\uC18C /library/${key} \uB97C AXIS \uD56D\uBAA9 ${axis.map((i) => i.id).join(", ")} \uAC00 \uD568\uAED8 \uC501\uB2C8\uB2E4`);
1133
+ }
1134
+ const keeper = axis[0] || group[0];
1135
+ for (const item of group) {
1136
+ result.set(item.id, item === keeper ? item.slug : `${sourceTypeOf(item)}-${item.slug}`);
1137
+ }
1138
+ }
1139
+ const seen = /* @__PURE__ */ new Map();
1140
+ for (const item of items) {
1141
+ const key = `${item.category}/${result.get(item.id)}`;
1142
+ const other = seen.get(key);
1143
+ if (other !== void 0 && other !== item.id) {
1144
+ throw new Error(`slug \uB97C \uBC14\uAFBC \uB4A4\uC5D0\uB3C4 \uC0C1\uC138 \uC8FC\uC18C /library/${key} \uAC00 ${other}, ${item.id} \uC5D0\uC11C \uACB9\uCE69\uB2C8\uB2E4`);
1145
+ }
1146
+ seen.set(key, item.id);
1147
+ }
1148
+ return result;
1149
+ }
1150
+ function withRouteSlugs(items) {
1151
+ const slugs = resolveRouteSlugs(items);
1152
+ return items.map((item) => slugs.get(item.id) === item.slug ? item : { ...item, slug: slugs.get(item.id) });
1153
+ }
1154
+ function componentFileOf(item) {
1155
+ return `components/${item.id}.json`;
1156
+ }
1109
1157
  var Publisher = class {
1110
1158
  defaultOutputDir = "apps/web/public/library";
1111
1159
  /**
1112
1160
  * 라이브러리 인덱스를 사이트에 배치
1113
1161
  */
1114
- async publish(index, options = {}) {
1162
+ async publish(input, options = {}) {
1115
1163
  const outputDir = options.outputDir || this.defaultOutputDir;
1116
1164
  const files = [];
1117
1165
  const errors = [];
1118
1166
  const spinner = ora("\uB77C\uC774\uBE0C\uB7EC\uB9AC \uBC30\uCE58 \uC911...").start();
1119
1167
  try {
1168
+ const index = { ...input, components: withRouteSlugs(input.components) };
1120
1169
  await fs3.ensureDir(outputDir);
1121
1170
  const indexPath = path3.join(outputDir, "index.json");
1122
1171
  await this.writeJson(indexPath, this.createPublicIndex(index), options.minify);
@@ -1143,7 +1192,7 @@ var Publisher = class {
1143
1192
  const componentsDir = path3.join(outputDir, "components");
1144
1193
  await fs3.ensureDir(componentsDir);
1145
1194
  for (const component of index.components) {
1146
- const componentPath = path3.join(componentsDir, `${component.slug}.json`);
1195
+ const componentPath = path3.join(outputDir, componentFileOf(component));
1147
1196
  await this.writeJson(componentPath, this.createPublicComponent(component), options.minify);
1148
1197
  files.push(componentPath);
1149
1198
  }
@@ -1400,6 +1449,25 @@ async function refreshAxisLibrary(options) {
1400
1449
  }
1401
1450
  for (const f of existingFiles.values()) if (f.data.source?.type === AXIS) oldAxisIds.add(f.data.id);
1402
1451
  const removed = [...oldAxisIds].filter((id) => !axisIds.has(id)).sort();
1452
+ const routeSlugs = resolveRouteSlugs([...extOrder.map((id) => extCategory.get(id)), ...axisComponents]);
1453
+ const renamedSlugs = {};
1454
+ const renamedFrom = {};
1455
+ for (const id of extOrder) {
1456
+ const slug = routeSlugs.get(id);
1457
+ const full = extCategory.get(id);
1458
+ if (slug === full.slug) continue;
1459
+ renamedSlugs[id] = slug;
1460
+ renamedFrom[id] = full.slug;
1461
+ extCategory.set(id, { ...full, slug });
1462
+ extIndex.set(id, { ...extIndex.get(id), slug });
1463
+ const s = extSearch.get(id);
1464
+ extSearch.set(id, { ...s, slug, url: `/library/${s.category}/${slug}` });
1465
+ }
1466
+ for (const c of axisComponents) {
1467
+ const slug = routeSlugs.get(c.id);
1468
+ if (slug === c.slug) continue;
1469
+ throw new Error(`AXIS \uD56D\uBAA9 ${c.id} \uC758 slug \uAC00 ${slug} \uB85C \uBC14\uB00C\uC5B4\uC57C \uD569\uB2C8\uB2E4. \uC218\uC9D1 \uACB0\uACFC\uB97C \uD655\uC778\uD558\uC138\uC694`);
1470
+ }
1403
1471
  const indexEntries = [
1404
1472
  ...extOrder.map((id) => extIndex.get(id)),
1405
1473
  ...axisComponents.map((c) => toIndexEntry(c))
@@ -1457,24 +1525,18 @@ async function refreshAxisLibrary(options) {
1457
1525
  })
1458
1526
  );
1459
1527
  }
1460
- const finalSlugs = /* @__PURE__ */ new Set();
1461
- const axisBySlug = new Map(axisComponents.map((c) => [c.slug, c]));
1462
- for (const c of allPublic) finalSlugs.add(c.slug);
1463
- const shadowedAxis = [];
1464
- for (const slug of [...finalSlugs].sort()) {
1465
- const axis = axisBySlug.get(slug);
1466
- const existing = existingFiles.get(slug);
1467
- const extForSlug = extOrder.map((id) => extCategory.get(id)).filter((c) => c.slug === slug);
1468
- const existingIsExternal = !!existing && extForSlug.some((c) => c.id === existing.data.id);
1469
- if (existingIsExternal) {
1470
- if (axis) shadowedAxis.push(axis.id);
1471
- continue;
1472
- }
1473
- if (axis) {
1474
- outputs.set(`components/${slug}.json`, serialize(axis));
1528
+ const finalFiles = /* @__PURE__ */ new Set();
1529
+ for (const c of allPublic) {
1530
+ const rel3 = componentFileOf(c);
1531
+ finalFiles.add(rel3);
1532
+ if (c.source.type === AXIS) {
1533
+ outputs.set(rel3, serialize(c));
1475
1534
  continue;
1476
1535
  }
1477
- outputs.set(`components/${slug}.json`, serialize(extForSlug[0]));
1536
+ const same = (f) => f.data?.id === c.id && isDeepStrictEqual(f.data, c);
1537
+ const atKey = existingFiles.get(c.id);
1538
+ const kept = atKey && same(atKey) ? atKey : [...existingFiles.values()].find(same);
1539
+ outputs.set(rel3, kept ? kept.raw : serialize(c));
1478
1540
  }
1479
1541
  const writtenFiles = [];
1480
1542
  for (const [rel3, content] of outputs) {
@@ -1485,13 +1547,13 @@ async function refreshAxisLibrary(options) {
1485
1547
  if (!options.dryRun) await fs4.writeFile(file, content, "utf-8");
1486
1548
  }
1487
1549
  const removedFiles = [];
1488
- for (const slug of existingFiles.keys()) {
1489
- if (finalSlugs.has(slug)) continue;
1490
- const rel3 = `components/${slug}.json`;
1550
+ for (const key of existingFiles.keys()) {
1551
+ const rel3 = `components/${key}.json`;
1552
+ if (finalFiles.has(rel3)) continue;
1491
1553
  removedFiles.push(rel3);
1492
1554
  if (!options.dryRun) await fs4.remove(path4.join(libraryDir, rel3));
1493
1555
  }
1494
- return { added, changed, unchanged, removed, shadowedAxis, writtenFiles, removedFiles, stats };
1556
+ return { added, changed, unchanged, removed, renamedSlugs, renamedFrom, writtenFiles, removedFiles, stats };
1495
1557
  }
1496
1558
  function registerRefreshAxisCommand(libraryCmd2) {
1497
1559
  libraryCmd2.command("refresh-axis").description("AXIS \uD56D\uBAA9\uB9CC \uC800\uC7A5\uC18C \uC18C\uC2A4\uC5D0\uC11C \uB2E4\uC2DC \uB9CC\uB4E6 (\uC678\uBD80 \uC18C\uC2A4 \uD56D\uBAA9\uC740 \uADF8\uB300\uB85C, \uB124\uD2B8\uC6CC\uD06C \uC5C6\uC74C)").option("--root <dir>", "\uC800\uC7A5\uC18C \uB8E8\uD2B8", process.cwd()).option("-o, --output <dir>", "library \uB514\uB809\uD130\uB9AC (\uB8E8\uD2B8 \uAE30\uC900)", "apps/web/public/library").option("--updated-at <iso>", "\uBC14\uB010 \uD56D\uBAA9\uC5D0 \uAE30\uB85D\uD560 \uC2DC\uAC01 (\uAE30\uBCF8: \uC624\uB298 KST \uB0A0\uC9DC\uC758 00:00:00.000Z)").option("--dry-run", "\uD30C\uC77C\uC744 \uC4F0\uC9C0 \uC54A\uACE0 \uBC14\uB014 \uB0B4\uC6A9\uB9CC \uCD9C\uB825").action(async (opts) => {
@@ -1511,9 +1573,12 @@ ${head}AXIS library \uC7AC\uC0DD\uC131`));
1511
1573
  for (const id of result.added) console.log(chalk2.green(` + ${id}`));
1512
1574
  for (const id of result.changed) console.log(chalk2.yellow(` ~ ${id}`));
1513
1575
  for (const id of result.removed) console.log(chalk2.red(` - ${id}`));
1514
- if (result.shadowedAxis.length > 0) {
1576
+ const renamed = Object.entries(result.renamedSlugs);
1577
+ if (renamed.length > 0) {
1515
1578
  console.log(
1516
- chalk2.yellow(` slug \uAC00 \uC678\uBD80 \uD56D\uBAA9 \uD30C\uC77C\uACFC \uACB9\uCCD0 \uD30C\uC77C\uC744 \uC4F0\uC9C0 \uC54A\uC74C(E4): ${result.shadowedAxis.join(", ")}`)
1579
+ chalk2.yellow(
1580
+ ` \uC0C1\uC138 \uC8FC\uC18C\uAC00 \uACB9\uCCD0 slug \uB97C \uBC14\uAFC8(WI-0060): ${renamed.map(([id, slug]) => `${id}: ${result.renamedFrom[id] ?? id} \u2192 ${slug}`).join(", ")}`
1581
+ )
1517
1582
  );
1518
1583
  }
1519
1584
  console.log(` \uD30C\uC77C \uC4F0\uAE30 ${result.writtenFiles.length}\uAC1C \xB7 \uC0AD\uC81C ${result.removedFiles.length}\uAC1C`);
@@ -1748,7 +1813,6 @@ import fs8 from "fs-extra";
1748
1813
  import path9 from "path";
1749
1814
  import chalk5 from "chalk";
1750
1815
  import ora3 from "ora";
1751
- import prompts from "prompts";
1752
1816
 
1753
1817
  // src/template/post-install.ts
1754
1818
  import fs6 from "fs-extra";
@@ -2067,22 +2131,17 @@ function majorFromVersion(version) {
2067
2131
  }
2068
2132
  function detectTailwind(baseDirInput, pkg) {
2069
2133
  const baseDir = resolve(baseDirInput);
2070
- const installedFile = join(baseDir, "node_modules", "tailwindcss", "package.json");
2071
2134
  const declared = pkg ? hasDependency2(pkg, "tailwindcss") : void 0;
2072
2135
  const bounds = declared !== void 0 ? tailwindMajorBounds(declared) : void 0;
2073
2136
  const declaredMajor = bounds?.lower;
2074
- try {
2075
- const installed = JSON.parse(readFileSync2(installedFile, "utf8"));
2076
- const version = isPlainObject2(installed) ? installed.version : void 0;
2077
- const major = majorFromVersion(version);
2078
- if (major !== void 0) {
2079
- const found = { major, source: "installed", value: String(version) };
2080
- if (declared !== void 0 && bounds !== void 0 && !majorWithin(major, bounds)) {
2081
- found.mismatch = { declared, declaredMajor: bounds.lower };
2082
- }
2083
- return found;
2137
+ const version = isPnpProject(baseDir) ? void 0 : findInstalledPackage(baseDir, "tailwindcss")?.version;
2138
+ const major = majorFromVersion(version);
2139
+ if (version !== void 0 && major !== void 0) {
2140
+ const found = { major, source: "installed", value: version };
2141
+ if (declared !== void 0 && bounds !== void 0 && !majorWithin(major, bounds)) {
2142
+ found.mismatch = { declared, declaredMajor: bounds.lower };
2084
2143
  }
2085
- } catch {
2144
+ return found;
2086
2145
  }
2087
2146
  if (declared !== void 0) {
2088
2147
  if (declaredMajor !== void 0) return { major: declaredMajor, source: "declared", value: declared };
@@ -2225,19 +2284,9 @@ function rangeLowerAtLeast(range, min) {
2225
2284
  if (!lower || !target) return void 0;
2226
2285
  return compareLower(lower, target) >= 0;
2227
2286
  }
2228
- function exactVersion(version) {
2229
- if (typeof version !== "string") return void 0;
2230
- const v = version.trim();
2231
- if (!/^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(v) || /^v?0\.0\.0-/.test(v)) return void 0;
2232
- return v;
2233
- }
2234
- function installedVersion(baseDir, name) {
2235
- try {
2236
- const parsed = JSON.parse(readFileSync2(join(baseDir, "node_modules", ...name.split("/"), "package.json"), "utf8"));
2237
- return isPlainObject2(parsed) ? exactVersion(parsed.version) : void 0;
2238
- } catch {
2239
- return void 0;
2240
- }
2287
+ function installedAxisVersion(baseDir, name) {
2288
+ if (isPnpProject(baseDir)) return void 0;
2289
+ return findInstalledPackage(baseDir, name)?.version;
2241
2290
  }
2242
2291
  function axisPackagesBelowTailwind4(pkg, baseDirInput) {
2243
2292
  if (!pkg) return [];
@@ -2247,7 +2296,7 @@ function axisPackagesBelowTailwind4(pkg, baseDirInput) {
2247
2296
  for (const name of TAILWIND4_AXIS_PACKAGES) {
2248
2297
  const range = hasDependency2(pkg, name);
2249
2298
  if (range === void 0 || range.trim().startsWith("workspace:")) continue;
2250
- const installed = baseDir !== void 0 ? installedVersion(baseDir, name) : void 0;
2299
+ const installed = baseDir !== void 0 ? installedAxisVersion(baseDir, name) : void 0;
2251
2300
  const installedLower = installed !== void 0 ? lowerOf(installed) : void 0;
2252
2301
  if (installed !== void 0 && installedLower !== void 0) {
2253
2302
  if (compareLower(installedLower, min) < 0) out.push({ name, range, status: "below", source: "installed", installed });
@@ -2834,12 +2883,23 @@ async function applyTemplate(slug, options) {
2834
2883
  for (const c of conflicts) {
2835
2884
  console.log(chalk5.gray(` - ${c}`));
2836
2885
  }
2837
- const { proceed } = await prompts({
2838
- type: "confirm",
2839
- name: "proceed",
2840
- message: "\uCDA9\uB3CC \uD30C\uC77C\uC744 \uB36E\uC5B4\uC4F0\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
2841
- initial: false
2842
- });
2886
+ const interactive = options.interactive ?? isInteractive();
2887
+ if (!interactive) {
2888
+ failNonInteractive("\uCDA9\uB3CC \uD30C\uC77C\uC744 \uB36E\uC5B4\uC4F8\uC9C0 \uBB3C\uC744 \uC218 \uC5C6\uC5B4 \uC544\uBB34\uAC83\uB3C4 \uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.", [
2889
+ `\uB36E\uC5B4\uC4F0\uB824\uBA74: axis template apply ${slug} -y`,
2890
+ `\uBBF8\uB9AC\uBCF4\uAE30: axis template apply ${slug} --dry-run`
2891
+ ]);
2892
+ return;
2893
+ }
2894
+ const { proceed } = await ask(
2895
+ {
2896
+ type: "confirm",
2897
+ name: "proceed",
2898
+ message: "\uCDA9\uB3CC \uD30C\uC77C\uC744 \uB36E\uC5B4\uC4F0\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
2899
+ initial: false
2900
+ },
2901
+ interactive
2902
+ );
2843
2903
  if (!proceed) {
2844
2904
  console.log(chalk5.gray("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
2845
2905
  return;
@@ -2891,20 +2951,31 @@ async function applyTemplate(slug, options) {
2891
2951
  '${detail.name}' \uD15C\uD50C\uB9BF \uC801\uC6A9 \uC644\uB8CC!`));
2892
2952
  }
2893
2953
  async function initTemplate(slug, options) {
2954
+ const interactive = options.interactive ?? isInteractive();
2894
2955
  let selectedSlug = slug;
2895
2956
  if (!selectedSlug) {
2957
+ if (!interactive) {
2958
+ failNonInteractive("\uD15C\uD50C\uB9BF\uC744 \uACE0\uB97C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD15C\uD50C\uB9BF \uC774\uB984\uC744 \uC8FC\uC138\uC694.", [
2959
+ "\uC0AC\uC6A9\uBC95: axis template init <name> [--dir <path>] [-y]",
2960
+ "\uBAA9\uB85D: axis template list"
2961
+ ]);
2962
+ return;
2963
+ }
2896
2964
  const { fetchTemplateIndex: fetchTemplateIndex2 } = await import("./registry-client-6WSGKPF2.js");
2897
2965
  const index = await fetchTemplateIndex2();
2898
- const { choice } = await prompts({
2899
- type: "select",
2900
- name: "choice",
2901
- message: "\uC0AC\uC6A9\uD560 \uD15C\uD50C\uB9BF\uC744 \uC120\uD0DD\uD558\uC138\uC694:",
2902
- choices: index.templates.map((t) => ({
2903
- title: `${t.name} (${t.category})`,
2904
- description: t.description,
2905
- value: t.slug
2906
- }))
2907
- });
2966
+ const { choice } = await ask(
2967
+ {
2968
+ type: "select",
2969
+ name: "choice",
2970
+ message: "\uC0AC\uC6A9\uD560 \uD15C\uD50C\uB9BF\uC744 \uC120\uD0DD\uD558\uC138\uC694:",
2971
+ choices: index.templates.map((t) => ({
2972
+ title: `${t.name} (${t.category})`,
2973
+ description: t.description,
2974
+ value: t.slug
2975
+ }))
2976
+ },
2977
+ interactive
2978
+ );
2908
2979
  if (!choice) {
2909
2980
  console.log(chalk5.gray("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
2910
2981
  return;
@@ -2912,13 +2983,22 @@ async function initTemplate(slug, options) {
2912
2983
  selectedSlug = choice;
2913
2984
  }
2914
2985
  let targetDir = options.dir;
2986
+ if (!targetDir && (!interactive || options.yes)) {
2987
+ targetDir = selectedSlug;
2988
+ const msg = `\uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC\uB97C \uAE30\uBCF8\uAC12 '${targetDir}' \uB85C \uC815\uD588\uC2B5\uB2C8\uB2E4(\uBC14\uAFB8\uB824\uBA74 --dir <path>).`;
2989
+ if (interactive) console.log(chalk5.gray(`-y \uB77C ${msg}`));
2990
+ else noteDefault(msg);
2991
+ }
2915
2992
  if (!targetDir) {
2916
- const { dir } = await prompts({
2917
- type: "text",
2918
- name: "dir",
2919
- message: "\uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC:",
2920
- initial: selectedSlug
2921
- });
2993
+ const { dir } = await ask(
2994
+ {
2995
+ type: "text",
2996
+ name: "dir",
2997
+ message: "\uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC:",
2998
+ initial: selectedSlug
2999
+ },
3000
+ interactive
3001
+ );
2922
3002
  if (!dir) {
2923
3003
  console.log(chalk5.gray("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
2924
3004
  return;
@@ -2929,13 +3009,26 @@ async function initTemplate(slug, options) {
2929
3009
  if (await fs8.pathExists(resolved)) {
2930
3010
  const entries = await fs8.readdir(resolved);
2931
3011
  if (entries.length > 0) {
2932
- const { proceed } = await prompts({
2933
- type: "confirm",
2934
- name: "proceed",
2935
- message: `'${targetDir}'\uC5D0 \uC774\uBBF8 \uD30C\uC77C\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uACC4\uC18D\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?`,
2936
- initial: false
2937
- });
2938
- if (!proceed) return;
3012
+ if (options.yes) {
3013
+ console.log(chalk5.yellow(`! '${targetDir}'\uC5D0 \uC774\uBBF8 \uD30C\uC77C\uC774 \uC788\uC9C0\uB9CC -y \uB77C \uC9C4\uD589\uD569\uB2C8\uB2E4(\uD15C\uD50C\uB9BF\uACFC \uAC19\uC740 \uACBD\uB85C\uC758 \uD30C\uC77C\uC740 \uB36E\uC5B4\uC500).`));
3014
+ } else if (!interactive) {
3015
+ failNonInteractive(`'${targetDir}'\uC5D0 \uC774\uBBF8 \uD30C\uC77C\uC774 \uC788\uC5B4 \uACC4\uC18D\uD560\uC9C0 \uBB3C\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC544\uBB34\uAC83\uB3C4 \uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`, [
3016
+ `\uBE48 \uB514\uB809\uD130\uB9AC\uB97C \uC8FC\uC138\uC694: axis template init ${selectedSlug} --dir <path>`,
3017
+ `\uADF8\uB300\uB85C \uC9C4\uD589\uD558\uB824\uBA74(\uAC19\uC740 \uACBD\uB85C\uC758 \uD30C\uC77C\uC740 \uB36E\uC5B4\uC500): axis template init ${selectedSlug} --dir ${targetDir} -y`
3018
+ ]);
3019
+ return;
3020
+ } else {
3021
+ const { proceed } = await ask(
3022
+ {
3023
+ type: "confirm",
3024
+ name: "proceed",
3025
+ message: `'${targetDir}'\uC5D0 \uC774\uBBF8 \uD30C\uC77C\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uACC4\uC18D\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?`,
3026
+ initial: false
3027
+ },
3028
+ interactive
3029
+ );
3030
+ if (!proceed) return;
3031
+ }
2939
3032
  }
2940
3033
  } else {
2941
3034
  await fs8.ensureDir(resolved);
@@ -3574,7 +3667,8 @@ function brandActivationResult(check) {
3574
3667
  }
3575
3668
 
3576
3669
  // src/template/index.ts
3577
- function registerTemplateCommand(program2) {
3670
+ function registerTemplateCommand(program2, deps = {}) {
3671
+ const interactive = () => (deps.isInteractive ?? isInteractive)();
3578
3672
  const tplCmd = program2.command("template").alias("tpl").description("\uD504\uB85C\uC81D\uD2B8 \uD15C\uD50C\uB9BF \uAD00\uB9AC");
3579
3673
  tplCmd.command("list").alias("ls").description("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uD15C\uD50C\uB9BF \uBAA9\uB85D").option("--category <type>", "\uCE74\uD14C\uACE0\uB9AC \uD544\uD130").action(async (options) => {
3580
3674
  const spinner = ora5("\uD15C\uD50C\uB9BF \uBAA9\uB85D \uAC00\uC838\uC624\uB294 \uC911...").start();
@@ -3640,12 +3734,12 @@ function registerTemplateCommand(program2) {
3640
3734
  for (const f of detail.files) {
3641
3735
  console.log(` ${chalk8.cyan(f.path)} ${chalk8.gray(`(${f.type})`)}`);
3642
3736
  }
3643
- const deps = Object.keys(detail.dependencies || {});
3737
+ const deps2 = Object.keys(detail.dependencies || {});
3644
3738
  const devDeps = Object.keys(detail.devDependencies || {});
3645
- if (deps.length > 0 || devDeps.length > 0) {
3739
+ if (deps2.length > 0 || devDeps.length > 0) {
3646
3740
  console.log(chalk8.bold("\n \uC758\uC874\uC131:"));
3647
- if (deps.length > 0) {
3648
- console.log(` dependencies: ${deps.join(", ")}`);
3741
+ if (deps2.length > 0) {
3742
+ console.log(` dependencies: ${deps2.join(", ")}`);
3649
3743
  }
3650
3744
  if (devDeps.length > 0) {
3651
3745
  console.log(` devDependencies: ${devDeps.join(", ")}`);
@@ -3660,10 +3754,10 @@ function registerTemplateCommand(program2) {
3660
3754
  }
3661
3755
  });
3662
3756
  tplCmd.command("apply <name>").description("\uD15C\uD50C\uB9BF\uC744 \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC801\uC6A9").option("-y, --yes", "\uD655\uC778 \uC5C6\uC774 \uB36E\uC5B4\uC4F0\uAE30").option("--dry-run", "\uC2E4\uC81C \uD30C\uC77C \uC0DD\uC131 \uC5C6\uC774 \uBBF8\uB9AC\uBCF4\uAE30").option("--skip-deps", "\uC758\uC874\uC131 \uC548\uB0B4 \uC0DD\uB7B5").option("-d, --dir <path>", "\uB300\uC0C1 \uB514\uB809\uD1A0\uB9AC").action(async (name, options) => {
3663
- await applyTemplate(name, options);
3757
+ await applyTemplate(name, { ...options, interactive: interactive() });
3664
3758
  });
3665
- tplCmd.command("init [name]").description("\uC0C8 \uD504\uB85C\uC81D\uD2B8\uB97C \uD15C\uD50C\uB9BF\uC73C\uB85C \uCD08\uAE30\uD654").option("-d, --dir <path>", "\uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC").action(async (name, options) => {
3666
- await initTemplate(name, options);
3759
+ tplCmd.command("init [name]").description("\uC0C8 \uD504\uB85C\uC81D\uD2B8\uB97C \uD15C\uD50C\uB9BF\uC73C\uB85C \uCD08\uAE30\uD654").option("-d, --dir <path>", "\uD504\uB85C\uC81D\uD2B8 \uB514\uB809\uD1A0\uB9AC(\uC5C6\uC73C\uBA74 \uD15C\uD50C\uB9BF \uC774\uB984)").option("-y, --yes", "\uC9C8\uBB38\uC744 \uAE30\uBCF8\uAC12\uC73C\uB85C \uB2F5\uD558\uACE0, \uBE44\uC5B4 \uC788\uC9C0 \uC54A\uC740 \uB514\uB809\uD1A0\uB9AC\uB3C4 \uC9C4\uD589(\uAC19\uC740 \uACBD\uB85C\uC758 \uD30C\uC77C\uC740 \uB36E\uC5B4\uC500)").action(async (name, options) => {
3760
+ await initTemplate(name, { ...options, interactive: interactive() });
3667
3761
  });
3668
3762
  tplCmd.command("diff <name>").description("\uB85C\uCEEC \uD504\uB85C\uC81D\uD2B8\uC640 \uD15C\uD50C\uB9BF\uC758 \uCC28\uC774 \uBE44\uAD50").option("--verbose", "\uB85C\uCEEC \uC804\uC6A9 \uD30C\uC77C\uB3C4 \uD45C\uC2DC").option("-d, --dir <path>", "\uB300\uC0C1 \uB514\uB809\uD1A0\uB9AC").action(async (name, options) => {
3669
3763
  await diffTemplate(name, options);
@@ -4370,7 +4464,6 @@ function registerThemeCommand(program2) {
4370
4464
 
4371
4465
  // src/init/index.ts
4372
4466
  import chalk11 from "chalk";
4373
- import prompts2 from "prompts";
4374
4467
 
4375
4468
  // ../axis-registry-core/src/registry/schema.ts
4376
4469
  import { createHash } from "crypto";
@@ -5228,7 +5321,7 @@ function planUtilsWrite(baseDirInput, utilsPath) {
5228
5321
  }
5229
5322
  return { action: "overwrite" };
5230
5323
  }
5231
- async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
5324
+ async function runInit(baseDirInput, answers, registryUrl, log = console.log, options = {}) {
5232
5325
  const baseDir = path15.resolve(baseDirInput);
5233
5326
  const current = await existingConfig(baseDir);
5234
5327
  const existingRegistry = nonEmptyString(current?.registry);
@@ -5275,6 +5368,12 @@ async function runInit(baseDirInput, answers, registryUrl, log = console.log) {
5275
5368
  const utilsPath = path15.resolve(baseDir, answers.componentsDir, "utils.ts");
5276
5369
  const utilsPlan = planUtilsWrite(baseDir, utilsPath);
5277
5370
  if (utilsPlan.action === "reject") rejects.push(utilsPlan.reason);
5371
+ if (utilsPlan.action === "overwrite" && options.allowOverwrite === false) {
5372
+ const shown = path15.relative(baseDir, utilsPath).split(path15.sep).join("/") || utilsPath;
5373
+ rejects.push(
5374
+ `\uBE44\uB300\uD654\uD615 \uC2E4\uD589\uC774\uB77C \uB0B4\uC6A9\uC774 \uB2E4\uB978 \uAE30\uC874 ${shown} \uB97C \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uB36E\uC5B4\uC368\uB3C4 \uB418\uBA74 axis init -y \uB85C \uB2E4\uC2DC \uC2E4\uD589\uD558\uAC70\uB098 \uB300\uD654\uD615 \uD130\uBBF8\uB110\uC5D0\uC11C \uC2E4\uD589\uD558\uC138\uC694`
5375
+ );
5376
+ }
5278
5377
  if (rejects.length > 0) {
5279
5378
  log(chalk10.red("\u2717 \uC4F8 \uC218 \uC5C6\uB294 \uD30C\uC77C\uC774 \uC788\uC5B4 \uC544\uBB34\uAC83\uB3C4 \uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4(axis.config.json \xB7 utils.ts \xB7 package.json \uADF8\uB300\uB85C)."));
5280
5379
  for (const r of rejects) log(chalk10.red(` - ${r}`));
@@ -5379,14 +5478,20 @@ ${head}`));
5379
5478
  }
5380
5479
 
5381
5480
  // src/init/index.ts
5382
- function registerInitCommand(program2, registryUrl = DEFAULT_REGISTRY_URL) {
5481
+ function registerInitCommand(program2, registryUrl = DEFAULT_REGISTRY_URL, deps = {}) {
5383
5482
  program2.command("init").description("\uD504\uB85C\uC81D\uD2B8\uC5D0 AXIS Design System \uCD08\uAE30\uD654").option("-y, --yes", "\uAE30\uBCF8\uAC12\uC73C\uB85C \uC790\uB3D9 \uC124\uC815").action(async (options) => {
5384
5483
  console.log(chalk11.blue("\n\u{1F3A8} AXIS Design System \uCD08\uAE30\uD654\n"));
5385
5484
  const baseDir = process.cwd();
5386
5485
  const tailwind4 = isTailwind4(planUtilsDependencies(baseDir).tailwind);
5387
5486
  const resolved = await resolveInitAnswers(baseDir, tailwind4);
5388
5487
  for (const note of resolved.notes) console.log(note.startsWith("!") ? chalk11.yellow(note) : chalk11.gray(note));
5389
- const config = options.yes ? { ...resolved.answers } : await prompts2([
5488
+ const interactive = (deps.isInteractive ?? isInteractive)();
5489
+ if (!options.yes && !interactive) {
5490
+ const a = resolved.answers;
5491
+ const values = [`componentsDir=${a.componentsDir}`, ...tailwind4 ? [] : [`tailwindConfig=${a.tailwindConfig}`], `globalCss=${a.globalCss}`];
5492
+ noteDefault(`\uAE30\uBCF8\uAC12\uC73C\uB85C \uC9C4\uD589\uD569\uB2C8\uB2E4(-y \uC640 \uAC19\uC74C): ${values.join(", ")}`);
5493
+ }
5494
+ const config = options.yes || !interactive ? { ...resolved.answers } : await ask([
5390
5495
  {
5391
5496
  type: "text",
5392
5497
  name: "componentsDir",
@@ -5405,14 +5510,13 @@ function registerInitCommand(program2, registryUrl = DEFAULT_REGISTRY_URL) {
5405
5510
  message: "\uAE00\uB85C\uBC8C CSS \uD30C\uC77C:",
5406
5511
  initial: resolved.answers.globalCss
5407
5512
  }
5408
- ]);
5409
- await runInit(baseDir, config, registryUrl);
5513
+ ], interactive);
5514
+ await runInit(baseDir, config, registryUrl, console.log, { allowOverwrite: options.yes === true || interactive });
5410
5515
  });
5411
5516
  }
5412
5517
 
5413
5518
  // src/add/index.ts
5414
5519
  import chalk12 from "chalk";
5415
- import prompts3 from "prompts";
5416
5520
 
5417
5521
  // ../axis-registry-core/src/add/add.ts
5418
5522
  import { existsSync as existsSync8, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
@@ -6477,7 +6581,7 @@ function configRegistryRejectedMessage(url, problem) {
6477
6581
  function createConfirm(interactive) {
6478
6582
  if (!interactive) return void 0;
6479
6583
  return async (message) => {
6480
- const { overwrite } = await prompts3({ type: "confirm", name: "overwrite", message, initial: false });
6584
+ const { overwrite } = await ask({ type: "confirm", name: "overwrite", message, initial: false }, interactive);
6481
6585
  return overwrite === true;
6482
6586
  };
6483
6587
  }
@@ -6485,7 +6589,7 @@ var defaultCreateClient = (cwd) => createClientFor(cwd, (m) => console.warn(chal
6485
6589
  function registerAddCommand(program2, deps = {}) {
6486
6590
  program2.command("add <components...>").description("\uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC758 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uD504\uB85C\uC81D\uD2B8\uC5D0 \uBCF5\uC0AC(registryDependencies \uD3EC\uD568)").option("--agentic", "\uC774\uB984 \uC55E\uC5D0 agentic- \uB97C \uBD99\uC5EC \uCC3E\uC74C(\uC804\uCCB4 \uC774\uB984\uB3C4 \uD5C8\uC6A9)").option("-y, --yes", "\uBB3B\uC9C0 \uC54A\uACE0 \uC9C4\uD589. \uB0B4\uC6A9\uC774 \uB2E4\uB978 \uAE30\uC874 \uD30C\uC77C\uC740 \uAC74\uB108\uB700").option("--overwrite", "\uB0B4\uC6A9\uC774 \uB2E4\uB978 \uAE30\uC874 \uD30C\uC77C\uC744 \uB36E\uC5B4\uC500").action(async (components, options) => {
6487
6591
  const client = await (deps.createClient ?? defaultCreateClient)(process.cwd());
6488
- const confirm = createConfirm((deps.isInteractive ?? (() => process.stdin.isTTY === true))());
6592
+ const confirm = createConfirm((deps.isInteractive ?? isInteractive)());
6489
6593
  const result = await runAdd(process.cwd(), components, options, {
6490
6594
  client,
6491
6595
  log: (...a) => console.log(...a),
@@ -6728,4 +6832,10 @@ registerSyncCommand(program);
6728
6832
  registerTemplateCommand(program);
6729
6833
  registerCheckCommand(program);
6730
6834
  registerThemeCommand(program);
6731
- program.parse();
6835
+ try {
6836
+ await program.parseAsync();
6837
+ } catch (err) {
6838
+ if (!(err instanceof NonInteractiveError)) throw err;
6839
+ console.error(chalk13.red(err.message));
6840
+ process.exitCode = 1;
6841
+ }
package/dist/monet.d.ts CHANGED
@@ -82,7 +82,10 @@ declare class MonetClient {
82
82
  searchComponents(query: string): Promise<void>;
83
83
  browseCategory(category: string): Promise<void>;
84
84
  private getCategoryExamples;
85
- importFromClipboard(): Promise<void>;
85
+ /** opts.interactive: 질문을 띄울 수 있는지(WI-0075). 없으면 stdin 이 TTY 인지로 정합니다 */
86
+ importFromClipboard(opts?: {
87
+ interactive?: boolean;
88
+ }): Promise<void>;
86
89
  }
87
90
  declare function getMonetUrl(path: string): string;
88
91
  declare function getMonetMcpConfig(): object;
package/dist/monet.js CHANGED
@@ -3,7 +3,8 @@ import {
3
3
  MonetClient,
4
4
  getMonetMcpConfig,
5
5
  getMonetUrl
6
- } from "./chunk-ZVVACWEW.js";
6
+ } from "./chunk-LVMDBOXD.js";
7
+ import "./chunk-HFMMK6R4.js";
7
8
  export {
8
9
  MONET_CATEGORIES,
9
10
  MonetClient,
@@ -13,7 +13,7 @@
13
13
  {
14
14
  "path": "components/agentic/agent-avatar.tsx",
15
15
  "type": "registry:ui",
16
- "content": "import * as React from \"react\";\nimport { cn } from \"@/registry/axis/lib/utils\";\nimport { AGENT_STATUS_LABELS } from \"@/registry/axis/lib/agentic-a11y-labels\";\n\nexport interface AgentAvatarProps {\n /** 에이전트 이름 */\n name: string;\n /** 이미지 URL */\n src?: string;\n /** 크기 */\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\";\n /** 상태 표시 */\n status?: \"online\" | \"busy\" | \"offline\";\n /** 에이전트 타입 */\n type?: \"assistant\" | \"tool\" | \"system\";\n /** 추가 클래스 */\n className?: string;\n}\n\nconst sizeStyles = {\n sm: \"w-6 h-6 text-xs\",\n md: \"w-8 h-8 text-sm\",\n lg: \"w-10 h-10 text-base\",\n xl: \"w-12 h-12 text-lg\",\n};\n\nconst statusStyles = {\n online: \"bg-[var(--axis-status-success)]\",\n busy: \"bg-[var(--axis-status-warning)]\",\n offline: \"bg-[var(--axis-status-neutral)]\",\n};\n\nconst typeColors = {\n assistant: \"bg-[var(--axis-agent-avatar-assistant)]\",\n tool: \"bg-[var(--axis-agent-avatar-tool)]\",\n system: \"bg-[var(--axis-agent-avatar-system)]\",\n};\n\n/**\n * 에이전트 아바타 컴포넌트\n */\nexport function AgentAvatar({\n name,\n src,\n size = \"md\",\n status,\n type = \"assistant\",\n className,\n}: AgentAvatarProps) {\n const initials = name\n .split(\" \")\n .map((n) => n[0])\n .join(\"\")\n .slice(0, 2)\n .toUpperCase();\n\n const statusLabel = status ? AGENT_STATUS_LABELS[status] : \"\";\n const ariaLabel = `${name}${statusLabel ? `, ${statusLabel}` : \"\"}`;\n\n return (\n <div\n role=\"img\"\n aria-label={ariaLabel}\n className={cn(\"relative inline-flex\", className)}\n >\n {src ? (\n <img\n src={src}\n alt=\"\"\n aria-hidden=\"true\"\n className={cn(\n \"rounded-full object-cover\",\n sizeStyles[size]\n )}\n />\n ) : (\n <div\n aria-hidden=\"true\"\n className={cn(\n \"rounded-full flex items-center justify-center font-medium text-[var(--axis-agent-avatar-text)]\",\n sizeStyles[size],\n typeColors[type]\n )}\n >\n {initials}\n </div>\n )}\n {status && (\n <span\n aria-hidden=\"true\"\n className={cn(\n \"absolute bottom-0 right-0 rounded-full border-2 border-[var(--axis-surface-default)]\",\n size === \"sm\" ? \"w-2 h-2\" : \"w-3 h-3\",\n statusStyles[status]\n )}\n />\n )}\n </div>\n );\n}\n"
16
+ "content": "import * as React from \"react\";\nimport { cn } from \"@/registry/axis/lib/utils\";\nimport { AGENT_STATUS_LABELS } from \"@/registry/axis/lib/agentic-a11y-labels\";\n\nexport interface AgentAvatarProps {\n /** 에이전트 이름 */\n name: string;\n /** 이미지 URL */\n src?: string;\n /** 크기 */\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\";\n /** 상태 표시 */\n status?: \"online\" | \"busy\" | \"offline\";\n /** 에이전트 타입 */\n type?: \"assistant\" | \"tool\" | \"system\";\n /** 추가 클래스 */\n className?: string;\n}\n\nconst sizeStyles = {\n sm: \"w-6 h-6 text-xs\",\n md: \"w-8 h-8 text-sm\",\n lg: \"w-10 h-10 text-base\",\n xl: \"w-12 h-12 text-lg\",\n};\n\nconst statusStyles = {\n online: \"bg-[var(--axis-text-success)]\",\n busy: \"bg-[var(--axis-text-warning)]\",\n offline: \"bg-[var(--axis-text-tertiary)]\",\n};\n\nconst typeColors = {\n assistant: \"bg-[var(--axis-agent-avatar-assistant)]\",\n tool: \"bg-[var(--axis-agent-avatar-tool)]\",\n system: \"bg-[var(--axis-agent-avatar-system)]\",\n};\n\n/**\n * 에이전트 아바타 컴포넌트\n */\nexport function AgentAvatar({\n name,\n src,\n size = \"md\",\n status,\n type = \"assistant\",\n className,\n}: AgentAvatarProps) {\n const initials = name\n .split(\" \")\n .map((n) => n[0])\n .join(\"\")\n .slice(0, 2)\n .toUpperCase();\n\n const statusLabel = status ? AGENT_STATUS_LABELS[status] : \"\";\n const ariaLabel = `${name}${statusLabel ? `, ${statusLabel}` : \"\"}`;\n\n return (\n <div\n role=\"img\"\n aria-label={ariaLabel}\n className={cn(\"relative inline-flex\", className)}\n >\n {src ? (\n <img\n src={src}\n alt=\"\"\n aria-hidden=\"true\"\n className={cn(\n \"rounded-full object-cover\",\n sizeStyles[size]\n )}\n />\n ) : (\n <div\n aria-hidden=\"true\"\n className={cn(\n \"rounded-full flex items-center justify-center font-medium text-[var(--axis-agent-avatar-text)]\",\n sizeStyles[size],\n typeColors[type]\n )}\n >\n {initials}\n </div>\n )}\n {status && (\n <span\n aria-hidden=\"true\"\n className={cn(\n \"absolute bottom-0 right-0 rounded-full border-2 border-[var(--axis-surface-default)]\",\n size === \"sm\" ? \"w-2 h-2\" : \"w-3 h-3\",\n statusStyles[status]\n )}\n />\n )}\n </div>\n );\n}\n"
17
17
  }
18
18
  ]
19
19
  }
@@ -13,7 +13,7 @@
13
13
  {
14
14
  "path": "components/agentic/approval-card.tsx",
15
15
  "type": "registry:ui",
16
- "content": "import * as React from \"react\";\nimport { cn } from \"@/registry/axis/lib/utils\";\nimport { SEVERITY_LABELS } from \"@/registry/axis/lib/agentic-a11y-labels\";\n\nexport type Severity = \"info\" | \"warning\" | \"error\";\n\nexport interface ApprovalAction {\n label: string;\n variant?: \"default\" | \"secondary\" | \"ghost\" | \"destructive\";\n onClick: () => void;\n}\n\nexport interface ApprovalCardProps {\n /** 고유 ID (접근성용) */\n id?: string;\n /** 제목 */\n title: string;\n /** 설명 */\n description?: string;\n /** 심각도 (색상 결정) */\n severity?: Severity;\n /** 액션 버튼 목록 */\n actions: ApprovalAction[];\n /** 추가 내용 */\n children?: React.ReactNode;\n /** 추가 클래스 */\n className?: string;\n}\n\nconst severityStyles: Record<Severity, { border: string; icon: string; bg: string }> = {\n info: {\n border: \"border-l-[var(--axis-status-info)]\",\n icon: \"text-[var(--axis-status-info)]\",\n bg: \"bg-[var(--axis-surface-info)]\",\n },\n warning: {\n border: \"border-l-[var(--axis-status-warning)]\",\n icon: \"text-[var(--axis-status-warning)]\",\n bg: \"bg-[var(--axis-surface-warning)]\",\n },\n error: {\n border: \"border-l-[var(--axis-status-error)]\",\n icon: \"text-[var(--axis-status-error)]\",\n bg: \"bg-[var(--axis-surface-error)]\",\n },\n};\n\nconst severityIcons: Record<Severity, React.ReactNode> = {\n info: (\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n ),\n warning: (\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\" />\n </svg>\n ),\n error: (\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n ),\n};\n\nconst actionVariantStyles: Record<string, string> = {\n default: \"bg-[var(--axis-button-bg-default)] text-[var(--axis-button-text-default)] hover:bg-[var(--axis-button-bg-hover)]\",\n secondary: \"bg-[var(--axis-button-secondary-bg-default)] text-[var(--axis-text-primary)] hover:bg-[var(--axis-button-secondary-bg-hover)]\",\n ghost: \"bg-transparent text-[var(--axis-text-primary)] hover:bg-[var(--axis-surface-secondary)]\",\n destructive: \"bg-[var(--axis-button-destructive-bg-default)] text-[var(--axis-button-destructive-text-default)] hover:bg-[var(--axis-button-destructive-bg-hover)]\",\n};\n\n/**\n * 사용자 승인이 필요한 작업을 표시하는 카드 컴포넌트\n */\nexport function ApprovalCard({\n id,\n title,\n description,\n severity = \"info\",\n actions,\n children,\n className,\n}: ApprovalCardProps) {\n const styles = severityStyles[severity];\n const generatedId = React.useId();\n const baseId = id || generatedId;\n const titleId = `${baseId}-title`;\n const descId = `${baseId}-desc`;\n const severityLabel = SEVERITY_LABELS[severity];\n\n return (\n <div\n role=\"region\"\n aria-labelledby={titleId}\n aria-describedby={description ? descId : undefined}\n className={cn(\n \"rounded-lg border border-l-4 p-4\",\n \"bg-[var(--axis-surface-default)] border-[var(--axis-border-default)]\",\n styles.border,\n className\n )}\n >\n <div className=\"flex items-start gap-3\">\n <div className={cn(\"flex-shrink-0 mt-0.5\", styles.icon)} aria-hidden=\"true\">\n {severityIcons[severity]}\n </div>\n <div className=\"flex-1 min-w-0\">\n <span className=\"sr-only\">{severityLabel} 알림:</span>\n <h4 id={titleId} className=\"text-sm font-semibold text-[var(--axis-text-primary)]\">\n {title}\n </h4>\n {description && (\n <p id={descId} className=\"mt-1 text-sm text-[var(--axis-text-secondary)]\">\n {description}\n </p>\n )}\n {children && <div className=\"mt-3\">{children}</div>}\n <div className=\"flex flex-wrap gap-2 mt-4\">\n {actions.map((action, index) => (\n <button\n key={index}\n onClick={action.onClick}\n className={cn(\n \"px-3 py-1.5 text-sm font-medium rounded-md transition-colors\",\n actionVariantStyles[action.variant || \"default\"]\n )}\n >\n {action.label}\n </button>\n ))}\n </div>\n </div>\n </div>\n </div>\n );\n}\n"
16
+ "content": "import * as React from \"react\";\nimport { cn } from \"@/registry/axis/lib/utils\";\nimport { SEVERITY_LABELS } from \"@/registry/axis/lib/agentic-a11y-labels\";\n\nexport type Severity = \"info\" | \"warning\" | \"error\";\n\nexport interface ApprovalAction {\n label: string;\n variant?: \"default\" | \"secondary\" | \"ghost\" | \"destructive\";\n onClick: () => void;\n}\n\nexport interface ApprovalCardProps {\n /** 고유 ID (접근성용) */\n id?: string;\n /** 제목 */\n title: string;\n /** 설명 */\n description?: string;\n /** 심각도 (색상 결정) */\n severity?: Severity;\n /** 액션 버튼 목록 */\n actions: ApprovalAction[];\n /** 추가 내용 */\n children?: React.ReactNode;\n /** 추가 클래스 */\n className?: string;\n}\n\nconst severityStyles: Record<Severity, { border: string; icon: string; bg: string }> = {\n info: {\n border: \"border-l-[var(--axis-status-info)]\",\n icon: \"text-[var(--axis-status-info)]\",\n bg: \"bg-[var(--axis-surface-info)]\",\n },\n warning: {\n border: \"border-l-[var(--axis-status-warning)]\",\n icon: \"text-[var(--axis-text-warning)]\",\n bg: \"bg-[var(--axis-surface-warning)]\",\n },\n error: {\n border: \"border-l-[var(--axis-status-error)]\",\n icon: \"text-[var(--axis-text-error)]\",\n bg: \"bg-[var(--axis-surface-error)]\",\n },\n};\n\nconst severityIcons: Record<Severity, React.ReactNode> = {\n info: (\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n ),\n warning: (\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\" />\n </svg>\n ),\n error: (\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n ),\n};\n\nconst actionVariantStyles: Record<string, string> = {\n default: \"bg-[var(--axis-button-bg-default)] text-[var(--axis-button-text-default)] hover:bg-[var(--axis-button-bg-hover)]\",\n secondary: \"bg-[var(--axis-button-secondary-bg-default)] text-[var(--axis-text-primary)] hover:bg-[var(--axis-button-secondary-bg-hover)]\",\n ghost: \"bg-transparent text-[var(--axis-text-primary)] hover:bg-[var(--axis-surface-secondary)]\",\n destructive: \"bg-[var(--axis-button-destructive-bg-default)] text-[var(--axis-button-destructive-text-default)] hover:bg-[var(--axis-button-destructive-bg-hover)]\",\n};\n\n/**\n * 사용자 승인이 필요한 작업을 표시하는 카드 컴포넌트\n */\nexport function ApprovalCard({\n id,\n title,\n description,\n severity = \"info\",\n actions,\n children,\n className,\n}: ApprovalCardProps) {\n const styles = severityStyles[severity];\n const generatedId = React.useId();\n const baseId = id || generatedId;\n const titleId = `${baseId}-title`;\n const descId = `${baseId}-desc`;\n const severityLabel = SEVERITY_LABELS[severity];\n\n return (\n <div\n role=\"region\"\n aria-labelledby={titleId}\n aria-describedby={description ? descId : undefined}\n className={cn(\n \"rounded-lg border border-l-4 p-4\",\n \"bg-[var(--axis-surface-default)] border-[var(--axis-border-default)]\",\n styles.border,\n className\n )}\n >\n <div className=\"flex items-start gap-3\">\n <div className={cn(\"flex-shrink-0 mt-0.5\", styles.icon)} aria-hidden=\"true\">\n {severityIcons[severity]}\n </div>\n <div className=\"flex-1 min-w-0\">\n <span className=\"sr-only\">{severityLabel} 알림:</span>\n <h4 id={titleId} className=\"text-sm font-semibold text-[var(--axis-text-primary)]\">\n {title}\n </h4>\n {description && (\n <p id={descId} className=\"mt-1 text-sm text-[var(--axis-text-secondary)]\">\n {description}\n </p>\n )}\n {children && <div className=\"mt-3\">{children}</div>}\n <div className=\"flex flex-wrap gap-2 mt-4\">\n {actions.map((action, index) => (\n <button\n key={index}\n onClick={action.onClick}\n className={cn(\n \"px-3 py-1.5 text-sm font-medium rounded-md transition-colors\",\n actionVariantStyles[action.variant || \"default\"]\n )}\n >\n {action.label}\n </button>\n ))}\n </div>\n </div>\n </div>\n </div>\n );\n}\n"
17
17
  }
18
18
  ]
19
19
  }