@linxin666/dsh-pet 0.2.1 → 0.2.2
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.i18n.yaml +2 -2
- package/README.md +12 -2
- package/README.zh.md +12 -2
- package/assets/whale/pet.json +1 -1
- package/assets/whale-refined/pet.json +7 -0
- package/assets/whale-refined/spritesheet.webp +0 -0
- package/lib/client.js +4 -4
- package/lib/client.js.map +1 -1
- package/lib/types/client/PetSprite.js +3 -3
- package/lib/types/client/spritesheet.d.ts +1 -3
- package/lib/types/client/spritesheet.d.ts.map +1 -1
- package/lib/types/client/spritesheet.js +1 -5
- package/lib/types/registry.d.ts +0 -2
- package/lib/types/registry.d.ts.map +1 -1
- package/lib/types/registry.js +0 -4
- package/lib/types/service.d.ts.map +1 -1
- package/package.json +1 -2
- package/src/client/PetSprite.tsx +3 -3
- package/src/client/spritesheet.ts +1 -6
- package/src/registry.test.ts +21 -1
- package/src/registry.ts +0 -4
- package/src/service.ts +0 -1
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# node scripts/verify-docs.mjs --write <dir>
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 7bbc716cb77d488334ef3bfb9638823d549fd530
|
|
6
|
+
README.zh.md: d72e002e27ea84dd9da33efb3b3b0585a70a273a
|
package/README.md
CHANGED
|
@@ -67,6 +67,15 @@ Where pets come from (later sources override earlier ones on id collision):
|
|
|
67
67
|
|
|
68
68
|
The registry is built once at host startup; add or change a pet, then restart `dsh web`.
|
|
69
69
|
|
|
70
|
+
## Built-in pets
|
|
71
|
+
|
|
72
|
+
| Registry id | Selector label | Source |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| `whale-girl` | 鲸鱼娘(原版) | The repository's original whale-girl atlas |
|
|
75
|
+
| `whale-girl-refined` | 鲸鱼娘(精致版) | An AI-assisted derivative with repaired and refined details, based on the whale-girl design direction |
|
|
76
|
+
|
|
77
|
+
The refined variant references DreamSkin's “DeepSeek-Whale” theme. The historical source record identifies `powerdog996` as the original theme author and marks the theme as MIT: [DreamSkin](https://dreamskin.cc), [repository source record](https://github.com/zhu1090093659/dsh-web-ui/commit/87edd7ff4800dffd40bc93fb76e4ae450390facd). This attribution records the source and derivative relationship; it does not present the refined variant as an official work of the original author or redefine the original artwork's licensing scope.
|
|
78
|
+
|
|
70
79
|
## Animation preview
|
|
71
80
|
|
|
72
81
|
The sprites are an 8-column × 9-row atlas (192×208 cells) generated by the [hatch-pet](https://github.com/dsh2026) pipeline; below are previews of each state:
|
|
@@ -101,7 +110,8 @@ dsh-pet/
|
|
|
101
110
|
| |-- sequences.ts # full-track scene sequence timing
|
|
102
111
|
| |-- spritesheet.ts # atlas geometry helpers + track trimming
|
|
103
112
|
| `-- pet.module.css
|
|
104
|
-
|-- assets/whale/ # built-in whale-girl (
|
|
113
|
+
|-- assets/whale/ # built-in original whale-girl (manifest + atlas + previews)
|
|
114
|
+
|-- assets/whale-refined/ # built-in refined whale-girl registry variant
|
|
105
115
|
`-- cordis.patch.yml # bundle patch: inserts the pet plugin row
|
|
106
116
|
```
|
|
107
117
|
|
|
@@ -156,7 +166,7 @@ The browser bundle rides the `window.__ModuleLoader__.load` contract; React/cord
|
|
|
156
166
|
|
|
157
167
|
## Sprites and animation-track calibration
|
|
158
168
|
|
|
159
|
-
The built-in whale-girl
|
|
169
|
+
The two built-in whale-girl atlases use the same 9-state × 8-column contract: `assets/whale/` is the original and `assets/whale-refined/` is the refined variant. Each atlas is 1536×1872 (8 columns × 9 rows of 192×208 cells). Frame counts, rhythm, and scene rotation live in each directory's `pet.json`; pets without overrides follow the hatch-pet contract rhythm and canonical single-track scene mapping (row order: 0 idle / 1 running-right / 2 running-left / 3 waving / 4 jumping / 5 failed / 6 waiting / 7 running / 8 review).
|
|
160
170
|
|
|
161
171
|
## License
|
|
162
172
|
|
package/README.zh.md
CHANGED
|
@@ -67,6 +67,15 @@
|
|
|
67
67
|
|
|
68
68
|
注册表在宿主启动时构建一次;新增或修改宠物后重启 `dsh web` 生效。
|
|
69
69
|
|
|
70
|
+
## 内置宠物
|
|
71
|
+
|
|
72
|
+
| 注册表 id | 选择器名称 | 来源 |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| `whale-girl` | 鲸鱼娘(原版) | 仓库原有的鲸鱼娘图集 |
|
|
75
|
+
| `whale-girl-refined` | 鲸鱼娘(精致版) | 以鲸鱼娘设计方向为基础,经 AI 辅助二次创作、修复和细节精修的衍生版本 |
|
|
76
|
+
|
|
77
|
+
精致版参考了 DreamSkin 的「DeepSeek-鲸鱼娘」主题。历史来源记录标注原主题作者为 `powerdog996`,并标注主题为 MIT:[DreamSkin](https://dreamskin.cc)、[仓库来源记录](https://github.com/zhu1090093659/dsh-web-ui/commit/87edd7ff4800dffd40bc93fb76e4ae450390facd)。此处用于记录素材来源与衍生关系;精致版不表述为原作者的官方作品,也不重新定义原始美术作品的授权范围。
|
|
78
|
+
|
|
70
79
|
## 动画预览
|
|
71
80
|
|
|
72
81
|
精灵图是由 [hatch-pet](https://github.com/dsh2026) 流水线生成的 8 列 × 9 行图集(192×208 单元格);各状态预览:
|
|
@@ -101,7 +110,8 @@ dsh-pet/
|
|
|
101
110
|
| |-- sequences.ts # 完整轨道场景序列计时
|
|
102
111
|
| |-- spritesheet.ts # 图集几何辅助 + 轨道裁剪
|
|
103
112
|
| `-- pet.module.css
|
|
104
|
-
|-- assets/whale/ #
|
|
113
|
+
|-- assets/whale/ # 内置原版鲸鱼娘(manifest + 图集 + 预览)
|
|
114
|
+
|-- assets/whale-refined/ # 内置精致版鲸鱼娘注册表变体
|
|
105
115
|
`-- cordis.patch.yml # bundle 补丁:插入宠物插件行
|
|
106
116
|
```
|
|
107
117
|
|
|
@@ -156,7 +166,7 @@ pnpm typecheck # 仅类型检查
|
|
|
156
166
|
|
|
157
167
|
## 精灵图与动画轨道校准
|
|
158
168
|
|
|
159
|
-
|
|
169
|
+
两套内置鲸鱼娘图集使用同一份 9 态 × 8 列契约:`assets/whale/` 是原版,`assets/whale-refined/` 是精致版。每张图集均为 1536×1872(8 列 × 9 行,192×208 单元格)。每行帧数、节奏与场景轮换写在各目录的 `pet.json` 中;未覆盖的宠物沿用 hatch-pet 契约节奏和标准单轨场景映射(行序:0 idle / 1 running-right / 2 running-left / 3 waving / 4 jumping / 5 failed / 6 waiting / 7 running / 8 review)。
|
|
160
170
|
|
|
161
171
|
## 许可证
|
|
162
172
|
|
package/assets/whale/pet.json
CHANGED
|
Binary file
|
package/lib/client.js
CHANGED
|
@@ -118,7 +118,7 @@ window.__ModuleLoader__.load({
|
|
|
118
118
|
* atlas coordinates here would drift each frame by the scale factor and
|
|
119
119
|
* render torn/overlapping frames.
|
|
120
120
|
*/
|
|
121
|
-
function framePosition(cell,
|
|
121
|
+
function framePosition(cell, row, col, scale = 1) {
|
|
122
122
|
return {
|
|
123
123
|
x: -col * cell.width * scale,
|
|
124
124
|
y: -row * cell.height * scale
|
|
@@ -277,7 +277,7 @@ window.__ModuleLoader__.load({
|
|
|
277
277
|
const row = rowOfTrack(leadAnimation);
|
|
278
278
|
const track = trimTrack(tracks[leadAnimation], rows[row] ?? tracks[leadAnimation].frames.length);
|
|
279
279
|
const leadCol = track.frames[0];
|
|
280
|
-
const lead = framePosition(cell,
|
|
280
|
+
const lead = framePosition(cell, row, leadCol, scaleRef.current);
|
|
281
281
|
if (spriteRef.current !== null) spriteRef.current.style.backgroundPosition = lead.x + "px " + lead.y + "px";
|
|
282
282
|
if (reduceMotion) return;
|
|
283
283
|
let raf = 0;
|
|
@@ -291,7 +291,7 @@ window.__ModuleLoader__.load({
|
|
|
291
291
|
const current = sequenceFrameAt(sequence, tracks, sequenceElapsed);
|
|
292
292
|
const currentRow = rowOfTrack(current.animation);
|
|
293
293
|
const col = trimTrack(tracks[current.animation], rows[currentRow] ?? tracks[current.animation].frames.length).frames[current.frameIndex];
|
|
294
|
-
const pos = framePosition(cell,
|
|
294
|
+
const pos = framePosition(cell, currentRow, col, scaleRef.current);
|
|
295
295
|
if (spriteRef.current !== null) spriteRef.current.style.backgroundPosition = pos.x + "px " + pos.y + "px";
|
|
296
296
|
raf = requestAnimationFrame(tick);
|
|
297
297
|
return;
|
|
@@ -313,7 +313,7 @@ window.__ModuleLoader__.load({
|
|
|
313
313
|
st.index = 0;
|
|
314
314
|
} else st.index = maxIndex;
|
|
315
315
|
const col = track.frames[st.index];
|
|
316
|
-
const pos = framePosition(cell,
|
|
316
|
+
const pos = framePosition(cell, row, col, scaleRef.current);
|
|
317
317
|
if (spriteRef.current !== null) spriteRef.current.style.backgroundPosition = pos.x + "px " + pos.y + "px";
|
|
318
318
|
raf = requestAnimationFrame(tick);
|
|
319
319
|
};
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["styles","styles","css","sectionCss"],"sources":["../../../packages/dsh-pet/src/client/pet-store.ts","../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","../../../packages/dsh-pet/src/state.ts","../../../packages/dsh-pet/src/client/spritesheet.ts","../../../packages/dsh-pet/src/client/sequences.ts","../../../packages/dsh-pet/src/client/PetSprite.tsx","../../../packages/dsh-pet/src/client/PetDockEntry.tsx","../../../packages/dsh-pet/src/client/PluginSettingsCard.tsx","../../../packages/dsh-pet/src/client/settings-form.ts","../../../packages/dsh-pet/src/client/PetSettingsCard.tsx","../../../packages/dsh-pet/src/client/locales.ts","../../../packages/dsh-pet/src/client/index.ts"],"sourcesContent":["/**\n * Browser-side pet store: the pet state snapshot plus transient UI feedback\n * (reaction bubbles), written only through the store's audit actions. The\n * RPC polling and interactions live in the plugin apply body; components\n * only ever read snapshots.\n * @module @linxin666/dsh-pet/client/pet-store\n */\n\nimport { defineStore } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { EngineStoreHandle, EngineStoreInstance } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { PetStateView } from '../service.ts'\nimport type { PetInteraction } from '../affinity.ts'\nimport type { PetDefinition } from '../registry.ts'\n\n/** One transient reaction bubble on the pet. */\nexport interface PetFeedback {\n /** Bubble copy. */\n text: string\n /** Interaction kind driving the reaction animation. */\n kind: PetInteraction | 'none'\n /** Epoch ms when the bubble appeared (for expiry). */\n at: number\n}\n\n/** Pet UI state as consumers see it. */\nexport interface PetUiState {\n /** Latest host snapshot; null before the first successful fetch. */\n snapshot: PetStateView | null\n /** The registry list the host serves (atlas URLs + geometry + tracks). */\n pets: PetDefinition[]\n /** Fetch lifecycle. */\n state: 'loading' | 'ready' | 'error'\n /** Transport error message (for the debug surface), when any. */\n error: string | null\n /** Active reaction bubble, if any. */\n feedback: PetFeedback | null\n}\n\n/** Store write set. */\nexport type PetUiActions = {\n /** Replace the host snapshot (poll result). */\n setSnapshot: (draft: PetUiState, snapshot: PetStateView) => void\n /** Replace the registry list. */\n setPets: (draft: PetUiState, pets: PetDefinition[]) => void\n /** Mark the fetch lifecycle. */\n setState: (draft: PetUiState, state: PetUiState['state'], error: string | null) => void\n /** Show a reaction bubble. */\n setFeedback: (draft: PetUiState, feedback: PetFeedback | null) => void\n}\n\n/** Create the pet store handle (apply world only; never module-level). */\nexport function createPetStore(): EngineStoreHandle<PetUiState, PetUiActions> {\n return defineStore({\n init: (): PetUiState => ({\n snapshot: null,\n pets: [],\n state: 'loading',\n error: null,\n feedback: null,\n }),\n actions: {\n setSnapshot: (draft, snapshot) => {\n draft.snapshot = snapshot\n draft.state = 'ready'\n draft.error = null\n },\n setPets: (draft, pets) => {\n draft.pets = pets\n },\n setState: (draft, state, error) => {\n draft.state = state\n draft.error = error\n },\n setFeedback: (draft, feedback) => {\n draft.feedback = feedback\n },\n },\n })\n}\n\nexport type { PetInteraction }\n\n/**\n * A live pet store instance (one per host, owned by the plugin apply body —\n * the pet itself is host-global, so its UI state must not ride the slot\n * system's per-session store scoping).\n */\nexport type PetStoreInstance = EngineStoreInstance<PetUiState, PetUiActions>\n\n","function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","/**\n * Pet state machine — pure, clock-injected. Maps official DSH session activity\n * and the legacy `activity/status` vocabulary onto the 9-state Codex pet\n * animation contract, plus turn-end celebration and no-session idle.\n *\n * The machine is deliberately dumb: it holds the last input phase, the\n * animation decision, and a one-shot \"celebration\" window after `done` so the\n * pet visibly jumps before settling back to idle. Everything here is a pure\n * function of (input, nowMs); persistence and RPC live in the service.\n * @module @linxin666/dsh-pet/state\n */\n\n/** Activity phases understood by the pet host. */\nexport type ActivityPhase = 'idle' | 'waiting' | 'thinking' | 'tool' | 'review' | 'done' | 'failed'\n\n/** The Codex-compatible 9-state animation contract (spritesheet rows). */\nexport type PetAnimation =\n | 'idle'\n | 'running-right'\n | 'running-left'\n | 'waving'\n | 'jumping'\n | 'failed'\n | 'waiting'\n | 'running'\n | 'review'\n\n/** One input snapshot consumed by the machine. */\nexport interface PetStateInput {\n /** Current activity phase of the active session. */\n phase: ActivityPhase\n /** Human-readable status line (plain text). */\n line?: string\n /** Playful phrase from the activity tracker, when any. */\n phrase?: string\n}\n\n/** Animation decision plus the copy the pet should show. */\nexport interface PetStateSnapshot {\n /** Which animation track to play. */\n animation: PetAnimation\n /** Optional status bubble copy (line or phrase), shown while active. */\n bubble?: string\n /** Wall-clock ms this animation started (client can sync loops). */\n animationStartedAt: number\n /** Raw phase, for debugging and client-side rendering decisions. */\n phase: ActivityPhase\n /** True when there is an active session (pet mounted). */\n sessionActive: boolean\n}\n\n/** Machine configuration. */\nexport interface PetStateConfig {\n /** Celebration window after `done` before settling to idle, ms (default 2400). */\n celebrateMs: number\n /** Failure display window before settling to idle, ms (default 2400). */\n failureMs: number\n}\n\nexport const defaultPetStateConfig: PetStateConfig = { celebrateMs: 2400, failureMs: 2400 }\n\n/**\n * Map one activity phase onto the animation contract.\n * - thinking → `running` and tool → `running-right` (focused work).\n * - review → `review` while answer text is streaming.\n * - waiting → `waiting` (expectant pose, needs user input).\n * - done → `jumping` (celebration), then back to `idle` after the window.\n * - failed → `failed` briefly, then back to `idle`.\n * - idle → `idle` (calm breathing loop).\n */\nexport function animationForPhase(phase: ActivityPhase): PetAnimation {\n switch (phase) {\n case 'thinking': return 'running'\n case 'tool': return 'running-right'\n case 'review': return 'review'\n case 'waiting': return 'waiting'\n case 'done': return 'jumping'\n case 'failed': return 'failed'\n case 'idle': return 'idle'\n }\n}\n\n/** The spritesheet row index for one animation track. */\nexport function rowOf(animation: PetAnimation): number {\n const rows: Record<PetAnimation, number> = {\n 'idle': 0,\n 'running-right': 1,\n 'running-left': 2,\n 'waving': 3,\n 'jumping': 4,\n 'failed': 5,\n 'waiting': 6,\n 'running': 7,\n 'review': 8,\n }\n return rows[animation]\n}\n\n/**\n * PetStateMachine — one instance per host process. Holds only the latest\n * input snapshot and terminal-state timing; no storage, no side effects.\n */\nexport class PetStateMachine {\n private phase: ActivityPhase = 'idle'\n private line: string | undefined\n private phrase: string | undefined\n private sessionActive = false\n private doneAt: number | undefined\n private failedAt: number | undefined\n private readonly config: PetStateConfig\n\n constructor(\n config: Partial<PetStateConfig> = defaultPetStateConfig,\n private readonly now: () => number = Date.now,\n ) {\n this.config = { ...defaultPetStateConfig, ...config }\n }\n\n /** Consume one projected activity update. */\n onActivityStatus(input: PetStateInput): void {\n this.phase = input.phase\n this.line = input.line\n this.phrase = input.phrase\n this.doneAt = input.phase === 'done' ? this.now() : undefined\n this.failedAt = input.phase === 'failed' ? this.now() : undefined\n }\n\n /** A session became the active one (or a fresh session started). */\n onSessionActive(): void {\n this.sessionActive = true\n }\n\n /** The active session was disposed (or none left). */\n onSessionDisposed(): void {\n this.sessionActive = false\n this.phase = 'idle'\n this.line = undefined\n this.phrase = undefined\n this.doneAt = undefined\n this.failedAt = undefined\n }\n\n /** Render the current animation decision. */\n render(): PetStateSnapshot {\n const nowMs = this.now()\n let animation = animationForPhase(this.phase)\n const doneSettled = this.phase === 'done'\n && this.doneAt !== undefined\n && nowMs - this.doneAt >= this.config.celebrateMs\n const failedSettled = this.phase === 'failed'\n && this.failedAt !== undefined\n && nowMs - this.failedAt >= this.config.failureMs\n if (doneSettled || failedSettled) animation = 'idle'\n // Settled sessions never bubble: idle (e.g. an aborted/stopped turn),\n // completed celebration expiry, and failed display expiry all fall silent.\n const settled = this.phase === 'idle' || doneSettled || failedSettled\n const bubble = settled ? undefined : this.phrase ?? this.line\n return {\n animation,\n ...(bubble === undefined ? {} : { bubble }),\n animationStartedAt: nowMs,\n phase: this.phase,\n sessionActive: this.sessionActive,\n }\n }\n}\n","/**\n * Spritesheet geometry helpers — parameterized by the pet definition the\n * host serves over '/api/pet/pets', so the browser half renders any registry\n * entry without per-pet code. The per-track tables (frames, durations, loop,\n * fallback) also come from the registry; these helpers only place frames,\n * guard track lengths, and map the fixed 9-row animation contract.\n * @module @linxin666/dsh-pet/client/spritesheet\n */\n\nimport { rowOf, type PetAnimation } from '../state.ts'\nimport type { PetCell, PetTrackDef } from '../registry.ts'\n\n/** Animation track shape the frame loop consumes. */\nexport type TrackDef = PetTrackDef\n\n/** Row index of one animation track (the fixed 9-row contract). */\nexport function rowOfTrack(animation: PetAnimation): number {\n // The table itself lives in state.ts (rowOf) — the single source of truth.\n return rowOf(animation)\n}\n\n/**\n * Background-position (px) of one frame cell within the scaled atlas.\n * The background image is scaled by `scale` (element size ÷ cell size), and\n * background-position offsets are applied in SCALED coordinates — using raw\n * atlas coordinates here would drift each frame by the scale factor and\n * render torn/overlapping frames.\n */\nexport function framePosition(cell: PetCell, columns: number, row: number, col: number, scale = 1): { x: number; y: number } {\n return { x: -col * cell.width * scale, y: -row * cell.height * scale }\n}\n\n/** Total duration of one track, ms. */\nexport function trackDuration(track: TrackDef): number {\n return track.durations.reduce((sum, d) => sum + d, 0)\n}\n\n/**\n * Trim a track to the actual frame count of its row (the manifest's per-row\n * counts are authoritative; this is a last-line guard against a definition\n * whose row count disagrees with its track table). A row with 0 detected\n * frames degrades to the first frame so the pet never renders blank.\n */\nexport function trimTrack(track: TrackDef, frameCount: number): TrackDef {\n const n = Math.max(1, Math.min(frameCount, track.frames.length, track.durations.length))\n return {\n frames: track.frames.slice(0, n),\n durations: track.durations.slice(0, n),\n loop: track.loop,\n ...(track.fallback === undefined ? {} : { fallback: track.fallback }),\n }\n}\n","/** Pure timing helpers for manifest-defined scene animation sequences. */\n\nimport type { PetTrackDef } from '../registry.ts'\nimport type { PetAnimation } from '../state.ts'\n\nexport interface SequenceFrame {\n animation: PetAnimation\n frameIndex: number\n}\n\n/** Resolve the active track and frame after elapsed milliseconds of a looping sequence. */\nexport function sequenceFrameAt(\n sequence: readonly PetAnimation[],\n tracks: Record<PetAnimation, PetTrackDef>,\n elapsedMs: number,\n): SequenceFrame {\n const itemDurations = sequence.map(animation => tracks[animation].durations.reduce((sum, value) => sum + value, 0))\n const sequenceDuration = itemDurations.reduce((sum, value) => sum + value, 0)\n let offset = Math.max(0, elapsedMs) % sequenceDuration\n let itemIndex = 0\n while (itemIndex < sequence.length - 1 && offset >= itemDurations[itemIndex]!) {\n offset -= itemDurations[itemIndex]!\n itemIndex += 1\n }\n const animation = sequence[itemIndex]!\n const track = tracks[animation]\n let frameIndex = 0\n while (frameIndex < track.frames.length - 1 && offset >= track.durations[frameIndex]!) {\n offset -= track.durations[frameIndex]!\n frameIndex += 1\n }\n return { animation, frameIndex }\n}\n","/**\n * Pet sprite companion component — the browser half's centerpiece. Renders a\n * fixed-position floating sprite (React portal onto document.body), plays\n * the track matching the host animation snapshot, and exposes the\n * interaction surface: click to pet, hover panel with feed/rename/hide, drag\n * to reposition (persisted via setConfig). Everything visual comes from the\n * pet definition the host serves ('/api/pet/pets' + the state snapshot's\n * pet id), so one component renders every registry entry.\n * @module @linxin666/dsh-pet/client/PetSprite\n */\n\nimport { useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport type { CSSProperties, PointerEvent as ReactPointerEvent, ReactPortal } from 'react'\nimport { createPortal } from 'react-dom'\nimport clsx from 'clsx'\nimport type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDisplayConfig } from '../persist.ts'\nimport type { PetStateView } from '../service.ts'\nimport type { PetDefinition } from '../registry.ts'\nimport type { PetFeedback } from './pet-store.ts'\nimport { framePosition, rowOfTrack, trimTrack } from './spritesheet.ts'\nimport { sequenceFrameAt } from './sequences.ts'\nimport { animationForPhase, type PetAnimation } from '../state.ts'\nimport { NS } from './locales.ts'\nimport styles from './pet.module.css'\n\n/** Props injected by the plugin apply body (store actions + locale). */\nexport interface PetSpriteProps {\n /** Latest host snapshot; null while loading. */\n snapshot: PetStateView | null\n /** The selected pet's registry definition (atlas URL + geometry + tracks). */\n definition: PetDefinition\n /** Display configuration (persisted by the host). */\n display: PetDisplayConfig\n /** Active reaction bubble, if any. */\n feedback: PetFeedback | null\n /** Pet the sprite (click). */\n onPet: () => void\n /** Feed the sprite (panel button). */\n onFeed: () => void\n /** Hide the sprite (panel button). */\n onHide: () => void\n /** Persist a drag position. */\n onDragEnd: (right: number, bottom: number) => void\n /** Rename the selected pet (persisted by the host). */\n onRename: (name: string) => void\n /** Navigate to the session one status bubble reports on. */\n onOpenSession: (sessionId: string) => void\n /** Clear the reaction bubble (after its CSS animation). */\n onFeedbackDone: () => void\n /** Locale translate seat (namespace-bound). */\n t: TranslateNS<typeof NS>\n}\n\n/** Clamp a drag offset inside the viewport with a margin. */\nfunction clampOffset(value: number, max: number): number {\n return Math.max(0, Math.min(max, value))\n}\n\n/**\n * The floating pet. The spritesheet frame advances on requestAnimationFrame\n * with per-frame durations from the definition's tracks; the atlas image is\n * loaded once and the background position is written straight to the sprite\n * element (no per-frame React state).\n */\nexport function PetSprite(props: PetSpriteProps): ReactPortal {\n const { snapshot, definition, display, feedback } = props\n const spriteRef = useRef<HTMLDivElement | null>(null)\n const floatRef = useRef<HTMLDivElement | null>(null)\n const panelRef = useRef<HTMLDivElement | null>(null)\n // Whichever bubble surface is currently rendered (feedback, the session\n // stack, or the legacy status bubble) — only one exists at a time.\n const bubbleRef = useRef<HTMLDivElement | null>(null)\n const [imageReady, setImageReady] = useState(false)\n const [hovered, setHovered] = useState(false)\n // Multi-session bubble stack: collapsed by default (only the display\n // session's bubble + a '+N' badge), expanded on stack hover (peek) or by\n // tapping the badge (pinned, for touch). The display session's bubble\n // anchors the bottom of the stack and never moves when extras open above\n // it, so the pointer target cannot flicker.\n const [stackPeek, setStackPeek] = useState(false)\n const [stackPinned, setStackPinned] = useState(false)\n const [renaming, setRenaming] = useState(false)\n const [panelAbove, setPanelAbove] = useState(false)\n // Extra margin-bottom for the above-panel so it stacks clear of the\n // bubbles instead of overlapping them (both anchor at the sprite's top).\n const [panelLift, setPanelLift] = useState(0)\n const [nameDraft, setNameDraft] = useState('')\n // Explicit IME composition tracking: some input methods (WeChat IME on\n // Windows) report keydowns with isComposing === false mid-composition, so\n // the native flag alone is not a safe submit/cancel guard (#303).\n const composingRef = useRef(false)\n const [dragPos, setDragPos] = useState<{ right: number; bottom: number } | null>(null)\n const dragRef = useRef<{ startX: number; startY: number; right: number; bottom: number } | null>(null)\n const hideTimerRef = useRef<number | null>(null)\n const frameRef = useRef<{ track: PetAnimation | null; index: number; elapsed: number }>({\n track: null,\n index: 0,\n elapsed: 0,\n })\n\n const cell = definition.cell\n const columns = definition.columns\n const rows = definition.rows\n const tracks = definition.tracks\n const sequences = definition.sequences\n\n // Load the atlas once; the definition carries the authoritative per-row\n // frame counts and per-track durations, so nothing else is fetched.\n useEffect(() => {\n let cancelled = false\n const img = new Image()\n img.onload = () => {\n if (!cancelled) setImageReady(true)\n }\n img.src = definition.atlasUrl\n return () => {\n cancelled = true\n img.onload = null\n }\n }, [definition.atlasUrl])\n\n // Frame loop: advance the current track and write background-position.\n // Offsets must be in SCALED coordinates (background-position applies to the\n // scaled background image), so the current sprite scale rides a ref that\n // the loop reads every tick. Under prefers-reduced-motion the sprite holds\n // its track's first frame instead of animating (presentation-only; the\n // animation state machine is untouched).\n const spriteScale = display.size / cell.height\n const phase = snapshot?.phase ?? 'idle'\n const animation = snapshot?.animation ?? 'idle'\n const scaleRef = useRef(spriteScale)\n scaleRef.current = spriteScale\n useEffect(() => {\n const reduceMotion = typeof window !== 'undefined'\n && window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches === true\n const sequence = animation === animationForPhase(phase) ? sequences?.[phase] : undefined\n const leadAnimation = sequence?.[0] ?? animation\n const row = rowOfTrack(leadAnimation)\n const track = trimTrack(tracks[leadAnimation], rows[row] ?? tracks[leadAnimation].frames.length)\n // Paint one static sprite frame up front either way, so the pet is never\n // blank while the loop heat-up runs.\n const leadCol = track.frames[0]!\n const lead = framePosition(cell, columns, row, leadCol, scaleRef.current)\n if (spriteRef.current !== null) {\n spriteRef.current.style.backgroundPosition = lead.x + 'px ' + lead.y + 'px'\n }\n if (reduceMotion) return\n let raf = 0\n let last = performance.now()\n let sequenceElapsed = 0\n const tick = (ts: number): void => {\n const delta = ts - last\n last = ts\n if (sequence !== undefined) {\n sequenceElapsed += delta\n const current = sequenceFrameAt(sequence, tracks, sequenceElapsed)\n const currentRow = rowOfTrack(current.animation)\n const currentTrack = trimTrack(\n tracks[current.animation],\n rows[currentRow] ?? tracks[current.animation].frames.length,\n )\n const col = currentTrack.frames[current.frameIndex]!\n const pos = framePosition(cell, columns, currentRow, col, scaleRef.current)\n if (spriteRef.current !== null) {\n spriteRef.current.style.backgroundPosition = pos.x + 'px ' + pos.y + 'px'\n }\n raf = requestAnimationFrame(tick)\n return\n }\n // row/track come from the effect scope: they were computed once above\n // and this effect re-runs when animation/tracks/rows change, so the\n // per-frame recompute (trimTrack slices fresh arrays) is pure waste.\n const st = frameRef.current\n if (st.track !== animation) {\n st.track = animation\n st.index = 0\n st.elapsed = 0\n }\n st.elapsed += delta\n const maxIndex = track.frames.length - 1\n while (st.elapsed >= (track.durations[st.index] ?? 0) && st.index < maxIndex) {\n st.elapsed -= track.durations[st.index] ?? 0\n st.index += 1\n }\n if (st.elapsed >= (track.durations[st.index] ?? 0)) {\n if (track.loop) {\n st.elapsed = 0\n st.index = 0\n } else {\n st.index = maxIndex // hold the final frame; the host switches tracks\n }\n }\n const col = track.frames[st.index]!\n const pos = framePosition(cell, columns, row, col, scaleRef.current)\n if (spriteRef.current !== null) {\n spriteRef.current.style.backgroundPosition = pos.x + 'px ' + pos.y + 'px'\n }\n raf = requestAnimationFrame(tick)\n }\n raf = requestAnimationFrame(tick)\n return () => cancelAnimationFrame(raf)\n }, [animation, phase, cell, columns, rows, tracks, sequences])\n\n // Auto-clear the feedback bubble after its CSS animation. The callback\n // rides a ref so re-renders never reset the timer: the 2s poll rebuilds\n // `props` every tick, and depending on it would starve the timeout.\n const feedbackDoneRef = useRef(props.onFeedbackDone)\n feedbackDoneRef.current = props.onFeedbackDone\n useEffect(() => {\n if (feedback === null) return\n const timer = window.setTimeout(() => feedbackDoneRef.current(), 2600)\n return () => window.clearTimeout(timer)\n }, [feedback])\n\n // Dragging: pointer events on the sprite; position is right/bottom based.\n // `draggedRef` records whether the pointer actually moved, so the browser's\n // trailing click (fired after pointerup) does not pet the sprite.\n const draggedRef = useRef(false)\n const clearHideTimer = (): void => {\n if (hideTimerRef.current !== null) {\n window.clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n }\n\n // Clear any pending auto-hide timer on unmount: a stray callback after\n // teardown reads window through react-dom and failed CI runs with\n // \"window is not defined\" (slow-runner timing, PetSprite.test.tsx).\n useEffect(() => () => clearHideTimer(), [])\n\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>): void => {\n e.preventDefault()\n ;(e.target as HTMLElement).setPointerCapture?.(e.pointerId)\n const current = dragPos ?? { right: display.right, bottom: display.bottom }\n dragRef.current = { startX: e.clientX, startY: e.clientY, ...current }\n draggedRef.current = false\n setHovered(false)\n }\n const onPointerMove = (e: ReactPointerEvent<HTMLDivElement>): void => {\n const drag = dragRef.current\n if (drag === null) return\n const dx = e.clientX - drag.startX\n const dy = e.clientY - drag.startY\n if (Math.abs(dx) > 4 || Math.abs(dy) > 4) draggedRef.current = true\n const right = clampOffset(drag.right - dx, window.innerWidth - 40)\n const bottom = clampOffset(drag.bottom - dy, window.innerHeight - 40)\n setDragPos({ right, bottom })\n }\n const onPointerUp = (): void => {\n if (dragRef.current === null) return\n dragRef.current = null\n if (dragPos !== null) props.onDragEnd(dragPos.right, dragPos.bottom)\n }\n\n const pos = dragPos ?? { right: display.right, bottom: display.bottom }\n const spriteWidth = Math.round(cell.width * spriteScale)\n const spriteHeight = Math.round(cell.height * spriteScale)\n\n // Concurrent sessions share one bubble slot: only the display session\n // speaks by default, and the rest hide behind a '+N' badge until the stack\n // is hovered/pinned open. The legacy single 'bubble' is the fallback when\n // the host serves no per-session list. The hover panel normally sits below\n // the sprite, so the bubbles stay visible and clickable — no region swap.\n const sessionBubbles = snapshot?.sessions ?? []\n const stackOpen = stackPeek || stackPinned\n const collapsed = !stackOpen && sessionBubbles.length > 1\n const visibleSessions = collapsed ? sessionBubbles.slice(0, 1) : sessionBubbles\n const statusBubble = feedback === null && sessionBubbles.length === 0\n ? snapshot?.bubble\n : undefined\n // The display session's inner whisper (碎碎念) — short inner-voice copy\n // woken by the model's output. Instead of a second bubble of its own, a\n // fresh whisper takes over the display session's bubble (the stack top, or\n // the single status bubble) and re-tints it, so the pet never wears two\n // voices at once. Interaction feedback takes over the whole bubble area\n // while it plays, so whispers yield to it like status copy.\n const whisper = feedback === null ? snapshot?.whisper : undefined\n const bubblePresent = feedback !== null || sessionBubbles.length > 0 || statusBubble !== undefined || whisper !== undefined\n const displayName = snapshot?.name ?? definition.displayName\n\n // A settled session list can no longer stay pinned open.\n useEffect(() => {\n if (sessionBubbles.length <= 1) setStackPinned(false)\n }, [sessionBubbles.length])\n\n useLayoutEffect(() => {\n if (!hovered) {\n setPanelAbove(false)\n setPanelLift(0)\n return\n }\n const updatePanelPlacement = (): void => {\n const sprite = spriteRef.current\n const panel = panelRef.current\n if (sprite === null || panel === null) return\n const availableBelow = window.innerHeight - sprite.getBoundingClientRect().bottom\n const above = availableBelow < panel.getBoundingClientRect().height + 8\n setPanelAbove(above)\n // The fallback above-placement shares the sprite's top edge with the\n // bubble(s); lift the panel by the bubble area's height so the two\n // never overlap (8px base gap + 6px clearance above the top bubble).\n const bubbleHeight = above ? bubbleRef.current?.getBoundingClientRect().height ?? 0 : 0\n setPanelLift(bubbleHeight > 0 ? Math.ceil(bubbleHeight) + 14 : 0)\n }\n updatePanelPlacement()\n window.addEventListener('resize', updatePanelPlacement)\n return () => window.removeEventListener('resize', updatePanelPlacement)\n }, [hovered, renaming, pos.right, pos.bottom, display.size, bubblePresent, sessionBubbles.length, stackOpen, feedback])\n\n const float = (\n <div\n ref={floatRef}\n className={styles.float}\n style={{ right: pos.right, bottom: pos.bottom, zIndex: 2147483000 }}\n onPointerEnter={() => {\n clearHideTimer()\n setHovered(true)\n }}\n onPointerLeave={(e) => {\n // The panel renders OUTSIDE the container's box (absolute, below\n // the sprite), so moving onto it fires pointerleave on the container.\n // Treat a target still inside the container's DOM (the overflowed\n // panel) as \"still hovering\"; otherwise give the pointer a short\n // grace period to reach the panel across the gap below the sprite.\n // The bridge ('.panel::after') keeps the pointer inside the hit\n // area, and the grace period covers a slow mouse crossing the\n // remaining sliver.\n const next = e.relatedTarget\n if (next instanceof Node && floatRef.current?.contains(next)) return\n // Never auto-hide while the rename box is open: moving the pointer\n // onto an IME candidate window (an OS-level window outside the\n // webview) fires pointerleave, and unmounting the input mid-IME-\n // composition crashes some input methods / the renderer (#303).\n if (renaming) return\n clearHideTimer()\n hideTimerRef.current = window.setTimeout(() => setHovered(false), 300)\n }}\n >\n <div\n ref={spriteRef}\n className={styles.sprite}\n style={{\n width: spriteWidth,\n height: spriteHeight,\n backgroundImage: imageReady ? 'url(' + definition.atlasUrl + ')' : undefined,\n backgroundSize: (cell.width * columns * spriteScale) + 'px ' + (cell.height * (definition.atlasRows ?? rows.length) * spriteScale) + 'px',\n backgroundRepeat: 'no-repeat',\n backgroundPosition: '0 0',\n cursor: dragRef.current === null ? 'grab' : 'grabbing',\n }}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onClick={() => {\n // A pointer sequence that moved (dragged) still fires a trailing\n // click; skip the pet when that happened.\n if (draggedRef.current) return\n props.onPet()\n }}\n role=\"button\"\n aria-label={definition.displayName}\n />\n {feedback !== null && (\n <div key={feedback.at} ref={bubbleRef} className={clsx(styles.bubble, feedback.kind === 'feed' ? styles.bubbleFeed : styles.bubblePet)}>\n {feedback.text}\n </div>\n )}\n {feedback === null && (sessionBubbles.length > 0 || statusBubble !== undefined || whisper !== undefined) && (\n <div\n ref={bubbleRef}\n className={styles.bubbleStack}\n onPointerEnter={() => setStackPeek(true)}\n onPointerLeave={() => setStackPeek(false)}\n >\n {visibleSessions.map((session, index) => {\n // The whisper rides the display session's bubble — the stack's\n // primary entry (DOM-first, rendered bottom-most by the reversed\n // column so it stays glued to the sprite when extras open above).\n // The key swap restarts the entrance animation so the mood change\n // reads as the bubble re-speaking.\n const speaksWhisper = index === 0 && whisper !== undefined\n const bubble = (\n <button\n key={speaksWhisper ? 'whisper:' + whisper : session.sessionId}\n type=\"button\"\n className={clsx(\n styles.bubble,\n styles.bubbleStatus,\n styles.bubbleClickable,\n speaksWhisper && styles.bubbleWhisper,\n )}\n title={props.t('pet.openSessionHint')}\n onClick={() => { props.onOpenSession(session.sessionId) }}\n >\n {speaksWhisper ? whisper : session.bubble}\n </button>\n )\n // The primary bubble carries the '+N' badge while other sessions\n // hide behind it; the badge toggles the pinned (touch) expansion.\n if (index !== 0 || sessionBubbles.length <= 1) return bubble\n return (\n <span key=\"primary\" className={styles.bubbleAnchor}>\n {bubble}\n <button\n type=\"button\"\n className={styles.bubbleMore}\n title={stackOpen\n ? props.t('pet.collapseSessions')\n : props.t('pet.moreSessions', { n: sessionBubbles.length - 1 })}\n aria-label={stackOpen\n ? props.t('pet.collapseSessions')\n : props.t('pet.moreSessions', { n: sessionBubbles.length - 1 })}\n aria-expanded={stackOpen}\n onClick={(e) => {\n e.stopPropagation()\n setStackPinned(open => !open)\n }}\n >\n {stackOpen ? '×' : '+' + String(sessionBubbles.length - 1)}\n </button>\n </span>\n )\n })}\n {sessionBubbles.length === 0 && (statusBubble !== undefined || whisper !== undefined) && (\n // The key swap (status copy <-> whisper) restarts the entrance\n // animation on every mood change.\n <div\n key={whisper === undefined ? 'status' : 'whisper:' + whisper}\n className={clsx(styles.bubble, styles.bubbleStatus, whisper !== undefined && styles.bubbleWhisper)}\n role=\"status\"\n aria-live=\"polite\"\n >\n {whisper ?? statusBubble}\n </div>\n )}\n </div>\n )}\n {hovered && dragRef.current === null && (\n <div\n ref={panelRef}\n className={clsx(styles.panel, panelAbove && styles.panelAbove)}\n data-placement={panelAbove ? 'above' : 'below'}\n style={panelAbove && panelLift > 0\n ? ({ marginBottom: panelLift } as CSSProperties)\n : undefined}\n onPointerEnter={() => {\n // Reaching the panel (or its bridge) must cancel any hide timer\n // the container's pointerleave may have armed while the pointer\n // crossed the sliver between the sprite and the panel.\n clearHideTimer()\n }}\n >\n {renaming ? (\n <div className={styles.renameRow}>\n <input\n className={styles.nameInput}\n value={nameDraft}\n maxLength={20}\n placeholder={props.t('pet.namePlaceholder')}\n autoFocus\n onChange={(e) => setNameDraft(e.target.value)}\n onCompositionStart={() => { composingRef.current = true }}\n onCompositionEnd={() => { composingRef.current = false }}\n onKeyDown={(e) => {\n // While an IME composition is active (e.g. selecting a\n // Chinese candidate), Enter/Escape keydowns belong to the\n // input method: ignore them so candidate selection can\n // neither submit the draft nor close the rename box. The\n // explicit ref and the 'Process' key cover IMEs that mark\n // composition keydowns with isComposing === false (#303).\n if (composingRef.current || e.nativeEvent.isComposing || e.key === 'Process') return\n if (e.key === 'Enter') {\n const trimmed = nameDraft.trim()\n if (trimmed !== '') {\n props.onRename(trimmed)\n setRenaming(false)\n }\n } else if (e.key === 'Escape') {\n setRenaming(false)\n }\n }}\n />\n <button\n type=\"button\"\n className={styles.action}\n onClick={() => {\n const trimmed = nameDraft.trim()\n if (trimmed !== '') {\n props.onRename(trimmed)\n setRenaming(false)\n }\n }}\n >\n {props.t('pet.confirm')}\n </button>\n </div>\n ) : (\n <>\n <div className={styles.rankRow}>\n <span className={styles.nameCell}>{displayName}</span>\n <span className={styles.statRank}>{props.t('pet.rank', { rank: snapshot?.affinity.rank ?? '?' })}</span>\n </div>\n <div className={styles.rankRow}>\n <span className={styles.statTreats}>{props.t('pet.treats', { n: snapshot?.treats.stocked ?? 0 })}</span>\n <span className={styles.statPoints}>{props.t('pet.points', { points: snapshot?.affinity.points ?? 0 })}</span>\n </div>\n <div className={styles.actions}>\n <button type=\"button\" className={styles.action} onClick={props.onFeed}>\n {props.t('pet.feed')}\n </button>\n <button\n type=\"button\"\n className={styles.action}\n onClick={() => {\n // Cancel any pending hide so the rename box cannot\n // unmount right as the user starts typing (#303).\n clearHideTimer()\n setNameDraft(displayName)\n setRenaming(true)\n }}\n >\n {props.t('pet.rename')}\n </button>\n <button type=\"button\" className={styles.action} onClick={props.onHide}>\n {props.t('pet.hide')}\n </button>\n </div>\n </>\n )}\n </div>\n )}\n </div>\n )\n\n return createPortal(float, document.body)\n}\n","/**\n * Global floating pet entry. The pet is host-global (its state, display and\n * interactions live on '/api/pet/*' endpoints with no session dimension), so\n * it must not ride a session-scoped slot — on the new-conversation screen no\n * session exists to scope a slot by, and the pet would vanish (issue #48).\n * The client half therefore mounts this entry straight onto 'document.body'\n * (see index.ts): while visible it renders the floating PetSprite (a\n * portal), while hidden it renders a fixed-position summon button. Which\n * sprite renders is decided by the host snapshot's pet id resolved against\n * the registry list — no per-pet component exists.\n * @module @linxin666/dsh-pet/client/PetDockEntry\n */\n\nimport { useEffect, useSyncExternalStore, type ReactElement } from 'react'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDisplayConfig } from '../persist.ts'\nimport type { PetStoreInstance } from './pet-store.ts'\nimport { PetSprite } from './PetSprite.tsx'\nimport { NS } from './locales.ts'\nimport styles from './pet.module.css'\n\n/** Injected actions handed to the dock entry component. */\nexport interface PetInjected {\n /** The app-wide pet store instance (snapshot + registry list + feedback). */\n store: PetStoreInstance\n /** Ensure the first snapshot (and registry list) is fetched (called on mount). */\n ensure: () => void\n /** Pet the sprite (click). */\n pet: () => void\n /** Feed the sprite. */\n feed: () => void\n /** Hide the sprite. */\n hide: () => void\n /** Summon the hidden sprite back. */\n summon: () => void\n /** Persist a drag position. */\n dragEnd: (right: number, bottom: number) => void\n /** Rename the selected pet (persisted by the host). */\n rename: (name: string) => void\n /** Navigate the GUI to the session a bubble reports on. */\n openSession: (sessionId: string) => void\n /** Clear the reaction bubble. */\n feedbackDone: () => void\n}\n\n/** Composed props of the global pet entry (locale + injected; no slot runtime share). */\nexport type PetDockEntryProps =\n PetInjected\n & PropsLocale<typeof NS>\n\nconst DEFAULT_DISPLAY: PetDisplayConfig = { visible: true, size: 160, right: 24, bottom: 20 }\n\n/**\n * Dock entry: while the pet is visible, mount the floating PetSprite (it\n * portals itself onto document.body); while hidden, render the summon\n * button so the pet can always come back. The store is the plugin-owned\n * single instance — the slot system provides none because the pet is\n * host-global, not session-scoped.\n */\nexport function PetDockEntry(props: PetDockEntryProps): ReactElement {\n const { store, ensure } = props\n const ui = useSyncExternalStore(store.subscribe, store.getSnapshot)\n const snapshot = ui.snapshot\n const feedback = ui.feedback\n const definition = ui.pets.find(entry => entry.id === snapshot?.pet.id) ?? null\n const visible = snapshot?.display.visible ?? true\n\n useEffect(() => {\n ensure()\n }, [ensure])\n\n if (visible) {\n return (\n <span data-pet-dock data-testid=\"pet-dock\">\n {snapshot === null || definition === null\n ? null\n : (\n <PetSprite\n snapshot={snapshot}\n definition={definition}\n display={snapshot.display}\n feedback={feedback}\n onPet={props.pet}\n onFeed={props.feed}\n onHide={props.hide}\n onDragEnd={props.dragEnd}\n onRename={props.rename}\n onOpenSession={props.openSession}\n onFeedbackDone={props.feedbackDone}\n t={props.t}\n />\n )}\n </span>\n )\n }\n const display = snapshot?.display ?? DEFAULT_DISPLAY\n return (\n <button\n type=\"button\"\n className={styles.summon}\n style={{\n position: 'fixed',\n right: display.right,\n bottom: display.bottom,\n zIndex: 2147483000,\n }}\n onClick={props.summon}\n data-testid=\"pet-summon\"\n data-dsh-part=\"summon-button\"\n >\n {props.t('pet.summon', { name: snapshot?.name ?? '' })}\n </button>\n )\n}\n","// Generated by scripts/sync-shared.mjs from shared/client/settings/PluginSettingsCard.tsx. Do not edit this copy; edit the shared source and run \"node scripts/sync-shared.mjs\".\n/**\n * Family-shared chrome for plugin settings cards: a disclosure header naming\n * the plugin and what its settings govern, the controls inside, and the save\n * that writes them. Renders nothing while the namespace is unavailable — a\n * deployment that does not compose the owning plugin should show no trace of\n * it. Inlined into each consumer's client bundle; mirrors the official\n * ui-plugin-config PluginCard in a self-contained slice.\n */\n\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState, type KeyboardEvent, type ReactNode } from 'react'\nimport type { CardShell } from './settings-form.ts'\nimport css from './settings-card.module.css'\n\n/** Copy keys the card chrome itself reads; every consumer locale carries this shared vocabulary. */\nexport const CARD_COPY_KEYS = [\n 'settings.collapse',\n 'settings.expand',\n 'settings.notExposed',\n 'settings.unsaved',\n 'settings.readOnly',\n 'settings.saveFailed',\n 'settings.discard',\n 'settings.save',\n 'settings.saving',\n] as const\n\n/** Copy key the card chrome itself reads. */\nexport type CardCopyKey = (typeof CARD_COPY_KEYS)[number]\n\n/** Key domain for the plugin's own copy (inferred per consumer). */\nexport type SettingsCardKey<TKey extends string = string> = TKey | CardCopyKey\n\n/** Card chrome shared by every plugin settings card. */\nexport interface PluginSettingsCardProps<TKey extends string = string> {\n /** Locale reader for this card's copy. */\n t: (key: SettingsCardKey<TKey>, params?: Record<string, string | number>) => string\n /** Locale key of the plugin's name. */\n titleKey: TKey\n /** Locale key of the line describing what this plugin's settings govern. */\n descriptionKey: TKey\n /** The card's form state: availability, writability, and what a save would do. */\n state: CardShell\n /** Write every staged edit. */\n onSave: () => void\n /** Drop every staged edit. */\n onDiscard: () => void\n /**\n * Render the controls expanded on arrival (still collapsible). Defaults to\n * true: promoted first-level sections show their settings immediately.\n */\n defaultOpen?: boolean\n /**\n * Render without the collapse affordance: a static header and an always\n * visible body. Used by first-level settings sections whose nav entry\n * already provides the selection.\n */\n alwaysOpen?: boolean\n /**\n * Hide the save/discard footer: cards whose body applies its own changes\n * immediately (an embedded external settings section) have no staged\n * edits, so the footer would sit there permanently disabled.\n */\n hideFooter?: boolean\n /** The plugin's controls. */\n children: ReactNode\n}\n\n/**\n * Render one plugin settings card.\n * @param props - the plugin's copy keys, its form state, and its controls.\n * @returns the card, or nothing while the namespace is still loading.\n */\nexport function PluginSettingsCard<TKey extends string = string>(props: PluginSettingsCardProps<TKey>) {\n const [open, setOpen] = useState(props.defaultOpen ?? true)\n const { state, alwaysOpen } = props\n if (!state.available) return null\n const title = props.t(props.titleKey)\n const description = props.t(props.descriptionKey)\n const blocked = !state.dirty || state.invalid || state.saving\n const expanded = alwaysOpen === true || open\n const cardClass = expanded ? `${css.cardOpen} ${css.card}` : css.card\n // With alwaysOpen the nav entry already provides the selection, so the\n // header is a static title row instead of a disclosure button.\n const header = alwaysOpen === true\n ? (\n <div className={css.headerStatic}>\n <span className={css.headText}>\n <span className={css.name} title={title}>{title}</span>\n <span className={css.description} title={description}>{description}</span>\n </span>\n {state.dirty ? <span className={css.pending} title={props.t('settings.unsaved')}>{props.t('settings.unsaved')}</span> : null}\n </div>\n )\n : (\n <button\n type=\"button\"\n className={css.header}\n aria-expanded={open}\n aria-label={`${props.t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`}\n onClick={() => { setOpen(!open) }}\n >\n <span className={css.headText}>\n <span className={css.name} title={title}>{title}</span>\n <span className={css.description} title={description}>{description}</span>\n </span>\n {state.dirty ? <span className={css.pending} title={props.t('settings.unsaved')}>{props.t('settings.unsaved')}</span> : null}\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 14 14\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={open ? `${css.chevron} ${css.chevronOpen}` : css.chevron}\n >\n <path\n d=\"M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z\"\n fill=\"currentColor\"\n />\n </svg>\n </button>\n )\n // The namespace exists but the Host does not serve it to this client (the\n // official settings allowlist omits third-party namespaces): show a card\n // that explains the gap instead of vanishing, so a missing card never\n // reads as a missing plugin.\n if (!state.exposed) {\n return (\n <li className={cardClass}>\n {header}\n {expanded\n ? (\n <div className={css.body}>\n <p className={css.notExposed} role=\"status\">{props.t('settings.notExposed')}</p>\n </div>\n )\n : null}\n </li>\n )\n }\n return (\n <li className={cardClass}>\n {header}\n {expanded\n ? (\n <div className={css.body}>\n {!state.writable ? <p className={css.readOnly} role=\"status\">{props.t('settings.readOnly')}</p> : null}\n {props.children}\n {props.hideFooter === true\n ? null\n : (\n <div className={css.footer}>\n {state.failed\n ? (\n <p className={css.failed} role=\"status\">\n {props.t('settings.saveFailed')}{state.failedReason ? ' - ' + state.failedReason : ''}\n </p>\n )\n : null}\n <button\n type=\"button\"\n className={css.discard}\n disabled={!state.dirty || state.saving}\n onClick={props.onDiscard}\n >\n {props.t('settings.discard')}\n </button>\n <button\n type=\"button\"\n className={css.save}\n disabled={blocked}\n onClick={props.onSave}\n >\n {props.t(!state.saving ? 'settings.save' : 'settings.saving')}\n </button>\n </div>\n )}\n </div>\n )\n : null}\n </li>\n )\n}\n\n/** Props every field control needs regardless of its value type. */\nexport interface FieldProps {\n /** Stable id associating the label with its control. */\n id: string\n /** Visible label. */\n label: string\n /** One-line explanation rendered under the control. */\n hint: string\n /** Draft text this control renders. */\n text: string\n /** True when saving would leave a user-layer entry for this field. */\n overridden: boolean\n /** True when the draft is not a value this field accepts. */\n invalid: boolean\n /** Copy for the overridden badge. */\n overriddenLabel: string\n /** Copy for the reset control. */\n resetLabel: string\n /** Copy shown in place of the hint while the draft is invalid. */\n invalidLabel: string\n /** Disables every control (read-only document, or an unavailable namespace). */\n disabled: boolean\n /** Stage draft text. */\n onEdit: (text: string) => void\n /** Stage a clear so the field re-inherits the composition layer. */\n onReset: () => void\n}\n\n/** A staged value field. `numeric` only hints the keypad: which drafts a field accepts is decided by its spec. */\nexport function ValueField(props: FieldProps & {\n /** Hints a numeric keypad without narrowing what the control accepts. */\n numeric?: boolean\n /** Placeholder shown while the draft is empty. */\n placeholder?: string\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <input\n id={props.id}\n className={props.invalid ? css.inputInvalid : css.input}\n type=\"text\"\n {...props.numeric === true ? { inputMode: 'numeric' as const } : {}}\n {...props.invalid ? { 'aria-invalid': true } : {}}\n value={props.text}\n placeholder={props.placeholder ?? ''}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n />\n <p className={props.invalid ? css.invalid : css.hint}>\n {props.invalid ? props.invalidLabel : props.hint}\n </p>\n </div>\n )\n}\n\nconst NON_SKIN_BODY_MARKERS = new Set(['dshSkinCenter', 'dshSidebarCollapsed'])\n// 检测使用使用皮肤,用了皮肤退回原生select样式,防止样式冲突。默认外观下使用优化后的select样式。\nfunction isSkinActive(): boolean {\n const datasetList = Object.keys(document.body.dataset)\n const isActive = datasetList.some(key => key.startsWith('dsh') && !NON_SKIN_BODY_MARKERS.has(key))\n return isActive\n}\n\ninterface SelectOption {\n value: string\n label: string\n}\n\nconst SELECT_CLOSE_MS = 100\n\n/**\n * The shared dual-mode select control. While an appearance skin is active it\n * renders the legacy native `<select>` untouched, so element-level skin\n * selectors keep working; under the default appearance it renders a\n * self-drawn `role=\"listbox\"` popup whose open/close is transition-animated.\n * Staged cards reach it through BooleanField/ChoiceField; immediate-apply\n * editors (the side-card prefs) bind it directly through onEdit.\n * 双模式下拉框:皮肤激活时用原生 select,默认外观用自绘动画弹层。\n */\nexport function SelectField(props: {\n id: string\n options: ReadonlyArray<SelectOption>\n value: string\n disabled: boolean\n invalid: boolean\n onEdit: (text: string) => void\n}) {\n const { id, options, value } = props\n const [open, setOpen] = useState(false)\n const [closing, setClosing] = useState(false)\n const [phase, setPhase] = useState<'initial' | 'open'>('initial')\n const [activeIndex, setActiveIndex] = useState(0)\n const closeTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const wrapRef = useRef<HTMLDivElement | null>(null)\n const popupRef = useRef<HTMLDivElement | null>(null)\n\n const currentIndex = () => {\n const index = options.findIndex(option => option.value === value)\n return index >= 0 ? index : 0\n }\n\n const close = useCallback(() => {\n if (closeTimer.current !== undefined) clearTimeout(closeTimer.current)\n setClosing(true)\n closeTimer.current = setTimeout(() => {\n setClosing(false)\n setOpen(false)\n }, SELECT_CLOSE_MS)\n }, [])\n\n const openPopup = () => {\n if (closeTimer.current !== undefined) clearTimeout(closeTimer.current)\n setActiveIndex(currentIndex())\n setPhase('initial')\n setClosing(false)\n setOpen(true)\n }\n\n const commit = (index: number) => {\n const option = options[index]\n if (option) props.onEdit(option.value)\n close()\n }\n\n const onTriggerClick = () => {\n if (props.disabled) return\n if (open && !closing) close()\n else openPopup()\n }\n\n const onKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n if (props.disabled) return\n const count = options.length\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowUp':\n case 'Enter':\n case ' ':\n event.preventDefault()\n if (!open) {\n openPopup()\n } else if (!closing) {\n if (event.key === 'ArrowDown') setActiveIndex(index => (index + 1) % count)\n else if (event.key === 'ArrowUp') setActiveIndex(index => (index - 1 + count) % count)\n else commit(activeIndex)\n }\n break\n case 'Escape':\n if (open) {\n event.preventDefault()\n event.stopPropagation()\n close()\n }\n break\n case 'Tab':\n if (open) close()\n break\n }\n }\n\n useEffect(() => () => {\n if (closeTimer.current !== undefined) clearTimeout(closeTimer.current)\n }, [])\n useLayoutEffect(() => {\n if (open && !closing && phase === 'initial') {\n void popupRef.current?.offsetHeight\n setPhase('open')\n }\n }, [open, closing, phase])\n\n useEffect(() => {\n if (!open) return\n const onPointerDown = (event: PointerEvent) => {\n const target = event.target\n if (target instanceof Node && !wrapRef.current?.contains(target)) close()\n }\n document.addEventListener('pointerdown', onPointerDown)\n return () => document.removeEventListener('pointerdown', onPointerDown)\n }, [open, close])\n\n useEffect(() => {\n if (props.disabled && open) close()\n }, [props.disabled, open, close])\n\n // 使用了皮肤则退回原生select样式防止样式冲突。\n if (isSkinActive()) {\n return (\n <select\n id={id}\n className={css.select}\n value={value}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n >\n {options.map(option => (\n <option key={option.value} value={option.value}>{option.label}</option>\n ))}\n </select>\n )\n }\n\n const label = options.find(option => option.value === value)?.label ?? ''\n const popupClass = closing\n ? `${css.selectPopup} ${css.selectPopupClose}`\n : phase === 'open'\n ? `${css.selectPopup} ${css.selectPopupOpen}`\n : css.selectPopup\n return (\n <div className={css.selectWrap} ref={wrapRef}>\n <button\n type=\"button\"\n id={id}\n className={`${css.select} ${css.selectButton}`}\n disabled={props.disabled}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-activedescendant={open ? `${id}-o${activeIndex}` : undefined}\n aria-invalid={props.invalid || undefined}\n onClick={onTriggerClick}\n onKeyDown={onKeyDown}\n >\n <span className={css.selectLabel}>{label}</span>\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 14 14\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={open ? `${css.selectChevron} ${css.selectChevronOpen}` : css.selectChevron}\n aria-hidden=\"true\"\n >\n <path\n d=\"M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z\"\n fill=\"currentColor\"\n />\n </svg>\n </button>\n {open\n ? (\n <div className={popupClass} role=\"listbox\" ref={popupRef}>\n {options.map((option, index) => (\n <div\n key={option.value}\n id={`${id}-o${index}`}\n role=\"option\"\n aria-selected={option.value === value}\n className={`${css.selectOption}${option.value === value ? ` ${css.selectOptionSelected}` : ''}${index === activeIndex && !closing ? ` ${css.selectOptionActive}` : ''}`}\n onClick={() => { commit(index) }}\n >\n {option.label}\n </div>\n ))}\n </div>\n )\n : null}\n </div>\n )\n}\n\n/** A staged boolean field: 继承 / 开 / 关. */\nexport function BooleanField(props: FieldProps & {\n /** Copy for the inherit option. */\n inheritLabel: string\n /** Copy for the on option. */\n onLabel: string\n /** Copy for the off option. */\n offLabel: string\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <SelectField\n id={props.id}\n options={[\n { value: '', label: props.inheritLabel },\n { value: 'true', label: props.onLabel },\n { value: 'false', label: props.offLabel },\n ]}\n value={props.text}\n disabled={props.disabled}\n invalid={props.invalid}\n onEdit={props.onEdit}\n />\n <p className={css.hint}>{props.hint}</p>\n </div>\n )\n}\n\n/** A staged enumerated field rendered as a select. */\nexport function ChoiceField(props: FieldProps & {\n /** Copy for the inherit option (draft text is the empty string). */\n inheritLabel: string\n /** Choices rendered in order; `value` is the draft/stored text. */\n choices: ReadonlyArray<{ value: string; label: string }>\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <SelectField\n id={props.id}\n options={[{ value: '', label: props.inheritLabel }, ...props.choices]}\n value={props.text}\n disabled={props.disabled}\n invalid={props.invalid}\n onEdit={props.onEdit}\n />\n <p className={props.invalid ? css.invalid : css.hint}>\n {props.invalid ? props.invalidLabel : props.hint}\n </p>\n </div>\n )\n}\n","// Generated by scripts/sync-shared.mjs from shared/client/settings/settings-form.ts. Do not edit this copy; edit the shared source and run \"node scripts/sync-shared.mjs\".\n/**\n * Staged form model behind the plugin settings card. A card stages what the\n * user types and writes it only when they save — the settings write is a\n * durable, revision-fenced document mutation, so staging keeps what is on\n * screen exactly what a save would store. Family-shared slice inlined into\n * each plugin's client bundle; mirrors the official ui-plugin-config\n * card-store pattern.\n */\n\nimport type { SettingsScope, SettingsScopeSnapshot, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'\nimport { createSnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'\n\n/** The write one field's staged text performs when the card is saved. */\nexport type FieldWrite =\n | { kind: 'set'; value: unknown }\n | { kind: 'clear' }\n\n/** How one field converts between its stored value and its draft text. */\nexport interface FieldSpec {\n /** Field name inside the namespace section. */\n field: string\n /**\n * Whether the Host treats this field as a secret and redacts its value from\n * the read-back (role('secret') in the section schema). Redacted secrets are\n * never compared against the draft on save; the field lands when the scope\n * reports the write succeeded (its secret-set marker under the bridge), so\n * a successful secret save is not misreported as failed.\n */\n secret?: boolean\n /** Render a stored value as draft text; the empty string when the section carries none. */\n format: (value: unknown) => string\n /**\n * The write this draft text stages, or undefined when the text is not a\n * value this field accepts — which blocks the save rather than discarding it.\n */\n parse: (text: string) => FieldWrite | undefined\n}\n\n/** One field as the card renders it. */\nexport interface FieldState {\n /** Draft text the control renders. */\n text: string\n /** Whether saving would leave a user-layer entry for this field. */\n overridden: boolean\n /** Whether the draft is not a value this field accepts, which blocks saving. */\n invalid: boolean\n}\n\n/** Form state every plugin settings card shares. */\nexport interface CardShell {\n /** False while the namespace is still loading; the card renders nothing. */\n available: boolean\n /**\n * Whether the namespace is actually served to this client. False when the\n * Host deployment does not expose it (e.g. the official apiproxy settings\n * allowlist omits third-party namespaces): the card renders an explanation\n * instead of its form, so a missing namespace never looks like a missing\n * plugin.\n */\n exposed: boolean\n /** Whether the Host document accepts writes. */\n writable: boolean\n /** Whether the form holds edits that a save would write. */\n dirty: boolean\n /** Whether any staged draft is invalid, which blocks the save. */\n invalid: boolean\n /** Whether a save is crossing the wire. */\n saving: boolean\n /** Whether the last save did not land as staged; cleared by the next edit or save. */\n failed: boolean\n /**\n * The rejection code/message the Host returned for the last failed save,\n * surfaced next to the generic failure text. Undefined while no save has\n * failed (or the failure carried no server reason).\n */\n failedReason?: string\n}\n\n/** The write actions the card's slot entry injects. */\nexport interface CardActions {\n /** Stage draft text for one field. */\n edit: (field: string, text: string) => void\n /** Stage a clear, so saving lets the field re-inherit the composition layer. */\n resetField: (field: string) => void\n /** Write every staged edit, then re-seed from what the Host accepted. */\n save: () => void\n /** Drop every staged edit. */\n discard: () => void\n}\n\n/** One field's staged edit. */\ninterface StagedEdit {\n /** Draft text the control renders. */\n text: string\n /** True when this edit clears the field whatever text it shows. */\n clear: boolean\n}\n\n/** One staged edit resolved into the write a save performs. */\ninterface PlannedWrite {\n /** Field this entry writes. */\n field: string\n /** The durable write this entry performs, described for a batched scope. */\n op: BatchedWrite\n /** Perform the write and report whether the Host holds the staged value afterwards. */\n run: (() => Promise<boolean>) | undefined\n}\n\n/** One durable write a batched settings scope performs. */\nexport interface BatchedWrite {\n /** Field this entry writes. */\n field: string\n /** set stores a value; unset drops the leaf. */\n op: 'set' | 'unset'\n /** Value for op set (absent for unset). */\n value?: unknown\n}\n\n/** Per-field outcome of one batched scope write. */\nexport interface BatchedFieldResult {\n /** Field this entry writes. */\n field: string\n /** Whether the Host accepted this field's write (per the read-back view). */\n landed: boolean\n}\n\n/**\n * Result of a batched scope write. The bridge scope posts every planned write\n * in one /mutate so the Host validate hook judges baseURL+model together; a\n * batched refusal fails the whole save rather than per-field.\n */\nexport interface BatchResult {\n /** Whether the whole mutate was accepted. */\n ok: boolean\n /** Per-field success, in the request order (always present when ok). */\n fields: BatchedFieldResult[]\n /** Host rejection code (mutate refused). */\n code?: string\n /** Host rejection message (mutate refused). */\n message?: string\n}\n\n/** The optional batch surface the bridge scope adds over the SettingsScope contract. */\ninterface BatchedSettingsScope {\n /** Write every operation in one scope mutation, reporting per-field success. */\n mutate: (writes: BatchedWrite[]) => Promise<BatchResult>\n}\n\n/** Constraints a numeric field's accepted drafts must satisfy, mirroring the host schema. */\nexport interface NumberConstraints {\n /** The accepted value must be a whole number. */\n integer?: boolean\n /** The accepted value must be at least this. */\n min?: number\n}\n\n/** A whole- or decimal-number field. An empty draft clears the field; any other draft that is not a finite number within the constraints blocks the save. */\nexport function numberField(field: string, constraints: NumberConstraints = {}): FieldSpec {\n const { integer = false, min } = constraints\n return {\n field,\n format: value => typeof value === 'number' ? String(value) : '',\n parse: (text) => {\n const trimmed = text.trim()\n if (trimmed === '') return { kind: 'clear' }\n const parsed = Number(trimmed)\n if (!Number.isFinite(parsed)) return undefined\n if (integer && !Number.isInteger(parsed)) return undefined\n if (min !== undefined && parsed < min) return undefined\n return { kind: 'set', value: parsed }\n },\n }\n}\n\n/** A free-text field. An empty draft clears the field. */\nexport function textField(field: string): FieldSpec {\n return {\n field,\n format: value => typeof value === 'string' ? value : '',\n parse: (text) => {\n const trimmed = text.trim()\n return trimmed === '' ? { kind: 'clear' } : { kind: 'set', value: trimmed }\n },\n }\n}\n\n/**\n * A free-text field the Host treats as a secret and redacts from the read-back\n * (role('secret') in the section schema). The card still edits it like text,\n * but a save never compares the redacted value back and relies on the scope\n * reporting the write landed.\n */\nexport function secretField(field: string): FieldSpec {\n return { ...textField(field), secret: true }\n}\n\n/** A boolean field, edited through true/false draft text. */\nexport function booleanField(field: string): FieldSpec {\n return {\n field,\n format: value => typeof value === 'boolean' ? String(value) : '',\n parse: (text) => {\n const trimmed = text.trim()\n if (trimmed === '') return { kind: 'clear' }\n if (trimmed === 'true') return { kind: 'set', value: true }\n if (trimmed === 'false') return { kind: 'set', value: false }\n return undefined\n },\n }\n}\n\n/** An enumerated string field; only the listed choices are accepted. An empty draft clears the field. */\nexport function choiceField(field: string, choices: readonly string[]): FieldSpec {\n return {\n field,\n format: value => typeof value === 'string' && choices.includes(value) ? value : '',\n parse: (text) => {\n if (text === '') return { kind: 'clear' }\n return choices.includes(text) ? { kind: 'set', value: text } : undefined\n },\n }\n}\n\n/**\n * Stages one card's edits over one settings namespace and writes them on save.\n *\n * The Host is the only authority on whether a value was accepted — its\n * validators own the constraints no schema can express — so the outcome is\n * read back from the section rather than predicted here. A save that did not\n * land keeps its drafts, so the user can correct them instead of retyping.\n */\nexport class CardForm<T> {\n private readonly specs: Map<string, FieldSpec>\n private readonly staged = new Map<string, StagedEdit>()\n private readonly listeners = new Set<() => void>()\n /** The scope subscription installed in the constructor; released by dispose(). */\n private readonly disposeScope: () => void\n private disposed = false\n private saving = false\n private failed = false\n private failedReason: string | undefined\n\n /** @param scope - the bound settings scope for this card's namespace. */\n constructor(\n private readonly scope: SettingsScope<T>,\n specs: FieldSpec[],\n ) {\n this.specs = new Map(specs.map(spec => [spec.field, spec]))\n this.disposeScope = scope.subscribe(() => { this.publish() })\n }\n\n /**\n * Release the scope subscription and every bound store listener. The card\n * must call this on teardown; later calls are no-ops.\n */\n dispose(): void {\n if (this.disposed) return\n this.disposed = true\n this.disposeScope()\n this.listeners.clear()\n }\n\n /** Publish a projection of this form, rebuilt whenever the scope or a draft changes. */\n bind<S>(project: () => S): SnapshotStore<S> {\n const store = createSnapshotStore(project())\n this.listeners.add(() => { store.set(project()) })\n return store\n }\n\n /** Read the card-level state: what the Host serves, and what a save would do. */\n shell(): CardShell {\n const snapshot = this.scope.getSnapshot()\n const plan = this.plan()\n return {\n available: snapshot.status !== 'loading',\n exposed: snapshot.status === 'ready',\n writable: snapshot.writable,\n dirty: plan.length > 0,\n invalid: plan.some(item => item.run === undefined),\n saving: this.saving,\n failed: this.failed,\n ...this.failedReason === undefined ? {} : { failedReason: this.failedReason },\n }\n }\n\n /** Read one field's state from the effective section and its staged draft. */\n field(field: string): FieldState {\n const spec = this.specOf(field)\n const staged = this.staged.get(field)\n if (staged === undefined) {\n return { text: spec.format(this.sectionValue(field)), overridden: this.stored(field), invalid: false }\n }\n const write = staged.clear ? { kind: 'clear' as const } : spec.parse(staged.text)\n return {\n text: staged.text,\n overridden: write?.kind === 'set',\n invalid: write === undefined,\n }\n }\n\n /** The actions the card's slot registration injects. */\n actions(): CardActions {\n return {\n edit: (field, text) => { this.stage(field, { text, clear: false }) },\n resetField: (field) => {\n this.stage(field, { text: this.specOf(field).format(this.baseValue(field)), clear: true })\n },\n save: () => { void this.save() },\n discard: () => {\n if (this.staged.size === 0 && !this.failed) return\n this.staged.clear()\n this.failed = false\n this.failedReason = undefined\n this.publish()\n },\n }\n }\n\n /**\n * Write every staged edit, then re-seed from what the Host accepted.\n *\n * When the scope carries the optional batch surface (the dsh-web-ui\n * bridge scope), every planned write rides one mutation so cross-field\n * validate hooks (baseURL+model) judge the batch as a unit instead of\n * deadlocking on per-field writes. Otherwise the per-field loop runs.\n * A field lands only when the Host reports it held the staged value; a\n * landed field's draft is dropped, a failed one stays staged for the user.\n * @returns settlement after every write and the read-back.\n */\n async save(): Promise<void> {\n const plan = this.plan()\n const valid = plan.filter(item => item.run !== undefined)\n if (plan.length === 0 || this.saving || valid.length !== plan.length) return\n const plannedWrites = valid.map(item => item.op)\n // Snapshot the fields this save writes, so edits staged while it is in\n // flight survive: only the staged keys this save actually wrote are cleared.\n const fields = new Set(plan.map(item => item.field))\n this.saving = true\n this.failed = false\n this.failedReason = undefined\n this.publish()\n const landed = new Set<string>()\n const batch = this.batchedScope()\n if (batch !== undefined) {\n const result = await batch.mutate(plannedWrites)\n if (result.ok) {\n for (const field of result.fields) {\n if (field.landed) landed.add(field.field)\n }\n } else {\n this.failedReason = result.message\n }\n } else {\n for (const item of valid) {\n if (await item.run!()) landed.add(item.field)\n }\n }\n for (const field of fields) {\n if (landed.has(field)) this.staged.delete(field)\n }\n this.saving = false\n this.failed = landed.size !== fields.size\n this.publish()\n }\n\n /** The scope's batch surface when it supports one; undefined conservatively otherwise. */\n private batchedScope(): BatchedSettingsScope | undefined {\n const candidate = this.scope as unknown as BatchedSettingsScope | undefined\n return typeof candidate?.mutate === 'function' ? candidate : undefined\n }\n\n /**\n * Every staged edit a save would write. An entry whose draft is not a value\n * its field accepts carries no write: the form is still dirty, and the save\n * refuses rather than dropping the edit. A staged edit that matches the\n * effective section is not a write at all.\n * @returns the planned writes, in the order the fields were staged.\n */\n private plan(): PlannedWrite[] {\n const plan: PlannedWrite[] = []\n for (const [field, staged] of this.staged) {\n const spec = this.specOf(field)\n if (staged.clear) {\n if (this.stored(field)) plan.push({ field, op: { field, op: 'unset' }, run: () => this.clear(field) })\n continue\n }\n if (staged.text === spec.format(this.sectionValue(field))) continue\n const write = spec.parse(staged.text)\n if (write === undefined) plan.push({ field, op: { field, op: 'unset' }, run: undefined })\n else if (write.kind === 'clear') plan.push({ field, op: { field, op: 'unset' }, run: () => this.clear(field) })\n else plan.push({ field, op: { field, op: 'set', value: write.value }, run: () => this.store(field, write.value) })\n }\n return plan\n }\n\n private async clear(field: string): Promise<boolean> {\n await this.scope.unset(field)\n return !this.stored(field)\n }\n\n private async store(field: string, value: unknown): Promise<boolean> {\n await this.scope.set(field, value)\n // A redacted secret never appears in the user layer read-back; judging it\n // by value would misreport a successful secret save as failed. The bridge\n // reports secret writes through its secret-set markers (batch path); on\n // the per-field path the scope resolved, so the write is landed.\n if (this.specOf(field).secret) return true\n return this.userLayer()?.[field] === value\n }\n\n private stage(field: string, edit: StagedEdit): void {\n this.staged.set(field, edit)\n this.failed = false\n this.failedReason = undefined\n this.publish()\n }\n\n private specOf(field: string): FieldSpec {\n const spec = this.specs.get(field)\n // Every call site names a field this card declared; a missing one is a\n // wiring mistake that must not degrade into a silently inert control.\n if (spec === undefined) throw new Error(`settings card has no field ${field}`)\n return spec\n }\n\n private snapshotOf(): SettingsScopeSnapshot<T> {\n return this.scope.getSnapshot()\n }\n\n private sectionValue(field: string): unknown {\n return (this.snapshotOf().value as Record<string, unknown> | undefined)?.[field]\n }\n\n private baseValue(field: string): unknown {\n return (this.snapshotOf().base as Record<string, unknown> | undefined)?.[field]\n }\n\n private userLayer(): Record<string, unknown> | undefined {\n return this.snapshotOf().user as Record<string, unknown> | undefined\n }\n\n private stored(field: string): boolean {\n const user = this.userLayer()\n return user !== undefined && Object.hasOwn(user, field)\n }\n\n private publish(): void {\n for (const listener of this.listeners) listener()\n }\n}\n","/**\n * The pet settings card: pet selection plus display layout, bound to the\n * 'pet' settings namespace the host plugin registers. Rendered as an\n * always-open first-level settings page; the section wrapper below mounts it\n * as the content of the top-level 'settings.section' nav entry. The petId\n * choices come from the registry endpoint ('/api/pet/pets') — the same list\n * the sprite renders from — so the card carries no per-pet knowledge.\n */\n\nimport type { ReactNode } from 'react'\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { SettingsScope, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'\n// Type-only: pulls the settings-surface SlotMap merge (the 'settings.section' entry).\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport { PluginSettingsCard, ValueField, BooleanField, ChoiceField } from './PluginSettingsCard.tsx'\nimport { CardForm, booleanField, choiceField, numberField, type CardActions, type CardShell, type FieldState as CardFieldState } from './settings-form.ts'\nimport sectionCss from './settings-section.module.css'\n\n/** The pet's settings fields this card edits (the namespace's full schema). */\nexport interface PetSettings {\n /** Master switch for the plugin. */\n enabled?: boolean\n /** Master switch. */\n visible?: boolean\n /** Scale of the rendered pet in px (sprite cell height). */\n size?: number\n /** Horizontal inset from the viewport right edge, px. */\n right?: number\n /** Vertical inset from the viewport bottom edge, px. */\n bottom?: number\n /** Selected pet id (a registry entry). */\n petId?: string\n}\n\n/** What the pet settings card renders. */\nexport interface PetSettingsCardState extends CardShell {\n /** Plugin master switch. */\n enabled: CardFieldState\n /** Master switch. */\n visible: CardFieldState\n /** Pet scale. */\n size: CardFieldState\n /** Right inset. */\n right: CardFieldState\n /** Bottom inset. */\n bottom: CardFieldState\n /** Selected pet. */\n petId: CardFieldState\n /** Pet choices (registry ids + display names), loaded from the host. */\n petChoices: readonly { value: string; label: string }[]\n}\n\n/** The registration-side face the card's slot entry injects. */\nexport interface PetSettingsCardFace extends CardActions {\n hooks: {\n /** Card snapshot bound by the renderer as usePetSettingsCard. */\n petSettingsCard: SnapshotStore<PetSettingsCardState>\n }\n}\n\n/** One registry choice as served by '/api/pet/pets'. */\ninterface PetChoice {\n id: string\n displayName: string\n}\n\n/** Fetch the registry list (the same data the sprite renders from). */\nasync function fetchPetChoices(): Promise<PetChoice[]> {\n const response = await fetch('/api/pet/pets')\n if (!response.ok) throw new Error('pet pets failed: ' + response.status)\n return (await response.json()) as PetChoice[]\n}\n\n/** Bridges the 'pet' scope onto the card's staged form. */\nexport class PetSettingsCardController {\n private readonly form: CardForm<PetSettings>\n private readonly store: SnapshotStore<PetSettingsCardState>\n // The choice list rides a mutable array shared with the choiceField spec,\n // so loading the registry re-validates and re-formats the petId field\n // without rebuilding the form.\n private readonly petChoices: string[] = []\n private readonly petLabels = new Map<string, string>()\n private loaded = false\n private attempts = 0\n\n /** @param scope - the bound settings scope for the 'pet' namespace. */\n constructor(scope: SettingsScope<PetSettings>) {\n this.form = new CardForm(scope, [\n booleanField('enabled'),\n booleanField('visible'),\n numberField('size'),\n numberField('right'),\n numberField('bottom'),\n choiceField('petId', this.petChoices),\n ])\n this.store = this.form.bind(() => this.projection())\n void this.loadPets()\n }\n\n /** Resolve the registry choices once (retried a few times on failure). */\n private async loadPets(): Promise<void> {\n if (this.loaded) return\n try {\n const list = await fetchPetChoices()\n this.petChoices.splice(0, this.petChoices.length, ...list.map(choice => choice.id))\n for (const choice of list) this.petLabels.set(choice.id, choice.displayName)\n this.loaded = true\n this.store.set(this.projection())\n } catch {\n this.attempts += 1\n if (this.attempts < 3) {\n window.setTimeout(() => { void this.loadPets() }, 3000)\n }\n }\n }\n\n private projection(): PetSettingsCardState {\n return {\n ...this.form.shell(),\n enabled: this.form.field('enabled'),\n visible: this.form.field('visible'),\n size: this.form.field('size'),\n right: this.form.field('right'),\n bottom: this.form.field('bottom'),\n petId: this.form.field('petId'),\n petChoices: this.petChoices.map(id => ({ value: id, label: this.petLabels.get(id) ?? id })),\n }\n }\n\n /**\n * Build the face the card's slot registration injects.\n * @returns the card's snapshot and its form actions.\n */\n inject(): PetSettingsCardFace {\n return { hooks: { petSettingsCard: this.store }, ...this.form.actions() }\n }\n\n /**\n * Release the card's scope subscription and bound stores; the slot\n * disposer calls this on teardown.\n */\n dispose(): void {\n this.form.dispose()\n }\n}\n\n/** Props the renderer binds for the pet settings card. */\nexport type PetSettingsCardProps =\n PropsLocale<'pet'>\n & InjectFace<PetSettingsCardFace>\n\n/**\n * Render the pet settings card.\n * @param props - locale copy, the card snapshot, and its form actions.\n * @returns the card.\n */\nexport function PetSettingsCard(props: PetSettingsCardProps) {\n const { t } = props\n const state = props.usePetSettingsCard(snapshot => snapshot)\n const disabled = !state.writable\n const fieldProps = {\n overriddenLabel: t('settings.overridden'),\n resetLabel: t('settings.reset'),\n invalidLabel: t('settings.invalidNumber'),\n disabled,\n }\n return (\n <PluginSettingsCard\n t={t}\n titleKey=\"settings.title\"\n descriptionKey=\"settings.description\"\n state={state}\n onSave={props.save}\n onDiscard={props.discard}\n alwaysOpen\n >\n <BooleanField\n id=\"settings-pet-enabled\"\n label={t('settings.enabled')}\n hint={t('settings.enabledHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.enabled}\n onEdit={(text) => { props.edit('enabled', text) }}\n onReset={() => { props.resetField('enabled') }}\n />\n <ChoiceField\n id=\"settings-pet-pet\"\n label={t('settings.pet')}\n hint={t('settings.petHint')}\n inheritLabel={t('settings.inherit')}\n {...fieldProps}\n {...state.petId}\n choices={state.petChoices}\n onEdit={(text) => { props.edit('petId', text) }}\n onReset={() => { props.resetField('petId') }}\n />\n <BooleanField\n id=\"settings-pet-visible\"\n label={t('settings.visible')}\n hint={t('settings.visibleHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.visible}\n onEdit={(text) => { props.edit('visible', text) }}\n onReset={() => { props.resetField('visible') }}\n />\n <ValueField\n id=\"settings-pet-size\"\n label={t('settings.size')}\n hint={t('settings.sizeHint')}\n numeric\n {...fieldProps}\n {...state.size}\n onEdit={(text) => { props.edit('size', text) }}\n onReset={() => { props.resetField('size') }}\n />\n <ValueField\n id=\"settings-pet-right\"\n label={t('settings.right')}\n hint={t('settings.rightHint')}\n numeric\n {...fieldProps}\n {...state.right}\n onEdit={(text) => { props.edit('right', text) }}\n onReset={() => { props.resetField('right') }}\n />\n <ValueField\n id=\"settings-pet-bottom\"\n label={t('settings.bottom')}\n hint={t('settings.bottomHint')}\n numeric\n {...fieldProps}\n {...state.bottom}\n onEdit={(text) => { props.edit('bottom', text) }}\n onReset={() => { props.resetField('bottom') }}\n />\n </PluginSettingsCard>\n )\n}\n\n/** Props the settings section binds for the pet card page. */\nexport type PetSettingsSectionProps =\n PropsRuntime<'settings.section'>\n & PropsLocale<'pet'>\n & InjectFace<PetSettingsCardFace>\n\n/** Render the pet settings card as a first-level settings page. */\nexport function PetSettingsSection(props: PetSettingsSectionProps): ReactNode {\n const { t, usePetSettingsCard, save, discard, edit, resetField } = props\n return (\n <ul className={sectionCss.sectionList}>\n <PetSettingsCard t={t} usePetSettingsCard={usePetSettingsCard} save={save} discard={discard} edit={edit} resetField={resetField} />\n </ul>\n )\n}\n","/**\n * dsh-pet locale dictionaries (zh/en).\n * @module @linxin666/dsh-pet/client/locales\n */\n\n/** Dictionary namespace this package registers. */\nexport const NS = 'pet'\n\n/** Chinese copy. */\nexport const zh = {\n 'pet.feed': '喂食',\n 'pet.hide': '隐藏',\n 'pet.rename': '改名',\n 'pet.confirm': '确定',\n 'pet.namePlaceholder': '输入新名字',\n 'pet.summon': '召唤{name}',\n 'pet.rank': '亲密度 {rank}',\n 'pet.points': '{points} 点',\n 'pet.treats': '小鱼干 ×{n}',\n 'pet.state.loading': '宠物正在赶来…',\n 'pet.state.error': '宠物迷路了(连接失败)',\n 'pet.openSessionHint': '点击跳转到对应会话',\n 'pet.moreSessions': '展开其余 {n} 个会话的气泡',\n 'pet.collapseSessions': '收起会话气泡',\n // 一级设置页(settings.section 席位)。\n 'settings.title': '宠物',\n 'settings.description': '选择宠物并调整它的显示布局。',\n 'settings.pet': '宠物',\n 'settings.petHint': '选择显示哪只宠物;每只宠物独立命名,可在宠物悬浮面板改名。',\n 'settings.enabled': '启用宠物',\n 'settings.enabledHint': '关闭后隐藏宠物并停止轮询,可在设置里重新启用。',\n 'settings.visible': '显示宠物',\n 'settings.visibleHint': '关闭后宠物隐藏,可从聊天输入区重新召唤。',\n 'settings.size': '大小(px)',\n 'settings.sizeHint': '精灵单元高度,范围 32–512。',\n 'settings.right': '距右侧(px)',\n 'settings.rightHint': '距视口右边缘的水平内缩距离。',\n 'settings.bottom': '距底部(px)',\n 'settings.bottomHint': '距视口底边的垂直内缩距离。',\n 'settings.inherit': '继承',\n 'settings.on': '开',\n 'settings.off': '关',\n 'settings.overridden': '已覆盖',\n 'settings.reset': '恢复默认',\n 'settings.notExposed': '当前 DSH 版本未向设置页暴露本插件的配置命名空间,表单不可用。可编辑 ~/.dsh/settings.yaml 直接配置,或为 dsh-host-apiproxy 的 WEB_SETTINGS_NAMESPACES 白名单补充本命名空间后重启。',\n 'settings.readOnly': '当前部署的设置只读。',\n 'settings.expand': '展开设置',\n 'settings.collapse': '收起设置',\n 'settings.save': '保存',\n 'settings.saving': '保存中…',\n 'settings.discard': '放弃',\n 'settings.unsaved': '未保存',\n 'settings.saveFailed': '部署未接受这些值,已保留供你修改。',\n 'settings.invalidNumber': '请输入数字,留空则使用默认值。',\n} as const\n\n/** English copy. */\nexport const en = {\n 'pet.feed': 'Feed',\n 'pet.hide': 'Hide',\n 'pet.rename': 'Rename',\n 'pet.confirm': 'OK',\n 'pet.namePlaceholder': 'Enter a new name',\n 'pet.summon': 'Summon {name}',\n 'pet.rank': 'Affinity {rank}',\n 'pet.points': '{points} pts',\n 'pet.treats': 'Treats ×{n}',\n 'pet.state.loading': 'The pet is on its way…',\n 'pet.state.error': 'The pet is lost (connection failed)',\n 'pet.openSessionHint': 'Click to jump to this session',\n 'pet.moreSessions': 'Expand {n} more session bubbles',\n 'pet.collapseSessions': 'Collapse session bubbles',\n // First-level settings section (the `settings.section` seat).\n 'settings.title': 'Pet',\n 'settings.description': 'Pick a pet and tune its display layout.',\n 'settings.pet': 'Pet',\n 'settings.petHint': 'Choose which pet shows. Names are stored per pet; rename from the pet hover panel.',\n 'settings.enabled': 'Enable the pet',\n 'settings.enabledHint': 'When off, the pet hides and polling stops; re-enable it here.',\n 'settings.visible': 'Show the pet',\n 'settings.visibleHint': 'When off, the pet hides; summon it again from the input row.',\n 'settings.size': 'Size (px)',\n 'settings.sizeHint': 'Sprite cell height, 32\\u2013512.',\n 'settings.right': 'Right inset (px)',\n 'settings.rightHint': 'Horizontal inset from the viewport right edge.',\n 'settings.bottom': 'Bottom inset (px)',\n 'settings.bottomHint': 'Vertical inset from the viewport bottom edge.',\n 'settings.inherit': 'Inherit',\n 'settings.on': 'On',\n 'settings.off': 'Off',\n 'settings.overridden': 'Overridden',\n 'settings.reset': 'Reset to default',\n 'settings.notExposed': 'This DSH version does not expose this plugin\\'s settings namespace to the configuration page, so the form is unavailable. Edit ~/.dsh/settings.yaml directly, or add the namespace to dsh-host-apiproxy\\'s WEB_SETTINGS_NAMESPACES allowlist and restart.',\n 'settings.readOnly': 'This deployment stores settings read-only.',\n 'settings.expand': 'Show settings',\n 'settings.collapse': 'Hide settings',\n 'settings.save': 'Save',\n 'settings.saving': 'Saving\\u2026',\n 'settings.discard': 'Discard',\n 'settings.unsaved': 'Unsaved',\n 'settings.saveFailed': 'The deployment did not accept these values; they were left for you to correct.',\n 'settings.invalidNumber': 'Enter a number, or leave blank to use the default.',\n} as const\n\n/** Key union for this namespace. */\nexport type PetKey = keyof typeof zh\n\n/** The settings-card slice of the pet dictionary. */\nexport type SettingsCardKey = PetKey\n\n/**\n * Active dictionary, picked by the document language at call time. The pet\n * mounts as a global floating surface (not a session-scoped slot), so it has\n * no framework locale seat and resolves its copy the same tiny way the\n * task-board's DOM-injected surface does.\n */\nexport function dictionary(): Record<PetKey, string> {\n const lang = typeof document !== 'undefined' ? document.documentElement.lang : 'zh'\n return lang.toLowerCase().startsWith('en') ? en : zh\n}\n\n/**\n * Translate a key with optional `{name}` template params. Mirrors the slot\n * `Translate` contract `(key, params?) => string` so it can be handed to the\n * same components that used to receive the framework-injected `t` seat. The\n * key is typed loosely (`string`) so the function is assignable to the slot's\n * `TranslateNS<'pet'>` (whose key domain also spans the shared common\n * vocabulary); a missing key degrades to the key itself rather than throwing.\n */\nexport function t(key: string, params?: Record<string, unknown>): string {\n let text: string = (dictionary() as Record<string, string>)[key] ?? key\n if (params !== undefined) {\n for (const [name, value] of Object.entries(params)) {\n text = text.replaceAll(`{${name}}`, String(value))\n }\n }\n return text\n}\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** dsh-pet UI copy. */\n pet: PetKey\n }\n}\n","/**\n * dsh-pet browser half — mounts the selected pet as a global floating\n * surface and drives it from the host's same-origin '/api/pet/*' JSON\n * endpoints: fetch the registry list once, poll the host snapshot (~2 s),\n * forward interactions, persist drag positions. The pet is host-global (no\n * session dimension), so it mounts directly onto 'document.body' via a\n * single React root rather than a session-scoped slot — on the\n * new-conversation screen no session exists, and a dock-mounted pet would\n * vanish there (issue #48). When the pet is hidden the entry becomes a\n * fixed-position summon button.\n * @module @linxin666/dsh-pet/client\n */\n\nimport type { ClientContext, ISessions, SessionId, SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-runtime/client'\n// Type-only: pulls the locale plugin's Context merge (ctx.locale).\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\n// Type-only: pulls the settings-surface Context merge (ctx.settingsScope).\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type { PetDisplayConfig } from '../persist.ts'\nimport type { PetInteractResult, PetStateView } from '../service.ts'\nimport type { PetInteraction } from '../affinity.ts'\nimport type { PetDefinition } from '../registry.ts'\nimport { createElement } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { createPetStore, type PetStoreInstance } from './pet-store.ts'\nimport { PetDockEntry, type PetInjected } from './PetDockEntry.tsx'\nimport { PetSettingsSection, PetSettingsCardController, type PetSettings } from './PetSettingsCard.tsx'\nimport { NS, en, zh, t } from './locales.ts'\n\n/** The host pet API as the browser sees it (same-origin JSON endpoints). */\ninterface PetHttpApi {\n state(): Promise<PetStateView>\n pets(): Promise<PetDefinition[]>\n interact(kind: PetInteraction): Promise<PetInteractResult>\n setVisible(visible: boolean): Promise<{ ok: true; display: PetDisplayConfig }>\n setConfig(patch: Partial<PetDisplayConfig>): Promise<{ ok: true; display: PetDisplayConfig }>\n setName(name: string): Promise<{ ok: true; name: string } | { ok: false; error: string }>\n setPet(petId: string): Promise<{ ok: true; petId: string } | { ok: false; error: string }>\n}\n\n/** Same-origin JSON fetch helper (GET without body, POST with JSON body). */\nasync function petFetch<T>(path: string, body?: unknown): Promise<T> {\n const response = await fetch(path, body === undefined\n ? {}\n : {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n })\n if (!response.ok) {\n throw new Error('pet ' + path + ' failed: ' + response.status)\n }\n return (await response.json()) as T\n}\n\n/** The live host API instance (always defined; failures surface per call). */\nconst petApi: PetHttpApi = {\n state: () => petFetch('/api/pet/state'),\n pets: () => petFetch('/api/pet/pets'),\n interact: (kind) => petFetch('/api/pet/interact', { kind }),\n setVisible: (visible) => petFetch('/api/pet/set-visible', { visible }),\n setConfig: (patch) => petFetch('/api/pet/set-config', patch),\n setName: (name) => petFetch('/api/pet/set-name', { name }),\n setPet: (petId) => petFetch('/api/pet/set-pet', { petId }),\n}\n\n/** Poll interval for the host snapshot. */\nconst POLL_MS = 2000\n\n/** Settings namespace the pet settings card edits (the Host plugin registers it). */\nconst PET_SETTINGS_NS = 'pet'\n\n/** Required services (sessions powers bubble-to-session navigation). */\nexport const inject = ['slots', 'locale', 'connection', 'settingsScope', 'remote', 'sessions']\n\n/** Re-exported for consumers that type against the injected face. */\nexport type { PetInjected, PetDockEntryProps } from './PetDockEntry.tsx'\nexport type { PetSpriteProps } from './PetSprite.tsx'\nexport type { PetUiState, PetFeedback } from './pet-store.ts'\nexport type { PetSettingsCardFace, PetSettingsCardState } from './PetSettingsCard.tsx'\nexport type { PetSettingsSectionProps } from './PetSettingsCard.tsx'\nexport type { PetDefinition } from '../registry.ts'\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n /**\n * Optional rc.6 compatibility binder provided by dsh-web-ui-settings;\n * absent when that group plugin is not installed, so callers fall back to\n * the official settings scope.\n */\n webUiSettings?: { bind<S>(spec: SettingsScopeSpec<S>): SettingsScope<S> }\n }\n}\n\n/**\n * Client plugin body: register dictionaries, mount the global pet entry and\n * poll loop while the plugin is enabled, and seat the settings card as a\n * first-level settings section.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'pet: dictionaries')\n\n const binder = ctx.get('webUiSettings') ?? ctx.settingsScope\n const settingsScope = binder.bind<PetSettings>({ namespace: PET_SETTINGS_NS })\n const enabled = (): boolean => {\n const snapshot = settingsScope.getSnapshot()\n return snapshot.status === 'ready'\n ? snapshot.value?.enabled ?? true\n : snapshot.status === 'unavailable'\n }\n\n // First-level settings section: one staged form over the 'pet' settings\n // namespace, registered as a top-level settings page. The controller loads\n // the petId choices from the registry endpoint itself.\n const petSettings = new PetSettingsCardController(settingsScope)\n ctx.slots.inject('settings.section', () => {\n const unregister = ctx.slots.register({\n name: 'settings.section',\n id: 'pet',\n order: 130,\n label: () => ctx.locale.bind('pet')('settings.title'),\n locale: 'pet',\n inject: () => petSettings.inject(),\n }, PetSettingsSection)\n return () => {\n petSettings.dispose()\n unregister()\n }\n })\n\n // The global pet entry, its store, and the poll loop live while the plugin\n // is enabled; toggling the setting off hides the pet and stops polling.\n let disposeUi: (() => void) | undefined\n const syncUi = (): void => {\n if (enabled() && disposeUi === undefined) {\n // ONE store instance for the whole app, owned by this apply body. The\n // pet is host-global (state/display/interactions are /api/pet/*\n // endpoints with no session dimension), so the slot system's per-session\n // store scoping would only reset the pet on session switches and leave\n // it stateless on the new-conversation screen (no session to scope by).\n const petStore: PetStoreInstance = createPetStore().create()\n const setSnapshot = petStore.actions.setSnapshot\n const setPets = petStore.actions.setPets\n const setState = petStore.actions.setState\n const setFeedback = petStore.actions.setFeedback\n\n // The registry list is fetched lazily with retries baked into the poll\n // cycle: until it lands, the dock entry renders nothing and every 2s\n // tick tries again. After it lands, one list feeds both the sprite and\n // the settings card's choices.\n let petsLoaded = false\n // Latest-wins guard: the 2s tick, visibility recovery, and\n // interaction-triggered refreshes can overlap; only the newest\n // response may publish, so a slow older one can never roll the\n // snapshot back.\n let stateSeq = 0\n const pollNow = (): void => {\n if (!petsLoaded) {\n petApi.pets().then((list) => {\n petsLoaded = true\n setPets(list)\n }, () => {\n // Retry on the next poll tick.\n })\n }\n const seq = stateSeq + 1\n stateSeq = seq\n petApi.state().then((snapshot) => {\n if (seq !== stateSeq) return\n setSnapshot(snapshot)\n }, () => {\n if (seq !== stateSeq) return\n setState('error', 'pet.state transport error')\n })\n }\n\n const disposePoll = ctx.effect(() => {\n // Poll only while the tab is visible: the host snapshot does not\n // change while the page is hidden, so a background interval would\n // only burn RPCs (browser throttling is an unreliable backstop).\n // Coming back to the tab refreshes the pet immediately instead of\n // waiting out the next 2 s cycle.\n let timer: number | undefined\n const stop = (): void => {\n if (timer !== undefined) {\n window.clearInterval(timer)\n timer = undefined\n }\n }\n const start = (): void => {\n if (timer === undefined && document.visibilityState === 'visible') {\n timer = window.setInterval(pollNow, POLL_MS)\n }\n }\n const onVisibility = (): void => {\n if (document.visibilityState === 'visible') {\n pollNow()\n start()\n } else {\n stop()\n }\n }\n start()\n document.addEventListener('visibilitychange', onVisibility)\n return () => {\n stop()\n document.removeEventListener('visibilitychange', onVisibility)\n }\n }, 'pet: poll')\n\n // Clicking a session bubble jumps the GUI to that session. A bubble\n // can outlive its disposed session by one poll tick, and the sessions\n // service fails loud on unknown ids, so consult the live list first.\n // The pet's type program also loads the host-side dsh-session package\n // through the service types, whose Context merge declares a different\n // 'sessions' face; pin the browser runtime's outward face here.\n const sessions = ctx.sessions as unknown as ISessions\n const openSession = (sessionId: string): void => {\n const list = sessions.list.getSnapshot()\n if (list.byId[sessionId as SessionId] === undefined) return\n sessions.open(sessionId as SessionId)\n }\n\n const injected = (): PetInjected => ({\n store: petStore,\n ensure: pollNow,\n openSession,\n pet: () => {\n petApi.interact('pet').then((result) => {\n setFeedback({\n text: result.reaction,\n kind: 'pet',\n at: Date.now(),\n })\n }, () => {\n // Ignore transport errors on interactions; the next poll resyncs.\n })\n },\n feed: () => {\n petApi.interact('feed').then((result) => {\n setFeedback({\n text: result.reaction,\n kind: 'feed',\n at: Date.now(),\n })\n }, () => {\n // Ignore transport errors on interactions; the next poll resyncs.\n })\n },\n hide: () => {\n petApi.setVisible(false).then(() => {\n pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n summon: () => {\n petApi.setVisible(true).then(() => {\n pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n dragEnd: (right, bottom) => {\n petApi.setConfig({ right, bottom }).then(() => {\n pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n rename: (name) => {\n petApi.setName(name).then((result) => {\n if (result.ok) pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n feedbackDone: () => {\n setFeedback(null)\n },\n })\n\n // The pet is host-global (its state/display/interactions have no session\n // dimension), and the official rc.6 shell declares no root-scoped slot\n // for a global floating surface — the dock is session-scoped, so a pet\n // mounted there would vanish on the new-conversation screen (issue #48).\n // The entry therefore mounts straight onto document.body via a single\n // React root for the page lifetime: PetSprite portals itself to body\n // when visible, and the hidden-state summon button is fixed-positioned.\n const container = document.createElement('div')\n container.dataset.dshPetRoot = ''\n container.dataset.dshPlugin = 'pet'\n document.body.appendChild(container)\n const petRoot = createRoot(container)\n petRoot.render(createElement(PetDockEntry, { ...injected(), t }))\n\n disposeUi = () => {\n petRoot.unmount()\n container.remove()\n disposePoll()\n disposeUi = undefined\n }\n } else if (!enabled() && disposeUi !== undefined) {\n disposeUi()\n disposeUi = undefined\n }\n }\n settingsScope.subscribe(syncUi)\n syncUi()\n}\n"],"x_google_ignoreList":[1],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["styles","styles","css","sectionCss"],"sources":["../../../packages/dsh-pet/src/client/pet-store.ts","../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","../../../packages/dsh-pet/src/state.ts","../../../packages/dsh-pet/src/client/spritesheet.ts","../../../packages/dsh-pet/src/client/sequences.ts","../../../packages/dsh-pet/src/client/PetSprite.tsx","../../../packages/dsh-pet/src/client/PetDockEntry.tsx","../../../packages/dsh-pet/src/client/PluginSettingsCard.tsx","../../../packages/dsh-pet/src/client/settings-form.ts","../../../packages/dsh-pet/src/client/PetSettingsCard.tsx","../../../packages/dsh-pet/src/client/locales.ts","../../../packages/dsh-pet/src/client/index.ts"],"sourcesContent":["/**\n * Browser-side pet store: the pet state snapshot plus transient UI feedback\n * (reaction bubbles), written only through the store's audit actions. The\n * RPC polling and interactions live in the plugin apply body; components\n * only ever read snapshots.\n * @module @linxin666/dsh-pet/client/pet-store\n */\n\nimport { defineStore } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { EngineStoreHandle, EngineStoreInstance } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { PetStateView } from '../service.ts'\nimport type { PetInteraction } from '../affinity.ts'\nimport type { PetDefinition } from '../registry.ts'\n\n/** One transient reaction bubble on the pet. */\nexport interface PetFeedback {\n /** Bubble copy. */\n text: string\n /** Interaction kind driving the reaction animation. */\n kind: PetInteraction | 'none'\n /** Epoch ms when the bubble appeared (for expiry). */\n at: number\n}\n\n/** Pet UI state as consumers see it. */\nexport interface PetUiState {\n /** Latest host snapshot; null before the first successful fetch. */\n snapshot: PetStateView | null\n /** The registry list the host serves (atlas URLs + geometry + tracks). */\n pets: PetDefinition[]\n /** Fetch lifecycle. */\n state: 'loading' | 'ready' | 'error'\n /** Transport error message (for the debug surface), when any. */\n error: string | null\n /** Active reaction bubble, if any. */\n feedback: PetFeedback | null\n}\n\n/** Store write set. */\nexport type PetUiActions = {\n /** Replace the host snapshot (poll result). */\n setSnapshot: (draft: PetUiState, snapshot: PetStateView) => void\n /** Replace the registry list. */\n setPets: (draft: PetUiState, pets: PetDefinition[]) => void\n /** Mark the fetch lifecycle. */\n setState: (draft: PetUiState, state: PetUiState['state'], error: string | null) => void\n /** Show a reaction bubble. */\n setFeedback: (draft: PetUiState, feedback: PetFeedback | null) => void\n}\n\n/** Create the pet store handle (apply world only; never module-level). */\nexport function createPetStore(): EngineStoreHandle<PetUiState, PetUiActions> {\n return defineStore({\n init: (): PetUiState => ({\n snapshot: null,\n pets: [],\n state: 'loading',\n error: null,\n feedback: null,\n }),\n actions: {\n setSnapshot: (draft, snapshot) => {\n draft.snapshot = snapshot\n draft.state = 'ready'\n draft.error = null\n },\n setPets: (draft, pets) => {\n draft.pets = pets\n },\n setState: (draft, state, error) => {\n draft.state = state\n draft.error = error\n },\n setFeedback: (draft, feedback) => {\n draft.feedback = feedback\n },\n },\n })\n}\n\nexport type { PetInteraction }\n\n/**\n * A live pet store instance (one per host, owned by the plugin apply body —\n * the pet itself is host-global, so its UI state must not ride the slot\n * system's per-session store scoping).\n */\nexport type PetStoreInstance = EngineStoreInstance<PetUiState, PetUiActions>\n\n","function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","/**\n * Pet state machine — pure, clock-injected. Maps official DSH session activity\n * and the legacy `activity/status` vocabulary onto the 9-state Codex pet\n * animation contract, plus turn-end celebration and no-session idle.\n *\n * The machine is deliberately dumb: it holds the last input phase, the\n * animation decision, and a one-shot \"celebration\" window after `done` so the\n * pet visibly jumps before settling back to idle. Everything here is a pure\n * function of (input, nowMs); persistence and RPC live in the service.\n * @module @linxin666/dsh-pet/state\n */\n\n/** Activity phases understood by the pet host. */\nexport type ActivityPhase = 'idle' | 'waiting' | 'thinking' | 'tool' | 'review' | 'done' | 'failed'\n\n/** The Codex-compatible 9-state animation contract (spritesheet rows). */\nexport type PetAnimation =\n | 'idle'\n | 'running-right'\n | 'running-left'\n | 'waving'\n | 'jumping'\n | 'failed'\n | 'waiting'\n | 'running'\n | 'review'\n\n/** One input snapshot consumed by the machine. */\nexport interface PetStateInput {\n /** Current activity phase of the active session. */\n phase: ActivityPhase\n /** Human-readable status line (plain text). */\n line?: string\n /** Playful phrase from the activity tracker, when any. */\n phrase?: string\n}\n\n/** Animation decision plus the copy the pet should show. */\nexport interface PetStateSnapshot {\n /** Which animation track to play. */\n animation: PetAnimation\n /** Optional status bubble copy (line or phrase), shown while active. */\n bubble?: string\n /** Wall-clock ms this animation started (client can sync loops). */\n animationStartedAt: number\n /** Raw phase, for debugging and client-side rendering decisions. */\n phase: ActivityPhase\n /** True when there is an active session (pet mounted). */\n sessionActive: boolean\n}\n\n/** Machine configuration. */\nexport interface PetStateConfig {\n /** Celebration window after `done` before settling to idle, ms (default 2400). */\n celebrateMs: number\n /** Failure display window before settling to idle, ms (default 2400). */\n failureMs: number\n}\n\nexport const defaultPetStateConfig: PetStateConfig = { celebrateMs: 2400, failureMs: 2400 }\n\n/**\n * Map one activity phase onto the animation contract.\n * - thinking → `running` and tool → `running-right` (focused work).\n * - review → `review` while answer text is streaming.\n * - waiting → `waiting` (expectant pose, needs user input).\n * - done → `jumping` (celebration), then back to `idle` after the window.\n * - failed → `failed` briefly, then back to `idle`.\n * - idle → `idle` (calm breathing loop).\n */\nexport function animationForPhase(phase: ActivityPhase): PetAnimation {\n switch (phase) {\n case 'thinking': return 'running'\n case 'tool': return 'running-right'\n case 'review': return 'review'\n case 'waiting': return 'waiting'\n case 'done': return 'jumping'\n case 'failed': return 'failed'\n case 'idle': return 'idle'\n }\n}\n\n/** The spritesheet row index for one animation track. */\nexport function rowOf(animation: PetAnimation): number {\n const rows: Record<PetAnimation, number> = {\n 'idle': 0,\n 'running-right': 1,\n 'running-left': 2,\n 'waving': 3,\n 'jumping': 4,\n 'failed': 5,\n 'waiting': 6,\n 'running': 7,\n 'review': 8,\n }\n return rows[animation]\n}\n\n/**\n * PetStateMachine — one instance per host process. Holds only the latest\n * input snapshot and terminal-state timing; no storage, no side effects.\n */\nexport class PetStateMachine {\n private phase: ActivityPhase = 'idle'\n private line: string | undefined\n private phrase: string | undefined\n private sessionActive = false\n private doneAt: number | undefined\n private failedAt: number | undefined\n private readonly config: PetStateConfig\n\n constructor(\n config: Partial<PetStateConfig> = defaultPetStateConfig,\n private readonly now: () => number = Date.now,\n ) {\n this.config = { ...defaultPetStateConfig, ...config }\n }\n\n /** Consume one projected activity update. */\n onActivityStatus(input: PetStateInput): void {\n this.phase = input.phase\n this.line = input.line\n this.phrase = input.phrase\n this.doneAt = input.phase === 'done' ? this.now() : undefined\n this.failedAt = input.phase === 'failed' ? this.now() : undefined\n }\n\n /** A session became the active one (or a fresh session started). */\n onSessionActive(): void {\n this.sessionActive = true\n }\n\n /** The active session was disposed (or none left). */\n onSessionDisposed(): void {\n this.sessionActive = false\n this.phase = 'idle'\n this.line = undefined\n this.phrase = undefined\n this.doneAt = undefined\n this.failedAt = undefined\n }\n\n /** Render the current animation decision. */\n render(): PetStateSnapshot {\n const nowMs = this.now()\n let animation = animationForPhase(this.phase)\n const doneSettled = this.phase === 'done'\n && this.doneAt !== undefined\n && nowMs - this.doneAt >= this.config.celebrateMs\n const failedSettled = this.phase === 'failed'\n && this.failedAt !== undefined\n && nowMs - this.failedAt >= this.config.failureMs\n if (doneSettled || failedSettled) animation = 'idle'\n // Settled sessions never bubble: idle (e.g. an aborted/stopped turn),\n // completed celebration expiry, and failed display expiry all fall silent.\n const settled = this.phase === 'idle' || doneSettled || failedSettled\n const bubble = settled ? undefined : this.phrase ?? this.line\n return {\n animation,\n ...(bubble === undefined ? {} : { bubble }),\n animationStartedAt: nowMs,\n phase: this.phase,\n sessionActive: this.sessionActive,\n }\n }\n}\n","/**\n * Spritesheet geometry helpers — parameterized by the pet definition the\n * host serves over '/api/pet/pets', so the browser half renders any registry\n * entry without per-pet code. The per-track tables (frames, durations, loop,\n * fallback) also come from the registry; these helpers only place frames,\n * guard track lengths, and map the fixed 9-row animation contract.\n * @module @linxin666/dsh-pet/client/spritesheet\n */\n\nimport { rowOf, type PetAnimation } from '../state.ts'\nimport type { PetCell, PetTrackDef } from '../registry.ts'\n\n/** Animation track shape the frame loop consumes. */\nexport type TrackDef = PetTrackDef\n\n/** Row index of one animation track (the fixed 9-row contract). */\nexport function rowOfTrack(animation: PetAnimation): number {\n // The table itself lives in state.ts (rowOf) — the single source of truth.\n return rowOf(animation)\n}\n\n/**\n * Background-position (px) of one frame cell within the scaled atlas.\n * The background image is scaled by `scale` (element size ÷ cell size), and\n * background-position offsets are applied in SCALED coordinates — using raw\n * atlas coordinates here would drift each frame by the scale factor and\n * render torn/overlapping frames.\n */\nexport function framePosition(cell: PetCell, row: number, col: number, scale = 1): { x: number; y: number } {\n return { x: -col * cell.width * scale, y: -row * cell.height * scale }\n}\n\n/**\n * Trim a track to the actual frame count of its row (the manifest's per-row\n * counts are authoritative; this is a last-line guard against a definition\n * whose row count disagrees with its track table). A row with 0 detected\n * frames degrades to the first frame so the pet never renders blank.\n */\nexport function trimTrack(track: TrackDef, frameCount: number): TrackDef {\n const n = Math.max(1, Math.min(frameCount, track.frames.length, track.durations.length))\n return {\n frames: track.frames.slice(0, n),\n durations: track.durations.slice(0, n),\n loop: track.loop,\n ...(track.fallback === undefined ? {} : { fallback: track.fallback }),\n }\n}\n","/** Pure timing helpers for manifest-defined scene animation sequences. */\n\nimport type { PetTrackDef } from '../registry.ts'\nimport type { PetAnimation } from '../state.ts'\n\nexport interface SequenceFrame {\n animation: PetAnimation\n frameIndex: number\n}\n\n/** Resolve the active track and frame after elapsed milliseconds of a looping sequence. */\nexport function sequenceFrameAt(\n sequence: readonly PetAnimation[],\n tracks: Record<PetAnimation, PetTrackDef>,\n elapsedMs: number,\n): SequenceFrame {\n const itemDurations = sequence.map(animation => tracks[animation].durations.reduce((sum, value) => sum + value, 0))\n const sequenceDuration = itemDurations.reduce((sum, value) => sum + value, 0)\n let offset = Math.max(0, elapsedMs) % sequenceDuration\n let itemIndex = 0\n while (itemIndex < sequence.length - 1 && offset >= itemDurations[itemIndex]!) {\n offset -= itemDurations[itemIndex]!\n itemIndex += 1\n }\n const animation = sequence[itemIndex]!\n const track = tracks[animation]\n let frameIndex = 0\n while (frameIndex < track.frames.length - 1 && offset >= track.durations[frameIndex]!) {\n offset -= track.durations[frameIndex]!\n frameIndex += 1\n }\n return { animation, frameIndex }\n}\n","/**\n * Pet sprite companion component — the browser half's centerpiece. Renders a\n * fixed-position floating sprite (React portal onto document.body), plays\n * the track matching the host animation snapshot, and exposes the\n * interaction surface: click to pet, hover panel with feed/rename/hide, drag\n * to reposition (persisted via setConfig). Everything visual comes from the\n * pet definition the host serves ('/api/pet/pets' + the state snapshot's\n * pet id), so one component renders every registry entry.\n * @module @linxin666/dsh-pet/client/PetSprite\n */\n\nimport { useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport type { CSSProperties, PointerEvent as ReactPointerEvent, ReactPortal } from 'react'\nimport { createPortal } from 'react-dom'\nimport clsx from 'clsx'\nimport type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDisplayConfig } from '../persist.ts'\nimport type { PetStateView } from '../service.ts'\nimport type { PetDefinition } from '../registry.ts'\nimport type { PetFeedback } from './pet-store.ts'\nimport { framePosition, rowOfTrack, trimTrack } from './spritesheet.ts'\nimport { sequenceFrameAt } from './sequences.ts'\nimport { animationForPhase, type PetAnimation } from '../state.ts'\nimport { NS } from './locales.ts'\nimport styles from './pet.module.css'\n\n/** Props injected by the plugin apply body (store actions + locale). */\nexport interface PetSpriteProps {\n /** Latest host snapshot; null while loading. */\n snapshot: PetStateView | null\n /** The selected pet's registry definition (atlas URL + geometry + tracks). */\n definition: PetDefinition\n /** Display configuration (persisted by the host). */\n display: PetDisplayConfig\n /** Active reaction bubble, if any. */\n feedback: PetFeedback | null\n /** Pet the sprite (click). */\n onPet: () => void\n /** Feed the sprite (panel button). */\n onFeed: () => void\n /** Hide the sprite (panel button). */\n onHide: () => void\n /** Persist a drag position. */\n onDragEnd: (right: number, bottom: number) => void\n /** Rename the selected pet (persisted by the host). */\n onRename: (name: string) => void\n /** Navigate to the session one status bubble reports on. */\n onOpenSession: (sessionId: string) => void\n /** Clear the reaction bubble (after its CSS animation). */\n onFeedbackDone: () => void\n /** Locale translate seat (namespace-bound). */\n t: TranslateNS<typeof NS>\n}\n\n/** Clamp a drag offset inside the viewport with a margin. */\nfunction clampOffset(value: number, max: number): number {\n return Math.max(0, Math.min(max, value))\n}\n\n/**\n * The floating pet. The spritesheet frame advances on requestAnimationFrame\n * with per-frame durations from the definition's tracks; the atlas image is\n * loaded once and the background position is written straight to the sprite\n * element (no per-frame React state).\n */\nexport function PetSprite(props: PetSpriteProps): ReactPortal {\n const { snapshot, definition, display, feedback } = props\n const spriteRef = useRef<HTMLDivElement | null>(null)\n const floatRef = useRef<HTMLDivElement | null>(null)\n const panelRef = useRef<HTMLDivElement | null>(null)\n // Whichever bubble surface is currently rendered (feedback, the session\n // stack, or the legacy status bubble) — only one exists at a time.\n const bubbleRef = useRef<HTMLDivElement | null>(null)\n const [imageReady, setImageReady] = useState(false)\n const [hovered, setHovered] = useState(false)\n // Multi-session bubble stack: collapsed by default (only the display\n // session's bubble + a '+N' badge), expanded on stack hover (peek) or by\n // tapping the badge (pinned, for touch). The display session's bubble\n // anchors the bottom of the stack and never moves when extras open above\n // it, so the pointer target cannot flicker.\n const [stackPeek, setStackPeek] = useState(false)\n const [stackPinned, setStackPinned] = useState(false)\n const [renaming, setRenaming] = useState(false)\n const [panelAbove, setPanelAbove] = useState(false)\n // Extra margin-bottom for the above-panel so it stacks clear of the\n // bubbles instead of overlapping them (both anchor at the sprite's top).\n const [panelLift, setPanelLift] = useState(0)\n const [nameDraft, setNameDraft] = useState('')\n // Explicit IME composition tracking: some input methods (WeChat IME on\n // Windows) report keydowns with isComposing === false mid-composition, so\n // the native flag alone is not a safe submit/cancel guard (#303).\n const composingRef = useRef(false)\n const [dragPos, setDragPos] = useState<{ right: number; bottom: number } | null>(null)\n const dragRef = useRef<{ startX: number; startY: number; right: number; bottom: number } | null>(null)\n const hideTimerRef = useRef<number | null>(null)\n const frameRef = useRef<{ track: PetAnimation | null; index: number; elapsed: number }>({\n track: null,\n index: 0,\n elapsed: 0,\n })\n\n const cell = definition.cell\n const columns = definition.columns\n const rows = definition.rows\n const tracks = definition.tracks\n const sequences = definition.sequences\n\n // Load the atlas once; the definition carries the authoritative per-row\n // frame counts and per-track durations, so nothing else is fetched.\n useEffect(() => {\n let cancelled = false\n const img = new Image()\n img.onload = () => {\n if (!cancelled) setImageReady(true)\n }\n img.src = definition.atlasUrl\n return () => {\n cancelled = true\n img.onload = null\n }\n }, [definition.atlasUrl])\n\n // Frame loop: advance the current track and write background-position.\n // Offsets must be in SCALED coordinates (background-position applies to the\n // scaled background image), so the current sprite scale rides a ref that\n // the loop reads every tick. Under prefers-reduced-motion the sprite holds\n // its track's first frame instead of animating (presentation-only; the\n // animation state machine is untouched).\n const spriteScale = display.size / cell.height\n const phase = snapshot?.phase ?? 'idle'\n const animation = snapshot?.animation ?? 'idle'\n const scaleRef = useRef(spriteScale)\n scaleRef.current = spriteScale\n useEffect(() => {\n const reduceMotion = typeof window !== 'undefined'\n && window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches === true\n const sequence = animation === animationForPhase(phase) ? sequences?.[phase] : undefined\n const leadAnimation = sequence?.[0] ?? animation\n const row = rowOfTrack(leadAnimation)\n const track = trimTrack(tracks[leadAnimation], rows[row] ?? tracks[leadAnimation].frames.length)\n // Paint one static sprite frame up front either way, so the pet is never\n // blank while the loop heat-up runs.\n const leadCol = track.frames[0]!\n const lead = framePosition(cell, row, leadCol, scaleRef.current)\n if (spriteRef.current !== null) {\n spriteRef.current.style.backgroundPosition = lead.x + 'px ' + lead.y + 'px'\n }\n if (reduceMotion) return\n let raf = 0\n let last = performance.now()\n let sequenceElapsed = 0\n const tick = (ts: number): void => {\n const delta = ts - last\n last = ts\n if (sequence !== undefined) {\n sequenceElapsed += delta\n const current = sequenceFrameAt(sequence, tracks, sequenceElapsed)\n const currentRow = rowOfTrack(current.animation)\n const currentTrack = trimTrack(\n tracks[current.animation],\n rows[currentRow] ?? tracks[current.animation].frames.length,\n )\n const col = currentTrack.frames[current.frameIndex]!\n const pos = framePosition(cell, currentRow, col, scaleRef.current)\n if (spriteRef.current !== null) {\n spriteRef.current.style.backgroundPosition = pos.x + 'px ' + pos.y + 'px'\n }\n raf = requestAnimationFrame(tick)\n return\n }\n // row/track come from the effect scope: they were computed once above\n // and this effect re-runs when animation/tracks/rows change, so the\n // per-frame recompute (trimTrack slices fresh arrays) is pure waste.\n const st = frameRef.current\n if (st.track !== animation) {\n st.track = animation\n st.index = 0\n st.elapsed = 0\n }\n st.elapsed += delta\n const maxIndex = track.frames.length - 1\n while (st.elapsed >= (track.durations[st.index] ?? 0) && st.index < maxIndex) {\n st.elapsed -= track.durations[st.index] ?? 0\n st.index += 1\n }\n if (st.elapsed >= (track.durations[st.index] ?? 0)) {\n if (track.loop) {\n st.elapsed = 0\n st.index = 0\n } else {\n st.index = maxIndex // hold the final frame; the host switches tracks\n }\n }\n const col = track.frames[st.index]!\n const pos = framePosition(cell, row, col, scaleRef.current)\n if (spriteRef.current !== null) {\n spriteRef.current.style.backgroundPosition = pos.x + 'px ' + pos.y + 'px'\n }\n raf = requestAnimationFrame(tick)\n }\n raf = requestAnimationFrame(tick)\n return () => cancelAnimationFrame(raf)\n }, [animation, phase, cell, columns, rows, tracks, sequences])\n\n // Auto-clear the feedback bubble after its CSS animation. The callback\n // rides a ref so re-renders never reset the timer: the 2s poll rebuilds\n // `props` every tick, and depending on it would starve the timeout.\n const feedbackDoneRef = useRef(props.onFeedbackDone)\n feedbackDoneRef.current = props.onFeedbackDone\n useEffect(() => {\n if (feedback === null) return\n const timer = window.setTimeout(() => feedbackDoneRef.current(), 2600)\n return () => window.clearTimeout(timer)\n }, [feedback])\n\n // Dragging: pointer events on the sprite; position is right/bottom based.\n // `draggedRef` records whether the pointer actually moved, so the browser's\n // trailing click (fired after pointerup) does not pet the sprite.\n const draggedRef = useRef(false)\n const clearHideTimer = (): void => {\n if (hideTimerRef.current !== null) {\n window.clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n }\n\n // Clear any pending auto-hide timer on unmount: a stray callback after\n // teardown reads window through react-dom and failed CI runs with\n // \"window is not defined\" (slow-runner timing, PetSprite.test.tsx).\n useEffect(() => () => clearHideTimer(), [])\n\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>): void => {\n e.preventDefault()\n ;(e.target as HTMLElement).setPointerCapture?.(e.pointerId)\n const current = dragPos ?? { right: display.right, bottom: display.bottom }\n dragRef.current = { startX: e.clientX, startY: e.clientY, ...current }\n draggedRef.current = false\n setHovered(false)\n }\n const onPointerMove = (e: ReactPointerEvent<HTMLDivElement>): void => {\n const drag = dragRef.current\n if (drag === null) return\n const dx = e.clientX - drag.startX\n const dy = e.clientY - drag.startY\n if (Math.abs(dx) > 4 || Math.abs(dy) > 4) draggedRef.current = true\n const right = clampOffset(drag.right - dx, window.innerWidth - 40)\n const bottom = clampOffset(drag.bottom - dy, window.innerHeight - 40)\n setDragPos({ right, bottom })\n }\n const onPointerUp = (): void => {\n if (dragRef.current === null) return\n dragRef.current = null\n if (dragPos !== null) props.onDragEnd(dragPos.right, dragPos.bottom)\n }\n\n const pos = dragPos ?? { right: display.right, bottom: display.bottom }\n const spriteWidth = Math.round(cell.width * spriteScale)\n const spriteHeight = Math.round(cell.height * spriteScale)\n\n // Concurrent sessions share one bubble slot: only the display session\n // speaks by default, and the rest hide behind a '+N' badge until the stack\n // is hovered/pinned open. The legacy single 'bubble' is the fallback when\n // the host serves no per-session list. The hover panel normally sits below\n // the sprite, so the bubbles stay visible and clickable — no region swap.\n const sessionBubbles = snapshot?.sessions ?? []\n const stackOpen = stackPeek || stackPinned\n const collapsed = !stackOpen && sessionBubbles.length > 1\n const visibleSessions = collapsed ? sessionBubbles.slice(0, 1) : sessionBubbles\n const statusBubble = feedback === null && sessionBubbles.length === 0\n ? snapshot?.bubble\n : undefined\n // The display session's inner whisper (碎碎念) — short inner-voice copy\n // woken by the model's output. Instead of a second bubble of its own, a\n // fresh whisper takes over the display session's bubble (the stack top, or\n // the single status bubble) and re-tints it, so the pet never wears two\n // voices at once. Interaction feedback takes over the whole bubble area\n // while it plays, so whispers yield to it like status copy.\n const whisper = feedback === null ? snapshot?.whisper : undefined\n const bubblePresent = feedback !== null || sessionBubbles.length > 0 || statusBubble !== undefined || whisper !== undefined\n const displayName = snapshot?.name ?? definition.displayName\n\n // A settled session list can no longer stay pinned open.\n useEffect(() => {\n if (sessionBubbles.length <= 1) setStackPinned(false)\n }, [sessionBubbles.length])\n\n useLayoutEffect(() => {\n if (!hovered) {\n setPanelAbove(false)\n setPanelLift(0)\n return\n }\n const updatePanelPlacement = (): void => {\n const sprite = spriteRef.current\n const panel = panelRef.current\n if (sprite === null || panel === null) return\n const availableBelow = window.innerHeight - sprite.getBoundingClientRect().bottom\n const above = availableBelow < panel.getBoundingClientRect().height + 8\n setPanelAbove(above)\n // The fallback above-placement shares the sprite's top edge with the\n // bubble(s); lift the panel by the bubble area's height so the two\n // never overlap (8px base gap + 6px clearance above the top bubble).\n const bubbleHeight = above ? bubbleRef.current?.getBoundingClientRect().height ?? 0 : 0\n setPanelLift(bubbleHeight > 0 ? Math.ceil(bubbleHeight) + 14 : 0)\n }\n updatePanelPlacement()\n window.addEventListener('resize', updatePanelPlacement)\n return () => window.removeEventListener('resize', updatePanelPlacement)\n }, [hovered, renaming, pos.right, pos.bottom, display.size, bubblePresent, sessionBubbles.length, stackOpen, feedback])\n\n const float = (\n <div\n ref={floatRef}\n className={styles.float}\n style={{ right: pos.right, bottom: pos.bottom, zIndex: 2147483000 }}\n onPointerEnter={() => {\n clearHideTimer()\n setHovered(true)\n }}\n onPointerLeave={(e) => {\n // The panel renders OUTSIDE the container's box (absolute, below\n // the sprite), so moving onto it fires pointerleave on the container.\n // Treat a target still inside the container's DOM (the overflowed\n // panel) as \"still hovering\"; otherwise give the pointer a short\n // grace period to reach the panel across the gap below the sprite.\n // The bridge ('.panel::after') keeps the pointer inside the hit\n // area, and the grace period covers a slow mouse crossing the\n // remaining sliver.\n const next = e.relatedTarget\n if (next instanceof Node && floatRef.current?.contains(next)) return\n // Never auto-hide while the rename box is open: moving the pointer\n // onto an IME candidate window (an OS-level window outside the\n // webview) fires pointerleave, and unmounting the input mid-IME-\n // composition crashes some input methods / the renderer (#303).\n if (renaming) return\n clearHideTimer()\n hideTimerRef.current = window.setTimeout(() => setHovered(false), 300)\n }}\n >\n <div\n ref={spriteRef}\n className={styles.sprite}\n style={{\n width: spriteWidth,\n height: spriteHeight,\n backgroundImage: imageReady ? 'url(' + definition.atlasUrl + ')' : undefined,\n backgroundSize: (cell.width * columns * spriteScale) + 'px ' + (cell.height * (definition.atlasRows ?? rows.length) * spriteScale) + 'px',\n backgroundRepeat: 'no-repeat',\n backgroundPosition: '0 0',\n cursor: dragRef.current === null ? 'grab' : 'grabbing',\n }}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onClick={() => {\n // A pointer sequence that moved (dragged) still fires a trailing\n // click; skip the pet when that happened.\n if (draggedRef.current) return\n props.onPet()\n }}\n role=\"button\"\n aria-label={definition.displayName}\n />\n {feedback !== null && (\n <div key={feedback.at} ref={bubbleRef} className={clsx(styles.bubble, feedback.kind === 'feed' ? styles.bubbleFeed : styles.bubblePet)}>\n {feedback.text}\n </div>\n )}\n {feedback === null && (sessionBubbles.length > 0 || statusBubble !== undefined || whisper !== undefined) && (\n <div\n ref={bubbleRef}\n className={styles.bubbleStack}\n onPointerEnter={() => setStackPeek(true)}\n onPointerLeave={() => setStackPeek(false)}\n >\n {visibleSessions.map((session, index) => {\n // The whisper rides the display session's bubble — the stack's\n // primary entry (DOM-first, rendered bottom-most by the reversed\n // column so it stays glued to the sprite when extras open above).\n // The key swap restarts the entrance animation so the mood change\n // reads as the bubble re-speaking.\n const speaksWhisper = index === 0 && whisper !== undefined\n const bubble = (\n <button\n key={speaksWhisper ? 'whisper:' + whisper : session.sessionId}\n type=\"button\"\n className={clsx(\n styles.bubble,\n styles.bubbleStatus,\n styles.bubbleClickable,\n speaksWhisper && styles.bubbleWhisper,\n )}\n title={props.t('pet.openSessionHint')}\n onClick={() => { props.onOpenSession(session.sessionId) }}\n >\n {speaksWhisper ? whisper : session.bubble}\n </button>\n )\n // The primary bubble carries the '+N' badge while other sessions\n // hide behind it; the badge toggles the pinned (touch) expansion.\n if (index !== 0 || sessionBubbles.length <= 1) return bubble\n return (\n <span key=\"primary\" className={styles.bubbleAnchor}>\n {bubble}\n <button\n type=\"button\"\n className={styles.bubbleMore}\n title={stackOpen\n ? props.t('pet.collapseSessions')\n : props.t('pet.moreSessions', { n: sessionBubbles.length - 1 })}\n aria-label={stackOpen\n ? props.t('pet.collapseSessions')\n : props.t('pet.moreSessions', { n: sessionBubbles.length - 1 })}\n aria-expanded={stackOpen}\n onClick={(e) => {\n e.stopPropagation()\n setStackPinned(open => !open)\n }}\n >\n {stackOpen ? '×' : '+' + String(sessionBubbles.length - 1)}\n </button>\n </span>\n )\n })}\n {sessionBubbles.length === 0 && (statusBubble !== undefined || whisper !== undefined) && (\n // The key swap (status copy <-> whisper) restarts the entrance\n // animation on every mood change.\n <div\n key={whisper === undefined ? 'status' : 'whisper:' + whisper}\n className={clsx(styles.bubble, styles.bubbleStatus, whisper !== undefined && styles.bubbleWhisper)}\n role=\"status\"\n aria-live=\"polite\"\n >\n {whisper ?? statusBubble}\n </div>\n )}\n </div>\n )}\n {hovered && dragRef.current === null && (\n <div\n ref={panelRef}\n className={clsx(styles.panel, panelAbove && styles.panelAbove)}\n data-placement={panelAbove ? 'above' : 'below'}\n style={panelAbove && panelLift > 0\n ? ({ marginBottom: panelLift } as CSSProperties)\n : undefined}\n onPointerEnter={() => {\n // Reaching the panel (or its bridge) must cancel any hide timer\n // the container's pointerleave may have armed while the pointer\n // crossed the sliver between the sprite and the panel.\n clearHideTimer()\n }}\n >\n {renaming ? (\n <div className={styles.renameRow}>\n <input\n className={styles.nameInput}\n value={nameDraft}\n maxLength={20}\n placeholder={props.t('pet.namePlaceholder')}\n autoFocus\n onChange={(e) => setNameDraft(e.target.value)}\n onCompositionStart={() => { composingRef.current = true }}\n onCompositionEnd={() => { composingRef.current = false }}\n onKeyDown={(e) => {\n // While an IME composition is active (e.g. selecting a\n // Chinese candidate), Enter/Escape keydowns belong to the\n // input method: ignore them so candidate selection can\n // neither submit the draft nor close the rename box. The\n // explicit ref and the 'Process' key cover IMEs that mark\n // composition keydowns with isComposing === false (#303).\n if (composingRef.current || e.nativeEvent.isComposing || e.key === 'Process') return\n if (e.key === 'Enter') {\n const trimmed = nameDraft.trim()\n if (trimmed !== '') {\n props.onRename(trimmed)\n setRenaming(false)\n }\n } else if (e.key === 'Escape') {\n setRenaming(false)\n }\n }}\n />\n <button\n type=\"button\"\n className={styles.action}\n onClick={() => {\n const trimmed = nameDraft.trim()\n if (trimmed !== '') {\n props.onRename(trimmed)\n setRenaming(false)\n }\n }}\n >\n {props.t('pet.confirm')}\n </button>\n </div>\n ) : (\n <>\n <div className={styles.rankRow}>\n <span className={styles.nameCell}>{displayName}</span>\n <span className={styles.statRank}>{props.t('pet.rank', { rank: snapshot?.affinity.rank ?? '?' })}</span>\n </div>\n <div className={styles.rankRow}>\n <span className={styles.statTreats}>{props.t('pet.treats', { n: snapshot?.treats.stocked ?? 0 })}</span>\n <span className={styles.statPoints}>{props.t('pet.points', { points: snapshot?.affinity.points ?? 0 })}</span>\n </div>\n <div className={styles.actions}>\n <button type=\"button\" className={styles.action} onClick={props.onFeed}>\n {props.t('pet.feed')}\n </button>\n <button\n type=\"button\"\n className={styles.action}\n onClick={() => {\n // Cancel any pending hide so the rename box cannot\n // unmount right as the user starts typing (#303).\n clearHideTimer()\n setNameDraft(displayName)\n setRenaming(true)\n }}\n >\n {props.t('pet.rename')}\n </button>\n <button type=\"button\" className={styles.action} onClick={props.onHide}>\n {props.t('pet.hide')}\n </button>\n </div>\n </>\n )}\n </div>\n )}\n </div>\n )\n\n return createPortal(float, document.body)\n}\n","/**\n * Global floating pet entry. The pet is host-global (its state, display and\n * interactions live on '/api/pet/*' endpoints with no session dimension), so\n * it must not ride a session-scoped slot — on the new-conversation screen no\n * session exists to scope a slot by, and the pet would vanish (issue #48).\n * The client half therefore mounts this entry straight onto 'document.body'\n * (see index.ts): while visible it renders the floating PetSprite (a\n * portal), while hidden it renders a fixed-position summon button. Which\n * sprite renders is decided by the host snapshot's pet id resolved against\n * the registry list — no per-pet component exists.\n * @module @linxin666/dsh-pet/client/PetDockEntry\n */\n\nimport { useEffect, useSyncExternalStore, type ReactElement } from 'react'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDisplayConfig } from '../persist.ts'\nimport type { PetStoreInstance } from './pet-store.ts'\nimport { PetSprite } from './PetSprite.tsx'\nimport { NS } from './locales.ts'\nimport styles from './pet.module.css'\n\n/** Injected actions handed to the dock entry component. */\nexport interface PetInjected {\n /** The app-wide pet store instance (snapshot + registry list + feedback). */\n store: PetStoreInstance\n /** Ensure the first snapshot (and registry list) is fetched (called on mount). */\n ensure: () => void\n /** Pet the sprite (click). */\n pet: () => void\n /** Feed the sprite. */\n feed: () => void\n /** Hide the sprite. */\n hide: () => void\n /** Summon the hidden sprite back. */\n summon: () => void\n /** Persist a drag position. */\n dragEnd: (right: number, bottom: number) => void\n /** Rename the selected pet (persisted by the host). */\n rename: (name: string) => void\n /** Navigate the GUI to the session a bubble reports on. */\n openSession: (sessionId: string) => void\n /** Clear the reaction bubble. */\n feedbackDone: () => void\n}\n\n/** Composed props of the global pet entry (locale + injected; no slot runtime share). */\nexport type PetDockEntryProps =\n PetInjected\n & PropsLocale<typeof NS>\n\nconst DEFAULT_DISPLAY: PetDisplayConfig = { visible: true, size: 160, right: 24, bottom: 20 }\n\n/**\n * Dock entry: while the pet is visible, mount the floating PetSprite (it\n * portals itself onto document.body); while hidden, render the summon\n * button so the pet can always come back. The store is the plugin-owned\n * single instance — the slot system provides none because the pet is\n * host-global, not session-scoped.\n */\nexport function PetDockEntry(props: PetDockEntryProps): ReactElement {\n const { store, ensure } = props\n const ui = useSyncExternalStore(store.subscribe, store.getSnapshot)\n const snapshot = ui.snapshot\n const feedback = ui.feedback\n const definition = ui.pets.find(entry => entry.id === snapshot?.pet.id) ?? null\n const visible = snapshot?.display.visible ?? true\n\n useEffect(() => {\n ensure()\n }, [ensure])\n\n if (visible) {\n return (\n <span data-pet-dock data-testid=\"pet-dock\">\n {snapshot === null || definition === null\n ? null\n : (\n <PetSprite\n snapshot={snapshot}\n definition={definition}\n display={snapshot.display}\n feedback={feedback}\n onPet={props.pet}\n onFeed={props.feed}\n onHide={props.hide}\n onDragEnd={props.dragEnd}\n onRename={props.rename}\n onOpenSession={props.openSession}\n onFeedbackDone={props.feedbackDone}\n t={props.t}\n />\n )}\n </span>\n )\n }\n const display = snapshot?.display ?? DEFAULT_DISPLAY\n return (\n <button\n type=\"button\"\n className={styles.summon}\n style={{\n position: 'fixed',\n right: display.right,\n bottom: display.bottom,\n zIndex: 2147483000,\n }}\n onClick={props.summon}\n data-testid=\"pet-summon\"\n data-dsh-part=\"summon-button\"\n >\n {props.t('pet.summon', { name: snapshot?.name ?? '' })}\n </button>\n )\n}\n","// Generated by scripts/sync-shared.mjs from shared/client/settings/PluginSettingsCard.tsx. Do not edit this copy; edit the shared source and run \"node scripts/sync-shared.mjs\".\n/**\n * Family-shared chrome for plugin settings cards: a disclosure header naming\n * the plugin and what its settings govern, the controls inside, and the save\n * that writes them. Renders nothing while the namespace is unavailable — a\n * deployment that does not compose the owning plugin should show no trace of\n * it. Inlined into each consumer's client bundle; mirrors the official\n * ui-plugin-config PluginCard in a self-contained slice.\n */\n\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState, type KeyboardEvent, type ReactNode } from 'react'\nimport type { CardShell } from './settings-form.ts'\nimport css from './settings-card.module.css'\n\n/** Copy keys the card chrome itself reads; every consumer locale carries this shared vocabulary. */\nexport const CARD_COPY_KEYS = [\n 'settings.collapse',\n 'settings.expand',\n 'settings.notExposed',\n 'settings.unsaved',\n 'settings.readOnly',\n 'settings.saveFailed',\n 'settings.discard',\n 'settings.save',\n 'settings.saving',\n] as const\n\n/** Copy key the card chrome itself reads. */\nexport type CardCopyKey = (typeof CARD_COPY_KEYS)[number]\n\n/** Key domain for the plugin's own copy (inferred per consumer). */\nexport type SettingsCardKey<TKey extends string = string> = TKey | CardCopyKey\n\n/** Card chrome shared by every plugin settings card. */\nexport interface PluginSettingsCardProps<TKey extends string = string> {\n /** Locale reader for this card's copy. */\n t: (key: SettingsCardKey<TKey>, params?: Record<string, string | number>) => string\n /** Locale key of the plugin's name. */\n titleKey: TKey\n /** Locale key of the line describing what this plugin's settings govern. */\n descriptionKey: TKey\n /** The card's form state: availability, writability, and what a save would do. */\n state: CardShell\n /** Write every staged edit. */\n onSave: () => void\n /** Drop every staged edit. */\n onDiscard: () => void\n /**\n * Render the controls expanded on arrival (still collapsible). Defaults to\n * true: promoted first-level sections show their settings immediately.\n */\n defaultOpen?: boolean\n /**\n * Render without the collapse affordance: a static header and an always\n * visible body. Used by first-level settings sections whose nav entry\n * already provides the selection.\n */\n alwaysOpen?: boolean\n /**\n * Hide the save/discard footer: cards whose body applies its own changes\n * immediately (an embedded external settings section) have no staged\n * edits, so the footer would sit there permanently disabled.\n */\n hideFooter?: boolean\n /** The plugin's controls. */\n children: ReactNode\n}\n\n/**\n * Render one plugin settings card.\n * @param props - the plugin's copy keys, its form state, and its controls.\n * @returns the card, or nothing while the namespace is still loading.\n */\nexport function PluginSettingsCard<TKey extends string = string>(props: PluginSettingsCardProps<TKey>) {\n const [open, setOpen] = useState(props.defaultOpen ?? true)\n const { state, alwaysOpen } = props\n if (!state.available) return null\n const title = props.t(props.titleKey)\n const description = props.t(props.descriptionKey)\n const blocked = !state.dirty || state.invalid || state.saving\n const expanded = alwaysOpen === true || open\n const cardClass = expanded ? `${css.cardOpen} ${css.card}` : css.card\n // With alwaysOpen the nav entry already provides the selection, so the\n // header is a static title row instead of a disclosure button.\n const header = alwaysOpen === true\n ? (\n <div className={css.headerStatic}>\n <span className={css.headText}>\n <span className={css.name} title={title}>{title}</span>\n <span className={css.description} title={description}>{description}</span>\n </span>\n {state.dirty ? <span className={css.pending} title={props.t('settings.unsaved')}>{props.t('settings.unsaved')}</span> : null}\n </div>\n )\n : (\n <button\n type=\"button\"\n className={css.header}\n aria-expanded={open}\n aria-label={`${props.t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`}\n onClick={() => { setOpen(!open) }}\n >\n <span className={css.headText}>\n <span className={css.name} title={title}>{title}</span>\n <span className={css.description} title={description}>{description}</span>\n </span>\n {state.dirty ? <span className={css.pending} title={props.t('settings.unsaved')}>{props.t('settings.unsaved')}</span> : null}\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 14 14\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={open ? `${css.chevron} ${css.chevronOpen}` : css.chevron}\n >\n <path\n d=\"M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z\"\n fill=\"currentColor\"\n />\n </svg>\n </button>\n )\n // The namespace exists but the Host does not serve it to this client (the\n // official settings allowlist omits third-party namespaces): show a card\n // that explains the gap instead of vanishing, so a missing card never\n // reads as a missing plugin.\n if (!state.exposed) {\n return (\n <li className={cardClass}>\n {header}\n {expanded\n ? (\n <div className={css.body}>\n <p className={css.notExposed} role=\"status\">{props.t('settings.notExposed')}</p>\n </div>\n )\n : null}\n </li>\n )\n }\n return (\n <li className={cardClass}>\n {header}\n {expanded\n ? (\n <div className={css.body}>\n {!state.writable ? <p className={css.readOnly} role=\"status\">{props.t('settings.readOnly')}</p> : null}\n {props.children}\n {props.hideFooter === true\n ? null\n : (\n <div className={css.footer}>\n {state.failed\n ? (\n <p className={css.failed} role=\"status\">\n {props.t('settings.saveFailed')}{state.failedReason ? ' - ' + state.failedReason : ''}\n </p>\n )\n : null}\n <button\n type=\"button\"\n className={css.discard}\n disabled={!state.dirty || state.saving}\n onClick={props.onDiscard}\n >\n {props.t('settings.discard')}\n </button>\n <button\n type=\"button\"\n className={css.save}\n disabled={blocked}\n onClick={props.onSave}\n >\n {props.t(!state.saving ? 'settings.save' : 'settings.saving')}\n </button>\n </div>\n )}\n </div>\n )\n : null}\n </li>\n )\n}\n\n/** Props every field control needs regardless of its value type. */\nexport interface FieldProps {\n /** Stable id associating the label with its control. */\n id: string\n /** Visible label. */\n label: string\n /** One-line explanation rendered under the control. */\n hint: string\n /** Draft text this control renders. */\n text: string\n /** True when saving would leave a user-layer entry for this field. */\n overridden: boolean\n /** True when the draft is not a value this field accepts. */\n invalid: boolean\n /** Copy for the overridden badge. */\n overriddenLabel: string\n /** Copy for the reset control. */\n resetLabel: string\n /** Copy shown in place of the hint while the draft is invalid. */\n invalidLabel: string\n /** Disables every control (read-only document, or an unavailable namespace). */\n disabled: boolean\n /** Stage draft text. */\n onEdit: (text: string) => void\n /** Stage a clear so the field re-inherits the composition layer. */\n onReset: () => void\n}\n\n/** A staged value field. `numeric` only hints the keypad: which drafts a field accepts is decided by its spec. */\nexport function ValueField(props: FieldProps & {\n /** Hints a numeric keypad without narrowing what the control accepts. */\n numeric?: boolean\n /** Placeholder shown while the draft is empty. */\n placeholder?: string\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <input\n id={props.id}\n className={props.invalid ? css.inputInvalid : css.input}\n type=\"text\"\n {...props.numeric === true ? { inputMode: 'numeric' as const } : {}}\n {...props.invalid ? { 'aria-invalid': true } : {}}\n value={props.text}\n placeholder={props.placeholder ?? ''}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n />\n <p className={props.invalid ? css.invalid : css.hint}>\n {props.invalid ? props.invalidLabel : props.hint}\n </p>\n </div>\n )\n}\n\nconst NON_SKIN_BODY_MARKERS = new Set(['dshSkinCenter', 'dshSidebarCollapsed'])\n// 检测使用使用皮肤,用了皮肤退回原生select样式,防止样式冲突。默认外观下使用优化后的select样式。\nfunction isSkinActive(): boolean {\n const datasetList = Object.keys(document.body.dataset)\n const isActive = datasetList.some(key => key.startsWith('dsh') && !NON_SKIN_BODY_MARKERS.has(key))\n return isActive\n}\n\ninterface SelectOption {\n value: string\n label: string\n}\n\nconst SELECT_CLOSE_MS = 100\n\n/**\n * The shared dual-mode select control. While an appearance skin is active it\n * renders the legacy native `<select>` untouched, so element-level skin\n * selectors keep working; under the default appearance it renders a\n * self-drawn `role=\"listbox\"` popup whose open/close is transition-animated.\n * Staged cards reach it through BooleanField/ChoiceField; immediate-apply\n * editors (the side-card prefs) bind it directly through onEdit.\n * 双模式下拉框:皮肤激活时用原生 select,默认外观用自绘动画弹层。\n */\nexport function SelectField(props: {\n id: string\n options: ReadonlyArray<SelectOption>\n value: string\n disabled: boolean\n invalid: boolean\n onEdit: (text: string) => void\n}) {\n const { id, options, value } = props\n const [open, setOpen] = useState(false)\n const [closing, setClosing] = useState(false)\n const [phase, setPhase] = useState<'initial' | 'open'>('initial')\n const [activeIndex, setActiveIndex] = useState(0)\n const closeTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const wrapRef = useRef<HTMLDivElement | null>(null)\n const popupRef = useRef<HTMLDivElement | null>(null)\n\n const currentIndex = () => {\n const index = options.findIndex(option => option.value === value)\n return index >= 0 ? index : 0\n }\n\n const close = useCallback(() => {\n if (closeTimer.current !== undefined) clearTimeout(closeTimer.current)\n setClosing(true)\n closeTimer.current = setTimeout(() => {\n setClosing(false)\n setOpen(false)\n }, SELECT_CLOSE_MS)\n }, [])\n\n const openPopup = () => {\n if (closeTimer.current !== undefined) clearTimeout(closeTimer.current)\n setActiveIndex(currentIndex())\n setPhase('initial')\n setClosing(false)\n setOpen(true)\n }\n\n const commit = (index: number) => {\n const option = options[index]\n if (option) props.onEdit(option.value)\n close()\n }\n\n const onTriggerClick = () => {\n if (props.disabled) return\n if (open && !closing) close()\n else openPopup()\n }\n\n const onKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n if (props.disabled) return\n const count = options.length\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowUp':\n case 'Enter':\n case ' ':\n event.preventDefault()\n if (!open) {\n openPopup()\n } else if (!closing) {\n if (event.key === 'ArrowDown') setActiveIndex(index => (index + 1) % count)\n else if (event.key === 'ArrowUp') setActiveIndex(index => (index - 1 + count) % count)\n else commit(activeIndex)\n }\n break\n case 'Escape':\n if (open) {\n event.preventDefault()\n event.stopPropagation()\n close()\n }\n break\n case 'Tab':\n if (open) close()\n break\n }\n }\n\n useEffect(() => () => {\n if (closeTimer.current !== undefined) clearTimeout(closeTimer.current)\n }, [])\n useLayoutEffect(() => {\n if (open && !closing && phase === 'initial') {\n void popupRef.current?.offsetHeight\n setPhase('open')\n }\n }, [open, closing, phase])\n\n useEffect(() => {\n if (!open) return\n const onPointerDown = (event: PointerEvent) => {\n const target = event.target\n if (target instanceof Node && !wrapRef.current?.contains(target)) close()\n }\n document.addEventListener('pointerdown', onPointerDown)\n return () => document.removeEventListener('pointerdown', onPointerDown)\n }, [open, close])\n\n useEffect(() => {\n if (props.disabled && open) close()\n }, [props.disabled, open, close])\n\n // 使用了皮肤则退回原生select样式防止样式冲突。\n if (isSkinActive()) {\n return (\n <select\n id={id}\n className={css.select}\n value={value}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n >\n {options.map(option => (\n <option key={option.value} value={option.value}>{option.label}</option>\n ))}\n </select>\n )\n }\n\n const label = options.find(option => option.value === value)?.label ?? ''\n const popupClass = closing\n ? `${css.selectPopup} ${css.selectPopupClose}`\n : phase === 'open'\n ? `${css.selectPopup} ${css.selectPopupOpen}`\n : css.selectPopup\n return (\n <div className={css.selectWrap} ref={wrapRef}>\n <button\n type=\"button\"\n id={id}\n className={`${css.select} ${css.selectButton}`}\n disabled={props.disabled}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-activedescendant={open ? `${id}-o${activeIndex}` : undefined}\n aria-invalid={props.invalid || undefined}\n onClick={onTriggerClick}\n onKeyDown={onKeyDown}\n >\n <span className={css.selectLabel}>{label}</span>\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 14 14\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={open ? `${css.selectChevron} ${css.selectChevronOpen}` : css.selectChevron}\n aria-hidden=\"true\"\n >\n <path\n d=\"M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z\"\n fill=\"currentColor\"\n />\n </svg>\n </button>\n {open\n ? (\n <div className={popupClass} role=\"listbox\" ref={popupRef}>\n {options.map((option, index) => (\n <div\n key={option.value}\n id={`${id}-o${index}`}\n role=\"option\"\n aria-selected={option.value === value}\n className={`${css.selectOption}${option.value === value ? ` ${css.selectOptionSelected}` : ''}${index === activeIndex && !closing ? ` ${css.selectOptionActive}` : ''}`}\n onClick={() => { commit(index) }}\n >\n {option.label}\n </div>\n ))}\n </div>\n )\n : null}\n </div>\n )\n}\n\n/** A staged boolean field: 继承 / 开 / 关. */\nexport function BooleanField(props: FieldProps & {\n /** Copy for the inherit option. */\n inheritLabel: string\n /** Copy for the on option. */\n onLabel: string\n /** Copy for the off option. */\n offLabel: string\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <SelectField\n id={props.id}\n options={[\n { value: '', label: props.inheritLabel },\n { value: 'true', label: props.onLabel },\n { value: 'false', label: props.offLabel },\n ]}\n value={props.text}\n disabled={props.disabled}\n invalid={props.invalid}\n onEdit={props.onEdit}\n />\n <p className={css.hint}>{props.hint}</p>\n </div>\n )\n}\n\n/** A staged enumerated field rendered as a select. */\nexport function ChoiceField(props: FieldProps & {\n /** Copy for the inherit option (draft text is the empty string). */\n inheritLabel: string\n /** Choices rendered in order; `value` is the draft/stored text. */\n choices: ReadonlyArray<{ value: string; label: string }>\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <SelectField\n id={props.id}\n options={[{ value: '', label: props.inheritLabel }, ...props.choices]}\n value={props.text}\n disabled={props.disabled}\n invalid={props.invalid}\n onEdit={props.onEdit}\n />\n <p className={props.invalid ? css.invalid : css.hint}>\n {props.invalid ? props.invalidLabel : props.hint}\n </p>\n </div>\n )\n}\n","// Generated by scripts/sync-shared.mjs from shared/client/settings/settings-form.ts. Do not edit this copy; edit the shared source and run \"node scripts/sync-shared.mjs\".\n/**\n * Staged form model behind the plugin settings card. A card stages what the\n * user types and writes it only when they save — the settings write is a\n * durable, revision-fenced document mutation, so staging keeps what is on\n * screen exactly what a save would store. Family-shared slice inlined into\n * each plugin's client bundle; mirrors the official ui-plugin-config\n * card-store pattern.\n */\n\nimport type { SettingsScope, SettingsScopeSnapshot, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'\nimport { createSnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'\n\n/** The write one field's staged text performs when the card is saved. */\nexport type FieldWrite =\n | { kind: 'set'; value: unknown }\n | { kind: 'clear' }\n\n/** How one field converts between its stored value and its draft text. */\nexport interface FieldSpec {\n /** Field name inside the namespace section. */\n field: string\n /**\n * Whether the Host treats this field as a secret and redacts its value from\n * the read-back (role('secret') in the section schema). Redacted secrets are\n * never compared against the draft on save; the field lands when the scope\n * reports the write succeeded (its secret-set marker under the bridge), so\n * a successful secret save is not misreported as failed.\n */\n secret?: boolean\n /** Render a stored value as draft text; the empty string when the section carries none. */\n format: (value: unknown) => string\n /**\n * The write this draft text stages, or undefined when the text is not a\n * value this field accepts — which blocks the save rather than discarding it.\n */\n parse: (text: string) => FieldWrite | undefined\n}\n\n/** One field as the card renders it. */\nexport interface FieldState {\n /** Draft text the control renders. */\n text: string\n /** Whether saving would leave a user-layer entry for this field. */\n overridden: boolean\n /** Whether the draft is not a value this field accepts, which blocks saving. */\n invalid: boolean\n}\n\n/** Form state every plugin settings card shares. */\nexport interface CardShell {\n /** False while the namespace is still loading; the card renders nothing. */\n available: boolean\n /**\n * Whether the namespace is actually served to this client. False when the\n * Host deployment does not expose it (e.g. the official apiproxy settings\n * allowlist omits third-party namespaces): the card renders an explanation\n * instead of its form, so a missing namespace never looks like a missing\n * plugin.\n */\n exposed: boolean\n /** Whether the Host document accepts writes. */\n writable: boolean\n /** Whether the form holds edits that a save would write. */\n dirty: boolean\n /** Whether any staged draft is invalid, which blocks the save. */\n invalid: boolean\n /** Whether a save is crossing the wire. */\n saving: boolean\n /** Whether the last save did not land as staged; cleared by the next edit or save. */\n failed: boolean\n /**\n * The rejection code/message the Host returned for the last failed save,\n * surfaced next to the generic failure text. Undefined while no save has\n * failed (or the failure carried no server reason).\n */\n failedReason?: string\n}\n\n/** The write actions the card's slot entry injects. */\nexport interface CardActions {\n /** Stage draft text for one field. */\n edit: (field: string, text: string) => void\n /** Stage a clear, so saving lets the field re-inherit the composition layer. */\n resetField: (field: string) => void\n /** Write every staged edit, then re-seed from what the Host accepted. */\n save: () => void\n /** Drop every staged edit. */\n discard: () => void\n}\n\n/** One field's staged edit. */\ninterface StagedEdit {\n /** Draft text the control renders. */\n text: string\n /** True when this edit clears the field whatever text it shows. */\n clear: boolean\n}\n\n/** One staged edit resolved into the write a save performs. */\ninterface PlannedWrite {\n /** Field this entry writes. */\n field: string\n /** The durable write this entry performs, described for a batched scope. */\n op: BatchedWrite\n /** Perform the write and report whether the Host holds the staged value afterwards. */\n run: (() => Promise<boolean>) | undefined\n}\n\n/** One durable write a batched settings scope performs. */\nexport interface BatchedWrite {\n /** Field this entry writes. */\n field: string\n /** set stores a value; unset drops the leaf. */\n op: 'set' | 'unset'\n /** Value for op set (absent for unset). */\n value?: unknown\n}\n\n/** Per-field outcome of one batched scope write. */\nexport interface BatchedFieldResult {\n /** Field this entry writes. */\n field: string\n /** Whether the Host accepted this field's write (per the read-back view). */\n landed: boolean\n}\n\n/**\n * Result of a batched scope write. The bridge scope posts every planned write\n * in one /mutate so the Host validate hook judges baseURL+model together; a\n * batched refusal fails the whole save rather than per-field.\n */\nexport interface BatchResult {\n /** Whether the whole mutate was accepted. */\n ok: boolean\n /** Per-field success, in the request order (always present when ok). */\n fields: BatchedFieldResult[]\n /** Host rejection code (mutate refused). */\n code?: string\n /** Host rejection message (mutate refused). */\n message?: string\n}\n\n/** The optional batch surface the bridge scope adds over the SettingsScope contract. */\ninterface BatchedSettingsScope {\n /** Write every operation in one scope mutation, reporting per-field success. */\n mutate: (writes: BatchedWrite[]) => Promise<BatchResult>\n}\n\n/** Constraints a numeric field's accepted drafts must satisfy, mirroring the host schema. */\nexport interface NumberConstraints {\n /** The accepted value must be a whole number. */\n integer?: boolean\n /** The accepted value must be at least this. */\n min?: number\n}\n\n/** A whole- or decimal-number field. An empty draft clears the field; any other draft that is not a finite number within the constraints blocks the save. */\nexport function numberField(field: string, constraints: NumberConstraints = {}): FieldSpec {\n const { integer = false, min } = constraints\n return {\n field,\n format: value => typeof value === 'number' ? String(value) : '',\n parse: (text) => {\n const trimmed = text.trim()\n if (trimmed === '') return { kind: 'clear' }\n const parsed = Number(trimmed)\n if (!Number.isFinite(parsed)) return undefined\n if (integer && !Number.isInteger(parsed)) return undefined\n if (min !== undefined && parsed < min) return undefined\n return { kind: 'set', value: parsed }\n },\n }\n}\n\n/** A free-text field. An empty draft clears the field. */\nexport function textField(field: string): FieldSpec {\n return {\n field,\n format: value => typeof value === 'string' ? value : '',\n parse: (text) => {\n const trimmed = text.trim()\n return trimmed === '' ? { kind: 'clear' } : { kind: 'set', value: trimmed }\n },\n }\n}\n\n/**\n * A free-text field the Host treats as a secret and redacts from the read-back\n * (role('secret') in the section schema). The card still edits it like text,\n * but a save never compares the redacted value back and relies on the scope\n * reporting the write landed.\n */\nexport function secretField(field: string): FieldSpec {\n return { ...textField(field), secret: true }\n}\n\n/** A boolean field, edited through true/false draft text. */\nexport function booleanField(field: string): FieldSpec {\n return {\n field,\n format: value => typeof value === 'boolean' ? String(value) : '',\n parse: (text) => {\n const trimmed = text.trim()\n if (trimmed === '') return { kind: 'clear' }\n if (trimmed === 'true') return { kind: 'set', value: true }\n if (trimmed === 'false') return { kind: 'set', value: false }\n return undefined\n },\n }\n}\n\n/** An enumerated string field; only the listed choices are accepted. An empty draft clears the field. */\nexport function choiceField(field: string, choices: readonly string[]): FieldSpec {\n return {\n field,\n format: value => typeof value === 'string' && choices.includes(value) ? value : '',\n parse: (text) => {\n if (text === '') return { kind: 'clear' }\n return choices.includes(text) ? { kind: 'set', value: text } : undefined\n },\n }\n}\n\n/**\n * Stages one card's edits over one settings namespace and writes them on save.\n *\n * The Host is the only authority on whether a value was accepted — its\n * validators own the constraints no schema can express — so the outcome is\n * read back from the section rather than predicted here. A save that did not\n * land keeps its drafts, so the user can correct them instead of retyping.\n */\nexport class CardForm<T> {\n private readonly specs: Map<string, FieldSpec>\n private readonly staged = new Map<string, StagedEdit>()\n private readonly listeners = new Set<() => void>()\n /** The scope subscription installed in the constructor; released by dispose(). */\n private readonly disposeScope: () => void\n private disposed = false\n private saving = false\n private failed = false\n private failedReason: string | undefined\n\n /** @param scope - the bound settings scope for this card's namespace. */\n constructor(\n private readonly scope: SettingsScope<T>,\n specs: FieldSpec[],\n ) {\n this.specs = new Map(specs.map(spec => [spec.field, spec]))\n this.disposeScope = scope.subscribe(() => { this.publish() })\n }\n\n /**\n * Release the scope subscription and every bound store listener. The card\n * must call this on teardown; later calls are no-ops.\n */\n dispose(): void {\n if (this.disposed) return\n this.disposed = true\n this.disposeScope()\n this.listeners.clear()\n }\n\n /** Publish a projection of this form, rebuilt whenever the scope or a draft changes. */\n bind<S>(project: () => S): SnapshotStore<S> {\n const store = createSnapshotStore(project())\n this.listeners.add(() => { store.set(project()) })\n return store\n }\n\n /** Read the card-level state: what the Host serves, and what a save would do. */\n shell(): CardShell {\n const snapshot = this.scope.getSnapshot()\n const plan = this.plan()\n return {\n available: snapshot.status !== 'loading',\n exposed: snapshot.status === 'ready',\n writable: snapshot.writable,\n dirty: plan.length > 0,\n invalid: plan.some(item => item.run === undefined),\n saving: this.saving,\n failed: this.failed,\n ...this.failedReason === undefined ? {} : { failedReason: this.failedReason },\n }\n }\n\n /** Read one field's state from the effective section and its staged draft. */\n field(field: string): FieldState {\n const spec = this.specOf(field)\n const staged = this.staged.get(field)\n if (staged === undefined) {\n return { text: spec.format(this.sectionValue(field)), overridden: this.stored(field), invalid: false }\n }\n const write = staged.clear ? { kind: 'clear' as const } : spec.parse(staged.text)\n return {\n text: staged.text,\n overridden: write?.kind === 'set',\n invalid: write === undefined,\n }\n }\n\n /** The actions the card's slot registration injects. */\n actions(): CardActions {\n return {\n edit: (field, text) => { this.stage(field, { text, clear: false }) },\n resetField: (field) => {\n this.stage(field, { text: this.specOf(field).format(this.baseValue(field)), clear: true })\n },\n save: () => { void this.save() },\n discard: () => {\n if (this.staged.size === 0 && !this.failed) return\n this.staged.clear()\n this.failed = false\n this.failedReason = undefined\n this.publish()\n },\n }\n }\n\n /**\n * Write every staged edit, then re-seed from what the Host accepted.\n *\n * When the scope carries the optional batch surface (the dsh-web-ui\n * bridge scope), every planned write rides one mutation so cross-field\n * validate hooks (baseURL+model) judge the batch as a unit instead of\n * deadlocking on per-field writes. Otherwise the per-field loop runs.\n * A field lands only when the Host reports it held the staged value; a\n * landed field's draft is dropped, a failed one stays staged for the user.\n * @returns settlement after every write and the read-back.\n */\n async save(): Promise<void> {\n const plan = this.plan()\n const valid = plan.filter(item => item.run !== undefined)\n if (plan.length === 0 || this.saving || valid.length !== plan.length) return\n const plannedWrites = valid.map(item => item.op)\n // Snapshot the fields this save writes, so edits staged while it is in\n // flight survive: only the staged keys this save actually wrote are cleared.\n const fields = new Set(plan.map(item => item.field))\n this.saving = true\n this.failed = false\n this.failedReason = undefined\n this.publish()\n const landed = new Set<string>()\n const batch = this.batchedScope()\n if (batch !== undefined) {\n const result = await batch.mutate(plannedWrites)\n if (result.ok) {\n for (const field of result.fields) {\n if (field.landed) landed.add(field.field)\n }\n } else {\n this.failedReason = result.message\n }\n } else {\n for (const item of valid) {\n if (await item.run!()) landed.add(item.field)\n }\n }\n for (const field of fields) {\n if (landed.has(field)) this.staged.delete(field)\n }\n this.saving = false\n this.failed = landed.size !== fields.size\n this.publish()\n }\n\n /** The scope's batch surface when it supports one; undefined conservatively otherwise. */\n private batchedScope(): BatchedSettingsScope | undefined {\n const candidate = this.scope as unknown as BatchedSettingsScope | undefined\n return typeof candidate?.mutate === 'function' ? candidate : undefined\n }\n\n /**\n * Every staged edit a save would write. An entry whose draft is not a value\n * its field accepts carries no write: the form is still dirty, and the save\n * refuses rather than dropping the edit. A staged edit that matches the\n * effective section is not a write at all.\n * @returns the planned writes, in the order the fields were staged.\n */\n private plan(): PlannedWrite[] {\n const plan: PlannedWrite[] = []\n for (const [field, staged] of this.staged) {\n const spec = this.specOf(field)\n if (staged.clear) {\n if (this.stored(field)) plan.push({ field, op: { field, op: 'unset' }, run: () => this.clear(field) })\n continue\n }\n if (staged.text === spec.format(this.sectionValue(field))) continue\n const write = spec.parse(staged.text)\n if (write === undefined) plan.push({ field, op: { field, op: 'unset' }, run: undefined })\n else if (write.kind === 'clear') plan.push({ field, op: { field, op: 'unset' }, run: () => this.clear(field) })\n else plan.push({ field, op: { field, op: 'set', value: write.value }, run: () => this.store(field, write.value) })\n }\n return plan\n }\n\n private async clear(field: string): Promise<boolean> {\n await this.scope.unset(field)\n return !this.stored(field)\n }\n\n private async store(field: string, value: unknown): Promise<boolean> {\n await this.scope.set(field, value)\n // A redacted secret never appears in the user layer read-back; judging it\n // by value would misreport a successful secret save as failed. The bridge\n // reports secret writes through its secret-set markers (batch path); on\n // the per-field path the scope resolved, so the write is landed.\n if (this.specOf(field).secret) return true\n return this.userLayer()?.[field] === value\n }\n\n private stage(field: string, edit: StagedEdit): void {\n this.staged.set(field, edit)\n this.failed = false\n this.failedReason = undefined\n this.publish()\n }\n\n private specOf(field: string): FieldSpec {\n const spec = this.specs.get(field)\n // Every call site names a field this card declared; a missing one is a\n // wiring mistake that must not degrade into a silently inert control.\n if (spec === undefined) throw new Error(`settings card has no field ${field}`)\n return spec\n }\n\n private snapshotOf(): SettingsScopeSnapshot<T> {\n return this.scope.getSnapshot()\n }\n\n private sectionValue(field: string): unknown {\n return (this.snapshotOf().value as Record<string, unknown> | undefined)?.[field]\n }\n\n private baseValue(field: string): unknown {\n return (this.snapshotOf().base as Record<string, unknown> | undefined)?.[field]\n }\n\n private userLayer(): Record<string, unknown> | undefined {\n return this.snapshotOf().user as Record<string, unknown> | undefined\n }\n\n private stored(field: string): boolean {\n const user = this.userLayer()\n return user !== undefined && Object.hasOwn(user, field)\n }\n\n private publish(): void {\n for (const listener of this.listeners) listener()\n }\n}\n","/**\n * The pet settings card: pet selection plus display layout, bound to the\n * 'pet' settings namespace the host plugin registers. Rendered as an\n * always-open first-level settings page; the section wrapper below mounts it\n * as the content of the top-level 'settings.section' nav entry. The petId\n * choices come from the registry endpoint ('/api/pet/pets') — the same list\n * the sprite renders from — so the card carries no per-pet knowledge.\n */\n\nimport type { ReactNode } from 'react'\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { SettingsScope, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'\n// Type-only: pulls the settings-surface SlotMap merge (the 'settings.section' entry).\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport { PluginSettingsCard, ValueField, BooleanField, ChoiceField } from './PluginSettingsCard.tsx'\nimport { CardForm, booleanField, choiceField, numberField, type CardActions, type CardShell, type FieldState as CardFieldState } from './settings-form.ts'\nimport sectionCss from './settings-section.module.css'\n\n/** The pet's settings fields this card edits (the namespace's full schema). */\nexport interface PetSettings {\n /** Master switch for the plugin. */\n enabled?: boolean\n /** Master switch. */\n visible?: boolean\n /** Scale of the rendered pet in px (sprite cell height). */\n size?: number\n /** Horizontal inset from the viewport right edge, px. */\n right?: number\n /** Vertical inset from the viewport bottom edge, px. */\n bottom?: number\n /** Selected pet id (a registry entry). */\n petId?: string\n}\n\n/** What the pet settings card renders. */\nexport interface PetSettingsCardState extends CardShell {\n /** Plugin master switch. */\n enabled: CardFieldState\n /** Master switch. */\n visible: CardFieldState\n /** Pet scale. */\n size: CardFieldState\n /** Right inset. */\n right: CardFieldState\n /** Bottom inset. */\n bottom: CardFieldState\n /** Selected pet. */\n petId: CardFieldState\n /** Pet choices (registry ids + display names), loaded from the host. */\n petChoices: readonly { value: string; label: string }[]\n}\n\n/** The registration-side face the card's slot entry injects. */\nexport interface PetSettingsCardFace extends CardActions {\n hooks: {\n /** Card snapshot bound by the renderer as usePetSettingsCard. */\n petSettingsCard: SnapshotStore<PetSettingsCardState>\n }\n}\n\n/** One registry choice as served by '/api/pet/pets'. */\ninterface PetChoice {\n id: string\n displayName: string\n}\n\n/** Fetch the registry list (the same data the sprite renders from). */\nasync function fetchPetChoices(): Promise<PetChoice[]> {\n const response = await fetch('/api/pet/pets')\n if (!response.ok) throw new Error('pet pets failed: ' + response.status)\n return (await response.json()) as PetChoice[]\n}\n\n/** Bridges the 'pet' scope onto the card's staged form. */\nexport class PetSettingsCardController {\n private readonly form: CardForm<PetSettings>\n private readonly store: SnapshotStore<PetSettingsCardState>\n // The choice list rides a mutable array shared with the choiceField spec,\n // so loading the registry re-validates and re-formats the petId field\n // without rebuilding the form.\n private readonly petChoices: string[] = []\n private readonly petLabels = new Map<string, string>()\n private loaded = false\n private attempts = 0\n\n /** @param scope - the bound settings scope for the 'pet' namespace. */\n constructor(scope: SettingsScope<PetSettings>) {\n this.form = new CardForm(scope, [\n booleanField('enabled'),\n booleanField('visible'),\n numberField('size'),\n numberField('right'),\n numberField('bottom'),\n choiceField('petId', this.petChoices),\n ])\n this.store = this.form.bind(() => this.projection())\n void this.loadPets()\n }\n\n /** Resolve the registry choices once (retried a few times on failure). */\n private async loadPets(): Promise<void> {\n if (this.loaded) return\n try {\n const list = await fetchPetChoices()\n this.petChoices.splice(0, this.petChoices.length, ...list.map(choice => choice.id))\n for (const choice of list) this.petLabels.set(choice.id, choice.displayName)\n this.loaded = true\n this.store.set(this.projection())\n } catch {\n this.attempts += 1\n if (this.attempts < 3) {\n window.setTimeout(() => { void this.loadPets() }, 3000)\n }\n }\n }\n\n private projection(): PetSettingsCardState {\n return {\n ...this.form.shell(),\n enabled: this.form.field('enabled'),\n visible: this.form.field('visible'),\n size: this.form.field('size'),\n right: this.form.field('right'),\n bottom: this.form.field('bottom'),\n petId: this.form.field('petId'),\n petChoices: this.petChoices.map(id => ({ value: id, label: this.petLabels.get(id) ?? id })),\n }\n }\n\n /**\n * Build the face the card's slot registration injects.\n * @returns the card's snapshot and its form actions.\n */\n inject(): PetSettingsCardFace {\n return { hooks: { petSettingsCard: this.store }, ...this.form.actions() }\n }\n\n /**\n * Release the card's scope subscription and bound stores; the slot\n * disposer calls this on teardown.\n */\n dispose(): void {\n this.form.dispose()\n }\n}\n\n/** Props the renderer binds for the pet settings card. */\nexport type PetSettingsCardProps =\n PropsLocale<'pet'>\n & InjectFace<PetSettingsCardFace>\n\n/**\n * Render the pet settings card.\n * @param props - locale copy, the card snapshot, and its form actions.\n * @returns the card.\n */\nexport function PetSettingsCard(props: PetSettingsCardProps) {\n const { t } = props\n const state = props.usePetSettingsCard(snapshot => snapshot)\n const disabled = !state.writable\n const fieldProps = {\n overriddenLabel: t('settings.overridden'),\n resetLabel: t('settings.reset'),\n invalidLabel: t('settings.invalidNumber'),\n disabled,\n }\n return (\n <PluginSettingsCard\n t={t}\n titleKey=\"settings.title\"\n descriptionKey=\"settings.description\"\n state={state}\n onSave={props.save}\n onDiscard={props.discard}\n alwaysOpen\n >\n <BooleanField\n id=\"settings-pet-enabled\"\n label={t('settings.enabled')}\n hint={t('settings.enabledHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.enabled}\n onEdit={(text) => { props.edit('enabled', text) }}\n onReset={() => { props.resetField('enabled') }}\n />\n <ChoiceField\n id=\"settings-pet-pet\"\n label={t('settings.pet')}\n hint={t('settings.petHint')}\n inheritLabel={t('settings.inherit')}\n {...fieldProps}\n {...state.petId}\n choices={state.petChoices}\n onEdit={(text) => { props.edit('petId', text) }}\n onReset={() => { props.resetField('petId') }}\n />\n <BooleanField\n id=\"settings-pet-visible\"\n label={t('settings.visible')}\n hint={t('settings.visibleHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.visible}\n onEdit={(text) => { props.edit('visible', text) }}\n onReset={() => { props.resetField('visible') }}\n />\n <ValueField\n id=\"settings-pet-size\"\n label={t('settings.size')}\n hint={t('settings.sizeHint')}\n numeric\n {...fieldProps}\n {...state.size}\n onEdit={(text) => { props.edit('size', text) }}\n onReset={() => { props.resetField('size') }}\n />\n <ValueField\n id=\"settings-pet-right\"\n label={t('settings.right')}\n hint={t('settings.rightHint')}\n numeric\n {...fieldProps}\n {...state.right}\n onEdit={(text) => { props.edit('right', text) }}\n onReset={() => { props.resetField('right') }}\n />\n <ValueField\n id=\"settings-pet-bottom\"\n label={t('settings.bottom')}\n hint={t('settings.bottomHint')}\n numeric\n {...fieldProps}\n {...state.bottom}\n onEdit={(text) => { props.edit('bottom', text) }}\n onReset={() => { props.resetField('bottom') }}\n />\n </PluginSettingsCard>\n )\n}\n\n/** Props the settings section binds for the pet card page. */\nexport type PetSettingsSectionProps =\n PropsRuntime<'settings.section'>\n & PropsLocale<'pet'>\n & InjectFace<PetSettingsCardFace>\n\n/** Render the pet settings card as a first-level settings page. */\nexport function PetSettingsSection(props: PetSettingsSectionProps): ReactNode {\n const { t, usePetSettingsCard, save, discard, edit, resetField } = props\n return (\n <ul className={sectionCss.sectionList}>\n <PetSettingsCard t={t} usePetSettingsCard={usePetSettingsCard} save={save} discard={discard} edit={edit} resetField={resetField} />\n </ul>\n )\n}\n","/**\n * dsh-pet locale dictionaries (zh/en).\n * @module @linxin666/dsh-pet/client/locales\n */\n\n/** Dictionary namespace this package registers. */\nexport const NS = 'pet'\n\n/** Chinese copy. */\nexport const zh = {\n 'pet.feed': '喂食',\n 'pet.hide': '隐藏',\n 'pet.rename': '改名',\n 'pet.confirm': '确定',\n 'pet.namePlaceholder': '输入新名字',\n 'pet.summon': '召唤{name}',\n 'pet.rank': '亲密度 {rank}',\n 'pet.points': '{points} 点',\n 'pet.treats': '小鱼干 ×{n}',\n 'pet.state.loading': '宠物正在赶来…',\n 'pet.state.error': '宠物迷路了(连接失败)',\n 'pet.openSessionHint': '点击跳转到对应会话',\n 'pet.moreSessions': '展开其余 {n} 个会话的气泡',\n 'pet.collapseSessions': '收起会话气泡',\n // 一级设置页(settings.section 席位)。\n 'settings.title': '宠物',\n 'settings.description': '选择宠物并调整它的显示布局。',\n 'settings.pet': '宠物',\n 'settings.petHint': '选择显示哪只宠物;每只宠物独立命名,可在宠物悬浮面板改名。',\n 'settings.enabled': '启用宠物',\n 'settings.enabledHint': '关闭后隐藏宠物并停止轮询,可在设置里重新启用。',\n 'settings.visible': '显示宠物',\n 'settings.visibleHint': '关闭后宠物隐藏,可从聊天输入区重新召唤。',\n 'settings.size': '大小(px)',\n 'settings.sizeHint': '精灵单元高度,范围 32–512。',\n 'settings.right': '距右侧(px)',\n 'settings.rightHint': '距视口右边缘的水平内缩距离。',\n 'settings.bottom': '距底部(px)',\n 'settings.bottomHint': '距视口底边的垂直内缩距离。',\n 'settings.inherit': '继承',\n 'settings.on': '开',\n 'settings.off': '关',\n 'settings.overridden': '已覆盖',\n 'settings.reset': '恢复默认',\n 'settings.notExposed': '当前 DSH 版本未向设置页暴露本插件的配置命名空间,表单不可用。可编辑 ~/.dsh/settings.yaml 直接配置,或为 dsh-host-apiproxy 的 WEB_SETTINGS_NAMESPACES 白名单补充本命名空间后重启。',\n 'settings.readOnly': '当前部署的设置只读。',\n 'settings.expand': '展开设置',\n 'settings.collapse': '收起设置',\n 'settings.save': '保存',\n 'settings.saving': '保存中…',\n 'settings.discard': '放弃',\n 'settings.unsaved': '未保存',\n 'settings.saveFailed': '部署未接受这些值,已保留供你修改。',\n 'settings.invalidNumber': '请输入数字,留空则使用默认值。',\n} as const\n\n/** English copy. */\nexport const en = {\n 'pet.feed': 'Feed',\n 'pet.hide': 'Hide',\n 'pet.rename': 'Rename',\n 'pet.confirm': 'OK',\n 'pet.namePlaceholder': 'Enter a new name',\n 'pet.summon': 'Summon {name}',\n 'pet.rank': 'Affinity {rank}',\n 'pet.points': '{points} pts',\n 'pet.treats': 'Treats ×{n}',\n 'pet.state.loading': 'The pet is on its way…',\n 'pet.state.error': 'The pet is lost (connection failed)',\n 'pet.openSessionHint': 'Click to jump to this session',\n 'pet.moreSessions': 'Expand {n} more session bubbles',\n 'pet.collapseSessions': 'Collapse session bubbles',\n // First-level settings section (the `settings.section` seat).\n 'settings.title': 'Pet',\n 'settings.description': 'Pick a pet and tune its display layout.',\n 'settings.pet': 'Pet',\n 'settings.petHint': 'Choose which pet shows. Names are stored per pet; rename from the pet hover panel.',\n 'settings.enabled': 'Enable the pet',\n 'settings.enabledHint': 'When off, the pet hides and polling stops; re-enable it here.',\n 'settings.visible': 'Show the pet',\n 'settings.visibleHint': 'When off, the pet hides; summon it again from the input row.',\n 'settings.size': 'Size (px)',\n 'settings.sizeHint': 'Sprite cell height, 32\\u2013512.',\n 'settings.right': 'Right inset (px)',\n 'settings.rightHint': 'Horizontal inset from the viewport right edge.',\n 'settings.bottom': 'Bottom inset (px)',\n 'settings.bottomHint': 'Vertical inset from the viewport bottom edge.',\n 'settings.inherit': 'Inherit',\n 'settings.on': 'On',\n 'settings.off': 'Off',\n 'settings.overridden': 'Overridden',\n 'settings.reset': 'Reset to default',\n 'settings.notExposed': 'This DSH version does not expose this plugin\\'s settings namespace to the configuration page, so the form is unavailable. Edit ~/.dsh/settings.yaml directly, or add the namespace to dsh-host-apiproxy\\'s WEB_SETTINGS_NAMESPACES allowlist and restart.',\n 'settings.readOnly': 'This deployment stores settings read-only.',\n 'settings.expand': 'Show settings',\n 'settings.collapse': 'Hide settings',\n 'settings.save': 'Save',\n 'settings.saving': 'Saving\\u2026',\n 'settings.discard': 'Discard',\n 'settings.unsaved': 'Unsaved',\n 'settings.saveFailed': 'The deployment did not accept these values; they were left for you to correct.',\n 'settings.invalidNumber': 'Enter a number, or leave blank to use the default.',\n} as const\n\n/** Key union for this namespace. */\nexport type PetKey = keyof typeof zh\n\n/** The settings-card slice of the pet dictionary. */\nexport type SettingsCardKey = PetKey\n\n/**\n * Active dictionary, picked by the document language at call time. The pet\n * mounts as a global floating surface (not a session-scoped slot), so it has\n * no framework locale seat and resolves its copy the same tiny way the\n * task-board's DOM-injected surface does.\n */\nexport function dictionary(): Record<PetKey, string> {\n const lang = typeof document !== 'undefined' ? document.documentElement.lang : 'zh'\n return lang.toLowerCase().startsWith('en') ? en : zh\n}\n\n/**\n * Translate a key with optional `{name}` template params. Mirrors the slot\n * `Translate` contract `(key, params?) => string` so it can be handed to the\n * same components that used to receive the framework-injected `t` seat. The\n * key is typed loosely (`string`) so the function is assignable to the slot's\n * `TranslateNS<'pet'>` (whose key domain also spans the shared common\n * vocabulary); a missing key degrades to the key itself rather than throwing.\n */\nexport function t(key: string, params?: Record<string, unknown>): string {\n let text: string = (dictionary() as Record<string, string>)[key] ?? key\n if (params !== undefined) {\n for (const [name, value] of Object.entries(params)) {\n text = text.replaceAll(`{${name}}`, String(value))\n }\n }\n return text\n}\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** dsh-pet UI copy. */\n pet: PetKey\n }\n}\n","/**\n * dsh-pet browser half — mounts the selected pet as a global floating\n * surface and drives it from the host's same-origin '/api/pet/*' JSON\n * endpoints: fetch the registry list once, poll the host snapshot (~2 s),\n * forward interactions, persist drag positions. The pet is host-global (no\n * session dimension), so it mounts directly onto 'document.body' via a\n * single React root rather than a session-scoped slot — on the\n * new-conversation screen no session exists, and a dock-mounted pet would\n * vanish there (issue #48). When the pet is hidden the entry becomes a\n * fixed-position summon button.\n * @module @linxin666/dsh-pet/client\n */\n\nimport type { ClientContext, ISessions, SessionId, SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-runtime/client'\n// Type-only: pulls the locale plugin's Context merge (ctx.locale).\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\n// Type-only: pulls the settings-surface Context merge (ctx.settingsScope).\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type { PetDisplayConfig } from '../persist.ts'\nimport type { PetInteractResult, PetStateView } from '../service.ts'\nimport type { PetInteraction } from '../affinity.ts'\nimport type { PetDefinition } from '../registry.ts'\nimport { createElement } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { createPetStore, type PetStoreInstance } from './pet-store.ts'\nimport { PetDockEntry, type PetInjected } from './PetDockEntry.tsx'\nimport { PetSettingsSection, PetSettingsCardController, type PetSettings } from './PetSettingsCard.tsx'\nimport { NS, en, zh, t } from './locales.ts'\n\n/** The host pet API as the browser sees it (same-origin JSON endpoints). */\ninterface PetHttpApi {\n state(): Promise<PetStateView>\n pets(): Promise<PetDefinition[]>\n interact(kind: PetInteraction): Promise<PetInteractResult>\n setVisible(visible: boolean): Promise<{ ok: true; display: PetDisplayConfig }>\n setConfig(patch: Partial<PetDisplayConfig>): Promise<{ ok: true; display: PetDisplayConfig }>\n setName(name: string): Promise<{ ok: true; name: string } | { ok: false; error: string }>\n setPet(petId: string): Promise<{ ok: true; petId: string } | { ok: false; error: string }>\n}\n\n/** Same-origin JSON fetch helper (GET without body, POST with JSON body). */\nasync function petFetch<T>(path: string, body?: unknown): Promise<T> {\n const response = await fetch(path, body === undefined\n ? {}\n : {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n })\n if (!response.ok) {\n throw new Error('pet ' + path + ' failed: ' + response.status)\n }\n return (await response.json()) as T\n}\n\n/** The live host API instance (always defined; failures surface per call). */\nconst petApi: PetHttpApi = {\n state: () => petFetch('/api/pet/state'),\n pets: () => petFetch('/api/pet/pets'),\n interact: (kind) => petFetch('/api/pet/interact', { kind }),\n setVisible: (visible) => petFetch('/api/pet/set-visible', { visible }),\n setConfig: (patch) => petFetch('/api/pet/set-config', patch),\n setName: (name) => petFetch('/api/pet/set-name', { name }),\n setPet: (petId) => petFetch('/api/pet/set-pet', { petId }),\n}\n\n/** Poll interval for the host snapshot. */\nconst POLL_MS = 2000\n\n/** Settings namespace the pet settings card edits (the Host plugin registers it). */\nconst PET_SETTINGS_NS = 'pet'\n\n/** Required services (sessions powers bubble-to-session navigation). */\nexport const inject = ['slots', 'locale', 'connection', 'settingsScope', 'remote', 'sessions']\n\n/** Re-exported for consumers that type against the injected face. */\nexport type { PetInjected, PetDockEntryProps } from './PetDockEntry.tsx'\nexport type { PetSpriteProps } from './PetSprite.tsx'\nexport type { PetUiState, PetFeedback } from './pet-store.ts'\nexport type { PetSettingsCardFace, PetSettingsCardState } from './PetSettingsCard.tsx'\nexport type { PetSettingsSectionProps } from './PetSettingsCard.tsx'\nexport type { PetDefinition } from '../registry.ts'\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n /**\n * Optional rc.6 compatibility binder provided by dsh-web-ui-settings;\n * absent when that group plugin is not installed, so callers fall back to\n * the official settings scope.\n */\n webUiSettings?: { bind<S>(spec: SettingsScopeSpec<S>): SettingsScope<S> }\n }\n}\n\n/**\n * Client plugin body: register dictionaries, mount the global pet entry and\n * poll loop while the plugin is enabled, and seat the settings card as a\n * first-level settings section.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'pet: dictionaries')\n\n const binder = ctx.get('webUiSettings') ?? ctx.settingsScope\n const settingsScope = binder.bind<PetSettings>({ namespace: PET_SETTINGS_NS })\n const enabled = (): boolean => {\n const snapshot = settingsScope.getSnapshot()\n return snapshot.status === 'ready'\n ? snapshot.value?.enabled ?? true\n : snapshot.status === 'unavailable'\n }\n\n // First-level settings section: one staged form over the 'pet' settings\n // namespace, registered as a top-level settings page. The controller loads\n // the petId choices from the registry endpoint itself.\n const petSettings = new PetSettingsCardController(settingsScope)\n ctx.slots.inject('settings.section', () => {\n const unregister = ctx.slots.register({\n name: 'settings.section',\n id: 'pet',\n order: 130,\n label: () => ctx.locale.bind('pet')('settings.title'),\n locale: 'pet',\n inject: () => petSettings.inject(),\n }, PetSettingsSection)\n return () => {\n petSettings.dispose()\n unregister()\n }\n })\n\n // The global pet entry, its store, and the poll loop live while the plugin\n // is enabled; toggling the setting off hides the pet and stops polling.\n let disposeUi: (() => void) | undefined\n const syncUi = (): void => {\n if (enabled() && disposeUi === undefined) {\n // ONE store instance for the whole app, owned by this apply body. The\n // pet is host-global (state/display/interactions are /api/pet/*\n // endpoints with no session dimension), so the slot system's per-session\n // store scoping would only reset the pet on session switches and leave\n // it stateless on the new-conversation screen (no session to scope by).\n const petStore: PetStoreInstance = createPetStore().create()\n const setSnapshot = petStore.actions.setSnapshot\n const setPets = petStore.actions.setPets\n const setState = petStore.actions.setState\n const setFeedback = petStore.actions.setFeedback\n\n // The registry list is fetched lazily with retries baked into the poll\n // cycle: until it lands, the dock entry renders nothing and every 2s\n // tick tries again. After it lands, one list feeds both the sprite and\n // the settings card's choices.\n let petsLoaded = false\n // Latest-wins guard: the 2s tick, visibility recovery, and\n // interaction-triggered refreshes can overlap; only the newest\n // response may publish, so a slow older one can never roll the\n // snapshot back.\n let stateSeq = 0\n const pollNow = (): void => {\n if (!petsLoaded) {\n petApi.pets().then((list) => {\n petsLoaded = true\n setPets(list)\n }, () => {\n // Retry on the next poll tick.\n })\n }\n const seq = stateSeq + 1\n stateSeq = seq\n petApi.state().then((snapshot) => {\n if (seq !== stateSeq) return\n setSnapshot(snapshot)\n }, () => {\n if (seq !== stateSeq) return\n setState('error', 'pet.state transport error')\n })\n }\n\n const disposePoll = ctx.effect(() => {\n // Poll only while the tab is visible: the host snapshot does not\n // change while the page is hidden, so a background interval would\n // only burn RPCs (browser throttling is an unreliable backstop).\n // Coming back to the tab refreshes the pet immediately instead of\n // waiting out the next 2 s cycle.\n let timer: number | undefined\n const stop = (): void => {\n if (timer !== undefined) {\n window.clearInterval(timer)\n timer = undefined\n }\n }\n const start = (): void => {\n if (timer === undefined && document.visibilityState === 'visible') {\n timer = window.setInterval(pollNow, POLL_MS)\n }\n }\n const onVisibility = (): void => {\n if (document.visibilityState === 'visible') {\n pollNow()\n start()\n } else {\n stop()\n }\n }\n start()\n document.addEventListener('visibilitychange', onVisibility)\n return () => {\n stop()\n document.removeEventListener('visibilitychange', onVisibility)\n }\n }, 'pet: poll')\n\n // Clicking a session bubble jumps the GUI to that session. A bubble\n // can outlive its disposed session by one poll tick, and the sessions\n // service fails loud on unknown ids, so consult the live list first.\n // The pet's type program also loads the host-side dsh-session package\n // through the service types, whose Context merge declares a different\n // 'sessions' face; pin the browser runtime's outward face here.\n const sessions = ctx.sessions as unknown as ISessions\n const openSession = (sessionId: string): void => {\n const list = sessions.list.getSnapshot()\n if (list.byId[sessionId as SessionId] === undefined) return\n sessions.open(sessionId as SessionId)\n }\n\n const injected = (): PetInjected => ({\n store: petStore,\n ensure: pollNow,\n openSession,\n pet: () => {\n petApi.interact('pet').then((result) => {\n setFeedback({\n text: result.reaction,\n kind: 'pet',\n at: Date.now(),\n })\n }, () => {\n // Ignore transport errors on interactions; the next poll resyncs.\n })\n },\n feed: () => {\n petApi.interact('feed').then((result) => {\n setFeedback({\n text: result.reaction,\n kind: 'feed',\n at: Date.now(),\n })\n }, () => {\n // Ignore transport errors on interactions; the next poll resyncs.\n })\n },\n hide: () => {\n petApi.setVisible(false).then(() => {\n pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n summon: () => {\n petApi.setVisible(true).then(() => {\n pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n dragEnd: (right, bottom) => {\n petApi.setConfig({ right, bottom }).then(() => {\n pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n rename: (name) => {\n petApi.setName(name).then((result) => {\n if (result.ok) pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n feedbackDone: () => {\n setFeedback(null)\n },\n })\n\n // The pet is host-global (its state/display/interactions have no session\n // dimension), and the official rc.6 shell declares no root-scoped slot\n // for a global floating surface — the dock is session-scoped, so a pet\n // mounted there would vanish on the new-conversation screen (issue #48).\n // The entry therefore mounts straight onto document.body via a single\n // React root for the page lifetime: PetSprite portals itself to body\n // when visible, and the hidden-state summon button is fixed-positioned.\n const container = document.createElement('div')\n container.dataset.dshPetRoot = ''\n container.dataset.dshPlugin = 'pet'\n document.body.appendChild(container)\n const petRoot = createRoot(container)\n petRoot.render(createElement(PetDockEntry, { ...injected(), t }))\n\n disposeUi = () => {\n petRoot.unmount()\n container.remove()\n disposePoll()\n disposeUi = undefined\n }\n } else if (!enabled() && disposeUi !== undefined) {\n disposeUi()\n disposeUi = undefined\n }\n }\n settingsScope.subscribe(syncUi)\n syncUi()\n}\n"],"x_google_ignoreList":[1],"mappings":"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"}
|
|
@@ -102,7 +102,7 @@ export function PetSprite(props) {
|
|
|
102
102
|
// Paint one static sprite frame up front either way, so the pet is never
|
|
103
103
|
// blank while the loop heat-up runs.
|
|
104
104
|
const leadCol = track.frames[0];
|
|
105
|
-
const lead = framePosition(cell,
|
|
105
|
+
const lead = framePosition(cell, row, leadCol, scaleRef.current);
|
|
106
106
|
if (spriteRef.current !== null) {
|
|
107
107
|
spriteRef.current.style.backgroundPosition = lead.x + 'px ' + lead.y + 'px';
|
|
108
108
|
}
|
|
@@ -120,7 +120,7 @@ export function PetSprite(props) {
|
|
|
120
120
|
const currentRow = rowOfTrack(current.animation);
|
|
121
121
|
const currentTrack = trimTrack(tracks[current.animation], rows[currentRow] ?? tracks[current.animation].frames.length);
|
|
122
122
|
const col = currentTrack.frames[current.frameIndex];
|
|
123
|
-
const pos = framePosition(cell,
|
|
123
|
+
const pos = framePosition(cell, currentRow, col, scaleRef.current);
|
|
124
124
|
if (spriteRef.current !== null) {
|
|
125
125
|
spriteRef.current.style.backgroundPosition = pos.x + 'px ' + pos.y + 'px';
|
|
126
126
|
}
|
|
@@ -152,7 +152,7 @@ export function PetSprite(props) {
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
const col = track.frames[st.index];
|
|
155
|
-
const pos = framePosition(cell,
|
|
155
|
+
const pos = framePosition(cell, row, col, scaleRef.current);
|
|
156
156
|
if (spriteRef.current !== null) {
|
|
157
157
|
spriteRef.current.style.backgroundPosition = pos.x + 'px ' + pos.y + 'px';
|
|
158
158
|
}
|
|
@@ -19,12 +19,10 @@ export declare function rowOfTrack(animation: PetAnimation): number;
|
|
|
19
19
|
* atlas coordinates here would drift each frame by the scale factor and
|
|
20
20
|
* render torn/overlapping frames.
|
|
21
21
|
*/
|
|
22
|
-
export declare function framePosition(cell: PetCell,
|
|
22
|
+
export declare function framePosition(cell: PetCell, row: number, col: number, scale?: number): {
|
|
23
23
|
x: number;
|
|
24
24
|
y: number;
|
|
25
25
|
};
|
|
26
|
-
/** Total duration of one track, ms. */
|
|
27
|
-
export declare function trackDuration(track: TrackDef): number;
|
|
28
26
|
/**
|
|
29
27
|
* Trim a track to the actual frame count of its row (the manifest's per-row
|
|
30
28
|
* counts are authoritative; this is a last-line guard against a definition
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spritesheet.d.ts","sourceRoot":"","sources":["../../../src/client/spritesheet.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAE1D,qDAAqD;AACrD,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAA;AAElC,mEAAmE;AACnE,wBAAgB,UAAU,CAAC,SAAS,EAAE,YAAY,GAAG,MAAM,CAG1D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"spritesheet.d.ts","sourceRoot":"","sources":["../../../src/client/spritesheet.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAE1D,qDAAqD;AACrD,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAA;AAElC,mEAAmE;AACnE,wBAAgB,UAAU,CAAC,SAAS,EAAE,YAAY,GAAG,MAAM,CAG1D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAE1G;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,GAAG,QAAQ,CAQvE"}
|
|
@@ -19,13 +19,9 @@ export function rowOfTrack(animation) {
|
|
|
19
19
|
* atlas coordinates here would drift each frame by the scale factor and
|
|
20
20
|
* render torn/overlapping frames.
|
|
21
21
|
*/
|
|
22
|
-
export function framePosition(cell,
|
|
22
|
+
export function framePosition(cell, row, col, scale = 1) {
|
|
23
23
|
return { x: -col * cell.width * scale, y: -row * cell.height * scale };
|
|
24
24
|
}
|
|
25
|
-
/** Total duration of one track, ms. */
|
|
26
|
-
export function trackDuration(track) {
|
|
27
|
-
return track.durations.reduce((sum, d) => sum + d, 0);
|
|
28
|
-
}
|
|
29
25
|
/**
|
|
30
26
|
* Trim a track to the actual frame count of its row (the manifest's per-row
|
|
31
27
|
* counts are authoritative; this is a last-line guard against a definition
|
package/lib/types/registry.d.ts
CHANGED
|
@@ -169,6 +169,4 @@ export declare function loadPetRegistry(options: PetRegistryOptions): PetRegistr
|
|
|
169
169
|
export declare function petEntryView(entry: PetEntry): PetDefinition;
|
|
170
170
|
/** The absolute file a pet's atlas resolves to (host asset route). */
|
|
171
171
|
export declare function petAtlasFile(entry: PetEntry): string;
|
|
172
|
-
/** The directory basename of one entry (legacy asset URL alias, e.g. whale). */
|
|
173
|
-
export declare function petDirAlias(entry: PetEntry): string;
|
|
174
172
|
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"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"}
|
package/lib/types/registry.js
CHANGED
|
@@ -317,7 +317,3 @@ export function petEntryView(entry) {
|
|
|
317
317
|
export function petAtlasFile(entry) {
|
|
318
318
|
return join(entry.dir, entry.spritesheetPath);
|
|
319
319
|
}
|
|
320
|
-
/** The directory basename of one entry (legacy asset URL alias, e.g. whale). */
|
|
321
|
-
export function petDirAlias(entry) {
|
|
322
|
-
return basename(entry.dir);
|
|
323
|
-
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACpF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAQ9C,OAAO,EAAgC,KAAK,uBAAuB,EAAE,MAAM,aAAa,CAAA;AACxF,OAAO,EASL,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linxin666/dsh-pet",
|
|
3
3
|
"description": "Multi-pet companion plugin for the dsh web GUI: a registry-driven floating pet that reacts to model activity, with per-pet naming, petting/feeding interactions and an affinity score",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -72,7 +72,6 @@
|
|
|
72
72
|
"@types/react": "~18.3.1",
|
|
73
73
|
"@types/react-dom": "~18.3.0",
|
|
74
74
|
"jsdom": "29.1.1",
|
|
75
|
-
"lightningcss": "^1.32.0",
|
|
76
75
|
"react": "^18.2.0",
|
|
77
76
|
"react-dom": "^18.2.0",
|
|
78
77
|
"tsdown": "^0.22.2",
|
package/src/client/PetSprite.tsx
CHANGED
|
@@ -141,7 +141,7 @@ export function PetSprite(props: PetSpriteProps): ReactPortal {
|
|
|
141
141
|
// Paint one static sprite frame up front either way, so the pet is never
|
|
142
142
|
// blank while the loop heat-up runs.
|
|
143
143
|
const leadCol = track.frames[0]!
|
|
144
|
-
const lead = framePosition(cell,
|
|
144
|
+
const lead = framePosition(cell, row, leadCol, scaleRef.current)
|
|
145
145
|
if (spriteRef.current !== null) {
|
|
146
146
|
spriteRef.current.style.backgroundPosition = lead.x + 'px ' + lead.y + 'px'
|
|
147
147
|
}
|
|
@@ -161,7 +161,7 @@ export function PetSprite(props: PetSpriteProps): ReactPortal {
|
|
|
161
161
|
rows[currentRow] ?? tracks[current.animation].frames.length,
|
|
162
162
|
)
|
|
163
163
|
const col = currentTrack.frames[current.frameIndex]!
|
|
164
|
-
const pos = framePosition(cell,
|
|
164
|
+
const pos = framePosition(cell, currentRow, col, scaleRef.current)
|
|
165
165
|
if (spriteRef.current !== null) {
|
|
166
166
|
spriteRef.current.style.backgroundPosition = pos.x + 'px ' + pos.y + 'px'
|
|
167
167
|
}
|
|
@@ -192,7 +192,7 @@ export function PetSprite(props: PetSpriteProps): ReactPortal {
|
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
const col = track.frames[st.index]!
|
|
195
|
-
const pos = framePosition(cell,
|
|
195
|
+
const pos = framePosition(cell, row, col, scaleRef.current)
|
|
196
196
|
if (spriteRef.current !== null) {
|
|
197
197
|
spriteRef.current.style.backgroundPosition = pos.x + 'px ' + pos.y + 'px'
|
|
198
198
|
}
|
|
@@ -26,15 +26,10 @@ export function rowOfTrack(animation: PetAnimation): number {
|
|
|
26
26
|
* atlas coordinates here would drift each frame by the scale factor and
|
|
27
27
|
* render torn/overlapping frames.
|
|
28
28
|
*/
|
|
29
|
-
export function framePosition(cell: PetCell,
|
|
29
|
+
export function framePosition(cell: PetCell, row: number, col: number, scale = 1): { x: number; y: number } {
|
|
30
30
|
return { x: -col * cell.width * scale, y: -row * cell.height * scale }
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/** Total duration of one track, ms. */
|
|
34
|
-
export function trackDuration(track: TrackDef): number {
|
|
35
|
-
return track.durations.reduce((sum, d) => sum + d, 0)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
33
|
/**
|
|
39
34
|
* Trim a track to the actual frame count of its row (the manifest's per-row
|
|
40
35
|
* counts are authoritative; this is a last-line guard against a definition
|
package/src/registry.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
3
3
|
import { tmpdir } from 'node:os'
|
|
4
4
|
import { join } from 'node:path'
|
|
5
5
|
import {
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
codexPetsDir,
|
|
9
9
|
loadPetRegistry,
|
|
10
10
|
petAtlasFile,
|
|
11
|
+
petPackageRoot,
|
|
11
12
|
resolvePetManifest,
|
|
12
13
|
} from './registry.ts'
|
|
13
14
|
|
|
@@ -147,6 +148,25 @@ describe('resolvePetManifest', () => {
|
|
|
147
148
|
})
|
|
148
149
|
|
|
149
150
|
describe('loadPetRegistry', () => {
|
|
151
|
+
it('ships the original and refined whale variants while keeping the original default', () => {
|
|
152
|
+
const registry = loadPetRegistry({
|
|
153
|
+
packageRoot: petPackageRoot(import.meta.url),
|
|
154
|
+
petsDir: '',
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
expect(registry.entries.map(entry => entry.id)).toEqual([
|
|
158
|
+
'whale-girl',
|
|
159
|
+
'whale-girl-refined',
|
|
160
|
+
])
|
|
161
|
+
expect(registry.byId('whale-girl')?.displayName).toBe('鲸鱼娘(原版)')
|
|
162
|
+
expect(registry.byId('whale-girl-refined')?.displayName).toBe('鲸鱼娘(精致版)')
|
|
163
|
+
expect(existsSync(petAtlasFile(registry.byId('whale-girl-refined')!))).toBe(true)
|
|
164
|
+
expect(readFileSync(petAtlasFile(registry.byId('whale-girl')!)).equals(
|
|
165
|
+
readFileSync(petAtlasFile(registry.byId('whale-girl-refined')!)),
|
|
166
|
+
)).toBe(false)
|
|
167
|
+
expect(registry.defaultEntry().id).toBe('whale-girl')
|
|
168
|
+
})
|
|
169
|
+
|
|
150
170
|
it('scans built-in assets, the custom pets dir, and composed extras with precedence', () => {
|
|
151
171
|
const root = tempDir()
|
|
152
172
|
try {
|
package/src/registry.ts
CHANGED
|
@@ -458,7 +458,3 @@ export function petAtlasFile(entry: PetEntry): string {
|
|
|
458
458
|
return join(entry.dir, entry.spritesheetPath)
|
|
459
459
|
}
|
|
460
460
|
|
|
461
|
-
/** The directory basename of one entry (legacy asset URL alias, e.g. whale). */
|
|
462
|
-
export function petDirAlias(entry: PetEntry): string {
|
|
463
|
-
return basename(entry.dir)
|
|
464
|
-
}
|