basalt-ui 1.21.0 → 1.23.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/AGENTS.md +11 -1
- package/CHANGELOG.md +33 -0
- package/MIGRATING.md +205 -9
- package/README.md +63 -16
- package/agent/rules/basalt-app.md +39 -9
- package/agent/rules/basalt-charts.md +41 -6
- package/agent/rules/basalt-router.md +1 -1
- package/agent/rules/basalt-state.md +4 -4
- package/agent/rules/basalt-tokens.md +25 -4
- package/agent/skills/basalt-app/SKILL.md +4 -3
- package/agent/skills/basalt-charts/SKILL.md +23 -8
- package/agent/templates/DESIGN.md.tpl +3 -1
- package/configs/oxlint-plugin.js +86 -9
- package/configs/oxlint-plugin.test.ts +173 -0
- package/dist/charts/hooks/useBandPlot.d.ts +138 -0
- package/dist/charts/hooks/useBandPlot.d.ts.map +1 -0
- package/dist/charts/hooks/useBandPlot.js +127 -0
- package/dist/charts/index.d.ts +4 -0
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +14 -0
- package/dist/charts/kinds/BandStrip.d.ts +113 -0
- package/dist/charts/kinds/BandStrip.d.ts.map +1 -0
- package/dist/charts/kinds/BandStrip.js +218 -0
- package/dist/charts/kinds/BandStrip.test.d.ts +2 -0
- package/dist/charts/kinds/BandStrip.test.d.ts.map +1 -0
- package/dist/charts/kinds/Bars.d.ts +5 -1
- package/dist/charts/kinds/Bars.d.ts.map +1 -1
- package/dist/charts/kinds/Bars.js +2 -0
- package/dist/charts/kinds/MirroredBars.d.ts +93 -0
- package/dist/charts/kinds/MirroredBars.d.ts.map +1 -0
- package/dist/charts/kinds/MirroredBars.js +315 -0
- package/dist/charts/kinds/MirroredBars.test.d.ts +2 -0
- package/dist/charts/kinds/MirroredBars.test.d.ts.map +1 -0
- package/dist/charts/kinds/MultiLine.d.ts +5 -1
- package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
- package/dist/charts/kinds/MultiLine.js +2 -0
- package/dist/charts/kinds/StackedArea.d.ts +5 -1
- package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
- package/dist/charts/kinds/StackedArea.js +2 -0
- package/dist/charts/kinds/ZonedLine.d.ts +5 -1
- package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
- package/dist/charts/kinds/ZonedLine.js +2 -0
- package/dist/charts/primitives/CartesianChart.d.ts +18 -2
- package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
- package/dist/charts/primitives/CartesianChart.js +5 -2
- package/dist/charts/primitives/HatchPattern.d.ts +31 -0
- package/dist/charts/primitives/HatchPattern.d.ts.map +1 -0
- package/dist/charts/primitives/HatchPattern.js +31 -0
- package/dist/cli/index.d.ts +9 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +275 -48
- package/dist/cli/shipped-versions.test.d.ts +2 -0
- package/dist/cli/shipped-versions.test.d.ts.map +1 -0
- package/dist/guard/index.d.ts +67 -0
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +70 -11
- package/dist/vite.d.ts +44 -2
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +71 -26
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/src/charts/hooks/useBandPlot.ts +302 -0
- package/src/charts/index.ts +14 -0
- package/src/charts/kinds/BandStrip.test.tsx +200 -0
- package/src/charts/kinds/BandStrip.tsx +364 -0
- package/src/charts/kinds/Bars.tsx +7 -1
- package/src/charts/kinds/DualPanel.test.tsx +10 -4
- package/src/charts/kinds/MirroredBars.test.tsx +193 -0
- package/src/charts/kinds/MirroredBars.tsx +455 -0
- package/src/charts/kinds/MultiLine.tsx +7 -1
- package/src/charts/kinds/StackedArea.tsx +7 -1
- package/src/charts/kinds/ZonedLine.tsx +7 -1
- package/src/charts/primitives/CartesianChart.test.tsx +86 -4
- package/src/charts/primitives/CartesianChart.tsx +28 -3
- package/src/charts/primitives/HatchPattern.tsx +55 -0
- package/src/cli/check-theme.test.ts +35 -0
- package/src/cli/help.test.ts +5 -1
- package/src/cli/index.ts +552 -79
- package/src/cli/shipped-versions.test.ts +83 -0
- package/src/cli/toolchain-wiring.test.ts +362 -6
- package/src/cli/waiver-audit.test.ts +88 -4
- package/src/guard/check-source.test.ts +293 -0
- package/src/guard/index.ts +209 -20
- package/src/vite.test.ts +112 -0
- package/src/vite.ts +147 -31
package/dist/guard/index.js
CHANGED
|
@@ -108,7 +108,7 @@ const RADIUS_PROP_RE = /\bradius=(?:\{[0-9]+\}|"[0-9]+")/g;
|
|
|
108
108
|
const MOTION_TRANSITION_NUMERIC = /\btransition\s*=\s*\{\{[^}]*\b(?:duration|stiffness|damping|mass)\s*:\s*-?\d/g;
|
|
109
109
|
const MOTION_TRANSITION_EASE_ARRAY = /\btransition\s*=\s*\{\{[^}]*\bease\s*:\s*\[/g;
|
|
110
110
|
const RAW_CHART_LEGEND_ARRAY = /<ChartLegend\b[^>]*?\bitems\s*=\s*\{\s*\[/g;
|
|
111
|
-
const CHART_ENTRY_POINT_TAG = /<(?:MultiLine|Bars|Donut|DualPanel|Heatmap|ZonedLine|StackedArea|LineSparkline|BarSparkline)\b(?:<[^<>]*>)?(?:=>|[^>])*?>/g;
|
|
111
|
+
const CHART_ENTRY_POINT_TAG = /<(?:MultiLine|Bars|BandStrip|Donut|DualPanel|Heatmap|MirroredBars|ZonedLine|StackedArea|LineSparkline|BarSparkline)\b(?:<[^<>]*>)?(?:=>|[^>])*?>/g;
|
|
112
112
|
const HAS_ARIA_LABEL_PROP = /\bariaLabel\s*=/;
|
|
113
113
|
const RAW_FORM_CONTROL = /<(?:input|select|textarea)\b/g;
|
|
114
114
|
const RAW_FORM_CONTROL_TAG = /<(?:input|select|textarea)\b(?:=>|[^>])*?>/g;
|
|
@@ -250,10 +250,24 @@ function parseAllowAnnotation(rest, scope) {
|
|
|
250
250
|
return { rules, unknownRules, hasReason: reason.length >= MIN_ALLOW_REASON_LENGTH, scope };
|
|
251
251
|
}
|
|
252
252
|
const MAX_CSS_CONTINUATION_LINES = 8;
|
|
253
|
-
|
|
253
|
+
function commentBlockEnd(lines, index, tokenAt, syntax) {
|
|
254
|
+
const line = lines[index] ?? "";
|
|
255
|
+
if (syntax === "ts" && /\/\/\s*$/.test(line.slice(0, tokenAt))) return index;
|
|
256
|
+
const closer = syntax === "markup" ? "-->" : "*/";
|
|
257
|
+
if (line.slice(tokenAt).includes(closer)) return index;
|
|
258
|
+
for (let ahead = index + 1; ahead < lines.length; ahead++) {
|
|
259
|
+
if ((lines[ahead] ?? "").includes(closer)) return ahead;
|
|
260
|
+
}
|
|
261
|
+
return index;
|
|
262
|
+
}
|
|
254
263
|
const COMMENT_OPENER_BEFORE = /(?:\/\/|\/\*|<!--)\s*$/;
|
|
255
264
|
const COMMENT_ONLY_LINE = /^\s*(?:\{\s*\/\*|\/\/|\/\*|\*|<!--)/;
|
|
256
|
-
const
|
|
265
|
+
const JSX_COMMENT_CLOSE = /\*\/\s*\}\s*$/;
|
|
266
|
+
function isCommentOnly(raw, stripped) {
|
|
267
|
+
const code = stripped.trim();
|
|
268
|
+
return code === "" || COMMENT_ONLY_LINE.test(raw) || code === "}" && JSX_COMMENT_CLOSE.test(raw);
|
|
269
|
+
}
|
|
270
|
+
const ANNOTATION_PREFIX = /(?:^|\/\/|\/\*\*?|<!--|^\s*\*)\s*$/;
|
|
257
271
|
function escapeRegExp(value) {
|
|
258
272
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
259
273
|
}
|
|
@@ -272,7 +286,7 @@ function collectAllowAnnotations(lines, codeLines, allowComment, syntax) {
|
|
|
272
286
|
list.push(annotation);
|
|
273
287
|
byLine.set(index, list);
|
|
274
288
|
};
|
|
275
|
-
const record = (index, annotation, trailing) => {
|
|
289
|
+
const record = (index, annotation, trailing, tokenAt) => {
|
|
276
290
|
declared.push({ line: index + 1, annotation });
|
|
277
291
|
if (annotation.scope === "file") {
|
|
278
292
|
fileScoped.push(annotation);
|
|
@@ -280,11 +294,14 @@ function collectAllowAnnotations(lines, codeLines, allowComment, syntax) {
|
|
|
280
294
|
}
|
|
281
295
|
add(index, annotation);
|
|
282
296
|
if (!trailing) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
297
|
+
const closesAt = commentBlockEnd(lines, index, tokenAt, syntax);
|
|
298
|
+
for (let ahead = 1; index + ahead < lines.length; ahead++) {
|
|
299
|
+
const at = index + ahead;
|
|
300
|
+
add(at, annotation);
|
|
301
|
+
if (at <= closesAt) continue;
|
|
302
|
+
const raw = lines[at] ?? "";
|
|
287
303
|
if (raw.trim() === "") break;
|
|
304
|
+
if (!isCommentOnly(raw, codeLines[at] ?? "")) break;
|
|
288
305
|
}
|
|
289
306
|
return;
|
|
290
307
|
}
|
|
@@ -300,10 +317,15 @@ function collectAllowAnnotations(lines, codeLines, allowComment, syntax) {
|
|
|
300
317
|
for (let i = 0; i < lines.length; i++) {
|
|
301
318
|
const line = lines[i] ?? "";
|
|
302
319
|
const stripped = codeLines[i] ?? "";
|
|
303
|
-
const isCommentOnlyLine =
|
|
320
|
+
const isCommentOnlyLine = isCommentOnly(line, stripped);
|
|
304
321
|
if (syntax === "markup") {
|
|
305
322
|
for (const m of line.matchAll(jsonKey)) {
|
|
306
|
-
record(
|
|
323
|
+
record(
|
|
324
|
+
i,
|
|
325
|
+
parseAllowAnnotation(m[2] ?? "", m[1] === void 0 ? "line" : "file"),
|
|
326
|
+
false,
|
|
327
|
+
m.index
|
|
328
|
+
);
|
|
307
329
|
}
|
|
308
330
|
}
|
|
309
331
|
for (const m of line.matchAll(token)) {
|
|
@@ -315,7 +337,8 @@ function collectAllowAnnotations(lines, codeLines, allowComment, syntax) {
|
|
|
315
337
|
record(
|
|
316
338
|
i,
|
|
317
339
|
parseAllowAnnotation(rest, m[1] === void 0 ? "line" : "file"),
|
|
318
|
-
!isCommentOnlyLine
|
|
340
|
+
!isCommentOnlyLine,
|
|
341
|
+
at
|
|
319
342
|
);
|
|
320
343
|
}
|
|
321
344
|
}
|
|
@@ -997,13 +1020,49 @@ function checkSource(text, relPath, cfg) {
|
|
|
997
1020
|
severity: severityOf(f.kind, cfg)
|
|
998
1021
|
}));
|
|
999
1022
|
}
|
|
1023
|
+
function findAllowAnnotations(text, relPath, cfg) {
|
|
1024
|
+
const lines = text.split("\n");
|
|
1025
|
+
const syntax = guardSyntaxFor(relPath);
|
|
1026
|
+
const codeText = syntax === "markup" ? stripMarkupComments(text) : stripComments(text, syntax);
|
|
1027
|
+
const { declared } = collectAllowAnnotations(
|
|
1028
|
+
lines,
|
|
1029
|
+
codeText.split("\n"),
|
|
1030
|
+
cfg.allowComment,
|
|
1031
|
+
syntax
|
|
1032
|
+
);
|
|
1033
|
+
return declared.map(({ line, annotation }) => ({
|
|
1034
|
+
line,
|
|
1035
|
+
scope: annotation.scope,
|
|
1036
|
+
rules: [...annotation.rules],
|
|
1037
|
+
guardKinds: annotation.rules.filter((id) => Object.hasOwn(GUARD_RULES, id)),
|
|
1038
|
+
pluginRules: annotation.rules.filter(
|
|
1039
|
+
(id) => !Object.hasOwn(GUARD_RULES, id) && PLUGIN_RULE_IDS.has(id)
|
|
1040
|
+
),
|
|
1041
|
+
unknownRules: [...annotation.unknownRules],
|
|
1042
|
+
hasReason: annotation.hasReason,
|
|
1043
|
+
bare: annotation.rules.length === 0 && annotation.unknownRules.length === 0,
|
|
1044
|
+
text: lines[line - 1] ?? ""
|
|
1045
|
+
}));
|
|
1046
|
+
}
|
|
1047
|
+
const NEUTRALIZED_ALLOW_TOKEN = "basalt-audit-neutralized";
|
|
1048
|
+
function neutralizeAllowAnnotation(text, line, cfg) {
|
|
1049
|
+
const lines = text.split("\n");
|
|
1050
|
+
const target = lines[line - 1];
|
|
1051
|
+
if (target === void 0) return text;
|
|
1052
|
+
lines[line - 1] = target.replaceAll(cfg.allowComment, NEUTRALIZED_ALLOW_TOKEN);
|
|
1053
|
+
return lines.join("\n");
|
|
1054
|
+
}
|
|
1000
1055
|
export {
|
|
1001
1056
|
DEFAULT_GUARD_CONFIG,
|
|
1002
1057
|
GENERATED_HEADER_LINE,
|
|
1003
1058
|
GUARD_RULES,
|
|
1059
|
+
NEUTRALIZED_ALLOW_TOKEN,
|
|
1060
|
+
PLUGIN_RULE_IDS,
|
|
1004
1061
|
TOKENS_ONLY_DISABLED_KINDS,
|
|
1005
1062
|
checkSource,
|
|
1063
|
+
findAllowAnnotations,
|
|
1006
1064
|
guardKindRemedy,
|
|
1007
1065
|
guardWaiverHint,
|
|
1066
|
+
neutralizeAllowAnnotation,
|
|
1008
1067
|
unmatchedExemptPatterns
|
|
1009
1068
|
};
|
package/dist/vite.d.ts
CHANGED
|
@@ -33,6 +33,29 @@ export type BasaltViteOptions = {
|
|
|
33
33
|
enforcementNotice?: boolean;
|
|
34
34
|
};
|
|
35
35
|
export declare function basaltViteConfig(opts: BasaltViteOptions): UserConfig;
|
|
36
|
+
/**
|
|
37
|
+
* One icon a consumer names itself — a manifest `icons` member, plus optionally the head `<link>`
|
|
38
|
+
* that points at the same file.
|
|
39
|
+
*
|
|
40
|
+
* The field names are the manifest's own, verbatim, so what a consumer writes here is what ships in
|
|
41
|
+
* `site.webmanifest`. `src` is root-relative (`'/favicon.svg'` or `'favicon.svg'`, both resolve the
|
|
42
|
+
* same) and is prefixed with Vite's `base` exactly like basalt's built-in set.
|
|
43
|
+
*/
|
|
44
|
+
export type BasaltAppIcon = {
|
|
45
|
+
/** Root-relative path under `public/`. Prefixed with Vite's resolved `base`. */
|
|
46
|
+
src: string;
|
|
47
|
+
/** Manifest `sizes`. `'any'` for a scalable SVG; `'192x192'` for a raster. */
|
|
48
|
+
sizes?: string;
|
|
49
|
+
/** MIME type, e.g. `'image/svg+xml'`. */
|
|
50
|
+
type?: string;
|
|
51
|
+
/** Manifest `purpose`, e.g. `'any maskable'`. */
|
|
52
|
+
purpose?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Emit a head `<link rel="…">` for this icon too. Omitted → manifest only, which is what an app
|
|
55
|
+
* that links its own favicon from `index.html` wants.
|
|
56
|
+
*/
|
|
57
|
+
rel?: 'icon' | 'shortcut icon' | 'apple-touch-icon' | 'mask-icon';
|
|
58
|
+
};
|
|
36
59
|
export type BasaltAppOptions = {
|
|
37
60
|
/** App display name — used verbatim as the manifest `name` and (unless overridden) OG title. */
|
|
38
61
|
name: string;
|
|
@@ -80,10 +103,29 @@ export type BasaltAppOptions = {
|
|
|
80
103
|
scope?: string;
|
|
81
104
|
/** Manifest `id`. Default: the resolved `startUrl` (default- or explicitly-derived, per above). */
|
|
82
105
|
id?: string;
|
|
83
|
-
/**
|
|
106
|
+
/**
|
|
107
|
+
* Which icons the plugin knows about — three forms, one question each.
|
|
108
|
+
*
|
|
109
|
+
* - **omitted / `{ dir }`** — basalt's own six-filename set (the realfavicongenerator layout argo
|
|
110
|
+
* ships), optionally under a subdirectory of `public/`. Unchanged default.
|
|
111
|
+
* - **`false`** — no head `<link>` icons and no manifest `icons` member, so a manifest is never
|
|
112
|
+
* emitted pointing at files that 404.
|
|
113
|
+
* - **an array** — the icons this app ACTUALLY has, named by the consumer.
|
|
114
|
+
*
|
|
115
|
+
* The array is the form for every app that was not scaffolded from basalt's icon set, which is
|
|
116
|
+
* most of them. `{ dir }` can only move the six fixed filenames; it cannot rename one, so an app
|
|
117
|
+
* whose single icon is `favicon.svg` had no way to say so and had to keep a hand-written manifest
|
|
118
|
+
* beside the generated head — the last thing standing between rb and deleting a file carrying a
|
|
119
|
+
* permanent `theme-allow-file`. One SVG at `sizes: 'any'` is a complete, installable icon set.
|
|
120
|
+
*
|
|
121
|
+
* Every entry becomes a manifest icon; an entry becomes a head `<link>` only when it names a
|
|
122
|
+
* {@link BasaltAppIcon.rel}. That split is deliberate — an app that already links its favicon
|
|
123
|
+
* from its own `index.html` wants the manifest fixed without a duplicate tag, and an app that
|
|
124
|
+
* wants the plugin to own the head says so per icon. An empty array reads as `false`.
|
|
125
|
+
*/
|
|
84
126
|
icons?: false | {
|
|
85
127
|
dir?: string;
|
|
86
|
-
};
|
|
128
|
+
} | readonly BasaltAppIcon[];
|
|
87
129
|
/** Emits `<meta name="darkreader-lock">` when `'lock'` (default). Pass `false` to omit it. */
|
|
88
130
|
darkreader?: 'lock' | false;
|
|
89
131
|
/** Site-wide SEO metadata — only the keys provided are emitted, no per-page SEO. */
|
package/dist/vite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAA6B,YAAY,EAAE,UAAU,EAAE,MAAM,MAAM,CAAA;AAI/E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAA;IACZ,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B,CAAA;AA6CD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,UAAU,CAgFpE;AAgBD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gGAAgG;IAChG,IAAI,EAAE,MAAM,CAAA;IACZ,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gGAAgG;IAChG,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACrD;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAA;IAC/C;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,wDAAwD;IACxD,OAAO,CAAC,EAAE,YAAY,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,CAAA;IAChE;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mGAAmG;IACnG,EAAE,CAAC,EAAE,MAAM,CAAA;IACX
|
|
1
|
+
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAA6B,YAAY,EAAE,UAAU,EAAE,MAAM,MAAM,CAAA;AAI/E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAA;IACZ,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B,CAAA;AA6CD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,UAAU,CAgFpE;AAgBD;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAA;IACX,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,kBAAkB,GAAG,WAAW,CAAA;CAClE,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gGAAgG;IAChG,IAAI,EAAE,MAAM,CAAA;IACZ,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gGAAgG;IAChG,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACrD;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAA;IAC/C;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,wDAAwD;IACxD,OAAO,CAAC,EAAE,YAAY,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,CAAA;IAChE;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mGAAmG;IACnG,EAAE,CAAC,EAAE,MAAM,CAAA;IACX;;;;;;;;;;;;;;;;;;;OAmBG;IACH,KAAK,CAAC,EAAE,KAAK,GAAG;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,aAAa,EAAE,CAAA;IAC3D,8FAA8F;IAC9F,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;IAC3B,oFAAoF;IACpF,GAAG,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,SAAS,GAAG,qBAAqB,CAAA;KAAE,CAAA;IACvF,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,KAAK,CAAA;IAChB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAClD,CAAA;AA0kBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,YAAY,EAAE,CAMzE"}
|
package/dist/vite.js
CHANGED
|
@@ -133,6 +133,18 @@ function withIconPath(dir, file) {
|
|
|
133
133
|
function withBase(base, path) {
|
|
134
134
|
return base + path.slice(1);
|
|
135
135
|
}
|
|
136
|
+
function isIconList(icons) {
|
|
137
|
+
return Array.isArray(icons);
|
|
138
|
+
}
|
|
139
|
+
function resolveIcons(icons) {
|
|
140
|
+
if (icons === false) return { mode: "none" };
|
|
141
|
+
if (icons === void 0) return { mode: "default", dir: void 0 };
|
|
142
|
+
if (!isIconList(icons)) return { mode: "default", dir: icons.dir };
|
|
143
|
+
return icons.length === 0 ? { mode: "none" } : { mode: "explicit", icons };
|
|
144
|
+
}
|
|
145
|
+
function iconHref(base, src) {
|
|
146
|
+
return withBase(base, src.startsWith("/") ? src : `/${src}`);
|
|
147
|
+
}
|
|
136
148
|
function hasViewportMeta(html) {
|
|
137
149
|
return /<meta\s+[^>]*name=["']viewport["']/i.test(html);
|
|
138
150
|
}
|
|
@@ -207,8 +219,7 @@ function buildStaticTags(input) {
|
|
|
207
219
|
shortName,
|
|
208
220
|
themeColor,
|
|
209
221
|
bootStyle,
|
|
210
|
-
|
|
211
|
-
iconsDir,
|
|
222
|
+
icons,
|
|
212
223
|
darkreader,
|
|
213
224
|
manifestEnabled,
|
|
214
225
|
base,
|
|
@@ -234,13 +245,13 @@ function buildStaticTags(input) {
|
|
|
234
245
|
}
|
|
235
246
|
);
|
|
236
247
|
tags.push({ tag: "style", children: bootStyle });
|
|
237
|
-
if (
|
|
248
|
+
if (icons.mode === "default") {
|
|
238
249
|
tags.push(
|
|
239
250
|
{
|
|
240
251
|
tag: "link",
|
|
241
252
|
attrs: {
|
|
242
253
|
rel: "shortcut icon",
|
|
243
|
-
href: withBase(base, withIconPath(
|
|
254
|
+
href: withBase(base, withIconPath(icons.dir, ICON_FILES.favicon))
|
|
244
255
|
}
|
|
245
256
|
},
|
|
246
257
|
{
|
|
@@ -248,7 +259,7 @@ function buildStaticTags(input) {
|
|
|
248
259
|
attrs: {
|
|
249
260
|
rel: "icon",
|
|
250
261
|
type: "image/svg+xml",
|
|
251
|
-
href: withBase(base, withIconPath(
|
|
262
|
+
href: withBase(base, withIconPath(icons.dir, ICON_FILES.svg))
|
|
252
263
|
}
|
|
253
264
|
},
|
|
254
265
|
{
|
|
@@ -256,7 +267,7 @@ function buildStaticTags(input) {
|
|
|
256
267
|
attrs: {
|
|
257
268
|
rel: "icon",
|
|
258
269
|
sizes: "96x96",
|
|
259
|
-
href: withBase(base, withIconPath(
|
|
270
|
+
href: withBase(base, withIconPath(icons.dir, ICON_FILES.png96))
|
|
260
271
|
}
|
|
261
272
|
},
|
|
262
273
|
{
|
|
@@ -264,11 +275,25 @@ function buildStaticTags(input) {
|
|
|
264
275
|
attrs: {
|
|
265
276
|
rel: "apple-touch-icon",
|
|
266
277
|
sizes: "180x180",
|
|
267
|
-
href: withBase(base, withIconPath(
|
|
278
|
+
href: withBase(base, withIconPath(icons.dir, ICON_FILES.appleTouch))
|
|
268
279
|
}
|
|
269
280
|
}
|
|
270
281
|
);
|
|
271
282
|
}
|
|
283
|
+
if (icons.mode === "explicit") {
|
|
284
|
+
for (const icon of icons.icons) {
|
|
285
|
+
if (icon.rel === void 0) continue;
|
|
286
|
+
tags.push({
|
|
287
|
+
tag: "link",
|
|
288
|
+
attrs: {
|
|
289
|
+
rel: icon.rel,
|
|
290
|
+
...icon.type === void 0 ? {} : { type: icon.type },
|
|
291
|
+
...icon.sizes === void 0 ? {} : { sizes: icon.sizes },
|
|
292
|
+
href: iconHref(base, icon.src)
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
272
297
|
tags.push(
|
|
273
298
|
{ tag: "meta", attrs: { name: "apple-mobile-web-app-title", content: shortName } },
|
|
274
299
|
{ tag: "meta", attrs: { name: "apple-mobile-web-app-capable", content: "yes" } },
|
|
@@ -286,6 +311,34 @@ function buildStaticTags(input) {
|
|
|
286
311
|
);
|
|
287
312
|
return tags;
|
|
288
313
|
}
|
|
314
|
+
function manifestIcons(icons, base) {
|
|
315
|
+
if (icons.mode === "none") return {};
|
|
316
|
+
if (icons.mode === "default") {
|
|
317
|
+
return {
|
|
318
|
+
icons: [
|
|
319
|
+
{
|
|
320
|
+
src: withBase(base, withIconPath(icons.dir, ICON_FILES.manifest192)),
|
|
321
|
+
sizes: "192x192",
|
|
322
|
+
type: "image/png"
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
src: withBase(base, withIconPath(icons.dir, ICON_FILES.manifest512)),
|
|
326
|
+
sizes: "512x512",
|
|
327
|
+
type: "image/png",
|
|
328
|
+
purpose: "any maskable"
|
|
329
|
+
}
|
|
330
|
+
]
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
icons: icons.icons.map((icon) => ({
|
|
335
|
+
src: iconHref(base, icon.src),
|
|
336
|
+
...icon.sizes === void 0 ? {} : { sizes: icon.sizes },
|
|
337
|
+
...icon.type === void 0 ? {} : { type: icon.type },
|
|
338
|
+
...icon.purpose === void 0 ? {} : { purpose: icon.purpose }
|
|
339
|
+
}))
|
|
340
|
+
};
|
|
341
|
+
}
|
|
289
342
|
function buildManifestJson(input) {
|
|
290
343
|
const {
|
|
291
344
|
options,
|
|
@@ -296,7 +349,7 @@ function buildManifestJson(input) {
|
|
|
296
349
|
display,
|
|
297
350
|
schemeColor,
|
|
298
351
|
backgroundColor,
|
|
299
|
-
|
|
352
|
+
icons,
|
|
300
353
|
base
|
|
301
354
|
} = input;
|
|
302
355
|
const manifest = {
|
|
@@ -311,19 +364,13 @@ function buildManifestJson(input) {
|
|
|
311
364
|
// `<meta name="theme-color">` pair. Unchanged for the default (`'dark'`) and for `'auto'`.
|
|
312
365
|
theme_color: schemeColor,
|
|
313
366
|
background_color: backgroundColor,
|
|
314
|
-
icons:
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
src: withBase(base, withIconPath(iconsDir, ICON_FILES.manifest512)),
|
|
322
|
-
sizes: "512x512",
|
|
323
|
-
type: "image/png",
|
|
324
|
-
purpose: "any maskable"
|
|
325
|
-
}
|
|
326
|
-
]
|
|
367
|
+
// `icons: false` reaches HERE too, not just the head links. The array was unconditional, so
|
|
368
|
+
// `{ manifest: true, icons: false }` shipped a manifest pointing at two PNGs the app never
|
|
369
|
+
// ships — an installable app with broken icons, and the reason rb went hybrid (plugin for the
|
|
370
|
+
// head, a hand-written manifest for the rest). A manifest with NO icons member is valid.
|
|
371
|
+
// The `'explicit'` arm is the other half of that fix: honest about icons it cannot see was
|
|
372
|
+
// only worth having once a consumer could tell it which icons it DOES have.
|
|
373
|
+
...manifestIcons(icons, base)
|
|
327
374
|
};
|
|
328
375
|
return JSON.stringify(manifest, null, 2);
|
|
329
376
|
}
|
|
@@ -331,8 +378,7 @@ function createMainPlugin(options) {
|
|
|
331
378
|
const shortName = options.shortName ?? options.name;
|
|
332
379
|
const display = options.display ?? "standalone";
|
|
333
380
|
const darkreader = options.darkreader ?? "lock";
|
|
334
|
-
const
|
|
335
|
-
const iconsDir = options.icons === false ? void 0 : options.icons?.dir;
|
|
381
|
+
const icons = resolveIcons(options.icons);
|
|
336
382
|
const manifestEnabled = options.manifest !== false;
|
|
337
383
|
const themeColor = resolveThemeColors(options.themeColor);
|
|
338
384
|
const colorScheme = options.colorScheme ?? "dark";
|
|
@@ -364,7 +410,7 @@ function createMainPlugin(options) {
|
|
|
364
410
|
display,
|
|
365
411
|
schemeColor,
|
|
366
412
|
backgroundColor,
|
|
367
|
-
|
|
413
|
+
icons,
|
|
368
414
|
base
|
|
369
415
|
});
|
|
370
416
|
staticTags = buildStaticTags({
|
|
@@ -372,8 +418,7 @@ function createMainPlugin(options) {
|
|
|
372
418
|
shortName,
|
|
373
419
|
themeColor,
|
|
374
420
|
bootStyle,
|
|
375
|
-
|
|
376
|
-
iconsDir,
|
|
421
|
+
icons,
|
|
377
422
|
darkreader,
|
|
378
423
|
manifestEnabled,
|
|
379
424
|
base,
|
package/llms.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# basalt-ui v1.
|
|
1
|
+
# basalt-ui v1.23.0
|
|
2
2
|
# Opinionated framework for Mantine-based React apps — theme, app shell, visx charts, toolchain presets
|
|
3
3
|
#
|
|
4
4
|
# basalt-ui is an opinionated framework for Mantine-based React apps.
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
# ./tokens.css subpath. Guide: docs/FRAMEWORK-FREE.md in the repo.
|
|
20
20
|
|
|
21
21
|
Package: basalt-ui
|
|
22
|
-
Version: 1.
|
|
22
|
+
Version: 1.23.0
|
|
23
23
|
|
|
24
24
|
## basalt-ui
|
|
25
25
|
Import: import { ... } from 'basalt-ui'
|