@vizejs/vite-plugin-musea 0.0.1-alpha.11 → 0.0.1-alpha.113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/a11y-Bvx5TJb8.d.ts +61 -0
  2. package/dist/a11y-Bvx5TJb8.d.ts.map +1 -0
  3. package/dist/a11y-C6xqILwZ.js +305 -0
  4. package/dist/a11y-C6xqILwZ.js.map +1 -0
  5. package/dist/a11y.d.ts +3 -0
  6. package/dist/a11y.js +3 -0
  7. package/dist/autogen-D3Zjc3zI.d.ts +64 -0
  8. package/dist/autogen-D3Zjc3zI.d.ts.map +1 -0
  9. package/dist/autogen-ymQnARZK.js +193 -0
  10. package/dist/autogen-ymQnARZK.js.map +1 -0
  11. package/dist/autogen.d.ts +2 -0
  12. package/dist/autogen.js +3 -0
  13. package/dist/cli.js +207 -30
  14. package/dist/cli.js.map +1 -1
  15. package/dist/gallery/assets/abap-Cry0R76c.js +1 -0
  16. package/dist/gallery/assets/apex-GS4zZi0I.js +1 -0
  17. package/dist/gallery/assets/azcli-DMImymmY.js +1 -0
  18. package/dist/gallery/assets/bat-D6epFECU.js +1 -0
  19. package/dist/gallery/assets/bicep-7klDZ283.js +2 -0
  20. package/dist/gallery/assets/cameligo-PvLD8t4t.js +1 -0
  21. package/dist/gallery/assets/clojure-BTbSGpb3.js +1 -0
  22. package/dist/gallery/assets/codicon-DCmgc-ay.ttf +0 -0
  23. package/dist/gallery/assets/coffee-Bhl_9YuJ.js +1 -0
  24. package/dist/gallery/assets/cpp-CM5j04eT.js +1 -0
  25. package/dist/gallery/assets/csharp-Dh0Ee7SY.js +1 -0
  26. package/dist/gallery/assets/csp-CLRC61y6.js +1 -0
  27. package/dist/gallery/assets/css-B0t_muXd.js +3 -0
  28. package/dist/gallery/assets/css.worker-Cbw1kvi8.js +88 -0
  29. package/dist/gallery/assets/cssMode-CaF-1r5A.js +4 -0
  30. package/dist/gallery/assets/cypher-C5e5inIh.js +1 -0
  31. package/dist/gallery/assets/dart-DIK3l8YT.js +1 -0
  32. package/dist/gallery/assets/dockerfile-D7OAO0hl.js +1 -0
  33. package/dist/gallery/assets/ecl-CP7nM2KN.js +1 -0
  34. package/dist/gallery/assets/editor-B55U_qvj.css +1 -0
  35. package/dist/gallery/assets/editor-F8AxQWwE.css +1 -0
  36. package/dist/gallery/assets/editor.api-DXavAYrv.js +644 -0
  37. package/dist/gallery/assets/editor.main-BPGOQG9_.js +63 -0
  38. package/dist/gallery/assets/editor.worker-Cs7HTPcl.js +12 -0
  39. package/dist/gallery/assets/elixir-DNRIIj6-.js +1 -0
  40. package/dist/gallery/assets/flow9-BC5Cr9X0.js +1 -0
  41. package/dist/gallery/assets/freemarker2-BvLjdc1z.js +3 -0
  42. package/dist/gallery/assets/fsharp-52P4yqMh.js +1 -0
  43. package/dist/gallery/assets/go-yKE3zUfB.js +1 -0
  44. package/dist/gallery/assets/graphql-D3sNVCLc.js +1 -0
  45. package/dist/gallery/assets/handlebars-BHP-ru_f.js +1 -0
  46. package/dist/gallery/assets/hcl-BB7aW7AX.js +1 -0
  47. package/dist/gallery/assets/html-vs5ObZxz.js +1 -0
  48. package/dist/gallery/assets/html.worker-CYmk49z4.js +495 -0
  49. package/dist/gallery/assets/htmlMode-gtvFmrd-.js +4 -0
  50. package/dist/gallery/assets/index-8vvVUIZe.js +63 -0
  51. package/dist/gallery/assets/index-B-vh50zZ.css +1 -0
  52. package/dist/gallery/assets/ini-BdRufzJj.js +1 -0
  53. package/dist/gallery/assets/java-CeUu-z7Y.js +1 -0
  54. package/dist/gallery/assets/javascript-Dyi3DN3w.js +1 -0
  55. package/dist/gallery/assets/json.worker-CWR6J9Qf.js +51 -0
  56. package/dist/gallery/assets/jsonMode-FphWEj_M.js +10 -0
  57. package/dist/gallery/assets/julia-CXu-Fn93.js +1 -0
  58. package/dist/gallery/assets/kotlin-TwsjxLJ3.js +1 -0
  59. package/dist/gallery/assets/less-CviwWNG4.js +2 -0
  60. package/dist/gallery/assets/lexon-BTOivnjP.js +1 -0
  61. package/dist/gallery/assets/liquid-EbeZCh25.js +1 -0
  62. package/dist/gallery/assets/lua-6W3WJOvj.js +1 -0
  63. package/dist/gallery/assets/m3-tlthQ8Fo.js +1 -0
  64. package/dist/gallery/assets/markdown-CPR4Kr9O.js +1 -0
  65. package/dist/gallery/assets/mdx-CT3P33NE.js +1 -0
  66. package/dist/gallery/assets/mips-BfxZbsD8.js +1 -0
  67. package/dist/gallery/assets/monaco.contribution-G2ulgCOc.js +2 -0
  68. package/dist/gallery/assets/msdax-eKsr2VtO.js +1 -0
  69. package/dist/gallery/assets/mysql-D6-LO0bt.js +1 -0
  70. package/dist/gallery/assets/objective-c-DYtfYpNc.js +1 -0
  71. package/dist/gallery/assets/pascal-CPGyHbal.js +1 -0
  72. package/dist/gallery/assets/pascaligo-Dsp_VKxo.js +1 -0
  73. package/dist/gallery/assets/perl-CUVa2_Cu.js +1 -0
  74. package/dist/gallery/assets/pgsql-C2nbbU56.js +1 -0
  75. package/dist/gallery/assets/php-DxX2tlkL.js +1 -0
  76. package/dist/gallery/assets/pla-D55LHImG.js +1 -0
  77. package/dist/gallery/assets/postiats-Dw_nWtoT.js +1 -0
  78. package/dist/gallery/assets/powerquery-BldVOeNZ.js +1 -0
  79. package/dist/gallery/assets/powershell-fdqyoMut.js +1 -0
  80. package/dist/gallery/assets/protobuf-C-2cnAYL.js +2 -0
  81. package/dist/gallery/assets/pug-bDrVOc6m.js +1 -0
  82. package/dist/gallery/assets/python-CS5TXnQl.js +1 -0
  83. package/dist/gallery/assets/qsharp-B83Ol6AR.js +1 -0
  84. package/dist/gallery/assets/r-DAxg6zn-.js +1 -0
  85. package/dist/gallery/assets/razor-Bav3uDeU.js +1 -0
  86. package/dist/gallery/assets/redis-BSRYxJDu.js +1 -0
  87. package/dist/gallery/assets/redshift-BrtVU4Ki.js +1 -0
  88. package/dist/gallery/assets/restructuredtext-DdU6AjLQ.js +1 -0
  89. package/dist/gallery/assets/ruby-C-s7ovR-.js +1 -0
  90. package/dist/gallery/assets/rust-CmSb_pkG.js +1 -0
  91. package/dist/gallery/assets/sb-Bfo5Ukmr.js +1 -0
  92. package/dist/gallery/assets/scala-Cx2bkddK.js +1 -0
  93. package/dist/gallery/assets/scheme-DkT6GPaV.js +1 -0
  94. package/dist/gallery/assets/scss-DOPngiM2.js +3 -0
  95. package/dist/gallery/assets/shell-NYt6Xulf.js +1 -0
  96. package/dist/gallery/assets/solidity-CUiq_T3F.js +1 -0
  97. package/dist/gallery/assets/sophia-B4sI8Ij-.js +1 -0
  98. package/dist/gallery/assets/sparql-BNfhekQe.js +1 -0
  99. package/dist/gallery/assets/sql-Bzn3OZV3.js +1 -0
  100. package/dist/gallery/assets/st-CV0zI_0P.js +1 -0
  101. package/dist/gallery/assets/swift-CR3-zK7D.js +1 -0
  102. package/dist/gallery/assets/systemverilog-BxgPwTIi.js +1 -0
  103. package/dist/gallery/assets/tcl-BSnnsp36.js +1 -0
  104. package/dist/gallery/assets/ts.worker-B_5n269U.js +51339 -0
  105. package/dist/gallery/assets/tsMode-Czu8iBG1.js +11 -0
  106. package/dist/gallery/assets/twig-Bx06vatZ.js +1 -0
  107. package/dist/gallery/assets/typescript-r7pqYU1X.js +1 -0
  108. package/dist/gallery/assets/typespec-COSap3s7.js +1 -0
  109. package/dist/gallery/assets/vb-DU0VXhXP.js +1 -0
  110. package/dist/gallery/assets/wgsl-BegdTer-.js +298 -0
  111. package/dist/gallery/assets/xml-DSt5pfef.js +1 -0
  112. package/dist/gallery/assets/yaml-6SyqWhY0.js +1 -0
  113. package/dist/gallery/index.html +16 -0
  114. package/dist/index.d.ts +51 -3
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js +1531 -65
  117. package/dist/index.js.map +1 -1
  118. package/dist/{vrt-DRwtnkE5.js → vrt-DP87vGIA.js} +222 -289
  119. package/dist/vrt-DP87vGIA.js.map +1 -0
  120. package/dist/vrt-Vb4aqPZE.d.ts +461 -0
  121. package/dist/vrt-Vb4aqPZE.d.ts.map +1 -0
  122. package/dist/vrt.d.ts +2 -2
  123. package/dist/vrt.js +1 -1
  124. package/package.json +28 -5
  125. package/dist/vrt-BfuTRv-J.d.ts +0 -217
  126. package/dist/vrt-BfuTRv-J.d.ts.map +0 -1
  127. package/dist/vrt-DRwtnkE5.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,7 +1,10 @@
1
- import { MuseaVrtRunner, generateVrtJsonReport, generateVrtReport } from "./vrt-DRwtnkE5.js";
2
- import fs from "node:fs";
1
+ import { MuseaA11yRunner } from "./a11y-C6xqILwZ.js";
2
+ import { generateArtFile, writeArtFile } from "./autogen-ymQnARZK.js";
3
+ import { MuseaVrtRunner, generateVrtJsonReport, generateVrtReport } from "./vrt-DP87vGIA.js";
3
4
  import path from "node:path";
4
5
  import { createRequire } from "node:module";
6
+ import fs from "node:fs";
7
+ import { vizeConfigStore } from "@vizejs/vite-plugin";
5
8
 
6
9
  //#region src/style-dictionary.ts
7
10
  /**
@@ -83,18 +86,21 @@ function extractSubcategories(obj) {
83
86
  function isTokenValue(value) {
84
87
  if (typeof value !== "object" || value === null) return false;
85
88
  const obj = value;
86
- return "value" in obj && (typeof obj.value === "string" || typeof obj.value === "number");
89
+ return "value" in obj && (typeof obj.value === "string" || typeof obj.value === "number") || "$value" in obj && (typeof obj.$value === "string" || typeof obj.$value === "number");
87
90
  }
88
91
  /**
89
92
  * Normalize token to DesignToken interface.
90
93
  */
91
94
  function normalizeToken(raw) {
92
- return {
93
- value: raw.value,
94
- type: raw.type,
95
+ const token = {
96
+ value: raw.value ?? raw.$value,
97
+ type: raw.type ?? raw.$type,
95
98
  description: raw.description,
96
99
  attributes: raw.attributes
97
100
  };
101
+ if (raw.$tier === "primitive" || raw.$tier === "semantic") token.$tier = raw.$tier;
102
+ if (typeof raw.$reference === "string") token.$reference = raw.$reference;
103
+ return token;
98
104
  }
99
105
  /**
100
106
  * Format category name for display.
@@ -103,6 +109,168 @@ function formatCategoryName(name) {
103
109
  return name.replace(/[-_]/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
104
110
  }
105
111
  /**
112
+ * Flatten nested categories into a flat map keyed by dot-path.
113
+ */
114
+ function buildTokenMap(categories, prefix = []) {
115
+ const map = {};
116
+ for (const cat of categories) {
117
+ const catKey = cat.name.toLowerCase().replace(/\s+/g, "-");
118
+ const catPath = [...prefix, catKey];
119
+ for (const [name, token] of Object.entries(cat.tokens)) {
120
+ const dotPath = [...catPath, name].join(".");
121
+ map[dotPath] = token;
122
+ }
123
+ if (cat.subcategories) {
124
+ const subMap = buildTokenMap(cat.subcategories, catPath);
125
+ Object.assign(map, subMap);
126
+ }
127
+ }
128
+ return map;
129
+ }
130
+ const REFERENCE_PATTERN = /^\{(.+)\}$/;
131
+ const MAX_RESOLVE_DEPTH = 10;
132
+ /**
133
+ * Resolve references in categories, setting $tier, $reference, and $resolvedValue.
134
+ */
135
+ function resolveReferences(categories, tokenMap) {
136
+ for (const cat of categories) {
137
+ for (const token of Object.values(cat.tokens)) resolveTokenReference(token, tokenMap);
138
+ if (cat.subcategories) resolveReferences(cat.subcategories, tokenMap);
139
+ }
140
+ }
141
+ function resolveTokenReference(token, tokenMap) {
142
+ if (typeof token.value === "string") {
143
+ const match = token.value.match(REFERENCE_PATTERN);
144
+ if (match) {
145
+ token.$tier = token.$tier ?? "semantic";
146
+ token.$reference = match[1];
147
+ token.$resolvedValue = resolveValue(match[1], tokenMap, 0, new Set());
148
+ return;
149
+ }
150
+ }
151
+ token.$tier = token.$tier ?? "primitive";
152
+ }
153
+ function resolveValue(ref, tokenMap, depth, visited) {
154
+ if (depth >= MAX_RESOLVE_DEPTH || visited.has(ref)) return void 0;
155
+ visited.add(ref);
156
+ const target = tokenMap[ref];
157
+ if (!target) return void 0;
158
+ if (typeof target.value === "string") {
159
+ const match = target.value.match(REFERENCE_PATTERN);
160
+ if (match) return resolveValue(match[1], tokenMap, depth + 1, visited);
161
+ }
162
+ return target.value;
163
+ }
164
+ /**
165
+ * Read raw JSON token file.
166
+ */
167
+ async function readRawTokenFile(tokensPath) {
168
+ const content = await fs.promises.readFile(tokensPath, "utf-8");
169
+ return JSON.parse(content);
170
+ }
171
+ /**
172
+ * Write raw JSON token file atomically (write tmp, rename).
173
+ */
174
+ async function writeRawTokenFile(tokensPath, data) {
175
+ const tmpPath = tokensPath + ".tmp";
176
+ await fs.promises.writeFile(tmpPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
177
+ await fs.promises.rename(tmpPath, tokensPath);
178
+ }
179
+ /**
180
+ * Set a token at a dot-separated path in the raw JSON structure.
181
+ */
182
+ function setTokenAtPath(data, dotPath, token) {
183
+ const parts = dotPath.split(".");
184
+ let current = data;
185
+ for (let i = 0; i < parts.length - 1; i++) {
186
+ const key = parts[i];
187
+ if (typeof current[key] !== "object" || current[key] === null) current[key] = {};
188
+ current = current[key];
189
+ }
190
+ const leafKey = parts[parts.length - 1];
191
+ const raw = { value: token.value };
192
+ if (token.type) raw.type = token.type;
193
+ if (token.description) raw.description = token.description;
194
+ if (token.$tier) raw.$tier = token.$tier;
195
+ if (token.$reference) raw.$reference = token.$reference;
196
+ if (token.attributes) raw.attributes = token.attributes;
197
+ current[leafKey] = raw;
198
+ }
199
+ /**
200
+ * Delete a token at a dot-separated path, cleaning empty parents.
201
+ */
202
+ function deleteTokenAtPath(data, dotPath) {
203
+ const parts = dotPath.split(".");
204
+ const parents = [];
205
+ let current = data;
206
+ for (let i = 0; i < parts.length - 1; i++) {
207
+ const key = parts[i];
208
+ if (typeof current[key] !== "object" || current[key] === null) return false;
209
+ parents.push({
210
+ obj: current,
211
+ key
212
+ });
213
+ current = current[key];
214
+ }
215
+ const leafKey = parts[parts.length - 1];
216
+ if (!(leafKey in current)) return false;
217
+ delete current[leafKey];
218
+ for (let i = parents.length - 1; i >= 0; i--) {
219
+ const { obj, key } = parents[i];
220
+ const child = obj[key];
221
+ if (Object.keys(child).length === 0) delete obj[key];
222
+ else break;
223
+ }
224
+ return true;
225
+ }
226
+ /**
227
+ * Validate that a semantic reference points to an existing token and has no cycles.
228
+ */
229
+ function validateSemanticReference(tokenMap, reference, selfPath) {
230
+ if (!tokenMap[reference]) return {
231
+ valid: false,
232
+ error: `Reference target "${reference}" does not exist`
233
+ };
234
+ const visited = new Set();
235
+ if (selfPath) visited.add(selfPath);
236
+ let current = reference;
237
+ let depth = 0;
238
+ while (depth < MAX_RESOLVE_DEPTH) {
239
+ if (visited.has(current)) return {
240
+ valid: false,
241
+ error: `Circular reference detected at "${current}"`
242
+ };
243
+ visited.add(current);
244
+ const target = tokenMap[current];
245
+ if (!target) break;
246
+ if (typeof target.value === "string") {
247
+ const match = target.value.match(REFERENCE_PATTERN);
248
+ if (match) {
249
+ current = match[1];
250
+ depth++;
251
+ continue;
252
+ }
253
+ }
254
+ break;
255
+ }
256
+ if (depth >= MAX_RESOLVE_DEPTH) return {
257
+ valid: false,
258
+ error: "Reference chain too deep (max 10)"
259
+ };
260
+ return { valid: true };
261
+ }
262
+ /**
263
+ * Find all tokens that reference the given path.
264
+ */
265
+ function findDependentTokens(tokenMap, targetPath) {
266
+ const dependents = [];
267
+ for (const [path$1, token] of Object.entries(tokenMap)) if (typeof token.value === "string") {
268
+ const match = token.value.match(REFERENCE_PATTERN);
269
+ if (match && match[1] === targetPath) dependents.push(path$1);
270
+ }
271
+ return dependents;
272
+ }
273
+ /**
106
274
  * Generate HTML documentation for tokens.
107
275
  */
108
276
  function generateTokensHtml(categories) {
@@ -272,6 +440,97 @@ async function processStyleDictionary(config) {
272
440
  }
273
441
  };
274
442
  }
443
+ /**
444
+ * Normalize a token value for comparison.
445
+ * - Lowercase, trim
446
+ * - Leading-zero: `.5rem` → `0.5rem`
447
+ * - Short hex: `#fff` → `#ffffff`
448
+ */
449
+ function normalizeTokenValue(value) {
450
+ let v = String(value).trim().toLowerCase();
451
+ const shortHex = v.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?$/);
452
+ if (shortHex) {
453
+ const [, r, g, b, a] = shortHex;
454
+ v = a ? `#${r}${r}${g}${g}${b}${b}${a}${a}` : `#${r}${r}${g}${g}${b}${b}`;
455
+ }
456
+ v = v.replace(/(?<![0-9])\.(\d)/g, "0.$1");
457
+ return v;
458
+ }
459
+ const STYLE_BLOCK_RE = /<style[^>]*>([\s\S]*?)<\/style>/g;
460
+ const CSS_PROPERTY_RE = /^\s*([\w-]+)\s*:\s*(.+?)\s*;?\s*$/;
461
+ /**
462
+ * Scan art file sources for token value matches in `<style>` blocks.
463
+ */
464
+ function scanTokenUsage(artFiles, tokenMap) {
465
+ const valueLookup = new Map();
466
+ for (const [tokenPath, token] of Object.entries(tokenMap)) {
467
+ const rawValue = token.$resolvedValue ?? token.value;
468
+ const normalized = normalizeTokenValue(rawValue);
469
+ if (!normalized) continue;
470
+ const existing = valueLookup.get(normalized);
471
+ if (existing) existing.push(tokenPath);
472
+ else valueLookup.set(normalized, [tokenPath]);
473
+ }
474
+ const usageMap = {};
475
+ for (const [artPath, artInfo] of artFiles) {
476
+ let source;
477
+ try {
478
+ source = fs.readFileSync(artPath, "utf-8");
479
+ } catch {
480
+ continue;
481
+ }
482
+ const allLines = source.split("\n");
483
+ const styleRegions = [];
484
+ let match;
485
+ STYLE_BLOCK_RE.lastIndex = 0;
486
+ while ((match = STYLE_BLOCK_RE.exec(source)) !== null) {
487
+ const beforeMatch = source.slice(0, match.index);
488
+ const startTag = source.slice(match.index, match.index + match[0].indexOf(match[1]));
489
+ const startLine = beforeMatch.split("\n").length + startTag.split("\n").length - 1;
490
+ styleRegions.push({
491
+ startLine,
492
+ content: match[1]
493
+ });
494
+ }
495
+ for (const region of styleRegions) {
496
+ const lines = region.content.split("\n");
497
+ for (let i = 0; i < lines.length; i++) {
498
+ const line = lines[i];
499
+ const propMatch = line.match(CSS_PROPERTY_RE);
500
+ if (!propMatch) continue;
501
+ const property = propMatch[1];
502
+ const valueStr = propMatch[2];
503
+ const valueParts = valueStr.split(/\s+/);
504
+ for (const part of valueParts) {
505
+ const normalizedPart = normalizeTokenValue(part);
506
+ const matchingTokens = valueLookup.get(normalizedPart);
507
+ if (!matchingTokens) continue;
508
+ const lineNumber = region.startLine + i;
509
+ const lineContent = allLines[lineNumber - 1]?.trim() ?? line.trim();
510
+ for (const tokenPath of matchingTokens) {
511
+ if (!usageMap[tokenPath]) usageMap[tokenPath] = [];
512
+ let entry = usageMap[tokenPath].find((e) => e.artPath === artPath);
513
+ if (!entry) {
514
+ entry = {
515
+ artPath,
516
+ artTitle: artInfo.metadata.title,
517
+ artCategory: artInfo.metadata.category,
518
+ matches: []
519
+ };
520
+ usageMap[tokenPath].push(entry);
521
+ }
522
+ if (!entry.matches.some((m) => m.line === lineNumber && m.property === property)) entry.matches.push({
523
+ line: lineNumber,
524
+ lineContent,
525
+ property
526
+ });
527
+ }
528
+ }
529
+ }
530
+ }
531
+ }
532
+ return usageMap;
533
+ }
275
534
 
276
535
  //#endregion
277
536
  //#region src/index.ts
@@ -290,17 +549,106 @@ function loadNative() {
290
549
  }
291
550
  }
292
551
  /**
552
+ * JS-based fallback for SFC analysis when native `analyzeSfc` is not available.
553
+ * Uses regex parsing to extract props and emits from Vue SFC source.
554
+ */
555
+ function analyzeSfcFallback(source, _options) {
556
+ try {
557
+ const props = [];
558
+ const emits = [];
559
+ const scriptSetupMatch = source.match(/<script\s+[^>]*setup[^>]*>([\s\S]*?)<\/script>/);
560
+ if (!scriptSetupMatch) {
561
+ const scriptMatch = source.match(/<script[^>]*>([\s\S]*?)<\/script>/);
562
+ if (!scriptMatch) return {
563
+ props: [],
564
+ emits: []
565
+ };
566
+ }
567
+ const scriptContent = scriptSetupMatch?.[1] || "";
568
+ const propsMatch = scriptContent.match(/defineProps\s*<\s*\{([\s\S]*?)\}>\s*\(/);
569
+ const propsMatch2 = scriptContent.match(/defineProps\s*<\s*\{([\s\S]*?)\}>/);
570
+ const propsBody = propsMatch?.[1] || propsMatch2?.[1];
571
+ if (propsBody) {
572
+ const lines = propsBody.split("\n");
573
+ let i = 0;
574
+ while (i < lines.length) {
575
+ const line = lines[i].trim();
576
+ if (line.startsWith("/**") || line.startsWith("*") || line.startsWith("*/")) {
577
+ i++;
578
+ continue;
579
+ }
580
+ const propMatch = line.match(/^(\w+)(\?)?:\s*(.+?)(?:;?\s*)$/);
581
+ if (propMatch) {
582
+ const name = propMatch[1];
583
+ const optional = !!propMatch[2];
584
+ let type = propMatch[3].replace(/;$/, "").trim();
585
+ const defaultPattern = new RegExp(`\\b${name}\\s*=\\s*([^,}\\n]+)`);
586
+ const defaultMatch = scriptContent.match(defaultPattern);
587
+ const defaultValue = defaultMatch ? defaultMatch[1].trim() : void 0;
588
+ props.push({
589
+ name,
590
+ type,
591
+ required: !optional && defaultValue === void 0,
592
+ ...defaultValue !== void 0 ? { default_value: defaultValue } : {}
593
+ });
594
+ }
595
+ i++;
596
+ }
597
+ }
598
+ const emitsMatch = scriptContent.match(/defineEmits\s*<\s*\{([\s\S]*?)\}>/);
599
+ if (emitsMatch) {
600
+ const emitsBody = emitsMatch[1];
601
+ const emitRegex = /(\w+)\s*:/g;
602
+ let match;
603
+ while ((match = emitRegex.exec(emitsBody)) !== null) emits.push(match[1]);
604
+ }
605
+ return {
606
+ props,
607
+ emits
608
+ };
609
+ } catch {
610
+ return {
611
+ props: [],
612
+ emits: []
613
+ };
614
+ }
615
+ }
616
+ /**
617
+ * Build the theme config object from plugin options for runtime injection.
618
+ */
619
+ function buildThemeConfig(theme) {
620
+ if (!theme) return void 0;
621
+ if (typeof theme === "string") return { default: theme };
622
+ const themes = Array.isArray(theme) ? theme : [theme];
623
+ const custom = {};
624
+ for (const t of themes) custom[t.name] = {
625
+ base: t.base,
626
+ colors: t.colors
627
+ };
628
+ return {
629
+ default: themes[0].name,
630
+ custom
631
+ };
632
+ }
633
+ /**
293
634
  * Create Musea Vite plugin.
294
635
  */
295
636
  function musea(options = {}) {
296
- const include = options.include ?? ["**/*.art.vue"];
297
- const exclude = options.exclude ?? ["node_modules/**", "dist/**"];
298
- const basePath = options.basePath ?? "/__musea__";
299
- const storybookCompat = options.storybookCompat ?? false;
637
+ let include = options.include ?? ["**/*.art.vue"];
638
+ let exclude = options.exclude ?? ["node_modules/**", "dist/**"];
639
+ let basePath = options.basePath ?? "/__musea__";
640
+ let storybookCompat = options.storybookCompat ?? false;
300
641
  const storybookOutDir = options.storybookOutDir ?? ".storybook/stories";
642
+ let inlineArt = options.inlineArt ?? false;
643
+ const tokensPath = options.tokensPath;
644
+ const themeConfig = buildThemeConfig(options.theme);
645
+ const previewCss = options.previewCss ?? [];
646
+ const previewSetup = options.previewSetup;
301
647
  let config;
302
648
  let server = null;
303
649
  const artFiles = new Map();
650
+ let resolvedPreviewCss = [];
651
+ let resolvedPreviewSetup = null;
304
652
  const mainPlugin = {
305
653
  name: "vite-plugin-musea",
306
654
  enforce: "pre",
@@ -309,15 +657,63 @@ function musea(options = {}) {
309
657
  },
310
658
  configResolved(resolvedConfig) {
311
659
  config = resolvedConfig;
660
+ const vizeConfig = vizeConfigStore.get(resolvedConfig.root);
661
+ if (vizeConfig?.musea) {
662
+ const mc = vizeConfig.musea;
663
+ if (!options.include && mc.include) include = mc.include;
664
+ if (!options.exclude && mc.exclude) exclude = mc.exclude;
665
+ if (!options.basePath && mc.basePath) basePath = mc.basePath;
666
+ if (options.storybookCompat === void 0 && mc.storybookCompat !== void 0) storybookCompat = mc.storybookCompat;
667
+ if (options.inlineArt === void 0 && mc.inlineArt !== void 0) inlineArt = mc.inlineArt;
668
+ }
669
+ resolvedPreviewCss = previewCss.map((cssPath) => path.isAbsolute(cssPath) ? cssPath : path.resolve(resolvedConfig.root, cssPath));
670
+ if (previewSetup) resolvedPreviewSetup = path.isAbsolute(previewSetup) ? previewSetup : path.resolve(resolvedConfig.root, previewSetup);
312
671
  },
313
672
  configureServer(devServer) {
314
673
  server = devServer;
315
674
  devServer.middlewares.use(basePath, async (req, res, next) => {
316
- if (req.url === "/" || req.url === "/index.html") {
317
- const html = generateGalleryHtml(basePath);
318
- res.setHeader("Content-Type", "text/html");
319
- res.end(html);
320
- return;
675
+ const url = req.url || "/";
676
+ if (url === "/" || url === "/index.html" || url.startsWith("/tokens") || url.startsWith("/component/")) {
677
+ const galleryDistDir = path.resolve(path.dirname(new URL(import.meta.url).pathname), "gallery");
678
+ const indexHtmlPath = path.join(galleryDistDir, "index.html");
679
+ try {
680
+ await fs.promises.access(indexHtmlPath);
681
+ let html = await fs.promises.readFile(indexHtmlPath, "utf-8");
682
+ const themeScript = themeConfig ? `window.__MUSEA_THEME_CONFIG__=${JSON.stringify(themeConfig)};` : "";
683
+ html = html.replace("</head>", `<script>window.__MUSEA_BASE_PATH__='${basePath}';${themeScript}</script></head>`);
684
+ res.setHeader("Content-Type", "text/html");
685
+ res.end(html);
686
+ return;
687
+ } catch {
688
+ const html = generateGalleryHtml(basePath, themeConfig);
689
+ res.setHeader("Content-Type", "text/html");
690
+ res.end(html);
691
+ return;
692
+ }
693
+ }
694
+ if (url.startsWith("/assets/")) {
695
+ const galleryDistDir = path.resolve(path.dirname(new URL(import.meta.url).pathname), "gallery");
696
+ const filePath = path.join(galleryDistDir, url);
697
+ try {
698
+ const stat = await fs.promises.stat(filePath);
699
+ if (stat.isFile()) {
700
+ const content = await fs.promises.readFile(filePath);
701
+ const ext = path.extname(filePath);
702
+ const mimeTypes = {
703
+ ".js": "application/javascript",
704
+ ".css": "text/css",
705
+ ".svg": "image/svg+xml",
706
+ ".png": "image/png",
707
+ ".ico": "image/x-icon",
708
+ ".woff2": "font/woff2",
709
+ ".woff": "font/woff"
710
+ };
711
+ res.setHeader("Content-Type", mimeTypes[ext] || "application/octet-stream");
712
+ res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
713
+ res.end(content);
714
+ return;
715
+ }
716
+ } catch {}
321
717
  }
322
718
  next();
323
719
  });
@@ -343,7 +739,7 @@ function musea(options = {}) {
343
739
  return;
344
740
  }
345
741
  const variantComponentName = toPascalCase(variant.name);
346
- const moduleCode = generatePreviewModule(art, variantComponentName, variant.name);
742
+ const moduleCode = generatePreviewModule(art, variantComponentName, variant.name, resolvedPreviewCss, resolvedPreviewSetup);
347
743
  try {
348
744
  const result = await devServer.transformRequest(`virtual:musea-preview:${artPath}:${variantName}`);
349
745
  if (result) {
@@ -378,8 +774,7 @@ function musea(options = {}) {
378
774
  res.end("Variant not found");
379
775
  return;
380
776
  }
381
- const rawHtml = generatePreviewHtml(art, variant, basePath);
382
- const html = await devServer.transformIndexHtml(`${basePath}/preview?art=${encodeURIComponent(artPath)}&variant=${encodeURIComponent(variantName)}`, rawHtml);
777
+ const html = generatePreviewHtml(art, variant, basePath, config.base);
383
778
  res.setHeader("Content-Type", "text/html");
384
779
  res.end(html);
385
780
  });
@@ -416,21 +811,525 @@ function musea(options = {}) {
416
811
  }
417
812
  });
418
813
  devServer.middlewares.use(`${basePath}/api`, async (req, res, next) => {
419
- if (req.url === "/arts" && req.method === "GET") {
814
+ const sendJson = (data, status = 200) => {
815
+ res.statusCode = status;
420
816
  res.setHeader("Content-Type", "application/json");
421
- res.end(JSON.stringify(Array.from(artFiles.values())));
817
+ res.end(JSON.stringify(data));
818
+ };
819
+ const sendError = (message, status = 500) => {
820
+ sendJson({ error: message }, status);
821
+ };
822
+ if (req.url === "/arts" && req.method === "GET") {
823
+ sendJson(Array.from(artFiles.values()));
824
+ return;
825
+ }
826
+ if (req.url === "/tokens/usage" && req.method === "GET") {
827
+ if (!tokensPath) {
828
+ sendJson({});
829
+ return;
830
+ }
831
+ try {
832
+ const absoluteTokensPath = path.resolve(config.root, tokensPath);
833
+ const categories = await parseTokens(absoluteTokensPath);
834
+ const tokenMap = buildTokenMap(categories);
835
+ resolveReferences(categories, tokenMap);
836
+ const resolvedTokenMap = buildTokenMap(categories);
837
+ const usage = scanTokenUsage(artFiles, resolvedTokenMap);
838
+ sendJson(usage);
839
+ } catch (e) {
840
+ console.error("[musea] Failed to scan token usage:", e);
841
+ sendJson({});
842
+ }
843
+ return;
844
+ }
845
+ if (req.url === "/tokens" && req.method === "GET") {
846
+ if (!tokensPath) {
847
+ sendJson({
848
+ categories: [],
849
+ tokenMap: {},
850
+ meta: {
851
+ filePath: "",
852
+ tokenCount: 0,
853
+ primitiveCount: 0,
854
+ semanticCount: 0
855
+ }
856
+ });
857
+ return;
858
+ }
859
+ try {
860
+ const absoluteTokensPath = path.resolve(config.root, tokensPath);
861
+ const categories = await parseTokens(absoluteTokensPath);
862
+ const tokenMap = buildTokenMap(categories);
863
+ resolveReferences(categories, tokenMap);
864
+ const resolvedTokenMap = buildTokenMap(categories);
865
+ let primitiveCount = 0;
866
+ let semanticCount = 0;
867
+ for (const token of Object.values(resolvedTokenMap)) if (token.$tier === "semantic") semanticCount++;
868
+ else primitiveCount++;
869
+ sendJson({
870
+ categories,
871
+ tokenMap: resolvedTokenMap,
872
+ meta: {
873
+ filePath: absoluteTokensPath,
874
+ tokenCount: Object.keys(resolvedTokenMap).length,
875
+ primitiveCount,
876
+ semanticCount
877
+ }
878
+ });
879
+ } catch (e) {
880
+ console.error("[musea] Failed to load tokens:", e);
881
+ sendJson({
882
+ categories: [],
883
+ tokenMap: {},
884
+ error: String(e)
885
+ });
886
+ }
887
+ return;
888
+ }
889
+ if (req.url === "/tokens" && req.method === "POST") {
890
+ if (!tokensPath) {
891
+ sendError("No tokens path configured", 400);
892
+ return;
893
+ }
894
+ let body = "";
895
+ req.on("data", (chunk) => {
896
+ body += chunk;
897
+ });
898
+ req.on("end", async () => {
899
+ try {
900
+ const { path: dotPath, token } = JSON.parse(body);
901
+ if (!dotPath || !token || token.value === void 0) {
902
+ sendError("Missing required fields: path, token.value", 400);
903
+ return;
904
+ }
905
+ const absoluteTokensPath = path.resolve(config.root, tokensPath);
906
+ const rawData = await readRawTokenFile(absoluteTokensPath);
907
+ const currentCategories = await parseTokens(absoluteTokensPath);
908
+ const currentMap = buildTokenMap(currentCategories);
909
+ if (currentMap[dotPath]) {
910
+ sendError(`Token already exists at path "${dotPath}"`, 409);
911
+ return;
912
+ }
913
+ if (token.$reference) {
914
+ const validation = validateSemanticReference(currentMap, token.$reference, dotPath);
915
+ if (!validation.valid) {
916
+ sendError(validation.error, 400);
917
+ return;
918
+ }
919
+ token.value = `{${token.$reference}}`;
920
+ token.$tier = "semantic";
921
+ }
922
+ setTokenAtPath(rawData, dotPath, token);
923
+ await writeRawTokenFile(absoluteTokensPath, rawData);
924
+ const categories = await parseTokens(absoluteTokensPath);
925
+ const tokenMap = buildTokenMap(categories);
926
+ resolveReferences(categories, tokenMap);
927
+ const resolvedTokenMap = buildTokenMap(categories);
928
+ sendJson({
929
+ categories,
930
+ tokenMap: resolvedTokenMap
931
+ }, 201);
932
+ } catch (e) {
933
+ sendError(e instanceof Error ? e.message : String(e));
934
+ }
935
+ });
936
+ return;
937
+ }
938
+ if (req.url === "/tokens" && req.method === "PUT") {
939
+ if (!tokensPath) {
940
+ sendError("No tokens path configured", 400);
941
+ return;
942
+ }
943
+ let body = "";
944
+ req.on("data", (chunk) => {
945
+ body += chunk;
946
+ });
947
+ req.on("end", async () => {
948
+ try {
949
+ const { path: dotPath, token } = JSON.parse(body);
950
+ if (!dotPath || !token || token.value === void 0) {
951
+ sendError("Missing required fields: path, token.value", 400);
952
+ return;
953
+ }
954
+ const absoluteTokensPath = path.resolve(config.root, tokensPath);
955
+ if (token.$reference) {
956
+ const currentCategories = await parseTokens(absoluteTokensPath);
957
+ const currentMap = buildTokenMap(currentCategories);
958
+ const validation = validateSemanticReference(currentMap, token.$reference, dotPath);
959
+ if (!validation.valid) {
960
+ sendError(validation.error, 400);
961
+ return;
962
+ }
963
+ token.value = `{${token.$reference}}`;
964
+ token.$tier = "semantic";
965
+ }
966
+ const rawData = await readRawTokenFile(absoluteTokensPath);
967
+ setTokenAtPath(rawData, dotPath, token);
968
+ await writeRawTokenFile(absoluteTokensPath, rawData);
969
+ const categories = await parseTokens(absoluteTokensPath);
970
+ const tokenMap = buildTokenMap(categories);
971
+ resolveReferences(categories, tokenMap);
972
+ const resolvedTokenMap = buildTokenMap(categories);
973
+ sendJson({
974
+ categories,
975
+ tokenMap: resolvedTokenMap
976
+ });
977
+ } catch (e) {
978
+ sendError(e instanceof Error ? e.message : String(e));
979
+ }
980
+ });
981
+ return;
982
+ }
983
+ if (req.url === "/tokens" && req.method === "DELETE") {
984
+ if (!tokensPath) {
985
+ sendError("No tokens path configured", 400);
986
+ return;
987
+ }
988
+ let body = "";
989
+ req.on("data", (chunk) => {
990
+ body += chunk;
991
+ });
992
+ req.on("end", async () => {
993
+ try {
994
+ const { path: dotPath } = JSON.parse(body);
995
+ if (!dotPath) {
996
+ sendError("Missing required field: path", 400);
997
+ return;
998
+ }
999
+ const absoluteTokensPath = path.resolve(config.root, tokensPath);
1000
+ const currentCategories = await parseTokens(absoluteTokensPath);
1001
+ const currentMap = buildTokenMap(currentCategories);
1002
+ const dependents = findDependentTokens(currentMap, dotPath);
1003
+ const rawData = await readRawTokenFile(absoluteTokensPath);
1004
+ const deleted = deleteTokenAtPath(rawData, dotPath);
1005
+ if (!deleted) {
1006
+ sendError(`Token not found at path "${dotPath}"`, 404);
1007
+ return;
1008
+ }
1009
+ await writeRawTokenFile(absoluteTokensPath, rawData);
1010
+ const categories = await parseTokens(absoluteTokensPath);
1011
+ const tokenMap = buildTokenMap(categories);
1012
+ resolveReferences(categories, tokenMap);
1013
+ const resolvedTokenMap = buildTokenMap(categories);
1014
+ sendJson({
1015
+ categories,
1016
+ tokenMap: resolvedTokenMap,
1017
+ dependentsWarning: dependents.length > 0 ? dependents : void 0
1018
+ });
1019
+ } catch (e) {
1020
+ sendError(e instanceof Error ? e.message : String(e));
1021
+ }
1022
+ });
1023
+ return;
1024
+ }
1025
+ if (req.url?.startsWith("/arts/") && req.method === "PUT") {
1026
+ const rest = req.url.slice(6);
1027
+ const sourceMatch = rest.match(/^(.+)\/source$/);
1028
+ if (sourceMatch) {
1029
+ const artPath = decodeURIComponent(sourceMatch[1]);
1030
+ const art = artFiles.get(artPath);
1031
+ if (!art) {
1032
+ sendError("Art not found", 404);
1033
+ return;
1034
+ }
1035
+ let body = "";
1036
+ req.on("data", (chunk) => {
1037
+ body += chunk;
1038
+ });
1039
+ req.on("end", async () => {
1040
+ try {
1041
+ const { source } = JSON.parse(body);
1042
+ if (typeof source !== "string") {
1043
+ sendError("Missing required field: source", 400);
1044
+ return;
1045
+ }
1046
+ await fs.promises.writeFile(artPath, source, "utf-8");
1047
+ await processArtFile(artPath);
1048
+ sendJson({ success: true });
1049
+ } catch (e) {
1050
+ sendError(e instanceof Error ? e.message : String(e));
1051
+ }
1052
+ });
1053
+ return;
1054
+ }
1055
+ next();
422
1056
  return;
423
1057
  }
424
1058
  if (req.url?.startsWith("/arts/") && req.method === "GET") {
425
- const artPath = decodeURIComponent(req.url.slice(6));
426
- const art = artFiles.get(artPath);
427
- if (art) {
428
- res.setHeader("Content-Type", "application/json");
429
- res.end(JSON.stringify(art));
430
- } else {
431
- res.statusCode = 404;
432
- res.end(JSON.stringify({ error: "Art not found" }));
1059
+ const rest = req.url.slice(6);
1060
+ const sourceMatch = rest.match(/^(.+)\/source$/);
1061
+ const paletteMatch = rest.match(/^(.+)\/palette$/);
1062
+ const analysisMatch = rest.match(/^(.+)\/analysis$/);
1063
+ const docsMatch = rest.match(/^(.+)\/docs$/);
1064
+ const a11yMatch = rest.match(/^(.+)\/variants\/([^/]+)\/a11y$/);
1065
+ if (sourceMatch) {
1066
+ const artPath$1 = decodeURIComponent(sourceMatch[1]);
1067
+ const art$1 = artFiles.get(artPath$1);
1068
+ if (!art$1) {
1069
+ sendError("Art not found", 404);
1070
+ return;
1071
+ }
1072
+ try {
1073
+ const source = await fs.promises.readFile(artPath$1, "utf-8");
1074
+ sendJson({
1075
+ source,
1076
+ path: artPath$1
1077
+ });
1078
+ } catch (e) {
1079
+ sendError(e instanceof Error ? e.message : String(e));
1080
+ }
1081
+ return;
1082
+ }
1083
+ if (paletteMatch) {
1084
+ const artPath$1 = decodeURIComponent(paletteMatch[1]);
1085
+ const art$1 = artFiles.get(artPath$1);
1086
+ if (!art$1) {
1087
+ sendError("Art not found", 404);
1088
+ return;
1089
+ }
1090
+ try {
1091
+ const source = await fs.promises.readFile(artPath$1, "utf-8");
1092
+ const binding = loadNative();
1093
+ let palette;
1094
+ if (binding.generateArtPalette) palette = binding.generateArtPalette(source, { filename: artPath$1 });
1095
+ else palette = {
1096
+ title: art$1.metadata.title,
1097
+ controls: [],
1098
+ groups: [],
1099
+ json: "{}",
1100
+ typescript: ""
1101
+ };
1102
+ if (palette.controls.length === 0 && art$1.metadata.component) {
1103
+ const resolvedComponentPath = path.isAbsolute(art$1.metadata.component) ? art$1.metadata.component : path.resolve(path.dirname(artPath$1), art$1.metadata.component);
1104
+ try {
1105
+ const componentSource = await fs.promises.readFile(resolvedComponentPath, "utf-8");
1106
+ const analysis = binding.analyzeSfc ? binding.analyzeSfc(componentSource, { filename: resolvedComponentPath }) : analyzeSfcFallback(componentSource, { filename: resolvedComponentPath });
1107
+ if (analysis.props.length > 0) {
1108
+ palette.controls = analysis.props.map((prop) => {
1109
+ let control = "text";
1110
+ if (prop.type === "boolean") control = "boolean";
1111
+ else if (prop.type === "number") control = "number";
1112
+ else if (prop.type.includes("|") && !prop.type.includes("=>")) control = "select";
1113
+ const options$1 = [];
1114
+ if (control === "select") {
1115
+ const optionMatches = prop.type.match(/"([^"]+)"/g);
1116
+ if (optionMatches) for (const opt of optionMatches) {
1117
+ const val = opt.replace(/"/g, "");
1118
+ options$1.push({
1119
+ label: val,
1120
+ value: val
1121
+ });
1122
+ }
1123
+ }
1124
+ return {
1125
+ name: prop.name,
1126
+ control,
1127
+ default_value: prop.default_value !== void 0 ? prop.default_value === "true" ? true : prop.default_value === "false" ? false : typeof prop.default_value === "string" && prop.default_value.startsWith("\"") ? prop.default_value.replace(/^"|"$/g, "") : prop.default_value : void 0,
1128
+ description: void 0,
1129
+ required: prop.required,
1130
+ options: options$1,
1131
+ range: void 0,
1132
+ group: void 0
1133
+ };
1134
+ });
1135
+ palette.json = JSON.stringify({
1136
+ title: palette.title,
1137
+ controls: palette.controls
1138
+ }, null, 2);
1139
+ palette.typescript = `export interface ${palette.title}Props {\n${palette.controls.map((c) => ` ${c.name}${c.required ? "" : "?"}: ${c.control === "boolean" ? "boolean" : c.control === "number" ? "number" : c.control === "select" ? c.options.map((o) => `"${String(o.value)}"`).join(" | ") : "string"};`).join("\n")}\n}\n`;
1140
+ }
1141
+ } catch {}
1142
+ }
1143
+ sendJson(palette);
1144
+ } catch (e) {
1145
+ sendError(e instanceof Error ? e.message : String(e));
1146
+ }
1147
+ return;
1148
+ }
1149
+ if (analysisMatch) {
1150
+ const artPath$1 = decodeURIComponent(analysisMatch[1]);
1151
+ const art$1 = artFiles.get(artPath$1);
1152
+ if (!art$1) {
1153
+ sendError("Art not found", 404);
1154
+ return;
1155
+ }
1156
+ try {
1157
+ const resolvedComponentPath = art$1.isInline && art$1.componentPath ? art$1.componentPath : art$1.metadata.component ? path.isAbsolute(art$1.metadata.component) ? art$1.metadata.component : path.resolve(path.dirname(artPath$1), art$1.metadata.component) : null;
1158
+ if (resolvedComponentPath) {
1159
+ const source = await fs.promises.readFile(resolvedComponentPath, "utf-8");
1160
+ const binding = loadNative();
1161
+ if (binding.analyzeSfc) {
1162
+ const analysis = binding.analyzeSfc(source, { filename: resolvedComponentPath });
1163
+ sendJson(analysis);
1164
+ } else {
1165
+ const analysis = analyzeSfcFallback(source, { filename: resolvedComponentPath });
1166
+ sendJson(analysis);
1167
+ }
1168
+ } else sendJson({
1169
+ props: [],
1170
+ emits: []
1171
+ });
1172
+ } catch (e) {
1173
+ sendError(e instanceof Error ? e.message : String(e));
1174
+ }
1175
+ return;
1176
+ }
1177
+ if (docsMatch) {
1178
+ const artPath$1 = decodeURIComponent(docsMatch[1]);
1179
+ const art$1 = artFiles.get(artPath$1);
1180
+ if (!art$1) {
1181
+ sendError("Art not found", 404);
1182
+ return;
1183
+ }
1184
+ try {
1185
+ const source = await fs.promises.readFile(artPath$1, "utf-8");
1186
+ const binding = loadNative();
1187
+ if (binding.generateArtDoc) {
1188
+ const doc = binding.generateArtDoc(source, { filename: artPath$1 });
1189
+ let markdown = doc.markdown || "";
1190
+ const componentName = art$1.metadata.title || "Component";
1191
+ markdown = markdown.replace(/<Self(\s|>|\/)/g, `<${componentName}$1`).replace(/<\/Self>/g, `</${componentName}>`);
1192
+ markdown = markdown.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, lang, code) => {
1193
+ const lines = code.split("\n");
1194
+ let minIndent = Infinity;
1195
+ for (const line of lines) if (line.trim()) {
1196
+ const indent = line.match(/^(\s*)/)?.[1].length || 0;
1197
+ minIndent = Math.min(minIndent, indent);
1198
+ }
1199
+ if (minIndent === Infinity) minIndent = 0;
1200
+ let formatted;
1201
+ if (minIndent > 0) formatted = lines.map((line) => line.slice(minIndent)).join("\n");
1202
+ else {
1203
+ let restIndent = Infinity;
1204
+ for (let i = 1; i < lines.length; i++) if (lines[i].trim()) {
1205
+ const indent = lines[i].match(/^(\s*)/)?.[1].length || 0;
1206
+ restIndent = Math.min(restIndent, indent);
1207
+ }
1208
+ if (restIndent === Infinity || restIndent === 0) formatted = lines.join("\n");
1209
+ else formatted = lines.map((line, i) => i === 0 ? line : line.slice(restIndent)).join("\n");
1210
+ }
1211
+ return "```" + lang + "\n" + formatted + "```";
1212
+ });
1213
+ sendJson({
1214
+ ...doc,
1215
+ markdown
1216
+ });
1217
+ } else sendJson({
1218
+ markdown: "",
1219
+ title: art$1.metadata.title,
1220
+ variant_count: art$1.variants.length
1221
+ });
1222
+ } catch (e) {
1223
+ sendError(e instanceof Error ? e.message : String(e));
1224
+ }
1225
+ return;
433
1226
  }
1227
+ if (a11yMatch) {
1228
+ const artPath$1 = decodeURIComponent(a11yMatch[1]);
1229
+ const _variantName = decodeURIComponent(a11yMatch[2]);
1230
+ const art$1 = artFiles.get(artPath$1);
1231
+ if (!art$1) {
1232
+ sendError("Art not found", 404);
1233
+ return;
1234
+ }
1235
+ sendJson({
1236
+ violations: [],
1237
+ passes: 0,
1238
+ incomplete: 0
1239
+ });
1240
+ return;
1241
+ }
1242
+ const artPath = decodeURIComponent(rest);
1243
+ const art = artFiles.get(artPath);
1244
+ if (art) sendJson(art);
1245
+ else sendError("Art not found", 404);
1246
+ return;
1247
+ }
1248
+ if (req.url === "/preview-with-props" && req.method === "POST") {
1249
+ let body = "";
1250
+ req.on("data", (chunk) => {
1251
+ body += chunk;
1252
+ });
1253
+ req.on("end", () => {
1254
+ try {
1255
+ const { artPath: reqArtPath, variantName, props: propsOverride } = JSON.parse(body);
1256
+ const art = artFiles.get(reqArtPath);
1257
+ if (!art) {
1258
+ sendError("Art not found", 404);
1259
+ return;
1260
+ }
1261
+ const variant = art.variants.find((v) => v.name === variantName);
1262
+ if (!variant) {
1263
+ sendError("Variant not found", 404);
1264
+ return;
1265
+ }
1266
+ const variantComponentName = toPascalCase(variant.name);
1267
+ const moduleCode = generatePreviewModuleWithProps(art, variantComponentName, variant.name, propsOverride, resolvedPreviewCss, resolvedPreviewSetup);
1268
+ res.setHeader("Content-Type", "application/javascript");
1269
+ res.end(moduleCode);
1270
+ } catch (e) {
1271
+ sendError(e instanceof Error ? e.message : String(e));
1272
+ }
1273
+ });
1274
+ return;
1275
+ }
1276
+ if (req.url === "/generate" && req.method === "POST") {
1277
+ let body = "";
1278
+ req.on("data", (chunk) => {
1279
+ body += chunk;
1280
+ });
1281
+ req.on("end", async () => {
1282
+ try {
1283
+ const { componentPath: reqComponentPath, options: autogenOptions } = JSON.parse(body);
1284
+ const { generateArtFile: genArt } = await import("./autogen.js");
1285
+ const result = await genArt(reqComponentPath, autogenOptions);
1286
+ sendJson({
1287
+ generated: true,
1288
+ componentName: result.componentName,
1289
+ variants: result.variants,
1290
+ artFileContent: result.artFileContent
1291
+ });
1292
+ } catch (e) {
1293
+ sendError(e instanceof Error ? e.message : String(e));
1294
+ }
1295
+ });
1296
+ return;
1297
+ }
1298
+ if (req.url === "/run-vrt" && req.method === "POST") {
1299
+ let body = "";
1300
+ req.on("data", (chunk) => {
1301
+ body += chunk;
1302
+ });
1303
+ req.on("end", async () => {
1304
+ try {
1305
+ const { artPath, updateSnapshots } = JSON.parse(body);
1306
+ const { MuseaVrtRunner: MuseaVrtRunner$1 } = await import("./vrt.js");
1307
+ const runner = new MuseaVrtRunner$1({ snapshotDir: path.resolve(config.root, ".vize/snapshots") });
1308
+ const port = devServer.config.server.port || 5173;
1309
+ const baseUrl = `http://localhost:${port}`;
1310
+ let artsToTest = Array.from(artFiles.values());
1311
+ if (artPath) artsToTest = artsToTest.filter((a) => a.path === artPath);
1312
+ await runner.start();
1313
+ const results = await runner.runTests(artsToTest, baseUrl, { updateSnapshots });
1314
+ const summary = runner.getSummary(results);
1315
+ await runner.stop();
1316
+ sendJson({
1317
+ success: true,
1318
+ summary,
1319
+ results: results.map((r) => ({
1320
+ artPath: r.artPath,
1321
+ variantName: r.variantName,
1322
+ viewport: r.viewport.name,
1323
+ passed: r.passed,
1324
+ isNew: r.isNew,
1325
+ diffPercentage: r.diffPercentage,
1326
+ error: r.error
1327
+ }))
1328
+ });
1329
+ } catch (e) {
1330
+ sendError(e instanceof Error ? e.message : String(e));
1331
+ }
1332
+ });
434
1333
  return;
435
1334
  }
436
1335
  next();
@@ -440,12 +1339,30 @@ function musea(options = {}) {
440
1339
  await processArtFile(file);
441
1340
  console.log(`[musea] Reloaded: ${path.relative(config.root, file)}`);
442
1341
  }
1342
+ if (inlineArt && file.endsWith(".vue") && !file.endsWith(".art.vue")) {
1343
+ const hadArt = artFiles.has(file);
1344
+ const source = await fs.promises.readFile(file, "utf-8");
1345
+ if (source.includes("<art")) {
1346
+ await processArtFile(file);
1347
+ console.log(`[musea] Reloaded inline art: ${path.relative(config.root, file)}`);
1348
+ } else if (hadArt) {
1349
+ artFiles.delete(file);
1350
+ console.log(`[musea] Removed inline art: ${path.relative(config.root, file)}`);
1351
+ }
1352
+ }
443
1353
  });
444
1354
  devServer.watcher.on("add", async (file) => {
445
1355
  if (file.endsWith(".art.vue") && shouldProcess(file, include, exclude, config.root)) {
446
1356
  await processArtFile(file);
447
1357
  console.log(`[musea] Added: ${path.relative(config.root, file)}`);
448
1358
  }
1359
+ if (inlineArt && file.endsWith(".vue") && !file.endsWith(".art.vue")) {
1360
+ const source = await fs.promises.readFile(file, "utf-8");
1361
+ if (source.includes("<art")) {
1362
+ await processArtFile(file);
1363
+ console.log(`[musea] Added inline art: ${path.relative(config.root, file)}`);
1364
+ }
1365
+ }
449
1366
  });
450
1367
  devServer.watcher.on("unlink", (file) => {
451
1368
  if (artFiles.has(file)) {
@@ -453,9 +1370,24 @@ function musea(options = {}) {
453
1370
  console.log(`[musea] Removed: ${path.relative(config.root, file)}`);
454
1371
  }
455
1372
  });
1373
+ return () => {
1374
+ devServer.httpServer?.once("listening", () => {
1375
+ const address = devServer.httpServer?.address();
1376
+ if (address && typeof address === "object") {
1377
+ const protocol = devServer.config.server.https ? "https" : "http";
1378
+ const rawHost = address.address;
1379
+ const host = rawHost === "::" || rawHost === "::1" || rawHost === "0.0.0.0" || rawHost === "127.0.0.1" ? "localhost" : rawHost;
1380
+ const port = address.port;
1381
+ const url = `${protocol}://${host}:${port}${basePath}`;
1382
+ console.log();
1383
+ console.log(` \x1b[36m➜\x1b[0m \x1b[1mMusea Gallery:\x1b[0m \x1b[36m${url}\x1b[0m`);
1384
+ }
1385
+ });
1386
+ };
456
1387
  },
457
1388
  async buildStart() {
458
- const files = await scanArtFiles(config.root, include, exclude);
1389
+ console.log(`[musea] config.root: ${config.root}, include: ${JSON.stringify(include)}`);
1390
+ const files = await scanArtFiles(config.root, include, exclude, inlineArt);
459
1391
  console.log(`[musea] Found ${files.length} art files`);
460
1392
  for (const file of files) await processArtFile(file);
461
1393
  if (storybookCompat) await generateStorybookFiles(artFiles, config.root, storybookOutDir);
@@ -466,11 +1398,15 @@ function musea(options = {}) {
466
1398
  if (id.startsWith("virtual:musea-preview:")) return "\0musea-preview:" + id.slice(22);
467
1399
  if (id.startsWith("virtual:musea-art:")) {
468
1400
  const artPath = id.slice(18);
469
- if (artFiles.has(artPath)) return "\0musea-art:" + artPath;
1401
+ if (artFiles.has(artPath)) return "\0musea-art:" + artPath + "?musea-virtual";
470
1402
  }
471
1403
  if (id.endsWith(".art.vue")) {
472
1404
  const resolved = path.resolve(config.root, id);
473
- if (artFiles.has(resolved)) return VIRTUAL_MUSEA_PREFIX + resolved;
1405
+ if (artFiles.has(resolved)) return VIRTUAL_MUSEA_PREFIX + resolved + "?musea-virtual";
1406
+ }
1407
+ if (inlineArt && id.endsWith(".vue") && !id.endsWith(".art.vue")) {
1408
+ const resolved = path.resolve(config.root, id);
1409
+ if (artFiles.has(resolved)) return VIRTUAL_MUSEA_PREFIX + resolved + "?musea-virtual";
474
1410
  }
475
1411
  return null;
476
1412
  },
@@ -486,17 +1422,17 @@ function musea(options = {}) {
486
1422
  const art = artFiles.get(artPath);
487
1423
  if (art) {
488
1424
  const variantComponentName = toPascalCase(variantName);
489
- return generatePreviewModule(art, variantComponentName, variantName);
1425
+ return generatePreviewModule(art, variantComponentName, variantName, resolvedPreviewCss, resolvedPreviewSetup);
490
1426
  }
491
1427
  }
492
1428
  }
493
1429
  if (id.startsWith("\0musea-art:")) {
494
- const artPath = id.slice(11);
1430
+ const artPath = id.slice(11).replace(/\?musea-virtual$/, "");
495
1431
  const art = artFiles.get(artPath);
496
1432
  if (art) return generateArtModule(art, artPath);
497
1433
  }
498
1434
  if (id.startsWith(VIRTUAL_MUSEA_PREFIX)) {
499
- const realPath = id.slice(VIRTUAL_MUSEA_PREFIX.length);
1435
+ const realPath = id.slice(VIRTUAL_MUSEA_PREFIX.length).replace(/\?musea-virtual$/, "");
500
1436
  const art = artFiles.get(realPath);
501
1437
  if (art) return generateArtModule(art, realPath);
502
1438
  }
@@ -505,6 +1441,12 @@ function musea(options = {}) {
505
1441
  async handleHotUpdate(ctx) {
506
1442
  const { file } = ctx;
507
1443
  if (file.endsWith(".art.vue") && artFiles.has(file)) {
1444
+ await processArtFile(file);
1445
+ const virtualId = VIRTUAL_MUSEA_PREFIX + file + "?musea-virtual";
1446
+ const modules = server?.moduleGraph.getModulesByFile(virtualId);
1447
+ if (modules) return [...modules];
1448
+ }
1449
+ if (inlineArt && file.endsWith(".vue") && !file.endsWith(".art.vue") && artFiles.has(file)) {
508
1450
  await processArtFile(file);
509
1451
  const virtualId = VIRTUAL_MUSEA_PREFIX + file;
510
1452
  const modules = server?.moduleGraph.getModulesByFile(virtualId);
@@ -518,12 +1460,14 @@ function musea(options = {}) {
518
1460
  const source = await fs.promises.readFile(filePath, "utf-8");
519
1461
  const binding = loadNative();
520
1462
  const parsed = binding.parseArt(source, { filename: filePath });
1463
+ if (!parsed.variants || parsed.variants.length === 0) return;
1464
+ const isInline = !filePath.endsWith(".art.vue");
521
1465
  const info = {
522
1466
  path: filePath,
523
1467
  metadata: {
524
- title: parsed.metadata.title,
1468
+ title: parsed.metadata.title || (isInline ? path.basename(filePath, ".vue") : ""),
525
1469
  description: parsed.metadata.description,
526
- component: parsed.metadata.component,
1470
+ component: isInline ? void 0 : parsed.metadata.component,
527
1471
  category: parsed.metadata.category,
528
1472
  tags: parsed.metadata.tags,
529
1473
  status: parsed.metadata.status,
@@ -532,12 +1476,15 @@ function musea(options = {}) {
532
1476
  variants: parsed.variants.map((v) => ({
533
1477
  name: v.name,
534
1478
  template: v.template,
535
- isDefault: v.is_default,
536
- skipVrt: v.skip_vrt
1479
+ isDefault: v.isDefault,
1480
+ skipVrt: v.skipVrt
537
1481
  })),
538
- hasScriptSetup: parsed.has_script_setup,
539
- hasScript: parsed.has_script,
540
- styleCount: parsed.style_count
1482
+ hasScriptSetup: isInline ? false : parsed.hasScriptSetup,
1483
+ scriptSetupContent: !isInline && parsed.hasScriptSetup ? extractScriptSetupContent(source) : void 0,
1484
+ hasScript: parsed.hasScript,
1485
+ styleCount: parsed.styleCount,
1486
+ isInline,
1487
+ componentPath: isInline ? filePath : void 0
541
1488
  };
542
1489
  artFiles.set(filePath, info);
543
1490
  } catch (e) {
@@ -553,10 +1500,11 @@ function shouldProcess(file, include, exclude, root) {
553
1500
  return false;
554
1501
  }
555
1502
  function matchGlob(filepath, pattern) {
556
- const regex = pattern.replace(/\./g, "\\.").replace(/\*\*/g, ".*").replace(/\*(?!\*)/g, "[^/]*");
1503
+ const PLACEHOLDER = "<<GLOBSTAR>>";
1504
+ const regex = pattern.replaceAll("**", PLACEHOLDER).replace(/\./g, "\\.").replace(/\*/g, "[^/]*").replaceAll(PLACEHOLDER, ".*");
557
1505
  return new RegExp(`^${regex}$`).test(filepath);
558
1506
  }
559
- async function scanArtFiles(root, include, exclude) {
1507
+ async function scanArtFiles(root, include, exclude, scanInlineArt = false) {
560
1508
  const files = [];
561
1509
  async function scan(dir) {
562
1510
  const entries = await fs.promises.readdir(dir, { withFileTypes: true });
@@ -575,19 +1523,24 @@ async function scanArtFiles(root, include, exclude) {
575
1523
  files.push(fullPath);
576
1524
  break;
577
1525
  }
1526
+ } else if (scanInlineArt && entry.isFile() && entry.name.endsWith(".vue") && !entry.name.endsWith(".art.vue")) {
1527
+ const content = await fs.promises.readFile(fullPath, "utf-8");
1528
+ if (content.includes("<art")) files.push(fullPath);
578
1529
  }
579
1530
  }
580
1531
  }
581
1532
  await scan(root);
582
1533
  return files;
583
1534
  }
584
- function generateGalleryHtml(basePath) {
1535
+ function generateGalleryHtml(basePath, themeConfig) {
1536
+ const themeScript = themeConfig ? `window.__MUSEA_THEME_CONFIG__=${JSON.stringify(themeConfig)};` : "";
585
1537
  return `<!DOCTYPE html>
586
1538
  <html lang="en">
587
1539
  <head>
588
1540
  <meta charset="UTF-8">
589
1541
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
590
1542
  <title>Musea - Component Gallery</title>
1543
+ <script>window.__MUSEA_BASE_PATH__='${basePath}';${themeScript}</script>
591
1544
  <link rel="preconnect" href="https://fonts.googleapis.com">
592
1545
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
593
1546
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
@@ -1320,19 +2273,334 @@ export async function loadArts() {
1320
2273
  }
1321
2274
  `;
1322
2275
  }
1323
- function generatePreviewModule(art, variantComponentName, variantName) {
2276
+ const MUSEA_ADDONS_INIT_CODE = `
2277
+ function __museaInitAddons(container, variantName) {
2278
+ // === DOM event capture ===
2279
+ // Note: mousemove, mouseenter, mouseleave, pointermove are excluded as they are too noisy
2280
+ const CAPTURE_EVENTS = ['click','dblclick','input','change','submit','focus','blur','keydown','keyup','mousedown','mouseup','wheel','contextmenu','pointerdown','pointerup'];
2281
+ for (const evt of CAPTURE_EVENTS) {
2282
+ container.addEventListener(evt, (e) => {
2283
+ // Extract raw event properties
2284
+ const rawEvent = {
2285
+ type: e.type,
2286
+ bubbles: e.bubbles,
2287
+ cancelable: e.cancelable,
2288
+ composed: e.composed,
2289
+ defaultPrevented: e.defaultPrevented,
2290
+ eventPhase: e.eventPhase,
2291
+ isTrusted: e.isTrusted,
2292
+ timeStamp: e.timeStamp,
2293
+ };
2294
+ // Mouse/Pointer event properties
2295
+ if ('clientX' in e) {
2296
+ rawEvent.clientX = e.clientX;
2297
+ rawEvent.clientY = e.clientY;
2298
+ rawEvent.screenX = e.screenX;
2299
+ rawEvent.screenY = e.screenY;
2300
+ rawEvent.pageX = e.pageX;
2301
+ rawEvent.pageY = e.pageY;
2302
+ rawEvent.offsetX = e.offsetX;
2303
+ rawEvent.offsetY = e.offsetY;
2304
+ rawEvent.button = e.button;
2305
+ rawEvent.buttons = e.buttons;
2306
+ rawEvent.altKey = e.altKey;
2307
+ rawEvent.ctrlKey = e.ctrlKey;
2308
+ rawEvent.metaKey = e.metaKey;
2309
+ rawEvent.shiftKey = e.shiftKey;
2310
+ }
2311
+ // Keyboard event properties
2312
+ if ('key' in e) {
2313
+ rawEvent.key = e.key;
2314
+ rawEvent.code = e.code;
2315
+ rawEvent.repeat = e.repeat;
2316
+ rawEvent.altKey = e.altKey;
2317
+ rawEvent.ctrlKey = e.ctrlKey;
2318
+ rawEvent.metaKey = e.metaKey;
2319
+ rawEvent.shiftKey = e.shiftKey;
2320
+ }
2321
+ // Input event properties
2322
+ if ('inputType' in e) {
2323
+ rawEvent.inputType = e.inputType;
2324
+ rawEvent.data = e.data;
2325
+ }
2326
+ // Wheel event properties
2327
+ if ('deltaX' in e) {
2328
+ rawEvent.deltaX = e.deltaX;
2329
+ rawEvent.deltaY = e.deltaY;
2330
+ rawEvent.deltaZ = e.deltaZ;
2331
+ rawEvent.deltaMode = e.deltaMode;
2332
+ }
2333
+ const payload = {
2334
+ name: evt,
2335
+ target: e.target?.tagName,
2336
+ timestamp: Date.now(),
2337
+ source: 'dom',
2338
+ rawEvent,
2339
+ variantName
2340
+ };
2341
+ if (e.target && 'value' in e.target) {
2342
+ payload.value = e.target.value;
2343
+ }
2344
+ window.parent.postMessage({ type: 'musea:event', payload }, '*');
2345
+ }, true);
2346
+ }
2347
+
2348
+ // === Message handler for parent commands ===
2349
+ let measureActive = false;
2350
+ let measureOverlay = null;
2351
+ let measureLabel = null;
2352
+
2353
+ function toggleStyleById(id, enabled, css) {
2354
+ let el = document.getElementById(id);
2355
+ if (enabled) {
2356
+ if (!el) {
2357
+ el = document.createElement('style');
2358
+ el.id = id;
2359
+ el.textContent = css;
2360
+ document.head.appendChild(el);
2361
+ }
2362
+ } else {
2363
+ if (el) el.remove();
2364
+ }
2365
+ }
2366
+
2367
+ function createMeasureOverlay() {
2368
+ if (measureOverlay) return;
2369
+ measureOverlay = document.createElement('div');
2370
+ measureOverlay.id = 'musea-measure-overlay';
2371
+ measureOverlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:99999;';
2372
+ document.body.appendChild(measureOverlay);
2373
+
2374
+ measureLabel = document.createElement('div');
2375
+ measureLabel.className = 'musea-measure-label';
2376
+ measureLabel.style.cssText = 'position:fixed;background:#333;color:#fff;font-size:11px;padding:2px 6px;border-radius:3px;pointer-events:none;z-index:100000;display:none;';
2377
+ document.body.appendChild(measureLabel);
2378
+ }
2379
+
2380
+ function removeMeasureOverlay() {
2381
+ if (measureOverlay) { measureOverlay.remove(); measureOverlay = null; }
2382
+ if (measureLabel) { measureLabel.remove(); measureLabel = null; }
2383
+ }
2384
+
2385
+ function onMeasureMouseMove(e) {
2386
+ if (!measureActive || !measureOverlay) return;
2387
+ const el = document.elementFromPoint(e.clientX, e.clientY);
2388
+ if (!el || el === measureOverlay || el === measureLabel) return;
2389
+
2390
+ const rect = el.getBoundingClientRect();
2391
+ const cs = getComputedStyle(el);
2392
+ const mt = parseFloat(cs.marginTop) || 0;
2393
+ const mr = parseFloat(cs.marginRight) || 0;
2394
+ const mb = parseFloat(cs.marginBottom) || 0;
2395
+ const ml = parseFloat(cs.marginLeft) || 0;
2396
+ const bt = parseFloat(cs.borderTopWidth) || 0;
2397
+ const br = parseFloat(cs.borderRightWidth) || 0;
2398
+ const bb = parseFloat(cs.borderBottomWidth) || 0;
2399
+ const blw = parseFloat(cs.borderLeftWidth) || 0;
2400
+ const pt = parseFloat(cs.paddingTop) || 0;
2401
+ const pr = parseFloat(cs.paddingRight) || 0;
2402
+ const pb = parseFloat(cs.paddingBottom) || 0;
2403
+ const pl = parseFloat(cs.paddingLeft) || 0;
2404
+
2405
+ const cw = rect.width - blw - br - pl - pr;
2406
+ const ch = rect.height - bt - bb - pt - pb;
2407
+
2408
+ measureOverlay.innerHTML = ''
2409
+ // Margin
2410
+ + '<div style="position:fixed;background:rgba(255,165,0,0.3);'
2411
+ + 'left:' + (rect.left - ml) + 'px;top:' + (rect.top - mt) + 'px;'
2412
+ + 'width:' + (rect.width + ml + mr) + 'px;height:' + mt + 'px;"></div>'
2413
+ + '<div style="position:fixed;background:rgba(255,165,0,0.3);'
2414
+ + 'left:' + (rect.left - ml) + 'px;top:' + (rect.bottom) + 'px;'
2415
+ + 'width:' + (rect.width + ml + mr) + 'px;height:' + mb + 'px;"></div>'
2416
+ + '<div style="position:fixed;background:rgba(255,165,0,0.3);'
2417
+ + 'left:' + (rect.left - ml) + 'px;top:' + rect.top + 'px;'
2418
+ + 'width:' + ml + 'px;height:' + rect.height + 'px;"></div>'
2419
+ + '<div style="position:fixed;background:rgba(255,165,0,0.3);'
2420
+ + 'left:' + rect.right + 'px;top:' + rect.top + 'px;'
2421
+ + 'width:' + mr + 'px;height:' + rect.height + 'px;"></div>'
2422
+ // Border
2423
+ + '<div style="position:fixed;background:rgba(255,255,0,0.3);'
2424
+ + 'left:' + rect.left + 'px;top:' + rect.top + 'px;'
2425
+ + 'width:' + rect.width + 'px;height:' + bt + 'px;"></div>'
2426
+ + '<div style="position:fixed;background:rgba(255,255,0,0.3);'
2427
+ + 'left:' + rect.left + 'px;top:' + (rect.bottom - bb) + 'px;'
2428
+ + 'width:' + rect.width + 'px;height:' + bb + 'px;"></div>'
2429
+ + '<div style="position:fixed;background:rgba(255,255,0,0.3);'
2430
+ + 'left:' + rect.left + 'px;top:' + (rect.top + bt) + 'px;'
2431
+ + 'width:' + blw + 'px;height:' + (rect.height - bt - bb) + 'px;"></div>'
2432
+ + '<div style="position:fixed;background:rgba(255,255,0,0.3);'
2433
+ + 'left:' + (rect.right - br) + 'px;top:' + (rect.top + bt) + 'px;'
2434
+ + 'width:' + br + 'px;height:' + (rect.height - bt - bb) + 'px;"></div>'
2435
+ // Padding
2436
+ + '<div style="position:fixed;background:rgba(144,238,144,0.3);'
2437
+ + 'left:' + (rect.left + blw) + 'px;top:' + (rect.top + bt) + 'px;'
2438
+ + 'width:' + (rect.width - blw - br) + 'px;height:' + pt + 'px;"></div>'
2439
+ + '<div style="position:fixed;background:rgba(144,238,144,0.3);'
2440
+ + 'left:' + (rect.left + blw) + 'px;top:' + (rect.bottom - bb - pb) + 'px;'
2441
+ + 'width:' + (rect.width - blw - br) + 'px;height:' + pb + 'px;"></div>'
2442
+ + '<div style="position:fixed;background:rgba(144,238,144,0.3);'
2443
+ + 'left:' + (rect.left + blw) + 'px;top:' + (rect.top + bt + pt) + 'px;'
2444
+ + 'width:' + pl + 'px;height:' + (rect.height - bt - bb - pt - pb) + 'px;"></div>'
2445
+ + '<div style="position:fixed;background:rgba(144,238,144,0.3);'
2446
+ + 'left:' + (rect.right - br - pr) + 'px;top:' + (rect.top + bt + pt) + 'px;'
2447
+ + 'width:' + pr + 'px;height:' + (rect.height - bt - bb - pt - pb) + 'px;"></div>'
2448
+ // Content
2449
+ + '<div style="position:fixed;background:rgba(100,149,237,0.3);'
2450
+ + 'left:' + (rect.left + blw + pl) + 'px;top:' + (rect.top + bt + pt) + 'px;'
2451
+ + 'width:' + cw + 'px;height:' + ch + 'px;"></div>';
2452
+
2453
+ // Label
2454
+ measureLabel.textContent = Math.round(rect.width) + ' x ' + Math.round(rect.height);
2455
+ measureLabel.style.display = 'block';
2456
+ measureLabel.style.left = (rect.right + 8) + 'px';
2457
+ measureLabel.style.top = rect.top + 'px';
2458
+ }
2459
+
2460
+ window.addEventListener('message', (e) => {
2461
+ if (!e.data?.type?.startsWith('musea:')) return;
2462
+ const { type, payload } = e.data;
2463
+ switch (type) {
2464
+ case 'musea:set-background': {
2465
+ if (payload.pattern === 'checkerboard') {
2466
+ document.body.style.background = '';
2467
+ document.body.classList.add('musea-bg-checkerboard');
2468
+ } else {
2469
+ document.body.classList.remove('musea-bg-checkerboard');
2470
+ document.body.style.background = payload.color || '';
2471
+ }
2472
+ break;
2473
+ }
2474
+ case 'musea:toggle-outline': {
2475
+ toggleStyleById('musea-outline', payload.enabled,
2476
+ '* { outline: 1px solid rgba(255, 0, 0, 0.3) !important; }');
2477
+ break;
2478
+ }
2479
+ case 'musea:toggle-measure': {
2480
+ measureActive = payload.enabled;
2481
+ if (measureActive) {
2482
+ createMeasureOverlay();
2483
+ document.addEventListener('mousemove', onMeasureMouseMove);
2484
+ } else {
2485
+ document.removeEventListener('mousemove', onMeasureMouseMove);
2486
+ removeMeasureOverlay();
2487
+ }
2488
+ break;
2489
+ }
2490
+ case 'musea:set-props': {
2491
+ // Store props for remount - handled by preview module
2492
+ if (window.__museaSetProps) {
2493
+ window.__museaSetProps(payload.props || {});
2494
+ }
2495
+ break;
2496
+ }
2497
+ case 'musea:set-slots': {
2498
+ // Store slots for remount - handled by preview module
2499
+ if (window.__museaSetSlots) {
2500
+ window.__museaSetSlots(payload.slots || {});
2501
+ }
2502
+ break;
2503
+ }
2504
+ case 'musea:run-a11y': {
2505
+ // Run axe-core a11y test
2506
+ (async () => {
2507
+ try {
2508
+ // Dynamically load axe-core from CDN if not already loaded
2509
+ if (!window.axe) {
2510
+ const script = document.createElement('script');
2511
+ script.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.10.2/axe.min.js';
2512
+ script.crossOrigin = 'anonymous';
2513
+ await new Promise((resolve, reject) => {
2514
+ script.onload = resolve;
2515
+ script.onerror = reject;
2516
+ document.head.appendChild(script);
2517
+ });
2518
+ }
2519
+ // Run axe-core on the .musea-variant container only (not the full document)
2520
+ const context = document.querySelector('.musea-variant') || document;
2521
+ const results = await window.axe.run(context, {
2522
+ // Run all rules without restrictions for comprehensive testing
2523
+ resultTypes: ['violations', 'incomplete', 'passes']
2524
+ });
2525
+ window.parent.postMessage({
2526
+ type: 'musea:a11y-result',
2527
+ payload: {
2528
+ violations: results.violations.map(v => ({
2529
+ id: v.id,
2530
+ impact: v.impact,
2531
+ description: v.description,
2532
+ helpUrl: v.helpUrl,
2533
+ nodes: v.nodes.map(n => ({
2534
+ html: n.html,
2535
+ target: n.target,
2536
+ failureSummary: n.failureSummary
2537
+ }))
2538
+ })),
2539
+ passes: results.passes.length,
2540
+ incomplete: results.incomplete.length
2541
+ }
2542
+ }, '*');
2543
+ } catch (err) {
2544
+ window.parent.postMessage({
2545
+ type: 'musea:a11y-result',
2546
+ payload: {
2547
+ error: err instanceof Error ? err.message : String(err),
2548
+ violations: [],
2549
+ passes: 0,
2550
+ incomplete: 0
2551
+ }
2552
+ }, '*');
2553
+ }
2554
+ })();
2555
+ break;
2556
+ }
2557
+ }
2558
+ });
2559
+
2560
+ // Notify parent that iframe is ready
2561
+ window.parent.postMessage({ type: 'musea:ready', payload: {} }, '*');
2562
+ }
2563
+ `;
2564
+ function generatePreviewModule(art, variantComponentName, variantName, cssImports = [], previewSetup = null) {
1324
2565
  const artModuleId = `virtual:musea-art:${art.path}`;
1325
2566
  const escapedVariantName = escapeTemplate(variantName);
2567
+ const cssImportStatements = cssImports.map((cssPath) => `import '${cssPath}';`).join("\n");
2568
+ const setupImport = previewSetup ? `import __museaPreviewSetup from '${previewSetup}';` : "";
2569
+ const setupCall = previewSetup ? "await __museaPreviewSetup(app);" : "";
1326
2570
  return `
1327
- import { createApp } from 'vue';
2571
+ ${cssImportStatements}
2572
+ ${setupImport}
2573
+ import { createApp, reactive, h } from 'vue';
1328
2574
  import * as artModule from '${artModuleId}';
1329
2575
 
1330
2576
  const container = document.getElementById('app');
1331
2577
 
2578
+ ${MUSEA_ADDONS_INIT_CODE}
2579
+
2580
+ let currentApp = null;
2581
+ const propsOverride = reactive({});
2582
+ const slotsOverride = reactive({ default: '' });
2583
+
2584
+ window.__museaSetProps = (props) => {
2585
+ // Clear old keys
2586
+ for (const key of Object.keys(propsOverride)) {
2587
+ delete propsOverride[key];
2588
+ }
2589
+ Object.assign(propsOverride, props);
2590
+ };
2591
+
2592
+ window.__museaSetSlots = (slots) => {
2593
+ for (const key of Object.keys(slotsOverride)) {
2594
+ delete slotsOverride[key];
2595
+ }
2596
+ Object.assign(slotsOverride, slots);
2597
+ };
2598
+
1332
2599
  async function mount() {
1333
2600
  try {
1334
2601
  // Get the specific variant component
1335
2602
  const VariantComponent = artModule['${variantComponentName}'];
2603
+ const RawComponent = artModule.__component__;
1336
2604
 
1337
2605
  if (!VariantComponent) {
1338
2606
  throw new Error('Variant component "${variantComponentName}" not found in art module');
@@ -1340,11 +2608,31 @@ async function mount() {
1340
2608
 
1341
2609
  // Create and mount the app
1342
2610
  const app = createApp(VariantComponent);
2611
+ ${setupCall}
1343
2612
  container.innerHTML = '';
1344
2613
  container.className = 'musea-variant';
1345
2614
  app.mount(container);
2615
+ currentApp = app;
1346
2616
 
1347
2617
  console.log('[musea-preview] Mounted variant: ${escapedVariantName}');
2618
+ __museaInitAddons(container, '${escapedVariantName}');
2619
+
2620
+ // Override set-props to remount with raw component + props
2621
+ const TargetComponent = RawComponent || VariantComponent;
2622
+ window.__museaSetProps = (props) => {
2623
+ for (const key of Object.keys(propsOverride)) {
2624
+ delete propsOverride[key];
2625
+ }
2626
+ Object.assign(propsOverride, props);
2627
+ remountWithProps(TargetComponent);
2628
+ };
2629
+ window.__museaSetSlots = (slots) => {
2630
+ for (const key of Object.keys(slotsOverride)) {
2631
+ delete slotsOverride[key];
2632
+ }
2633
+ Object.assign(slotsOverride, slots);
2634
+ remountWithProps(TargetComponent);
2635
+ };
1348
2636
  } catch (error) {
1349
2637
  console.error('[musea-preview] Failed to mount:', error);
1350
2638
  container.innerHTML = \`
@@ -1357,6 +2645,27 @@ async function mount() {
1357
2645
  }
1358
2646
  }
1359
2647
 
2648
+ async function remountWithProps(Component) {
2649
+ if (currentApp) {
2650
+ currentApp.unmount();
2651
+ }
2652
+ const app = createApp({
2653
+ setup() {
2654
+ return () => {
2655
+ const slotFns = {};
2656
+ for (const [name, content] of Object.entries(slotsOverride)) {
2657
+ if (content) slotFns[name] = () => h('span', { innerHTML: content });
2658
+ }
2659
+ return h(Component, { ...propsOverride }, slotFns);
2660
+ };
2661
+ }
2662
+ });
2663
+ ${setupCall}
2664
+ container.innerHTML = '';
2665
+ app.mount(container);
2666
+ currentApp = app;
2667
+ }
2668
+
1360
2669
  mount();
1361
2670
  `;
1362
2671
  }
@@ -1364,32 +2673,121 @@ function generateManifestModule(artFiles) {
1364
2673
  const arts = Array.from(artFiles.values());
1365
2674
  return `export const arts = ${JSON.stringify(arts, null, 2)};`;
1366
2675
  }
2676
+ /**
2677
+ * Extract the content of the first <script setup> block from a Vue SFC source.
2678
+ */
2679
+ function extractScriptSetupContent(source) {
2680
+ const match = source.match(/<script\s+[^>]*setup[^>]*>([\s\S]*?)<\/script>/);
2681
+ return match?.[1]?.trim();
2682
+ }
2683
+ /**
2684
+ * Parse script setup content into imports and setup body.
2685
+ * Returns the import lines, setup body lines, and all identifiers to expose.
2686
+ */
2687
+ function parseScriptSetupForArt(content) {
2688
+ const lines = content.split("\n");
2689
+ const imports = [];
2690
+ const setupBody = [];
2691
+ const returnNames = new Set();
2692
+ for (const line of lines) {
2693
+ const trimmed = line.trim();
2694
+ if (!trimmed || trimmed.startsWith("//")) continue;
2695
+ if (trimmed.startsWith("import ")) {
2696
+ imports.push(line);
2697
+ const defaultMatch = trimmed.match(/^import\s+(\w+)/);
2698
+ if (defaultMatch && defaultMatch[1] !== "type") returnNames.add(defaultMatch[1]);
2699
+ const namedMatch = trimmed.match(/\{([^}]+)\}/);
2700
+ if (namedMatch) for (const part of namedMatch[1].split(",")) {
2701
+ const name = part.trim().split(/\s+as\s+/).pop()?.trim();
2702
+ if (name && !name.startsWith("type ")) returnNames.add(name);
2703
+ }
2704
+ } else {
2705
+ setupBody.push(line);
2706
+ const constMatch = trimmed.match(/^(?:const|let|var)\s+(\w+)/);
2707
+ if (constMatch) returnNames.add(constMatch[1]);
2708
+ const destructMatch = trimmed.match(/^(?:const|let|var)\s+\{([^}]+)\}/);
2709
+ if (destructMatch) for (const part of destructMatch[1].split(",")) {
2710
+ const name = part.trim().split(/\s*:\s*/).shift()?.trim();
2711
+ if (name) returnNames.add(name);
2712
+ }
2713
+ const arrayMatch = trimmed.match(/^(?:const|let|var)\s+\[([^\]]+)\]/);
2714
+ if (arrayMatch) for (const part of arrayMatch[1].split(",")) {
2715
+ const name = part.trim();
2716
+ if (name && name !== "...") returnNames.add(name);
2717
+ }
2718
+ }
2719
+ }
2720
+ returnNames.delete("type");
2721
+ return {
2722
+ imports,
2723
+ setupBody,
2724
+ returnNames: [...returnNames]
2725
+ };
2726
+ }
1367
2727
  function generateArtModule(art, filePath) {
1368
- const componentPath = art.metadata.component;
1369
- let resolvedComponentPath = componentPath;
1370
- if (componentPath && !path.isAbsolute(componentPath)) {
1371
- const artDir = path.dirname(filePath);
1372
- resolvedComponentPath = path.resolve(artDir, componentPath);
2728
+ let componentImportPath;
2729
+ let componentName;
2730
+ if (art.isInline && art.componentPath) {
2731
+ componentImportPath = art.componentPath;
2732
+ componentName = path.basename(art.componentPath, ".vue");
2733
+ } else if (art.metadata.component) {
2734
+ const comp = art.metadata.component;
2735
+ componentImportPath = path.isAbsolute(comp) ? comp : path.resolve(path.dirname(filePath), comp);
2736
+ componentName = path.basename(comp, ".vue");
1373
2737
  }
1374
- const componentName = componentPath ? path.basename(componentPath, ".vue") : null;
2738
+ const scriptSetup = art.scriptSetupContent ? parseScriptSetupForArt(art.scriptSetupContent) : null;
1375
2739
  let code = `
1376
2740
  // Auto-generated module for: ${path.basename(filePath)}
1377
2741
  import { defineComponent, h } from 'vue';
1378
2742
  `;
1379
- if (resolvedComponentPath && componentName) code += `import ${componentName} from '${resolvedComponentPath}';\n`;
2743
+ if (scriptSetup) {
2744
+ const artDir = path.dirname(filePath);
2745
+ for (const imp of scriptSetup.imports) {
2746
+ const resolved = imp.replace(/from\s+(['"])(\.[^'"]+)\1/, (_match, quote, relPath) => {
2747
+ const absPath = path.resolve(artDir, relPath);
2748
+ return `from ${quote}${absPath}${quote}`;
2749
+ });
2750
+ code += `${resolved}\n`;
2751
+ }
2752
+ }
2753
+ if (componentImportPath && componentName) {
2754
+ const alreadyImported = scriptSetup?.imports.some((imp) => {
2755
+ if (imp.includes(`from '${componentImportPath}'`) || imp.includes(`from "${componentImportPath}"`)) return true;
2756
+ return new RegExp(`^import\\s+${componentName}[\\s,]`).test(imp.trim());
2757
+ });
2758
+ if (!alreadyImported) code += `import ${componentName} from '${componentImportPath}';\n`;
2759
+ code += `export const __component__ = ${componentName};\n`;
2760
+ }
1380
2761
  code += `
1381
2762
  export const metadata = ${JSON.stringify(art.metadata)};
1382
2763
  export const variants = ${JSON.stringify(art.variants)};
1383
2764
  `;
1384
2765
  for (const variant of art.variants) {
1385
2766
  const variantComponentName = toPascalCase(variant.name);
1386
- const escapedTemplate = variant.template.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
1387
- const fullTemplate = `<div class="musea-variant" data-variant="${variant.name}">${escapedTemplate}</div>`;
1388
- if (componentName) code += `
1389
- export const ${variantComponentName} = {
2767
+ let template = variant.template;
2768
+ if (componentName) template = template.replace(/<Self/g, `<${componentName}`).replace(/<\/Self>/g, `</${componentName}>`);
2769
+ const escapedTemplate = template.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
2770
+ const fullTemplate = `<div data-variant="${variant.name}">${escapedTemplate}</div>`;
2771
+ const componentNames = new Set();
2772
+ if (componentName) componentNames.add(componentName);
2773
+ if (scriptSetup) {
2774
+ for (const name of scriptSetup.returnNames) if (/^[A-Z]/.test(name)) componentNames.add(name);
2775
+ }
2776
+ const components = componentNames.size > 0 ? ` components: { ${[...componentNames].join(", ")} },\n` : "";
2777
+ if (scriptSetup && scriptSetup.setupBody.length > 0) code += `
2778
+ export const ${variantComponentName} = defineComponent({
1390
2779
  name: '${variantComponentName}',
1391
- components: { ${componentName} },
2780
+ ${components} setup() {
2781
+ ${scriptSetup.setupBody.map((l) => ` ${l}`).join("\n")}
2782
+ return { ${scriptSetup.returnNames.join(", ")} };
2783
+ },
1392
2784
  template: \`${fullTemplate}\`,
2785
+ });
2786
+ `;
2787
+ else if (componentName) code += `
2788
+ export const ${variantComponentName} = {
2789
+ name: '${variantComponentName}',
2790
+ ${components} template: \`${fullTemplate}\`,
1393
2791
  };
1394
2792
  `;
1395
2793
  else code += `
@@ -1425,14 +2823,63 @@ function toPascalCase(str) {
1425
2823
  function escapeTemplate(str) {
1426
2824
  return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n");
1427
2825
  }
1428
- function generatePreviewHtml(art, variant, basePath) {
1429
- const previewModuleUrl = `${basePath}/preview-module?art=${encodeURIComponent(art.path)}&variant=${encodeURIComponent(variant.name)}`;
2826
+ function generatePreviewModuleWithProps(art, variantComponentName, variantName, propsOverride, cssImports = [], previewSetup = null) {
2827
+ const artModuleId = `virtual:musea-art:${art.path}`;
2828
+ const escapedVariantName = escapeTemplate(variantName);
2829
+ const propsJson = JSON.stringify(propsOverride);
2830
+ const cssImportStatements = cssImports.map((cssPath) => `import '${cssPath}';`).join("\n");
2831
+ const setupImport = previewSetup ? `import __museaPreviewSetup from '${previewSetup}';` : "";
2832
+ const setupCall = previewSetup ? "await __museaPreviewSetup(app);" : "";
2833
+ return `
2834
+ ${cssImportStatements}
2835
+ ${setupImport}
2836
+ import { createApp, h } from 'vue';
2837
+ import * as artModule from '${artModuleId}';
2838
+
2839
+ const container = document.getElementById('app');
2840
+ const propsOverride = ${propsJson};
2841
+
2842
+ ${MUSEA_ADDONS_INIT_CODE}
2843
+
2844
+ async function mount() {
2845
+ try {
2846
+ const VariantComponent = artModule['${variantComponentName}'];
2847
+ if (!VariantComponent) {
2848
+ throw new Error('Variant component "${variantComponentName}" not found');
2849
+ }
2850
+
2851
+ const WrappedComponent = {
2852
+ render() {
2853
+ return h(VariantComponent, propsOverride);
2854
+ }
2855
+ };
2856
+
2857
+ const app = createApp(WrappedComponent);
2858
+ ${setupCall}
2859
+ container.innerHTML = '';
2860
+ container.className = 'musea-variant';
2861
+ app.mount(container);
2862
+ console.log('[musea-preview] Mounted variant: ${escapedVariantName} with props override');
2863
+ __museaInitAddons(container, '${escapedVariantName}');
2864
+ } catch (error) {
2865
+ console.error('[musea-preview] Failed to mount:', error);
2866
+ container.innerHTML = '<div class="musea-error"><div class="musea-error-title">Failed to render</div><div>' + error.message + '</div></div>';
2867
+ }
2868
+ }
2869
+
2870
+ mount();
2871
+ `;
2872
+ }
2873
+ function generatePreviewHtml(art, variant, _basePath, viteBase) {
2874
+ const previewModuleUrl = `${_basePath}/preview-module?art=${encodeURIComponent(art.path)}&variant=${encodeURIComponent(variant.name)}`;
2875
+ const base = (viteBase || "/").replace(/\/$/, "");
1430
2876
  return `<!DOCTYPE html>
1431
2877
  <html lang="en">
1432
2878
  <head>
1433
2879
  <meta charset="UTF-8">
1434
2880
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1435
2881
  <title>${escapeHtml(art.metadata.title)} - ${escapeHtml(variant.name)}</title>
2882
+ <script type="module" src="${base}/@vite/client"></script>
1436
2883
  <style>
1437
2884
  * { box-sizing: border-box; margin: 0; padding: 0; }
1438
2885
  html, body {
@@ -1444,10 +2891,6 @@ function generatePreviewHtml(art, variant, basePath) {
1444
2891
  background: #ffffff;
1445
2892
  }
1446
2893
  .musea-variant {
1447
- padding: 1.5rem;
1448
- display: flex;
1449
- align-items: center;
1450
- justify-content: center;
1451
2894
  min-height: 100vh;
1452
2895
  }
1453
2896
  .musea-error {
@@ -1489,6 +2932,29 @@ function generatePreviewHtml(art, variant, basePath) {
1489
2932
  animation: spin 0.8s linear infinite;
1490
2933
  }
1491
2934
  @keyframes spin { to { transform: rotate(360deg); } }
2935
+
2936
+ /* Musea Addons: Checkerboard background for transparent mode */
2937
+ .musea-bg-checkerboard {
2938
+ background-image:
2939
+ linear-gradient(45deg, #ccc 25%, transparent 25%),
2940
+ linear-gradient(-45deg, #ccc 25%, transparent 25%),
2941
+ linear-gradient(45deg, transparent 75%, #ccc 75%),
2942
+ linear-gradient(-45deg, transparent 75%, #ccc 75%) !important;
2943
+ background-size: 20px 20px !important;
2944
+ background-position: 0 0, 0 10px, 10px -10px, -10px 0 !important;
2945
+ }
2946
+
2947
+ /* Musea Addons: Measure label */
2948
+ .musea-measure-label {
2949
+ position: fixed;
2950
+ background: #333;
2951
+ color: #fff;
2952
+ font-size: 11px;
2953
+ padding: 2px 6px;
2954
+ border-radius: 3px;
2955
+ pointer-events: none;
2956
+ z-index: 100000;
2957
+ }
1492
2958
  </style>
1493
2959
  </head>
1494
2960
  <body>
@@ -1508,5 +2974,5 @@ function escapeHtml(str) {
1508
2974
  var src_default = musea;
1509
2975
 
1510
2976
  //#endregion
1511
- export { MuseaVrtRunner, src_default as default, generateTokensHtml, generateTokensMarkdown, generateVrtJsonReport, generateVrtReport, musea, parseTokens, processStyleDictionary };
2977
+ export { MuseaA11yRunner, MuseaVrtRunner, buildTokenMap, src_default as default, generateArtFile, generateTokensHtml, generateTokensMarkdown, generateVrtJsonReport, generateVrtReport, musea, parseTokens, processStyleDictionary, resolveReferences, scanTokenUsage, writeArtFile };
1512
2978
  //# sourceMappingURL=index.js.map