@motion-proto/live-tokens 0.97.0 → 0.99.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/.claude/skills/live-tokens-check-compliance/SKILL.md +5 -0
  2. package/.claude/skills/live-tokens-create-component/SKILL.md +4 -3
  3. package/.claude/skills/live-tokens-create-component/references/contract-tests.md +71 -11
  4. package/CHANGELOG.md +339 -0
  5. package/README.md +2 -2
  6. package/bin/check-component.mjs +8 -0
  7. package/bin/check-page.mjs +6 -0
  8. package/bin/cli.mjs +36 -6
  9. package/bin/contractRunner.mjs +144 -19
  10. package/bin/lib/findings.mjs +20 -4
  11. package/bin/rules/a11y.mjs +53 -0
  12. package/dist-plugin/{chunk-A4TBP4MM.js → chunk-SLICH4IH.js} +2 -2
  13. package/dist-plugin/index.cjs +204 -3
  14. package/dist-plugin/index.js +306 -105
  15. package/dist-plugin/setColors/index.js +1 -1
  16. package/package.json +47 -2
  17. package/src/demo/LabeledSelect.svelte +1 -1
  18. package/src/editor/bootstrap.ts +5 -1
  19. package/src/editor/component-editor/BadgeEditor.svelte +13 -0
  20. package/src/editor/component-editor/ButtonEditor.svelte +33 -2
  21. package/src/editor/component-editor/CalloutEditor.svelte +13 -0
  22. package/src/editor/component-editor/CardEditor.svelte +12 -2
  23. package/src/editor/component-editor/CodeSnippetEditor.svelte +11 -1
  24. package/src/editor/component-editor/CollapsibleSectionEditor.svelte +18 -1
  25. package/src/editor/component-editor/CornerBadgeEditor.svelte +13 -0
  26. package/src/editor/component-editor/DialogEditor.svelte +15 -0
  27. package/src/editor/component-editor/IconButtonEditor.svelte +29 -1
  28. package/src/editor/component-editor/ImageEditor.svelte +20 -9
  29. package/src/editor/component-editor/ImageLightboxEditor.svelte +44 -0
  30. package/src/editor/component-editor/InlineEditActionsEditor.svelte +13 -1
  31. package/src/editor/component-editor/InputEditor.svelte +23 -0
  32. package/src/editor/component-editor/MenuSelectEditor.svelte +27 -1
  33. package/src/editor/component-editor/NotificationEditor.svelte +15 -0
  34. package/src/editor/component-editor/PanelEditor.svelte +1 -1
  35. package/src/editor/component-editor/ProgressBarEditor.svelte +13 -0
  36. package/src/editor/component-editor/RadioButtonEditor.svelte +20 -1
  37. package/src/editor/component-editor/SectionDividerEditor.svelte +40 -13
  38. package/src/editor/component-editor/SegmentedControlEditor.svelte +29 -1
  39. package/src/editor/component-editor/SideNavigationEditor.svelte +40 -1
  40. package/src/editor/component-editor/SlidePagerEditor.svelte +20 -0
  41. package/src/editor/component-editor/SliderEditor.svelte +33 -0
  42. package/src/editor/component-editor/TabBarEditor.svelte +23 -2
  43. package/src/editor/component-editor/TableEditor.svelte +14 -0
  44. package/src/editor/component-editor/ToggleEditor.svelte +17 -0
  45. package/src/editor/component-editor/TooltipEditor.svelte +11 -2
  46. package/src/editor/component-editor/VideoFrameEditor.svelte +8 -0
  47. package/src/editor/component-editor/VideoLightboxEditor.svelte +10 -0
  48. package/src/editor/component-editor/registration.ts +2 -1
  49. package/src/editor/component-editor/registry.ts +69 -27
  50. package/src/editor/component-editor/scaffolding/DemoHeader.svelte +2 -0
  51. package/src/editor/component-editor/scaffolding/StateBlock.svelte +19 -8
  52. package/src/editor/component-editor/scaffolding/TokenLayout.svelte +3 -0
  53. package/src/editor/component-editor/scaffolding/VariantGroup.svelte +70 -3
  54. package/src/editor/component-editor/scaffolding/editorContext.ts +7 -0
  55. package/src/editor/core/a11y/a11yReport.ts +164 -0
  56. package/src/editor/core/a11y/a11yReportTypes.ts +77 -0
  57. package/src/editor/core/a11y/contrastFailures.ts +22 -0
  58. package/src/editor/core/a11y/contrastFixes.ts +41 -0
  59. package/src/editor/core/components/contrastPairRegistry.ts +18 -0
  60. package/src/editor/core/palettes/contrast.ts +44 -0
  61. package/src/editor/core/palettes/contrastPairs.ts +82 -0
  62. package/src/editor/core/palettes/resolveColor.ts +77 -0
  63. package/src/editor/core/store/editorStore.ts +4 -0
  64. package/src/editor/core/store/editorViewStore.ts +2 -2
  65. package/src/editor/core/store/unsavedEdits.ts +10 -0
  66. package/src/editor/core/themes/liveStateStream.ts +7 -1
  67. package/src/editor/core/themes/parsers/gradient.ts +5 -2
  68. package/src/editor/docs/chapters.ts +1 -0
  69. package/src/editor/docs/content/accessibility.md +411 -0
  70. package/src/editor/docs/content/editing-tokens.md +32 -1
  71. package/src/editor/docs/content.generated.ts +2 -1
  72. package/src/editor/pages/ComponentEditorPage.svelte +11 -1
  73. package/src/editor/pages/EditorShell.svelte +98 -10
  74. package/src/editor/skill-atlas/skillSources.generated.ts +3 -3
  75. package/src/editor/skill-atlas/trees/check-compliance.ts +3 -3
  76. package/src/editor/skill-atlas/trees/create-component.ts +12 -12
  77. package/src/editor/styles/ui-editor.css +2 -0
  78. package/src/editor/ui/EditorViewSwitcher.svelte +15 -2
  79. package/src/editor/ui/GradientEditor.svelte +9 -1
  80. package/src/editor/ui/ThemePanel.svelte +4 -4
  81. package/src/editor/ui/UIContrastReadout.svelte +186 -0
  82. package/src/editor/ui/UIInfoPopover.svelte +16 -2
  83. package/src/editor/ui/UIPaletteSelector.svelte +9 -0
  84. package/src/editor/ui/UITokenSelector.svelte +26 -2
  85. package/src/editor/ui/a11y/A11yByEye.svelte +264 -0
  86. package/src/editor/ui/a11y/A11yComponentIssues.svelte +121 -0
  87. package/src/editor/ui/a11y/A11yFindingRow.svelte +119 -0
  88. package/src/editor/ui/a11y/A11yIssueRow.svelte +152 -0
  89. package/src/editor/ui/a11y/A11yOverview.svelte +246 -0
  90. package/src/editor/ui/a11y/A11yPanel.svelte +153 -0
  91. package/src/editor/ui/a11y/A11yPropertyMark.svelte +84 -0
  92. package/src/editor/ui/a11y/A11yRailMark.svelte +41 -0
  93. package/src/editor/ui/a11y/AccessibilityTab.svelte +321 -0
  94. package/src/editor/ui/a11y/a11yView.ts +257 -0
  95. package/src/editor/ui/contrastReadout.ts +45 -0
  96. package/src/live-tokens/data/colors-and-type/halloween.json +1 -1
  97. package/src/live-tokens/data/themes/autumn.json +35 -35
  98. package/src/live-tokens/data/themes/halloween.json +36 -36
  99. package/src/live-tokens/data/themes/midnight-study.json +35 -35
  100. package/src/live-tokens/data/themes/ocean.json +35 -35
  101. package/src/live-tokens/data/themes/royal-velvet.json +35 -35
  102. package/src/live-tokens/data/themes/sketchy.json +35 -35
  103. package/src/live-tokens/data/themes/spring-meadow.json +35 -35
  104. package/src/live-tokens/data/themes/sunset.json +35 -35
  105. package/src/system/components/Badge.svelte +3 -3
  106. package/src/system/components/Button.svelte +12 -6
  107. package/src/system/components/CodeSnippet.svelte +8 -1
  108. package/src/system/components/CornerBadge.svelte +3 -3
  109. package/src/system/components/Dialog.svelte +43 -14
  110. package/src/system/components/IconButton.svelte +1 -1
  111. package/src/system/components/Image.svelte +7 -0
  112. package/src/system/components/ImageLightbox.svelte +18 -25
  113. package/src/system/components/InlineEditActions.svelte +1 -1
  114. package/src/system/components/Input.svelte +9 -2
  115. package/src/system/components/MenuSelect.svelte +31 -3
  116. package/src/system/components/Notification.svelte +1 -1
  117. package/src/system/components/ProgressBar.svelte +7 -1
  118. package/src/system/components/RadioButton.svelte +36 -3
  119. package/src/system/components/SegmentedControl.svelte +36 -2
  120. package/src/system/components/SlidePager.svelte +3 -3
  121. package/src/system/components/Slider.svelte +19 -15
  122. package/src/system/components/TabBar.svelte +33 -3
  123. package/src/system/components/Toggle.svelte +3 -3
  124. package/src/system/components/VideoFrame.svelte +1 -1
  125. package/src/system/components/VideoLightbox.svelte +2 -2
  126. package/src/system/internal/focusTrap.ts +35 -0
  127. package/src/testing-js/__aria__/badge.yml +40 -0
  128. package/src/testing-js/__aria__/button.yml +88 -0
  129. package/src/testing-js/__aria__/callout.yml +12 -0
  130. package/src/testing-js/__aria__/card.yml +8 -0
  131. package/src/testing-js/__aria__/codesnippet.yml +21 -0
  132. package/src/testing-js/__aria__/collapsiblesection.yml +23 -0
  133. package/src/testing-js/__aria__/cornerbadge.yml +24 -0
  134. package/src/testing-js/__aria__/dialog.yml +42 -0
  135. package/src/testing-js/__aria__/iconbutton.yml +57 -0
  136. package/src/testing-js/__aria__/image.yml +10 -0
  137. package/src/testing-js/__aria__/imagelightbox.yml +50 -0
  138. package/src/testing-js/__aria__/inlineeditactions.yml +16 -0
  139. package/src/testing-js/__aria__/input.yml +61 -0
  140. package/src/testing-js/__aria__/menuselect.yml +30 -0
  141. package/src/testing-js/__aria__/notification.yml +30 -0
  142. package/src/testing-js/__aria__/panel.yml +2 -0
  143. package/src/testing-js/__aria__/progressbar.yml +2 -0
  144. package/src/testing-js/__aria__/radiobutton.yml +20 -0
  145. package/src/testing-js/__aria__/sectiondivider.yml +9 -0
  146. package/src/testing-js/__aria__/segmentedcontrol.yml +35 -0
  147. package/src/testing-js/__aria__/sidenavigation.yml +304 -0
  148. package/src/testing-js/__aria__/slidepager.yml +72 -0
  149. package/src/testing-js/__aria__/slider.yml +28 -0
  150. package/src/testing-js/__aria__/tabbar.yml +35 -0
  151. package/src/testing-js/__aria__/table.yml +144 -0
  152. package/src/testing-js/__aria__/toggle.yml +12 -0
  153. package/src/testing-js/__aria__/tooltip.yml +2 -0
  154. package/src/testing-js/__aria__/videoframe.yml +6 -0
  155. package/src/testing-js/__aria__/videolightbox.yml +12 -0
  156. package/src/testing-js/{chunk-WIZ6W7UT.js → chunk-3WCLMOM7.js} +2 -2
  157. package/src/testing-js/{chunk-NB3NZRBM.js → chunk-7CM5D7PF.js} +5 -1
  158. package/src/testing-js/chunk-7CM5D7PF.js.map +1 -0
  159. package/src/testing-js/{chunk-X6KHEVIG.js → chunk-7QXUJRSO.js} +97 -29
  160. package/src/testing-js/chunk-7QXUJRSO.js.map +1 -0
  161. package/src/testing-js/chunk-GZKUYLDW.js +193 -0
  162. package/src/testing-js/chunk-GZKUYLDW.js.map +1 -0
  163. package/src/testing-js/chunk-RTZQLW3F.js +649 -0
  164. package/src/testing-js/chunk-RTZQLW3F.js.map +1 -0
  165. package/src/testing-js/{chunk-SHBYZUPD.js → chunk-RWOT2XDB.js} +212 -15
  166. package/src/testing-js/chunk-RWOT2XDB.js.map +1 -0
  167. package/src/testing-js/{chunk-GKO27NGN.js → chunk-THWGMSRH.js} +2 -2
  168. package/src/testing-js/{chunk-3UKGXCDL.js → chunk-YVVR3GJZ.js} +1 -1
  169. package/src/testing-js/chunk-YVVR3GJZ.js.map +1 -0
  170. package/src/testing-js/component-a11y.contract.js +313 -0
  171. package/src/testing-js/component-a11y.contract.js.map +1 -0
  172. package/src/testing-js/component-alias.contract.js +2 -2
  173. package/src/testing-js/component-behavior.contract.js +2 -2
  174. package/src/testing-js/component-editor.contract.js +6 -5
  175. package/src/testing-js/component-editor.contract.js.map +1 -1
  176. package/src/testing-js/component-render.contract.js +10 -6
  177. package/src/testing-js/component-render.contract.js.map +1 -1
  178. package/src/testing-js/index.d.ts +44 -16
  179. package/src/testing-js/index.js +11 -11
  180. package/src/testing-js/index.js.map +1 -1
  181. package/src/testing-js/page-compliance.contract.js +132 -13
  182. package/src/testing-js/page-compliance.contract.js.map +1 -1
  183. package/src/testing-js/vitest.js +3 -3
  184. package/template/_gitignore +3 -0
  185. package/template/package.json +9 -0
  186. package/src/testing-js/chunk-3UKGXCDL.js.map +0 -1
  187. package/src/testing-js/chunk-NB3NZRBM.js.map +0 -1
  188. package/src/testing-js/chunk-SHBYZUPD.js.map +0 -1
  189. package/src/testing-js/chunk-X6KHEVIG.js.map +0 -1
  190. /package/src/testing-js/{chunk-WIZ6W7UT.js.map → chunk-3WCLMOM7.js.map} +0 -0
  191. /package/src/testing-js/{chunk-GKO27NGN.js.map → chunk-THWGMSRH.js.map} +0 -0
@@ -123,7 +123,7 @@ var STOP_RE = new RegExp(String.raw`^(.*?)\s+(${NUM})%$`);
123
123
  var CIRCLE_RADIUS_RE = new RegExp(String.raw`^circle\s+(${NUM})px$`, "i");
124
124
  var ELLIPSE_RE = new RegExp(String.raw`^ellipse\s+(${NUM})px\s+(${NUM})px$`, "i");
125
125
  var VAR_RE = /^var\((--[a-z0-9-]+)\)$/i;
126
- function splitTopLevel(s) {
126
+ function splitTopLevel(s, isSeparator = (c) => c === ",") {
127
127
  const parts = [];
128
128
  let depth = 0;
129
129
  let start = 0;
@@ -131,7 +131,7 @@ function splitTopLevel(s) {
131
131
  const c = s[i];
132
132
  if (c === "(") depth++;
133
133
  else if (c === ")") depth--;
134
- else if (c === "," && depth === 0) {
134
+ else if (depth === 0 && isSeparator(c)) {
135
135
  parts.push(s.slice(start, i));
136
136
  start = i + 1;
137
137
  }
@@ -5619,6 +5619,146 @@ function formatReport({ propertiesBefore, propertiesAfter, bytesSaved }) {
5619
5619
  return `live-tokens: the production CSS declares ${propertiesAfter.toLocaleString("en-US")} of ${propertiesBefore.toLocaleString("en-US")} custom properties and is ${kB} kB ${bytesSaved >= 0 ? "smaller" : "larger"}.`;
5620
5620
  }
5621
5621
 
5622
+ // vite-plugin/a11y/a11yRunServer.ts
5623
+ var import_node_child_process = require("child_process");
5624
+ var import_node_fs = __toESM(require("fs"), 1);
5625
+ var import_node_path2 = __toESM(require("path"), 1);
5626
+ var A11Y_RUN_ARGS = ["check-component", "--tests", "--a11y", "--no-fix", "--json", "--open-theme"];
5627
+ var A11Y_REPORTS_DIR_NAME = "_a11y-reports";
5628
+ var STDERR_TAIL = 2e3;
5629
+ var CONTRAST_ROLES = /* @__PURE__ */ new Set(["text", "non-text"]);
5630
+ function spawnCli(cliPath) {
5631
+ return (args, cwd) => {
5632
+ const child = (0, import_node_child_process.spawn)(process.execPath, [cliPath, ...args], { cwd, stdio: ["ignore", "pipe", "pipe"] });
5633
+ const done = new Promise((resolve) => {
5634
+ let stdout = "";
5635
+ let stderr = "";
5636
+ child.stdout.on("data", (chunk) => stdout += chunk);
5637
+ child.stderr.on("data", (chunk) => stderr += chunk);
5638
+ child.on("error", (error) => resolve({ code: null, stdout, stderr: `${stderr}${error.message}` }));
5639
+ child.on("close", (code) => resolve({ code, stdout, stderr }));
5640
+ });
5641
+ return { done, stop: () => child.kill("SIGINT") };
5642
+ };
5643
+ }
5644
+ function isContrastFailure(value) {
5645
+ const v = value;
5646
+ return !!v && typeof v === "object" && typeof v.component === "string" && typeof v.variable === "string" && v.variable.startsWith("--") && Array.isArray(v.backdrop) && v.backdrop.every((layer) => typeof layer === "string") && typeof v.role === "string" && CONTRAST_ROLES.has(v.role) && typeof v.ratio === "number" && typeof v.floor === "number" && (v.context === void 0 || typeof v.context === "string");
5647
+ }
5648
+ function readContrast(body) {
5649
+ const contrast = body?.contrast;
5650
+ return Array.isArray(contrast) && contrast.every(isContrastFailure) ? contrast : null;
5651
+ }
5652
+ function parseCliReport(stdout) {
5653
+ try {
5654
+ const parsed = JSON.parse(stdout);
5655
+ return parsed && Array.isArray(parsed.findings) ? parsed : null;
5656
+ } catch {
5657
+ return null;
5658
+ }
5659
+ }
5660
+ function a11yRunServer({ root, reportsDir, openTheme, runCli, broadcast }) {
5661
+ let running = null;
5662
+ const reportFile = (theme) => import_node_path2.default.join(reportsDir, `${theme}.json`);
5663
+ function readReport(theme) {
5664
+ const listed = import_node_fs.default.existsSync(reportsDir) && import_node_fs.default.readdirSync(reportsDir).includes(`${theme}.json`);
5665
+ return listed ? JSON.parse(import_node_fs.default.readFileSync(reportFile(theme), "utf-8")) : null;
5666
+ }
5667
+ function writeReport(report) {
5668
+ import_node_fs.default.mkdirSync(reportsDir, { recursive: true });
5669
+ import_node_fs.default.writeFileSync(reportFile(report.theme), `${JSON.stringify(report, null, 2)}
5670
+ `);
5671
+ }
5672
+ function withConfirmed(report, confirmed) {
5673
+ const review = new Set(report.findings.filter((f) => f.rule === "a11y-review").map((f) => f.message));
5674
+ const kept = [...new Set(confirmed)].filter((message) => review.has(message)).sort();
5675
+ const out = { ...report, confirmed: kept };
5676
+ if (kept.length === 0) delete out.confirmed;
5677
+ return out;
5678
+ }
5679
+ function reports() {
5680
+ if (!import_node_fs.default.existsSync(reportsDir)) return {};
5681
+ return Object.fromEntries(
5682
+ import_node_fs.default.readdirSync(reportsDir).filter((name) => name.endsWith(".json")).map((name) => [name.slice(0, -".json".length), JSON.parse(import_node_fs.default.readFileSync(import_node_path2.default.join(reportsDir, name), "utf-8"))])
5683
+ );
5684
+ }
5685
+ function settle(started, outcome) {
5686
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
5687
+ const cli = parseCliReport(outcome.stdout);
5688
+ if (!cli) {
5689
+ const stderr = outcome.stderr.trim().slice(-STDERR_TAIL);
5690
+ return {
5691
+ ...started,
5692
+ state: "failed",
5693
+ finishedAt,
5694
+ error: `check-component exited with code ${outcome.code} and printed no report.${stderr ? `
5695
+ ${stderr}` : ""}`
5696
+ };
5697
+ }
5698
+ return {
5699
+ ...started,
5700
+ state: "finished",
5701
+ finishedAt,
5702
+ findings: cli.findings.filter((f) => f.details?.engineRule !== "color-contrast")
5703
+ };
5704
+ }
5705
+ function start(body) {
5706
+ if (running) {
5707
+ return { status: 409, body: { error: "An accessibility run is already going.", code: "A11Y_RUN_IN_PROGRESS" } };
5708
+ }
5709
+ const contrast = readContrast(body);
5710
+ if (!contrast) {
5711
+ return {
5712
+ status: 400,
5713
+ body: { error: "Body must be { contrast: [...] } with one entry per failing pair", code: "INVALID_BODY" }
5714
+ };
5715
+ }
5716
+ const report = { state: "running", startedAt: (/* @__PURE__ */ new Date()).toISOString(), theme: openTheme(), contrast };
5717
+ const run = runCli(A11Y_RUN_ARGS, root);
5718
+ running = { report, stop: run.stop };
5719
+ broadcast(report);
5720
+ void run.done.then((outcome) => {
5721
+ const settled = settle(report, outcome);
5722
+ const final = settled.state === "finished" ? withConfirmed(settled, readReport(settled.theme)?.confirmed ?? []) : settled;
5723
+ if (final.state === "finished") writeReport(final);
5724
+ running = null;
5725
+ broadcast(final);
5726
+ });
5727
+ return { status: 202, body: report };
5728
+ }
5729
+ function confirm(body) {
5730
+ const { theme, messages, confirmed } = body ?? {};
5731
+ if (typeof theme !== "string" || typeof confirmed !== "boolean" || !Array.isArray(messages) || !messages.every((m) => typeof m === "string")) {
5732
+ return {
5733
+ status: 400,
5734
+ body: { error: "Body must be { theme, messages: [...], confirmed: true | false }", code: "INVALID_BODY" }
5735
+ };
5736
+ }
5737
+ const report = readReport(theme);
5738
+ if (!report) return { status: 404, body: { error: `No report for theme "${theme}"`, code: "NO_REPORT" } };
5739
+ const marked = new Set(report.confirmed ?? []);
5740
+ for (const message of messages) {
5741
+ if (confirmed) marked.add(message);
5742
+ else marked.delete(message);
5743
+ }
5744
+ const updated = withConfirmed(report, marked);
5745
+ writeReport(updated);
5746
+ broadcast(updated);
5747
+ return { status: 200, body: updated };
5748
+ }
5749
+ return {
5750
+ start,
5751
+ confirm,
5752
+ report: () => ({ status: 200, body: { running: running?.report ?? null, reports: reports() } }),
5753
+ /** Removes a deleted theme's report. */
5754
+ forget: (theme) => import_node_fs.default.rmSync(reportFile(theme), { force: true }),
5755
+ stop: () => running?.stop()
5756
+ };
5757
+ }
5758
+
5759
+ // src/editor/core/a11y/a11yReportTypes.ts
5760
+ var A11Y_EVENT = "a11y";
5761
+
5622
5762
  // vite-plugin/themeFileApi.ts
5623
5763
  var import_node_url2 = require("url");
5624
5764
  var import_meta2 = {};
@@ -5679,6 +5819,7 @@ function themeFileApi(opts) {
5679
5819
  const ISOLATED_DATA_DIR = testDataDir();
5680
5820
  const GENERATED_CSS_PATH = ISOLATED_DATA_DIR ? import_path5.default.join(ISOLATED_DATA_DIR, "tokens.generated.css") : opts.tokensGeneratedCssPath ? import_path5.default.resolve(opts.tokensGeneratedCssPath) : import_path5.default.join(dataDirs.dataDir, "tokens.generated.css");
5681
5821
  const FONTS_CSS_PATH = ISOLATED_DATA_DIR ? import_path5.default.join(ISOLATED_DATA_DIR, "fonts.css") : opts.fontsCssPath ? import_path5.default.resolve(opts.fontsCssPath) : import_path5.default.join(import_path5.default.dirname(CSS_PATH), "fonts.css");
5822
+ const A11Y_REPORTS_DIR = import_path5.default.join(dataDirs.dataDir, A11Y_REPORTS_DIR_NAME);
5682
5823
  const API_BASE = opts.apiBase ?? "/api/live-tokens";
5683
5824
  const consumerComponentDirs = opts.componentsSrcDir ? [import_path5.default.resolve(opts.componentsSrcDir)] : [import_path5.default.resolve("src/components"), import_path5.default.resolve("src/system/components")];
5684
5825
  const packageComponentsDir = import_path5.default.resolve(
@@ -5688,6 +5829,7 @@ function themeFileApi(opts) {
5688
5829
  "system",
5689
5830
  "components"
5690
5831
  );
5832
+ const PACKAGE_CLI_PATH = import_path5.default.resolve(import_path5.default.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url)), "..", "bin", "cli.mjs");
5691
5833
  const packageDataDir = testPackageDataDir() ?? import_path5.default.resolve(
5692
5834
  import_path5.default.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url)),
5693
5835
  "..",
@@ -6359,6 +6501,9 @@ ${lines.join("\n")}
6359
6501
  const SKETCH_STYLE_BY_NAME_REGEX = new RegExp(`^${escapedBase}/sketch-styles/([a-z0-9\\-_]+)$`);
6360
6502
  const PRODUCTION_ROUTE = `${API_BASE}/production`;
6361
6503
  const EVENTS_ROUTE = `${API_BASE}/events`;
6504
+ const A11Y_RUN_ROUTE = `${API_BASE}/a11y/run`;
6505
+ const A11Y_REPORT_ROUTE = `${API_BASE}/a11y/report`;
6506
+ const A11Y_CONFIRMED_ROUTE = `${API_BASE}/a11y/confirmed`;
6362
6507
  async function handleListColorsAndType(_ctx) {
6363
6508
  const files = colorsAndTypeResource.listNames().map((fileName) => {
6364
6509
  const data = colorsAndTypeResource.readJson(fileName);
@@ -6697,6 +6842,7 @@ ${lines.join("\n")}
6697
6842
  }
6698
6843
  }
6699
6844
  import_fs5.default.unlinkSync(filePath);
6845
+ a11yRuns.forget(fileName);
6700
6846
  if (themesResource.existingPath(fileName) === null && deletingActive) {
6701
6847
  themesResource.setActiveName("default");
6702
6848
  }
@@ -6785,7 +6931,49 @@ data: ${JSON.stringify(state)}
6785
6931
 
6786
6932
  `);
6787
6933
  });
6788
- req.on("close", unsubscribe);
6934
+ eventClients.add(res);
6935
+ req.on("close", () => {
6936
+ unsubscribe();
6937
+ eventClients.delete(res);
6938
+ });
6939
+ }
6940
+ const eventClients = /* @__PURE__ */ new Set();
6941
+ const a11yRuns = a11yRunServer({
6942
+ root: process.cwd(),
6943
+ reportsDir: A11Y_REPORTS_DIR,
6944
+ openTheme: () => themesResource.getActiveName(),
6945
+ runCli: spawnCli(PACKAGE_CLI_PATH),
6946
+ broadcast: (report) => {
6947
+ const frame = `event: ${A11Y_EVENT}
6948
+ data: ${JSON.stringify(report)}
6949
+
6950
+ `;
6951
+ for (const client of eventClients) client.write(frame);
6952
+ }
6953
+ });
6954
+ async function handleStartA11yRun({ req, res }) {
6955
+ let body;
6956
+ try {
6957
+ body = JSON.parse(await readBody(req));
6958
+ } catch {
6959
+ body = void 0;
6960
+ }
6961
+ const answer = a11yRuns.start(body);
6962
+ jsonResponse(res, answer.status, answer.body);
6963
+ }
6964
+ async function handleConfirmA11yReview({ req, res }) {
6965
+ let body;
6966
+ try {
6967
+ body = JSON.parse(await readBody(req));
6968
+ } catch {
6969
+ body = void 0;
6970
+ }
6971
+ const answer = a11yRuns.confirm(body);
6972
+ jsonResponse(res, answer.status, answer.body);
6973
+ }
6974
+ async function handleGetA11yReport({ res }) {
6975
+ const answer = a11yRuns.report();
6976
+ jsonResponse(res, answer.status, answer.body);
6789
6977
  }
6790
6978
  async function handleExportTheme({ params, res }) {
6791
6979
  const [fileName] = params;
@@ -7058,6 +7246,18 @@ data: ${JSON.stringify(state)}
7058
7246
  { method: "DELETE", pattern: SKETCH_STYLE_BY_NAME_REGEX, handler: handleSketchStyleByName },
7059
7247
  { method: "POST", pattern: SKETCH_STYLE_BY_NAME_REGEX, handler: methodNotAllowed },
7060
7248
  { method: "GET", pattern: EVENTS_ROUTE, handler: handleEvents },
7249
+ { method: "POST", pattern: A11Y_RUN_ROUTE, handler: handleStartA11yRun },
7250
+ { method: "GET", pattern: A11Y_RUN_ROUTE, handler: methodNotAllowed },
7251
+ { method: "PUT", pattern: A11Y_RUN_ROUTE, handler: methodNotAllowed },
7252
+ { method: "DELETE", pattern: A11Y_RUN_ROUTE, handler: methodNotAllowed },
7253
+ { method: "GET", pattern: A11Y_REPORT_ROUTE, handler: handleGetA11yReport },
7254
+ { method: "PUT", pattern: A11Y_REPORT_ROUTE, handler: methodNotAllowed },
7255
+ { method: "POST", pattern: A11Y_REPORT_ROUTE, handler: methodNotAllowed },
7256
+ { method: "DELETE", pattern: A11Y_REPORT_ROUTE, handler: methodNotAllowed },
7257
+ { method: "PUT", pattern: A11Y_CONFIRMED_ROUTE, handler: handleConfirmA11yReview },
7258
+ { method: "GET", pattern: A11Y_CONFIRMED_ROUTE, handler: methodNotAllowed },
7259
+ { method: "POST", pattern: A11Y_CONFIRMED_ROUTE, handler: methodNotAllowed },
7260
+ { method: "DELETE", pattern: A11Y_CONFIRMED_ROUTE, handler: methodNotAllowed },
7061
7261
  { method: "PUT", pattern: PRODUCTION_ROUTE, handler: handleAdoptTheme },
7062
7262
  { method: "GET", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed },
7063
7263
  { method: "POST", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed },
@@ -7136,6 +7336,7 @@ data: ${JSON.stringify(state)}
7136
7336
  const handled = await dispatch(req, res, routes);
7137
7337
  if (!handled) next();
7138
7338
  });
7339
+ server.httpServer?.once("close", () => a11yRuns.stop());
7139
7340
  },
7140
7341
  handleHotUpdate(ctx) {
7141
7342
  if (legacyLayout) return;