@axis-ds/mcp 1.4.9 → 1.5.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
@@ -58,8 +58,8 @@ function listTokens(category) {
58
58
  }
59
59
  return tokens;
60
60
  }
61
- function getToken(path12) {
62
- const q = path12.trim().toLowerCase();
61
+ function getToken(path13) {
62
+ const q = path13.trim().toLowerCase();
63
63
  return loadTokens().tokens.find(
64
64
  (t) => t.path.toLowerCase() === q || (t.aliases ?? []).some((a) => a.toLowerCase() === q) || t.cssVariable.toLowerCase() === q
65
65
  );
@@ -2318,9 +2318,9 @@ function checkTailwindSetup(baseDirInput) {
2318
2318
  };
2319
2319
  }
2320
2320
  for (const file of TAILWIND_CONFIG_FILES) {
2321
- const path12 = join2(baseDir, file);
2322
- if (!existsSync3(path12)) continue;
2323
- const content = readFileSync4(path12, "utf8");
2321
+ const path13 = join2(baseDir, file);
2322
+ if (!existsSync3(path13)) continue;
2323
+ const content = readFileSync4(path13, "utf8");
2324
2324
  if (hasTokensPresetReference(content)) {
2325
2325
  const code = stripJsComments(content);
2326
2326
  const bareCjs = isCommonJsConfig(file, code) && hasBareCjsPresetRequire(code);
@@ -3763,11 +3763,11 @@ async function handleGetComponent(params, deps = {}) {
3763
3763
  return { component, source: describeOrigin(client, latest.version), notices };
3764
3764
  }
3765
3765
  function handleGetToken(params) {
3766
- const { path: path12 } = params;
3767
- if (!path12 || path12.trim() === "") {
3766
+ const { path: path13 } = params;
3767
+ if (!path13 || path13.trim() === "") {
3768
3768
  throw new Error("\uD1A0\uD070 \uACBD\uB85C(path)\uB294 \uD544\uC218\uC785\uB2C8\uB2E4.");
3769
3769
  }
3770
- const token = getToken(path12);
3770
+ const token = getToken(path13);
3771
3771
  if (!token) {
3772
3772
  return null;
3773
3773
  }
@@ -3848,9 +3848,9 @@ function cssUsage(token) {
3848
3848
  const shadcnColor = !token.cssVariable.startsWith("--axis-") && token.value.startsWith("hsl(");
3849
3849
  return shadcnColor ? `hsl(var(${token.cssVariable}))` : `var(${token.cssVariable})`;
3850
3850
  }
3851
- function formatTokenNotFound(path12) {
3851
+ function formatTokenNotFound(path13) {
3852
3852
  return [
3853
- `\uD1A0\uD070\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: \`${path12}\``,
3853
+ `\uD1A0\uD070\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: \`${path13}\``,
3854
3854
  "",
3855
3855
  "- \uACBD\uB85C\uB294 `axis.list_tokens` \uB85C \uD655\uC778\uD558\uC138\uC694. \uACBD\uB85C \xB7 tokens \uC774\uB984 \uAE30\uBC18 \uACBD\uB85C(\uC608: `control.h.md`) \xB7 CSS \uBCC0\uC218 \uC774\uB984(\uC608: `--axis-control-h-md`)\uC744 \uBC1B\uC2B5\uB2C8\uB2E4.",
3856
3856
  "- \uBE0C\uB79C\uB4DC \uAC12\uC740 `axis.list_brands` \xB7 `axis.get_brand` \uB85C \uC870\uD68C\uD558\uC138\uC694."
@@ -5548,7 +5548,7 @@ function formatSaveResult(result) {
5548
5548
  // src/tools/template.ts
5549
5549
  import fetch from "node-fetch";
5550
5550
  import fs6 from "fs-extra";
5551
- import path10 from "path";
5551
+ import path11 from "path";
5552
5552
 
5553
5553
  // src/tools/brand-core.ts
5554
5554
  import { createHash as createHash2 } from "crypto";
@@ -6315,6 +6315,605 @@ function writeTemplateFiles(plan) {
6315
6315
  }
6316
6316
  }
6317
6317
 
6318
+ // ../axis-registry-core/src/template/template-checks.ts
6319
+ import { lstatSync as lstatSync4, readdirSync as readdirSync4, readFileSync as readFileSync11, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
6320
+ import path10 from "path";
6321
+ var TEMPLATE_CHECK_DEFINITIONS = /* @__PURE__ */ new Map([
6322
+ ["ontology-rules", { id: "ontology-rules", script: "check:ontology" }]
6323
+ ]);
6324
+ var TEMPLATE_CHECKS = /* @__PURE__ */ new Map([["ontology-console", ["ontology-rules"]]]);
6325
+ var TEMPLATE_SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
6326
+ var ONTOLOGY_RULE_IDS = ["em-dash", "exclamation", "one-side-line", "card-shadow", "palette"];
6327
+ var ONTOLOGY_TARGETS = ["app", "components", "lib"];
6328
+ var ONTOLOGY_MAX_FILE_BYTES = 256 * 1024;
6329
+ var ONTOLOGY_DIR_DEPTH_LIMIT = 32;
6330
+ var ONTOLOGY_MAX_UNIT_CHARS = 8e3;
6331
+ var ONTOLOGY_SCAN_STEPS_PER_CHAR = 16;
6332
+ var ONTOLOGY_SCAN_STEPS_BASE = 1e5;
6333
+ var ONTOLOGY_MAX_FILES = 4e3;
6334
+ var ONTOLOGY_MAX_TOTAL_BYTES = 16 * 1024 * 1024;
6335
+ var ONTOLOGY_MAX_ENTRIES = 5e4;
6336
+ var ONTOLOGY_DEFAULT_BUDGET = Object.freeze({
6337
+ maxFiles: ONTOLOGY_MAX_FILES,
6338
+ maxBytes: ONTOLOGY_MAX_TOTAL_BYTES,
6339
+ maxEntries: ONTOLOGY_MAX_ENTRIES
6340
+ });
6341
+ var MESSAGE_PATH_MAX = 120;
6342
+ function templateChecksFor(baseDirInput) {
6343
+ let config;
6344
+ try {
6345
+ config = JSON.parse(readFileSync11(path10.join(path10.resolve(baseDirInput), "axis.config.json"), "utf8"));
6346
+ } catch {
6347
+ return null;
6348
+ }
6349
+ if (typeof config !== "object" || config === null || Array.isArray(config)) return null;
6350
+ if (!Object.prototype.hasOwnProperty.call(config, "template")) return null;
6351
+ const template = config.template;
6352
+ if (typeof template !== "string" || !TEMPLATE_SLUG.test(template)) return null;
6353
+ const ids = TEMPLATE_CHECKS.get(template);
6354
+ if (!ids || ids.length === 0) return null;
6355
+ return { template, ids };
6356
+ }
6357
+ function collectTemplateCheckResults(baseDirInput) {
6358
+ const baseDir = path10.resolve(baseDirInput);
6359
+ const picked = templateChecksFor(baseDir);
6360
+ if (!picked) return [];
6361
+ const out = [];
6362
+ for (const id of picked.ids) {
6363
+ const def = TEMPLATE_CHECK_DEFINITIONS.get(id);
6364
+ if (def?.id === "ontology-rules") out.push(ontologyRulesResult(baseDir, picked.template, def));
6365
+ }
6366
+ return out;
6367
+ }
6368
+ function hasPackageScript(baseDir, script) {
6369
+ try {
6370
+ const pkg = JSON.parse(readFileSync11(path10.join(baseDir, "package.json"), "utf8"));
6371
+ if (typeof pkg !== "object" || pkg === null || Array.isArray(pkg)) return false;
6372
+ const scripts = pkg.scripts;
6373
+ if (typeof scripts !== "object" || scripts === null || Array.isArray(scripts)) return false;
6374
+ return Object.prototype.hasOwnProperty.call(scripts, script) && typeof scripts[script] === "string";
6375
+ } catch {
6376
+ return false;
6377
+ }
6378
+ }
6379
+ var MESSAGE_PATH_KEEP = /^[\p{L}\p{N}\p{P}\p{S} ]$/u;
6380
+ function messageSafePath(p) {
6381
+ let out = "";
6382
+ for (const ch of p) {
6383
+ const cp = ch.codePointAt(0);
6384
+ const piece = MESSAGE_PATH_KEEP.test(ch) && ch !== '"' && ch !== "`" && ch !== "\\" ? ch : cp > 65535 ? `\\u{${cp.toString(16)}}` : `\\u${cp.toString(16).padStart(4, "0")}`;
6385
+ if (out.length + piece.length > MESSAGE_PATH_MAX) return `${out}...`;
6386
+ out += piece;
6387
+ }
6388
+ return out;
6389
+ }
6390
+ function ontologyRulesResult(baseDir, template, def) {
6391
+ const name = `template.${def.id}`;
6392
+ const head = `\uD15C\uD50C\uB9BF ${template} \uADDC\uCE59 \uAC80\uC0AC(axis \uB0B4\uC7A5 \uADDC\uCE59, \uC124\uCE58\uD55C axis \uBC84\uC804 \uAE30\uC900 \xB7 \uD504\uB85C\uC81D\uD2B8 \uC2A4\uD06C\uB9BD\uD2B8\uB294 \uC2E4\uD589\uD558\uC9C0 \uC54A\uC74C)`;
6393
+ let inspection;
6394
+ try {
6395
+ inspection = inspectOntologyRules(baseDir);
6396
+ } catch (e) {
6397
+ const code = typeof e?.code === "string" && /^[A-Z0-9_]{1,32}$/.test(e.code) ? e.code : "\uC624\uB958";
6398
+ return { name, severity: "warn", message: `${head}: \uCE21\uC815 \uC2E4\uD328, \uD30C\uC77C\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(${code})` };
6399
+ }
6400
+ const parts = ontologySkippedParts(inspection);
6401
+ const skipped = parts.length > 0 ? `, \uAC74\uB108\uB700: ${parts.join(" \xB7 ")}` : "";
6402
+ if (inspection.checked === 0) {
6403
+ return {
6404
+ name,
6405
+ severity: "warn",
6406
+ message: `${head}: \uCE21\uC815 \uC2E4\uD328, \uAC80\uC0AC\uD560 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(\uB300\uC0C1: ${ONTOLOGY_TARGETS.join(" \xB7 ")})${skipped}`
6407
+ };
6408
+ }
6409
+ const { violations } = inspection;
6410
+ if (violations.length === 0) {
6411
+ if (isOntologyIncomplete(inspection)) {
6412
+ return { name, severity: "warn", message: `${head}: \uC77C\uBD80 \uCE21\uC815 \uC2E4\uD328, \uD30C\uC77C ${inspection.checked}\uAC1C \uC704\uBC18 0\uAC74${skipped}` };
6413
+ }
6414
+ return { name, severity: "pass", message: `${head}: \uD30C\uC77C ${inspection.checked}\uAC1C, \uC704\uBC18 0\uAC74${skipped}` };
6415
+ }
6416
+ const counts = ONTOLOGY_RULE_IDS.map((rule) => [rule, violations.filter((v) => v.rule === rule).length]).filter(([, n]) => n > 0).map(([rule, n]) => `${rule} ${n}`).join(" \xB7 ");
6417
+ const first = violations[0];
6418
+ const hint = hasPackageScript(baseDir, def.script) ? ` \u2192 \uC804\uCCB4 \uBCF4\uAE30: npm run ${def.script}` : "";
6419
+ return {
6420
+ name,
6421
+ severity: "warn",
6422
+ message: `${head}: \uD30C\uC77C ${inspection.checked}\uAC1C, \uC704\uBC18 ${violations.length}\uAC74(${counts}), \uCCAB \uC704\uCE58 "${messageSafePath(first.file)}":${first.line} ${first.rule}${skipped}${hint}`
6423
+ };
6424
+ }
6425
+ function ontologySkippedParts(found) {
6426
+ const parts = [];
6427
+ if (found.skippedLarge > 0) parts.push(`\uD06C\uAE30 \uC0C1\uD55C(256KB) \uCD08\uACFC ${found.skippedLarge}\uAC1C`);
6428
+ if (found.skippedLinks > 0) parts.push(`\uD504\uB85C\uC81D\uD2B8 \uBC16 \xB7 \uAE68\uC9C4 \uB9C1\uD06C ${found.skippedLinks}\uAC1C`);
6429
+ if (found.skippedDeep > 0) parts.push(`\uAE4A\uC774 ${ONTOLOGY_DIR_DEPTH_LIMIT} \uB2E8\uACC4 \uBC16 \uB514\uB809\uD130\uB9AC ${found.skippedDeep}\uAC1C`);
6430
+ if (found.partialFiles > 0) parts.push(`\uD074\uB798\uC2A4 \uBB36\uC74C \uC791\uC5C5\uB7C9 \uC0C1\uD55C \uCD08\uACFC(\uC77C\uBD80\uB9CC \uD310\uC815) ${found.partialFiles}\uAC1C`);
6431
+ if (found.budgetStop) parts.push(ontologyBudgetStopText(found.budgetStop));
6432
+ return parts;
6433
+ }
6434
+ function formatBytes(n) {
6435
+ if (n % (1024 * 1024) === 0) return `${n / (1024 * 1024)}MB`;
6436
+ if (n % 1024 === 0) return `${n / 1024}KB`;
6437
+ return `${n}B`;
6438
+ }
6439
+ function ontologyBudgetStopText(stop) {
6440
+ if (stop.reason === "files") return `\uD504\uB85C\uC81D\uD2B8 \uC608\uC0B0 \uCD08\uACFC\uB85C \uCE21\uC815 \uC911\uB2E8(\uD30C\uC77C ${stop.limit}\uAC1C \uC0C1\uD55C)`;
6441
+ if (stop.reason === "entries") return `\uD504\uB85C\uC81D\uD2B8 \uC608\uC0B0 \uCD08\uACFC\uB85C \uCE21\uC815 \uC911\uB2E8(\uB514\uB809\uD130\uB9AC \uD56D\uBAA9 ${stop.limit}\uAC1C \uC0C1\uD55C)`;
6442
+ return `\uD504\uB85C\uC81D\uD2B8 \uC608\uC0B0 \uCD08\uACFC\uB85C \uCE21\uC815 \uC911\uB2E8(\uC77D\uAE30 ${formatBytes(stop.limit)} \uC0C1\uD55C)`;
6443
+ }
6444
+ function isOntologyIncomplete(found) {
6445
+ return found.skippedLarge > 0 || found.skippedDeep > 0 || found.partialFiles > 0 || found.budgetStop !== null;
6446
+ }
6447
+ var EXTS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".css"]);
6448
+ var EM_DASH = String.fromCharCode(8212);
6449
+ var FULLWIDTH_BANG = String.fromCharCode(65281);
6450
+ var NEUTRAL_BORDER = /* @__PURE__ */ new Set(["border-border", "border-input", "border-transparent", "border-inherit", "border-current"]);
6451
+ var SIDE_WIDTH = /(?:^|\s|:)border-[lrse](?:-(?:0|2|4|8|\[[^\]]+\]))?(?=\s|$)/;
6452
+ var BORDER_COLOR = /(?:^|\s|:)(border-(?:[lrse]-)?(?!(?:0|2|4|8|x|y|t|b|l|r|s|e|solid|dashed|dotted|double|none|hidden|collapse|separate|spacing)(?:\s|$))[a-z][\w-]*(?:\/\d+)?|border-(?:[lrse]-)?\[(?:#|rgb|hsl)[^\]]*\])(?=\s|$)/g;
6453
+ var BORDER_NOT_COLOR = /^border-(?:(?:[lrsetbxy])(?:-(?:0|2|4|8|\[[^\]]+\]))?|0|2|4|8|\[\d[^\]]*\]|solid|dashed|dotted|double|none|hidden|collapse|separate)$/;
6454
+ var SHADOW = /(?:^|\s|:)shadow(?:-(?!none(?:\s|$))[\w[\]().,%-]+)?(?=\s|$)/;
6455
+ var PALETTE_NAMES = "slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose";
6456
+ var PALETTE_CLASS = new RegExp(
6457
+ `(?:^|[\\s:"'\`])((?:bg|text|border|ring|fill|stroke|from|via|to|outline|divide|decoration|shadow|accent|caret|placeholder)-(?:(?:${PALETTE_NAMES})-\\d{2,3}|white|black)(?:\\/\\d+)?)(?=[\\s"'\`]|$)`,
6458
+ "g"
6459
+ );
6460
+ var HEX = /#[0-9a-fA-F]{3,8}\b/g;
6461
+ var NAMED_COLORS = new Set(
6462
+ "aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue blueviolet brown burlywood cadetblue chartreuse chocolate coral cornflowerblue cornsilk crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgreen darkgrey darkkhaki darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue dimgray dimgrey dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite gold goldenrod gray green greenyellow grey honeydew hotpink indianred indigo ivory khaki lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan lightgoldenrodyellow lightgray lightgreen lightgrey lightpink lightsalmon lightseagreen lightskyblue lightslategray lightslategrey lightsteelblue lightyellow lime limegreen linen magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple mediumseagreen mediumslateblue mediumspringgreen mediumturquoise mediumvioletred midnightblue mintcream mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum powderblue purple rebeccapurple red rosybrown royalblue saddlebrown salmon sandybrown seagreen seashell sienna silver skyblue slateblue slategray slategrey snow springgreen steelblue tan teal thistle tomato turquoise violet wheat white whitesmoke yellow yellowgreen".split(" ")
6463
+ );
6464
+ var CSS_COLOR_PROP = /(?:^|[;{\s])((?:background|border|outline|text-decoration|column-rule)(?:-(?:top|right|bottom|left|inline-start|inline-end))?(?:-color)?|color|fill|stroke|caret-color|accent-color|box-shadow)\s*:\s*([^;}]+)/g;
6465
+ var STYLE_COLOR_KEY = /\b((?:background|border|outline|textDecoration|columnRule)(?:Top|Right|Bottom|Left|InlineStart|InlineEnd)?(?:Color)?|color|fill|stroke|caretColor|accentColor|boxShadow)\s*:\s*(["'`])([^"'`]*)\2/g;
6466
+ var COLOR_FN = /\b(?:rgba?|hsla?)\(\s*\d/g;
6467
+ var HANGUL = /[가-힣]/;
6468
+ function stripComments2(src) {
6469
+ const blank = (m) => m.replace(/[^\n]/g, " ");
6470
+ let out = "";
6471
+ let last = 0;
6472
+ for (let i = src.indexOf("/*"); i !== -1; i = src.indexOf("/*", last)) {
6473
+ const end = src.indexOf("*/", i + 2);
6474
+ if (end === -1) break;
6475
+ out += src.slice(last, i) + blank(src.slice(i, end + 2));
6476
+ last = end + 2;
6477
+ }
6478
+ out += src.slice(last);
6479
+ return out.replace(/(^|[^:'"`\\])(\/\/[^\n]*)/g, (_, pre, c) => pre + blank(c));
6480
+ }
6481
+ function lineIndex(src) {
6482
+ const breaks = [];
6483
+ for (let i = src.indexOf("\n"); i !== -1; i = src.indexOf("\n", i + 1)) breaks.push(i);
6484
+ return (index) => {
6485
+ let lo = 0;
6486
+ let hi = breaks.length;
6487
+ while (lo < hi) {
6488
+ const mid = lo + hi >> 1;
6489
+ if (breaks[mid] < index) lo = mid + 1;
6490
+ else hi = mid;
6491
+ }
6492
+ return lo + 1;
6493
+ };
6494
+ }
6495
+ var isLineTerminator = (c) => c === "\n" || c === "\r" || c === "\u2028" || c === "\u2029";
6496
+ function literalEnds(code, q) {
6497
+ const n = code.length;
6498
+ const end = new Int32Array(n + 2).fill(-1);
6499
+ for (let i = n - 1; i >= 0; i--) {
6500
+ const c = code[i];
6501
+ if (c === q) end[i] = i;
6502
+ else if (c === "\n") end[i] = -1;
6503
+ else if (c === "\\") end[i] = i + 1 < n && !isLineTerminator(code[i + 1]) ? end[i + 2] : -1;
6504
+ else end[i] = end[i + 1];
6505
+ }
6506
+ return end;
6507
+ }
6508
+ function stringLiterals2(code) {
6509
+ const ends = { '"': literalEnds(code, '"'), "'": literalEnds(code, "'"), "`": literalEnds(code, "`") };
6510
+ const out = [];
6511
+ for (let i = 0; i < code.length; i++) {
6512
+ const c = code[i];
6513
+ if (c !== '"' && c !== "'" && c !== "`") continue;
6514
+ const e = ends[c][i + 1];
6515
+ if (e === -1) continue;
6516
+ out.push({ text: code.slice(i + 1, e), index: i });
6517
+ i = e;
6518
+ }
6519
+ return out;
6520
+ }
6521
+ function textChunks(code, isJsx) {
6522
+ const out = stringLiterals2(code).map((x) => ({ text: x.text, index: x.index, kind: "string" }));
6523
+ let m;
6524
+ if (isJsx) {
6525
+ const jsx = /[>}]([^<>{}]+)(?=[<{])/g;
6526
+ while (m = jsx.exec(code)) {
6527
+ if (/[=;]/.test(m[1])) continue;
6528
+ const text = m[1].replace(/(["'`])(?:(?!\1)[^\n])*\1/g, " ");
6529
+ out.push({ text, index: m.index + 1, kind: "jsx" });
6530
+ }
6531
+ }
6532
+ return out;
6533
+ }
6534
+ var SCAN_BUDGET_EXCEEDED = /* @__PURE__ */ Symbol("scan-budget-exceeded");
6535
+ function spend(budget, steps) {
6536
+ budget.left -= steps;
6537
+ if (budget.left < 0) throw SCAN_BUDGET_EXCEEDED;
6538
+ }
6539
+ function skipEnds(code, q) {
6540
+ const n = code.length;
6541
+ const end = new Int32Array(n + 2).fill(n);
6542
+ for (let i = n - 1; i >= 0; i--) {
6543
+ const c = code[i];
6544
+ end[i] = c === q ? i : c === "\\" ? end[Math.min(i + 2, n)] : end[i + 1];
6545
+ }
6546
+ return end;
6547
+ }
6548
+ function closeMatcher(code, budget) {
6549
+ const memo = /* @__PURE__ */ new Map();
6550
+ const pair = { "(": ")", "{": "}" };
6551
+ const skips = { '"': skipEnds(code, '"'), "'": skipEnds(code, "'"), "`": skipEnds(code, "`") };
6552
+ return (open) => {
6553
+ const known = memo.get(open);
6554
+ if (known !== void 0) return known;
6555
+ const limit = open + ONTOLOGY_MAX_UNIT_CHARS;
6556
+ const stack = [pair[code[open]]];
6557
+ const opens = [open];
6558
+ let steps = 0;
6559
+ for (let i = open + 1; i < code.length; i++) {
6560
+ if (++steps === 1024) {
6561
+ spend(budget, steps);
6562
+ steps = 0;
6563
+ }
6564
+ if (i >= limit) {
6565
+ spend(budget, steps);
6566
+ memo.set(open, -1);
6567
+ return -1;
6568
+ }
6569
+ const c = code[i];
6570
+ if (c === '"' || c === "'" || c === "`") {
6571
+ i = skips[c][i + 1];
6572
+ continue;
6573
+ }
6574
+ if (c === "(" || c === "{") {
6575
+ const inner = memo.get(i);
6576
+ if (inner !== void 0) {
6577
+ if (inner === -1) {
6578
+ spend(budget, steps);
6579
+ for (const p of opens) memo.set(p, -1);
6580
+ return -1;
6581
+ }
6582
+ i = inner;
6583
+ continue;
6584
+ }
6585
+ stack.push(pair[c]);
6586
+ opens.push(i);
6587
+ } else if (c === stack[stack.length - 1]) {
6588
+ stack.pop();
6589
+ memo.set(opens.pop(), i);
6590
+ if (stack.length === 0) {
6591
+ spend(budget, steps);
6592
+ return i;
6593
+ }
6594
+ }
6595
+ }
6596
+ spend(budget, steps);
6597
+ for (const p of opens) memo.set(p, -1);
6598
+ return -1;
6599
+ };
6600
+ }
6601
+ function joinStrings(expr, budget) {
6602
+ spend(budget, expr.length);
6603
+ return stringLiterals2(expr).map((x) => x.text).join(" ");
6604
+ }
6605
+ function isCardTagAt(code, at, end) {
6606
+ if (at < 0 || at + 5 > end || !code.startsWith("<Card", at)) return false;
6607
+ const next = at + 5;
6608
+ return next >= end || !/\w/.test(code[next]);
6609
+ }
6610
+ function lastLtFinder(code) {
6611
+ let pos = 0;
6612
+ let last = -1;
6613
+ return (index) => {
6614
+ for (; pos <= index && pos < code.length; pos++) if (code[pos] === "<") last = pos;
6615
+ return last;
6616
+ };
6617
+ }
6618
+ function cardClassStrings(code) {
6619
+ const out = [];
6620
+ let from = 0;
6621
+ let noQuoteAfter = Infinity;
6622
+ for (let at = code.indexOf("<Card", from); at !== -1; at = code.indexOf("<Card", from)) {
6623
+ from = at + 1;
6624
+ if (!isCardTagAt(code, at, code.length)) continue;
6625
+ for (let k = at + 5; k < code.length; k++) {
6626
+ if (k + 11 <= code.length && code.startsWith('className="', k) && k + 11 < noQuoteAfter) {
6627
+ const q = code.indexOf('"', k + 11);
6628
+ if (q !== -1) {
6629
+ out.push({ text: code.slice(k + 11, q), index: at, card: true });
6630
+ from = q + 1;
6631
+ break;
6632
+ }
6633
+ noQuoteAfter = k + 11;
6634
+ }
6635
+ const c = code[k];
6636
+ if (c === "<") break;
6637
+ if (c === ">" && !(k - 1 >= at + 5 && code[k - 1] === "=")) break;
6638
+ }
6639
+ }
6640
+ return out;
6641
+ }
6642
+ function classUnits(code, budget) {
6643
+ const out = [];
6644
+ const matchClose = closeMatcher(code, budget);
6645
+ const lastLt = lastLtFinder(code);
6646
+ let m;
6647
+ const attr = /className=\{/g;
6648
+ while (m = attr.exec(code)) {
6649
+ const open = m.index + m[0].length - 1;
6650
+ const close = matchClose(open);
6651
+ if (close === -1 || close - open + 1 > ONTOLOGY_MAX_UNIT_CHARS) continue;
6652
+ out.push({ text: joinStrings(code.slice(open, close + 1), budget), index: m.index, card: isCardTagAt(code, lastLt(m.index), m.index) });
6653
+ }
6654
+ const call = /\b(?:cn|clsx|cva|twMerge)\(/g;
6655
+ while (m = call.exec(code)) {
6656
+ const open = m.index + m[0].length - 1;
6657
+ const close = matchClose(open);
6658
+ if (close === -1 || close - open + 1 > ONTOLOGY_MAX_UNIT_CHARS) continue;
6659
+ out.push({ text: joinStrings(code.slice(open, close + 1), budget), index: m.index, card: false });
6660
+ }
6661
+ out.push(...cardClassStrings(code));
6662
+ return out;
6663
+ }
6664
+ function cssRules(code) {
6665
+ const out = [];
6666
+ const nextBrace = (from) => {
6667
+ for (let k = from; k < code.length; k++) if (code[k] === "{" || code[k] === "}") return k;
6668
+ return -1;
6669
+ };
6670
+ let i = 0;
6671
+ while (i < code.length) {
6672
+ if (code[i] === "{" || code[i] === "}") {
6673
+ i++;
6674
+ continue;
6675
+ }
6676
+ const open = nextBrace(i);
6677
+ if (open === -1) break;
6678
+ if (code[open] !== "{") {
6679
+ i = open + 1;
6680
+ continue;
6681
+ }
6682
+ const close = nextBrace(open + 1);
6683
+ if (close === -1) break;
6684
+ if (code[close] !== "}") {
6685
+ i = open + 1;
6686
+ continue;
6687
+ }
6688
+ out.push({ selector: code.slice(i, open), body: code.slice(open + 1, close), open });
6689
+ i = close + 1;
6690
+ }
6691
+ return out;
6692
+ }
6693
+ function oneSideColors(t) {
6694
+ if (!SIDE_WIDTH.test(t)) return [];
6695
+ return [...t.matchAll(BORDER_COLOR)].map((x) => x[1]).filter((c) => !NEUTRAL_BORDER.has(c.replace(/^border-[lrse]-/, "border-")) && !BORDER_NOT_COLOR.test(c));
6696
+ }
6697
+ function isCardLike(t, card) {
6698
+ return card || /(?:^|\s)bg-card(?=\s|$)/.test(t) || /(?:^|\s)rounded(?:-[\w[\]]+)?(?=\s|$)/.test(t) && /(?:^|\s)border(?:-(?:0|2|4|8))?(?=\s|$)/.test(t);
6699
+ }
6700
+ function sideValueColored(value) {
6701
+ const v = value.trim();
6702
+ const neutral = /^(?:0|none|hidden)$/.test(v) || /var\(--border\)|hsl\(var\(--border\)\)|transparent|currentColor/.test(v);
6703
+ const colored = /#|rgb|hsl|var\(--/.test(v) || v.split(/[\s,()]+/).some((w) => NAMED_COLORS.has(w.toLowerCase()));
6704
+ return colored && !neutral;
6705
+ }
6706
+ function byLineThenRule(a, b) {
6707
+ return a.line - b.line || ONTOLOGY_RULE_IDS.indexOf(a.rule) - ONTOLOGY_RULE_IDS.indexOf(b.rule);
6708
+ }
6709
+ function inspectOntologySourceDetailed(raw, ext) {
6710
+ const out = [];
6711
+ let partial = false;
6712
+ const seen = /* @__PURE__ */ new Set();
6713
+ const code = stripComments2(raw);
6714
+ const lineOf = lineIndex(raw);
6715
+ const report = (index, rule) => {
6716
+ const line = lineOf(index);
6717
+ const key = `${line}:${rule}`;
6718
+ if (seen.has(key)) return;
6719
+ seen.add(key);
6720
+ out.push({ line, rule });
6721
+ };
6722
+ for (let i = raw.indexOf(EM_DASH); i !== -1; i = raw.indexOf(EM_DASH, i + 1)) report(i, "em-dash");
6723
+ if (ext === ".css") {
6724
+ const side = /border-(?:left|right|inline-start|inline-end)(?:-color)?\s*:\s*([^;}]+)/g;
6725
+ let m2;
6726
+ while (m2 = side.exec(code)) {
6727
+ if (sideValueColored(m2[1])) report(m2.index, "one-side-line");
6728
+ }
6729
+ for (const r of cssRules(code)) {
6730
+ if (/card/i.test(r.selector) && /box-shadow\s*:(?!\s*(?:none\b|0 0 #0000))/.test(r.body)) report(r.open, "card-shadow");
6731
+ }
6732
+ } else {
6733
+ const isJsx = ext === ".tsx" || ext === ".jsx";
6734
+ for (const chunk of textChunks(code, isJsx)) {
6735
+ const t = chunk.text;
6736
+ if (HANGUL.test(t) && (t.includes("!") || t.includes(FULLWIDTH_BANG))) report(chunk.index, "exclamation");
6737
+ if (chunk.kind !== "string") continue;
6738
+ if (t.length > ONTOLOGY_MAX_UNIT_CHARS) continue;
6739
+ if (oneSideColors(t).length > 0) report(chunk.index, "one-side-line");
6740
+ if (isCardLike(t, false) && SHADOW.test(t)) report(chunk.index, "card-shadow");
6741
+ }
6742
+ let units = [];
6743
+ try {
6744
+ units = classUnits(code, { left: code.length * ONTOLOGY_SCAN_STEPS_PER_CHAR + ONTOLOGY_SCAN_STEPS_BASE });
6745
+ } catch (e) {
6746
+ if (e !== SCAN_BUDGET_EXCEEDED) throw e;
6747
+ partial = true;
6748
+ }
6749
+ for (const unit of units) {
6750
+ const t = unit.text;
6751
+ if (oneSideColors(t).length > 0) report(unit.index, "one-side-line");
6752
+ if (isCardLike(t, unit.card) && SHADOW.test(t)) report(unit.index, "card-shadow");
6753
+ }
6754
+ const lastLt = lastLtFinder(code);
6755
+ let m2;
6756
+ STYLE_COLOR_KEY.lastIndex = 0;
6757
+ while (m2 = STYLE_COLOR_KEY.exec(code)) {
6758
+ const key = m2[1];
6759
+ const value = m2[3];
6760
+ if (/^border(?:Left|Right|InlineStart|InlineEnd)(?:Color)?$/.test(key) && sideValueColored(value)) {
6761
+ report(m2.index, "one-side-line");
6762
+ }
6763
+ if (key === "boxShadow" && !/^(?:none|0 0 #0000)$/.test(value.trim())) {
6764
+ if (isCardTagAt(code, lastLt(m2.index), code.length)) report(m2.index, "card-shadow");
6765
+ }
6766
+ if (value.split(/[\s,()]+/).some((w) => NAMED_COLORS.has(w.toLowerCase()))) report(m2.index, "palette");
6767
+ }
6768
+ }
6769
+ if (ext === ".css") {
6770
+ let m2;
6771
+ CSS_COLOR_PROP.lastIndex = 0;
6772
+ while (m2 = CSS_COLOR_PROP.exec(code)) {
6773
+ if (m2[2].split(/[\s,()]+/).some((w) => NAMED_COLORS.has(w.toLowerCase()))) report(m2.index, "palette");
6774
+ }
6775
+ }
6776
+ let m;
6777
+ HEX.lastIndex = 0;
6778
+ while (m = HEX.exec(code)) {
6779
+ if (code[m.index - 1] === "&") continue;
6780
+ if (/[\w/]/.test(code[m.index - 1] ?? "")) continue;
6781
+ if (/(?:href|to|id)=\{?\s*["'`]$/.test(code.slice(Math.max(0, m.index - 12), m.index))) continue;
6782
+ report(m.index, "palette");
6783
+ }
6784
+ COLOR_FN.lastIndex = 0;
6785
+ while (m = COLOR_FN.exec(code)) report(m.index, "palette");
6786
+ PALETTE_CLASS.lastIndex = 0;
6787
+ while (m = PALETTE_CLASS.exec(code)) report(m.index, "palette");
6788
+ return { violations: out.sort(byLineThenRule), partial };
6789
+ }
6790
+ function collectOntologyFiles(baseDir, targets, maxFiles, maxEntries) {
6791
+ const rootReal = realpathSync5(baseDir);
6792
+ const found = {
6793
+ files: [],
6794
+ skippedLarge: 0,
6795
+ skippedLinks: 0,
6796
+ skippedDeep: 0,
6797
+ partialFiles: 0,
6798
+ budgetStop: null
6799
+ };
6800
+ const visitedDirs = /* @__PURE__ */ new Set();
6801
+ const seenFiles = /* @__PURE__ */ new Set();
6802
+ let entries = 0;
6803
+ const toRel = (p) => path10.relative(baseDir, p).split(path10.sep).join("/");
6804
+ const resolveEntry = (p, st) => {
6805
+ if (!st.isSymbolicLink()) return { st, real: void 0 };
6806
+ let real;
6807
+ let target;
6808
+ try {
6809
+ real = realpathSync5(p);
6810
+ target = statSync7(real);
6811
+ } catch {
6812
+ found.skippedLinks++;
6813
+ return void 0;
6814
+ }
6815
+ if (!isSameOrInside(rootReal, real)) {
6816
+ found.skippedLinks++;
6817
+ return void 0;
6818
+ }
6819
+ return { st: target, real };
6820
+ };
6821
+ const addFile = (p, real, st, ext) => {
6822
+ if (seenFiles.has(real)) return;
6823
+ seenFiles.add(real);
6824
+ if (st.size > ONTOLOGY_MAX_FILE_BYTES) {
6825
+ found.skippedLarge++;
6826
+ return;
6827
+ }
6828
+ if (found.files.length >= maxFiles) {
6829
+ found.budgetStop = { reason: "files", limit: maxFiles };
6830
+ return;
6831
+ }
6832
+ found.files.push({ rel: toRel(p), real, ext, size: st.size });
6833
+ };
6834
+ const walk = (dir, dirReal, depth) => {
6835
+ const names = readdirSync4(dir).sort();
6836
+ for (const name of names) {
6837
+ if (found.budgetStop) return;
6838
+ if (entries >= maxEntries) {
6839
+ found.budgetStop = { reason: "entries", limit: maxEntries };
6840
+ return;
6841
+ }
6842
+ entries++;
6843
+ if (name === "node_modules" || name.startsWith(".")) continue;
6844
+ const p = path10.join(dir, name);
6845
+ const entry = resolveEntry(p, lstatSync4(p));
6846
+ if (!entry) continue;
6847
+ const real = entry.real ?? path10.join(dirReal, name);
6848
+ if (entry.st.isDirectory()) {
6849
+ if (visitedDirs.has(real)) continue;
6850
+ if (depth >= ONTOLOGY_DIR_DEPTH_LIMIT) {
6851
+ found.skippedDeep++;
6852
+ continue;
6853
+ }
6854
+ visitedDirs.add(real);
6855
+ walk(p, real, depth + 1);
6856
+ } else if (entry.st.isFile() && EXTS.has(path10.extname(name))) {
6857
+ addFile(p, real, entry.st, path10.extname(name));
6858
+ }
6859
+ }
6860
+ };
6861
+ for (const t of targets) {
6862
+ if (found.budgetStop) break;
6863
+ const p = path10.join(baseDir, t);
6864
+ let st;
6865
+ try {
6866
+ st = lstatSync4(p);
6867
+ } catch (e) {
6868
+ if (e?.code === "ENOENT") continue;
6869
+ throw e;
6870
+ }
6871
+ const entry = resolveEntry(p, st);
6872
+ if (!entry) continue;
6873
+ const real = entry.real ?? realpathSync5(p);
6874
+ if (!isSameOrInside(rootReal, real)) {
6875
+ found.skippedLinks++;
6876
+ continue;
6877
+ }
6878
+ if (entry.st.isDirectory()) {
6879
+ if (visitedDirs.has(real)) continue;
6880
+ visitedDirs.add(real);
6881
+ walk(p, real, 1);
6882
+ } else if (entry.st.isFile() && EXTS.has(path10.extname(p))) {
6883
+ addFile(p, real, entry.st, path10.extname(p));
6884
+ }
6885
+ }
6886
+ return found;
6887
+ }
6888
+ function inspectOntologyRules(baseDirInput, targets = ONTOLOGY_TARGETS, budget = ONTOLOGY_DEFAULT_BUDGET) {
6889
+ const baseDir = path10.resolve(baseDirInput);
6890
+ const found = collectOntologyFiles(baseDir, targets, budget.maxFiles, budget.maxEntries);
6891
+ const violations = [];
6892
+ let checked = 0;
6893
+ let bytes = 0;
6894
+ for (const f of found.files) {
6895
+ if (bytes + f.size > budget.maxBytes) {
6896
+ found.budgetStop = { reason: "bytes", limit: budget.maxBytes };
6897
+ break;
6898
+ }
6899
+ bytes += f.size;
6900
+ const raw = readFileSync11(f.real, "utf8");
6901
+ checked++;
6902
+ const result = inspectOntologySourceDetailed(raw, f.ext);
6903
+ if (result.partial) found.partialFiles++;
6904
+ for (const v of result.violations) violations.push({ file: f.rel, line: v.line, rule: v.rule });
6905
+ }
6906
+ return {
6907
+ checked,
6908
+ violations,
6909
+ skippedLarge: found.skippedLarge,
6910
+ skippedLinks: found.skippedLinks,
6911
+ skippedDeep: found.skippedDeep,
6912
+ partialFiles: found.partialFiles,
6913
+ budgetStop: found.budgetStop
6914
+ };
6915
+ }
6916
+
6318
6917
  // src/site.ts
6319
6918
  var AXIS_SITE_URL2 = "https://axis-design-system.pages.dev";
6320
6919
 
@@ -6368,7 +6967,7 @@ async function handleGetTemplate(params) {
6368
6967
  }
6369
6968
  async function handleApplyTemplate(params) {
6370
6969
  const detail = await fetchTemplateDetail(params.name);
6371
- const baseDir = path10.resolve(params.targetDir);
6970
+ const baseDir = path11.resolve(params.targetDir);
6372
6971
  const installCommand = detectInstallCommand(baseDir);
6373
6972
  const plan = planTemplateFiles(baseDir, detail.files, (detail.postInstall ?? []).map((p) => p.file));
6374
6973
  if (!params.dryRun) writeTemplateFiles(plan);
@@ -6407,7 +7006,7 @@ async function handleApplyTemplate(params) {
6407
7006
  }
6408
7007
  async function handleDiffTemplate(params) {
6409
7008
  const detail = await fetchTemplateDetail(params.name);
6410
- const baseDir = path10.resolve(params.targetDir || ".");
7009
+ const baseDir = path11.resolve(params.targetDir || ".");
6411
7010
  const entries = [];
6412
7011
  const localPaths = detail.files.map((file) => resolveTemplatePath(baseDir, file.path));
6413
7012
  for (const [i, file] of detail.files.entries()) {
@@ -6499,9 +7098,9 @@ function brandActivationResult(check) {
6499
7098
  };
6500
7099
  }
6501
7100
  async function handleCheckProject(params) {
6502
- const baseDir = path10.resolve(params.targetDir || ".");
7101
+ const baseDir = path11.resolve(params.targetDir || ".");
6503
7102
  const results = [];
6504
- const configExists = await fs6.pathExists(path10.join(baseDir, "axis.config.json"));
7103
+ const configExists = await fs6.pathExists(path11.join(baseDir, "axis.config.json"));
6505
7104
  results.push({
6506
7105
  name: "axis.config.json",
6507
7106
  severity: configExists ? "pass" : "fail",
@@ -6518,7 +7117,7 @@ async function handleCheckProject(params) {
6518
7117
  ];
6519
7118
  let utilsFound = false;
6520
7119
  for (const candidate of utilsCandidates) {
6521
- const fullPath = path10.join(baseDir, candidate);
7120
+ const fullPath = path11.join(baseDir, candidate);
6522
7121
  if (await fs6.pathExists(fullPath)) {
6523
7122
  const content2 = await fs6.readFile(fullPath, "utf-8");
6524
7123
  const hasCn = content2.includes("function cn");
@@ -6547,6 +7146,7 @@ async function handleCheckProject(params) {
6547
7146
  if (content) results.push(content);
6548
7147
  const animation = checkAnimationPlugin(baseDir);
6549
7148
  if (animation) results.push(animation);
7149
+ results.push(...collectTemplateCheckResults(baseDir));
6550
7150
  const summary = {
6551
7151
  pass: results.filter((r) => r.severity === "pass").length,
6552
7152
  warn: results.filter((r) => r.severity === "warn").length,
@@ -6669,10 +7269,10 @@ function formatCheckProjectResult(result) {
6669
7269
  }
6670
7270
 
6671
7271
  // src/tools/brand.ts
6672
- import path11 from "path";
7272
+ import path12 from "path";
6673
7273
  var TOKENS_INSTALL_COMMAND = addCommandFor("pnpm install", { [TOKENS_PACKAGE]: `^${TOKENS_DEPENDENCY_MIN_VERSION}` });
6674
7274
  function baseDirOf(targetDir) {
6675
- return path11.resolve(targetDir || process.cwd());
7275
+ return path12.resolve(targetDir || process.cwd());
6676
7276
  }
6677
7277
  function tokensInstallGuideFor(targetDir) {
6678
7278
  return dependencyInstallGuide(baseDirOf(targetDir), TOKENS_PACKAGE, `^${TOKENS_DEPENDENCY_MIN_VERSION}`);
@@ -6960,10 +7560,10 @@ server.tool(
6960
7560
  {
6961
7561
  path: z.string().describe("\uD1A0\uD070 \uACBD\uB85C (\uC608: colors.primary, spacing.4, control.h.md) \uB610\uB294 CSS \uBCC0\uC218 \uC774\uB984 (\uC608: --axis-control-h-md)")
6962
7562
  },
6963
- async ({ path: path12 }) => {
7563
+ async ({ path: path13 }) => {
6964
7564
  try {
6965
- const token = handleGetToken({ path: path12 });
6966
- const formatted = formatTokenDetail(token, path12);
7565
+ const token = handleGetToken({ path: path13 });
7566
+ const formatted = formatTokenDetail(token, path13);
6967
7567
  return {
6968
7568
  content: [
6969
7569
  {
@@ -7337,7 +7937,7 @@ server.tool(
7337
7937
  );
7338
7938
  server.tool(
7339
7939
  "axis.check_project",
7340
- "\uD504\uB85C\uC81D\uD2B8\uC758 AXIS \uB514\uC790\uC778 \uC2DC\uC2A4\uD15C \uC124\uC815 \uC0C1\uD0DC\uB97C \uAC80\uC99D\uD569\uB2C8\uB2E4.",
7940
+ "\uD504\uB85C\uC81D\uD2B8\uC758 AXIS \uB514\uC790\uC778 \uC2DC\uC2A4\uD15C \uC124\uC815 \uC0C1\uD0DC\uB97C \uAC80\uC99D\uD569\uB2C8\uB2E4. \uC801\uC6A9\uD55C \uD15C\uD50C\uB9BF\uC758 \uADDC\uCE59 \uAC80\uC0AC \uD3EC\uD568(\uC77D\uAE30 \uC804\uC6A9, \uC2A4\uD06C\uB9BD\uD2B8 \uC2E4\uD589 \uC548 \uD568).",
7341
7941
  {
7342
7942
  targetDir: z.string().optional().describe("\uAC80\uC99D \uB300\uC0C1 \uB514\uB809\uD1A0\uB9AC (\uAE30\uBCF8: \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC)")
7343
7943
  },
@@ -13,7 +13,7 @@
13
13
  {
14
14
  "path": "components/agentic/tool-call-card.tsx",
15
15
  "type": "registry:ui",
16
- "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@/registry/axis/lib/utils\";\nimport { STATUS_LABELS } from \"@/registry/axis/lib/agentic-a11y-labels\";\n\nexport type ToolStatus = \"pending\" | \"running\" | \"success\" | \"error\";\n\nexport interface ToolCallCardProps {\n /** 도구 이름 */\n toolName: string;\n /** 도구 설명 */\n description?: string;\n /** 실행 상태 */\n status: ToolStatus;\n /** 입력 파라미터 */\n input?: Record<string, unknown>;\n /** 출력 결과 */\n output?: unknown;\n /** 에러 메시지 */\n error?: string;\n /** 실행 시간 (ms) */\n duration?: number;\n /** 펼침 상태 */\n defaultExpanded?: boolean;\n /** 접근성 레이블 */\n \"aria-label\"?: string;\n /** 추가 클래스 */\n className?: string;\n}\n\nconst statusConfig: Record<ToolStatus, { color: string; icon: React.ReactNode }> = {\n pending: {\n color: \"text-[var(--axis-text-tertiary)]\",\n icon: (\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" strokeWidth={2} />\n </svg>\n ),\n },\n running: {\n color: \"text-[var(--axis-status-running)]\",\n icon: (\n <svg className=\"w-4 h-4 animate-spin motion-reduce:![animation-duration:3s]\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\" />\n </svg>\n ),\n },\n success: {\n color: \"text-[var(--axis-text-success)]\",\n icon: (\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M5 13l4 4L19 7\" />\n </svg>\n ),\n },\n error: {\n color: \"text-[var(--axis-text-error)]\",\n icon: (\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n ),\n },\n};\n\n/**\n * AI 도구 호출 정보를 표시하는 카드 컴포넌트\n */\nexport function ToolCallCard({\n toolName,\n description,\n status,\n input,\n output,\n error,\n duration,\n defaultExpanded = false,\n \"aria-label\": ariaLabel,\n className,\n}: ToolCallCardProps) {\n const [isExpanded, setIsExpanded] = React.useState(defaultExpanded);\n const config = statusConfig[status];\n const contentId = React.useId();\n const statusLabel = STATUS_LABELS[status] || status;\n\n return (\n <div\n className={cn(\n \"rounded-lg border overflow-hidden\",\n \"bg-[var(--axis-surface-default)] border-[var(--axis-border-default)]\",\n className\n )}\n >\n {/* 헤더 */}\n <button\n onClick={() => setIsExpanded(!isExpanded)}\n aria-expanded={isExpanded}\n aria-controls={contentId}\n aria-label={ariaLabel || `${toolName} - ${statusLabel}`}\n className=\"w-full flex items-center justify-between p-3 hover:bg-[var(--axis-surface-secondary)] transition-colors\"\n >\n <div className=\"flex items-center gap-2\">\n <span className={config.color} aria-hidden=\"true\">{config.icon}</span>\n <code className=\"text-sm font-mono text-[var(--axis-text-primary)]\">\n {toolName}\n </code>\n {description && (\n <span className=\"text-xs text-[var(--axis-text-secondary)]\">\n — {description}\n </span>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n {duration !== undefined && (\n <span className=\"text-xs text-[var(--axis-text-secondary)]\">\n {duration}ms\n </span>\n )}\n <svg\n className={cn(\n \"w-4 h-4 text-[var(--axis-text-secondary)] transition-transform\",\n isExpanded && \"rotate-180\"\n )}\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n </div>\n </button>\n\n {/* 내용 */}\n {isExpanded && (\n <div id={contentId} className=\"border-t border-[var(--axis-border-default)] p-3 space-y-3\">\n {input && (\n <div>\n <h5 className=\"text-xs font-medium text-[var(--axis-text-secondary)] mb-1\">\n 입력\n </h5>\n <pre className=\"text-xs p-2 rounded bg-[var(--axis-surface-secondary)] overflow-x-auto\">\n <code className=\"text-[var(--axis-text-primary)]\">\n {JSON.stringify(input, null, 2)}\n </code>\n </pre>\n </div>\n )}\n {output !== undefined && (\n <div>\n <h5 className=\"text-xs font-medium text-[var(--axis-text-secondary)] mb-1\">\n 출력\n </h5>\n <pre className=\"text-xs p-2 rounded bg-[var(--axis-surface-secondary)] overflow-x-auto\">\n <code className=\"text-[var(--axis-text-primary)]\">\n {typeof output === \"string\" ? output : JSON.stringify(output, null, 2)}\n </code>\n </pre>\n </div>\n )}\n {error && (\n <div>\n <h5 className=\"text-xs font-medium text-[var(--axis-text-error)] mb-1\">\n 에러\n </h5>\n <pre className=\"text-xs p-2 rounded bg-[var(--axis-surface-error)] overflow-x-auto\">\n <code className=\"text-[var(--axis-text-error)]\">{error}</code>\n </pre>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n"
16
+ "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@/registry/axis/lib/utils\";\nimport { STATUS_LABELS } from \"@/registry/axis/lib/agentic-a11y-labels\";\n\nexport type ToolStatus = \"pending\" | \"running\" | \"success\" | \"error\";\n\nexport interface ToolCallCardProps {\n /** 도구 이름 */\n toolName: string;\n /** 도구 설명 */\n description?: string;\n /** 실행 상태 */\n status: ToolStatus;\n /** 입력 파라미터 */\n input?: Record<string, unknown>;\n /** 출력 결과 */\n output?: unknown;\n /** 에러 메시지 */\n error?: string;\n /** 실행 시간 (ms) */\n duration?: number;\n /** 펼침 상태 */\n defaultExpanded?: boolean;\n /** 접근성 레이블 */\n \"aria-label\"?: string;\n /** 추가 클래스 */\n className?: string;\n}\n\nconst statusConfig: Record<ToolStatus, { color: string; icon: React.ReactNode }> = {\n pending: {\n color: \"text-[var(--axis-text-tertiary)]\",\n icon: (\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" strokeWidth={2} />\n </svg>\n ),\n },\n running: {\n color: \"text-[var(--axis-status-running)]\",\n icon: (\n <svg className=\"w-4 h-4 animate-spin motion-reduce:![animation-duration:3s]\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\" />\n </svg>\n ),\n },\n success: {\n color: \"text-[var(--axis-text-success)]\",\n icon: (\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M5 13l4 4L19 7\" />\n </svg>\n ),\n },\n error: {\n color: \"text-[var(--axis-text-error)]\",\n icon: (\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n ),\n },\n};\n\n/**\n * AI 도구 호출 정보를 표시하는 카드 컴포넌트\n */\nexport function ToolCallCard({\n toolName,\n description,\n status,\n input,\n output,\n error,\n duration,\n defaultExpanded = false,\n \"aria-label\": ariaLabel,\n className,\n}: ToolCallCardProps) {\n const [isExpanded, setIsExpanded] = React.useState(defaultExpanded);\n const config = statusConfig[status];\n const contentId = React.useId();\n const statusLabel = STATUS_LABELS[status] || status;\n\n return (\n <div\n className={cn(\n \"rounded-lg border overflow-hidden\",\n \"bg-[var(--axis-surface-default)] border-[var(--axis-border-default)]\",\n className\n )}\n >\n {/* 헤더 */}\n <button\n onClick={() => setIsExpanded(!isExpanded)}\n aria-expanded={isExpanded}\n aria-controls={contentId}\n aria-label={ariaLabel || `${toolName} - ${statusLabel}`}\n className=\"w-full flex items-center justify-between p-3 hover:bg-[var(--axis-surface-secondary)] transition-colors\"\n >\n <div className=\"flex items-center gap-2\">\n <span className={config.color} aria-hidden=\"true\">{config.icon}</span>\n <code className=\"text-sm font-mono text-[var(--axis-text-primary)]\">\n {toolName}\n </code>\n {description && (\n <span className=\"text-xs text-[var(--axis-text-secondary)]\">\n · {description}\n </span>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n {duration !== undefined && (\n <span className=\"text-xs text-[var(--axis-text-secondary)]\">\n {duration}ms\n </span>\n )}\n <svg\n className={cn(\n \"w-4 h-4 text-[var(--axis-text-secondary)] transition-transform\",\n isExpanded && \"rotate-180\"\n )}\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n </div>\n </button>\n\n {/* 내용 */}\n {isExpanded && (\n <div id={contentId} className=\"border-t border-[var(--axis-border-default)] p-3 space-y-3\">\n {input && (\n <div>\n <h5 className=\"text-xs font-medium text-[var(--axis-text-secondary)] mb-1\">\n 입력\n </h5>\n <pre className=\"text-xs p-2 rounded bg-[var(--axis-surface-secondary)] overflow-x-auto\">\n <code className=\"text-[var(--axis-text-primary)]\">\n {JSON.stringify(input, null, 2)}\n </code>\n </pre>\n </div>\n )}\n {output !== undefined && (\n <div>\n <h5 className=\"text-xs font-medium text-[var(--axis-text-secondary)] mb-1\">\n 출력\n </h5>\n <pre className=\"text-xs p-2 rounded bg-[var(--axis-surface-secondary)] overflow-x-auto\">\n <code className=\"text-[var(--axis-text-primary)]\">\n {typeof output === \"string\" ? output : JSON.stringify(output, null, 2)}\n </code>\n </pre>\n </div>\n )}\n {error && (\n <div>\n <h5 className=\"text-xs font-medium text-[var(--axis-text-error)] mb-1\">\n 에러\n </h5>\n <pre className=\"text-xs p-2 rounded bg-[var(--axis-surface-error)] overflow-x-auto\">\n <code className=\"text-[var(--axis-text-error)]\">{error}</code>\n </pre>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n"
17
17
  }
18
18
  ]
19
19
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "axis",
3
3
  "homepage": "https://axis-design-system.pages.dev",
4
- "version": "1.8.2",
4
+ "version": "1.8.4",
5
5
  "items": [
6
6
  {
7
7
  "name": "utils",
@@ -64,7 +64,7 @@
64
64
  "type": "registry:ui",
65
65
  "title": "Select",
66
66
  "description": "AXIS 셀렉트 컴포넌트 - 드롭다운 선택",
67
- "sha256": "3c38d5bbe13dee18e111a4453a0fc4cb43e0bab40233bb3ae7282e96a67d1b7d"
67
+ "sha256": "cf6a8a5ef905a09580a7420b0ee7d77751599ef9455dfba3382ca79414d02cef"
68
68
  },
69
69
  {
70
70
  "name": "tabs",
@@ -78,7 +78,7 @@
78
78
  "type": "registry:ui",
79
79
  "title": "Toast",
80
80
  "description": "AXIS 토스트 컴포넌트 - 알림 메시지",
81
- "sha256": "c56513bb2338c57ffee093d7c610ce2ec493857621607ad735b1c4bb4806a21a"
81
+ "sha256": "c21626ca0ef04ccb6b88a3a4728bec32867e7bc4430f728f630b7e16fcc6c217"
82
82
  },
83
83
  {
84
84
  "name": "agentic-a11y-labels",
@@ -155,7 +155,7 @@
155
155
  "type": "registry:ui",
156
156
  "title": "Tool Call Card",
157
157
  "description": "도구 호출 결과 표시 카드 컴포넌트",
158
- "sha256": "7601f15cb9d796a5bae068ccb5a12b1c02078e2273bdb6be5e9d00324fd54bfb"
158
+ "sha256": "4411f955e7f2ee5199efb171ca218be0373d4ddac46cd7dad8bca3b4a3c19226"
159
159
  },
160
160
  {
161
161
  "name": "agentic-message-bubble",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://ui.shadcn.com/schema/registry.json",
3
3
  "name": "axis",
4
4
  "homepage": "https://axis-design-system.pages.dev",
5
- "version": "1.8.2",
5
+ "version": "1.8.4",
6
6
  "items": [
7
7
  {
8
8
  "name": "utils",
@@ -65,7 +65,7 @@
65
65
  "type": "registry:ui",
66
66
  "title": "Select",
67
67
  "description": "AXIS 셀렉트 컴포넌트 - 드롭다운 선택",
68
- "sha256": "3c38d5bbe13dee18e111a4453a0fc4cb43e0bab40233bb3ae7282e96a67d1b7d"
68
+ "sha256": "cf6a8a5ef905a09580a7420b0ee7d77751599ef9455dfba3382ca79414d02cef"
69
69
  },
70
70
  {
71
71
  "name": "tabs",
@@ -79,7 +79,7 @@
79
79
  "type": "registry:ui",
80
80
  "title": "Toast",
81
81
  "description": "AXIS 토스트 컴포넌트 - 알림 메시지",
82
- "sha256": "c56513bb2338c57ffee093d7c610ce2ec493857621607ad735b1c4bb4806a21a"
82
+ "sha256": "c21626ca0ef04ccb6b88a3a4728bec32867e7bc4430f728f630b7e16fcc6c217"
83
83
  },
84
84
  {
85
85
  "name": "agentic-a11y-labels",
@@ -156,7 +156,7 @@
156
156
  "type": "registry:ui",
157
157
  "title": "Tool Call Card",
158
158
  "description": "도구 호출 결과 표시 카드 컴포넌트",
159
- "sha256": "7601f15cb9d796a5bae068ccb5a12b1c02078e2273bdb6be5e9d00324fd54bfb"
159
+ "sha256": "4411f955e7f2ee5199efb171ca218be0373d4ddac46cd7dad8bca3b4a3c19226"
160
160
  },
161
161
  {
162
162
  "name": "agentic-message-bubble",
@@ -14,7 +14,7 @@
14
14
  {
15
15
  "path": "components/ui/select.tsx",
16
16
  "type": "registry:ui",
17
- "content": "import * as React from \"react\";\nimport * as SelectPrimitive from \"@radix-ui/react-select\";\nimport { cn } from \"@/registry/axis/lib/utils\";\n\n/** 셀렉트의 루트 컴포넌트. 열림/닫힘 및 선택 상태를 관리한다. */\nconst Select = SelectPrimitive.Root;\n\n/** 셀렉트 옵션들을 논리적으로 그룹화하는 컴포넌트 */\nconst SelectGroup = SelectPrimitive.Group;\n\n/** 셀렉트 트리거 내에서 현재 선택된 값을 표시하는 컴포넌트 */\nconst SelectValue = SelectPrimitive.Value;\n\n/** 셀렉트 드롭다운을 여는 트리거 버튼 컴포넌트. 선택된 값과 하향 화살표 아이콘을 표시한다. */\nconst SelectTrigger = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Trigger\n ref={ref}\n className={cn(\n \"flex h-[var(--axis-field-h,2.5rem)] w-full items-center justify-between rounded-[var(--axis-radius-field,0.375rem)] border px-3 py-2 text-sm\",\n \"bg-[var(--axis-input-bg-default)] border-[var(--axis-input-border-default)]\",\n // placeholder 글자는 SelectValue 의 span 이라 ::placeholder 가 닿지 않는다. 값이 없을 때 Radix 가 붙이는 data-placeholder 로 칠한다(WI-0082)\n // Trigger 가 아니라 그 안의 span 에만 준다. Trigger 에 주면 화살표(currentColor, 50%)도 따라가 대비가 3:1 아래로 떨어진다\n \"[&[data-placeholder]>span]:text-[var(--axis-input-text-placeholder)]\",\n \"hover:border-[var(--axis-input-border-hover)]\",\n \"focus:outline-none focus:ring-2 focus:ring-[var(--axis-input-border-focus)] focus:ring-offset-2\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n \"[&>span]:line-clamp-1\",\n className\n )}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <svg\n className=\"h-4 w-4 opacity-50\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 9l-7 7-7-7\"\n />\n </svg>\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n));\nSelectTrigger.displayName = \"SelectTrigger\";\n\n/** 셀렉트 드롭다운의 본문 콘텐츠 컴포넌트. 포털을 통해 렌더링되며 진입/퇴장 애니메이션을 포함한다. */\nconst SelectContent = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>\n>(({ className, children, position = \"popper\", ...props }, ref) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n className={cn(\n \"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border shadow-md\",\n \"bg-[var(--axis-surface-default)] border-[var(--axis-border-default)]\",\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 motion-reduce:!animate-none\",\n position === \"popper\" &&\n \"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1\",\n className\n )}\n position={position}\n {...props}\n >\n <SelectPrimitive.Viewport\n className={cn(\n \"p-1\",\n position === \"popper\" &&\n \"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]\"\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n));\nSelectContent.displayName = \"SelectContent\";\n\n/** 셀렉트 옵션 그룹의 레이블 컴포넌트. 비선택 텍스트로 그룹 제목을 표시한다. */\nconst SelectLabel = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn(\n \"py-1.5 pl-8 pr-2 text-sm font-semibold text-[var(--axis-text-secondary)]\",\n className\n )}\n {...props}\n />\n));\nSelectLabel.displayName = \"SelectLabel\";\n\n/** 셀렉트 드롭다운의 개별 선택 항목 컴포넌트. 선택 시 좌측에 체크 아이콘이 표시된다. */\nconst SelectItem = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none\",\n \"text-[var(--axis-text-primary)]\",\n \"focus:bg-[var(--axis-surface-secondary)] focus:text-[var(--axis-text-primary)]\",\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <svg\n className=\"h-4 w-4\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n));\nSelectItem.displayName = \"SelectItem\";\n\n/** 셀렉트 옵션 사이의 구분선 컴포넌트 */\nconst SelectSeparator = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-[var(--axis-border-default)]\", className)}\n {...props}\n />\n));\nSelectSeparator.displayName = \"SelectSeparator\";\n\nexport {\n Select,\n SelectGroup,\n SelectValue,\n SelectTrigger,\n SelectContent,\n SelectLabel,\n SelectItem,\n SelectSeparator,\n};\n"
17
+ "content": "import * as React from \"react\";\nimport * as SelectPrimitive from \"@radix-ui/react-select\";\nimport { cn } from \"@/registry/axis/lib/utils\";\n\n/** 셀렉트의 루트 컴포넌트. 열림/닫힘 및 선택 상태를 관리한다. */\nconst Select = SelectPrimitive.Root;\n\n/** 셀렉트 옵션들을 논리적으로 그룹화하는 컴포넌트 */\nconst SelectGroup = SelectPrimitive.Group;\n\n/** 셀렉트 트리거 내에서 현재 선택된 값을 표시하는 컴포넌트 */\nconst SelectValue = SelectPrimitive.Value;\n\n/** 셀렉트 드롭다운을 여는 트리거 버튼 컴포넌트. 선택된 값과 하향 화살표 아이콘을 표시한다. */\nconst SelectTrigger = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Trigger\n ref={ref}\n className={cn(\n \"flex h-[var(--axis-field-h,2.5rem)] w-full items-center justify-between rounded-[var(--axis-radius-field,0.375rem)] border px-3 py-2 text-sm\",\n \"bg-[var(--axis-input-bg-default)] border-[var(--axis-input-border-default)]\",\n // placeholder 글자는 SelectValue 의 span 이라 ::placeholder 가 닿지 않는다. 값이 없을 때 Radix 가 붙이는 data-placeholder 로 칠한다(WI-0082)\n // Trigger 가 아니라 그 안의 span 에만 준다. Trigger 에 주면 화살표(currentColor, 50%)도 따라가 대비가 3:1 아래로 떨어진다\n \"[&[data-placeholder]>span]:text-[var(--axis-input-text-placeholder)]\",\n \"hover:border-[var(--axis-input-border-hover)]\",\n \"focus:outline-none focus:ring-2 focus:ring-[var(--axis-input-border-focus)] focus:ring-offset-2\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n \"[&>span]:line-clamp-1\",\n className\n )}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <svg\n className=\"h-4 w-4 opacity-50\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 9l-7 7-7-7\"\n />\n </svg>\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n));\nSelectTrigger.displayName = \"SelectTrigger\";\n\n/** 셀렉트 드롭다운의 본문 콘텐츠 컴포넌트. 포털을 통해 렌더링되며 진입/퇴장 애니메이션을 포함한다. */\nconst SelectContent = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>\n>(({ className, children, position = \"popper\", ...props }, ref) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n className={cn(\n \"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border shadow-[shadow:var(--axis-shadow-lg)]\",\n \"bg-[var(--axis-surface-default)] border-[var(--axis-border-default)]\",\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 motion-reduce:!animate-none\",\n position === \"popper\" &&\n \"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1\",\n className\n )}\n position={position}\n {...props}\n >\n <SelectPrimitive.Viewport\n className={cn(\n \"p-1\",\n position === \"popper\" &&\n \"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]\"\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n));\nSelectContent.displayName = \"SelectContent\";\n\n/** 셀렉트 옵션 그룹의 레이블 컴포넌트. 비선택 텍스트로 그룹 제목을 표시한다. */\nconst SelectLabel = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn(\n \"py-1.5 pl-8 pr-2 text-sm font-semibold text-[var(--axis-text-secondary)]\",\n className\n )}\n {...props}\n />\n));\nSelectLabel.displayName = \"SelectLabel\";\n\n/** 셀렉트 드롭다운의 개별 선택 항목 컴포넌트. 선택 시 좌측에 체크 아이콘이 표시된다. */\nconst SelectItem = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none\",\n \"text-[var(--axis-text-primary)]\",\n \"focus:bg-[var(--axis-surface-secondary)] focus:text-[var(--axis-text-primary)]\",\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <svg\n className=\"h-4 w-4\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n));\nSelectItem.displayName = \"SelectItem\";\n\n/** 셀렉트 옵션 사이의 구분선 컴포넌트 */\nconst SelectSeparator = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-[var(--axis-border-default)]\", className)}\n {...props}\n />\n));\nSelectSeparator.displayName = \"SelectSeparator\";\n\nexport {\n Select,\n SelectGroup,\n SelectValue,\n SelectTrigger,\n SelectContent,\n SelectLabel,\n SelectItem,\n SelectSeparator,\n};\n"
18
18
  }
19
19
  ]
20
20
  }
@@ -15,7 +15,7 @@
15
15
  {
16
16
  "path": "components/ui/toast.tsx",
17
17
  "type": "registry:ui",
18
- "content": "import * as React from \"react\";\nimport * as ToastPrimitives from \"@radix-ui/react-toast\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"@/registry/axis/lib/utils\";\n\n/** 토스트 알림 시스템의 프로바이더 컴포넌트. 앱 루트에 배치하여 토스트 기능을 활성화한다. */\nconst ToastProvider = ToastPrimitives.Provider;\n\n/** 토스트 알림이 표시되는 뷰포트 영역. 화면 하단 우측에 고정 위치로 렌더링된다. */\nconst ToastViewport = React.forwardRef<\n React.ElementRef<typeof ToastPrimitives.Viewport>,\n React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>\n>(({ className, ...props }, ref) => (\n <ToastPrimitives.Viewport\n ref={ref}\n className={cn(\n \"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]\",\n className\n )}\n {...props}\n />\n));\nToastViewport.displayName = ToastPrimitives.Viewport.displayName;\n\nconst toastVariants = cva(\n \"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full motion-reduce:!animate-none\",\n {\n variants: {\n variant: {\n default:\n \"border-[var(--axis-border-default)] bg-[var(--axis-surface-default)] text-[var(--axis-text-primary)]\",\n success:\n \"border-[var(--axis-border-success)] bg-[var(--axis-surface-success)] text-[var(--axis-text-success)]\",\n error:\n \"border-[var(--axis-border-error)] bg-[var(--axis-surface-error)] text-[var(--axis-text-error)]\",\n warning:\n \"border-transparent bg-[var(--axis-surface-warning)] text-[var(--axis-text-warning)]\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n);\n\n/** 토스트 알림의 루트 컴포넌트. default, success, error, warning 변형을 지원하며 스와이프로 닫을 수 있다. */\nconst Toast = React.forwardRef<\n React.ElementRef<typeof ToastPrimitives.Root>,\n React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &\n VariantProps<typeof toastVariants>\n>(({ className, variant, ...props }, ref) => (\n <ToastPrimitives.Root\n ref={ref}\n className={cn(toastVariants({ variant }), className)}\n {...props}\n />\n));\nToast.displayName = ToastPrimitives.Root.displayName;\n\n/** 토스트 내에 표시되는 액션 버튼 컴포넌트. 사용자 상호작용을 위한 CTA를 제공한다. */\nconst ToastAction = React.forwardRef<\n React.ElementRef<typeof ToastPrimitives.Action>,\n React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>\n>(({ className, ...props }, ref) => (\n <ToastPrimitives.Action\n ref={ref}\n className={cn(\n \"inline-flex h-8 shrink-0 items-center justify-center rounded-md border px-3 text-sm font-medium transition-colors\",\n \"border-[var(--axis-border-default)] bg-transparent\",\n \"hover:bg-[var(--axis-surface-secondary)]\",\n \"focus:outline-none focus:ring-2 focus:ring-[var(--axis-border-focus)]\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n className\n )}\n {...props}\n />\n));\nToastAction.displayName = ToastPrimitives.Action.displayName;\n\n/** 토스트를 닫는 닫기 버튼 컴포넌트. 호버 시 표시되며 X 아이콘을 포함한다. */\nconst ToastClose = React.forwardRef<\n React.ElementRef<typeof ToastPrimitives.Close>,\n React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>\n>(({ className, ...props }, ref) => (\n <ToastPrimitives.Close\n ref={ref}\n className={cn(\n \"absolute right-2 top-2 rounded-md p-1 text-[var(--axis-text-secondary)] opacity-0 transition-opacity\",\n \"hover:text-[var(--axis-text-primary)]\",\n \"focus:opacity-100 focus:outline-none focus:ring-2\",\n \"group-hover:opacity-100\",\n className\n )}\n toast-close=\"\"\n {...props}\n >\n <svg className=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </ToastPrimitives.Close>\n));\nToastClose.displayName = ToastPrimitives.Close.displayName;\n\n/** 토스트의 제목을 표시하는 컴포넌트 */\nconst ToastTitle = React.forwardRef<\n React.ElementRef<typeof ToastPrimitives.Title>,\n React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>\n>(({ className, ...props }, ref) => (\n <ToastPrimitives.Title\n ref={ref}\n className={cn(\"text-sm font-semibold\", className)}\n {...props}\n />\n));\nToastTitle.displayName = ToastPrimitives.Title.displayName;\n\n/** 토스트의 상세 설명을 표시하는 컴포넌트 */\nconst ToastDescription = React.forwardRef<\n React.ElementRef<typeof ToastPrimitives.Description>,\n React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>\n>(({ className, ...props }, ref) => (\n <ToastPrimitives.Description\n ref={ref}\n className={cn(\"text-sm\", className)}\n {...props}\n />\n));\nToastDescription.displayName = ToastPrimitives.Description.displayName;\n\nexport {\n ToastProvider,\n ToastViewport,\n Toast,\n ToastTitle,\n ToastDescription,\n ToastClose,\n ToastAction,\n};\n"
18
+ "content": "import * as React from \"react\";\nimport * as ToastPrimitives from \"@radix-ui/react-toast\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"@/registry/axis/lib/utils\";\n\n/** 토스트 알림 시스템의 프로바이더 컴포넌트. 앱 루트에 배치하여 토스트 기능을 활성화한다. */\nconst ToastProvider = ToastPrimitives.Provider;\n\n/** 토스트 알림이 표시되는 뷰포트 영역. 화면 하단 우측에 고정 위치로 렌더링된다. */\nconst ToastViewport = React.forwardRef<\n React.ElementRef<typeof ToastPrimitives.Viewport>,\n React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>\n>(({ className, ...props }, ref) => (\n <ToastPrimitives.Viewport\n ref={ref}\n className={cn(\n \"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]\",\n className\n )}\n {...props}\n />\n));\nToastViewport.displayName = ToastPrimitives.Viewport.displayName;\n\nconst toastVariants = cva(\n \"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-[shadow:var(--axis-shadow-lg)] transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full motion-reduce:!animate-none\",\n {\n variants: {\n variant: {\n default:\n \"border-[var(--axis-border-default)] bg-[var(--axis-surface-default)] text-[var(--axis-text-primary)]\",\n success:\n \"border-[var(--axis-border-success)] bg-[var(--axis-surface-success)] text-[var(--axis-text-success)]\",\n error:\n \"border-[var(--axis-border-error)] bg-[var(--axis-surface-error)] text-[var(--axis-text-error)]\",\n warning:\n \"border-transparent bg-[var(--axis-surface-warning)] text-[var(--axis-text-warning)]\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n);\n\n/** 토스트 알림의 루트 컴포넌트. default, success, error, warning 변형을 지원하며 스와이프로 닫을 수 있다. */\nconst Toast = React.forwardRef<\n React.ElementRef<typeof ToastPrimitives.Root>,\n React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &\n VariantProps<typeof toastVariants>\n>(({ className, variant, ...props }, ref) => (\n <ToastPrimitives.Root\n ref={ref}\n className={cn(toastVariants({ variant }), className)}\n {...props}\n />\n));\nToast.displayName = ToastPrimitives.Root.displayName;\n\n/** 토스트 내에 표시되는 액션 버튼 컴포넌트. 사용자 상호작용을 위한 CTA를 제공한다. */\nconst ToastAction = React.forwardRef<\n React.ElementRef<typeof ToastPrimitives.Action>,\n React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>\n>(({ className, ...props }, ref) => (\n <ToastPrimitives.Action\n ref={ref}\n className={cn(\n \"inline-flex h-8 shrink-0 items-center justify-center rounded-md border px-3 text-sm font-medium transition-colors\",\n \"border-[var(--axis-border-default)] bg-transparent\",\n \"hover:bg-[var(--axis-surface-secondary)]\",\n \"focus:outline-none focus:ring-2 focus:ring-[var(--axis-border-focus)]\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n className\n )}\n {...props}\n />\n));\nToastAction.displayName = ToastPrimitives.Action.displayName;\n\n/** 토스트를 닫는 닫기 버튼 컴포넌트. 호버 시 표시되며 X 아이콘을 포함한다. */\nconst ToastClose = React.forwardRef<\n React.ElementRef<typeof ToastPrimitives.Close>,\n React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>\n>(({ className, ...props }, ref) => (\n <ToastPrimitives.Close\n ref={ref}\n className={cn(\n \"absolute right-2 top-2 rounded-md p-1 text-[var(--axis-text-secondary)] opacity-0 transition-opacity\",\n \"hover:text-[var(--axis-text-primary)]\",\n \"focus:opacity-100 focus:outline-none focus:ring-2\",\n \"group-hover:opacity-100\",\n className\n )}\n toast-close=\"\"\n {...props}\n >\n <svg className=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </ToastPrimitives.Close>\n));\nToastClose.displayName = ToastPrimitives.Close.displayName;\n\n/** 토스트의 제목을 표시하는 컴포넌트 */\nconst ToastTitle = React.forwardRef<\n React.ElementRef<typeof ToastPrimitives.Title>,\n React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>\n>(({ className, ...props }, ref) => (\n <ToastPrimitives.Title\n ref={ref}\n className={cn(\"text-sm font-semibold\", className)}\n {...props}\n />\n));\nToastTitle.displayName = ToastPrimitives.Title.displayName;\n\n/** 토스트의 상세 설명을 표시하는 컴포넌트 */\nconst ToastDescription = React.forwardRef<\n React.ElementRef<typeof ToastPrimitives.Description>,\n React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>\n>(({ className, ...props }, ref) => (\n <ToastPrimitives.Description\n ref={ref}\n className={cn(\"text-sm\", className)}\n {...props}\n />\n));\nToastDescription.displayName = ToastPrimitives.Description.displayName;\n\nexport {\n ToastProvider,\n ToastViewport,\n Toast,\n ToastTitle,\n ToastDescription,\n ToastClose,\n ToastAction,\n};\n"
19
19
  }
20
20
  ]
21
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axis-ds/mcp",
3
- "version": "1.4.9",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "description": "AXIS Design System - MCP Server for AI-assisted component discovery",
6
6
  "bin": {