dsh-theme-gallery 0.2.0 → 0.3.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 +66 -30
- package/lib/client.js +1189 -33
- package/lib/index.js +3 -3
- package/lib/themes/hu-po-mao-mi.json +290 -0
- package/lib/themes/hu-zi-a-huang.json +290 -0
- package/package.json +7 -4
- package/schema/theme.schema.json +3 -3
package/lib/client.js
CHANGED
|
@@ -84,13 +84,152 @@ window.__ModuleLoader__.load({
|
|
|
84
84
|
const BUILT_IN_LABELS = { light: '浅色', dark: '深色', system: '跟随系统' }
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
-
*
|
|
87
|
+
* One-line copy for the built-in cards.
|
|
88
88
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
89
|
+
* The official themes declare no `description` of their own, so without this their
|
|
90
|
+
* cards would be a bare label. These two cards switch the PALETTE back to the system
|
|
91
|
+
* appearance — and they deliberately keep the previous skin's sidebar scenery on
|
|
92
|
+
* screen, which is the behaviour the second sentence advertises (see `syncAmbient`).
|
|
92
93
|
*/
|
|
93
|
-
const
|
|
94
|
+
const BUILT_IN_DESCRIPTIONS = {
|
|
95
|
+
light: '系统默认浅色外观:与「设置 → 通用 → 外观」里的浅色是同一套配色。'
|
|
96
|
+
+ '先选中主题皮肤,再切换浅色/深色 会有惊喜哦',
|
|
97
|
+
dark: '系统默认深色外观:与「设置 → 通用 → 外观」里的深色是同一套配色。'
|
|
98
|
+
+ '先选中主题皮肤,再切换浅色/深色 会有惊喜哦',
|
|
99
|
+
system: '跟随系统外观设置(按桌面端的浅色/深色自动决定,因此不作为卡片展示)',
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Built-in ids the gallery does not show as a card.
|
|
104
|
+
*
|
|
105
|
+
* `system` is a preference value rather than a look of its own — it resolves to
|
|
106
|
+
* `light` or `dark` depending on the desktop — so a card for it would duplicate one
|
|
107
|
+
* of the two cards above and change meaning with the OS setting. `light` and `dark`
|
|
108
|
+
* are BOTH shown now: together they are the way back out of every skin.
|
|
109
|
+
*/
|
|
110
|
+
const OMITTED_IDS = new Set(['system'])
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Display order of the gallery's cards — larger first.
|
|
114
|
+
*
|
|
115
|
+
* These are the user's own ranking numbers, kept as one table so the order is data
|
|
116
|
+
* rather than an implicit consequence of some other list. The REGISTRY order (and
|
|
117
|
+
* with it the contribution order in `BUNDLED_THEMES`) is deliberately untouched:
|
|
118
|
+
* this table is applied to the copy the page renders, and to nothing else.
|
|
119
|
+
*
|
|
120
|
+
* A theme with no entry sorts after every ranked one and keeps its registry order
|
|
121
|
+
* among the unranked (the sort is stable), so a theme contributed by another plugin
|
|
122
|
+
* still appears on the page instead of silently vanishing.
|
|
123
|
+
*/
|
|
124
|
+
const CARD_ORDER = {
|
|
125
|
+
light: 99,
|
|
126
|
+
dark: 98,
|
|
127
|
+
'ying-mu-cai-yun': 96,
|
|
128
|
+
'shan-qing-ting-cai': 95,
|
|
129
|
+
'pei-an-jie-xin': 91,
|
|
130
|
+
'meng-hai-you-yu': 80,
|
|
131
|
+
'hu-po-mao-mi': 76,
|
|
132
|
+
'hu-zi-a-huang': 75,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The skin this plugin puts in effect when the user has never chosen anything.
|
|
137
|
+
*
|
|
138
|
+
* Requested behaviour: installing/enabling the plugin lands on 山青婷彩 instead of the
|
|
139
|
+
* built-in white theme. Applied by the ordinary restore path, so it obeys the same
|
|
140
|
+
* global write budget and paint confirmation as a remembered skin — and it stops the
|
|
141
|
+
* moment the user makes any choice of their own, because that choice is recorded.
|
|
142
|
+
*/
|
|
143
|
+
const DEFAULT_SKIN = 'shan-qing-ting-cai'
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The two keys this plugin owns in `localStorage`.
|
|
147
|
+
*
|
|
148
|
+
* Never `ui-theme.preference`: that field is read while the boot graph is still
|
|
149
|
+
* assembling, and a skin id in it makes `buildSnapshot()` throw `theme registry lost`
|
|
150
|
+
* and refuse to start. `localStorage` is private to this plugin, so a disabled or
|
|
151
|
+
* uninstalled plugin simply stops being consulted — that is the fallback.
|
|
152
|
+
*
|
|
153
|
+
* They live at module scope because the card-click handler runs in the page component,
|
|
154
|
+
* far from `mountGallery`, and it must be able to write them BEFORE the click reaches
|
|
155
|
+
* the theme service (which publishes synchronously).
|
|
156
|
+
*/
|
|
157
|
+
const SKIN_KEY = 'theme-gallery:last-skin'
|
|
158
|
+
const BUILT_IN_KEY = 'theme-gallery:built-in-choice'
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* A theme's card rank; unranked themes sort last.
|
|
162
|
+
* @param id - the theme id.
|
|
163
|
+
* @returns its rank, or -1 when the table has no entry for it.
|
|
164
|
+
*/
|
|
165
|
+
function cardRank(id) {
|
|
166
|
+
const rank = CARD_ORDER[id]
|
|
167
|
+
return typeof rank === 'number' ? rank : -1
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Order a card list for display: highest rank first.
|
|
172
|
+
*
|
|
173
|
+
* Applied to the copy the page renders, never to the array the service is given, so a
|
|
174
|
+
* theme's registration and its card position are independent concerns (and a reorder
|
|
175
|
+
* can never change which themes exist or what they contain).
|
|
176
|
+
* @param themes - theme definitions, in registry order.
|
|
177
|
+
* @returns a new array in display order.
|
|
178
|
+
*/
|
|
179
|
+
function cardsInDisplayOrder(themes) {
|
|
180
|
+
return [...themes].sort((a, b) => cardRank(b.id) - cardRank(a.id))
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Record the user's card choice BEFORE it reaches the theme service.
|
|
185
|
+
*
|
|
186
|
+
* ── WHY THE ORDER IS LOAD-BEARING ───────────────────────────────────────────
|
|
187
|
+
*
|
|
188
|
+
* `ctx.theme.setTheme()` publishes synchronously, and the publisher re-applies the
|
|
189
|
+
* remembered skin whenever the active theme is not one of ours. So a built-in card
|
|
190
|
+
* clicked after any skin had been chosen used to be undone a microtask later: the click
|
|
191
|
+
* landed, the restore read `SKIN_KEY`, and the skin came straight back. On screen the
|
|
192
|
+
* 深色 card simply did nothing.
|
|
193
|
+
*
|
|
194
|
+
* Removing `SKIN_KEY` here, before the write, is what makes a built-in choice stick.
|
|
195
|
+
* `BUILT_IN_KEY` is written so the default skin does NOT fire on the next restart and
|
|
196
|
+
* undo the choice — see `wantedSkin`.
|
|
197
|
+
*
|
|
198
|
+
* Any theme this package does not provide counts as "not a skin": another plugin's
|
|
199
|
+
* theme must win as well, for exactly the same reason.
|
|
200
|
+
* @param id - the card's theme id.
|
|
201
|
+
*/
|
|
202
|
+
function rememberCardChoice(id) {
|
|
203
|
+
try {
|
|
204
|
+
if (typeof window === 'undefined') return
|
|
205
|
+
const storage = window.localStorage
|
|
206
|
+
if (storage === undefined || storage === null) return
|
|
207
|
+
if (bundledTheme(id) === undefined) {
|
|
208
|
+
storage.removeItem(SKIN_KEY)
|
|
209
|
+
storage.setItem(BUILT_IN_KEY, String(id))
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
// A skin: it is recorded by the restore once it is genuinely active, so the stale
|
|
213
|
+
// built-in marker is cleared here rather than there.
|
|
214
|
+
storage.removeItem(BUILT_IN_KEY)
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error('[theme-gallery] could not record the card choice:', error)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* The built-in appearance the user picked in this panel, if any.
|
|
222
|
+
* @returns the id, or null when the user never picked one here.
|
|
223
|
+
*/
|
|
224
|
+
function builtInChoice() {
|
|
225
|
+
try {
|
|
226
|
+
if (typeof window === 'undefined') return null
|
|
227
|
+
const value = window.localStorage?.getItem(BUILT_IN_KEY)
|
|
228
|
+
return typeof value === 'string' && value !== '' ? value : null
|
|
229
|
+
} catch {
|
|
230
|
+
return null
|
|
231
|
+
}
|
|
232
|
+
}
|
|
94
233
|
|
|
95
234
|
/** Body attribute publishing the reading state; the injected rule reads it. */
|
|
96
235
|
const READING_ATTRIBUTE = 'data-dsh-theme-reading'
|
|
@@ -118,7 +257,7 @@ window.__ModuleLoader__.load({
|
|
|
118
257
|
* package.json, and the release workflow fails when re-running the embed step
|
|
119
258
|
* changes a tracked file.
|
|
120
259
|
*/
|
|
121
|
-
const BUNDLED_VERSION = '0.
|
|
260
|
+
const BUNDLED_VERSION = '0.3.0'
|
|
122
261
|
|
|
123
262
|
/**
|
|
124
263
|
* Themes this package contributes, inlined from lib/themes/*.json.
|
|
@@ -131,6 +270,582 @@ window.__ModuleLoader__.load({
|
|
|
131
270
|
* Regenerate after editing those files: `node scripts/embed-themes.mjs`.
|
|
132
271
|
*/
|
|
133
272
|
const BUNDLED_THEMES = [
|
|
273
|
+
{
|
|
274
|
+
"id": "hu-po-mao-mi",
|
|
275
|
+
"label": "琥珀猫咪",
|
|
276
|
+
"description": "琥珀暖光,双猫伴读 —— 原创宠物主题:两只虎斑短毛猫守着洒满阳光的窗台",
|
|
277
|
+
"colorScheme": "light",
|
|
278
|
+
"tokens": {
|
|
279
|
+
"--dsw-alias-bg-base": {
|
|
280
|
+
"light": "linear-gradient(to bottom,#FEFBF5 0%,#FBF2E3 55%,#F8EDD8 100%)",
|
|
281
|
+
"dark": "linear-gradient(to bottom,#FEFBF5 0%,#FBF2E3 55%,#F8EDD8 100%)"
|
|
282
|
+
},
|
|
283
|
+
"--dsw-alias-bg-layer-1": {
|
|
284
|
+
"light": "#FEFCF7",
|
|
285
|
+
"dark": "#FEFCF7"
|
|
286
|
+
},
|
|
287
|
+
"--dsw-alias-bg-layer-2": {
|
|
288
|
+
"light": "#FFFFFF",
|
|
289
|
+
"dark": "#FFFFFF"
|
|
290
|
+
},
|
|
291
|
+
"--dsw-alias-bg-layer-3": {
|
|
292
|
+
"light": "#FFFFFF",
|
|
293
|
+
"dark": "#FFFFFF"
|
|
294
|
+
},
|
|
295
|
+
"--dsw-alias-bg-overlay": {
|
|
296
|
+
"light": "#F3E5CD",
|
|
297
|
+
"dark": "#F3E5CD"
|
|
298
|
+
},
|
|
299
|
+
"--dsw-alias-bg-skeleton": {
|
|
300
|
+
"light": "#9C5F2414",
|
|
301
|
+
"dark": "#9C5F2414"
|
|
302
|
+
},
|
|
303
|
+
"--dsw-alias-bg-module-platform": {
|
|
304
|
+
"light": "#F3E5CD",
|
|
305
|
+
"dark": "#F3E5CD"
|
|
306
|
+
},
|
|
307
|
+
"--dsw-alias-bg-multi-select": {
|
|
308
|
+
"light": "#F3E5CD",
|
|
309
|
+
"dark": "#F3E5CD"
|
|
310
|
+
},
|
|
311
|
+
"--dsw-alias-border-l1": {
|
|
312
|
+
"light": "#9C5F240F",
|
|
313
|
+
"dark": "#9C5F240F"
|
|
314
|
+
},
|
|
315
|
+
"--dsw-alias-border-l2": {
|
|
316
|
+
"light": "#9C5F2421",
|
|
317
|
+
"dark": "#9C5F2421"
|
|
318
|
+
},
|
|
319
|
+
"--dsw-alias-border-l3": {
|
|
320
|
+
"light": "#9C5F242E",
|
|
321
|
+
"dark": "#9C5F242E"
|
|
322
|
+
},
|
|
323
|
+
"--dsw-alias-border-l4": {
|
|
324
|
+
"light": "#9C5F243D",
|
|
325
|
+
"dark": "#9C5F243D"
|
|
326
|
+
},
|
|
327
|
+
"--dsw-alias-brand-primary": {
|
|
328
|
+
"light": "#9C5F24",
|
|
329
|
+
"dark": "#9C5F24"
|
|
330
|
+
},
|
|
331
|
+
"--dsw-alias-brand-primary-invert": {
|
|
332
|
+
"light": "#FFFFFF",
|
|
333
|
+
"dark": "#FFFFFF"
|
|
334
|
+
},
|
|
335
|
+
"--dsw-alias-brand-text": {
|
|
336
|
+
"light": "#9C5F24",
|
|
337
|
+
"dark": "#9C5F24"
|
|
338
|
+
},
|
|
339
|
+
"--dsw-alias-label-primary": {
|
|
340
|
+
"light": "#453019",
|
|
341
|
+
"dark": "#453019"
|
|
342
|
+
},
|
|
343
|
+
"--dsw-alias-label-primary-bluish": {
|
|
344
|
+
"light": "#453019",
|
|
345
|
+
"dark": "#453019"
|
|
346
|
+
},
|
|
347
|
+
"--dsw-alias-label-secondary": {
|
|
348
|
+
"light": "#6A4E2F",
|
|
349
|
+
"dark": "#6A4E2F"
|
|
350
|
+
},
|
|
351
|
+
"--dsw-alias-label-tertiary": {
|
|
352
|
+
"light": "#8C6F4C",
|
|
353
|
+
"dark": "#8C6F4C"
|
|
354
|
+
},
|
|
355
|
+
"--dsw-alias-label-caption": {
|
|
356
|
+
"light": "#8C6F4C",
|
|
357
|
+
"dark": "#8C6F4C"
|
|
358
|
+
},
|
|
359
|
+
"--dsw-alias-label-dimmed": {
|
|
360
|
+
"light": "#B49C7F",
|
|
361
|
+
"dark": "#B49C7F"
|
|
362
|
+
},
|
|
363
|
+
"--dsw-alias-label-primary-dimmed": {
|
|
364
|
+
"light": "#6A4E2F",
|
|
365
|
+
"dark": "#6A4E2F"
|
|
366
|
+
},
|
|
367
|
+
"--dsw-alias-label-primary-foreground": {
|
|
368
|
+
"light": "#FFFFFF",
|
|
369
|
+
"dark": "#FFFFFF"
|
|
370
|
+
},
|
|
371
|
+
"--dsw-alias-label-primary-inverted": {
|
|
372
|
+
"light": "#FFFFFF",
|
|
373
|
+
"dark": "#FFFFFF"
|
|
374
|
+
},
|
|
375
|
+
"--dsw-alias-link": {
|
|
376
|
+
"light": "#CA6924",
|
|
377
|
+
"dark": "#CA6924"
|
|
378
|
+
},
|
|
379
|
+
"--dsw-alias-interactive-bg-hover": {
|
|
380
|
+
"light": "#9C5F2414",
|
|
381
|
+
"dark": "#9C5F2414"
|
|
382
|
+
},
|
|
383
|
+
"--dsw-alias-interactive-bg-active": {
|
|
384
|
+
"light": "#CA69242E",
|
|
385
|
+
"dark": "#CA69242E"
|
|
386
|
+
},
|
|
387
|
+
"--dsw-alias-interactive-bg-hover-solid": {
|
|
388
|
+
"light": "#F3E5CD",
|
|
389
|
+
"dark": "#F3E5CD"
|
|
390
|
+
},
|
|
391
|
+
"--dsw-alias-interactive-bg-hover-accent": {
|
|
392
|
+
"light": "#CA69243D",
|
|
393
|
+
"dark": "#CA69243D"
|
|
394
|
+
},
|
|
395
|
+
"--dsw-alias-button-primary-fill": {
|
|
396
|
+
"light": "#9C5F24",
|
|
397
|
+
"dark": "#9C5F24"
|
|
398
|
+
},
|
|
399
|
+
"--dsw-alias-button-primary-hover": {
|
|
400
|
+
"light": "#B0702F",
|
|
401
|
+
"dark": "#B0702F"
|
|
402
|
+
},
|
|
403
|
+
"--dsw-alias-button-primary-dimmed": {
|
|
404
|
+
"light": "#9C5F2447",
|
|
405
|
+
"dark": "#9C5F2447"
|
|
406
|
+
},
|
|
407
|
+
"--dsw-alias-button-ghost-active-fill": {
|
|
408
|
+
"light": "#CA692429",
|
|
409
|
+
"dark": "#CA692429"
|
|
410
|
+
},
|
|
411
|
+
"--dsw-alias-button-ghost-active-border": {
|
|
412
|
+
"light": "#CA6924",
|
|
413
|
+
"dark": "#CA6924"
|
|
414
|
+
},
|
|
415
|
+
"--dsw-alias-button-ghost-active-hover": {
|
|
416
|
+
"light": "#CA692447",
|
|
417
|
+
"dark": "#CA692447"
|
|
418
|
+
},
|
|
419
|
+
"--dsw-alias-button-info-fill": {
|
|
420
|
+
"light": "#B0702F",
|
|
421
|
+
"dark": "#B0702F"
|
|
422
|
+
},
|
|
423
|
+
"--dsw-alias-button-info-hover": {
|
|
424
|
+
"light": "#9C5F24",
|
|
425
|
+
"dark": "#9C5F24"
|
|
426
|
+
},
|
|
427
|
+
"--dsw-alias-button-elevated-fill": {
|
|
428
|
+
"light": "#FFFFFF",
|
|
429
|
+
"dark": "#FFFFFF"
|
|
430
|
+
},
|
|
431
|
+
"--dsw-alias-button-floating-fill": {
|
|
432
|
+
"light": "#FFFFFF",
|
|
433
|
+
"dark": "#FFFFFF"
|
|
434
|
+
},
|
|
435
|
+
"--dsw-alias-button-floating-hover": {
|
|
436
|
+
"light": "#FDF8F0",
|
|
437
|
+
"dark": "#FDF8F0"
|
|
438
|
+
},
|
|
439
|
+
"--dsw-alias-button-contrast-fill": {
|
|
440
|
+
"light": "#453019",
|
|
441
|
+
"dark": "#453019"
|
|
442
|
+
},
|
|
443
|
+
"--dsw-alias-markdown-code-block": {
|
|
444
|
+
"light": "#F7EEDF",
|
|
445
|
+
"dark": "#F7EEDF"
|
|
446
|
+
},
|
|
447
|
+
"--dsw-alias-markdown-code-block-banner": {
|
|
448
|
+
"light": "#F0E3CB",
|
|
449
|
+
"dark": "#F0E3CB"
|
|
450
|
+
},
|
|
451
|
+
"--dsw-alias-markdown-inline-code": {
|
|
452
|
+
"light": "#F3E9D6",
|
|
453
|
+
"dark": "#F3E9D6"
|
|
454
|
+
},
|
|
455
|
+
"--dsw-alias-markdown-citation": {
|
|
456
|
+
"light": "#F3E9D6",
|
|
457
|
+
"dark": "#F3E9D6"
|
|
458
|
+
},
|
|
459
|
+
"--dsw-alias-markdown-tag": {
|
|
460
|
+
"light": "#F3E9D6",
|
|
461
|
+
"dark": "#F3E9D6"
|
|
462
|
+
},
|
|
463
|
+
"--dsw-alias-markdown-placeholder": {
|
|
464
|
+
"light": "#F0E3CB",
|
|
465
|
+
"dark": "#F0E3CB"
|
|
466
|
+
},
|
|
467
|
+
"--dsw-alias-markdown-code-segment-selected": {
|
|
468
|
+
"light": "#CA692429",
|
|
469
|
+
"dark": "#CA692429"
|
|
470
|
+
},
|
|
471
|
+
"--dsw-alias-markdown-code-segment-unselected": {
|
|
472
|
+
"light": "#F7EEDF",
|
|
473
|
+
"dark": "#F7EEDF"
|
|
474
|
+
},
|
|
475
|
+
"--dsw-alias-scrollbar-bg-l1": {
|
|
476
|
+
"light": "#9C5F2429",
|
|
477
|
+
"dark": "#9C5F2429"
|
|
478
|
+
},
|
|
479
|
+
"--dsw-alias-scrollbar-bg-l2": {
|
|
480
|
+
"light": "#9C5F2438",
|
|
481
|
+
"dark": "#9C5F2438"
|
|
482
|
+
},
|
|
483
|
+
"--dsw-alias-scrollbar-hover-l1": {
|
|
484
|
+
"light": "#9C5F2452",
|
|
485
|
+
"dark": "#9C5F2452"
|
|
486
|
+
},
|
|
487
|
+
"--dsw-alias-scrollbar-hover-l2": {
|
|
488
|
+
"light": "#9C5F2466",
|
|
489
|
+
"dark": "#9C5F2466"
|
|
490
|
+
},
|
|
491
|
+
"--dsw-alias-tooltip-bg": {
|
|
492
|
+
"light": "#453019",
|
|
493
|
+
"dark": "#453019"
|
|
494
|
+
},
|
|
495
|
+
"--dsw-alias-toast-bg": {
|
|
496
|
+
"light": "#FFFFFF",
|
|
497
|
+
"dark": "#FFFFFF"
|
|
498
|
+
},
|
|
499
|
+
"--dsw-alias-state-error-primary": {
|
|
500
|
+
"light": "#C65B6A",
|
|
501
|
+
"dark": "#C65B6A"
|
|
502
|
+
},
|
|
503
|
+
"--dsw-alias-state-error-secondary": {
|
|
504
|
+
"light": "#DB5A6B",
|
|
505
|
+
"dark": "#DB5A6B"
|
|
506
|
+
},
|
|
507
|
+
"--dsw-alias-state-success-primary": {
|
|
508
|
+
"light": "#21A675",
|
|
509
|
+
"dark": "#21A675"
|
|
510
|
+
},
|
|
511
|
+
"--dsw-alias-state-success-secondary": {
|
|
512
|
+
"light": "#9C5F24",
|
|
513
|
+
"dark": "#9C5F24"
|
|
514
|
+
},
|
|
515
|
+
"--dsw-alias-state-success-tertiary": {
|
|
516
|
+
"light": "#CFBA9C",
|
|
517
|
+
"dark": "#CFBA9C"
|
|
518
|
+
},
|
|
519
|
+
"--dsw-alias-state-warn-primary": {
|
|
520
|
+
"light": "#B97F3A",
|
|
521
|
+
"dark": "#B97F3A"
|
|
522
|
+
},
|
|
523
|
+
"--dsw-alias-state-warn-secondary": {
|
|
524
|
+
"light": "#D9A45E",
|
|
525
|
+
"dark": "#D9A45E"
|
|
526
|
+
},
|
|
527
|
+
"--dsw-alias-state-warn-tertiary": {
|
|
528
|
+
"light": "#EFDCC0",
|
|
529
|
+
"dark": "#EFDCC0"
|
|
530
|
+
},
|
|
531
|
+
"--dsw-alias-state-warn-label": {
|
|
532
|
+
"light": "#8A5A22",
|
|
533
|
+
"dark": "#8A5A22"
|
|
534
|
+
},
|
|
535
|
+
"--dsw-alias-state-business-primary": {
|
|
536
|
+
"light": "#CA6924",
|
|
537
|
+
"dark": "#CA6924"
|
|
538
|
+
},
|
|
539
|
+
"--dsw-alias-state-business-tertiary": {
|
|
540
|
+
"light": "#E29C45",
|
|
541
|
+
"dark": "#E29C45"
|
|
542
|
+
},
|
|
543
|
+
"--dsw-specific-sidebar-fill": {
|
|
544
|
+
"light": "linear-gradient(to bottom,#FBF0DE 0%,#F5E3C6 20%,#EDD5AE 36%,#E3C593 50%,#D8B57D 62%,#CBA468 74%,#C69E5F 86%,#BB904F 100%)",
|
|
545
|
+
"dark": "linear-gradient(to bottom,#FBF0DE 0%,#F5E3C6 20%,#EDD5AE 36%,#E3C593 50%,#D8B57D 62%,#CBA468 74%,#C69E5F 86%,#BB904F 100%)"
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
"reading": {
|
|
549
|
+
"colorScheme": "light",
|
|
550
|
+
"bg": "#FFFFFF",
|
|
551
|
+
"alpha": 0.62,
|
|
552
|
+
"blur": 3,
|
|
553
|
+
"maxWidth": 640
|
|
554
|
+
},
|
|
555
|
+
"accent": "#CA6924",
|
|
556
|
+
"ambient": {
|
|
557
|
+
"kind": "humao",
|
|
558
|
+
"dust": 6
|
|
559
|
+
}
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
"id": "hu-zi-a-huang",
|
|
563
|
+
"label": "虎子阿黄",
|
|
564
|
+
"description": "麦香田园,黄犬相迎 —— 原创宠物主题:中黄田园犬「虎子阿黄」摇着尾巴守在田埂上",
|
|
565
|
+
"colorScheme": "light",
|
|
566
|
+
"tokens": {
|
|
567
|
+
"--dsw-alias-bg-base": {
|
|
568
|
+
"light": "linear-gradient(to bottom,#FEFDF6 0%,#FBF5E0 55%,#F7EFD0 100%)",
|
|
569
|
+
"dark": "linear-gradient(to bottom,#FEFDF6 0%,#FBF5E0 55%,#F7EFD0 100%)"
|
|
570
|
+
},
|
|
571
|
+
"--dsw-alias-bg-layer-1": {
|
|
572
|
+
"light": "#FEFCF4",
|
|
573
|
+
"dark": "#FEFCF4"
|
|
574
|
+
},
|
|
575
|
+
"--dsw-alias-bg-layer-2": {
|
|
576
|
+
"light": "#FFFFFF",
|
|
577
|
+
"dark": "#FFFFFF"
|
|
578
|
+
},
|
|
579
|
+
"--dsw-alias-bg-layer-3": {
|
|
580
|
+
"light": "#FFFFFF",
|
|
581
|
+
"dark": "#FFFFFF"
|
|
582
|
+
},
|
|
583
|
+
"--dsw-alias-bg-overlay": {
|
|
584
|
+
"light": "#F4EAD0",
|
|
585
|
+
"dark": "#F4EAD0"
|
|
586
|
+
},
|
|
587
|
+
"--dsw-alias-bg-skeleton": {
|
|
588
|
+
"light": "#896C3914",
|
|
589
|
+
"dark": "#896C3914"
|
|
590
|
+
},
|
|
591
|
+
"--dsw-alias-bg-module-platform": {
|
|
592
|
+
"light": "#F4EAD0",
|
|
593
|
+
"dark": "#F4EAD0"
|
|
594
|
+
},
|
|
595
|
+
"--dsw-alias-bg-multi-select": {
|
|
596
|
+
"light": "#F4EAD0",
|
|
597
|
+
"dark": "#F4EAD0"
|
|
598
|
+
},
|
|
599
|
+
"--dsw-alias-border-l1": {
|
|
600
|
+
"light": "#896C390F",
|
|
601
|
+
"dark": "#896C390F"
|
|
602
|
+
},
|
|
603
|
+
"--dsw-alias-border-l2": {
|
|
604
|
+
"light": "#896C3921",
|
|
605
|
+
"dark": "#896C3921"
|
|
606
|
+
},
|
|
607
|
+
"--dsw-alias-border-l3": {
|
|
608
|
+
"light": "#896C392E",
|
|
609
|
+
"dark": "#896C392E"
|
|
610
|
+
},
|
|
611
|
+
"--dsw-alias-border-l4": {
|
|
612
|
+
"light": "#896C393D",
|
|
613
|
+
"dark": "#896C393D"
|
|
614
|
+
},
|
|
615
|
+
"--dsw-alias-brand-primary": {
|
|
616
|
+
"light": "#896C39",
|
|
617
|
+
"dark": "#896C39"
|
|
618
|
+
},
|
|
619
|
+
"--dsw-alias-brand-primary-invert": {
|
|
620
|
+
"light": "#FFFFFF",
|
|
621
|
+
"dark": "#FFFFFF"
|
|
622
|
+
},
|
|
623
|
+
"--dsw-alias-brand-text": {
|
|
624
|
+
"light": "#896C39",
|
|
625
|
+
"dark": "#896C39"
|
|
626
|
+
},
|
|
627
|
+
"--dsw-alias-label-primary": {
|
|
628
|
+
"light": "#3B3018",
|
|
629
|
+
"dark": "#3B3018"
|
|
630
|
+
},
|
|
631
|
+
"--dsw-alias-label-primary-bluish": {
|
|
632
|
+
"light": "#3B3018",
|
|
633
|
+
"dark": "#3B3018"
|
|
634
|
+
},
|
|
635
|
+
"--dsw-alias-label-secondary": {
|
|
636
|
+
"light": "#5D4E2B",
|
|
637
|
+
"dark": "#5D4E2B"
|
|
638
|
+
},
|
|
639
|
+
"--dsw-alias-label-tertiary": {
|
|
640
|
+
"light": "#857451",
|
|
641
|
+
"dark": "#857451"
|
|
642
|
+
},
|
|
643
|
+
"--dsw-alias-label-caption": {
|
|
644
|
+
"light": "#857451",
|
|
645
|
+
"dark": "#857451"
|
|
646
|
+
},
|
|
647
|
+
"--dsw-alias-label-dimmed": {
|
|
648
|
+
"light": "#B2A47E",
|
|
649
|
+
"dark": "#B2A47E"
|
|
650
|
+
},
|
|
651
|
+
"--dsw-alias-label-primary-dimmed": {
|
|
652
|
+
"light": "#5D4E2B",
|
|
653
|
+
"dark": "#5D4E2B"
|
|
654
|
+
},
|
|
655
|
+
"--dsw-alias-label-primary-foreground": {
|
|
656
|
+
"light": "#FFFFFF",
|
|
657
|
+
"dark": "#FFFFFF"
|
|
658
|
+
},
|
|
659
|
+
"--dsw-alias-label-primary-inverted": {
|
|
660
|
+
"light": "#FFFFFF",
|
|
661
|
+
"dark": "#FFFFFF"
|
|
662
|
+
},
|
|
663
|
+
"--dsw-alias-link": {
|
|
664
|
+
"light": "#AE7000",
|
|
665
|
+
"dark": "#AE7000"
|
|
666
|
+
},
|
|
667
|
+
"--dsw-alias-interactive-bg-hover": {
|
|
668
|
+
"light": "#896C3914",
|
|
669
|
+
"dark": "#896C3914"
|
|
670
|
+
},
|
|
671
|
+
"--dsw-alias-interactive-bg-active": {
|
|
672
|
+
"light": "#F0C2392E",
|
|
673
|
+
"dark": "#F0C2392E"
|
|
674
|
+
},
|
|
675
|
+
"--dsw-alias-interactive-bg-hover-solid": {
|
|
676
|
+
"light": "#F4EAD0",
|
|
677
|
+
"dark": "#F4EAD0"
|
|
678
|
+
},
|
|
679
|
+
"--dsw-alias-interactive-bg-hover-accent": {
|
|
680
|
+
"light": "#F0C2393D",
|
|
681
|
+
"dark": "#F0C2393D"
|
|
682
|
+
},
|
|
683
|
+
"--dsw-alias-button-primary-fill": {
|
|
684
|
+
"light": "#896C39",
|
|
685
|
+
"dark": "#896C39"
|
|
686
|
+
},
|
|
687
|
+
"--dsw-alias-button-primary-hover": {
|
|
688
|
+
"light": "#A88462",
|
|
689
|
+
"dark": "#A88462"
|
|
690
|
+
},
|
|
691
|
+
"--dsw-alias-button-primary-dimmed": {
|
|
692
|
+
"light": "#896C3947",
|
|
693
|
+
"dark": "#896C3947"
|
|
694
|
+
},
|
|
695
|
+
"--dsw-alias-button-ghost-active-fill": {
|
|
696
|
+
"light": "#F0C23929",
|
|
697
|
+
"dark": "#F0C23929"
|
|
698
|
+
},
|
|
699
|
+
"--dsw-alias-button-ghost-active-border": {
|
|
700
|
+
"light": "#F0C239",
|
|
701
|
+
"dark": "#F0C239"
|
|
702
|
+
},
|
|
703
|
+
"--dsw-alias-button-ghost-active-hover": {
|
|
704
|
+
"light": "#F0C23947",
|
|
705
|
+
"dark": "#F0C23947"
|
|
706
|
+
},
|
|
707
|
+
"--dsw-alias-button-info-fill": {
|
|
708
|
+
"light": "#A88462",
|
|
709
|
+
"dark": "#A88462"
|
|
710
|
+
},
|
|
711
|
+
"--dsw-alias-button-info-hover": {
|
|
712
|
+
"light": "#896C39",
|
|
713
|
+
"dark": "#896C39"
|
|
714
|
+
},
|
|
715
|
+
"--dsw-alias-button-elevated-fill": {
|
|
716
|
+
"light": "#FFFFFF",
|
|
717
|
+
"dark": "#FFFFFF"
|
|
718
|
+
},
|
|
719
|
+
"--dsw-alias-button-floating-fill": {
|
|
720
|
+
"light": "#FFFFFF",
|
|
721
|
+
"dark": "#FFFFFF"
|
|
722
|
+
},
|
|
723
|
+
"--dsw-alias-button-floating-hover": {
|
|
724
|
+
"light": "#FCF8EC",
|
|
725
|
+
"dark": "#FCF8EC"
|
|
726
|
+
},
|
|
727
|
+
"--dsw-alias-button-contrast-fill": {
|
|
728
|
+
"light": "#3B3018",
|
|
729
|
+
"dark": "#3B3018"
|
|
730
|
+
},
|
|
731
|
+
"--dsw-alias-markdown-code-block": {
|
|
732
|
+
"light": "#F8F1DC",
|
|
733
|
+
"dark": "#F8F1DC"
|
|
734
|
+
},
|
|
735
|
+
"--dsw-alias-markdown-code-block-banner": {
|
|
736
|
+
"light": "#F1E7C6",
|
|
737
|
+
"dark": "#F1E7C6"
|
|
738
|
+
},
|
|
739
|
+
"--dsw-alias-markdown-inline-code": {
|
|
740
|
+
"light": "#F4EAD0",
|
|
741
|
+
"dark": "#F4EAD0"
|
|
742
|
+
},
|
|
743
|
+
"--dsw-alias-markdown-citation": {
|
|
744
|
+
"light": "#F4EAD0",
|
|
745
|
+
"dark": "#F4EAD0"
|
|
746
|
+
},
|
|
747
|
+
"--dsw-alias-markdown-tag": {
|
|
748
|
+
"light": "#F4EAD0",
|
|
749
|
+
"dark": "#F4EAD0"
|
|
750
|
+
},
|
|
751
|
+
"--dsw-alias-markdown-placeholder": {
|
|
752
|
+
"light": "#F1E7C6",
|
|
753
|
+
"dark": "#F1E7C6"
|
|
754
|
+
},
|
|
755
|
+
"--dsw-alias-markdown-code-segment-selected": {
|
|
756
|
+
"light": "#F0C23929",
|
|
757
|
+
"dark": "#F0C23929"
|
|
758
|
+
},
|
|
759
|
+
"--dsw-alias-markdown-code-segment-unselected": {
|
|
760
|
+
"light": "#F8F1DC",
|
|
761
|
+
"dark": "#F8F1DC"
|
|
762
|
+
},
|
|
763
|
+
"--dsw-alias-scrollbar-bg-l1": {
|
|
764
|
+
"light": "#896C3929",
|
|
765
|
+
"dark": "#896C3929"
|
|
766
|
+
},
|
|
767
|
+
"--dsw-alias-scrollbar-bg-l2": {
|
|
768
|
+
"light": "#896C3938",
|
|
769
|
+
"dark": "#896C3938"
|
|
770
|
+
},
|
|
771
|
+
"--dsw-alias-scrollbar-hover-l1": {
|
|
772
|
+
"light": "#896C3952",
|
|
773
|
+
"dark": "#896C3952"
|
|
774
|
+
},
|
|
775
|
+
"--dsw-alias-scrollbar-hover-l2": {
|
|
776
|
+
"light": "#896C3966",
|
|
777
|
+
"dark": "#896C3966"
|
|
778
|
+
},
|
|
779
|
+
"--dsw-alias-tooltip-bg": {
|
|
780
|
+
"light": "#3B3018",
|
|
781
|
+
"dark": "#3B3018"
|
|
782
|
+
},
|
|
783
|
+
"--dsw-alias-toast-bg": {
|
|
784
|
+
"light": "#FFFFFF",
|
|
785
|
+
"dark": "#FFFFFF"
|
|
786
|
+
},
|
|
787
|
+
"--dsw-alias-state-error-primary": {
|
|
788
|
+
"light": "#C65B6A",
|
|
789
|
+
"dark": "#C65B6A"
|
|
790
|
+
},
|
|
791
|
+
"--dsw-alias-state-error-secondary": {
|
|
792
|
+
"light": "#DB5A6B",
|
|
793
|
+
"dark": "#DB5A6B"
|
|
794
|
+
},
|
|
795
|
+
"--dsw-alias-state-success-primary": {
|
|
796
|
+
"light": "#21A675",
|
|
797
|
+
"dark": "#21A675"
|
|
798
|
+
},
|
|
799
|
+
"--dsw-alias-state-success-secondary": {
|
|
800
|
+
"light": "#896C39",
|
|
801
|
+
"dark": "#896C39"
|
|
802
|
+
},
|
|
803
|
+
"--dsw-alias-state-success-tertiary": {
|
|
804
|
+
"light": "#D1C69C",
|
|
805
|
+
"dark": "#D1C69C"
|
|
806
|
+
},
|
|
807
|
+
"--dsw-alias-state-warn-primary": {
|
|
808
|
+
"light": "#B97F3A",
|
|
809
|
+
"dark": "#B97F3A"
|
|
810
|
+
},
|
|
811
|
+
"--dsw-alias-state-warn-secondary": {
|
|
812
|
+
"light": "#D9A45E",
|
|
813
|
+
"dark": "#D9A45E"
|
|
814
|
+
},
|
|
815
|
+
"--dsw-alias-state-warn-tertiary": {
|
|
816
|
+
"light": "#EFDCC0",
|
|
817
|
+
"dark": "#EFDCC0"
|
|
818
|
+
},
|
|
819
|
+
"--dsw-alias-state-warn-label": {
|
|
820
|
+
"light": "#8A5A22",
|
|
821
|
+
"dark": "#8A5A22"
|
|
822
|
+
},
|
|
823
|
+
"--dsw-alias-state-business-primary": {
|
|
824
|
+
"light": "#F0C239",
|
|
825
|
+
"dark": "#F0C239"
|
|
826
|
+
},
|
|
827
|
+
"--dsw-alias-state-business-tertiary": {
|
|
828
|
+
"light": "#F5D46B",
|
|
829
|
+
"dark": "#F5D46B"
|
|
830
|
+
},
|
|
831
|
+
"--dsw-specific-sidebar-fill": {
|
|
832
|
+
"light": "linear-gradient(to bottom,#FAF3DC 0%,#F4EAC2 20%,#ECDFAB 36%,#E2D191 50%,#D6C078 62%,#C9AF63 74%,#C4A85F 86%,#B89B51 100%)",
|
|
833
|
+
"dark": "linear-gradient(to bottom,#FAF3DC 0%,#F4EAC2 20%,#ECDFAB 36%,#E2D191 50%,#D6C078 62%,#C9AF63 74%,#C4A85F 86%,#B89B51 100%)"
|
|
834
|
+
}
|
|
835
|
+
},
|
|
836
|
+
"reading": {
|
|
837
|
+
"colorScheme": "light",
|
|
838
|
+
"bg": "#FFFFFF",
|
|
839
|
+
"alpha": 0.62,
|
|
840
|
+
"blur": 3,
|
|
841
|
+
"maxWidth": 640
|
|
842
|
+
},
|
|
843
|
+
"accent": "#F0C239",
|
|
844
|
+
"ambient": {
|
|
845
|
+
"kind": "ahuang",
|
|
846
|
+
"dust": 5
|
|
847
|
+
}
|
|
848
|
+
},
|
|
134
849
|
{
|
|
135
850
|
"id": "meng-hai-you-yu",
|
|
136
851
|
"label": "梦海游鱼",
|
|
@@ -1610,6 +2325,32 @@ window.__ModuleLoader__.load({
|
|
|
1610
2325
|
'@keyframes dsh-amb-gc-wing{0%{transform:rotate(0deg)}40%{transform:rotate(-3.5deg)}60%{transform:rotate(2.5deg)}100%{transform:rotate(0deg)}}',
|
|
1611
2326
|
'@keyframes dsh-amb-gc-dew{0%{opacity:.3;transform:scale(1)}100%{opacity:1;transform:scale(1.5)}}',
|
|
1612
2327
|
|
|
2328
|
+
/* ── 琥珀猫咪 / 虎子阿黄(原创宠物场景)──────────────────────────────
|
|
2329
|
+
Like the five ported scenes above: element geometry, colour and
|
|
2330
|
+
per-element timing are INLINE in the scene builders, so only the
|
|
2331
|
+
@keyframes live here — each prefixed (hm / hz) so they cannot collide,
|
|
2332
|
+
and each defined exactly once (a duplicate would silently win). Every
|
|
2333
|
+
animated vertical term stays at or below the resting baseline, because
|
|
2334
|
+
the scene box clips at its own top edge. */
|
|
2335
|
+
'@keyframes dsh-amb-hm-glow{from{opacity:.55}to{opacity:.85}}',
|
|
2336
|
+
'@keyframes dsh-amb-hm-dust{0%{transform:translate(0,0);opacity:0}12%{opacity:.9}'
|
|
2337
|
+
+ '60%{transform:translate(-.5em,-3.2em);opacity:.45}100%{transform:translate(.4em,-4.6em);opacity:0}}',
|
|
2338
|
+
'@keyframes dsh-amb-hm-tail{from{transform:rotate(-3deg)}to{transform:rotate(5deg)}}',
|
|
2339
|
+
'@keyframes dsh-amb-hm-breathe{0%,100%{transform:scaleY(1)}50%{transform:scaleY(1.03)}}',
|
|
2340
|
+
'@keyframes dsh-amb-hm-ear{0%,86%,100%{transform:rotate(0)}90%{transform:rotate(-9deg)}94%{transform:rotate(4deg)}}',
|
|
2341
|
+
'@keyframes dsh-amb-hm-zzz{0%{transform:translate(0,0);opacity:0}18%{opacity:.85}'
|
|
2342
|
+
+ '100%{transform:translate(1.2em,-2.6em);opacity:0}}',
|
|
2343
|
+
'@keyframes dsh-amb-hz-glow{from{opacity:.45}to{opacity:.75}}',
|
|
2344
|
+
/* The fluff crosses the whole band and dies at the far edge; its vertical
|
|
2345
|
+
wobble stays within ±1.2em so a low-seeded puff never reaches the clip. */
|
|
2346
|
+
'@keyframes dsh-amb-hz-fluff{0%{transform:translate(0,0);opacity:0}7%{opacity:.95}'
|
|
2347
|
+
+ '30%{transform:translate(8em,-1.2em)}55%{transform:translate(14em,.7em)}'
|
|
2348
|
+
+ '80%{transform:translate(20em,-.9em);opacity:.75}96%{opacity:.4}'
|
|
2349
|
+
+ '100%{transform:translate(26em,0);opacity:0}}',
|
|
2350
|
+
'@keyframes dsh-amb-hz-wag{from{transform:rotate(-9deg)}to{transform:rotate(11deg)}}',
|
|
2351
|
+
'@keyframes dsh-amb-hz-grass{from{transform:rotate(-2.2deg)}to{transform:rotate(2.6deg)}}',
|
|
2352
|
+
'@keyframes dsh-amb-hz-tilt{0%,40%,100%{transform:rotate(0)}55%{transform:rotate(2.4deg)}75%{transform:rotate(-1.4deg)}}',
|
|
2353
|
+
|
|
1613
2354
|
/* Respect a user who has asked the system for less motion: the scenery
|
|
1614
2355
|
stays, the movement does not. */
|
|
1615
2356
|
'@media (prefers-reduced-motion:reduce){#dsh-theme-ambient *{animation:none!important}}',
|
|
@@ -1619,7 +2360,7 @@ window.__ModuleLoader__.load({
|
|
|
1619
2360
|
const zh = {
|
|
1620
2361
|
title: '主题皮肤',
|
|
1621
2362
|
hint: '点一下即切换,选中会记入设置',
|
|
1622
|
-
count: '{count}
|
|
2363
|
+
count: '{count} 款皮肤(另有内置浅色/深色两张卡)',
|
|
1623
2364
|
current: '当前',
|
|
1624
2365
|
applied: '已应用',
|
|
1625
2366
|
empty: '正在读取官方主题注册表…',
|
|
@@ -1631,7 +2372,7 @@ window.__ModuleLoader__.load({
|
|
|
1631
2372
|
const en = {
|
|
1632
2373
|
title: 'Theme skins',
|
|
1633
2374
|
hint: 'Click to switch; the choice is remembered',
|
|
1634
|
-
count: '{count}
|
|
2375
|
+
count: '{count} skins (+ built-in light/dark cards)',
|
|
1635
2376
|
current: 'Current',
|
|
1636
2377
|
applied: 'Applied',
|
|
1637
2378
|
empty: 'Reading the theme registry…',
|
|
@@ -1711,10 +2452,11 @@ window.__ModuleLoader__.load({
|
|
|
1711
2452
|
// localizes them from its own dictionaries — so name them here
|
|
1712
2453
|
// rather than showing a bare `light` / `dark` id.
|
|
1713
2454
|
labels[theme.id] = theme.label || BUILT_IN_LABELS[theme.id] || theme.id
|
|
1714
|
-
// A built-in theme derives its colours from the base palette rather
|
|
1715
|
-
//
|
|
1716
|
-
|
|
1717
|
-
|
|
2455
|
+
// A built-in theme derives its colours from the base palette rather than
|
|
2456
|
+
// declaring tokens, so its card gets a plain line — and it carries no
|
|
2457
|
+
// description of its own, so the copy for the built-in cards is the
|
|
2458
|
+
// gallery's own (`BUILT_IN_DESCRIPTIONS`).
|
|
2459
|
+
descriptions[theme.id] = theme.description || BUILT_IN_DESCRIPTIONS[theme.id] || ''
|
|
1718
2460
|
// A card's colour strip comes from the theme's own tokens, so a
|
|
1719
2461
|
// theme authored anywhere shows its identity without extra metadata.
|
|
1720
2462
|
const tokens = theme.tokens || {}
|
|
@@ -3279,6 +4021,328 @@ window.__ModuleLoader__.load({
|
|
|
3279
4021
|
+ '</div>'
|
|
3280
4022
|
}
|
|
3281
4023
|
|
|
4024
|
+
/**
|
|
4025
|
+
* One sun-warmed haze for the two ORIGINAL pet scenes (琥珀猫咪 / 虎子阿黄).
|
|
4026
|
+
*
|
|
4027
|
+
* Both scenes open with the same soft light pool at the band's top, built the way
|
|
4028
|
+
* dream's corner glow was taught to behave: it starts AT the edge, is dimmer than
|
|
4029
|
+
* the source skies, and is masked to zero at its top so the scene box's
|
|
4030
|
+
* `overflow:hidden` clip never cuts a hard line against the un-lit sidebar above.
|
|
4031
|
+
* @param tint - the rgba() fill of the haze, in the theme's own family.
|
|
4032
|
+
* @param keyframe - the pulse animation, `dsh-amb-hm-glow` or `dsh-amb-hz-glow`.
|
|
4033
|
+
* @returns the haze markup.
|
|
4034
|
+
*/
|
|
4035
|
+
function petHazeMarkup(tint, keyframe) {
|
|
4036
|
+
return open('div', {
|
|
4037
|
+
class: 'pet-haze',
|
|
4038
|
+
style: `position:absolute;top:0;left:-20%;width:130%;height:52%;z-index:2;`
|
|
4039
|
+
+ `background:radial-gradient(ellipse at 30% 0%,${tint} 0%,rgba(255,236,200,0) 64%);`
|
|
4040
|
+
+ 'filter:blur(10px);'
|
|
4041
|
+
+ '-webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 42%);'
|
|
4042
|
+
+ 'mask-image:linear-gradient(to bottom,transparent 0,#000 42%);'
|
|
4043
|
+
+ `animation:${keyframe} 9s ease-in-out infinite alternate`,
|
|
4044
|
+
}) + '</div>'
|
|
4045
|
+
}
|
|
4046
|
+
|
|
4047
|
+
/**
|
|
4048
|
+
* The 琥珀猫咪 scene: a warm haze, a sunlit sill, TWO tabby shorthairs —
|
|
4049
|
+
* one sitting with a slow tail sway and the odd ear twitch, one curled asleep,
|
|
4050
|
+
* breathing, with tiny z's drifting off it — and amber dust motes floating
|
|
4051
|
+
* through the light.
|
|
4052
|
+
*
|
|
4053
|
+
* This is the first ORIGINAL scene in the pack (not ported from a source
|
|
4054
|
+
* system), drawn in the pack's own idiom: inline geometry in % / em, flat
|
|
4055
|
+
* fills from one warm amber family, the count knob seeding the dust, and
|
|
4056
|
+
* every animated transform bounded below the band's top edge — the sleeping
|
|
4057
|
+
* cat breathes from its base and the z's fade out well before they could
|
|
4058
|
+
* reach the clip.
|
|
4059
|
+
* @param dust - how many dust motes to seed.
|
|
4060
|
+
* @returns the scene markup.
|
|
4061
|
+
*/
|
|
4062
|
+
function humaoAmbientScene(dust) {
|
|
4063
|
+
const count = Math.max(0, Math.min(20, dust ?? 6))
|
|
4064
|
+
let dustNodes = ''
|
|
4065
|
+
for (let n = 1; n <= count; n += 1) {
|
|
4066
|
+
const size = 2.5 + ((n * 5) % 4)
|
|
4067
|
+
dustNodes += open('div', {
|
|
4068
|
+
class: 'hm-dust',
|
|
4069
|
+
style: 'position:absolute;border-radius:50%;'
|
|
4070
|
+
+ `left:${((n * 19) % 52) + 22}%;top:${((n * 13) % 24) + 36}%;`
|
|
4071
|
+
+ `width:${size}px;height:${size}px;`
|
|
4072
|
+
+ 'background:radial-gradient(circle,rgba(255,236,200,.95) 0%,rgba(255,214,140,0) 100%);'
|
|
4073
|
+
+ 'box-shadow:0 0 6px rgba(255,214,140,.75);'
|
|
4074
|
+
+ `animation:dsh-amb-hm-dust ${9 + ((n * 7) % 6)}s linear infinite;`
|
|
4075
|
+
+ `animation-delay:${-(n * 2.3)}s`,
|
|
4076
|
+
}) + '</div>'
|
|
4077
|
+
}
|
|
4078
|
+
|
|
4079
|
+
// 猫 A —— 坐姿虎斑,面朝左(第一版形态:地面绕尾、橄榄形上身、后侧三道
|
|
4080
|
+
// 弧纹)。连接方式按用户指定:**不垫任何脖子形状**,头部整组(头圆 + 双耳
|
|
4081
|
+
// + 五官 + 胡须 + 额头纹)作为整体下移 28、右移 2,头圆下缘直接压进身体
|
|
4082
|
+
// 上缘约 7.5 个单位(肩点 (52,52) 距头心 14.1 < r15,也埋进头圆),同色
|
|
4083
|
+
// 填充使接缝不可见;下巴与胸口之间留下的小凹角正是猫下巴该有的位置。
|
|
4084
|
+
// 奶油色胸部椭圆按用户要求落在 (39,76)。
|
|
4085
|
+
const catSit = open('svg', {
|
|
4086
|
+
viewBox: '0 0 100 118',
|
|
4087
|
+
style: 'display:block;width:100%;height:auto;overflow:visible',
|
|
4088
|
+
})
|
|
4089
|
+
+ open('g', {
|
|
4090
|
+
class: 'hm-tail',
|
|
4091
|
+
style: 'transform-box:fill-box;transform-origin:0% 100%;'
|
|
4092
|
+
+ 'animation:dsh-amb-hm-tail 5.5s ease-in-out infinite alternate',
|
|
4093
|
+
})
|
|
4094
|
+
+ open('path', {
|
|
4095
|
+
d: 'M62 112 C82 115 90 103 85 90 C82 82 74 80 70 84',
|
|
4096
|
+
fill: 'none', stroke: '#C98B4B', 'stroke-width': '7', 'stroke-linecap': 'round',
|
|
4097
|
+
}) + '</path>'
|
|
4098
|
+
+ open('path', {
|
|
4099
|
+
d: 'M86 92 C83 84 76 81 71 84',
|
|
4100
|
+
fill: 'none', stroke: '#8A5A2B', 'stroke-width': '7', 'stroke-linecap': 'round',
|
|
4101
|
+
}) + '</path>'
|
|
4102
|
+
+ '</g>'
|
|
4103
|
+
+ open('path', {
|
|
4104
|
+
d: 'M30 116 C26 106 26 92 30 78 C34 64 42 56 52 52 C62 48 70 56 72 70 C74 86 72 104 66 116 Z',
|
|
4105
|
+
fill: '#D9A05B',
|
|
4106
|
+
}) + '</path>'
|
|
4107
|
+
+ open('ellipse', { cx: '39', cy: '76', rx: '10', ry: '19', fill: '#F0D6A6' }) + '</ellipse>'
|
|
4108
|
+
+ open('rect', { x: '32', y: '88', width: '6', height: '28', rx: '3', fill: '#E5BC7E', stroke: '#C98B4B', 'stroke-width': '1' }) + '</rect>'
|
|
4109
|
+
+ open('rect', { x: '42', y: '88', width: '6', height: '28', rx: '3', fill: '#E5BC7E', stroke: '#C98B4B', 'stroke-width': '1' }) + '</rect>'
|
|
4110
|
+
+ open('path', { d: 'M56 54 C64 58 68 64 70 72', fill: 'none', stroke: '#8A5A2B', 'stroke-width': '4', 'stroke-linecap': 'round', opacity: '0.85' }) + '</path>'
|
|
4111
|
+
+ open('path', { d: 'M60 74 C66 78 69 84 70 90', fill: 'none', stroke: '#8A5A2B', 'stroke-width': '4', 'stroke-linecap': 'round', opacity: '0.85' }) + '</path>'
|
|
4112
|
+
+ open('path', { d: 'M58 96 C63 99 66 104 67 110', fill: 'none', stroke: '#8A5A2B', 'stroke-width': '4', 'stroke-linecap': 'round', opacity: '0.85' }) + '</path>'
|
|
4113
|
+
+ open('path', { d: 'M34 40 L34 46 M38 39 L38 45 M30 41 L30 47', stroke: '#8A5A2B', 'stroke-width': '2', 'stroke-linecap': 'round' }) + '</path>'
|
|
4114
|
+
+ open('path', { d: 'M26 46 L23 31 L35 39 Z', fill: '#D9A05B' }) + '</path>'
|
|
4115
|
+
+ open('path', { d: 'M27.5 43 L26 35 L33 40 Z', fill: '#C97B6B', opacity: '0.75' }) + '</path>'
|
|
4116
|
+
+ open('g', {
|
|
4117
|
+
class: 'hm-ear',
|
|
4118
|
+
style: 'transform-box:fill-box;transform-origin:0% 100%;'
|
|
4119
|
+
+ 'animation:dsh-amb-hm-ear 7s ease-in-out infinite',
|
|
4120
|
+
})
|
|
4121
|
+
+ open('path', { d: 'M44 42 L52 30 L50 44 Z', fill: '#D9A05B' }) + '</path>'
|
|
4122
|
+
+ open('path', { d: 'M46 40.5 L50.5 33.5 L48.5 41.5 Z', fill: '#C97B6B', opacity: '0.75' }) + '</path>'
|
|
4123
|
+
+ '</g>'
|
|
4124
|
+
+ open('circle', { cx: '38', cy: '54', r: '15', fill: '#D9A05B' }) + '</circle>'
|
|
4125
|
+
+ open('circle', { cx: '32', cy: '52', r: '2.2', fill: '#3B2A16' }) + '</circle>'
|
|
4126
|
+
+ open('circle', { cx: '44', cy: '52', r: '2.2', fill: '#3B2A16' }) + '</circle>'
|
|
4127
|
+
+ open('circle', { cx: '32.7', cy: '51.2', r: '0.7', fill: '#FFFFFF' }) + '</circle>'
|
|
4128
|
+
+ open('circle', { cx: '44.7', cy: '51.2', r: '0.7', fill: '#FFFFFF' }) + '</circle>'
|
|
4129
|
+
+ open('path', { d: 'M36 58 L40 58 L38 61 Z', fill: '#C97B6B' }) + '</path>'
|
|
4130
|
+
+ open('path', { d: 'M38 61 C38 63 36 64 34 64 M38 61 C38 63 40 64 42 64', fill: 'none', stroke: '#8A5A2B', 'stroke-width': '1', 'stroke-linecap': 'round' }) + '</path>'
|
|
4131
|
+
+ open('path', { d: 'M26 57 L14 55 M26 60 L14 62 M52 57 L62 55 M52 60 L62 62', stroke: '#B98A55', 'stroke-width': '1', 'stroke-linecap': 'round', opacity: '0.8' }) + '</path>'
|
|
4132
|
+
+ '</svg>'
|
|
4133
|
+
|
|
4134
|
+
// 猫 B —— 蜷成一团酣睡:圆头明显叠在身子右前侧(同色相接),折耳 +
|
|
4135
|
+
// 闭眼 + 鼻尖胡须 + 身前露出一只奶油色前爪,尾巴沿身前绕一圈、
|
|
4136
|
+
// 深色尾尖收在左侧;整体随呼吸微微起伏,头顶冒出两枚会消散的小 z。
|
|
4137
|
+
const catCurled = open('svg', {
|
|
4138
|
+
viewBox: '0 0 112 66',
|
|
4139
|
+
style: 'display:block;width:100%;height:auto;overflow:visible',
|
|
4140
|
+
})
|
|
4141
|
+
+ open('g', {
|
|
4142
|
+
class: 'hm-breathe',
|
|
4143
|
+
style: 'transform-box:fill-box;transform-origin:50% 100%;'
|
|
4144
|
+
+ 'animation:dsh-amb-hm-breathe 3.8s ease-in-out infinite',
|
|
4145
|
+
})
|
|
4146
|
+
+ open('path', {
|
|
4147
|
+
d: 'M6 62 C6 44 24 28 52 27 C80 26 100 40 99 53 C98 60 92 62 84 62 Z',
|
|
4148
|
+
fill: '#D9A05B',
|
|
4149
|
+
}) + '</path>'
|
|
4150
|
+
+ open('path', { d: 'M26 30 C28 38 28 48 26 57', fill: 'none', stroke: '#8A5A2B', 'stroke-width': '4', 'stroke-linecap': 'round', opacity: '0.8' }) + '</path>'
|
|
4151
|
+
+ open('path', { d: 'M38 28 C40 37 40 49 38 59', fill: 'none', stroke: '#8A5A2B', 'stroke-width': '4', 'stroke-linecap': 'round', opacity: '0.8' }) + '</path>'
|
|
4152
|
+
+ open('path', { d: 'M50 27 C52 36 52 47 51 56', fill: 'none', stroke: '#8A5A2B', 'stroke-width': '4', 'stroke-linecap': 'round', opacity: '0.8' }) + '</path>'
|
|
4153
|
+
+ open('path', { d: 'M6 58 C26 66 62 67 92 59', fill: 'none', stroke: '#C98B4B', 'stroke-width': '6', 'stroke-linecap': 'round' }) + '</path>'
|
|
4154
|
+
+ open('path', { d: 'M6 58 C12 61 18 63 24 64', fill: 'none', stroke: '#8A5A2B', 'stroke-width': '6', 'stroke-linecap': 'round' }) + '</path>'
|
|
4155
|
+
+ open('circle', { cx: '80', cy: '44', r: '15', fill: '#D9A05B' }) + '</circle>'
|
|
4156
|
+
+ open('path', { d: 'M70 33 L65 23 L75.5 28.5 Z', fill: '#D9A05B' }) + '</path>'
|
|
4157
|
+
+ open('path', { d: 'M88 31 L94 22 L95.5 32 Z', fill: '#D9A05B' }) + '</path>'
|
|
4158
|
+
+ open('path', { d: 'M70.5 30.5 L68 25 L73.5 28 Z', fill: '#C97B6B', opacity: '0.75' }) + '</path>'
|
|
4159
|
+
+ open('path', { d: 'M88.5 29.5 L92.5 24 L93.5 30.5 Z', fill: '#C97B6B', opacity: '0.75' }) + '</path>'
|
|
4160
|
+
+ open('path', { d: 'M72 45 Q75 48 78 45', fill: 'none', stroke: '#8A5A2B', 'stroke-width': '1.4', 'stroke-linecap': 'round' }) + '</path>'
|
|
4161
|
+
+ open('path', { d: 'M65.5 46.5 L69 46.5 L67.25 49.5 Z', fill: '#C97B6B' }) + '</path>'
|
|
4162
|
+
+ open('path', { d: 'M62 48.5 L54 47.5 M62 51 L55 52.5', stroke: '#B98A55', 'stroke-width': '1', 'stroke-linecap': 'round', opacity: '0.8' }) + '</path>'
|
|
4163
|
+
+ open('ellipse', { cx: '60', cy: '60', rx: '7', ry: '3.5', fill: '#F0D6A6', stroke: '#C98B4B', 'stroke-width': '1' }) + '</ellipse>'
|
|
4164
|
+
+ '</g>'
|
|
4165
|
+
+ '</svg>'
|
|
4166
|
+
+ open('span', {
|
|
4167
|
+
class: 'hm-z hm-z-1',
|
|
4168
|
+
style: 'position:absolute;right:12%;top:-12%;font-size:.85em;font-style:italic;'
|
|
4169
|
+
+ "font-family:Georgia,'Times New Roman',serif;color:#A97F4C;"
|
|
4170
|
+
+ 'animation:dsh-amb-hm-zzz 5s ease-in-out infinite',
|
|
4171
|
+
}) + 'z</span>'
|
|
4172
|
+
+ open('span', {
|
|
4173
|
+
class: 'hm-z hm-z-2',
|
|
4174
|
+
style: 'position:absolute;right:5%;top:-22%;font-size:.65em;font-style:italic;'
|
|
4175
|
+
+ "font-family:Georgia,'Times New Roman',serif;color:#B98F55;"
|
|
4176
|
+
+ 'animation:dsh-amb-hm-zzz 5s ease-in-out infinite;animation-delay:-2.5s',
|
|
4177
|
+
}) + 'z</span>'
|
|
4178
|
+
|
|
4179
|
+
return open('div', { class: 'hm', style: 'position:absolute;inset:0;display:block' })
|
|
4180
|
+
+ petHazeMarkup('rgba(255,214,140,.5)', 'dsh-amb-hm-glow')
|
|
4181
|
+
+ open('div', {
|
|
4182
|
+
class: 'hm-sill',
|
|
4183
|
+
style: 'position:absolute;left:0;right:0;bottom:0;height:12%;z-index:4;'
|
|
4184
|
+
+ 'background:linear-gradient(to bottom,rgba(154,95,36,0) 0%,rgba(154,95,36,.16) 40%,rgba(138,84,30,.3) 100%)',
|
|
4185
|
+
})
|
|
4186
|
+
+ open('div', {
|
|
4187
|
+
class: 'hm-sill-line',
|
|
4188
|
+
style: 'position:absolute;top:0;left:0;right:0;height:1.2px;opacity:.55;'
|
|
4189
|
+
+ 'background:linear-gradient(90deg,transparent,rgba(255,244,220,.9),transparent)',
|
|
4190
|
+
}) + '</div>'
|
|
4191
|
+
+ '</div>'
|
|
4192
|
+
+ open('div', {
|
|
4193
|
+
class: 'hm-cat-sit',
|
|
4194
|
+
style: 'position:absolute;left:7%;bottom:9%;width:7.5em;z-index:6',
|
|
4195
|
+
}) + catSit + '</div>'
|
|
4196
|
+
+ open('div', {
|
|
4197
|
+
class: 'hm-cat-curled',
|
|
4198
|
+
style: 'position:absolute;right:6%;bottom:7%;width:8.4em;z-index:6',
|
|
4199
|
+
}) + catCurled + '</div>'
|
|
4200
|
+
+ dustNodes
|
|
4201
|
+
+ '</div>'
|
|
4202
|
+
}
|
|
4203
|
+
|
|
4204
|
+
/**
|
|
4205
|
+
* The 虎子阿黄 scene: a golden haze, a field-path bank, the yellow Chinese
|
|
4206
|
+
* pastoral dog himself — upright ears, a sickle tail wagging at speed, an
|
|
4207
|
+
* occasional curious head tilt, a straw collar with a bell — with dry
|
|
4208
|
+
* grass tufts, a ball in the theme's own 缃色, and dandelion fluff drifting
|
|
4209
|
+
* across on the wind.
|
|
4210
|
+
*
|
|
4211
|
+
* The second ORIGINAL scene in the pack, same idiom as the cat: inline
|
|
4212
|
+
* % / em geometry, one warm wheat-gold family, and every animated transform
|
|
4213
|
+
* bounded below the band's top edge — the fluff fades out at the far edge
|
|
4214
|
+
* instead of clipping.
|
|
4215
|
+
* @param dust - how many dandelion-fluff puffs to seed.
|
|
4216
|
+
* @returns the scene markup.
|
|
4217
|
+
*/
|
|
4218
|
+
function ahuangAmbientScene(dust) {
|
|
4219
|
+
const count = Math.max(0, Math.min(20, dust ?? 5))
|
|
4220
|
+
let fluffNodes = ''
|
|
4221
|
+
for (let n = 1; n <= count; n += 1) {
|
|
4222
|
+
const size = 4 + ((n * 3) % 3)
|
|
4223
|
+
fluffNodes += open('div', {
|
|
4224
|
+
class: 'hz-fluff',
|
|
4225
|
+
style: 'position:absolute;border-radius:50%;'
|
|
4226
|
+
+ `left:-6%;top:${((n * 11) % 30) + 18}%;`
|
|
4227
|
+
+ `width:${size}px;height:${size}px;`
|
|
4228
|
+
+ 'background:radial-gradient(circle,#FFFDF4 0%,rgba(255,246,214,.85) 55%,rgba(255,246,214,0) 100%);'
|
|
4229
|
+
+ 'box-shadow:0 0 5px rgba(255,244,200,.8);'
|
|
4230
|
+
+ `animation:dsh-amb-hz-fluff ${15 + ((n * 7) % 8)}s linear infinite;`
|
|
4231
|
+
+ `animation-delay:${-(n * 2.6)}s`,
|
|
4232
|
+
}) + '</div>'
|
|
4233
|
+
}
|
|
4234
|
+
|
|
4235
|
+
// 虎子阿黄 —— 坐姿面朝左。头与躯干同样按「必须重叠」构造:头部圆下缘
|
|
4236
|
+
// 压进胸腔椭圆上缘 3 个单位,项圈画在重叠带上把脖子束出来;立耳、
|
|
4237
|
+
// 镰刀尾(尾根埋进后臀圆里)快速摇摆、偶尔歪头,吐着舌头等主人。
|
|
4238
|
+
const dog = open('svg', {
|
|
4239
|
+
viewBox: '0 0 112 122',
|
|
4240
|
+
style: 'display:block;width:100%;height:auto;overflow:visible',
|
|
4241
|
+
})
|
|
4242
|
+
+ open('g', {
|
|
4243
|
+
class: 'hz-tail',
|
|
4244
|
+
style: 'transform-box:fill-box;transform-origin:0% 100%;'
|
|
4245
|
+
+ 'animation:dsh-amb-hz-wag .95s ease-in-out infinite alternate',
|
|
4246
|
+
})
|
|
4247
|
+
+ open('path', {
|
|
4248
|
+
d: 'M76 70 C92 66 100 50 92 36 C88 30 82 29 78 32',
|
|
4249
|
+
fill: 'none', stroke: '#D8A44E', 'stroke-width': '8', 'stroke-linecap': 'round',
|
|
4250
|
+
}) + '</path>'
|
|
4251
|
+
+ open('path', {
|
|
4252
|
+
d: 'M93 37 C90 32 85 30 79 32',
|
|
4253
|
+
fill: 'none', stroke: '#B97F3A', 'stroke-width': '8', 'stroke-linecap': 'round',
|
|
4254
|
+
}) + '</path>'
|
|
4255
|
+
+ '</g>'
|
|
4256
|
+
+ open('circle', { cx: '72', cy: '94', r: '26', fill: '#E8B45A' }) + '</circle>'
|
|
4257
|
+
+ open('ellipse', { cx: '44', cy: '93', rx: '18', ry: '27', fill: '#E8B45A' }) + '</ellipse>'
|
|
4258
|
+
+ open('ellipse', { cx: '56', cy: '117', rx: '13', ry: '5', fill: '#E8B45A' }) + '</ellipse>'
|
|
4259
|
+
+ open('ellipse', { cx: '42', cy: '97', rx: '10', ry: '19', fill: '#F6E3B8' }) + '</ellipse>'
|
|
4260
|
+
+ open('rect', { x: '32', y: '92', width: '7.5', height: '26', rx: '3.7', fill: '#E8B45A', stroke: '#C98F35', 'stroke-width': '1' }) + '</rect>'
|
|
4261
|
+
+ open('rect', { x: '44', y: '92', width: '7.5', height: '26', rx: '3.7', fill: '#E8B45A', stroke: '#C98F35', 'stroke-width': '1' }) + '</rect>'
|
|
4262
|
+
+ open('ellipse', { cx: '35.7', cy: '117', rx: '6', ry: '3.2', fill: '#F6E3B8' }) + '</ellipse>'
|
|
4263
|
+
+ open('ellipse', { cx: '47.7', cy: '117', rx: '6', ry: '3.2', fill: '#F6E3B8' }) + '</ellipse>'
|
|
4264
|
+
+ open('path', { d: 'M31 68 C38 74 50 74 56 66', fill: 'none', stroke: '#896C39', 'stroke-width': '5', 'stroke-linecap': 'round' }) + '</path>'
|
|
4265
|
+
+ open('circle', { cx: '33', cy: '72.5', r: '2.6', fill: '#F0C239', stroke: '#B98A20', 'stroke-width': '0.8' }) + '</circle>'
|
|
4266
|
+
+ open('path', { d: 'M30.6 72.5 L35.4 72.5', stroke: '#B98A20', 'stroke-width': '0.8' }) + '</path>'
|
|
4267
|
+
+ open('g', {
|
|
4268
|
+
class: 'hz-head',
|
|
4269
|
+
style: 'transform-box:fill-box;transform-origin:60% 88%;'
|
|
4270
|
+
+ 'animation:dsh-amb-hz-tilt 7s ease-in-out infinite',
|
|
4271
|
+
})
|
|
4272
|
+
+ open('path', { d: 'M28 40 C24 30 23 23 26 18 C30 21 34 28 35.5 36 Z', fill: '#D8A44E' }) + '</path>'
|
|
4273
|
+
+ open('path', { d: 'M48 38 C50 28 52 21 56 18 C58 23 56 30 52.5 38.5 Z', fill: '#D8A44E' }) + '</path>'
|
|
4274
|
+
+ open('path', { d: 'M29 36 C27 29 26.5 25 28 21.5 C30.5 24.5 32.5 29 33.5 34 Z', fill: '#C97B6B', opacity: '0.7' }) + '</path>'
|
|
4275
|
+
+ open('path', { d: 'M49.5 34 C51 27.5 52.5 23.5 55 21 C56 25 54.5 30 52 35.5 Z', fill: '#C97B6B', opacity: '0.7' }) + '</path>'
|
|
4276
|
+
+ open('circle', { cx: '42', cy: '51', r: '18', fill: '#E8B45A' }) + '</circle>'
|
|
4277
|
+
+ open('ellipse', { cx: '27', cy: '57', rx: '11', ry: '8.5', fill: '#F6E3B8' }) + '</ellipse>'
|
|
4278
|
+
+ open('ellipse', { cx: '19', cy: '53.5', rx: '3', ry: '2.4', fill: '#4A331C' }) + '</ellipse>'
|
|
4279
|
+
+ open('circle', { cx: '44', cy: '47', r: '2.4', fill: '#3B2A16' }) + '</circle>'
|
|
4280
|
+
+ open('circle', { cx: '44.8', cy: '46.2', r: '0.8', fill: '#FFFFFF' }) + '</circle>'
|
|
4281
|
+
+ open('path', { d: 'M22.5 61 C24.5 64.5 29.5 65.5 32.5 62.5', fill: 'none', stroke: '#8A5F2B', 'stroke-width': '1.3', 'stroke-linecap': 'round' }) + '</path>'
|
|
4282
|
+
+ open('path', { d: 'M24 62.5 C28 63.5 29 67.5 27 70 C25 72 22 71 22 68.5 C22 66 22.5 64 24 62.5 Z', fill: '#DE7E68' }) + '</path>'
|
|
4283
|
+
+ open('path', { d: 'M24.5 64 C25.5 65.5 25.8 67.5 25.2 69.5', fill: 'none', stroke: '#C05E4E', 'stroke-width': '0.9', 'stroke-linecap': 'round' }) + '</path>'
|
|
4284
|
+
+ '</g>'
|
|
4285
|
+
+ '</svg>'
|
|
4286
|
+
|
|
4287
|
+
const grassTuft = (left, bottom, width, zIndex) => open('div', {
|
|
4288
|
+
class: 'hz-grass',
|
|
4289
|
+
style: `position:absolute;${left};bottom:${bottom};width:${width};z-index:${zIndex}`,
|
|
4290
|
+
})
|
|
4291
|
+
+ open('svg', {
|
|
4292
|
+
viewBox: '0 0 60 30',
|
|
4293
|
+
style: 'display:block;width:100%;height:auto;overflow:visible',
|
|
4294
|
+
})
|
|
4295
|
+
+ open('g', {
|
|
4296
|
+
class: 'hz-blades',
|
|
4297
|
+
style: 'transform-box:fill-box;transform-origin:50% 100%;'
|
|
4298
|
+
+ 'animation:dsh-amb-hz-grass 5.2s ease-in-out infinite alternate',
|
|
4299
|
+
})
|
|
4300
|
+
+ open('path', { d: 'M6 30 C4 22 6 14 10 8', fill: 'none', stroke: '#C2A24E', 'stroke-width': '2.6', 'stroke-linecap': 'round' }) + '</path>'
|
|
4301
|
+
+ open('path', { d: 'M14 30 C13 20 16 12 20 6', fill: 'none', stroke: '#B08F3E', 'stroke-width': '2.4', 'stroke-linecap': 'round' }) + '</path>'
|
|
4302
|
+
+ open('path', { d: 'M24 30 C24 20 27 12 32 7', fill: 'none', stroke: '#C2A24E', 'stroke-width': '2.2', 'stroke-linecap': 'round' }) + '</path>'
|
|
4303
|
+
+ open('path', { d: 'M36 30 C38 22 42 15 48 10', fill: 'none', stroke: '#B08F3E', 'stroke-width': '2.4', 'stroke-linecap': 'round' }) + '</path>'
|
|
4304
|
+
+ open('path', { d: 'M46 30 C48 24 52 18 56 14', fill: 'none', stroke: '#C2A24E', 'stroke-width': '2.2', 'stroke-linecap': 'round' }) + '</path>'
|
|
4305
|
+
+ '</g>'
|
|
4306
|
+
+ '</svg>'
|
|
4307
|
+
+ '</div>'
|
|
4308
|
+
|
|
4309
|
+
return open('div', { class: 'hz', style: 'position:absolute;inset:0;display:block' })
|
|
4310
|
+
+ petHazeMarkup('rgba(240,194,57,.42)', 'dsh-amb-hz-glow')
|
|
4311
|
+
+ open('div', {
|
|
4312
|
+
class: 'hz-bank',
|
|
4313
|
+
style: 'position:absolute;left:0;right:0;bottom:0;height:14%;z-index:4;'
|
|
4314
|
+
+ 'background:linear-gradient(to bottom,rgba(137,108,57,0) 0%,rgba(137,108,57,.16) 40%,rgba(120,92,44,.3) 100%)',
|
|
4315
|
+
})
|
|
4316
|
+
+ open('div', {
|
|
4317
|
+
class: 'hz-bank-line',
|
|
4318
|
+
style: 'position:absolute;top:0;left:0;right:0;height:1.2px;opacity:.5;'
|
|
4319
|
+
+ 'background:linear-gradient(90deg,transparent,rgba(255,250,228,.9),transparent)',
|
|
4320
|
+
}) + '</div>'
|
|
4321
|
+
+ '</div>'
|
|
4322
|
+
+ grassTuft('left:2%', '0', '4.5em', '5')
|
|
4323
|
+
+ grassTuft('left:17%', '1%', '3.6em', '7')
|
|
4324
|
+
+ grassTuft('right:3%', '0', '4.2em', '5')
|
|
4325
|
+
+ open('div', {
|
|
4326
|
+
class: 'hz-ball',
|
|
4327
|
+
style: 'position:absolute;left:14%;bottom:8%;width:1.7em;z-index:6',
|
|
4328
|
+
})
|
|
4329
|
+
+ open('svg', {
|
|
4330
|
+
viewBox: '0 0 20 20',
|
|
4331
|
+
style: 'display:block;width:100%;height:auto',
|
|
4332
|
+
})
|
|
4333
|
+
+ open('circle', { cx: '10', cy: '10', r: '9', fill: '#F0C239', stroke: '#C9A02F', 'stroke-width': '1' }) + '</circle>'
|
|
4334
|
+
+ open('path', { d: 'M4 4 C8 8 8 12 4 16 M16 4 C12 8 12 12 16 16', fill: 'none', stroke: '#B98A20', 'stroke-width': '1.2', 'stroke-linecap': 'round' }) + '</path>'
|
|
4335
|
+
+ open('ellipse', { cx: '7', cy: '6.5', rx: '2.6', ry: '1.6', fill: '#FFF3C4', opacity: '0.85' }) + '</ellipse>'
|
|
4336
|
+
+ '</svg>'
|
|
4337
|
+
+ '</div>'
|
|
4338
|
+
+ open('div', {
|
|
4339
|
+
class: 'hz-dog',
|
|
4340
|
+
style: 'position:absolute;left:32%;bottom:7%;width:8em;z-index:6',
|
|
4341
|
+
}) + dog + '</div>'
|
|
4342
|
+
+ fluffNodes
|
|
4343
|
+
+ '</div>'
|
|
4344
|
+
}
|
|
4345
|
+
|
|
3282
4346
|
/**
|
|
3283
4347
|
* Apply a function on every tick until the thing it observes stops changing.
|
|
3284
4348
|
*
|
|
@@ -3369,7 +4433,11 @@ window.__ModuleLoader__.load({
|
|
|
3369
4433
|
? jiexinAmbientScene(options?.dust)
|
|
3370
4434
|
: kind === 'fengchen'
|
|
3371
4435
|
? fengchenAmbientScene(options?.feathers, options?.dew)
|
|
3372
|
-
: ''
|
|
4436
|
+
: kind === 'humao'
|
|
4437
|
+
? humaoAmbientScene(options?.dust)
|
|
4438
|
+
: kind === 'ahuang'
|
|
4439
|
+
? ahuangAmbientScene(options?.dust)
|
|
4440
|
+
: ''
|
|
3373
4441
|
// The builder is fed only attributes and hex values, but this is the one place
|
|
3374
4442
|
// markup from outside this file could ever arrive, so it refuses anyway.
|
|
3375
4443
|
if (/<script|\son[a-z]+\s*=/i.test(markup)) {
|
|
@@ -3731,11 +4799,12 @@ window.__ModuleLoader__.load({
|
|
|
3731
4799
|
}
|
|
3732
4800
|
|
|
3733
4801
|
/**
|
|
3734
|
-
* Remove every ambient
|
|
4802
|
+
* Remove every legacy ambient SEAT, ownership aside.
|
|
3735
4803
|
*
|
|
3736
|
-
*
|
|
3737
|
-
*
|
|
3738
|
-
* theme
|
|
4804
|
+
* Despite the name it does NOT touch the live `.dsh-amb-control` layer: that layer is
|
|
4805
|
+
* owned by `drawScene`, and it is only rewritten when a theme actually has scenery. So
|
|
4806
|
+
* this call is safe on the "theme has no scenery" path, where leaving the layer in
|
|
4807
|
+
* place is the INTENDED behaviour — see the note in `syncAmbient`.
|
|
3739
4808
|
*/
|
|
3740
4809
|
function removeAllAmbientSeats() {
|
|
3741
4810
|
if (typeof document === 'undefined') return
|
|
@@ -3858,10 +4927,21 @@ window.__ModuleLoader__.load({
|
|
|
3858
4927
|
/**
|
|
3859
4928
|
* Render the active theme's scenery into the sidebar column.
|
|
3860
4929
|
*
|
|
3861
|
-
* Idempotent: the
|
|
3862
|
-
*
|
|
3863
|
-
*
|
|
3864
|
-
*
|
|
4930
|
+
* Idempotent: the scene box is reused and its content is replaced only when the markup
|
|
4931
|
+
* changes, so re-running never stacks duplicate scenes (two copies of a scene = two of
|
|
4932
|
+
* every animated element, which is what produced the duplicated dragonflies).
|
|
4933
|
+
*
|
|
4934
|
+
* Two different "no scenery" cases, and they are NOT the same:
|
|
4935
|
+
*
|
|
4936
|
+
* · switching to ANOTHER skin — the markup differs, so the box is repainted with the
|
|
4937
|
+
* new skin's artwork;
|
|
4938
|
+
* · switching to a theme that has no `ambient` at all — the BUILT-IN 浅色 / 深色 cards.
|
|
4939
|
+
* Only the legacy seats are swept; the live `.dsh-amb-control` layer is deliberately
|
|
4940
|
+
* LEFT ALONE, so the previous skin's artwork (balloons, fish, mountains…) stays on
|
|
4941
|
+
* screen over the system palette. That leftover is a feature the user asked to keep
|
|
4942
|
+
* — the built-in cards advertise it ("会有惊喜哦") and
|
|
4943
|
+
* `tests/check-boot-path.mjs` asserts it, so a later "cleanup" cannot remove it
|
|
4944
|
+
* without turning the test red.
|
|
3865
4945
|
* @param kind - the ambient kind, or undefined for none.
|
|
3866
4946
|
* @param options - the theme's ambient options.
|
|
3867
4947
|
*/
|
|
@@ -3934,6 +5014,18 @@ window.__ModuleLoader__.load({
|
|
|
3934
5014
|
const markup = sceneMarkup(kind, options)
|
|
3935
5015
|
|
|
3936
5016
|
if (markup === '') {
|
|
5017
|
+
// ── THE LIVE LAYER IS NOT TOUCHED HERE, ON PURPOSE ───────────────────────
|
|
5018
|
+
//
|
|
5019
|
+
// This branch runs when the active theme has no scenery — i.e. after clicking one of
|
|
5020
|
+
// the built-in 浅色 / 深色 cards. `removeAllAmbientSeats()` only sweeps
|
|
5021
|
+
// `#dsh-theme-ambient` SEATS (a container earlier builds created); the layer that
|
|
5022
|
+
// actually paints is `.dsh-amb-control`, and it is only ever rewritten by
|
|
5023
|
+
// `drawScene()`. So the previous skin's artwork stays where it is.
|
|
5024
|
+
//
|
|
5025
|
+
// That is the user's "惊喜": pick a skin, then switch to 浅色 / 深色, and the
|
|
5026
|
+
// sidebar keeps the balloons / fish / mountains while the palette goes back to the
|
|
5027
|
+
// system appearance. It is asserted in `tests/check-boot-path.mjs`
|
|
5028
|
+
// (「切到内置主题后装饰层保留」) — do not "fix" it by clearing the layer here.
|
|
3937
5029
|
removeAllAmbientSeats()
|
|
3938
5030
|
if (record) noteAmbientAttempt({ kind, column: true, band: '无装饰' })
|
|
3939
5031
|
ambientReport = { found: true, kind: '(none)', note: '该主题无装饰' }
|
|
@@ -4511,6 +5603,14 @@ window.__ModuleLoader__.load({
|
|
|
4511
5603
|
// Detail lines are opt-in; a line that reports a problem is not detail.
|
|
4512
5604
|
const scenery = sceneryLine(selected)
|
|
4513
5605
|
const showScenery = scenery !== null && (debugEnabled() || sceneryLineIsWarning(scenery))
|
|
5606
|
+
// ── THE HEADER COUNT IS SKINS, NOT CARDS ─────────────────────────────────
|
|
5607
|
+
//
|
|
5608
|
+
// The built-in 浅色 / 深色 cards are not skins — they only switch the palette back to the
|
|
5609
|
+
// official appearance — so counting them here would advertise a number that disagrees
|
|
5610
|
+
// with the README and with npm's description ("6 skins"). The cards are named in the
|
|
5611
|
+
// same line instead, so nothing is hidden by leaving them out of the number.
|
|
5612
|
+
const builtInCards = ids.filter((id) => BUILT_IN_LABELS[id] !== undefined).length
|
|
5613
|
+
const skinCount = ids.length - builtInCards
|
|
4514
5614
|
return jsxs('div', {
|
|
4515
5615
|
className: 'tg-page',
|
|
4516
5616
|
children: [
|
|
@@ -4519,7 +5619,7 @@ window.__ModuleLoader__.load({
|
|
|
4519
5619
|
children: [
|
|
4520
5620
|
jsx('span', { className: 'tg-title', children: t('title') }),
|
|
4521
5621
|
jsx('span', { className: 'tg-hint', children: t('hint') }),
|
|
4522
|
-
jsx('span', { className: 'tg-hint', children: t('count', { count:
|
|
5622
|
+
jsx('span', { className: 'tg-hint', children: t('count', { count: skinCount }) }),
|
|
4523
5623
|
// What this copy is, so a reader can compare it with the version npm
|
|
4524
5624
|
// publishes without digging through the profile.
|
|
4525
5625
|
jsx('span', { className: 'tg-hint', children: `v${BUNDLED_VERSION}` }),
|
|
@@ -4534,7 +5634,11 @@ window.__ModuleLoader__.load({
|
|
|
4534
5634
|
swatches: swatches[id] || [],
|
|
4535
5635
|
selected: id === selected,
|
|
4536
5636
|
applied: t('applied'),
|
|
4537
|
-
|
|
5637
|
+
// The click is recorded BEFORE it is handed to the shell, because the theme
|
|
5638
|
+
// service publishes synchronously and the publisher re-applies the remembered
|
|
5639
|
+
// skin: a built-in card clicked after a skin used to be undone immediately.
|
|
5640
|
+
// `rememberCardChoice` is what lets 浅色 / 深色 win — see its documentation.
|
|
5641
|
+
onSelect: (id) => { rememberCardChoice(id); setTheme(id) },
|
|
4538
5642
|
t,
|
|
4539
5643
|
}, id)),
|
|
4540
5644
|
}),
|
|
@@ -4918,8 +6022,11 @@ window.__ModuleLoader__.load({
|
|
|
4918
6022
|
* handler, so the safety has to live in the CONFIGURATION CONTRACT rather than in code.
|
|
4919
6023
|
* The one way to break it is to hand-write a skin id into `preference` — which the
|
|
4920
6024
|
* official service would then reject at boot. Never do that.
|
|
6025
|
+
*
|
|
6026
|
+
* The keys themselves live at MODULE scope ({@link SKIN_KEY} / {@link BUILT_IN_KEY}):
|
|
6027
|
+
* the card-click handler runs in the page component, far from this body, and it must
|
|
6028
|
+
* be able to write them before the click reaches the theme service.
|
|
4921
6029
|
*/
|
|
4922
|
-
const SKIN_KEY = 'theme-gallery:last-skin'
|
|
4923
6030
|
|
|
4924
6031
|
/**
|
|
4925
6032
|
* The token used to prove a skin's palette reached the document.
|
|
@@ -4952,6 +6059,45 @@ window.__ModuleLoader__.load({
|
|
|
4952
6059
|
}
|
|
4953
6060
|
}
|
|
4954
6061
|
|
|
6062
|
+
/**
|
|
6063
|
+
* Record that a skin is the active theme — the restart-time memory.
|
|
6064
|
+
*
|
|
6065
|
+
* Written only once the service actually reports the skin, so the key never names a
|
|
6066
|
+
* skin that was merely requested and then lost. Clearing the built-in marker in the
|
|
6067
|
+
* same breath keeps the two keys from disagreeing about what the user last chose.
|
|
6068
|
+
* @param id - the active skin id.
|
|
6069
|
+
*/
|
|
6070
|
+
function rememberActiveSkin(id) {
|
|
6071
|
+
try {
|
|
6072
|
+
if (window.localStorage.getItem(SKIN_KEY) !== id) window.localStorage.setItem(SKIN_KEY, id)
|
|
6073
|
+
if (window.localStorage.getItem(BUILT_IN_KEY) !== null) window.localStorage.removeItem(BUILT_IN_KEY)
|
|
6074
|
+
} catch (error) {
|
|
6075
|
+
console.error('[theme-gallery] could not remember the active skin:', error)
|
|
6076
|
+
}
|
|
6077
|
+
}
|
|
6078
|
+
|
|
6079
|
+
/**
|
|
6080
|
+
* The skin this plugin should be showing, or null when the user is entitled to a
|
|
6081
|
+
* built-in appearance.
|
|
6082
|
+
*
|
|
6083
|
+
* Three-way precedence, and all three arms are load-bearing:
|
|
6084
|
+
*
|
|
6085
|
+
* 1. a skin the user chose — restore it, across restarts (the original feature);
|
|
6086
|
+
* 2. a built-in the user chose IN THIS PANEL — null, so the restore does not fight it;
|
|
6087
|
+
* 3. nothing chosen yet — the gallery's own default ({@link DEFAULT_SKIN}), which is
|
|
6088
|
+
* what makes a fresh install land on 山青婷彩 instead of the built-in white.
|
|
6089
|
+
*
|
|
6090
|
+
* Arm 2 is why the built-in cards work at all: without it, arm 1 keeps winning and the
|
|
6091
|
+
* click is undone a moment later.
|
|
6092
|
+
* @returns the skin id, or null.
|
|
6093
|
+
*/
|
|
6094
|
+
function wantedSkin() {
|
|
6095
|
+
const remembered = rememberedSkin()
|
|
6096
|
+
if (remembered !== null) return remembered
|
|
6097
|
+
if (builtInChoice() !== null) return null
|
|
6098
|
+
return DEFAULT_SKIN
|
|
6099
|
+
}
|
|
6100
|
+
|
|
4955
6101
|
/** The skin whose palette layer is currently stacked, and how to remove it. */
|
|
4956
6102
|
let stackedSkin
|
|
4957
6103
|
let stackedSkinDispose
|
|
@@ -5221,17 +6367,20 @@ window.__ModuleLoader__.load({
|
|
|
5221
6367
|
// the presenter subscribed leaves the service reporting the skin while the page still
|
|
5222
6368
|
// shows the built-in palette.
|
|
5223
6369
|
if (typeof activeId === 'string' && bundledTheme(activeId) !== undefined) {
|
|
5224
|
-
|
|
5225
|
-
window.localStorage.setItem(SKIN_KEY, activeId)
|
|
5226
|
-
}
|
|
6370
|
+
rememberActiveSkin(activeId)
|
|
5227
6371
|
ensureSkinPainted(activeId, activeId)
|
|
5228
6372
|
return
|
|
5229
6373
|
}
|
|
5230
6374
|
|
|
5231
|
-
// The app is on a built-in theme even though
|
|
5232
|
-
// the preference
|
|
5233
|
-
//
|
|
5234
|
-
|
|
6375
|
+
// The app is on a built-in theme even though one of our skins is wanted: either the
|
|
6376
|
+
// boot race lost the preference, or nothing has been chosen yet and the gallery's
|
|
6377
|
+
// default has still to be applied. Re-checked on every publish rather than once per
|
|
6378
|
+
// session, because a single attempt can be swallowed by a presenter that is not
|
|
6379
|
+
// listening yet.
|
|
6380
|
+
//
|
|
6381
|
+
// `wantedSkin()` returns null when the user deliberately picked a built-in card, and
|
|
6382
|
+
// that null is the whole fix for "点深色又被弹回皮肤" — see its documentation.
|
|
6383
|
+
const wanted = wantedSkin()
|
|
5235
6384
|
if (wanted === null) return
|
|
5236
6385
|
ensureSkinPainted(wanted, activeId)
|
|
5237
6386
|
} catch (error) {
|
|
@@ -5300,7 +6449,12 @@ window.__ModuleLoader__.load({
|
|
|
5300
6449
|
*/
|
|
5301
6450
|
function publish(snapshot) {
|
|
5302
6451
|
revision += 1
|
|
5303
|
-
|
|
6452
|
+
// DISPLAY ORDER IS APPLIED HERE, TO THE PAGE'S COPY ONLY.
|
|
6453
|
+
//
|
|
6454
|
+
// The registry keeps the order things were registered in, so re-ranking a card can
|
|
6455
|
+
// never change which themes exist, what they contain, or the order they were
|
|
6456
|
+
// contributed — `cardsInDisplayOrder` is a view over the filtered list.
|
|
6457
|
+
const themes = cardsInDisplayOrder([...snapshot.themes].filter((theme) => !OMITTED_IDS.has(theme.id)))
|
|
5304
6458
|
storeActions.sync(themes, snapshot.preference, revision)
|
|
5305
6459
|
syncSkin(snapshot)
|
|
5306
6460
|
syncRememberedSkin(snapshot)
|
|
@@ -5556,7 +6710,9 @@ window.__ModuleLoader__.load({
|
|
|
5556
6710
|
try {
|
|
5557
6711
|
const snapshot = ctx.theme.getTheme()
|
|
5558
6712
|
const active = snapshot?.active?.id
|
|
5559
|
-
|
|
6713
|
+
// The default skin counts here too: on a fresh install this window is what keeps
|
|
6714
|
+
// asking until 山青婷彩 is genuinely on the document, not merely requested.
|
|
6715
|
+
const wanted = wantedSkin()
|
|
5560
6716
|
if (wanted === null) {
|
|
5561
6717
|
done = true
|
|
5562
6718
|
} else if (skinIsPainted(wanted)) {
|