@motion-proto/live-tokens 0.74.0 → 0.76.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/live-tokens-check-compliance/SKILL.md +33 -38
- package/.claude/skills/live-tokens-create-component/SKILL copy.md +196 -0
- package/.claude/skills/live-tokens-create-component/SKILL.md +198 -146
- package/.claude/skills/live-tokens-create-component/references/contract-tests.md +95 -52
- package/.claude/skills/live-tokens-create-component/references/intrinsics.md +2 -2
- package/.claude/skills/live-tokens-create-component/references/linked-siblings.md +2 -2
- package/.claude/skills/live-tokens-create-component/references/sketch-mode.md +12 -12
- package/.claude/skills/live-tokens-create-component/references/token-naming.md +2 -1
- package/.claude/skills/live-tokens-create-page/SKILL.md +187 -0
- package/.claude/skills/live-tokens-create-page/references/interaction-sources.md +66 -0
- package/.claude/skills/live-tokens-create-page/references/layout-sources.md +87 -0
- package/.claude/skills/live-tokens-create-theme/SKILL.md +52 -48
- package/.claude/skills/live-tokens-create-theme/references/design-directions.md +1 -1
- package/.claude/skills/live-tokens-fix-findings/SKILL.md +69 -60
- package/.claude/skills/live-tokens-pick-component/SKILL.md +64 -79
- package/.claude/skills/live-tokens-set-colors/SKILL.md +47 -38
- package/.claude/skills/live-tokens-set-geometry/SKILL.md +58 -35
- package/.claude/skills/live-tokens-set-geometry/references/geometry-anchors.md +5 -3
- package/.claude/skills/live-tokens-set-type/SKILL.md +30 -30
- package/CHANGELOG.md +138 -0
- package/README.md +17 -7
- package/bin/check-component.mjs +91 -19
- package/bin/check-page.mjs +102 -21
- package/bin/cli.mjs +87 -113
- package/bin/contractRunner.mjs +945 -0
- package/bin/create.mjs +1 -1
- package/bin/lib/catalogue.mjs +37 -30
- package/bin/lib/findings.mjs +46 -15
- package/bin/lib/report.mjs +3 -3
- package/bin/lib/tokenVocabulary.mjs +4 -4
- package/bin/migrate-routes.mjs +5 -5
- package/bin/migrate.mjs +4 -4
- package/bin/save-theme.mjs +8 -9
- package/bin/set-colors.mjs +9 -11
- package/bin/set-geometry.mjs +7 -7
- package/bin/set-type.mjs +5 -7
- package/bin/setup-claude.mjs +110 -0
- package/dist-plugin/{chunk-W6Y4BWFB.js → chunk-6WGFOJXO.js} +22 -0
- package/dist-plugin/{chunk-7VRTBGJT.js → chunk-PDNL4NC5.js} +9 -2
- package/dist-plugin/{chunk-V3YF6CGT.js → chunk-SWXRVZKT.js} +43 -1
- package/dist-plugin/{dataPaths-BhWzd5cL.d.cts → dataPaths-BpIK_Obx.d.cts} +1 -0
- package/dist-plugin/{dataPaths-BhWzd5cL.d.ts → dataPaths-BpIK_Obx.d.ts} +1 -0
- package/dist-plugin/index.cjs +310 -147
- package/dist-plugin/index.d.cts +1 -1
- package/dist-plugin/index.d.ts +1 -1
- package/dist-plugin/index.js +218 -122
- package/dist-plugin/migrateData/index.cjs +66 -2
- package/dist-plugin/migrateData/index.d.cts +1 -1
- package/dist-plugin/migrateData/index.d.ts +1 -1
- package/dist-plugin/migrateData/index.js +3 -3
- package/dist-plugin/setColors/index.cjs +61 -4
- package/dist-plugin/setColors/index.d.cts +1 -1
- package/dist-plugin/setColors/index.d.ts +1 -1
- package/dist-plugin/setColors/index.js +5 -5
- package/dist-plugin/setGeometry/index.cjs +107 -44
- package/dist-plugin/setGeometry/index.d.cts +5 -5
- package/dist-plugin/setGeometry/index.d.ts +5 -5
- package/dist-plugin/setGeometry/index.js +51 -45
- package/dist-plugin/setType/index.cjs +15 -0
- package/dist-plugin/setType/index.d.cts +1 -1
- package/dist-plugin/setType/index.d.ts +1 -1
- package/dist-plugin/setType/index.js +1 -1
- package/dist-plugin/tokensCssMigrations/index.d.cts +1 -1
- package/dist-plugin/tokensCssMigrations/index.d.ts +1 -1
- package/dist-plugin/tokensCssMigrations/index.js +1 -1
- package/package.json +35 -8
- package/src/app/site.css +19 -9
- package/src/editor/bootstrap.ts +2 -0
- package/src/editor/component-editor/CollapsibleSectionEditor.svelte +4 -4
- package/src/editor/component-editor/DialogEditor.svelte +4 -4
- package/src/editor/component-editor/MenuSelectEditor.svelte +4 -1
- package/src/editor/component-editor/SegmentedControlEditor.svelte +6 -1
- package/src/editor/component-editor/TabBarEditor.svelte +1 -1
- package/src/editor/component-editor/TableEditor.svelte +2 -2
- package/src/editor/component-editor/scaffolding/TokenLayout.svelte +6 -3
- package/src/editor/component-editor/scaffolding/VariantGroup.svelte +41 -20
- package/src/editor/core/components/adjustAliases.ts +59 -45
- package/src/editor/core/components/aliasKinds.ts +9 -5
- package/src/editor/core/preview/themePreview.ts +9 -2
- package/src/editor/core/sketch/sketchLayer.ts +22 -0
- package/src/editor/core/store/editorStore.ts +10 -1
- package/src/editor/core/themes/buildColors.ts +3 -3
- package/src/editor/core/themes/liveStateStream.ts +26 -0
- package/src/editor/core/themes/migrations/2026-09-07-stroke-role-renames.ts +52 -0
- package/src/editor/core/themes/migrations/index.ts +2 -0
- package/src/editor/core/themes/themeDocumentSync.ts +22 -11
- package/src/editor/core/themes/themeService.ts +9 -2
- package/src/editor/pages/ComponentEditorPage.svelte +17 -1
- package/src/editor/pages/liveTokensEditorHandle.ts +23 -0
- package/src/editor/skill-atlas/SkillAtlas.svelte +75 -573
- package/src/editor/skill-atlas/TreeCanvas.svelte +263 -0
- package/src/editor/skill-atlas/TreeNodeCard.svelte +136 -53
- package/src/editor/skill-atlas/edges.ts +31 -0
- package/src/editor/skill-atlas/skillSources.generated.ts +20 -19
- package/src/editor/skill-atlas/skillTrees.ts +19 -3840
- package/src/editor/skill-atlas/trees/check-compliance.ts +183 -0
- package/src/editor/skill-atlas/trees/create-component.ts +275 -0
- package/src/editor/skill-atlas/trees/create-page.ts +320 -0
- package/src/editor/skill-atlas/trees/create-theme.ts +255 -0
- package/src/editor/skill-atlas/trees/fix-findings.ts +469 -0
- package/src/editor/skill-atlas/trees/pick-component.ts +300 -0
- package/src/editor/skill-atlas/trees/set-colors.ts +148 -0
- package/src/editor/skill-atlas/trees/set-geometry.ts +136 -0
- package/src/editor/skill-atlas/trees/set-type.ts +142 -0
- package/src/editor/skill-atlas/types.ts +3 -4
- package/src/editor/skill-atlas/wireLayout.ts +287 -0
- package/src/live-tokens/data/themes/autumn.json +15 -15
- package/src/live-tokens/data/themes/halloween.json +15 -15
- package/src/live-tokens/data/themes/midnight-study.json +15 -15
- package/src/live-tokens/data/themes/ocean.json +15 -15
- package/src/live-tokens/data/themes/royal-velvet.json +15 -15
- package/src/live-tokens/data/themes/sketchy.json +15 -15
- package/src/live-tokens/data/themes/spring-meadow.json +15 -15
- package/src/live-tokens/data/themes/sunset.json +15 -15
- package/src/system/components/Badge.svelte +7 -0
- package/src/system/components/Button.svelte +7 -0
- package/src/system/components/Callout.svelte +10 -6
- package/src/system/components/Card.svelte +23 -4
- package/src/system/components/CodeSnippet.svelte +4 -3
- package/src/system/components/CollapsibleSection.svelte +23 -8
- package/src/system/components/CornerBadge.svelte +6 -0
- package/src/system/components/Dialog.svelte +13 -6
- package/src/system/components/IconButton.svelte +9 -0
- package/src/system/components/Image.svelte +8 -0
- package/src/system/components/ImageLightbox.svelte +6 -0
- package/src/system/components/InlineEditActions.svelte +7 -0
- package/src/system/components/Input.svelte +7 -0
- package/src/system/components/MenuSelect.svelte +7 -0
- package/src/system/components/Notification.svelte +7 -0
- package/src/system/components/Panel.svelte +6 -0
- package/src/system/components/ProgressBar.svelte +5 -0
- package/src/system/components/RadioButton.svelte +11 -5
- package/src/system/components/SectionDivider.svelte +8 -0
- package/src/system/components/SegmentedControl.svelte +6 -0
- package/src/system/components/SideNavigation.svelte +6 -0
- package/src/system/components/Slider.svelte +7 -4
- package/src/system/components/TabBar.svelte +15 -9
- package/src/system/components/Table.svelte +8 -3
- package/src/system/components/Toggle.svelte +4 -4
- package/src/system/components/Tooltip.svelte +6 -0
- package/src/testing-js/chunk-AO7EZHYV.js +776 -0
- package/src/testing-js/chunk-AO7EZHYV.js.map +1 -0
- package/src/testing-js/chunk-FAFOAWYL.js +39 -0
- package/src/testing-js/chunk-FAFOAWYL.js.map +1 -0
- package/src/testing-js/chunk-L73N4NSO.js +23 -0
- package/src/testing-js/chunk-L73N4NSO.js.map +1 -0
- package/src/testing-js/chunk-LXR3MN6N.js +3063 -0
- package/src/testing-js/chunk-LXR3MN6N.js.map +1 -0
- package/src/testing-js/chunk-ZMSX6CXR.js +53 -0
- package/src/testing-js/chunk-ZMSX6CXR.js.map +1 -0
- package/src/testing-js/component-alias.contract.js +81 -0
- package/src/testing-js/component-alias.contract.js.map +1 -0
- package/src/testing-js/component-editor.contract.js +62 -0
- package/src/testing-js/component-editor.contract.js.map +1 -0
- package/src/testing-js/component-render.contract.js +568 -0
- package/src/testing-js/component-render.contract.js.map +1 -0
- package/src/testing-js/index.d.ts +293 -0
- package/src/testing-js/index.js +222 -0
- package/src/testing-js/index.js.map +1 -0
- package/src/testing-js/registry.contract.js +39 -0
- package/src/testing-js/registry.contract.js.map +1 -0
- package/src/testing-js/vitest-BE6uGF31.d.ts +73 -0
- package/src/testing-js/vitest.d.ts +3 -0
- package/src/testing-js/vitest.js +13 -0
- package/src/testing-js/vitest.js.map +1 -0
- package/template/README.md +13 -0
- package/template/_gitignore +6 -0
- package/template/package.json +3 -1
- package/template/src/pages/Home.svelte +4 -18
- package/.claude/skills/live-tokens-build-page/SKILL.md +0 -103
- package/.claude/skills/live-tokens-build-page/references/layout-sources.md +0 -48
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ContractHarness
|
|
3
|
+
} from "./chunk-AO7EZHYV.js";
|
|
4
|
+
import {
|
|
5
|
+
openComponentsEditor
|
|
6
|
+
} from "./chunk-L73N4NSO.js";
|
|
7
|
+
import {
|
|
8
|
+
selectedContracts
|
|
9
|
+
} from "./chunk-LXR3MN6N.js";
|
|
10
|
+
import "./chunk-ZMSX6CXR.js";
|
|
11
|
+
|
|
12
|
+
// src/testing/component-render.contract.ts
|
|
13
|
+
import fs from "fs";
|
|
14
|
+
import path from "path";
|
|
15
|
+
import { expect, test } from "@playwright/test";
|
|
16
|
+
test.describe.configure({ mode: "parallel" });
|
|
17
|
+
function discoverDefaultAliases() {
|
|
18
|
+
const root = path.resolve(process.env.LIVE_TOKENS_DATA_DIR ?? "src/live-tokens/data", "component-configs");
|
|
19
|
+
const cases = [];
|
|
20
|
+
for (const directory of fs.readdirSync(root).sort()) {
|
|
21
|
+
const file = path.join(root, directory, "default.json");
|
|
22
|
+
if (!fs.existsSync(file)) continue;
|
|
23
|
+
const data = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
24
|
+
const component = data.component ?? directory;
|
|
25
|
+
for (const variable of Object.keys(data.aliases ?? {}).sort()) {
|
|
26
|
+
cases.push({ component, variable });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return cases;
|
|
30
|
+
}
|
|
31
|
+
var requestedComponent = process.env.LIVE_TOKENS_COMPONENT;
|
|
32
|
+
var aliasCases = discoverDefaultAliases().filter(({ component }) => !requestedComponent || component === requestedComponent);
|
|
33
|
+
var aliasesByComponent = /* @__PURE__ */ new Map();
|
|
34
|
+
for (const { component, variable } of aliasCases) {
|
|
35
|
+
const variables = aliasesByComponent.get(component) ?? [];
|
|
36
|
+
variables.push(variable);
|
|
37
|
+
aliasesByComponent.set(component, variables);
|
|
38
|
+
}
|
|
39
|
+
async function exerciseVisibleTokenControls(page, componentAliases, exercised) {
|
|
40
|
+
const group = page.locator(".variant-group:visible");
|
|
41
|
+
const settle = () => page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve()))));
|
|
42
|
+
await settle();
|
|
43
|
+
const variables = await group.locator(".ui-token-selector:visible[data-token-variable]:not(.disabled):not(.locked)").evaluateAll((elements) => [...new Set(elements.map((element) => element.dataset.tokenVariable).filter((value) => !!value))]);
|
|
44
|
+
for (const variable of variables) {
|
|
45
|
+
if (!componentAliases.includes(variable) || exercised.has(variable)) continue;
|
|
46
|
+
const selector = group.locator(`.ui-token-selector:visible[data-token-variable="${variable}"]:not(.disabled):not(.locked)`).first();
|
|
47
|
+
if (await selector.count() === 0) continue;
|
|
48
|
+
const before = await page.evaluate((name) => document.documentElement.style.getPropertyValue(name).trim(), variable);
|
|
49
|
+
const optionSelector = [
|
|
50
|
+
".ui-ts-dropdown .static-chip:not(.active):not(:disabled)",
|
|
51
|
+
".ui-ts-dropdown .ui-option-item:not(.active):not(:disabled)",
|
|
52
|
+
".ui-ts-dropdown .font-size-row:not(.active):not(:disabled)"
|
|
53
|
+
].join(", ");
|
|
54
|
+
let after = before;
|
|
55
|
+
let foundOption = false;
|
|
56
|
+
for (let attempt = 0; attempt < 4 && after === before; attempt++) {
|
|
57
|
+
await settle();
|
|
58
|
+
await selector.locator(".ui-ts-trigger").click();
|
|
59
|
+
const options = selector.locator(optionSelector);
|
|
60
|
+
const count = await options.count();
|
|
61
|
+
if (count === 0) {
|
|
62
|
+
await selector.locator(".ui-ts-trigger").click();
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
foundOption = true;
|
|
66
|
+
await options.nth(attempt % count).click();
|
|
67
|
+
await settle();
|
|
68
|
+
after = await page.evaluate((name) => document.documentElement.style.getPropertyValue(name).trim(), variable);
|
|
69
|
+
}
|
|
70
|
+
if (!foundOption) continue;
|
|
71
|
+
expect(after, `${variable} did not write through its editor control`).not.toBe(before);
|
|
72
|
+
exercised.add(variable);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function exerciseVisibleSplitPaddingControls(page, componentAliases, exercised) {
|
|
76
|
+
const group = page.locator(".variant-group:visible");
|
|
77
|
+
const variables = await group.locator(".merge-btn:visible[data-padding-parent-variable]").evaluateAll((elements) => [...new Set(elements.map((element) => element.dataset.paddingParentVariable).filter((value) => !!value))]);
|
|
78
|
+
for (const variable of variables) {
|
|
79
|
+
if (!componentAliases.includes(variable) || exercised.has(variable)) continue;
|
|
80
|
+
const merge = group.locator(`.merge-btn:visible[data-padding-parent-variable="${variable}"]`).first();
|
|
81
|
+
if (await merge.count()) await merge.click();
|
|
82
|
+
}
|
|
83
|
+
if (variables.length > 0) {
|
|
84
|
+
await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve()))));
|
|
85
|
+
await exerciseVisibleTokenControls(page, componentAliases, exercised);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function exerciseVisibleCompositeControls(page, componentAliases, exercised) {
|
|
89
|
+
const toggled = [];
|
|
90
|
+
const group = page.locator(".variant-group:visible");
|
|
91
|
+
const controls = group.locator("[data-token-variables]:visible");
|
|
92
|
+
for (let index = 0; index < await controls.count(); index++) {
|
|
93
|
+
const control = controls.nth(index);
|
|
94
|
+
const variables = (await control.getAttribute("data-token-variables") ?? "").split(/\s+/).filter((variable) => componentAliases.includes(variable) && !exercised.has(variable));
|
|
95
|
+
if (variables.length === 0) continue;
|
|
96
|
+
const before = await page.evaluate((names) => Object.fromEntries(names.map((name) => [
|
|
97
|
+
name,
|
|
98
|
+
document.documentElement.style.getPropertyValue(name).trim()
|
|
99
|
+
])), variables);
|
|
100
|
+
const input = control.locator('input[type="checkbox"], input[type="radio"], [role="switch"]').first();
|
|
101
|
+
if (await input.count() === 0) continue;
|
|
102
|
+
await input.click();
|
|
103
|
+
toggled.push(input);
|
|
104
|
+
await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve()))));
|
|
105
|
+
const after = await page.evaluate((names) => Object.fromEntries(names.map((name) => [
|
|
106
|
+
name,
|
|
107
|
+
document.documentElement.style.getPropertyValue(name).trim()
|
|
108
|
+
])), variables);
|
|
109
|
+
for (const variable of variables) {
|
|
110
|
+
expect(after[variable], `${variable} did not write through its composite control`).not.toBe(before[variable]);
|
|
111
|
+
exercised.add(variable);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return toggled;
|
|
115
|
+
}
|
|
116
|
+
async function exerciseVisibleGradientControls(page, componentAliases, exercised) {
|
|
117
|
+
const group = page.locator(".variant-group:visible");
|
|
118
|
+
const editors = group.locator(".gradient-editor:visible[data-token-variable]");
|
|
119
|
+
for (let index = 0; index < await editors.count(); index++) {
|
|
120
|
+
const editor = editors.nth(index);
|
|
121
|
+
const variable = await editor.getAttribute("data-token-variable");
|
|
122
|
+
if (!variable || !componentAliases.includes(variable) || exercised.has(variable)) continue;
|
|
123
|
+
const before = await page.evaluate((name) => document.documentElement.style.getPropertyValue(name).trim(), variable);
|
|
124
|
+
const solid = editor.getByRole("radio", { name: "Solid" });
|
|
125
|
+
if (await solid.count() && !await solid.isChecked()) await solid.check({ force: true });
|
|
126
|
+
const picker = editor.locator(".picker-slot .ui-token-selector").first();
|
|
127
|
+
await picker.locator(".ui-ts-trigger").click({ force: true });
|
|
128
|
+
const swatch = picker.locator(".static-chip:not(.active):not(:disabled)").first();
|
|
129
|
+
if (await swatch.count() === 0) continue;
|
|
130
|
+
await swatch.click({ force: true });
|
|
131
|
+
await expect.poll(
|
|
132
|
+
() => page.evaluate((name) => document.documentElement.style.getPropertyValue(name).trim(), variable),
|
|
133
|
+
{ message: `${variable} did not write through its gradient editor` }
|
|
134
|
+
).not.toMatch(new RegExp(`^${escapeRegExp(before)}$`));
|
|
135
|
+
exercised.add(variable);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function escapeRegExp(value) {
|
|
139
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
140
|
+
}
|
|
141
|
+
async function probeCurrentView(page, componentAliases, forcedVariables = [], provenVariables = []) {
|
|
142
|
+
return page.evaluate(async ({ allAliases, forced, proven }) => {
|
|
143
|
+
const group = Array.from(document.querySelectorAll(".variant-group")).find((element) => element.offsetParent !== null);
|
|
144
|
+
const preview = group?.querySelector(".tabs-preview");
|
|
145
|
+
if (!group || !preview) return { covered: [], unchanged: [] };
|
|
146
|
+
const visibleVariables = /* @__PURE__ */ new Set();
|
|
147
|
+
if (forced.length > 0) {
|
|
148
|
+
for (const variable of forced) {
|
|
149
|
+
if (allAliases.includes(variable)) visibleVariables.add(variable);
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
for (const element of group.querySelectorAll("[data-token-variable]")) {
|
|
153
|
+
const variable = element.dataset.tokenVariable;
|
|
154
|
+
if (element.offsetParent !== null && variable && allAliases.includes(variable)) {
|
|
155
|
+
visibleVariables.add(variable);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
for (const element of group.querySelectorAll("[data-token-variables]")) {
|
|
159
|
+
if (element.offsetParent === null) continue;
|
|
160
|
+
for (const variable of (element.dataset.tokenVariables ?? "").split(/\s+/)) {
|
|
161
|
+
if (allAliases.includes(variable)) visibleVariables.add(variable);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
for (const variable of [...visibleVariables]) {
|
|
166
|
+
if (!variable.endsWith("-padding")) continue;
|
|
167
|
+
for (const side of ["top", "right", "bottom", "left"]) {
|
|
168
|
+
const sideVariable = `${variable}-${side}`;
|
|
169
|
+
if (allAliases.includes(sideVariable)) visibleVariables.add(sideVariable);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
for (const variable of proven) visibleVariables.delete(variable);
|
|
173
|
+
if (visibleVariables.size === 0) return { covered: [], unchanged: [] };
|
|
174
|
+
const properties = [
|
|
175
|
+
"display",
|
|
176
|
+
"visibility",
|
|
177
|
+
"opacity",
|
|
178
|
+
"position",
|
|
179
|
+
"zIndex",
|
|
180
|
+
"color",
|
|
181
|
+
"background",
|
|
182
|
+
"backgroundColor",
|
|
183
|
+
"backgroundImage",
|
|
184
|
+
"backgroundSize",
|
|
185
|
+
"backgroundPosition",
|
|
186
|
+
"border",
|
|
187
|
+
"borderColor",
|
|
188
|
+
"borderWidth",
|
|
189
|
+
"borderStyle",
|
|
190
|
+
"borderTopColor",
|
|
191
|
+
"borderRightColor",
|
|
192
|
+
"borderBottomColor",
|
|
193
|
+
"borderLeftColor",
|
|
194
|
+
"borderTopWidth",
|
|
195
|
+
"borderRightWidth",
|
|
196
|
+
"borderBottomWidth",
|
|
197
|
+
"borderLeftWidth",
|
|
198
|
+
"borderRadius",
|
|
199
|
+
"borderTopLeftRadius",
|
|
200
|
+
"borderTopRightRadius",
|
|
201
|
+
"borderBottomRightRadius",
|
|
202
|
+
"borderBottomLeftRadius",
|
|
203
|
+
"outline",
|
|
204
|
+
"outlineColor",
|
|
205
|
+
"outlineWidth",
|
|
206
|
+
"boxShadow",
|
|
207
|
+
"filter",
|
|
208
|
+
"backdropFilter",
|
|
209
|
+
"mixBlendMode",
|
|
210
|
+
"fontFamily",
|
|
211
|
+
"fontSize",
|
|
212
|
+
"fontWeight",
|
|
213
|
+
"fontStyle",
|
|
214
|
+
"lineHeight",
|
|
215
|
+
"letterSpacing",
|
|
216
|
+
"textAlign",
|
|
217
|
+
"textDecoration",
|
|
218
|
+
"textTransform",
|
|
219
|
+
"textShadow",
|
|
220
|
+
"whiteSpace",
|
|
221
|
+
"padding",
|
|
222
|
+
"paddingTop",
|
|
223
|
+
"paddingRight",
|
|
224
|
+
"paddingBottom",
|
|
225
|
+
"paddingLeft",
|
|
226
|
+
"margin",
|
|
227
|
+
"marginTop",
|
|
228
|
+
"marginRight",
|
|
229
|
+
"marginBottom",
|
|
230
|
+
"marginLeft",
|
|
231
|
+
"gap",
|
|
232
|
+
"rowGap",
|
|
233
|
+
"columnGap",
|
|
234
|
+
"width",
|
|
235
|
+
"minWidth",
|
|
236
|
+
"maxWidth",
|
|
237
|
+
"height",
|
|
238
|
+
"minHeight",
|
|
239
|
+
"maxHeight",
|
|
240
|
+
"top",
|
|
241
|
+
"right",
|
|
242
|
+
"bottom",
|
|
243
|
+
"left",
|
|
244
|
+
"transform",
|
|
245
|
+
"transformOrigin",
|
|
246
|
+
"translate",
|
|
247
|
+
"rotate",
|
|
248
|
+
"scale",
|
|
249
|
+
"justifyContent",
|
|
250
|
+
"justifyItems",
|
|
251
|
+
"justifySelf",
|
|
252
|
+
"alignContent",
|
|
253
|
+
"alignItems",
|
|
254
|
+
"alignSelf",
|
|
255
|
+
"gridTemplateColumns",
|
|
256
|
+
"gridTemplateRows",
|
|
257
|
+
"gridColumn",
|
|
258
|
+
"gridRow",
|
|
259
|
+
"flex",
|
|
260
|
+
"flexDirection",
|
|
261
|
+
"overflow",
|
|
262
|
+
"overflowX",
|
|
263
|
+
"overflowY",
|
|
264
|
+
"clipPath",
|
|
265
|
+
"objectFit",
|
|
266
|
+
"objectPosition",
|
|
267
|
+
"fill",
|
|
268
|
+
"stroke",
|
|
269
|
+
"strokeWidth",
|
|
270
|
+
"strokeDasharray",
|
|
271
|
+
"strokeDashoffset",
|
|
272
|
+
"cursor",
|
|
273
|
+
"pointerEvents",
|
|
274
|
+
"content",
|
|
275
|
+
"transitionProperty",
|
|
276
|
+
"transitionDuration",
|
|
277
|
+
"transitionTimingFunction",
|
|
278
|
+
"transitionDelay",
|
|
279
|
+
"scrollbarColor",
|
|
280
|
+
"scrollbarWidth"
|
|
281
|
+
];
|
|
282
|
+
const ignoredAttribute = /^(class|style|data-|aria-)/;
|
|
283
|
+
const stableAttributes = (element) => Array.from(element.attributes).filter((attribute) => !ignoredAttribute.test(attribute.name)).map((attribute) => `${attribute.name}=${attribute.value}`).sort().join(";");
|
|
284
|
+
const pseudoSelectorsFor = (variable) => {
|
|
285
|
+
const pseudoSelectors = ["::before", "::after"];
|
|
286
|
+
if (variable.includes("placeholder")) pseudoSelectors.push("::placeholder");
|
|
287
|
+
if (variable.includes("scrollbar")) {
|
|
288
|
+
pseudoSelectors.push(
|
|
289
|
+
"::-webkit-scrollbar",
|
|
290
|
+
"::-webkit-scrollbar-track",
|
|
291
|
+
"::-webkit-scrollbar-thumb"
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
return pseudoSelectors;
|
|
295
|
+
};
|
|
296
|
+
const fingerprintNodes = () => {
|
|
297
|
+
const roots = [preview, ...document.querySelectorAll('[role="dialog"]')];
|
|
298
|
+
return roots.flatMap((root2) => [root2, ...root2.querySelectorAll("*")]);
|
|
299
|
+
};
|
|
300
|
+
const nodeFingerprint = (element, index, pseudoSelectors) => {
|
|
301
|
+
const style = getComputedStyle(element);
|
|
302
|
+
const rect = element.getBoundingClientRect();
|
|
303
|
+
const values = properties.map((property) => style[property]).join("|");
|
|
304
|
+
const pseudoValues = pseudoSelectors.map((selector) => {
|
|
305
|
+
const pseudo = getComputedStyle(element, selector);
|
|
306
|
+
return properties.map((property) => pseudo[property]).join("|");
|
|
307
|
+
}).join("~");
|
|
308
|
+
return [
|
|
309
|
+
index,
|
|
310
|
+
element.tagName,
|
|
311
|
+
values,
|
|
312
|
+
pseudoValues,
|
|
313
|
+
`${rect.x},${rect.y},${rect.width},${rect.height}`,
|
|
314
|
+
`${element.scrollWidth},${element.scrollHeight}`,
|
|
315
|
+
stableAttributes(element)
|
|
316
|
+
].join("~");
|
|
317
|
+
};
|
|
318
|
+
const fingerprint = (variable) => {
|
|
319
|
+
const pseudoSelectors = pseudoSelectorsFor(variable);
|
|
320
|
+
return fingerprintNodes().map((element, index) => nodeFingerprint(element, index, pseudoSelectors));
|
|
321
|
+
};
|
|
322
|
+
const movedFrom = (variable, baseline) => {
|
|
323
|
+
const pseudoSelectors = pseudoSelectorsFor(variable);
|
|
324
|
+
const nodes = fingerprintNodes();
|
|
325
|
+
if (nodes.length !== baseline.length) return true;
|
|
326
|
+
for (let index = 0; index < nodes.length; index++) {
|
|
327
|
+
if (nodeFingerprint(nodes[index], index, pseudoSelectors) !== baseline[index]) return true;
|
|
328
|
+
}
|
|
329
|
+
return false;
|
|
330
|
+
};
|
|
331
|
+
const alternateValues = (variable, current) => {
|
|
332
|
+
const values = [];
|
|
333
|
+
const add = (...candidates) => {
|
|
334
|
+
for (const candidate of candidates) {
|
|
335
|
+
if (candidate !== current && !values.includes(candidate)) values.push(candidate);
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
if (/(surface|color|text|border)(?!-width)|-icon$|outline-color/.test(variable)) {
|
|
339
|
+
add("rgb(1, 2, 3)", "rgb(253, 127, 3)");
|
|
340
|
+
}
|
|
341
|
+
if (variable.includes("font-family")) add("monospace", "serif");
|
|
342
|
+
if (variable.includes("font-weight")) add("900", "200");
|
|
343
|
+
if (variable.includes("line-height")) add("2.75", "0.75");
|
|
344
|
+
if (variable.includes("letter-spacing")) add("7px", "-2px");
|
|
345
|
+
if (variable.includes("shadow")) add("rgb(1, 2, 3) 7px 9px 0 3px", "none");
|
|
346
|
+
if (variable.includes("blur")) add("blur(7px)", "none");
|
|
347
|
+
if (variable.includes("gradient") || variable.endsWith("-background")) {
|
|
348
|
+
add("linear-gradient(90deg, rgb(1, 2, 3), rgb(253, 127, 3))", "none");
|
|
349
|
+
}
|
|
350
|
+
if (variable.includes("duration") || variable.includes("transition")) add("9s", "0s");
|
|
351
|
+
if (variable.includes("opacity")) add("0.17", "0.83");
|
|
352
|
+
if (variable.includes("display")) add(current === "none" ? "block" : "none", "contents");
|
|
353
|
+
if (variable.includes("align")) add("right", "center", "flex-end");
|
|
354
|
+
if (variable.includes("position")) add("above-label", "below-description", "center");
|
|
355
|
+
if (variable.includes("button-variant")) add("danger", "outline");
|
|
356
|
+
if (variable.includes("shimmer")) {
|
|
357
|
+
add("linear-gradient(90deg, transparent, rgb(255 255 255 / 80%), transparent)", "none");
|
|
358
|
+
}
|
|
359
|
+
if (/(padding|margin|radius|width|height|size|gap|offset|inset|thickness)/.test(variable)) {
|
|
360
|
+
add("37px", "3px");
|
|
361
|
+
}
|
|
362
|
+
if (variable.includes("scale")) add("1.73", "0.53");
|
|
363
|
+
add("__live_tokens_invalid_probe__", "37px", "rgb(1, 2, 3)", "none", "2");
|
|
364
|
+
return values;
|
|
365
|
+
};
|
|
366
|
+
const root = document.documentElement;
|
|
367
|
+
const covered = [];
|
|
368
|
+
const unchanged = [];
|
|
369
|
+
const settle = async () => {
|
|
370
|
+
await new Promise((resolve) => requestAnimationFrame(() => resolve()));
|
|
371
|
+
for (const animation of document.getAnimations()) animation.finish();
|
|
372
|
+
};
|
|
373
|
+
const freeze = document.createElement("style");
|
|
374
|
+
freeze.dataset.reactivityProbe = "true";
|
|
375
|
+
freeze.textContent = "*,*::before,*::after{animation:none!important;caret-color:transparent!important}";
|
|
376
|
+
document.head.appendChild(freeze);
|
|
377
|
+
await document.fonts?.ready;
|
|
378
|
+
await settle();
|
|
379
|
+
for (const variable of visibleVariables) {
|
|
380
|
+
const mutatedVariables = [variable];
|
|
381
|
+
if (variable.endsWith("-padding")) {
|
|
382
|
+
for (const side of ["top", "right", "bottom", "left"]) {
|
|
383
|
+
const sideVariable = `${variable}-${side}`;
|
|
384
|
+
if (allAliases.includes(sideVariable)) mutatedVariables.push(sideVariable);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
const previous = mutatedVariables.map((name) => ({
|
|
388
|
+
name,
|
|
389
|
+
value: root.style.getPropertyValue(name),
|
|
390
|
+
priority: root.style.getPropertyPriority(name)
|
|
391
|
+
}));
|
|
392
|
+
const current = getComputedStyle(root).getPropertyValue(variable).trim();
|
|
393
|
+
const baseline = fingerprint(variable);
|
|
394
|
+
let changed = false;
|
|
395
|
+
for (const candidate of alternateValues(variable, current)) {
|
|
396
|
+
for (const name of mutatedVariables) root.style.setProperty(name, candidate, "important");
|
|
397
|
+
await settle();
|
|
398
|
+
if (movedFrom(variable, baseline)) {
|
|
399
|
+
changed = true;
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
for (const prior of previous) {
|
|
404
|
+
if (prior.value) root.style.setProperty(prior.name, prior.value, prior.priority);
|
|
405
|
+
else root.style.removeProperty(prior.name);
|
|
406
|
+
}
|
|
407
|
+
await settle();
|
|
408
|
+
(changed ? covered : unchanged).push(variable);
|
|
409
|
+
}
|
|
410
|
+
freeze.remove();
|
|
411
|
+
return { covered, unchanged };
|
|
412
|
+
}, { allAliases: componentAliases, forced: forcedVariables, proven: provenVariables });
|
|
413
|
+
}
|
|
414
|
+
async function selectComponent(page, component) {
|
|
415
|
+
await page.evaluate(async (id) => {
|
|
416
|
+
const editor = window.__liveTokensEditor;
|
|
417
|
+
if (!editor) throw new Error("window.__liveTokensEditor is not present on the components route");
|
|
418
|
+
editor.selectComponent(id);
|
|
419
|
+
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve())));
|
|
420
|
+
}, component);
|
|
421
|
+
await page.locator(".variant-group").filter({ visible: true }).waitFor();
|
|
422
|
+
}
|
|
423
|
+
for (const [component, aliases] of aliasesByComponent) {
|
|
424
|
+
test(`${component} repaints every property in its standardized runtime preview`, async ({ page }) => {
|
|
425
|
+
test.setTimeout(6e5);
|
|
426
|
+
await openComponentsEditor(page);
|
|
427
|
+
const covered = /* @__PURE__ */ new Set();
|
|
428
|
+
const seen = /* @__PURE__ */ new Set();
|
|
429
|
+
const unchanged = /* @__PURE__ */ new Set();
|
|
430
|
+
const controlExercised = /* @__PURE__ */ new Set();
|
|
431
|
+
const probe = async (componentAliases, forced = []) => {
|
|
432
|
+
const remaining = forced.length > 0 ? forced.filter((variable) => !covered.has(variable)) : [];
|
|
433
|
+
if (forced.length > 0 && remaining.length === 0) return;
|
|
434
|
+
if (forced.length > 0) {
|
|
435
|
+
const unchecked = page.locator(
|
|
436
|
+
'.variant-group:visible .tabs-preview input[type="checkbox"]:not(:checked):not(:disabled), .variant-group:visible .properties-header input[type="checkbox"]:not(:checked):not(:disabled)'
|
|
437
|
+
);
|
|
438
|
+
for (let index = 0; index < await unchecked.count(); index++) {
|
|
439
|
+
await unchecked.nth(index).click({ force: true });
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
const dialogAction = page.locator('[role="dialog"] button:not(:disabled)').first();
|
|
443
|
+
if (await dialogAction.count()) await dialogAction.hover({ force: true });
|
|
444
|
+
await exerciseVisibleTokenControls(page, componentAliases, controlExercised);
|
|
445
|
+
const gates = await exerciseVisibleCompositeControls(page, componentAliases, controlExercised);
|
|
446
|
+
await exerciseVisibleTokenControls(page, componentAliases, controlExercised);
|
|
447
|
+
await exerciseVisibleSplitPaddingControls(page, componentAliases, controlExercised);
|
|
448
|
+
await exerciseVisibleGradientControls(page, componentAliases, controlExercised);
|
|
449
|
+
const result = await probeCurrentView(page, componentAliases, remaining, [...covered]);
|
|
450
|
+
for (const gate of gates) {
|
|
451
|
+
if (await gate.count()) await gate.click();
|
|
452
|
+
}
|
|
453
|
+
result.covered.forEach((variable) => covered.add(variable));
|
|
454
|
+
result.covered.forEach((variable) => seen.add(variable));
|
|
455
|
+
result.unchanged.forEach((variable) => {
|
|
456
|
+
seen.add(variable);
|
|
457
|
+
if (!covered.has(variable)) unchanged.add(variable);
|
|
458
|
+
});
|
|
459
|
+
};
|
|
460
|
+
const traverseComponent = async (componentId, componentAliases, forced = []) => {
|
|
461
|
+
await selectComponent(page, componentId);
|
|
462
|
+
const visitViews = async () => {
|
|
463
|
+
if (forced.length > 0 && forced.every((variable) => covered.has(variable))) return;
|
|
464
|
+
const sizeSelect = page.locator(".variant-group:visible .preview-actions select").first();
|
|
465
|
+
const sizeValues = await sizeSelect.count() ? await sizeSelect.locator("option").evaluateAll((options) => options.map((option) => option.value)) : [""];
|
|
466
|
+
for (const sizeValue of sizeValues) {
|
|
467
|
+
const currentSizeSelect = page.locator(".variant-group:visible .preview-actions select").first();
|
|
468
|
+
if (sizeValue && await currentSizeSelect.count()) await currentSizeSelect.selectOption(sizeValue);
|
|
469
|
+
const variantTabs = page.locator(".variant-group:visible .variant-tabs .variant-tab-btn");
|
|
470
|
+
const variantLabels = await variantTabs.count() ? await variantTabs.allTextContents() : [""];
|
|
471
|
+
for (let variantIndex = 0; variantIndex < variantLabels.length; variantIndex++) {
|
|
472
|
+
if (variantLabels[variantIndex]) {
|
|
473
|
+
await page.locator(".variant-group:visible .variant-tabs .variant-tab-btn").nth(variantIndex).click({ force: true });
|
|
474
|
+
}
|
|
475
|
+
await probe(componentAliases, forced);
|
|
476
|
+
const primaryTabs = page.locator(
|
|
477
|
+
".variant-group:visible .tabs-states-block > .tabs-selectors:first-of-type .state-tab-btn"
|
|
478
|
+
);
|
|
479
|
+
const primaryLabels = await primaryTabs.allTextContents();
|
|
480
|
+
for (let primaryIndex = 0; primaryIndex < primaryLabels.length; primaryIndex++) {
|
|
481
|
+
await page.locator(
|
|
482
|
+
".variant-group:visible .tabs-states-block > .tabs-selectors:first-of-type .state-tab-btn"
|
|
483
|
+
).nth(primaryIndex).click({ force: true });
|
|
484
|
+
await probe(componentAliases, forced);
|
|
485
|
+
const secondaryTabs = page.locator(
|
|
486
|
+
".variant-group:visible .tabs-states-block > .tabs-selectors.substrip .state-tab-btn"
|
|
487
|
+
);
|
|
488
|
+
const secondaryLabels = await secondaryTabs.allTextContents();
|
|
489
|
+
for (let secondaryIndex = 0; secondaryIndex < secondaryLabels.length; secondaryIndex++) {
|
|
490
|
+
await page.locator(
|
|
491
|
+
".variant-group:visible .tabs-states-block > .tabs-selectors.substrip .state-tab-btn"
|
|
492
|
+
).nth(secondaryIndex).click({ force: true });
|
|
493
|
+
await probe(componentAliases, forced);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
await visitViews();
|
|
500
|
+
if (forced.length === 0 || forced.every((variable) => covered.has(variable))) return;
|
|
501
|
+
const dialogTrigger = page.locator(
|
|
502
|
+
'.variant-group:visible .tabs-preview [aria-haspopup="dialog"][aria-expanded="false"]'
|
|
503
|
+
).first();
|
|
504
|
+
if (await dialogTrigger.count()) {
|
|
505
|
+
await dialogTrigger.click({ force: true });
|
|
506
|
+
await page.locator('[role="dialog"]').waitFor();
|
|
507
|
+
await visitViews();
|
|
508
|
+
}
|
|
509
|
+
const canvasSelects = page.locator(".variant-group:visible .tabs-preview .canvas-toolbar select");
|
|
510
|
+
for (let selectIndex = 0; selectIndex < await canvasSelects.count(); selectIndex++) {
|
|
511
|
+
const values = await canvasSelects.nth(selectIndex).locator("option").evaluateAll((options) => options.map((option) => option.value));
|
|
512
|
+
for (const value of values) {
|
|
513
|
+
if (forced.every((variable) => covered.has(variable))) return;
|
|
514
|
+
await canvasSelects.nth(selectIndex).selectOption(value);
|
|
515
|
+
await visitViews();
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
const expanders = page.locator(
|
|
519
|
+
'.variant-group:visible .tabs-preview [aria-expanded]:not([aria-haspopup="dialog"])'
|
|
520
|
+
);
|
|
521
|
+
for (let index = 0; index < await expanders.count(); index++) {
|
|
522
|
+
if (forced.every((variable) => covered.has(variable))) return;
|
|
523
|
+
await expanders.nth(index).click({ force: true });
|
|
524
|
+
await visitViews();
|
|
525
|
+
}
|
|
526
|
+
const firstStateTab = page.locator(
|
|
527
|
+
".variant-group:visible .tabs-states-block > .tabs-selectors:first-of-type .state-tab-btn"
|
|
528
|
+
).first();
|
|
529
|
+
if (await firstStateTab.count()) await firstStateTab.click({ force: true });
|
|
530
|
+
const hoverTargets = page.locator(".variant-group:visible .tabs-preview *:visible");
|
|
531
|
+
for (let index = 0; index < await hoverTargets.count(); index++) {
|
|
532
|
+
if (forced.every((variable) => covered.has(variable))) return;
|
|
533
|
+
try {
|
|
534
|
+
await hoverTargets.nth(index).hover({ force: true, timeout: 2e3 });
|
|
535
|
+
} catch {
|
|
536
|
+
continue;
|
|
537
|
+
}
|
|
538
|
+
await probe(componentAliases, forced);
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
await traverseComponent(component, aliases);
|
|
542
|
+
const unresolved = aliases.filter((variable) => !covered.has(variable));
|
|
543
|
+
if (unresolved.length > 0) await traverseComponent(component, aliases, unresolved);
|
|
544
|
+
const notExposed = aliases.filter((variable) => !seen.has(variable));
|
|
545
|
+
const notReactive = aliases.filter((variable) => seen.has(variable) && !covered.has(variable));
|
|
546
|
+
const notControlExercised = aliases.filter((variable) => !controlExercised.has(variable));
|
|
547
|
+
expect(notExposed, `Properties not exposed by a standardized component view:
|
|
548
|
+
${notExposed.join("\n")}`).toEqual([]);
|
|
549
|
+
expect(notReactive, `Properties whose rendered preview did not change:
|
|
550
|
+
${notReactive.join("\n")}`).toEqual([]);
|
|
551
|
+
expect(notControlExercised, `Properties not exercised through a rendered editor control:
|
|
552
|
+
${notControlExercised.join("\n")}`).toEqual([]);
|
|
553
|
+
expect(covered.size).toBe(aliases.length);
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
test("component discovery covers every alias exactly once", () => {
|
|
557
|
+
const perComponent = [...aliasesByComponent.values()].reduce((sum, a) => sum + a.length, 0);
|
|
558
|
+
expect(aliasCases.length).toBeGreaterThan(0);
|
|
559
|
+
expect(perComponent).toBe(aliasCases.length);
|
|
560
|
+
});
|
|
561
|
+
for (const contract of await selectedContracts()) {
|
|
562
|
+
test(`${contract.id} paints each declared property on its declared part`, async ({ page }) => {
|
|
563
|
+
test.setTimeout(18e4);
|
|
564
|
+
const harness = await ContractHarness.open(page, contract);
|
|
565
|
+
expect(await harness.assertProperties()).toBeGreaterThan(0);
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
//# sourceMappingURL=component-render.contract.js.map
|