@robomous/ui-core 0.1.0 → 0.2.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/README.md +19 -7
- package/dist/{primitives → components}/alert.js +1 -1
- package/dist/{primitives → components}/badge.js +1 -1
- package/dist/{primitives → components}/button.d.ts +1 -1
- package/dist/{primitives → components}/button.js +4 -1
- package/dist/{primitives → components}/card.d.ts +1 -1
- package/dist/{primitives → components}/card.js +2 -2
- package/dist/{primitives → components}/combobox.js +4 -4
- package/dist/{primitives → components}/dialog.js +1 -1
- package/dist/{primitives → components}/dropdown-menu.js +12 -7
- package/dist/{primitives → components}/field.js +2 -4
- package/dist/{primitives → components}/input-group.js +1 -1
- package/dist/{primitives → components}/input.js +1 -1
- package/dist/{primitives → components}/label.js +1 -1
- package/dist/components/progress.js +7 -0
- package/dist/{primitives → components}/select.d.ts +3 -1
- package/dist/{primitives → components}/select.js +7 -4
- package/dist/{primitives → components}/separator.js +1 -1
- package/dist/{primitives → components}/sheet.js +1 -1
- package/dist/{primitives → components}/skeleton.js +1 -1
- package/dist/{primitives → components}/sonner.js +17 -12
- package/dist/{primitives → components}/table.d.ts +1 -1
- package/dist/{primitives → components}/table.js +3 -3
- package/dist/{primitives → components}/tabs.js +1 -1
- package/dist/{primitives → components}/textarea.js +1 -1
- package/dist/{primitives → components}/tooltip.js +1 -1
- package/dist/gates/index.d.ts +28 -0
- package/dist/gates/index.js +142 -0
- package/dist/index.d.ts +31 -32
- package/dist/index.js +32 -33
- package/dist/{tokens.d.ts → theme/tokens.d.ts} +5 -5
- package/dist/{tokens.js → theme/tokens.js} +12 -12
- package/package.json +23 -12
- package/{shadcn → src/components}/alert.tsx +14 -21
- package/src/{primitives → components}/badge.tsx +15 -21
- package/src/{primitives → components}/button.tsx +13 -10
- package/{shadcn → src/components}/card.tsx +16 -31
- package/src/{primitives → components}/combobox.test.tsx +15 -2
- package/src/{primitives → components}/combobox.tsx +45 -74
- package/src/{primitives/primitives.test.tsx → components/components.test.tsx} +106 -69
- package/src/{primitives → components}/dialog.tsx +31 -54
- package/src/{primitives → components}/dropdown-menu.tsx +51 -65
- package/src/{primitives → components}/field.tsx +48 -62
- package/src/{primitives → components}/input-group.tsx +37 -49
- package/{shadcn → src/components}/input.tsx +5 -5
- package/{shadcn → src/components}/label.tsx +7 -10
- package/src/{primitives → components}/progress.tsx +7 -6
- package/{shadcn → src/components}/select.tsx +40 -40
- package/{shadcn → src/components}/separator.tsx +7 -7
- package/src/{primitives → components}/sheet.tsx +27 -44
- package/{shadcn → src/components}/skeleton.tsx +3 -3
- package/src/{primitives → components}/sonner.test.tsx +12 -4
- package/src/components/sonner.tsx +67 -0
- package/{shadcn → src/components}/table.tsx +17 -44
- package/{shadcn → src/components}/tabs.tsx +16 -26
- package/{shadcn → src/components}/textarea.tsx +5 -5
- package/{shadcn → src/components}/tooltip.tsx +12 -16
- package/src/gates/design.test.ts +188 -0
- package/src/gates/index.test.ts +11 -0
- package/src/gates/index.ts +165 -0
- package/src/gates/tokens.test.ts +215 -0
- package/src/harness.test.tsx +1 -1
- package/src/index.ts +63 -33
- package/src/theme/imports.test.ts +160 -0
- package/src/{statusTone.test.ts → theme/statusTone.test.ts} +5 -1
- package/src/{styles.css → theme/styles.css} +45 -31
- package/src/theme/tailwind.css +646 -0
- package/src/{tokens.test.ts → theme/tokens.test.ts} +30 -23
- package/src/{tokens.ts → theme/tokens.ts} +12 -12
- package/components.json +0 -13
- package/dist/lib/button.d.ts +0 -1
- package/dist/lib/button.js +0 -4
- package/dist/lib/cn.d.ts +0 -18
- package/dist/lib/cn.js +0 -21
- package/dist/lib/menu.d.ts +0 -1
- package/dist/lib/menu.js +0 -16
- package/dist/lib/progress.d.ts +0 -3
- package/dist/lib/progress.js +0 -6
- package/dist/lib/select.d.ts +0 -1
- package/dist/lib/select.js +0 -3
- package/dist/primitives/progress.js +0 -7
- package/gates/canonical.test.mjs +0 -71
- package/gates/extensions.test.mjs +0 -373
- package/gates/index.d.mts +0 -51
- package/gates/index.mjs +0 -514
- package/gates/tokens.test.mjs +0 -276
- package/shadcn/README.md +0 -13
- package/shadcn/badge.tsx +0 -49
- package/shadcn/button.tsx +0 -67
- package/shadcn/combobox.tsx +0 -299
- package/shadcn/dialog.tsx +0 -166
- package/shadcn/dropdown-menu.tsx +0 -267
- package/shadcn/field.tsx +0 -236
- package/shadcn/input-group.tsx +0 -154
- package/shadcn/progress.tsx +0 -29
- package/shadcn/sheet.tsx +0 -145
- package/shadcn/sonner.tsx +0 -49
- package/src/lib/button.ts +0 -4
- package/src/lib/cn.test.ts +0 -33
- package/src/lib/cn.ts +0 -23
- package/src/lib/menu.ts +0 -16
- package/src/lib/progress.ts +0 -6
- package/src/lib/select.ts +0 -4
- package/src/primitives/alert.tsx +0 -76
- package/src/primitives/card.tsx +0 -103
- package/src/primitives/input.tsx +0 -19
- package/src/primitives/label.tsx +0 -22
- package/src/primitives/select.tsx +0 -190
- package/src/primitives/separator.tsx +0 -28
- package/src/primitives/skeleton.tsx +0 -13
- package/src/primitives/sonner.tsx +0 -65
- package/src/primitives/table.tsx +0 -114
- package/src/primitives/tabs.tsx +0 -88
- package/src/primitives/textarea.tsx +0 -18
- package/src/primitives/tooltip.tsx +0 -57
- /package/dist/{primitives → components}/alert.d.ts +0 -0
- /package/dist/{primitives → components}/badge.d.ts +0 -0
- /package/dist/{primitives → components}/combobox.d.ts +0 -0
- /package/dist/{primitives → components}/dialog.d.ts +0 -0
- /package/dist/{primitives → components}/dropdown-menu.d.ts +0 -0
- /package/dist/{primitives → components}/field.d.ts +0 -0
- /package/dist/{primitives → components}/input-group.d.ts +0 -0
- /package/dist/{primitives → components}/input.d.ts +0 -0
- /package/dist/{primitives → components}/label.d.ts +0 -0
- /package/dist/{primitives → components}/progress.d.ts +0 -0
- /package/dist/{primitives → components}/separator.d.ts +0 -0
- /package/dist/{primitives → components}/sheet.d.ts +0 -0
- /package/dist/{primitives → components}/skeleton.d.ts +0 -0
- /package/dist/{primitives → components}/sonner.d.ts +0 -0
- /package/dist/{primitives → components}/tabs.d.ts +0 -0
- /package/dist/{primitives → components}/textarea.d.ts +0 -0
- /package/dist/{primitives → components}/tooltip.d.ts +0 -0
- /package/dist/{statusTone.d.ts → theme/statusTone.d.ts} +0 -0
- /package/dist/{statusTone.js → theme/statusTone.js} +0 -0
- /package/src/{statusTone.ts → theme/statusTone.ts} +0 -0
package/gates/tokens.test.mjs
DELETED
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `DESIGN.md`'s first principle, machine-enforced: **never a colour in a class
|
|
3
|
-
* string.** The helpers live in `./index.mjs` so consumers run the same scans;
|
|
4
|
-
* this file keeps their fabricated-input self-tests and runs the repo gates
|
|
5
|
-
* over this package's own sources.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import assert from "node:assert/strict";
|
|
9
|
-
import { readFileSync } from "node:fs";
|
|
10
|
-
import path from "node:path";
|
|
11
|
-
import { spawnSync } from "node:child_process";
|
|
12
|
-
import { fileURLToPath } from "node:url";
|
|
13
|
-
import { test } from "node:test";
|
|
14
|
-
|
|
15
|
-
import { brandUsagesIn, colouredClassesIn, retiredDeclarationsIn } from "./index.mjs";
|
|
16
|
-
|
|
17
|
-
const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
18
|
-
|
|
19
|
-
const SOURCE = /\.(?:ts|tsx|css)$/;
|
|
20
|
-
|
|
21
|
-
// Fragments for fabricating violating input without this file matching itself.
|
|
22
|
-
const HEX = ["#", "[0-9a-fA-F]{3,8}"].join("");
|
|
23
|
-
const dash = (...parts) => parts.join("-");
|
|
24
|
-
|
|
25
|
-
test("the scan finds a colour smuggled into a class, and nothing that merely looks like one", () => {
|
|
26
|
-
assert.deepEqual(colouredClassesIn("a.tsx", ` <div className="bg-[${HEX.slice(0, 1)}eb5a47]" />`), [
|
|
27
|
-
`a.tsx:1: <div className="bg-[#eb5a47]" />`,
|
|
28
|
-
]);
|
|
29
|
-
assert.deepEqual(colouredClassesIn("b.tsx", ` className="text-[var(--accent)]"`), [
|
|
30
|
-
`b.tsx:1: className="text-[var(--accent)]"`,
|
|
31
|
-
]);
|
|
32
|
-
assert.deepEqual(colouredClassesIn("c.tsx", ` className="ring-[rgb(0 0 0)]"`), [
|
|
33
|
-
`c.tsx:1: className="ring-[rgb(0 0 0)]"`,
|
|
34
|
-
]);
|
|
35
|
-
|
|
36
|
-
// A token utility is the whole point of the rule and must pass.
|
|
37
|
-
assert.deepEqual(colouredClassesIn("d.tsx", ` className="bg-primary text-primary-foreground"`), []);
|
|
38
|
-
// The accent at 10% is a token with an opacity modifier, not a colour.
|
|
39
|
-
assert.deepEqual(colouredClassesIn("e.tsx", ` className="bg-primary/10 border-primary"`), []);
|
|
40
|
-
// An arbitrary value that is *not* a colour stays legal — the rule is about
|
|
41
|
-
// colour, and a one-off `top-[50%]` is not what v1 got wrong.
|
|
42
|
-
assert.deepEqual(colouredClassesIn("f.tsx", ` className="translate-y-[3px]"`), []);
|
|
43
|
-
// An inline style carrying a schema-supplied colour is the sanctioned road:
|
|
44
|
-
// `classColor` answers with whatever the kernel stored, and Tailwind has never
|
|
45
|
-
// seen it, so no utility could name it.
|
|
46
|
-
assert.deepEqual(
|
|
47
|
-
colouredClassesIn("g.tsx", ` style={{ background: classColor(declared, name) }}`),
|
|
48
|
-
[],
|
|
49
|
-
);
|
|
50
|
-
// A docstring explaining the rule must pass, or the gate forbids its own
|
|
51
|
-
// explanation — the mistake a boundary scan makes when it matches its own prose.
|
|
52
|
-
assert.deepEqual(colouredClassesIn("h.tsx", ` * Never write \`bg-[${"#"}eb5a47]\`.`), []);
|
|
53
|
-
// And a CSS custom property *declaration* is where colours are supposed to live.
|
|
54
|
-
assert.deepEqual(colouredClassesIn("i.css", ` --color-primary: #eb5a47;`), []);
|
|
55
|
-
// A colour-mix of two tokens names no colour of its own — the preset's own
|
|
56
|
-
// Button hover step.
|
|
57
|
-
assert.deepEqual(colouredClassesIn("x.tsx", 'className="hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)]"'), []);
|
|
58
|
-
// A colour-mix that mixes in a literal is still a colour smuggled into a class.
|
|
59
|
-
assert.equal(colouredClassesIn("x.tsx", 'className="bg-[color-mix(in_srgb,#fff,var(--x))]"').length, 1);
|
|
60
|
-
// A named CSS colour inside color-mix is still a colour.
|
|
61
|
-
assert.equal(colouredClassesIn("x.tsx", 'className="bg-[color-mix(in_oklch,red,var(--x))]"').length, 1);
|
|
62
|
-
// Whatever order the tokens come in, and whichever colour space, two tokens
|
|
63
|
-
// stay two tokens.
|
|
64
|
-
assert.equal(colouredClassesIn("x.tsx", 'className="bg-[color-mix(in_srgb,var(--a)_40%,var(--b))]"').length, 0);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("the brand scan counts a usage, and not the comment that states the rule", () => {
|
|
68
|
-
// A utility usage on any of the six colour-bearing prefixes is counted.
|
|
69
|
-
assert.deepEqual(brandUsagesIn("a.tsx", ` <span className="text-brand">Robomous</span>`), [
|
|
70
|
-
{ file: "a.tsx", at: 1, text: `<span className="text-brand">Robomous</span>` },
|
|
71
|
-
]);
|
|
72
|
-
assert.deepEqual(brandUsagesIn("b.tsx", ` className="h-full bg-brand transition-transform"`), [
|
|
73
|
-
{ file: "b.tsx", at: 1, text: `className="h-full bg-brand transition-transform"` },
|
|
74
|
-
]);
|
|
75
|
-
// An opacity modifier is still a usage of the brand colour.
|
|
76
|
-
assert.deepEqual(
|
|
77
|
-
brandUsagesIn("c.tsx", ` className="bg-brand/10"`).map((u) => u.at),
|
|
78
|
-
[1],
|
|
79
|
-
);
|
|
80
|
-
// A comment line states the rule rather than applying it.
|
|
81
|
-
assert.deepEqual(brandUsagesIn("d.css", ` * a third \`bg-brand\` is a design decision`), []);
|
|
82
|
-
assert.deepEqual(brandUsagesIn("e.tsx", ` // never add bg-brand here`), []);
|
|
83
|
-
// Another token on the same prefixes is not the brand.
|
|
84
|
-
assert.deepEqual(brandUsagesIn("f.tsx", ` className="bg-primary text-primary-foreground"`), []);
|
|
85
|
-
// The token *name* without a utility prefix is not a usage — tokens.test.ts
|
|
86
|
-
// asserts LIGHT_THEME.brand's value and must not trip the gate.
|
|
87
|
-
assert.deepEqual(brandUsagesIn("g.ts", ` expect(COLOR.brand).toBe("#e85d44");`), []);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
/** Every tracked source in this repo. */
|
|
91
|
-
function repoSources() {
|
|
92
|
-
const listed = spawnSync("git", ["ls-files", "-z"], { cwd: REPO, encoding: "utf8" });
|
|
93
|
-
assert.equal(listed.status, 0, `git ls-files failed: ${listed.stderr}`);
|
|
94
|
-
return listed.stdout.split("\0").filter((name) => SOURCE.test(name));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
test("no source puts a colour inside a class name", () => {
|
|
98
|
-
const tracked = repoSources();
|
|
99
|
-
assert.ok(tracked.length > 0, "the scan found no sources, so it proves nothing");
|
|
100
|
-
|
|
101
|
-
const offenders = tracked.flatMap((file) =>
|
|
102
|
-
colouredClassesIn(file, readFileSync(path.join(REPO, file), "utf8")),
|
|
103
|
-
);
|
|
104
|
-
assert.deepEqual(
|
|
105
|
-
offenders,
|
|
106
|
-
[],
|
|
107
|
-
"colour belongs to the token contract — add a token to " +
|
|
108
|
-
`src/styles.css and name the intent:\n${offenders.join("\n")}`,
|
|
109
|
-
);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* This package has zero brand sites: it declares the `--brand` token and never
|
|
114
|
-
* paints with it. `DESIGN.md` "Where the brand is": brand sites are a consumer
|
|
115
|
-
* decision — two enumerated sites per consuming app (the wordmark and the
|
|
116
|
-
* styleguide swatch that displays the value), gated in the consumer's repo.
|
|
117
|
-
*/
|
|
118
|
-
const BRAND_SITES = [];
|
|
119
|
-
|
|
120
|
-
test("the brand colour paints nothing here — brand sites are a consumer decision", () => {
|
|
121
|
-
const tracked = repoSources();
|
|
122
|
-
assert.ok(tracked.length > 0, "the scan found no sources, so it proves nothing");
|
|
123
|
-
|
|
124
|
-
const usages = tracked.flatMap((file) =>
|
|
125
|
-
brandUsagesIn(file, readFileSync(path.join(REPO, file), "utf8")),
|
|
126
|
-
);
|
|
127
|
-
assert.deepEqual(
|
|
128
|
-
usages.map((u) => u.file).sort(),
|
|
129
|
-
BRAND_SITES,
|
|
130
|
-
"DESIGN.md 'Where the brand is': this package declares the brand token and never uses it — " +
|
|
131
|
-
"a brand-coloured site belongs to a consuming app (two enumerated sites per app):\n" +
|
|
132
|
-
usages.map((u) => `${u.file}:${u.at}: ${u.text}`).join("\n"),
|
|
133
|
-
);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
test("the scan finds a retired declaration, and not a comment or a longer name that merely contains it", () => {
|
|
137
|
-
assert.deepEqual(retiredDeclarationsIn(` ${dash("--color", "disabled")}: oklch(0.9 0 0);`), [
|
|
138
|
-
`1: ${dash("--color", "disabled")}`,
|
|
139
|
-
]);
|
|
140
|
-
assert.deepEqual(retiredDeclarationsIn(` ${dash("--text", "meta")}: 0.75rem;`), [
|
|
141
|
-
`1: ${dash("--text", "meta")}`,
|
|
142
|
-
]);
|
|
143
|
-
// A comment recalling the retired name states history, not a declaration.
|
|
144
|
-
assert.deepEqual(
|
|
145
|
-
retiredDeclarationsIn(` /* ${dash("--color", "primary", "hover")} no longer exists */`),
|
|
146
|
-
[],
|
|
147
|
-
);
|
|
148
|
-
// A name that merely starts with a retired one is a different declaration —
|
|
149
|
-
// disabled-foreground is its own retired entry, and its presence must not
|
|
150
|
-
// be double-counted as disabled's.
|
|
151
|
-
assert.deepEqual(retiredDeclarationsIn(` ${dash("--color", "disabled", "foreground")}: red;`), [
|
|
152
|
-
`1: ${dash("--color", "disabled", "foreground")}`,
|
|
153
|
-
]);
|
|
154
|
-
// A current, kept token is not a retired one.
|
|
155
|
-
assert.deepEqual(retiredDeclarationsIn(` --brand: white;`), []);
|
|
156
|
-
// success/warning retired alongside the forked primitives that needed them —
|
|
157
|
-
// no longer a kept extension, so this now reports a hit.
|
|
158
|
-
assert.deepEqual(retiredDeclarationsIn(` --success-foreground: white;`), [
|
|
159
|
-
`1: ${dash("--success", "foreground")}`,
|
|
160
|
-
]);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
test("the retired foundation vocabulary is absent from the stylesheet", () => {
|
|
164
|
-
const STYLES_PATH = "src/styles.css";
|
|
165
|
-
const stylesheet = readFileSync(path.join(REPO, STYLES_PATH), "utf8");
|
|
166
|
-
const present = retiredDeclarationsIn(stylesheet);
|
|
167
|
-
assert.deepEqual(
|
|
168
|
-
present,
|
|
169
|
-
[],
|
|
170
|
-
"styles.css still declares a name the original audit retired — " +
|
|
171
|
-
`it has no shadcn analogue and no extension:\n${present.join("\n")}`,
|
|
172
|
-
);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* `components.json` carries the preset properties shadcn's own tools read, and
|
|
177
|
-
* only those: the fields its config schema defines. The schema is **strict** —
|
|
178
|
-
* `rawConfigSchema.safeParse` answers `unrecognized_keys` for anything else — so
|
|
179
|
-
* a decoded preset property the schema has no field for cannot be added here
|
|
180
|
-
* even as documentation. It would not be ignored; it would break every `shadcn`
|
|
181
|
-
* invocation that reads the file.
|
|
182
|
-
*
|
|
183
|
-
* `radius` is the property that keeps inviting the mistake: the preset decodes to
|
|
184
|
-
* `radius: medium`, and the obvious repair for "the config does not say so" is to
|
|
185
|
-
* write it in. The medium step's one home is `styles.css`'s `--radius: 0.625rem`
|
|
186
|
-
* (asserted by `tokens.test.ts`); this test is the other half, refusing the field
|
|
187
|
-
* that would look like a second home while doing nothing. Keys rather than a
|
|
188
|
-
* count, so a failure names what moved.
|
|
189
|
-
*/
|
|
190
|
-
const CONFIG_PATH = "components.json";
|
|
191
|
-
const SCHEMA_SUPPORTED_KEYS = [
|
|
192
|
-
"$schema",
|
|
193
|
-
"aliases",
|
|
194
|
-
"iconLibrary",
|
|
195
|
-
"menuAccent",
|
|
196
|
-
"menuColor",
|
|
197
|
-
"registries",
|
|
198
|
-
"rsc",
|
|
199
|
-
"rtl",
|
|
200
|
-
"style",
|
|
201
|
-
"tailwind",
|
|
202
|
-
"tsx",
|
|
203
|
-
];
|
|
204
|
-
|
|
205
|
-
test("components.json holds the schema-supported preset fields, and no others", () => {
|
|
206
|
-
const config = JSON.parse(readFileSync(path.join(REPO, CONFIG_PATH), "utf8"));
|
|
207
|
-
assert.deepEqual(
|
|
208
|
-
Object.keys(config).sort(),
|
|
209
|
-
SCHEMA_SUPPORTED_KEYS,
|
|
210
|
-
`${CONFIG_PATH} must carry exactly the fields shadcn's strict config schema defines. ` +
|
|
211
|
-
"A decoded preset property with no field here belongs in src/styles.css " +
|
|
212
|
-
"as a value — see DESIGN.md 'Source of Truth'",
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
// The preset's own values, where the schema does have a field for them.
|
|
216
|
-
assert.equal(config.style, "radix-nova");
|
|
217
|
-
assert.equal(config.iconLibrary, "lucide");
|
|
218
|
-
assert.equal(config.menuColor, "inverted");
|
|
219
|
-
assert.equal(config.menuAccent, "subtle");
|
|
220
|
-
assert.equal(config.tailwind.baseColor, "neutral");
|
|
221
|
-
assert.equal(config.tailwind.css, "src/styles.css");
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Lucide is the icon set, and the only one.
|
|
226
|
-
*
|
|
227
|
-
* The rule is "one icon library", not "this particular library". So this
|
|
228
|
-
* guards whichever set is currently *not* in use, and the value below is the
|
|
229
|
-
* whole of what changes when that decision changes.
|
|
230
|
-
*
|
|
231
|
-
* Assembled from fragments so this file never holds the package's name as a
|
|
232
|
-
* contiguous string, and a repository-wide sweep for it never mistakes its own
|
|
233
|
-
* guard for a lingering usage.
|
|
234
|
-
*/
|
|
235
|
-
const RETIRED_ICON_PACKAGE = ["@tabler", "icons-react"].join("/");
|
|
236
|
-
|
|
237
|
-
test("no package declares a second icon set, and no source imports one", () => {
|
|
238
|
-
const listed = spawnSync("git", ["ls-files", "-z"], { cwd: REPO, encoding: "utf8" });
|
|
239
|
-
assert.equal(listed.status, 0, `git ls-files failed: ${listed.stderr}`);
|
|
240
|
-
const tracked = listed.stdout.split("\0").filter(Boolean);
|
|
241
|
-
|
|
242
|
-
const manifests = tracked.filter((name) => /(?:^|\/)package\.json$/.test(name));
|
|
243
|
-
assert.ok(manifests.length > 0, "no manifests were read, so this proves nothing");
|
|
244
|
-
const declaring = manifests.filter((name) =>
|
|
245
|
-
readFileSync(path.join(REPO, name), "utf8").includes(`"${RETIRED_ICON_PACKAGE}"`),
|
|
246
|
-
);
|
|
247
|
-
assert.deepEqual(
|
|
248
|
-
declaring,
|
|
249
|
-
[],
|
|
250
|
-
`this package draws one icon set, and ${RETIRED_ICON_PACKAGE} is not it. ` +
|
|
251
|
-
`A second one is a decision for DESIGN.md, not a dependency:\n${declaring.join("\n")}`,
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
const sources = tracked.filter((name) => SOURCE.test(name));
|
|
255
|
-
assert.ok(sources.length > 0, "no sources were read, so this proves nothing");
|
|
256
|
-
const importing = sources.filter((name) =>
|
|
257
|
-
new RegExp(String.raw`(?:from|require\()\s*["']${RETIRED_ICON_PACKAGE}["']`).test(
|
|
258
|
-
readFileSync(path.join(REPO, name), "utf8"),
|
|
259
|
-
),
|
|
260
|
-
);
|
|
261
|
-
assert.deepEqual(importing, [], `these draw from the retired icon set:\n${importing.join("\n")}`);
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
test("the tokens have exactly one home, and it is the stylesheet", () => {
|
|
265
|
-
const listed = spawnSync("git", ["ls-files", "-z"], { cwd: REPO, encoding: "utf8" });
|
|
266
|
-
assert.equal(listed.status, 0, `git ls-files failed: ${listed.stderr}`);
|
|
267
|
-
const configs = listed.stdout
|
|
268
|
-
.split("\0")
|
|
269
|
-
.filter((name) => /(?:^|\/)tailwind\.config\.[cm]?[jt]s$/.test(name));
|
|
270
|
-
assert.deepEqual(
|
|
271
|
-
configs,
|
|
272
|
-
[],
|
|
273
|
-
"Tailwind v4 is CSS-first: the tokens live in src/styles.css. " +
|
|
274
|
-
`A config file gives them a second definition that wins for some utilities and not others:\n${configs.join("\n")}`,
|
|
275
|
-
);
|
|
276
|
-
});
|
package/shadcn/README.md
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# shadcn canonical snapshots
|
|
2
|
-
|
|
3
|
-
Each file here is exactly what `shadcn@4.19.0 add <name>` wrote for this
|
|
4
|
-
package's `components.json` (radix-nova, preset b2iH), before the import
|
|
5
|
-
relativisation `scripts/shadcn_relativize.mjs` applies. `tests/scripts/shadcn_canonical.test.mjs`
|
|
6
|
-
holds every `src/primitives/<name>.tsx` to its snapshot plus added lines only.
|
|
7
|
-
Regenerate with `pnpm --filter @visionset/ui-core shadcn:add <name>`; never edit by hand.
|
|
8
|
-
|
|
9
|
-
The gate matches the snapshot as an **ordered subsequence**, so a green run is a
|
|
10
|
-
strong signal rather than a proof: an edited line whose original text also appears
|
|
11
|
-
further down — including on a line that was added — still satisfies the match, and
|
|
12
|
-
the edit goes unreported. Read the diff when a primitive changes; the gate does not
|
|
13
|
-
stand in for that.
|
package/shadcn/badge.tsx
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
-
import { Slot } from "radix-ui"
|
|
4
|
-
|
|
5
|
-
import { cn } from "@/lib/cn"
|
|
6
|
-
|
|
7
|
-
const badgeVariants = cva(
|
|
8
|
-
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
|
|
9
|
-
{
|
|
10
|
-
variants: {
|
|
11
|
-
variant: {
|
|
12
|
-
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
13
|
-
secondary:
|
|
14
|
-
"bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
|
15
|
-
destructive:
|
|
16
|
-
"bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
|
|
17
|
-
outline:
|
|
18
|
-
"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
|
19
|
-
ghost:
|
|
20
|
-
"hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
|
21
|
-
link: "text-primary underline-offset-4 hover:underline",
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
defaultVariants: {
|
|
25
|
-
variant: "default",
|
|
26
|
-
},
|
|
27
|
-
}
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
function Badge({
|
|
31
|
-
className,
|
|
32
|
-
variant = "default",
|
|
33
|
-
asChild = false,
|
|
34
|
-
...props
|
|
35
|
-
}: React.ComponentProps<"span"> &
|
|
36
|
-
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
|
37
|
-
const Comp = asChild ? Slot.Root : "span"
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<Comp
|
|
41
|
-
data-slot="badge"
|
|
42
|
-
data-variant={variant}
|
|
43
|
-
className={cn(badgeVariants({ variant }), className)}
|
|
44
|
-
{...props}
|
|
45
|
-
/>
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export { Badge, badgeVariants }
|
package/shadcn/button.tsx
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
-
import { Slot } from "radix-ui"
|
|
4
|
-
|
|
5
|
-
import { cn } from "@/lib/cn"
|
|
6
|
-
|
|
7
|
-
const buttonVariants = cva(
|
|
8
|
-
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
9
|
-
{
|
|
10
|
-
variants: {
|
|
11
|
-
variant: {
|
|
12
|
-
default: "bg-primary text-primary-foreground hover:bg-primary/80",
|
|
13
|
-
outline:
|
|
14
|
-
"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
15
|
-
secondary:
|
|
16
|
-
"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
17
|
-
ghost:
|
|
18
|
-
"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
|
|
19
|
-
destructive:
|
|
20
|
-
"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
|
|
21
|
-
link: "text-primary underline-offset-4 hover:underline",
|
|
22
|
-
},
|
|
23
|
-
size: {
|
|
24
|
-
default:
|
|
25
|
-
"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
26
|
-
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
27
|
-
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
28
|
-
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
29
|
-
icon: "size-8",
|
|
30
|
-
"icon-xs":
|
|
31
|
-
"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
|
|
32
|
-
"icon-sm":
|
|
33
|
-
"size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
|
|
34
|
-
"icon-lg": "size-9",
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
defaultVariants: {
|
|
38
|
-
variant: "default",
|
|
39
|
-
size: "default",
|
|
40
|
-
},
|
|
41
|
-
}
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
function Button({
|
|
45
|
-
className,
|
|
46
|
-
variant = "default",
|
|
47
|
-
size = "default",
|
|
48
|
-
asChild = false,
|
|
49
|
-
...props
|
|
50
|
-
}: React.ComponentProps<"button"> &
|
|
51
|
-
VariantProps<typeof buttonVariants> & {
|
|
52
|
-
asChild?: boolean
|
|
53
|
-
}) {
|
|
54
|
-
const Comp = asChild ? Slot.Root : "button"
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<Comp
|
|
58
|
-
data-slot="button"
|
|
59
|
-
data-variant={variant}
|
|
60
|
-
data-size={size}
|
|
61
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
62
|
-
{...props}
|
|
63
|
-
/>
|
|
64
|
-
)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export { Button, buttonVariants }
|