@lemoncat7/dsh-theme-xiaohei 0.4.4 → 0.4.5
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/docs/releases/0.4.5.md +21 -0
- package/docs/startup-audit-20261013.md +47 -0
- package/lib/chrome/conversation-messages.js +17 -4
- package/lib/chrome/conversation-submissions.d.ts +3 -0
- package/lib/chrome/conversation-submissions.js +68 -0
- package/lib/chrome.js +2 -0
- package/lib/client.decorations.js +51 -0
- package/lib/client.js +114 -750
- package/lib/client.pose-chin-dark.js +4 -0
- package/lib/client.pose-chin-light.js +4 -0
- package/lib/client.pose-doze-dark.js +4 -0
- package/lib/client.pose-doze-light.js +4 -0
- package/lib/client.pose-halfpeek-dark.js +4 -0
- package/lib/client.pose-halfpeek-light.js +4 -0
- package/lib/client.pose-peek-dark.js +4 -0
- package/lib/client.pose-peek-light.js +4 -0
- package/lib/client.pose-seated-dark.js +4 -0
- package/lib/client.pose-seated-light.js +4 -0
- package/lib/client.rig-chin-dark.js +4 -0
- package/lib/client.rig-chin-light.js +4 -0
- package/lib/client.rig-halfpeek-dark.js +4 -0
- package/lib/client.rig-halfpeek-light.js +4 -0
- package/lib/client.rig-peek-dark.js +4 -0
- package/lib/client.rig-peek-light.js +4 -0
- package/lib/client.rig-seated-dark.js +4 -0
- package/lib/client.rig-seated-light.js +4 -0
- package/lib/composer-draft-preview.d.ts +4 -0
- package/lib/composer-draft-preview.js +8 -0
- package/lib/decorations-entry.d.ts +3 -0
- package/lib/decorations-entry.js +18 -0
- package/lib/deferred-decorations.d.ts +3 -0
- package/lib/deferred-decorations.js +51 -0
- package/lib/plugin.js +11 -4
- package/lib/portal.js +1 -1
- package/lib/scene/character-assets.d.ts +13 -0
- package/lib/scene/character-assets.js +26 -0
- package/lib/scene/character-motion.js +14 -7
- package/lib/scene/ids.d.ts +5 -0
- package/lib/scene/ids.js +5 -0
- package/lib/scene/runtime.d.ts +1 -1
- package/lib/scene/runtime.js +2 -15
- package/lib/scene/styles.d.ts +1 -3
- package/lib/scene/styles.js +2 -8
- package/lib/scene/wallpaper-character-styles.js +1 -5
- package/lib/scene/wallpaper-character.d.ts +2 -1
- package/lib/scene/wallpaper-character.js +24 -15
- package/lib/sidebar-glass.js +1 -1
- package/package.json +3 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { characterAssets } from './character-assets.js';
|
|
2
2
|
import { createCharacterMotionClock } from './character-motion-clock.js';
|
|
3
3
|
import { createRigRenderer } from './character-rig-renderer.js';
|
|
4
4
|
import { EYE_RIGS } from './character-eye-rig.js';
|
|
@@ -10,6 +10,7 @@ export function createCharacterMotion(doc) {
|
|
|
10
10
|
let disposed = false, part, rig;
|
|
11
11
|
let canvas, render;
|
|
12
12
|
let pose = '', appearance = '', revision = 0, nearby = false, lastPointer = 0;
|
|
13
|
+
let selection = '';
|
|
13
14
|
let attentionTimer, lastAttention = -Infinity;
|
|
14
15
|
let gazePoint = [0, 0], activeAction, activeProgress = 0;
|
|
15
16
|
let bounds;
|
|
@@ -46,9 +47,10 @@ export function createCharacterMotion(doc) {
|
|
|
46
47
|
return;
|
|
47
48
|
const mode = doc.documentElement.dataset.xiaoheiAppearance === 'light' ? 'light' : 'dark';
|
|
48
49
|
const moving = doc.documentElement.hasAttribute('data-xiaohei-sidebar-resizing');
|
|
49
|
-
const
|
|
50
|
-
if (
|
|
50
|
+
const key = !doc.hidden && !reduced.matches && !moving && part ? `${pose}-${mode}` : '';
|
|
51
|
+
if (key === selection && appearance === mode)
|
|
51
52
|
return;
|
|
53
|
+
selection = key;
|
|
52
54
|
cancelAttention();
|
|
53
55
|
render = undefined;
|
|
54
56
|
clock.setClips(undefined);
|
|
@@ -61,15 +63,19 @@ export function createCharacterMotion(doc) {
|
|
|
61
63
|
canvas = undefined;
|
|
62
64
|
if (part)
|
|
63
65
|
part.dataset.motion = 'rest';
|
|
64
|
-
rig =
|
|
66
|
+
rig = undefined;
|
|
65
67
|
appearance = mode;
|
|
66
68
|
nearby = false;
|
|
67
|
-
if (!
|
|
69
|
+
if (!key || !part)
|
|
68
70
|
return;
|
|
69
71
|
const currentPart = part;
|
|
70
|
-
void
|
|
72
|
+
void characterAssets.rig(pose, mode).then(async (next) => {
|
|
73
|
+
if (!next || disposed || current !== revision)
|
|
74
|
+
return;
|
|
75
|
+
await Promise.all(Object.values(next.layers).map(layer => decode(layer.src)));
|
|
71
76
|
if (disposed || current !== revision || Object.values(next.layers).some(layer => !images.get(layer.src)))
|
|
72
77
|
return;
|
|
78
|
+
rig = next;
|
|
73
79
|
canvas = doc.createElement('canvas');
|
|
74
80
|
canvas.className = 'xiaohei-character-motion';
|
|
75
81
|
canvas.width = next.size[0];
|
|
@@ -87,7 +93,7 @@ export function createCharacterMotion(doc) {
|
|
|
87
93
|
...(next.layers.tail ? { tail: { duration: 2400 } } : {}),
|
|
88
94
|
attention: { duration: 2400 },
|
|
89
95
|
});
|
|
90
|
-
});
|
|
96
|
+
}).catch(() => { });
|
|
91
97
|
}
|
|
92
98
|
const pointer = (event) => {
|
|
93
99
|
if (!part || !render || event.pointerType !== 'mouse')
|
|
@@ -141,6 +147,7 @@ export function createCharacterMotion(doc) {
|
|
|
141
147
|
if (nextPart !== part || pose !== nextPose) {
|
|
142
148
|
cancelAttention();
|
|
143
149
|
revision++;
|
|
150
|
+
selection = '';
|
|
144
151
|
render = undefined;
|
|
145
152
|
clock.setClips(undefined);
|
|
146
153
|
gaze.reset();
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Asset-free identifiers shared by the startup shell and deferred scenery. */
|
|
2
|
+
export declare const XIAOHEI_SCENE_STYLE_ID = "dsh-theme-xiaohei/scene-style";
|
|
3
|
+
export declare const XIAOHEI_SCENE_LAYER_ID = "dsh-theme-xiaohei/scene-layer";
|
|
4
|
+
export declare const XIAOHEI_SCENE_WORLD_CLASS = "xiaohei-scene__world";
|
|
5
|
+
//# sourceMappingURL=ids.d.ts.map
|
package/lib/scene/ids.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Asset-free identifiers shared by the startup shell and deferred scenery. */
|
|
2
|
+
export const XIAOHEI_SCENE_STYLE_ID = 'dsh-theme-xiaohei/scene-style';
|
|
3
|
+
export const XIAOHEI_SCENE_LAYER_ID = 'dsh-theme-xiaohei/scene-layer';
|
|
4
|
+
export const XIAOHEI_SCENE_WORLD_CLASS = 'xiaohei-scene__world';
|
|
5
|
+
//# sourceMappingURL=ids.js.map
|
package/lib/scene/runtime.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export declare function configureXiaoheiWorldRenderer(renderer: XiaoheiWorldRend
|
|
|
4
4
|
/** Number of top-level decorative parts installed into the ambient layer. */
|
|
5
5
|
export declare const XIAOHEI_SCENE_PART_COUNT: 1;
|
|
6
6
|
/**
|
|
7
|
-
* Install the static wallpaper
|
|
7
|
+
* Install the small static wallpaper immediately with the theme. Appearance is
|
|
8
8
|
* CSS-driven; no image load handlers or per-frame character animation.
|
|
9
9
|
*/
|
|
10
10
|
export declare function installXiaoheiScene(doc?: Document | undefined): () => void;
|
package/lib/scene/runtime.js
CHANGED
|
@@ -14,7 +14,7 @@ const PARTS = [
|
|
|
14
14
|
/** Number of top-level decorative parts installed into the ambient layer. */
|
|
15
15
|
export const XIAOHEI_SCENE_PART_COUNT = PARTS.length;
|
|
16
16
|
/**
|
|
17
|
-
* Install the static wallpaper
|
|
17
|
+
* Install the small static wallpaper immediately with the theme. Appearance is
|
|
18
18
|
* CSS-driven; no image load handlers or per-frame character animation.
|
|
19
19
|
*/
|
|
20
20
|
export function installXiaoheiScene(doc = typeof document === 'undefined' ? undefined : document) {
|
|
@@ -60,22 +60,9 @@ export function installXiaoheiScene(doc = typeof document === 'undefined' ? unde
|
|
|
60
60
|
style.remove();
|
|
61
61
|
};
|
|
62
62
|
};
|
|
63
|
-
|
|
64
|
-
let cancelSchedule = () => { };
|
|
65
|
-
if (win !== null && typeof win.requestIdleCallback === 'function') {
|
|
66
|
-
const idleId = win.requestIdleCallback(mount, { timeout: 800 });
|
|
67
|
-
cancelSchedule = () => win.cancelIdleCallback(idleId);
|
|
68
|
-
}
|
|
69
|
-
else if (win !== null) {
|
|
70
|
-
const timeoutId = win.setTimeout(mount, 0);
|
|
71
|
-
cancelSchedule = () => win.clearTimeout(timeoutId);
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
mount();
|
|
75
|
-
}
|
|
63
|
+
mount();
|
|
76
64
|
return () => {
|
|
77
65
|
disposed = true;
|
|
78
|
-
cancelSchedule();
|
|
79
66
|
removeMountedScene();
|
|
80
67
|
};
|
|
81
68
|
}
|
package/lib/scene/styles.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
+
export { XIAOHEI_SCENE_STYLE_ID, XIAOHEI_SCENE_LAYER_ID, XIAOHEI_SCENE_WORLD_CLASS } from './ids.js';
|
|
1
2
|
/** Stable scene and material ids, shared with lifecycle and browser checks. */
|
|
2
|
-
export declare const XIAOHEI_SCENE_STYLE_ID = "dsh-theme-xiaohei/scene-style";
|
|
3
|
-
export declare const XIAOHEI_SCENE_LAYER_ID = "dsh-theme-xiaohei/scene-layer";
|
|
4
|
-
export declare const XIAOHEI_SCENE_WORLD_CLASS = "xiaohei-scene__world";
|
|
5
3
|
/** Static paired wallpapers; no per-frame effects or session-state dependency. */
|
|
6
4
|
export declare const XIAOHEI_SCENE_CSS: string;
|
|
7
5
|
//# sourceMappingURL=styles.d.ts.map
|
package/lib/scene/styles.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { XIAOHEI_WALLPAPER_LIGHT, XIAOHEI_WALLPAPER_DARK, } from '../generated-backgrounds.js';
|
|
2
|
-
import {
|
|
2
|
+
import { XIAOHEI_SCENE_LAYER_ID, XIAOHEI_SCENE_WORLD_CLASS } from './ids.js';
|
|
3
|
+
export { XIAOHEI_SCENE_STYLE_ID, XIAOHEI_SCENE_LAYER_ID, XIAOHEI_SCENE_WORLD_CLASS } from './ids.js';
|
|
3
4
|
/** Stable scene and material ids, shared with lifecycle and browser checks. */
|
|
4
|
-
export const XIAOHEI_SCENE_STYLE_ID = 'dsh-theme-xiaohei/scene-style';
|
|
5
|
-
export const XIAOHEI_SCENE_LAYER_ID = 'dsh-theme-xiaohei/scene-layer';
|
|
6
|
-
export const XIAOHEI_SCENE_WORLD_CLASS = 'xiaohei-scene__world';
|
|
7
5
|
/** Static paired wallpapers; no per-frame effects or session-state dependency. */
|
|
8
6
|
export const XIAOHEI_SCENE_CSS = `
|
|
9
7
|
body {
|
|
@@ -57,10 +55,6 @@ html[data-xiaohei-appearance='light'] .${XIAOHEI_SCENE_WORLD_CLASS} {
|
|
|
57
55
|
--xiaohei-wallpaper-wide: url("${XIAOHEI_WALLPAPER_LIGHT}");
|
|
58
56
|
}
|
|
59
57
|
|
|
60
|
-
/* The atmosphere contains no character; responsive poses are placed separately
|
|
61
|
-
* against measured free space, never by cropping a portrait into messages. */
|
|
62
|
-
${XIAOHEI_WALLPAPER_CHARACTER_CSS}
|
|
63
|
-
|
|
64
58
|
.${XIAOHEI_SCENE_WORLD_CLASS} > * {
|
|
65
59
|
width: 100%;
|
|
66
60
|
height: 100%;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CHARACTER_POSE_NAMES } from './character-assets.js';
|
|
2
2
|
/** Complete raster poses remain crisp; only entry/exit opacity and transform move. */
|
|
3
3
|
export const XIAOHEI_WALLPAPER_CHARACTER_CSS = `
|
|
4
4
|
.xiaohei-wallpaper-character {
|
|
5
|
-
${CHARACTER_POSE_NAMES.map(pose => `--xiaohei-${pose}-art: url("${CHARACTER_POSES[pose].dark}");`).join('\n ')}
|
|
6
5
|
position: absolute;
|
|
7
6
|
inset: 0;
|
|
8
7
|
pointer-events: none;
|
|
@@ -11,9 +10,6 @@ export const XIAOHEI_WALLPAPER_CHARACTER_CSS = `
|
|
|
11
10
|
html[data-xiaohei-sidebar-resizing] .xiaohei-wallpaper-character {
|
|
12
11
|
visibility: hidden;
|
|
13
12
|
}
|
|
14
|
-
html[data-xiaohei-appearance='light'] .xiaohei-wallpaper-character {
|
|
15
|
-
${CHARACTER_POSE_NAMES.map(pose => `--xiaohei-${pose}-art: url("${CHARACTER_POSES[pose].light}");`).join('\n ')}
|
|
16
|
-
}
|
|
17
13
|
.xiaohei-wallpaper-character > span {
|
|
18
14
|
position: absolute;
|
|
19
15
|
display: block;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
import { type CharacterPose, type CharacterAppearance } from './character-assets.js';
|
|
1
2
|
export declare const XIAOHEI_WALLPAPER_CHARACTER_ID = "dsh-theme-xiaohei/wallpaper-character";
|
|
2
3
|
/** Places the layered character only when the host geometry is settled. */
|
|
3
|
-
export declare function installXiaoheiWallpaperCharacter(doc?: Document | undefined): () => void;
|
|
4
|
+
export declare function installXiaoheiWallpaperCharacter(doc?: Document | undefined, loadPose?: (pose: CharacterPose, appearance: CharacterAppearance) => Promise<string>): () => void;
|
|
4
5
|
//# sourceMappingURL=wallpaper-character.d.ts.map
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { subscribeXiaoheiHostDom } from '../host-dom.js';
|
|
2
2
|
import { XIAOHEI_HOST_SELECTORS } from '../host-contract.js';
|
|
3
|
-
import { XIAOHEI_SCENE_LAYER_ID } from './
|
|
3
|
+
import { XIAOHEI_SCENE_LAYER_ID } from './ids.js';
|
|
4
4
|
import { resolveCharacterPlacement } from './character-layout.js';
|
|
5
5
|
import { createCharacterIdleController } from './character-idle.js';
|
|
6
|
-
import {
|
|
6
|
+
import { characterAssets, CHARACTER_POSE_NAMES } from './character-assets.js';
|
|
7
7
|
import { createCharacterMotion } from './character-motion.js';
|
|
8
8
|
export const XIAOHEI_WALLPAPER_CHARACTER_ID = 'dsh-theme-xiaohei/wallpaper-character';
|
|
9
9
|
/** Places the layered character only when the host geometry is settled. */
|
|
10
|
-
export function installXiaoheiWallpaperCharacter(doc = typeof document === 'undefined' ? undefined : document) {
|
|
10
|
+
export function installXiaoheiWallpaperCharacter(doc = typeof document === 'undefined' ? undefined : document, loadPose = characterAssets.pose) {
|
|
11
11
|
const win = doc?.defaultView;
|
|
12
12
|
if (!doc || !win)
|
|
13
13
|
return () => { };
|
|
@@ -18,7 +18,7 @@ export function installXiaoheiWallpaperCharacter(doc = typeof document === 'unde
|
|
|
18
18
|
let targets = [];
|
|
19
19
|
let dirty = true;
|
|
20
20
|
let placement = { pose: 'hidden', left: 0, top: 0, width: 0, height: 0 };
|
|
21
|
-
const ready = new
|
|
21
|
+
const ready = new Map();
|
|
22
22
|
const loading = new Set();
|
|
23
23
|
const failed = new Set();
|
|
24
24
|
const motion = typeof win.matchMedia === 'function' ? createCharacterMotion(doc) : undefined;
|
|
@@ -132,33 +132,39 @@ export function installXiaoheiWallpaperCharacter(doc = typeof document === 'unde
|
|
|
132
132
|
motion?.setPose(undefined, 'hidden');
|
|
133
133
|
return;
|
|
134
134
|
}
|
|
135
|
-
|
|
135
|
+
const appearance = doc.documentElement.dataset.xiaoheiAppearance === 'light' ? 'light' : 'dark';
|
|
136
|
+
const key = `${pose}-${appearance}`;
|
|
137
|
+
if (!ready.has(key)) {
|
|
136
138
|
motion?.setPose(undefined, 'hidden');
|
|
137
139
|
// Do not retain the old location across a layout change: it could now
|
|
138
140
|
// overlap a message. Idle changes keep the already decoded seated pose.
|
|
139
141
|
if (placement.pose !== 'seated' || !['seated', 'chin', 'doze'].includes(host.dataset.pose ?? ''))
|
|
140
142
|
host.dataset.pose = 'hidden';
|
|
141
|
-
if (!loading.has(
|
|
142
|
-
loading.add(
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
if (!loading.has(key) && !failed.has(key)) {
|
|
144
|
+
loading.add(key);
|
|
145
|
+
// Fetch only the requested pose and palette, not the complete catalog.
|
|
146
|
+
void loadPose(pose, appearance).then(async (src) => {
|
|
147
|
+
if (disposed)
|
|
148
|
+
return;
|
|
146
149
|
const image = new win.Image();
|
|
147
150
|
image.src = src;
|
|
148
|
-
|
|
149
|
-
})).then(() => {
|
|
151
|
+
await image.decode();
|
|
150
152
|
if (disposed)
|
|
151
153
|
return;
|
|
152
|
-
loading.delete(
|
|
153
|
-
ready.
|
|
154
|
+
loading.delete(key);
|
|
155
|
+
ready.set(key, src);
|
|
154
156
|
schedule();
|
|
155
|
-
}
|
|
157
|
+
}).catch(() => { loading.delete(key); failed.add(key); });
|
|
156
158
|
}
|
|
157
159
|
return;
|
|
158
160
|
}
|
|
159
161
|
// Each pose keeps its own coordinates during the short crossfade. No
|
|
160
162
|
// travelling from the right edge through the conversation to the sidebar.
|
|
161
163
|
const part = host.querySelector(`.xiaohei-wallpaper-character__${pose}`);
|
|
164
|
+
if (part.dataset.appearance !== appearance) {
|
|
165
|
+
part.style.setProperty(`--xiaohei-${pose}-art`, `url("${ready.get(key)}")`);
|
|
166
|
+
part.dataset.appearance = appearance;
|
|
167
|
+
}
|
|
162
168
|
for (const key of ['left', 'top', 'width', 'height']) {
|
|
163
169
|
const value = `${Math.round(placement[key] * 100) / 100}px`;
|
|
164
170
|
if (part.style[key] !== value)
|
|
@@ -183,6 +189,8 @@ export function installXiaoheiWallpaperCharacter(doc = typeof document === 'unde
|
|
|
183
189
|
settled = win.setTimeout(() => { settled = undefined; dirty = true; schedule(); }, 240);
|
|
184
190
|
}
|
|
185
191
|
const unsubscribe = subscribeXiaoheiHostDom(doc, schedule);
|
|
192
|
+
const appearanceObserver = new win.MutationObserver(schedule);
|
|
193
|
+
appearanceObserver.observe(doc.documentElement, { attributes: true, attributeFilter: ['data-xiaohei-appearance'] });
|
|
186
194
|
const resized = () => { dirty = true; schedule(); scheduleSettled(); };
|
|
187
195
|
win.addEventListener('resize', resized, { passive: true });
|
|
188
196
|
const activity = () => idle.activity();
|
|
@@ -201,6 +209,7 @@ export function installXiaoheiWallpaperCharacter(doc = typeof document === 'unde
|
|
|
201
209
|
win.clearTimeout(settled);
|
|
202
210
|
unsubscribe();
|
|
203
211
|
observer?.disconnect();
|
|
212
|
+
appearanceObserver.disconnect();
|
|
204
213
|
idle.dispose();
|
|
205
214
|
motion?.dispose();
|
|
206
215
|
for (const event of events)
|
package/lib/sidebar-glass.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { XIAOHEI_SCENE_LAYER_ID, XIAOHEI_SCENE_WORLD_CLASS } from './scene.js';
|
|
1
|
+
import { XIAOHEI_SCENE_LAYER_ID, XIAOHEI_SCENE_WORLD_CLASS } from './scene/ids.js';
|
|
2
2
|
import { subscribeXiaoheiHostDom } from './host-dom.js';
|
|
3
3
|
import { XIAOHEI_HOST_SELECTORS } from './host-contract.js';
|
|
4
4
|
import { createSidebarReveal } from './sidebar-reveal.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lemoncat7/dsh-theme-xiaohei",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
4
4
|
"description": "A Luo Xiaohei-inspired animated dark theme for DeepSeek Harness",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
"./package.json": "./package.json"
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
|
+
"docs/releases/0.4.5.md",
|
|
30
|
+
"docs/startup-audit-20261013.md",
|
|
29
31
|
"docs/releases/0.4.4.md",
|
|
30
32
|
"docs/releases/0.4.3.md",
|
|
31
33
|
"lib/**/*.js",
|