@linxin666/dsh-pet 0.1.6 → 0.1.7
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/lib/client.js +20 -10
- package/lib/client.js.map +1 -1
- package/lib/types/client/PetDockEntry.d.ts +5 -6
- package/lib/types/client/PetDockEntry.d.ts.map +1 -1
- package/lib/types/client/PetDockEntry.js +4 -5
- package/lib/types/client/WhalePet.d.ts.map +1 -1
- package/lib/types/client/WhalePet.js +15 -3
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +7 -6
- package/package.json +1 -1
- package/src/client/PetDockEntry.tsx +5 -6
- package/src/client/WhalePet.tsx +16 -3
- package/src/client/index.ts +7 -6
- package/src/client/pet.module.css +1 -1
- package/lib/types/client/slots-augment.d.ts +0 -19
- package/lib/types/client/slots-augment.d.ts.map +0 -1
- package/lib/types/client/slots-augment.js +0 -1
- package/src/client/slots-augment.ts +0 -27
package/lib/client.js
CHANGED
|
@@ -304,7 +304,7 @@ window.__ModuleLoader__.load({
|
|
|
304
304
|
}
|
|
305
305
|
//#endregion
|
|
306
306
|
//#region \0dsh-css:/home/runner/work/dsh-web-ui/dsh-web-ui/packages/dsh-pet/src/client/pet.module.css.mjs
|
|
307
|
-
const css$1 = ".l0P8iq_float{pointer-events:auto;-webkit-user-select:none;user-select:none;flex-direction:column;align-items:center;display:flex;position:fixed}.l0P8iq_sprite{image-rendering:auto;touch-action:none;position:relative}.l0P8iq_bubble{white-space:nowrap;color:#fff;pointer-events:none;border-radius:999px;margin-bottom:6px;padding:4px 10px;font-size:12px;line-height:1.4;animation:2.6s ease-out forwards l0P8iq_pet-bubble-pop;position:absolute;bottom:100%;box-shadow:0 2px 8px #00000040}.l0P8iq_bubblePet{background:#f472b6eb}.l0P8iq_bubbleFeed{background:#38bdf8eb}@keyframes l0P8iq_pet-bubble-pop{0%{opacity:0;transform:translateY(6px)scale(.85)}15%{opacity:1;transform:translateY(0)scale(1.05)}25%{transform:translateY(0)scale(1)}75%{opacity:1}to{opacity:0;transform:translateY(-8px)scale(.95)}}.l0P8iq_panel{color:#e2e8f0;backdrop-filter:blur(6px);background:#0f172aeb;border:1px solid #94a3b859;border-radius:10px;flex-direction:column;gap:6px;min-width:132px;padding:8px 10px;font-size:12px;display:flex;position:absolute;bottom:
|
|
307
|
+
const css$1 = ".l0P8iq_float{pointer-events:auto;-webkit-user-select:none;user-select:none;flex-direction:column;align-items:center;display:flex;position:fixed}.l0P8iq_sprite{image-rendering:auto;touch-action:none;position:relative}.l0P8iq_bubble{white-space:nowrap;color:#fff;pointer-events:none;border-radius:999px;margin-bottom:6px;padding:4px 10px;font-size:12px;line-height:1.4;animation:2.6s ease-out forwards l0P8iq_pet-bubble-pop;position:absolute;bottom:100%;box-shadow:0 2px 8px #00000040}.l0P8iq_bubblePet{background:#f472b6eb}.l0P8iq_bubbleFeed{background:#38bdf8eb}@keyframes l0P8iq_pet-bubble-pop{0%{opacity:0;transform:translateY(6px)scale(.85)}15%{opacity:1;transform:translateY(0)scale(1.05)}25%{transform:translateY(0)scale(1)}75%{opacity:1}to{opacity:0;transform:translateY(-8px)scale(.95)}}.l0P8iq_panel{color:#e2e8f0;backdrop-filter:blur(6px);background:#0f172aeb;border:1px solid #94a3b859;border-radius:10px;flex-direction:column;gap:6px;min-width:132px;padding:8px 10px;font-size:12px;display:flex;position:absolute;bottom:100%;box-shadow:0 4px 16px #00000059}.l0P8iq_rankRow{white-space:nowrap;justify-content:space-between;gap:10px;display:flex}.l0P8iq_nameCell{font-weight:600}.l0P8iq_renameRow{align-items:center;gap:6px;display:flex}.l0P8iq_nameInput{color:#e2e8f0;background:#1e293be6;border:1px solid #7dd3fc80;border-radius:6px;outline:none;flex:1;min-width:0;padding:3px 6px;font-size:12px}.l0P8iq_nameInput:focus{border-color:#38bdf8;box-shadow:0 0 0 2px #38bdf873}.l0P8iq_actions{gap:6px;display:flex}.l0P8iq_action{cursor:pointer;color:#0f172a;background:linear-gradient(#7dd3fc,#38bdf8);border:none;border-radius:6px;flex:1;padding:4px 8px;font-size:12px;transition:filter .12s,box-shadow .12s}.l0P8iq_action:hover{filter:brightness(1.08)}.l0P8iq_action:active{filter:brightness(.94)}.l0P8iq_action:focus-visible{outline:none;box-shadow:0 0 0 2px #38bdf8d9}.l0P8iq_summon{color:#7dd3fc;cursor:pointer;background:#0f172abf;border:1px dashed #7dd3fc99;border-radius:999px;padding:2px 10px;font-size:11px;transition:border-color .12s,color .12s,background .12s,box-shadow .12s}.l0P8iq_summon:hover{color:#bae6fd;background:#0f172ae6;border-color:#7dd3fcf2}.l0P8iq_summon:active{color:#7dd3fc;border-color:#7dd3fccc}.l0P8iq_summon:focus-visible{outline:none;box-shadow:0 0 0 2px #38bdf8d9}@media (prefers-reduced-motion:reduce){.l0P8iq_bubble{opacity:1;animation:none}.l0P8iq_action,.l0P8iq_summon{transition:none}}";
|
|
308
308
|
const tagId$1 = "@linxin666/dsh-pet/pet.module.css";
|
|
309
309
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
310
310
|
const tag = document.createElement("style");
|
|
@@ -364,6 +364,7 @@ window.__ModuleLoader__.load({
|
|
|
364
364
|
const [nameDraft, setNameDraft] = (0, react.useState)("");
|
|
365
365
|
const [dragPos, setDragPos] = (0, react.useState)(null);
|
|
366
366
|
const dragRef = (0, react.useRef)(null);
|
|
367
|
+
const hideTimerRef = (0, react.useRef)(null);
|
|
367
368
|
const frameRef = (0, react.useRef)({
|
|
368
369
|
track: null,
|
|
369
370
|
index: 0,
|
|
@@ -440,6 +441,12 @@ window.__ModuleLoader__.load({
|
|
|
440
441
|
return () => window.clearTimeout(timer);
|
|
441
442
|
}, [feedback]);
|
|
442
443
|
const draggedRef = (0, react.useRef)(false);
|
|
444
|
+
const clearHideTimer = () => {
|
|
445
|
+
if (hideTimerRef.current !== null) {
|
|
446
|
+
window.clearTimeout(hideTimerRef.current);
|
|
447
|
+
hideTimerRef.current = null;
|
|
448
|
+
}
|
|
449
|
+
};
|
|
443
450
|
const onPointerDown = (e) => {
|
|
444
451
|
e.preventDefault();
|
|
445
452
|
e.target.setPointerCapture?.(e.pointerId);
|
|
@@ -487,11 +494,15 @@ window.__ModuleLoader__.load({
|
|
|
487
494
|
bottom: pos.bottom,
|
|
488
495
|
zIndex: 2147483e3
|
|
489
496
|
},
|
|
490
|
-
onPointerEnter: () =>
|
|
497
|
+
onPointerEnter: () => {
|
|
498
|
+
clearHideTimer();
|
|
499
|
+
setHovered(true);
|
|
500
|
+
},
|
|
491
501
|
onPointerLeave: (e) => {
|
|
492
502
|
const next = e.relatedTarget;
|
|
493
503
|
if (next instanceof Node && floatRef.current?.contains(next)) return;
|
|
494
|
-
|
|
504
|
+
clearHideTimer();
|
|
505
|
+
hideTimerRef.current = window.setTimeout(() => setHovered(false), 200);
|
|
495
506
|
},
|
|
496
507
|
children: [
|
|
497
508
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -598,11 +609,10 @@ window.__ModuleLoader__.load({
|
|
|
598
609
|
//#endregion
|
|
599
610
|
//#region src/client/PetDockEntry.tsx
|
|
600
611
|
/**
|
|
601
|
-
* Dock anchor inside `conversation.input.
|
|
602
|
-
*
|
|
603
|
-
*
|
|
604
|
-
*
|
|
605
|
-
* floating WhalePet (portal); while hidden it renders the summon button.
|
|
612
|
+
* Dock anchor inside the official SDK's `conversation.input.dock` seat
|
|
613
|
+
* (the ambient readout band above the composer card, listed so several
|
|
614
|
+
* additions can stack). While visible it mounts the floating WhalePet
|
|
615
|
+
* (portal); while hidden it renders the summon button.
|
|
606
616
|
* @module @linxin666/dsh-pet/client/PetDockEntry
|
|
607
617
|
*/
|
|
608
618
|
const DEFAULT_DISPLAY = {
|
|
@@ -1485,8 +1495,8 @@ window.__ModuleLoader__.load({
|
|
|
1485
1495
|
setFeedback(null);
|
|
1486
1496
|
}
|
|
1487
1497
|
});
|
|
1488
|
-
const disposeDock = ctx.slots.inject("conversation.input.
|
|
1489
|
-
name: "conversation.input.
|
|
1498
|
+
const disposeDock = ctx.slots.inject("conversation.input.dock", () => ctx.slots.register({
|
|
1499
|
+
name: "conversation.input.dock",
|
|
1490
1500
|
id: "pet",
|
|
1491
1501
|
order: 110,
|
|
1492
1502
|
inject: injected,
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["styles","styles","css"],"sources":["../../../packages/dsh-pet/src/client/pet-store.ts","../../../packages/dsh-pet/src/client/spritesheet.ts","../../../packages/dsh-pet/src/client/WhalePet.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'\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 /** 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 /** 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 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 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","/**\n * Whale-girl spritesheet geometry and animation tracks.\n *\n * The atlas follows the Codex/hatch-pet contract: 8 columns × 9 rows of\n * 192×208 cells (1536×1872 total), rows in this order:\n * 0 idle, 1 running-right, 2 running-left, 3 waving, 4 jumping,\n * 5 failed, 6 waiting, 7 running, 8 review\n *\n * Frame counts and per-frame durations are per-track definitions below; the\n * whale-girl atlas is produced by the hatch-pet pipeline, so calibrate\n * `TRACKS` against the actual run (`pet_request.json` frame counts) when the\n * asset lands. Tracks that do not loop hand off to `fallback`.\n * @module @linxin666/dsh-pet/client/spritesheet\n */\n\nimport type { PetAnimation } from '../state.ts'\n\n/** Atlas cell size in px (Codex contract). */\nexport const FRAME_WIDTH = 192\nexport const FRAME_HEIGHT = 208\n/** Columns per row (max frames per track). */\nexport const FRAME_COLUMNS = 8\n\n/** One animation track: frame indices into the row + per-frame durations. */\nexport interface TrackDef {\n /** Frame indices (columns) played in order; must be < FRAME_COLUMNS. */\n frames: readonly number[]\n /** Per-frame duration in ms; same length as frames. */\n durations: readonly number[]\n /** Whether the track loops; a non-looping track hands off to fallback. */\n loop: boolean\n /** Track to play after a non-looping track finishes. */\n fallback?: PetAnimation\n}\n\n/**\n * Track definitions for the whale-girl. Durations are tuned for a soft,\n * slow-healing feel (roughly 2.5× the earlier fast draft — the pet should\n * breathe, not race); calibrate frame counts against the hatch-pet run when\n * the asset lands (rows may carry 4–8 frames).\n */\nexport const TRACKS: Record<PetAnimation, TrackDef> = {\n idle: { frames: [0, 1, 2, 3, 4, 5], durations: [400, 400, 500, 400, 400, 500], loop: true },\n 'running-right': { frames: [0, 1, 2, 3, 4, 5, 6, 7], durations: [225, 225, 225, 225, 225, 225, 225, 225], loop: true },\n 'running-left': { frames: [0, 1, 2, 3, 4, 5, 6, 7], durations: [225, 225, 225, 225, 225, 225, 225, 225], loop: true },\n waving: { frames: [0, 1, 2, 3], durations: [350, 350, 350, 350], loop: true },\n jumping: { frames: [0, 1, 2, 3, 4], durations: [300, 300, 300, 350, 350], loop: false, fallback: 'idle' },\n failed: { frames: [0, 1, 2, 3, 4, 5, 6, 7], durations: [450, 450, 450, 500, 550, 600, 450, 450], loop: false, fallback: 'idle' },\n waiting: { frames: [0, 1, 2, 3, 4, 5], durations: [450, 450, 500, 450, 450, 500], loop: true },\n running: { frames: [0, 1, 2, 3, 4, 5], durations: [250, 250, 250, 250, 250, 250], loop: true },\n review: { frames: [0, 1, 2, 3, 4, 5], durations: [550, 550, 550, 550, 550, 550], loop: true },\n}\n\n/** Row index of one animation track (mirrors state.ts rowOf). */\nexport function rowOfTrack(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 * 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(row: number, col: number, scale = 1): { x: number; y: number } {\n return { x: -col * FRAME_WIDTH * scale, y: -row * FRAME_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 * Detect how many frames each row actually carries by scanning the decoded\n * atlas for non-transparent cells (hatch-pet rows may hold 4–8 frames; the\n * unused trailing cells are fully transparent). Rows whose every sample is\n * transparent report 0.\n * @param image - the fully decoded spritesheet (natural size 1536×1872).\n * @returns per-row frame counts, length 9.\n */\nexport function detectFrameCounts(image: HTMLImageElement): number[] {\n const canvas = document.createElement('canvas')\n canvas.width = image.naturalWidth\n canvas.height = image.naturalHeight\n const ctx = canvas.getContext('2d')\n if (ctx === null) return Array.from({ length: 9 }, () => FRAME_COLUMNS)\n ctx.drawImage(image, 0, 0)\n const data = ctx.getImageData(0, 0, canvas.width, canvas.height).data\n const counts: number[] = []\n const stride = FRAME_COLUMNS * FRAME_WIDTH\n const probeStep = 8\n const margin = 12\n for (let row = 0; row < 9; row++) {\n let count = 0\n for (let col = 0; col < FRAME_COLUMNS; col++) {\n let hasContent = false\n const x0 = col * FRAME_WIDTH\n const y0 = row * FRAME_HEIGHT\n for (let y = y0 + margin; y < y0 + FRAME_HEIGHT - margin && !hasContent; y += probeStep) {\n for (let x = x0 + margin; x < x0 + FRAME_WIDTH - margin && !hasContent; x += probeStep) {\n const idx = (y * stride + x) * 4\n if ((data[idx + 3] ?? 0) > 8) hasContent = true\n }\n }\n if (hasContent) count += 1\n }\n counts.push(count)\n }\n return counts\n}\n\n/**\n * Trim a track to the actual frame count of its row. A row with 0 detected\n * frames degrades to the first frame (the atlas is still loading or corrupt)\n * 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))\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","/**\n * Whale-girl companion component — the browser half's centerpiece. Renders a\n * fixed-position floating sprite (React portal onto document.body), plays\n * the spritesheet track matching the host animation snapshot, and exposes\n * the interaction surface: click to pet, hover panel with feed/hide, drag to\n * reposition (persisted via setConfig).\n * @module @linxin666/dsh-pet/client/WhalePet\n */\n\nimport { useEffect, useRef, useState } from 'react'\nimport type { PointerEvent as ReactPointerEvent, ReactPortal } from 'react'\nimport { createPortal } from 'react-dom'\nimport type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDisplayConfig } from '../persist.ts'\nimport type { PetStateView } from '../service.ts'\nimport type { PetFeedback } from './pet-store.ts'\nimport { framePosition, FRAME_WIDTH, FRAME_HEIGHT, FRAME_COLUMNS, TRACKS, rowOfTrack, trimTrack, detectFrameCounts } from './spritesheet.ts'\nimport type { PetAnimation } from '../state.ts'\nimport { NS } from './locales.ts'\nimport styles from './pet.module.css'\n\n/** Browser URL of the whale-girl atlas (served by the host half's own route). */\nexport const PET_SPRITESHEET_URL = '/pet/whale/spritesheet.webp'\n\n/** Browser URL of the whale-girl manifest (authoritative per-row frame counts). */\nexport const PET_MANIFEST_URL = '/pet/whale/pet.json'\n\n/** Props injected by the slot registration (store actions + locale). */\nexport interface WhalePetProps {\n /** Latest host snapshot; null while loading. */\n snapshot: PetStateView | null\n /** Display configuration (persisted by the host). */\n display: PetDisplayConfig\n /** Active reaction bubble, if any. */\n feedback: PetFeedback | null\n /** Pet the whale girl (click). */\n onPet: () => void\n /** Feed the whale girl (panel button). */\n onFeed: () => void\n /** Hide the whale girl (panel button). */\n onHide: () => void\n /** Persist a drag position. */\n onDragEnd: (right: number, bottom: number) => void\n /** Rename the pet (persisted by the host). */\n onRename: (name: 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 TRACKS; the atlas image is loaded once and\n * the background position is written straight to the sprite element (no\n * per-frame React state).\n */\nexport function WhalePet(props: WhalePetProps): ReactPortal {\n const { snapshot, display, feedback } = props\n const spriteRef = useRef<HTMLDivElement | null>(null)\n const floatRef = useRef<HTMLDivElement | null>(null)\n const [imageReady, setImageReady] = useState(false)\n const [frameCounts, setFrameCounts] = useState<number[] | null>(null)\n const [hovered, setHovered] = useState(false)\n const [renaming, setRenaming] = useState(false)\n const [nameDraft, setNameDraft] = useState('')\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 frameRef = useRef<{ track: PetAnimation | null; index: number; elapsed: number }>({\n track: null,\n index: 0,\n elapsed: 0,\n })\n\n // Load the atlas once; then resolve per-row frame counts so tracks never\n // play the transparent trailing cells of a short row. One decoded Image\n // feeds both the sprite render and the frame-count detection. The counts\n // prefer the authoritatively recorded `frames` field on the pet.json\n // manifest route and only fall back to the getImageData atlas scan when\n // that field is absent (older manifests).\n useEffect(() => {\n let cancelled = false\n const img = new Image()\n img.onload = () => {\n if (cancelled) return\n setImageReady(true)\n fetch(PET_MANIFEST_URL)\n .then((res) => (res.ok ? res.json() : Promise.resolve<{ frames?: unknown }>({})))\n .then((manifest: { frames?: unknown }) => {\n if (cancelled) return\n const frames = manifest.frames\n if (Array.isArray(frames) && frames.length === 9 && frames.every((n) => typeof n === 'number')) {\n setFrameCounts(frames as number[])\n } else {\n setFrameCounts(detectFrameCounts(img))\n }\n })\n .catch(() => {\n if (!cancelled) setFrameCounts(detectFrameCounts(img))\n })\n }\n img.src = PET_SPRITESHEET_URL\n return () => {\n cancelled = true\n img.onload = null\n }\n }, [])\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 / FRAME_HEIGHT\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 // Paint one static sprite frame up front either way, so the pet is never\n // blank while the loop heat-up runs.\n const row = rowOfTrack(animation)\n const track = frameCounts === null\n ? TRACKS[animation]\n : trimTrack(TRACKS[animation], frameCounts[row] ?? TRACKS[animation].frames.length)\n const leadCol = track.frames[0]!\n const lead = framePosition(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 const tick = (ts: number): void => {\n const delta = ts - last\n last = ts\n // Trim the track to the row's real frame count (transparent cells\n // would render as a vanishing pet).\n const row = rowOfTrack(animation)\n const track = frameCounts === null\n ? TRACKS[animation]\n : trimTrack(TRACKS[animation], frameCounts[row] ?? TRACKS[animation].frames.length)\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 { x, y } = framePosition(row, col, scaleRef.current)\n if (spriteRef.current !== null) {\n spriteRef.current.style.backgroundPosition = `${x}px ${y}px`\n }\n raf = requestAnimationFrame(tick)\n }\n raf = requestAnimationFrame(tick)\n return () => cancelAnimationFrame(raf)\n }, [animation, frameCounts])\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 800ms 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 whale.\n const draggedRef = useRef(false)\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(FRAME_WIDTH * spriteScale)\n const spriteHeight = Math.round(FRAME_HEIGHT * spriteScale)\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={() => setHovered(true)}\n onPointerLeave={(e) => {\n // The panel and bubble render OUTSIDE the container's box (absolute,\n // above the sprite), so moving onto them fires pointerleave on the\n // container. Treat a target still inside the container's DOM (the\n // overflowed panel) as \"still hovering\".\n const next = e.relatedTarget\n if (next instanceof Node && floatRef.current?.contains(next)) return\n setHovered(false)\n }}\n >\n <div\n ref={spriteRef}\n className={styles.sprite}\n style={{\n width: spriteWidth,\n height: spriteHeight,\n backgroundImage: imageReady ? `url(${PET_SPRITESHEET_URL})` : undefined,\n backgroundSize: `${FRAME_WIDTH * FRAME_COLUMNS * spriteScale}px ${FRAME_HEIGHT * 9 * 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=\"whale girl\"\n />\n {feedback !== null && (\n <div key={feedback.at} className={`${styles.bubble} ${feedback.kind === 'feed' ? styles.bubbleFeed : styles.bubblePet}`}>\n {feedback.text}\n </div>\n )}\n {hovered && dragRef.current === null && (\n <div className={styles.panel}>\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 onKeyDown={(e) => {\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}>{snapshot?.name ?? '鲸鱼娘'}</span>\n <span>{props.t('pet.rank', { rank: snapshot?.affinity.rank ?? '?' })}</span>\n </div>\n <div className={styles.rankRow}>\n <span>{props.t('pet.treats', { n: snapshot?.treats.stocked ?? 0 })}</span>\n <span>{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 setNameDraft(snapshot?.name ?? '')\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 * Dock anchor inside `conversation.input.selector.context`: the input\n * selector row mounts in EVERY conversation phase (no-session cold start,\n * the blank-session hero, and the active seat), so the floating pet stays on\n * screen on the new-conversation screen too. While visible it mounts the\n * floating WhalePet (portal); while hidden it renders the summon button.\n * @module @linxin666/dsh-pet/client/PetDockEntry\n */\n\nimport { useEffect, useSyncExternalStore, type ReactElement } from 'react'\nimport type { PropsLocale, PropsRuntime } 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 { PetStoreInstance } from './pet-store.ts'\nimport { WhalePet } from './WhalePet.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 + feedback). */\n store: PetStoreInstance\n /** Ensure the first snapshot is fetched (called on mount). */\n ensure: () => void\n /** Pet the whale girl (click). */\n pet: () => void\n /** Feed the whale girl. */\n feed: () => void\n /** Hide the whale girl. */\n hide: () => void\n /** Summon the hidden whale girl back. */\n summon: () => void\n /** Persist a drag position. */\n dragEnd: (right: number, bottom: number) => void\n /** Rename the pet (persisted by the host). */\n rename: (name: string) => void\n /** Clear the reaction bubble. */\n feedbackDone: () => void\n}\n\n/** Composed props of the dock entry (runtime + locale + injected). */\nexport type PetDockEntryProps =\n PropsRuntime<'conversation.input.selector.context'>\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 WhalePet (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 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 <WhalePet\n snapshot={snapshot}\n display={snapshot?.display ?? DEFAULT_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 onFeedbackDone={props.feedbackDone}\n t={props.t}\n />\n </span>\n )\n }\n return (\n <button\n type=\"button\"\n className={styles.summon}\n onClick={props.summon}\n data-testid=\"pet-summon\"\n >\n {props.t('pet.summon', { name: snapshot?.name ?? '鲸鱼娘' })}\n </button>\n )\n}\n","/**\n * Shared chrome for the plugin settings card: a disclosure header naming the\n * plugin and what its settings govern, the controls inside, and the save that\n * 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. Mirrors the official ui-plugin-config PluginCard in a self-contained\n * slice (this package must not depend on a sibling UI package).\n */\n\nimport { useState, type ReactNode } from 'react'\nimport type { CardShell } from './settings-form.ts'\nimport type { SettingsCardKey } from './locales.ts'\nimport css from './settings-card.module.css'\n\n/** Card chrome shared by every plugin settings card. */\nexport interface PluginSettingsCardProps {\n /** Locale reader for this card's copy. */\n t: (key: SettingsCardKey) => string\n /** Locale key of the plugin's name. */\n titleKey: SettingsCardKey\n /** Locale key of the line describing what this plugin's settings govern. */\n descriptionKey: SettingsCardKey\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 /** 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 when the namespace is unavailable.\n */\nexport function PluginSettingsCard(props: PluginSettingsCardProps) {\n const [open, setOpen] = useState(false)\n const { state } = props\n if (!state.available) return null\n const title = props.t(props.titleKey)\n const blocked = !state.dirty || state.invalid || state.saving\n return (\n <li className={css.card}>\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}</span>\n <span className={css.description}>{props.t(props.descriptionKey)}</span>\n </span>\n {state.dirty ? <span className={css.pending}>{props.t('settings.unsaved')}</span> : null}\n <span className={open ? css.chevronOpen : css.chevron}>▾</span>\n </button>\n {open\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 <div className={css.footer}>\n {state.failed ? <p className={css.failed} role=\"status\">{props.t('settings.saveFailed')}</p> : 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 </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\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 <select\n id={props.id}\n className={css.select}\n value={props.text}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n >\n <option value=\"\">{props.inheritLabel}</option>\n <option value=\"true\">{props.onLabel}</option>\n <option value=\"false\">{props.offLabel}</option>\n </select>\n <p className={css.hint}>{props.hint}</p>\n </div>\n )\n}\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. Mirrors the official\n * ui-plugin-config card-store pattern in a self-contained slice: this\n * package must not depend on a sibling UI package.\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 /** 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 not served to this client; the card renders nothing. */\n available: 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\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 /** Perform the write and report whether the Host holds the staged value afterwards. */\n run: (() => Promise<boolean>) | undefined\n}\n\n/** A whole-number field. An empty draft clears the field; any other draft that is not a finite number blocks the save. */\nexport function numberField(field: string): FieldSpec {\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 return Number.isFinite(parsed) ? { kind: 'set', value: parsed } : undefined\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/** 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 if (text === 'true') return { kind: 'set', value: true }\n if (text === 'false') return { kind: 'set', value: false }\n return 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 private saving = false\n private failed = false\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 scope.subscribe(() => { this.publish() })\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 === '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 }\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.publish()\n },\n }\n }\n\n /**\n * Write every staged edit, then re-seed from what the Host accepted.\n * @returns settlement after every write and the read-back.\n */\n async save(): Promise<void> {\n const plan = this.plan()\n const writes = plan.flatMap(item => item.run === undefined ? [] : [item.run])\n if (plan.length === 0 || this.saving || writes.length !== plan.length) return\n this.saving = true\n this.failed = false\n this.publish()\n let landed = true\n for (const write of writes) {\n landed = await write() && landed\n }\n if (landed) this.staged.clear()\n this.saving = false\n this.failed = !landed\n this.publish()\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, 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, run: undefined })\n else if (write.kind === 'clear') plan.push({ field, run: () => this.clear(field) })\n else plan.push({ field, 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 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.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: display layout and name, bound to the `pet` settings\n * namespace the host plugin registers. Registered into the\n * `settings.plugin.item` slot the plugin-configuration section renders.\n */\n\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { SettingsScope, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'\nimport { PluginSettingsCard, ValueField, BooleanField } from './PluginSettingsCard.tsx'\nimport { CardForm, booleanField, numberField, textField, type CardActions, type CardShell, type FieldState as CardFieldState } from './settings-form.ts'\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 /** User-customizable pet display name. */\n name?: 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 /** Pet name. */\n name: CardFieldState\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/** 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\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 textField('name'),\n ])\n this.store = this.form.bind(() => this.projection())\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 name: this.form.field('name'),\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/** Props the renderer binds for the pet settings card. */\nexport type PetSettingsCardProps =\n PropsRuntime<'web-ui.plugin.item'>\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 >\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 <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 <ValueField\n id=\"settings-pet-name\"\n label={t('settings.name')}\n hint={t('settings.nameHint')}\n {...fieldProps}\n {...state.name}\n onEdit={(text) => { props.edit('name', text) }}\n onReset={() => { props.resetField('name') }}\n />\n </PluginSettingsCard>\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 // 插件设置卡片(settings.plugin.item 席位)。\n 'settings.title': '宠物',\n 'settings.description': '鲸鱼娘的显示布局与名字。',\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.name': '名字',\n 'settings.nameHint': '宠物显示名,1–20 个字符。',\n 'settings.inherit': '继承',\n 'settings.on': '开',\n 'settings.off': '关',\n 'settings.overridden': '已覆盖',\n 'settings.reset': '恢复默认',\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 whale girl is on her way…',\n 'pet.state.error': 'The whale girl is lost (connection failed)',\n // Plugin settings card (the `settings.plugin.item` seat).\n 'settings.title': 'Pet',\n 'settings.description': 'The whale girl\\u2019s display layout and name.',\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.name': 'Name',\n 'settings.nameHint': 'The pet\\u2019s display name, 1\\u201320 characters.',\n 'settings.inherit': 'Inherit',\n 'settings.on': 'On',\n 'settings.off': 'Off',\n 'settings.overridden': 'Overridden',\n 'settings.reset': 'Reset to default',\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\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 — registers the whale-girl into the conversation\n * input selector row (the same every-phase row the git branch chip uses) and\n * drives it from the host's same-origin `/api/pet/*` JSON endpoints: poll the\n * host snapshot (~800 ms), forward interactions, persist drag positions. The\n * row anchor mounts the floating pet via portal; when the pet is hidden the\n * anchor becomes the summon button. Anchoring in the selector row (rather\n * than the session-only composer dock band) keeps the pet floating on the\n * new-conversation screen too, where no session exists to scope a slot by.\n * @module @linxin666/dsh-pet/client\n */\n\nimport type { ClientContext } 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 { createPetStore, type PetStoreInstance } from './pet-store.ts'\nimport { PetDockEntry, type PetInjected } from './PetDockEntry.tsx'\nimport { PetSettingsCard, PetSettingsCardController, type PetSettings } from './PetSettingsCard.tsx'\nimport { NS, en, zh } 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 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}\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 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}\n\n/** Poll interval for the host snapshot. */\nconst POLL_MS = 800\n\n/** Settings namespace the pet settings card edits (the Host plugin registers it). */\nconst PET_SETTINGS_NS = 'pet'\n\n/** Required services. */\nexport const inject = ['slots', 'locale', 'connection', 'settingsScope', 'remote']\n\n/** Re-exported for consumers that type against the injected face. */\nexport type { PetInjected, PetDockEntryProps } from './PetDockEntry.tsx'\nexport type { PetUiState, PetFeedback } from './pet-store.ts'\nexport type { PetSettingsCardFace, PetSettingsCardState } from './PetSettingsCard.tsx'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface SlotMap {\n /**\n * The child slot the Web UI plugin group declares; this card registers\n * into the group instead of the top-level `settings.plugin.item` list.\n * Spelled here with the same shape so this package can register without\n * depending on the sibling UI package.\n */\n 'web-ui.plugin.item': { kind: 'list'; scope: 'root'; owner: SettingsPluginItemOwnerProps }\n }\n}\n\n/** Owner share of a plugin card (the group card supplies nothing). */\nexport interface SettingsPluginItemOwnerProps {\n /** Marker field: card owner props are intentionally empty. */\n children?: never\n}\n\n/**\n * Client plugin body: register dictionaries, mount the dock entry and poll\n * loop while the plugin is enabled, and seat the settings card in the Web UI\n * plugin group.\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 settingsScope = ctx.settingsScope.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 // Plugin configuration card: one staged form over the `pet` settings\n // namespace, contributed to the Web UI plugin group.\n const petSettings = new PetSettingsCardController(settingsScope)\n ctx.slots.inject('web-ui.plugin.item', () => ctx.slots.register({\n name: 'web-ui.plugin.item',\n id: 'pet-settings',\n order: 140,\n locale: NS,\n inject: () => petSettings.inject(),\n }, PetSettingsCard))\n\n // The dock entry, its store, and the poll loop live while the plugin is\n // 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 setState = petStore.actions.setState\n const setFeedback = petStore.actions.setFeedback\n\n const pollNow = (): void => {\n petApi.state().then((snapshot) => {\n setSnapshot(snapshot)\n }, () => {\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 800 ms 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 const injected = (): PetInjected => ({\n store: petStore,\n ensure: pollNow,\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 input selector row mounts in EVERY conversation phase (cold\n // start, blank-session hero, active seat) — the composer dock band\n // only renders for an active session, which is why the pet used to\n // vanish on the new-conversation screen.\n const disposeDock = ctx.slots.inject('conversation.input.selector.context', () =>\n ctx.slots.register({\n name: 'conversation.input.selector.context',\n id: 'pet',\n order: 110,\n inject: injected,\n locale: NS,\n }, PetDockEntry))\n\n disposeUi = () => {\n disposeDock()\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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["styles","styles","css"],"sources":["../../../packages/dsh-pet/src/client/pet-store.ts","../../../packages/dsh-pet/src/client/spritesheet.ts","../../../packages/dsh-pet/src/client/WhalePet.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'\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 /** 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 /** 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 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 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","/**\n * Whale-girl spritesheet geometry and animation tracks.\n *\n * The atlas follows the Codex/hatch-pet contract: 8 columns × 9 rows of\n * 192×208 cells (1536×1872 total), rows in this order:\n * 0 idle, 1 running-right, 2 running-left, 3 waving, 4 jumping,\n * 5 failed, 6 waiting, 7 running, 8 review\n *\n * Frame counts and per-frame durations are per-track definitions below; the\n * whale-girl atlas is produced by the hatch-pet pipeline, so calibrate\n * `TRACKS` against the actual run (`pet_request.json` frame counts) when the\n * asset lands. Tracks that do not loop hand off to `fallback`.\n * @module @linxin666/dsh-pet/client/spritesheet\n */\n\nimport type { PetAnimation } from '../state.ts'\n\n/** Atlas cell size in px (Codex contract). */\nexport const FRAME_WIDTH = 192\nexport const FRAME_HEIGHT = 208\n/** Columns per row (max frames per track). */\nexport const FRAME_COLUMNS = 8\n\n/** One animation track: frame indices into the row + per-frame durations. */\nexport interface TrackDef {\n /** Frame indices (columns) played in order; must be < FRAME_COLUMNS. */\n frames: readonly number[]\n /** Per-frame duration in ms; same length as frames. */\n durations: readonly number[]\n /** Whether the track loops; a non-looping track hands off to fallback. */\n loop: boolean\n /** Track to play after a non-looping track finishes. */\n fallback?: PetAnimation\n}\n\n/**\n * Track definitions for the whale-girl. Durations are tuned for a soft,\n * slow-healing feel (roughly 2.5× the earlier fast draft — the pet should\n * breathe, not race); calibrate frame counts against the hatch-pet run when\n * the asset lands (rows may carry 4–8 frames).\n */\nexport const TRACKS: Record<PetAnimation, TrackDef> = {\n idle: { frames: [0, 1, 2, 3, 4, 5], durations: [400, 400, 500, 400, 400, 500], loop: true },\n 'running-right': { frames: [0, 1, 2, 3, 4, 5, 6, 7], durations: [225, 225, 225, 225, 225, 225, 225, 225], loop: true },\n 'running-left': { frames: [0, 1, 2, 3, 4, 5, 6, 7], durations: [225, 225, 225, 225, 225, 225, 225, 225], loop: true },\n waving: { frames: [0, 1, 2, 3], durations: [350, 350, 350, 350], loop: true },\n jumping: { frames: [0, 1, 2, 3, 4], durations: [300, 300, 300, 350, 350], loop: false, fallback: 'idle' },\n failed: { frames: [0, 1, 2, 3, 4, 5, 6, 7], durations: [450, 450, 450, 500, 550, 600, 450, 450], loop: false, fallback: 'idle' },\n waiting: { frames: [0, 1, 2, 3, 4, 5], durations: [450, 450, 500, 450, 450, 500], loop: true },\n running: { frames: [0, 1, 2, 3, 4, 5], durations: [250, 250, 250, 250, 250, 250], loop: true },\n review: { frames: [0, 1, 2, 3, 4, 5], durations: [550, 550, 550, 550, 550, 550], loop: true },\n}\n\n/** Row index of one animation track (mirrors state.ts rowOf). */\nexport function rowOfTrack(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 * 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(row: number, col: number, scale = 1): { x: number; y: number } {\n return { x: -col * FRAME_WIDTH * scale, y: -row * FRAME_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 * Detect how many frames each row actually carries by scanning the decoded\n * atlas for non-transparent cells (hatch-pet rows may hold 4–8 frames; the\n * unused trailing cells are fully transparent). Rows whose every sample is\n * transparent report 0.\n * @param image - the fully decoded spritesheet (natural size 1536×1872).\n * @returns per-row frame counts, length 9.\n */\nexport function detectFrameCounts(image: HTMLImageElement): number[] {\n const canvas = document.createElement('canvas')\n canvas.width = image.naturalWidth\n canvas.height = image.naturalHeight\n const ctx = canvas.getContext('2d')\n if (ctx === null) return Array.from({ length: 9 }, () => FRAME_COLUMNS)\n ctx.drawImage(image, 0, 0)\n const data = ctx.getImageData(0, 0, canvas.width, canvas.height).data\n const counts: number[] = []\n const stride = FRAME_COLUMNS * FRAME_WIDTH\n const probeStep = 8\n const margin = 12\n for (let row = 0; row < 9; row++) {\n let count = 0\n for (let col = 0; col < FRAME_COLUMNS; col++) {\n let hasContent = false\n const x0 = col * FRAME_WIDTH\n const y0 = row * FRAME_HEIGHT\n for (let y = y0 + margin; y < y0 + FRAME_HEIGHT - margin && !hasContent; y += probeStep) {\n for (let x = x0 + margin; x < x0 + FRAME_WIDTH - margin && !hasContent; x += probeStep) {\n const idx = (y * stride + x) * 4\n if ((data[idx + 3] ?? 0) > 8) hasContent = true\n }\n }\n if (hasContent) count += 1\n }\n counts.push(count)\n }\n return counts\n}\n\n/**\n * Trim a track to the actual frame count of its row. A row with 0 detected\n * frames degrades to the first frame (the atlas is still loading or corrupt)\n * 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))\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","/**\n * Whale-girl companion component — the browser half's centerpiece. Renders a\n * fixed-position floating sprite (React portal onto document.body), plays\n * the spritesheet track matching the host animation snapshot, and exposes\n * the interaction surface: click to pet, hover panel with feed/hide, drag to\n * reposition (persisted via setConfig).\n * @module @linxin666/dsh-pet/client/WhalePet\n */\n\nimport { useEffect, useRef, useState } from 'react'\nimport type { PointerEvent as ReactPointerEvent, ReactPortal } from 'react'\nimport { createPortal } from 'react-dom'\nimport type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDisplayConfig } from '../persist.ts'\nimport type { PetStateView } from '../service.ts'\nimport type { PetFeedback } from './pet-store.ts'\nimport { framePosition, FRAME_WIDTH, FRAME_HEIGHT, FRAME_COLUMNS, TRACKS, rowOfTrack, trimTrack, detectFrameCounts } from './spritesheet.ts'\nimport type { PetAnimation } from '../state.ts'\nimport { NS } from './locales.ts'\nimport styles from './pet.module.css'\n\n/** Browser URL of the whale-girl atlas (served by the host half's own route). */\nexport const PET_SPRITESHEET_URL = '/pet/whale/spritesheet.webp'\n\n/** Browser URL of the whale-girl manifest (authoritative per-row frame counts). */\nexport const PET_MANIFEST_URL = '/pet/whale/pet.json'\n\n/** Props injected by the slot registration (store actions + locale). */\nexport interface WhalePetProps {\n /** Latest host snapshot; null while loading. */\n snapshot: PetStateView | null\n /** Display configuration (persisted by the host). */\n display: PetDisplayConfig\n /** Active reaction bubble, if any. */\n feedback: PetFeedback | null\n /** Pet the whale girl (click). */\n onPet: () => void\n /** Feed the whale girl (panel button). */\n onFeed: () => void\n /** Hide the whale girl (panel button). */\n onHide: () => void\n /** Persist a drag position. */\n onDragEnd: (right: number, bottom: number) => void\n /** Rename the pet (persisted by the host). */\n onRename: (name: 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 TRACKS; the atlas image is loaded once and\n * the background position is written straight to the sprite element (no\n * per-frame React state).\n */\nexport function WhalePet(props: WhalePetProps): ReactPortal {\n const { snapshot, display, feedback } = props\n const spriteRef = useRef<HTMLDivElement | null>(null)\n const floatRef = useRef<HTMLDivElement | null>(null)\n const [imageReady, setImageReady] = useState(false)\n const [frameCounts, setFrameCounts] = useState<number[] | null>(null)\n const [hovered, setHovered] = useState(false)\n const [renaming, setRenaming] = useState(false)\n const [nameDraft, setNameDraft] = useState('')\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 // Load the atlas once; then resolve per-row frame counts so tracks never\n // play the transparent trailing cells of a short row. One decoded Image\n // feeds both the sprite render and the frame-count detection. The counts\n // prefer the authoritatively recorded `frames` field on the pet.json\n // manifest route and only fall back to the getImageData atlas scan when\n // that field is absent (older manifests).\n useEffect(() => {\n let cancelled = false\n const img = new Image()\n img.onload = () => {\n if (cancelled) return\n setImageReady(true)\n fetch(PET_MANIFEST_URL)\n .then((res) => (res.ok ? res.json() : Promise.resolve<{ frames?: unknown }>({})))\n .then((manifest: { frames?: unknown }) => {\n if (cancelled) return\n const frames = manifest.frames\n if (Array.isArray(frames) && frames.length === 9 && frames.every((n) => typeof n === 'number')) {\n setFrameCounts(frames as number[])\n } else {\n setFrameCounts(detectFrameCounts(img))\n }\n })\n .catch(() => {\n if (!cancelled) setFrameCounts(detectFrameCounts(img))\n })\n }\n img.src = PET_SPRITESHEET_URL\n return () => {\n cancelled = true\n img.onload = null\n }\n }, [])\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 / FRAME_HEIGHT\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 // Paint one static sprite frame up front either way, so the pet is never\n // blank while the loop heat-up runs.\n const row = rowOfTrack(animation)\n const track = frameCounts === null\n ? TRACKS[animation]\n : trimTrack(TRACKS[animation], frameCounts[row] ?? TRACKS[animation].frames.length)\n const leadCol = track.frames[0]!\n const lead = framePosition(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 const tick = (ts: number): void => {\n const delta = ts - last\n last = ts\n // Trim the track to the row's real frame count (transparent cells\n // would render as a vanishing pet).\n const row = rowOfTrack(animation)\n const track = frameCounts === null\n ? TRACKS[animation]\n : trimTrack(TRACKS[animation], frameCounts[row] ?? TRACKS[animation].frames.length)\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 { x, y } = framePosition(row, col, scaleRef.current)\n if (spriteRef.current !== null) {\n spriteRef.current.style.backgroundPosition = `${x}px ${y}px`\n }\n raf = requestAnimationFrame(tick)\n }\n raf = requestAnimationFrame(tick)\n return () => cancelAnimationFrame(raf)\n }, [animation, frameCounts])\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 800ms 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 whale.\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 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(FRAME_WIDTH * spriteScale)\n const spriteHeight = Math.round(FRAME_HEIGHT * spriteScale)\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 and bubble render OUTSIDE the container's box (absolute,\n // above the sprite), so moving onto them fires pointerleave on the\n // container. Treat a target still inside the container's DOM (the\n // overflowed panel) as \"still hovering\"; otherwise give the pointer a\n // short grace period to reach the panel across the gap above it.\n const next = e.relatedTarget\n if (next instanceof Node && floatRef.current?.contains(next)) return\n clearHideTimer()\n hideTimerRef.current = window.setTimeout(() => setHovered(false), 200)\n }}\n >\n <div\n ref={spriteRef}\n className={styles.sprite}\n style={{\n width: spriteWidth,\n height: spriteHeight,\n backgroundImage: imageReady ? `url(${PET_SPRITESHEET_URL})` : undefined,\n backgroundSize: `${FRAME_WIDTH * FRAME_COLUMNS * spriteScale}px ${FRAME_HEIGHT * 9 * 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=\"whale girl\"\n />\n {feedback !== null && (\n <div key={feedback.at} className={`${styles.bubble} ${feedback.kind === 'feed' ? styles.bubbleFeed : styles.bubblePet}`}>\n {feedback.text}\n </div>\n )}\n {hovered && dragRef.current === null && (\n <div className={styles.panel}>\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 onKeyDown={(e) => {\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}>{snapshot?.name ?? '鲸鱼娘'}</span>\n <span>{props.t('pet.rank', { rank: snapshot?.affinity.rank ?? '?' })}</span>\n </div>\n <div className={styles.rankRow}>\n <span>{props.t('pet.treats', { n: snapshot?.treats.stocked ?? 0 })}</span>\n <span>{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 setNameDraft(snapshot?.name ?? '')\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 * Dock anchor inside the official SDK's `conversation.input.dock` seat\n * (the ambient readout band above the composer card, listed so several\n * additions can stack). While visible it mounts the floating WhalePet\n * (portal); while hidden it renders the summon button.\n * @module @linxin666/dsh-pet/client/PetDockEntry\n */\n\nimport { useEffect, useSyncExternalStore, type ReactElement } from 'react'\nimport type { PropsLocale, PropsRuntime } 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 { PetStoreInstance } from './pet-store.ts'\nimport { WhalePet } from './WhalePet.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 + feedback). */\n store: PetStoreInstance\n /** Ensure the first snapshot is fetched (called on mount). */\n ensure: () => void\n /** Pet the whale girl (click). */\n pet: () => void\n /** Feed the whale girl. */\n feed: () => void\n /** Hide the whale girl. */\n hide: () => void\n /** Summon the hidden whale girl back. */\n summon: () => void\n /** Persist a drag position. */\n dragEnd: (right: number, bottom: number) => void\n /** Rename the pet (persisted by the host). */\n rename: (name: string) => void\n /** Clear the reaction bubble. */\n feedbackDone: () => void\n}\n\n/** Composed props of the dock entry (runtime + locale + injected). */\nexport type PetDockEntryProps =\n PropsRuntime<'conversation.input.dock'>\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 WhalePet (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 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 <WhalePet\n snapshot={snapshot}\n display={snapshot?.display ?? DEFAULT_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 onFeedbackDone={props.feedbackDone}\n t={props.t}\n />\n </span>\n )\n }\n return (\n <button\n type=\"button\"\n className={styles.summon}\n onClick={props.summon}\n data-testid=\"pet-summon\"\n >\n {props.t('pet.summon', { name: snapshot?.name ?? '鲸鱼娘' })}\n </button>\n )\n}\n","/**\n * Shared chrome for the plugin settings card: a disclosure header naming the\n * plugin and what its settings govern, the controls inside, and the save that\n * 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. Mirrors the official ui-plugin-config PluginCard in a self-contained\n * slice (this package must not depend on a sibling UI package).\n */\n\nimport { useState, type ReactNode } from 'react'\nimport type { CardShell } from './settings-form.ts'\nimport type { SettingsCardKey } from './locales.ts'\nimport css from './settings-card.module.css'\n\n/** Card chrome shared by every plugin settings card. */\nexport interface PluginSettingsCardProps {\n /** Locale reader for this card's copy. */\n t: (key: SettingsCardKey) => string\n /** Locale key of the plugin's name. */\n titleKey: SettingsCardKey\n /** Locale key of the line describing what this plugin's settings govern. */\n descriptionKey: SettingsCardKey\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 /** 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 when the namespace is unavailable.\n */\nexport function PluginSettingsCard(props: PluginSettingsCardProps) {\n const [open, setOpen] = useState(false)\n const { state } = props\n if (!state.available) return null\n const title = props.t(props.titleKey)\n const blocked = !state.dirty || state.invalid || state.saving\n return (\n <li className={css.card}>\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}</span>\n <span className={css.description}>{props.t(props.descriptionKey)}</span>\n </span>\n {state.dirty ? <span className={css.pending}>{props.t('settings.unsaved')}</span> : null}\n <span className={open ? css.chevronOpen : css.chevron}>▾</span>\n </button>\n {open\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 <div className={css.footer}>\n {state.failed ? <p className={css.failed} role=\"status\">{props.t('settings.saveFailed')}</p> : 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 </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\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 <select\n id={props.id}\n className={css.select}\n value={props.text}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n >\n <option value=\"\">{props.inheritLabel}</option>\n <option value=\"true\">{props.onLabel}</option>\n <option value=\"false\">{props.offLabel}</option>\n </select>\n <p className={css.hint}>{props.hint}</p>\n </div>\n )\n}\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. Mirrors the official\n * ui-plugin-config card-store pattern in a self-contained slice: this\n * package must not depend on a sibling UI package.\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 /** 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 not served to this client; the card renders nothing. */\n available: 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\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 /** Perform the write and report whether the Host holds the staged value afterwards. */\n run: (() => Promise<boolean>) | undefined\n}\n\n/** A whole-number field. An empty draft clears the field; any other draft that is not a finite number blocks the save. */\nexport function numberField(field: string): FieldSpec {\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 return Number.isFinite(parsed) ? { kind: 'set', value: parsed } : undefined\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/** 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 if (text === 'true') return { kind: 'set', value: true }\n if (text === 'false') return { kind: 'set', value: false }\n return 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 private saving = false\n private failed = false\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 scope.subscribe(() => { this.publish() })\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 === '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 }\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.publish()\n },\n }\n }\n\n /**\n * Write every staged edit, then re-seed from what the Host accepted.\n * @returns settlement after every write and the read-back.\n */\n async save(): Promise<void> {\n const plan = this.plan()\n const writes = plan.flatMap(item => item.run === undefined ? [] : [item.run])\n if (plan.length === 0 || this.saving || writes.length !== plan.length) return\n this.saving = true\n this.failed = false\n this.publish()\n let landed = true\n for (const write of writes) {\n landed = await write() && landed\n }\n if (landed) this.staged.clear()\n this.saving = false\n this.failed = !landed\n this.publish()\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, 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, run: undefined })\n else if (write.kind === 'clear') plan.push({ field, run: () => this.clear(field) })\n else plan.push({ field, 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 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.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: display layout and name, bound to the `pet` settings\n * namespace the host plugin registers. Registered into the\n * `settings.plugin.item` slot the plugin-configuration section renders.\n */\n\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { SettingsScope, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'\nimport { PluginSettingsCard, ValueField, BooleanField } from './PluginSettingsCard.tsx'\nimport { CardForm, booleanField, numberField, textField, type CardActions, type CardShell, type FieldState as CardFieldState } from './settings-form.ts'\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 /** User-customizable pet display name. */\n name?: 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 /** Pet name. */\n name: CardFieldState\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/** 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\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 textField('name'),\n ])\n this.store = this.form.bind(() => this.projection())\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 name: this.form.field('name'),\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/** Props the renderer binds for the pet settings card. */\nexport type PetSettingsCardProps =\n PropsRuntime<'web-ui.plugin.item'>\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 >\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 <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 <ValueField\n id=\"settings-pet-name\"\n label={t('settings.name')}\n hint={t('settings.nameHint')}\n {...fieldProps}\n {...state.name}\n onEdit={(text) => { props.edit('name', text) }}\n onReset={() => { props.resetField('name') }}\n />\n </PluginSettingsCard>\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 // 插件设置卡片(settings.plugin.item 席位)。\n 'settings.title': '宠物',\n 'settings.description': '鲸鱼娘的显示布局与名字。',\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.name': '名字',\n 'settings.nameHint': '宠物显示名,1–20 个字符。',\n 'settings.inherit': '继承',\n 'settings.on': '开',\n 'settings.off': '关',\n 'settings.overridden': '已覆盖',\n 'settings.reset': '恢复默认',\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 whale girl is on her way…',\n 'pet.state.error': 'The whale girl is lost (connection failed)',\n // Plugin settings card (the `settings.plugin.item` seat).\n 'settings.title': 'Pet',\n 'settings.description': 'The whale girl\\u2019s display layout and name.',\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.name': 'Name',\n 'settings.nameHint': 'The pet\\u2019s display name, 1\\u201320 characters.',\n 'settings.inherit': 'Inherit',\n 'settings.on': 'On',\n 'settings.off': 'Off',\n 'settings.overridden': 'Overridden',\n 'settings.reset': 'Reset to default',\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\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 — registers the whale-girl into the conversation\n * input selector row (the same every-phase row the git branch chip uses) and\n * drives it from the host's same-origin `/api/pet/*` JSON endpoints: poll the\n * host snapshot (~800 ms), forward interactions, persist drag positions. The\n * row anchor mounts the floating pet via portal; when the pet is hidden the\n * anchor becomes the summon button. Anchoring in the selector row (rather\n * than the session-only composer dock band) keeps the pet floating on the\n * new-conversation screen too, where no session exists to scope a slot by.\n * @module @linxin666/dsh-pet/client\n */\n\nimport type { ClientContext } 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 { createPetStore, type PetStoreInstance } from './pet-store.ts'\nimport { PetDockEntry, type PetInjected } from './PetDockEntry.tsx'\nimport { PetSettingsCard, PetSettingsCardController, type PetSettings } from './PetSettingsCard.tsx'\nimport { NS, en, zh } 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 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}\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 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}\n\n/** Poll interval for the host snapshot. */\nconst POLL_MS = 800\n\n/** Settings namespace the pet settings card edits (the Host plugin registers it). */\nconst PET_SETTINGS_NS = 'pet'\n\n/** Required services. */\nexport const inject = ['slots', 'locale', 'connection', 'settingsScope', 'remote']\n\n/** Re-exported for consumers that type against the injected face. */\nexport type { PetInjected, PetDockEntryProps } from './PetDockEntry.tsx'\nexport type { PetUiState, PetFeedback } from './pet-store.ts'\nexport type { PetSettingsCardFace, PetSettingsCardState } from './PetSettingsCard.tsx'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface SlotMap {\n /**\n * The child slot the Web UI plugin group declares; this card registers\n * into the group instead of the top-level `settings.plugin.item` list.\n * Spelled here with the same shape so this package can register without\n * depending on the sibling UI package.\n */\n 'web-ui.plugin.item': { kind: 'list'; scope: 'root'; owner: SettingsPluginItemOwnerProps }\n }\n}\n\n/** Owner share of a plugin card (the group card supplies nothing). */\nexport interface SettingsPluginItemOwnerProps {\n /** Marker field: card owner props are intentionally empty. */\n children?: never\n}\n\n/**\n * Client plugin body: register dictionaries, mount the dock entry and poll\n * loop while the plugin is enabled, and seat the settings card in the Web UI\n * plugin group.\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 settingsScope = ctx.settingsScope.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 // Plugin configuration card: one staged form over the `pet` settings\n // namespace, contributed to the Web UI plugin group.\n const petSettings = new PetSettingsCardController(settingsScope)\n ctx.slots.inject('web-ui.plugin.item', () => ctx.slots.register({\n name: 'web-ui.plugin.item',\n id: 'pet-settings',\n order: 140,\n locale: NS,\n inject: () => petSettings.inject(),\n }, PetSettingsCard))\n\n // The dock entry, its store, and the poll loop live while the plugin is\n // 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 setState = petStore.actions.setState\n const setFeedback = petStore.actions.setFeedback\n\n const pollNow = (): void => {\n petApi.state().then((snapshot) => {\n setSnapshot(snapshot)\n }, () => {\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 800 ms 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 const injected = (): PetInjected => ({\n store: petStore,\n ensure: pollNow,\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 legacy selector-context hole (conversation.input.selector.context)\n // is no longer declared by newer shells (rc.6 dropped it), so the pet\n // mounts on the composer dock band instead. The dock is session-scoped,\n // so the pet appears once a session is active (accepted downgrade, see\n // PR #17).\n const disposeDock = ctx.slots.inject('conversation.input.dock', () =>\n ctx.slots.register({\n name: 'conversation.input.dock',\n id: 'pet',\n order: 110,\n inject: injected,\n locale: NS,\n }, PetDockEntry))\n\n disposeUi = () => {\n disposeDock()\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"],"mappings":";;;;;;;;;;;;;;;;;;;EA8CA,SAAgB,iBAA8D;GAC5E,QAAA,GAAA,uCAAA,YAAA,CAAmB;IACjB,aAAyB;KACvB,UAAU;KACV,OAAO;KACP,OAAO;KACP,UAAU;IACZ;IACA,SAAS;KACP,cAAc,OAAO,aAAa;MAChC,MAAM,WAAW;MACjB,MAAM,QAAQ;MACd,MAAM,QAAQ;KAChB;KACA,WAAW,OAAO,OAAO,UAAU;MACjC,MAAM,QAAQ;MACd,MAAM,QAAQ;KAChB;KACA,cAAc,OAAO,aAAa;MAChC,MAAM,WAAW;KACnB;IACF;GACF,CAAC;EACH;;;;;;;EC5BA,MAAa,SAAyC;GACpD,MAAM;IAAE,QAAQ;KAAC;KAAG;KAAG;KAAG;KAAG;KAAG;IAAC;IAAG,WAAW;KAAC;KAAK;KAAK;KAAK;KAAK;KAAK;IAAG;IAAG,MAAM;GAAK;GAC1F,iBAAiB;IAAE,QAAQ;KAAC;KAAG;KAAG;KAAG;KAAG;KAAG;KAAG;KAAG;IAAC;IAAG,WAAW;KAAC;KAAK;KAAK;KAAK;KAAK;KAAK;KAAK;KAAK;IAAG;IAAG,MAAM;GAAK;GACrH,gBAAgB;IAAE,QAAQ;KAAC;KAAG;KAAG;KAAG;KAAG;KAAG;KAAG;KAAG;IAAC;IAAG,WAAW;KAAC;KAAK;KAAK;KAAK;KAAK;KAAK;KAAK;KAAK;IAAG;IAAG,MAAM;GAAK;GACpH,QAAQ;IAAE,QAAQ;KAAC;KAAG;KAAG;KAAG;IAAC;IAAG,WAAW;KAAC;KAAK;KAAK;KAAK;IAAG;IAAG,MAAM;GAAK;GAC5E,SAAS;IAAE,QAAQ;KAAC;KAAG;KAAG;KAAG;KAAG;IAAC;IAAG,WAAW;KAAC;KAAK;KAAK;KAAK;KAAK;IAAG;IAAG,MAAM;IAAO,UAAU;GAAO;GACxG,QAAQ;IAAE,QAAQ;KAAC;KAAG;KAAG;KAAG;KAAG;KAAG;KAAG;KAAG;IAAC;IAAG,WAAW;KAAC;KAAK;KAAK;KAAK;KAAK;KAAK;KAAK;KAAK;IAAG;IAAG,MAAM;IAAO,UAAU;GAAO;GAC/H,SAAS;IAAE,QAAQ;KAAC;KAAG;KAAG;KAAG;KAAG;KAAG;IAAC;IAAG,WAAW;KAAC;KAAK;KAAK;KAAK;KAAK;KAAK;IAAG;IAAG,MAAM;GAAK;GAC7F,SAAS;IAAE,QAAQ;KAAC;KAAG;KAAG;KAAG;KAAG;KAAG;IAAC;IAAG,WAAW;KAAC;KAAK;KAAK;KAAK;KAAK;KAAK;IAAG;IAAG,MAAM;GAAK;GAC7F,QAAQ;IAAE,QAAQ;KAAC;KAAG;KAAG;KAAG;KAAG;KAAG;IAAC;IAAG,WAAW;KAAC;KAAK;KAAK;KAAK;KAAK;KAAK;IAAG;IAAG,MAAM;GAAK;EAC9F;;EAGA,SAAgB,WAAW,WAAiC;GAY1D,OAAO;IAVL,MAAM;IACN,iBAAiB;IACjB,gBAAgB;IAChB,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,SAAS;IACT,SAAS;IACT,QAAQ;GAEA,EAAE;EACd;;;;;;;;EASA,SAAgB,cAAc,KAAa,KAAa,QAAQ,GAA6B;GAC3F,OAAO;IAAE,GAAG,CAAC,MAAA,MAAoB;IAAO,GAAG,CAAC,MAAA,MAAqB;GAAM;EACzE;;;;;;;;;EAeA,SAAgB,kBAAkB,OAAmC;GACnE,MAAM,SAAS,SAAS,cAAc,QAAQ;GAC9C,OAAO,QAAQ,MAAM;GACrB,OAAO,SAAS,MAAM;GACtB,MAAM,MAAM,OAAO,WAAW,IAAI;GAClC,IAAI,QAAQ,MAAM,OAAO,MAAM,KAAK,EAAE,QAAQ,EAAE,SAAA,CAAsB;GACtE,IAAI,UAAU,OAAO,GAAG,CAAC;GACzB,MAAM,OAAO,IAAI,aAAa,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM,CAAC,CAAC;GACjE,MAAM,SAAmB,CAAC;GAC1B,MAAM,SAAA;GACN,MAAM,YAAY;GAClB,MAAM,SAAS;GACf,KAAK,IAAI,MAAM,GAAG,MAAM,GAAG,OAAO;IAChC,IAAI,QAAQ;IACZ,KAAK,IAAI,MAAM,GAAG,MAAA,GAAqB,OAAO;KAC5C,IAAI,aAAa;KACjB,MAAM,KAAK,MAAA;KACX,MAAM,KAAK,MAAA;KACX,KAAK,IAAI,IAAI,KAAK,QAAQ,IAAI,KAAA,MAAoB,UAAU,CAAC,YAAY,KAAK,WAC5E,KAAK,IAAI,IAAI,KAAK,QAAQ,IAAI,KAAA,MAAmB,UAAU,CAAC,YAAY,KAAK,WAE3E,KAAK,MADQ,IAAI,SAAS,KAAK,IACf,MAAM,KAAK,GAAG,aAAa;KAG/C,IAAI,YAAY,SAAS;IAC3B;IACA,OAAO,KAAK,KAAK;GACnB;GACA,OAAO;EACT;;;;;;EAOA,SAAgB,UAAU,OAAiB,YAA8B;GACvE,MAAM,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,YAAY,MAAM,OAAO,MAAM,CAAC;GAC/D,OAAO;IACL,QAAQ,MAAM,OAAO,MAAM,GAAG,CAAC;IAC/B,WAAW,MAAM,UAAU,MAAM,GAAG,CAAC;IACrC,MAAM,MAAM;IACZ,GAAI,MAAM,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU,MAAM,SAAS;GACrE;EACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECnHA,MAAa,sBAAsB;;EAGnC,MAAa,mBAAmB;;EA2BhC,SAAS,YAAY,OAAe,KAAqB;GACvD,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC;EACzC;;;;;;;EAQA,SAAgB,SAAS,OAAmC;GAC1D,MAAM,EAAE,UAAU,SAAS,aAAa;GACxC,MAAM,aAAA,GAAA,MAAA,OAAA,CAA0C,IAAI;GACpD,MAAM,YAAA,GAAA,MAAA,OAAA,CAAyC,IAAI;GACnD,MAAM,CAAC,YAAY,kBAAA,GAAA,MAAA,SAAA,CAA0B,KAAK;GAClD,MAAM,CAAC,aAAa,mBAAA,GAAA,MAAA,SAAA,CAA4C,IAAI;GACpE,MAAM,CAAC,SAAS,eAAA,GAAA,MAAA,SAAA,CAAuB,KAAK;GAC5C,MAAM,CAAC,UAAU,gBAAA,GAAA,MAAA,SAAA,CAAwB,KAAK;GAC9C,MAAM,CAAC,WAAW,iBAAA,GAAA,MAAA,SAAA,CAAyB,EAAE;GAC7C,MAAM,CAAC,SAAS,eAAA,GAAA,MAAA,SAAA,CAAiE,IAAI;GACrF,MAAM,WAAA,GAAA,MAAA,OAAA,CAA2F,IAAI;GACrG,MAAM,gBAAA,GAAA,MAAA,OAAA,CAAqC,IAAI;GAC/C,MAAM,YAAA,GAAA,MAAA,OAAA,CAAkF;IACtF,OAAO;IACP,OAAO;IACP,SAAS;GACX,CAAC;GAQD,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,YAAY;IAChB,MAAM,MAAM,IAAI,MAAM;IACtB,IAAI,eAAe;KACjB,IAAI,WAAW;KACf,cAAc,IAAI;KAClB,MAAM,gBAAgB,CAAC,CACpB,MAAM,QAAS,IAAI,KAAK,IAAI,KAAK,IAAI,QAAQ,QAA8B,CAAC,CAAC,CAAE,CAAC,CAChF,MAAM,aAAmC;MACxC,IAAI,WAAW;MACf,MAAM,SAAS,SAAS;MACxB,IAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,KAAK,OAAO,OAAO,MAAM,OAAO,MAAM,QAAQ,GAC3F,eAAe,MAAkB;WAEjC,eAAe,kBAAkB,GAAG,CAAC;KAEzC,CAAC,CAAC,CACD,YAAY;MACX,IAAI,CAAC,WAAW,eAAe,kBAAkB,GAAG,CAAC;KACvD,CAAC;IACL;IACA,IAAI,MAAM;IACV,aAAa;KACX,YAAY;KACZ,IAAI,SAAS;IACf;GACF,GAAG,CAAC,CAAC;GAQL,MAAM,cAAc,QAAQ,OAAA;GAC5B,MAAM,YAAY,UAAU,aAAa;GACzC,MAAM,YAAA,GAAA,MAAA,OAAA,CAAkB,WAAW;GACnC,SAAS,UAAU;GACnB,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,MAAM,eAAe,OAAO,WAAW,eAClC,OAAO,aAAa,kCAAkC,CAAC,EAAE,YAAY;IAG1E,MAAM,MAAM,WAAW,SAAS;IAIhC,MAAM,WAHQ,gBAAgB,OAC1B,OAAO,aACP,UAAU,OAAO,YAAY,YAAY,QAAQ,OAAO,UAAU,CAAC,OAAO,MAAM,EAAA,CAC9D,OAAO;IAC7B,MAAM,OAAO,cAAc,KAAK,SAAS,SAAS,OAAO;IACzD,IAAI,UAAU,YAAY,MACxB,UAAU,QAAQ,MAAM,qBAAqB,GAAG,KAAK,EAAE,KAAK,KAAK,EAAE;IAErE,IAAI,cAAc;IAClB,IAAI,MAAM;IACV,IAAI,OAAO,YAAY,IAAI;IAC3B,MAAM,QAAQ,OAAqB;KACjC,MAAM,QAAQ,KAAK;KACnB,OAAO;KAGP,MAAM,MAAM,WAAW,SAAS;KAChC,MAAM,QAAQ,gBAAgB,OAC1B,OAAO,aACP,UAAU,OAAO,YAAY,YAAY,QAAQ,OAAO,UAAU,CAAC,OAAO,MAAM;KACpF,MAAM,KAAK,SAAS;KACpB,IAAI,GAAG,UAAU,WAAW;MAC1B,GAAG,QAAQ;MACX,GAAG,QAAQ;MACX,GAAG,UAAU;KACf;KACA,GAAG,WAAW;KACd,MAAM,WAAW,MAAM,OAAO,SAAS;KACvC,OAAO,GAAG,YAAY,MAAM,UAAU,GAAG,UAAU,MAAM,GAAG,QAAQ,UAAU;MAC5E,GAAG,WAAW,MAAM,UAAU,GAAG,UAAU;MAC3C,GAAG,SAAS;KACd;KACA,IAAI,GAAG,YAAY,MAAM,UAAU,GAAG,UAAU,IAC9C,IAAI,MAAM,MAAM;MACd,GAAG,UAAU;MACb,GAAG,QAAQ;KACb,OACE,GAAG,QAAQ;KAGf,MAAM,MAAM,MAAM,OAAO,GAAG;KAC5B,MAAM,EAAE,GAAG,MAAM,cAAc,KAAK,KAAK,SAAS,OAAO;KACzD,IAAI,UAAU,YAAY,MACxB,UAAU,QAAQ,MAAM,qBAAqB,GAAG,EAAE,KAAK,EAAE;KAE3D,MAAM,sBAAsB,IAAI;IAClC;IACA,MAAM,sBAAsB,IAAI;IAChC,aAAa,qBAAqB,GAAG;GACvC,GAAG,CAAC,WAAW,WAAW,CAAC;GAK3B,MAAM,mBAAA,GAAA,MAAA,OAAA,CAAyB,MAAM,cAAc;GACnD,gBAAgB,UAAU,MAAM;GAChC,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,aAAa,MAAM;IACvB,MAAM,QAAQ,OAAO,iBAAiB,gBAAgB,QAAQ,GAAG,IAAI;IACrE,aAAa,OAAO,aAAa,KAAK;GACxC,GAAG,CAAC,QAAQ,CAAC;GAKb,MAAM,cAAA,GAAA,MAAA,OAAA,CAAoB,KAAK;GAC/B,MAAM,uBAA6B;IACjC,IAAI,aAAa,YAAY,MAAM;KACjC,OAAO,aAAa,aAAa,OAAO;KACxC,aAAa,UAAU;IACzB;GACF;GAEA,MAAM,iBAAiB,MAA+C;IACpE,EAAE,eAAe;IAChB,EAAG,OAAuB,oBAAoB,EAAE,SAAS;IAC1D,MAAM,UAAU,WAAW;KAAE,OAAO,QAAQ;KAAO,QAAQ,QAAQ;IAAO;IAC1E,QAAQ,UAAU;KAAE,QAAQ,EAAE;KAAS,QAAQ,EAAE;KAAS,GAAG;IAAQ;IACrE,WAAW,UAAU;IACrB,WAAW,KAAK;GAClB;GACA,MAAM,iBAAiB,MAA+C;IACpE,MAAM,OAAO,QAAQ;IACrB,IAAI,SAAS,MAAM;IACnB,MAAM,KAAK,EAAE,UAAU,KAAK;IAC5B,MAAM,KAAK,EAAE,UAAU,KAAK;IAC5B,IAAI,KAAK,IAAI,EAAE,IAAI,KAAK,KAAK,IAAI,EAAE,IAAI,GAAG,WAAW,UAAU;IAC/D,MAAM,QAAQ,YAAY,KAAK,QAAQ,IAAI,OAAO,aAAa,EAAE;IACjE,MAAM,SAAS,YAAY,KAAK,SAAS,IAAI,OAAO,cAAc,EAAE;IACpE,WAAW;KAAE;KAAO;IAAO,CAAC;GAC9B;GACA,MAAM,oBAA0B;IAC9B,IAAI,QAAQ,YAAY,MAAM;IAC9B,QAAQ,UAAU;IAClB,IAAI,YAAY,MAAM,MAAM,UAAU,QAAQ,OAAO,QAAQ,MAAM;GACrE;GAEA,MAAM,MAAM,WAAW;IAAE,OAAO,QAAQ;IAAO,QAAQ,QAAQ;GAAO;GACtE,MAAM,cAAc,KAAK,MAAA,MAAoB,WAAW;GACxD,MAAM,eAAe,KAAK,MAAA,MAAqB,WAAW;GA4H1D,QAAA,GAAA,UAAA,aAAA,CAAoB,iBAAA,GAAA,kBAAA,KAAA,CAzHjB,OAAD;IACE,KAAK;IACL,WAAWA,uBAAO;IAClB,OAAO;KAAE,OAAO,IAAI;KAAO,QAAQ,IAAI;KAAQ,QAAQ;IAAW;IAClE,sBAAsB;KACpB,eAAe;KACf,WAAW,IAAI;IACjB;IACA,iBAAiB,MAAM;KAMrB,MAAM,OAAO,EAAE;KACf,IAAI,gBAAgB,QAAQ,SAAS,SAAS,SAAS,IAAI,GAAG;KAC9D,eAAe;KACf,aAAa,UAAU,OAAO,iBAAiB,WAAW,KAAK,GAAG,GAAG;IACvE;cAlBF;KAoBE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MACE,KAAK;MACL,WAAWA,uBAAO;MAClB,OAAO;OACL,OAAO;OACP,QAAQ;OACR,iBAAiB,aAAa,OAAO,oBAAoB,KAAK,KAAA;OAC9D,gBAAgB,GAAA,OAAiC,YAAY,KAAA,OAAwB,YAAY;OACjG,kBAAkB;OAClB,oBAAoB;OACpB,QAAQ,QAAQ,YAAY,OAAO,SAAS;MAC9C;MACe;MACA;MACF;MACb,eAAe;OAGb,IAAI,WAAW,SAAS;OACxB,MAAM,MAAM;MACd;MACA,MAAK;MACL,cAAW;KACZ,CAAA;KACA,aAAa,QACZ,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAuB,WAAW,GAAGA,uBAAO,OAAO,GAAG,SAAS,SAAS,SAASA,uBAAO,aAAaA,uBAAO;gBACzG,SAAS;KACP,GAFK,SAAS,EAEd;KAEN,WAAW,QAAQ,YAAY,QAC9B,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAWA,uBAAO;gBACpB,WACC,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAWA,uBAAO;iBAAvB,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;QACE,WAAWA,uBAAO;QAClB,OAAO;QACP,WAAW;QACX,aAAa,MAAM,EAAE,qBAAqB;QAC1C,WAAA;QACA,WAAW,MAAM,aAAa,EAAE,OAAO,KAAK;QAC5C,YAAY,MAAM;SAChB,IAAI,EAAE,QAAQ,SAAS;UACrB,MAAM,UAAU,UAAU,KAAK;UAC/B,IAAI,YAAY,IAAI;WAClB,MAAM,SAAS,OAAO;WACtB,YAAY,KAAK;UACnB;SACF,OAAO,IAAI,EAAE,QAAQ,UACnB,YAAY,KAAK;QAErB;OACD,CAAA,GACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QACE,MAAK;QACL,WAAWA,uBAAO;QAClB,eAAe;SACb,MAAM,UAAU,UAAU,KAAK;SAC/B,IAAI,YAAY,IAAI;UAClB,MAAM,SAAS,OAAO;UACtB,YAAY,KAAK;SACnB;QACF;kBAEC,MAAM,EAAE,aAAa;OAChB,CAAA,CACL;WAEL,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA;OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAWA,uBAAO;kBAAvB,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAWA,uBAAO;mBAAW,UAAU,QAAQ;QAAY,CAAA,GACjE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAO,MAAM,EAAE,YAAY,EAAE,MAAM,UAAU,SAAS,QAAQ,IAAI,CAAC,EAAQ,CAAA,CACxE;;OACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAWA,uBAAO;kBAAvB,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAO,MAAM,EAAE,cAAc,EAAE,GAAG,UAAU,OAAO,WAAW,EAAE,CAAC,EAAQ,CAAA,GACzE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAO,MAAM,EAAE,cAAc,EAAE,QAAQ,UAAU,SAAS,UAAU,EAAE,CAAC,EAAQ,CAAA,CAC5E;;OACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAWA,uBAAO;kBAAvB;SACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UAAQ,MAAK;UAAS,WAAWA,uBAAO;UAAQ,SAAS,MAAM;oBAC5D,MAAM,EAAE,UAAU;SACb,CAAA;SACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UACE,MAAK;UACL,WAAWA,uBAAO;UAClB,eAAe;WACb,aAAa,UAAU,QAAQ,EAAE;WACjC,YAAY,IAAI;UAClB;oBAEC,MAAM,EAAE,YAAY;SACf,CAAA;SACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UAAQ,MAAK;UAAS,WAAWA,uBAAO;UAAQ,SAAS,MAAM;oBAC5D,MAAM,EAAE,UAAU;SACb,CAAA;QACL;;MACL,EAAA,CAAA;KAED,CAAA;IAEJ;IAGiB,GAAG,SAAS,IAAI;EAC1C;;;;;;;;;;ECrTA,MAAM,kBAAoC;GAAE,SAAS;GAAM,MAAM;GAAK,OAAO;GAAI,QAAQ;EAAG;;;;;;;;EAS5F,SAAgB,aAAa,OAAwC;GACnE,MAAM,EAAE,OAAO,WAAW;GAC1B,MAAM,MAAA,GAAA,MAAA,qBAAA,CAA0B,MAAM,WAAW,MAAM,WAAW;GAClE,MAAM,WAAW,GAAG;GACpB,MAAM,WAAW,GAAG;GACpB,MAAM,UAAU,UAAU,QAAQ,WAAW;GAE7C,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,OAAO;GACT,GAAG,CAAC,MAAM,CAAC;GAEX,IAAI,SACF,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;IAAM,iBAAA;IAAc,eAAY;cAC9B,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;KACY;KACV,SAAS,UAAU,WAAW;KACpB;KACV,OAAO,MAAM;KACb,QAAQ,MAAM;KACd,QAAQ,MAAM;KACd,WAAW,MAAM;KACjB,UAAU,MAAM;KAChB,gBAAgB,MAAM;KACtB,GAAG,MAAM;IACV,CAAA;GACG,CAAA;GAGV,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;IACE,MAAK;IACL,WAAWC,uBAAO;IAClB,SAAS,MAAM;IACf,eAAY;cAEX,MAAM,EAAE,cAAc,EAAE,MAAM,UAAU,QAAQ,MAAM,CAAC;GAClD,CAAA;EAEZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECxDA,SAAgB,mBAAmB,OAAgC;GACjE,MAAM,CAAC,MAAM,YAAA,GAAA,MAAA,SAAA,CAAoB,KAAK;GACtC,MAAM,EAAE,UAAU;GAClB,IAAI,CAAC,MAAM,WAAW,OAAO;GAC7B,MAAM,QAAQ,MAAM,EAAE,MAAM,QAAQ;GACpC,MAAM,UAAU,CAAC,MAAM,SAAS,MAAM,WAAW,MAAM;GACvD,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,MAAD;IAAI,WAAWC,iCAAI;cAAnB,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;KACE,MAAK;KACL,WAAWA,iCAAI;KACf,iBAAe;KACf,cAAY,GAAG,MAAM,EAAE,OAAO,sBAAsB,iBAAiB,EAAE,IAAI;KAC3E,eAAe;MAAE,QAAQ,CAAC,IAAI;KAAE;eALlC;MAOE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;OAAM,WAAWA,iCAAI;iBAArB,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAWA,iCAAI;kBAAO;OAAY,CAAA,GACxC,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAWA,iCAAI;kBAAc,MAAM,EAAE,MAAM,cAAc;OAAQ,CAAA,CACnE;;MACL,MAAM,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAAM,WAAWA,iCAAI;iBAAU,MAAM,EAAE,kBAAkB;MAAQ,CAAA,IAAI;MACpF,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAAM,WAAW,OAAOA,iCAAI,cAAcA,iCAAI;iBAAS;MAAO,CAAA;KACxD;QACP,OAEG,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAWA,iCAAI;eAApB;MACG,CAAC,MAAM,WAAW,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;OAAG,WAAWA,iCAAI;OAAU,MAAK;iBAAU,MAAM,EAAE,mBAAmB;MAAK,CAAA,IAAI;MACjG,MAAM;MACP,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAWA,iCAAI;iBAApB;QACG,MAAM,SAAS,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;SAAG,WAAWA,iCAAI;SAAQ,MAAK;mBAAU,MAAM,EAAE,qBAAqB;QAAK,CAAA,IAAI;QAC/F,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SACE,MAAK;SACL,WAAWA,iCAAI;SACf,UAAU,CAAC,MAAM,SAAS,MAAM;SAChC,SAAS,MAAM;mBAEd,MAAM,EAAE,kBAAkB;QACrB,CAAA;QACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SACE,MAAK;SACL,WAAWA,iCAAI;SACf,UAAU;SACV,SAAS,MAAM;mBAEd,MAAM,EAAE,CAAC,MAAM,SAAS,kBAAkB,iBAAiB;QACtD,CAAA;OACL;;KACF;SAEL,IACF;;EAER;;EA+BA,SAAgB,WAAW,OAKxB;GACD,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAWA,iCAAI;cAApB;KACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAWA,iCAAI;gBAApB,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;OAAO,WAAWA,iCAAI;OAAO,SAAS,MAAM;iBAAK,MAAM;MAAa,CAAA,GACnE,MAAM,aAEH,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;OAAM,WAAWA,iCAAI;iBAArB,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAWA,iCAAI;kBAAQ,MAAM;OAAsB,CAAA,GACzD,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QACE,MAAK;QACL,WAAWA,iCAAI;QACf,UAAU,MAAM;QAChB,SAAS,MAAM;kBAEd,MAAM;OACD,CAAA,CACJ;WAEN,IACD;;KACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;MACE,IAAI,MAAM;MACV,WAAW,MAAM,UAAUA,iCAAI,eAAeA,iCAAI;MAClD,MAAK;MACL,GAAI,MAAM,YAAY,OAAO,EAAE,WAAW,UAAmB,IAAI,CAAC;MAClE,GAAI,MAAM,UAAU,EAAE,gBAAgB,KAAK,IAAI,CAAC;MAChD,OAAO,MAAM;MACb,aAAa,MAAM,eAAe;MAClC,UAAU,MAAM;MAChB,WAAW,UAAU;OAAE,MAAM,OAAO,MAAM,OAAO,KAAK;MAAE;KACzD,CAAA;KACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;MAAG,WAAW,MAAM,UAAUA,iCAAI,UAAUA,iCAAI;gBAC7C,MAAM,UAAU,MAAM,eAAe,MAAM;KAC3C,CAAA;IACA;;EAET;;EAGA,SAAgB,aAAa,OAO1B;GACD,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAWA,iCAAI;cAApB;KACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAWA,iCAAI;gBAApB,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;OAAO,WAAWA,iCAAI;OAAO,SAAS,MAAM;iBAAK,MAAM;MAAa,CAAA,GACnE,MAAM,aAEH,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;OAAM,WAAWA,iCAAI;iBAArB,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAWA,iCAAI;kBAAQ,MAAM;OAAsB,CAAA,GACzD,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QACE,MAAK;QACL,WAAWA,iCAAI;QACf,UAAU,MAAM;QAChB,SAAS,MAAM;kBAEd,MAAM;OACD,CAAA,CACJ;WAEN,IACD;;KACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;MACE,IAAI,MAAM;MACV,WAAWA,iCAAI;MACf,OAAO,MAAM;MACb,UAAU,MAAM;MAChB,WAAW,UAAU;OAAE,MAAM,OAAO,MAAM,OAAO,KAAK;MAAE;gBAL1D;OAOE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAQ,OAAM;kBAAI,MAAM;OAAqB,CAAA;OAC7C,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAQ,OAAM;kBAAQ,MAAM;OAAgB,CAAA;OAC5C,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAQ,OAAM;kBAAS,MAAM;OAAiB,CAAA;MACxC;;KACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;MAAG,WAAWA,iCAAI;gBAAO,MAAM;KAAQ,CAAA;IACpC;;EAET;;;;ECzHA,SAAgB,YAAY,OAA0B;GACpD,OAAO;IACL;IACA,SAAQ,UAAS,OAAO,UAAU,WAAW,OAAO,KAAK,IAAI;IAC7D,QAAQ,SAAS;KACf,MAAM,UAAU,KAAK,KAAK;KAC1B,IAAI,YAAY,IAAI,OAAO,EAAE,MAAM,QAAQ;KAC3C,MAAM,SAAS,OAAO,OAAO;KAC7B,OAAO,OAAO,SAAS,MAAM,IAAI;MAAE,MAAM;MAAO,OAAO;KAAO,IAAI,KAAA;IACpE;GACF;EACF;;EAGA,SAAgB,UAAU,OAA0B;GAClD,OAAO;IACL;IACA,SAAQ,UAAS,OAAO,UAAU,WAAW,QAAQ;IACrD,QAAQ,SAAS;KACf,MAAM,UAAU,KAAK,KAAK;KAC1B,OAAO,YAAY,KAAK,EAAE,MAAM,QAAQ,IAAI;MAAE,MAAM;MAAO,OAAO;KAAQ;IAC5E;GACF;EACF;;EAGA,SAAgB,aAAa,OAA0B;GACrD,OAAO;IACL;IACA,SAAQ,UAAS,OAAO,UAAU,YAAY,OAAO,KAAK,IAAI;IAC9D,QAAQ,SAAS;KACf,IAAI,SAAS,QAAQ,OAAO;MAAE,MAAM;MAAO,OAAO;KAAK;KACvD,IAAI,SAAS,SAAS,OAAO;MAAE,MAAM;MAAO,OAAO;KAAM;IAE3D;GACF;EACF;;;;;;;;;EAUA,IAAa,WAAb,MAAyB;GASJ;GARnB;GACA,yBAA0B,IAAI,IAAwB;GACtD,4BAA6B,IAAI,IAAgB;GACjD,SAAiB;GACjB,SAAiB;;GAGjB,YACE,OACA,OACA;IAFiB,KAAA,QAAA;IAGjB,KAAK,QAAQ,IAAI,IAAI,MAAM,KAAI,SAAQ,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC;IAC1D,MAAM,gBAAgB;KAAE,KAAK,QAAQ;IAAE,CAAC;GAC1C;;GAGA,KAAQ,SAAoC;IAC1C,MAAM,SAAA,GAAA,uCAAA,oBAAA,CAA4B,QAAQ,CAAC;IAC3C,KAAK,UAAU,UAAU;KAAE,MAAM,IAAI,QAAQ,CAAC;IAAE,CAAC;IACjD,OAAO;GACT;;GAGA,QAAmB;IACjB,MAAM,WAAW,KAAK,MAAM,YAAY;IACxC,MAAM,OAAO,KAAK,KAAK;IACvB,OAAO;KACL,WAAW,SAAS,WAAW;KAC/B,UAAU,SAAS;KACnB,OAAO,KAAK,SAAS;KACrB,SAAS,KAAK,MAAK,SAAQ,KAAK,QAAQ,KAAA,CAAS;KACjD,QAAQ,KAAK;KACb,QAAQ,KAAK;IACf;GACF;;GAGA,MAAM,OAA2B;IAC/B,MAAM,OAAO,KAAK,OAAO,KAAK;IAC9B,MAAM,SAAS,KAAK,OAAO,IAAI,KAAK;IACpC,IAAI,WAAW,KAAA,GACb,OAAO;KAAE,MAAM,KAAK,OAAO,KAAK,aAAa,KAAK,CAAC;KAAG,YAAY,KAAK,OAAO,KAAK;KAAG,SAAS;IAAM;IAEvG,MAAM,QAAQ,OAAO,QAAQ,EAAE,MAAM,QAAiB,IAAI,KAAK,MAAM,OAAO,IAAI;IAChF,OAAO;KACL,MAAM,OAAO;KACb,YAAY,OAAO,SAAS;KAC5B,SAAS,UAAU,KAAA;IACrB;GACF;;GAGA,UAAuB;IACrB,OAAO;KACL,OAAO,OAAO,SAAS;MAAE,KAAK,MAAM,OAAO;OAAE;OAAM,OAAO;MAAM,CAAC;KAAE;KACnE,aAAa,UAAU;MACrB,KAAK,MAAM,OAAO;OAAE,MAAM,KAAK,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK,UAAU,KAAK,CAAC;OAAG,OAAO;MAAK,CAAC;KAC3F;KACA,YAAY;MAAE,KAAU,KAAK;KAAE;KAC/B,eAAe;MACb,IAAI,KAAK,OAAO,SAAS,KAAK,CAAC,KAAK,QAAQ;MAC5C,KAAK,OAAO,MAAM;MAClB,KAAK,SAAS;MACd,KAAK,QAAQ;KACf;IACF;GACF;;;;;GAMA,MAAM,OAAsB;IAC1B,MAAM,OAAO,KAAK,KAAK;IACvB,MAAM,SAAS,KAAK,SAAQ,SAAQ,KAAK,QAAQ,KAAA,IAAY,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC;IAC5E,IAAI,KAAK,WAAW,KAAK,KAAK,UAAU,OAAO,WAAW,KAAK,QAAQ;IACvE,KAAK,SAAS;IACd,KAAK,SAAS;IACd,KAAK,QAAQ;IACb,IAAI,SAAS;IACb,KAAK,MAAM,SAAS,QAClB,SAAS,MAAM,MAAM,KAAK;IAE5B,IAAI,QAAQ,KAAK,OAAO,MAAM;IAC9B,KAAK,SAAS;IACd,KAAK,SAAS,CAAC;IACf,KAAK,QAAQ;GACf;;;;;;;;GASA,OAA+B;IAC7B,MAAM,OAAuB,CAAC;IAC9B,KAAK,MAAM,CAAC,OAAO,WAAW,KAAK,QAAQ;KACzC,MAAM,OAAO,KAAK,OAAO,KAAK;KAC9B,IAAI,OAAO,OAAO;MAChB,IAAI,KAAK,OAAO,KAAK,GAAG,KAAK,KAAK;OAAE;OAAO,WAAW,KAAK,MAAM,KAAK;MAAE,CAAC;MACzE;KACF;KACA,IAAI,OAAO,SAAS,KAAK,OAAO,KAAK,aAAa,KAAK,CAAC,GAAG;KAC3D,MAAM,QAAQ,KAAK,MAAM,OAAO,IAAI;KACpC,IAAI,UAAU,KAAA,GAAW,KAAK,KAAK;MAAE;MAAO,KAAK,KAAA;KAAU,CAAC;UACvD,IAAI,MAAM,SAAS,SAAS,KAAK,KAAK;MAAE;MAAO,WAAW,KAAK,MAAM,KAAK;KAAE,CAAC;UAC7E,KAAK,KAAK;MAAE;MAAO,WAAW,KAAK,MAAM,OAAO,MAAM,KAAK;KAAE,CAAC;IACrE;IACA,OAAO;GACT;GAEA,MAAc,MAAM,OAAiC;IACnD,MAAM,KAAK,MAAM,MAAM,KAAK;IAC5B,OAAO,CAAC,KAAK,OAAO,KAAK;GAC3B;GAEA,MAAc,MAAM,OAAe,OAAkC;IACnE,MAAM,KAAK,MAAM,IAAI,OAAO,KAAK;IACjC,OAAO,KAAK,UAAU,CAAC,GAAG,WAAW;GACvC;GAEA,MAAc,OAAe,MAAwB;IACnD,KAAK,OAAO,IAAI,OAAO,IAAI;IAC3B,KAAK,SAAS;IACd,KAAK,QAAQ;GACf;GAEA,OAAe,OAA0B;IACvC,MAAM,OAAO,KAAK,MAAM,IAAI,KAAK;IAGjC,IAAI,SAAS,KAAA,GAAW,MAAM,IAAI,MAAM,8BAA8B,OAAO;IAC7E,OAAO;GACT;GAEA,aAA+C;IAC7C,OAAO,KAAK,MAAM,YAAY;GAChC;GAEA,aAAqB,OAAwB;IAC3C,OAAQ,KAAK,WAAW,CAAC,CAAC,QAAgD;GAC5E;GAEA,UAAkB,OAAwB;IACxC,OAAQ,KAAK,WAAW,CAAC,CAAC,OAA+C;GAC3E;GAEA,YAAyD;IACvD,OAAO,KAAK,WAAW,CAAC,CAAC;GAC3B;GAEA,OAAe,OAAwB;IACrC,MAAM,OAAO,KAAK,UAAU;IAC5B,OAAO,SAAS,KAAA,KAAa,OAAO,OAAO,MAAM,KAAK;GACxD;GAEA,UAAwB;IACtB,KAAK,MAAM,YAAY,KAAK,WAAW,SAAS;GAClD;EACF;;;;ECjPA,IAAa,4BAAb,MAAuC;GACrC;GACA;;GAGA,YAAY,OAAmC;IAC7C,KAAK,OAAO,IAAI,SAAS,OAAO;KAC9B,aAAa,SAAS;KACtB,aAAa,SAAS;KACtB,YAAY,MAAM;KAClB,YAAY,OAAO;KACnB,YAAY,QAAQ;KACpB,UAAU,MAAM;IAClB,CAAC;IACD,KAAK,QAAQ,KAAK,KAAK,WAAW,KAAK,WAAW,CAAC;GACrD;GAEA,aAA2C;IACzC,OAAO;KACL,GAAG,KAAK,KAAK,MAAM;KACnB,SAAS,KAAK,KAAK,MAAM,SAAS;KAClC,SAAS,KAAK,KAAK,MAAM,SAAS;KAClC,MAAM,KAAK,KAAK,MAAM,MAAM;KAC5B,OAAO,KAAK,KAAK,MAAM,OAAO;KAC9B,QAAQ,KAAK,KAAK,MAAM,QAAQ;KAChC,MAAM,KAAK,KAAK,MAAM,MAAM;IAC9B;GACF;;;;;GAMA,SAA8B;IAC5B,OAAO;KAAE,OAAO,EAAE,iBAAiB,KAAK,MAAM;KAAG,GAAG,KAAK,KAAK,QAAQ;IAAE;GAC1E;EACF;;;;;;EAaA,SAAgB,gBAAgB,OAA6B;GAC3D,MAAM,EAAE,MAAM;GACd,MAAM,QAAQ,MAAM,oBAAmB,aAAY,QAAQ;GAC3D,MAAM,WAAW,CAAC,MAAM;GACxB,MAAM,aAAa;IACjB,iBAAiB,EAAE,qBAAqB;IACxC,YAAY,EAAE,gBAAgB;IAC9B,cAAc,EAAE,wBAAwB;IACxC;GACF;GACA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,oBAAD;IACK;IACH,UAAS;IACT,gBAAe;IACR;IACP,QAAQ,MAAM;IACd,WAAW,MAAM;cANnB;KAQE,iBAAA,GAAA,kBAAA,IAAA,CAAC,cAAD;MACE,IAAG;MACH,OAAO,EAAE,kBAAkB;MAC3B,MAAM,EAAE,sBAAsB;MAC9B,cAAc,EAAE,kBAAkB;MAClC,SAAS,EAAE,aAAa;MACxB,UAAU,EAAE,cAAc;MAC1B,GAAI;MACJ,GAAI,MAAM;MACV,SAAS,SAAS;OAAE,MAAM,KAAK,WAAW,IAAI;MAAE;MAChD,eAAe;OAAE,MAAM,WAAW,SAAS;MAAE;KAC9C,CAAA;KACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,cAAD;MACE,IAAG;MACH,OAAO,EAAE,kBAAkB;MAC3B,MAAM,EAAE,sBAAsB;MAC9B,cAAc,EAAE,kBAAkB;MAClC,SAAS,EAAE,aAAa;MACxB,UAAU,EAAE,cAAc;MAC1B,GAAI;MACJ,GAAI,MAAM;MACV,SAAS,SAAS;OAAE,MAAM,KAAK,WAAW,IAAI;MAAE;MAChD,eAAe;OAAE,MAAM,WAAW,SAAS;MAAE;KAC9C,CAAA;KACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,YAAD;MACE,IAAG;MACH,OAAO,EAAE,eAAe;MACxB,MAAM,EAAE,mBAAmB;MAC3B,SAAA;MACA,GAAI;MACJ,GAAI,MAAM;MACV,SAAS,SAAS;OAAE,MAAM,KAAK,QAAQ,IAAI;MAAE;MAC7C,eAAe;OAAE,MAAM,WAAW,MAAM;MAAE;KAC3C,CAAA;KACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,YAAD;MACE,IAAG;MACH,OAAO,EAAE,gBAAgB;MACzB,MAAM,EAAE,oBAAoB;MAC5B,SAAA;MACA,GAAI;MACJ,GAAI,MAAM;MACV,SAAS,SAAS;OAAE,MAAM,KAAK,SAAS,IAAI;MAAE;MAC9C,eAAe;OAAE,MAAM,WAAW,OAAO;MAAE;KAC5C,CAAA;KACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,YAAD;MACE,IAAG;MACH,OAAO,EAAE,iBAAiB;MAC1B,MAAM,EAAE,qBAAqB;MAC7B,SAAA;MACA,GAAI;MACJ,GAAI,MAAM;MACV,SAAS,SAAS;OAAE,MAAM,KAAK,UAAU,IAAI;MAAE;MAC/C,eAAe;OAAE,MAAM,WAAW,QAAQ;MAAE;KAC7C,CAAA;KACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,YAAD;MACE,IAAG;MACH,OAAO,EAAE,eAAe;MACxB,MAAM,EAAE,mBAAmB;MAC3B,GAAI;MACJ,GAAI,MAAM;MACV,SAAS,SAAS;OAAE,MAAM,KAAK,QAAQ,IAAI;MAAE;MAC7C,eAAe;OAAE,MAAM,WAAW,MAAM;MAAE;KAC3C,CAAA;IACiB;;EAExB;;;;EChLA,MAAa,KAAK;GAChB,YAAY;GACZ,YAAY;GACZ,cAAc;GACd,eAAe;GACf,uBAAuB;GACvB,cAAc;GACd,YAAY;GACZ,cAAc;GACd,cAAc;GACd,qBAAqB;GACrB,mBAAmB;GAEnB,kBAAkB;GAClB,wBAAwB;GACxB,oBAAoB;GACpB,wBAAwB;GACxB,oBAAoB;GACpB,wBAAwB;GACxB,iBAAiB;GACjB,qBAAqB;GACrB,kBAAkB;GAClB,sBAAsB;GACtB,mBAAmB;GACnB,uBAAuB;GACvB,iBAAiB;GACjB,qBAAqB;GACrB,oBAAoB;GACpB,eAAe;GACf,gBAAgB;GAChB,uBAAuB;GACvB,kBAAkB;GAClB,qBAAqB;GACrB,mBAAmB;GACnB,qBAAqB;GACrB,iBAAiB;GACjB,mBAAmB;GACnB,oBAAoB;GACpB,oBAAoB;GACpB,uBAAuB;GACvB,0BAA0B;EAC5B;;EAGA,MAAa,KAAK;GAChB,YAAY;GACZ,YAAY;GACZ,cAAc;GACd,eAAe;GACf,uBAAuB;GACvB,cAAc;GACd,YAAY;GACZ,cAAc;GACd,cAAc;GACd,qBAAqB;GACrB,mBAAmB;GAEnB,kBAAkB;GAClB,wBAAwB;GACxB,oBAAoB;GACpB,wBAAwB;GACxB,oBAAoB;GACpB,wBAAwB;GACxB,iBAAiB;GACjB,qBAAqB;GACrB,kBAAkB;GAClB,sBAAsB;GACtB,mBAAmB;GACnB,uBAAuB;GACvB,iBAAiB;GACjB,qBAAqB;GACrB,oBAAoB;GACpB,eAAe;GACf,gBAAgB;GAChB,uBAAuB;GACvB,kBAAkB;GAClB,qBAAqB;GACrB,mBAAmB;GACnB,qBAAqB;GACrB,iBAAiB;GACjB,mBAAmB;GACnB,oBAAoB;GACpB,oBAAoB;GACpB,uBAAuB;GACvB,0BAA0B;EAC5B;;;;ECzDA,eAAe,SAAY,MAAc,MAA4B;GACnE,MAAM,WAAW,MAAM,MAAM,MAAM,SAAS,KAAA,IACxC,CAAC,IACD;IACE,QAAQ;IACR,SAAS,EAAE,gBAAgB,mBAAmB;IAC9C,MAAM,KAAK,UAAU,IAAI;GAC3B,CAAC;GACL,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,OAAO,KAAK,WAAW,SAAS,QAAQ;GAE1D,OAAQ,MAAM,SAAS,KAAK;EAC9B;;EAGA,MAAM,SAAqB;GACzB,aAAa,SAAS,gBAAgB;GACtC,WAAW,SAAS,SAAS,qBAAqB,EAAE,KAAK,CAAC;GAC1D,aAAa,YAAY,SAAS,wBAAwB,EAAE,QAAQ,CAAC;GACrE,YAAY,UAAU,SAAS,uBAAuB,KAAK;GAC3D,UAAU,SAAS,SAAS,qBAAqB,EAAE,KAAK,CAAC;EAC3D;;EAGA,MAAM,UAAU;;EAGhB,MAAM,kBAAkB;;EAGxB,MAAa,SAAS;GAAC;GAAS;GAAU;GAAc;GAAiB;EAAQ;;;;;;;EA+BjF,SAAgB,MAAM,KAA0B;GAC9C,IAAI,aAAa,IAAI,OAAO,SAAA,OAAa;IAAE;IAAI;GAAG,CAAC,GAAG,mBAAmB;GAEzE,MAAM,gBAAgB,IAAI,cAAc,KAAkB,EAAE,WAAW,gBAAgB,CAAC;GACxF,MAAM,gBAAyB;IAC7B,MAAM,WAAW,cAAc,YAAY;IAC3C,OAAO,SAAS,WAAW,UACvB,SAAS,OAAO,WAAW,OAC3B,SAAS,WAAW;GAC1B;GAIA,MAAM,cAAc,IAAI,0BAA0B,aAAa;GAC/D,IAAI,MAAM,OAAO,4BAA4B,IAAI,MAAM,SAAS;IAC9D,MAAM;IACN,IAAI;IACJ,OAAO;IACP,QAAA;IACA,cAAc,YAAY,OAAO;GACnC,GAAG,eAAe,CAAC;GAInB,IAAI;GACJ,MAAM,eAAqB;IACzB,IAAI,QAAQ,KAAK,cAAc,KAAA,GAAW;KAMxC,MAAM,WAA6B,eAAe,CAAC,CAAC,OAAO;KAC3D,MAAM,cAAc,SAAS,QAAQ;KACrC,MAAM,WAAW,SAAS,QAAQ;KAClC,MAAM,cAAc,SAAS,QAAQ;KAErC,MAAM,gBAAsB;MAC1B,OAAO,MAAM,CAAC,CAAC,MAAM,aAAa;OAChC,YAAY,QAAQ;MACtB,SAAS;OACP,SAAS,SAAS,2BAA2B;MAC/C,CAAC;KACH;KAEA,MAAM,cAAc,IAAI,aAAa;MAMnC,IAAI;MACJ,MAAM,aAAmB;OACvB,IAAI,UAAU,KAAA,GAAW;QACvB,OAAO,cAAc,KAAK;QAC1B,QAAQ,KAAA;OACV;MACF;MACA,MAAM,cAAoB;OACxB,IAAI,UAAU,KAAA,KAAa,SAAS,oBAAoB,WACtD,QAAQ,OAAO,YAAY,SAAS,OAAO;MAE/C;MACA,MAAM,qBAA2B;OAC/B,IAAI,SAAS,oBAAoB,WAAW;QAC1C,QAAQ;QACR,MAAM;OACR,OACE,KAAK;MAET;MACA,MAAM;MACN,SAAS,iBAAiB,oBAAoB,YAAY;MAC1D,aAAa;OACX,KAAK;OACL,SAAS,oBAAoB,oBAAoB,YAAY;MAC/D;KACF,GAAG,WAAW;KAEd,MAAM,kBAA+B;MACnC,OAAO;MACP,QAAQ;MACR,WAAW;OACT,OAAO,SAAS,KAAK,CAAC,CAAC,MAAM,WAAW;QACtC,YAAY;SACV,MAAM,OAAO;SACb,MAAM;SACN,IAAI,KAAK,IAAI;QACf,CAAC;OACH,SAAS,CAET,CAAC;MACH;MACA,YAAY;OACV,OAAO,SAAS,MAAM,CAAC,CAAC,MAAM,WAAW;QACvC,YAAY;SACV,MAAM,OAAO;SACb,MAAM;SACN,IAAI,KAAK,IAAI;QACf,CAAC;OACH,SAAS,CAET,CAAC;MACH;MACA,YAAY;OACV,OAAO,WAAW,KAAK,CAAC,CAAC,WAAW;QAClC,QAAQ;OACV,SAAS,CAET,CAAC;MACH;MACA,cAAc;OACZ,OAAO,WAAW,IAAI,CAAC,CAAC,WAAW;QACjC,QAAQ;OACV,SAAS,CAET,CAAC;MACH;MACA,UAAU,OAAO,WAAW;OAC1B,OAAO,UAAU;QAAE;QAAO;OAAO,CAAC,CAAC,CAAC,WAAW;QAC7C,QAAQ;OACV,SAAS,CAET,CAAC;MACH;MACA,SAAS,SAAS;OAChB,OAAO,QAAQ,IAAI,CAAC,CAAC,MAAM,WAAW;QACpC,IAAI,OAAO,IAAI,QAAQ;OACzB,SAAS,CAET,CAAC;MACH;MACA,oBAAoB;OAClB,YAAY,IAAI;MAClB;KACF;KAOA,MAAM,cAAc,IAAI,MAAM,OAAO,iCACnC,IAAI,MAAM,SAAS;MACjB,MAAM;MACN,IAAI;MACJ,OAAO;MACP,QAAQ;MACR,QAAA;KACF,GAAG,YAAY,CAAC;KAElB,kBAAkB;MAChB,YAAY;MACZ,YAAY;MACZ,YAAY,KAAA;KACd;IACF,OAAO,IAAI,CAAC,QAAQ,KAAK,cAAc,KAAA,GAAW;KAChD,UAAU;KACV,YAAY,KAAA;IACd;GACF;GACA,cAAc,UAAU,MAAM;GAC9B,OAAO;EACT"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Dock anchor inside `conversation.input.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* floating WhalePet (portal); while hidden it renders the summon button.
|
|
2
|
+
* Dock anchor inside the official SDK's `conversation.input.dock` seat
|
|
3
|
+
* (the ambient readout band above the composer card, listed so several
|
|
4
|
+
* additions can stack). While visible it mounts the floating WhalePet
|
|
5
|
+
* (portal); while hidden it renders the summon button.
|
|
7
6
|
* @module @linxin666/dsh-pet/client/PetDockEntry
|
|
8
7
|
*/
|
|
9
8
|
import { type ReactElement } from 'react';
|
|
@@ -32,7 +31,7 @@ export interface PetInjected {
|
|
|
32
31
|
feedbackDone: () => void;
|
|
33
32
|
}
|
|
34
33
|
/** Composed props of the dock entry (runtime + locale + injected). */
|
|
35
|
-
export type PetDockEntryProps = PropsRuntime<'conversation.input.
|
|
34
|
+
export type PetDockEntryProps = PropsRuntime<'conversation.input.dock'> & PetInjected & PropsLocale<typeof NS>;
|
|
36
35
|
/**
|
|
37
36
|
* Dock entry: while the pet is visible, mount the floating WhalePet (it
|
|
38
37
|
* portals itself onto document.body); while hidden, render the summon
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetDockEntry.d.ts","sourceRoot":"","sources":["../../../src/client/PetDockEntry.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"PetDockEntry.d.ts","sourceRoot":"","sources":["../../../src/client/PetDockEntry.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAmC,KAAK,YAAY,EAAE,MAAM,OAAO,CAAA;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAGjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAEtD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,6DAA6D;IAC7D,KAAK,EAAE,gBAAgB,CAAA;IACvB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,kCAAkC;IAClC,GAAG,EAAE,MAAM,IAAI,CAAA;IACf,2BAA2B;IAC3B,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,2BAA2B;IAC3B,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,yCAAyC;IACzC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAChD,8CAA8C;IAC9C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,iCAAiC;IACjC,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB;AAED,sEAAsE;AACtE,MAAM,MAAM,iBAAiB,GAC3B,YAAY,CAAC,yBAAyB,CAAC,GACrC,WAAW,GACX,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;AAI1B;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,CAuCnE"}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
|
-
* Dock anchor inside `conversation.input.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* floating WhalePet (portal); while hidden it renders the summon button.
|
|
3
|
+
* Dock anchor inside the official SDK's `conversation.input.dock` seat
|
|
4
|
+
* (the ambient readout band above the composer card, listed so several
|
|
5
|
+
* additions can stack). While visible it mounts the floating WhalePet
|
|
6
|
+
* (portal); while hidden it renders the summon button.
|
|
8
7
|
* @module @linxin666/dsh-pet/client/PetDockEntry
|
|
9
8
|
*/
|
|
10
9
|
import { useEffect, useSyncExternalStore } from 'react';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WhalePet.d.ts","sourceRoot":"","sources":["../../../src/client/WhalePet.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAqC,WAAW,EAAE,MAAM,OAAO,CAAA;AAE3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAGjD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,gCAAgC,CAAA;AAEhE,mFAAmF;AACnF,eAAO,MAAM,gBAAgB,wBAAwB,CAAA;AAErD,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,qDAAqD;IACrD,OAAO,EAAE,gBAAgB,CAAA;IACzB,sCAAsC;IACtC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,kCAAkC;IAClC,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,8CAA8C;IAC9C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,2DAA2D;IAC3D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AAOD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"WhalePet.d.ts","sourceRoot":"","sources":["../../../src/client/WhalePet.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAqC,WAAW,EAAE,MAAM,OAAO,CAAA;AAE3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAGjD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,gCAAgC,CAAA;AAEhE,mFAAmF;AACnF,eAAO,MAAM,gBAAgB,wBAAwB,CAAA;AAErD,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,qDAAqD;IACrD,OAAO,EAAE,gBAAgB,CAAA;IACzB,sCAAsC;IACtC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,kCAAkC;IAClC,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,8CAA8C;IAC9C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,2DAA2D;IAC3D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AAOD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,WAAW,CAoS1D"}
|
|
@@ -36,6 +36,7 @@ export function WhalePet(props) {
|
|
|
36
36
|
const [nameDraft, setNameDraft] = useState('');
|
|
37
37
|
const [dragPos, setDragPos] = useState(null);
|
|
38
38
|
const dragRef = useRef(null);
|
|
39
|
+
const hideTimerRef = useRef(null);
|
|
39
40
|
const frameRef = useRef({
|
|
40
41
|
track: null,
|
|
41
42
|
index: 0,
|
|
@@ -161,6 +162,12 @@ export function WhalePet(props) {
|
|
|
161
162
|
// `draggedRef` records whether the pointer actually moved, so the browser's
|
|
162
163
|
// trailing click (fired after pointerup) does not pet the whale.
|
|
163
164
|
const draggedRef = useRef(false);
|
|
165
|
+
const clearHideTimer = () => {
|
|
166
|
+
if (hideTimerRef.current !== null) {
|
|
167
|
+
window.clearTimeout(hideTimerRef.current);
|
|
168
|
+
hideTimerRef.current = null;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
164
171
|
const onPointerDown = (e) => {
|
|
165
172
|
e.preventDefault();
|
|
166
173
|
e.target.setPointerCapture?.(e.pointerId);
|
|
@@ -191,15 +198,20 @@ export function WhalePet(props) {
|
|
|
191
198
|
const pos = dragPos ?? { right: display.right, bottom: display.bottom };
|
|
192
199
|
const spriteWidth = Math.round(FRAME_WIDTH * spriteScale);
|
|
193
200
|
const spriteHeight = Math.round(FRAME_HEIGHT * spriteScale);
|
|
194
|
-
const float = (_jsxs("div", { ref: floatRef, className: styles.float, style: { right: pos.right, bottom: pos.bottom, zIndex: 2147483000 }, onPointerEnter: () =>
|
|
201
|
+
const float = (_jsxs("div", { ref: floatRef, className: styles.float, style: { right: pos.right, bottom: pos.bottom, zIndex: 2147483000 }, onPointerEnter: () => {
|
|
202
|
+
clearHideTimer();
|
|
203
|
+
setHovered(true);
|
|
204
|
+
}, onPointerLeave: (e) => {
|
|
195
205
|
// The panel and bubble render OUTSIDE the container's box (absolute,
|
|
196
206
|
// above the sprite), so moving onto them fires pointerleave on the
|
|
197
207
|
// container. Treat a target still inside the container's DOM (the
|
|
198
|
-
// overflowed panel) as "still hovering"
|
|
208
|
+
// overflowed panel) as "still hovering"; otherwise give the pointer a
|
|
209
|
+
// short grace period to reach the panel across the gap above it.
|
|
199
210
|
const next = e.relatedTarget;
|
|
200
211
|
if (next instanceof Node && floatRef.current?.contains(next))
|
|
201
212
|
return;
|
|
202
|
-
|
|
213
|
+
clearHideTimer();
|
|
214
|
+
hideTimerRef.current = window.setTimeout(() => setHovered(false), 200);
|
|
203
215
|
}, children: [_jsx("div", { ref: spriteRef, className: styles.sprite, style: {
|
|
204
216
|
width: spriteWidth,
|
|
205
217
|
height: spriteHeight,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAsD3E,yBAAyB;AACzB,eAAO,MAAM,MAAM,UAA+D,CAAA;AAElF,qEAAqE;AACrE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACxE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC7D,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAEtF,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,OAAO;QACf;;;;;WAKG;QACH,oBAAoB,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,4BAA4B,CAAA;SAAE,CAAA;KAC3F;CACF;AAED,sEAAsE;AACtE,MAAM,WAAW,4BAA4B;IAC3C,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,KAAK,CAAA;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAsD3E,yBAAyB;AACzB,eAAO,MAAM,MAAM,UAA+D,CAAA;AAElF,qEAAqE;AACrE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACxE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC7D,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAEtF,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,OAAO;QACf;;;;;WAKG;QACH,oBAAoB,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,4BAA4B,CAAA;SAAE,CAAA;KAC3F;CACF;AAED,sEAAsE;AACtE,MAAM,WAAW,4BAA4B;IAC3C,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,KAAK,CAAA;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAmK9C"}
|
|
@@ -179,12 +179,13 @@ export function apply(ctx) {
|
|
|
179
179
|
setFeedback(null);
|
|
180
180
|
},
|
|
181
181
|
});
|
|
182
|
-
// The
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
|
|
182
|
+
// The legacy selector-context hole (conversation.input.selector.context)
|
|
183
|
+
// is no longer declared by newer shells (rc.6 dropped it), so the pet
|
|
184
|
+
// mounts on the composer dock band instead. The dock is session-scoped,
|
|
185
|
+
// so the pet appears once a session is active (accepted downgrade, see
|
|
186
|
+
// PR #17).
|
|
187
|
+
const disposeDock = ctx.slots.inject('conversation.input.dock', () => ctx.slots.register({
|
|
188
|
+
name: 'conversation.input.dock',
|
|
188
189
|
id: 'pet',
|
|
189
190
|
order: 110,
|
|
190
191
|
inject: injected,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linxin666/dsh-pet",
|
|
3
3
|
"description": "鲸鱼娘宠物插件 for the dsh web GUI: a soft healing whale-girl companion that reacts to model activity (idle/waiting/thinking/tool/done), with petting/feeding interactions and an affinity score",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.7",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Dock anchor inside `conversation.input.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* floating WhalePet (portal); while hidden it renders the summon button.
|
|
2
|
+
* Dock anchor inside the official SDK's `conversation.input.dock` seat
|
|
3
|
+
* (the ambient readout band above the composer card, listed so several
|
|
4
|
+
* additions can stack). While visible it mounts the floating WhalePet
|
|
5
|
+
* (portal); while hidden it renders the summon button.
|
|
7
6
|
* @module @linxin666/dsh-pet/client/PetDockEntry
|
|
8
7
|
*/
|
|
9
8
|
|
|
@@ -40,7 +39,7 @@ export interface PetInjected {
|
|
|
40
39
|
|
|
41
40
|
/** Composed props of the dock entry (runtime + locale + injected). */
|
|
42
41
|
export type PetDockEntryProps =
|
|
43
|
-
PropsRuntime<'conversation.input.
|
|
42
|
+
PropsRuntime<'conversation.input.dock'>
|
|
44
43
|
& PetInjected
|
|
45
44
|
& PropsLocale<typeof NS>
|
|
46
45
|
|
package/src/client/WhalePet.tsx
CHANGED
|
@@ -71,6 +71,7 @@ export function WhalePet(props: WhalePetProps): ReactPortal {
|
|
|
71
71
|
const [nameDraft, setNameDraft] = useState('')
|
|
72
72
|
const [dragPos, setDragPos] = useState<{ right: number; bottom: number } | null>(null)
|
|
73
73
|
const dragRef = useRef<{ startX: number; startY: number; right: number; bottom: number } | null>(null)
|
|
74
|
+
const hideTimerRef = useRef<number | null>(null)
|
|
74
75
|
const frameRef = useRef<{ track: PetAnimation | null; index: number; elapsed: number }>({
|
|
75
76
|
track: null,
|
|
76
77
|
index: 0,
|
|
@@ -193,6 +194,13 @@ export function WhalePet(props: WhalePetProps): ReactPortal {
|
|
|
193
194
|
// `draggedRef` records whether the pointer actually moved, so the browser's
|
|
194
195
|
// trailing click (fired after pointerup) does not pet the whale.
|
|
195
196
|
const draggedRef = useRef(false)
|
|
197
|
+
const clearHideTimer = (): void => {
|
|
198
|
+
if (hideTimerRef.current !== null) {
|
|
199
|
+
window.clearTimeout(hideTimerRef.current)
|
|
200
|
+
hideTimerRef.current = null
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
196
204
|
const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>): void => {
|
|
197
205
|
e.preventDefault()
|
|
198
206
|
;(e.target as HTMLElement).setPointerCapture?.(e.pointerId)
|
|
@@ -226,15 +234,20 @@ export function WhalePet(props: WhalePetProps): ReactPortal {
|
|
|
226
234
|
ref={floatRef}
|
|
227
235
|
className={styles.float}
|
|
228
236
|
style={{ right: pos.right, bottom: pos.bottom, zIndex: 2147483000 }}
|
|
229
|
-
onPointerEnter={() =>
|
|
237
|
+
onPointerEnter={() => {
|
|
238
|
+
clearHideTimer()
|
|
239
|
+
setHovered(true)
|
|
240
|
+
}}
|
|
230
241
|
onPointerLeave={(e) => {
|
|
231
242
|
// The panel and bubble render OUTSIDE the container's box (absolute,
|
|
232
243
|
// above the sprite), so moving onto them fires pointerleave on the
|
|
233
244
|
// container. Treat a target still inside the container's DOM (the
|
|
234
|
-
// overflowed panel) as "still hovering"
|
|
245
|
+
// overflowed panel) as "still hovering"; otherwise give the pointer a
|
|
246
|
+
// short grace period to reach the panel across the gap above it.
|
|
235
247
|
const next = e.relatedTarget
|
|
236
248
|
if (next instanceof Node && floatRef.current?.contains(next)) return
|
|
237
|
-
|
|
249
|
+
clearHideTimer()
|
|
250
|
+
hideTimerRef.current = window.setTimeout(() => setHovered(false), 200)
|
|
238
251
|
}}
|
|
239
252
|
>
|
|
240
253
|
<div
|
package/src/client/index.ts
CHANGED
|
@@ -233,13 +233,14 @@ export function apply(ctx: ClientContext): void {
|
|
|
233
233
|
},
|
|
234
234
|
})
|
|
235
235
|
|
|
236
|
-
// The
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
|
|
236
|
+
// The legacy selector-context hole (conversation.input.selector.context)
|
|
237
|
+
// is no longer declared by newer shells (rc.6 dropped it), so the pet
|
|
238
|
+
// mounts on the composer dock band instead. The dock is session-scoped,
|
|
239
|
+
// so the pet appears once a session is active (accepted downgrade, see
|
|
240
|
+
// PR #17).
|
|
241
|
+
const disposeDock = ctx.slots.inject('conversation.input.dock', () =>
|
|
241
242
|
ctx.slots.register({
|
|
242
|
-
name: 'conversation.input.
|
|
243
|
+
name: 'conversation.input.dock',
|
|
243
244
|
id: 'pet',
|
|
244
245
|
order: 110,
|
|
245
246
|
inject: injected,
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
2
|
-
interface SlotMap {
|
|
3
|
-
/**
|
|
4
|
-
* The input selector context-chip hole: feature chips rendered right
|
|
5
|
-
* after the workspace selector. Session-maybe: entries stay mounted
|
|
6
|
-
* without a session and hide themselves when their data source is absent.
|
|
7
|
-
*/
|
|
8
|
-
'conversation.input.selector.context': {
|
|
9
|
-
kind: 'list';
|
|
10
|
-
scope: 'session-maybe';
|
|
11
|
-
owner: InputSelectorContextOwnerProps;
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
/** Owner share of the input selector context-chip hole (empty by contract). */
|
|
15
|
-
interface InputSelectorContextOwnerProps {
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
export {};
|
|
19
|
-
//# sourceMappingURL=slots-augment.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"slots-augment.d.ts","sourceRoot":"","sources":["../../../src/client/slots-augment.ts"],"names":[],"mappings":"AAUA,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,OAAO;QACf;;;;WAIG;QACH,qCAAqC,EAAE;YACrC,IAAI,EAAE,MAAM,CAAA;YACZ,KAAK,EAAE,eAAe,CAAA;YACtB,KAAK,EAAE,8BAA8B,CAAA;SACtC,CAAA;KACF;IAED,+EAA+E;IAC/E,UAAU,8BAA8B;KAAG;CAC5C"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Local slot-table augmentation for slots the official NPM SDK
|
|
3
|
-
* (ui-conversation rc.6) does not publish yet, but the running dsh worktree
|
|
4
|
-
* boots with (`conversation.input.selector.*`). Type-only supplement — the
|
|
5
|
-
* runtime surface comes from the dsh profile. Module form (with an import)
|
|
6
|
-
* so TS merges into the package declarations instead of shadowing them.
|
|
7
|
-
* Remove once the SDK ships these slots.
|
|
8
|
-
*/
|
|
9
|
-
import type {} from '@deepseek-ai/dsh-client-ui-slots'
|
|
10
|
-
|
|
11
|
-
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
12
|
-
interface SlotMap {
|
|
13
|
-
/**
|
|
14
|
-
* The input selector context-chip hole: feature chips rendered right
|
|
15
|
-
* after the workspace selector. Session-maybe: entries stay mounted
|
|
16
|
-
* without a session and hide themselves when their data source is absent.
|
|
17
|
-
*/
|
|
18
|
-
'conversation.input.selector.context': {
|
|
19
|
-
kind: 'list'
|
|
20
|
-
scope: 'session-maybe'
|
|
21
|
-
owner: InputSelectorContextOwnerProps
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** Owner share of the input selector context-chip hole (empty by contract). */
|
|
26
|
-
interface InputSelectorContextOwnerProps {}
|
|
27
|
-
}
|