@dixy-ui/themes 0.0.0-stage → 0.1.0-beta.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/LICENSE +21 -0
- package/README.md +104 -2
- package/dist/ansi/ornament.d.ts +3 -0
- package/dist/ansi/ornament.js +8 -0
- package/dist/ansi-panes.css +929 -0
- package/dist/art-deco.css +850 -0
- package/dist/atelier/ornament.d.ts +4 -0
- package/dist/atelier/ornament.js +9 -0
- package/dist/atomic/ornament.d.ts +3 -0
- package/dist/atomic/ornament.js +8 -0
- package/dist/atomic-lounge.css +946 -0
- package/dist/authoring.d.ts +142 -0
- package/dist/authoring.js +83 -0
- package/dist/azulejo/ornament.d.ts +3 -0
- package/dist/azulejo/ornament.js +8 -0
- package/dist/azulejo-tile.css +962 -0
- package/dist/blueprint/ornament.d.ts +3 -0
- package/dist/blueprint/ornament.js +8 -0
- package/dist/blueprint.css +897 -0
- package/dist/brutalist/ornament.d.ts +3 -0
- package/dist/brutalist/ornament.js +8 -0
- package/dist/brutalist-concrete.css +936 -0
- package/dist/bytes.json +162 -0
- package/dist/candy-clay.css +778 -0
- package/dist/catalog.d.ts +1346 -0
- package/dist/catalog.js +1379 -0
- package/dist/character.d.ts +103 -0
- package/dist/character.js +477 -0
- package/dist/chrome.d.ts +114 -0
- package/dist/chrome.js +247 -0
- package/dist/clay/ornament.d.ts +3 -0
- package/dist/clay/ornament.js +8 -0
- package/dist/compile-css.d.ts +3 -0
- package/dist/compile-css.js +133 -0
- package/dist/deco/ornament.d.ts +3 -0
- package/dist/deco/ornament.js +8 -0
- package/dist/diwali/ornament.d.ts +3 -0
- package/dist/diwali/ornament.js +8 -0
- package/dist/diwali-lights.css +894 -0
- package/dist/documents.d.ts +104 -0
- package/dist/documents.js +1411 -0
- package/dist/forest/ornament.d.ts +3 -0
- package/dist/forest/ornament.js +12 -0
- package/dist/foundations.d.ts +105 -0
- package/dist/foundations.js +140 -0
- package/dist/frame-corners.d.ts +5 -0
- package/dist/frame-corners.js +5 -0
- package/dist/frame-styles.d.ts +36 -0
- package/dist/frame-styles.js +146 -0
- package/dist/freeze.d.ts +2 -0
- package/dist/freeze.js +9 -0
- package/dist/freighter/ornament.d.ts +3 -0
- package/dist/freighter/ornament.js +8 -0
- package/dist/freighter.css +942 -0
- package/dist/glass/ornament.d.ts +3 -0
- package/dist/glass/ornament.js +8 -0
- package/dist/glass-ui.css +923 -0
- package/dist/hardware/ornament.d.ts +4 -0
- package/dist/hardware/ornament.js +10 -0
- package/dist/holi/ornament.d.ts +3 -0
- package/dist/holi/ornament.js +8 -0
- package/dist/holi-colours.css +887 -0
- package/dist/hud/ornament.d.ts +3 -0
- package/dist/hud/ornament.js +8 -0
- package/dist/light/ornament.d.ts +3 -0
- package/dist/light/ornament.js +10 -0
- package/dist/living-forest.css +917 -0
- package/dist/lunar/ornament.d.ts +3 -0
- package/dist/lunar/ornament.js +8 -0
- package/dist/lunar-new-year.css +811 -0
- package/dist/manifests.d.ts +2485 -0
- package/dist/manifests.js +264 -0
- package/dist/material-light.css +880 -0
- package/dist/neon/ornament.d.ts +3 -0
- package/dist/neon/ornament.js +8 -0
- package/dist/neon-night-market.css +944 -0
- package/dist/ocean/ornament.d.ts +3 -0
- package/dist/ocean/ornament.js +8 -0
- package/dist/ocean-depths.css +825 -0
- package/dist/orbital-hud.css +973 -0
- package/dist/organic-css.d.ts +3 -0
- package/dist/organic-css.js +23 -0
- package/dist/organic-geometry.d.ts +20 -0
- package/dist/organic-geometry.js +74 -0
- package/dist/organic-ornament.d.ts +9 -0
- package/dist/organic-ornament.js +28 -0
- package/dist/organic-paint-launch.d.ts +43 -0
- package/dist/organic-paint-launch.js +1156 -0
- package/dist/organic-paint-world.d.ts +24 -0
- package/dist/organic-paint-world.js +676 -0
- package/dist/organic-paint.d.ts +50 -0
- package/dist/organic-paint.js +695 -0
- package/dist/organic-runtime.d.ts +4 -0
- package/dist/organic-runtime.js +231 -0
- package/dist/organic-settings.d.ts +680 -0
- package/dist/organic-settings.js +433 -0
- package/dist/ornament-parts.d.ts +20 -0
- package/dist/ornament-parts.js +15 -0
- package/dist/papel-picado.css +904 -0
- package/dist/phosphor/ornament.d.ts +3 -0
- package/dist/phosphor/ornament.js +8 -0
- package/dist/phosphor-terminal.css +1011 -0
- package/dist/picado/ornament.d.ts +3 -0
- package/dist/picado/ornament.js +8 -0
- package/dist/prism/ornament.d.ts +4 -0
- package/dist/prism/ornament.js +9 -0
- package/dist/prism-glass.css +970 -0
- package/dist/recipes.d.ts +938 -0
- package/dist/recipes.js +939 -0
- package/dist/resolve.d.ts +40 -0
- package/dist/resolve.js +24 -0
- package/dist/retro/ornament.d.ts +5 -0
- package/dist/retro/ornament.js +10 -0
- package/dist/retro-os.css +867 -0
- package/dist/riso/ornament.d.ts +3 -0
- package/dist/riso/ornament.js +8 -0
- package/dist/riso-zine.css +786 -0
- package/dist/sail/ornament.d.ts +3 -0
- package/dist/sail/ornament.js +8 -0
- package/dist/scrapbook.css +685 -0
- package/dist/signal/ornament.d.ts +4 -0
- package/dist/signal/ornament.js +11 -0
- package/dist/skins/ansi-panes.d.ts +68 -0
- package/dist/skins/ansi-panes.js +68 -0
- package/dist/skins/art-deco.d.ts +68 -0
- package/dist/skins/art-deco.js +68 -0
- package/dist/skins/atomic-lounge.d.ts +68 -0
- package/dist/skins/atomic-lounge.js +68 -0
- package/dist/skins/azulejo-tile.d.ts +68 -0
- package/dist/skins/azulejo-tile.js +68 -0
- package/dist/skins/blueprint.d.ts +68 -0
- package/dist/skins/blueprint.js +68 -0
- package/dist/skins/brutalist-concrete.d.ts +68 -0
- package/dist/skins/brutalist-concrete.js +68 -0
- package/dist/skins/candy-clay.d.ts +57 -0
- package/dist/skins/candy-clay.js +57 -0
- package/dist/skins/diwali-lights.d.ts +68 -0
- package/dist/skins/diwali-lights.js +68 -0
- package/dist/skins/freighter.d.ts +68 -0
- package/dist/skins/freighter.js +68 -0
- package/dist/skins/glass-ui.d.ts +68 -0
- package/dist/skins/glass-ui.js +68 -0
- package/dist/skins/holi-colours.d.ts +68 -0
- package/dist/skins/holi-colours.js +68 -0
- package/dist/skins/living-forest.d.ts +68 -0
- package/dist/skins/living-forest.js +68 -0
- package/dist/skins/lunar-new-year.d.ts +68 -0
- package/dist/skins/lunar-new-year.js +68 -0
- package/dist/skins/material-light.d.ts +57 -0
- package/dist/skins/material-light.js +57 -0
- package/dist/skins/neon-night-market.d.ts +68 -0
- package/dist/skins/neon-night-market.js +68 -0
- package/dist/skins/ocean-depths.d.ts +57 -0
- package/dist/skins/ocean-depths.js +57 -0
- package/dist/skins/orbital-hud.d.ts +68 -0
- package/dist/skins/orbital-hud.js +68 -0
- package/dist/skins/papel-picado.d.ts +68 -0
- package/dist/skins/papel-picado.js +68 -0
- package/dist/skins/phosphor-terminal.d.ts +68 -0
- package/dist/skins/phosphor-terminal.js +68 -0
- package/dist/skins/prism-glass.d.ts +57 -0
- package/dist/skins/prism-glass.js +57 -0
- package/dist/skins/retro-os.d.ts +57 -0
- package/dist/skins/retro-os.js +57 -0
- package/dist/skins/riso-zine.d.ts +68 -0
- package/dist/skins/riso-zine.js +68 -0
- package/dist/skins/scrapbook.d.ts +57 -0
- package/dist/skins/scrapbook.js +57 -0
- package/dist/skins/solar-sail.d.ts +68 -0
- package/dist/skins/solar-sail.js +68 -0
- package/dist/skins/studio-hardware.d.ts +57 -0
- package/dist/skins/studio-hardware.js +57 -0
- package/dist/skins/sumi-ink.d.ts +68 -0
- package/dist/skins/sumi-ink.js +68 -0
- package/dist/skins/swiss-poster.d.ts +68 -0
- package/dist/skins/swiss-poster.js +68 -0
- package/dist/skins/tactile-atelier.d.ts +57 -0
- package/dist/skins/tactile-atelier.js +57 -0
- package/dist/skins/tokyo-transit.d.ts +68 -0
- package/dist/skins/tokyo-transit.js +68 -0
- package/dist/skins/winamp-signal.d.ts +57 -0
- package/dist/skins/winamp-signal.js +57 -0
- package/dist/skins/y2k-titanium.d.ts +57 -0
- package/dist/skins/y2k-titanium.js +57 -0
- package/dist/skins/yule-knit.d.ts +68 -0
- package/dist/skins/yule-knit.js +68 -0
- package/dist/solar-sail.css +901 -0
- package/dist/starters-launch.d.ts +361 -0
- package/dist/starters-launch.js +808 -0
- package/dist/studio-hardware.css +867 -0
- package/dist/sumi/ornament.d.ts +3 -0
- package/dist/sumi/ornament.js +8 -0
- package/dist/sumi-ink.css +733 -0
- package/dist/swiss/ornament.d.ts +3 -0
- package/dist/swiss/ornament.js +8 -0
- package/dist/swiss-poster.css +727 -0
- package/dist/tactile-atelier.css +922 -0
- package/dist/theme-document.schema.json +790 -0
- package/dist/theme-schema.d.ts +453 -0
- package/dist/theme-schema.js +416 -0
- package/dist/titanium/ornament.d.ts +3 -0
- package/dist/titanium/ornament.js +11 -0
- package/dist/tokyo-transit.css +756 -0
- package/dist/transit/ornament.d.ts +3 -0
- package/dist/transit/ornament.js +8 -0
- package/dist/winamp-signal.css +1048 -0
- package/dist/y2k-titanium.css +979 -0
- package/dist/yule/ornament.d.ts +3 -0
- package/dist/yule/ornament.js +8 -0
- package/dist/yule-knit.css +850 -0
- package/package.json +351 -3
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { organicStyles, canopyShapes, stemCharacters, defaultOrganicOrnament, } from "./organic-settings.js";
|
|
2
|
+
import { colorFamilies, neutralColorFamilies, radiusPresets, spacingDensity, } from "@dixy-ui/tokens";
|
|
3
|
+
import { freezeData } from "./freeze.js";
|
|
4
|
+
import { backdropPatterns, controlStyles, defaultCharacter, dividerStyles, focusStyles, headingCases, headingWeights, motionStyles, numeralStyles, scrollbarStyles, selectionStyles, stateStyles, } from "./character.js";
|
|
5
|
+
import { cornerStyles, defaultChrome, edgeStyles, titlebarStyles, wellStyles, } from "./chrome.js";
|
|
6
|
+
import { frameRecipes, materialRecipes } from "./recipes.js";
|
|
7
|
+
const choice = (values) => ({ enum: [...values] });
|
|
8
|
+
const keys = (value) => Object.keys(value);
|
|
9
|
+
const object = (properties) => ({
|
|
10
|
+
type: "object",
|
|
11
|
+
properties,
|
|
12
|
+
required: Object.keys(properties),
|
|
13
|
+
additionalProperties: false,
|
|
14
|
+
});
|
|
15
|
+
const number = (title, minimum, maximum) => ({ type: "number", title, minimum, maximum });
|
|
16
|
+
const fontKeys = [
|
|
17
|
+
"inter",
|
|
18
|
+
"grotesk",
|
|
19
|
+
"system",
|
|
20
|
+
"serif",
|
|
21
|
+
"mono",
|
|
22
|
+
"fraunces",
|
|
23
|
+
"deco",
|
|
24
|
+
"pixel",
|
|
25
|
+
"techno",
|
|
26
|
+
"rounded",
|
|
27
|
+
"lcd",
|
|
28
|
+
"plex",
|
|
29
|
+
"archivo",
|
|
30
|
+
"neon",
|
|
31
|
+
"mincho",
|
|
32
|
+
"gothic",
|
|
33
|
+
"playfair",
|
|
34
|
+
"caveat",
|
|
35
|
+
"bricolage",
|
|
36
|
+
"terminal",
|
|
37
|
+
"jetbrains",
|
|
38
|
+
"manrope",
|
|
39
|
+
"figtree",
|
|
40
|
+
"cormorant",
|
|
41
|
+
];
|
|
42
|
+
/** This closed JSON Schema also supplies TypeScript types, validation and control metadata. */
|
|
43
|
+
export const themeDocumentSchema = freezeData({
|
|
44
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
45
|
+
...object({
|
|
46
|
+
schemaVersion: choice([6]),
|
|
47
|
+
id: {
|
|
48
|
+
type: "string",
|
|
49
|
+
minLength: 3,
|
|
50
|
+
maxLength: 100,
|
|
51
|
+
pattern: "^[a-z][a-z0-9-]*/[a-z][a-z0-9-]*$",
|
|
52
|
+
},
|
|
53
|
+
name: { type: "string", minLength: 1, maxLength: 80, pattern: "\\S" },
|
|
54
|
+
revision: { type: "integer", minimum: 1, maximum: 2147483647 },
|
|
55
|
+
foundations: object({
|
|
56
|
+
accentColor: choice(colorFamilies),
|
|
57
|
+
customAccent: choice([false, true]),
|
|
58
|
+
lightness: number("Accent lightness", 0.48, 0.83),
|
|
59
|
+
chroma: number("Accent chroma", 0, 0.3),
|
|
60
|
+
hue: number("Accent hue", 0, 360),
|
|
61
|
+
neutralColor: choice(neutralColorFamilies),
|
|
62
|
+
radius: choice(keys(radiusPresets)),
|
|
63
|
+
density: choice(keys(spacingDensity)),
|
|
64
|
+
material: choice(["matte", "glossy", "soft", "metal", "paper"]),
|
|
65
|
+
componentFinish: choice([
|
|
66
|
+
"inherit",
|
|
67
|
+
"matte",
|
|
68
|
+
"glossy",
|
|
69
|
+
"soft",
|
|
70
|
+
"metal",
|
|
71
|
+
"paper",
|
|
72
|
+
]),
|
|
73
|
+
shape: choice(["rounded", "pill", "square"]),
|
|
74
|
+
appearance: choice(["light", "dark"]),
|
|
75
|
+
bodyFont: choice(fontKeys),
|
|
76
|
+
headingFont: choice(fontKeys),
|
|
77
|
+
border: choice([1, 2]),
|
|
78
|
+
depth: choice(["none", "raised", "floating"]),
|
|
79
|
+
ornament: choice(["quiet", "balanced", "expressive"]),
|
|
80
|
+
relief: choice([0, 1, 2]),
|
|
81
|
+
effects: choice(["static", "backdrop", "reduced"]),
|
|
82
|
+
}),
|
|
83
|
+
recipes: object({
|
|
84
|
+
material: object({
|
|
85
|
+
id: choice(keys(materialRecipes)),
|
|
86
|
+
version: choice([1]),
|
|
87
|
+
parameters: object({
|
|
88
|
+
surfaceMix: number("Light surface neutral mix", 0, 100),
|
|
89
|
+
darkSurfaceMix: number("Dark surface neutral mix", 0, 100),
|
|
90
|
+
sheen: number("Highlight strength", 0, 1),
|
|
91
|
+
cushion: number("Cushion relief", 0, 1),
|
|
92
|
+
inkTint: number("Text tint strength", 0, 1),
|
|
93
|
+
textureScale: number("Grain scale", 0.5, 3),
|
|
94
|
+
lightAngle: number("Light angle", 0, 360),
|
|
95
|
+
backdropBlur: number("Glass blur (px)", 0, 40),
|
|
96
|
+
backdropSaturate: number("Glass saturation (%)", 100, 200),
|
|
97
|
+
}),
|
|
98
|
+
}),
|
|
99
|
+
frame: object({
|
|
100
|
+
id: choice(keys(frameRecipes)),
|
|
101
|
+
version: choice([1]),
|
|
102
|
+
ornament: object({
|
|
103
|
+
style: choice(organicStyles),
|
|
104
|
+
canopy: choice(canopyShapes),
|
|
105
|
+
stem: choice(stemCharacters),
|
|
106
|
+
density: number("Detail density", 0, 100),
|
|
107
|
+
cornerDensity: number("Corner clusters", 0, 100),
|
|
108
|
+
canopyDepth: number("Canopy depth / relief", 0, 100),
|
|
109
|
+
variation: number("Organic variation", 0, 100),
|
|
110
|
+
scale: number("Detail size", 10, 32),
|
|
111
|
+
stemWeight: number("Stem / rim weight", 0, 100),
|
|
112
|
+
motion: number("Motion strength", 0, 100),
|
|
113
|
+
seed: {
|
|
114
|
+
type: "integer",
|
|
115
|
+
title: "Variation seed",
|
|
116
|
+
minimum: 1,
|
|
117
|
+
maximum: 9999,
|
|
118
|
+
},
|
|
119
|
+
}),
|
|
120
|
+
parameters: object({
|
|
121
|
+
width: number("Decorative frame width", 0, 12),
|
|
122
|
+
paddingTop: number("Panel top inset", 16, 64),
|
|
123
|
+
paddingInline: number("Panel inline inset", 16, 64),
|
|
124
|
+
paddingBottom: number("Panel bottom inset", 16, 64),
|
|
125
|
+
inset: number("Scene frame inset", 0, 24),
|
|
126
|
+
cornerVariation: number("Organic corner variation", 0, 0.5),
|
|
127
|
+
cornerScale: number("Corner fullness", 0.5, 3),
|
|
128
|
+
}),
|
|
129
|
+
}),
|
|
130
|
+
}),
|
|
131
|
+
character: object({
|
|
132
|
+
controls: choice(controlStyles),
|
|
133
|
+
motion: object({
|
|
134
|
+
style: choice(motionStyles),
|
|
135
|
+
press: number("Press depth", 0, 4),
|
|
136
|
+
tempo: number("Tempo", 0.5, 2),
|
|
137
|
+
}),
|
|
138
|
+
type: object({
|
|
139
|
+
headingCase: choice(headingCases),
|
|
140
|
+
headingTracking: number("Heading tracking", -0.05, 0.2),
|
|
141
|
+
headingWeight: choice(headingWeights),
|
|
142
|
+
numerals: choice(numeralStyles),
|
|
143
|
+
}),
|
|
144
|
+
states: choice(stateStyles),
|
|
145
|
+
details: object({
|
|
146
|
+
focus: choice(focusStyles),
|
|
147
|
+
divider: choice(dividerStyles),
|
|
148
|
+
selection: choice(selectionStyles),
|
|
149
|
+
scrollbar: choice(scrollbarStyles),
|
|
150
|
+
iconStroke: number("Icon stroke", 1, 2.75),
|
|
151
|
+
}),
|
|
152
|
+
backdrop: object({
|
|
153
|
+
pattern: choice(backdropPatterns),
|
|
154
|
+
intensity: number("Backdrop intensity", 0, 1),
|
|
155
|
+
scale: number("Backdrop scale", 0.5, 3),
|
|
156
|
+
}),
|
|
157
|
+
chrome: object({
|
|
158
|
+
titlebar: choice(titlebarStyles),
|
|
159
|
+
edge: choice(edgeStyles),
|
|
160
|
+
well: choice(wellStyles),
|
|
161
|
+
corners: choice(cornerStyles),
|
|
162
|
+
}),
|
|
163
|
+
}),
|
|
164
|
+
}),
|
|
165
|
+
});
|
|
166
|
+
export class ThemeDocumentError extends Error {
|
|
167
|
+
issues;
|
|
168
|
+
constructor(issues) {
|
|
169
|
+
super(issues.map((issue) => `${issue.path}: ${issue.message}`).join("\n"));
|
|
170
|
+
this.name = "ThemeDocumentError";
|
|
171
|
+
this.issues = issues;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/** Deliberately validates only our engine-owned schema vocabulary; not a general JSON Schema evaluator. */
|
|
175
|
+
function validate(schema, value, path, issues) {
|
|
176
|
+
const fail = (message) => {
|
|
177
|
+
issues.push({ path, message });
|
|
178
|
+
return undefined;
|
|
179
|
+
};
|
|
180
|
+
if (schema.enum)
|
|
181
|
+
return schema.enum.includes(value)
|
|
182
|
+
? value
|
|
183
|
+
: fail("Unsupported value.");
|
|
184
|
+
if (schema.type === "object") {
|
|
185
|
+
if (!value ||
|
|
186
|
+
typeof value !== "object" ||
|
|
187
|
+
Array.isArray(value) ||
|
|
188
|
+
![Object.prototype, null].includes(Object.getPrototypeOf(value)))
|
|
189
|
+
return fail("Expected a plain data object.");
|
|
190
|
+
const source = value;
|
|
191
|
+
const properties = schema.properties;
|
|
192
|
+
const output = {};
|
|
193
|
+
for (const key of Reflect.ownKeys(source)) {
|
|
194
|
+
if (typeof key !== "string" || !Object.hasOwn(properties, key)) {
|
|
195
|
+
issues.push({
|
|
196
|
+
path: `${path}.${String(key)}`,
|
|
197
|
+
message: "Unknown field.",
|
|
198
|
+
});
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const descriptor = Object.getOwnPropertyDescriptor(source, key);
|
|
202
|
+
if (!Object.hasOwn(descriptor, "value")) {
|
|
203
|
+
issues.push({
|
|
204
|
+
path: `${path}.${key}`,
|
|
205
|
+
message: "Accessors are not theme data.",
|
|
206
|
+
});
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
output[key] = validate(properties[key], descriptor.value, `${path}.${key}`, issues);
|
|
210
|
+
}
|
|
211
|
+
for (const key of schema.required)
|
|
212
|
+
if (!Object.hasOwn(source, key))
|
|
213
|
+
issues.push({ path: `${path}.${key}`, message: "Required field." });
|
|
214
|
+
return Object.freeze(output);
|
|
215
|
+
}
|
|
216
|
+
if (schema.type === "string") {
|
|
217
|
+
if (typeof value !== "string" ||
|
|
218
|
+
value.length < (schema.minLength ?? 0) ||
|
|
219
|
+
value.length > (schema.maxLength ?? Infinity) ||
|
|
220
|
+
(schema.pattern && !new RegExp(schema.pattern).test(value)))
|
|
221
|
+
return fail("Invalid text.");
|
|
222
|
+
}
|
|
223
|
+
else if (typeof value !== "number" ||
|
|
224
|
+
!Number.isFinite(value) ||
|
|
225
|
+
value < schema.minimum ||
|
|
226
|
+
value > schema.maximum ||
|
|
227
|
+
(schema.type === "integer" && !Number.isInteger(value)))
|
|
228
|
+
return fail("Number is outside the supported range.");
|
|
229
|
+
return value;
|
|
230
|
+
}
|
|
231
|
+
export function parseThemeDocument(input) {
|
|
232
|
+
let value = input;
|
|
233
|
+
if (typeof input === "string") {
|
|
234
|
+
if (input.length > 32768 || new TextEncoder().encode(input).length > 32768)
|
|
235
|
+
throw new ThemeDocumentError([
|
|
236
|
+
{ path: "$", message: "Theme document exceeds 32 KiB." },
|
|
237
|
+
]);
|
|
238
|
+
try {
|
|
239
|
+
value = JSON.parse(input);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
throw new ThemeDocumentError([{ path: "$", message: "Invalid JSON." }]);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// Version 1 had fixed material/frame pairs and fewer paint parameters.
|
|
246
|
+
if (value &&
|
|
247
|
+
typeof value === "object" &&
|
|
248
|
+
Object.getOwnPropertyDescriptor(value, "schemaVersion")?.value === 1) {
|
|
249
|
+
const legacy = value;
|
|
250
|
+
// Copy through JSON only after checking descriptors; no accessors/functions are executed.
|
|
251
|
+
assertData(legacy);
|
|
252
|
+
const migrated = JSON.parse(JSON.stringify(legacy));
|
|
253
|
+
const pairs = {
|
|
254
|
+
forest: "botanical-paper",
|
|
255
|
+
pixel: "pixel-enamel",
|
|
256
|
+
rails: "gunmetal",
|
|
257
|
+
ceramic: "ceramic",
|
|
258
|
+
glass: "prismatic-glass",
|
|
259
|
+
stitch: "atelier-paper",
|
|
260
|
+
};
|
|
261
|
+
if (!pairs[migrated.recipes?.frame?.id] ||
|
|
262
|
+
pairs[migrated.recipes.frame.id] !== migrated.recipes?.material?.id ||
|
|
263
|
+
(migrated.recipes.frame.id !== "forest" &&
|
|
264
|
+
migrated.recipes.frame.parameters?.cornerVariation !== 0))
|
|
265
|
+
throw new ThemeDocumentError([
|
|
266
|
+
{ path: "$.recipes", message: "Invalid version-1 recipe pair." },
|
|
267
|
+
]);
|
|
268
|
+
for (const key of [
|
|
269
|
+
"customAccent",
|
|
270
|
+
"lightness",
|
|
271
|
+
"chroma",
|
|
272
|
+
"hue",
|
|
273
|
+
"componentFinish",
|
|
274
|
+
])
|
|
275
|
+
if (Object.hasOwn(migrated.foundations ?? {}, key))
|
|
276
|
+
throw new ThemeDocumentError([
|
|
277
|
+
{ path: "$.foundations." + key, message: "Not a version-1 field." },
|
|
278
|
+
]);
|
|
279
|
+
for (const key of [
|
|
280
|
+
"sheen",
|
|
281
|
+
"cushion",
|
|
282
|
+
"inkTint",
|
|
283
|
+
"textureScale",
|
|
284
|
+
"lightAngle",
|
|
285
|
+
])
|
|
286
|
+
if (Object.hasOwn(migrated.recipes?.material?.parameters ?? {}, key))
|
|
287
|
+
throw new ThemeDocumentError([
|
|
288
|
+
{
|
|
289
|
+
path: "$.recipes.material.parameters." + key,
|
|
290
|
+
message: "Not a version-1 field.",
|
|
291
|
+
},
|
|
292
|
+
]);
|
|
293
|
+
if (Object.hasOwn(migrated.recipes?.frame?.parameters ?? {}, "cornerScale"))
|
|
294
|
+
throw new ThemeDocumentError([
|
|
295
|
+
{
|
|
296
|
+
path: "$.recipes.frame.parameters.cornerScale",
|
|
297
|
+
message: "Not a version-1 field.",
|
|
298
|
+
},
|
|
299
|
+
]);
|
|
300
|
+
migrated.schemaVersion = 2;
|
|
301
|
+
migrated.foundations = {
|
|
302
|
+
...migrated.foundations,
|
|
303
|
+
componentFinish: "inherit",
|
|
304
|
+
customAccent: false,
|
|
305
|
+
lightness: 0.54,
|
|
306
|
+
chroma: 0.2,
|
|
307
|
+
hue: 305,
|
|
308
|
+
};
|
|
309
|
+
migrated.recipes.material.parameters = {
|
|
310
|
+
...migrated.recipes.material.parameters,
|
|
311
|
+
sheen: 0.35,
|
|
312
|
+
cushion: 0,
|
|
313
|
+
inkTint: 0,
|
|
314
|
+
textureScale: 1,
|
|
315
|
+
lightAngle: 145,
|
|
316
|
+
};
|
|
317
|
+
migrated.recipes.frame.parameters.cornerScale = 1;
|
|
318
|
+
value = migrated;
|
|
319
|
+
}
|
|
320
|
+
// V2 had geometry and paint, but no procedural ornament controls.
|
|
321
|
+
if (value &&
|
|
322
|
+
typeof value === "object" &&
|
|
323
|
+
Object.getOwnPropertyDescriptor(value, "schemaVersion")?.value === 2) {
|
|
324
|
+
assertData(value);
|
|
325
|
+
const migrated = JSON.parse(JSON.stringify(value));
|
|
326
|
+
if (Object.hasOwn(migrated.recipes?.frame ?? {}, "ornament"))
|
|
327
|
+
throw new ThemeDocumentError([
|
|
328
|
+
{ path: "$.recipes.frame.ornament", message: "Not a version-2 field." },
|
|
329
|
+
]);
|
|
330
|
+
if (migrated.recipes?.frame)
|
|
331
|
+
migrated.recipes.frame.ornament = defaultOrganicOrnament(migrated.recipes.frame.id);
|
|
332
|
+
migrated.schemaVersion = 3;
|
|
333
|
+
value = migrated;
|
|
334
|
+
}
|
|
335
|
+
// V3 had paint and frames but no control/motion/type character; neutral character keeps its look.
|
|
336
|
+
if (value &&
|
|
337
|
+
typeof value === "object" &&
|
|
338
|
+
Object.getOwnPropertyDescriptor(value, "schemaVersion")?.value === 3) {
|
|
339
|
+
assertData(value);
|
|
340
|
+
const migrated = JSON.parse(JSON.stringify(value));
|
|
341
|
+
if (Object.hasOwn(migrated, "character"))
|
|
342
|
+
throw new ThemeDocumentError([
|
|
343
|
+
{ path: "$.character", message: "Not a version-3 field." },
|
|
344
|
+
]);
|
|
345
|
+
// Version-4 character, without the version-5 chrome block.
|
|
346
|
+
const { chrome: _chrome, ...v4Character } = defaultCharacter;
|
|
347
|
+
migrated.character = JSON.parse(JSON.stringify(v4Character));
|
|
348
|
+
migrated.schemaVersion = 4;
|
|
349
|
+
value = migrated;
|
|
350
|
+
}
|
|
351
|
+
// V4 had character but no window chrome; no chrome compiles to no rules.
|
|
352
|
+
if (value &&
|
|
353
|
+
typeof value === "object" &&
|
|
354
|
+
Object.getOwnPropertyDescriptor(value, "schemaVersion")?.value === 4) {
|
|
355
|
+
assertData(value);
|
|
356
|
+
const migrated = JSON.parse(JSON.stringify(value));
|
|
357
|
+
if (migrated.character && typeof migrated.character === "object") {
|
|
358
|
+
if (Object.hasOwn(migrated.character, "chrome"))
|
|
359
|
+
throw new ThemeDocumentError([
|
|
360
|
+
{ path: "$.character.chrome", message: "Not a version-4 field." },
|
|
361
|
+
]);
|
|
362
|
+
migrated.character.chrome = { ...defaultChrome };
|
|
363
|
+
}
|
|
364
|
+
migrated.schemaVersion = 5;
|
|
365
|
+
value = migrated;
|
|
366
|
+
}
|
|
367
|
+
// V5 materials had no backdrop glass; 0px blur and 100% saturation compile to no filter.
|
|
368
|
+
if (value &&
|
|
369
|
+
typeof value === "object" &&
|
|
370
|
+
Object.getOwnPropertyDescriptor(value, "schemaVersion")?.value === 5) {
|
|
371
|
+
assertData(value);
|
|
372
|
+
const migrated = JSON.parse(JSON.stringify(value));
|
|
373
|
+
const parameters = migrated.recipes?.material?.parameters;
|
|
374
|
+
if (parameters && typeof parameters === "object") {
|
|
375
|
+
for (const key of ["backdropBlur", "backdropSaturate"])
|
|
376
|
+
if (Object.hasOwn(parameters, key))
|
|
377
|
+
throw new ThemeDocumentError([
|
|
378
|
+
{
|
|
379
|
+
path: "$.recipes.material.parameters." + key,
|
|
380
|
+
message: "Not a version-5 field.",
|
|
381
|
+
},
|
|
382
|
+
]);
|
|
383
|
+
parameters.backdropBlur = 0;
|
|
384
|
+
parameters.backdropSaturate = 100;
|
|
385
|
+
}
|
|
386
|
+
migrated.schemaVersion = 6;
|
|
387
|
+
value = migrated;
|
|
388
|
+
}
|
|
389
|
+
const issues = [];
|
|
390
|
+
const document = validate(themeDocumentSchema, value, "$", issues);
|
|
391
|
+
if (issues.length)
|
|
392
|
+
throw new ThemeDocumentError(issues);
|
|
393
|
+
return document;
|
|
394
|
+
}
|
|
395
|
+
function assertData(value, depth = 0) {
|
|
396
|
+
if (depth > 10)
|
|
397
|
+
throw new ThemeDocumentError([
|
|
398
|
+
{ path: "$", message: "Theme data is too deeply nested." },
|
|
399
|
+
]);
|
|
400
|
+
if (value && typeof value === "object") {
|
|
401
|
+
if (![Object.prototype, Array.prototype, null].includes(Object.getPrototypeOf(value)))
|
|
402
|
+
throw new ThemeDocumentError([
|
|
403
|
+
{ path: "$", message: "Expected plain data." },
|
|
404
|
+
]);
|
|
405
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
406
|
+
const d = Object.getOwnPropertyDescriptor(value, key);
|
|
407
|
+
if (!Object.hasOwn(d, "value") ||
|
|
408
|
+
typeof d.value === "function" ||
|
|
409
|
+
typeof key === "symbol")
|
|
410
|
+
throw new ThemeDocumentError([
|
|
411
|
+
{ path: "$", message: "Only plain data is supported." },
|
|
412
|
+
]);
|
|
413
|
+
assertData(d.value, depth + 1);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { FrameCorners } from "../frame-corners.js";
|
|
4
|
+
import { OrnamentLayer, Fasteners, useOrnamentId, } from "../ornament-parts.js";
|
|
5
|
+
export function TitaniumOrnament(props) {
|
|
6
|
+
const id = useOrnamentId("titanium");
|
|
7
|
+
if (props.part === "theme" || props.part === "switch-thumb")
|
|
8
|
+
return null;
|
|
9
|
+
return (_jsxs(OrnamentLayer, { ...props, skin: "titanium", children: [_jsx(Fasteners, { id: id }), _jsxs(FrameCorners, { children: [_jsx("path", { className: "dixy-skin-motion", d: "M32 5H58L65 12H76", stroke: "var(--dixy-color-background-action-primary)", strokeWidth: "2" }), Array.from({ length: 15 }, (_, i) => (_jsxs("g", { children: [_jsx("circle", { cx: 8 + (i % 5) * 3, cy: 51 + Math.floor(i / 5) * 5, r: "1.2", fill: "#000", opacity: ".25" }), _jsx("circle", { cx: 8 + (i % 5) * 3, cy: 51.7 + Math.floor(i / 5) * 5, r: "1.2", stroke: "#fff", strokeOpacity: ".3", strokeWidth: ".4" })] }, i)))] })] }));
|
|
10
|
+
}
|
|
11
|
+
export default TitaniumOrnament;
|