@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(
|
|
62
|
-
const q =
|
|
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
|
|
2322
|
-
if (!existsSync3(
|
|
2323
|
-
const content = readFileSync4(
|
|
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:
|
|
3767
|
-
if (!
|
|
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(
|
|
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(
|
|
3851
|
+
function formatTokenNotFound(path13) {
|
|
3852
3852
|
return [
|
|
3853
|
-
`\uD1A0\uD070\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: \`${
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
7101
|
+
const baseDir = path11.resolve(params.targetDir || ".");
|
|
6503
7102
|
const results = [];
|
|
6504
|
-
const configExists = await fs6.pathExists(
|
|
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 =
|
|
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
|
|
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
|
|
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:
|
|
7563
|
+
async ({ path: path13 }) => {
|
|
6964
7564
|
try {
|
|
6965
|
-
const token = handleGetToken({ path:
|
|
6966
|
-
const formatted = formatTokenDetail(token,
|
|
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
|
|
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
|
}
|
package/dist/registry/index.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "axis",
|
|
3
3
|
"homepage": "https://axis-design-system.pages.dev",
|
|
4
|
-
"version": "1.8.
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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.
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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-
|
|
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
|
}
|
package/dist/registry/toast.json
CHANGED
|
@@ -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
|
}
|