@5even7/dlc-ui 0.2.18 → 0.2.20
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/CHANGELOG.md +158 -144
- package/EXTENDING.md +114 -0
- package/LICENSE +21 -21
- package/README.md +389 -338
- package/THIRD_PARTY_NOTICES.md +221 -0
- package/dist/avatar-creator/styles/humation.mjs +26 -0
- package/dist/avatar-creator/styles.mjs +76 -0
- package/dist/avatar-creator.cjs +7831 -0
- package/dist/avatar-creator.mjs +74 -0
- package/dist/capsule.cjs +185 -66
- package/dist/capsule.mjs +364 -1340
- package/dist/chunks/avatar-creator-DiAmhpRa.mjs +687 -0
- package/dist/chunks/color-DnmNceaf.mjs +350 -0
- package/dist/chunks/controls-B5_ctvvA.mjs +160 -0
- package/dist/chunks/copy-7J0XIj_0.mjs +13 -0
- package/dist/chunks/cosmic-fallback-v8G5_ibh.mjs +408 -0
- package/dist/chunks/emitter-CxLYMSbw.mjs +38 -0
- package/dist/chunks/emo-Dvahy5Qr.mjs +1140 -0
- package/dist/chunks/hash-BiO02UNb.mjs +38 -0
- package/dist/chunks/humation-B9OPUGL7.mjs +2987 -0
- package/dist/chunks/identicon-BUdMd-yf.mjs +3063 -0
- package/dist/chunks/index-BchW1tWE.mjs +478 -0
- package/dist/chunks/index-BtH8Lbra.mjs +296 -0
- package/dist/chunks/index-C02dvGLD.mjs +263 -0
- package/dist/chunks/index-CFybYBA1.mjs +278 -0
- package/dist/chunks/index-DgI872-7.mjs +241 -0
- package/dist/chunks/index-_sf_14Zu.mjs +455 -0
- package/dist/chunks/index-w5Pzdwhv.mjs +305 -0
- package/dist/chunks/motion-a8_f8_Ui.mjs +177 -0
- package/dist/chunks/number-BqWLX0iQ.mjs +231 -0
- package/dist/chunks/progress-KweS4Rx0.mjs +1836 -0
- package/dist/chunks/size-Bztk2ryJ.mjs +22 -0
- package/dist/chunks/wrapper-DXhy1E7Z.mjs +514 -0
- package/dist/color.cjs +186 -67
- package/dist/color.mjs +341 -1280
- package/dist/emo/miao/angry.mjs +4 -0
- package/dist/emo/miao/asleep.mjs +4 -0
- package/dist/emo/miao/badminton.mjs +4 -0
- package/dist/emo/miao/confident.mjs +4 -0
- package/dist/emo/miao/cry.mjs +4 -0
- package/dist/emo/miao/investigate.mjs +4 -0
- package/dist/emo/miao/laugh.mjs +4 -0
- package/dist/emo/miao/leisure.mjs +4 -0
- package/dist/emo/miao/mock.mjs +4 -0
- package/dist/emo/miao/music.mjs +4 -0
- package/dist/emo/miao/mute.mjs +4 -0
- package/dist/emo/miao/panic.mjs +4 -0
- package/dist/emo/miao/ponder.mjs +4 -0
- package/dist/emo/miao/question.mjs +4 -0
- package/dist/emo/miao/sad.mjs +4 -0
- package/dist/emo/miao/shocked.mjs +4 -0
- package/dist/emo/miao/shy.mjs +4 -0
- package/dist/emo/miao/sigh.mjs +4 -0
- package/dist/emo/miao/smile.mjs +4 -0
- package/dist/emo/miao/snigger.mjs +4 -0
- package/dist/emo/miao/static.mjs +4 -0
- package/dist/emo/miao/yawn.mjs +4 -0
- package/dist/emo/miao/yummy.mjs +4 -0
- package/dist/emo.cjs +27161 -25964
- package/dist/emo.mjs +8 -1421
- package/dist/index.cjs +32729 -24466
- package/dist/index.mjs +16 -4757
- package/dist/index.umd.js +33115 -24852
- package/dist/index.umd.min.js +3 -1
- package/dist/progress.cjs +195 -76
- package/dist/progress.mjs +7 -2448
- package/dist/vue2.cjs +33282 -25002
- package/dist/vue2.mjs +63 -5131
- package/dist/vue3.cjs +33282 -25002
- package/dist/vue3.mjs +53 -5122
- package/package.json +161 -136
- package/styles/avatar-creator.css +38 -0
- package/styles/base.css +32 -32
- package/styles/color.css +18 -18
- package/styles/emo.css +110 -89
- package/styles/progress.css +96 -96
- package/styles/theme.css +1 -0
- package/types/avatar-creator-styles.d.ts +10 -0
- package/types/avatar-creator.d.ts +143 -0
- package/types/capsule.d.ts +15 -15
- package/types/color.d.ts +15 -15
- package/types/emo-miao.d.ts +9 -0
- package/types/emo.d.ts +38 -37
- package/types/index.d.ts +593 -554
- package/types/progress.d.ts +16 -16
- package/types/vue2.d.ts +11 -10
- package/types/vue3.d.ts +169 -146
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color helpers. `normalizeColor` converts any supported CSS color
|
|
3
|
+
* (hex / named / rgb() / rgba() / hsl() / hsla()) into a 6-digit hex string,
|
|
4
|
+
* so renderers can keep working with #rrggbb only. rgba()/hsla() alpha is
|
|
5
|
+
* intentionally dropped: the WebGL shader has no per-color alpha channel, and
|
|
6
|
+
* the component is opaque by design — use component-level `opacity` for
|
|
7
|
+
* transparency.
|
|
8
|
+
*
|
|
9
|
+
* `normalizeColorWithAlpha` keeps the alpha channel: it returns a 6-digit hex
|
|
10
|
+
* when alpha is 1, an 8-digit #rrggbbaa hex when alpha < 1, and the literal
|
|
11
|
+
* string 'transparent' for `transparent`. Use it for renderers (e.g. SVG)
|
|
12
|
+
* that can represent per-color transparency.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const NAMED_COLORS = {
|
|
16
|
+
aliceblue: '#f0f8ff',
|
|
17
|
+
antiquewhite: '#faebd7',
|
|
18
|
+
aqua: '#00ffff',
|
|
19
|
+
aquamarine: '#7fffd4',
|
|
20
|
+
azure: '#f0ffff',
|
|
21
|
+
beige: '#f5f5dc',
|
|
22
|
+
bisque: '#ffe4c4',
|
|
23
|
+
black: '#000000',
|
|
24
|
+
blanchedalmond: '#ffebcd',
|
|
25
|
+
blue: '#0000ff',
|
|
26
|
+
blueviolet: '#8a2be2',
|
|
27
|
+
brown: '#a52a2a',
|
|
28
|
+
burlywood: '#deb887',
|
|
29
|
+
cadetblue: '#5f9ea0',
|
|
30
|
+
chartreuse: '#7fff00',
|
|
31
|
+
chocolate: '#d2691e',
|
|
32
|
+
coral: '#ff7f50',
|
|
33
|
+
cornflowerblue: '#6495ed',
|
|
34
|
+
cornsilk: '#fff8dc',
|
|
35
|
+
crimson: '#dc143c',
|
|
36
|
+
cyan: '#00ffff',
|
|
37
|
+
darkblue: '#00008b',
|
|
38
|
+
darkcyan: '#008b8b',
|
|
39
|
+
darkgoldenrod: '#b8860b',
|
|
40
|
+
darkgray: '#a9a9a9',
|
|
41
|
+
darkgreen: '#006400',
|
|
42
|
+
darkgrey: '#a9a9a9',
|
|
43
|
+
darkkhaki: '#bdb76b',
|
|
44
|
+
darkmagenta: '#8b008b',
|
|
45
|
+
darkolivegreen: '#556b2f',
|
|
46
|
+
darkorange: '#ff8c00',
|
|
47
|
+
darkorchid: '#9932cc',
|
|
48
|
+
darkred: '#8b0000',
|
|
49
|
+
darksalmon: '#e9967a',
|
|
50
|
+
darkseagreen: '#8fbc8f',
|
|
51
|
+
darkslateblue: '#483d8b',
|
|
52
|
+
darkslategray: '#2f4f4f',
|
|
53
|
+
darkslategrey: '#2f4f4f',
|
|
54
|
+
darkturquoise: '#00ced1',
|
|
55
|
+
darkviolet: '#9400d3',
|
|
56
|
+
deeppink: '#ff1493',
|
|
57
|
+
deepskyblue: '#00bfff',
|
|
58
|
+
dimgray: '#696969',
|
|
59
|
+
dimgrey: '#696969',
|
|
60
|
+
dodgerblue: '#1e90ff',
|
|
61
|
+
firebrick: '#b22222',
|
|
62
|
+
floralwhite: '#fffaf0',
|
|
63
|
+
forestgreen: '#228b22',
|
|
64
|
+
fuchsia: '#ff00ff',
|
|
65
|
+
gainsboro: '#dcdcdc',
|
|
66
|
+
ghostwhite: '#f8f8ff',
|
|
67
|
+
gold: '#ffd700',
|
|
68
|
+
goldenrod: '#daa520',
|
|
69
|
+
gray: '#808080',
|
|
70
|
+
green: '#008000',
|
|
71
|
+
greenyellow: '#adff2f',
|
|
72
|
+
grey: '#808080',
|
|
73
|
+
honeydew: '#f0fff0',
|
|
74
|
+
hotpink: '#ff69b4',
|
|
75
|
+
indianred: '#cd5c5c',
|
|
76
|
+
indigo: '#4b0082',
|
|
77
|
+
ivory: '#fffff0',
|
|
78
|
+
khaki: '#f0e68c',
|
|
79
|
+
lavender: '#e6e6fa',
|
|
80
|
+
lavenderblush: '#fff0f5',
|
|
81
|
+
lawngreen: '#7cfc00',
|
|
82
|
+
lemonchiffon: '#fffacd',
|
|
83
|
+
lightblue: '#add8e6',
|
|
84
|
+
lightcoral: '#f08080',
|
|
85
|
+
lightcyan: '#e0ffff',
|
|
86
|
+
lightgoldenrodyellow: '#fafad2',
|
|
87
|
+
lightgray: '#d3d3d3',
|
|
88
|
+
lightgreen: '#90ee90',
|
|
89
|
+
lightgrey: '#d3d3d3',
|
|
90
|
+
lightpink: '#ffb6c1',
|
|
91
|
+
lightsalmon: '#ffa07a',
|
|
92
|
+
lightseagreen: '#20b2aa',
|
|
93
|
+
lightskyblue: '#87cefa',
|
|
94
|
+
lightslategray: '#778899',
|
|
95
|
+
lightslategrey: '#778899',
|
|
96
|
+
lightsteelblue: '#b0c4de',
|
|
97
|
+
lightyellow: '#ffffe0',
|
|
98
|
+
lime: '#00ff00',
|
|
99
|
+
limegreen: '#32cd32',
|
|
100
|
+
linen: '#faf0e6',
|
|
101
|
+
magenta: '#ff00ff',
|
|
102
|
+
maroon: '#800000',
|
|
103
|
+
mediumaquamarine: '#66cdaa',
|
|
104
|
+
mediumblue: '#0000cd',
|
|
105
|
+
mediumorchid: '#ba55d3',
|
|
106
|
+
mediumpurple: '#9370db',
|
|
107
|
+
mediumseagreen: '#3cb371',
|
|
108
|
+
mediumslateblue: '#7b68ee',
|
|
109
|
+
mediumspringgreen: '#00fa9a',
|
|
110
|
+
mediumturquoise: '#48d1cc',
|
|
111
|
+
mediumvioletred: '#c71585',
|
|
112
|
+
midnightblue: '#191970',
|
|
113
|
+
mintcream: '#f5fffa',
|
|
114
|
+
mistyrose: '#ffe4e1',
|
|
115
|
+
moccasin: '#ffe4b5',
|
|
116
|
+
navajowhite: '#ffdead',
|
|
117
|
+
navy: '#000080',
|
|
118
|
+
oldlace: '#fdf5e6',
|
|
119
|
+
olive: '#808000',
|
|
120
|
+
olivedrab: '#6b8e23',
|
|
121
|
+
orange: '#ffa500',
|
|
122
|
+
orangered: '#ff4500',
|
|
123
|
+
orchid: '#da70d6',
|
|
124
|
+
palegoldenrod: '#eee8aa',
|
|
125
|
+
palegreen: '#98fb98',
|
|
126
|
+
paleturquoise: '#afeeee',
|
|
127
|
+
palevioletred: '#db7093',
|
|
128
|
+
papayawhip: '#ffefd5',
|
|
129
|
+
peachpuff: '#ffdab9',
|
|
130
|
+
peru: '#cd853f',
|
|
131
|
+
pink: '#ffc0cb',
|
|
132
|
+
plum: '#dda0dd',
|
|
133
|
+
powderblue: '#b0e0e6',
|
|
134
|
+
purple: '#800080',
|
|
135
|
+
rebeccapurple: '#663399',
|
|
136
|
+
red: '#ff0000',
|
|
137
|
+
rosybrown: '#bc8f8f',
|
|
138
|
+
royalblue: '#4169e1',
|
|
139
|
+
saddlebrown: '#8b4513',
|
|
140
|
+
salmon: '#fa8072',
|
|
141
|
+
sandybrown: '#f4a460',
|
|
142
|
+
seagreen: '#2e8b57',
|
|
143
|
+
seashell: '#fff5ee',
|
|
144
|
+
sienna: '#a0522d',
|
|
145
|
+
silver: '#c0c0c0',
|
|
146
|
+
skyblue: '#87ceeb',
|
|
147
|
+
slateblue: '#6a5acd',
|
|
148
|
+
slategray: '#708090',
|
|
149
|
+
slategrey: '#708090',
|
|
150
|
+
snow: '#fffafa',
|
|
151
|
+
springgreen: '#00ff7f',
|
|
152
|
+
steelblue: '#4682b4',
|
|
153
|
+
tan: '#d2b48c',
|
|
154
|
+
teal: '#008080',
|
|
155
|
+
thistle: '#d8bfd8',
|
|
156
|
+
tomato: '#ff6347',
|
|
157
|
+
turquoise: '#40e0d0',
|
|
158
|
+
violet: '#ee82ee',
|
|
159
|
+
wheat: '#f5deb3',
|
|
160
|
+
white: '#ffffff',
|
|
161
|
+
whitesmoke: '#f5f5f5',
|
|
162
|
+
yellow: '#ffff00',
|
|
163
|
+
yellowgreen: '#9acd32'
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
function clampChannel(value) {
|
|
167
|
+
return Math.min(255, Math.max(0, Math.round(value)));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function clampUnit(value) {
|
|
171
|
+
return Math.min(1, Math.max(0, value));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function hexPair(value) {
|
|
175
|
+
const text = clampChannel(value).toString(16);
|
|
176
|
+
return text.length === 1 ? `0${text}` : text;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Parse the argument list of rgb()/rgba()/hsl()/hsla() (comma syntax or
|
|
181
|
+
* modern space + slash syntax). Returns { parts, alpha } where alpha is 1 when
|
|
182
|
+
* omitted. Returns null when the argument list cannot be parsed.
|
|
183
|
+
*/
|
|
184
|
+
function parseFunctionArgs(args) {
|
|
185
|
+
const parts = args
|
|
186
|
+
.split(/[,\s]+/)
|
|
187
|
+
.map((part) => part.trim())
|
|
188
|
+
.filter((part) => part !== '' && part !== '/');
|
|
189
|
+
if (parts.length < 3) return null;
|
|
190
|
+
let alpha = 1;
|
|
191
|
+
if (parts.length >= 4) {
|
|
192
|
+
const alphaText = parts.pop().trim();
|
|
193
|
+
if (alphaText.endsWith('%')) {
|
|
194
|
+
const percent = parseFloat(alphaText);
|
|
195
|
+
if (!Number.isFinite(percent)) return null;
|
|
196
|
+
alpha = clampUnit(percent / 100);
|
|
197
|
+
} else {
|
|
198
|
+
const number = Number(alphaText);
|
|
199
|
+
if (!Number.isFinite(number)) return null;
|
|
200
|
+
alpha = clampUnit(number);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return { parts, alpha };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function parseChannel(value) {
|
|
207
|
+
if (typeof value !== 'string' || value === '') return null;
|
|
208
|
+
const text = value.trim();
|
|
209
|
+
if (text.endsWith('%')) return clampChannel((parseFloat(text) / 100) * 255);
|
|
210
|
+
const number = Number(text);
|
|
211
|
+
return Number.isFinite(number) ? clampChannel(number) : null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function parsePercent(value) {
|
|
215
|
+
if (typeof value !== 'string' || value === '') return null;
|
|
216
|
+
const text = value.trim();
|
|
217
|
+
if (!text.endsWith('%')) return null;
|
|
218
|
+
const number = parseFloat(text);
|
|
219
|
+
return Number.isFinite(number) ? Math.min(100, Math.max(0, number)) : null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function parseHue(value) {
|
|
223
|
+
if (typeof value !== 'string' || value === '') return null;
|
|
224
|
+
const number = parseFloat(value);
|
|
225
|
+
return Number.isFinite(number) ? number : null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Standard HSL -> RGB. h in degrees, s/l in 0..100, returns r/g/b in 0..255. */
|
|
229
|
+
function hslToRgb(h, s, l) {
|
|
230
|
+
const hue = ((h % 360) + 360) % 360 / 360;
|
|
231
|
+
const saturation = s / 100;
|
|
232
|
+
const lightness = l / 100;
|
|
233
|
+
const chroma = (1 - Math.abs(2 * lightness - 1)) * saturation;
|
|
234
|
+
const section = hue * 6;
|
|
235
|
+
const x = chroma * (1 - Math.abs((section % 2) - 1));
|
|
236
|
+
let red = 0;
|
|
237
|
+
let green = 0;
|
|
238
|
+
let blue = 0;
|
|
239
|
+
if (section < 1) { red = chroma; green = x; }
|
|
240
|
+
else if (section < 2) { red = x; green = chroma; }
|
|
241
|
+
else if (section < 3) { green = chroma; blue = x; }
|
|
242
|
+
else if (section < 4) { green = x; blue = chroma; }
|
|
243
|
+
else if (section < 5) { red = x; blue = chroma; }
|
|
244
|
+
else { red = chroma; blue = x; }
|
|
245
|
+
const match = lightness - chroma / 2;
|
|
246
|
+
return [
|
|
247
|
+
Math.round((red + match) * 255),
|
|
248
|
+
Math.round((green + match) * 255),
|
|
249
|
+
Math.round((blue + match) * 255)
|
|
250
|
+
];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Parse any supported CSS color. When keepAlpha is true the alpha channel is
|
|
255
|
+
* preserved (8-digit hex for alpha < 1); when false it is dropped. 'transparent'
|
|
256
|
+
* is only preserved when keepAlpha is true.
|
|
257
|
+
*/
|
|
258
|
+
function parseColor(value, keepAlpha) {
|
|
259
|
+
if (typeof value !== 'string') return null;
|
|
260
|
+
const input = value.trim();
|
|
261
|
+
if (!input) return null;
|
|
262
|
+
const lower = input.toLowerCase();
|
|
263
|
+
if (keepAlpha && lower === 'transparent') return 'transparent';
|
|
264
|
+
if (NAMED_COLORS[lower]) return NAMED_COLORS[lower];
|
|
265
|
+
// #rgb / #rgba / #rrggbb / #rrggbbaa,以及无 # 前缀的 6/8 位 hex(dicebear 习惯)
|
|
266
|
+
const hexMatch = input.match(/^#?([0-9a-f]{3,8})$/i);
|
|
267
|
+
if (hexMatch) {
|
|
268
|
+
let hex = hexMatch[1].toLowerCase();
|
|
269
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
270
|
+
hex = hex.split('').map((c) => c + c).join('');
|
|
271
|
+
}
|
|
272
|
+
if (keepAlpha && hex.length === 8) return `#${hex}`;
|
|
273
|
+
return `#${hex.slice(0, 6)}`;
|
|
274
|
+
}
|
|
275
|
+
const funcMatch = input.match(/^(rgba?|hsla?)\(([^)]*)\)$/i);
|
|
276
|
+
if (!funcMatch) return null;
|
|
277
|
+
const kind = funcMatch[1].toLowerCase();
|
|
278
|
+
const parsed = parseFunctionArgs(funcMatch[2]);
|
|
279
|
+
if (!parsed) return null;
|
|
280
|
+
const { parts, alpha } = parsed;
|
|
281
|
+
let red;
|
|
282
|
+
let green;
|
|
283
|
+
let blue;
|
|
284
|
+
if (kind === 'rgb' || kind === 'rgba') {
|
|
285
|
+
if (parts.length !== 3) return null;
|
|
286
|
+
red = parseChannel(parts[0]);
|
|
287
|
+
green = parseChannel(parts[1]);
|
|
288
|
+
blue = parseChannel(parts[2]);
|
|
289
|
+
if (red === null || green === null || blue === null) return null;
|
|
290
|
+
} else {
|
|
291
|
+
if (parts.length !== 3) return null;
|
|
292
|
+
const hue = parseHue(parts[0]);
|
|
293
|
+
const saturation = parsePercent(parts[1]);
|
|
294
|
+
const lightness = parsePercent(parts[2]);
|
|
295
|
+
if (hue === null || saturation === null || lightness === null) return null;
|
|
296
|
+
[red, green, blue] = hslToRgb(hue, saturation, lightness);
|
|
297
|
+
}
|
|
298
|
+
const hex = `#${hexPair(red)}${hexPair(green)}${hexPair(blue)}`;
|
|
299
|
+
if (keepAlpha && alpha < 1) return `${hex}${hexPair(alpha * 255)}`;
|
|
300
|
+
return hex;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Convert any supported CSS color into `#rrggbb`. Returns null when the
|
|
305
|
+
* value cannot be parsed. Supports: #rgb / #rgba / #rrggbb / #rrggbbaa
|
|
306
|
+
* (case-insensitive), bare 6/8-digit hex, CSS named colors, rgb() / rgba() /
|
|
307
|
+
* hsl() / hsla() with comma or modern space syntax, including percentages.
|
|
308
|
+
* Alpha is ignored.
|
|
309
|
+
*/
|
|
310
|
+
function normalizeColor(value) {
|
|
311
|
+
return parseColor(value, false);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Convert any supported CSS color into `#rrggbb` (alpha = 1) or `#rrggbbaa`
|
|
316
|
+
* (alpha < 1). The literal string 'transparent' is returned as-is. Returns
|
|
317
|
+
* null when the value cannot be parsed.
|
|
318
|
+
*/
|
|
319
|
+
function normalizeColorWithAlpha(value) {
|
|
320
|
+
return parseColor(value, true);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function hexToRgb01(color) {
|
|
324
|
+
const hex = normalizeColor(color);
|
|
325
|
+
if (!hex) return [0, 0, 0];
|
|
326
|
+
const value = Number.parseInt(hex.slice(1), 16);
|
|
327
|
+
return [
|
|
328
|
+
((value >> 16) & 255) / 255,
|
|
329
|
+
((value >> 8) & 255) / 255,
|
|
330
|
+
(value & 255) / 255
|
|
331
|
+
];
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function hexToRgba(color, alpha = 1) {
|
|
335
|
+
const hex = normalizeColor(color);
|
|
336
|
+
if (!hex) return 'rgba(0, 0, 0, 0)';
|
|
337
|
+
const value = Number.parseInt(hex.slice(1), 16);
|
|
338
|
+
const red = (value >> 16) & 255;
|
|
339
|
+
const green = (value >> 8) & 255;
|
|
340
|
+
const blue = value & 255;
|
|
341
|
+
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function validateColors(colors) {
|
|
345
|
+
return Array.isArray(colors)
|
|
346
|
+
&& colors.length === 4
|
|
347
|
+
&& colors.every((color) => normalizeColor(color) !== null);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export { hexToRgba as a, normalizeColorWithAlpha as b, hexToRgb01 as h, normalizeColor as n, validateColors as v };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SVG helpers for the avatar pipeline: XML escaping, size application,
|
|
3
|
+
* data-URI encoding and a light sanitizer for render output.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const EVENT_ATTR_PATTERN = /(\son\w+\s*=\s*["'][^"']*["'])/gi;
|
|
7
|
+
const SCRIPT_TAG_PATTERN = /<script[\s\S]*?<\/script\s*>/gi;
|
|
8
|
+
|
|
9
|
+
function escapeXml(content) {
|
|
10
|
+
return content
|
|
11
|
+
.replace(/&/g, '&')
|
|
12
|
+
.replace(/'/g, ''')
|
|
13
|
+
.replace(/"/g, '"')
|
|
14
|
+
.replace(/</g, '<')
|
|
15
|
+
.replace(/>/g, '>');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Normalize an SVG string's width/height attributes to a target size.
|
|
20
|
+
* Leaves the viewBox untouched so the aspect ratio is preserved.
|
|
21
|
+
*/
|
|
22
|
+
function applySize(svg, size) {
|
|
23
|
+
if (size == null || size === '') return svg;
|
|
24
|
+
const value = typeof size === 'number' ? String(size) : String(size);
|
|
25
|
+
return svg
|
|
26
|
+
.replace(/(<svg[^>]*?)\swidth="[^"]*"/i, `$1 width="${escapeXml(value)}"`)
|
|
27
|
+
.replace(/(<svg[^>]*?)\sheight="[^"]*"/i, `$1 height="${escapeXml(value)}"`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function svgToDataUri(svg) {
|
|
31
|
+
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Light sanitizer: removes <script> blocks and on* event handler attributes.
|
|
36
|
+
* Upstream assets are trusted (vendored and licensed), this is defense in
|
|
37
|
+
* depth for user-supplied seed strings that get interpolated into markup.
|
|
38
|
+
*/
|
|
39
|
+
function sanitizeSvg(svg) {
|
|
40
|
+
return svg
|
|
41
|
+
.replace(SCRIPT_TAG_PATTERN, '')
|
|
42
|
+
.replace(EVENT_ATTR_PATTERN, '');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Clean arbitrary user text down to a safe seed string. */
|
|
46
|
+
function cleanText(value, maxLength = 128) {
|
|
47
|
+
if (value == null) return '';
|
|
48
|
+
const text = String(value).trim().slice(0, maxLength);
|
|
49
|
+
return text.replace(/[<>&"']/g, '');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Derive UI control descriptors from an upstream JSON-Schema.
|
|
54
|
+
*
|
|
55
|
+
* The generated controls are display metadata only — the actual parameter
|
|
56
|
+
* names, types, defaults and array semantics are never changed; the demo/docs
|
|
57
|
+
* layer renders widgets from these descriptors and writes values back into
|
|
58
|
+
* providerOptions verbatim.
|
|
59
|
+
*
|
|
60
|
+
* array + items.enum -> select (multi-choice)
|
|
61
|
+
* array + items.pattern (color) -> color
|
|
62
|
+
* integer + min/max -> range
|
|
63
|
+
* boolean -> switch
|
|
64
|
+
* string + enum -> select (single)
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
function buildControlsFromSchema(schema) {
|
|
68
|
+
const props = (schema && schema.properties) || {};
|
|
69
|
+
const controls = [];
|
|
70
|
+
for (const key of Object.keys(props)) {
|
|
71
|
+
const prop = props[key];
|
|
72
|
+
const label = toLabel(key);
|
|
73
|
+
const definition = prop.default;
|
|
74
|
+
|
|
75
|
+
if (prop.type === 'array' && prop.items) {
|
|
76
|
+
if (prop.items.enum) {
|
|
77
|
+
controls.push({
|
|
78
|
+
id: key,
|
|
79
|
+
label,
|
|
80
|
+
kind: 'select',
|
|
81
|
+
options: prop.items.enum,
|
|
82
|
+
default: definition !== undefined ? [...definition] : undefined
|
|
83
|
+
});
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (isColorArray(key, prop)) {
|
|
87
|
+
controls.push({
|
|
88
|
+
id: key,
|
|
89
|
+
label,
|
|
90
|
+
kind: 'color',
|
|
91
|
+
default: definition !== undefined ? definition[0] : undefined
|
|
92
|
+
});
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
controls.push({ id: key, label, kind: 'tags', default: definition });
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (prop.type === 'integer') {
|
|
99
|
+
controls.push({
|
|
100
|
+
id: key,
|
|
101
|
+
label,
|
|
102
|
+
kind: 'range',
|
|
103
|
+
min: prop.minimum !== undefined ? prop.minimum : 0,
|
|
104
|
+
max: prop.maximum !== undefined ? prop.maximum : 100,
|
|
105
|
+
step: 1,
|
|
106
|
+
default: definition !== undefined ? definition : undefined
|
|
107
|
+
});
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (prop.type === 'boolean') {
|
|
111
|
+
controls.push({
|
|
112
|
+
id: key,
|
|
113
|
+
label,
|
|
114
|
+
kind: 'switch',
|
|
115
|
+
default: definition !== undefined ? definition : undefined
|
|
116
|
+
});
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (prop.type === 'string' && prop.enum) {
|
|
120
|
+
controls.push({
|
|
121
|
+
id: key,
|
|
122
|
+
label,
|
|
123
|
+
kind: 'select',
|
|
124
|
+
options: prop.enum,
|
|
125
|
+
default: definition
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// UI 简化标记:上游用 "<x>Probability"(0–100)控制“部位出现概率”。
|
|
130
|
+
// 这里把概率滑杆与对应的 "<x>" 下拉配对,渲染层可以把两者合并成
|
|
131
|
+
// 「无(不显示)/ 具体款式」二选一,避免让终端用户理解概率语义。
|
|
132
|
+
const selectsById = new Map(
|
|
133
|
+
controls.filter((c) => c.kind === 'select').map((c) => [c.id, c])
|
|
134
|
+
);
|
|
135
|
+
for (const control of controls) {
|
|
136
|
+
if (control.kind === 'range' && /Probability$/.test(control.id)) {
|
|
137
|
+
const baseId = control.id.slice(0, -'Probability'.length);
|
|
138
|
+
const base = selectsById.get(baseId);
|
|
139
|
+
if (base) {
|
|
140
|
+
base.probability = control.id;
|
|
141
|
+
control.probabilityOf = baseId;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return controls;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function toLabel(key) {
|
|
149
|
+
return key
|
|
150
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
151
|
+
.replace(/^./, (c) => c.toUpperCase());
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Color arrays in dicebear schemas: hex pattern with optional "transparent". */
|
|
155
|
+
function isColorArray(key, prop) {
|
|
156
|
+
if (prop.items.type !== 'string' || !prop.items.pattern) return false;
|
|
157
|
+
return /transparent/.test(prop.items.pattern) || /[Cc]olor$/.test(key);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export { applySize as a, buildControlsFromSchema as b, cleanText as c, svgToDataUri as d, escapeXml as e, sanitizeSvg as s };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal accessibility labels and small UI text. Since the copy API was
|
|
3
|
+
* removed (slots own the text area), these are no longer user-overridable.
|
|
4
|
+
* Templates use {brand} {code} {name} placeholders.
|
|
5
|
+
*/
|
|
6
|
+
const COPY = {
|
|
7
|
+
brandName: '画境观屿',
|
|
8
|
+
valueSuffix: '%',
|
|
9
|
+
progressAria: '{brand} {code} 加载进度',
|
|
10
|
+
capsuleAria: '打开 {name} 沉浸预览'
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export { COPY as C };
|