@linxin666/dsh-pet 0.3.20 → 0.3.21

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.
Files changed (50) hide show
  1. package/lib/client.js +127 -26
  2. package/lib/client.js.map +1 -1
  3. package/lib/index.js +99 -4
  4. package/lib/types/client/gameplay-hud.d.ts +15 -0
  5. package/lib/types/client/gameplay-hud.d.ts.map +1 -1
  6. package/lib/types/client/gameplay-hud.js +48 -6
  7. package/lib/types/client/index.d.ts.map +1 -1
  8. package/lib/types/client/index.js +21 -13
  9. package/lib/types/client/renderers/Frames2dVisualMount.d.ts.map +1 -1
  10. package/lib/types/client/renderers/Frames2dVisualMount.js +5 -0
  11. package/lib/types/client/renderers/frames2d.d.ts +5 -4
  12. package/lib/types/client/renderers/frames2d.d.ts.map +1 -1
  13. package/lib/types/client/renderers/frames2d.js +27 -18
  14. package/lib/types/client/work-tick-gate.d.ts +40 -0
  15. package/lib/types/client/work-tick-gate.d.ts.map +1 -0
  16. package/lib/types/client/work-tick-gate.js +49 -0
  17. package/lib/types/gameplay.d.ts +4 -0
  18. package/lib/types/gameplay.d.ts.map +1 -1
  19. package/lib/types/gameplay.js +11 -2
  20. package/lib/types/ledger.d.ts +8 -0
  21. package/lib/types/ledger.d.ts.map +1 -1
  22. package/lib/types/ledger.js +25 -0
  23. package/lib/types/persist.d.ts +6 -0
  24. package/lib/types/persist.d.ts.map +1 -1
  25. package/lib/types/persist.js +25 -1
  26. package/lib/types/routes.d.ts.map +1 -1
  27. package/lib/types/routes.js +6 -0
  28. package/lib/types/service.d.ts +24 -0
  29. package/lib/types/service.d.ts.map +1 -1
  30. package/lib/types/service.js +32 -0
  31. package/package.json +1 -1
  32. package/src/client/PetDockEntry.test.tsx +1 -0
  33. package/src/client/gameplay-hud.test.tsx +165 -2
  34. package/src/client/gameplay-hud.tsx +62 -6
  35. package/src/client/index.ts +23 -13
  36. package/src/client/pet.module.css +1 -1
  37. package/src/client/renderers/Frames2dVisualMount.test.tsx +73 -0
  38. package/src/client/renderers/Frames2dVisualMount.tsx +4 -0
  39. package/src/client/renderers/frames2d.test.ts +38 -6
  40. package/src/client/renderers/frames2d.ts +27 -19
  41. package/src/client/work-tick-gate.test.ts +53 -0
  42. package/src/client/work-tick-gate.ts +63 -0
  43. package/src/gameplay.test.ts +36 -0
  44. package/src/gameplay.ts +14 -2
  45. package/src/ledger.test.ts +19 -0
  46. package/src/ledger.ts +24 -0
  47. package/src/persist.test.ts +13 -0
  48. package/src/persist.ts +29 -1
  49. package/src/routes.ts +5 -0
  50. package/src/service.ts +38 -0
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["styles","styles","styles","css","sectionCss"],"sources":["../../../packages/dsh-pet/src/client/pet-store.ts","../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","../../../packages/dsh-pet/src/announce.ts","../../../packages/dsh-pet/src/state.ts","../../../packages/dsh-pet/src/client/spritesheet.ts","../../../packages/dsh-pet/src/client/sequences.ts","../../../packages/dsh-pet/src/client/PetSprite.tsx","../../../packages/dsh-pet/src/client/drag-stream.ts","../../../packages/dsh-pet/src/client/renderers/registry.ts","../../../packages/dsh-pet/src/client/phase-stream.ts","../../../packages/dsh-pet/src/client/renderers/live2d/Live2dVisualMount.tsx","../../../packages/dsh-pet/src/client/renderers/Frames2dVisualMount.tsx","../../../packages/dsh-pet/src/client/renderers/PetRendererSwitch.tsx","../../../packages/dsh-pet/src/gameplay.ts","../../../packages/dsh-pet/src/client/gameplay-hud.tsx","../../../packages/dsh-pet/src/client/PetDockEntry.tsx","../../../packages/dsh-pet/src/contracts/renderer.ts","../../../packages/dsh-pet/src/client/renderers/live2d/runtime.ts","../../../packages/dsh-pet/src/client/renderers/live2d.ts","../../../packages/dsh-pet/src/client/renderers/frames2d.ts","../../../packages/dsh-pet/src/client/ui-teardown.ts","../../../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/telemetry.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-store'\nimport type { EngineStoreHandle, EngineStoreInstance } from '@deepseek-ai/dsh-client-store'\nimport type { PetGameplayStateView, PetStateView } from '../service.ts'\nimport type { PetInteraction } from '../affinity.ts'\nimport type { PetDefinition } from '../registry.ts'\n\n/** One transient reaction bubble on the pet. */\nexport interface PetFeedback {\n /** Bubble copy. */\n text: string\n /** Interaction kind driving the reaction animation. */\n kind: PetInteraction | 'none'\n /** Epoch ms when the bubble appeared (for expiry). */\n at: number\n}\n\n/** Pet UI state as consumers see it. */\nexport interface PetUiState {\n /** Latest host snapshot; null before the first successful fetch. */\n snapshot: PetStateView | null\n /** The registry list the host serves (atlas URLs + geometry + tracks). */\n pets: PetDefinition[]\n /** Fetch lifecycle. */\n state: 'loading' | 'ready' | 'error'\n /** Transport error message (for the debug surface), when any. */\n error: string | null\n /** Active reaction bubble, if any. */\n feedback: PetFeedback | null\n}\n\n/** Store write set. */\nexport type PetUiActions = {\n /** Replace the host snapshot (poll result). */\n setSnapshot: (draft: PetUiState, snapshot: PetStateView) => void\n /** Replace the registry list. */\n setPets: (draft: PetUiState, pets: PetDefinition[]) => void\n /** Mark the fetch lifecycle. */\n setState: (draft: PetUiState, state: PetUiState['state'], error: string | null) => void\n /** Show a reaction bubble. */\n setFeedback: (draft: PetUiState, feedback: PetFeedback | null) => void\n /**\n * Patch the gameplay slice of the snapshot (verb write-back), so the HUD\n * reflects a touch/mode/buy result immediately instead of waiting one\n * poll tick (2 s).\n */\n setGameplayView: (draft: PetUiState, view: PetGameplayStateView) => void\n}\n\n/** Create the pet store handle (apply world only; never module-level). */\nexport function createPetStore(): EngineStoreHandle<PetUiState, PetUiActions> {\n return defineStore({\n init: (): PetUiState => ({\n snapshot: null,\n pets: [],\n state: 'loading',\n error: null,\n feedback: null,\n }),\n actions: {\n setSnapshot: (draft, snapshot) => {\n // The 2 s poll republishes the full snapshot even while the pet is\n // idle. Skipping an unchanged payload keeps immer's produce at zero\n // modifications, so zustand never notifies and the whole sprite tree\n // skips the re-render. Equality is JSON-based and skip-only: both\n // sides come from the same host serializer, and any mismatch falls\n // through to the normal publish — the failure mode is one extra\n // render, never a stale pet.\n if (draft.state === 'ready' && draft.error === null && sameSnapshot(draft.snapshot, snapshot)) return\n draft.snapshot = snapshot\n draft.state = 'ready'\n draft.error = null\n },\n setPets: (draft, pets) => {\n draft.pets = pets\n },\n setState: (draft, state, error) => {\n draft.state = state\n draft.error = error\n },\n setFeedback: (draft, feedback) => {\n draft.feedback = feedback\n },\n setGameplayView: (draft, view) => {\n if (draft.snapshot !== null) draft.snapshot = { ...draft.snapshot, gameplay: view }\n },\n },\n })\n}\n\nexport type { PetInteraction }\n\n/**\n * Content equality for consecutive poll snapshots. JSON compare, not field\n * enumeration: an exact string match is the only way to skip the publish, so\n * a missed field can never freeze the UI — it can only cost the render the\n * optimization exists to save.\n */\nfunction sameSnapshot(previous: PetStateView | null, next: PetStateView): boolean {\n return previous !== null && JSON.stringify(previous) === JSON.stringify(next)\n}\n\n/**\n * A live pet store instance (one per host, owned by the plugin apply body —\n * the pet itself is host-global, so its UI state must not ride the slot\n * system's per-session store scoping).\n */\nexport type PetStoreInstance = EngineStoreInstance<PetUiState, PetUiActions>\n\n","function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","/**\n * The pet announcement-bubble contract (dsh-usage linkage): a sibling plugin\n * pushes one structured announcement through `pet.announce(...)`, the host\n * validates it into a bounded payload, and the browser half renders it as a\n * dedicated, specially styled bubble above the session bubble stack.\n *\n * The validation lives in this pure module so the wire contract has exactly\n * one home and stays testable without the cordis service.\n * @module @linxin666/dsh-pet/announce\n */\n\n/** One plugin-authored announcement bubble. */\nexport interface PetAnnouncement {\n /** Authoring plugin's source tag (`dsh-usage` for the usage statistics). */\n source: string\n /** Bubble content kind: a spend estimate, an account balance, or a plan-quota status. */\n kind: 'balance' | 'cost' | 'plan'\n /** Lead text (usually the provider display name). */\n title: string\n /** Balance or today-spend amount, formatted for display (kinds `balance` and `cost`). */\n amount?: string\n /** Plan usage percent 0-100 (kind `plan`). */\n percent?: number\n /** ISO 8601 reset instant (kind `plan`). */\n resetAt?: string\n /** Short trailing note (plan tier name, peak-period status, currency code, ...). */\n note?: string\n /** Visual tone; drives the bubble's accent color. */\n tone: 'ok' | 'warn' | 'low'\n /** Freshness window in ms; an expired announcement stops rendering. */\n ttlMs: number\n /** Epoch ms the announcement arrived. */\n at: number\n}\n\n/** Default freshness window. */\nexport const ANNOUNCE_DEFAULT_TTL_MS = 10_000\n\n/**\n * Hard TTL ceiling. A repeating announcer (dsh-usage) declares its poll\n * cadence as the TTL, so an `always`-mode bubble stays continuous across\n * polls; the ceiling means a source that dies unmounts its bubble within at\n * most one missed refresh cycle rather than lingering forever.\n */\nexport const ANNOUNCE_MAX_TTL_MS = 7_200_000\n\n/** Hard bounds: lengths and the TTL range (1 s .. 60 s). */\nconst TITLE_MAX = 80\nconst TEXT_MAX = 120\nconst SOURCE_MAX = 64\n\nfunction boundedString(value: unknown, max: number): string | undefined {\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n if (trimmed === '') return undefined\n return trimmed.length <= max ? trimmed : trimmed.slice(0, max)\n}\n\n/**\n * Validate one announce payload. Unknown fields are dropped, oversized text\n * is truncated, and anything structurally wrong resolves to undefined — a\n * malformed announcement never reaches the pet's bubble surface.\n * @param input - the payload a sibling plugin passed to `pet.announce`.\n * @param now - epoch ms the announcement arrived.\n */\nexport function parseAnnouncement(input: unknown, now: number): PetAnnouncement | undefined {\n if (typeof input !== 'object' || input === null) return undefined\n const data = input as Record<string, unknown>\n const source = boundedString(data.source, SOURCE_MAX)\n const title = boundedString(data.title, TITLE_MAX)\n if (source === undefined || title === undefined) return undefined\n const kind = data.kind === 'balance' || data.kind === 'cost' || data.kind === 'plan' ? data.kind : undefined\n if (kind === undefined) return undefined\n const amount = boundedString(data.amount, TEXT_MAX)\n const resetAt = boundedString(data.resetAt, TEXT_MAX)\n const note = boundedString(data.note, TITLE_MAX)\n const percent = typeof data.percent === 'number' && Number.isFinite(data.percent)\n ? Math.max(0, Math.min(100, data.percent))\n : undefined\n if ((kind === 'balance' || kind === 'cost') && amount === undefined) return undefined\n if (kind === 'plan' && percent === undefined) return undefined\n const tone = data.tone === 'warn' || data.tone === 'low' ? data.tone : 'ok'\n const ttlMs = typeof data.ttlMs === 'number' && Number.isFinite(data.ttlMs)\n ? Math.max(1000, Math.min(ANNOUNCE_MAX_TTL_MS, data.ttlMs))\n : ANNOUNCE_DEFAULT_TTL_MS\n return {\n source,\n kind,\n title,\n ...(amount !== undefined ? { amount } : {}),\n ...(percent !== undefined ? { percent } : {}),\n ...(resetAt !== undefined ? { resetAt } : {}),\n ...(note !== undefined ? { note } : {}),\n tone,\n ttlMs,\n at: now,\n }\n}\n\n/** Whether an announcement is still fresh at `now`. */\nexport function announcementFresh(announcement: PetAnnouncement, now: number): boolean {\n return now - announcement.at < announcement.ttlMs\n}\n","/**\n * Pet state machine — pure, clock-injected. Maps official DSH session activity\n * and the legacy `activity/status` vocabulary onto the 9-state Codex pet\n * animation contract, plus turn-end celebration and no-session idle.\n *\n * The machine is deliberately dumb: it holds the last input phase, the\n * animation decision, and a one-shot \"celebration\" window after `done` so the\n * pet visibly jumps before settling back to idle. Everything here is a pure\n * function of (input, nowMs); persistence and RPC live in the service.\n * @module @linxin666/dsh-pet/state\n */\n\n/** Activity phases understood by the pet host. */\nexport type ActivityPhase = 'idle' | 'waiting' | 'thinking' | 'tool' | 'review' | 'done' | 'failed'\n\n/** The Codex-compatible 9-state animation contract (spritesheet rows). */\nexport type PetAnimation =\n | 'idle'\n | 'running-right'\n | 'running-left'\n | 'waving'\n | 'jumping'\n | 'failed'\n | 'waiting'\n | 'running'\n | 'review'\n\n/** One input snapshot consumed by the machine. */\nexport interface PetStateInput {\n /** Current activity phase of the active session. */\n phase: ActivityPhase\n /** Human-readable status line (plain text). */\n line?: string\n /** Playful phrase from the activity tracker, when any. */\n phrase?: string\n}\n\n/** Animation decision plus the copy the pet should show. */\nexport interface PetStateSnapshot {\n /** Which animation track to play. */\n animation: PetAnimation\n /** Optional status bubble copy (line or phrase), shown while active. */\n bubble?: string\n /** Wall-clock ms this animation started (client can sync loops). */\n animationStartedAt: number\n /** Raw phase, for debugging and client-side rendering decisions. */\n phase: ActivityPhase\n /** True when there is an active session (pet mounted). */\n sessionActive: boolean\n}\n\n/** Machine configuration. */\nexport interface PetStateConfig {\n /** Celebration window after `done` before settling to idle, ms (default 2400). */\n celebrateMs: number\n /** Failure display window before settling to idle, ms (default 2400). */\n failureMs: number\n}\n\nexport const defaultPetStateConfig: PetStateConfig = { celebrateMs: 2400, failureMs: 2400 }\n\n/**\n * Map one activity phase onto the animation contract.\n * - thinking → `running` and tool → `running-right` (focused work).\n * - review → `review` while answer text is streaming.\n * - waiting → `waiting` (expectant pose, needs user input).\n * - done → `jumping` (celebration), then back to `idle` after the window.\n * - failed → `failed` briefly, then back to `idle`.\n * - idle → `idle` (calm breathing loop).\n */\nexport function animationForPhase(phase: ActivityPhase): PetAnimation {\n switch (phase) {\n case 'thinking': return 'running'\n case 'tool': return 'running-right'\n case 'review': return 'review'\n case 'waiting': return 'waiting'\n case 'done': return 'jumping'\n case 'failed': return 'failed'\n case 'idle': return 'idle'\n }\n}\n\n/** The spritesheet row index for one animation track. */\nexport function rowOf(animation: PetAnimation): number {\n const rows: Record<PetAnimation, number> = {\n 'idle': 0,\n 'running-right': 1,\n 'running-left': 2,\n 'waving': 3,\n 'jumping': 4,\n 'failed': 5,\n 'waiting': 6,\n 'running': 7,\n 'review': 8,\n }\n return rows[animation]\n}\n\n/**\n * PetStateMachine — one instance per host process. Holds only the latest\n * input snapshot and terminal-state timing; no storage, no side effects.\n */\nexport class PetStateMachine {\n private phase: ActivityPhase = 'idle'\n private line: string | undefined\n private phrase: string | undefined\n private sessionActive = false\n private doneAt: number | undefined\n private failedAt: number | undefined\n private readonly config: PetStateConfig\n\n constructor(\n config: Partial<PetStateConfig> = defaultPetStateConfig,\n private readonly now: () => number = Date.now,\n ) {\n this.config = { ...defaultPetStateConfig, ...config }\n }\n\n /** Consume one projected activity update. */\n onActivityStatus(input: PetStateInput): void {\n this.phase = input.phase\n this.line = input.line\n this.phrase = input.phrase\n this.doneAt = input.phase === 'done' ? this.now() : undefined\n this.failedAt = input.phase === 'failed' ? this.now() : undefined\n }\n\n /** A session became the active one (or a fresh session started). */\n onSessionActive(): void {\n this.sessionActive = true\n }\n\n /** The active session was disposed (or none left). */\n onSessionDisposed(): void {\n this.sessionActive = false\n this.phase = 'idle'\n this.line = undefined\n this.phrase = undefined\n this.doneAt = undefined\n this.failedAt = undefined\n }\n\n /** Render the current animation decision. */\n render(): PetStateSnapshot {\n const nowMs = this.now()\n let animation = animationForPhase(this.phase)\n const doneSettled = this.phase === 'done'\n && this.doneAt !== undefined\n && nowMs - this.doneAt >= this.config.celebrateMs\n const failedSettled = this.phase === 'failed'\n && this.failedAt !== undefined\n && nowMs - this.failedAt >= this.config.failureMs\n if (doneSettled || failedSettled) animation = 'idle'\n // Settled sessions never bubble: idle (e.g. an aborted/stopped turn),\n // completed celebration expiry, and failed display expiry all fall silent.\n const settled = this.phase === 'idle' || doneSettled || failedSettled\n const bubble = settled ? undefined : this.phrase ?? this.line\n return {\n animation,\n ...(bubble === undefined ? {} : { bubble }),\n animationStartedAt: nowMs,\n phase: this.phase,\n sessionActive: this.sessionActive,\n }\n }\n}\n","/**\n * Spritesheet geometry helpers — parameterized by the pet definition the\n * host serves over '/api/pet/pets', so the browser half renders any registry\n * entry without per-pet code. The per-track tables (frames, durations, loop,\n * fallback) also come from the registry; these helpers only place frames,\n * guard track lengths, and map the fixed 9-row animation contract.\n * @module @linxin666/dsh-pet/client/spritesheet\n */\n\nimport { rowOf, type PetAnimation } from '../state.ts'\nimport type { PetCell, PetTrackDef } from '../registry.ts'\n\n/** Animation track shape the frame loop consumes. */\nexport type TrackDef = PetTrackDef\n\n/** Row index of one animation track (the fixed 9-row contract). */\nexport function rowOfTrack(animation: PetAnimation): number {\n // The table itself lives in state.ts (rowOf) — the single source of truth.\n return rowOf(animation)\n}\n\n/**\n * Background-position (px) of one frame cell within the scaled atlas.\n * The background image is scaled by `scale` (element size ÷ cell size), and\n * background-position offsets are applied in SCALED coordinates — using raw\n * atlas coordinates here would drift each frame by the scale factor and\n * render torn/overlapping frames.\n */\nexport function framePosition(cell: PetCell, row: number, col: number, scale = 1): { x: number; y: number } {\n return { x: -col * cell.width * scale, y: -row * cell.height * scale }\n}\n\n/**\n * Trim a track to the actual frame count of its row (the manifest's per-row\n * counts are authoritative; this is a last-line guard against a definition\n * whose row count disagrees with its track table). A row with 0 detected\n * frames degrades to the first frame so the pet never renders blank.\n */\nexport function trimTrack(track: TrackDef, frameCount: number): TrackDef {\n const n = Math.max(1, Math.min(frameCount, track.frames.length, track.durations.length))\n return {\n frames: track.frames.slice(0, n),\n durations: track.durations.slice(0, n),\n loop: track.loop,\n ...(track.fallback === undefined ? {} : { fallback: track.fallback }),\n }\n}\n","/** Pure timing helpers for manifest-defined scene animation sequences. */\n\nimport type { PetTrackDef } from '../registry.ts'\nimport type { PetAnimation } from '../state.ts'\n\nexport interface SequenceFrame {\n animation: PetAnimation\n frameIndex: number\n}\n\n/**\n * Precomputed timeline for one looping sequence: the per-item duration table\n * is built once instead of per query. The sprite frame loop asks at\n * animation rate (~60 Hz), where the per-call map/reduce is pure waste.\n */\nexport interface SequenceTimeline {\n frameAt(elapsedMs: number): SequenceFrame\n}\n\n/** Build a {@link SequenceTimeline} over one manifest sequence. */\nexport function createSequenceTimeline(\n sequence: readonly PetAnimation[],\n tracks: Record<PetAnimation, PetTrackDef>,\n): SequenceTimeline {\n const itemDurations = sequence.map(animation => tracks[animation].durations.reduce((sum, value) => sum + value, 0))\n const sequenceDuration = itemDurations.reduce((sum, value) => sum + value, 0)\n return {\n frameAt(elapsedMs: number): SequenceFrame {\n let offset = Math.max(0, elapsedMs) % sequenceDuration\n let itemIndex = 0\n while (itemIndex < sequence.length - 1 && offset >= itemDurations[itemIndex]!) {\n offset -= itemDurations[itemIndex]!\n itemIndex += 1\n }\n const animation = sequence[itemIndex]!\n const track = tracks[animation]\n let frameIndex = 0\n while (frameIndex < track.frames.length - 1 && offset >= track.durations[frameIndex]!) {\n offset -= track.durations[frameIndex]!\n frameIndex += 1\n }\n return { animation, frameIndex }\n },\n }\n}\n\n/** Resolve the active track and frame after elapsed milliseconds of a looping sequence. */\nexport function sequenceFrameAt(\n sequence: readonly PetAnimation[],\n tracks: Record<PetAnimation, PetTrackDef>,\n elapsedMs: number,\n): SequenceFrame {\n return createSequenceTimeline(sequence, tracks).frameAt(elapsedMs)\n}\n","/**\n * Pet sprite companion component — the browser half's centerpiece. Renders a\n * fixed-position floating sprite (React portal onto document.body), plays\n * the track matching the host animation snapshot, and exposes the\n * interaction surface: click to pet, hover panel with feed/rename/hide, drag\n * to reposition (persisted via setConfig). Everything visual comes from the\n * pet definition the host serves ('/api/pet/pets' + the state snapshot's\n * pet id), so one component renders every registry entry.\n * @module @linxin666/dsh-pet/client/PetSprite\n */\n\nimport { useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport type { CSSProperties, PointerEvent as ReactPointerEvent, ReactElement, ReactNode, ReactPortal } from 'react'\nimport { createPortal } from 'react-dom'\nimport clsx from 'clsx'\nimport type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDisplayConfig } from '../persist.ts'\nimport type { PetStateView } from '../service.ts'\nimport { announcementFresh, type PetAnnouncement } from '../announce.ts'\nimport type { PetDefinition } from '../registry.ts'\nimport type { DecorationView } from '../contracts/status-decoration.ts'\nimport type { PetFeedback } from './pet-store.ts'\nimport { framePosition, rowOfTrack, trimTrack } from './spritesheet.ts'\nimport { createSequenceTimeline } from './sequences.ts'\nimport { animationForPhase, type ActivityPhase, type PetAnimation } from '../state.ts'\nimport { NS } from './locales.ts'\nimport styles from './pet.module.css'\n\n/** Props injected by the plugin apply body (store actions + locale). */\nexport interface PetSpriteProps {\n /** Latest host snapshot; null while loading. */\n snapshot: PetStateView | null\n /** The selected pet's registry definition (atlas URL + geometry + tracks). */\n definition: PetDefinition\n /** Display configuration (persisted by the host). */\n display: PetDisplayConfig\n /** Active reaction bubble, if any. */\n feedback: PetFeedback | null\n /** Pet the sprite (click). */\n onPet: () => void\n /** Feed the sprite (panel button). */\n onFeed: () => void\n /** Hide the sprite (panel button). */\n onHide: () => void\n /** Persist a drag position. */\n onDragEnd: (right: number, bottom: number) => void\n /** Drag gesture notifications for renderers with a drag track (frames2d). */\n onDraggingChange?: (dragging: boolean) => void\n /** Rename the selected pet (persisted by the host). */\n onRename: (name: string) => void\n /** Navigate to the session one status bubble reports on. */\n onOpenSession: (sessionId: string) => void\n /** Clear the reaction bubble (after its CSS animation). */\n onFeedbackDone: () => void\n /**\n * Custom visual replacing the sprite2d atlas animation (pet-center M3).\n * The chrome (drag, bubbles, panel, tap economy) is untouched: the visual\n * renders inside the sprite box, and the atlas load + frame loop skip.\n */\n visual?: ReactNode\n /**\n * Gameplay overlay (miku-pet generalization): rendered inside the float\n * container so hover containment and stacking work unchanged. Absent for\n * pets without a gameplay block.\n */\n hud?: ReactNode\n /**\n * Gameplay tap sink: receives the tap point as sprite-box fractions\n * (0..1). When present the chrome reports the tap IN ADDITION to the\n * affinity pet; the HUD decides zones/no-ops.\n */\n onGameplayTap?: (fractionX: number, fractionY: number) => void\n /**\n * Gameplay entry (miku-pet generalization): when present the hover panel\n * renders a 玩法 action that opens/closes the gameplay card. The chrome\n * wires it to the HUD through the per-pet bus (openCard), mirroring the\n * onGameplayTap sink. Absent for pets without a gameplay block.\n */\n onGameplayMenu?: () => void\n /** Disable the drag gesture (gameplay work mode blocks dragging). */\n dragDisabled?: boolean\n /**\n * DOM node the floating chrome portals into. Defaults to document.body\n * (the legacy behavior); the plugin apply passes its owning root (the\n * [data-dsh-plugin=\"pet\"] container) so the root owns the whole surface\n * and a root-keyed suppressor (the portrait mobile layer) hides the pet\n * as one unit instead of missing the portaled sprite.\n */\n portalTarget?: Element\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 status decoration ornament (pet-center M5, #567). Renders the active\n * phase's frame segment as a CSS-background strip at a compact bubble\n * height; prefers-reduced-motion holds the segment's first frame, and a\n * missing or undecodable asset simply paints nothing (CSS background\n * failure) — the bubble text is never disturbed. The span is aria-hidden;\n * the bubble keeps its own semantics untouched.\n */\nfunction StatusOrnament(props: { decoration: DecorationView; phase: ActivityPhase }): ReactElement | null {\n const { decoration, phase } = props\n const segment = decoration.phases[phase]\n const shown = segment !== undefined && segment !== 'hide'\n const segmentKey = segment !== undefined && segment !== 'hide' ? segment.from + ':' + segment.to : 'none'\n const spanRef = useRef<HTMLSpanElement | null>(null)\n const scale = 18 / decoration.cell.height\n const frameWidth = Math.round(decoration.cell.width * scale)\n const stripWidth = decoration.columns * frameWidth\n // Value-stable dependency key: the host serves a fresh DecorationView\n // object on every state poll (2 s), so the effect must not depend on the\n // object identity — otherwise each poll would cancel and restart the\n // frame loop and the animation would jump back to its first frame.\n const durationsKey = decoration.durations.join(',')\n useEffect(() => {\n if (segment === undefined || segment === 'hide') return\n const el = spanRef.current\n if (el === null) return\n const position = (index: number): string => (-index * frameWidth) + 'px 0px'\n const reduceMotion = window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches === true\n el.style.backgroundPosition = position(segment.from)\n // A single-frame segment (from === to) has nothing to animate: with\n // loop=true the wrap branch would reset index to the same frame and the\n // tick would keep rescheduling a no-op rAF forever. Settle on the one\n // frame instead — same as the reduced-motion static hold.\n if (reduceMotion || segment.from === segment.to) return\n let timer = 0\n let index = segment.from\n let elapsed = 0\n let last = performance.now()\n const tick = (): void => {\n const now = performance.now()\n const delta = now - last\n last = now\n elapsed += delta\n const duration = decoration.durations[index] ?? 120\n // The segment's frame rate (duration ms, typically 90-160) is far\n // below the rAF cadence, so a 60fps loop would spend ~90% of its\n // ticks doing nothing. Schedule by the remaining time to the next\n // frame instead — the ornament wakes once per frame, not once per\n // screen refresh. A late wake (background tab, jank) carries extra\n // elapsed time, so catch up every due frame like the sprite loop.\n if (elapsed >= duration) {\n do {\n elapsed -= duration\n if (index < segment.to) index += 1\n else if (decoration.loop) index = segment.from\n } while (elapsed >= duration)\n // Only advance the background when the frame actually changes.\n el.style.backgroundPosition = position(index)\n }\n // A non-looping segment settles on its last frame; stop scheduling\n // instead of repainting the same position every frame.\n if (!decoration.loop && index === segment.to) return\n timer = window.setTimeout(tick, Math.max(1, duration - elapsed))\n }\n timer = window.setTimeout(tick, 0)\n return () => window.clearTimeout(timer)\n }, [shown, segmentKey, frameWidth, decoration.loop, durationsKey])\n if (!shown) return null\n return (\n <span\n ref={spanRef}\n aria-hidden=\"true\"\n data-dsh-pet-decoration={decoration.id}\n style={{\n display: 'inline-block',\n width: frameWidth,\n height: 18,\n marginRight: 6,\n verticalAlign: 'middle',\n flexShrink: 0,\n backgroundImage: 'url(' + decoration.entryUrl + ')',\n backgroundSize: stripWidth + 'px 18px',\n backgroundRepeat: 'no-repeat',\n backgroundPosition: '0px 0px',\n }}\n />\n )\n}\n\n/**\n * The announcement bubble (dsh-usage linkage): a dedicated, specially\n * designed surface for sibling-plugin facts — a balance or today-spend pill,\n * or a plan-quota card with a tone-tinted accent, a mini meter for percent\n * windows, and the reset instant. It rides the top of the session bubble\n * stack (column-reverse puts the DOM-last child farthest from the sprite)\n * and persists for its TTL instead of the short feedback pop.\n */\nfunction UsageAnnouncementBubble(props: { announcement: PetAnnouncement }): ReactNode {\n const { announcement } = props\n const tone = announcement.tone === 'low'\n ? styles.bubbleUsageLow\n : announcement.tone === 'warn'\n ? styles.bubbleUsageWarn\n : styles.bubbleUsageOk\n return (\n <div\n className={clsx(styles.bubble, styles.bubbleUsage, tone)}\n role=\"status\"\n aria-live=\"polite\"\n data-dsh-pet-announcement={announcement.source}\n >\n <span className={styles.bubbleUsageHead}>\n <span className={styles.bubbleUsageTitle}>{announcement.title}</span>\n {(announcement.kind === 'balance' || announcement.kind === 'cost') && announcement.amount !== undefined && (\n <span className={styles.bubbleUsageValue}>{announcement.amount}</span>\n )}\n {announcement.kind === 'plan' && announcement.percent !== undefined && (\n <span className={styles.bubbleUsageValue}>{Math.round(announcement.percent) + '%'}</span>\n )}\n </span>\n {announcement.kind === 'plan' && announcement.percent !== undefined && (\n <span className={styles.bubbleUsageMeter}>\n <span\n className={styles.bubbleUsageMeterFill}\n style={{ width: Math.min(100, Math.max(0, announcement.percent)) + '%' }}\n />\n </span>\n )}\n {announcement.note !== undefined && (\n <span className={styles.bubbleUsageNote}>{announcement.note}</span>\n )}\n </div>\n )\n}\n\n/**\n * The floating pet. The spritesheet frame advances on requestAnimationFrame\n * with per-frame durations from the definition's tracks; the atlas image is\n * loaded once and the background position is written straight to the sprite\n * element (no per-frame React state).\n */\nexport function PetSprite(props: PetSpriteProps): ReactPortal {\n const { snapshot, definition, display, feedback } = props\n const spriteRef = useRef<HTMLDivElement | null>(null)\n const floatRef = useRef<HTMLDivElement | null>(null)\n const panelRef = useRef<HTMLDivElement | null>(null)\n // Whichever bubble surface is currently rendered (feedback, the session\n // stack, or the legacy status bubble) — only one exists at a time.\n const bubbleRef = useRef<HTMLDivElement | null>(null)\n const [imageReady, setImageReady] = useState(false)\n const [hovered, setHovered] = useState(false)\n // Multi-session bubble stack: collapsed by default (only the display\n // session's bubble + a '+N' badge), expanded on stack hover (peek) or by\n // tapping the badge (pinned, for touch). The display session's bubble\n // anchors the bottom of the stack and never moves when extras open above\n // it, so the pointer target cannot flicker.\n const [stackPeek, setStackPeek] = useState(false)\n const [stackPinned, setStackPinned] = useState(false)\n const [renaming, setRenaming] = useState(false)\n const [panelAbove, setPanelAbove] = useState(false)\n // Extra margin-bottom for the above-panel so it stacks clear of the\n // bubbles instead of overlapping them (both anchor at the sprite's top).\n const [panelLift, setPanelLift] = useState(0)\n const [nameDraft, setNameDraft] = useState('')\n // Explicit IME composition tracking: some input methods (WeChat IME on\n // Windows) report keydowns with isComposing === false mid-composition, so\n // the native flag alone is not a safe submit/cancel guard (#303).\n const composingRef = useRef(false)\n const [dragPos, setDragPos] = useState<{ right: number; bottom: number } | null>(null)\n const dragRef = useRef<{ startX: number; startY: number; right: number; bottom: number } | null>(null)\n const hideTimerRef = useRef<number | null>(null)\n const frameRef = useRef<{ track: PetAnimation | null; index: number; elapsed: number }>({\n track: null,\n index: 0,\n elapsed: 0,\n })\n\n const cell = definition.cell\n const columns = definition.columns\n const rows = definition.rows\n const tracks = definition.tracks\n const sequences = definition.sequences\n // Hover-panel chrome from the pet's voice pack (pet-center M4, issue\n // #677): every slot falls back to the i18n dictionary when unset. Stat\n // formats carry {rank}/{n}/{points} placeholders the host validated.\n const panel = definition.panel\n const panelLabel = (slot: 'feed' | 'rename' | 'hide' | 'confirm', i18n: string): string =>\n panel?.labels?.[slot] ?? i18n\n const panelStat = (\n slot: 'rank' | 'treats' | 'points',\n i18nKey: 'pet.rank' | 'pet.treats' | 'pet.points',\n values: Record<string, string | number>,\n ): string => {\n const format = panel?.stats?.[slot] ?? props.t(i18nKey, values)\n if (panel?.stats?.[slot] === undefined) return format\n // The host whitelists {rank}/{n}/{points} in every stat slot, so a pack\n // format may reference any of them; substitute all three live values\n // (the slot's own value plus the siblings) instead of only the slot's.\n const all: Record<string, string | number> = {\n rank: snapshot?.affinity.rank ?? '?',\n n: snapshot?.treats.stocked ?? 0,\n points: snapshot?.affinity.points ?? 0,\n }\n let text = format\n for (const [name, value] of Object.entries(all)) text = text.replaceAll('{' + name + '}', String(value))\n return text\n }\n const panelShows = (action: 'feed' | 'rename' | 'hide'): boolean =>\n panel?.actions === undefined || panel.actions.includes(action)\n\n // Load the atlas once; the definition carries the authoritative per-row\n // frame counts and per-track durations, so nothing else is fetched. A\n // custom visual (pet-center M3) replaces the atlas entirely.\n useEffect(() => {\n if (props.visual !== undefined) return\n let cancelled = false\n const img = new Image()\n img.onload = () => {\n if (!cancelled) setImageReady(true)\n }\n img.src = definition.atlasUrl\n return () => {\n cancelled = true\n img.onload = null\n }\n }, [definition.atlasUrl, props.visual])\n\n // Frame loop: advance the current track and write background-position.\n // Offsets must be in SCALED coordinates (background-position applies to the\n // scaled background image), so the current sprite scale rides a ref that\n // the loop reads every tick. Under prefers-reduced-motion the sprite holds\n // its track's first frame instead of animating (presentation-only; the\n // animation state machine is untouched).\n const spriteScale = display.size / cell.height\n const phase = snapshot?.phase ?? 'idle'\n const animation = snapshot?.animation ?? 'idle'\n const scaleRef = useRef(spriteScale)\n scaleRef.current = spriteScale\n useEffect(() => {\n if (props.visual !== undefined) return\n const reduceMotion = typeof window !== 'undefined'\n && window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches === true\n const sequence = animation === animationForPhase(phase) ? sequences?.[phase] : undefined\n // Sequence state hoisted into the effect scope: the cumulative duration\n // table and each item's trimmed track would otherwise be recomputed every\n // tick (map/reduce plus two slices per frame), the same waste the\n // single-track branch below avoids.\n const timeline = sequence === undefined ? undefined : createSequenceTimeline(sequence, tracks)\n const sequenceItems = sequence === undefined ? undefined : new Map(\n sequence.map(itemAnimation => {\n const itemRow = rowOfTrack(itemAnimation)\n return [itemAnimation, {\n row: itemRow,\n track: trimTrack(tracks[itemAnimation], rows[itemRow] ?? tracks[itemAnimation].frames.length),\n }]\n }),\n )\n const leadAnimation = sequence?.[0] ?? animation\n const row = rowOfTrack(leadAnimation)\n const track = trimTrack(tracks[leadAnimation], rows[row] ?? tracks[leadAnimation].frames.length)\n // Paint one static sprite frame up front either way, so the pet is never\n // blank while the loop heat-up runs.\n const leadCol = track.frames[0]!\n const lead = framePosition(cell, row, leadCol, scaleRef.current)\n let lastPosStr = lead.x + 'px ' + lead.y + 'px'\n if (spriteRef.current !== null) {\n spriteRef.current.style.backgroundPosition = lastPosStr\n }\n if (reduceMotion) return\n let raf = 0\n let last = performance.now()\n let sequenceElapsed = 0\n const tick = (ts: number): void => {\n const delta = ts - last\n last = ts\n if (timeline !== undefined && sequenceItems !== undefined) {\n sequenceElapsed += delta\n const current = timeline.frameAt(sequenceElapsed)\n const item = sequenceItems.get(current.animation)!\n const col = item.track.frames[current.frameIndex]!\n const pos = framePosition(cell, item.row, col, scaleRef.current)\n const posStr = pos.x + 'px ' + pos.y + 'px'\n if (posStr !== lastPosStr) {\n lastPosStr = posStr\n if (spriteRef.current !== null) {\n spriteRef.current.style.backgroundPosition = posStr\n }\n }\n raf = requestAnimationFrame(tick)\n return\n }\n // row/track come from the effect scope: they were computed once above\n // and this effect re-runs when animation/tracks/rows change, so the\n // per-frame recompute (trimTrack slices fresh arrays) is pure waste.\n const st = frameRef.current\n if (st.track !== animation) {\n st.track = animation\n st.index = 0\n st.elapsed = 0\n }\n st.elapsed += delta\n const maxIndex = track.frames.length - 1\n while (st.elapsed >= (track.durations[st.index] ?? 0) && st.index < maxIndex) {\n st.elapsed -= track.durations[st.index] ?? 0\n st.index += 1\n }\n if (st.elapsed >= (track.durations[st.index] ?? 0)) {\n if (track.loop) {\n st.elapsed = 0\n st.index = 0\n } else {\n st.index = maxIndex // hold the final frame; the host switches tracks\n }\n }\n const col = track.frames[st.index]!\n const pos = framePosition(cell, row, col, scaleRef.current)\n const posStr = pos.x + 'px ' + pos.y + 'px'\n if (posStr !== lastPosStr) {\n lastPosStr = posStr\n if (spriteRef.current !== null) {\n spriteRef.current.style.backgroundPosition = posStr\n }\n }\n raf = requestAnimationFrame(tick)\n }\n raf = requestAnimationFrame(tick)\n return () => cancelAnimationFrame(raf)\n }, [animation, phase, cell, columns, rows, tracks, sequences, props.visual])\n\n // Auto-clear the feedback bubble after its CSS animation. The callback\n // rides a ref so re-renders never reset the timer: the 2s poll rebuilds\n // `props` every tick, and depending on it would starve the timeout.\n const feedbackDoneRef = useRef(props.onFeedbackDone)\n feedbackDoneRef.current = props.onFeedbackDone\n useEffect(() => {\n if (feedback === null) return\n const timer = window.setTimeout(() => feedbackDoneRef.current(), 2600)\n return () => window.clearTimeout(timer)\n }, [feedback])\n\n // Dragging: pointer events on the sprite; position is right/bottom based.\n // `draggedRef` records whether the pointer actually moved, so the browser's\n // trailing click (fired after pointerup) does not pet the sprite.\n const draggedRef = useRef(false)\n const clearHideTimer = (): void => {\n if (hideTimerRef.current !== null) {\n window.clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n }\n\n // Clear any pending auto-hide timer on unmount: a stray callback after\n // teardown reads window through react-dom and failed CI runs with\n // \"window is not defined\" (slow-runner timing, PetSprite.test.tsx).\n useEffect(() => () => clearHideTimer(), [])\n\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>): void => {\n if (props.dragDisabled === true) return\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) {\n if (!draggedRef.current) props.onDraggingChange?.(true)\n draggedRef.current = true\n }\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 (draggedRef.current) props.onDraggingChange?.(false)\n if (dragPos !== null) props.onDragEnd(dragPos.right, dragPos.bottom)\n }\n\n const pos = dragPos ?? { right: display.right, bottom: display.bottom }\n const spriteWidth = Math.round(cell.width * spriteScale)\n const spriteHeight = Math.round(cell.height * spriteScale)\n\n // Concurrent sessions share one bubble slot: only the display session\n // speaks by default, and the rest hide behind a '+N' badge until the stack\n // is hovered/pinned open. The legacy single 'bubble' is the fallback when\n // the host serves no per-session list. The hover panel normally sits below\n // the sprite, so the bubbles stay visible and clickable — no region swap.\n const sessionBubbles = snapshot?.sessions ?? []\n const stackOpen = stackPeek || stackPinned\n const collapsed = !stackOpen && sessionBubbles.length > 1\n const visibleSessions = collapsed ? sessionBubbles.slice(0, 1) : sessionBubbles\n const statusBubble = feedback === null && sessionBubbles.length === 0\n ? snapshot?.bubble\n : undefined\n // The freshest plugin-authored announcement (dsh-usage linkage): a\n // dedicated, specially styled bubble above the session stack. The host\n // already TTL-filters; this client-side check covers the last poll tick.\n const announcement = snapshot?.announcement\n const usageAnnouncement = feedback === null && announcement !== undefined && announcementFresh(announcement, Date.now())\n ? announcement\n : undefined\n // Each session's inner whisper (碎碎念) rides its own bubble — short\n // inner-voice copy woken by that session's activity, never the model's or\n // another session's. Instead of a second bubble of its own, a fresh\n // whisper takes over its session's bubble and re-tints it, so the pet\n // never wears two voices at once. Interaction feedback takes over the\n // whole bubble area while it plays, so whispers yield to it like status\n // copy.\n const bubblePresent = feedback !== null || sessionBubbles.length > 0 || statusBubble !== undefined || usageAnnouncement !== undefined\n const displayName = snapshot?.name ?? definition.displayName\n // The host-served status decoration (M5, #567); absent = text-only bubbles.\n const decoration = snapshot?.decoration\n\n // A settled session list can no longer stay pinned open.\n useEffect(() => {\n if (sessionBubbles.length <= 1) setStackPinned(false)\n }, [sessionBubbles.length])\n\n useLayoutEffect(() => {\n if (!hovered) {\n setPanelAbove(false)\n setPanelLift(0)\n return\n }\n const updatePanelPlacement = (): void => {\n const sprite = spriteRef.current\n const panel = panelRef.current\n if (sprite === null || panel === null) return\n const availableBelow = window.innerHeight - sprite.getBoundingClientRect().bottom\n const above = availableBelow < panel.getBoundingClientRect().height + 8\n setPanelAbove(above)\n // The fallback above-placement shares the sprite's top edge with the\n // bubble(s); lift the panel by the bubble area's height so the two\n // never overlap (8px base gap + 6px clearance above the top bubble).\n const bubbleHeight = above ? bubbleRef.current?.getBoundingClientRect().height ?? 0 : 0\n setPanelLift(bubbleHeight > 0 ? Math.ceil(bubbleHeight) + 14 : 0)\n }\n updatePanelPlacement()\n window.addEventListener('resize', updatePanelPlacement)\n return () => window.removeEventListener('resize', updatePanelPlacement)\n }, [hovered, renaming, pos.right, pos.bottom, display.size, bubblePresent, sessionBubbles.length, stackOpen, feedback])\n\n const float = (\n <div\n ref={floatRef}\n className={styles.float}\n style={{ right: pos.right, bottom: pos.bottom, zIndex: 2147483000 }}\n onPointerEnter={() => {\n clearHideTimer()\n setHovered(true)\n }}\n onPointerLeave={(e) => {\n // The panel renders OUTSIDE the container's box (absolute, below\n // the sprite), so moving onto it fires pointerleave on the container.\n // Treat a target still inside the container's DOM (the overflowed\n // panel) as \"still hovering\"; otherwise give the pointer a short\n // grace period to reach the panel across the gap below the sprite.\n // The bridge ('.panel::after') keeps the pointer inside the hit\n // area, and the grace period covers a slow mouse crossing the\n // remaining sliver.\n const next = e.relatedTarget\n if (next instanceof Node && floatRef.current?.contains(next)) return\n // Never auto-hide while the rename box is open: moving the pointer\n // onto an IME candidate window (an OS-level window outside the\n // webview) fires pointerleave, and unmounting the input mid-IME-\n // composition crashes some input methods / the renderer (#303).\n if (renaming) return\n clearHideTimer()\n hideTimerRef.current = window.setTimeout(() => setHovered(false), 300)\n }}\n >\n <div\n className={styles.spriteWrap}\n style={{ width: spriteWidth, height: spriteHeight }}\n >\n <div\n ref={spriteRef}\n className={styles.sprite}\n style={{\n width: spriteWidth,\n height: spriteHeight,\n ...(props.visual === undefined\n ? {\n backgroundImage: imageReady ? 'url(' + definition.atlasUrl + ')' : undefined,\n backgroundSize: (cell.width * columns * spriteScale) + 'px ' + (cell.height * (definition.atlasRows ?? rows.length) * spriteScale) + 'px',\n backgroundRepeat: 'no-repeat',\n backgroundPosition: '0 0',\n }\n : {}),\n cursor: dragRef.current === null ? 'grab' : 'grabbing',\n }}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onClick={(e) => {\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 if (props.onGameplayTap !== undefined && spriteRef.current !== null) {\n const rect = spriteRef.current.getBoundingClientRect()\n if (rect.width > 0 && rect.height > 0) {\n props.onGameplayTap((e.clientX - rect.left) / rect.width, (e.clientY - rect.top) / rect.height)\n }\n }\n props.onPet()\n }}\n role=\"button\"\n aria-label={definition.displayName}\n >\n {props.visual}\n </div>\n </div>\n {props.hud}\n {feedback !== null && (\n <div key={feedback.at} ref={bubbleRef} className={clsx(styles.bubble, feedback.kind === 'feed' ? styles.bubbleFeed : styles.bubblePet)}>\n {feedback.text}\n </div>\n )}\n {feedback === null && (sessionBubbles.length > 0 || statusBubble !== undefined || usageAnnouncement !== undefined) && (\n <div\n ref={bubbleRef}\n className={styles.bubbleStack}\n onPointerEnter={() => setStackPeek(true)}\n onPointerLeave={() => setStackPeek(false)}\n >\n {visibleSessions.map((session, index) => {\n // A session's whisper rides ITS OWN bubble (the stack lead when\n // the current session has one, any bubble when the stack is\n // expanded). The key swap restarts the entrance animation so the\n // mood change reads as the bubble re-speaking.\n const speaksWhisper = session.whisper !== undefined\n const bubble = (\n <button\n key={speaksWhisper ? 'whisper:' + session.whisper : session.sessionId}\n type=\"button\"\n className={clsx(\n styles.bubble,\n styles.bubbleStatus,\n styles.bubbleClickable,\n speaksWhisper && styles.bubbleWhisper,\n )}\n title={props.t('pet.openSessionHint')}\n onClick={() => { props.onOpenSession(session.sessionId) }}\n >\n {index === 0 && !speaksWhisper && decoration !== undefined && (\n <StatusOrnament decoration={decoration} phase={phase} />\n )}\n {session.whisper ?? session.bubble}\n </button>\n )\n // The primary bubble carries the '+N' badge while other sessions\n // hide behind it; the badge toggles the pinned (touch) expansion.\n if (index !== 0 || sessionBubbles.length <= 1) return bubble\n return (\n <span key=\"primary\" className={styles.bubbleAnchor}>\n {bubble}\n <button\n type=\"button\"\n className={styles.bubbleMore}\n title={stackOpen\n ? props.t('pet.collapseSessions')\n : props.t('pet.moreSessions', { n: sessionBubbles.length - 1 })}\n aria-label={stackOpen\n ? props.t('pet.collapseSessions')\n : props.t('pet.moreSessions', { n: sessionBubbles.length - 1 })}\n aria-expanded={stackOpen}\n onClick={(e) => {\n e.stopPropagation()\n setStackPinned(open => !open)\n }}\n >\n {stackOpen ? '×' : '+' + String(sessionBubbles.length - 1)}\n </button>\n </span>\n )\n })}\n {sessionBubbles.length === 0 && statusBubble !== undefined && (\n <div\n key=\"status\"\n className={clsx(styles.bubble, styles.bubbleStatus)}\n role=\"status\"\n aria-live=\"polite\"\n >\n {decoration !== undefined && (\n <StatusOrnament decoration={decoration} phase={phase} />\n )}\n {statusBubble}\n </div>\n )}\n {usageAnnouncement !== undefined && <UsageAnnouncementBubble announcement={usageAnnouncement} />}\n </div>\n )}\n {hovered && dragRef.current === null && (\n <div\n ref={panelRef}\n className={clsx(styles.panel, panelAbove && styles.panelAbove)}\n data-placement={panelAbove ? 'above' : 'below'}\n style={panelAbove && panelLift > 0\n ? ({ marginBottom: panelLift } as CSSProperties)\n : undefined}\n onPointerEnter={() => {\n // Reaching the panel (or its bridge) must cancel any hide timer\n // the container's pointerleave may have armed while the pointer\n // crossed the sliver between the sprite and the panel.\n clearHideTimer()\n }}\n >\n {renaming ? (\n <div className={styles.renameRow}>\n <input\n className={styles.nameInput}\n value={nameDraft}\n maxLength={20}\n placeholder={props.t('pet.namePlaceholder')}\n autoFocus\n onChange={(e) => setNameDraft(e.target.value)}\n onCompositionStart={() => { composingRef.current = true }}\n onCompositionEnd={() => { composingRef.current = false }}\n onKeyDown={(e) => {\n // While an IME composition is active (e.g. selecting a\n // Chinese candidate), Enter/Escape keydowns belong to the\n // input method: ignore them so candidate selection can\n // neither submit the draft nor close the rename box. The\n // explicit ref and the 'Process' key cover IMEs that mark\n // composition keydowns with isComposing === false (#303).\n if (composingRef.current || e.nativeEvent.isComposing || e.key === 'Process') return\n if (e.key === 'Enter') {\n const trimmed = nameDraft.trim()\n if (trimmed !== '') {\n props.onRename(trimmed)\n setRenaming(false)\n }\n } else if (e.key === 'Escape') {\n setRenaming(false)\n }\n }}\n />\n <button\n type=\"button\"\n className={styles.action}\n onClick={() => {\n const trimmed = nameDraft.trim()\n if (trimmed !== '') {\n props.onRename(trimmed)\n setRenaming(false)\n }\n }}\n >\n {panelLabel('confirm', props.t('pet.confirm'))}\n </button>\n </div>\n ) : (\n <>\n <div className={styles.rankRow}>\n <span className={styles.nameCell}>{displayName}</span>\n <span className={styles.statRank}>\n {(() => {\n const rawRank = snapshot?.affinity.rank ?? '?'\n const rankKey = `pet.rank.name.${rawRank}`\n const localized = props.t(rankKey as any)\n const rankName = localized !== rankKey ? localized : rawRank\n return panelStat('rank', 'pet.rank', { rank: rankName })\n })()}\n </span>\n </div>\n <div className={styles.rankRow}>\n <span className={styles.statTreats}>{panelStat('treats', 'pet.treats', { n: snapshot?.treats.stocked ?? 0 })}</span>\n <span className={styles.statPoints}>{panelStat('points', 'pet.points', { points: snapshot?.affinity.points ?? 0 })}</span>\n </div>\n <div className={styles.actions}>\n {panelShows('feed') && (\n <button type=\"button\" className={styles.action} onClick={props.onFeed}>\n {panelLabel('feed', props.t('pet.feed'))}\n </button>\n )}\n {panelShows('rename') && (\n <button\n type=\"button\"\n className={styles.action}\n onClick={() => {\n // Cancel any pending hide so the rename box cannot\n // unmount right as the user starts typing (#303).\n clearHideTimer()\n setNameDraft(displayName)\n setRenaming(true)\n }}\n >\n {panelLabel('rename', props.t('pet.rename'))}\n </button>\n )}\n {panelShows('hide') && (\n <button type=\"button\" className={styles.action} onClick={props.onHide}>\n {panelLabel('hide', props.t('pet.hide'))}\n </button>\n )}\n {props.onGameplayMenu !== undefined && (\n <button type=\"button\" className={styles.action} onClick={props.onGameplayMenu}>\n {props.t('pet.gameplay.menu')}\n </button>\n )}\n </div>\n </>\n )}\n </div>\n )}\n </div>\n )\n\n return createPortal(float, props.portalTarget ?? document.body)\n}\n","/**\n * Drag stream — bridges the chrome's pointer drag state onto the renderer\n * contract's { get, subscribe } shape, so renderers with a dedicated drag\n * track (frames2d) can follow the chrome-owned drag gesture. Dispatch is\n * change-only, mirroring the phase stream.\n * @module @linxin666/dsh-pet/client/drag-stream\n */\n\n/** The renderer-facing drag stream. */\nexport interface DragStream {\n /** Whether the chrome reports an active drag gesture. */\n get(): boolean\n /** Subscribe to drag state changes; returns the unsubscribe. */\n subscribe(listener: (dragging: boolean) => void): () => void\n /** Feed a fresh drag state; no-op when unchanged. */\n push(dragging: boolean): void\n}\n\n/** Create the stream (one per pet activation, owned by the renderer switch). */\nexport function createDragStream(): DragStream {\n let current = false\n const listeners = new Set<(dragging: boolean) => void>()\n return {\n get: () => current,\n subscribe(listener) {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n },\n push(dragging) {\n if (dragging === current) return\n current = dragging\n for (const listener of [...listeners]) listener(dragging)\n },\n }\n}\n","/**\n * Renderer registry — dispatches a manifest's renderer kind to its\n * implementation (pet-center M2 P4, issue #623). Unknown kinds never blank\n * the pet: a fallback card names the problem and reports the kinds this\n * build actually supports.\n * @module @linxin666/dsh-pet/client/renderers/registry\n */\n\nimport type { PetRenderer, PetRendererContext, PetRendererHandle } from '../../contracts/renderer.ts'\n\n/** Renderer dispatch table. */\nexport class RendererRegistry {\n private readonly renderers = new Map<string, PetRenderer>()\n\n /** Register one renderer implementation (id wins on re-register). */\n register(renderer: PetRenderer): void {\n this.renderers.set(renderer.id, renderer)\n }\n\n /** Whether a renderer kind is available in this build. */\n has(id: string): boolean {\n return this.renderers.has(id)\n }\n\n /** The registered renderer kinds (for diagnostics). */\n kinds(): string[] {\n return [...this.renderers.keys()].sort()\n }\n\n /** Remove every registration (tests; the client index registers once). */\n clear(): void {\n this.renderers.clear()\n }\n\n /**\n * Mount a renderer for one activation. An unknown kind renders a clear\n * diagnostic card into the container instead of failing silently.\n */\n mount(kind: string, ctx: PetRendererContext, config: unknown): PetRendererHandle {\n const renderer = this.renderers.get(kind)\n if (renderer === undefined) {\n const note = document.createElement('div')\n note.dataset.dshPetRendererFallback = kind\n note.textContent = 'Pet renderer \"' + kind + '\" is not available in this build (supported: ' + this.kinds().join(', ') + ').'\n ctx.container.appendChild(note)\n ctx.onCleanup(() => note.remove())\n return { dispose: () => note.remove() }\n }\n return renderer.mount(ctx, renderer.validateConfig(config))\n }\n}\n\n/**\n * The plugin-wide renderer registry. The client entry registers the\n * built-in renderers at apply time; the renderer switch and the live2d\n * bridge dispatch through this instance.\n */\nexport const defaultPetRendererRegistry = new RendererRegistry()\n","/**\n * Phase stream — bridges the polled host snapshots onto the renderer\n * contract's { get, subscribe } shape (pet-center M2 P4, issue #623). The\n * existing poll loop pushes each snapshot's ActivityPhase; subscribers are\n * dispatched on CHANGE only (phase transitions are sparse — done/failed hold\n * a timed window before falling back to idle — and renderers like Live2D pay\n * per transition, not per tick).\n * @module @linxin666/dsh-pet/client/phase-stream\n */\n\nimport type { ActivityPhase } from '../state.ts'\n\n/** The renderer-facing phase stream. */\nexport interface PhaseStream {\n /** The latest pushed phase. */\n get(): ActivityPhase\n /** Subscribe to phase changes; returns the unsubscribe. */\n subscribe(listener: (phase: ActivityPhase) => void): () => void\n /** Feed a fresh snapshot phase; no-op when unchanged. */\n push(phase: ActivityPhase): void\n}\n\n/** Create the stream (one per pet entry lifetime, owned by the plugin body). */\nexport function createPhaseStream(initial: ActivityPhase = 'idle'): PhaseStream {\n let current = initial\n const listeners = new Set<(phase: ActivityPhase) => void>()\n return {\n get: () => current,\n subscribe(listener) {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n },\n push(phase) {\n if (phase === current) return\n current = phase\n for (const listener of [...listeners]) listener(phase)\n },\n }\n}\n","/**\n * Live2D visual mount (pet-center M3) — the React bridge between the pet\n * center chrome and the imperative live2d renderer. The bridge owns the\n * contract context (asset base, phase stream, interaction write-back,\n * activation cleanups), feeds the polled phase into the stream, forwards\n * sub-4px taps as hit-test coordinates, and renders the localized error\n * card when the renderer reports a fatal boot failure.\n * @module @linxin666/dsh-pet/client/renderers/live2d/Live2dVisualMount\n */\n\nimport { useEffect, useRef, useState, type ReactElement } from 'react'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDefinition } from '../../../registry.ts'\nimport type { ActivityPhase } from '../../../state.ts'\nimport { createPhaseStream, type PhaseStream } from '../../phase-stream.ts'\nimport type { PetRendererContext } from '../../../contracts/renderer.ts'\nimport { defaultPetRendererRegistry } from '../registry.ts'\nimport type { Live2dErrorCode, Live2dRendererHandle } from '../live2d.ts'\nimport type { NS } from '../../locales.ts'\n\n/** Mount the live2d renderer as the sprite's visual (inside the chrome). */\nexport function Live2dVisualMount(props: {\n definition: PetDefinition\n phase: ActivityPhase\n onPet: () => void\n t: PropsLocale<typeof NS>['t']\n}): ReactElement {\n const containerRef = useRef<HTMLDivElement | null>(null)\n const streamRef = useRef<PhaseStream | null>(null)\n const handleRef = useRef<Live2dRendererHandle | null>(null)\n const downRef = useRef<{ x: number; y: number } | null>(null)\n const [error, setError] = useState<Live2dErrorCode | null>(null)\n\n // One activation per pet definition: build the contract context and mount.\n useEffect(() => {\n setError(null)\n const container = containerRef.current\n const live2d = props.definition.live2d\n if (container === null || live2d === undefined) return undefined\n streamRef.current ??= createPhaseStream(props.phase)\n const cleanups: (() => void)[] = []\n const ctx: PetRendererContext = {\n petId: props.definition.id,\n assetBase: '/pet/' + encodeURIComponent(props.definition.id),\n container,\n phase: streamRef.current,\n interact: props.onPet,\n onCleanup: (fn) => { cleanups.push(fn) },\n }\n let handle: Live2dRendererHandle\n try {\n handle = defaultPetRendererRegistry.mount('live2d', ctx, live2d) as Live2dRendererHandle\n } catch {\n setError('load-failed')\n return () => { for (const fn of cleanups.splice(0)) fn() }\n }\n handleRef.current = handle\n handle.onError?.(setError)\n return () => {\n handleRef.current = null\n for (const fn of cleanups.splice(0)) fn()\n handle.dispose()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one activation per pet identity\n }, [props.definition])\n\n // Feed the polled phase into the activation's stream (change-only).\n useEffect(() => {\n streamRef.current?.push(props.phase)\n }, [props.phase])\n\n return (\n <div\n ref={containerRef}\n data-dsh-pet-live2d={props.definition.id}\n style={{ width: '100%', height: '100%' }}\n onPointerDown={(e) => { downRef.current = { x: e.clientX, y: e.clientY } }}\n onPointerUp={(e) => {\n const down = downRef.current\n downRef.current = null\n if (down === null) return\n // A moved pointer is a drag (the chrome owns it), not a tap.\n if (Math.abs(e.clientX - down.x) > 4 || Math.abs(e.clientY - down.y) > 4) return\n const rect = e.currentTarget.getBoundingClientRect()\n handleRef.current?.tap(e.clientX - rect.left, e.clientY - rect.top)\n }}\n >\n {error !== null && (\n <span data-dsh-pet-live2d-error={error}>\n {error === 'core-missing'\n ? props.t('pet.live2d.core-missing')\n : error === 'vendor-missing'\n ? props.t('pet.live2d.vendor-missing')\n : props.t('pet.live2d.load-failed')}\n </span>\n )}\n </div>\n )\n}\n","/**\n * Frames2d visual mount — the React bridge between the pet center chrome\n * and the imperative frames2d renderer, mirroring the live2d mount. The\n * bridge owns the contract context (asset base, phase stream, interaction\n * write-back, activation cleanups), feeds the polled phase into the stream,\n * forwards the chrome's drag gesture onto the conventional 'drag' track\n * (when the pet declares one), and renders the localized fallback card when\n * the served config is invalid.\n * @module @linxin666/dsh-pet/client/renderers/Frames2dVisualMount\n */\n\nimport { useEffect, useRef, useState, type ReactElement } from 'react'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDefinition } from '../../registry.ts'\nimport type { ActivityPhase } from '../../state.ts'\nimport { createPhaseStream, type PhaseStream } from '../phase-stream.ts'\nimport type { DragStream } from '../drag-stream.ts'\nimport type { PetRendererContext } from '../../contracts/renderer.ts'\nimport { defaultPetRendererRegistry } from './registry.ts'\nimport type { Frames2dRendererHandle } from './frames2d.ts'\nimport type { GameplayBus } from '../gameplay-hud.tsx'\nimport type { NS } from '../locales.ts'\n\n/** Mount the frames2d renderer as the sprite's visual (inside the chrome). */\nexport function Frames2dVisualMount(props: {\n definition: PetDefinition\n phase: ActivityPhase\n onPet: () => void\n /** Chrome drag gesture stream (the renderer switch owns it). */\n drag: DragStream\n /** Gameplay coordination bus; the mount registers its track override. */\n bus?: GameplayBus\n t: PropsLocale<typeof NS>['t']\n}): ReactElement {\n const containerRef = useRef<HTMLDivElement | null>(null)\n const streamRef = useRef<PhaseStream | null>(null)\n const handleRef = useRef<Frames2dRendererHandle | null>(null)\n const [invalid, setInvalid] = useState(false)\n\n // One activation per pet definition: build the contract context and mount.\n useEffect(() => {\n setInvalid(false)\n const container = containerRef.current\n const frames2d = props.definition.frames2d\n if (container === null || frames2d === undefined) return undefined\n streamRef.current ??= createPhaseStream(props.phase)\n const cleanups: (() => void)[] = []\n const ctx: PetRendererContext = {\n petId: props.definition.id,\n assetBase: '/pet/' + encodeURIComponent(props.definition.id),\n container,\n phase: streamRef.current,\n interact: props.onPet,\n onCleanup: (fn) => { cleanups.push(fn) },\n }\n let handle: Frames2dRendererHandle\n try {\n handle = defaultPetRendererRegistry.mount('frames2d', ctx, frames2d) as Frames2dRendererHandle\n } catch {\n setInvalid(true)\n return () => { for (const fn of cleanups.splice(0)) fn() }\n }\n handleRef.current = handle\n // The gameplay HUD steers one shared override slot through the bus;\n // mode rules (work blocks drag, sleep wakes on it) keep the two\n // producers from fighting over the slot.\n if (props.bus !== undefined) {\n const gameplayBus = props.bus\n gameplayBus.setTrack = (track) => { handleRef.current?.setState(track) }\n gameplayBus.setIdleTrack = (track) => { handleRef.current?.setIdleTrack(track) }\n cleanups.push(() => {\n gameplayBus.setTrack = undefined\n gameplayBus.setIdleTrack = undefined\n })\n }\n // The drag gesture drives the conventional 'drag' track when declared.\n // On release, a declared gameplay.dragEndState (miku: standup) plays\n // once; its fallback auto-releases the override back to the phase map.\n const dragTrack = props.definition.gameplay?.dragState ?? (frames2d.tracks.drag === undefined ? undefined : 'drag')\n const offDrag = props.drag.subscribe((dragging) => {\n if (dragTrack === undefined) return\n if (dragging) {\n handle.setState(dragTrack)\n return\n }\n handle.setState(props.definition.gameplay?.dragEndState)\n })\n cleanups.push(offDrag)\n return () => {\n handleRef.current = null\n for (const fn of cleanups.splice(0)) fn()\n handle.dispose()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one activation per pet identity\n }, [props.definition])\n\n // Feed the polled phase into the activation's stream (change-only).\n useEffect(() => {\n streamRef.current?.push(props.phase)\n }, [props.phase])\n\n return (\n <div\n ref={containerRef}\n data-dsh-pet-frames2d={props.definition.id}\n style={{ width: '100%', height: '100%', pointerEvents: 'none' }}\n >\n {invalid && (\n <span data-dsh-pet-frames2d-error=\"invalid-config\">\n {props.t('pet.renderer.unavailable', { renderer: 'frames2d' })}\n </span>\n )}\n </div>\n )\n}\n","/**\n * Renderer switch — the client dispatch seam of the pet center (issue #623,\n * milestone M2 P5 / M3). The pet's manifest picks the renderer: sprite2d\n * hands straight through to the sprite; live2d injects its visual INTO the\n * sprite chrome (the dock, bubbles and panel belong to the pet center, not\n * the renderer); a renderer this build cannot serve renders a clear\n * diagnostic card instead of blanking.\n * @module @linxin666/dsh-pet/client/renderers/PetRendererSwitch\n */\n\nimport { cloneElement, isValidElement, useRef, type ReactElement, type ReactNode } from 'react'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDefinition } from '../../registry.ts'\nimport type { ActivityPhase } from '../../state.ts'\nimport type { PetSpriteProps } from '../PetSprite.tsx'\nimport { createDragStream, type DragStream } from '../drag-stream.ts'\nimport type { GameplayBus } from '../gameplay-hud.tsx'\nimport { defaultPetRendererRegistry } from './registry.ts'\nimport { Live2dVisualMount } from './live2d/Live2dVisualMount.tsx'\nimport { Frames2dVisualMount } from './Frames2dVisualMount.tsx'\nimport type { NS } from '../locales.ts'\n\n/** Dispatch one pet definition to its renderer; unknown kinds get a card. */\nexport function PetRendererSwitch(props: {\n definition: PetDefinition\n /** Current activity phase (fed to renderer visuals). */\n phase: ActivityPhase\n /** The chrome's pet interaction (affinity write-back owner). */\n onPet: () => void\n /** External drag stream (gameplay HUD shares it); created locally otherwise. */\n drag?: DragStream\n /** Gameplay coordination bus forwarded to the frames2d visual mount. */\n bus?: GameplayBus\n t: PropsLocale<typeof NS>['t']\n children?: ReactNode\n}): ReactElement {\n const renderer = props.definition.renderer ?? 'sprite2d'\n // The frames2d drag stream lives one level up: the chrome (PetSprite)\n // reports the gesture through onDraggingChange, the visual subscribes.\n const dragRef = useRef<{ id: string; stream: ReturnType<typeof createDragStream> } | null>(null)\n if (dragRef.current === null || dragRef.current.id !== props.definition.id) {\n dragRef.current = { id: props.definition.id, stream: createDragStream() }\n }\n const drag = props.drag ?? dragRef.current.stream\n if (renderer === 'sprite2d') return <>{props.children}</>\n if (renderer === 'frames2d' && defaultPetRendererRegistry.has('frames2d') && isValidElement<PetSpriteProps>(props.children)) {\n const visual = (\n <Frames2dVisualMount\n definition={props.definition}\n phase={props.phase}\n onPet={props.onPet}\n drag={drag}\n {...(props.bus === undefined ? {} : { bus: props.bus })}\n t={props.t}\n />\n )\n return cloneElement(props.children, { visual, onDraggingChange: (dragging: boolean) => drag.push(dragging) })\n }\n if (renderer === 'live2d' && defaultPetRendererRegistry.has('live2d') && isValidElement<PetSpriteProps>(props.children)) {\n const visual = (\n <Live2dVisualMount\n definition={props.definition}\n phase={props.phase}\n onPet={props.onPet}\n t={props.t}\n />\n )\n return cloneElement(props.children, { visual })\n }\n return (\n <span data-dsh-pet-renderer-fallback={renderer}>\n {props.t('pet.renderer.unavailable', { renderer })}\n </span>\n )\n}\n","/**\n * Pet gameplay — the optional manifest 'gameplay' block and its host engine.\n * The block layers an opt-in mini-game over any frames2d pet: decaying stat\n * bars, currencies, a weighted idle director, touch zones, a work loop, a\n * sleep loop, passive income and a shop (issue: miku-pet generalization).\n *\n * Discipline split matches manifest-v2: STRUCTURE is fail-closed (types,\n * ranges, references into stats/tracks); the host engine is pure — every\n * verb takes an explicit clock and rng so tests stay deterministic. Decay,\n * passive income and sleep restore are lazy-settled on read (the treats.ts\n * discipline): the host runs no timers for gameplay.\n * @module @linxin666/dsh-pet/gameplay\n */\n\n/** One gameplay effect: add amount to a declared stat or a currency. */\nexport interface PetGameplayEffect {\n stat?: string\n currency?: string\n amount: number\n}\n\n/** One roll branch inside a touch zone; uncovered roll mass is a no-op. */\nexport interface PetGameplayTouchBranch {\n probability: number\n effects?: PetGameplayEffect[]\n /** Track played on hit (held for stateMs, then the renderer settles). */\n state?: string\n stateMs?: number\n /** Bubble phrase pool; one is picked on hit. */\n phrases?: string[]\n}\n\nexport interface PetGameplayTouchZone {\n name: string\n /** Vertical slice of the hit box (fractions, y0 < y1). */\n y0: number\n y1: number\n branches: PetGameplayTouchBranch[]\n}\n\nexport interface PetGameplayStatDef {\n max: number\n initial?: number\n decayPerMinute?: number\n /** Decay rate while the work mode is active (defaults to decayPerMinute). */\n workingDecayPerMinute?: number\n /** Extra decay rate while no session is active. */\n idleDecayPerMinute?: number\n}\n\nexport interface PetGameplayShopItem {\n id: string\n label: string\n /** Optional frame path (manifest-relative) shown as the item icon. */\n image?: string\n price: number\n currency: string\n effects?: PetGameplayEffect[]\n lottery?: {\n effects?: PetGameplayEffect[]\n /** Default currency the prize is paid in (tiers may override). */\n currency?: string\n tiers: { probability: number; prize: number; currency?: string }[]\n }\n}\n\n/** The validated manifest 'gameplay' block. */\nexport interface PetGameplayManifest {\n idleDirector?: {\n intervalMs: number\n maxMiss: number\n idleWeight: number\n acts: { track: string; weight: number; phrases?: string[] }[]\n }\n stats?: Record<string, PetGameplayStatDef>\n /** Click hit box inside the sprite box (fractions). */\n hitBox?: { x0: number; y0: number; x1: number; y1: number }\n touch?: {\n zones: PetGameplayTouchZone[]\n /** Plain-click effect while a touch animation holds (miku: mood +0..3). */\n clickBoost?: { stat: string; min: number; max: number }\n }\n work?: {\n state: string\n successState: string\n failState: string\n tickMs: number\n /** Hold time of the result track before the next round. */\n resultMs?: { success: number; fail: number }\n successProbability: number\n success?: { effects: PetGameplayEffect[] }\n fail?: { effects: PetGameplayEffect[] }\n }\n sleep?: {\n state: string\n wakeState?: string\n restore: { stat: string; amount: number; intervalMs: number }\n }\n passiveIncome?: { currency: string; amount: number; intervalMs: number }\n shop?: { state?: string; items: PetGameplayShopItem[] }\n /** Track played while the chrome reports dragging (default 'drag'). */\n dragState?: string\n /** Track played once when a drag ends (miku: standup), before settling. */\n dragEndState?: string\n}\n\n/* ------------------------------------------------------------------ *\n * Manifest parsing (fail-closed structure)\n * ------------------------------------------------------------------ */\n\nconst KEBAB = /^[a-z0-9][a-z0-9-]*$/\nconst MAX_STATS = 16\nconst MAX_ZONES = 8\nconst MAX_BRANCHES = 8\nconst MAX_ACTS = 16\nconst MAX_SHOP_ITEMS = 32\nconst MAX_LOTTERY_TIERS = 16\nconst MAX_PHRASES = 64\nconst PHRASE_MAX_LENGTH = 120\nconst STAT_VALUE_MAX = 1_000_000\nconst CURRENCY_MAX = 9_999_999\n\nconst KNOWN_GAMEPLAY = new Set(['idleDirector', 'stats', 'hitBox', 'touch', 'work', 'sleep', 'passiveIncome', 'shop', 'dragState', 'dragEndState'])\nconst KNOWN_STAT = new Set(['max', 'initial', 'decayPerMinute', 'workingDecayPerMinute', 'idleDecayPerMinute'])\nconst KNOWN_ZONE = new Set(['name', 'y0', 'y1', 'branches'])\nconst KNOWN_TOUCH = new Set(['zones', 'clickBoost'])\nconst KNOWN_BRANCH = new Set(['probability', 'effects', 'state', 'stateMs', 'phrases'])\nconst KNOWN_EFFECT = new Set(['stat', 'currency', 'amount'])\nconst KNOWN_WORK = new Set(['state', 'successState', 'failState', 'tickMs', 'resultMs', 'successProbability', 'success', 'fail'])\nconst KNOWN_SLEEP = new Set(['state', 'wakeState', 'restore'])\nconst KNOWN_SHOP_ITEM = new Set(['id', 'label', 'image', 'price', 'currency', 'effects', 'lottery'])\nconst KNOWN_LOTTERY = new Set(['effects', 'currency', 'tiers'])\nconst KNOWN_IDLE_DIRECTOR = new Set(['intervalMs', 'maxMiss', 'idleWeight', 'acts'])\nconst KNOWN_ACT = new Set(['track', 'weight', 'phrases'])\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction unknownKeys(source: Record<string, unknown>, known: Set<string>): string[] {\n return Object.keys(source).filter(key => !known.has(key))\n}\n\nexport interface GameplayParseHooks {\n /** State names the renderer can play (frames2d track ids). */\n stateNames: ReadonlySet<string>\n error: (message: string) => void\n}\n\nfunction validName(name: unknown, max = 32): name is string {\n return typeof name === 'string' && name.length <= max && KEBAB.test(name)\n}\n\nfunction intIn(value: unknown, min: number, max: number): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value >= min && value <= max\n}\n\nfunction numIn(value: unknown, min: number, max: number): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= min && value <= max\n}\n\nfunction parseEffects(raw: unknown, field: string, stats: Record<string, PetGameplayStatDef>, hooks: GameplayParseHooks): PetGameplayEffect[] | undefined {\n if (raw === undefined) return undefined\n if (!Array.isArray(raw) || raw.length === 0) {\n hooks.error(field + ' must be a non-empty array of effects')\n return undefined\n }\n const effects: PetGameplayEffect[] = []\n for (const entry of raw) {\n if (!isRecord(entry)) {\n hooks.error(field + ': every effect must be an object')\n continue\n }\n const extra = unknownKeys(entry, KNOWN_EFFECT)\n if (extra.length > 0) hooks.error(field + ': unknown effect field(s) ' + extra.map(k => JSON.stringify(k)).join(', '))\n const hasStat = typeof entry.stat === 'string'\n const hasCurrency = typeof entry.currency === 'string'\n if (hasStat === hasCurrency) {\n hooks.error(field + ': an effect needs exactly one of stat or currency')\n continue\n }\n if (!intIn(entry.amount, -STAT_VALUE_MAX, STAT_VALUE_MAX) || entry.amount === 0) {\n hooks.error(field + ': effect amount must be a non-zero integer within ±' + STAT_VALUE_MAX)\n continue\n }\n if (hasStat && stats[entry.stat as string] === undefined) {\n hooks.error(field + ': effect references undeclared stat ' + JSON.stringify(entry.stat))\n continue\n }\n if (hasCurrency && !validName(entry.currency, 24)) {\n hooks.error(field + ': effect currency must be a kebab id')\n continue\n }\n effects.push({\n ...(hasStat ? { stat: entry.stat as string } : {}),\n ...(hasCurrency ? { currency: entry.currency as string } : {}),\n amount: entry.amount as number,\n })\n }\n return effects.length === 0 ? undefined : effects\n}\n\nfunction parsePhrases(raw: unknown, field: string, hooks: GameplayParseHooks): string[] | undefined {\n if (raw === undefined) return undefined\n if (!Array.isArray(raw) || raw.length === 0 || raw.length > MAX_PHRASES\n || raw.some(line => typeof line !== 'string' || line.trim() === '' || line.length > PHRASE_MAX_LENGTH)) {\n hooks.error(field + ' must be 1..' + MAX_PHRASES + ' non-empty lines of at most ' + PHRASE_MAX_LENGTH + ' chars')\n return undefined\n }\n return raw as string[]\n}\n\nfunction parseStateRef(raw: unknown, field: string, hooks: GameplayParseHooks): string | undefined {\n if (raw === undefined) return undefined\n if (typeof raw !== 'string' || !hooks.stateNames.has(raw)) {\n hooks.error(field + ' must name a declared frames2d track')\n return undefined\n }\n return raw\n}\n\n/**\n * Validate the manifest 'gameplay' block (fail-closed). Only frames2d pets\n * may declare gameplay today: every state reference checks against the\n * declared track names.\n */\nexport function parseGameplayManifest(raw: unknown, hooks: GameplayParseHooks): PetGameplayManifest | undefined {\n const error = (message: string): void => hooks.error(message)\n if (!isRecord(raw)) {\n error('gameplay must be an object')\n return undefined\n }\n const extra = unknownKeys(raw, KNOWN_GAMEPLAY)\n if (extra.length > 0) error('gameplay: unknown field(s) ' + extra.map(k => JSON.stringify(k)).join(', '))\n let failed = false\n const fail = (message: string): void => { failed = true; error(message) }\n\n // --- stats (parsed first: effects reference them) ---\n const stats: Record<string, PetGameplayStatDef> = {}\n if (raw.stats !== undefined) {\n if (!isRecord(raw.stats)) fail('gameplay.stats must be an object keyed by stat id')\n else {\n const entries = Object.entries(raw.stats)\n if (entries.length > MAX_STATS) fail('gameplay.stats declares too many stats (max ' + MAX_STATS + ')')\n for (const [name, value] of entries) {\n if (!validName(name, 24)) {\n fail('gameplay.stats: invalid stat id ' + JSON.stringify(name))\n continue\n }\n if (!isRecord(value)) {\n fail('gameplay.stats.' + name + ' must be an object')\n continue\n }\n const statExtra = unknownKeys(value, KNOWN_STAT)\n if (statExtra.length > 0) fail('gameplay.stats.' + name + ': unknown field(s) ' + statExtra.map(k => JSON.stringify(k)).join(', '))\n if (!intIn(value.max, 1, STAT_VALUE_MAX)) {\n fail('gameplay.stats.' + name + '.max must be an integer in [1, ' + STAT_VALUE_MAX + ']')\n continue\n }\n const def: PetGameplayStatDef = { max: value.max }\n if (value.initial !== undefined) {\n if (!numIn(value.initial, 0, value.max)) fail('gameplay.stats.' + name + '.initial must be within [0, max]')\n else def.initial = value.initial\n }\n for (const key of ['decayPerMinute', 'workingDecayPerMinute', 'idleDecayPerMinute'] as const) {\n if (value[key] !== undefined) {\n if (!numIn(value[key], 0, 1000)) fail('gameplay.stats.' + name + '.' + key + ' must be a number in [0, 1000]')\n else def[key] = value[key] as number\n }\n }\n stats[name] = def\n }\n }\n }\n\n const block: PetGameplayManifest = {}\n if (Object.keys(stats).length > 0) block.stats = stats\n\n // --- idle director ---\n if (raw.idleDirector !== undefined) {\n if (!isRecord(raw.idleDirector) || !Array.isArray(raw.idleDirector.acts)) {\n fail('gameplay.idleDirector must be an object with an acts array')\n } else {\n const d = raw.idleDirector as Record<string, unknown> & { acts: unknown[] }\n const dExtra = unknownKeys(d, KNOWN_IDLE_DIRECTOR)\n if (dExtra.length > 0) fail('gameplay.idleDirector: unknown field(s) ' + dExtra.map(k => JSON.stringify(k)).join(', '))\n if (d.intervalMs !== undefined && !intIn(d.intervalMs, 1000, 60_000)) fail('gameplay.idleDirector.intervalMs must be an integer in [1000, 60000]')\n if (d.maxMiss !== undefined && !intIn(d.maxMiss, 0, 10)) fail('gameplay.idleDirector.maxMiss must be an integer in [0, 10]')\n if (d.idleWeight !== undefined && !intIn(d.idleWeight, 0, 10_000)) fail('gameplay.idleDirector.idleWeight must be an integer in [0, 10000]')\n if (d.acts.length === 0 || d.acts.length > MAX_ACTS) fail('gameplay.idleDirector.acts must declare 1..' + MAX_ACTS + ' acts')\n const acts: { track: string; weight: number; phrases?: string[] }[] = []\n for (const act of d.acts as unknown[]) {\n if (!isRecord(act) || !intIn(act.weight, 1, 10_000)) {\n fail('gameplay.idleDirector.acts entries need a weight integer in [1, 10000]')\n continue\n }\n const aExtra = unknownKeys(act, KNOWN_ACT)\n if (aExtra.length > 0) fail('gameplay.idleDirector.acts: unknown field(s) ' + aExtra.map(k => JSON.stringify(k)).join(', '))\n const track = parseStateRef(act.track, 'gameplay.idleDirector.acts.track', hooks)\n if (track === undefined) continue\n const entry: { track: string; weight: number; phrases?: string[] } = { track, weight: act.weight }\n const phrases = parsePhrases(act.phrases, 'gameplay.idleDirector.acts.phrases', hooks)\n if (phrases !== undefined) entry.phrases = phrases\n acts.push(entry)\n }\n if (acts.length > 0) {\n block.idleDirector = {\n intervalMs: intIn(d.intervalMs, 1000, 60_000) ? d.intervalMs as number : 5000,\n maxMiss: intIn(d.maxMiss, 0, 10) ? d.maxMiss as number : 2,\n idleWeight: intIn(d.idleWeight, 0, 10_000) ? d.idleWeight as number : 0,\n acts,\n }\n }\n }\n }\n\n // --- hit box ---\n if (raw.hitBox !== undefined) {\n const b = raw.hitBox\n if (!isRecord(b) || !numIn(b.x0, 0, 1) || !numIn(b.x1, 0, 1) || !numIn(b.y0, 0, 1) || !numIn(b.y1, 0, 1)\n || !(b.x0 < b.x1) || !(b.y0 < b.y1)) {\n fail('gameplay.hitBox must be { x0, y0, x1, y1 } fractions with x0 < x1 and y0 < y1')\n } else {\n block.hitBox = { x0: b.x0, y0: b.y0, x1: b.x1, y1: b.y1 }\n }\n }\n\n // --- touch zones ---\n if (raw.touch !== undefined) {\n if (!isRecord(raw.touch) || !Array.isArray(raw.touch.zones)) {\n fail('gameplay.touch must be an object with a zones array')\n } else {\n const tExtra = unknownKeys(raw.touch, KNOWN_TOUCH)\n if (tExtra.length > 0) fail('gameplay.touch: unknown field(s) ' + tExtra.map(k => JSON.stringify(k)).join(', '))\n let clickBoost: { stat: string; min: number; max: number } | undefined\n if (raw.touch.clickBoost !== undefined) {\n const cb = raw.touch.clickBoost\n if (!isRecord(cb) || typeof cb.stat !== 'string' || stats[cb.stat] === undefined\n || !intIn(cb.min, 0, 1000) || !intIn(cb.max, 0, 1000) || (cb.min as number) > (cb.max as number)) {\n fail('gameplay.touch.clickBoost must be { stat (declared), min, max } integers with 0 <= min <= max <= 1000')\n } else clickBoost = { stat: cb.stat, min: cb.min, max: cb.max }\n }\n const zones: PetGameplayTouchZone[] = []\n if (raw.touch.zones.length === 0 || raw.touch.zones.length > MAX_ZONES) fail('gameplay.touch.zones must declare 1..' + MAX_ZONES + ' zones')\n for (const zoneRaw of raw.touch.zones as unknown[]) {\n if (!isRecord(zoneRaw) || !validName(zoneRaw.name) || !numIn(zoneRaw.y0, 0, 1) || !numIn(zoneRaw.y1, 0, 1) || !(zoneRaw.y0 < zoneRaw.y1)) {\n fail('gameplay.touch.zones entries need a kebab name and 0 <= y0 < y1 <= 1')\n continue\n }\n const zExtra = unknownKeys(zoneRaw, KNOWN_ZONE)\n if (zExtra.length > 0) fail('gameplay.touch.' + zoneRaw.name + ': unknown field(s) ' + zExtra.map(k => JSON.stringify(k)).join(', '))\n if (!Array.isArray(zoneRaw.branches) || zoneRaw.branches.length === 0 || zoneRaw.branches.length > MAX_BRANCHES) {\n fail('gameplay.touch.' + zoneRaw.name + '.branches must declare 1..' + MAX_BRANCHES + ' branches')\n continue\n }\n let probabilitySum = 0\n const branches: PetGameplayTouchBranch[] = []\n for (const branchRaw of zoneRaw.branches as unknown[]) {\n if (!isRecord(branchRaw) || !numIn(branchRaw.probability, 0, 1) || branchRaw.probability === 0) {\n fail('gameplay.touch.' + zoneRaw.name + '.branches entries need a probability in (0, 1]')\n continue\n }\n const bExtra = unknownKeys(branchRaw, KNOWN_BRANCH)\n if (bExtra.length > 0) fail('gameplay.touch.' + zoneRaw.name + ': unknown branch field(s) ' + bExtra.map(k => JSON.stringify(k)).join(', '))\n probabilitySum += branchRaw.probability\n const branch: PetGameplayTouchBranch = { probability: branchRaw.probability }\n const effects = parseEffects(branchRaw.effects, 'gameplay.touch.' + zoneRaw.name + '.effects', stats, hooks)\n if (effects !== undefined) branch.effects = effects\n const state = parseStateRef(branchRaw.state, 'gameplay.touch.' + zoneRaw.name + '.state', hooks)\n if (state !== undefined) branch.state = state\n if (branchRaw.stateMs !== undefined) {\n if (!intIn(branchRaw.stateMs, 200, 10_000)) fail('gameplay.touch.' + zoneRaw.name + '.stateMs must be an integer in [200, 10000]')\n else branch.stateMs = branchRaw.stateMs\n }\n const phrases = parsePhrases(branchRaw.phrases, 'gameplay.touch.' + zoneRaw.name + '.phrases', hooks)\n if (phrases !== undefined) branch.phrases = phrases\n branches.push(branch)\n }\n if (probabilitySum > 1 + 1e-9) fail('gameplay.touch.' + zoneRaw.name + ': branch probabilities must sum to at most 1')\n if (branches.length > 0) zones.push({ name: zoneRaw.name, y0: zoneRaw.y0, y1: zoneRaw.y1, branches })\n }\n if (zones.length > 0 || clickBoost !== undefined) block.touch = { zones, ...(clickBoost === undefined ? {} : { clickBoost }) }\n }\n }\n\n // --- work ---\n if (raw.work !== undefined) {\n const w = raw.work\n if (!isRecord(w)) fail('gameplay.work must be an object')\n else {\n const wExtra = unknownKeys(w, KNOWN_WORK)\n if (wExtra.length > 0) fail('gameplay.work: unknown field(s) ' + wExtra.map(k => JSON.stringify(k)).join(', '))\n const state = parseStateRef(w.state, 'gameplay.work.state', hooks)\n const successState = parseStateRef(w.successState, 'gameplay.work.successState', hooks)\n const failState = parseStateRef(w.failState, 'gameplay.work.failState', hooks)\n if (!intIn(w.tickMs, 1000, 60_000)) fail('gameplay.work.tickMs must be an integer in [1000, 60000]')\n if (!numIn(w.successProbability, 0, 1)) fail('gameplay.work.successProbability must be a number in [0, 1]')\n if (state !== undefined && successState !== undefined && failState !== undefined\n && intIn(w.tickMs, 1000, 60_000) && numIn(w.successProbability, 0, 1)) {\n const work: NonNullable<PetGameplayManifest['work']> = {\n state, successState, failState,\n tickMs: w.tickMs,\n successProbability: w.successProbability,\n }\n if (w.resultMs !== undefined) {\n if (!isRecord(w.resultMs) || !intIn(w.resultMs.success, 200, 10_000) || !intIn(w.resultMs.fail, 200, 10_000)) {\n fail('gameplay.work.resultMs must be { success, fail } integers in [200, 10000]')\n } else work.resultMs = { success: w.resultMs.success, fail: w.resultMs.fail }\n }\n for (const key of ['success', 'fail'] as const) {\n if (w[key] !== undefined) {\n if (!isRecord(w[key])) fail('gameplay.work.' + key + ' must be an object { effects }')\n else {\n const effects = parseEffects((w[key] as Record<string, unknown>).effects, 'gameplay.work.' + key + '.effects', stats, hooks)\n if (effects !== undefined) work[key] = { effects }\n }\n }\n }\n block.work = work\n }\n }\n }\n\n // --- sleep ---\n if (raw.sleep !== undefined) {\n const s = raw.sleep\n if (!isRecord(s) || !isRecord(s.restore)) fail('gameplay.sleep must be an object with a restore block')\n else {\n const sExtra = unknownKeys(s, KNOWN_SLEEP)\n if (sExtra.length > 0) fail('gameplay.sleep: unknown field(s) ' + sExtra.map(k => JSON.stringify(k)).join(', '))\n const state = parseStateRef(s.state, 'gameplay.sleep.state', hooks)\n const wakeState = parseStateRef(s.wakeState, 'gameplay.sleep.wakeState', hooks)\n const restoreStat = typeof s.restore.stat === 'string' && stats[s.restore.stat] !== undefined ? s.restore.stat : undefined\n if (restoreStat === undefined) fail('gameplay.sleep.restore.stat must reference a declared stat')\n if (!intIn(s.restore.amount, 1, 1000)) fail('gameplay.sleep.restore.amount must be an integer in [1, 1000]')\n if (!intIn(s.restore.intervalMs, 1000, 600_000)) fail('gameplay.sleep.restore.intervalMs must be an integer in [1000, 600000]')\n if (state !== undefined && restoreStat !== undefined && intIn(s.restore.amount, 1, 1000) && intIn(s.restore.intervalMs, 1000, 600_000)) {\n block.sleep = {\n state,\n ...(wakeState === undefined ? {} : { wakeState }),\n restore: { stat: restoreStat, amount: s.restore.amount, intervalMs: s.restore.intervalMs },\n }\n }\n }\n }\n\n // --- passive income ---\n if (raw.passiveIncome !== undefined) {\n const p = raw.passiveIncome\n if (!isRecord(p) || !validName(p.currency, 24) || !intIn(p.amount, 1, 10_000) || !intIn(p.intervalMs, 1000, 86_400_000)) {\n fail('gameplay.passiveIncome must be { currency (kebab), amount 1..10000, intervalMs 1000..86400000 }')\n } else {\n block.passiveIncome = { currency: p.currency, amount: p.amount, intervalMs: p.intervalMs }\n }\n }\n\n // --- shop ---\n if (raw.shop !== undefined) {\n const s = raw.shop\n if (!isRecord(s) || !Array.isArray(s.items) || s.items.length === 0 || s.items.length > MAX_SHOP_ITEMS) {\n fail('gameplay.shop must be an object with 1..' + MAX_SHOP_ITEMS + ' items')\n } else {\n const shopState = parseStateRef(s.state, 'gameplay.shop.state', hooks)\n const items: PetGameplayShopItem[] = []\n const seen = new Set<string>()\n for (const itemRaw of s.items as unknown[]) {\n if (!isRecord(itemRaw) || !validName(itemRaw.id, 24)) {\n fail('gameplay.shop.items entries need a kebab id')\n continue\n }\n if (seen.has(itemRaw.id)) {\n fail('gameplay.shop: duplicate item id ' + JSON.stringify(itemRaw.id))\n continue\n }\n seen.add(itemRaw.id)\n const iExtra = unknownKeys(itemRaw, KNOWN_SHOP_ITEM)\n if (iExtra.length > 0) fail('gameplay.shop.' + itemRaw.id + ': unknown field(s) ' + iExtra.map(k => JSON.stringify(k)).join(', '))\n if (typeof itemRaw.label !== 'string' || itemRaw.label.trim() === '' || itemRaw.label.length > 80) {\n fail('gameplay.shop.' + itemRaw.id + '.label must be a non-empty string of at most 80 chars')\n continue\n }\n if (!intIn(itemRaw.price, 1, 1_000_000)) {\n fail('gameplay.shop.' + itemRaw.id + '.price must be an integer in [1, 1000000]')\n continue\n }\n if (!validName(itemRaw.currency, 24)) {\n fail('gameplay.shop.' + itemRaw.id + '.currency must be a kebab id')\n continue\n }\n const item: PetGameplayShopItem = {\n id: itemRaw.id,\n label: itemRaw.label.trim(),\n price: itemRaw.price,\n currency: itemRaw.currency,\n }\n if (itemRaw.image !== undefined) {\n if (typeof itemRaw.image !== 'string' || itemRaw.image.includes('..') || itemRaw.image.includes('\\\\') || itemRaw.image.startsWith('/')) {\n fail('gameplay.shop.' + itemRaw.id + '.image must be a safe manifest-relative frame path')\n } else item.image = itemRaw.image\n }\n const effects = parseEffects(itemRaw.effects, 'gameplay.shop.' + itemRaw.id + '.effects', stats, hooks)\n if (effects !== undefined) item.effects = effects\n if (itemRaw.lottery !== undefined) {\n const l = itemRaw.lottery\n if (!isRecord(l) || !Array.isArray(l.tiers) || l.tiers.length === 0 || l.tiers.length > MAX_LOTTERY_TIERS) {\n fail('gameplay.shop.' + itemRaw.id + '.lottery needs 1..' + MAX_LOTTERY_TIERS + ' tiers')\n } else {\n const lExtra = unknownKeys(l, KNOWN_LOTTERY)\n if (lExtra.length > 0) fail('gameplay.shop.' + itemRaw.id + '.lottery: unknown field(s) ' + lExtra.map(k => JSON.stringify(k)).join(', '))\n if (l.currency !== undefined && !validName(l.currency, 24)) fail('gameplay.shop.' + itemRaw.id + '.lottery.currency must be a kebab id')\n let tierSum = 0\n const tiers: { probability: number; prize: number; currency?: string }[] = []\n for (const tierRaw of l.tiers as unknown[]) {\n if (!isRecord(tierRaw) || !numIn(tierRaw.probability, 0, 1) || tierRaw.probability === 0\n || !intIn(tierRaw.prize, 0, 1_000_000_000)) {\n fail('gameplay.shop.' + itemRaw.id + '.lottery.tiers entries need probability (0,1] and prize 0..1e9')\n continue\n }\n tierSum += tierRaw.probability\n const tier: { probability: number; prize: number; currency?: string } = {\n probability: tierRaw.probability,\n prize: tierRaw.prize,\n }\n if (tierRaw.currency !== undefined) {\n if (!validName(tierRaw.currency, 24)) fail('gameplay.shop.' + itemRaw.id + '.lottery tier currency must be a kebab id')\n else tier.currency = tierRaw.currency\n }\n tiers.push(tier)\n }\n if (tierSum > 1 + 1e-9) fail('gameplay.shop.' + itemRaw.id + '.lottery tier probabilities must sum to at most 1')\n if (tiers.length > 0) {\n const lotteryEffects = parseEffects(l.effects, 'gameplay.shop.' + itemRaw.id + '.lottery.effects', stats, hooks)\n item.lottery = {\n tiers,\n ...(lotteryEffects === undefined ? {} : { effects: lotteryEffects }),\n ...(validName(l.currency, 24) ? { currency: l.currency } : {}),\n }\n }\n }\n }\n if (item.effects === undefined && item.lottery === undefined) {\n fail('gameplay.shop.' + itemRaw.id + ' needs effects or a lottery')\n continue\n }\n items.push(item)\n }\n if (items.length > 0) block.shop = { ...(shopState === undefined ? {} : { state: shopState }), items }\n }\n }\n\n // --- drag state ---\n if (raw.dragState !== undefined) {\n const state = parseStateRef(raw.dragState, 'gameplay.dragState', hooks)\n if (state !== undefined) block.dragState = state\n }\n if (raw.dragEndState !== undefined) {\n const state = parseStateRef(raw.dragEndState, 'gameplay.dragEndState', hooks)\n if (state !== undefined) block.dragEndState = state\n }\n\n return failed ? undefined : block\n}\n\n/* ------------------------------------------------------------------ *\n * Host engine (pure; clock and rng injected)\n * ------------------------------------------------------------------ */\n\n/** Persisted per-pet gameplay state (pet.json 'gameplay' map values). */\nexport interface PetGameplayState {\n stats: Record<string, number>\n currencies: Record<string, number>\n mode: 'work' | 'sleep' | null\n /** Epoch ms of the last lazy settle. */\n settledAt: number\n}\n\n/** Fresh state for one pet: stats at their initial (default max), no currency. */\nexport function initialGameplayState(manifest: PetGameplayManifest, now: number): PetGameplayState {\n const stats: Record<string, number> = {}\n for (const [name, def] of Object.entries(manifest.stats ?? {})) {\n stats[name] = def.initial ?? def.max\n }\n return { stats, currencies: {}, mode: null, settledAt: now }\n}\n\n/** Clamp one stat value into [0, max]; currencies into [0, CURRENCY_MAX]. */\nexport function clampGameplay(state: PetGameplayState, manifest: PetGameplayManifest): void {\n for (const [name, def] of Object.entries(manifest.stats ?? {})) {\n const value = state.stats[name]\n if (value === undefined) state.stats[name] = def.initial ?? def.max\n else state.stats[name] = Math.min(def.max, Math.max(0, value))\n }\n for (const [name, value] of Object.entries(state.currencies)) {\n state.currencies[name] = Math.min(CURRENCY_MAX, Math.max(0, Math.floor(value)))\n }\n}\n\n/**\n * Lazy settle: apply stat decay, passive income and sleep restore for the\n * elapsed wall time since the last settle. Mirrors the treats.ts discipline\n * (no host timers; read paths settle). Returns whether anything changed.\n */\nexport function settleGameplay(\n state: PetGameplayState,\n manifest: PetGameplayManifest,\n now: number,\n options: { sessionActive: boolean },\n): boolean {\n const elapsedMs = now - state.settledAt\n if (elapsedMs <= 0) return false\n const minutes = elapsedMs / 60_000\n let changed = false\n for (const [name, def] of Object.entries(manifest.stats ?? {})) {\n const current = state.stats[name]\n if (current === undefined || current <= 0) continue\n let rate = def.decayPerMinute ?? 0\n if (state.mode === 'work' && def.workingDecayPerMinute !== undefined) rate = def.workingDecayPerMinute\n if (!options.sessionActive) rate += def.idleDecayPerMinute ?? 0\n if (rate <= 0) continue\n const next = Math.max(0, current - rate * minutes)\n if (next !== current) {\n state.stats[name] = next\n changed = true\n }\n }\n if (manifest.passiveIncome !== undefined) {\n const ticks = Math.floor(elapsedMs / manifest.passiveIncome.intervalMs)\n if (ticks > 0) {\n const currency = manifest.passiveIncome.currency\n state.currencies[currency] = (state.currencies[currency] ?? 0) + ticks * manifest.passiveIncome.amount\n changed = true\n }\n }\n if (state.mode === 'sleep' && manifest.sleep !== undefined) {\n const ticks = Math.floor(elapsedMs / manifest.sleep.restore.intervalMs)\n if (ticks > 0) {\n const stat = manifest.sleep.restore.stat\n state.stats[stat] = (state.stats[stat] ?? 0) + ticks * manifest.sleep.restore.amount\n changed = true\n }\n }\n state.settledAt = now\n clampGameplay(state, manifest)\n return changed\n}\n\n/** Apply one effect vector (touch/work/shop), clamped. */\nexport function applyGameplayEffects(state: PetGameplayState, manifest: PetGameplayManifest, effects: readonly PetGameplayEffect[]): void {\n for (const effect of effects) {\n if (effect.stat !== undefined) {\n state.stats[effect.stat] = (state.stats[effect.stat] ?? 0) + effect.amount\n } else if (effect.currency !== undefined) {\n state.currencies[effect.currency] = (state.currencies[effect.currency] ?? 0) + effect.amount\n }\n }\n clampGameplay(state, manifest)\n}\n\n/** Roll one touch zone branch; undefined when the roll lands in no-op mass. */\nexport function rollTouchBranch(zone: PetGameplayTouchZone, rng: () => number): PetGameplayTouchBranch | undefined {\n const roll = rng()\n let acc = 0\n for (const branch of zone.branches) {\n acc += branch.probability\n if (roll < acc) return branch\n }\n return undefined\n}\n\n/** Roll one work tick outcome. */\nexport function rollWorkOutcome(work: NonNullable<PetGameplayManifest['work']>, rng: () => number): 'success' | 'fail' {\n return rng() < work.successProbability ? 'success' : 'fail'\n}\n\n/** Draw one lottery prize tier; uncovered mass falls through to the last tier. */\nexport function drawLotteryTier(\n lottery: NonNullable<PetGameplayShopItem['lottery']>,\n rng: () => number,\n): { probability: number; prize: number; currency?: string } {\n const roll = rng()\n let acc = 0\n for (const tier of lottery.tiers) {\n acc += tier.probability\n if (roll < acc) return tier\n }\n return lottery.tiers[lottery.tiers.length - 1]!\n}\n\n/** The zone one normalized hit-box point lands in, if any. */\nexport function touchZoneAt(touch: { zones: PetGameplayTouchZone[] }, yFraction: number): PetGameplayTouchZone | undefined {\n return touch.zones.find(zone => yFraction >= zone.y0 && yFraction < zone.y1)\n}\n","/**\n * Gameplay HUD — the client half of the manifest 'gameplay' block (miku-pet\n * generalization). One component owns everything the block needs: the stat\n * bars and shop card (menu / shop pages), the touch-zone tap\n * handling, the idle director rolls, the work and sleep loops, and the\n * float-text toasts. It talks to the host through the injected verb API,\n * writes results straight back into the store (the 2 s poll stays the\n * backstop), and steers the frames2d renderer through the per-pet bus.\n * @module @linxin666/dsh-pet/client/GameplayHud\n */\n\nimport { useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore, type ReactElement } from 'react'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDefinition, PetSkinDefinition } from '../registry.ts'\nimport type { PetGameplayVerbResult } from '../service.ts'\nimport { touchZoneAt } from '../gameplay.ts'\nimport type { PetStoreInstance } from './pet-store.ts'\nimport type { DragStream } from './drag-stream.ts'\nimport type { NS } from './locales.ts'\nimport styles from './pet.module.css'\n\n/** The gameplay verb API the plugin apply body injects (host-authoritative). */\nexport interface GameplayApi {\n touch: (zone?: string) => Promise<PetGameplayVerbResult>\n setMode: (mode: 'work' | 'sleep' | null) => Promise<PetGameplayVerbResult>\n workTick: () => Promise<PetGameplayVerbResult>\n buy: (item: string) => Promise<PetGameplayVerbResult>\n}\n\n/**\n * The per-pet coordination bus. The frames2d visual mount registers the\n * track override (setTrack); the HUD registers the sprite tap handler; the\n * chrome calls into whatever is registered. Optional chaining everywhere —\n * either side may be mid-remount during a hot reload.\n */\nexport interface GameplayBus {\n setTrack?: (track?: string) => void\n /** Swap the pet's base idle track (skin switch); undefined restores default. */\n setIdleTrack?: (track?: string) => void\n tap?: (fx: number, fy: number) => void\n /**\n * Card open/close request from the chrome (the hover panel's 玩法 action):\n * the HUD registers this, and calling it with no argument toggles the card\n * while a boolean pins the state (same chrome -> HUD direction as tap).\n */\n openCard?: (open?: boolean) => void\n}\n\ntype HudPage = 'root' | 'shop' | 'skins'\n\n/** One floating toast (prize / insufficient funds). */\ninterface HudFloat {\n id: number\n text: string\n}\n\nlet floatSeq = 0\n\n/** The gameplay overlay for one frames2d pet that declares 'gameplay'. */\nexport function GameplayHud(props: {\n definition: PetDefinition\n store: PetStoreInstance\n api: GameplayApi\n bus: GameplayBus\n drag: DragStream\n t: PropsLocale<typeof NS>['t']\n}): ReactElement | null {\n const { definition, store, api, bus } = props\n const ui = useSyncExternalStore(store.subscribe, store.getSnapshot)\n const def = definition.gameplay\n const view = ui.snapshot?.gameplay\n const phase = ui.snapshot?.phase ?? 'idle'\n\n const [open, setOpen] = useState(false)\n const [page, setPage] = useState<HudPage>('root')\n // Currently selected skin id (base idle swap); undefined = default look.\n const [skinId, setSkinId] = useState<string | undefined>(undefined)\n const skinIdRef = useRef<string | undefined>(undefined)\n skinIdRef.current = skinId\n const hudRef = useRef<HTMLDivElement | null>(null)\n const cardRef = useRef<HTMLDivElement | null>(null)\n const [floats, setFloats] = useState<HudFloat[]>([])\n // Mutable driver state (refs so intervals never re-arm on a poll tick).\n const modeRef = useRef<'work' | 'sleep' | null>(view?.mode ?? null)\n modeRef.current = view?.mode ?? null\n const phaseRef = useRef(phase)\n phaseRef.current = phase\n const draggingRef = useRef(false)\n const touchLockUntilRef = useRef(0)\n const missRef = useRef(0)\n const busyRef = useRef(false)\n\n // Dynamic-key lookups (stat ids / currency ids are manifest data).\n const tr = props.t as unknown as (key: string, values?: Record<string, string | number>) => string\n const statLabel = (name: string): string => tr('pet.gameplay.stat.' + name)\n const currencyLabel = (name: string): string => tr('pet.gameplay.currency.' + name)\n\n const pushFloat = (text: string): void => {\n const id = ++floatSeq\n setFloats(list => [...list.slice(-3), { id, text }])\n window.setTimeout(() => {\n setFloats(list => list.filter(entry => entry.id !== id))\n }, 1100)\n }\n\n const applyResult = (result: PetGameplayVerbResult): void => {\n if (result.view !== undefined) store.actions.setGameplayView(result.view)\n }\n\n // Tap handling (registered on the bus; PetSprite reports sprite-box\n // fractions). Sleep wakes on tap; work blocks taps; a held touch\n // animation turns taps into the plain-click boost.\n // Skin contract: while a skin is selected, taps only ever play that\n // skin's own click actions — a miss resolves to the plain click boost,\n // never the default touch zones (so a skinned pet cannot trigger the\n // default pet's shy/work reactions).\n useEffect(() => {\n if (def === undefined) return undefined\n // Play a one-shot override track and lock tap input for its duration so\n // consecutive taps cannot retrigger mid-play (skin click actions and\n // default touch-zone reactions share this path).\n const holdTrack = (track: string, holdMs: number): void => {\n bus.setTrack?.(track)\n touchLockUntilRef.current = Date.now() + holdMs\n window.setTimeout(() => {\n if (Date.now() >= touchLockUntilRef.current) bus.setTrack?.(undefined)\n }, holdMs)\n }\n const speak = (phrases?: string[]): void => {\n if (phrases !== undefined && phrases.length > 0) {\n const phrase = phrases[Math.floor(Math.random() * phrases.length)]!\n store.actions.setFeedback({ text: phrase, kind: 'none', at: Date.now() })\n }\n }\n // Total play time of a track in ms (its fallback lands back in the skin\n // base idle, so holding the lock for the full loop is unnecessary).\n const trackDuration = (track: string): number =>\n definition.frames2d?.tracks[track]?.durations.reduce((sum, ms) => sum + ms, 0) ?? 0\n bus.tap = (fx, fy) => {\n if (modeRef.current === 'sleep') {\n void api.setMode(null).then(applyResult, () => undefined)\n return\n }\n if (modeRef.current === 'work') return\n const box = def.hitBox ?? { x0: 0, y0: 0, x1: 1, y1: 1 }\n const hx = (fx - box.x0) / (box.x1 - box.x0)\n const hy = (fy - box.y0) / (box.y1 - box.y0)\n if (hx < 0 || hx > 1 || hy < 0 || hy > 1) return\n if (Date.now() < touchLockUntilRef.current) {\n void api.touch().then(applyResult, () => undefined)\n return\n }\n const activeSkin = definition.frames2d?.skins?.find(skin => skin.id === skinIdRef.current)\n if (activeSkin !== undefined) {\n // Skin click actions roll first (declared order, cumulative\n // probabilities); on a hit play the action's track once. A miss\n // stays on the plain click boost — never the default touch zones.\n const actions = activeSkin.clickActions ?? []\n if (actions.length > 0) {\n let roll = Math.random()\n const fired = actions.find(action => {\n if (roll < action.probability) return true\n roll -= action.probability\n return false\n })\n if (fired !== undefined) {\n holdTrack(fired.track, trackDuration(fired.track) || 3000)\n speak(fired.phrases)\n return\n }\n }\n void api.touch().then(applyResult, () => undefined)\n return\n }\n const zone = def.touch === undefined ? undefined : touchZoneAt(def.touch, hy)\n if (zone === undefined) return\n void api.touch(zone.name).then((result) => {\n applyResult(result)\n if (result.hit !== true) return\n if (result.state !== undefined) holdTrack(result.state, result.stateMs ?? 3000)\n if (result.phrase !== undefined) {\n store.actions.setFeedback({ text: result.phrase, kind: 'none', at: Date.now() })\n }\n }, () => undefined)\n }\n return () => { bus.tap = undefined }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one registration per pet definition\n }, [definition.id, def])\n\n // Panel entry (chrome -> HUD): the hover panel's 玩法 button drives the\n // card through this channel so the chrome never needs the card's state.\n useEffect(() => {\n bus.openCard = (next?: boolean) => {\n setOpen(prev => next ?? !prev)\n setPage('root')\n }\n return () => { bus.openCard = undefined }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one registration per bus\n }, [bus])\n\n // Card placement: the card must not enter the bubble band above the sprite\n // or the hover-panel band below it, so it opens beside the sprite instead\n // of growing upward from the pet's feet. It is vertically centered on the\n // sprite box, clamped to the sprite's height, and the side is chosen by\n // the space available to each side (right first; a pet parked near the\n // right viewport edge flips the card to the left).\n useLayoutEffect(() => {\n if (!open) return undefined\n const hud = hudRef.current\n const card = cardRef.current\n if (hud === null || card === null) return undefined\n const place = (): void => {\n const box = hud.parentElement?.getBoundingClientRect()\n if (box === undefined) return\n const gap = 8\n const width = card.getBoundingClientRect().width\n const toRight = window.innerWidth - box.right\n const x = toRight >= width + gap ? box.width + gap : -(width + gap)\n card.style.transform = 'translate(' + Math.round(x) + 'px, ' + Math.round(-box.height / 2) + 'px) translateY(50%)'\n card.style.maxHeight = Math.round(box.height) + 'px'\n }\n place()\n window.addEventListener('resize', place)\n return () => { window.removeEventListener('resize', place) }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one placement per open/page change\n }, [open, page])\n\n // Drag gestures wake a sleeping pet (miku behavior); the drag stream also\n // feeds the idle director's suppression check.\n useEffect(() => {\n return props.drag.subscribe((dragging) => {\n draggingRef.current = dragging\n if (dragging && modeRef.current === 'sleep') {\n void api.setMode(null).then(applyResult, () => undefined)\n }\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one subscription per drag stream\n }, [props.drag])\n\n // Idle director: weighted rolls between staying idle and playing an act.\n // Runs only while the phase mapping owns the visual (idle phase, no mode,\n // no drag, no held touch animation); maxMiss forces an act after too many\n // idle rolls in a row.\n useEffect(() => {\n const director = def?.idleDirector\n if (def === undefined || director === undefined) return undefined\n const total = director.idleWeight + director.acts.reduce((sum, act) => sum + act.weight, 0)\n if (total <= 0) return undefined\n const timer = window.setInterval(() => {\n if (phaseRef.current !== 'idle') return\n if (modeRef.current !== null || draggingRef.current) return\n if (Date.now() < touchLockUntilRef.current) return\n let pickedAct: { track: string; weight: number; phrases?: string[] } | undefined\n if (missRef.current >= director.maxMiss) {\n // Forced act: pick among the acts only.\n const actTotal = director.acts.reduce((sum, act) => sum + act.weight, 0)\n let actRoll = Math.random() * actTotal\n for (const act of director.acts) {\n actRoll -= act.weight\n if (actRoll < 0) { pickedAct = act; break }\n }\n } else {\n let roll = Math.random() * total\n for (const act of director.acts) {\n roll -= act.weight\n if (roll < 0) { pickedAct = act; break }\n }\n }\n if (pickedAct === undefined) {\n missRef.current += 1\n return\n }\n missRef.current = 0\n bus.setTrack?.(pickedAct.track)\n // Acts with a phrase pool speak one line while they play (miku parity).\n if (pickedAct.phrases !== undefined && pickedAct.phrases.length > 0) {\n const phrase = pickedAct.phrases[Math.floor(Math.random() * pickedAct.phrases.length)]!\n store.actions.setFeedback({ text: phrase, kind: 'none', at: Date.now() })\n }\n }, director.intervalMs)\n return () => window.clearInterval(timer)\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one director per pet definition\n }, [definition.id, def])\n\n // Work loop: hold the work track, adjudicate one round per tick, play the\n // result track for its hold window, then resume. Leaving the mode\n // releases the override so the phase mapping takes over.\n useEffect(() => {\n const work = def?.work\n if (def === undefined || work === undefined || view?.mode !== 'work') return undefined\n bus.setTrack?.(work.state)\n let resultTimer = 0\n const timer = window.setInterval(() => {\n if (busyRef.current) return\n busyRef.current = true\n void api.workTick().then((result) => {\n busyRef.current = false\n applyResult(result)\n if (result.ok !== true || result.outcome === undefined) return\n const resultTrack = result.outcome === 'success' ? work.successState : work.failState\n const hold = result.outcome === 'success' ? work.resultMs?.success ?? 1300 : work.resultMs?.fail ?? 1900\n bus.setTrack?.(resultTrack)\n resultTimer = window.setTimeout(() => {\n if (modeRef.current === 'work') bus.setTrack?.(work.state)\n }, hold)\n }, () => { busyRef.current = false })\n }, work.tickMs)\n return () => {\n window.clearInterval(timer)\n window.clearTimeout(resultTimer)\n bus.setTrack?.(undefined)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- the loop keys on the mode value\n }, [definition.id, def, view?.mode])\n\n // Sleep loop: hold the sleep track; restore is host-side (lazy settle).\n // While a skin with a gameplayTracks.sleep override is selected, the skin's\n // own track replaces the default sleep track (e.g. a skin-specific doze).\n useEffect(() => {\n const sleep = def?.sleep\n if (def === undefined || sleep === undefined || view?.mode !== 'sleep') return undefined\n const skinGameplay = definition.frames2d?.skins?.find(skin => skin.id === skinIdRef.current)?.gameplayTracks\n const hold = skinGameplay?.['sleep'] ?? sleep.state\n bus.setTrack?.(hold)\n return () => bus.setTrack?.(undefined)\n // eslint-disable-next-line react-hooks/exhaustive-deps -- the loop keys on the mode value\n }, [definition.id, def, view?.mode, skinId])\n\n if (def === undefined || view === undefined) return null\n\n const mode = view.mode\n const stats = def.stats ?? {}\n const shop = def.shop\n\n const buy = (itemId: string): void => {\n void api.buy(itemId).then((result) => {\n applyResult(result)\n if (result.ok !== true) {\n if (result.error === 'insufficient-funds') {\n const item = shop?.items.find(entry => entry.id === itemId)\n pushFloat(tr('pet.gameplay.insufficient', { currency: currencyLabel(item?.currency ?? 'treats') }))\n }\n return\n }\n if (result.prize !== undefined) {\n pushFloat(tr('pet.gameplay.prize', { amount: result.prize.amount, currency: currencyLabel(result.prize.currency) }))\n }\n }, () => undefined)\n }\n\n const setMode = (next: 'work' | 'sleep' | null): void => {\n void api.setMode(next).then(applyResult, () => undefined)\n }\n\n const skins = definition.frames2d?.skins\n const selectSkin = (skin: PetSkinDefinition | undefined): void => {\n setSkinId(skin?.id)\n bus.setIdleTrack?.(skin?.idleTrack)\n }\n\n return (\n <div ref={hudRef} className={styles.gameplayHud} data-dsh-pet-gameplay={definition.id}>\n {floats.map(entry => (\n <div key={entry.id} className={styles.gameplayFloat}>{entry.text}</div>\n ))}\n {mode !== null && (\n <div className={styles.gameplayModeChip}>\n {tr(mode === 'work' ? 'pet.gameplay.working' : 'pet.gameplay.sleeping')}\n </div>\n )}\n {open && (\n <div ref={cardRef} className={styles.gameplayCard} data-page={page}>\n {page === 'root' && (\n <>\n <div className={styles.gameplayBars}>\n {Object.entries(stats).map(([name, stat]) => {\n const value = view.stats[name] ?? 0\n return (\n <div key={name} className={styles.gameplayBarRow} title={statLabel(name) + ' ' + String(value) + '/' + String(stat.max)}>\n <span className={styles.gameplayBarLabel}>{statLabel(name)}</span>\n <span className={styles.gameplayBarTrack}>\n <span\n className={styles.gameplayBarFill}\n style={{ width: Math.round((value / stat.max) * 100) + '%' }}\n />\n </span>\n </div>\n )\n })}\n </div>\n <div className={styles.gameplayActions}>\n {def.sleep !== undefined && (\n <button\n type=\"button\"\n className={styles.action}\n onClick={() => setMode(mode === 'sleep' ? null : 'sleep')}\n >\n {tr(mode === 'sleep' ? 'pet.gameplay.wake' : 'pet.gameplay.sleep')}\n </button>\n )}\n {shop !== undefined && (\n <button type=\"button\" className={styles.action} onClick={() => setPage('shop')}>\n {tr('pet.gameplay.shop')}\n </button>\n )}\n {skins !== undefined && skins.length > 0 && (\n <button type=\"button\" className={styles.action} onClick={() => setPage('skins')}>\n {tr('pet.gameplay.skin')}\n </button>\n )}\n {def.work !== undefined && (\n <button\n type=\"button\"\n className={styles.action}\n onClick={() => setMode(mode === 'work' ? null : 'work')}\n >\n {tr(mode === 'work' ? 'pet.gameplay.stopWork' : 'pet.gameplay.work')}\n </button>\n )}\n </div>\n </>\n )}\n {page === 'shop' && shop !== undefined && (\n <>\n <div className={styles.gameplayShopItems}>\n {shop.items.map(item => (\n <button\n key={item.id}\n type=\"button\"\n className={styles.gameplayShopItem}\n onClick={() => buy(item.id)}\n title={item.label + ' — ' + String(item.price) + ' ' + currencyLabel(item.currency)}\n >\n {item.image !== undefined && (\n <img className={styles.gameplayShopItemImage} src={item.image} alt=\"\" draggable={false} />\n )}\n <span className={styles.gameplayShopItemLabel}>{item.label}</span>\n <span className={styles.gameplayShopItemPrice}>\n {item.price} {currencyLabel(item.currency)}\n </span>\n </button>\n ))}\n </div>\n <div className={styles.gameplayActions}>\n <button type=\"button\" className={styles.action} onClick={() => setPage('root')}>\n {tr('pet.gameplay.back')}\n </button>\n </div>\n </>\n )}\n {page === 'skins' && skins !== undefined && (\n <>\n <div className={styles.gameplaySkinItems}>\n <button\n type=\"button\"\n className={skinId === undefined ? styles.gameplaySkinItem + ' ' + styles.gameplaySkinItemActive : styles.gameplaySkinItem}\n onClick={() => selectSkin(undefined)}\n >\n {tr('pet.gameplay.skinDefault')}\n </button>\n {skins.map(skin => (\n <button\n key={skin.id}\n type=\"button\"\n className={skinId === skin.id ? styles.gameplaySkinItem + ' ' + styles.gameplaySkinItemActive : styles.gameplaySkinItem}\n onClick={() => selectSkin(skin)}\n >\n {skin.label}\n </button>\n ))}\n </div>\n <div className={styles.gameplayActions}>\n <button type=\"button\" className={styles.action} onClick={() => setPage('root')}>\n {tr('pet.gameplay.back')}\n </button>\n </div>\n </>\n )}\n <button\n type=\"button\"\n className={styles.gameplayClose}\n aria-label={tr('pet.gameplay.back')}\n onClick={() => setOpen(false)}\n >\n ×\n </button>\n </div>\n )}\n </div>\n )\n}\n","/**\n * Global floating pet entry. The pet is host-global (its state, display and\n * interactions live on '/api/pet/*' endpoints with no session dimension), so\n * it must not ride a session-scoped slot — on the new-conversation screen no\n * session exists to scope a slot by, and the pet would vanish (issue #48).\n * The client half therefore mounts this entry straight onto 'document.body'\n * (see index.ts): while visible it renders the floating PetSprite (a portal\n * into the plugin root, so the root owns the whole surface), while hidden it\n * renders a fixed-position summon button. Which sprite renders is decided by\n * the host snapshot's pet id resolved against the registry list — no per-pet\n * component exists.\n * @module @linxin666/dsh-pet/client/PetDockEntry\n */\n\nimport { useEffect, useRef, useSyncExternalStore, type ReactElement } from 'react'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDisplayConfig } from '../persist.ts'\nimport type { PetStoreInstance } from './pet-store.ts'\nimport { PetSprite } from './PetSprite.tsx'\nimport { PetRendererSwitch } from './renderers/PetRendererSwitch.tsx'\nimport { createDragStream, type DragStream } from './drag-stream.ts'\nimport { GameplayHud, type GameplayApi, type GameplayBus } from './gameplay-hud.tsx'\nimport { NS } from './locales.ts'\nimport styles from './pet.module.css'\n\n/** Injected actions handed to the dock entry component. */\nexport interface PetInjected {\n /** The app-wide pet store instance (snapshot + registry list + feedback). */\n store: PetStoreInstance\n /** Ensure the first snapshot (and registry list) is fetched (called on mount). */\n ensure: () => void\n /** Pet the sprite (click). */\n pet: () => void\n /** Feed the sprite. */\n feed: () => void\n /** Hide the sprite. */\n hide: () => void\n /** Summon the hidden sprite back. */\n summon: () => void\n /** Persist a drag position. */\n dragEnd: (right: number, bottom: number) => void\n /** Rename the selected pet (persisted by the host). */\n rename: (name: string) => void\n /** Navigate the GUI to the session a bubble reports on. */\n openSession: (sessionId: string) => void\n /** Clear the reaction bubble. */\n feedbackDone: () => void\n /** Gameplay verb API (miku-pet generalization); wired but unused for pets without a gameplay block. */\n gameplay: GameplayApi\n}\n\n/** Composed props of the global pet entry (locale + injected; no slot runtime share). */\nexport type PetDockEntryProps =\n PetInjected\n & PropsLocale<typeof NS>\n & {\n /**\n * DOM node the floating sprite chrome portals into. Defaults to\n * document.body; the plugin apply passes its [data-dsh-plugin=\"pet\"]\n * root so root-keyed suppressors (the portrait mobile layer) and skins\n * own the whole pet surface as one unit.\n */\n portalTarget?: Element\n }\n\nconst DEFAULT_DISPLAY: PetDisplayConfig = { visible: true, size: 160, right: 24, bottom: 20 }\n\n/**\n * Dock entry: while the pet is visible, mount the floating PetSprite (it\n * portals itself onto document.body); while hidden, render the summon\n * button so the pet can always come back. The store is the plugin-owned\n * single instance — the slot system provides none because the pet is\n * host-global, not session-scoped.\n */\nexport function PetDockEntry(props: PetDockEntryProps): ReactElement {\n const { store, ensure } = props\n const ui = useSyncExternalStore(store.subscribe, store.getSnapshot)\n const snapshot = ui.snapshot\n const feedback = ui.feedback\n const definition = ui.pets.find(entry => entry.id === snapshot?.pet.id) ?? null\n const visible = snapshot?.display.visible ?? true\n\n useEffect(() => {\n ensure()\n }, [ensure])\n\n // Per-pet gameplay wiring: the coordination bus (HUD taps <-> chrome,\n // HUD track overrides <-> frames2d mount) and the shared drag stream.\n const auxRef = useRef<{ id: string; bus: GameplayBus; drag: DragStream } | null>(null)\n if (definition !== null && (auxRef.current === null || auxRef.current.id !== definition.id)) {\n auxRef.current = { id: definition.id, bus: {}, drag: createDragStream() }\n }\n const aux = auxRef.current\n const gameplay = definition?.gameplay\n\n if (visible) {\n return (\n <span data-pet-dock data-testid=\"pet-dock\">\n {snapshot === null || definition === null\n ? null\n : (\n <PetRendererSwitch\n definition={definition}\n phase={snapshot?.phase ?? 'idle'}\n onPet={props.pet}\n {...(aux === null ? {} : { drag: aux.drag, bus: aux.bus })}\n t={props.t}\n >\n <PetSprite\n snapshot={snapshot}\n definition={definition}\n display={snapshot.display}\n feedback={feedback}\n onPet={props.pet}\n onFeed={props.feed}\n onHide={props.hide}\n onDragEnd={props.dragEnd}\n onRename={props.rename}\n onOpenSession={props.openSession}\n onFeedbackDone={props.feedbackDone}\n portalTarget={props.portalTarget}\n dragDisabled={snapshot.gameplay?.mode === 'work'}\n {...(gameplay === undefined || aux === null\n ? {}\n : {\n onGameplayTap: (fx: number, fy: number) => aux.bus.tap?.(fx, fy),\n onGameplayMenu: () => aux.bus.openCard?.(),\n hud: (\n <GameplayHud\n definition={definition}\n store={store}\n api={props.gameplay}\n bus={aux.bus}\n drag={aux.drag}\n t={props.t}\n />\n ),\n })}\n t={props.t}\n />\n </PetRendererSwitch>\n )}\n </span>\n )\n }\n const display = snapshot?.display ?? DEFAULT_DISPLAY\n return (\n <button\n type=\"button\"\n className={styles.summon}\n style={{\n position: 'fixed',\n right: display.right,\n bottom: display.bottom,\n zIndex: 2147483000,\n }}\n onClick={props.summon}\n data-testid=\"pet-summon\"\n data-dsh-part=\"summon-button\"\n >\n {props.t('pet.summon', { name: snapshot?.name ?? '' })}\n </button>\n )\n}\n","/**\n * Renderer contract — the seam between the pet center and its renderers\n * (issue #623, milestone M2 P4). Renderers never see the DSH session, the\n * registry, or the network: they receive exactly three capabilities — an\n * asset base URL, the ActivityPhase stream, and the interaction write-back —\n * inside a center-owned container. Every mount is a fresh activation whose\n * cleanups must be idempotent.\n *\n * This contract only serves real consumers: sprite2d (existing) and live2d\n * (M3). Speculative capabilities join only when a renderer actually needs\n * them.\n * @module @linxin666/dsh-pet/contracts/renderer\n */\n\nimport type { ActivityPhase } from '../state.ts'\n\n/** Contract version renderers declare against (independent of the manifest). */\nexport const PET_RENDERER_API_VERSION = 'x-org.linxin666.pet-center/v1alpha1'\n\n/** What the pet center hands a renderer on mount. */\nexport interface PetRendererContext {\n /** The selected pet's id. */\n readonly petId: string\n /** Same-origin URL prefix of this pet's assets ('/pet/<id>'). */\n readonly assetBase: string\n /** Center-owned mount root; renderers must not attach to document.body. */\n readonly container: HTMLElement\n /** The ActivityPhase stream (pet-center owned; renderers subscribe). */\n readonly phase: {\n get(): ActivityPhase\n subscribe(listener: (phase: ActivityPhase) => void): () => void\n }\n /** The single interaction write-back into the affinity economy. */\n readonly interact: (kind: 'tap') => void\n /** Register an activation-scoped cleanup; run on dispose, idempotently. */\n onCleanup(fn: () => void): void\n}\n\n/** A mounted renderer activation. */\nexport interface PetRendererHandle {\n /** Tear the activation down; may be called 0/1/N times. */\n dispose(): void\n}\n\n/**\n * One renderer implementation. validateConfig is fail-closed over the\n * renderer-specific manifest block (schema v2 'sprite2d'/'live2d' blocks).\n */\nexport interface PetRenderer<Config = unknown> {\n readonly id: string\n readonly apiVersion: string\n validateConfig(config: unknown): Config\n mount(ctx: PetRendererContext, config: Config): PetRendererHandle\n}\n","/**\n * Live2D runtime loading (pet-center M3) — the two scripts a live2d mount\n * needs, fetched lazily through the plugin's own runtime route: the\n * user-supplied Cubism Core (proprietary; the plugin never bundles or\n * downloads it — issue #623 M1 §0) and the plugin-shipped MIT vendor bundle\n * (pixi.js + untitled-pixi-live2d-engine). Each loads at most once per page;\n * concurrent mounts share the in-flight promise, and a failure is cached as\n * 'absent' so a broken install stops retrying the network every mount.\n *\n * The vendor surface below is the structural slice the renderer consumes;\n * the real objects come from 'window.__dshPetLive2d' (lib/live2d-vendor.js),\n * so this module never imports pixi — the client bundle stays lean.\n * @module @linxin666/dsh-pet/client/renderers/live2d/runtime\n */\n\n/** Runtime file URLs the host serves ('/api/pet/runtime/<name>', M3-2). */\nconst CORE_URL = '/api/pet/runtime/live2dcubismcore.min.js'\nconst VENDOR_URL = '/api/pet/runtime/live2d-vendor.js'\n\n/** The pixi Application slice the renderer uses. */\nexport interface Live2dVendorApp {\n canvas: HTMLCanvasElement\n stage: { addChild(child: unknown): unknown }\n renderer: {\n readonly width: number\n readonly height: number\n resize(width: number, height: number): void\n }\n init(options: Record<string, unknown>): Promise<void>\n destroy(rendererOptions?: boolean | { removeView?: boolean; releaseGlobalResources?: boolean }, options?: Record<string, unknown>): void\n}\n\n/** The Live2DModel slice the renderer uses. */\nexport interface Live2dVendorModel {\n automator: { autoUpdate: boolean }\n anchor: { set(x: number, y?: number): void }\n position: { set(x: number, y: number): void }\n scale: { set(x: number, y?: number): void }\n readonly width: number\n readonly height: number\n internalModel: {\n settings: {\n motions?: Record<string, unknown[]>\n hitAreas?: readonly { Name?: string }[]\n }\n }\n motion(group: string, index?: number): Promise<unknown>\n expression(name?: string): unknown\n hitTest(x: number, y: number): string[]\n on(event: string, fn: () => void): unknown\n destroy(options?: { children?: boolean; texture?: boolean; baseTexture?: boolean }): void\n}\n\n/** The vendor bundle global (window.__dshPetLive2d). */\nexport interface Live2dVendor {\n Application: new () => Live2dVendorApp\n extensions: { add(...items: unknown[]): void }\n Live2DPlugin: unknown\n configureCubismSDK(options: Record<string, unknown>): void\n Live2DModel: { from(source: string, options?: Record<string, unknown>): Promise<Live2dVendorModel> }\n}\n\ndeclare global {\n interface Window {\n Live2DCubismCore?: unknown\n __dshPetLive2d?: Live2dVendor\n }\n}\n\n/** Injects one classic script tag; resolves on load, rejects on error. */\ntype ScriptInjector = (src: string) => Promise<void>\n\nconst defaultInjector: ScriptInjector = (src) => new Promise<void>((resolve, reject) => {\n const tag = document.createElement('script')\n tag.src = src\n tag.onload = () => resolve()\n tag.onerror = () => reject(new Error('script failed to load: ' + src))\n document.head.appendChild(tag)\n})\n\n/** Test seam: swap the network for a stub injector. */\nexport interface Live2dRuntimeProbe {\n inject?: ScriptInjector\n}\n\nlet corePromise: Promise<boolean> | undefined\nlet vendorPromise: Promise<Live2dVendor | undefined> | undefined\n\n/**\n * Ensure the Cubism Core global exists, injecting the runtime-route script\n * once when absent. Resolves false when the user has not installed the core\n * (a normal state — the renderer turns it into install guidance).\n */\nexport function ensureCubismCore(probe: Live2dRuntimeProbe = {}): Promise<boolean> {\n if (typeof window !== 'undefined' && window.Live2DCubismCore !== undefined) return Promise.resolve(true)\n if (probe.inject !== undefined) {\n return probe.inject(CORE_URL)\n .then(() => typeof window !== 'undefined' && window.Live2DCubismCore !== undefined)\n .catch(() => false)\n }\n corePromise ??= defaultInjector(CORE_URL)\n .then(() => typeof window !== 'undefined' && window.Live2DCubismCore !== undefined)\n .catch(() => false)\n return corePromise\n}\n\n/** Ensure the plugin vendor bundle global exists (same caching discipline). */\nexport function ensureLive2dVendor(probe: Live2dRuntimeProbe = {}): Promise<Live2dVendor | undefined> {\n if (typeof window !== 'undefined' && window.__dshPetLive2d !== undefined) return Promise.resolve(window.__dshPetLive2d)\n if (probe.inject !== undefined) {\n return probe.inject(VENDOR_URL)\n .then(() => typeof window !== 'undefined' ? window.__dshPetLive2d : undefined)\n .catch(() => undefined)\n }\n vendorPromise ??= defaultInjector(VENDOR_URL)\n .then(() => typeof window !== 'undefined' ? window.__dshPetLive2d : undefined)\n .catch(() => undefined)\n return vendorPromise\n}\n\n/** Reset the cached script promises (tests). */\nexport function resetLive2dRuntime(): void {\n corePromise = undefined\n vendorPromise = undefined\n}\n","/**\n * Live2D renderer (pet-center M3, issue #623) — mounts a Cubism model into\n * the center-owned container through the lazy vendor stack. The mount call\n * itself is synchronous per the renderer contract: the boot (core script →\n * vendor script → pixi → model) continues asynchronously and reports fatal\n * failures through the handle's error sink, so the React bridge can render\n * localized guidance. Disposing mid-boot is race-safe.\n *\n * Interaction model: the center chrome owns the affinity economy (its click\n * handler fires the pet interaction exactly like sprite2d); this renderer's\n * 'tap' affordance only drives the model's hit-area motion feedback. The\n * contract's interact write-back stays available for future standalone\n * mounts and is intentionally not invoked here.\n *\n * Motion mapping: manifests map ActivityPhases to motion GROUP names; every\n * unmapped phase (and any mapped-but-absent group) falls back to the idle\n * group — official sample models only ship Idle/TapBody, so the fallback is\n * mandatory. Groups with multiple motions pick a random entry, and a tap\n * that hits a declared hit area plays the conventional 'TapBody' group,\n * returning to the phase's group when the tap motion finishes.\n * @module @linxin666/dsh-pet/client/renderers/live2d\n */\n\nimport type { ActivityPhase } from '../../state.ts'\nimport {\n PET_RENDERER_API_VERSION,\n type PetRenderer,\n type PetRendererContext,\n type PetRendererHandle,\n} from '../../contracts/renderer.ts'\nimport {\n ensureCubismCore,\n ensureLive2dVendor,\n type Live2dVendor,\n type Live2dVendorApp,\n type Live2dVendorModel,\n} from './live2d/runtime.ts'\n\n/** Renderer config: the client-visible live2d block (fail-closed validated). */\nexport interface PetLive2dConfig {\n modelUrl: string\n scale?: number\n translate?: { x?: number; y?: number }\n motions: Partial<Record<ActivityPhase, string>> & { idle: string }\n expressions?: Partial<Record<ActivityPhase, string>>\n hitAreas?: string[]\n}\n\n/** Fatal mount failure codes the bridge localizes. */\nexport type Live2dErrorCode = 'core-missing' | 'vendor-missing' | 'load-failed'\n\n/** The live2d activation handle: contract dispose plus tap + error sink. */\nexport interface Live2dRendererHandle extends PetRendererHandle {\n /** Forward a chrome tap in container coordinates; plays the hit motion. */\n tap(x: number, y: number): void\n /** Subscribe to fatal mount errors (at most one fires per activation). */\n onError(listener: (code: Live2dErrorCode) => void): void\n}\n\n/** The de-facto tap-motion group of Cubism sample models. */\nconst TAP_GROUP = 'TapBody'\n\n/**\n * Keep one screen-appropriate atlas LOD instead of asking Pixi for the\n * engine's default full mip chain. A user model can legitimately carry an\n * 8192px texture while the pet itself is only a few hundred pixels tall;\n * `single-auto` preserves the source for larger renders and generates one\n * downsampled atlas only when the effective on-screen scale warrants it.\n */\nconst TEXTURE_OPTIONS = { lod: 'single-auto' } as const\n/** Recursively release the activation without invalidating shared texture caches. */\nconst DESTROY_OPTIONS = { children: true } as const\n/** Remove only this activation's canvas; `true` would release Pixi globals. */\nconst RENDERER_DESTROY_OPTIONS = { removeView: true } as const\n\ninterface Live2dModelSize {\n width: number\n height: number\n}\n\n/** Ignore hidden/zero boxes and keep Pixi dimensions stable and integral. */\nfunction normalizeRendererSize(width: number, height: number): Live2dModelSize | undefined {\n if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return undefined\n return {\n width: Math.max(1, Math.round(width)),\n height: Math.max(1, Math.round(height)),\n }\n}\n\n/** Fit the model from its unscaled dimensions into the current Pixi screen. */\nfunction layoutModel(\n app: Live2dVendorApp,\n model: Live2dVendorModel,\n sourceSize: Live2dModelSize,\n config: PetLive2dConfig,\n): void {\n const fit = Math.min(\n app.renderer.width / sourceSize.width,\n app.renderer.height / sourceSize.height,\n ) * 0.92\n model.scale.set(fit * (config.scale ?? 1))\n model.anchor.set(0.5)\n model.position.set(\n app.renderer.width / 2 + (config.translate?.x ?? 0),\n app.renderer.height / 2 + (config.translate?.y ?? 0),\n )\n}\n\nlet vendorConfigured = false\n\n/** Configure pixi extensions + the Cubism SDK once per page. */\nfunction configureOnce(vendor: Live2dVendor): void {\n if (vendorConfigured) return\n vendorConfigured = true\n vendor.extensions.add(vendor.Live2DPlugin)\n vendor.configureCubismSDK({ memorySizeMB: 32 })\n}\n\n/** Reset module state (tests). */\nexport function resetLive2dRenderer(): void {\n vendorConfigured = false\n}\n\n/** Fail-closed config validation (contract: unknown manifest block in). */\nfunction validateLive2dConfig(config: unknown): PetLive2dConfig {\n if (typeof config !== 'object' || config === null) throw new Error('live2d config is not an object')\n const source = config as Record<string, unknown>\n if (typeof source.modelUrl !== 'string' || source.modelUrl === '') throw new Error('live2d config modelUrl is required')\n const motions = source.motions\n if (typeof motions !== 'object' || motions === null || typeof (motions as Record<string, unknown>).idle !== 'string') {\n throw new Error('live2d config motions.idle is required')\n }\n return config as PetLive2dConfig\n}\n\n/** The live2d renderer implementation. */\nexport const live2dRenderer: PetRenderer<PetLive2dConfig> = {\n id: 'live2d',\n apiVersion: PET_RENDERER_API_VERSION,\n validateConfig: validateLive2dConfig,\n mount(ctx: PetRendererContext, config: PetLive2dConfig): Live2dRendererHandle {\n let disposed = false\n let app: Live2dVendorApp | undefined\n let model: Live2dVendorModel | undefined\n let modelAttached = false\n let modelSourceSize: Live2dModelSize | undefined\n let resizeObserver: ResizeObserver | undefined\n let resizeTracking = false\n let errorListener: ((code: Live2dErrorCode) => void) | undefined\n let unsubscribe: (() => void) | undefined\n /** The motion group the current phase maps to (resume target after taps). */\n let phaseGroup: string = config.motions.idle\n let tapPlaying = false\n\n const stopResizeTracking = (): void => {\n resizeTracking = false\n resizeObserver?.disconnect()\n resizeObserver = undefined\n }\n\n const resizeRenderer = (pixiApp: Live2dVendorApp, width: number, height: number): void => {\n const next = normalizeRendererSize(width, height)\n if (disposed || !resizeTracking || next === undefined) return\n if (pixiApp.renderer.width === next.width && pixiApp.renderer.height === next.height) return\n pixiApp.renderer.resize(next.width, next.height)\n if (model !== undefined && modelSourceSize !== undefined) {\n layoutModel(pixiApp, model, modelSourceSize, config)\n }\n }\n\n const trackContainerSize = (pixiApp: Live2dVendorApp): void => {\n if (typeof ResizeObserver === 'undefined') return\n resizeTracking = true\n resizeObserver = new ResizeObserver((entries) => {\n const entry = entries.find(candidate => candidate.target === ctx.container)\n if (entry === undefined) return\n resizeRenderer(pixiApp, entry.contentRect.width, entry.contentRect.height)\n })\n resizeObserver.observe(ctx.container)\n // Catch a synchronous layout change between init() and observe().\n resizeRenderer(pixiApp, ctx.container.clientWidth, ctx.container.clientHeight)\n }\n\n /** Release every resource currently owned by this activation exactly once. */\n const destroyResources = (): void => {\n stopResizeTracking()\n unsubscribe?.()\n unsubscribe = undefined\n const currentApp = app\n const currentModel = model\n const modelOwnedByApp = currentApp !== undefined && modelAttached\n app = undefined\n model = undefined\n modelSourceSize = undefined\n modelAttached = false\n try {\n if (currentModel !== undefined && !modelOwnedByApp) currentModel.destroy(DESTROY_OPTIONS)\n } finally {\n currentApp?.destroy(RENDERER_DESTROY_OPTIONS, DESTROY_OPTIONS)\n }\n }\n\n const playGroup = (group: string): void => {\n if (model === undefined) return\n const groups = model.internalModel.settings.motions ?? {}\n const count = Array.isArray(groups[group]) ? groups[group]!.length : 0\n if (count === 0) {\n // A mapped-but-absent group falls back to idle (never blank motion).\n if (group !== config.motions.idle) playGroup(config.motions.idle)\n return\n }\n const index = count > 1 ? Math.floor(Math.random() * count) : 0\n void model.motion(group, index)\n }\n\n const applyPhase = (phase: ActivityPhase): void => {\n phaseGroup = config.motions[phase] ?? config.motions.idle\n playGroup(phaseGroup)\n const expression = config.expressions?.[phase]\n if (expression !== undefined && model !== undefined) void model.expression(expression)\n }\n\n const boot = async (): Promise<void> => {\n if (!await ensureCubismCore()) {\n if (!disposed) errorListener?.('core-missing')\n return\n }\n const vendor = await ensureLive2dVendor()\n if (vendor === undefined) {\n if (!disposed) errorListener?.('vendor-missing')\n return\n }\n configureOnce(vendor)\n const pixiApp = new vendor.Application()\n const initialSize = normalizeRendererSize(ctx.container.clientWidth, ctx.container.clientHeight) ?? {\n width: 160,\n height: 174,\n }\n try {\n await pixiApp.init({\n width: initialSize.width,\n height: initialSize.height,\n backgroundAlpha: 0,\n antialias: true,\n autoDensity: true,\n preference: 'webgl',\n })\n } catch (error) {\n // init() can fail after allocating a partial renderer; cleanup is\n // best-effort because Pixi may not consider that partial app ready.\n try { pixiApp.destroy(RENDERER_DESTROY_OPTIONS, DESTROY_OPTIONS) } catch {}\n throw error\n }\n if (disposed) {\n pixiApp.destroy(RENDERER_DESTROY_OPTIONS, DESTROY_OPTIONS)\n return\n }\n app = pixiApp\n pixiApp.canvas.style.display = 'block'\n pixiApp.canvas.style.width = '100%'\n pixiApp.canvas.style.height = '100%'\n ctx.container.appendChild(pixiApp.canvas)\n // Keep a model that rejects during setup off Ticker.shared; from()\n // does not expose that partial instance to callers for disposal.\n trackContainerSize(pixiApp)\n const loaded = await vendor.Live2DModel.from(config.modelUrl, {\n autoUpdate: false,\n autoHitTest: false,\n autoFocus: false,\n textureOptions: TEXTURE_OPTIONS,\n })\n model = loaded\n if (disposed) {\n destroyResources()\n return\n }\n modelSourceSize = {\n width: Math.max(1, loaded.width),\n height: Math.max(1, loaded.height),\n }\n // Auto-fit the model into the container; the manifest scale multiplies\n // the fit and translate offsets from the center anchor.\n layoutModel(pixiApp, loaded, modelSourceSize, config)\n pixiApp.stage.addChild(loaded)\n modelAttached = true\n loaded.automator.autoUpdate = true\n // Resume the phase group once a tap motion finishes playing.\n loaded.on('motionFinish', () => {\n if (tapPlaying) {\n tapPlaying = false\n playGroup(phaseGroup)\n }\n })\n applyPhase(ctx.phase.get())\n unsubscribe = ctx.phase.subscribe(applyPhase)\n }\n\n void boot().catch(() => {\n try {\n destroyResources()\n } finally {\n if (!disposed) errorListener?.('load-failed')\n }\n })\n\n return {\n dispose() {\n if (disposed) return\n disposed = true\n destroyResources()\n },\n tap(x: number, y: number) {\n const current = model\n if (disposed || current === undefined) return\n const hits = current.hitTest(x, y)\n const allowed = config.hitAreas\n const hit = allowed === undefined ? hits.length > 0 : hits.some(name => allowed.includes(name))\n if (!hit) return\n const groups = current.internalModel.settings.motions ?? {}\n const group = groups[TAP_GROUP]\n if (!Array.isArray(group) || group.length === 0) return\n tapPlaying = true\n const index = group.length > 1 ? Math.floor(Math.random() * group.length) : 0\n void current.motion(TAP_GROUP, index)\n },\n onError(listener: (code: Live2dErrorCode) => void) {\n errorListener = listener\n },\n }\n },\n}\n","/**\n * frames2d renderer — plays the free-form named frame-sequence tracks of a\n * frames2d pet (manifest v2 'frames2d' block). The pet center picks the\n * track from the ActivityPhase stream (phase -> track map, idle fallback);\n * the gameplay driver may force a track through the handle's setState\n * override (drag/work/sleep/shop...), and a finished non-loop track settles\n * into its fallback, releasing the override when the fallback matches the\n * phase-mapped track. Rendering never throws: a broken track list degrades\n * to the first decodable frame, and the 1.2 s stall watchdog re-kicks the\n * playback chain after timer throttling.\n *\n * Frame presentation has two modes picked once at mount by capability\n * probing:\n * - Canvas bitmap buffer (default where createImageBitmap/fetch/2D context\n * exist): every frame is decoded exactly once into an ImageBitmap during\n * the warm pass and drawn onto one <canvas> - steady-state playback issues\n * zero DOM mutations and zero re-decodes (measured hotspot: swapping\n * <img>.src per frame drove image decode + invalidation every tick).\n * - Classic <img> fallback (jsdom/tests or missing APIs): identical to the\n * historical behavior - cache-warm Image elements plus guarded src swaps,\n * so environments without modern decoding keep working unchanged.\n *\n * @module @linxin666/dsh-pet/client/renderers/frames2d\n */\n\nimport { PET_RENDERER_API_VERSION, type PetRenderer, type PetRendererContext, type PetRendererHandle } from '../../contracts/renderer.ts'\nimport type { ActivityPhase } from '../../state.ts'\n\n/** One track as served inside the pet definition (browser URLs). */\nexport interface Frames2dTrackConfig {\n frames: string[]\n durations: number[]\n loop: boolean\n fallback?: string\n}\n\n/** The frames2d block as served inside the pet definition. */\nexport interface PetFrames2dConfig {\n tracks: Record<string, Frames2dTrackConfig>\n phases: Partial<Record<ActivityPhase, string>> & { idle: string }\n /** Selectable skins; each swaps the base idle target while selected. */\n skins?: Frames2dSkinConfig[]\n}\n\n/** One selectable skins entry as served inside the pet definition. */\nexport interface Frames2dSkinConfig {\n id: string\n label: string\n /** A declared looping track that becomes the base idle while selected. */\n idleTrack: string\n /** Click actions exclusive to this skin (roll by probability; miss → touch zones). */\n clickActions?: Frames2dSkinClickActionConfig[]\n /** Gameplay-state overrides (state -> track) swapped in while selected. */\n gameplayTracks?: Record<string, string>\n}\n\n/** One probability-rolled tap action a skin may declare. */\nexport interface Frames2dSkinClickActionConfig {\n track: string\n probability: number\n phrases?: string[]\n}\n\nexport interface Frames2dRendererHandle extends PetRendererHandle {\n /** Force a track id (gameplay override); undefined returns to phase mapping. */\n setState(track: string | undefined): void\n /**\n * Swap the base idle target (idle phase, unmapped phases and every\n * fallback back to idle) to a declared looping track — a skin. undefined\n * restores the manifest idle track.\n */\n setIdleTrack(track: string | undefined): void\n /** The track currently playing (diagnostics and tests). */\n currentTrack(): string\n}\n\n/** Stall watchdog period: a looping track stuck longer re-kicks its chain. */\nconst WATCHDOG_MS = 1200\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** Fail-closed client-side validation of the served frames2d block. */\nfunction validateFrames2dConfig(config: unknown): PetFrames2dConfig {\n if (!isRecord(config) || !isRecord(config.tracks) || !isRecord(config.phases)) {\n throw new Error('frames2d config requires tracks and phases objects')\n }\n const tracks: Record<string, Frames2dTrackConfig> = {}\n for (const [name, raw] of Object.entries(config.tracks)) {\n if (!isRecord(raw) || !Array.isArray(raw.frames) || raw.frames.length === 0\n || raw.frames.some(f => typeof f !== 'string' || f === '')\n || !Array.isArray(raw.durations) || raw.durations.length !== raw.frames.length\n || raw.durations.some(d => typeof d !== 'number' || !(d > 0))) {\n throw new Error('frames2d track ' + JSON.stringify(name) + ' needs same-length frames/durations')\n }\n tracks[name] = {\n frames: raw.frames as string[],\n durations: raw.durations as number[],\n loop: raw.loop !== false,\n ...(typeof raw.fallback === 'string' ? { fallback: raw.fallback } : {}),\n }\n }\n const phases = config.phases as Partial<Record<ActivityPhase, string>>\n if (typeof phases.idle !== 'string' || tracks[phases.idle] === undefined) {\n throw new Error('frames2d phases.idle must name an existing track')\n }\n // Skins: keep entries whose idleTrack survives validation and loops —\n // a non-looping base idle would settle into itself forever. Click actions\n // with an unresolvable track are dropped from that skin.\n let skins: Frames2dSkinConfig[] | undefined\n if (Array.isArray(config.skins)) {\n const resolved: Frames2dSkinConfig[] = []\n for (const skin of config.skins as unknown[]) {\n if (!isRecord(skin) || typeof skin.id !== 'string' || typeof skin.label !== 'string'\n || typeof skin.idleTrack !== 'string' || skin.idleTrack === '') continue\n const target = tracks[skin.idleTrack]\n if (target === undefined || !target.loop) continue\n let clickActions: Frames2dSkinClickActionConfig[] | undefined\n if (Array.isArray(skin.clickActions)) {\n const kept: Frames2dSkinClickActionConfig[] = []\n for (const action of skin.clickActions as unknown[]) {\n if (!isRecord(action) || typeof action.track !== 'string' || action.track === ''\n || typeof action.probability !== 'number' || !(action.probability > 0) || action.probability > 1) continue\n if (tracks[action.track] === undefined) continue\n kept.push({\n track: action.track,\n probability: action.probability,\n ...(Array.isArray(action.phrases) ? { phrases: action.phrases as string[] } : {}),\n })\n }\n if (kept.length > 0) clickActions = kept\n }\n let gameplayTracks: Record<string, string> | undefined\n if (isRecord(skin.gameplayTracks)) {\n const kept: Record<string, string> = {}\n for (const [state, trackName] of Object.entries(skin.gameplayTracks)) {\n if (typeof trackName !== 'string' || trackName === '' || tracks[trackName] === undefined) continue\n kept[state] = trackName\n }\n if (Object.keys(kept).length > 0) gameplayTracks = kept\n }\n resolved.push({\n id: skin.id,\n label: skin.label,\n idleTrack: skin.idleTrack,\n ...(clickActions === undefined ? {} : { clickActions }),\n ...(gameplayTracks === undefined ? {} : { gameplayTracks }),\n })\n }\n if (resolved.length > 0) skins = resolved\n }\n return { tracks, phases: phases as PetFrames2dConfig['phases'], ...(skins === undefined ? {} : { skins }) }\n}\n\ninterface DecodedFrame {\n source: ImageBitmap | HTMLImageElement\n width: number\n height: number\n}\n\nexport const frames2dRenderer: PetRenderer<PetFrames2dConfig> = {\n id: 'frames2d',\n apiVersion: PET_RENDERER_API_VERSION,\n validateConfig: validateFrames2dConfig,\n\n mount(ctx: PetRendererContext, config: PetFrames2dConfig): Frames2dRendererHandle {\n const reducedMotion = typeof window !== 'undefined'\n && typeof window.matchMedia === 'function'\n && window.matchMedia('(prefers-reduced-motion: reduce)').matches\n\n // Capability probe: the canvas path needs decode-to-bitmap, same-origin\n // fetch, and a real 2D context (jsdom returns null and falls back below).\n let canvas: HTMLCanvasElement | null = null\n let context2d: CanvasRenderingContext2D | null = null\n let img: HTMLImageElement | null = null\n try {\n if (typeof createImageBitmap === 'function' && typeof fetch === 'function') {\n const probe = document.createElement('canvas')\n const c2d = probe.getContext('2d')\n if (c2d !== null) {\n canvas = probe\n context2d = c2d\n }\n }\n } catch {\n canvas = null\n context2d = null\n }\n\n if (canvas !== null && context2d !== null) {\n canvas.dataset.dshPetFrames2d = ctx.petId\n canvas.draggable = false\n canvas.style.width = '100%'\n canvas.style.height = '100%'\n canvas.style.objectFit = 'contain'\n canvas.style.pointerEvents = 'none'\n ctx.container.appendChild(canvas)\n } else {\n img = document.createElement('img')\n img.dataset.dshPetFrames2d = ctx.petId\n img.alt = ''\n img.draggable = false\n img.style.width = '100%'\n img.style.height = '100%'\n img.style.objectFit = 'contain'\n img.style.pointerEvents = 'none'\n ctx.container.appendChild(img)\n }\n\n // Decode cache: one concurrent decode per URL, memoized forever (frames\n // are tiny same-origin webp files; failures resolve undefined and keep\n // the last painted frame instead of breaking playback).\n const decoding = new Map<string, Promise<DecodedFrame | undefined>>()\n const decodedAll: Promise<void>[] = []\n // All frame fetches funnel through a small pool. Full-library warm passes\n // on large pets fire 1100+ requests at once, which trips the browser's\n // in-flight request limit (net::ERR_INSUFFICIENT_RESOURCES) and fails\n // whole batches of frames while starving the rest of the page. Playback\n // demand jumps ahead of the warm backlog.\n const FRAME_POOL_LIMIT = 8\n const frameQueue: Array<{ url: string; release: () => void }> = []\n let activeFrames = 0\n\n const decodeFrame = async (url: string): Promise<DecodedFrame | undefined> => {\n try {\n const response = await fetch(url)\n if (!response.ok) throw new Error('http ' + response.status)\n const bitmap = await createImageBitmap(await response.blob())\n return { source: bitmap, width: bitmap.width, height: bitmap.height }\n } catch {\n // Fail-open: classic Image decode keeps non-modern runtimes alive.\n return await new Promise<DecodedFrame | undefined>((resolve) => {\n try {\n const pre = new Image()\n pre.onload = (): void => {\n resolve(pre.naturalWidth > 0 ? { source: pre, width: pre.naturalWidth, height: pre.naturalHeight } : undefined)\n }\n pre.onerror = (): void => resolve(undefined)\n pre.src = url\n } catch {\n resolve(undefined)\n }\n })\n }\n }\n\n const pumpFrames = (): void => {\n while (activeFrames < FRAME_POOL_LIMIT && frameQueue.length > 0) {\n const queued = frameQueue.shift()!\n activeFrames += 1\n queued.release()\n }\n }\n\n const loadFrame = (url: string, jump = false): Promise<DecodedFrame | undefined> => {\n // Jump first: warm-enqueued frames already carry their memo, so a\n // playback demand must reorder the unstarted entry before the cache\n // lookup short-circuits.\n if (jump) {\n const index = frameQueue.findIndex((queued) => queued.url === url)\n if (index > 0) frameQueue.unshift(frameQueue.splice(index, 1)[0]!)\n }\n const cached = decoding.get(url)\n if (cached !== undefined) return cached\n let release!: () => void\n const gate = new Promise<void>((resolve) => { release = resolve })\n const job: Promise<DecodedFrame | undefined> = gate.then(() => (disposed ? undefined : decodeFrame(url)))\n job.then(\n (frame) => { if (frame === undefined) decoding.delete(url) },\n () => decoding.delete(url),\n )\n void job.finally(() => {\n activeFrames -= 1\n pumpFrames()\n })\n decoding.set(url, job)\n decodedAll.push(job.then(() => undefined, () => undefined))\n const entry = { url, release }\n if (jump) frameQueue.unshift(entry)\n else frameQueue.push(entry)\n pumpFrames()\n return job\n }\n\n let disposed = false\n let timer: ReturnType<typeof setTimeout> | undefined\n let watchdog: ReturnType<typeof setInterval> | undefined\n let track: string = config.phases.idle\n let frameIndex = 0\n let lastAdvance = Date.now()\n let override: string | undefined\n // Skin base idle: every \"back to idle\" target resolves through this\n // (idle phase, unmapped phases and fallbacks), so a selected skin swaps\n // the pet's resting look without touching gameplay tracks.\n let baseIdle: string = config.phases.idle\n let drawToken = 0\n let lastDrawnUrl: string | undefined\n\n const trackForPhase = (phase: ActivityPhase): string => {\n const mapped = config.phases[phase]\n if (mapped === undefined) return baseIdle\n const target = mapped === config.phases.idle ? baseIdle : mapped\n return config.tracks[target] !== undefined ? target : baseIdle\n }\n\n /** Canvas path: paints the newest requested frame; stale draws drop out. */\n const paintCanvas = (url: string): void => {\n if (context2d === null || canvas === null) return\n const myToken = ++drawToken\n void loadFrame(url, true).then((frame) => {\n if (disposed || frame === undefined || myToken !== drawToken) return\n if (lastDrawnUrl === url) return\n lastDrawnUrl = url\n // Resizing clears the canvas, so size only when it actually differs.\n if (canvas.width !== frame.width || canvas.height !== frame.height) {\n canvas.width = frame.width\n canvas.height = frame.height\n } else {\n context2d.clearRect(0, 0, canvas.width, canvas.height)\n }\n context2d.drawImage(frame.source as CanvasImageSource, 0, 0)\n }).catch(() => { /* keep last frame */ })\n }\n\n const show = (trackId: string, index: number): void => {\n const def = config.tracks[trackId]\n const url = def?.frames[index]\n if (url === undefined) return\n if (img !== null) {\n if (img.getAttribute('src') !== url) img.src = url\n return\n }\n paintCanvas(url)\n }\n\n const schedule = (ms: number): void => {\n if (disposed) return\n timer = setTimeout(tick, ms)\n }\n\n function tick(): void {\n if (disposed) return\n const def = config.tracks[track]\n if (def === undefined || def.frames.length === 0) return\n lastAdvance = Date.now()\n const next = frameIndex + 1\n if (next < def.frames.length) {\n frameIndex = next\n show(track, frameIndex)\n schedule(def.durations[frameIndex] ?? 200)\n return\n }\n if (def.loop) {\n frameIndex = 0\n show(track, frameIndex)\n schedule(def.durations[frameIndex] ?? 200)\n return\n }\n // Non-loop completion: settle into the fallback — an explicit fallback to\n // a real track wins, except when it points back at the manifest idle\n // (that resolves through the skin base idle); anything else lands on\n // the skin base idle. When the settle target is what the phase map\n // plays anyway, release the gameplay override.\n const target = def.fallback !== undefined && config.tracks[def.fallback] !== undefined\n ? (def.fallback === config.phases.idle ? baseIdle : def.fallback)\n : baseIdle\n if (target === trackForPhase(ctx.phase.get())) override = undefined\n play(target)\n }\n\n function play(trackId: string): void {\n if (disposed) return\n if (config.tracks[trackId] === undefined) trackId = baseIdle\n if (timer !== undefined) clearTimeout(timer)\n track = trackId\n frameIndex = 0\n lastAdvance = Date.now()\n show(track, frameIndex)\n if (reducedMotion) return\n const def = config.tracks[track]!\n schedule(def.durations[0] ?? 200)\n }\n\n const unsubscribe = ctx.phase.subscribe((phase) => {\n if (override !== undefined) return\n const target = trackForPhase(phase)\n if (target !== track) play(target)\n })\n\n // Watchdog: a throttled-away timer (background tab) leaves the chain\n // dead; re-kick when a looping track has not advanced on schedule.\n if (!reducedMotion) {\n watchdog = setInterval(() => {\n if (disposed) return\n const def = config.tracks[track]\n if (def === undefined || !def.loop) return\n const expected = (def.durations[frameIndex] ?? 200) + WATCHDOG_MS\n if (Date.now() - lastAdvance > expected) tick()\n }, WATCHDOG_MS)\n }\n\n // Warm pass: decode every frame up front (tiny same-origin webp files)\n // so loops and phase switches never wait on a first decode - same intent\n // as the historical Image-cache warm loop, now feeding the decode cache.\n // Phase-reachable tracks enqueue first so early switches never trail the\n // full warm backlog; demand loads jump the queue regardless.\n const warmTrackIds: string[] = [\n ...new Set([...Object.values(config.phases), config.phases.idle]),\n ]\n for (const warmTrack of [...warmTrackIds, ...Object.keys(config.tracks)].map(\n (id) => config.tracks[id],\n )) {\n if (warmTrack === undefined) continue\n for (const warmUrl of warmTrack.frames) void loadFrame(warmUrl)\n }\n\n play(track)\n\n let disposedOnce = false\n const dispose = (): void => {\n if (disposedOnce) return\n disposedOnce = true\n disposed = true\n unsubscribe()\n if (timer !== undefined) clearTimeout(timer)\n if (watchdog !== undefined) clearInterval(watchdog)\n // Release decoded bitmaps after pending decodes settle; close() is\n // browser-only, so guard it for exotic hosts. Queued-but-unstarted\n // frames release immediately as no-ops so the settle barrier drains.\n for (const queued of frameQueue.splice(0)) queued.release()\n void Promise.allSettled(decodedAll).then(() => {\n for (const job of decoding.values()) {\n void job.then((frame) => {\n try {\n const maybeClose = (frame?.source as { close?: () => void } | undefined)?.close\n if (typeof maybeClose === 'function' && frame !== undefined) maybeClose.call(frame.source)\n } catch { /* already released */ }\n }).catch(() => { /* never rejects anyway */ })\n }\n decoding.clear()\n })\n canvas?.remove()\n img?.remove()\n }\n ctx.onCleanup(dispose)\n\n return {\n dispose,\n setState(next: string | undefined): void {\n if (disposed) return\n if (next === undefined) {\n override = undefined\n const target = trackForPhase(ctx.phase.get())\n if (target !== track) play(target)\n return\n }\n if (config.tracks[next] === undefined) return\n override = next\n if (next !== track) play(next)\n },\n setIdleTrack(next: string | undefined): void {\n if (disposed) return\n if (next === undefined || (config.tracks[next] !== undefined && config.tracks[next]!.loop)) {\n baseIdle = next ?? config.phases.idle\n }\n // A skin only owns the resting look: re-resolve only when no\n // gameplay override is active (active overrides end into baseIdle).\n if (override === undefined) {\n const target = trackForPhase(ctx.phase.get())\n if (target !== track) play(target)\n }\n },\n currentTrack(): string {\n return track\n },\n }\n },\n}\n","/**\n * Cross-bundle-instance teardown slot for the page-global pet root\n * (issue #785).\n *\n * A client bundle swap (HMR rebuilt frame, plugin update, duplicate\n * injection) runs a new apply body while the previous instance's fiber\n * may still be draining. Module state does not survive the swap, so a\n * closure guard only sees one apply body and the previous instance's\n * container keeps sitting on document.body: the page shows two pets\n * until a full refresh. The slot rides globalThis (which does survive)\n * so a re-apply can find the previous instance and unmount its React\n * root cleanly before mounting its own; the previous fiber's later\n * disposal stays a no-op through the idempotent teardowns.\n */\n\nconst SLOT = Symbol.for('dsh-pet.client-ui-teardown')\n\ninterface TeardownSlot {\n [SLOT]?: (() => void) | undefined\n}\n\n/**\n * Claim the page-global pet UI slot with the current instance's teardown\n * (React root unmount + container removal + poll stop).\n * @param teardown - what a later instance runs to take the slot over.\n * @returns a disposer that clears the slot when the current instance's\n * UI is torn down (settings toggle, takeover, or fiber disposal).\n */\nexport function registerPetUiTeardown(teardown: () => void): () => void {\n const slot = globalThis as TeardownSlot\n slot[SLOT] = teardown\n return () => {\n if (slot[SLOT] === teardown) slot[SLOT] = undefined\n }\n}\n\n/**\n * Run the previous instance's teardown if one is registered, so the\n * re-applying instance becomes the sole owner of the page-global pet\n * root. No-op when the previous fiber already tore down cleanly.\n */\nexport function takeoverPetUiTeardown(): void {\n const slot = globalThis as TeardownSlot\n const teardown = slot[SLOT]\n slot[SLOT] = undefined\n teardown?.()\n}\n","// Generated by scripts/sync-shared.mjs from shared/client/settings/PluginSettingsCard.tsx. Do not edit this copy; edit the shared source and run \"node scripts/sync-shared.mjs\".\n/**\n * Family-shared chrome for plugin settings cards: a disclosure header naming\n * the plugin and what its settings govern, the controls inside, and the save\n * that writes them. Renders nothing while the namespace is unavailable — a\n * deployment that does not compose the owning plugin should show no trace of\n * it. Inlined into each consumer's client bundle; mirrors the official\n * ui-plugin-config PluginCard in a self-contained slice.\n */\n\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState, type KeyboardEvent, type ReactNode } from 'react'\nimport type { CardShell } from './settings-form.ts'\nimport css from './settings-card.module.css'\n\n/** Copy keys the card chrome itself reads; every consumer locale carries this shared vocabulary. */\nexport const CARD_COPY_KEYS = [\n 'settings.collapse',\n 'settings.expand',\n 'settings.notExposed',\n 'settings.unsaved',\n 'settings.readOnly',\n 'settings.saveFailed',\n 'settings.discard',\n 'settings.save',\n 'settings.saving',\n] as const\n\n/** Copy key the card chrome itself reads. */\nexport type CardCopyKey = (typeof CARD_COPY_KEYS)[number]\n\n/** Key domain for the plugin's own copy (inferred per consumer). */\nexport type SettingsCardKey<TKey extends string = string> = TKey | CardCopyKey\n\n/** Card chrome shared by every plugin settings card. */\nexport interface PluginSettingsCardProps<TKey extends string = string> {\n /** Locale reader for this card's copy. */\n t: (key: SettingsCardKey<TKey>, params?: Record<string, string | number>) => string\n /** Locale key of the plugin's name. */\n titleKey: TKey\n /** Locale key of the line describing what this plugin's settings govern. */\n descriptionKey: TKey\n /** Optional rich header description rendered instead of the plain t(descriptionKey) text (the plain text stays as the tooltip). */\n descriptionNode?: ReactNode\n /** The card's form state: availability, writability, and what a save would do. */\n state: CardShell\n /** Write every staged edit. */\n onSave: () => void\n /** Drop every staged edit. */\n onDiscard: () => void\n /**\n * Render the controls expanded on arrival (still collapsible). Defaults to\n * true: promoted first-level sections show their settings immediately.\n */\n defaultOpen?: boolean\n /**\n * Render without the collapse affordance: a static header and an always\n * visible body. Used by first-level settings sections whose nav entry\n * already provides the selection.\n */\n alwaysOpen?: boolean\n /**\n * Hide the save/discard footer: cards whose body applies its own changes\n * immediately (an embedded external settings section) have no staged\n * edits, so the footer would sit there permanently disabled.\n */\n hideFooter?: boolean\n /** The plugin's controls. */\n children: ReactNode\n}\n\n/**\n * Render one plugin settings card.\n * @param props - the plugin's copy keys, its form state, and its controls.\n * @returns the card, or nothing while the namespace is still loading.\n */\nexport function PluginSettingsCard<TKey extends string = string>(props: PluginSettingsCardProps<TKey>) {\n const [open, setOpen] = useState(props.defaultOpen ?? true)\n const { state, alwaysOpen } = props\n if (!state.available) return null\n const title = props.t(props.titleKey)\n const description = props.t(props.descriptionKey)\n const blocked = !state.dirty || state.invalid || state.saving\n const expanded = alwaysOpen === true || open\n const cardClass = expanded ? `${css.cardOpen} ${css.card}` : css.card\n // With alwaysOpen the nav entry already provides the selection, so the\n // header is a static title row instead of a disclosure button.\n const header = alwaysOpen === true\n ? (\n <div className={css.headerStatic}>\n <span className={css.headText}>\n <span className={css.name} title={title}>{title}</span>\n <span className={css.description} title={description}>{props.descriptionNode ?? description}</span>\n </span>\n {state.dirty ? <span className={css.pending} title={props.t('settings.unsaved')}>{props.t('settings.unsaved')}</span> : null}\n </div>\n )\n : (\n <button\n type=\"button\"\n className={css.header}\n aria-expanded={open}\n aria-label={`${props.t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`}\n onClick={() => { setOpen(!open) }}\n >\n <span className={css.headText}>\n <span className={css.name} title={title}>{title}</span>\n <span className={css.description} title={description}>{props.descriptionNode ?? description}</span>\n </span>\n {state.dirty ? <span className={css.pending} title={props.t('settings.unsaved')}>{props.t('settings.unsaved')}</span> : null}\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 14 14\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={open ? `${css.chevron} ${css.chevronOpen}` : css.chevron}\n >\n <path\n d=\"M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z\"\n fill=\"currentColor\"\n />\n </svg>\n </button>\n )\n // The namespace exists but the Host does not serve it to this client (the\n // official settings allowlist omits third-party namespaces): show a card\n // that explains the gap instead of vanishing, so a missing card never\n // reads as a missing plugin.\n if (!state.exposed) {\n return (\n <li className={cardClass}>\n {header}\n {expanded\n ? (\n <div className={css.body}>\n <p className={css.notExposed} role=\"status\">{props.t('settings.notExposed')}</p>\n </div>\n )\n : null}\n </li>\n )\n }\n return (\n <li className={cardClass}>\n {header}\n {expanded\n ? (\n <div className={css.body}>\n {!state.writable ? <p className={css.readOnly} role=\"status\">{props.t('settings.readOnly')}</p> : null}\n {props.children}\n {props.hideFooter === true\n ? null\n : (\n <div className={css.footer}>\n {state.failed\n ? (\n <p className={css.failed} role=\"status\">\n {props.t('settings.saveFailed')}{state.failedReason ? ' - ' + state.failedReason : ''}\n </p>\n )\n : null}\n <button\n type=\"button\"\n className={css.discard}\n disabled={!state.dirty || state.saving}\n onClick={props.onDiscard}\n >\n {props.t('settings.discard')}\n </button>\n <button\n type=\"button\"\n className={css.save}\n disabled={blocked}\n onClick={props.onSave}\n >\n {props.t(!state.saving ? 'settings.save' : 'settings.saving')}\n </button>\n </div>\n )}\n </div>\n )\n : null}\n </li>\n )\n}\n\n/** Props every field control needs regardless of its value type. */\nexport interface FieldProps {\n /** Stable id associating the label with its control. */\n id: string\n /** Visible label. */\n label: string\n /** One-line explanation rendered under the control. */\n hint: string\n /** Draft text this control renders. */\n text: string\n /** True when saving would leave a user-layer entry for this field. */\n overridden: boolean\n /** True when the draft is not a value this field accepts. */\n invalid: boolean\n /** Copy for the overridden badge. */\n overriddenLabel: string\n /** Copy for the reset control. */\n resetLabel: string\n /** Copy shown in place of the hint while the draft is invalid. */\n invalidLabel: string\n /** Disables every control (read-only document, or an unavailable namespace). */\n disabled: boolean\n /** Stage draft text. */\n onEdit: (text: string) => void\n /** Stage a clear so the field re-inherits the composition layer. */\n onReset: () => void\n}\n\n/** A staged value field. `numeric` only hints the keypad: which drafts a field accepts is decided by its spec. */\nexport function ValueField(props: FieldProps & {\n /** Hints a numeric keypad without narrowing what the control accepts. */\n numeric?: boolean\n /** Placeholder shown while the draft is empty. */\n placeholder?: string\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <input\n id={props.id}\n className={props.invalid ? css.inputInvalid : css.input}\n type=\"text\"\n {...props.numeric === true ? { inputMode: 'numeric' as const } : {}}\n {...props.invalid ? { 'aria-invalid': true } : {}}\n value={props.text}\n placeholder={props.placeholder ?? ''}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n />\n <p className={props.invalid ? css.invalid : css.hint}>\n {props.invalid ? props.invalidLabel : props.hint}\n </p>\n </div>\n )\n}\n\nconst NON_SKIN_BODY_MARKERS = new Set(['dshSkinCenter', 'dshSidebarCollapsed'])\n// 检测使用使用皮肤,用了皮肤退回原生select样式,防止样式冲突。默认外观下使用优化后的select样式。\nfunction isSkinActive(): boolean {\n const datasetList = Object.keys(document.body.dataset)\n const isActive = datasetList.some(key => key.startsWith('dsh') && !NON_SKIN_BODY_MARKERS.has(key))\n return isActive\n}\n\ninterface SelectOption {\n value: string\n label: string\n}\n\nconst SELECT_CLOSE_MS = 100\n\n/**\n * The shared dual-mode select control. While an appearance skin is active it\n * renders the legacy native `<select>` untouched, so element-level skin\n * selectors keep working; under the default appearance it renders a\n * self-drawn `role=\"listbox\"` popup whose open/close is transition-animated.\n * Staged cards reach it through BooleanField/ChoiceField; immediate-apply\n * editors (the side-card prefs) bind it directly through onEdit.\n * 双模式下拉框:皮肤激活时用原生 select,默认外观用自绘动画弹层。\n */\nexport function SelectField(props: {\n id: string\n options: ReadonlyArray<SelectOption>\n value: string\n disabled: boolean\n invalid: boolean\n onEdit: (text: string) => void\n}) {\n const { id, options, value } = props\n const [open, setOpen] = useState(false)\n const [closing, setClosing] = useState(false)\n const [phase, setPhase] = useState<'initial' | 'open'>('initial')\n const [activeIndex, setActiveIndex] = useState(0)\n const closeTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const wrapRef = useRef<HTMLDivElement | null>(null)\n const popupRef = useRef<HTMLDivElement | null>(null)\n\n const currentIndex = () => {\n const index = options.findIndex(option => option.value === value)\n return index >= 0 ? index : 0\n }\n\n const close = useCallback(() => {\n if (closeTimer.current !== undefined) clearTimeout(closeTimer.current)\n setClosing(true)\n closeTimer.current = setTimeout(() => {\n setClosing(false)\n setOpen(false)\n }, SELECT_CLOSE_MS)\n }, [])\n\n const openPopup = () => {\n if (closeTimer.current !== undefined) clearTimeout(closeTimer.current)\n setActiveIndex(currentIndex())\n setPhase('initial')\n setClosing(false)\n setOpen(true)\n }\n\n const commit = (index: number) => {\n const option = options[index]\n if (option) props.onEdit(option.value)\n close()\n }\n\n const onTriggerClick = () => {\n if (props.disabled) return\n if (open && !closing) close()\n else openPopup()\n }\n\n const onKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n if (props.disabled) return\n const count = options.length\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowUp':\n case 'Enter':\n case ' ':\n event.preventDefault()\n if (!open) {\n openPopup()\n } else if (!closing) {\n if (event.key === 'ArrowDown') setActiveIndex(index => (index + 1) % count)\n else if (event.key === 'ArrowUp') setActiveIndex(index => (index - 1 + count) % count)\n else commit(activeIndex)\n }\n break\n case 'Escape':\n if (open) {\n event.preventDefault()\n event.stopPropagation()\n close()\n }\n break\n case 'Tab':\n if (open) close()\n break\n }\n }\n\n useEffect(() => () => {\n if (closeTimer.current !== undefined) clearTimeout(closeTimer.current)\n }, [])\n useLayoutEffect(() => {\n if (open && !closing && phase === 'initial') {\n void popupRef.current?.offsetHeight\n setPhase('open')\n }\n }, [open, closing, phase])\n\n useEffect(() => {\n if (!open) return\n const onPointerDown = (event: PointerEvent) => {\n const target = event.target\n if (target instanceof Node && !wrapRef.current?.contains(target)) close()\n }\n document.addEventListener('pointerdown', onPointerDown)\n return () => document.removeEventListener('pointerdown', onPointerDown)\n }, [open, close])\n\n useEffect(() => {\n if (props.disabled && open) close()\n }, [props.disabled, open, close])\n\n // 使用了皮肤则退回原生select样式防止样式冲突。\n if (isSkinActive()) {\n return (\n <select\n id={id}\n className={css.select}\n value={value}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n >\n {options.map(option => (\n <option key={option.value} value={option.value}>{option.label}</option>\n ))}\n </select>\n )\n }\n\n const label = options.find(option => option.value === value)?.label ?? ''\n const popupClass = closing\n ? `${css.selectPopup} ${css.selectPopupClose}`\n : phase === 'open'\n ? `${css.selectPopup} ${css.selectPopupOpen}`\n : css.selectPopup\n return (\n <div className={css.selectWrap} ref={wrapRef}>\n <button\n type=\"button\"\n id={id}\n className={`${css.select} ${css.selectButton}`}\n disabled={props.disabled}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-activedescendant={open ? `${id}-o${activeIndex}` : undefined}\n aria-invalid={props.invalid || undefined}\n onClick={onTriggerClick}\n onKeyDown={onKeyDown}\n >\n <span className={css.selectLabel}>{label}</span>\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 14 14\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={open ? `${css.selectChevron} ${css.selectChevronOpen}` : css.selectChevron}\n aria-hidden=\"true\"\n >\n <path\n d=\"M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z\"\n fill=\"currentColor\"\n />\n </svg>\n </button>\n {open\n ? (\n <div className={popupClass} role=\"listbox\" ref={popupRef}>\n {options.map((option, index) => (\n <div\n key={option.value}\n id={`${id}-o${index}`}\n role=\"option\"\n aria-selected={option.value === value}\n className={`${css.selectOption}${option.value === value ? ` ${css.selectOptionSelected}` : ''}${index === activeIndex && !closing ? ` ${css.selectOptionActive}` : ''}`}\n onClick={() => { commit(index) }}\n >\n {option.label}\n </div>\n ))}\n </div>\n )\n : null}\n </div>\n )\n}\n\n/** A staged boolean field: 继承 / 开 / 关. */\nexport function BooleanField(props: FieldProps & {\n /** Copy for the inherit option. */\n inheritLabel: string\n /** Copy for the on option. */\n onLabel: string\n /** Copy for the off option. */\n offLabel: string\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <SelectField\n id={props.id}\n options={[\n { value: '', label: props.inheritLabel },\n { value: 'true', label: props.onLabel },\n { value: 'false', label: props.offLabel },\n ]}\n value={props.text}\n disabled={props.disabled}\n invalid={props.invalid}\n onEdit={props.onEdit}\n />\n <p className={css.hint}>{props.hint}</p>\n </div>\n )\n}\n\n/** A staged enumerated field rendered as a select. */\nexport function ChoiceField(props: FieldProps & {\n /** Copy for the inherit option (draft text is the empty string). */\n inheritLabel: string\n /** Choices rendered in order; `value` is the draft/stored text. */\n choices: ReadonlyArray<{ value: string; label: string }>\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <SelectField\n id={props.id}\n options={[{ value: '', label: props.inheritLabel }, ...props.choices]}\n value={props.text}\n disabled={props.disabled}\n invalid={props.invalid}\n onEdit={props.onEdit}\n />\n <p className={props.invalid ? css.invalid : css.hint}>\n {props.invalid ? props.invalidLabel : props.hint}\n </p>\n </div>\n )\n}\n","// Generated by scripts/sync-shared.mjs from shared/client/settings/settings-form.ts. Do not edit this copy; edit the shared source and run \"node scripts/sync-shared.mjs\".\n/**\n * Staged form model behind the plugin settings card. A card stages what the\n * user types and writes it only when they save — the settings write is a\n * durable, revision-fenced document mutation, so staging keeps what is on\n * screen exactly what a save would store. Family-shared slice inlined into\n * each plugin's client bundle; mirrors the official ui-plugin-config\n * card-store pattern.\n */\n\nimport type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type { SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport { createSnapshotStore } from '@deepseek-ai/dsh-client-store'\n\n/** The write one field's staged text performs when the card is saved. */\nexport type FieldWrite =\n | { kind: 'set'; value: unknown }\n | { kind: 'clear' }\n\n/** How one field converts between its stored value and its draft text. */\nexport interface FieldSpec {\n /** Field name inside the namespace section. */\n field: string\n /**\n * Whether the Host treats this field as a secret and redacts its value from\n * the read-back (role('secret') in the section schema). Redacted secrets are\n * never compared against the draft on save: the Host strips them from every\n * wire view layer, so the settled snapshot carries nothing to read back. A\n * staged secret set is judged by the mutation settling; the rest of its\n * batch, when one exists, is still judged by read-back, and the atomic\n * mutation lands every write or none.\n */\n secret?: boolean\n /** Render a stored value as draft text; the empty string when the section carries none. */\n format: (value: unknown) => string\n /**\n * The write this draft text stages, or undefined when the text is not a\n * value this field accepts — which blocks the save rather than discarding it.\n */\n parse: (text: string) => FieldWrite | undefined\n}\n\n/** One field as the card renders it. */\nexport interface FieldState {\n /** Draft text the control renders. */\n text: string\n /** Whether saving would leave a user-layer entry for this field. */\n overridden: boolean\n /** Whether the draft is not a value this field accepts, which blocks saving. */\n invalid: boolean\n}\n\n/** Form state every plugin settings card shares. */\nexport interface CardShell {\n /** False while the namespace is still loading; the card renders nothing. */\n available: boolean\n /**\n * Whether the namespace is actually served to this client. False when the\n * Host deployment does not expose it (e.g. the owning plugin's settings\n * domain is not mounted): the card renders an explanation\n * instead of its form, so a missing namespace never looks like a missing\n * plugin.\n */\n exposed: boolean\n /** Whether the Host document accepts writes. */\n writable: boolean\n /** Whether the form holds edits that a save would write. */\n dirty: boolean\n /** Whether any staged draft is invalid, which blocks the save. */\n invalid: boolean\n /** Whether a save is crossing the wire. */\n saving: boolean\n /** Whether the last save did not land as staged; cleared by the next edit or save. */\n failed: boolean\n /**\n * The rejection code/message the Host returned for the last failed save,\n * surfaced next to the generic failure text. Undefined while no save has\n * failed (or the failure carried no server reason).\n */\n failedReason?: string\n}\n\n/** The write actions the card's slot entry injects. */\nexport interface CardActions {\n /** Stage draft text for one field. */\n edit: (field: string, text: string) => void\n /** Stage a clear, so saving lets the field re-inherit the composition layer. */\n resetField: (field: string) => void\n /** Write every staged edit, then re-seed from what the Host accepted. */\n save: () => void\n /** Drop every staged edit. */\n discard: () => void\n}\n\n/** One field's staged edit. */\ninterface StagedEdit {\n /** Draft text the control renders. */\n text: string\n /** True when this edit clears the field whatever text it shows. */\n clear: boolean\n}\n\n/** One staged edit resolved into the write a save performs. */\ninterface PlannedWrite {\n /** Field this entry writes. */\n field: string\n /** The durable write this entry performs, inside the save's one atomic mutation. */\n op: BatchedWrite\n /**\n * Read the settled snapshot back and report whether the Host holds this\n * write's effect. Undefined when the draft is not a value the field\n * accepts: there is nothing to write, and the entry blocks the save.\n */\n judge: (() => boolean) | undefined\n}\n\n/** One durable write inside the save's atomic scope mutation. */\nexport interface BatchedWrite {\n /** Field this entry writes. */\n field: string\n /** set stores a value; unset drops the leaf. */\n op: 'set' | 'unset'\n /** Value for op set (absent for unset). */\n value?: unknown\n}\n\n/** Constraints a numeric field's accepted drafts must satisfy, mirroring the host schema. */\nexport interface NumberConstraints {\n /** The accepted value must be a whole number. */\n integer?: boolean\n /** The accepted value must be at least this. */\n min?: number\n}\n\n/** A whole- or decimal-number field. An empty draft clears the field; any other draft that is not a finite number within the constraints blocks the save. */\nexport function numberField(field: string, constraints: NumberConstraints = {}): FieldSpec {\n const { integer = false, min } = constraints\n return {\n field,\n format: value => typeof value === 'number' ? String(value) : '',\n parse: (text) => {\n const trimmed = text.trim()\n if (trimmed === '') return { kind: 'clear' }\n const parsed = Number(trimmed)\n if (!Number.isFinite(parsed)) return undefined\n if (integer && !Number.isInteger(parsed)) return undefined\n if (min !== undefined && parsed < min) return undefined\n return { kind: 'set', value: parsed }\n },\n }\n}\n\n/** A free-text field. An empty draft clears the field. */\nexport function textField(field: string): FieldSpec {\n return {\n field,\n format: value => typeof value === 'string' ? value : '',\n parse: (text) => {\n const trimmed = text.trim()\n return trimmed === '' ? { kind: 'clear' } : { kind: 'set', value: trimmed }\n },\n }\n}\n\n/**\n * A free-text field the Host treats as a secret and redacts from the read-back\n * (role('secret') in the section schema). The card still edits it like text,\n * but a save never compares the redacted value back: the staged set is judged\n * by the mutation settling (see {@link FieldSpec.secret}).\n */\nexport function secretField(field: string): FieldSpec {\n return { ...textField(field), secret: true }\n}\n\n/** A boolean field, edited through true/false draft text. */\nexport function booleanField(field: string): FieldSpec {\n return {\n field,\n format: value => typeof value === 'boolean' ? String(value) : '',\n parse: (text) => {\n const trimmed = text.trim()\n if (trimmed === '') return { kind: 'clear' }\n if (trimmed === 'true') return { kind: 'set', value: true }\n if (trimmed === 'false') return { kind: 'set', value: false }\n return undefined\n },\n }\n}\n\n/** An enumerated string field; only the listed choices are accepted. An empty draft clears the field. */\nexport function choiceField(field: string, choices: readonly string[]): FieldSpec {\n return {\n field,\n format: value => typeof value === 'string' && choices.includes(value) ? value : '',\n parse: (text) => {\n if (text === '') return { kind: 'clear' }\n return choices.includes(text) ? { kind: 'set', value: text } : undefined\n },\n }\n}\n\n/**\n * Stages one card's edits over one settings namespace and writes them on save.\n *\n * The Host is the only authority on whether a value was accepted — its\n * validators own the constraints no schema can express — so the outcome is\n * read back from the section rather than predicted here. A save that did not\n * land keeps its drafts, so the user can correct them instead of retyping.\n */\nexport class CardForm<T> {\n private readonly specs: Map<string, FieldSpec>\n private readonly staged = new Map<string, StagedEdit>()\n private readonly listeners = new Set<() => void>()\n /** The scope subscription installed in the constructor; released by dispose(). */\n private readonly disposeScope: () => void\n private disposed = false\n private saving = false\n private failed = false\n private failedReason: string | undefined\n\n /** @param scope - the bound settings scope for this card's namespace. */\n constructor(\n private readonly scope: SettingsScope<T>,\n specs: FieldSpec[],\n ) {\n this.specs = new Map(specs.map(spec => [spec.field, spec]))\n this.disposeScope = scope.subscribe(() => { this.publish() })\n }\n\n /**\n * Release the scope subscription and every bound store listener. The card\n * must call this on teardown; later calls are no-ops.\n */\n dispose(): void {\n if (this.disposed) return\n this.disposed = true\n this.disposeScope()\n this.listeners.clear()\n }\n\n /** Publish a projection of this form, rebuilt whenever the scope or a draft changes. */\n bind<S>(project: () => S): SnapshotStore<S> {\n const store = createSnapshotStore(project())\n this.listeners.add(() => { store.set(project()) })\n return store\n }\n\n /** Read the card-level state: what the Host serves, and what a save would do. */\n shell(): CardShell {\n const snapshot = this.scope.getSnapshot()\n const plan = this.plan()\n return {\n available: snapshot.status !== 'loading',\n exposed: snapshot.status === 'ready',\n writable: snapshot.writable,\n dirty: plan.length > 0,\n invalid: plan.some(item => item.judge === undefined),\n saving: this.saving,\n failed: this.failed,\n ...this.failedReason === undefined ? {} : { failedReason: this.failedReason },\n }\n }\n\n /** Read one field's state from the effective section and its staged draft. */\n field(field: string): FieldState {\n const spec = this.specOf(field)\n const staged = this.staged.get(field)\n if (staged === undefined) {\n return { text: spec.format(this.sectionValue(field)), overridden: this.stored(field), invalid: false }\n }\n const write = staged.clear ? { kind: 'clear' as const } : spec.parse(staged.text)\n return {\n text: staged.text,\n overridden: write?.kind === 'set',\n invalid: write === undefined,\n }\n }\n\n /** The actions the card's slot registration injects. */\n actions(): CardActions {\n return {\n edit: (field, text) => { this.stage(field, { text, clear: false }) },\n resetField: (field) => {\n this.stage(field, { text: this.specOf(field).format(this.baseValue(field)), clear: true })\n },\n save: () => { void this.save() },\n discard: () => {\n if (this.staged.size === 0 && !this.failed) return\n this.staged.clear()\n this.failed = false\n this.failedReason = undefined\n this.publish()\n },\n }\n }\n\n /**\n * Write every staged edit in one atomic scope mutation, then re-seed from\n * what the Host accepted.\n *\n * The whole batch rides one mutate, so cross-field validate hooks\n * (baseURL+model) judge it as a unit: the Host either applies every write\n * or refuses the batch. The 0.1.2 scope contract never rejects a refused\n * mutation — the scope recovers with a fresh Host view and resolves — so\n * resolution alone proves nothing: the outcome is judged by reading the\n * settled snapshot back, one planned write at a time, and one missed write\n * fails the whole save. A scope that still rejects on refusal (the dsh-web\n * bridge scope) reports through the same failure path with its rejection\n * message. A save that did not land keeps its drafts, so the user can\n * correct them instead of retyping.\n * @returns settlement after the mutation and the read-back.\n */\n async save(): Promise<void> {\n const plan = this.plan()\n const valid = plan.filter((item): item is PlannedWrite & { judge: () => boolean } => item.judge !== undefined)\n if (plan.length === 0 || this.saving || valid.length !== plan.length) return\n // Snapshot the staged entries this save writes, so an edit staged while it\n // is in flight (which replaces the same key) survives: only delete the key\n // when the entry is still the one this save started from.\n const pending = new Map<string, StagedEdit | undefined>()\n for (const item of plan) pending.set(item.field, this.staged.get(item.field))\n this.saving = true\n this.failed = false\n this.failedReason = undefined\n this.publish()\n // One atomic namespace mutation: the 0.1.2 scope contract takes ordered\n // path operations, so the whole staged batch is validated, persisted, and\n // recovered together — either every write lands or none does.\n const ops: Array<{ op: 'set'; path: string[]; value: string | number | boolean } | { op: 'unset'; path: string[] }> = valid.map(item => item.op.op === 'set'\n ? { op: 'set', path: [item.field], value: (item.op as { value: string | number | boolean }).value }\n : { op: 'unset', path: [item.field] })\n let failedReason: string | undefined\n try {\n await this.scope.mutate(ops)\n } catch (error) {\n failedReason = error instanceof Error ? error.message : String(error)\n }\n // The 0.1.2 scope resolves even a refused mutation (it recovers with a\n // fresh view instead of throwing), so resolution alone proves nothing:\n // judge every planned write against the settled snapshot. The mutation is\n // atomic, so one missed write fails the whole save and keeps the drafts.\n const landed = failedReason === undefined && valid.every(item => item.judge())\n for (const [field, before] of pending) {\n if (landed && this.staged.get(field) === before) this.staged.delete(field)\n }\n this.saving = false\n this.failed = !landed\n // A read-back failure carries no server reason: the card surfaces its\n // generic failure copy; a rejecting scope (the bridge) adds its message.\n this.failedReason = failedReason\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, op: { field, op: 'unset' }, judge: () => this.landedUnset(field) })\n continue\n }\n if (staged.text === spec.format(this.sectionValue(field))) continue\n const write = spec.parse(staged.text)\n if (write === undefined) plan.push({ field, op: { field, op: 'unset' }, judge: undefined })\n else if (write.kind === 'clear') plan.push({ field, op: { field, op: 'unset' }, judge: () => this.landedUnset(field) })\n else plan.push({ field, op: { field, op: 'set', value: write.value }, judge: () => this.landedSet(field, write.value) })\n }\n return plan\n }\n\n /**\n * Read-back judgment for a planned set: the user layer must hold the\n * intended value once the mutation has settled.\n */\n private landedSet(field: string, value: unknown): boolean {\n // A redacted secret never appears in any wire view layer: the Host strips\n // role('secret') fields and reports them through a sidecar the scope\n // snapshot does not expose, so there is nothing to compare the draft\n // against. Settling is the only signal the form has; the rest of the\n // batch, when one exists, still carries the atomic verdict by read-back.\n if (this.specOf(field).secret) return true\n return this.userLayer()?.[field] === value\n }\n\n /**\n * Read-back judgment for a planned unset: the field must be gone from the\n * user layer once the mutation has settled.\n */\n private landedUnset(field: string): boolean {\n return !this.stored(field)\n }\n\n private stage(field: string, edit: StagedEdit): void {\n this.staged.set(field, edit)\n this.failed = false\n this.failedReason = undefined\n this.publish()\n }\n\n private specOf(field: string): FieldSpec {\n const spec = this.specs.get(field)\n // Every call site names a field this card declared; a missing one is a\n // wiring mistake that must not degrade into a silently inert control.\n if (spec === undefined) throw new Error(`settings card has no field ${field}`)\n return spec\n }\n\n private snapshotOf(): SettingsScopeSnapshot<T> {\n return this.scope.getSnapshot()\n }\n\n private sectionValue(field: string): unknown {\n return (this.snapshotOf().value as Record<string, unknown> | undefined)?.[field]\n }\n\n private baseValue(field: string): unknown {\n return (this.snapshotOf().base as Record<string, unknown> | undefined)?.[field]\n }\n\n private userLayer(): Record<string, unknown> | undefined {\n return this.snapshotOf().user as Record<string, unknown> | undefined\n }\n\n private stored(field: string): boolean {\n const user = this.userLayer()\n return user !== undefined && Object.hasOwn(user, field)\n }\n\n private publish(): void {\n for (const listener of this.listeners) listener()\n }\n}\n","/**\n * The pet settings card: pet selection plus display layout, bound to the\n * 'pet' settings namespace the host plugin registers. Rendered as an\n * always-open first-level settings page; the section wrapper below mounts it\n * as the content of the top-level 'settings.section' nav entry. The petId\n * choices come from the registry endpoint ('/api/pet/pets') — the same list\n * the sprite renders from — so the card carries no per-pet knowledge.\n */\n\nimport type { ReactNode } from 'react'\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type { SnapshotStore } from '@deepseek-ai/dsh-client-store'\n// Type-only: pulls the settings-surface SlotMap merge (the 'settings.section' entry).\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport { PluginSettingsCard, ValueField, BooleanField, ChoiceField } from './PluginSettingsCard.tsx'\nimport { CardForm, booleanField, choiceField, numberField, type CardActions, type CardShell, type FieldState as CardFieldState } from './settings-form.ts'\nimport sectionCss from './settings-section.module.css'\n\n/** The pet's settings fields this card edits (the namespace's full schema). */\nexport interface PetSettings {\n /** Master switch for the plugin. */\n enabled?: boolean\n /** Master switch. */\n visible?: boolean\n /** Scale of the rendered pet in px (sprite cell height). */\n size?: number\n /** Horizontal inset from the viewport right edge, px. */\n right?: number\n /** Vertical inset from the viewport bottom edge, px. */\n bottom?: number\n /** Selected pet id (a registry entry). */\n petId?: string\n /** Status-decoration master switch (pet-center M5, #567). */\n decorationEnabled?: boolean\n}\n\n/** What the pet settings card renders. */\nexport interface PetSettingsCardState extends CardShell {\n /** Plugin master switch. */\n enabled: CardFieldState\n /** Master switch. */\n visible: CardFieldState\n /** Pet scale. */\n size: CardFieldState\n /** Right inset. */\n right: CardFieldState\n /** Bottom inset. */\n bottom: CardFieldState\n /** Selected pet. */\n petId: CardFieldState\n /** Status-decoration master switch. */\n decorationEnabled: CardFieldState\n /** Pet choices (registry ids + display names), loaded from the host. */\n petChoices: readonly { value: string; label: string }[]\n /** Registry diagnostics (v1 migration hints, invalid entries), host-served. */\n petDiagnostics: readonly PetDiagnosticView[]\n}\n\n/** The registration-side face the card's slot entry injects. */\nexport interface PetSettingsCardFace extends CardActions {\n hooks: {\n /** Card snapshot bound by the renderer as usePetSettingsCard. */\n petSettingsCard: SnapshotStore<PetSettingsCardState>\n }\n}\n\n/** One registry choice as served by '/api/pet/pets'. */\ninterface PetChoice {\n id: string\n displayName: string\n}\n\n/** One registry diagnostic as served by '/api/pet/diagnostics' (#623). */\nexport interface PetDiagnosticView {\n level: 'error' | 'warning'\n message: string\n}\n\n/** Fetch the registry list (the same data the sprite renders from). */\nasync function fetchPetChoices(): Promise<PetChoice[]> {\n const response = await fetch('/api/pet/pets')\n if (!response.ok) throw new Error('pet pets failed: ' + response.status)\n return (await response.json()) as PetChoice[]\n}\n\n/** Fetch the registry diagnostics (v1 migration hints, invalid entries). */\nasync function fetchPetDiagnostics(): Promise<PetDiagnosticView[]> {\n const response = await fetch('/api/pet/diagnostics')\n if (!response.ok) throw new Error('pet diagnostics failed: ' + response.status)\n const body = (await response.json()) as { diagnostics?: PetDiagnosticView[] }\n return body.diagnostics ?? []\n}\n\n/** Bridges the 'pet' scope onto the card's staged form. */\nexport class PetSettingsCardController {\n private readonly form: CardForm<PetSettings>\n private readonly store: SnapshotStore<PetSettingsCardState>\n // The choice list rides a mutable array shared with the choiceField spec,\n // so loading the registry re-validates and re-formats the petId field\n // without rebuilding the form.\n private readonly petChoices: string[] = []\n private readonly petLabels = new Map<string, string>()\n private diagnostics: PetDiagnosticView[] = []\n private loaded = false\n private attempts = 0\n private disposed = false\n /** Pending deferred-load or retry timer; cancelled by dispose(). */\n private pendingTimer: number | undefined\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('decorationEnabled'),\n booleanField('visible'),\n numberField('size'),\n numberField('right'),\n numberField('bottom'),\n choiceField('petId', this.petChoices),\n ])\n this.store = this.form.bind(() => this.projection())\n // Client plugins are applied synchronously during shell startup. Defer\n // the first registry request until that pass completes so transport\n // plugins (notably remote-web-ui on a paired non-loopback origin) can\n // install their fetch channel before /api/pet/pets is issued.\n this.pendingTimer = window.setTimeout(() => {\n this.pendingTimer = undefined\n if (this.disposed) return\n void this.loadPets()\n void this.loadDiagnostics()\n }, 0)\n }\n\n /** Fetch registry diagnostics once (soft-fail: an empty list on error). */\n private async loadDiagnostics(): Promise<void> {\n try {\n this.diagnostics = await fetchPetDiagnostics()\n if (this.disposed) return\n this.store.set(this.projection())\n } catch {\n this.diagnostics = []\n }\n }\n\n /** Resolve the registry choices once (retried a few times on failure). */\n private async loadPets(): Promise<void> {\n if (this.loaded || this.disposed) return\n try {\n const list = await fetchPetChoices()\n if (this.disposed) return\n this.petChoices.splice(0, this.petChoices.length, ...list.map(choice => choice.id))\n for (const choice of list) this.petLabels.set(choice.id, choice.displayName)\n this.loaded = true\n this.store.set(this.projection())\n } catch {\n if (this.disposed) return\n this.attempts += 1\n if (this.attempts < 3) {\n this.pendingTimer = window.setTimeout(() => {\n this.pendingTimer = undefined\n if (this.disposed) return\n void this.loadPets()\n }, 3000)\n }\n }\n }\n\n private projection(): PetSettingsCardState {\n return {\n ...this.form.shell(),\n enabled: this.form.field('enabled'),\n decorationEnabled: this.form.field('decorationEnabled'),\n visible: this.form.field('visible'),\n size: this.form.field('size'),\n right: this.form.field('right'),\n bottom: this.form.field('bottom'),\n petId: this.form.field('petId'),\n petChoices: this.petChoices.map(id => ({ value: id, label: this.petLabels.get(id) ?? id })),\n petDiagnostics: this.diagnostics,\n }\n }\n\n /**\n * Build the face the card's slot registration injects.\n * @returns the card's snapshot and its form actions.\n */\n inject(): PetSettingsCardFace {\n return { hooks: { petSettingsCard: this.store }, ...this.form.actions() }\n }\n\n /**\n * Release the card's scope subscription, bound stores and pending load\n * timers; the slot disposer calls this on teardown.\n */\n dispose(): void {\n if (this.disposed) return\n this.disposed = true\n if (this.pendingTimer !== undefined) {\n window.clearTimeout(this.pendingTimer)\n this.pendingTimer = undefined\n }\n this.form.dispose()\n }\n}\n\n/** Props the renderer binds for the pet settings card. */\nexport type PetSettingsCardProps =\n PropsLocale<'pet'>\n & InjectFace<PetSettingsCardFace>\n\n/**\n * Render the pet settings card.\n * @param props - locale copy, the card snapshot, and its form actions.\n * @returns the card.\n */\nexport function PetSettingsCard(props: PetSettingsCardProps) {\n const { t } = props\n const state = props.usePetSettingsCard(snapshot => snapshot)\n const disabled = !state.writable\n const fieldProps = {\n overriddenLabel: t('settings.overridden'),\n resetLabel: t('settings.reset'),\n invalidLabel: t('settings.invalidNumber'),\n disabled,\n }\n return (\n <PluginSettingsCard\n t={t}\n titleKey=\"settings.title\"\n descriptionKey=\"settings.description\"\n state={state}\n onSave={props.save}\n onDiscard={props.discard}\n alwaysOpen\n >\n <BooleanField\n id=\"settings-pet-enabled\"\n label={t('settings.enabled')}\n hint={t('settings.enabledHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.enabled}\n onEdit={(text) => { props.edit('enabled', text) }}\n onReset={() => { props.resetField('enabled') }}\n />\n <BooleanField\n id=\"settings-pet-decoration\"\n label={t('settings.decoration')}\n hint={t('settings.decorationHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.decorationEnabled}\n onEdit={(text) => { props.edit('decorationEnabled', text) }}\n onReset={() => { props.resetField('decorationEnabled') }}\n />\n <ChoiceField\n id=\"settings-pet-pet\"\n label={t('settings.pet')}\n hint={t('settings.petHint')}\n inheritLabel={t('settings.inherit')}\n {...fieldProps}\n {...state.petId}\n choices={state.petChoices}\n onEdit={(text) => { props.edit('petId', text) }}\n onReset={() => { props.resetField('petId') }}\n />\n {state.petDiagnostics.length === 0 ? null : (\n <li className={sectionCss.diagnostics} data-dsh-part=\"diagnostics\">\n <span className={sectionCss.diagnosticsTitle}>{t('settings.diagnosticsTitle')}</span>\n <ul>\n {state.petDiagnostics.map((diagnostic, index) => (\n <li key={index} data-level={diagnostic.level}>{diagnostic.message}</li>\n ))}\n </ul>\n </li>\n )}\n <BooleanField\n id=\"settings-pet-visible\"\n label={t('settings.visible')}\n hint={t('settings.visibleHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.visible}\n onEdit={(text) => { props.edit('visible', text) }}\n onReset={() => { props.resetField('visible') }}\n />\n <ValueField\n id=\"settings-pet-size\"\n label={t('settings.size')}\n hint={t('settings.sizeHint')}\n numeric\n {...fieldProps}\n {...state.size}\n onEdit={(text) => { props.edit('size', text) }}\n onReset={() => { props.resetField('size') }}\n />\n <ValueField\n id=\"settings-pet-right\"\n label={t('settings.right')}\n hint={t('settings.rightHint')}\n numeric\n {...fieldProps}\n {...state.right}\n onEdit={(text) => { props.edit('right', text) }}\n onReset={() => { props.resetField('right') }}\n />\n <ValueField\n id=\"settings-pet-bottom\"\n label={t('settings.bottom')}\n hint={t('settings.bottomHint')}\n numeric\n {...fieldProps}\n {...state.bottom}\n onEdit={(text) => { props.edit('bottom', text) }}\n onReset={() => { props.resetField('bottom') }}\n />\n </PluginSettingsCard>\n )\n}\n\n/** Props the settings section binds for the pet card page. */\nexport type PetSettingsSectionProps =\n PropsRuntime<'settings.section'>\n & PropsLocale<'pet'>\n & InjectFace<PetSettingsCardFace>\n\n/** Render the pet settings card as a first-level settings page. */\nexport function PetSettingsSection(props: PetSettingsSectionProps): ReactNode {\n const { t, usePetSettingsCard, save, discard, edit, resetField } = props\n return (\n <ul className={sectionCss.sectionList}>\n <PetSettingsCard t={t} usePetSettingsCard={usePetSettingsCard} save={save} discard={discard} edit={edit} resetField={resetField} />\n </ul>\n )\n}\n","/**\n * dsh-pet locale dictionaries (zh/en).\n * @module @linxin666/dsh-pet/client/locales\n */\n\n/** Dictionary namespace this package registers. */\nexport const NS = 'pet'\n\n/** Chinese copy. */\nexport const zh = {\n 'pet.feed': '喂食',\n 'pet.hide': '隐藏',\n 'pet.rename': '改名',\n 'pet.confirm': '确定',\n 'pet.namePlaceholder': '输入新名字',\n 'pet.summon': '召唤{name}',\n 'pet.rank': '亲密度 {rank}',\n 'pet.rank.name.幼鲸': '幼鲸',\n 'pet.rank.name.伙伴': '伙伴',\n 'pet.rank.name.挚友': '挚友',\n 'pet.rank.name.深海羁绊': '深海羁绊',\n 'pet.rank.name.心有灵犀': '心有灵犀',\n 'pet.rank.name.传说羁绊': '传说羁绊',\n 'pet.rank.name.神话羁绊': '神话羁绊',\n 'pet.rank.name.永恒之契': '永恒之契',\n 'pet.rank.name.鲸生共渡': '鲸生共渡',\n 'pet.points': '{points} 点',\n 'pet.treats': '小鱼干 ×{n}',\n 'pet.state.loading': '宠物正在赶来…',\n 'pet.state.error': '宠物迷路了(连接失败)',\n 'pet.renderer.unavailable': '这只宠物需要的渲染器({renderer})在当前版本不可用。',\n 'pet.live2d.core-missing': 'Live2D 核心未安装:请把官方 live2dcubismcore.min.js 放入 $DSH_HOME/pets/.runtime/ 后刷新(步骤见宠物插件 README)。',\n 'pet.live2d.vendor-missing': 'Live2D 组件缺失,请升级宠物插件。',\n 'pet.live2d.load-failed': 'Live2D 模型加载失败,请检查该宠物目录的完整性。',\n 'pet.openSessionHint': '点击跳转到对应会话',\n // 玩法 HUD(miku-pet 泛化:属性条 / 打工 / 睡觉 / 商店;统一小鱼干经济)。\n 'pet.gameplay.menu': '玩法',\n 'pet.gameplay.work': '打工',\n 'pet.gameplay.stopWork': '收工',\n 'pet.gameplay.sleep': '休息',\n 'pet.gameplay.wake': '起床',\n 'pet.gameplay.shop': '商店',\n 'pet.gameplay.skin': '皮肤',\n 'pet.gameplay.skinDefault': '默认',\n 'pet.gameplay.back': '返回',\n 'pet.gameplay.buy': '购买',\n 'pet.gameplay.insufficient': '{currency}不足',\n 'pet.gameplay.prize': '中奖 +{amount} {currency}',\n 'pet.gameplay.working': '打工中',\n 'pet.gameplay.sleeping': '睡觉中',\n 'pet.gameplay.stat.hunger': '饱食',\n 'pet.gameplay.stat.mood': '心情',\n 'pet.gameplay.stat.energy': '精力',\n 'pet.gameplay.stat.affection': '好感',\n 'pet.gameplay.currency.treats': '小鱼干',\n 'pet.moreSessions': '展开其余 {n} 个会话的气泡',\n 'pet.collapseSessions': '收起会话气泡',\n // 一级设置页(settings.section 席位)。\n 'settings.title': '宠物',\n 'settings.diagnosticsTitle': '宠物目录诊断',\n 'settings.description': '选择宠物并调整它的显示布局。',\n 'settings.pet': '宠物',\n 'settings.petHint': '选择显示哪只宠物;每只宠物独立命名,可在宠物悬浮面板改名。',\n 'settings.enabled': '启用宠物',\n 'settings.enabledHint': '关闭后隐藏宠物并停止轮询,可在设置里重新启用。',\n 'settings.decoration': '状态装饰',\n 'settings.decorationHint': '在宠物状态气泡里显示喷水鲸鱼等状态装饰;关闭后气泡只剩文字。',\n 'settings.visible': '显示宠物',\n 'settings.visibleHint': '关闭后宠物隐藏,可从聊天输入区重新召唤。',\n 'settings.size': '大小(px)',\n 'settings.sizeHint': '精灵单元高度,范围 32–1024。',\n 'settings.right': '距右侧(px)',\n 'settings.rightHint': '距视口右边缘的水平内缩距离。',\n 'settings.bottom': '距底部(px)',\n 'settings.bottomHint': '距视口底边的垂直内缩距离。',\n 'settings.inherit': '继承',\n 'settings.on': '开',\n 'settings.off': '关',\n 'settings.overridden': '已覆盖',\n 'settings.reset': '恢复默认',\n 'settings.notExposed': '当前 DSH 版本未向设置页暴露本插件的配置命名空间,表单不可用。可编辑 $DSH_HOME/settings.yaml 直接配置,或确认提供该命名空间的插件已挂载其设置域并重启。',\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.rank.name.幼鲸': 'Baby Whale',\n 'pet.rank.name.伙伴': 'Companion',\n 'pet.rank.name.挚友': 'Close Friend',\n 'pet.rank.name.深海羁绊': 'Deep Sea Bond',\n 'pet.rank.name.心有灵犀': 'Kindred Spirit',\n 'pet.rank.name.传说羁绊': 'Legendary Bond',\n 'pet.rank.name.神话羁绊': 'Mythic Bond',\n 'pet.rank.name.永恒之契': 'Eternal Covenant',\n 'pet.rank.name.鲸生共渡': 'Lifelong Companion',\n 'pet.points': '{points} pts',\n 'pet.treats': 'Treats ×{n}',\n 'pet.state.loading': 'The pet is on its way…',\n 'pet.state.error': 'The pet is lost (connection failed)',\n 'pet.renderer.unavailable': 'This pet needs a renderer ({renderer}) that is not available in this build.',\n 'pet.live2d.core-missing': 'Live2D Cubism Core is not installed: place the official live2dcubismcore.min.js under $DSH_HOME/pets/.runtime/ and refresh (see the pet plugin README).',\n 'pet.live2d.vendor-missing': 'The Live2D component is missing; please update the pet plugin.',\n 'pet.live2d.load-failed': 'The Live2D model failed to load; check the pet directory is complete.',\n 'pet.openSessionHint': 'Click to jump to this session',\n // Gameplay HUD (miku-pet generalization: stat bars / work / sleep / shop; unified treats economy).\n 'pet.gameplay.menu': 'Play',\n 'pet.gameplay.work': 'Work',\n 'pet.gameplay.stopWork': 'Stop work',\n 'pet.gameplay.sleep': 'Sleep',\n 'pet.gameplay.wake': 'Wake up',\n 'pet.gameplay.shop': 'Shop',\n 'pet.gameplay.skin': 'Skin',\n 'pet.gameplay.skinDefault': 'Default',\n 'pet.gameplay.back': 'Back',\n 'pet.gameplay.buy': 'Buy',\n 'pet.gameplay.insufficient': 'Not enough {currency}',\n 'pet.gameplay.prize': 'Prize +{amount} {currency}',\n 'pet.gameplay.working': 'Working',\n 'pet.gameplay.sleeping': 'Sleeping',\n 'pet.gameplay.stat.hunger': 'Hunger',\n 'pet.gameplay.stat.mood': 'Mood',\n 'pet.gameplay.stat.energy': 'Energy',\n 'pet.gameplay.stat.affection': 'Affection',\n 'pet.gameplay.currency.treats': 'Treats',\n 'pet.moreSessions': 'Expand {n} more session bubbles',\n 'pet.collapseSessions': 'Collapse session bubbles',\n // First-level settings section (the `settings.section` seat).\n 'settings.title': 'Pet',\n 'settings.diagnosticsTitle': 'Pet directory diagnostics',\n 'settings.description': 'Pick a pet and tune its display layout.',\n 'settings.pet': 'Pet',\n 'settings.petHint': 'Choose which pet shows. Names are stored per pet; rename from the pet hover panel.',\n 'settings.enabled': 'Enable the pet',\n 'settings.enabledHint': 'When off, the pet hides and polling stops; re-enable it here.',\n 'settings.decoration': 'Status decoration',\n 'settings.decorationHint': 'Show ornaments like the spouting whale inside the pet status bubbles; when off, bubbles stay text-only.',\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\\u20131024.',\n 'settings.right': 'Right inset (px)',\n 'settings.rightHint': 'Horizontal inset from the viewport right edge.',\n 'settings.bottom': 'Bottom inset (px)',\n 'settings.bottomHint': 'Vertical inset from the viewport bottom edge.',\n 'settings.inherit': 'Inherit',\n 'settings.on': 'On',\n 'settings.off': 'Off',\n 'settings.overridden': 'Overridden',\n 'settings.reset': 'Reset to default',\n 'settings.notExposed': 'This DSH version does not expose this plugin\\'s settings namespace to the configuration page, so the form is unavailable. Edit $DSH_HOME/settings.yaml directly, or confirm that the plugin owning the namespace is mounted with its settings domain and restart.',\n 'settings.readOnly': 'This deployment stores settings read-only.',\n 'settings.expand': 'Show settings',\n 'settings.collapse': 'Hide settings',\n 'settings.save': 'Save',\n 'settings.saving': 'Saving\\u2026',\n 'settings.discard': 'Discard',\n 'settings.unsaved': 'Unsaved',\n 'settings.saveFailed': 'The deployment did not accept these values; they were left for you to correct.',\n 'settings.invalidNumber': 'Enter a number, or leave blank to use the default.',\n} as const\n\n/** Key union for this namespace. */\nexport type PetKey = keyof typeof zh\n\n/** The settings-card slice of the pet dictionary. */\nexport type SettingsCardKey = PetKey\n\n/**\n * Active dictionary, picked by the document language at call time. The pet\n * mounts as a global floating surface (not a session-scoped slot), so it has\n * no framework locale seat and resolves its copy the same tiny way the\n * task-board's DOM-injected surface does.\n */\nexport function dictionary(): Record<PetKey, string> {\n const lang = typeof document !== 'undefined' ? document.documentElement.lang : 'zh'\n return lang.toLowerCase().startsWith('en') ? en : zh\n}\n\n/**\n * Translate a key with optional `{name}` template params. Mirrors the slot\n * `Translate` contract `(key, params?) => string` so it can be handed to the\n * same components that used to receive the framework-injected `t` seat. The\n * key is typed loosely (`string`) so the function is assignable to the slot's\n * `TranslateNS<'pet'>` (whose key domain also spans the shared common\n * vocabulary); a missing key degrades to the key itself rather than throwing.\n */\nexport function t(key: string, params?: Record<string, unknown>): string {\n let text: string = (dictionary() as Record<string, string>)[key] ?? key\n if (params !== undefined) {\n for (const [name, value] of Object.entries(params)) {\n text = text.replaceAll(`{${name}}`, String(value))\n }\n }\n return text\n}\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** dsh-pet UI copy. */\n pet: PetKey\n }\n}\n","// Generated by scripts/sync-shared.mjs from shared/client/telemetry.ts. Do not edit this copy; edit the shared source and run \"node scripts/sync-shared.mjs\".\n/**\n * Anonymous install telemetry shared by the DSH Web UI family plugins.\n *\n * Once per UTC day per browser, each wired plugin sends one heartbeat to\n * the dsh-market edge API listing its package name (and version when known).\n * The payload carries no conversation data and no identifiers beyond a random\n * UUID generated in localStorage; the server hashes it with a deployment salt\n * before storage and never persists IP addresses. Sends are fire-and-forget:\n * failures stay silent and simply retry on a later mount or day. The system\n * is documented in docs/telemetry.md.\n */\n\nexport type TelemetryChannel = 'market' | 'npm' | 'unknown'\n\nexport interface TelemetryItem {\n /** npm package name or asset id, e.g. \"@linxin666/dsh-pet\" or \"skin:harbor\". */\n name: string\n /** Installed version when known; omitted otherwise. */\n version?: string\n /** Install channel when determinable (market = Workshop install). */\n channel?: TelemetryChannel\n}\n\nconst VISITOR_KEY = 'dsh-web-ui-telemetry-visitor'\nconst DAY_KEY_PREFIX = 'dsh-web-ui-telemetry-day:'\nconst ENDPOINT = 'https://dsh-market.com/api/telemetry/event'\n\n// Baked into each client bundle by shared/tsdown.client.ts (the package's own\n// package.json version at build time). Undefined in dev/test builds.\ndeclare const __DSH_PKG_VERSION__: string | undefined\n\n/** The building package's version, when the bundle carries it. */\nfunction bakedVersion(): string | undefined {\n try {\n return typeof __DSH_PKG_VERSION__ === 'string' && __DSH_PKG_VERSION__ !== ''\n ? __DSH_PKG_VERSION__\n : undefined\n } catch {\n return undefined\n }\n}\n\n/** Read or lazily create the anonymous visitor id; null when storage is unavailable. */\nfunction visitorId(): string | null {\n try {\n const existing = localStorage.getItem(VISITOR_KEY)\n if (existing && /^[A-Za-z0-9_-]{16,64}$/.test(existing)) return existing\n const fresh = crypto.randomUUID().replaceAll('-', '')\n localStorage.setItem(VISITOR_KEY, fresh)\n return fresh\n } catch {\n // Storage unavailable (privacy mode, sandboxed frame): report nothing.\n return null\n }\n}\n\n/** Drop stale per-day dedup keys so localStorage does not grow forever. */\nfunction pruneDayKeys(today: string): void {\n try {\n for (let index = localStorage.length - 1; index >= 0; index -= 1) {\n const key = localStorage.key(index)\n if (key !== null && key.startsWith(DAY_KEY_PREFIX) && key !== DAY_KEY_PREFIX + today) {\n localStorage.removeItem(key)\n }\n }\n } catch { /* best effort */ }\n}\n\n/**\n * Fire the daily heartbeat for the given items at most once per UTC day per\n * browser. Never throws and never blocks the caller. Items without an explicit\n * version inherit the bundle's baked build version.\n */\nexport function reportDailyHeartbeat(items: readonly TelemetryItem[]): void {\n try {\n if (items.length === 0) return\n const today = new Date().toISOString().slice(0, 10)\n // Automation contexts (CDP-driven QA browsers, fresh isolated profiles)\n // mint a new visitor id per run and would inflate instance counts.\n if (navigator.webdriver) return\n if (localStorage.getItem(DAY_KEY_PREFIX + today) !== null) return\n const visitor = visitorId()\n if (visitor === null) return\n pruneDayKeys(today)\n const payloadItems = items.map((item) => {\n const out: Record<string, string> = { name: item.name }\n const version = item.version ?? bakedVersion()\n if (version !== undefined) out.version = version\n if (item.channel !== undefined) out.channel = item.channel\n return out\n })\n const body = JSON.stringify({ kind: 'heartbeat', visitor, items: payloadItems })\n void fetch(ENDPOINT, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body,\n keepalive: true,\n }).then((response) => {\n // Mark the day only after an accepted send, so offline browsers retry\n // on a later mount instead of going dark until tomorrow.\n if (response.ok) localStorage.setItem(DAY_KEY_PREFIX + today, '1')\n }).catch(() => { /* unreachable endpoint: stay silent */ })\n } catch { /* never break the host UI over telemetry */ }\n}\n","/**\n * dsh-pet browser half — mounts the selected pet as a global floating\n * surface and drives it from the host's same-origin '/api/pet/*' JSON\n * endpoints: fetch the registry list once, poll the host snapshot (~2 s),\n * forward interactions, persist drag positions. The pet is host-global (no\n * session dimension), so it mounts directly onto 'document.body' via a\n * single React root — the [data-dsh-plugin=\"pet\"] container — rather than a\n * session-scoped slot: on the new-conversation screen no session exists, and\n * a dock-mounted pet would vanish there (issue #48). The sprite chrome\n * portals into that same root, so the root owns the whole surface and a\n * root-keyed suppressor can hide it as one unit. When the pet is hidden the\n * entry becomes a fixed-position summon button.\n * @module @linxin666/dsh-pet/client\n */\n\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type { ISessions } from '@deepseek-ai/dsh-api-session-controller/client'\nimport type { SessionId } from '@deepseek-ai/dsh-api-remotes/client'\nimport type { SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-ui-settings/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'\n// Type-only: pulls the ctx.slots merge (the renderer owns the slot registry since 0.1.2).\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type { PetDisplayConfig } from '../persist.ts'\nimport type { PetGameplayVerbResult, PetInteractResult, PetStateView } from '../service.ts'\nimport type { PetInteraction } from '../affinity.ts'\nimport type { PetDefinition } from '../registry.ts'\nimport { createElement } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { createPetStore, type PetStoreInstance } from './pet-store.ts'\nimport { PetDockEntry, type PetInjected } from './PetDockEntry.tsx'\nimport { defaultPetRendererRegistry } from './renderers/registry.ts'\nimport { live2dRenderer } from './renderers/live2d.ts'\nimport { frames2dRenderer } from './renderers/frames2d.ts'\nimport { registerPetUiTeardown, takeoverPetUiTeardown } from './ui-teardown.ts'\nimport { PetSettingsSection, PetSettingsCardController, type PetSettings } from './PetSettingsCard.tsx'\nimport { NS, en, zh, t } from './locales.ts'\nimport { reportDailyHeartbeat } from './telemetry.ts'\n\n/** The host pet API as the browser sees it (same-origin JSON endpoints). */\ninterface PetHttpApi {\n /** Poll the host snapshot; the GUI's current session id rides the query. */\n state(currentSessionId?: string): Promise<PetStateView>\n pets(): Promise<PetDefinition[]>\n interact(kind: PetInteraction): Promise<PetInteractResult>\n setVisible(visible: boolean): Promise<{ ok: true; display: PetDisplayConfig }>\n setConfig(patch: Partial<PetDisplayConfig>): Promise<{ ok: true; display: PetDisplayConfig }>\n setName(name: string): Promise<{ ok: true; name: string } | { ok: false; error: string }>\n setPet(petId: string): Promise<{ ok: true; petId: string } | { ok: false; error: string }>\n gameplayTouch(zone?: string): Promise<PetGameplayVerbResult>\n gameplaySetMode(mode: 'work' | 'sleep' | null): Promise<PetGameplayVerbResult>\n gameplayWorkTick(): Promise<PetGameplayVerbResult>\n gameplayBuy(item: string): Promise<PetGameplayVerbResult>\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: (currentSessionId) => petFetch('/api/pet/state'\n + (currentSessionId === undefined ? '' : '?current=' + encodeURIComponent(currentSessionId))),\n pets: () => petFetch('/api/pet/pets'),\n interact: (kind) => petFetch('/api/pet/interact', { kind }),\n setVisible: (visible) => petFetch('/api/pet/set-visible', { visible }),\n setConfig: (patch) => petFetch('/api/pet/set-config', patch),\n setName: (name) => petFetch('/api/pet/set-name', { name }),\n setPet: (petId) => petFetch('/api/pet/set-pet', { petId }),\n gameplayTouch: (zone) => petFetch('/api/pet/gameplay/touch', zone === undefined ? {} : { zone }),\n gameplaySetMode: (mode) => petFetch('/api/pet/gameplay/mode', { mode }),\n gameplayWorkTick: () => petFetch('/api/pet/gameplay/work-tick', {}),\n gameplayBuy: (item) => petFetch('/api/pet/gameplay/buy', { item }),\n}\n\n/** Poll interval for the host snapshot. */\nconst POLL_MS = 2000\n\n/** Settings namespace the pet settings card edits (the Host plugin registers it). */\nconst PET_SETTINGS_NS = 'pet'\n\n/** Required services (sessions powers bubble-to-session navigation). */\nexport const inject = ['slots', 'locale', 'connection', 'settingsScope', 'remote', 'sessions']\n\n/** Re-exported for consumers that type against the injected face. */\nexport type { PetInjected, PetDockEntryProps } from './PetDockEntry.tsx'\nexport type { PetSpriteProps } from './PetSprite.tsx'\nexport type { PetUiState, PetFeedback } from './pet-store.ts'\nexport type { PetSettingsCardFace, PetSettingsCardState } from './PetSettingsCard.tsx'\nexport type { PetSettingsSectionProps } from './PetSettingsCard.tsx'\nexport type { PetDefinition } from '../registry.ts'\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n /**\n * Optional rc.6 compatibility binder provided by dsh-web-settings;\n * absent when that group plugin is not installed, so callers fall back to\n * the official settings scope.\n */\n webUiSettings?: { bind<S>(spec: SettingsScopeSpec<S>): SettingsScope<S> }\n }\n}\n\n/**\n * Client plugin body: register dictionaries, mount the global pet entry and\n * poll loop while the plugin is enabled, and seat the settings card as a\n * first-level settings section.\n * @param ctx - client root context.\n */\n\n/**\n * Module-wide work-tick throttle (ms). Hot reloads can leave several\n * GameplayHud instances alive, each running its own 10s work interval;\n * without a shared gate every interval would call workTick and each stale\n * call re-rolls, re-grants treats and re-plays the success/fail track, so\n * the outcome appears to play several times per window. This shared marker\n * accepts the first adjudication of a window and silently suppresses the\n * duplicates that follow. Reset when (re-)entering work mode.\n */\nlet lastWorkTickAt = 0\n\nexport function apply(ctx: ClientContext): void {\n // Anonymous install heartbeat (docs/telemetry.md): one beat per browser per\n // UTC day, package name only, silent failure.\n reportDailyHeartbeat([{ name: '@linxin666/dsh-pet' }])\n\n ctx.effect(() => {\n try {\n return ctx.locale.register(NS, { zh, en })\n } catch {\n return () => {}\n }\n }, 'pet: dictionaries')\n\n // Built-in renderers dispatch through the plugin-wide registry (pet-center\n // M3). Registration is idempotent (id wins), so re-applies stay clean.\n defaultPetRendererRegistry.register(live2dRenderer)\n defaultPetRendererRegistry.register(frames2dRenderer)\n\n const binder = ctx.get('webUiSettings') ?? ctx.settingsScope\n const settingsScope = binder.bind<PetSettings>({ namespace: PET_SETTINGS_NS })\n const enabled = (): boolean => {\n const snapshot = settingsScope.getSnapshot()\n return snapshot.status === 'ready'\n ? snapshot.value?.enabled ?? true\n : snapshot.status === 'unavailable'\n }\n\n // First-level settings section: one staged form over the 'pet' settings\n // namespace, registered as a top-level settings page. The controller loads\n // the petId choices from the registry endpoint itself — the registry lists\n // the available pets (built-in assets plus user dirs), so the section only\n // ever shows installed pets. Installing new pets happens in the Workshop\n // store.\n const petSettings = new PetSettingsCardController(settingsScope)\n // The section entry owns the controller: unregistering it (fiber disposal,\n // hot reload) releases the scope subscription through petSettings.dispose.\n ctx.slots.inject('settings.section', () => {\n try {\n const unregister = ctx.slots.register({\n name: 'settings.section',\n id: 'pet',\n order: 130,\n label: () => ctx.locale.bind('pet')('settings.title'),\n locale: 'pet',\n inject: () => petSettings.inject(),\n }, PetSettingsSection)\n return () => {\n unregister()\n petSettings.dispose()\n }\n } catch {\n return () => {}\n }\n })\n\n // The global pet entry, its store, and the poll loop live while the plugin\n // is enabled; toggling the setting off hides the pet and stops polling.\n // 'uiDead' marks a terminal teardown (takeover by a later bundle instance\n // or fiber disposal): a taken-over or disposed instance must never remount\n // from a late settings callback (issue #785).\n let disposeUi: (() => void) | undefined\n let clearUiTeardown: (() => void) | undefined\n let uiDead = false\n const killUi = (): void => {\n if (uiDead) return\n uiDead = true\n clearUiTeardown?.()\n clearUiTeardown = undefined\n disposeUi?.()\n disposeUi = undefined\n }\n const syncUi = (): void => {\n if (!uiDead && enabled() && disposeUi === undefined) {\n // ONE store instance for the whole app, owned by this apply body. The\n // pet is host-global (state/display/interactions are /api/pet/*\n // endpoints with no session dimension), so the slot system's per-session\n // store scoping would only reset the pet on session switches and leave\n // it stateless on the new-conversation screen (no session to scope by).\n const petStore: PetStoreInstance = createPetStore().create()\n const setSnapshot = petStore.actions.setSnapshot\n const setPets = petStore.actions.setPets\n const setState = petStore.actions.setState\n const setFeedback = petStore.actions.setFeedback\n\n // Clicking a session bubble jumps the GUI to that session; the same\n // sessions face reports which session the user is currently on, so the\n // host can lead the bubble stack with it. A bubble can outlive its\n // disposed session by one poll tick, and the sessions service fails\n // loud on unknown ids, so consult the live list first. The pet's type\n // program also loads the host-side dsh-session package through the\n // service types, whose Context merge declares a different 'sessions'\n // face; pin the browser runtime's outward face here.\n const sessions = ctx.sessions as unknown as ISessions\n const currentSessionId = (): string | undefined => {\n const current = sessions.list.getSnapshot().current\n return current === undefined ? undefined : String(current)\n }\n\n // The registry list is fetched lazily with retries baked into the poll\n // cycle: until it lands, the dock entry renders nothing and every 2s\n // tick tries again. After it lands, one list feeds both the sprite and\n // the settings card's choices.\n let petsLoaded = false\n // Latest-wins guard: the 2s tick, visibility recovery, and\n // interaction-triggered refreshes can overlap; only the newest\n // response may publish, so a slow older one can never roll the\n // snapshot back.\n let stateSeq = 0\n const pollNow = (): void => {\n if (!petsLoaded) {\n petApi.pets().then((list) => {\n petsLoaded = true\n setPets(list)\n }, () => {\n // Retry on the next poll tick.\n })\n }\n const seq = stateSeq + 1\n stateSeq = seq\n petApi.state(currentSessionId()).then((snapshot) => {\n if (seq !== stateSeq) return\n setSnapshot(snapshot)\n }, () => {\n if (seq !== stateSeq) return\n setState('error', 'pet.state transport error')\n })\n }\n\n const disposePoll = ctx.effect(() => {\n // Poll only while the tab is visible: the host snapshot does not\n // change while the page is hidden, so a background interval would\n // only burn RPCs (browser throttling is an unreliable backstop).\n // Coming back to the tab refreshes the pet immediately instead of\n // waiting out the next 2 s cycle.\n let timer: number | undefined\n const stop = (): void => {\n if (timer !== undefined) {\n window.clearInterval(timer)\n timer = undefined\n }\n }\n const start = (): void => {\n if (timer === undefined && document.visibilityState === 'visible') {\n timer = window.setInterval(pollNow, POLL_MS)\n }\n }\n const onVisibility = (): void => {\n if (document.visibilityState === 'visible') {\n pollNow()\n start()\n } else {\n stop()\n }\n }\n start()\n document.addEventListener('visibilitychange', onVisibility)\n return () => {\n stop()\n document.removeEventListener('visibilitychange', onVisibility)\n }\n }, 'pet: poll')\n\n // A current-session switch should re-order the bubble stack right away,\n // not on the next 2s tick. Poll only while the tab is visible, as the\n // poll loop itself does.\n const disposeSessionWatch = ctx.effect(() => {\n const unsubscribe = sessions.list.subscribe(() => {\n if (document.visibilityState === 'visible') pollNow()\n })\n return unsubscribe\n }, 'pet: current-session watch')\n\n const openSession = (sessionId: string): void => {\n const list = sessions.list.getSnapshot()\n if ((list.byId as any)[sessionId] === undefined) return\n sessions.open(sessionId as never)\n }\n\n const injected = (): PetInjected => ({\n store: petStore,\n ensure: pollNow,\n openSession,\n pet: () => {\n petApi.interact('pet').then((result) => {\n setFeedback({\n text: result.reaction,\n kind: 'pet',\n at: Date.now(),\n })\n }, () => {\n // Ignore transport errors on interactions; the next poll resyncs.\n })\n },\n feed: () => {\n petApi.interact('feed').then((result) => {\n setFeedback({\n text: result.reaction,\n kind: 'feed',\n at: Date.now(),\n })\n }, () => {\n // Ignore transport errors on interactions; the next poll resyncs.\n })\n },\n hide: () => {\n petApi.setVisible(false).then(() => {\n pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n summon: () => {\n petApi.setVisible(true).then(() => {\n pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n dragEnd: (right, bottom) => {\n petApi.setConfig({ right, bottom }).then(() => {\n pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n rename: (name) => {\n petApi.setName(name).then((result) => {\n if (result.ok) pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n feedbackDone: () => {\n setFeedback(null)\n },\n gameplay: {\n touch: (zone) => petApi.gameplayTouch(zone),\n setMode: async (mode) => {\n if (mode === 'work') lastWorkTickAt = 0\n return petApi.gameplaySetMode(mode)\n },\n workTick: async () => {\n // One adjudication per tick window, page-wide: suppress stale\n // duplicate intervals (HMR) re-playing the result track.\n const now = Date.now()\n if (now - lastWorkTickAt < 8500) {\n return { ok: true } as PetGameplayVerbResult\n }\n lastWorkTickAt = now\n return petApi.gameplayWorkTick()\n },\n buy: (item) => petApi.gameplayBuy(item),\n },\n })\n\n // The pet is host-global (its state/display/interactions have no session\n // dimension), and the official rc.6 shell declares no root-scoped slot\n // for a global floating surface — the dock is session-scoped, so a pet\n // mounted there would vanish on the new-conversation screen (issue #48).\n // The entry therefore mounts straight onto document.body via a single\n // React root for the page lifetime: PetSprite portals itself to body\n // when visible, and the hidden-state summon button is fixed-positioned.\n //\n // Cross-instance single-mount guard (issue #785): take over the\n // page-global slot first — the previous bundle instance's fiber may\n // still be draining during a client reload, so unmount its React root\n // and remove its container — then sweep containers left behind by\n // instances that predate the teardown registry, so this mount is the\n // page's only [data-dsh-pet-root].\n takeoverPetUiTeardown()\n for (const stale of Array.from(document.querySelectorAll('div[data-dsh-pet-root]'))) {\n stale.remove()\n }\n const container = document.createElement('div')\n container.dataset.dshPetRoot = ''\n container.dataset.dshPlugin = 'pet'\n document.body.appendChild(container)\n const petRoot = createRoot(container)\n // The sprite chrome portals into THIS root (not document.body): the\n // root then owns the whole surface, so a root-keyed suppressor (the\n // portrait mobile layer, which hides [data-dsh-plugin=\"pet\"]) really\n // hides the sprite instead of missing the portaled float.\n petRoot.render(createElement(PetDockEntry, { ...injected(), t, portalTarget: container }))\n\n let uiGone = false\n disposeUi = () => {\n if (uiGone) return\n uiGone = true\n clearUiTeardown?.()\n clearUiTeardown = undefined\n petRoot.unmount()\n container.remove()\n disposePoll()\n disposeSessionWatch()\n disposeUi = undefined\n }\n // The slot teardown is the takeover hook a later apply body runs; it\n // marks this instance terminal so a late settings callback from the\n // still-draining instance cannot remount a second pet.\n clearUiTeardown = registerPetUiTeardown(() => {\n uiDead = true\n disposeUi?.()\n })\n } else if (!uiDead && !enabled() && disposeUi !== undefined) {\n disposeUi()\n disposeUi = undefined\n }\n }\n // The settings subscription and the pet UI lifetime follow the fiber\n // (issue #785): disposal drops the subscription and tears the UI down\n // (terminal), so a hot-reloaded or re-injected bundle never leaves the\n // previous React root, container, or poll loop behind on document.body.\n const unsubscribeSettings = settingsScope.subscribe(syncUi)\n ctx.effect(\n () => () => {\n unsubscribeSettings()\n killUi()\n },\n 'pet: client lifecycle',\n )\n syncUi()\n}\n"],"x_google_ignoreList":[1],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["styles","styles","styles","css","sectionCss"],"sources":["../../../packages/dsh-pet/src/client/pet-store.ts","../../../packages/dsh-pet/src/client/work-tick-gate.ts","../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","../../../packages/dsh-pet/src/announce.ts","../../../packages/dsh-pet/src/state.ts","../../../packages/dsh-pet/src/client/spritesheet.ts","../../../packages/dsh-pet/src/client/sequences.ts","../../../packages/dsh-pet/src/client/PetSprite.tsx","../../../packages/dsh-pet/src/client/drag-stream.ts","../../../packages/dsh-pet/src/client/renderers/registry.ts","../../../packages/dsh-pet/src/client/phase-stream.ts","../../../packages/dsh-pet/src/client/renderers/live2d/Live2dVisualMount.tsx","../../../packages/dsh-pet/src/client/renderers/Frames2dVisualMount.tsx","../../../packages/dsh-pet/src/client/renderers/PetRendererSwitch.tsx","../../../packages/dsh-pet/src/gameplay.ts","../../../packages/dsh-pet/src/client/gameplay-hud.tsx","../../../packages/dsh-pet/src/client/PetDockEntry.tsx","../../../packages/dsh-pet/src/contracts/renderer.ts","../../../packages/dsh-pet/src/client/renderers/live2d/runtime.ts","../../../packages/dsh-pet/src/client/renderers/live2d.ts","../../../packages/dsh-pet/src/client/renderers/frames2d.ts","../../../packages/dsh-pet/src/client/ui-teardown.ts","../../../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/telemetry.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-store'\nimport type { EngineStoreHandle, EngineStoreInstance } from '@deepseek-ai/dsh-client-store'\nimport type { PetGameplayStateView, PetStateView } from '../service.ts'\nimport type { PetInteraction } from '../affinity.ts'\nimport type { PetDefinition } from '../registry.ts'\n\n/** One transient reaction bubble on the pet. */\nexport interface PetFeedback {\n /** Bubble copy. */\n text: string\n /** Interaction kind driving the reaction animation. */\n kind: PetInteraction | 'none'\n /** Epoch ms when the bubble appeared (for expiry). */\n at: number\n}\n\n/** Pet UI state as consumers see it. */\nexport interface PetUiState {\n /** Latest host snapshot; null before the first successful fetch. */\n snapshot: PetStateView | null\n /** The registry list the host serves (atlas URLs + geometry + tracks). */\n pets: PetDefinition[]\n /** Fetch lifecycle. */\n state: 'loading' | 'ready' | 'error'\n /** Transport error message (for the debug surface), when any. */\n error: string | null\n /** Active reaction bubble, if any. */\n feedback: PetFeedback | null\n}\n\n/** Store write set. */\nexport type PetUiActions = {\n /** Replace the host snapshot (poll result). */\n setSnapshot: (draft: PetUiState, snapshot: PetStateView) => void\n /** Replace the registry list. */\n setPets: (draft: PetUiState, pets: PetDefinition[]) => void\n /** Mark the fetch lifecycle. */\n setState: (draft: PetUiState, state: PetUiState['state'], error: string | null) => void\n /** Show a reaction bubble. */\n setFeedback: (draft: PetUiState, feedback: PetFeedback | null) => void\n /**\n * Patch the gameplay slice of the snapshot (verb write-back), so the HUD\n * reflects a touch/mode/buy result immediately instead of waiting one\n * poll tick (2 s).\n */\n setGameplayView: (draft: PetUiState, view: PetGameplayStateView) => void\n}\n\n/** Create the pet store handle (apply world only; never module-level). */\nexport function createPetStore(): EngineStoreHandle<PetUiState, PetUiActions> {\n return defineStore({\n init: (): PetUiState => ({\n snapshot: null,\n pets: [],\n state: 'loading',\n error: null,\n feedback: null,\n }),\n actions: {\n setSnapshot: (draft, snapshot) => {\n // The 2 s poll republishes the full snapshot even while the pet is\n // idle. Skipping an unchanged payload keeps immer's produce at zero\n // modifications, so zustand never notifies and the whole sprite tree\n // skips the re-render. Equality is JSON-based and skip-only: both\n // sides come from the same host serializer, and any mismatch falls\n // through to the normal publish — the failure mode is one extra\n // render, never a stale pet.\n if (draft.state === 'ready' && draft.error === null && sameSnapshot(draft.snapshot, snapshot)) return\n draft.snapshot = snapshot\n draft.state = 'ready'\n draft.error = null\n },\n setPets: (draft, pets) => {\n draft.pets = pets\n },\n setState: (draft, state, error) => {\n draft.state = state\n draft.error = error\n },\n setFeedback: (draft, feedback) => {\n draft.feedback = feedback\n },\n setGameplayView: (draft, view) => {\n if (draft.snapshot !== null) draft.snapshot = { ...draft.snapshot, gameplay: view }\n },\n },\n })\n}\n\nexport type { PetInteraction }\n\n/**\n * Content equality for consecutive poll snapshots. JSON compare, not field\n * enumeration: an exact string match is the only way to skip the publish, so\n * a missed field can never freeze the UI — it can only cost the render the\n * optimization exists to save.\n */\nfunction sameSnapshot(previous: PetStateView | null, next: PetStateView): boolean {\n return previous !== null && JSON.stringify(previous) === JSON.stringify(next)\n}\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 * Page-wide work-tick gate for the pet's work mode. Hot reloads can leave\n * several GameplayHud instances alive, each running its own work interval;\n * without a shared gate every interval would call workTick and each stale call\n * re-rolls, re-grants treats and re-plays the success/fail track, so the\n * outcome appears to play several times per window. The gate accepts the first\n * adjudication of a window and silently suppresses the duplicates that follow.\n *\n * The window is the active pet's own `gameplay.work.tickMs`, not a constant: a\n * pet configured with a shorter cadence must actually adjudicate that often,\n * while a fixed window would downgrade it without saying so (#1494).\n * @module @linxin666/dsh-pet/client/work-tick-gate\n */\n\n/** Window used when the active pet declares no work cadence. */\nexport const DEFAULT_WORK_TICK_MS = 10_000\n\n/** Manifest bounds for `gameplay.work.tickMs` (src/gameplay.ts). */\nconst MIN_WORK_TICK_MS = 1_000\nconst MAX_WORK_TICK_MS = 60_000\n\n/**\n * The gate window for one pet: its configured cadence, clamped to the manifest's\n * own bounds so a malformed registry entry cannot disable or flood the gate.\n * @param tickMs - the active definition's `gameplay.work.tickMs`, when it has one.\n * @returns the window in milliseconds.\n */\nexport function workTickWindowMs(tickMs: number | undefined): number {\n if (typeof tickMs !== 'number' || !Number.isFinite(tickMs)) return DEFAULT_WORK_TICK_MS\n return Math.min(MAX_WORK_TICK_MS, Math.max(MIN_WORK_TICK_MS, tickMs))\n}\n\n/** The shared work-tick gate. */\nexport interface WorkTickGate {\n /**\n * Admit one adjudication for the current window.\n * @param tickMs - the active pet's configured work cadence.\n * @returns true when this call may adjudicate, false for a duplicate.\n */\n allow: (tickMs: number | undefined) => boolean\n /** Forget the last adjudication (used when work mode is entered). */\n reset: () => void\n}\n\n/**\n * Create a gate.\n * @param now - the clock, injectable so tests control the window.\n * @returns the gate over that clock.\n */\nexport function createWorkTickGate(now: () => number = Date.now): WorkTickGate {\n let lastAdjudicatedAt = 0\n return {\n allow: (tickMs) => {\n const at = now()\n if (at - lastAdjudicatedAt < workTickWindowMs(tickMs)) return false\n lastAdjudicatedAt = at\n return true\n },\n reset: () => {\n lastAdjudicatedAt = 0\n },\n }\n}\n","function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","/**\n * The pet announcement-bubble contract (dsh-usage linkage): a sibling plugin\n * pushes one structured announcement through `pet.announce(...)`, the host\n * validates it into a bounded payload, and the browser half renders it as a\n * dedicated, specially styled bubble above the session bubble stack.\n *\n * The validation lives in this pure module so the wire contract has exactly\n * one home and stays testable without the cordis service.\n * @module @linxin666/dsh-pet/announce\n */\n\n/** One plugin-authored announcement bubble. */\nexport interface PetAnnouncement {\n /** Authoring plugin's source tag (`dsh-usage` for the usage statistics). */\n source: string\n /** Bubble content kind: a spend estimate, an account balance, or a plan-quota status. */\n kind: 'balance' | 'cost' | 'plan'\n /** Lead text (usually the provider display name). */\n title: string\n /** Balance or today-spend amount, formatted for display (kinds `balance` and `cost`). */\n amount?: string\n /** Plan usage percent 0-100 (kind `plan`). */\n percent?: number\n /** ISO 8601 reset instant (kind `plan`). */\n resetAt?: string\n /** Short trailing note (plan tier name, peak-period status, currency code, ...). */\n note?: string\n /** Visual tone; drives the bubble's accent color. */\n tone: 'ok' | 'warn' | 'low'\n /** Freshness window in ms; an expired announcement stops rendering. */\n ttlMs: number\n /** Epoch ms the announcement arrived. */\n at: number\n}\n\n/** Default freshness window. */\nexport const ANNOUNCE_DEFAULT_TTL_MS = 10_000\n\n/**\n * Hard TTL ceiling. A repeating announcer (dsh-usage) declares its poll\n * cadence as the TTL, so an `always`-mode bubble stays continuous across\n * polls; the ceiling means a source that dies unmounts its bubble within at\n * most one missed refresh cycle rather than lingering forever.\n */\nexport const ANNOUNCE_MAX_TTL_MS = 7_200_000\n\n/** Hard bounds: lengths and the TTL range (1 s .. 60 s). */\nconst TITLE_MAX = 80\nconst TEXT_MAX = 120\nconst SOURCE_MAX = 64\n\nfunction boundedString(value: unknown, max: number): string | undefined {\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n if (trimmed === '') return undefined\n return trimmed.length <= max ? trimmed : trimmed.slice(0, max)\n}\n\n/**\n * Validate one announce payload. Unknown fields are dropped, oversized text\n * is truncated, and anything structurally wrong resolves to undefined — a\n * malformed announcement never reaches the pet's bubble surface.\n * @param input - the payload a sibling plugin passed to `pet.announce`.\n * @param now - epoch ms the announcement arrived.\n */\nexport function parseAnnouncement(input: unknown, now: number): PetAnnouncement | undefined {\n if (typeof input !== 'object' || input === null) return undefined\n const data = input as Record<string, unknown>\n const source = boundedString(data.source, SOURCE_MAX)\n const title = boundedString(data.title, TITLE_MAX)\n if (source === undefined || title === undefined) return undefined\n const kind = data.kind === 'balance' || data.kind === 'cost' || data.kind === 'plan' ? data.kind : undefined\n if (kind === undefined) return undefined\n const amount = boundedString(data.amount, TEXT_MAX)\n const resetAt = boundedString(data.resetAt, TEXT_MAX)\n const note = boundedString(data.note, TITLE_MAX)\n const percent = typeof data.percent === 'number' && Number.isFinite(data.percent)\n ? Math.max(0, Math.min(100, data.percent))\n : undefined\n if ((kind === 'balance' || kind === 'cost') && amount === undefined) return undefined\n if (kind === 'plan' && percent === undefined) return undefined\n const tone = data.tone === 'warn' || data.tone === 'low' ? data.tone : 'ok'\n const ttlMs = typeof data.ttlMs === 'number' && Number.isFinite(data.ttlMs)\n ? Math.max(1000, Math.min(ANNOUNCE_MAX_TTL_MS, data.ttlMs))\n : ANNOUNCE_DEFAULT_TTL_MS\n return {\n source,\n kind,\n title,\n ...(amount !== undefined ? { amount } : {}),\n ...(percent !== undefined ? { percent } : {}),\n ...(resetAt !== undefined ? { resetAt } : {}),\n ...(note !== undefined ? { note } : {}),\n tone,\n ttlMs,\n at: now,\n }\n}\n\n/** Whether an announcement is still fresh at `now`. */\nexport function announcementFresh(announcement: PetAnnouncement, now: number): boolean {\n return now - announcement.at < announcement.ttlMs\n}\n","/**\n * Pet state machine — pure, clock-injected. Maps official DSH session activity\n * and the legacy `activity/status` vocabulary onto the 9-state Codex pet\n * animation contract, plus turn-end celebration and no-session idle.\n *\n * The machine is deliberately dumb: it holds the last input phase, the\n * animation decision, and a one-shot \"celebration\" window after `done` so the\n * pet visibly jumps before settling back to idle. Everything here is a pure\n * function of (input, nowMs); persistence and RPC live in the service.\n * @module @linxin666/dsh-pet/state\n */\n\n/** Activity phases understood by the pet host. */\nexport type ActivityPhase = 'idle' | 'waiting' | 'thinking' | 'tool' | 'review' | 'done' | 'failed'\n\n/** The Codex-compatible 9-state animation contract (spritesheet rows). */\nexport type PetAnimation =\n | 'idle'\n | 'running-right'\n | 'running-left'\n | 'waving'\n | 'jumping'\n | 'failed'\n | 'waiting'\n | 'running'\n | 'review'\n\n/** One input snapshot consumed by the machine. */\nexport interface PetStateInput {\n /** Current activity phase of the active session. */\n phase: ActivityPhase\n /** Human-readable status line (plain text). */\n line?: string\n /** Playful phrase from the activity tracker, when any. */\n phrase?: string\n}\n\n/** Animation decision plus the copy the pet should show. */\nexport interface PetStateSnapshot {\n /** Which animation track to play. */\n animation: PetAnimation\n /** Optional status bubble copy (line or phrase), shown while active. */\n bubble?: string\n /** Wall-clock ms this animation started (client can sync loops). */\n animationStartedAt: number\n /** Raw phase, for debugging and client-side rendering decisions. */\n phase: ActivityPhase\n /** True when there is an active session (pet mounted). */\n sessionActive: boolean\n}\n\n/** Machine configuration. */\nexport interface PetStateConfig {\n /** Celebration window after `done` before settling to idle, ms (default 2400). */\n celebrateMs: number\n /** Failure display window before settling to idle, ms (default 2400). */\n failureMs: number\n}\n\nexport const defaultPetStateConfig: PetStateConfig = { celebrateMs: 2400, failureMs: 2400 }\n\n/**\n * Map one activity phase onto the animation contract.\n * - thinking → `running` and tool → `running-right` (focused work).\n * - review → `review` while answer text is streaming.\n * - waiting → `waiting` (expectant pose, needs user input).\n * - done → `jumping` (celebration), then back to `idle` after the window.\n * - failed → `failed` briefly, then back to `idle`.\n * - idle → `idle` (calm breathing loop).\n */\nexport function animationForPhase(phase: ActivityPhase): PetAnimation {\n switch (phase) {\n case 'thinking': return 'running'\n case 'tool': return 'running-right'\n case 'review': return 'review'\n case 'waiting': return 'waiting'\n case 'done': return 'jumping'\n case 'failed': return 'failed'\n case 'idle': return 'idle'\n }\n}\n\n/** The spritesheet row index for one animation track. */\nexport function rowOf(animation: PetAnimation): number {\n const rows: Record<PetAnimation, number> = {\n 'idle': 0,\n 'running-right': 1,\n 'running-left': 2,\n 'waving': 3,\n 'jumping': 4,\n 'failed': 5,\n 'waiting': 6,\n 'running': 7,\n 'review': 8,\n }\n return rows[animation]\n}\n\n/**\n * PetStateMachine — one instance per host process. Holds only the latest\n * input snapshot and terminal-state timing; no storage, no side effects.\n */\nexport class PetStateMachine {\n private phase: ActivityPhase = 'idle'\n private line: string | undefined\n private phrase: string | undefined\n private sessionActive = false\n private doneAt: number | undefined\n private failedAt: number | undefined\n private readonly config: PetStateConfig\n\n constructor(\n config: Partial<PetStateConfig> = defaultPetStateConfig,\n private readonly now: () => number = Date.now,\n ) {\n this.config = { ...defaultPetStateConfig, ...config }\n }\n\n /** Consume one projected activity update. */\n onActivityStatus(input: PetStateInput): void {\n this.phase = input.phase\n this.line = input.line\n this.phrase = input.phrase\n this.doneAt = input.phase === 'done' ? this.now() : undefined\n this.failedAt = input.phase === 'failed' ? this.now() : undefined\n }\n\n /** A session became the active one (or a fresh session started). */\n onSessionActive(): void {\n this.sessionActive = true\n }\n\n /** The active session was disposed (or none left). */\n onSessionDisposed(): void {\n this.sessionActive = false\n this.phase = 'idle'\n this.line = undefined\n this.phrase = undefined\n this.doneAt = undefined\n this.failedAt = undefined\n }\n\n /** Render the current animation decision. */\n render(): PetStateSnapshot {\n const nowMs = this.now()\n let animation = animationForPhase(this.phase)\n const doneSettled = this.phase === 'done'\n && this.doneAt !== undefined\n && nowMs - this.doneAt >= this.config.celebrateMs\n const failedSettled = this.phase === 'failed'\n && this.failedAt !== undefined\n && nowMs - this.failedAt >= this.config.failureMs\n if (doneSettled || failedSettled) animation = 'idle'\n // Settled sessions never bubble: idle (e.g. an aborted/stopped turn),\n // completed celebration expiry, and failed display expiry all fall silent.\n const settled = this.phase === 'idle' || doneSettled || failedSettled\n const bubble = settled ? undefined : this.phrase ?? this.line\n return {\n animation,\n ...(bubble === undefined ? {} : { bubble }),\n animationStartedAt: nowMs,\n phase: this.phase,\n sessionActive: this.sessionActive,\n }\n }\n}\n","/**\n * Spritesheet geometry helpers — parameterized by the pet definition the\n * host serves over '/api/pet/pets', so the browser half renders any registry\n * entry without per-pet code. The per-track tables (frames, durations, loop,\n * fallback) also come from the registry; these helpers only place frames,\n * guard track lengths, and map the fixed 9-row animation contract.\n * @module @linxin666/dsh-pet/client/spritesheet\n */\n\nimport { rowOf, type PetAnimation } from '../state.ts'\nimport type { PetCell, PetTrackDef } from '../registry.ts'\n\n/** Animation track shape the frame loop consumes. */\nexport type TrackDef = PetTrackDef\n\n/** Row index of one animation track (the fixed 9-row contract). */\nexport function rowOfTrack(animation: PetAnimation): number {\n // The table itself lives in state.ts (rowOf) — the single source of truth.\n return rowOf(animation)\n}\n\n/**\n * Background-position (px) of one frame cell within the scaled atlas.\n * The background image is scaled by `scale` (element size ÷ cell size), and\n * background-position offsets are applied in SCALED coordinates — using raw\n * atlas coordinates here would drift each frame by the scale factor and\n * render torn/overlapping frames.\n */\nexport function framePosition(cell: PetCell, row: number, col: number, scale = 1): { x: number; y: number } {\n return { x: -col * cell.width * scale, y: -row * cell.height * scale }\n}\n\n/**\n * Trim a track to the actual frame count of its row (the manifest's per-row\n * counts are authoritative; this is a last-line guard against a definition\n * whose row count disagrees with its track table). A row with 0 detected\n * frames degrades to the first frame so the pet never renders blank.\n */\nexport function trimTrack(track: TrackDef, frameCount: number): TrackDef {\n const n = Math.max(1, Math.min(frameCount, track.frames.length, track.durations.length))\n return {\n frames: track.frames.slice(0, n),\n durations: track.durations.slice(0, n),\n loop: track.loop,\n ...(track.fallback === undefined ? {} : { fallback: track.fallback }),\n }\n}\n","/** Pure timing helpers for manifest-defined scene animation sequences. */\n\nimport type { PetTrackDef } from '../registry.ts'\nimport type { PetAnimation } from '../state.ts'\n\nexport interface SequenceFrame {\n animation: PetAnimation\n frameIndex: number\n}\n\n/**\n * Precomputed timeline for one looping sequence: the per-item duration table\n * is built once instead of per query. The sprite frame loop asks at\n * animation rate (~60 Hz), where the per-call map/reduce is pure waste.\n */\nexport interface SequenceTimeline {\n frameAt(elapsedMs: number): SequenceFrame\n}\n\n/** Build a {@link SequenceTimeline} over one manifest sequence. */\nexport function createSequenceTimeline(\n sequence: readonly PetAnimation[],\n tracks: Record<PetAnimation, PetTrackDef>,\n): SequenceTimeline {\n const itemDurations = sequence.map(animation => tracks[animation].durations.reduce((sum, value) => sum + value, 0))\n const sequenceDuration = itemDurations.reduce((sum, value) => sum + value, 0)\n return {\n frameAt(elapsedMs: number): SequenceFrame {\n let offset = Math.max(0, elapsedMs) % sequenceDuration\n let itemIndex = 0\n while (itemIndex < sequence.length - 1 && offset >= itemDurations[itemIndex]!) {\n offset -= itemDurations[itemIndex]!\n itemIndex += 1\n }\n const animation = sequence[itemIndex]!\n const track = tracks[animation]\n let frameIndex = 0\n while (frameIndex < track.frames.length - 1 && offset >= track.durations[frameIndex]!) {\n offset -= track.durations[frameIndex]!\n frameIndex += 1\n }\n return { animation, frameIndex }\n },\n }\n}\n\n/** Resolve the active track and frame after elapsed milliseconds of a looping sequence. */\nexport function sequenceFrameAt(\n sequence: readonly PetAnimation[],\n tracks: Record<PetAnimation, PetTrackDef>,\n elapsedMs: number,\n): SequenceFrame {\n return createSequenceTimeline(sequence, tracks).frameAt(elapsedMs)\n}\n","/**\n * Pet sprite companion component — the browser half's centerpiece. Renders a\n * fixed-position floating sprite (React portal onto document.body), plays\n * the track matching the host animation snapshot, and exposes the\n * interaction surface: click to pet, hover panel with feed/rename/hide, drag\n * to reposition (persisted via setConfig). Everything visual comes from the\n * pet definition the host serves ('/api/pet/pets' + the state snapshot's\n * pet id), so one component renders every registry entry.\n * @module @linxin666/dsh-pet/client/PetSprite\n */\n\nimport { useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport type { CSSProperties, PointerEvent as ReactPointerEvent, ReactElement, ReactNode, ReactPortal } from 'react'\nimport { createPortal } from 'react-dom'\nimport clsx from 'clsx'\nimport type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDisplayConfig } from '../persist.ts'\nimport type { PetStateView } from '../service.ts'\nimport { announcementFresh, type PetAnnouncement } from '../announce.ts'\nimport type { PetDefinition } from '../registry.ts'\nimport type { DecorationView } from '../contracts/status-decoration.ts'\nimport type { PetFeedback } from './pet-store.ts'\nimport { framePosition, rowOfTrack, trimTrack } from './spritesheet.ts'\nimport { createSequenceTimeline } from './sequences.ts'\nimport { animationForPhase, type ActivityPhase, type PetAnimation } from '../state.ts'\nimport { NS } from './locales.ts'\nimport styles from './pet.module.css'\n\n/** Props injected by the plugin apply body (store actions + locale). */\nexport interface PetSpriteProps {\n /** Latest host snapshot; null while loading. */\n snapshot: PetStateView | null\n /** The selected pet's registry definition (atlas URL + geometry + tracks). */\n definition: PetDefinition\n /** Display configuration (persisted by the host). */\n display: PetDisplayConfig\n /** Active reaction bubble, if any. */\n feedback: PetFeedback | null\n /** Pet the sprite (click). */\n onPet: () => void\n /** Feed the sprite (panel button). */\n onFeed: () => void\n /** Hide the sprite (panel button). */\n onHide: () => void\n /** Persist a drag position. */\n onDragEnd: (right: number, bottom: number) => void\n /** Drag gesture notifications for renderers with a drag track (frames2d). */\n onDraggingChange?: (dragging: boolean) => void\n /** Rename the selected pet (persisted by the host). */\n onRename: (name: string) => void\n /** Navigate to the session one status bubble reports on. */\n onOpenSession: (sessionId: string) => void\n /** Clear the reaction bubble (after its CSS animation). */\n onFeedbackDone: () => void\n /**\n * Custom visual replacing the sprite2d atlas animation (pet-center M3).\n * The chrome (drag, bubbles, panel, tap economy) is untouched: the visual\n * renders inside the sprite box, and the atlas load + frame loop skip.\n */\n visual?: ReactNode\n /**\n * Gameplay overlay (miku-pet generalization): rendered inside the float\n * container so hover containment and stacking work unchanged. Absent for\n * pets without a gameplay block.\n */\n hud?: ReactNode\n /**\n * Gameplay tap sink: receives the tap point as sprite-box fractions\n * (0..1). When present the chrome reports the tap IN ADDITION to the\n * affinity pet; the HUD decides zones/no-ops.\n */\n onGameplayTap?: (fractionX: number, fractionY: number) => void\n /**\n * Gameplay entry (miku-pet generalization): when present the hover panel\n * renders a 玩法 action that opens/closes the gameplay card. The chrome\n * wires it to the HUD through the per-pet bus (openCard), mirroring the\n * onGameplayTap sink. Absent for pets without a gameplay block.\n */\n onGameplayMenu?: () => void\n /** Disable the drag gesture (gameplay work mode blocks dragging). */\n dragDisabled?: boolean\n /**\n * DOM node the floating chrome portals into. Defaults to document.body\n * (the legacy behavior); the plugin apply passes its owning root (the\n * [data-dsh-plugin=\"pet\"] container) so the root owns the whole surface\n * and a root-keyed suppressor (the portrait mobile layer) hides the pet\n * as one unit instead of missing the portaled sprite.\n */\n portalTarget?: Element\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 status decoration ornament (pet-center M5, #567). Renders the active\n * phase's frame segment as a CSS-background strip at a compact bubble\n * height; prefers-reduced-motion holds the segment's first frame, and a\n * missing or undecodable asset simply paints nothing (CSS background\n * failure) — the bubble text is never disturbed. The span is aria-hidden;\n * the bubble keeps its own semantics untouched.\n */\nfunction StatusOrnament(props: { decoration: DecorationView; phase: ActivityPhase }): ReactElement | null {\n const { decoration, phase } = props\n const segment = decoration.phases[phase]\n const shown = segment !== undefined && segment !== 'hide'\n const segmentKey = segment !== undefined && segment !== 'hide' ? segment.from + ':' + segment.to : 'none'\n const spanRef = useRef<HTMLSpanElement | null>(null)\n const scale = 18 / decoration.cell.height\n const frameWidth = Math.round(decoration.cell.width * scale)\n const stripWidth = decoration.columns * frameWidth\n // Value-stable dependency key: the host serves a fresh DecorationView\n // object on every state poll (2 s), so the effect must not depend on the\n // object identity — otherwise each poll would cancel and restart the\n // frame loop and the animation would jump back to its first frame.\n const durationsKey = decoration.durations.join(',')\n useEffect(() => {\n if (segment === undefined || segment === 'hide') return\n const el = spanRef.current\n if (el === null) return\n const position = (index: number): string => (-index * frameWidth) + 'px 0px'\n const reduceMotion = window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches === true\n el.style.backgroundPosition = position(segment.from)\n // A single-frame segment (from === to) has nothing to animate: with\n // loop=true the wrap branch would reset index to the same frame and the\n // tick would keep rescheduling a no-op rAF forever. Settle on the one\n // frame instead — same as the reduced-motion static hold.\n if (reduceMotion || segment.from === segment.to) return\n let timer = 0\n let index = segment.from\n let elapsed = 0\n let last = performance.now()\n const tick = (): void => {\n const now = performance.now()\n const delta = now - last\n last = now\n elapsed += delta\n const duration = decoration.durations[index] ?? 120\n // The segment's frame rate (duration ms, typically 90-160) is far\n // below the rAF cadence, so a 60fps loop would spend ~90% of its\n // ticks doing nothing. Schedule by the remaining time to the next\n // frame instead — the ornament wakes once per frame, not once per\n // screen refresh. A late wake (background tab, jank) carries extra\n // elapsed time, so catch up every due frame like the sprite loop.\n if (elapsed >= duration) {\n do {\n elapsed -= duration\n if (index < segment.to) index += 1\n else if (decoration.loop) index = segment.from\n } while (elapsed >= duration)\n // Only advance the background when the frame actually changes.\n el.style.backgroundPosition = position(index)\n }\n // A non-looping segment settles on its last frame; stop scheduling\n // instead of repainting the same position every frame.\n if (!decoration.loop && index === segment.to) return\n timer = window.setTimeout(tick, Math.max(1, duration - elapsed))\n }\n timer = window.setTimeout(tick, 0)\n return () => window.clearTimeout(timer)\n }, [shown, segmentKey, frameWidth, decoration.loop, durationsKey])\n if (!shown) return null\n return (\n <span\n ref={spanRef}\n aria-hidden=\"true\"\n data-dsh-pet-decoration={decoration.id}\n style={{\n display: 'inline-block',\n width: frameWidth,\n height: 18,\n marginRight: 6,\n verticalAlign: 'middle',\n flexShrink: 0,\n backgroundImage: 'url(' + decoration.entryUrl + ')',\n backgroundSize: stripWidth + 'px 18px',\n backgroundRepeat: 'no-repeat',\n backgroundPosition: '0px 0px',\n }}\n />\n )\n}\n\n/**\n * The announcement bubble (dsh-usage linkage): a dedicated, specially\n * designed surface for sibling-plugin facts — a balance or today-spend pill,\n * or a plan-quota card with a tone-tinted accent, a mini meter for percent\n * windows, and the reset instant. It rides the top of the session bubble\n * stack (column-reverse puts the DOM-last child farthest from the sprite)\n * and persists for its TTL instead of the short feedback pop.\n */\nfunction UsageAnnouncementBubble(props: { announcement: PetAnnouncement }): ReactNode {\n const { announcement } = props\n const tone = announcement.tone === 'low'\n ? styles.bubbleUsageLow\n : announcement.tone === 'warn'\n ? styles.bubbleUsageWarn\n : styles.bubbleUsageOk\n return (\n <div\n className={clsx(styles.bubble, styles.bubbleUsage, tone)}\n role=\"status\"\n aria-live=\"polite\"\n data-dsh-pet-announcement={announcement.source}\n >\n <span className={styles.bubbleUsageHead}>\n <span className={styles.bubbleUsageTitle}>{announcement.title}</span>\n {(announcement.kind === 'balance' || announcement.kind === 'cost') && announcement.amount !== undefined && (\n <span className={styles.bubbleUsageValue}>{announcement.amount}</span>\n )}\n {announcement.kind === 'plan' && announcement.percent !== undefined && (\n <span className={styles.bubbleUsageValue}>{Math.round(announcement.percent) + '%'}</span>\n )}\n </span>\n {announcement.kind === 'plan' && announcement.percent !== undefined && (\n <span className={styles.bubbleUsageMeter}>\n <span\n className={styles.bubbleUsageMeterFill}\n style={{ width: Math.min(100, Math.max(0, announcement.percent)) + '%' }}\n />\n </span>\n )}\n {announcement.note !== undefined && (\n <span className={styles.bubbleUsageNote}>{announcement.note}</span>\n )}\n </div>\n )\n}\n\n/**\n * The floating pet. The spritesheet frame advances on requestAnimationFrame\n * with per-frame durations from the definition's tracks; the atlas image is\n * loaded once and the background position is written straight to the sprite\n * element (no per-frame React state).\n */\nexport function PetSprite(props: PetSpriteProps): ReactPortal {\n const { snapshot, definition, display, feedback } = props\n const spriteRef = useRef<HTMLDivElement | null>(null)\n const floatRef = useRef<HTMLDivElement | null>(null)\n const panelRef = useRef<HTMLDivElement | null>(null)\n // Whichever bubble surface is currently rendered (feedback, the session\n // stack, or the legacy status bubble) — only one exists at a time.\n const bubbleRef = useRef<HTMLDivElement | null>(null)\n const [imageReady, setImageReady] = useState(false)\n const [hovered, setHovered] = useState(false)\n // Multi-session bubble stack: collapsed by default (only the display\n // session's bubble + a '+N' badge), expanded on stack hover (peek) or by\n // tapping the badge (pinned, for touch). The display session's bubble\n // anchors the bottom of the stack and never moves when extras open above\n // it, so the pointer target cannot flicker.\n const [stackPeek, setStackPeek] = useState(false)\n const [stackPinned, setStackPinned] = useState(false)\n const [renaming, setRenaming] = useState(false)\n const [panelAbove, setPanelAbove] = useState(false)\n // Extra margin-bottom for the above-panel so it stacks clear of the\n // bubbles instead of overlapping them (both anchor at the sprite's top).\n const [panelLift, setPanelLift] = useState(0)\n const [nameDraft, setNameDraft] = useState('')\n // Explicit IME composition tracking: some input methods (WeChat IME on\n // Windows) report keydowns with isComposing === false mid-composition, so\n // the native flag alone is not a safe submit/cancel guard (#303).\n const composingRef = useRef(false)\n const [dragPos, setDragPos] = useState<{ right: number; bottom: number } | null>(null)\n const dragRef = useRef<{ startX: number; startY: number; right: number; bottom: number } | null>(null)\n const hideTimerRef = useRef<number | null>(null)\n const frameRef = useRef<{ track: PetAnimation | null; index: number; elapsed: number }>({\n track: null,\n index: 0,\n elapsed: 0,\n })\n\n const cell = definition.cell\n const columns = definition.columns\n const rows = definition.rows\n const tracks = definition.tracks\n const sequences = definition.sequences\n // Hover-panel chrome from the pet's voice pack (pet-center M4, issue\n // #677): every slot falls back to the i18n dictionary when unset. Stat\n // formats carry {rank}/{n}/{points} placeholders the host validated.\n const panel = definition.panel\n const panelLabel = (slot: 'feed' | 'rename' | 'hide' | 'confirm', i18n: string): string =>\n panel?.labels?.[slot] ?? i18n\n const panelStat = (\n slot: 'rank' | 'treats' | 'points',\n i18nKey: 'pet.rank' | 'pet.treats' | 'pet.points',\n values: Record<string, string | number>,\n ): string => {\n const format = panel?.stats?.[slot] ?? props.t(i18nKey, values)\n if (panel?.stats?.[slot] === undefined) return format\n // The host whitelists {rank}/{n}/{points} in every stat slot, so a pack\n // format may reference any of them; substitute all three live values\n // (the slot's own value plus the siblings) instead of only the slot's.\n const all: Record<string, string | number> = {\n rank: snapshot?.affinity.rank ?? '?',\n n: snapshot?.treats.stocked ?? 0,\n points: snapshot?.affinity.points ?? 0,\n }\n let text = format\n for (const [name, value] of Object.entries(all)) text = text.replaceAll('{' + name + '}', String(value))\n return text\n }\n const panelShows = (action: 'feed' | 'rename' | 'hide'): boolean =>\n panel?.actions === undefined || panel.actions.includes(action)\n\n // Load the atlas once; the definition carries the authoritative per-row\n // frame counts and per-track durations, so nothing else is fetched. A\n // custom visual (pet-center M3) replaces the atlas entirely.\n useEffect(() => {\n if (props.visual !== undefined) return\n let cancelled = false\n const img = new Image()\n img.onload = () => {\n if (!cancelled) setImageReady(true)\n }\n img.src = definition.atlasUrl\n return () => {\n cancelled = true\n img.onload = null\n }\n }, [definition.atlasUrl, props.visual])\n\n // Frame loop: advance the current track and write background-position.\n // Offsets must be in SCALED coordinates (background-position applies to the\n // scaled background image), so the current sprite scale rides a ref that\n // the loop reads every tick. Under prefers-reduced-motion the sprite holds\n // its track's first frame instead of animating (presentation-only; the\n // animation state machine is untouched).\n const spriteScale = display.size / cell.height\n const phase = snapshot?.phase ?? 'idle'\n const animation = snapshot?.animation ?? 'idle'\n const scaleRef = useRef(spriteScale)\n scaleRef.current = spriteScale\n useEffect(() => {\n if (props.visual !== undefined) return\n const reduceMotion = typeof window !== 'undefined'\n && window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches === true\n const sequence = animation === animationForPhase(phase) ? sequences?.[phase] : undefined\n // Sequence state hoisted into the effect scope: the cumulative duration\n // table and each item's trimmed track would otherwise be recomputed every\n // tick (map/reduce plus two slices per frame), the same waste the\n // single-track branch below avoids.\n const timeline = sequence === undefined ? undefined : createSequenceTimeline(sequence, tracks)\n const sequenceItems = sequence === undefined ? undefined : new Map(\n sequence.map(itemAnimation => {\n const itemRow = rowOfTrack(itemAnimation)\n return [itemAnimation, {\n row: itemRow,\n track: trimTrack(tracks[itemAnimation], rows[itemRow] ?? tracks[itemAnimation].frames.length),\n }]\n }),\n )\n const leadAnimation = sequence?.[0] ?? animation\n const row = rowOfTrack(leadAnimation)\n const track = trimTrack(tracks[leadAnimation], rows[row] ?? tracks[leadAnimation].frames.length)\n // Paint one static sprite frame up front either way, so the pet is never\n // blank while the loop heat-up runs.\n const leadCol = track.frames[0]!\n const lead = framePosition(cell, row, leadCol, scaleRef.current)\n let lastPosStr = lead.x + 'px ' + lead.y + 'px'\n if (spriteRef.current !== null) {\n spriteRef.current.style.backgroundPosition = lastPosStr\n }\n if (reduceMotion) return\n let raf = 0\n let last = performance.now()\n let sequenceElapsed = 0\n const tick = (ts: number): void => {\n const delta = ts - last\n last = ts\n if (timeline !== undefined && sequenceItems !== undefined) {\n sequenceElapsed += delta\n const current = timeline.frameAt(sequenceElapsed)\n const item = sequenceItems.get(current.animation)!\n const col = item.track.frames[current.frameIndex]!\n const pos = framePosition(cell, item.row, col, scaleRef.current)\n const posStr = pos.x + 'px ' + pos.y + 'px'\n if (posStr !== lastPosStr) {\n lastPosStr = posStr\n if (spriteRef.current !== null) {\n spriteRef.current.style.backgroundPosition = posStr\n }\n }\n raf = requestAnimationFrame(tick)\n return\n }\n // row/track come from the effect scope: they were computed once above\n // and this effect re-runs when animation/tracks/rows change, so the\n // per-frame recompute (trimTrack slices fresh arrays) is pure waste.\n const st = frameRef.current\n if (st.track !== animation) {\n st.track = animation\n st.index = 0\n st.elapsed = 0\n }\n st.elapsed += delta\n const maxIndex = track.frames.length - 1\n while (st.elapsed >= (track.durations[st.index] ?? 0) && st.index < maxIndex) {\n st.elapsed -= track.durations[st.index] ?? 0\n st.index += 1\n }\n if (st.elapsed >= (track.durations[st.index] ?? 0)) {\n if (track.loop) {\n st.elapsed = 0\n st.index = 0\n } else {\n st.index = maxIndex // hold the final frame; the host switches tracks\n }\n }\n const col = track.frames[st.index]!\n const pos = framePosition(cell, row, col, scaleRef.current)\n const posStr = pos.x + 'px ' + pos.y + 'px'\n if (posStr !== lastPosStr) {\n lastPosStr = posStr\n if (spriteRef.current !== null) {\n spriteRef.current.style.backgroundPosition = posStr\n }\n }\n raf = requestAnimationFrame(tick)\n }\n raf = requestAnimationFrame(tick)\n return () => cancelAnimationFrame(raf)\n }, [animation, phase, cell, columns, rows, tracks, sequences, props.visual])\n\n // Auto-clear the feedback bubble after its CSS animation. The callback\n // rides a ref so re-renders never reset the timer: the 2s poll rebuilds\n // `props` every tick, and depending on it would starve the timeout.\n const feedbackDoneRef = useRef(props.onFeedbackDone)\n feedbackDoneRef.current = props.onFeedbackDone\n useEffect(() => {\n if (feedback === null) return\n const timer = window.setTimeout(() => feedbackDoneRef.current(), 2600)\n return () => window.clearTimeout(timer)\n }, [feedback])\n\n // Dragging: pointer events on the sprite; position is right/bottom based.\n // `draggedRef` records whether the pointer actually moved, so the browser's\n // trailing click (fired after pointerup) does not pet the sprite.\n const draggedRef = useRef(false)\n const clearHideTimer = (): void => {\n if (hideTimerRef.current !== null) {\n window.clearTimeout(hideTimerRef.current)\n hideTimerRef.current = null\n }\n }\n\n // Clear any pending auto-hide timer on unmount: a stray callback after\n // teardown reads window through react-dom and failed CI runs with\n // \"window is not defined\" (slow-runner timing, PetSprite.test.tsx).\n useEffect(() => () => clearHideTimer(), [])\n\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>): void => {\n if (props.dragDisabled === true) return\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) {\n if (!draggedRef.current) props.onDraggingChange?.(true)\n draggedRef.current = true\n }\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 (draggedRef.current) props.onDraggingChange?.(false)\n if (dragPos !== null) props.onDragEnd(dragPos.right, dragPos.bottom)\n }\n\n const pos = dragPos ?? { right: display.right, bottom: display.bottom }\n const spriteWidth = Math.round(cell.width * spriteScale)\n const spriteHeight = Math.round(cell.height * spriteScale)\n\n // Concurrent sessions share one bubble slot: only the display session\n // speaks by default, and the rest hide behind a '+N' badge until the stack\n // is hovered/pinned open. The legacy single 'bubble' is the fallback when\n // the host serves no per-session list. The hover panel normally sits below\n // the sprite, so the bubbles stay visible and clickable — no region swap.\n const sessionBubbles = snapshot?.sessions ?? []\n const stackOpen = stackPeek || stackPinned\n const collapsed = !stackOpen && sessionBubbles.length > 1\n const visibleSessions = collapsed ? sessionBubbles.slice(0, 1) : sessionBubbles\n const statusBubble = feedback === null && sessionBubbles.length === 0\n ? snapshot?.bubble\n : undefined\n // The freshest plugin-authored announcement (dsh-usage linkage): a\n // dedicated, specially styled bubble above the session stack. The host\n // already TTL-filters; this client-side check covers the last poll tick.\n const announcement = snapshot?.announcement\n const usageAnnouncement = feedback === null && announcement !== undefined && announcementFresh(announcement, Date.now())\n ? announcement\n : undefined\n // Each session's inner whisper (碎碎念) rides its own bubble — short\n // inner-voice copy woken by that session's activity, never the model's or\n // another session's. Instead of a second bubble of its own, a fresh\n // whisper takes over its session's bubble and re-tints it, so the pet\n // never wears two voices at once. Interaction feedback takes over the\n // whole bubble area while it plays, so whispers yield to it like status\n // copy.\n const bubblePresent = feedback !== null || sessionBubbles.length > 0 || statusBubble !== undefined || usageAnnouncement !== undefined\n const displayName = snapshot?.name ?? definition.displayName\n // The host-served status decoration (M5, #567); absent = text-only bubbles.\n const decoration = snapshot?.decoration\n\n // A settled session list can no longer stay pinned open.\n useEffect(() => {\n if (sessionBubbles.length <= 1) setStackPinned(false)\n }, [sessionBubbles.length])\n\n useLayoutEffect(() => {\n if (!hovered) {\n setPanelAbove(false)\n setPanelLift(0)\n return\n }\n const updatePanelPlacement = (): void => {\n const sprite = spriteRef.current\n const panel = panelRef.current\n if (sprite === null || panel === null) return\n const availableBelow = window.innerHeight - sprite.getBoundingClientRect().bottom\n const above = availableBelow < panel.getBoundingClientRect().height + 8\n setPanelAbove(above)\n // The fallback above-placement shares the sprite's top edge with the\n // bubble(s); lift the panel by the bubble area's height so the two\n // never overlap (8px base gap + 6px clearance above the top bubble).\n const bubbleHeight = above ? bubbleRef.current?.getBoundingClientRect().height ?? 0 : 0\n setPanelLift(bubbleHeight > 0 ? Math.ceil(bubbleHeight) + 14 : 0)\n }\n updatePanelPlacement()\n window.addEventListener('resize', updatePanelPlacement)\n return () => window.removeEventListener('resize', updatePanelPlacement)\n }, [hovered, renaming, pos.right, pos.bottom, display.size, bubblePresent, sessionBubbles.length, stackOpen, feedback])\n\n const float = (\n <div\n ref={floatRef}\n className={styles.float}\n style={{ right: pos.right, bottom: pos.bottom, zIndex: 2147483000 }}\n onPointerEnter={() => {\n clearHideTimer()\n setHovered(true)\n }}\n onPointerLeave={(e) => {\n // The panel renders OUTSIDE the container's box (absolute, below\n // the sprite), so moving onto it fires pointerleave on the container.\n // Treat a target still inside the container's DOM (the overflowed\n // panel) as \"still hovering\"; otherwise give the pointer a short\n // grace period to reach the panel across the gap below the sprite.\n // The bridge ('.panel::after') keeps the pointer inside the hit\n // area, and the grace period covers a slow mouse crossing the\n // remaining sliver.\n const next = e.relatedTarget\n if (next instanceof Node && floatRef.current?.contains(next)) return\n // Never auto-hide while the rename box is open: moving the pointer\n // onto an IME candidate window (an OS-level window outside the\n // webview) fires pointerleave, and unmounting the input mid-IME-\n // composition crashes some input methods / the renderer (#303).\n if (renaming) return\n clearHideTimer()\n hideTimerRef.current = window.setTimeout(() => setHovered(false), 300)\n }}\n >\n <div\n className={styles.spriteWrap}\n style={{ width: spriteWidth, height: spriteHeight }}\n >\n <div\n ref={spriteRef}\n className={styles.sprite}\n style={{\n width: spriteWidth,\n height: spriteHeight,\n ...(props.visual === undefined\n ? {\n backgroundImage: imageReady ? 'url(' + definition.atlasUrl + ')' : undefined,\n backgroundSize: (cell.width * columns * spriteScale) + 'px ' + (cell.height * (definition.atlasRows ?? rows.length) * spriteScale) + 'px',\n backgroundRepeat: 'no-repeat',\n backgroundPosition: '0 0',\n }\n : {}),\n cursor: dragRef.current === null ? 'grab' : 'grabbing',\n }}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onClick={(e) => {\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 if (props.onGameplayTap !== undefined && spriteRef.current !== null) {\n const rect = spriteRef.current.getBoundingClientRect()\n if (rect.width > 0 && rect.height > 0) {\n props.onGameplayTap((e.clientX - rect.left) / rect.width, (e.clientY - rect.top) / rect.height)\n }\n }\n props.onPet()\n }}\n role=\"button\"\n aria-label={definition.displayName}\n >\n {props.visual}\n </div>\n </div>\n {props.hud}\n {feedback !== null && (\n <div key={feedback.at} ref={bubbleRef} className={clsx(styles.bubble, feedback.kind === 'feed' ? styles.bubbleFeed : styles.bubblePet)}>\n {feedback.text}\n </div>\n )}\n {feedback === null && (sessionBubbles.length > 0 || statusBubble !== undefined || usageAnnouncement !== undefined) && (\n <div\n ref={bubbleRef}\n className={styles.bubbleStack}\n onPointerEnter={() => setStackPeek(true)}\n onPointerLeave={() => setStackPeek(false)}\n >\n {visibleSessions.map((session, index) => {\n // A session's whisper rides ITS OWN bubble (the stack lead when\n // the current session has one, any bubble when the stack is\n // expanded). The key swap restarts the entrance animation so the\n // mood change reads as the bubble re-speaking.\n const speaksWhisper = session.whisper !== undefined\n const bubble = (\n <button\n key={speaksWhisper ? 'whisper:' + session.whisper : session.sessionId}\n type=\"button\"\n className={clsx(\n styles.bubble,\n styles.bubbleStatus,\n styles.bubbleClickable,\n speaksWhisper && styles.bubbleWhisper,\n )}\n title={props.t('pet.openSessionHint')}\n onClick={() => { props.onOpenSession(session.sessionId) }}\n >\n {index === 0 && !speaksWhisper && decoration !== undefined && (\n <StatusOrnament decoration={decoration} phase={phase} />\n )}\n {session.whisper ?? session.bubble}\n </button>\n )\n // The primary bubble carries the '+N' badge while other sessions\n // hide behind it; the badge toggles the pinned (touch) expansion.\n if (index !== 0 || sessionBubbles.length <= 1) return bubble\n return (\n <span key=\"primary\" className={styles.bubbleAnchor}>\n {bubble}\n <button\n type=\"button\"\n className={styles.bubbleMore}\n title={stackOpen\n ? props.t('pet.collapseSessions')\n : props.t('pet.moreSessions', { n: sessionBubbles.length - 1 })}\n aria-label={stackOpen\n ? props.t('pet.collapseSessions')\n : props.t('pet.moreSessions', { n: sessionBubbles.length - 1 })}\n aria-expanded={stackOpen}\n onClick={(e) => {\n e.stopPropagation()\n setStackPinned(open => !open)\n }}\n >\n {stackOpen ? '×' : '+' + String(sessionBubbles.length - 1)}\n </button>\n </span>\n )\n })}\n {sessionBubbles.length === 0 && statusBubble !== undefined && (\n <div\n key=\"status\"\n className={clsx(styles.bubble, styles.bubbleStatus)}\n role=\"status\"\n aria-live=\"polite\"\n >\n {decoration !== undefined && (\n <StatusOrnament decoration={decoration} phase={phase} />\n )}\n {statusBubble}\n </div>\n )}\n {usageAnnouncement !== undefined && <UsageAnnouncementBubble announcement={usageAnnouncement} />}\n </div>\n )}\n {hovered && dragRef.current === null && (\n <div\n ref={panelRef}\n className={clsx(styles.panel, panelAbove && styles.panelAbove)}\n data-placement={panelAbove ? 'above' : 'below'}\n style={panelAbove && panelLift > 0\n ? ({ marginBottom: panelLift } as CSSProperties)\n : undefined}\n onPointerEnter={() => {\n // Reaching the panel (or its bridge) must cancel any hide timer\n // the container's pointerleave may have armed while the pointer\n // crossed the sliver between the sprite and the panel.\n clearHideTimer()\n }}\n >\n {renaming ? (\n <div className={styles.renameRow}>\n <input\n className={styles.nameInput}\n value={nameDraft}\n maxLength={20}\n placeholder={props.t('pet.namePlaceholder')}\n autoFocus\n onChange={(e) => setNameDraft(e.target.value)}\n onCompositionStart={() => { composingRef.current = true }}\n onCompositionEnd={() => { composingRef.current = false }}\n onKeyDown={(e) => {\n // While an IME composition is active (e.g. selecting a\n // Chinese candidate), Enter/Escape keydowns belong to the\n // input method: ignore them so candidate selection can\n // neither submit the draft nor close the rename box. The\n // explicit ref and the 'Process' key cover IMEs that mark\n // composition keydowns with isComposing === false (#303).\n if (composingRef.current || e.nativeEvent.isComposing || e.key === 'Process') return\n if (e.key === 'Enter') {\n const trimmed = nameDraft.trim()\n if (trimmed !== '') {\n props.onRename(trimmed)\n setRenaming(false)\n }\n } else if (e.key === 'Escape') {\n setRenaming(false)\n }\n }}\n />\n <button\n type=\"button\"\n className={styles.action}\n onClick={() => {\n const trimmed = nameDraft.trim()\n if (trimmed !== '') {\n props.onRename(trimmed)\n setRenaming(false)\n }\n }}\n >\n {panelLabel('confirm', props.t('pet.confirm'))}\n </button>\n </div>\n ) : (\n <>\n <div className={styles.rankRow}>\n <span className={styles.nameCell}>{displayName}</span>\n <span className={styles.statRank}>\n {(() => {\n const rawRank = snapshot?.affinity.rank ?? '?'\n const rankKey = `pet.rank.name.${rawRank}`\n const localized = props.t(rankKey as any)\n const rankName = localized !== rankKey ? localized : rawRank\n return panelStat('rank', 'pet.rank', { rank: rankName })\n })()}\n </span>\n </div>\n <div className={styles.rankRow}>\n <span className={styles.statTreats}>{panelStat('treats', 'pet.treats', { n: snapshot?.treats.stocked ?? 0 })}</span>\n <span className={styles.statPoints}>{panelStat('points', 'pet.points', { points: snapshot?.affinity.points ?? 0 })}</span>\n </div>\n <div className={styles.actions}>\n {panelShows('feed') && (\n <button type=\"button\" className={styles.action} onClick={props.onFeed}>\n {panelLabel('feed', props.t('pet.feed'))}\n </button>\n )}\n {panelShows('rename') && (\n <button\n type=\"button\"\n className={styles.action}\n onClick={() => {\n // Cancel any pending hide so the rename box cannot\n // unmount right as the user starts typing (#303).\n clearHideTimer()\n setNameDraft(displayName)\n setRenaming(true)\n }}\n >\n {panelLabel('rename', props.t('pet.rename'))}\n </button>\n )}\n {panelShows('hide') && (\n <button type=\"button\" className={styles.action} onClick={props.onHide}>\n {panelLabel('hide', props.t('pet.hide'))}\n </button>\n )}\n {props.onGameplayMenu !== undefined && (\n <button type=\"button\" className={styles.action} onClick={props.onGameplayMenu}>\n {props.t('pet.gameplay.menu')}\n </button>\n )}\n </div>\n </>\n )}\n </div>\n )}\n </div>\n )\n\n return createPortal(float, props.portalTarget ?? document.body)\n}\n","/**\n * Drag stream — bridges the chrome's pointer drag state onto the renderer\n * contract's { get, subscribe } shape, so renderers with a dedicated drag\n * track (frames2d) can follow the chrome-owned drag gesture. Dispatch is\n * change-only, mirroring the phase stream.\n * @module @linxin666/dsh-pet/client/drag-stream\n */\n\n/** The renderer-facing drag stream. */\nexport interface DragStream {\n /** Whether the chrome reports an active drag gesture. */\n get(): boolean\n /** Subscribe to drag state changes; returns the unsubscribe. */\n subscribe(listener: (dragging: boolean) => void): () => void\n /** Feed a fresh drag state; no-op when unchanged. */\n push(dragging: boolean): void\n}\n\n/** Create the stream (one per pet activation, owned by the renderer switch). */\nexport function createDragStream(): DragStream {\n let current = false\n const listeners = new Set<(dragging: boolean) => void>()\n return {\n get: () => current,\n subscribe(listener) {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n },\n push(dragging) {\n if (dragging === current) return\n current = dragging\n for (const listener of [...listeners]) listener(dragging)\n },\n }\n}\n","/**\n * Renderer registry — dispatches a manifest's renderer kind to its\n * implementation (pet-center M2 P4, issue #623). Unknown kinds never blank\n * the pet: a fallback card names the problem and reports the kinds this\n * build actually supports.\n * @module @linxin666/dsh-pet/client/renderers/registry\n */\n\nimport type { PetRenderer, PetRendererContext, PetRendererHandle } from '../../contracts/renderer.ts'\n\n/** Renderer dispatch table. */\nexport class RendererRegistry {\n private readonly renderers = new Map<string, PetRenderer>()\n\n /** Register one renderer implementation (id wins on re-register). */\n register(renderer: PetRenderer): void {\n this.renderers.set(renderer.id, renderer)\n }\n\n /** Whether a renderer kind is available in this build. */\n has(id: string): boolean {\n return this.renderers.has(id)\n }\n\n /** The registered renderer kinds (for diagnostics). */\n kinds(): string[] {\n return [...this.renderers.keys()].sort()\n }\n\n /** Remove every registration (tests; the client index registers once). */\n clear(): void {\n this.renderers.clear()\n }\n\n /**\n * Mount a renderer for one activation. An unknown kind renders a clear\n * diagnostic card into the container instead of failing silently.\n */\n mount(kind: string, ctx: PetRendererContext, config: unknown): PetRendererHandle {\n const renderer = this.renderers.get(kind)\n if (renderer === undefined) {\n const note = document.createElement('div')\n note.dataset.dshPetRendererFallback = kind\n note.textContent = 'Pet renderer \"' + kind + '\" is not available in this build (supported: ' + this.kinds().join(', ') + ').'\n ctx.container.appendChild(note)\n ctx.onCleanup(() => note.remove())\n return { dispose: () => note.remove() }\n }\n return renderer.mount(ctx, renderer.validateConfig(config))\n }\n}\n\n/**\n * The plugin-wide renderer registry. The client entry registers the\n * built-in renderers at apply time; the renderer switch and the live2d\n * bridge dispatch through this instance.\n */\nexport const defaultPetRendererRegistry = new RendererRegistry()\n","/**\n * Phase stream — bridges the polled host snapshots onto the renderer\n * contract's { get, subscribe } shape (pet-center M2 P4, issue #623). The\n * existing poll loop pushes each snapshot's ActivityPhase; subscribers are\n * dispatched on CHANGE only (phase transitions are sparse — done/failed hold\n * a timed window before falling back to idle — and renderers like Live2D pay\n * per transition, not per tick).\n * @module @linxin666/dsh-pet/client/phase-stream\n */\n\nimport type { ActivityPhase } from '../state.ts'\n\n/** The renderer-facing phase stream. */\nexport interface PhaseStream {\n /** The latest pushed phase. */\n get(): ActivityPhase\n /** Subscribe to phase changes; returns the unsubscribe. */\n subscribe(listener: (phase: ActivityPhase) => void): () => void\n /** Feed a fresh snapshot phase; no-op when unchanged. */\n push(phase: ActivityPhase): void\n}\n\n/** Create the stream (one per pet entry lifetime, owned by the plugin body). */\nexport function createPhaseStream(initial: ActivityPhase = 'idle'): PhaseStream {\n let current = initial\n const listeners = new Set<(phase: ActivityPhase) => void>()\n return {\n get: () => current,\n subscribe(listener) {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n },\n push(phase) {\n if (phase === current) return\n current = phase\n for (const listener of [...listeners]) listener(phase)\n },\n }\n}\n","/**\n * Live2D visual mount (pet-center M3) — the React bridge between the pet\n * center chrome and the imperative live2d renderer. The bridge owns the\n * contract context (asset base, phase stream, interaction write-back,\n * activation cleanups), feeds the polled phase into the stream, forwards\n * sub-4px taps as hit-test coordinates, and renders the localized error\n * card when the renderer reports a fatal boot failure.\n * @module @linxin666/dsh-pet/client/renderers/live2d/Live2dVisualMount\n */\n\nimport { useEffect, useRef, useState, type ReactElement } from 'react'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDefinition } from '../../../registry.ts'\nimport type { ActivityPhase } from '../../../state.ts'\nimport { createPhaseStream, type PhaseStream } from '../../phase-stream.ts'\nimport type { PetRendererContext } from '../../../contracts/renderer.ts'\nimport { defaultPetRendererRegistry } from '../registry.ts'\nimport type { Live2dErrorCode, Live2dRendererHandle } from '../live2d.ts'\nimport type { NS } from '../../locales.ts'\n\n/** Mount the live2d renderer as the sprite's visual (inside the chrome). */\nexport function Live2dVisualMount(props: {\n definition: PetDefinition\n phase: ActivityPhase\n onPet: () => void\n t: PropsLocale<typeof NS>['t']\n}): ReactElement {\n const containerRef = useRef<HTMLDivElement | null>(null)\n const streamRef = useRef<PhaseStream | null>(null)\n const handleRef = useRef<Live2dRendererHandle | null>(null)\n const downRef = useRef<{ x: number; y: number } | null>(null)\n const [error, setError] = useState<Live2dErrorCode | null>(null)\n\n // One activation per pet definition: build the contract context and mount.\n useEffect(() => {\n setError(null)\n const container = containerRef.current\n const live2d = props.definition.live2d\n if (container === null || live2d === undefined) return undefined\n streamRef.current ??= createPhaseStream(props.phase)\n const cleanups: (() => void)[] = []\n const ctx: PetRendererContext = {\n petId: props.definition.id,\n assetBase: '/pet/' + encodeURIComponent(props.definition.id),\n container,\n phase: streamRef.current,\n interact: props.onPet,\n onCleanup: (fn) => { cleanups.push(fn) },\n }\n let handle: Live2dRendererHandle\n try {\n handle = defaultPetRendererRegistry.mount('live2d', ctx, live2d) as Live2dRendererHandle\n } catch {\n setError('load-failed')\n return () => { for (const fn of cleanups.splice(0)) fn() }\n }\n handleRef.current = handle\n handle.onError?.(setError)\n return () => {\n handleRef.current = null\n for (const fn of cleanups.splice(0)) fn()\n handle.dispose()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one activation per pet identity\n }, [props.definition])\n\n // Feed the polled phase into the activation's stream (change-only).\n useEffect(() => {\n streamRef.current?.push(props.phase)\n }, [props.phase])\n\n return (\n <div\n ref={containerRef}\n data-dsh-pet-live2d={props.definition.id}\n style={{ width: '100%', height: '100%' }}\n onPointerDown={(e) => { downRef.current = { x: e.clientX, y: e.clientY } }}\n onPointerUp={(e) => {\n const down = downRef.current\n downRef.current = null\n if (down === null) return\n // A moved pointer is a drag (the chrome owns it), not a tap.\n if (Math.abs(e.clientX - down.x) > 4 || Math.abs(e.clientY - down.y) > 4) return\n const rect = e.currentTarget.getBoundingClientRect()\n handleRef.current?.tap(e.clientX - rect.left, e.clientY - rect.top)\n }}\n >\n {error !== null && (\n <span data-dsh-pet-live2d-error={error}>\n {error === 'core-missing'\n ? props.t('pet.live2d.core-missing')\n : error === 'vendor-missing'\n ? props.t('pet.live2d.vendor-missing')\n : props.t('pet.live2d.load-failed')}\n </span>\n )}\n </div>\n )\n}\n","/**\n * Frames2d visual mount — the React bridge between the pet center chrome\n * and the imperative frames2d renderer, mirroring the live2d mount. The\n * bridge owns the contract context (asset base, phase stream, interaction\n * write-back, activation cleanups), feeds the polled phase into the stream,\n * forwards the chrome's drag gesture onto the conventional 'drag' track\n * (when the pet declares one), and renders the localized fallback card when\n * the served config is invalid.\n * @module @linxin666/dsh-pet/client/renderers/Frames2dVisualMount\n */\n\nimport { useEffect, useRef, useState, type ReactElement } from 'react'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDefinition } from '../../registry.ts'\nimport type { ActivityPhase } from '../../state.ts'\nimport { createPhaseStream, type PhaseStream } from '../phase-stream.ts'\nimport type { DragStream } from '../drag-stream.ts'\nimport type { PetRendererContext } from '../../contracts/renderer.ts'\nimport { defaultPetRendererRegistry } from './registry.ts'\nimport type { Frames2dRendererHandle } from './frames2d.ts'\nimport type { GameplayBus } from '../gameplay-hud.tsx'\nimport type { NS } from '../locales.ts'\n\n/** Mount the frames2d renderer as the sprite's visual (inside the chrome). */\nexport function Frames2dVisualMount(props: {\n definition: PetDefinition\n phase: ActivityPhase\n onPet: () => void\n /** Chrome drag gesture stream (the renderer switch owns it). */\n drag: DragStream\n /** Gameplay coordination bus; the mount registers its track override. */\n bus?: GameplayBus\n t: PropsLocale<typeof NS>['t']\n}): ReactElement {\n const containerRef = useRef<HTMLDivElement | null>(null)\n const streamRef = useRef<PhaseStream | null>(null)\n const handleRef = useRef<Frames2dRendererHandle | null>(null)\n const [invalid, setInvalid] = useState(false)\n\n // One activation per pet definition: build the contract context and mount.\n useEffect(() => {\n setInvalid(false)\n const container = containerRef.current\n const frames2d = props.definition.frames2d\n if (container === null || frames2d === undefined) return undefined\n streamRef.current ??= createPhaseStream(props.phase)\n const cleanups: (() => void)[] = []\n const ctx: PetRendererContext = {\n petId: props.definition.id,\n assetBase: '/pet/' + encodeURIComponent(props.definition.id),\n container,\n phase: streamRef.current,\n interact: props.onPet,\n onCleanup: (fn) => { cleanups.push(fn) },\n }\n let handle: Frames2dRendererHandle\n try {\n handle = defaultPetRendererRegistry.mount('frames2d', ctx, frames2d) as Frames2dRendererHandle\n } catch {\n setInvalid(true)\n return () => { for (const fn of cleanups.splice(0)) fn() }\n }\n handleRef.current = handle\n // The gameplay HUD steers one shared override slot through the bus;\n // mode rules (work blocks drag, sleep wakes on it) keep the two\n // producers from fighting over the slot.\n if (props.bus !== undefined) {\n const gameplayBus = props.bus\n gameplayBus.setTrack = (track) => { handleRef.current?.setState(track) }\n gameplayBus.setIdleTrack = (track) => { handleRef.current?.setIdleTrack(track) }\n // The HUD latches the wanted base idle (skin selection, restored from the\n // host snapshot): apply it on activation, so a late or repeated mount\n // never repaints the pet with the default look.\n if (gameplayBus.idleTrack !== undefined) handle.setIdleTrack(gameplayBus.idleTrack)\n cleanups.push(() => {\n gameplayBus.setTrack = undefined\n gameplayBus.setIdleTrack = undefined\n })\n }\n // The drag gesture drives the conventional 'drag' track when declared.\n // On release, a declared gameplay.dragEndState (miku: standup) plays\n // once; its fallback auto-releases the override back to the phase map.\n const dragTrack = props.definition.gameplay?.dragState ?? (frames2d.tracks.drag === undefined ? undefined : 'drag')\n const offDrag = props.drag.subscribe((dragging) => {\n if (dragTrack === undefined) return\n if (dragging) {\n handle.setState(dragTrack)\n return\n }\n handle.setState(props.definition.gameplay?.dragEndState)\n })\n cleanups.push(offDrag)\n return () => {\n handleRef.current = null\n for (const fn of cleanups.splice(0)) fn()\n handle.dispose()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one activation per pet identity\n }, [props.definition])\n\n // Feed the polled phase into the activation's stream (change-only).\n useEffect(() => {\n streamRef.current?.push(props.phase)\n }, [props.phase])\n\n return (\n <div\n ref={containerRef}\n data-dsh-pet-frames2d={props.definition.id}\n style={{ width: '100%', height: '100%', pointerEvents: 'none' }}\n >\n {invalid && (\n <span data-dsh-pet-frames2d-error=\"invalid-config\">\n {props.t('pet.renderer.unavailable', { renderer: 'frames2d' })}\n </span>\n )}\n </div>\n )\n}\n","/**\n * Renderer switch — the client dispatch seam of the pet center (issue #623,\n * milestone M2 P5 / M3). The pet's manifest picks the renderer: sprite2d\n * hands straight through to the sprite; live2d injects its visual INTO the\n * sprite chrome (the dock, bubbles and panel belong to the pet center, not\n * the renderer); a renderer this build cannot serve renders a clear\n * diagnostic card instead of blanking.\n * @module @linxin666/dsh-pet/client/renderers/PetRendererSwitch\n */\n\nimport { cloneElement, isValidElement, useRef, type ReactElement, type ReactNode } from 'react'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDefinition } from '../../registry.ts'\nimport type { ActivityPhase } from '../../state.ts'\nimport type { PetSpriteProps } from '../PetSprite.tsx'\nimport { createDragStream, type DragStream } from '../drag-stream.ts'\nimport type { GameplayBus } from '../gameplay-hud.tsx'\nimport { defaultPetRendererRegistry } from './registry.ts'\nimport { Live2dVisualMount } from './live2d/Live2dVisualMount.tsx'\nimport { Frames2dVisualMount } from './Frames2dVisualMount.tsx'\nimport type { NS } from '../locales.ts'\n\n/** Dispatch one pet definition to its renderer; unknown kinds get a card. */\nexport function PetRendererSwitch(props: {\n definition: PetDefinition\n /** Current activity phase (fed to renderer visuals). */\n phase: ActivityPhase\n /** The chrome's pet interaction (affinity write-back owner). */\n onPet: () => void\n /** External drag stream (gameplay HUD shares it); created locally otherwise. */\n drag?: DragStream\n /** Gameplay coordination bus forwarded to the frames2d visual mount. */\n bus?: GameplayBus\n t: PropsLocale<typeof NS>['t']\n children?: ReactNode\n}): ReactElement {\n const renderer = props.definition.renderer ?? 'sprite2d'\n // The frames2d drag stream lives one level up: the chrome (PetSprite)\n // reports the gesture through onDraggingChange, the visual subscribes.\n const dragRef = useRef<{ id: string; stream: ReturnType<typeof createDragStream> } | null>(null)\n if (dragRef.current === null || dragRef.current.id !== props.definition.id) {\n dragRef.current = { id: props.definition.id, stream: createDragStream() }\n }\n const drag = props.drag ?? dragRef.current.stream\n if (renderer === 'sprite2d') return <>{props.children}</>\n if (renderer === 'frames2d' && defaultPetRendererRegistry.has('frames2d') && isValidElement<PetSpriteProps>(props.children)) {\n const visual = (\n <Frames2dVisualMount\n definition={props.definition}\n phase={props.phase}\n onPet={props.onPet}\n drag={drag}\n {...(props.bus === undefined ? {} : { bus: props.bus })}\n t={props.t}\n />\n )\n return cloneElement(props.children, { visual, onDraggingChange: (dragging: boolean) => drag.push(dragging) })\n }\n if (renderer === 'live2d' && defaultPetRendererRegistry.has('live2d') && isValidElement<PetSpriteProps>(props.children)) {\n const visual = (\n <Live2dVisualMount\n definition={props.definition}\n phase={props.phase}\n onPet={props.onPet}\n t={props.t}\n />\n )\n return cloneElement(props.children, { visual })\n }\n return (\n <span data-dsh-pet-renderer-fallback={renderer}>\n {props.t('pet.renderer.unavailable', { renderer })}\n </span>\n )\n}\n","/**\n * Pet gameplay — the optional manifest 'gameplay' block and its host engine.\n * The block layers an opt-in mini-game over any frames2d pet: decaying stat\n * bars, currencies, a weighted idle director, touch zones, a work loop, a\n * sleep loop, passive income and a shop (issue: miku-pet generalization).\n *\n * Discipline split matches manifest-v2: STRUCTURE is fail-closed (types,\n * ranges, references into stats/tracks); the host engine is pure — every\n * verb takes an explicit clock and rng so tests stay deterministic. Decay,\n * passive income and sleep restore are lazy-settled on read (the treats.ts\n * discipline): the host runs no timers for gameplay.\n * @module @linxin666/dsh-pet/gameplay\n */\n\n/** One gameplay effect: add amount to a declared stat or a currency. */\nexport interface PetGameplayEffect {\n stat?: string\n currency?: string\n amount: number\n}\n\n/** One roll branch inside a touch zone; uncovered roll mass is a no-op. */\nexport interface PetGameplayTouchBranch {\n probability: number\n effects?: PetGameplayEffect[]\n /** Track played on hit (held for stateMs, then the renderer settles). */\n state?: string\n stateMs?: number\n /** Bubble phrase pool; one is picked on hit. */\n phrases?: string[]\n}\n\nexport interface PetGameplayTouchZone {\n name: string\n /** Vertical slice of the hit box (fractions, y0 < y1). */\n y0: number\n y1: number\n branches: PetGameplayTouchBranch[]\n}\n\nexport interface PetGameplayStatDef {\n max: number\n initial?: number\n decayPerMinute?: number\n /** Decay rate while the work mode is active (defaults to decayPerMinute). */\n workingDecayPerMinute?: number\n /** Extra decay rate while no session is active. */\n idleDecayPerMinute?: number\n}\n\nexport interface PetGameplayShopItem {\n id: string\n label: string\n /** Optional frame path (manifest-relative) shown as the item icon. */\n image?: string\n price: number\n currency: string\n effects?: PetGameplayEffect[]\n lottery?: {\n effects?: PetGameplayEffect[]\n /** Default currency the prize is paid in (tiers may override). */\n currency?: string\n tiers: { probability: number; prize: number; currency?: string }[]\n }\n}\n\n/** The validated manifest 'gameplay' block. */\nexport interface PetGameplayManifest {\n idleDirector?: {\n intervalMs: number\n maxMiss: number\n idleWeight: number\n acts: { track: string; weight: number; phrases?: string[] }[]\n }\n stats?: Record<string, PetGameplayStatDef>\n /** Click hit box inside the sprite box (fractions). */\n hitBox?: { x0: number; y0: number; x1: number; y1: number }\n touch?: {\n zones: PetGameplayTouchZone[]\n /** Plain-click effect while a touch animation holds (miku: mood +0..3). */\n clickBoost?: { stat: string; min: number; max: number }\n }\n work?: {\n state: string\n successState: string\n failState: string\n tickMs: number\n /** Hold time of the result track before the next round. */\n resultMs?: { success: number; fail: number }\n successProbability: number\n success?: { effects: PetGameplayEffect[] }\n fail?: { effects: PetGameplayEffect[] }\n }\n sleep?: {\n state: string\n wakeState?: string\n restore: { stat: string; amount: number; intervalMs: number }\n }\n passiveIncome?: { currency: string; amount: number; intervalMs: number }\n shop?: { state?: string; items: PetGameplayShopItem[] }\n /** Track played while the chrome reports dragging (default 'drag'). */\n dragState?: string\n /** Track played once when a drag ends (miku: standup), before settling. */\n dragEndState?: string\n}\n\n/* ------------------------------------------------------------------ *\n * Manifest parsing (fail-closed structure)\n * ------------------------------------------------------------------ */\n\nconst KEBAB = /^[a-z0-9][a-z0-9-]*$/\nconst MAX_STATS = 16\nconst MAX_ZONES = 8\nconst MAX_BRANCHES = 8\nconst MAX_ACTS = 16\nconst MAX_SHOP_ITEMS = 32\nconst MAX_LOTTERY_TIERS = 16\nconst MAX_PHRASES = 64\nconst PHRASE_MAX_LENGTH = 120\nconst STAT_VALUE_MAX = 1_000_000\nconst CURRENCY_MAX = 9_999_999\n\nconst KNOWN_GAMEPLAY = new Set(['idleDirector', 'stats', 'hitBox', 'touch', 'work', 'sleep', 'passiveIncome', 'shop', 'dragState', 'dragEndState'])\nconst KNOWN_STAT = new Set(['max', 'initial', 'decayPerMinute', 'workingDecayPerMinute', 'idleDecayPerMinute'])\nconst KNOWN_ZONE = new Set(['name', 'y0', 'y1', 'branches'])\nconst KNOWN_TOUCH = new Set(['zones', 'clickBoost'])\nconst KNOWN_BRANCH = new Set(['probability', 'effects', 'state', 'stateMs', 'phrases'])\nconst KNOWN_EFFECT = new Set(['stat', 'currency', 'amount'])\nconst KNOWN_WORK = new Set(['state', 'successState', 'failState', 'tickMs', 'resultMs', 'successProbability', 'success', 'fail'])\nconst KNOWN_SLEEP = new Set(['state', 'wakeState', 'restore'])\nconst KNOWN_SHOP_ITEM = new Set(['id', 'label', 'image', 'price', 'currency', 'effects', 'lottery'])\nconst KNOWN_LOTTERY = new Set(['effects', 'currency', 'tiers'])\nconst KNOWN_IDLE_DIRECTOR = new Set(['intervalMs', 'maxMiss', 'idleWeight', 'acts'])\nconst KNOWN_ACT = new Set(['track', 'weight', 'phrases'])\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction unknownKeys(source: Record<string, unknown>, known: Set<string>): string[] {\n return Object.keys(source).filter(key => !known.has(key))\n}\n\nexport interface GameplayParseHooks {\n /** State names the renderer can play (frames2d track ids). */\n stateNames: ReadonlySet<string>\n error: (message: string) => void\n}\n\nfunction validName(name: unknown, max = 32): name is string {\n return typeof name === 'string' && name.length <= max && KEBAB.test(name)\n}\n\nfunction intIn(value: unknown, min: number, max: number): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value >= min && value <= max\n}\n\nfunction numIn(value: unknown, min: number, max: number): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= min && value <= max\n}\n\nfunction parseEffects(raw: unknown, field: string, stats: Record<string, PetGameplayStatDef>, hooks: GameplayParseHooks): PetGameplayEffect[] | undefined {\n if (raw === undefined) return undefined\n if (!Array.isArray(raw) || raw.length === 0) {\n hooks.error(field + ' must be a non-empty array of effects')\n return undefined\n }\n const effects: PetGameplayEffect[] = []\n for (const entry of raw) {\n if (!isRecord(entry)) {\n hooks.error(field + ': every effect must be an object')\n continue\n }\n const extra = unknownKeys(entry, KNOWN_EFFECT)\n if (extra.length > 0) hooks.error(field + ': unknown effect field(s) ' + extra.map(k => JSON.stringify(k)).join(', '))\n const hasStat = typeof entry.stat === 'string'\n const hasCurrency = typeof entry.currency === 'string'\n if (hasStat === hasCurrency) {\n hooks.error(field + ': an effect needs exactly one of stat or currency')\n continue\n }\n if (!intIn(entry.amount, -STAT_VALUE_MAX, STAT_VALUE_MAX) || entry.amount === 0) {\n hooks.error(field + ': effect amount must be a non-zero integer within ±' + STAT_VALUE_MAX)\n continue\n }\n if (hasStat && stats[entry.stat as string] === undefined) {\n hooks.error(field + ': effect references undeclared stat ' + JSON.stringify(entry.stat))\n continue\n }\n if (hasCurrency && !validName(entry.currency, 24)) {\n hooks.error(field + ': effect currency must be a kebab id')\n continue\n }\n effects.push({\n ...(hasStat ? { stat: entry.stat as string } : {}),\n ...(hasCurrency ? { currency: entry.currency as string } : {}),\n amount: entry.amount as number,\n })\n }\n return effects.length === 0 ? undefined : effects\n}\n\nfunction parsePhrases(raw: unknown, field: string, hooks: GameplayParseHooks): string[] | undefined {\n if (raw === undefined) return undefined\n if (!Array.isArray(raw) || raw.length === 0 || raw.length > MAX_PHRASES\n || raw.some(line => typeof line !== 'string' || line.trim() === '' || line.length > PHRASE_MAX_LENGTH)) {\n hooks.error(field + ' must be 1..' + MAX_PHRASES + ' non-empty lines of at most ' + PHRASE_MAX_LENGTH + ' chars')\n return undefined\n }\n return raw as string[]\n}\n\nfunction parseStateRef(raw: unknown, field: string, hooks: GameplayParseHooks): string | undefined {\n if (raw === undefined) return undefined\n if (typeof raw !== 'string' || !hooks.stateNames.has(raw)) {\n hooks.error(field + ' must name a declared frames2d track')\n return undefined\n }\n return raw\n}\n\n/**\n * Validate the manifest 'gameplay' block (fail-closed). Only frames2d pets\n * may declare gameplay today: every state reference checks against the\n * declared track names.\n */\nexport function parseGameplayManifest(raw: unknown, hooks: GameplayParseHooks): PetGameplayManifest | undefined {\n const error = (message: string): void => hooks.error(message)\n if (!isRecord(raw)) {\n error('gameplay must be an object')\n return undefined\n }\n const extra = unknownKeys(raw, KNOWN_GAMEPLAY)\n if (extra.length > 0) error('gameplay: unknown field(s) ' + extra.map(k => JSON.stringify(k)).join(', '))\n let failed = false\n const fail = (message: string): void => { failed = true; error(message) }\n\n // --- stats (parsed first: effects reference them) ---\n const stats: Record<string, PetGameplayStatDef> = {}\n if (raw.stats !== undefined) {\n if (!isRecord(raw.stats)) fail('gameplay.stats must be an object keyed by stat id')\n else {\n const entries = Object.entries(raw.stats)\n if (entries.length > MAX_STATS) fail('gameplay.stats declares too many stats (max ' + MAX_STATS + ')')\n for (const [name, value] of entries) {\n if (!validName(name, 24)) {\n fail('gameplay.stats: invalid stat id ' + JSON.stringify(name))\n continue\n }\n if (!isRecord(value)) {\n fail('gameplay.stats.' + name + ' must be an object')\n continue\n }\n const statExtra = unknownKeys(value, KNOWN_STAT)\n if (statExtra.length > 0) fail('gameplay.stats.' + name + ': unknown field(s) ' + statExtra.map(k => JSON.stringify(k)).join(', '))\n if (!intIn(value.max, 1, STAT_VALUE_MAX)) {\n fail('gameplay.stats.' + name + '.max must be an integer in [1, ' + STAT_VALUE_MAX + ']')\n continue\n }\n const def: PetGameplayStatDef = { max: value.max }\n if (value.initial !== undefined) {\n if (!numIn(value.initial, 0, value.max)) fail('gameplay.stats.' + name + '.initial must be within [0, max]')\n else def.initial = value.initial\n }\n for (const key of ['decayPerMinute', 'workingDecayPerMinute', 'idleDecayPerMinute'] as const) {\n if (value[key] !== undefined) {\n if (!numIn(value[key], 0, 1000)) fail('gameplay.stats.' + name + '.' + key + ' must be a number in [0, 1000]')\n else def[key] = value[key] as number\n }\n }\n stats[name] = def\n }\n }\n }\n\n const block: PetGameplayManifest = {}\n if (Object.keys(stats).length > 0) block.stats = stats\n\n // --- idle director ---\n if (raw.idleDirector !== undefined) {\n if (!isRecord(raw.idleDirector) || !Array.isArray(raw.idleDirector.acts)) {\n fail('gameplay.idleDirector must be an object with an acts array')\n } else {\n const d = raw.idleDirector as Record<string, unknown> & { acts: unknown[] }\n const dExtra = unknownKeys(d, KNOWN_IDLE_DIRECTOR)\n if (dExtra.length > 0) fail('gameplay.idleDirector: unknown field(s) ' + dExtra.map(k => JSON.stringify(k)).join(', '))\n if (d.intervalMs !== undefined && !intIn(d.intervalMs, 1000, 60_000)) fail('gameplay.idleDirector.intervalMs must be an integer in [1000, 60000]')\n if (d.maxMiss !== undefined && !intIn(d.maxMiss, 0, 10)) fail('gameplay.idleDirector.maxMiss must be an integer in [0, 10]')\n if (d.idleWeight !== undefined && !intIn(d.idleWeight, 0, 10_000)) fail('gameplay.idleDirector.idleWeight must be an integer in [0, 10000]')\n if (d.acts.length === 0 || d.acts.length > MAX_ACTS) fail('gameplay.idleDirector.acts must declare 1..' + MAX_ACTS + ' acts')\n const acts: { track: string; weight: number; phrases?: string[] }[] = []\n for (const act of d.acts as unknown[]) {\n if (!isRecord(act) || !intIn(act.weight, 1, 10_000)) {\n fail('gameplay.idleDirector.acts entries need a weight integer in [1, 10000]')\n continue\n }\n const aExtra = unknownKeys(act, KNOWN_ACT)\n if (aExtra.length > 0) fail('gameplay.idleDirector.acts: unknown field(s) ' + aExtra.map(k => JSON.stringify(k)).join(', '))\n const track = parseStateRef(act.track, 'gameplay.idleDirector.acts.track', hooks)\n if (track === undefined) continue\n const entry: { track: string; weight: number; phrases?: string[] } = { track, weight: act.weight }\n const phrases = parsePhrases(act.phrases, 'gameplay.idleDirector.acts.phrases', hooks)\n if (phrases !== undefined) entry.phrases = phrases\n acts.push(entry)\n }\n if (acts.length > 0) {\n block.idleDirector = {\n intervalMs: intIn(d.intervalMs, 1000, 60_000) ? d.intervalMs as number : 5000,\n maxMiss: intIn(d.maxMiss, 0, 10) ? d.maxMiss as number : 2,\n idleWeight: intIn(d.idleWeight, 0, 10_000) ? d.idleWeight as number : 0,\n acts,\n }\n }\n }\n }\n\n // --- hit box ---\n if (raw.hitBox !== undefined) {\n const b = raw.hitBox\n if (!isRecord(b) || !numIn(b.x0, 0, 1) || !numIn(b.x1, 0, 1) || !numIn(b.y0, 0, 1) || !numIn(b.y1, 0, 1)\n || !(b.x0 < b.x1) || !(b.y0 < b.y1)) {\n fail('gameplay.hitBox must be { x0, y0, x1, y1 } fractions with x0 < x1 and y0 < y1')\n } else {\n block.hitBox = { x0: b.x0, y0: b.y0, x1: b.x1, y1: b.y1 }\n }\n }\n\n // --- touch zones ---\n if (raw.touch !== undefined) {\n if (!isRecord(raw.touch) || !Array.isArray(raw.touch.zones)) {\n fail('gameplay.touch must be an object with a zones array')\n } else {\n const tExtra = unknownKeys(raw.touch, KNOWN_TOUCH)\n if (tExtra.length > 0) fail('gameplay.touch: unknown field(s) ' + tExtra.map(k => JSON.stringify(k)).join(', '))\n let clickBoost: { stat: string; min: number; max: number } | undefined\n if (raw.touch.clickBoost !== undefined) {\n const cb = raw.touch.clickBoost\n if (!isRecord(cb) || typeof cb.stat !== 'string' || stats[cb.stat] === undefined\n || !intIn(cb.min, 0, 1000) || !intIn(cb.max, 0, 1000) || (cb.min as number) > (cb.max as number)) {\n fail('gameplay.touch.clickBoost must be { stat (declared), min, max } integers with 0 <= min <= max <= 1000')\n } else clickBoost = { stat: cb.stat, min: cb.min, max: cb.max }\n }\n const zones: PetGameplayTouchZone[] = []\n if (raw.touch.zones.length === 0 || raw.touch.zones.length > MAX_ZONES) fail('gameplay.touch.zones must declare 1..' + MAX_ZONES + ' zones')\n for (const zoneRaw of raw.touch.zones as unknown[]) {\n if (!isRecord(zoneRaw) || !validName(zoneRaw.name) || !numIn(zoneRaw.y0, 0, 1) || !numIn(zoneRaw.y1, 0, 1) || !(zoneRaw.y0 < zoneRaw.y1)) {\n fail('gameplay.touch.zones entries need a kebab name and 0 <= y0 < y1 <= 1')\n continue\n }\n const zExtra = unknownKeys(zoneRaw, KNOWN_ZONE)\n if (zExtra.length > 0) fail('gameplay.touch.' + zoneRaw.name + ': unknown field(s) ' + zExtra.map(k => JSON.stringify(k)).join(', '))\n if (!Array.isArray(zoneRaw.branches) || zoneRaw.branches.length === 0 || zoneRaw.branches.length > MAX_BRANCHES) {\n fail('gameplay.touch.' + zoneRaw.name + '.branches must declare 1..' + MAX_BRANCHES + ' branches')\n continue\n }\n let probabilitySum = 0\n const branches: PetGameplayTouchBranch[] = []\n for (const branchRaw of zoneRaw.branches as unknown[]) {\n if (!isRecord(branchRaw) || !numIn(branchRaw.probability, 0, 1) || branchRaw.probability === 0) {\n fail('gameplay.touch.' + zoneRaw.name + '.branches entries need a probability in (0, 1]')\n continue\n }\n const bExtra = unknownKeys(branchRaw, KNOWN_BRANCH)\n if (bExtra.length > 0) fail('gameplay.touch.' + zoneRaw.name + ': unknown branch field(s) ' + bExtra.map(k => JSON.stringify(k)).join(', '))\n probabilitySum += branchRaw.probability\n const branch: PetGameplayTouchBranch = { probability: branchRaw.probability }\n const effects = parseEffects(branchRaw.effects, 'gameplay.touch.' + zoneRaw.name + '.effects', stats, hooks)\n if (effects !== undefined) branch.effects = effects\n const state = parseStateRef(branchRaw.state, 'gameplay.touch.' + zoneRaw.name + '.state', hooks)\n if (state !== undefined) branch.state = state\n if (branchRaw.stateMs !== undefined) {\n if (!intIn(branchRaw.stateMs, 200, 10_000)) fail('gameplay.touch.' + zoneRaw.name + '.stateMs must be an integer in [200, 10000]')\n else branch.stateMs = branchRaw.stateMs\n }\n const phrases = parsePhrases(branchRaw.phrases, 'gameplay.touch.' + zoneRaw.name + '.phrases', hooks)\n if (phrases !== undefined) branch.phrases = phrases\n branches.push(branch)\n }\n if (probabilitySum > 1 + 1e-9) fail('gameplay.touch.' + zoneRaw.name + ': branch probabilities must sum to at most 1')\n if (branches.length > 0) zones.push({ name: zoneRaw.name, y0: zoneRaw.y0, y1: zoneRaw.y1, branches })\n }\n if (zones.length > 0 || clickBoost !== undefined) block.touch = { zones, ...(clickBoost === undefined ? {} : { clickBoost }) }\n }\n }\n\n // --- work ---\n if (raw.work !== undefined) {\n const w = raw.work\n if (!isRecord(w)) fail('gameplay.work must be an object')\n else {\n const wExtra = unknownKeys(w, KNOWN_WORK)\n if (wExtra.length > 0) fail('gameplay.work: unknown field(s) ' + wExtra.map(k => JSON.stringify(k)).join(', '))\n const state = parseStateRef(w.state, 'gameplay.work.state', hooks)\n const successState = parseStateRef(w.successState, 'gameplay.work.successState', hooks)\n const failState = parseStateRef(w.failState, 'gameplay.work.failState', hooks)\n if (!intIn(w.tickMs, 1000, 60_000)) fail('gameplay.work.tickMs must be an integer in [1000, 60000]')\n if (!numIn(w.successProbability, 0, 1)) fail('gameplay.work.successProbability must be a number in [0, 1]')\n if (state !== undefined && successState !== undefined && failState !== undefined\n && intIn(w.tickMs, 1000, 60_000) && numIn(w.successProbability, 0, 1)) {\n const work: NonNullable<PetGameplayManifest['work']> = {\n state, successState, failState,\n tickMs: w.tickMs,\n successProbability: w.successProbability,\n }\n if (w.resultMs !== undefined) {\n if (!isRecord(w.resultMs) || !intIn(w.resultMs.success, 200, 10_000) || !intIn(w.resultMs.fail, 200, 10_000)) {\n fail('gameplay.work.resultMs must be { success, fail } integers in [200, 10000]')\n } else work.resultMs = { success: w.resultMs.success, fail: w.resultMs.fail }\n }\n for (const key of ['success', 'fail'] as const) {\n if (w[key] !== undefined) {\n if (!isRecord(w[key])) fail('gameplay.work.' + key + ' must be an object { effects }')\n else {\n const effects = parseEffects((w[key] as Record<string, unknown>).effects, 'gameplay.work.' + key + '.effects', stats, hooks)\n if (effects !== undefined) work[key] = { effects }\n }\n }\n }\n block.work = work\n }\n }\n }\n\n // --- sleep ---\n if (raw.sleep !== undefined) {\n const s = raw.sleep\n if (!isRecord(s) || !isRecord(s.restore)) fail('gameplay.sleep must be an object with a restore block')\n else {\n const sExtra = unknownKeys(s, KNOWN_SLEEP)\n if (sExtra.length > 0) fail('gameplay.sleep: unknown field(s) ' + sExtra.map(k => JSON.stringify(k)).join(', '))\n const state = parseStateRef(s.state, 'gameplay.sleep.state', hooks)\n const wakeState = parseStateRef(s.wakeState, 'gameplay.sleep.wakeState', hooks)\n const restoreStat = typeof s.restore.stat === 'string' && stats[s.restore.stat] !== undefined ? s.restore.stat : undefined\n if (restoreStat === undefined) fail('gameplay.sleep.restore.stat must reference a declared stat')\n if (!intIn(s.restore.amount, 1, 1000)) fail('gameplay.sleep.restore.amount must be an integer in [1, 1000]')\n if (!intIn(s.restore.intervalMs, 1000, 600_000)) fail('gameplay.sleep.restore.intervalMs must be an integer in [1000, 600000]')\n if (state !== undefined && restoreStat !== undefined && intIn(s.restore.amount, 1, 1000) && intIn(s.restore.intervalMs, 1000, 600_000)) {\n block.sleep = {\n state,\n ...(wakeState === undefined ? {} : { wakeState }),\n restore: { stat: restoreStat, amount: s.restore.amount, intervalMs: s.restore.intervalMs },\n }\n }\n }\n }\n\n // --- passive income ---\n if (raw.passiveIncome !== undefined) {\n const p = raw.passiveIncome\n if (!isRecord(p) || !validName(p.currency, 24) || !intIn(p.amount, 1, 10_000) || !intIn(p.intervalMs, 1000, 86_400_000)) {\n fail('gameplay.passiveIncome must be { currency (kebab), amount 1..10000, intervalMs 1000..86400000 }')\n } else {\n block.passiveIncome = { currency: p.currency, amount: p.amount, intervalMs: p.intervalMs }\n }\n }\n\n // --- shop ---\n if (raw.shop !== undefined) {\n const s = raw.shop\n if (!isRecord(s) || !Array.isArray(s.items) || s.items.length === 0 || s.items.length > MAX_SHOP_ITEMS) {\n fail('gameplay.shop must be an object with 1..' + MAX_SHOP_ITEMS + ' items')\n } else {\n const shopState = parseStateRef(s.state, 'gameplay.shop.state', hooks)\n const items: PetGameplayShopItem[] = []\n const seen = new Set<string>()\n for (const itemRaw of s.items as unknown[]) {\n if (!isRecord(itemRaw) || !validName(itemRaw.id, 24)) {\n fail('gameplay.shop.items entries need a kebab id')\n continue\n }\n if (seen.has(itemRaw.id)) {\n fail('gameplay.shop: duplicate item id ' + JSON.stringify(itemRaw.id))\n continue\n }\n seen.add(itemRaw.id)\n const iExtra = unknownKeys(itemRaw, KNOWN_SHOP_ITEM)\n if (iExtra.length > 0) fail('gameplay.shop.' + itemRaw.id + ': unknown field(s) ' + iExtra.map(k => JSON.stringify(k)).join(', '))\n if (typeof itemRaw.label !== 'string' || itemRaw.label.trim() === '' || itemRaw.label.length > 80) {\n fail('gameplay.shop.' + itemRaw.id + '.label must be a non-empty string of at most 80 chars')\n continue\n }\n if (!intIn(itemRaw.price, 1, 1_000_000)) {\n fail('gameplay.shop.' + itemRaw.id + '.price must be an integer in [1, 1000000]')\n continue\n }\n if (!validName(itemRaw.currency, 24)) {\n fail('gameplay.shop.' + itemRaw.id + '.currency must be a kebab id')\n continue\n }\n const item: PetGameplayShopItem = {\n id: itemRaw.id,\n label: itemRaw.label.trim(),\n price: itemRaw.price,\n currency: itemRaw.currency,\n }\n if (itemRaw.image !== undefined) {\n if (typeof itemRaw.image !== 'string' || itemRaw.image.includes('..') || itemRaw.image.includes('\\\\') || itemRaw.image.startsWith('/')) {\n fail('gameplay.shop.' + itemRaw.id + '.image must be a safe manifest-relative frame path')\n } else item.image = itemRaw.image\n }\n const effects = parseEffects(itemRaw.effects, 'gameplay.shop.' + itemRaw.id + '.effects', stats, hooks)\n if (effects !== undefined) item.effects = effects\n if (itemRaw.lottery !== undefined) {\n const l = itemRaw.lottery\n if (!isRecord(l) || !Array.isArray(l.tiers) || l.tiers.length === 0 || l.tiers.length > MAX_LOTTERY_TIERS) {\n fail('gameplay.shop.' + itemRaw.id + '.lottery needs 1..' + MAX_LOTTERY_TIERS + ' tiers')\n } else {\n const lExtra = unknownKeys(l, KNOWN_LOTTERY)\n if (lExtra.length > 0) fail('gameplay.shop.' + itemRaw.id + '.lottery: unknown field(s) ' + lExtra.map(k => JSON.stringify(k)).join(', '))\n if (l.currency !== undefined && !validName(l.currency, 24)) fail('gameplay.shop.' + itemRaw.id + '.lottery.currency must be a kebab id')\n let tierSum = 0\n const tiers: { probability: number; prize: number; currency?: string }[] = []\n for (const tierRaw of l.tiers as unknown[]) {\n if (!isRecord(tierRaw) || !numIn(tierRaw.probability, 0, 1) || tierRaw.probability === 0\n || !intIn(tierRaw.prize, 0, 1_000_000_000)) {\n fail('gameplay.shop.' + itemRaw.id + '.lottery.tiers entries need probability (0,1] and prize 0..1e9')\n continue\n }\n tierSum += tierRaw.probability\n const tier: { probability: number; prize: number; currency?: string } = {\n probability: tierRaw.probability,\n prize: tierRaw.prize,\n }\n if (tierRaw.currency !== undefined) {\n if (!validName(tierRaw.currency, 24)) fail('gameplay.shop.' + itemRaw.id + '.lottery tier currency must be a kebab id')\n else tier.currency = tierRaw.currency\n }\n tiers.push(tier)\n }\n if (tierSum > 1 + 1e-9) fail('gameplay.shop.' + itemRaw.id + '.lottery tier probabilities must sum to at most 1')\n if (tiers.length > 0) {\n const lotteryEffects = parseEffects(l.effects, 'gameplay.shop.' + itemRaw.id + '.lottery.effects', stats, hooks)\n item.lottery = {\n tiers,\n ...(lotteryEffects === undefined ? {} : { effects: lotteryEffects }),\n ...(validName(l.currency, 24) ? { currency: l.currency } : {}),\n }\n }\n }\n }\n if (item.effects === undefined && item.lottery === undefined) {\n fail('gameplay.shop.' + itemRaw.id + ' needs effects or a lottery')\n continue\n }\n items.push(item)\n }\n if (items.length > 0) block.shop = { ...(shopState === undefined ? {} : { state: shopState }), items }\n }\n }\n\n // --- drag state ---\n if (raw.dragState !== undefined) {\n const state = parseStateRef(raw.dragState, 'gameplay.dragState', hooks)\n if (state !== undefined) block.dragState = state\n }\n if (raw.dragEndState !== undefined) {\n const state = parseStateRef(raw.dragEndState, 'gameplay.dragEndState', hooks)\n if (state !== undefined) block.dragEndState = state\n }\n\n return failed ? undefined : block\n}\n\n/* ------------------------------------------------------------------ *\n * Host engine (pure; clock and rng injected)\n * ------------------------------------------------------------------ */\n\n/** Persisted per-pet gameplay state (pet.json 'gameplay' map values). */\nexport interface PetGameplayState {\n stats: Record<string, number>\n currencies: Record<string, number>\n mode: 'work' | 'sleep' | null\n /** Epoch ms of the last lazy settle. */\n settledAt: number\n /** Accumulated remainder ms towards the next passive income tick. */\n incomeCarryMs?: number\n /** Accumulated remainder ms towards the next sleep restore tick. */\n restoreCarryMs?: number\n}\n\n/** Fresh state for one pet: stats at their initial (default max), no currency. */\nexport function initialGameplayState(manifest: PetGameplayManifest, now: number): PetGameplayState {\n const stats: Record<string, number> = {}\n for (const [name, def] of Object.entries(manifest.stats ?? {})) {\n stats[name] = def.initial ?? def.max\n }\n return { stats, currencies: {}, mode: null, settledAt: now }\n}\n\n/** Clamp one stat value into [0, max]; currencies into [0, CURRENCY_MAX]. */\nexport function clampGameplay(state: PetGameplayState, manifest: PetGameplayManifest): void {\n for (const [name, def] of Object.entries(manifest.stats ?? {})) {\n const value = state.stats[name]\n if (value === undefined) state.stats[name] = def.initial ?? def.max\n else state.stats[name] = Math.min(def.max, Math.max(0, value))\n }\n for (const [name, value] of Object.entries(state.currencies)) {\n state.currencies[name] = Math.min(CURRENCY_MAX, Math.max(0, Math.floor(value)))\n }\n}\n\n/**\n * Lazy settle: apply stat decay, passive income and sleep restore for the\n * elapsed wall time since the last settle. Mirrors the treats.ts discipline\n * (no host timers; read paths settle). Returns whether anything changed.\n */\nexport function settleGameplay(\n state: PetGameplayState,\n manifest: PetGameplayManifest,\n now: number,\n options: { sessionActive: boolean },\n): boolean {\n const elapsedMs = now - state.settledAt\n if (elapsedMs <= 0) return false\n const minutes = elapsedMs / 60_000\n let changed = false\n for (const [name, def] of Object.entries(manifest.stats ?? {})) {\n const current = state.stats[name]\n if (current === undefined || current <= 0) continue\n let rate = def.decayPerMinute ?? 0\n if (state.mode === 'work' && def.workingDecayPerMinute !== undefined) rate = def.workingDecayPerMinute\n if (!options.sessionActive) rate += def.idleDecayPerMinute ?? 0\n if (rate <= 0) continue\n const next = Math.max(0, current - rate * minutes)\n if (next !== current) {\n state.stats[name] = next\n changed = true\n }\n }\n if (manifest.passiveIncome !== undefined) {\n const incomeElapsed = elapsedMs + (state.incomeCarryMs ?? 0)\n const interval = manifest.passiveIncome.intervalMs\n const ticks = Math.floor(incomeElapsed / interval)\n state.incomeCarryMs = incomeElapsed % interval\n if (ticks > 0) {\n const currency = manifest.passiveIncome.currency\n state.currencies[currency] = (state.currencies[currency] ?? 0) + ticks * manifest.passiveIncome.amount\n changed = true\n }\n }\n if (state.mode === 'sleep' && manifest.sleep !== undefined) {\n const restoreElapsed = elapsedMs + (state.restoreCarryMs ?? 0)\n const interval = manifest.sleep.restore.intervalMs\n const ticks = Math.floor(restoreElapsed / interval)\n state.restoreCarryMs = restoreElapsed % interval\n if (ticks > 0) {\n const stat = manifest.sleep.restore.stat\n state.stats[stat] = (state.stats[stat] ?? 0) + ticks * manifest.sleep.restore.amount\n changed = true\n }\n } else {\n state.restoreCarryMs = 0\n }\n state.settledAt = now\n clampGameplay(state, manifest)\n return changed\n}\n\n/** Apply one effect vector (touch/work/shop), clamped. */\nexport function applyGameplayEffects(state: PetGameplayState, manifest: PetGameplayManifest, effects: readonly PetGameplayEffect[]): void {\n for (const effect of effects) {\n if (effect.stat !== undefined) {\n state.stats[effect.stat] = (state.stats[effect.stat] ?? 0) + effect.amount\n } else if (effect.currency !== undefined) {\n state.currencies[effect.currency] = (state.currencies[effect.currency] ?? 0) + effect.amount\n }\n }\n clampGameplay(state, manifest)\n}\n\n/** Roll one touch zone branch; undefined when the roll lands in no-op mass. */\nexport function rollTouchBranch(zone: PetGameplayTouchZone, rng: () => number): PetGameplayTouchBranch | undefined {\n const roll = rng()\n let acc = 0\n for (const branch of zone.branches) {\n acc += branch.probability\n if (roll < acc) return branch\n }\n return undefined\n}\n\n/** Roll one work tick outcome. */\nexport function rollWorkOutcome(work: NonNullable<PetGameplayManifest['work']>, rng: () => number): 'success' | 'fail' {\n return rng() < work.successProbability ? 'success' : 'fail'\n}\n\n/** Draw one lottery prize tier; uncovered mass falls through to the last tier. */\nexport function drawLotteryTier(\n lottery: NonNullable<PetGameplayShopItem['lottery']>,\n rng: () => number,\n): { probability: number; prize: number; currency?: string } {\n const roll = rng()\n let acc = 0\n for (const tier of lottery.tiers) {\n acc += tier.probability\n if (roll < acc) return tier\n }\n return lottery.tiers[lottery.tiers.length - 1]!\n}\n\n/** The zone one normalized hit-box point lands in, if any. */\nexport function touchZoneAt(touch: { zones: PetGameplayTouchZone[] }, yFraction: number): PetGameplayTouchZone | undefined {\n return touch.zones.find(zone => yFraction >= zone.y0 && yFraction < zone.y1)\n}\n","/**\n * Gameplay HUD — the client half of the manifest 'gameplay' block (miku-pet\n * generalization). One component owns everything the block needs: the stat\n * bars and shop card (menu / shop pages), the touch-zone tap\n * handling, the idle director rolls, the work and sleep loops, and the\n * float-text toasts. It talks to the host through the injected verb API,\n * writes results straight back into the store (the 2 s poll stays the\n * backstop), and steers the frames2d renderer through the per-pet bus.\n * @module @linxin666/dsh-pet/client/GameplayHud\n */\n\nimport { useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore, type ReactElement } from 'react'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDefinition, PetSkinDefinition } from '../registry.ts'\nimport type { PetGameplayVerbResult } from '../service.ts'\nimport { touchZoneAt } from '../gameplay.ts'\nimport type { PetStoreInstance } from './pet-store.ts'\nimport type { DragStream } from './drag-stream.ts'\nimport type { NS } from './locales.ts'\nimport styles from './pet.module.css'\n\n/** The gameplay verb API the plugin apply body injects (host-authoritative). */\nexport interface GameplayApi {\n touch: (zone?: string) => Promise<PetGameplayVerbResult>\n setMode: (mode: 'work' | 'sleep' | null) => Promise<PetGameplayVerbResult>\n workTick: () => Promise<PetGameplayVerbResult>\n buy: (item: string) => Promise<PetGameplayVerbResult>\n /**\n * Persist the selected skin for the current pet (host-authoritative;\n * `undefined` restores the pet's default look).\n */\n setSkin: (skin: string | undefined) => Promise<{ ok: boolean; error?: string }>\n}\n\n/**\n * The per-pet coordination bus. The frames2d visual mount registers the\n * track override (setTrack); the HUD registers the sprite tap handler; the\n * chrome calls into whatever is registered. Optional chaining everywhere —\n * either side may be mid-remount during a hot reload.\n */\nexport interface GameplayBus {\n setTrack?: (track?: string) => void\n /** Swap the pet's base idle track (skin switch); undefined restores default. */\n setIdleTrack?: (track?: string) => void\n /**\n * The base idle track the HUD wants right now, latched on the bus so a\n * renderer that registers late (or remounts: hidden/summoned, StrictMode's\n * double mount) still applies the restored skin instead of snapping back to\n * the default look. Mutating it never requires a re-render.\n */\n idleTrack?: string\n tap?: (fx: number, fy: number) => void\n /**\n * Card open/close request from the chrome (the hover panel's 玩法 action):\n * the HUD registers this, and calling it with no argument toggles the card\n * while a boolean pins the state (same chrome -> HUD direction as tap).\n */\n openCard?: (open?: boolean) => void\n}\n\ntype HudPage = 'root' | 'shop' | 'skins'\n\n/** One floating toast (prize / insufficient funds). */\ninterface HudFloat {\n id: number\n text: string\n}\n\nlet floatSeq = 0\n\n/** The gameplay overlay for one frames2d pet that declares 'gameplay'. */\nexport function GameplayHud(props: {\n definition: PetDefinition\n store: PetStoreInstance\n api: GameplayApi\n bus: GameplayBus\n drag: DragStream\n t: PropsLocale<typeof NS>['t']\n}): ReactElement | null {\n const { definition, store, api, bus } = props\n const ui = useSyncExternalStore(store.subscribe, store.getSnapshot)\n const def = definition.gameplay\n const view = ui.snapshot?.gameplay\n const phase = ui.snapshot?.phase ?? 'idle'\n // Host-persisted skin selection for this pet (undefined = default look).\n const persistedSkin = ui.snapshot?.skin\n\n const [open, setOpen] = useState(false)\n const [page, setPage] = useState<HudPage>('root')\n // Currently selected skin id (base idle swap); undefined = default look.\n const [skinId, setSkinId] = useState<string | undefined>(undefined)\n const skinIdRef = useRef<string | undefined>(undefined)\n skinIdRef.current = skinId\n const hudRef = useRef<HTMLDivElement | null>(null)\n const cardRef = useRef<HTMLDivElement | null>(null)\n const [floats, setFloats] = useState<HudFloat[]>([])\n // Mutable driver state (refs so intervals never re-arm on a poll tick).\n const modeRef = useRef<'work' | 'sleep' | null>(view?.mode ?? null)\n modeRef.current = view?.mode ?? null\n const phaseRef = useRef(phase)\n phaseRef.current = phase\n const draggingRef = useRef(false)\n const touchLockUntilRef = useRef(0)\n const missRef = useRef(0)\n const busyRef = useRef(false)\n\n // Dynamic-key lookups (stat ids / currency ids are manifest data).\n const tr = props.t as unknown as (key: string, values?: Record<string, string | number>) => string\n const statLabel = (name: string): string => tr('pet.gameplay.stat.' + name)\n const currencyLabel = (name: string): string => tr('pet.gameplay.currency.' + name)\n\n const pushFloat = (text: string): void => {\n const id = ++floatSeq\n setFloats(list => [...list.slice(-3), { id, text }])\n window.setTimeout(() => {\n setFloats(list => list.filter(entry => entry.id !== id))\n }, 1100)\n }\n\n const applyResult = (result: PetGameplayVerbResult): void => {\n if (result.view !== undefined) store.actions.setGameplayView(result.view)\n }\n\n // Tap handling (registered on the bus; PetSprite reports sprite-box\n // fractions). Sleep wakes on tap; work blocks taps; a held touch\n // animation turns taps into the plain-click boost.\n // Skin contract: while a skin is selected, taps only ever play that\n // skin's own click actions — a miss resolves to the plain click boost,\n // never the default touch zones (so a skinned pet cannot trigger the\n // default pet's shy/work reactions).\n useEffect(() => {\n if (def === undefined) return undefined\n // Play a one-shot override track and lock tap input for its duration so\n // consecutive taps cannot retrigger mid-play (skin click actions and\n // default touch-zone reactions share this path).\n const holdTrack = (track: string, holdMs: number): void => {\n bus.setTrack?.(track)\n touchLockUntilRef.current = Date.now() + holdMs\n window.setTimeout(() => {\n if (Date.now() >= touchLockUntilRef.current) bus.setTrack?.(undefined)\n }, holdMs)\n }\n const speak = (phrases?: string[]): void => {\n if (phrases !== undefined && phrases.length > 0) {\n const phrase = phrases[Math.floor(Math.random() * phrases.length)]!\n store.actions.setFeedback({ text: phrase, kind: 'none', at: Date.now() })\n }\n }\n // Total play time of a track in ms (its fallback lands back in the skin\n // base idle, so holding the lock for the full loop is unnecessary).\n const trackDuration = (track: string): number =>\n definition.frames2d?.tracks[track]?.durations.reduce((sum, ms) => sum + ms, 0) ?? 0\n bus.tap = (fx, fy) => {\n if (modeRef.current === 'sleep') {\n void api.setMode(null).then(applyResult, () => undefined)\n return\n }\n if (modeRef.current === 'work') return\n const box = def.hitBox ?? { x0: 0, y0: 0, x1: 1, y1: 1 }\n const hx = (fx - box.x0) / (box.x1 - box.x0)\n const hy = (fy - box.y0) / (box.y1 - box.y0)\n if (hx < 0 || hx > 1 || hy < 0 || hy > 1) return\n if (Date.now() < touchLockUntilRef.current) {\n void api.touch().then(applyResult, () => undefined)\n return\n }\n const activeSkin = definition.frames2d?.skins?.find(skin => skin.id === skinIdRef.current)\n if (activeSkin !== undefined) {\n // Skin click actions roll first (declared order, cumulative\n // probabilities); on a hit play the action's track once. A miss\n // stays on the plain click boost — never the default touch zones.\n const actions = activeSkin.clickActions ?? []\n if (actions.length > 0) {\n let roll = Math.random()\n const fired = actions.find(action => {\n if (roll < action.probability) return true\n roll -= action.probability\n return false\n })\n if (fired !== undefined) {\n holdTrack(fired.track, trackDuration(fired.track) || 3000)\n speak(fired.phrases)\n return\n }\n }\n void api.touch().then(applyResult, () => undefined)\n return\n }\n const zone = def.touch === undefined ? undefined : touchZoneAt(def.touch, hy)\n if (zone === undefined) return\n void api.touch(zone.name).then((result) => {\n applyResult(result)\n if (result.hit !== true) return\n if (result.state !== undefined) holdTrack(result.state, result.stateMs ?? 3000)\n if (result.phrase !== undefined) {\n store.actions.setFeedback({ text: result.phrase, kind: 'none', at: Date.now() })\n }\n }, () => undefined)\n }\n return () => { bus.tap = undefined }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one registration per pet definition\n }, [definition.id, def])\n\n // Panel entry (chrome -> HUD): the hover panel's 玩法 button drives the\n // card through this channel so the chrome never needs the card's state.\n useEffect(() => {\n bus.openCard = (next?: boolean) => {\n setOpen(prev => next ?? !prev)\n setPage('root')\n }\n return () => { bus.openCard = undefined }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one registration per bus\n }, [bus])\n\n // Card placement: the card must not enter the bubble band above the sprite\n // or the hover-panel band below it, so it opens beside the sprite instead\n // of growing upward from the pet's feet. It is vertically centered on the\n // sprite box, clamped to the sprite's height, and the side is chosen by\n // the space available to each side (right first; a pet parked near the\n // right viewport edge flips the card to the left).\n useLayoutEffect(() => {\n if (!open) return undefined\n const hud = hudRef.current\n const card = cardRef.current\n if (hud === null || card === null) return undefined\n const place = (): void => {\n const box = hud.parentElement?.getBoundingClientRect()\n if (box === undefined) return\n const gap = 8\n const width = card.getBoundingClientRect().width\n const toRight = window.innerWidth - box.right\n const x = toRight >= width + gap ? box.width + gap : -(width + gap)\n card.style.transform = 'translate(' + Math.round(x) + 'px, ' + Math.round(-box.height / 2) + 'px) translateY(50%)'\n card.style.maxHeight = Math.round(box.height) + 'px'\n }\n place()\n window.addEventListener('resize', place)\n return () => { window.removeEventListener('resize', place) }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one placement per open/page change\n }, [open, page])\n\n // Drag gestures wake a sleeping pet (miku behavior); the drag stream also\n // feeds the idle director's suppression check.\n useEffect(() => {\n return props.drag.subscribe((dragging) => {\n draggingRef.current = dragging\n if (dragging && modeRef.current === 'sleep') {\n void api.setMode(null).then(applyResult, () => undefined)\n }\n })\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one subscription per drag stream\n }, [props.drag])\n\n // Idle director: weighted rolls between staying idle and playing an act.\n // Runs only while the phase mapping owns the visual (idle phase, no mode,\n // no drag, no held touch animation); maxMiss forces an act after too many\n // idle rolls in a row.\n useEffect(() => {\n const director = def?.idleDirector\n if (def === undefined || director === undefined) return undefined\n const total = director.idleWeight + director.acts.reduce((sum, act) => sum + act.weight, 0)\n if (total <= 0) return undefined\n const timer = window.setInterval(() => {\n if (phaseRef.current !== 'idle') return\n if (modeRef.current !== null || draggingRef.current) return\n if (Date.now() < touchLockUntilRef.current) return\n let pickedAct: { track: string; weight: number; phrases?: string[] } | undefined\n if (missRef.current >= director.maxMiss) {\n // Forced act: pick among the acts only.\n const actTotal = director.acts.reduce((sum, act) => sum + act.weight, 0)\n let actRoll = Math.random() * actTotal\n for (const act of director.acts) {\n actRoll -= act.weight\n if (actRoll < 0) { pickedAct = act; break }\n }\n } else {\n let roll = Math.random() * total\n for (const act of director.acts) {\n roll -= act.weight\n if (roll < 0) { pickedAct = act; break }\n }\n }\n if (pickedAct === undefined) {\n missRef.current += 1\n return\n }\n missRef.current = 0\n bus.setTrack?.(pickedAct.track)\n // Acts with a phrase pool speak one line while they play (miku parity).\n if (pickedAct.phrases !== undefined && pickedAct.phrases.length > 0) {\n const phrase = pickedAct.phrases[Math.floor(Math.random() * pickedAct.phrases.length)]!\n store.actions.setFeedback({ text: phrase, kind: 'none', at: Date.now() })\n }\n }, director.intervalMs)\n return () => window.clearInterval(timer)\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one director per pet definition\n }, [definition.id, def])\n\n // Work loop: hold the work track, adjudicate one round per tick, play the\n // result track for its hold window, then resume. Leaving the mode\n // releases the override so the phase mapping takes over. A skin that\n // declares gameplayTracks for the work states plays its own art instead.\n useEffect(() => {\n const work = def?.work\n if (def === undefined || work === undefined || view?.mode !== 'work') return undefined\n const skinGameplay = definition.frames2d?.skins?.find(skin => skin.id === skinIdRef.current)?.gameplayTracks\n /** The state's track, swapped for the skin's override when it declares one. */\n const trackOf = (state: string): string => skinGameplay?.[state] ?? state\n bus.setTrack?.(trackOf(work.state))\n let resultTimer = 0\n const timer = window.setInterval(() => {\n if (busyRef.current) return\n busyRef.current = true\n void api.workTick().then((result) => {\n busyRef.current = false\n // Leaving work mode while the adjudication is in flight drops the late\n // result: writing it back would show work rewards and play the result\n // track for a mode the user has already left (#1495).\n if (modeRef.current !== 'work') return\n applyResult(result)\n if (result.ok !== true || result.outcome === undefined) return\n const resultTrack = trackOf(result.outcome === 'success' ? work.successState : work.failState)\n const hold = result.outcome === 'success' ? work.resultMs?.success ?? 1300 : work.resultMs?.fail ?? 1900\n bus.setTrack?.(resultTrack)\n resultTimer = window.setTimeout(() => {\n if (modeRef.current === 'work') bus.setTrack?.(trackOf(work.state))\n }, hold)\n }, () => { busyRef.current = false })\n }, work.tickMs)\n return () => {\n window.clearInterval(timer)\n window.clearTimeout(resultTimer)\n bus.setTrack?.(undefined)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- the loop keys on the mode and the selected skin\n }, [definition.id, def, view?.mode, skinId])\n\n // Sleep loop: hold the sleep track; restore is host-side (lazy settle).\n // While a skin with a gameplayTracks.sleep override is selected, the skin's\n // own track replaces the default sleep track (e.g. a skin-specific doze).\n useEffect(() => {\n const sleep = def?.sleep\n if (def === undefined || sleep === undefined || view?.mode !== 'sleep') return undefined\n const skinGameplay = definition.frames2d?.skins?.find(skin => skin.id === skinIdRef.current)?.gameplayTracks\n const hold = skinGameplay?.['sleep'] ?? sleep.state\n bus.setTrack?.(hold)\n return () => bus.setTrack?.(undefined)\n // eslint-disable-next-line react-hooks/exhaustive-deps -- the loop keys on the mode value\n }, [definition.id, def, view?.mode, skinId])\n\n if (def === undefined || view === undefined) return null\n\n const mode = view.mode\n const stats = def.stats ?? {}\n const shop = def.shop\n\n const buy = (itemId: string): void => {\n void api.buy(itemId).then((result) => {\n applyResult(result)\n if (result.ok !== true) {\n if (result.error === 'insufficient-funds') {\n const item = shop?.items.find(entry => entry.id === itemId)\n pushFloat(tr('pet.gameplay.insufficient', { currency: currencyLabel(item?.currency ?? 'treats') }))\n }\n return\n }\n if (result.prize !== undefined) {\n pushFloat(tr('pet.gameplay.prize', { amount: result.prize.amount, currency: currencyLabel(result.prize.currency) }))\n }\n }, () => undefined)\n }\n\n const setMode = (next: 'work' | 'sleep' | null): void => {\n void api.setMode(next).then(applyResult, () => undefined)\n }\n\n // Skin selection is persisted host-side (per pet): re-seed the menu from\n // every fresh state view, so a page reload or client restart keeps the last\n // choice instead of snapping back to the default look.\n useEffect(() => {\n setSkinId(persistedSkin)\n }, [definition.id, persistedSkin])\n\n // Push the resolved base idle track into the renderer whenever the pet or\n // the selection changes. The value is latched on the bus first: the visual\n // may register later, or remount later (hidden/summoned), and reads the\n // latch back on activation so a restored skin never falls back to default.\n useEffect(() => {\n const skin = definition.frames2d?.skins?.find(candidate => candidate.id === skinId)\n bus.idleTrack = skin?.idleTrack\n bus.setIdleTrack?.(skin?.idleTrack)\n // persistedSkin rides the deps so the host's value also re-pushes on\n // arrival (a renderer that mounted early still converges).\n // eslint-disable-next-line react-hooks/exhaustive-deps -- one push per selection\n }, [definition.id, skinId, persistedSkin])\n\n const skins = definition.frames2d?.skins\n /** The base idle track one skin id resolves to (undefined = default look). */\n const skinTrackOf = (id: string | undefined): string | undefined =>\n id === undefined ? undefined : definition.frames2d?.skins?.find(candidate => candidate.id === id)?.idleTrack\n\n const selectSkin = (skin: PetSkinDefinition | undefined): void => {\n setSkinId(skin?.id)\n bus.setIdleTrack?.(skin?.idleTrack)\n // The host owns the choice: a refusal (unknown skin) restores both the\n // menu highlight and the renderer to the value it still serves.\n const restore = (): void => {\n setSkinId(persistedSkin)\n bus.setIdleTrack?.(skinTrackOf(persistedSkin))\n }\n void api.setSkin(skin?.id).then((result) => {\n if (result.ok) return\n restore()\n }, restore)\n }\n\n return (\n <div ref={hudRef} className={styles.gameplayHud} data-dsh-pet-gameplay={definition.id}>\n {floats.map(entry => (\n <div key={entry.id} className={styles.gameplayFloat}>{entry.text}</div>\n ))}\n {mode !== null && (\n <div className={styles.gameplayModeChip}>\n {tr(mode === 'work' ? 'pet.gameplay.working' : 'pet.gameplay.sleeping')}\n </div>\n )}\n {open && (\n <div ref={cardRef} className={styles.gameplayCard} data-page={page}>\n {page === 'root' && (\n <>\n <div className={styles.gameplayBars}>\n {Object.entries(stats).map(([name, stat]) => {\n const value = view.stats[name] ?? 0\n return (\n <div key={name} className={styles.gameplayBarRow} title={statLabel(name) + ' ' + String(value) + '/' + String(stat.max)}>\n <span className={styles.gameplayBarLabel}>{statLabel(name)}</span>\n <span className={styles.gameplayBarTrack}>\n <span\n className={styles.gameplayBarFill}\n style={{ width: Math.round((value / stat.max) * 100) + '%' }}\n />\n </span>\n </div>\n )\n })}\n </div>\n <div className={styles.gameplayActions}>\n {def.sleep !== undefined && (\n <button\n type=\"button\"\n className={styles.action}\n onClick={() => setMode(mode === 'sleep' ? null : 'sleep')}\n >\n {tr(mode === 'sleep' ? 'pet.gameplay.wake' : 'pet.gameplay.sleep')}\n </button>\n )}\n {shop !== undefined && (\n <button type=\"button\" className={styles.action} onClick={() => setPage('shop')}>\n {tr('pet.gameplay.shop')}\n </button>\n )}\n {skins !== undefined && skins.length > 0 && (\n <button type=\"button\" className={styles.action} onClick={() => setPage('skins')}>\n {tr('pet.gameplay.skin')}\n </button>\n )}\n {def.work !== undefined && (\n <button\n type=\"button\"\n className={styles.action}\n onClick={() => setMode(mode === 'work' ? null : 'work')}\n >\n {tr(mode === 'work' ? 'pet.gameplay.stopWork' : 'pet.gameplay.work')}\n </button>\n )}\n </div>\n </>\n )}\n {page === 'shop' && shop !== undefined && (\n <>\n <div className={styles.gameplayShopItems}>\n {shop.items.map(item => (\n <button\n key={item.id}\n type=\"button\"\n className={styles.gameplayShopItem}\n onClick={() => buy(item.id)}\n title={item.label + ' — ' + String(item.price) + ' ' + currencyLabel(item.currency)}\n >\n {item.image !== undefined && (\n <img className={styles.gameplayShopItemImage} src={item.image} alt=\"\" draggable={false} />\n )}\n <span className={styles.gameplayShopItemLabel}>{item.label}</span>\n <span className={styles.gameplayShopItemPrice}>\n {item.price} {currencyLabel(item.currency)}\n </span>\n </button>\n ))}\n </div>\n <div className={styles.gameplayActions}>\n <button type=\"button\" className={styles.action} onClick={() => setPage('root')}>\n {tr('pet.gameplay.back')}\n </button>\n </div>\n </>\n )}\n {page === 'skins' && skins !== undefined && (\n <>\n <div className={styles.gameplaySkinItems}>\n <button\n type=\"button\"\n className={skinId === undefined ? styles.gameplaySkinItem + ' ' + styles.gameplaySkinItemActive : styles.gameplaySkinItem}\n onClick={() => selectSkin(undefined)}\n >\n {tr('pet.gameplay.skinDefault')}\n </button>\n {skins.map(skin => (\n <button\n key={skin.id}\n type=\"button\"\n className={skinId === skin.id ? styles.gameplaySkinItem + ' ' + styles.gameplaySkinItemActive : styles.gameplaySkinItem}\n onClick={() => selectSkin(skin)}\n >\n {skin.label}\n </button>\n ))}\n </div>\n <div className={styles.gameplayActions}>\n <button type=\"button\" className={styles.action} onClick={() => setPage('root')}>\n {tr('pet.gameplay.back')}\n </button>\n </div>\n </>\n )}\n <button\n type=\"button\"\n className={styles.gameplayClose}\n aria-label={tr('pet.gameplay.back')}\n onClick={() => setOpen(false)}\n >\n ×\n </button>\n </div>\n )}\n </div>\n )\n}\n","/**\n * Global floating pet entry. The pet is host-global (its state, display and\n * interactions live on '/api/pet/*' endpoints with no session dimension), so\n * it must not ride a session-scoped slot — on the new-conversation screen no\n * session exists to scope a slot by, and the pet would vanish (issue #48).\n * The client half therefore mounts this entry straight onto 'document.body'\n * (see index.ts): while visible it renders the floating PetSprite (a portal\n * into the plugin root, so the root owns the whole surface), while hidden it\n * renders a fixed-position summon button. Which sprite renders is decided by\n * the host snapshot's pet id resolved against the registry list — no per-pet\n * component exists.\n * @module @linxin666/dsh-pet/client/PetDockEntry\n */\n\nimport { useEffect, useRef, useSyncExternalStore, type ReactElement } from 'react'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { PetDisplayConfig } from '../persist.ts'\nimport type { PetStoreInstance } from './pet-store.ts'\nimport { PetSprite } from './PetSprite.tsx'\nimport { PetRendererSwitch } from './renderers/PetRendererSwitch.tsx'\nimport { createDragStream, type DragStream } from './drag-stream.ts'\nimport { GameplayHud, type GameplayApi, type GameplayBus } from './gameplay-hud.tsx'\nimport { NS } from './locales.ts'\nimport styles from './pet.module.css'\n\n/** Injected actions handed to the dock entry component. */\nexport interface PetInjected {\n /** The app-wide pet store instance (snapshot + registry list + feedback). */\n store: PetStoreInstance\n /** Ensure the first snapshot (and registry list) is fetched (called on mount). */\n ensure: () => void\n /** Pet the sprite (click). */\n pet: () => void\n /** Feed the sprite. */\n feed: () => void\n /** Hide the sprite. */\n hide: () => void\n /** Summon the hidden sprite back. */\n summon: () => void\n /** Persist a drag position. */\n dragEnd: (right: number, bottom: number) => void\n /** Rename the selected pet (persisted by the host). */\n rename: (name: string) => void\n /** Navigate the GUI to the session a bubble reports on. */\n openSession: (sessionId: string) => void\n /** Clear the reaction bubble. */\n feedbackDone: () => void\n /** Gameplay verb API (miku-pet generalization); wired but unused for pets without a gameplay block. */\n gameplay: GameplayApi\n}\n\n/** Composed props of the global pet entry (locale + injected; no slot runtime share). */\nexport type PetDockEntryProps =\n PetInjected\n & PropsLocale<typeof NS>\n & {\n /**\n * DOM node the floating sprite chrome portals into. Defaults to\n * document.body; the plugin apply passes its [data-dsh-plugin=\"pet\"]\n * root so root-keyed suppressors (the portrait mobile layer) and skins\n * own the whole pet surface as one unit.\n */\n portalTarget?: Element\n }\n\nconst DEFAULT_DISPLAY: PetDisplayConfig = { visible: true, size: 160, right: 24, bottom: 20 }\n\n/**\n * Dock entry: while the pet is visible, mount the floating PetSprite (it\n * portals itself onto document.body); while hidden, render the summon\n * button so the pet can always come back. The store is the plugin-owned\n * single instance — the slot system provides none because the pet is\n * host-global, not session-scoped.\n */\nexport function PetDockEntry(props: PetDockEntryProps): ReactElement {\n const { store, ensure } = props\n const ui = useSyncExternalStore(store.subscribe, store.getSnapshot)\n const snapshot = ui.snapshot\n const feedback = ui.feedback\n const definition = ui.pets.find(entry => entry.id === snapshot?.pet.id) ?? null\n const visible = snapshot?.display.visible ?? true\n\n useEffect(() => {\n ensure()\n }, [ensure])\n\n // Per-pet gameplay wiring: the coordination bus (HUD taps <-> chrome,\n // HUD track overrides <-> frames2d mount) and the shared drag stream.\n const auxRef = useRef<{ id: string; bus: GameplayBus; drag: DragStream } | null>(null)\n if (definition !== null && (auxRef.current === null || auxRef.current.id !== definition.id)) {\n auxRef.current = { id: definition.id, bus: {}, drag: createDragStream() }\n }\n const aux = auxRef.current\n const gameplay = definition?.gameplay\n\n if (visible) {\n return (\n <span data-pet-dock data-testid=\"pet-dock\">\n {snapshot === null || definition === null\n ? null\n : (\n <PetRendererSwitch\n definition={definition}\n phase={snapshot?.phase ?? 'idle'}\n onPet={props.pet}\n {...(aux === null ? {} : { drag: aux.drag, bus: aux.bus })}\n t={props.t}\n >\n <PetSprite\n snapshot={snapshot}\n definition={definition}\n display={snapshot.display}\n feedback={feedback}\n onPet={props.pet}\n onFeed={props.feed}\n onHide={props.hide}\n onDragEnd={props.dragEnd}\n onRename={props.rename}\n onOpenSession={props.openSession}\n onFeedbackDone={props.feedbackDone}\n portalTarget={props.portalTarget}\n dragDisabled={snapshot.gameplay?.mode === 'work'}\n {...(gameplay === undefined || aux === null\n ? {}\n : {\n onGameplayTap: (fx: number, fy: number) => aux.bus.tap?.(fx, fy),\n onGameplayMenu: () => aux.bus.openCard?.(),\n hud: (\n <GameplayHud\n definition={definition}\n store={store}\n api={props.gameplay}\n bus={aux.bus}\n drag={aux.drag}\n t={props.t}\n />\n ),\n })}\n t={props.t}\n />\n </PetRendererSwitch>\n )}\n </span>\n )\n }\n const display = snapshot?.display ?? DEFAULT_DISPLAY\n return (\n <button\n type=\"button\"\n className={styles.summon}\n style={{\n position: 'fixed',\n right: display.right,\n bottom: display.bottom,\n zIndex: 2147483000,\n }}\n onClick={props.summon}\n data-testid=\"pet-summon\"\n data-dsh-part=\"summon-button\"\n >\n {props.t('pet.summon', { name: snapshot?.name ?? '' })}\n </button>\n )\n}\n","/**\n * Renderer contract — the seam between the pet center and its renderers\n * (issue #623, milestone M2 P4). Renderers never see the DSH session, the\n * registry, or the network: they receive exactly three capabilities — an\n * asset base URL, the ActivityPhase stream, and the interaction write-back —\n * inside a center-owned container. Every mount is a fresh activation whose\n * cleanups must be idempotent.\n *\n * This contract only serves real consumers: sprite2d (existing) and live2d\n * (M3). Speculative capabilities join only when a renderer actually needs\n * them.\n * @module @linxin666/dsh-pet/contracts/renderer\n */\n\nimport type { ActivityPhase } from '../state.ts'\n\n/** Contract version renderers declare against (independent of the manifest). */\nexport const PET_RENDERER_API_VERSION = 'x-org.linxin666.pet-center/v1alpha1'\n\n/** What the pet center hands a renderer on mount. */\nexport interface PetRendererContext {\n /** The selected pet's id. */\n readonly petId: string\n /** Same-origin URL prefix of this pet's assets ('/pet/<id>'). */\n readonly assetBase: string\n /** Center-owned mount root; renderers must not attach to document.body. */\n readonly container: HTMLElement\n /** The ActivityPhase stream (pet-center owned; renderers subscribe). */\n readonly phase: {\n get(): ActivityPhase\n subscribe(listener: (phase: ActivityPhase) => void): () => void\n }\n /** The single interaction write-back into the affinity economy. */\n readonly interact: (kind: 'tap') => void\n /** Register an activation-scoped cleanup; run on dispose, idempotently. */\n onCleanup(fn: () => void): void\n}\n\n/** A mounted renderer activation. */\nexport interface PetRendererHandle {\n /** Tear the activation down; may be called 0/1/N times. */\n dispose(): void\n}\n\n/**\n * One renderer implementation. validateConfig is fail-closed over the\n * renderer-specific manifest block (schema v2 'sprite2d'/'live2d' blocks).\n */\nexport interface PetRenderer<Config = unknown> {\n readonly id: string\n readonly apiVersion: string\n validateConfig(config: unknown): Config\n mount(ctx: PetRendererContext, config: Config): PetRendererHandle\n}\n","/**\n * Live2D runtime loading (pet-center M3) — the two scripts a live2d mount\n * needs, fetched lazily through the plugin's own runtime route: the\n * user-supplied Cubism Core (proprietary; the plugin never bundles or\n * downloads it — issue #623 M1 §0) and the plugin-shipped MIT vendor bundle\n * (pixi.js + untitled-pixi-live2d-engine). Each loads at most once per page;\n * concurrent mounts share the in-flight promise, and a failure is cached as\n * 'absent' so a broken install stops retrying the network every mount.\n *\n * The vendor surface below is the structural slice the renderer consumes;\n * the real objects come from 'window.__dshPetLive2d' (lib/live2d-vendor.js),\n * so this module never imports pixi — the client bundle stays lean.\n * @module @linxin666/dsh-pet/client/renderers/live2d/runtime\n */\n\n/** Runtime file URLs the host serves ('/api/pet/runtime/<name>', M3-2). */\nconst CORE_URL = '/api/pet/runtime/live2dcubismcore.min.js'\nconst VENDOR_URL = '/api/pet/runtime/live2d-vendor.js'\n\n/** The pixi Application slice the renderer uses. */\nexport interface Live2dVendorApp {\n canvas: HTMLCanvasElement\n stage: { addChild(child: unknown): unknown }\n renderer: {\n readonly width: number\n readonly height: number\n resize(width: number, height: number): void\n }\n init(options: Record<string, unknown>): Promise<void>\n destroy(rendererOptions?: boolean | { removeView?: boolean; releaseGlobalResources?: boolean }, options?: Record<string, unknown>): void\n}\n\n/** The Live2DModel slice the renderer uses. */\nexport interface Live2dVendorModel {\n automator: { autoUpdate: boolean }\n anchor: { set(x: number, y?: number): void }\n position: { set(x: number, y: number): void }\n scale: { set(x: number, y?: number): void }\n readonly width: number\n readonly height: number\n internalModel: {\n settings: {\n motions?: Record<string, unknown[]>\n hitAreas?: readonly { Name?: string }[]\n }\n }\n motion(group: string, index?: number): Promise<unknown>\n expression(name?: string): unknown\n hitTest(x: number, y: number): string[]\n on(event: string, fn: () => void): unknown\n destroy(options?: { children?: boolean; texture?: boolean; baseTexture?: boolean }): void\n}\n\n/** The vendor bundle global (window.__dshPetLive2d). */\nexport interface Live2dVendor {\n Application: new () => Live2dVendorApp\n extensions: { add(...items: unknown[]): void }\n Live2DPlugin: unknown\n configureCubismSDK(options: Record<string, unknown>): void\n Live2DModel: { from(source: string, options?: Record<string, unknown>): Promise<Live2dVendorModel> }\n}\n\ndeclare global {\n interface Window {\n Live2DCubismCore?: unknown\n __dshPetLive2d?: Live2dVendor\n }\n}\n\n/** Injects one classic script tag; resolves on load, rejects on error. */\ntype ScriptInjector = (src: string) => Promise<void>\n\nconst defaultInjector: ScriptInjector = (src) => new Promise<void>((resolve, reject) => {\n const tag = document.createElement('script')\n tag.src = src\n tag.onload = () => resolve()\n tag.onerror = () => reject(new Error('script failed to load: ' + src))\n document.head.appendChild(tag)\n})\n\n/** Test seam: swap the network for a stub injector. */\nexport interface Live2dRuntimeProbe {\n inject?: ScriptInjector\n}\n\nlet corePromise: Promise<boolean> | undefined\nlet vendorPromise: Promise<Live2dVendor | undefined> | undefined\n\n/**\n * Ensure the Cubism Core global exists, injecting the runtime-route script\n * once when absent. Resolves false when the user has not installed the core\n * (a normal state — the renderer turns it into install guidance).\n */\nexport function ensureCubismCore(probe: Live2dRuntimeProbe = {}): Promise<boolean> {\n if (typeof window !== 'undefined' && window.Live2DCubismCore !== undefined) return Promise.resolve(true)\n if (probe.inject !== undefined) {\n return probe.inject(CORE_URL)\n .then(() => typeof window !== 'undefined' && window.Live2DCubismCore !== undefined)\n .catch(() => false)\n }\n corePromise ??= defaultInjector(CORE_URL)\n .then(() => typeof window !== 'undefined' && window.Live2DCubismCore !== undefined)\n .catch(() => false)\n return corePromise\n}\n\n/** Ensure the plugin vendor bundle global exists (same caching discipline). */\nexport function ensureLive2dVendor(probe: Live2dRuntimeProbe = {}): Promise<Live2dVendor | undefined> {\n if (typeof window !== 'undefined' && window.__dshPetLive2d !== undefined) return Promise.resolve(window.__dshPetLive2d)\n if (probe.inject !== undefined) {\n return probe.inject(VENDOR_URL)\n .then(() => typeof window !== 'undefined' ? window.__dshPetLive2d : undefined)\n .catch(() => undefined)\n }\n vendorPromise ??= defaultInjector(VENDOR_URL)\n .then(() => typeof window !== 'undefined' ? window.__dshPetLive2d : undefined)\n .catch(() => undefined)\n return vendorPromise\n}\n\n/** Reset the cached script promises (tests). */\nexport function resetLive2dRuntime(): void {\n corePromise = undefined\n vendorPromise = undefined\n}\n","/**\n * Live2D renderer (pet-center M3, issue #623) — mounts a Cubism model into\n * the center-owned container through the lazy vendor stack. The mount call\n * itself is synchronous per the renderer contract: the boot (core script →\n * vendor script → pixi → model) continues asynchronously and reports fatal\n * failures through the handle's error sink, so the React bridge can render\n * localized guidance. Disposing mid-boot is race-safe.\n *\n * Interaction model: the center chrome owns the affinity economy (its click\n * handler fires the pet interaction exactly like sprite2d); this renderer's\n * 'tap' affordance only drives the model's hit-area motion feedback. The\n * contract's interact write-back stays available for future standalone\n * mounts and is intentionally not invoked here.\n *\n * Motion mapping: manifests map ActivityPhases to motion GROUP names; every\n * unmapped phase (and any mapped-but-absent group) falls back to the idle\n * group — official sample models only ship Idle/TapBody, so the fallback is\n * mandatory. Groups with multiple motions pick a random entry, and a tap\n * that hits a declared hit area plays the conventional 'TapBody' group,\n * returning to the phase's group when the tap motion finishes.\n * @module @linxin666/dsh-pet/client/renderers/live2d\n */\n\nimport type { ActivityPhase } from '../../state.ts'\nimport {\n PET_RENDERER_API_VERSION,\n type PetRenderer,\n type PetRendererContext,\n type PetRendererHandle,\n} from '../../contracts/renderer.ts'\nimport {\n ensureCubismCore,\n ensureLive2dVendor,\n type Live2dVendor,\n type Live2dVendorApp,\n type Live2dVendorModel,\n} from './live2d/runtime.ts'\n\n/** Renderer config: the client-visible live2d block (fail-closed validated). */\nexport interface PetLive2dConfig {\n modelUrl: string\n scale?: number\n translate?: { x?: number; y?: number }\n motions: Partial<Record<ActivityPhase, string>> & { idle: string }\n expressions?: Partial<Record<ActivityPhase, string>>\n hitAreas?: string[]\n}\n\n/** Fatal mount failure codes the bridge localizes. */\nexport type Live2dErrorCode = 'core-missing' | 'vendor-missing' | 'load-failed'\n\n/** The live2d activation handle: contract dispose plus tap + error sink. */\nexport interface Live2dRendererHandle extends PetRendererHandle {\n /** Forward a chrome tap in container coordinates; plays the hit motion. */\n tap(x: number, y: number): void\n /** Subscribe to fatal mount errors (at most one fires per activation). */\n onError(listener: (code: Live2dErrorCode) => void): void\n}\n\n/** The de-facto tap-motion group of Cubism sample models. */\nconst TAP_GROUP = 'TapBody'\n\n/**\n * Keep one screen-appropriate atlas LOD instead of asking Pixi for the\n * engine's default full mip chain. A user model can legitimately carry an\n * 8192px texture while the pet itself is only a few hundred pixels tall;\n * `single-auto` preserves the source for larger renders and generates one\n * downsampled atlas only when the effective on-screen scale warrants it.\n */\nconst TEXTURE_OPTIONS = { lod: 'single-auto' } as const\n/** Recursively release the activation without invalidating shared texture caches. */\nconst DESTROY_OPTIONS = { children: true } as const\n/** Remove only this activation's canvas; `true` would release Pixi globals. */\nconst RENDERER_DESTROY_OPTIONS = { removeView: true } as const\n\ninterface Live2dModelSize {\n width: number\n height: number\n}\n\n/** Ignore hidden/zero boxes and keep Pixi dimensions stable and integral. */\nfunction normalizeRendererSize(width: number, height: number): Live2dModelSize | undefined {\n if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return undefined\n return {\n width: Math.max(1, Math.round(width)),\n height: Math.max(1, Math.round(height)),\n }\n}\n\n/** Fit the model from its unscaled dimensions into the current Pixi screen. */\nfunction layoutModel(\n app: Live2dVendorApp,\n model: Live2dVendorModel,\n sourceSize: Live2dModelSize,\n config: PetLive2dConfig,\n): void {\n const fit = Math.min(\n app.renderer.width / sourceSize.width,\n app.renderer.height / sourceSize.height,\n ) * 0.92\n model.scale.set(fit * (config.scale ?? 1))\n model.anchor.set(0.5)\n model.position.set(\n app.renderer.width / 2 + (config.translate?.x ?? 0),\n app.renderer.height / 2 + (config.translate?.y ?? 0),\n )\n}\n\nlet vendorConfigured = false\n\n/** Configure pixi extensions + the Cubism SDK once per page. */\nfunction configureOnce(vendor: Live2dVendor): void {\n if (vendorConfigured) return\n vendorConfigured = true\n vendor.extensions.add(vendor.Live2DPlugin)\n vendor.configureCubismSDK({ memorySizeMB: 32 })\n}\n\n/** Reset module state (tests). */\nexport function resetLive2dRenderer(): void {\n vendorConfigured = false\n}\n\n/** Fail-closed config validation (contract: unknown manifest block in). */\nfunction validateLive2dConfig(config: unknown): PetLive2dConfig {\n if (typeof config !== 'object' || config === null) throw new Error('live2d config is not an object')\n const source = config as Record<string, unknown>\n if (typeof source.modelUrl !== 'string' || source.modelUrl === '') throw new Error('live2d config modelUrl is required')\n const motions = source.motions\n if (typeof motions !== 'object' || motions === null || typeof (motions as Record<string, unknown>).idle !== 'string') {\n throw new Error('live2d config motions.idle is required')\n }\n return config as PetLive2dConfig\n}\n\n/** The live2d renderer implementation. */\nexport const live2dRenderer: PetRenderer<PetLive2dConfig> = {\n id: 'live2d',\n apiVersion: PET_RENDERER_API_VERSION,\n validateConfig: validateLive2dConfig,\n mount(ctx: PetRendererContext, config: PetLive2dConfig): Live2dRendererHandle {\n let disposed = false\n let app: Live2dVendorApp | undefined\n let model: Live2dVendorModel | undefined\n let modelAttached = false\n let modelSourceSize: Live2dModelSize | undefined\n let resizeObserver: ResizeObserver | undefined\n let resizeTracking = false\n let errorListener: ((code: Live2dErrorCode) => void) | undefined\n let unsubscribe: (() => void) | undefined\n /** The motion group the current phase maps to (resume target after taps). */\n let phaseGroup: string = config.motions.idle\n let tapPlaying = false\n\n const stopResizeTracking = (): void => {\n resizeTracking = false\n resizeObserver?.disconnect()\n resizeObserver = undefined\n }\n\n const resizeRenderer = (pixiApp: Live2dVendorApp, width: number, height: number): void => {\n const next = normalizeRendererSize(width, height)\n if (disposed || !resizeTracking || next === undefined) return\n if (pixiApp.renderer.width === next.width && pixiApp.renderer.height === next.height) return\n pixiApp.renderer.resize(next.width, next.height)\n if (model !== undefined && modelSourceSize !== undefined) {\n layoutModel(pixiApp, model, modelSourceSize, config)\n }\n }\n\n const trackContainerSize = (pixiApp: Live2dVendorApp): void => {\n if (typeof ResizeObserver === 'undefined') return\n resizeTracking = true\n resizeObserver = new ResizeObserver((entries) => {\n const entry = entries.find(candidate => candidate.target === ctx.container)\n if (entry === undefined) return\n resizeRenderer(pixiApp, entry.contentRect.width, entry.contentRect.height)\n })\n resizeObserver.observe(ctx.container)\n // Catch a synchronous layout change between init() and observe().\n resizeRenderer(pixiApp, ctx.container.clientWidth, ctx.container.clientHeight)\n }\n\n /** Release every resource currently owned by this activation exactly once. */\n const destroyResources = (): void => {\n stopResizeTracking()\n unsubscribe?.()\n unsubscribe = undefined\n const currentApp = app\n const currentModel = model\n const modelOwnedByApp = currentApp !== undefined && modelAttached\n app = undefined\n model = undefined\n modelSourceSize = undefined\n modelAttached = false\n try {\n if (currentModel !== undefined && !modelOwnedByApp) currentModel.destroy(DESTROY_OPTIONS)\n } finally {\n currentApp?.destroy(RENDERER_DESTROY_OPTIONS, DESTROY_OPTIONS)\n }\n }\n\n const playGroup = (group: string): void => {\n if (model === undefined) return\n const groups = model.internalModel.settings.motions ?? {}\n const count = Array.isArray(groups[group]) ? groups[group]!.length : 0\n if (count === 0) {\n // A mapped-but-absent group falls back to idle (never blank motion).\n if (group !== config.motions.idle) playGroup(config.motions.idle)\n return\n }\n const index = count > 1 ? Math.floor(Math.random() * count) : 0\n void model.motion(group, index)\n }\n\n const applyPhase = (phase: ActivityPhase): void => {\n phaseGroup = config.motions[phase] ?? config.motions.idle\n playGroup(phaseGroup)\n const expression = config.expressions?.[phase]\n if (expression !== undefined && model !== undefined) void model.expression(expression)\n }\n\n const boot = async (): Promise<void> => {\n if (!await ensureCubismCore()) {\n if (!disposed) errorListener?.('core-missing')\n return\n }\n const vendor = await ensureLive2dVendor()\n if (vendor === undefined) {\n if (!disposed) errorListener?.('vendor-missing')\n return\n }\n configureOnce(vendor)\n const pixiApp = new vendor.Application()\n const initialSize = normalizeRendererSize(ctx.container.clientWidth, ctx.container.clientHeight) ?? {\n width: 160,\n height: 174,\n }\n try {\n await pixiApp.init({\n width: initialSize.width,\n height: initialSize.height,\n backgroundAlpha: 0,\n antialias: true,\n autoDensity: true,\n preference: 'webgl',\n })\n } catch (error) {\n // init() can fail after allocating a partial renderer; cleanup is\n // best-effort because Pixi may not consider that partial app ready.\n try { pixiApp.destroy(RENDERER_DESTROY_OPTIONS, DESTROY_OPTIONS) } catch {}\n throw error\n }\n if (disposed) {\n pixiApp.destroy(RENDERER_DESTROY_OPTIONS, DESTROY_OPTIONS)\n return\n }\n app = pixiApp\n pixiApp.canvas.style.display = 'block'\n pixiApp.canvas.style.width = '100%'\n pixiApp.canvas.style.height = '100%'\n ctx.container.appendChild(pixiApp.canvas)\n // Keep a model that rejects during setup off Ticker.shared; from()\n // does not expose that partial instance to callers for disposal.\n trackContainerSize(pixiApp)\n const loaded = await vendor.Live2DModel.from(config.modelUrl, {\n autoUpdate: false,\n autoHitTest: false,\n autoFocus: false,\n textureOptions: TEXTURE_OPTIONS,\n })\n model = loaded\n if (disposed) {\n destroyResources()\n return\n }\n modelSourceSize = {\n width: Math.max(1, loaded.width),\n height: Math.max(1, loaded.height),\n }\n // Auto-fit the model into the container; the manifest scale multiplies\n // the fit and translate offsets from the center anchor.\n layoutModel(pixiApp, loaded, modelSourceSize, config)\n pixiApp.stage.addChild(loaded)\n modelAttached = true\n loaded.automator.autoUpdate = true\n // Resume the phase group once a tap motion finishes playing.\n loaded.on('motionFinish', () => {\n if (tapPlaying) {\n tapPlaying = false\n playGroup(phaseGroup)\n }\n })\n applyPhase(ctx.phase.get())\n unsubscribe = ctx.phase.subscribe(applyPhase)\n }\n\n void boot().catch(() => {\n try {\n destroyResources()\n } finally {\n if (!disposed) errorListener?.('load-failed')\n }\n })\n\n return {\n dispose() {\n if (disposed) return\n disposed = true\n destroyResources()\n },\n tap(x: number, y: number) {\n const current = model\n if (disposed || current === undefined) return\n const hits = current.hitTest(x, y)\n const allowed = config.hitAreas\n const hit = allowed === undefined ? hits.length > 0 : hits.some(name => allowed.includes(name))\n if (!hit) return\n const groups = current.internalModel.settings.motions ?? {}\n const group = groups[TAP_GROUP]\n if (!Array.isArray(group) || group.length === 0) return\n tapPlaying = true\n const index = group.length > 1 ? Math.floor(Math.random() * group.length) : 0\n void current.motion(TAP_GROUP, index)\n },\n onError(listener: (code: Live2dErrorCode) => void) {\n errorListener = listener\n },\n }\n },\n}\n","/**\n * frames2d renderer — plays the free-form named frame-sequence tracks of a\n * frames2d pet (manifest v2 'frames2d' block). The pet center picks the\n * track from the ActivityPhase stream (phase -> track map, idle fallback);\n * the gameplay driver may force a track through the handle's setState\n * override (drag/work/sleep/shop...), and a finished non-loop track settles\n * into its fallback, releasing the override when the fallback matches the\n * phase-mapped track. Rendering never throws: a broken track list degrades\n * to the first decodable frame, and the 1.2 s stall watchdog re-kicks the\n * playback chain after timer throttling.\n *\n * Frame presentation has two modes picked once at mount by capability\n * probing:\n * - Canvas bitmap buffer (default where createImageBitmap/fetch/2D context\n * exist): frames are decoded once into an ImageBitmap on demand, with a\n * bounded look-ahead window over the playing track, and drawn onto one\n * <canvas> - steady-state playback issues zero DOM mutations and zero\n * re-decodes (measured hotspot: swapping <img>.src per frame drove image\n * decode + invalidation every tick).\n * - Classic <img> fallback (jsdom/tests or missing APIs): identical to the\n * historical behavior - cache-warm Image elements plus guarded src swaps,\n * so environments without modern decoding keep working unchanged.\n *\n * @module @linxin666/dsh-pet/client/renderers/frames2d\n */\n\nimport { PET_RENDERER_API_VERSION, type PetRenderer, type PetRendererContext, type PetRendererHandle } from '../../contracts/renderer.ts'\nimport type { ActivityPhase } from '../../state.ts'\n\n/** One track as served inside the pet definition (browser URLs). */\nexport interface Frames2dTrackConfig {\n frames: string[]\n durations: number[]\n loop: boolean\n fallback?: string\n}\n\n/** The frames2d block as served inside the pet definition. */\nexport interface PetFrames2dConfig {\n tracks: Record<string, Frames2dTrackConfig>\n phases: Partial<Record<ActivityPhase, string>> & { idle: string }\n /** Selectable skins; each swaps the base idle target while selected. */\n skins?: Frames2dSkinConfig[]\n}\n\n/** One selectable skins entry as served inside the pet definition. */\nexport interface Frames2dSkinConfig {\n id: string\n label: string\n /** A declared looping track that becomes the base idle while selected. */\n idleTrack: string\n /** Click actions exclusive to this skin (roll by probability; miss → touch zones). */\n clickActions?: Frames2dSkinClickActionConfig[]\n /** Gameplay-state overrides (state -> track) swapped in while selected. */\n gameplayTracks?: Record<string, string>\n}\n\n/** One probability-rolled tap action a skin may declare. */\nexport interface Frames2dSkinClickActionConfig {\n track: string\n probability: number\n phrases?: string[]\n}\n\nexport interface Frames2dRendererHandle extends PetRendererHandle {\n /** Force a track id (gameplay override); undefined returns to phase mapping. */\n setState(track: string | undefined): void\n /**\n * Swap the base idle target (idle phase, unmapped phases and every\n * fallback back to idle) to a declared looping track — a skin. undefined\n * restores the manifest idle track.\n */\n setIdleTrack(track: string | undefined): void\n /** The track currently playing (diagnostics and tests). */\n currentTrack(): string\n}\n\n/** Stall watchdog period: a looping track stuck longer re-kicks its chain. */\nconst WATCHDOG_MS = 1200\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** Fail-closed client-side validation of the served frames2d block. */\nfunction validateFrames2dConfig(config: unknown): PetFrames2dConfig {\n if (!isRecord(config) || !isRecord(config.tracks) || !isRecord(config.phases)) {\n throw new Error('frames2d config requires tracks and phases objects')\n }\n const tracks: Record<string, Frames2dTrackConfig> = {}\n for (const [name, raw] of Object.entries(config.tracks)) {\n if (!isRecord(raw) || !Array.isArray(raw.frames) || raw.frames.length === 0\n || raw.frames.some(f => typeof f !== 'string' || f === '')\n || !Array.isArray(raw.durations) || raw.durations.length !== raw.frames.length\n || raw.durations.some(d => typeof d !== 'number' || !(d > 0))) {\n throw new Error('frames2d track ' + JSON.stringify(name) + ' needs same-length frames/durations')\n }\n tracks[name] = {\n frames: raw.frames as string[],\n durations: raw.durations as number[],\n loop: raw.loop !== false,\n ...(typeof raw.fallback === 'string' ? { fallback: raw.fallback } : {}),\n }\n }\n const phases = config.phases as Partial<Record<ActivityPhase, string>>\n if (typeof phases.idle !== 'string' || tracks[phases.idle] === undefined) {\n throw new Error('frames2d phases.idle must name an existing track')\n }\n // Skins: keep entries whose idleTrack survives validation and loops —\n // a non-looping base idle would settle into itself forever. Click actions\n // with an unresolvable track are dropped from that skin.\n let skins: Frames2dSkinConfig[] | undefined\n if (Array.isArray(config.skins)) {\n const resolved: Frames2dSkinConfig[] = []\n for (const skin of config.skins as unknown[]) {\n if (!isRecord(skin) || typeof skin.id !== 'string' || typeof skin.label !== 'string'\n || typeof skin.idleTrack !== 'string' || skin.idleTrack === '') continue\n const target = tracks[skin.idleTrack]\n if (target === undefined || !target.loop) continue\n let clickActions: Frames2dSkinClickActionConfig[] | undefined\n if (Array.isArray(skin.clickActions)) {\n const kept: Frames2dSkinClickActionConfig[] = []\n for (const action of skin.clickActions as unknown[]) {\n if (!isRecord(action) || typeof action.track !== 'string' || action.track === ''\n || typeof action.probability !== 'number' || !(action.probability > 0) || action.probability > 1) continue\n if (tracks[action.track] === undefined) continue\n kept.push({\n track: action.track,\n probability: action.probability,\n ...(Array.isArray(action.phrases) ? { phrases: action.phrases as string[] } : {}),\n })\n }\n if (kept.length > 0) clickActions = kept\n }\n let gameplayTracks: Record<string, string> | undefined\n if (isRecord(skin.gameplayTracks)) {\n const kept: Record<string, string> = {}\n for (const [state, trackName] of Object.entries(skin.gameplayTracks)) {\n if (typeof trackName !== 'string' || trackName === '' || tracks[trackName] === undefined) continue\n kept[state] = trackName\n }\n if (Object.keys(kept).length > 0) gameplayTracks = kept\n }\n resolved.push({\n id: skin.id,\n label: skin.label,\n idleTrack: skin.idleTrack,\n ...(clickActions === undefined ? {} : { clickActions }),\n ...(gameplayTracks === undefined ? {} : { gameplayTracks }),\n })\n }\n if (resolved.length > 0) skins = resolved\n }\n return { tracks, phases: phases as PetFrames2dConfig['phases'], ...(skins === undefined ? {} : { skins }) }\n}\n\ninterface DecodedFrame {\n source: ImageBitmap | HTMLImageElement\n width: number\n height: number\n}\n\nexport const frames2dRenderer: PetRenderer<PetFrames2dConfig> = {\n id: 'frames2d',\n apiVersion: PET_RENDERER_API_VERSION,\n validateConfig: validateFrames2dConfig,\n\n mount(ctx: PetRendererContext, config: PetFrames2dConfig): Frames2dRendererHandle {\n const reducedMotion = typeof window !== 'undefined'\n && typeof window.matchMedia === 'function'\n && window.matchMedia('(prefers-reduced-motion: reduce)').matches\n\n // Capability probe: the canvas path needs decode-to-bitmap, same-origin\n // fetch, and a real 2D context (jsdom returns null and falls back below).\n let canvas: HTMLCanvasElement | null = null\n let context2d: CanvasRenderingContext2D | null = null\n let img: HTMLImageElement | null = null\n try {\n if (typeof createImageBitmap === 'function' && typeof fetch === 'function') {\n const probe = document.createElement('canvas')\n const c2d = probe.getContext('2d')\n if (c2d !== null) {\n canvas = probe\n context2d = c2d\n }\n }\n } catch {\n canvas = null\n context2d = null\n }\n\n if (canvas !== null && context2d !== null) {\n canvas.dataset.dshPetFrames2d = ctx.petId\n canvas.draggable = false\n canvas.style.width = '100%'\n canvas.style.height = '100%'\n canvas.style.objectFit = 'contain'\n canvas.style.pointerEvents = 'none'\n ctx.container.appendChild(canvas)\n } else {\n img = document.createElement('img')\n img.dataset.dshPetFrames2d = ctx.petId\n img.alt = ''\n img.draggable = false\n img.style.width = '100%'\n img.style.height = '100%'\n img.style.objectFit = 'contain'\n img.style.pointerEvents = 'none'\n ctx.container.appendChild(img)\n }\n\n // Decode cache: one concurrent decode per URL, memoized forever (frames\n // are tiny same-origin webp files; failures resolve undefined and keep\n // the last painted frame instead of breaking playback).\n const decoding = new Map<string, Promise<DecodedFrame | undefined>>()\n const decodedAll: Promise<void>[] = []\n // All frame fetches funnel through a small pool. Full-library warm passes\n // on large pets fire 1100+ requests at once, which trips the browser's\n // in-flight request limit (net::ERR_INSUFFICIENT_RESOURCES) and fails\n // whole batches of frames while starving the rest of the page. Playback\n // demand jumps ahead of the warm backlog.\n const FRAME_POOL_LIMIT = 8\n const frameQueue: Array<{ url: string; release: () => void }> = []\n let activeFrames = 0\n\n const decodeFrame = async (url: string): Promise<DecodedFrame | undefined> => {\n try {\n const response = await fetch(url)\n if (!response.ok) throw new Error('http ' + response.status)\n const bitmap = await createImageBitmap(await response.blob())\n return { source: bitmap, width: bitmap.width, height: bitmap.height }\n } catch {\n // Fail-open: classic Image decode keeps non-modern runtimes alive.\n return await new Promise<DecodedFrame | undefined>((resolve) => {\n try {\n const pre = new Image()\n pre.onload = (): void => {\n resolve(pre.naturalWidth > 0 ? { source: pre, width: pre.naturalWidth, height: pre.naturalHeight } : undefined)\n }\n pre.onerror = (): void => resolve(undefined)\n pre.src = url\n } catch {\n resolve(undefined)\n }\n })\n }\n }\n\n const pumpFrames = (): void => {\n while (activeFrames < FRAME_POOL_LIMIT && frameQueue.length > 0) {\n const queued = frameQueue.shift()!\n activeFrames += 1\n queued.release()\n }\n }\n\n const loadFrame = (url: string, jump = false): Promise<DecodedFrame | undefined> => {\n // Jump first: warm-enqueued frames already carry their memo, so a\n // playback demand must reorder the unstarted entry before the cache\n // lookup short-circuits.\n if (jump) {\n const index = frameQueue.findIndex((queued) => queued.url === url)\n if (index > 0) frameQueue.unshift(frameQueue.splice(index, 1)[0]!)\n }\n const cached = decoding.get(url)\n if (cached !== undefined) return cached\n let release!: () => void\n const gate = new Promise<void>((resolve) => { release = resolve })\n const job: Promise<DecodedFrame | undefined> = gate.then(() => (disposed ? undefined : decodeFrame(url)))\n job.then(\n (frame) => { if (frame === undefined) decoding.delete(url) },\n () => decoding.delete(url),\n )\n void job.finally(() => {\n activeFrames -= 1\n pumpFrames()\n })\n decoding.set(url, job)\n decodedAll.push(job.then(() => undefined, () => undefined))\n const entry = { url, release }\n if (jump) frameQueue.unshift(entry)\n else frameQueue.push(entry)\n pumpFrames()\n return job\n }\n\n /**\n * Bounded look-ahead window: decode only the frames playback is about to\n * need. The historical warm pass decoded every frame of every track up\n * front - a shipped pet carries ~1.1k 512x683 frames, so that pass pulls\n * tens of megabytes and retains every decoded bitmap for the life of the\n * page. Prefetching the playing track's next frames keeps loops and phase\n * switches warm while unplayed tracks (and every unselected skin's\n * frames) stay on demand, where playback jumps the queue anyway.\n */\n const PREFETCH_AHEAD = 12\n\n const prefetchAhead = (trackId: string, index: number): void => {\n const def = config.tracks[trackId]\n if (def === undefined) return\n const end = Math.min(def.frames.length, index + 1 + PREFETCH_AHEAD)\n for (let ahead = index + 1; ahead < end; ahead += 1) {\n const url = def.frames[ahead]\n if (url !== undefined) void loadFrame(url)\n }\n }\n\n let disposed = false\n let timer: ReturnType<typeof setTimeout> | undefined\n let watchdog: ReturnType<typeof setInterval> | undefined\n let track: string = config.phases.idle\n let frameIndex = 0\n let lastAdvance = Date.now()\n let override: string | undefined\n // Skin base idle: every \"back to idle\" target resolves through this\n // (idle phase, unmapped phases and fallbacks), so a selected skin swaps\n // the pet's resting look without touching gameplay tracks.\n let baseIdle: string = config.phases.idle\n let drawToken = 0\n let lastDrawnUrl: string | undefined\n\n const trackForPhase = (phase: ActivityPhase): string => {\n const mapped = config.phases[phase]\n if (mapped === undefined) return baseIdle\n const target = mapped === config.phases.idle ? baseIdle : mapped\n return config.tracks[target] !== undefined ? target : baseIdle\n }\n\n /** Canvas path: paints the newest requested frame; stale draws drop out. */\n const paintCanvas = (url: string): void => {\n if (context2d === null || canvas === null) return\n const myToken = ++drawToken\n void loadFrame(url, true).then((frame) => {\n if (disposed || frame === undefined || myToken !== drawToken) return\n if (lastDrawnUrl === url) return\n lastDrawnUrl = url\n // Resizing clears the canvas, so size only when it actually differs.\n if (canvas.width !== frame.width || canvas.height !== frame.height) {\n canvas.width = frame.width\n canvas.height = frame.height\n } else {\n context2d.clearRect(0, 0, canvas.width, canvas.height)\n }\n context2d.drawImage(frame.source as CanvasImageSource, 0, 0)\n }).catch(() => { /* keep last frame */ })\n }\n\n const show = (trackId: string, index: number): void => {\n const def = config.tracks[trackId]\n const url = def?.frames[index]\n if (url === undefined) return\n prefetchAhead(trackId, index)\n if (img !== null) {\n if (img.getAttribute('src') !== url) img.src = url\n return\n }\n paintCanvas(url)\n }\n\n const schedule = (ms: number): void => {\n if (disposed) return\n timer = setTimeout(tick, ms)\n }\n\n function tick(): void {\n if (disposed) return\n const def = config.tracks[track]\n if (def === undefined || def.frames.length === 0) return\n lastAdvance = Date.now()\n const next = frameIndex + 1\n if (next < def.frames.length) {\n frameIndex = next\n show(track, frameIndex)\n schedule(def.durations[frameIndex] ?? 200)\n return\n }\n if (def.loop) {\n frameIndex = 0\n show(track, frameIndex)\n schedule(def.durations[frameIndex] ?? 200)\n return\n }\n // Non-loop completion: settle into the fallback — an explicit fallback to\n // a real track wins, except when it points back at the manifest idle\n // (that resolves through the skin base idle); anything else lands on\n // the skin base idle. When the settle target is what the phase map\n // plays anyway, release the gameplay override.\n const target = def.fallback !== undefined && config.tracks[def.fallback] !== undefined\n ? (def.fallback === config.phases.idle ? baseIdle : def.fallback)\n : baseIdle\n if (target === trackForPhase(ctx.phase.get())) override = undefined\n play(target)\n }\n\n function play(trackId: string): void {\n if (disposed) return\n if (config.tracks[trackId] === undefined) trackId = baseIdle\n if (timer !== undefined) clearTimeout(timer)\n track = trackId\n frameIndex = 0\n lastAdvance = Date.now()\n show(track, frameIndex)\n if (reducedMotion) return\n const def = config.tracks[track]!\n schedule(def.durations[0] ?? 200)\n }\n\n const unsubscribe = ctx.phase.subscribe((phase) => {\n if (override !== undefined) return\n const target = trackForPhase(phase)\n if (target !== track) play(target)\n })\n\n // Watchdog: a throttled-away timer (background tab) leaves the chain\n // dead; re-kick when a looping track has not advanced on schedule.\n if (!reducedMotion) {\n watchdog = setInterval(() => {\n if (disposed) return\n const def = config.tracks[track]\n if (def === undefined || !def.loop) return\n const expected = (def.durations[frameIndex] ?? 200) + WATCHDOG_MS\n if (Date.now() - lastAdvance > expected) tick()\n }, WATCHDOG_MS)\n }\n\n play(track)\n\n let disposedOnce = false\n const dispose = (): void => {\n if (disposedOnce) return\n disposedOnce = true\n disposed = true\n unsubscribe()\n if (timer !== undefined) clearTimeout(timer)\n if (watchdog !== undefined) clearInterval(watchdog)\n // Release decoded bitmaps after pending decodes settle; close() is\n // browser-only, so guard it for exotic hosts. Queued-but-unstarted\n // frames release immediately as no-ops so the settle barrier drains.\n for (const queued of frameQueue.splice(0)) queued.release()\n void Promise.allSettled(decodedAll).then(() => {\n for (const job of decoding.values()) {\n void job.then((frame) => {\n try {\n const maybeClose = (frame?.source as { close?: () => void } | undefined)?.close\n if (typeof maybeClose === 'function' && frame !== undefined) maybeClose.call(frame.source)\n } catch { /* already released */ }\n }).catch(() => { /* never rejects anyway */ })\n }\n decoding.clear()\n })\n canvas?.remove()\n img?.remove()\n }\n ctx.onCleanup(dispose)\n\n return {\n dispose,\n setState(next: string | undefined): void {\n if (disposed) return\n if (next === undefined) {\n override = undefined\n const target = trackForPhase(ctx.phase.get())\n if (target !== track) play(target)\n return\n }\n if (config.tracks[next] === undefined) return\n override = next\n if (next !== track) play(next)\n },\n setIdleTrack(next: string | undefined): void {\n if (disposed) return\n if (next === undefined || (config.tracks[next] !== undefined && config.tracks[next]!.loop)) {\n baseIdle = next ?? config.phases.idle\n }\n // A skin only owns the resting look: re-resolve only when no\n // gameplay override is active (active overrides end into baseIdle).\n if (override === undefined) {\n const target = trackForPhase(ctx.phase.get())\n if (target !== track) play(target)\n }\n },\n currentTrack(): string {\n return track\n },\n }\n },\n}\n","/**\n * Cross-bundle-instance teardown slot for the page-global pet root\n * (issue #785).\n *\n * A client bundle swap (HMR rebuilt frame, plugin update, duplicate\n * injection) runs a new apply body while the previous instance's fiber\n * may still be draining. Module state does not survive the swap, so a\n * closure guard only sees one apply body and the previous instance's\n * container keeps sitting on document.body: the page shows two pets\n * until a full refresh. The slot rides globalThis (which does survive)\n * so a re-apply can find the previous instance and unmount its React\n * root cleanly before mounting its own; the previous fiber's later\n * disposal stays a no-op through the idempotent teardowns.\n */\n\nconst SLOT = Symbol.for('dsh-pet.client-ui-teardown')\n\ninterface TeardownSlot {\n [SLOT]?: (() => void) | undefined\n}\n\n/**\n * Claim the page-global pet UI slot with the current instance's teardown\n * (React root unmount + container removal + poll stop).\n * @param teardown - what a later instance runs to take the slot over.\n * @returns a disposer that clears the slot when the current instance's\n * UI is torn down (settings toggle, takeover, or fiber disposal).\n */\nexport function registerPetUiTeardown(teardown: () => void): () => void {\n const slot = globalThis as TeardownSlot\n slot[SLOT] = teardown\n return () => {\n if (slot[SLOT] === teardown) slot[SLOT] = undefined\n }\n}\n\n/**\n * Run the previous instance's teardown if one is registered, so the\n * re-applying instance becomes the sole owner of the page-global pet\n * root. No-op when the previous fiber already tore down cleanly.\n */\nexport function takeoverPetUiTeardown(): void {\n const slot = globalThis as TeardownSlot\n const teardown = slot[SLOT]\n slot[SLOT] = undefined\n teardown?.()\n}\n","// Generated by scripts/sync-shared.mjs from shared/client/settings/PluginSettingsCard.tsx. Do not edit this copy; edit the shared source and run \"node scripts/sync-shared.mjs\".\n/**\n * Family-shared chrome for plugin settings cards: a disclosure header naming\n * the plugin and what its settings govern, the controls inside, and the save\n * that writes them. Renders nothing while the namespace is unavailable — a\n * deployment that does not compose the owning plugin should show no trace of\n * it. Inlined into each consumer's client bundle; mirrors the official\n * ui-plugin-config PluginCard in a self-contained slice.\n */\n\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState, type KeyboardEvent, type ReactNode } from 'react'\nimport type { CardShell } from './settings-form.ts'\nimport css from './settings-card.module.css'\n\n/** Copy keys the card chrome itself reads; every consumer locale carries this shared vocabulary. */\nexport const CARD_COPY_KEYS = [\n 'settings.collapse',\n 'settings.expand',\n 'settings.notExposed',\n 'settings.unsaved',\n 'settings.readOnly',\n 'settings.saveFailed',\n 'settings.discard',\n 'settings.save',\n 'settings.saving',\n] as const\n\n/** Copy key the card chrome itself reads. */\nexport type CardCopyKey = (typeof CARD_COPY_KEYS)[number]\n\n/** Key domain for the plugin's own copy (inferred per consumer). */\nexport type SettingsCardKey<TKey extends string = string> = TKey | CardCopyKey\n\n/** Card chrome shared by every plugin settings card. */\nexport interface PluginSettingsCardProps<TKey extends string = string> {\n /** Locale reader for this card's copy. */\n t: (key: SettingsCardKey<TKey>, params?: Record<string, string | number>) => string\n /** Locale key of the plugin's name. */\n titleKey: TKey\n /** Locale key of the line describing what this plugin's settings govern. */\n descriptionKey: TKey\n /** Optional rich header description rendered instead of the plain t(descriptionKey) text (the plain text stays as the tooltip). */\n descriptionNode?: ReactNode\n /** The card's form state: availability, writability, and what a save would do. */\n state: CardShell\n /** Write every staged edit. */\n onSave: () => void\n /** Drop every staged edit. */\n onDiscard: () => void\n /**\n * Render the controls expanded on arrival (still collapsible). Defaults to\n * true: promoted first-level sections show their settings immediately.\n */\n defaultOpen?: boolean\n /**\n * Render without the collapse affordance: a static header and an always\n * visible body. Used by first-level settings sections whose nav entry\n * already provides the selection.\n */\n alwaysOpen?: boolean\n /**\n * Hide the save/discard footer: cards whose body applies its own changes\n * immediately (an embedded external settings section) have no staged\n * edits, so the footer would sit there permanently disabled.\n */\n hideFooter?: boolean\n /** The plugin's controls. */\n children: ReactNode\n}\n\n/**\n * Render one plugin settings card.\n * @param props - the plugin's copy keys, its form state, and its controls.\n * @returns the card, or nothing while the namespace is still loading.\n */\nexport function PluginSettingsCard<TKey extends string = string>(props: PluginSettingsCardProps<TKey>) {\n const [open, setOpen] = useState(props.defaultOpen ?? true)\n const { state, alwaysOpen } = props\n if (!state.available) return null\n const title = props.t(props.titleKey)\n const description = props.t(props.descriptionKey)\n const blocked = !state.dirty || state.invalid || state.saving\n const expanded = alwaysOpen === true || open\n const cardClass = expanded ? `${css.cardOpen} ${css.card}` : css.card\n // With alwaysOpen the nav entry already provides the selection, so the\n // header is a static title row instead of a disclosure button.\n const header = alwaysOpen === true\n ? (\n <div className={css.headerStatic}>\n <span className={css.headText}>\n <span className={css.name} title={title}>{title}</span>\n <span className={css.description} title={description}>{props.descriptionNode ?? description}</span>\n </span>\n {state.dirty ? <span className={css.pending} title={props.t('settings.unsaved')}>{props.t('settings.unsaved')}</span> : null}\n </div>\n )\n : (\n <button\n type=\"button\"\n className={css.header}\n aria-expanded={open}\n aria-label={`${props.t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`}\n onClick={() => { setOpen(!open) }}\n >\n <span className={css.headText}>\n <span className={css.name} title={title}>{title}</span>\n <span className={css.description} title={description}>{props.descriptionNode ?? description}</span>\n </span>\n {state.dirty ? <span className={css.pending} title={props.t('settings.unsaved')}>{props.t('settings.unsaved')}</span> : null}\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 14 14\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={open ? `${css.chevron} ${css.chevronOpen}` : css.chevron}\n >\n <path\n d=\"M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z\"\n fill=\"currentColor\"\n />\n </svg>\n </button>\n )\n // The namespace exists but the Host does not serve it to this client (the\n // official settings allowlist omits third-party namespaces): show a card\n // that explains the gap instead of vanishing, so a missing card never\n // reads as a missing plugin.\n if (!state.exposed) {\n return (\n <li className={cardClass}>\n {header}\n {expanded\n ? (\n <div className={css.body}>\n <p className={css.notExposed} role=\"status\">{props.t('settings.notExposed')}</p>\n </div>\n )\n : null}\n </li>\n )\n }\n return (\n <li className={cardClass}>\n {header}\n {expanded\n ? (\n <div className={css.body}>\n {!state.writable ? <p className={css.readOnly} role=\"status\">{props.t('settings.readOnly')}</p> : null}\n {props.children}\n {props.hideFooter === true\n ? null\n : (\n <div className={css.footer}>\n {state.failed\n ? (\n <p className={css.failed} role=\"status\">\n {props.t('settings.saveFailed')}{state.failedReason ? ' - ' + state.failedReason : ''}\n </p>\n )\n : null}\n <button\n type=\"button\"\n className={css.discard}\n disabled={!state.dirty || state.saving}\n onClick={props.onDiscard}\n >\n {props.t('settings.discard')}\n </button>\n <button\n type=\"button\"\n className={css.save}\n disabled={blocked}\n onClick={props.onSave}\n >\n {props.t(!state.saving ? 'settings.save' : 'settings.saving')}\n </button>\n </div>\n )}\n </div>\n )\n : null}\n </li>\n )\n}\n\n/** Props every field control needs regardless of its value type. */\nexport interface FieldProps {\n /** Stable id associating the label with its control. */\n id: string\n /** Visible label. */\n label: string\n /** One-line explanation rendered under the control. */\n hint: string\n /** Draft text this control renders. */\n text: string\n /** True when saving would leave a user-layer entry for this field. */\n overridden: boolean\n /** True when the draft is not a value this field accepts. */\n invalid: boolean\n /** Copy for the overridden badge. */\n overriddenLabel: string\n /** Copy for the reset control. */\n resetLabel: string\n /** Copy shown in place of the hint while the draft is invalid. */\n invalidLabel: string\n /** Disables every control (read-only document, or an unavailable namespace). */\n disabled: boolean\n /** Stage draft text. */\n onEdit: (text: string) => void\n /** Stage a clear so the field re-inherits the composition layer. */\n onReset: () => void\n}\n\n/** A staged value field. `numeric` only hints the keypad: which drafts a field accepts is decided by its spec. */\nexport function ValueField(props: FieldProps & {\n /** Hints a numeric keypad without narrowing what the control accepts. */\n numeric?: boolean\n /** Placeholder shown while the draft is empty. */\n placeholder?: string\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <input\n id={props.id}\n className={props.invalid ? css.inputInvalid : css.input}\n type=\"text\"\n {...props.numeric === true ? { inputMode: 'numeric' as const } : {}}\n {...props.invalid ? { 'aria-invalid': true } : {}}\n value={props.text}\n placeholder={props.placeholder ?? ''}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n />\n <p className={props.invalid ? css.invalid : css.hint}>\n {props.invalid ? props.invalidLabel : props.hint}\n </p>\n </div>\n )\n}\n\nconst NON_SKIN_BODY_MARKERS = new Set(['dshSkinCenter', 'dshSidebarCollapsed'])\n// 检测使用使用皮肤,用了皮肤退回原生select样式,防止样式冲突。默认外观下使用优化后的select样式。\nfunction isSkinActive(): boolean {\n const datasetList = Object.keys(document.body.dataset)\n const isActive = datasetList.some(key => key.startsWith('dsh') && !NON_SKIN_BODY_MARKERS.has(key))\n return isActive\n}\n\ninterface SelectOption {\n value: string\n label: string\n}\n\nconst SELECT_CLOSE_MS = 100\n\n/**\n * The shared dual-mode select control. While an appearance skin is active it\n * renders the legacy native `<select>` untouched, so element-level skin\n * selectors keep working; under the default appearance it renders a\n * self-drawn `role=\"listbox\"` popup whose open/close is transition-animated.\n * Staged cards reach it through BooleanField/ChoiceField; immediate-apply\n * editors (the side-card prefs) bind it directly through onEdit.\n * 双模式下拉框:皮肤激活时用原生 select,默认外观用自绘动画弹层。\n */\nexport function SelectField(props: {\n id: string\n options: ReadonlyArray<SelectOption>\n value: string\n disabled: boolean\n invalid: boolean\n onEdit: (text: string) => void\n}) {\n const { id, options, value } = props\n const [open, setOpen] = useState(false)\n const [closing, setClosing] = useState(false)\n const [phase, setPhase] = useState<'initial' | 'open'>('initial')\n const [activeIndex, setActiveIndex] = useState(0)\n const closeTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const wrapRef = useRef<HTMLDivElement | null>(null)\n const popupRef = useRef<HTMLDivElement | null>(null)\n\n const currentIndex = () => {\n const index = options.findIndex(option => option.value === value)\n return index >= 0 ? index : 0\n }\n\n const close = useCallback(() => {\n if (closeTimer.current !== undefined) clearTimeout(closeTimer.current)\n setClosing(true)\n closeTimer.current = setTimeout(() => {\n setClosing(false)\n setOpen(false)\n }, SELECT_CLOSE_MS)\n }, [])\n\n const openPopup = () => {\n if (closeTimer.current !== undefined) clearTimeout(closeTimer.current)\n setActiveIndex(currentIndex())\n setPhase('initial')\n setClosing(false)\n setOpen(true)\n }\n\n const commit = (index: number) => {\n const option = options[index]\n if (option) props.onEdit(option.value)\n close()\n }\n\n const onTriggerClick = () => {\n if (props.disabled) return\n if (open && !closing) close()\n else openPopup()\n }\n\n const onKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n if (props.disabled) return\n const count = options.length\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowUp':\n case 'Enter':\n case ' ':\n event.preventDefault()\n if (!open) {\n openPopup()\n } else if (!closing) {\n if (event.key === 'ArrowDown') setActiveIndex(index => (index + 1) % count)\n else if (event.key === 'ArrowUp') setActiveIndex(index => (index - 1 + count) % count)\n else commit(activeIndex)\n }\n break\n case 'Escape':\n if (open) {\n event.preventDefault()\n event.stopPropagation()\n close()\n }\n break\n case 'Tab':\n if (open) close()\n break\n }\n }\n\n useEffect(() => () => {\n if (closeTimer.current !== undefined) clearTimeout(closeTimer.current)\n }, [])\n useLayoutEffect(() => {\n if (open && !closing && phase === 'initial') {\n void popupRef.current?.offsetHeight\n setPhase('open')\n }\n }, [open, closing, phase])\n\n useEffect(() => {\n if (!open) return\n const onPointerDown = (event: PointerEvent) => {\n const target = event.target\n if (target instanceof Node && !wrapRef.current?.contains(target)) close()\n }\n document.addEventListener('pointerdown', onPointerDown)\n return () => document.removeEventListener('pointerdown', onPointerDown)\n }, [open, close])\n\n useEffect(() => {\n if (props.disabled && open) close()\n }, [props.disabled, open, close])\n\n // 使用了皮肤则退回原生select样式防止样式冲突。\n if (isSkinActive()) {\n return (\n <select\n id={id}\n className={css.select}\n value={value}\n disabled={props.disabled}\n onChange={(event) => { props.onEdit(event.target.value) }}\n >\n {options.map(option => (\n <option key={option.value} value={option.value}>{option.label}</option>\n ))}\n </select>\n )\n }\n\n const label = options.find(option => option.value === value)?.label ?? ''\n const popupClass = closing\n ? `${css.selectPopup} ${css.selectPopupClose}`\n : phase === 'open'\n ? `${css.selectPopup} ${css.selectPopupOpen}`\n : css.selectPopup\n return (\n <div className={css.selectWrap} ref={wrapRef}>\n <button\n type=\"button\"\n id={id}\n className={`${css.select} ${css.selectButton}`}\n disabled={props.disabled}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-activedescendant={open ? `${id}-o${activeIndex}` : undefined}\n aria-invalid={props.invalid || undefined}\n onClick={onTriggerClick}\n onKeyDown={onKeyDown}\n >\n <span className={css.selectLabel}>{label}</span>\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 14 14\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={open ? `${css.selectChevron} ${css.selectChevronOpen}` : css.selectChevron}\n aria-hidden=\"true\"\n >\n <path\n d=\"M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z\"\n fill=\"currentColor\"\n />\n </svg>\n </button>\n {open\n ? (\n <div className={popupClass} role=\"listbox\" ref={popupRef}>\n {options.map((option, index) => (\n <div\n key={option.value}\n id={`${id}-o${index}`}\n role=\"option\"\n aria-selected={option.value === value}\n className={`${css.selectOption}${option.value === value ? ` ${css.selectOptionSelected}` : ''}${index === activeIndex && !closing ? ` ${css.selectOptionActive}` : ''}`}\n onClick={() => { commit(index) }}\n >\n {option.label}\n </div>\n ))}\n </div>\n )\n : null}\n </div>\n )\n}\n\n/** A staged boolean field: 继承 / 开 / 关. */\nexport function BooleanField(props: FieldProps & {\n /** Copy for the inherit option. */\n inheritLabel: string\n /** Copy for the on option. */\n onLabel: string\n /** Copy for the off option. */\n offLabel: string\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <SelectField\n id={props.id}\n options={[\n { value: '', label: props.inheritLabel },\n { value: 'true', label: props.onLabel },\n { value: 'false', label: props.offLabel },\n ]}\n value={props.text}\n disabled={props.disabled}\n invalid={props.invalid}\n onEdit={props.onEdit}\n />\n <p className={css.hint}>{props.hint}</p>\n </div>\n )\n}\n\n/** A staged enumerated field rendered as a select. */\nexport function ChoiceField(props: FieldProps & {\n /** Copy for the inherit option (draft text is the empty string). */\n inheritLabel: string\n /** Choices rendered in order; `value` is the draft/stored text. */\n choices: ReadonlyArray<{ value: string; label: string }>\n}) {\n return (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor={props.id}>{props.label}</label>\n {props.overridden\n ? (\n <span className={css.badges}>\n <span className={css.badge}>{props.overriddenLabel}</span>\n <button\n type=\"button\"\n className={css.reset}\n disabled={props.disabled}\n onClick={props.onReset}\n >\n {props.resetLabel}\n </button>\n </span>\n )\n : null}\n </div>\n <SelectField\n id={props.id}\n options={[{ value: '', label: props.inheritLabel }, ...props.choices]}\n value={props.text}\n disabled={props.disabled}\n invalid={props.invalid}\n onEdit={props.onEdit}\n />\n <p className={props.invalid ? css.invalid : css.hint}>\n {props.invalid ? props.invalidLabel : props.hint}\n </p>\n </div>\n )\n}\n","// Generated by scripts/sync-shared.mjs from shared/client/settings/settings-form.ts. Do not edit this copy; edit the shared source and run \"node scripts/sync-shared.mjs\".\n/**\n * Staged form model behind the plugin settings card. A card stages what the\n * user types and writes it only when they save — the settings write is a\n * durable, revision-fenced document mutation, so staging keeps what is on\n * screen exactly what a save would store. Family-shared slice inlined into\n * each plugin's client bundle; mirrors the official ui-plugin-config\n * card-store pattern.\n */\n\nimport type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type { SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport { createSnapshotStore } from '@deepseek-ai/dsh-client-store'\n\n/** The write one field's staged text performs when the card is saved. */\nexport type FieldWrite =\n | { kind: 'set'; value: unknown }\n | { kind: 'clear' }\n\n/** How one field converts between its stored value and its draft text. */\nexport interface FieldSpec {\n /** Field name inside the namespace section. */\n field: string\n /**\n * Whether the Host treats this field as a secret and redacts its value from\n * the read-back (role('secret') in the section schema). Redacted secrets are\n * never compared against the draft on save: the Host strips them from every\n * wire view layer, so the settled snapshot carries nothing to read back. A\n * staged secret set is judged by the mutation settling; the rest of its\n * batch, when one exists, is still judged by read-back, and the atomic\n * mutation lands every write or none.\n */\n secret?: boolean\n /** Render a stored value as draft text; the empty string when the section carries none. */\n format: (value: unknown) => string\n /**\n * The write this draft text stages, or undefined when the text is not a\n * value this field accepts — which blocks the save rather than discarding it.\n */\n parse: (text: string) => FieldWrite | undefined\n}\n\n/** One field as the card renders it. */\nexport interface FieldState {\n /** Draft text the control renders. */\n text: string\n /** Whether saving would leave a user-layer entry for this field. */\n overridden: boolean\n /** Whether the draft is not a value this field accepts, which blocks saving. */\n invalid: boolean\n}\n\n/** Form state every plugin settings card shares. */\nexport interface CardShell {\n /** False while the namespace is still loading; the card renders nothing. */\n available: boolean\n /**\n * Whether the namespace is actually served to this client. False when the\n * Host deployment does not expose it (e.g. the owning plugin's settings\n * domain is not mounted): the card renders an explanation\n * instead of its form, so a missing namespace never looks like a missing\n * plugin.\n */\n exposed: boolean\n /** Whether the Host document accepts writes. */\n writable: boolean\n /** Whether the form holds edits that a save would write. */\n dirty: boolean\n /** Whether any staged draft is invalid, which blocks the save. */\n invalid: boolean\n /** Whether a save is crossing the wire. */\n saving: boolean\n /** Whether the last save did not land as staged; cleared by the next edit or save. */\n failed: boolean\n /**\n * The rejection code/message the Host returned for the last failed save,\n * surfaced next to the generic failure text. Undefined while no save has\n * failed (or the failure carried no server reason).\n */\n failedReason?: string\n}\n\n/** The write actions the card's slot entry injects. */\nexport interface CardActions {\n /** Stage draft text for one field. */\n edit: (field: string, text: string) => void\n /** Stage a clear, so saving lets the field re-inherit the composition layer. */\n resetField: (field: string) => void\n /** Write every staged edit, then re-seed from what the Host accepted. */\n save: () => void\n /** Drop every staged edit. */\n discard: () => void\n}\n\n/** One field's staged edit. */\ninterface StagedEdit {\n /** Draft text the control renders. */\n text: string\n /** True when this edit clears the field whatever text it shows. */\n clear: boolean\n}\n\n/** One staged edit resolved into the write a save performs. */\ninterface PlannedWrite {\n /** Field this entry writes. */\n field: string\n /** The durable write this entry performs, inside the save's one atomic mutation. */\n op: BatchedWrite\n /**\n * Read the settled snapshot back and report whether the Host holds this\n * write's effect. Undefined when the draft is not a value the field\n * accepts: there is nothing to write, and the entry blocks the save.\n */\n judge: (() => boolean) | undefined\n}\n\n/** One durable write inside the save's atomic scope mutation. */\nexport interface BatchedWrite {\n /** Field this entry writes. */\n field: string\n /** set stores a value; unset drops the leaf. */\n op: 'set' | 'unset'\n /** Value for op set (absent for unset). */\n value?: unknown\n}\n\n/** Constraints a numeric field's accepted drafts must satisfy, mirroring the host schema. */\nexport interface NumberConstraints {\n /** The accepted value must be a whole number. */\n integer?: boolean\n /** The accepted value must be at least this. */\n min?: number\n}\n\n/** A whole- or decimal-number field. An empty draft clears the field; any other draft that is not a finite number within the constraints blocks the save. */\nexport function numberField(field: string, constraints: NumberConstraints = {}): FieldSpec {\n const { integer = false, min } = constraints\n return {\n field,\n format: value => typeof value === 'number' ? String(value) : '',\n parse: (text) => {\n const trimmed = text.trim()\n if (trimmed === '') return { kind: 'clear' }\n const parsed = Number(trimmed)\n if (!Number.isFinite(parsed)) return undefined\n if (integer && !Number.isInteger(parsed)) return undefined\n if (min !== undefined && parsed < min) return undefined\n return { kind: 'set', value: parsed }\n },\n }\n}\n\n/** A free-text field. An empty draft clears the field. */\nexport function textField(field: string): FieldSpec {\n return {\n field,\n format: value => typeof value === 'string' ? value : '',\n parse: (text) => {\n const trimmed = text.trim()\n return trimmed === '' ? { kind: 'clear' } : { kind: 'set', value: trimmed }\n },\n }\n}\n\n/**\n * A free-text field the Host treats as a secret and redacts from the read-back\n * (role('secret') in the section schema). The card still edits it like text,\n * but a save never compares the redacted value back: the staged set is judged\n * by the mutation settling (see {@link FieldSpec.secret}).\n */\nexport function secretField(field: string): FieldSpec {\n return { ...textField(field), secret: true }\n}\n\n/** A boolean field, edited through true/false draft text. */\nexport function booleanField(field: string): FieldSpec {\n return {\n field,\n format: value => typeof value === 'boolean' ? String(value) : '',\n parse: (text) => {\n const trimmed = text.trim()\n if (trimmed === '') return { kind: 'clear' }\n if (trimmed === 'true') return { kind: 'set', value: true }\n if (trimmed === 'false') return { kind: 'set', value: false }\n return undefined\n },\n }\n}\n\n/** An enumerated string field; only the listed choices are accepted. An empty draft clears the field. */\nexport function choiceField(field: string, choices: readonly string[]): FieldSpec {\n return {\n field,\n format: value => typeof value === 'string' && choices.includes(value) ? value : '',\n parse: (text) => {\n if (text === '') return { kind: 'clear' }\n return choices.includes(text) ? { kind: 'set', value: text } : undefined\n },\n }\n}\n\n/**\n * Stages one card's edits over one settings namespace and writes them on save.\n *\n * The Host is the only authority on whether a value was accepted — its\n * validators own the constraints no schema can express — so the outcome is\n * read back from the section rather than predicted here. A save that did not\n * land keeps its drafts, so the user can correct them instead of retyping.\n */\nexport class CardForm<T> {\n private readonly specs: Map<string, FieldSpec>\n private readonly staged = new Map<string, StagedEdit>()\n private readonly listeners = new Set<() => void>()\n /** The scope subscription installed in the constructor; released by dispose(). */\n private readonly disposeScope: () => void\n private disposed = false\n private saving = false\n private failed = false\n private failedReason: string | undefined\n\n /** @param scope - the bound settings scope for this card's namespace. */\n constructor(\n private readonly scope: SettingsScope<T>,\n specs: FieldSpec[],\n ) {\n this.specs = new Map(specs.map(spec => [spec.field, spec]))\n this.disposeScope = scope.subscribe(() => { this.publish() })\n }\n\n /**\n * Release the scope subscription and every bound store listener. The card\n * must call this on teardown; later calls are no-ops.\n */\n dispose(): void {\n if (this.disposed) return\n this.disposed = true\n this.disposeScope()\n this.listeners.clear()\n }\n\n /** Publish a projection of this form, rebuilt whenever the scope or a draft changes. */\n bind<S>(project: () => S): SnapshotStore<S> {\n const store = createSnapshotStore(project())\n this.listeners.add(() => { store.set(project()) })\n return store\n }\n\n /** Read the card-level state: what the Host serves, and what a save would do. */\n shell(): CardShell {\n const snapshot = this.scope.getSnapshot()\n const plan = this.plan()\n return {\n available: snapshot.status !== 'loading',\n exposed: snapshot.status === 'ready',\n writable: snapshot.writable,\n dirty: plan.length > 0,\n invalid: plan.some(item => item.judge === undefined),\n saving: this.saving,\n failed: this.failed,\n ...this.failedReason === undefined ? {} : { failedReason: this.failedReason },\n }\n }\n\n /** Read one field's state from the effective section and its staged draft. */\n field(field: string): FieldState {\n const spec = this.specOf(field)\n const staged = this.staged.get(field)\n if (staged === undefined) {\n return { text: spec.format(this.sectionValue(field)), overridden: this.stored(field), invalid: false }\n }\n const write = staged.clear ? { kind: 'clear' as const } : spec.parse(staged.text)\n return {\n text: staged.text,\n overridden: write?.kind === 'set',\n invalid: write === undefined,\n }\n }\n\n /** The actions the card's slot registration injects. */\n actions(): CardActions {\n return {\n edit: (field, text) => { this.stage(field, { text, clear: false }) },\n resetField: (field) => {\n this.stage(field, { text: this.specOf(field).format(this.baseValue(field)), clear: true })\n },\n save: () => { void this.save() },\n discard: () => {\n if (this.staged.size === 0 && !this.failed) return\n this.staged.clear()\n this.failed = false\n this.failedReason = undefined\n this.publish()\n },\n }\n }\n\n /**\n * Write every staged edit in one atomic scope mutation, then re-seed from\n * what the Host accepted.\n *\n * The whole batch rides one mutate, so cross-field validate hooks\n * (baseURL+model) judge it as a unit: the Host either applies every write\n * or refuses the batch. The 0.1.2 scope contract never rejects a refused\n * mutation — the scope recovers with a fresh Host view and resolves — so\n * resolution alone proves nothing: the outcome is judged by reading the\n * settled snapshot back, one planned write at a time, and one missed write\n * fails the whole save. A scope that still rejects on refusal (the dsh-web\n * bridge scope) reports through the same failure path with its rejection\n * message. A save that did not land keeps its drafts, so the user can\n * correct them instead of retyping.\n * @returns settlement after the mutation and the read-back.\n */\n async save(): Promise<void> {\n const plan = this.plan()\n const valid = plan.filter((item): item is PlannedWrite & { judge: () => boolean } => item.judge !== undefined)\n if (plan.length === 0 || this.saving || valid.length !== plan.length) return\n // Snapshot the staged entries this save writes, so an edit staged while it\n // is in flight (which replaces the same key) survives: only delete the key\n // when the entry is still the one this save started from.\n const pending = new Map<string, StagedEdit | undefined>()\n for (const item of plan) pending.set(item.field, this.staged.get(item.field))\n this.saving = true\n this.failed = false\n this.failedReason = undefined\n this.publish()\n // One atomic namespace mutation: the 0.1.2 scope contract takes ordered\n // path operations, so the whole staged batch is validated, persisted, and\n // recovered together — either every write lands or none does.\n const ops: Array<{ op: 'set'; path: string[]; value: string | number | boolean } | { op: 'unset'; path: string[] }> = valid.map(item => item.op.op === 'set'\n ? { op: 'set', path: [item.field], value: (item.op as { value: string | number | boolean }).value }\n : { op: 'unset', path: [item.field] })\n let failedReason: string | undefined\n try {\n await this.scope.mutate(ops)\n } catch (error) {\n failedReason = error instanceof Error ? error.message : String(error)\n }\n // The 0.1.2 scope resolves even a refused mutation (it recovers with a\n // fresh view instead of throwing), so resolution alone proves nothing:\n // judge every planned write against the settled snapshot. The mutation is\n // atomic, so one missed write fails the whole save and keeps the drafts.\n const landed = failedReason === undefined && valid.every(item => item.judge())\n for (const [field, before] of pending) {\n if (landed && this.staged.get(field) === before) this.staged.delete(field)\n }\n this.saving = false\n this.failed = !landed\n // A read-back failure carries no server reason: the card surfaces its\n // generic failure copy; a rejecting scope (the bridge) adds its message.\n this.failedReason = failedReason\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, op: { field, op: 'unset' }, judge: () => this.landedUnset(field) })\n continue\n }\n if (staged.text === spec.format(this.sectionValue(field))) continue\n const write = spec.parse(staged.text)\n if (write === undefined) plan.push({ field, op: { field, op: 'unset' }, judge: undefined })\n else if (write.kind === 'clear') plan.push({ field, op: { field, op: 'unset' }, judge: () => this.landedUnset(field) })\n else plan.push({ field, op: { field, op: 'set', value: write.value }, judge: () => this.landedSet(field, write.value) })\n }\n return plan\n }\n\n /**\n * Read-back judgment for a planned set: the user layer must hold the\n * intended value once the mutation has settled.\n */\n private landedSet(field: string, value: unknown): boolean {\n // A redacted secret never appears in any wire view layer: the Host strips\n // role('secret') fields and reports them through a sidecar the scope\n // snapshot does not expose, so there is nothing to compare the draft\n // against. Settling is the only signal the form has; the rest of the\n // batch, when one exists, still carries the atomic verdict by read-back.\n if (this.specOf(field).secret) return true\n return this.userLayer()?.[field] === value\n }\n\n /**\n * Read-back judgment for a planned unset: the field must be gone from the\n * user layer once the mutation has settled.\n */\n private landedUnset(field: string): boolean {\n return !this.stored(field)\n }\n\n private stage(field: string, edit: StagedEdit): void {\n this.staged.set(field, edit)\n this.failed = false\n this.failedReason = undefined\n this.publish()\n }\n\n private specOf(field: string): FieldSpec {\n const spec = this.specs.get(field)\n // Every call site names a field this card declared; a missing one is a\n // wiring mistake that must not degrade into a silently inert control.\n if (spec === undefined) throw new Error(`settings card has no field ${field}`)\n return spec\n }\n\n private snapshotOf(): SettingsScopeSnapshot<T> {\n return this.scope.getSnapshot()\n }\n\n private sectionValue(field: string): unknown {\n return (this.snapshotOf().value as Record<string, unknown> | undefined)?.[field]\n }\n\n private baseValue(field: string): unknown {\n return (this.snapshotOf().base as Record<string, unknown> | undefined)?.[field]\n }\n\n private userLayer(): Record<string, unknown> | undefined {\n return this.snapshotOf().user as Record<string, unknown> | undefined\n }\n\n private stored(field: string): boolean {\n const user = this.userLayer()\n return user !== undefined && Object.hasOwn(user, field)\n }\n\n private publish(): void {\n for (const listener of this.listeners) listener()\n }\n}\n","/**\n * The pet settings card: pet selection plus display layout, bound to the\n * 'pet' settings namespace the host plugin registers. Rendered as an\n * always-open first-level settings page; the section wrapper below mounts it\n * as the content of the top-level 'settings.section' nav entry. The petId\n * choices come from the registry endpoint ('/api/pet/pets') — the same list\n * the sprite renders from — so the card carries no per-pet knowledge.\n */\n\nimport type { ReactNode } from 'react'\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type { SnapshotStore } from '@deepseek-ai/dsh-client-store'\n// Type-only: pulls the settings-surface SlotMap merge (the 'settings.section' entry).\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport { PluginSettingsCard, ValueField, BooleanField, ChoiceField } from './PluginSettingsCard.tsx'\nimport { CardForm, booleanField, choiceField, numberField, type CardActions, type CardShell, type FieldState as CardFieldState } from './settings-form.ts'\nimport sectionCss from './settings-section.module.css'\n\n/** The pet's settings fields this card edits (the namespace's full schema). */\nexport interface PetSettings {\n /** Master switch for the plugin. */\n enabled?: boolean\n /** Master switch. */\n visible?: boolean\n /** Scale of the rendered pet in px (sprite cell height). */\n size?: number\n /** Horizontal inset from the viewport right edge, px. */\n right?: number\n /** Vertical inset from the viewport bottom edge, px. */\n bottom?: number\n /** Selected pet id (a registry entry). */\n petId?: string\n /** Status-decoration master switch (pet-center M5, #567). */\n decorationEnabled?: boolean\n}\n\n/** What the pet settings card renders. */\nexport interface PetSettingsCardState extends CardShell {\n /** Plugin master switch. */\n enabled: CardFieldState\n /** Master switch. */\n visible: CardFieldState\n /** Pet scale. */\n size: CardFieldState\n /** Right inset. */\n right: CardFieldState\n /** Bottom inset. */\n bottom: CardFieldState\n /** Selected pet. */\n petId: CardFieldState\n /** Status-decoration master switch. */\n decorationEnabled: CardFieldState\n /** Pet choices (registry ids + display names), loaded from the host. */\n petChoices: readonly { value: string; label: string }[]\n /** Registry diagnostics (v1 migration hints, invalid entries), host-served. */\n petDiagnostics: readonly PetDiagnosticView[]\n}\n\n/** The registration-side face the card's slot entry injects. */\nexport interface PetSettingsCardFace extends CardActions {\n hooks: {\n /** Card snapshot bound by the renderer as usePetSettingsCard. */\n petSettingsCard: SnapshotStore<PetSettingsCardState>\n }\n}\n\n/** One registry choice as served by '/api/pet/pets'. */\ninterface PetChoice {\n id: string\n displayName: string\n}\n\n/** One registry diagnostic as served by '/api/pet/diagnostics' (#623). */\nexport interface PetDiagnosticView {\n level: 'error' | 'warning'\n message: string\n}\n\n/** Fetch the registry list (the same data the sprite renders from). */\nasync function fetchPetChoices(): Promise<PetChoice[]> {\n const response = await fetch('/api/pet/pets')\n if (!response.ok) throw new Error('pet pets failed: ' + response.status)\n return (await response.json()) as PetChoice[]\n}\n\n/** Fetch the registry diagnostics (v1 migration hints, invalid entries). */\nasync function fetchPetDiagnostics(): Promise<PetDiagnosticView[]> {\n const response = await fetch('/api/pet/diagnostics')\n if (!response.ok) throw new Error('pet diagnostics failed: ' + response.status)\n const body = (await response.json()) as { diagnostics?: PetDiagnosticView[] }\n return body.diagnostics ?? []\n}\n\n/** Bridges the 'pet' scope onto the card's staged form. */\nexport class PetSettingsCardController {\n private readonly form: CardForm<PetSettings>\n private readonly store: SnapshotStore<PetSettingsCardState>\n // The choice list rides a mutable array shared with the choiceField spec,\n // so loading the registry re-validates and re-formats the petId field\n // without rebuilding the form.\n private readonly petChoices: string[] = []\n private readonly petLabels = new Map<string, string>()\n private diagnostics: PetDiagnosticView[] = []\n private loaded = false\n private attempts = 0\n private disposed = false\n /** Pending deferred-load or retry timer; cancelled by dispose(). */\n private pendingTimer: number | undefined\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('decorationEnabled'),\n booleanField('visible'),\n numberField('size'),\n numberField('right'),\n numberField('bottom'),\n choiceField('petId', this.petChoices),\n ])\n this.store = this.form.bind(() => this.projection())\n // Client plugins are applied synchronously during shell startup. Defer\n // the first registry request until that pass completes so transport\n // plugins (notably remote-web-ui on a paired non-loopback origin) can\n // install their fetch channel before /api/pet/pets is issued.\n this.pendingTimer = window.setTimeout(() => {\n this.pendingTimer = undefined\n if (this.disposed) return\n void this.loadPets()\n void this.loadDiagnostics()\n }, 0)\n }\n\n /** Fetch registry diagnostics once (soft-fail: an empty list on error). */\n private async loadDiagnostics(): Promise<void> {\n try {\n this.diagnostics = await fetchPetDiagnostics()\n if (this.disposed) return\n this.store.set(this.projection())\n } catch {\n this.diagnostics = []\n }\n }\n\n /** Resolve the registry choices once (retried a few times on failure). */\n private async loadPets(): Promise<void> {\n if (this.loaded || this.disposed) return\n try {\n const list = await fetchPetChoices()\n if (this.disposed) return\n this.petChoices.splice(0, this.petChoices.length, ...list.map(choice => choice.id))\n for (const choice of list) this.petLabels.set(choice.id, choice.displayName)\n this.loaded = true\n this.store.set(this.projection())\n } catch {\n if (this.disposed) return\n this.attempts += 1\n if (this.attempts < 3) {\n this.pendingTimer = window.setTimeout(() => {\n this.pendingTimer = undefined\n if (this.disposed) return\n void this.loadPets()\n }, 3000)\n }\n }\n }\n\n private projection(): PetSettingsCardState {\n return {\n ...this.form.shell(),\n enabled: this.form.field('enabled'),\n decorationEnabled: this.form.field('decorationEnabled'),\n visible: this.form.field('visible'),\n size: this.form.field('size'),\n right: this.form.field('right'),\n bottom: this.form.field('bottom'),\n petId: this.form.field('petId'),\n petChoices: this.petChoices.map(id => ({ value: id, label: this.petLabels.get(id) ?? id })),\n petDiagnostics: this.diagnostics,\n }\n }\n\n /**\n * Build the face the card's slot registration injects.\n * @returns the card's snapshot and its form actions.\n */\n inject(): PetSettingsCardFace {\n return { hooks: { petSettingsCard: this.store }, ...this.form.actions() }\n }\n\n /**\n * Release the card's scope subscription, bound stores and pending load\n * timers; the slot disposer calls this on teardown.\n */\n dispose(): void {\n if (this.disposed) return\n this.disposed = true\n if (this.pendingTimer !== undefined) {\n window.clearTimeout(this.pendingTimer)\n this.pendingTimer = undefined\n }\n this.form.dispose()\n }\n}\n\n/** Props the renderer binds for the pet settings card. */\nexport type PetSettingsCardProps =\n PropsLocale<'pet'>\n & InjectFace<PetSettingsCardFace>\n\n/**\n * Render the pet settings card.\n * @param props - locale copy, the card snapshot, and its form actions.\n * @returns the card.\n */\nexport function PetSettingsCard(props: PetSettingsCardProps) {\n const { t } = props\n const state = props.usePetSettingsCard(snapshot => snapshot)\n const disabled = !state.writable\n const fieldProps = {\n overriddenLabel: t('settings.overridden'),\n resetLabel: t('settings.reset'),\n invalidLabel: t('settings.invalidNumber'),\n disabled,\n }\n return (\n <PluginSettingsCard\n t={t}\n titleKey=\"settings.title\"\n descriptionKey=\"settings.description\"\n state={state}\n onSave={props.save}\n onDiscard={props.discard}\n alwaysOpen\n >\n <BooleanField\n id=\"settings-pet-enabled\"\n label={t('settings.enabled')}\n hint={t('settings.enabledHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.enabled}\n onEdit={(text) => { props.edit('enabled', text) }}\n onReset={() => { props.resetField('enabled') }}\n />\n <BooleanField\n id=\"settings-pet-decoration\"\n label={t('settings.decoration')}\n hint={t('settings.decorationHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.decorationEnabled}\n onEdit={(text) => { props.edit('decorationEnabled', text) }}\n onReset={() => { props.resetField('decorationEnabled') }}\n />\n <ChoiceField\n id=\"settings-pet-pet\"\n label={t('settings.pet')}\n hint={t('settings.petHint')}\n inheritLabel={t('settings.inherit')}\n {...fieldProps}\n {...state.petId}\n choices={state.petChoices}\n onEdit={(text) => { props.edit('petId', text) }}\n onReset={() => { props.resetField('petId') }}\n />\n {state.petDiagnostics.length === 0 ? null : (\n <li className={sectionCss.diagnostics} data-dsh-part=\"diagnostics\">\n <span className={sectionCss.diagnosticsTitle}>{t('settings.diagnosticsTitle')}</span>\n <ul>\n {state.petDiagnostics.map((diagnostic, index) => (\n <li key={index} data-level={diagnostic.level}>{diagnostic.message}</li>\n ))}\n </ul>\n </li>\n )}\n <BooleanField\n id=\"settings-pet-visible\"\n label={t('settings.visible')}\n hint={t('settings.visibleHint')}\n inheritLabel={t('settings.inherit')}\n onLabel={t('settings.on')}\n offLabel={t('settings.off')}\n {...fieldProps}\n {...state.visible}\n onEdit={(text) => { props.edit('visible', text) }}\n onReset={() => { props.resetField('visible') }}\n />\n <ValueField\n id=\"settings-pet-size\"\n label={t('settings.size')}\n hint={t('settings.sizeHint')}\n numeric\n {...fieldProps}\n {...state.size}\n onEdit={(text) => { props.edit('size', text) }}\n onReset={() => { props.resetField('size') }}\n />\n <ValueField\n id=\"settings-pet-right\"\n label={t('settings.right')}\n hint={t('settings.rightHint')}\n numeric\n {...fieldProps}\n {...state.right}\n onEdit={(text) => { props.edit('right', text) }}\n onReset={() => { props.resetField('right') }}\n />\n <ValueField\n id=\"settings-pet-bottom\"\n label={t('settings.bottom')}\n hint={t('settings.bottomHint')}\n numeric\n {...fieldProps}\n {...state.bottom}\n onEdit={(text) => { props.edit('bottom', text) }}\n onReset={() => { props.resetField('bottom') }}\n />\n </PluginSettingsCard>\n )\n}\n\n/** Props the settings section binds for the pet card page. */\nexport type PetSettingsSectionProps =\n PropsRuntime<'settings.section'>\n & PropsLocale<'pet'>\n & InjectFace<PetSettingsCardFace>\n\n/** Render the pet settings card as a first-level settings page. */\nexport function PetSettingsSection(props: PetSettingsSectionProps): ReactNode {\n const { t, usePetSettingsCard, save, discard, edit, resetField } = props\n return (\n <ul className={sectionCss.sectionList}>\n <PetSettingsCard t={t} usePetSettingsCard={usePetSettingsCard} save={save} discard={discard} edit={edit} resetField={resetField} />\n </ul>\n )\n}\n","/**\n * dsh-pet locale dictionaries (zh/en).\n * @module @linxin666/dsh-pet/client/locales\n */\n\n/** Dictionary namespace this package registers. */\nexport const NS = 'pet'\n\n/** Chinese copy. */\nexport const zh = {\n 'pet.feed': '喂食',\n 'pet.hide': '隐藏',\n 'pet.rename': '改名',\n 'pet.confirm': '确定',\n 'pet.namePlaceholder': '输入新名字',\n 'pet.summon': '召唤{name}',\n 'pet.rank': '亲密度 {rank}',\n 'pet.rank.name.幼鲸': '幼鲸',\n 'pet.rank.name.伙伴': '伙伴',\n 'pet.rank.name.挚友': '挚友',\n 'pet.rank.name.深海羁绊': '深海羁绊',\n 'pet.rank.name.心有灵犀': '心有灵犀',\n 'pet.rank.name.传说羁绊': '传说羁绊',\n 'pet.rank.name.神话羁绊': '神话羁绊',\n 'pet.rank.name.永恒之契': '永恒之契',\n 'pet.rank.name.鲸生共渡': '鲸生共渡',\n 'pet.points': '{points} 点',\n 'pet.treats': '小鱼干 ×{n}',\n 'pet.state.loading': '宠物正在赶来…',\n 'pet.state.error': '宠物迷路了(连接失败)',\n 'pet.renderer.unavailable': '这只宠物需要的渲染器({renderer})在当前版本不可用。',\n 'pet.live2d.core-missing': 'Live2D 核心未安装:请把官方 live2dcubismcore.min.js 放入 $DSH_HOME/pets/.runtime/ 后刷新(步骤见宠物插件 README)。',\n 'pet.live2d.vendor-missing': 'Live2D 组件缺失,请升级宠物插件。',\n 'pet.live2d.load-failed': 'Live2D 模型加载失败,请检查该宠物目录的完整性。',\n 'pet.openSessionHint': '点击跳转到对应会话',\n // 玩法 HUD(miku-pet 泛化:属性条 / 打工 / 睡觉 / 商店;统一小鱼干经济)。\n 'pet.gameplay.menu': '玩法',\n 'pet.gameplay.work': '打工',\n 'pet.gameplay.stopWork': '收工',\n 'pet.gameplay.sleep': '休息',\n 'pet.gameplay.wake': '起床',\n 'pet.gameplay.shop': '商店',\n 'pet.gameplay.skin': '皮肤',\n 'pet.gameplay.skinDefault': '默认',\n 'pet.gameplay.back': '返回',\n 'pet.gameplay.buy': '购买',\n 'pet.gameplay.insufficient': '{currency}不足',\n 'pet.gameplay.prize': '中奖 +{amount} {currency}',\n 'pet.gameplay.working': '打工中',\n 'pet.gameplay.sleeping': '睡觉中',\n 'pet.gameplay.stat.hunger': '饱食',\n 'pet.gameplay.stat.mood': '心情',\n 'pet.gameplay.stat.energy': '精力',\n 'pet.gameplay.stat.affection': '好感',\n 'pet.gameplay.currency.treats': '小鱼干',\n 'pet.moreSessions': '展开其余 {n} 个会话的气泡',\n 'pet.collapseSessions': '收起会话气泡',\n // 一级设置页(settings.section 席位)。\n 'settings.title': '宠物',\n 'settings.diagnosticsTitle': '宠物目录诊断',\n 'settings.description': '选择宠物并调整它的显示布局。',\n 'settings.pet': '宠物',\n 'settings.petHint': '选择显示哪只宠物;每只宠物独立命名,可在宠物悬浮面板改名。',\n 'settings.enabled': '启用宠物',\n 'settings.enabledHint': '关闭后隐藏宠物并停止轮询,可在设置里重新启用。',\n 'settings.decoration': '状态装饰',\n 'settings.decorationHint': '在宠物状态气泡里显示喷水鲸鱼等状态装饰;关闭后气泡只剩文字。',\n 'settings.visible': '显示宠物',\n 'settings.visibleHint': '关闭后宠物隐藏,可从聊天输入区重新召唤。',\n 'settings.size': '大小(px)',\n 'settings.sizeHint': '精灵单元高度,范围 32–1024。',\n 'settings.right': '距右侧(px)',\n 'settings.rightHint': '距视口右边缘的水平内缩距离。',\n 'settings.bottom': '距底部(px)',\n 'settings.bottomHint': '距视口底边的垂直内缩距离。',\n 'settings.inherit': '继承',\n 'settings.on': '开',\n 'settings.off': '关',\n 'settings.overridden': '已覆盖',\n 'settings.reset': '恢复默认',\n 'settings.notExposed': '当前 DSH 版本未向设置页暴露本插件的配置命名空间,表单不可用。可编辑 $DSH_HOME/settings.yaml 直接配置,或确认提供该命名空间的插件已挂载其设置域并重启。',\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.rank.name.幼鲸': 'Baby Whale',\n 'pet.rank.name.伙伴': 'Companion',\n 'pet.rank.name.挚友': 'Close Friend',\n 'pet.rank.name.深海羁绊': 'Deep Sea Bond',\n 'pet.rank.name.心有灵犀': 'Kindred Spirit',\n 'pet.rank.name.传说羁绊': 'Legendary Bond',\n 'pet.rank.name.神话羁绊': 'Mythic Bond',\n 'pet.rank.name.永恒之契': 'Eternal Covenant',\n 'pet.rank.name.鲸生共渡': 'Lifelong Companion',\n 'pet.points': '{points} pts',\n 'pet.treats': 'Treats ×{n}',\n 'pet.state.loading': 'The pet is on its way…',\n 'pet.state.error': 'The pet is lost (connection failed)',\n 'pet.renderer.unavailable': 'This pet needs a renderer ({renderer}) that is not available in this build.',\n 'pet.live2d.core-missing': 'Live2D Cubism Core is not installed: place the official live2dcubismcore.min.js under $DSH_HOME/pets/.runtime/ and refresh (see the pet plugin README).',\n 'pet.live2d.vendor-missing': 'The Live2D component is missing; please update the pet plugin.',\n 'pet.live2d.load-failed': 'The Live2D model failed to load; check the pet directory is complete.',\n 'pet.openSessionHint': 'Click to jump to this session',\n // Gameplay HUD (miku-pet generalization: stat bars / work / sleep / shop; unified treats economy).\n 'pet.gameplay.menu': 'Play',\n 'pet.gameplay.work': 'Work',\n 'pet.gameplay.stopWork': 'Stop work',\n 'pet.gameplay.sleep': 'Sleep',\n 'pet.gameplay.wake': 'Wake up',\n 'pet.gameplay.shop': 'Shop',\n 'pet.gameplay.skin': 'Skin',\n 'pet.gameplay.skinDefault': 'Default',\n 'pet.gameplay.back': 'Back',\n 'pet.gameplay.buy': 'Buy',\n 'pet.gameplay.insufficient': 'Not enough {currency}',\n 'pet.gameplay.prize': 'Prize +{amount} {currency}',\n 'pet.gameplay.working': 'Working',\n 'pet.gameplay.sleeping': 'Sleeping',\n 'pet.gameplay.stat.hunger': 'Hunger',\n 'pet.gameplay.stat.mood': 'Mood',\n 'pet.gameplay.stat.energy': 'Energy',\n 'pet.gameplay.stat.affection': 'Affection',\n 'pet.gameplay.currency.treats': 'Treats',\n 'pet.moreSessions': 'Expand {n} more session bubbles',\n 'pet.collapseSessions': 'Collapse session bubbles',\n // First-level settings section (the `settings.section` seat).\n 'settings.title': 'Pet',\n 'settings.diagnosticsTitle': 'Pet directory diagnostics',\n 'settings.description': 'Pick a pet and tune its display layout.',\n 'settings.pet': 'Pet',\n 'settings.petHint': 'Choose which pet shows. Names are stored per pet; rename from the pet hover panel.',\n 'settings.enabled': 'Enable the pet',\n 'settings.enabledHint': 'When off, the pet hides and polling stops; re-enable it here.',\n 'settings.decoration': 'Status decoration',\n 'settings.decorationHint': 'Show ornaments like the spouting whale inside the pet status bubbles; when off, bubbles stay text-only.',\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\\u20131024.',\n 'settings.right': 'Right inset (px)',\n 'settings.rightHint': 'Horizontal inset from the viewport right edge.',\n 'settings.bottom': 'Bottom inset (px)',\n 'settings.bottomHint': 'Vertical inset from the viewport bottom edge.',\n 'settings.inherit': 'Inherit',\n 'settings.on': 'On',\n 'settings.off': 'Off',\n 'settings.overridden': 'Overridden',\n 'settings.reset': 'Reset to default',\n 'settings.notExposed': 'This DSH version does not expose this plugin\\'s settings namespace to the configuration page, so the form is unavailable. Edit $DSH_HOME/settings.yaml directly, or confirm that the plugin owning the namespace is mounted with its settings domain and restart.',\n 'settings.readOnly': 'This deployment stores settings read-only.',\n 'settings.expand': 'Show settings',\n 'settings.collapse': 'Hide settings',\n 'settings.save': 'Save',\n 'settings.saving': 'Saving\\u2026',\n 'settings.discard': 'Discard',\n 'settings.unsaved': 'Unsaved',\n 'settings.saveFailed': 'The deployment did not accept these values; they were left for you to correct.',\n 'settings.invalidNumber': 'Enter a number, or leave blank to use the default.',\n} as const\n\n/** Key union for this namespace. */\nexport type PetKey = keyof typeof zh\n\n/** The settings-card slice of the pet dictionary. */\nexport type SettingsCardKey = PetKey\n\n/**\n * Active dictionary, picked by the document language at call time. The pet\n * mounts as a global floating surface (not a session-scoped slot), so it has\n * no framework locale seat and resolves its copy the same tiny way the\n * task-board's DOM-injected surface does.\n */\nexport function dictionary(): Record<PetKey, string> {\n const lang = typeof document !== 'undefined' ? document.documentElement.lang : 'zh'\n return lang.toLowerCase().startsWith('en') ? en : zh\n}\n\n/**\n * Translate a key with optional `{name}` template params. Mirrors the slot\n * `Translate` contract `(key, params?) => string` so it can be handed to the\n * same components that used to receive the framework-injected `t` seat. The\n * key is typed loosely (`string`) so the function is assignable to the slot's\n * `TranslateNS<'pet'>` (whose key domain also spans the shared common\n * vocabulary); a missing key degrades to the key itself rather than throwing.\n */\nexport function t(key: string, params?: Record<string, unknown>): string {\n let text: string = (dictionary() as Record<string, string>)[key] ?? key\n if (params !== undefined) {\n for (const [name, value] of Object.entries(params)) {\n text = text.replaceAll(`{${name}}`, String(value))\n }\n }\n return text\n}\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** dsh-pet UI copy. */\n pet: PetKey\n }\n}\n","// Generated by scripts/sync-shared.mjs from shared/client/telemetry.ts. Do not edit this copy; edit the shared source and run \"node scripts/sync-shared.mjs\".\n/**\n * Anonymous install telemetry shared by the DSH Web UI family plugins.\n *\n * Once per UTC day per browser, each wired plugin sends one heartbeat to\n * the dsh-market edge API listing its package name (and version when known).\n * The payload carries no conversation data and no identifiers beyond a random\n * UUID generated in localStorage; the server hashes it with a deployment salt\n * before storage and never persists IP addresses. Sends are fire-and-forget:\n * failures stay silent and simply retry on a later mount or day. The system\n * is documented in docs/telemetry.md.\n */\n\nexport type TelemetryChannel = 'market' | 'npm' | 'unknown'\n\nexport interface TelemetryItem {\n /** npm package name or asset id, e.g. \"@linxin666/dsh-pet\" or \"skin:harbor\". */\n name: string\n /** Installed version when known; omitted otherwise. */\n version?: string\n /** Install channel when determinable (market = Workshop install). */\n channel?: TelemetryChannel\n}\n\nconst VISITOR_KEY = 'dsh-web-ui-telemetry-visitor'\nconst DAY_KEY_PREFIX = 'dsh-web-ui-telemetry-day:'\nconst ENDPOINT = 'https://dsh-market.com/api/telemetry/event'\n\n// Baked into each client bundle by shared/tsdown.client.ts (the package's own\n// package.json version at build time). Undefined in dev/test builds.\ndeclare const __DSH_PKG_VERSION__: string | undefined\n\n/** The building package's version, when the bundle carries it. */\nfunction bakedVersion(): string | undefined {\n try {\n return typeof __DSH_PKG_VERSION__ === 'string' && __DSH_PKG_VERSION__ !== ''\n ? __DSH_PKG_VERSION__\n : undefined\n } catch {\n return undefined\n }\n}\n\n/** Read or lazily create the anonymous visitor id; null when storage is unavailable. */\nfunction visitorId(): string | null {\n try {\n const existing = localStorage.getItem(VISITOR_KEY)\n if (existing && /^[A-Za-z0-9_-]{16,64}$/.test(existing)) return existing\n const fresh = crypto.randomUUID().replaceAll('-', '')\n localStorage.setItem(VISITOR_KEY, fresh)\n return fresh\n } catch {\n // Storage unavailable (privacy mode, sandboxed frame): report nothing.\n return null\n }\n}\n\n/** Drop stale per-day dedup keys so localStorage does not grow forever. */\nfunction pruneDayKeys(today: string): void {\n try {\n for (let index = localStorage.length - 1; index >= 0; index -= 1) {\n const key = localStorage.key(index)\n if (key !== null && key.startsWith(DAY_KEY_PREFIX) && key !== DAY_KEY_PREFIX + today) {\n localStorage.removeItem(key)\n }\n }\n } catch { /* best effort */ }\n}\n\n/**\n * Fire the daily heartbeat for the given items at most once per UTC day per\n * browser. Never throws and never blocks the caller. Items without an explicit\n * version inherit the bundle's baked build version.\n */\nexport function reportDailyHeartbeat(items: readonly TelemetryItem[]): void {\n try {\n if (items.length === 0) return\n const today = new Date().toISOString().slice(0, 10)\n // Automation contexts (CDP-driven QA browsers, fresh isolated profiles)\n // mint a new visitor id per run and would inflate instance counts.\n if (navigator.webdriver) return\n if (localStorage.getItem(DAY_KEY_PREFIX + today) !== null) return\n const visitor = visitorId()\n if (visitor === null) return\n pruneDayKeys(today)\n const payloadItems = items.map((item) => {\n const out: Record<string, string> = { name: item.name }\n const version = item.version ?? bakedVersion()\n if (version !== undefined) out.version = version\n if (item.channel !== undefined) out.channel = item.channel\n return out\n })\n const body = JSON.stringify({ kind: 'heartbeat', visitor, items: payloadItems })\n void fetch(ENDPOINT, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body,\n keepalive: true,\n }).then((response) => {\n // Mark the day only after an accepted send, so offline browsers retry\n // on a later mount instead of going dark until tomorrow.\n if (response.ok) localStorage.setItem(DAY_KEY_PREFIX + today, '1')\n }).catch(() => { /* unreachable endpoint: stay silent */ })\n } catch { /* never break the host UI over telemetry */ }\n}\n","/**\n * dsh-pet browser half — mounts the selected pet as a global floating\n * surface and drives it from the host's same-origin '/api/pet/*' JSON\n * endpoints: fetch the registry list once, poll the host snapshot (~2 s),\n * forward interactions, persist drag positions. The pet is host-global (no\n * session dimension), so it mounts directly onto 'document.body' via a\n * single React root — the [data-dsh-plugin=\"pet\"] container — rather than a\n * session-scoped slot: on the new-conversation screen no session exists, and\n * a dock-mounted pet would vanish there (issue #48). The sprite chrome\n * portals into that same root, so the root owns the whole surface and a\n * root-keyed suppressor can hide it as one unit. When the pet is hidden the\n * entry becomes a fixed-position summon button.\n * @module @linxin666/dsh-pet/client\n */\n\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type { ISessions } from '@deepseek-ai/dsh-api-session-controller/client'\nimport type { SessionId } from '@deepseek-ai/dsh-api-remotes/client'\nimport type { SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-ui-settings/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'\n// Type-only: pulls the ctx.slots merge (the renderer owns the slot registry since 0.1.2).\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type { PetDisplayConfig } from '../persist.ts'\nimport type { PetGameplayVerbResult, PetInteractResult, PetStateView } from '../service.ts'\nimport type { PetInteraction } from '../affinity.ts'\nimport type { PetDefinition } from '../registry.ts'\nimport { createElement } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport { createPetStore, type PetStoreInstance } from './pet-store.ts'\nimport { createWorkTickGate } from './work-tick-gate.ts'\nimport { PetDockEntry, type PetInjected } from './PetDockEntry.tsx'\nimport { defaultPetRendererRegistry } from './renderers/registry.ts'\nimport { live2dRenderer } from './renderers/live2d.ts'\nimport { frames2dRenderer } from './renderers/frames2d.ts'\nimport { registerPetUiTeardown, takeoverPetUiTeardown } from './ui-teardown.ts'\nimport { PetSettingsSection, PetSettingsCardController, type PetSettings } from './PetSettingsCard.tsx'\nimport { NS, en, zh, t } from './locales.ts'\nimport { reportDailyHeartbeat } from './telemetry.ts'\n\n/** The host pet API as the browser sees it (same-origin JSON endpoints). */\ninterface PetHttpApi {\n /** Poll the host snapshot; the GUI's current session id rides the query. */\n state(currentSessionId?: string): Promise<PetStateView>\n pets(): Promise<PetDefinition[]>\n interact(kind: PetInteraction): Promise<PetInteractResult>\n setVisible(visible: boolean): Promise<{ ok: true; display: PetDisplayConfig }>\n setConfig(patch: Partial<PetDisplayConfig>): Promise<{ ok: true; display: PetDisplayConfig }>\n setName(name: string): Promise<{ ok: true; name: string } | { ok: false; error: string }>\n setPet(petId: string): Promise<{ ok: true; petId: string } | { ok: false; error: string }>\n setSkin(skin?: string): Promise<{ ok: boolean; error?: string; skin?: string }>\n gameplayTouch(zone?: string): Promise<PetGameplayVerbResult>\n gameplaySetMode(mode: 'work' | 'sleep' | null): Promise<PetGameplayVerbResult>\n gameplayWorkTick(): Promise<PetGameplayVerbResult>\n gameplayBuy(item: string): Promise<PetGameplayVerbResult>\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: (currentSessionId) => petFetch('/api/pet/state'\n + (currentSessionId === undefined ? '' : '?current=' + encodeURIComponent(currentSessionId))),\n pets: () => petFetch('/api/pet/pets'),\n interact: (kind) => petFetch('/api/pet/interact', { kind }),\n setVisible: (visible) => petFetch('/api/pet/set-visible', { visible }),\n setConfig: (patch) => petFetch('/api/pet/set-config', patch),\n setName: (name) => petFetch('/api/pet/set-name', { name }),\n setPet: (petId) => petFetch('/api/pet/set-pet', { petId }),\n setSkin: (skin) => petFetch('/api/pet/set-skin', skin === undefined ? {} : { skin }),\n gameplayTouch: (zone) => petFetch('/api/pet/gameplay/touch', zone === undefined ? {} : { zone }),\n gameplaySetMode: (mode) => petFetch('/api/pet/gameplay/mode', { mode }),\n gameplayWorkTick: () => petFetch('/api/pet/gameplay/work-tick', {}),\n gameplayBuy: (item) => petFetch('/api/pet/gameplay/buy', { item }),\n}\n\n/** Poll interval for the host snapshot. */\nconst POLL_MS = 2000\n\n/** Settings namespace the pet settings card edits (the Host plugin registers it). */\nconst PET_SETTINGS_NS = 'pet'\n\n/** Required services (sessions powers bubble-to-session navigation). */\nexport const inject = ['slots', 'locale', 'connection', 'settingsScope', 'remote', 'sessions']\n\n/** Re-exported for consumers that type against the injected face. */\nexport type { PetInjected, PetDockEntryProps } from './PetDockEntry.tsx'\nexport type { PetSpriteProps } from './PetSprite.tsx'\nexport type { PetUiState, PetFeedback } from './pet-store.ts'\nexport type { PetSettingsCardFace, PetSettingsCardState } from './PetSettingsCard.tsx'\nexport type { PetSettingsSectionProps } from './PetSettingsCard.tsx'\nexport type { PetDefinition } from '../registry.ts'\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n /**\n * Optional rc.6 compatibility binder provided by dsh-web-settings;\n * absent when that group plugin is not installed, so callers fall back to\n * the official settings scope.\n */\n webUiSettings?: { bind<S>(spec: SettingsScopeSpec<S>): SettingsScope<S> }\n }\n}\n\n/**\n * Client plugin body: register dictionaries, mount the global pet entry and\n * poll loop while the plugin is enabled, and seat the settings card as a\n * first-level settings section.\n * @param ctx - client root context.\n */\n\n/** The page-wide work-tick gate; its window follows the active pet's cadence. */\nconst workTickGate = createWorkTickGate()\n\n/**\n * The work cadence the active pet declares, when its registry entry is known.\n * @param store - the pet store holding the host snapshot and the registry list.\n * @returns the configured `gameplay.work.tickMs`, or undefined when unknown.\n */\nfunction activeWorkTickMs(store: PetStoreInstance): number | undefined {\n const state = store.getSnapshot()\n const definition = state.pets.find((entry) => entry.id === state.snapshot?.pet.id)\n return definition?.gameplay?.work?.tickMs\n}\n\nexport function apply(ctx: ClientContext): void {\n // Anonymous install heartbeat (docs/telemetry.md): one beat per browser per\n // UTC day, package name only, silent failure.\n reportDailyHeartbeat([{ name: '@linxin666/dsh-pet' }])\n\n ctx.effect(() => {\n try {\n return ctx.locale.register(NS, { zh, en })\n } catch {\n return () => {}\n }\n }, 'pet: dictionaries')\n\n // Built-in renderers dispatch through the plugin-wide registry (pet-center\n // M3). Registration is idempotent (id wins), so re-applies stay clean.\n defaultPetRendererRegistry.register(live2dRenderer)\n defaultPetRendererRegistry.register(frames2dRenderer)\n\n const binder = ctx.get('webUiSettings') ?? ctx.settingsScope\n const settingsScope = binder.bind<PetSettings>({ namespace: PET_SETTINGS_NS })\n const enabled = (): boolean => {\n const snapshot = settingsScope.getSnapshot()\n return snapshot.status === 'ready'\n ? snapshot.value?.enabled ?? true\n : snapshot.status === 'unavailable'\n }\n\n // First-level settings section: one staged form over the 'pet' settings\n // namespace, registered as a top-level settings page. The controller loads\n // the petId choices from the registry endpoint itself — the registry lists\n // the available pets (built-in assets plus user dirs), so the section only\n // ever shows installed pets. Installing new pets happens in the Workshop\n // store.\n const petSettings = new PetSettingsCardController(settingsScope)\n // The section entry owns the controller: unregistering it (fiber disposal,\n // hot reload) releases the scope subscription through petSettings.dispose.\n ctx.slots.inject('settings.section', () => {\n try {\n const unregister = ctx.slots.register({\n name: 'settings.section',\n id: 'pet',\n order: 130,\n label: () => ctx.locale.bind('pet')('settings.title'),\n locale: 'pet',\n inject: () => petSettings.inject(),\n }, PetSettingsSection)\n return () => {\n unregister()\n petSettings.dispose()\n }\n } catch {\n return () => {}\n }\n })\n\n // The global pet entry, its store, and the poll loop live while the plugin\n // is enabled; toggling the setting off hides the pet and stops polling.\n // 'uiDead' marks a terminal teardown (takeover by a later bundle instance\n // or fiber disposal): a taken-over or disposed instance must never remount\n // from a late settings callback (issue #785).\n let disposeUi: (() => void) | undefined\n let clearUiTeardown: (() => void) | undefined\n let uiDead = false\n const killUi = (): void => {\n if (uiDead) return\n uiDead = true\n clearUiTeardown?.()\n clearUiTeardown = undefined\n disposeUi?.()\n disposeUi = undefined\n }\n const syncUi = (): void => {\n if (!uiDead && enabled() && disposeUi === undefined) {\n // ONE store instance for the whole app, owned by this apply body. The\n // pet is host-global (state/display/interactions are /api/pet/*\n // endpoints with no session dimension), so the slot system's per-session\n // store scoping would only reset the pet on session switches and leave\n // it stateless on the new-conversation screen (no session to scope by).\n const petStore: PetStoreInstance = createPetStore().create()\n const setSnapshot = petStore.actions.setSnapshot\n const setPets = petStore.actions.setPets\n const setState = petStore.actions.setState\n const setFeedback = petStore.actions.setFeedback\n\n // Clicking a session bubble jumps the GUI to that session; the same\n // sessions face reports which session the user is currently on, so the\n // host can lead the bubble stack with it. A bubble can outlive its\n // disposed session by one poll tick, and the sessions service fails\n // loud on unknown ids, so consult the live list first. The pet's type\n // program also loads the host-side dsh-session package through the\n // service types, whose Context merge declares a different 'sessions'\n // face; pin the browser runtime's outward face here.\n const sessions = ctx.sessions as unknown as ISessions\n const currentSessionId = (): string | undefined => {\n const current = sessions.list.getSnapshot().current\n return current === undefined ? undefined : String(current)\n }\n\n // The registry list is fetched lazily with retries baked into the poll\n // cycle: until it lands, the dock entry renders nothing and every 2s\n // tick tries again. After it lands, one list feeds both the sprite and\n // the settings card's choices.\n let petsLoaded = false\n // Latest-wins guard: the 2s tick, visibility recovery, and\n // interaction-triggered refreshes can overlap; only the newest\n // response may publish, so a slow older one can never roll the\n // snapshot back.\n let stateSeq = 0\n const pollNow = (): void => {\n if (!petsLoaded) {\n petApi.pets().then((list) => {\n petsLoaded = true\n setPets(list)\n }, () => {\n // Retry on the next poll tick.\n })\n }\n const seq = stateSeq + 1\n stateSeq = seq\n petApi.state(currentSessionId()).then((snapshot) => {\n if (seq !== stateSeq) return\n setSnapshot(snapshot)\n }, () => {\n if (seq !== stateSeq) return\n setState('error', 'pet.state transport error')\n })\n }\n\n const disposePoll = ctx.effect(() => {\n // Poll only while the tab is visible: the host snapshot does not\n // change while the page is hidden, so a background interval would\n // only burn RPCs (browser throttling is an unreliable backstop).\n // Coming back to the tab refreshes the pet immediately instead of\n // waiting out the next 2 s cycle.\n let timer: number | undefined\n const stop = (): void => {\n if (timer !== undefined) {\n window.clearInterval(timer)\n timer = undefined\n }\n }\n const start = (): void => {\n if (timer === undefined && document.visibilityState === 'visible') {\n timer = window.setInterval(pollNow, POLL_MS)\n }\n }\n const onVisibility = (): void => {\n if (document.visibilityState === 'visible') {\n pollNow()\n start()\n } else {\n stop()\n }\n }\n start()\n document.addEventListener('visibilitychange', onVisibility)\n return () => {\n stop()\n document.removeEventListener('visibilitychange', onVisibility)\n }\n }, 'pet: poll')\n\n // A current-session switch should re-order the bubble stack right away,\n // not on the next 2s tick. Poll only while the tab is visible, as the\n // poll loop itself does.\n const disposeSessionWatch = ctx.effect(() => {\n const unsubscribe = sessions.list.subscribe(() => {\n if (document.visibilityState === 'visible') pollNow()\n })\n return unsubscribe\n }, 'pet: current-session watch')\n\n const openSession = (sessionId: string): void => {\n const list = sessions.list.getSnapshot()\n if ((list.byId as any)[sessionId] === undefined) return\n sessions.open(sessionId as never)\n }\n\n const injected = (): PetInjected => ({\n store: petStore,\n ensure: pollNow,\n openSession,\n pet: () => {\n petApi.interact('pet').then((result) => {\n setFeedback({\n text: result.reaction,\n kind: 'pet',\n at: Date.now(),\n })\n }, () => {\n // Ignore transport errors on interactions; the next poll resyncs.\n })\n },\n feed: () => {\n petApi.interact('feed').then((result) => {\n setFeedback({\n text: result.reaction,\n kind: 'feed',\n at: Date.now(),\n })\n }, () => {\n // Ignore transport errors on interactions; the next poll resyncs.\n })\n },\n hide: () => {\n petApi.setVisible(false).then(() => {\n pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n summon: () => {\n petApi.setVisible(true).then(() => {\n pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n dragEnd: (right, bottom) => {\n petApi.setConfig({ right, bottom }).then(() => {\n pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n rename: (name) => {\n petApi.setName(name).then((result) => {\n if (result.ok) pollNow()\n }, () => {\n // Ignore; next poll resyncs.\n })\n },\n feedbackDone: () => {\n setFeedback(null)\n },\n gameplay: {\n touch: (zone) => petApi.gameplayTouch(zone),\n setSkin: (skin) => petApi.setSkin(skin).then((result) => {\n if (result.ok) pollNow()\n return result\n }, () => ({ ok: false, error: 'transport' })),\n \n setMode: async (mode) => {\n if (mode === 'work') workTickGate.reset()\n return petApi.gameplaySetMode(mode)\n },\n workTick: async () => {\n // One adjudication per tick window, page-wide: suppress stale\n // duplicate intervals (HMR) re-playing the result track. The window\n // is the pet's own cadence, so a shorter tickMs is not downgraded.\n if (!workTickGate.allow(activeWorkTickMs(petStore))) {\n return { ok: true } as PetGameplayVerbResult\n }\n return petApi.gameplayWorkTick()\n },\n buy: (item) => petApi.gameplayBuy(item),\n },\n })\n\n // The pet is host-global (its state/display/interactions have no session\n // dimension), and the official rc.6 shell declares no root-scoped slot\n // for a global floating surface — the dock is session-scoped, so a pet\n // mounted there would vanish on the new-conversation screen (issue #48).\n // The entry therefore mounts straight onto document.body via a single\n // React root for the page lifetime: PetSprite portals itself to body\n // when visible, and the hidden-state summon button is fixed-positioned.\n //\n // Cross-instance single-mount guard (issue #785): take over the\n // page-global slot first — the previous bundle instance's fiber may\n // still be draining during a client reload, so unmount its React root\n // and remove its container — then sweep containers left behind by\n // instances that predate the teardown registry, so this mount is the\n // page's only [data-dsh-pet-root].\n takeoverPetUiTeardown()\n for (const stale of Array.from(document.querySelectorAll('div[data-dsh-pet-root]'))) {\n stale.remove()\n }\n const container = document.createElement('div')\n container.dataset.dshPetRoot = ''\n container.dataset.dshPlugin = 'pet'\n document.body.appendChild(container)\n const petRoot = createRoot(container)\n // The sprite chrome portals into THIS root (not document.body): the\n // root then owns the whole surface, so a root-keyed suppressor (the\n // portrait mobile layer, which hides [data-dsh-plugin=\"pet\"]) really\n // hides the sprite instead of missing the portaled float.\n petRoot.render(createElement(PetDockEntry, { ...injected(), t, portalTarget: container }))\n\n let uiGone = false\n disposeUi = () => {\n if (uiGone) return\n uiGone = true\n clearUiTeardown?.()\n clearUiTeardown = undefined\n petRoot.unmount()\n container.remove()\n disposePoll()\n disposeSessionWatch()\n disposeUi = undefined\n }\n // The slot teardown is the takeover hook a later apply body runs; it\n // marks this instance terminal so a late settings callback from the\n // still-draining instance cannot remount a second pet.\n clearUiTeardown = registerPetUiTeardown(() => {\n uiDead = true\n disposeUi?.()\n })\n } else if (!uiDead && !enabled() && disposeUi !== undefined) {\n disposeUi()\n disposeUi = undefined\n }\n }\n // The settings subscription and the pet UI lifetime follow the fiber\n // (issue #785): disposal drops the subscription and tears the UI down\n // (terminal), so a hot-reloaded or re-injected bundle never leaves the\n // previous React root, container, or poll loop behind on document.body.\n const unsubscribeSettings = settingsScope.subscribe(syncUi)\n ctx.effect(\n () => () => {\n unsubscribeSettings()\n killUi()\n },\n 'pet: client lifecycle',\n )\n syncUi()\n}\n"],"x_google_ignoreList":[2],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyDA,SAAgB,iBAA8D;GAC5E,OAAO,YAAY;IACjB,aAAyB;KACvB,UAAU;KACV,MAAM,CAAC;KACP,OAAO;KACP,OAAO;KACP,UAAU;IACZ;IACA,SAAS;KACP,cAAc,OAAO,aAAa;MAQhC,IAAI,MAAM,UAAU,WAAW,MAAM,UAAU,QAAQ,aAAa,MAAM,UAAU,QAAQ,GAAG;MAC/F,MAAM,WAAW;MACjB,MAAM,QAAQ;MACd,MAAM,QAAQ;KAChB;KACA,UAAU,OAAO,SAAS;MACxB,MAAM,OAAO;KACf;KACA,WAAW,OAAO,OAAO,UAAU;MACjC,MAAM,QAAQ;MACd,MAAM,QAAQ;KAChB;KACA,cAAc,OAAO,aAAa;MAChC,MAAM,WAAW;KACnB;KACA,kBAAkB,OAAO,SAAS;MAChC,IAAI,MAAM,aAAa,MAAM,MAAM,WAAW;OAAE,GAAG,MAAM;OAAU,UAAU;MAAK;KACpF;IACF;GACF,CAAC;EACH;;;;;;;EAUA,SAAS,aAAa,UAA+B,MAA6B;GAChF,OAAO,aAAa,QAAQ,KAAK,UAAU,QAAQ,MAAM,KAAK,UAAU,IAAI;EAC9E;;;;;;;;;;;;;;;;;EC5FA,MAAa,uBAAuB;;EAGpC,MAAM,mBAAmB;EACzB,MAAM,mBAAmB;;;;;;;EAQzB,SAAgB,iBAAiB,QAAoC;GACnE,IAAI,OAAO,WAAW,YAAY,CAAC,OAAO,SAAS,MAAM,GAAG,OAAO;GACnE,OAAO,KAAK,IAAI,kBAAkB,KAAK,IAAI,kBAAkB,MAAM,CAAC;EACtE;;;;;;EAmBA,SAAgB,mBAAmB,MAAoB,KAAK,KAAmB;GAC7E,IAAI,oBAAoB;GACxB,OAAO;IACL,QAAQ,WAAW;KACjB,MAAM,KAAK,IAAI;KACf,IAAI,KAAK,oBAAoB,iBAAiB,MAAM,GAAG,OAAO;KAC9D,oBAAoB;KACpB,OAAO;IACT;IACA,aAAa;KACX,oBAAoB;IACtB;GACF;EACF;;;EC9DA,SAAS,EAAE,GAAE;GAAC,IAAI,GAAE,GAAE,IAAE;GAAG,IAAG,YAAU,OAAO,KAAG,YAAU,OAAO,GAAE,KAAG;QAAO,IAAG,YAAU,OAAO,GAAE,IAAG,MAAM,QAAQ,CAAC,GAAE;IAAC,IAAI,IAAE,EAAE;IAAO,KAAI,IAAE,GAAE,IAAE,GAAE,KAAI,EAAE,OAAK,IAAE,EAAE,EAAE,EAAE,OAAK,MAAI,KAAG,MAAK,KAAG;GAAE,OAAM,KAAI,KAAK,GAAE,EAAE,OAAK,MAAI,KAAG,MAAK,KAAG;GAAG,OAAO;EAAC;EAAC,SAAgB,OAAM;GAAC,KAAI,IAAI,GAAE,GAAE,IAAE,GAAE,IAAE,IAAG,IAAE,UAAU,QAAO,IAAE,GAAE,KAAI,CAAC,IAAE,UAAU,QAAM,IAAE,EAAE,CAAC,OAAK,MAAI,KAAG,MAAK,KAAG;GAAG,OAAO;EAAC;;;;ECoG/W,SAAgB,kBAAkB,cAA+B,KAAsB;GACrF,OAAO,MAAM,aAAa,KAAK,aAAa;EAC9C;;;;;;;;;;;;EChCA,SAAgB,kBAAkB,OAAoC;GACpE,QAAQ,OAAR;IACE,KAAK,YAAY,OAAO;IACxB,KAAK,QAAQ,OAAO;IACpB,KAAK,UAAU,OAAO;IACtB,KAAK,WAAW,OAAO;IACvB,KAAK,QAAQ,OAAO;IACpB,KAAK,UAAU,OAAO;IACtB,KAAK,QAAQ,OAAO;GACtB;EACF;;EAGA,SAAgB,MAAM,WAAiC;GAYrD,OAAO;IAVL,QAAQ;IACR,iBAAiB;IACjB,gBAAgB;IAChB,UAAU;IACV,WAAW;IACX,UAAU;IACV,WAAW;IACX,WAAW;IACX,UAAU;GAEF,EAAE;EACd;;;;;;;;;;;;EChFA,SAAgB,WAAW,WAAiC;GAE1D,OAAO,MAAM,SAAS;EACxB;;;;;;;;EASA,SAAgB,cAAc,MAAe,KAAa,KAAa,QAAQ,GAA6B;GAC1G,OAAO;IAAE,GAAG,CAAC,MAAM,KAAK,QAAQ;IAAO,GAAG,CAAC,MAAM,KAAK,SAAS;GAAM;EACvE;;;;;;;EAQA,SAAgB,UAAU,OAAiB,YAA8B;GACvE,MAAM,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,YAAY,MAAM,OAAO,QAAQ,MAAM,UAAU,MAAM,CAAC;GACvF,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;;;;EC1BA,SAAgB,uBACd,UACA,QACkB;GAClB,MAAM,gBAAgB,SAAS,KAAI,cAAa,OAAO,UAAU,CAAC,UAAU,QAAQ,KAAK,UAAU,MAAM,OAAO,CAAC,CAAC;GAClH,MAAM,mBAAmB,cAAc,QAAQ,KAAK,UAAU,MAAM,OAAO,CAAC;GAC5E,OAAO,EACL,QAAQ,WAAkC;IACxC,IAAI,SAAS,KAAK,IAAI,GAAG,SAAS,IAAI;IACtC,IAAI,YAAY;IAChB,OAAO,YAAY,SAAS,SAAS,KAAK,UAAU,cAAc,YAAa;KAC7E,UAAU,cAAc;KACxB,aAAa;IACf;IACA,MAAM,YAAY,SAAS;IAC3B,MAAM,QAAQ,OAAO;IACrB,IAAI,aAAa;IACjB,OAAO,aAAa,MAAM,OAAO,SAAS,KAAK,UAAU,MAAM,UAAU,aAAc;KACrF,UAAU,MAAM,UAAU;KAC1B,cAAc;IAChB;IACA,OAAO;KAAE;KAAW;IAAW;GACjC,EACF;EACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECkDA,SAAS,YAAY,OAAe,KAAqB;GACvD,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC;EACzC;;;;;;;;;EAUA,SAAS,eAAe,OAAkF;GACxG,MAAM,EAAE,YAAY,UAAU;GAC9B,MAAM,UAAU,WAAW,OAAO;GAClC,MAAM,QAAQ,YAAY,KAAA,KAAa,YAAY;GACnD,MAAM,aAAa,YAAY,KAAA,KAAa,YAAY,SAAS,QAAQ,OAAO,MAAM,QAAQ,KAAK;GACnG,MAAM,WAAA,GAAA,MAAA,OAAA,CAAyC,IAAI;GACnD,MAAM,QAAQ,KAAK,WAAW,KAAK;GACnC,MAAM,aAAa,KAAK,MAAM,WAAW,KAAK,QAAQ,KAAK;GAC3D,MAAM,aAAa,WAAW,UAAU;GAKxC,MAAM,eAAe,WAAW,UAAU,KAAK,GAAG;GAClD,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,YAAY,KAAA,KAAa,YAAY,QAAQ;IACjD,MAAM,KAAK,QAAQ;IACnB,IAAI,OAAO,MAAM;IACjB,MAAM,YAAY,UAA2B,CAAC,QAAQ,aAAc;IACpE,MAAM,eAAe,OAAO,aAAa,kCAAkC,CAAC,EAAE,YAAY;IAC1F,GAAG,MAAM,qBAAqB,SAAS,QAAQ,IAAI;IAKnD,IAAI,gBAAgB,QAAQ,SAAS,QAAQ,IAAI;IACjD,IAAI,QAAQ;IACZ,IAAI,QAAQ,QAAQ;IACpB,IAAI,UAAU;IACd,IAAI,OAAO,YAAY,IAAI;IAC3B,MAAM,aAAmB;KACvB,MAAM,MAAM,YAAY,IAAI;KAC5B,MAAM,QAAQ,MAAM;KACpB,OAAO;KACP,WAAW;KACX,MAAM,WAAW,WAAW,UAAU,UAAU;KAOhD,IAAI,WAAW,UAAU;MACvB,GAAG;OACD,WAAW;OACX,IAAI,QAAQ,QAAQ,IAAI,SAAS;YAC5B,IAAI,WAAW,MAAM,QAAQ,QAAQ;MAC5C,SAAS,WAAW;MAEpB,GAAG,MAAM,qBAAqB,SAAS,KAAK;KAC9C;KAGA,IAAI,CAAC,WAAW,QAAQ,UAAU,QAAQ,IAAI;KAC9C,QAAQ,OAAO,WAAW,MAAM,KAAK,IAAI,GAAG,WAAW,OAAO,CAAC;IACjE;IACA,QAAQ,OAAO,WAAW,MAAM,CAAC;IACjC,aAAa,OAAO,aAAa,KAAK;GACxC,GAAG;IAAC;IAAO;IAAY;IAAY,WAAW;IAAM;GAAY,CAAC;GACjE,IAAI,CAAC,OAAO,OAAO;GACnB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;IACE,KAAK;IACL,eAAY;IACZ,2BAAyB,WAAW;IACpC,OAAO;KACL,SAAS;KACT,OAAO;KACP,QAAQ;KACR,aAAa;KACb,eAAe;KACf,YAAY;KACZ,iBAAiB,SAAS,WAAW,WAAW;KAChD,gBAAgB,aAAa;KAC7B,kBAAkB;KAClB,oBAAoB;IACtB;GACD,CAAA;EAEL;;;;;;;;;EAUA,SAAS,wBAAwB,OAAqD;GACpF,MAAM,EAAE,iBAAiB;GACzB,MAAM,OAAO,aAAa,SAAS,QAC/BA,uBAAO,iBACP,aAAa,SAAS,SACpBA,uBAAO,kBACPA,uBAAO;GACb,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IACE,WAAW,KAAKA,uBAAO,QAAQA,uBAAO,aAAa,IAAI;IACvD,MAAK;IACL,aAAU;IACV,6BAA2B,aAAa;cAJ1C;KAME,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;MAAM,WAAWA,uBAAO;gBAAxB;OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAWA,uBAAO;kBAAmB,aAAa;OAAY,CAAA;QAClE,aAAa,SAAS,aAAa,aAAa,SAAS,WAAW,aAAa,WAAW,KAAA,KAC5F,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAWA,uBAAO;kBAAmB,aAAa;OAAa,CAAA;OAEtE,aAAa,SAAS,UAAU,aAAa,YAAY,KAAA,KACxD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAWA,uBAAO;kBAAmB,KAAK,MAAM,aAAa,OAAO,IAAI;OAAU,CAAA;MAEtF;;KACL,aAAa,SAAS,UAAU,aAAa,YAAY,KAAA,KACxD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAWA,uBAAO;gBACtB,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OACE,WAAWA,uBAAO;OAClB,OAAO,EAAE,OAAO,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,aAAa,OAAO,CAAC,IAAI,IAAI;MACxE,CAAA;KACG,CAAA;KAEP,aAAa,SAAS,KAAA,KACrB,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAWA,uBAAO;gBAAkB,aAAa;KAAW,CAAA;IAEjE;;EAET;;;;;;;EAQA,SAAgB,UAAU,OAAoC;GAC5D,MAAM,EAAE,UAAU,YAAY,SAAS,aAAa;GACpD,MAAM,aAAA,GAAA,MAAA,OAAA,CAA0C,IAAI;GACpD,MAAM,YAAA,GAAA,MAAA,OAAA,CAAyC,IAAI;GACnD,MAAM,YAAA,GAAA,MAAA,OAAA,CAAyC,IAAI;GAGnD,MAAM,aAAA,GAAA,MAAA,OAAA,CAA0C,IAAI;GACpD,MAAM,CAAC,YAAY,kBAAA,GAAA,MAAA,SAAA,CAA0B,KAAK;GAClD,MAAM,CAAC,SAAS,eAAA,GAAA,MAAA,SAAA,CAAuB,KAAK;GAM5C,MAAM,CAAC,WAAW,iBAAA,GAAA,MAAA,SAAA,CAAyB,KAAK;GAChD,MAAM,CAAC,aAAa,mBAAA,GAAA,MAAA,SAAA,CAA2B,KAAK;GACpD,MAAM,CAAC,UAAU,gBAAA,GAAA,MAAA,SAAA,CAAwB,KAAK;GAC9C,MAAM,CAAC,YAAY,kBAAA,GAAA,MAAA,SAAA,CAA0B,KAAK;GAGlD,MAAM,CAAC,WAAW,iBAAA,GAAA,MAAA,SAAA,CAAyB,CAAC;GAC5C,MAAM,CAAC,WAAW,iBAAA,GAAA,MAAA,SAAA,CAAyB,EAAE;GAI7C,MAAM,gBAAA,GAAA,MAAA,OAAA,CAAsB,KAAK;GACjC,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;GAED,MAAM,OAAO,WAAW;GACxB,MAAM,UAAU,WAAW;GAC3B,MAAM,OAAO,WAAW;GACxB,MAAM,SAAS,WAAW;GAC1B,MAAM,YAAY,WAAW;GAI7B,MAAM,QAAQ,WAAW;GACzB,MAAM,cAAc,MAA8C,SAChE,OAAO,SAAS,SAAS;GAC3B,MAAM,aACJ,MACA,SACA,WACW;IACX,MAAM,SAAS,OAAO,QAAQ,SAAS,MAAM,EAAE,SAAS,MAAM;IAC9D,IAAI,OAAO,QAAQ,UAAU,KAAA,GAAW,OAAO;IAI/C,MAAM,MAAuC;KAC3C,MAAM,UAAU,SAAS,QAAQ;KACjC,GAAG,UAAU,OAAO,WAAW;KAC/B,QAAQ,UAAU,SAAS,UAAU;IACvC;IACA,IAAI,OAAO;IACX,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,GAAG,GAAG,OAAO,KAAK,WAAW,MAAM,OAAO,KAAK,OAAO,KAAK,CAAC;IACvG,OAAO;GACT;GACA,MAAM,cAAc,WAClB,OAAO,YAAY,KAAA,KAAa,MAAM,QAAQ,SAAS,MAAM;GAK/D,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,MAAM,WAAW,KAAA,GAAW;IAChC,IAAI,YAAY;IAChB,MAAM,MAAM,IAAI,MAAM;IACtB,IAAI,eAAe;KACjB,IAAI,CAAC,WAAW,cAAc,IAAI;IACpC;IACA,IAAI,MAAM,WAAW;IACrB,aAAa;KACX,YAAY;KACZ,IAAI,SAAS;IACf;GACF,GAAG,CAAC,WAAW,UAAU,MAAM,MAAM,CAAC;GAQtC,MAAM,cAAc,QAAQ,OAAO,KAAK;GACxC,MAAM,QAAQ,UAAU,SAAS;GACjC,MAAM,YAAY,UAAU,aAAa;GACzC,MAAM,YAAA,GAAA,MAAA,OAAA,CAAkB,WAAW;GACnC,SAAS,UAAU;GACnB,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,MAAM,WAAW,KAAA,GAAW;IAChC,MAAM,eAAe,OAAO,WAAW,eAClC,OAAO,aAAa,kCAAkC,CAAC,EAAE,YAAY;IAC1E,MAAM,WAAW,cAAc,kBAAkB,KAAK,IAAI,YAAY,SAAS,KAAA;IAK/E,MAAM,WAAW,aAAa,KAAA,IAAY,KAAA,IAAY,uBAAuB,UAAU,MAAM;IAC7F,MAAM,gBAAgB,aAAa,KAAA,IAAY,KAAA,IAAY,IAAI,IAC7D,SAAS,KAAI,kBAAiB;KAC5B,MAAM,UAAU,WAAW,aAAa;KACxC,OAAO,CAAC,eAAe;MACrB,KAAK;MACL,OAAO,UAAU,OAAO,gBAAgB,KAAK,YAAY,OAAO,cAAc,CAAC,OAAO,MAAM;KAC9F,CAAC;IACH,CAAC,CACH;IACA,MAAM,gBAAgB,WAAW,MAAM;IACvC,MAAM,MAAM,WAAW,aAAa;IACpC,MAAM,QAAQ,UAAU,OAAO,gBAAgB,KAAK,QAAQ,OAAO,cAAc,CAAC,OAAO,MAAM;IAG/F,MAAM,UAAU,MAAM,OAAO;IAC7B,MAAM,OAAO,cAAc,MAAM,KAAK,SAAS,SAAS,OAAO;IAC/D,IAAI,aAAa,KAAK,IAAI,QAAQ,KAAK,IAAI;IAC3C,IAAI,UAAU,YAAY,MACxB,UAAU,QAAQ,MAAM,qBAAqB;IAE/C,IAAI,cAAc;IAClB,IAAI,MAAM;IACV,IAAI,OAAO,YAAY,IAAI;IAC3B,IAAI,kBAAkB;IACtB,MAAM,QAAQ,OAAqB;KACjC,MAAM,QAAQ,KAAK;KACnB,OAAO;KACP,IAAI,aAAa,KAAA,KAAa,kBAAkB,KAAA,GAAW;MACzD,mBAAmB;MACnB,MAAM,UAAU,SAAS,QAAQ,eAAe;MAChD,MAAM,OAAO,cAAc,IAAI,QAAQ,SAAS;MAChD,MAAM,MAAM,KAAK,MAAM,OAAO,QAAQ;MACtC,MAAM,MAAM,cAAc,MAAM,KAAK,KAAK,KAAK,SAAS,OAAO;MAC/D,MAAM,SAAS,IAAI,IAAI,QAAQ,IAAI,IAAI;MACvC,IAAI,WAAW,YAAY;OACzB,aAAa;OACb,IAAI,UAAU,YAAY,MACxB,UAAU,QAAQ,MAAM,qBAAqB;MAEjD;MACA,MAAM,sBAAsB,IAAI;MAChC;KACF;KAIA,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,MAAM,cAAc,MAAM,KAAK,KAAK,SAAS,OAAO;KAC1D,MAAM,SAAS,IAAI,IAAI,QAAQ,IAAI,IAAI;KACvC,IAAI,WAAW,YAAY;MACzB,aAAa;MACb,IAAI,UAAU,YAAY,MACxB,UAAU,QAAQ,MAAM,qBAAqB;KAEjD;KACA,MAAM,sBAAsB,IAAI;IAClC;IACA,MAAM,sBAAsB,IAAI;IAChC,aAAa,qBAAqB,GAAG;GACvC,GAAG;IAAC;IAAW;IAAO;IAAM;IAAS;IAAM;IAAQ;IAAW,MAAM;GAAM,CAAC;GAK3E,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;GAKA,CAAA,GAAA,MAAA,UAAA,aAAsB,eAAe,GAAG,CAAC,CAAC;GAE1C,MAAM,iBAAiB,MAA+C;IACpE,IAAI,MAAM,iBAAiB,MAAM;IACjC,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;KACxC,IAAI,CAAC,WAAW,SAAS,MAAM,mBAAmB,IAAI;KACtD,WAAW,UAAU;IACvB;IACA,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,WAAW,SAAS,MAAM,mBAAmB,KAAK;IACtD,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,MAAM,KAAK,QAAQ,WAAW;GACvD,MAAM,eAAe,KAAK,MAAM,KAAK,SAAS,WAAW;GAOzD,MAAM,iBAAiB,UAAU,YAAY,CAAC;GAC9C,MAAM,YAAY,aAAa;GAE/B,MAAM,kBADY,CAAC,aAAa,eAAe,SAAS,IACpB,eAAe,MAAM,GAAG,CAAC,IAAI;GACjE,MAAM,eAAe,aAAa,QAAQ,eAAe,WAAW,IAChE,UAAU,SACV,KAAA;GAIJ,MAAM,eAAe,UAAU;GAC/B,MAAM,oBAAoB,aAAa,QAAQ,iBAAiB,KAAA,KAAa,kBAAkB,cAAc,KAAK,IAAI,CAAC,IACnH,eACA,KAAA;GAQJ,MAAM,gBAAgB,aAAa,QAAQ,eAAe,SAAS,KAAK,iBAAiB,KAAA,KAAa,sBAAsB,KAAA;GAC5H,MAAM,cAAc,UAAU,QAAQ,WAAW;GAEjD,MAAM,aAAa,UAAU;GAG7B,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,eAAe,UAAU,GAAG,eAAe,KAAK;GACtD,GAAG,CAAC,eAAe,MAAM,CAAC;GAE1B,CAAA,GAAA,MAAA,gBAAA,OAAsB;IACpB,IAAI,CAAC,SAAS;KACZ,cAAc,KAAK;KACnB,aAAa,CAAC;KACd;IACF;IACA,MAAM,6BAAmC;KACvC,MAAM,SAAS,UAAU;KACzB,MAAM,QAAQ,SAAS;KACvB,IAAI,WAAW,QAAQ,UAAU,MAAM;KAEvC,MAAM,QADiB,OAAO,cAAc,OAAO,sBAAsB,CAAC,CAAC,SAC5C,MAAM,sBAAsB,CAAC,CAAC,SAAS;KACtE,cAAc,KAAK;KAInB,MAAM,eAAe,QAAQ,UAAU,SAAS,sBAAsB,CAAC,CAAC,UAAU,IAAI;KACtF,aAAa,eAAe,IAAI,KAAK,KAAK,YAAY,IAAI,KAAK,CAAC;IAClE;IACA,qBAAqB;IACrB,OAAO,iBAAiB,UAAU,oBAAoB;IACtD,aAAa,OAAO,oBAAoB,UAAU,oBAAoB;GACxE,GAAG;IAAC;IAAS;IAAU,IAAI;IAAO,IAAI;IAAQ,QAAQ;IAAM;IAAe,eAAe;IAAQ;IAAW;GAAQ,CAAC;GA4QtH,QAAA,GAAA,UAAA,aAAA,CAAoB,iBAAA,GAAA,kBAAA,KAAA,CAzQjB,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;KASrB,MAAM,OAAO,EAAE;KACf,IAAI,gBAAgB,QAAQ,SAAS,SAAS,SAAS,IAAI,GAAG;KAK9D,IAAI,UAAU;KACd,eAAe;KACf,aAAa,UAAU,OAAO,iBAAiB,WAAW,KAAK,GAAG,GAAG;IACvE;cA1BF;KA4BE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MACE,WAAWA,uBAAO;MAClB,OAAO;OAAE,OAAO;OAAa,QAAQ;MAAa;gBAElD,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OACE,KAAK;OACL,WAAWA,uBAAO;OAClB,OAAO;QACL,OAAO;QACP,QAAQ;QACR,GAAI,MAAM,WAAW,KAAA,IACjB;SACE,iBAAiB,aAAa,SAAS,WAAW,WAAW,MAAM,KAAA;SACnE,gBAAiB,KAAK,QAAQ,UAAU,cAAe,QAAS,KAAK,UAAU,WAAW,aAAa,KAAK,UAAU,cAAe;SACrI,kBAAkB;SAClB,oBAAoB;QACtB,IACA,CAAC;QACL,QAAQ,QAAQ,YAAY,OAAO,SAAS;OAC9C;OACe;OACA;OACF;OACb,UAAU,MAAM;QAGd,IAAI,WAAW,SAAS;QACxB,IAAI,MAAM,kBAAkB,KAAA,KAAa,UAAU,YAAY,MAAM;SACnE,MAAM,OAAO,UAAU,QAAQ,sBAAsB;SACrD,IAAI,KAAK,QAAQ,KAAK,KAAK,SAAS,GAClC,MAAM,eAAe,EAAE,UAAU,KAAK,QAAQ,KAAK,QAAQ,EAAE,UAAU,KAAK,OAAO,KAAK,MAAM;QAElG;QACA,MAAM,MAAM;OACd;OACA,MAAK;OACL,cAAY,WAAW;iBAEtB,MAAM;MACJ,CAAA;KACF,CAAA;KACJ,MAAM;KACN,aAAa,QACZ,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAuB,KAAK;MAAW,WAAW,KAAKA,uBAAO,QAAQ,SAAS,SAAS,SAASA,uBAAO,aAAaA,uBAAO,SAAS;gBAClI,SAAS;KACP,GAFK,SAAS,EAEd;KAEN,aAAa,SAAS,eAAe,SAAS,KAAK,iBAAiB,KAAA,KAAa,sBAAsB,KAAA,MACtG,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MACE,KAAK;MACL,WAAWA,uBAAO;MAClB,sBAAsB,aAAa,IAAI;MACvC,sBAAsB,aAAa,KAAK;gBAJ1C;OAMG,gBAAgB,KAAK,SAAS,UAAU;QAKvC,MAAM,gBAAgB,QAAQ,YAAY,KAAA;QAC1C,MAAM,SACJ,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;SAEE,MAAK;SACL,WAAW,KACTA,uBAAO,QACPA,uBAAO,cACPA,uBAAO,iBACP,iBAAiBA,uBAAO,aAC1B;SACA,OAAO,MAAM,EAAE,qBAAqB;SACpC,eAAe;UAAE,MAAM,cAAc,QAAQ,SAAS;SAAE;mBAV1D,CAYG,UAAU,KAAK,CAAC,iBAAiB,eAAe,KAAA,KAC/C,iBAAA,GAAA,kBAAA,IAAA,CAAC,gBAAD;UAA4B;UAAmB;SAAQ,CAAA,GAExD,QAAQ,WAAW,QAAQ,MACtB;WAfD,gBAAgB,aAAa,QAAQ,UAAU,QAAQ,SAetD;QAIV,IAAI,UAAU,KAAK,eAAe,UAAU,GAAG,OAAO;QACtD,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;SAAoB,WAAWA,uBAAO;mBAAtC,CACG,QACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UACE,MAAK;UACL,WAAWA,uBAAO;UAClB,OAAO,YACH,MAAM,EAAE,sBAAsB,IAC9B,MAAM,EAAE,oBAAoB,EAAE,GAAG,eAAe,SAAS,EAAE,CAAC;UAChE,cAAY,YACR,MAAM,EAAE,sBAAsB,IAC9B,MAAM,EAAE,oBAAoB,EAAE,GAAG,eAAe,SAAS,EAAE,CAAC;UAChE,iBAAe;UACf,UAAU,MAAM;WACd,EAAE,gBAAgB;WAClB,gBAAe,SAAQ,CAAC,IAAI;UAC9B;oBAEC,YAAY,MAAM,MAAM,OAAO,eAAe,SAAS,CAAC;SACnD,CAAA,CACJ;WAnBI,SAmBJ;OAEV,CAAC;OACA,eAAe,WAAW,KAAK,iBAAiB,KAAA,KAC/C,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAEE,WAAW,KAAKA,uBAAO,QAAQA,uBAAO,YAAY;QAClD,MAAK;QACL,aAAU;kBAJZ,CAMG,eAAe,KAAA,KACd,iBAAA,GAAA,kBAAA,IAAA,CAAC,gBAAD;SAA4B;SAAmB;QAAQ,CAAA,GAExD,YACE;UATC,QASD;OAEN,sBAAsB,KAAA,KAAa,iBAAA,GAAA,kBAAA,IAAA,CAAC,yBAAD,EAAyB,cAAc,kBAAoB,CAAA;MAC5F;;KAEN,WAAW,QAAQ,YAAY,QAC9B,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MACE,KAAK;MACL,WAAW,KAAKA,uBAAO,OAAO,cAAcA,uBAAO,UAAU;MAC7D,kBAAgB,aAAa,UAAU;MACvC,OAAO,cAAc,YAAY,IAC5B,EAAE,cAAc,UAAU,IAC3B,KAAA;MACJ,sBAAsB;OAIpB,eAAe;MACjB;gBAEC,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,0BAA0B;SAAE,aAAa,UAAU;QAAK;QACxD,wBAAwB;SAAE,aAAa,UAAU;QAAM;QACvD,YAAY,MAAM;SAOhB,IAAI,aAAa,WAAW,EAAE,YAAY,eAAe,EAAE,QAAQ,WAAW;SAC9E,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,WAAW,WAAW,MAAM,EAAE,aAAa,CAAC;OACvC,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;QAAkB,CAAA,GACrD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAWA,uBAAO;0BACd;UACN,MAAM,UAAU,UAAU,SAAS,QAAQ;UAC3C,MAAM,UAAU,iBAAiB;UACjC,MAAM,YAAY,MAAM,EAAE,OAAc;UAExC,OAAO,UAAU,QAAQ,YAAY,EAAE,MADtB,cAAc,UAAU,YAAY,QACC,CAAC;SACzD,EAAA,CAAG;QACC,CAAA,CACH;;OACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAWA,uBAAO;kBAAvB,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAWA,uBAAO;mBAAa,UAAU,UAAU,cAAc,EAAE,GAAG,UAAU,OAAO,WAAW,EAAE,CAAC;QAAQ,CAAA,GACnH,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAWA,uBAAO;mBAAa,UAAU,UAAU,cAAc,EAAE,QAAQ,UAAU,SAAS,UAAU,EAAE,CAAC;QAAQ,CAAA,CACtH;;OACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAWA,uBAAO;kBAAvB;SACG,WAAW,MAAM,KAChB,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UAAQ,MAAK;UAAS,WAAWA,uBAAO;UAAQ,SAAS,MAAM;oBAC5D,WAAW,QAAQ,MAAM,EAAE,UAAU,CAAC;SACjC,CAAA;SAET,WAAW,QAAQ,KAClB,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UACE,MAAK;UACL,WAAWA,uBAAO;UAClB,eAAe;WAGb,eAAe;WACf,aAAa,WAAW;WACxB,YAAY,IAAI;UAClB;oBAEC,WAAW,UAAU,MAAM,EAAE,YAAY,CAAC;SACrC,CAAA;SAET,WAAW,MAAM,KAChB,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UAAQ,MAAK;UAAS,WAAWA,uBAAO;UAAQ,SAAS,MAAM;oBAC5D,WAAW,QAAQ,MAAM,EAAE,UAAU,CAAC;SACjC,CAAA;SAET,MAAM,mBAAmB,KAAA,KACxB,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UAAQ,MAAK;UAAS,WAAWA,uBAAO;UAAQ,SAAS,MAAM;oBAC5D,MAAM,EAAE,mBAAmB;SACtB,CAAA;QAEP;;MACL,EAAA,CAAA;KAED,CAAA;IAEJ;IAGiB,GAAG,MAAM,gBAAgB,SAAS,IAAI;EAChE;;;;EC3xBA,SAAgB,mBAA+B;GAC7C,IAAI,UAAU;GACd,MAAM,4BAAY,IAAI,IAAiC;GACvD,OAAO;IACL,WAAW;IACX,UAAU,UAAU;KAClB,UAAU,IAAI,QAAQ;KACtB,aAAa;MAAE,UAAU,OAAO,QAAQ;KAAE;IAC5C;IACA,KAAK,UAAU;KACb,IAAI,aAAa,SAAS;KAC1B,UAAU;KACV,KAAK,MAAM,YAAY,CAAC,GAAG,SAAS,GAAG,SAAS,QAAQ;IAC1D;GACF;EACF;;;;ECvBA,IAAa,mBAAb,MAA8B;GAC5B,4BAA6B,IAAI,IAAyB;;GAG1D,SAAS,UAA6B;IACpC,KAAK,UAAU,IAAI,SAAS,IAAI,QAAQ;GAC1C;;GAGA,IAAI,IAAqB;IACvB,OAAO,KAAK,UAAU,IAAI,EAAE;GAC9B;;GAGA,QAAkB;IAChB,OAAO,CAAC,GAAG,KAAK,UAAU,KAAK,CAAC,CAAC,CAAC,KAAK;GACzC;;GAGA,QAAc;IACZ,KAAK,UAAU,MAAM;GACvB;;;;;GAMA,MAAM,MAAc,KAAyB,QAAoC;IAC/E,MAAM,WAAW,KAAK,UAAU,IAAI,IAAI;IACxC,IAAI,aAAa,KAAA,GAAW;KAC1B,MAAM,OAAO,SAAS,cAAc,KAAK;KACzC,KAAK,QAAQ,yBAAyB;KACtC,KAAK,cAAc,oBAAmB,OAAO,mDAAkD,KAAK,MAAM,CAAC,CAAC,KAAK,IAAI,IAAI;KACzH,IAAI,UAAU,YAAY,IAAI;KAC9B,IAAI,gBAAgB,KAAK,OAAO,CAAC;KACjC,OAAO,EAAE,eAAe,KAAK,OAAO,EAAE;IACxC;IACA,OAAO,SAAS,MAAM,KAAK,SAAS,eAAe,MAAM,CAAC;GAC5D;EACF;;;;;;EAOA,MAAa,6BAA6B,IAAI,iBAAiB;;;;EClC/D,SAAgB,kBAAkB,UAAyB,QAAqB;GAC9E,IAAI,UAAU;GACd,MAAM,4BAAY,IAAI,IAAoC;GAC1D,OAAO;IACL,WAAW;IACX,UAAU,UAAU;KAClB,UAAU,IAAI,QAAQ;KACtB,aAAa;MAAE,UAAU,OAAO,QAAQ;KAAE;IAC5C;IACA,KAAK,OAAO;KACV,IAAI,UAAU,SAAS;KACvB,UAAU;KACV,KAAK,MAAM,YAAY,CAAC,GAAG,SAAS,GAAG,SAAS,KAAK;IACvD;GACF;EACF;;;;;;;;;;;;;ECjBA,SAAgB,kBAAkB,OAKjB;GACf,MAAM,gBAAA,GAAA,MAAA,OAAA,CAA6C,IAAI;GACvD,MAAM,aAAA,GAAA,MAAA,OAAA,CAAuC,IAAI;GACjD,MAAM,aAAA,GAAA,MAAA,OAAA,CAAgD,IAAI;GAC1D,MAAM,WAAA,GAAA,MAAA,OAAA,CAAkD,IAAI;GAC5D,MAAM,CAAC,OAAO,aAAA,GAAA,MAAA,SAAA,CAA6C,IAAI;GAG/D,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,SAAS,IAAI;IACb,MAAM,YAAY,aAAa;IAC/B,MAAM,SAAS,MAAM,WAAW;IAChC,IAAI,cAAc,QAAQ,WAAW,KAAA,GAAW,OAAO,KAAA;IACvD,UAAU,YAAY,kBAAkB,MAAM,KAAK;IACnD,MAAM,WAA2B,CAAC;IAClC,MAAM,MAA0B;KAC9B,OAAO,MAAM,WAAW;KACxB,WAAW,UAAU,mBAAmB,MAAM,WAAW,EAAE;KAC3D;KACA,OAAO,UAAU;KACjB,UAAU,MAAM;KAChB,YAAY,OAAO;MAAE,SAAS,KAAK,EAAE;KAAE;IACzC;IACA,IAAI;IACJ,IAAI;KACF,SAAS,2BAA2B,MAAM,UAAU,KAAK,MAAM;IACjE,QAAQ;KACN,SAAS,aAAa;KACtB,aAAa;MAAE,KAAK,MAAM,MAAM,SAAS,OAAO,CAAC,GAAG,GAAG;KAAE;IAC3D;IACA,UAAU,UAAU;IACpB,OAAO,UAAU,QAAQ;IACzB,aAAa;KACX,UAAU,UAAU;KACpB,KAAK,MAAM,MAAM,SAAS,OAAO,CAAC,GAAG,GAAG;KACxC,OAAO,QAAQ;IACjB;GAEF,GAAG,CAAC,MAAM,UAAU,CAAC;GAGrB,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,UAAU,SAAS,KAAK,MAAM,KAAK;GACrC,GAAG,CAAC,MAAM,KAAK,CAAC;GAEhB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IACE,KAAK;IACL,uBAAqB,MAAM,WAAW;IACtC,OAAO;KAAE,OAAO;KAAQ,QAAQ;IAAO;IACvC,gBAAgB,MAAM;KAAE,QAAQ,UAAU;MAAE,GAAG,EAAE;MAAS,GAAG,EAAE;KAAQ;IAAE;IACzE,cAAc,MAAM;KAClB,MAAM,OAAO,QAAQ;KACrB,QAAQ,UAAU;KAClB,IAAI,SAAS,MAAM;KAEnB,IAAI,KAAK,IAAI,EAAE,UAAU,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,EAAE,UAAU,KAAK,CAAC,IAAI,GAAG;KAC1E,MAAM,OAAO,EAAE,cAAc,sBAAsB;KACnD,UAAU,SAAS,IAAI,EAAE,UAAU,KAAK,MAAM,EAAE,UAAU,KAAK,GAAG;IACpE;cAEC,UAAU,QACT,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAAM,6BAA2B;eAC9B,UAAU,iBACP,MAAM,EAAE,yBAAyB,IACjC,UAAU,mBACR,MAAM,EAAE,2BAA2B,IACnC,MAAM,EAAE,wBAAwB;IAClC,CAAA;GAEL,CAAA;EAET;;;;;;;;;;;;;;EC1EA,SAAgB,oBAAoB,OASnB;GACf,MAAM,gBAAA,GAAA,MAAA,OAAA,CAA6C,IAAI;GACvD,MAAM,aAAA,GAAA,MAAA,OAAA,CAAuC,IAAI;GACjD,MAAM,aAAA,GAAA,MAAA,OAAA,CAAkD,IAAI;GAC5D,MAAM,CAAC,SAAS,eAAA,GAAA,MAAA,SAAA,CAAuB,KAAK;GAG5C,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,WAAW,KAAK;IAChB,MAAM,YAAY,aAAa;IAC/B,MAAM,WAAW,MAAM,WAAW;IAClC,IAAI,cAAc,QAAQ,aAAa,KAAA,GAAW,OAAO,KAAA;IACzD,UAAU,YAAY,kBAAkB,MAAM,KAAK;IACnD,MAAM,WAA2B,CAAC;IAClC,MAAM,MAA0B;KAC9B,OAAO,MAAM,WAAW;KACxB,WAAW,UAAU,mBAAmB,MAAM,WAAW,EAAE;KAC3D;KACA,OAAO,UAAU;KACjB,UAAU,MAAM;KAChB,YAAY,OAAO;MAAE,SAAS,KAAK,EAAE;KAAE;IACzC;IACA,IAAI;IACJ,IAAI;KACF,SAAS,2BAA2B,MAAM,YAAY,KAAK,QAAQ;IACrE,QAAQ;KACN,WAAW,IAAI;KACf,aAAa;MAAE,KAAK,MAAM,MAAM,SAAS,OAAO,CAAC,GAAG,GAAG;KAAE;IAC3D;IACA,UAAU,UAAU;IAIpB,IAAI,MAAM,QAAQ,KAAA,GAAW;KAC3B,MAAM,cAAc,MAAM;KAC1B,YAAY,YAAY,UAAU;MAAE,UAAU,SAAS,SAAS,KAAK;KAAE;KACvE,YAAY,gBAAgB,UAAU;MAAE,UAAU,SAAS,aAAa,KAAK;KAAE;KAI/E,IAAI,YAAY,cAAc,KAAA,GAAW,OAAO,aAAa,YAAY,SAAS;KAClF,SAAS,WAAW;MAClB,YAAY,WAAW,KAAA;MACvB,YAAY,eAAe,KAAA;KAC7B,CAAC;IACH;IAIA,MAAM,YAAY,MAAM,WAAW,UAAU,cAAc,SAAS,OAAO,SAAS,KAAA,IAAY,KAAA,IAAY;IAC5G,MAAM,UAAU,MAAM,KAAK,WAAW,aAAa;KACjD,IAAI,cAAc,KAAA,GAAW;KAC7B,IAAI,UAAU;MACZ,OAAO,SAAS,SAAS;MACzB;KACF;KACA,OAAO,SAAS,MAAM,WAAW,UAAU,YAAY;IACzD,CAAC;IACD,SAAS,KAAK,OAAO;IACrB,aAAa;KACX,UAAU,UAAU;KACpB,KAAK,MAAM,MAAM,SAAS,OAAO,CAAC,GAAG,GAAG;KACxC,OAAO,QAAQ;IACjB;GAEF,GAAG,CAAC,MAAM,UAAU,CAAC;GAGrB,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,UAAU,SAAS,KAAK,MAAM,KAAK;GACrC,GAAG,CAAC,MAAM,KAAK,CAAC;GAEhB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IACE,KAAK;IACL,yBAAuB,MAAM,WAAW;IACxC,OAAO;KAAE,OAAO;KAAQ,QAAQ;KAAQ,eAAe;IAAO;cAE7D,WACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAAM,+BAA4B;eAC/B,MAAM,EAAE,4BAA4B,EAAE,UAAU,WAAW,CAAC;IACzD,CAAA;GAEL,CAAA;EAET;;;;;;;;;;;;;EC/FA,SAAgB,kBAAkB,OAYjB;GACf,MAAM,WAAW,MAAM,WAAW,YAAY;GAG9C,MAAM,WAAA,GAAA,MAAA,OAAA,CAAqF,IAAI;GAC/F,IAAI,QAAQ,YAAY,QAAQ,QAAQ,QAAQ,OAAO,MAAM,WAAW,IACtE,QAAQ,UAAU;IAAE,IAAI,MAAM,WAAW;IAAI,QAAQ,iBAAiB;GAAE;GAE1E,MAAM,OAAO,MAAM,QAAQ,QAAQ,QAAQ;GAC3C,IAAI,aAAa,YAAY,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAA,kBAAA,UAAA,EAAA,UAAG,MAAM,SAAW,CAAA;GACxD,IAAI,aAAa,cAAc,2BAA2B,IAAI,UAAU,MAAA,GAAA,MAAA,eAAA,CAAoC,MAAM,QAAQ,GAAG;IAC3H,MAAM,SACJ,iBAAA,GAAA,kBAAA,IAAA,CAAC,qBAAD;KACE,YAAY,MAAM;KAClB,OAAO,MAAM;KACb,OAAO,MAAM;KACP;KACN,GAAK,MAAM,QAAQ,KAAA,IAAY,CAAC,IAAI,EAAE,KAAK,MAAM,IAAI;KACrD,GAAG,MAAM;IACV,CAAA;IAEH,QAAA,GAAA,MAAA,aAAA,CAAoB,MAAM,UAAU;KAAE;KAAQ,mBAAmB,aAAsB,KAAK,KAAK,QAAQ;IAAE,CAAC;GAC9G;GACA,IAAI,aAAa,YAAY,2BAA2B,IAAI,QAAQ,MAAA,GAAA,MAAA,eAAA,CAAoC,MAAM,QAAQ,GAAG;IACvH,MAAM,SACJ,iBAAA,GAAA,kBAAA,IAAA,CAAC,mBAAD;KACE,YAAY,MAAM;KAClB,OAAO,MAAM;KACb,OAAO,MAAM;KACb,GAAG,MAAM;IACV,CAAA;IAEH,QAAA,GAAA,MAAA,aAAA,CAAoB,MAAM,UAAU,EAAE,OAAO,CAAC;GAChD;GACA,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;IAAM,kCAAgC;cACnC,MAAM,EAAE,4BAA4B,EAAE,SAAS,CAAC;GAC7C,CAAA;EAEV;;;;ECmnBA,SAAgB,YAAY,OAA0C,WAAqD;GACzH,OAAO,MAAM,MAAM,MAAK,SAAQ,aAAa,KAAK,MAAM,YAAY,KAAK,EAAE;EAC7E;;;;;;;;;;;;;EC3nBA,IAAI,WAAW;;EAGf,SAAgB,YAAY,OAOJ;GACtB,MAAM,EAAE,YAAY,OAAO,KAAK,QAAQ;GACxC,MAAM,MAAA,GAAA,MAAA,qBAAA,CAA0B,MAAM,WAAW,MAAM,WAAW;GAClE,MAAM,MAAM,WAAW;GACvB,MAAM,OAAO,GAAG,UAAU;GAC1B,MAAM,QAAQ,GAAG,UAAU,SAAS;GAEpC,MAAM,gBAAgB,GAAG,UAAU;GAEnC,MAAM,CAAC,MAAM,YAAA,GAAA,MAAA,SAAA,CAAoB,KAAK;GACtC,MAAM,CAAC,MAAM,YAAA,GAAA,MAAA,SAAA,CAA6B,MAAM;GAEhD,MAAM,CAAC,QAAQ,cAAA,GAAA,MAAA,SAAA,CAA0C,KAAA,CAAS;GAClE,MAAM,aAAA,GAAA,MAAA,OAAA,CAAuC,KAAA,CAAS;GACtD,UAAU,UAAU;GACpB,MAAM,UAAA,GAAA,MAAA,OAAA,CAAuC,IAAI;GACjD,MAAM,WAAA,GAAA,MAAA,OAAA,CAAwC,IAAI;GAClD,MAAM,CAAC,QAAQ,cAAA,GAAA,MAAA,SAAA,CAAkC,CAAC,CAAC;GAEnD,MAAM,WAAA,GAAA,MAAA,OAAA,CAA0C,MAAM,QAAQ,IAAI;GAClE,QAAQ,UAAU,MAAM,QAAQ;GAChC,MAAM,YAAA,GAAA,MAAA,OAAA,CAAkB,KAAK;GAC7B,SAAS,UAAU;GACnB,MAAM,eAAA,GAAA,MAAA,OAAA,CAAqB,KAAK;GAChC,MAAM,qBAAA,GAAA,MAAA,OAAA,CAA2B,CAAC;GAClC,MAAM,WAAA,GAAA,MAAA,OAAA,CAAiB,CAAC;GACxB,MAAM,WAAA,GAAA,MAAA,OAAA,CAAiB,KAAK;GAG5B,MAAM,KAAK,MAAM;GACjB,MAAM,aAAa,SAAyB,GAAG,uBAAuB,IAAI;GAC1E,MAAM,iBAAiB,SAAyB,GAAG,2BAA2B,IAAI;GAElF,MAAM,aAAa,SAAuB;IACxC,MAAM,KAAK,EAAE;IACb,WAAU,SAAQ,CAAC,GAAG,KAAK,MAAM,EAAE,GAAG;KAAE;KAAI;IAAK,CAAC,CAAC;IACnD,OAAO,iBAAiB;KACtB,WAAU,SAAQ,KAAK,QAAO,UAAS,MAAM,OAAO,EAAE,CAAC;IACzD,GAAG,IAAI;GACT;GAEA,MAAM,eAAe,WAAwC;IAC3D,IAAI,OAAO,SAAS,KAAA,GAAW,MAAM,QAAQ,gBAAgB,OAAO,IAAI;GAC1E;GASA,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,QAAQ,KAAA,GAAW,OAAO,KAAA;IAI9B,MAAM,aAAa,OAAe,WAAyB;KACzD,IAAI,WAAW,KAAK;KACpB,kBAAkB,UAAU,KAAK,IAAI,IAAI;KACzC,OAAO,iBAAiB;MACtB,IAAI,KAAK,IAAI,KAAK,kBAAkB,SAAS,IAAI,WAAW,KAAA,CAAS;KACvE,GAAG,MAAM;IACX;IACA,MAAM,SAAS,YAA6B;KAC1C,IAAI,YAAY,KAAA,KAAa,QAAQ,SAAS,GAAG;MAC/C,MAAM,SAAS,QAAQ,KAAK,MAAM,KAAK,OAAO,IAAI,QAAQ,MAAM;MAChE,MAAM,QAAQ,YAAY;OAAE,MAAM;OAAQ,MAAM;OAAQ,IAAI,KAAK,IAAI;MAAE,CAAC;KAC1E;IACF;IAGA,MAAM,iBAAiB,UACrB,WAAW,UAAU,OAAO,MAAM,EAAE,UAAU,QAAQ,KAAK,OAAO,MAAM,IAAI,CAAC,KAAK;IACpF,IAAI,OAAO,IAAI,OAAO;KACpB,IAAI,QAAQ,YAAY,SAAS;MAC/B,IAAS,QAAQ,IAAI,CAAC,CAAC,KAAK,mBAAmB,KAAA,CAAS;MACxD;KACF;KACA,IAAI,QAAQ,YAAY,QAAQ;KAChC,MAAM,MAAM,IAAI,UAAU;MAAE,IAAI;MAAG,IAAI;MAAG,IAAI;MAAG,IAAI;KAAE;KACvD,MAAM,MAAM,KAAK,IAAI,OAAO,IAAI,KAAK,IAAI;KACzC,MAAM,MAAM,KAAK,IAAI,OAAO,IAAI,KAAK,IAAI;KACzC,IAAI,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;KAC1C,IAAI,KAAK,IAAI,IAAI,kBAAkB,SAAS;MAC1C,IAAS,MAAM,CAAC,CAAC,KAAK,mBAAmB,KAAA,CAAS;MAClD;KACF;KACA,MAAM,aAAa,WAAW,UAAU,OAAO,MAAK,SAAQ,KAAK,OAAO,UAAU,OAAO;KACzF,IAAI,eAAe,KAAA,GAAW;MAI5B,MAAM,UAAU,WAAW,gBAAgB,CAAC;MAC5C,IAAI,QAAQ,SAAS,GAAG;OACtB,IAAI,OAAO,KAAK,OAAO;OACvB,MAAM,QAAQ,QAAQ,MAAK,WAAU;QACnC,IAAI,OAAO,OAAO,aAAa,OAAO;QACtC,QAAQ,OAAO;QACf,OAAO;OACT,CAAC;OACD,IAAI,UAAU,KAAA,GAAW;QACvB,UAAU,MAAM,OAAO,cAAc,MAAM,KAAK,KAAK,GAAI;QACzD,MAAM,MAAM,OAAO;QACnB;OACF;MACF;MACA,IAAS,MAAM,CAAC,CAAC,KAAK,mBAAmB,KAAA,CAAS;MAClD;KACF;KACA,MAAM,OAAO,IAAI,UAAU,KAAA,IAAY,KAAA,IAAY,YAAY,IAAI,OAAO,EAAE;KAC5E,IAAI,SAAS,KAAA,GAAW;KACxB,IAAS,MAAM,KAAK,IAAI,CAAC,CAAC,MAAM,WAAW;MACzC,YAAY,MAAM;MAClB,IAAI,OAAO,QAAQ,MAAM;MACzB,IAAI,OAAO,UAAU,KAAA,GAAW,UAAU,OAAO,OAAO,OAAO,WAAW,GAAI;MAC9E,IAAI,OAAO,WAAW,KAAA,GACpB,MAAM,QAAQ,YAAY;OAAE,MAAM,OAAO;OAAQ,MAAM;OAAQ,IAAI,KAAK,IAAI;MAAE,CAAC;KAEnF,SAAS,KAAA,CAAS;IACpB;IACA,aAAa;KAAE,IAAI,MAAM,KAAA;IAAU;GAErC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC;GAIvB,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,YAAY,SAAmB;KACjC,SAAQ,SAAQ,QAAQ,CAAC,IAAI;KAC7B,QAAQ,MAAM;IAChB;IACA,aAAa;KAAE,IAAI,WAAW,KAAA;IAAU;GAE1C,GAAG,CAAC,GAAG,CAAC;GAQR,CAAA,GAAA,MAAA,gBAAA,OAAsB;IACpB,IAAI,CAAC,MAAM,OAAO,KAAA;IAClB,MAAM,MAAM,OAAO;IACnB,MAAM,OAAO,QAAQ;IACrB,IAAI,QAAQ,QAAQ,SAAS,MAAM,OAAO,KAAA;IAC1C,MAAM,cAAoB;KACxB,MAAM,MAAM,IAAI,eAAe,sBAAsB;KACrD,IAAI,QAAQ,KAAA,GAAW;KACvB,MAAM,MAAM;KACZ,MAAM,QAAQ,KAAK,sBAAsB,CAAC,CAAC;KAE3C,MAAM,IADU,OAAO,aAAa,IAAI,SACnB,QAAQ,MAAM,IAAI,QAAQ,MAAM,EAAE,QAAQ;KAC/D,KAAK,MAAM,YAAY,eAAe,KAAK,MAAM,CAAC,IAAI,SAAS,KAAK,MAAM,CAAC,IAAI,SAAS,CAAC,IAAI;KAC7F,KAAK,MAAM,YAAY,KAAK,MAAM,IAAI,MAAM,IAAI;IAClD;IACA,MAAM;IACN,OAAO,iBAAiB,UAAU,KAAK;IACvC,aAAa;KAAE,OAAO,oBAAoB,UAAU,KAAK;IAAE;GAE7D,GAAG,CAAC,MAAM,IAAI,CAAC;GAIf,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,OAAO,MAAM,KAAK,WAAW,aAAa;KACxC,YAAY,UAAU;KACtB,IAAI,YAAY,QAAQ,YAAY,SAClC,IAAS,QAAQ,IAAI,CAAC,CAAC,KAAK,mBAAmB,KAAA,CAAS;IAE5D,CAAC;GAEH,GAAG,CAAC,MAAM,IAAI,CAAC;GAMf,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,MAAM,WAAW,KAAK;IACtB,IAAI,QAAQ,KAAA,KAAa,aAAa,KAAA,GAAW,OAAO,KAAA;IACxD,MAAM,QAAQ,SAAS,aAAa,SAAS,KAAK,QAAQ,KAAK,QAAQ,MAAM,IAAI,QAAQ,CAAC;IAC1F,IAAI,SAAS,GAAG,OAAO,KAAA;IACvB,MAAM,QAAQ,OAAO,kBAAkB;KACrC,IAAI,SAAS,YAAY,QAAQ;KACjC,IAAI,QAAQ,YAAY,QAAQ,YAAY,SAAS;KACrD,IAAI,KAAK,IAAI,IAAI,kBAAkB,SAAS;KAC5C,IAAI;KACJ,IAAI,QAAQ,WAAW,SAAS,SAAS;MAEvC,MAAM,WAAW,SAAS,KAAK,QAAQ,KAAK,QAAQ,MAAM,IAAI,QAAQ,CAAC;MACvE,IAAI,UAAU,KAAK,OAAO,IAAI;MAC9B,KAAK,MAAM,OAAO,SAAS,MAAM;OAC/B,WAAW,IAAI;OACf,IAAI,UAAU,GAAG;QAAE,YAAY;QAAK;OAAM;MAC5C;KACF,OAAO;MACL,IAAI,OAAO,KAAK,OAAO,IAAI;MAC3B,KAAK,MAAM,OAAO,SAAS,MAAM;OAC/B,QAAQ,IAAI;OACZ,IAAI,OAAO,GAAG;QAAE,YAAY;QAAK;OAAM;MACzC;KACF;KACA,IAAI,cAAc,KAAA,GAAW;MAC3B,QAAQ,WAAW;MACnB;KACF;KACA,QAAQ,UAAU;KAClB,IAAI,WAAW,UAAU,KAAK;KAE9B,IAAI,UAAU,YAAY,KAAA,KAAa,UAAU,QAAQ,SAAS,GAAG;MACnE,MAAM,SAAS,UAAU,QAAQ,KAAK,MAAM,KAAK,OAAO,IAAI,UAAU,QAAQ,MAAM;MACpF,MAAM,QAAQ,YAAY;OAAE,MAAM;OAAQ,MAAM;OAAQ,IAAI,KAAK,IAAI;MAAE,CAAC;KAC1E;IACF,GAAG,SAAS,UAAU;IACtB,aAAa,OAAO,cAAc,KAAK;GAEzC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC;GAMvB,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,MAAM,OAAO,KAAK;IAClB,IAAI,QAAQ,KAAA,KAAa,SAAS,KAAA,KAAa,MAAM,SAAS,QAAQ,OAAO,KAAA;IAC7E,MAAM,eAAe,WAAW,UAAU,OAAO,MAAK,SAAQ,KAAK,OAAO,UAAU,OAAO,CAAC,EAAE;;IAE9F,MAAM,WAAW,UAA0B,eAAe,UAAU;IACpE,IAAI,WAAW,QAAQ,KAAK,KAAK,CAAC;IAClC,IAAI,cAAc;IAClB,MAAM,QAAQ,OAAO,kBAAkB;KACrC,IAAI,QAAQ,SAAS;KACrB,QAAQ,UAAU;KAClB,IAAS,SAAS,CAAC,CAAC,MAAM,WAAW;MACnC,QAAQ,UAAU;MAIlB,IAAI,QAAQ,YAAY,QAAQ;MAChC,YAAY,MAAM;MAClB,IAAI,OAAO,OAAO,QAAQ,OAAO,YAAY,KAAA,GAAW;MACxD,MAAM,cAAc,QAAQ,OAAO,YAAY,YAAY,KAAK,eAAe,KAAK,SAAS;MAC7F,MAAM,OAAO,OAAO,YAAY,YAAY,KAAK,UAAU,WAAW,OAAO,KAAK,UAAU,QAAQ;MACpG,IAAI,WAAW,WAAW;MAC1B,cAAc,OAAO,iBAAiB;OACpC,IAAI,QAAQ,YAAY,QAAQ,IAAI,WAAW,QAAQ,KAAK,KAAK,CAAC;MACpE,GAAG,IAAI;KACT,SAAS;MAAE,QAAQ,UAAU;KAAM,CAAC;IACtC,GAAG,KAAK,MAAM;IACd,aAAa;KACX,OAAO,cAAc,KAAK;KAC1B,OAAO,aAAa,WAAW;KAC/B,IAAI,WAAW,KAAA,CAAS;IAC1B;GAEF,GAAG;IAAC,WAAW;IAAI;IAAK,MAAM;IAAM;GAAM,CAAC;GAK3C,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,MAAM,QAAQ,KAAK;IACnB,IAAI,QAAQ,KAAA,KAAa,UAAU,KAAA,KAAa,MAAM,SAAS,SAAS,OAAO,KAAA;IAE/E,MAAM,QADe,WAAW,UAAU,OAAO,MAAK,SAAQ,KAAK,OAAO,UAAU,OAAO,CAAC,EAAE,eAAA,GAClE,YAAY,MAAM;IAC9C,IAAI,WAAW,IAAI;IACnB,aAAa,IAAI,WAAW,KAAA,CAAS;GAEvC,GAAG;IAAC,WAAW;IAAI;IAAK,MAAM;IAAM;GAAM,CAAC;GAE3C,IAAI,QAAQ,KAAA,KAAa,SAAS,KAAA,GAAW,OAAO;GAEpD,MAAM,OAAO,KAAK;GAClB,MAAM,QAAQ,IAAI,SAAS,CAAC;GAC5B,MAAM,OAAO,IAAI;GAEjB,MAAM,OAAO,WAAyB;IACpC,IAAS,IAAI,MAAM,CAAC,CAAC,MAAM,WAAW;KACpC,YAAY,MAAM;KAClB,IAAI,OAAO,OAAO,MAAM;MACtB,IAAI,OAAO,UAAU,sBAAsB;OACzC,MAAM,OAAO,MAAM,MAAM,MAAK,UAAS,MAAM,OAAO,MAAM;OAC1D,UAAU,GAAG,6BAA6B,EAAE,UAAU,cAAc,MAAM,YAAY,QAAQ,EAAE,CAAC,CAAC;MACpG;MACA;KACF;KACA,IAAI,OAAO,UAAU,KAAA,GACnB,UAAU,GAAG,sBAAsB;MAAE,QAAQ,OAAO,MAAM;MAAQ,UAAU,cAAc,OAAO,MAAM,QAAQ;KAAE,CAAC,CAAC;IAEvH,SAAS,KAAA,CAAS;GACpB;GAEA,MAAM,WAAW,SAAwC;IACvD,IAAS,QAAQ,IAAI,CAAC,CAAC,KAAK,mBAAmB,KAAA,CAAS;GAC1D;GAKA,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,UAAU,aAAa;GACzB,GAAG,CAAC,WAAW,IAAI,aAAa,CAAC;GAMjC,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,MAAM,OAAO,WAAW,UAAU,OAAO,MAAK,cAAa,UAAU,OAAO,MAAM;IAClF,IAAI,YAAY,MAAM;IACtB,IAAI,eAAe,MAAM,SAAS;GAIpC,GAAG;IAAC,WAAW;IAAI;IAAQ;GAAa,CAAC;GAEzC,MAAM,QAAQ,WAAW,UAAU;;GAEnC,MAAM,eAAe,OACnB,OAAO,KAAA,IAAY,KAAA,IAAY,WAAW,UAAU,OAAO,MAAK,cAAa,UAAU,OAAO,EAAE,CAAC,EAAE;GAErG,MAAM,cAAc,SAA8C;IAChE,UAAU,MAAM,EAAE;IAClB,IAAI,eAAe,MAAM,SAAS;IAGlC,MAAM,gBAAsB;KAC1B,UAAU,aAAa;KACvB,IAAI,eAAe,YAAY,aAAa,CAAC;IAC/C;IACA,IAAS,QAAQ,MAAM,EAAE,CAAC,CAAC,MAAM,WAAW;KAC1C,IAAI,OAAO,IAAI;KACf,QAAQ;IACV,GAAG,OAAO;GACZ;GAEA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,KAAK;IAAQ,WAAWC,uBAAO;IAAa,yBAAuB,WAAW;cAAnF;KACG,OAAO,KAAI,UACV,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAoB,WAAWA,uBAAO;gBAAgB,MAAM;KAAU,GAA5D,MAAM,EAAsD,CACvE;KACA,SAAS,QACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAWA,uBAAO;gBACpB,GAAG,SAAS,SAAS,yBAAyB,uBAAuB;KACnE,CAAA;KAEN,QACC,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,KAAK;MAAS,WAAWA,uBAAO;MAAc,aAAW;gBAA9D;OACG,SAAS,UACR,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAWA,uBAAO;kBACpB,OAAO,QAAQ,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,UAAU;SAC3C,MAAM,QAAQ,KAAK,MAAM,SAAS;SAClC,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;UAAgB,WAAWA,uBAAO;UAAgB,OAAO,UAAU,IAAI,IAAI,MAAM,OAAO,KAAK,IAAI,MAAM,OAAO,KAAK,GAAG;oBAAtH,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,uBAAO;qBAAmB,UAAU,IAAI;UAAQ,CAAA,GACjE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,uBAAO;qBACtB,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;YACE,WAAWA,uBAAO;YAClB,OAAO,EAAE,OAAO,KAAK,MAAO,QAAQ,KAAK,MAAO,GAAG,IAAI,IAAI;WAC5D,CAAA;UACG,CAAA,CACH;YARK,IAQL;QAET,CAAC;OACE,CAAA,GACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAWA,uBAAO;kBAAvB;SACG,IAAI,UAAU,KAAA,KACb,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UACE,MAAK;UACL,WAAWA,uBAAO;UAClB,eAAe,QAAQ,SAAS,UAAU,OAAO,OAAO;oBAEvD,GAAG,SAAS,UAAU,sBAAsB,oBAAoB;SAC3D,CAAA;SAET,SAAS,KAAA,KACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UAAQ,MAAK;UAAS,WAAWA,uBAAO;UAAQ,eAAe,QAAQ,MAAM;oBAC1E,GAAG,mBAAmB;SACjB,CAAA;SAET,UAAU,KAAA,KAAa,MAAM,SAAS,KACrC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UAAQ,MAAK;UAAS,WAAWA,uBAAO;UAAQ,eAAe,QAAQ,OAAO;oBAC3E,GAAG,mBAAmB;SACjB,CAAA;SAET,IAAI,SAAS,KAAA,KACZ,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UACE,MAAK;UACL,WAAWA,uBAAO;UAClB,eAAe,QAAQ,SAAS,SAAS,OAAO,MAAM;oBAErD,GAAG,SAAS,SAAS,0BAA0B,mBAAmB;SAC7D,CAAA;QAEP;SACL,EAAA,CAAA;OAEH,SAAS,UAAU,SAAS,KAAA,KAC3B,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAWA,uBAAO;kBACpB,KAAK,MAAM,KAAI,SACd,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;SAEE,MAAK;SACL,WAAWA,uBAAO;SAClB,eAAe,IAAI,KAAK,EAAE;SAC1B,OAAO,KAAK,QAAQ,QAAQ,OAAO,KAAK,KAAK,IAAI,MAAM,cAAc,KAAK,QAAQ;mBALpF;UAOG,KAAK,UAAU,KAAA,KACd,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;WAAK,WAAWA,uBAAO;WAAuB,KAAK,KAAK;WAAO,KAAI;WAAG,WAAW;UAAQ,CAAA;UAE3F,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAWA,uBAAO;qBAAwB,KAAK;UAAY,CAAA;UACjE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;WAAM,WAAWA,uBAAO;qBAAxB;YACG,KAAK;YAAM;YAAE,cAAc,KAAK,QAAQ;WACrC;;SACA;WAbD,KAAK,EAaJ,CACT;OACE,CAAA,GACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAWA,uBAAO;kBACrB,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SAAQ,MAAK;SAAS,WAAWA,uBAAO;SAAQ,eAAe,QAAQ,MAAM;mBAC1E,GAAG,mBAAmB;QACjB,CAAA;OACL,CAAA,CACL,EAAA,CAAA;OAEH,SAAS,WAAW,UAAU,KAAA,KAC7B,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAWA,uBAAO;kBAAvB,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SACE,MAAK;SACL,WAAW,WAAW,KAAA,IAAYA,uBAAO,mBAAmB,MAAMA,uBAAO,yBAAyBA,uBAAO;SACzG,eAAe,WAAW,KAAA,CAAS;mBAElC,GAAG,0BAA0B;QACxB,CAAA,GACP,MAAM,KAAI,SACT,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SAEE,MAAK;SACL,WAAW,WAAW,KAAK,KAAKA,uBAAO,mBAAmB,MAAMA,uBAAO,yBAAyBA,uBAAO;SACvG,eAAe,WAAW,IAAI;mBAE7B,KAAK;QACA,GAND,KAAK,EAMJ,CACT,CACE;WACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAWA,uBAAO;kBACrB,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SAAQ,MAAK;SAAS,WAAWA,uBAAO;SAAQ,eAAe,QAAQ,MAAM;mBAC1E,GAAG,mBAAmB;QACjB,CAAA;OACL,CAAA,CACL,EAAA,CAAA;OAEJ,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QACE,MAAK;QACL,WAAWA,uBAAO;QAClB,cAAY,GAAG,mBAAmB;QAClC,eAAe,QAAQ,KAAK;kBAC7B;OAEO,CAAA;MACL;;IAEJ;;EAET;;;;;;;;;;;;;;;;ECjeA,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,aAAa,GAAG,KAAK,MAAK,UAAS,MAAM,OAAO,UAAU,IAAI,EAAE,KAAK;GAC3E,MAAM,UAAU,UAAU,QAAQ,WAAW;GAE7C,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,OAAO;GACT,GAAG,CAAC,MAAM,CAAC;GAIX,MAAM,UAAA,GAAA,MAAA,OAAA,CAA2E,IAAI;GACrF,IAAI,eAAe,SAAS,OAAO,YAAY,QAAQ,OAAO,QAAQ,OAAO,WAAW,KACtF,OAAO,UAAU;IAAE,IAAI,WAAW;IAAI,KAAK,CAAC;IAAG,MAAM,iBAAiB;GAAE;GAE1E,MAAM,MAAM,OAAO;GACnB,MAAM,WAAW,YAAY;GAE7B,IAAI,SACF,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;IAAM,iBAAA;IAAc,eAAY;cAC7B,aAAa,QAAQ,eAAe,OACjC,OAEA,iBAAA,GAAA,kBAAA,IAAA,CAAC,mBAAD;KACc;KACZ,OAAO,UAAU,SAAS;KAC1B,OAAO,MAAM;KACb,GAAK,QAAQ,OAAO,CAAC,IAAI;MAAE,MAAM,IAAI;MAAM,KAAK,IAAI;KAAI;KACxD,GAAG,MAAM;eAET,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;MACY;MACE;MACZ,SAAS,SAAS;MACR;MACV,OAAO,MAAM;MACb,QAAQ,MAAM;MACd,QAAQ,MAAM;MACd,WAAW,MAAM;MACjB,UAAU,MAAM;MAChB,eAAe,MAAM;MACrB,gBAAgB,MAAM;MACtB,cAAc,MAAM;MACpB,cAAc,SAAS,UAAU,SAAS;MAC1C,GAAK,aAAa,KAAA,KAAa,QAAQ,OACnC,CAAC,IACD;OACE,gBAAgB,IAAY,OAAe,IAAI,IAAI,MAAM,IAAI,EAAE;OAC/D,sBAAsB,IAAI,IAAI,WAAW;OACzC,KACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAD;QACc;QACL;QACP,KAAK,MAAM;QACX,KAAK,IAAI;QACT,MAAM,IAAI;QACV,GAAG,MAAM;OACV,CAAA;MAEL;MACJ,GAAG,MAAM;KACV,CAAA;IACgB,CAAA;GAEnB,CAAA;GAGV,MAAM,UAAU,UAAU,WAAW;GACrC,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;IACE,MAAK;IACL,WAAWC,uBAAO;IAClB,OAAO;KACL,UAAU;KACV,OAAO,QAAQ;KACf,QAAQ,QAAQ;KAChB,QAAQ;IACV;IACA,SAAS,MAAM;IACf,eAAY;IACZ,iBAAc;cAEb,MAAM,EAAE,cAAc,EAAE,MAAM,UAAU,QAAQ,GAAG,CAAC;GAC/C,CAAA;EAEZ;;;;EClJA,MAAa,2BAA2B;;;;;;;;;;;;;;;;;;ECDxC,MAAM,WAAW;EACjB,MAAM,aAAa;EAuDnB,MAAM,mBAAmC,QAAQ,IAAI,SAAe,SAAS,WAAW;GACtF,MAAM,MAAM,SAAS,cAAc,QAAQ;GAC3C,IAAI,MAAM;GACV,IAAI,eAAe,QAAQ;GAC3B,IAAI,gBAAgB,uBAAO,IAAI,MAAM,4BAA4B,GAAG,CAAC;GACrE,SAAS,KAAK,YAAY,GAAG;EAC/B,CAAC;EAOD,IAAI;EACJ,IAAI;;;;;;EAOJ,SAAgB,iBAAiB,QAA4B,CAAC,GAAqB;GACjF,IAAI,OAAO,WAAW,eAAe,OAAO,qBAAqB,KAAA,GAAW,OAAO,QAAQ,QAAQ,IAAI;GACvG,IAAI,MAAM,WAAW,KAAA,GACnB,OAAO,MAAM,OAAO,QAAQ,CAAC,CAC1B,WAAW,OAAO,WAAW,eAAe,OAAO,qBAAqB,KAAA,CAAS,CAAC,CAClF,YAAY,KAAK;GAEtB,gBAAgB,gBAAgB,QAAQ,CAAC,CACtC,WAAW,OAAO,WAAW,eAAe,OAAO,qBAAqB,KAAA,CAAS,CAAC,CAClF,YAAY,KAAK;GACpB,OAAO;EACT;;EAGA,SAAgB,mBAAmB,QAA4B,CAAC,GAAsC;GACpG,IAAI,OAAO,WAAW,eAAe,OAAO,mBAAmB,KAAA,GAAW,OAAO,QAAQ,QAAQ,OAAO,cAAc;GACtH,IAAI,MAAM,WAAW,KAAA,GACnB,OAAO,MAAM,OAAO,UAAU,CAAC,CAC5B,WAAW,OAAO,WAAW,cAAc,OAAO,iBAAiB,KAAA,CAAS,CAAC,CAC7E,YAAY,KAAA,CAAS;GAE1B,kBAAkB,gBAAgB,UAAU,CAAC,CAC1C,WAAW,OAAO,WAAW,cAAc,OAAO,iBAAiB,KAAA,CAAS,CAAC,CAC7E,YAAY,KAAA,CAAS;GACxB,OAAO;EACT;;;;EC1DA,MAAM,YAAY;;;;;;;;EASlB,MAAM,kBAAkB,EAAE,KAAK,cAAc;;EAE7C,MAAM,kBAAkB,EAAE,UAAU,KAAK;;EAEzC,MAAM,2BAA2B,EAAE,YAAY,KAAK;;EAQpD,SAAS,sBAAsB,OAAe,QAA6C;GACzF,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,CAAC,OAAO,SAAS,MAAM,KAAK,SAAS,KAAK,UAAU,GAAG,OAAO,KAAA;GAC7F,OAAO;IACL,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC;IACpC,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,CAAC;GACxC;EACF;;EAGA,SAAS,YACP,KACA,OACA,YACA,QACM;GACN,MAAM,MAAM,KAAK,IACf,IAAI,SAAS,QAAQ,WAAW,OAChC,IAAI,SAAS,SAAS,WAAW,MACnC,IAAI;GACJ,MAAM,MAAM,IAAI,OAAO,OAAO,SAAS,EAAE;GACzC,MAAM,OAAO,IAAI,EAAG;GACpB,MAAM,SAAS,IACb,IAAI,SAAS,QAAQ,KAAK,OAAO,WAAW,KAAK,IACjD,IAAI,SAAS,SAAS,KAAK,OAAO,WAAW,KAAK,EACpD;EACF;EAEA,IAAI,mBAAmB;;EAGvB,SAAS,cAAc,QAA4B;GACjD,IAAI,kBAAkB;GACtB,mBAAmB;GACnB,OAAO,WAAW,IAAI,OAAO,YAAY;GACzC,OAAO,mBAAmB,EAAE,cAAc,GAAG,CAAC;EAChD;;EAQA,SAAS,qBAAqB,QAAkC;GAC9D,IAAI,OAAO,WAAW,YAAY,WAAW,MAAM,MAAM,IAAI,MAAM,gCAAgC;GACnG,MAAM,SAAS;GACf,IAAI,OAAO,OAAO,aAAa,YAAY,OAAO,aAAa,IAAI,MAAM,IAAI,MAAM,oCAAoC;GACvH,MAAM,UAAU,OAAO;GACvB,IAAI,OAAO,YAAY,YAAY,YAAY,QAAQ,OAAQ,QAAoC,SAAS,UAC1G,MAAM,IAAI,MAAM,wCAAwC;GAE1D,OAAO;EACT;;EAGA,MAAa,iBAA+C;GAC1D,IAAI;GACJ,YAAY;GACZ,gBAAgB;GAChB,MAAM,KAAyB,QAA+C;IAC5E,IAAI,WAAW;IACf,IAAI;IACJ,IAAI;IACJ,IAAI,gBAAgB;IACpB,IAAI;IACJ,IAAI;IACJ,IAAI,iBAAiB;IACrB,IAAI;IACJ,IAAI;;IAEJ,IAAI,aAAqB,OAAO,QAAQ;IACxC,IAAI,aAAa;IAEjB,MAAM,2BAAiC;KACrC,iBAAiB;KACjB,gBAAgB,WAAW;KAC3B,iBAAiB,KAAA;IACnB;IAEA,MAAM,kBAAkB,SAA0B,OAAe,WAAyB;KACxF,MAAM,OAAO,sBAAsB,OAAO,MAAM;KAChD,IAAI,YAAY,CAAC,kBAAkB,SAAS,KAAA,GAAW;KACvD,IAAI,QAAQ,SAAS,UAAU,KAAK,SAAS,QAAQ,SAAS,WAAW,KAAK,QAAQ;KACtF,QAAQ,SAAS,OAAO,KAAK,OAAO,KAAK,MAAM;KAC/C,IAAI,UAAU,KAAA,KAAa,oBAAoB,KAAA,GAC7C,YAAY,SAAS,OAAO,iBAAiB,MAAM;IAEvD;IAEA,MAAM,sBAAsB,YAAmC;KAC7D,IAAI,OAAO,mBAAmB,aAAa;KAC3C,iBAAiB;KACjB,iBAAiB,IAAI,gBAAgB,YAAY;MAC/C,MAAM,QAAQ,QAAQ,MAAK,cAAa,UAAU,WAAW,IAAI,SAAS;MAC1E,IAAI,UAAU,KAAA,GAAW;MACzB,eAAe,SAAS,MAAM,YAAY,OAAO,MAAM,YAAY,MAAM;KAC3E,CAAC;KACD,eAAe,QAAQ,IAAI,SAAS;KAEpC,eAAe,SAAS,IAAI,UAAU,aAAa,IAAI,UAAU,YAAY;IAC/E;;IAGA,MAAM,yBAA+B;KACnC,mBAAmB;KACnB,cAAc;KACd,cAAc,KAAA;KACd,MAAM,aAAa;KACnB,MAAM,eAAe;KACrB,MAAM,kBAAkB,eAAe,KAAA,KAAa;KACpD,MAAM,KAAA;KACN,QAAQ,KAAA;KACR,kBAAkB,KAAA;KAClB,gBAAgB;KAChB,IAAI;MACF,IAAI,iBAAiB,KAAA,KAAa,CAAC,iBAAiB,aAAa,QAAQ,eAAe;KAC1F,UAAU;MACR,YAAY,QAAQ,0BAA0B,eAAe;KAC/D;IACF;IAEA,MAAM,aAAa,UAAwB;KACzC,IAAI,UAAU,KAAA,GAAW;KACzB,MAAM,SAAS,MAAM,cAAc,SAAS,WAAW,CAAC;KACxD,MAAM,QAAQ,MAAM,QAAQ,OAAO,MAAM,IAAI,OAAO,MAAM,CAAE,SAAS;KACrE,IAAI,UAAU,GAAG;MAEf,IAAI,UAAU,OAAO,QAAQ,MAAM,UAAU,OAAO,QAAQ,IAAI;MAChE;KACF;KACA,MAAM,QAAQ,QAAQ,IAAI,KAAK,MAAM,KAAK,OAAO,IAAI,KAAK,IAAI;KAC9D,MAAW,OAAO,OAAO,KAAK;IAChC;IAEA,MAAM,cAAc,UAA+B;KACjD,aAAa,OAAO,QAAQ,UAAU,OAAO,QAAQ;KACrD,UAAU,UAAU;KACpB,MAAM,aAAa,OAAO,cAAc;KACxC,IAAI,eAAe,KAAA,KAAa,UAAU,KAAA,GAAW,MAAW,WAAW,UAAU;IACvF;IAEA,MAAM,OAAO,YAA2B;KACtC,IAAI,CAAC,MAAM,iBAAiB,GAAG;MAC7B,IAAI,CAAC,UAAU,gBAAgB,cAAc;MAC7C;KACF;KACA,MAAM,SAAS,MAAM,mBAAmB;KACxC,IAAI,WAAW,KAAA,GAAW;MACxB,IAAI,CAAC,UAAU,gBAAgB,gBAAgB;MAC/C;KACF;KACA,cAAc,MAAM;KACpB,MAAM,UAAU,IAAI,OAAO,YAAY;KACvC,MAAM,cAAc,sBAAsB,IAAI,UAAU,aAAa,IAAI,UAAU,YAAY,KAAK;MAClG,OAAO;MACP,QAAQ;KACV;KACA,IAAI;MACF,MAAM,QAAQ,KAAK;OACjB,OAAO,YAAY;OACnB,QAAQ,YAAY;OACpB,iBAAiB;OACjB,WAAW;OACX,aAAa;OACb,YAAY;MACd,CAAC;KACH,SAAS,OAAO;MAGd,IAAI;OAAE,QAAQ,QAAQ,0BAA0B,eAAe;MAAE,QAAQ,CAAC;MAC1E,MAAM;KACR;KACA,IAAI,UAAU;MACZ,QAAQ,QAAQ,0BAA0B,eAAe;MACzD;KACF;KACA,MAAM;KACN,QAAQ,OAAO,MAAM,UAAU;KAC/B,QAAQ,OAAO,MAAM,QAAQ;KAC7B,QAAQ,OAAO,MAAM,SAAS;KAC9B,IAAI,UAAU,YAAY,QAAQ,MAAM;KAGxC,mBAAmB,OAAO;KAC1B,MAAM,SAAS,MAAM,OAAO,YAAY,KAAK,OAAO,UAAU;MAC5D,YAAY;MACZ,aAAa;MACb,WAAW;MACX,gBAAgB;KAClB,CAAC;KACD,QAAQ;KACR,IAAI,UAAU;MACZ,iBAAiB;MACjB;KACF;KACA,kBAAkB;MAChB,OAAO,KAAK,IAAI,GAAG,OAAO,KAAK;MAC/B,QAAQ,KAAK,IAAI,GAAG,OAAO,MAAM;KACnC;KAGA,YAAY,SAAS,QAAQ,iBAAiB,MAAM;KACpD,QAAQ,MAAM,SAAS,MAAM;KAC7B,gBAAgB;KAChB,OAAO,UAAU,aAAa;KAE9B,OAAO,GAAG,sBAAsB;MAC9B,IAAI,YAAY;OACd,aAAa;OACb,UAAU,UAAU;MACtB;KACF,CAAC;KACD,WAAW,IAAI,MAAM,IAAI,CAAC;KAC1B,cAAc,IAAI,MAAM,UAAU,UAAU;IAC9C;IAEA,KAAU,CAAC,CAAC,YAAY;KACtB,IAAI;MACF,iBAAiB;KACnB,UAAU;MACR,IAAI,CAAC,UAAU,gBAAgB,aAAa;KAC9C;IACF,CAAC;IAED,OAAO;KACL,UAAU;MACR,IAAI,UAAU;MACd,WAAW;MACX,iBAAiB;KACnB;KACA,IAAI,GAAW,GAAW;MACxB,MAAM,UAAU;MAChB,IAAI,YAAY,YAAY,KAAA,GAAW;MACvC,MAAM,OAAO,QAAQ,QAAQ,GAAG,CAAC;MACjC,MAAM,UAAU,OAAO;MAEvB,IAAI,EADQ,YAAY,KAAA,IAAY,KAAK,SAAS,IAAI,KAAK,MAAK,SAAQ,QAAQ,SAAS,IAAI,CAAC,IACpF;MAEV,MAAM,SADS,QAAQ,cAAc,SAAS,WAAW,CAAC,EAAA,CACrC;MACrB,IAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;MACjD,aAAa;MACb,MAAM,QAAQ,MAAM,SAAS,IAAI,KAAK,MAAM,KAAK,OAAO,IAAI,MAAM,MAAM,IAAI;MAC5E,QAAa,OAAO,WAAW,KAAK;KACtC;KACA,QAAQ,UAA2C;MACjD,gBAAgB;KAClB;IACF;GACF;EACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EC5PA,MAAM,cAAc;EAEpB,SAAS,SAAS,OAAkD;GAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;EAC5E;;EAGA,SAAS,uBAAuB,QAAoC;GAClE,IAAI,CAAC,SAAS,MAAM,KAAK,CAAC,SAAS,OAAO,MAAM,KAAK,CAAC,SAAS,OAAO,MAAM,GAC1E,MAAM,IAAI,MAAM,oDAAoD;GAEtE,MAAM,SAA8C,CAAC;GACrD,KAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,OAAO,MAAM,GAAG;IACvD,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,MAAM,QAAQ,IAAI,MAAM,KAAK,IAAI,OAAO,WAAW,KACrE,IAAI,OAAO,MAAK,MAAK,OAAO,MAAM,YAAY,MAAM,EAAE,KACtD,CAAC,MAAM,QAAQ,IAAI,SAAS,KAAK,IAAI,UAAU,WAAW,IAAI,OAAO,UACrE,IAAI,UAAU,MAAK,MAAK,OAAO,MAAM,YAAY,EAAE,IAAI,EAAE,GAC5D,MAAM,IAAI,MAAM,oBAAoB,KAAK,UAAU,IAAI,IAAI,qCAAqC;IAElG,OAAO,QAAQ;KACb,QAAQ,IAAI;KACZ,WAAW,IAAI;KACf,MAAM,IAAI,SAAS;KACnB,GAAI,OAAO,IAAI,aAAa,WAAW,EAAE,UAAU,IAAI,SAAS,IAAI,CAAC;IACvE;GACF;GACA,MAAM,SAAS,OAAO;GACtB,IAAI,OAAO,OAAO,SAAS,YAAY,OAAO,OAAO,UAAU,KAAA,GAC7D,MAAM,IAAI,MAAM,kDAAkD;GAKpE,IAAI;GACJ,IAAI,MAAM,QAAQ,OAAO,KAAK,GAAG;IAC/B,MAAM,WAAiC,CAAC;IACxC,KAAK,MAAM,QAAQ,OAAO,OAAoB;KAC5C,IAAI,CAAC,SAAS,IAAI,KAAK,OAAO,KAAK,OAAO,YAAY,OAAO,KAAK,UAAU,YACvE,OAAO,KAAK,cAAc,YAAY,KAAK,cAAc,IAAI;KAClE,MAAM,SAAS,OAAO,KAAK;KAC3B,IAAI,WAAW,KAAA,KAAa,CAAC,OAAO,MAAM;KAC1C,IAAI;KACJ,IAAI,MAAM,QAAQ,KAAK,YAAY,GAAG;MACpC,MAAM,OAAwC,CAAC;MAC/C,KAAK,MAAM,UAAU,KAAK,cAA2B;OACnD,IAAI,CAAC,SAAS,MAAM,KAAK,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,MACzE,OAAO,OAAO,gBAAgB,YAAY,EAAE,OAAO,cAAc,MAAM,OAAO,cAAc,GAAG;OACpG,IAAI,OAAO,OAAO,WAAW,KAAA,GAAW;OACxC,KAAK,KAAK;QACR,OAAO,OAAO;QACd,aAAa,OAAO;QACpB,GAAI,MAAM,QAAQ,OAAO,OAAO,IAAI,EAAE,SAAS,OAAO,QAAoB,IAAI,CAAC;OACjF,CAAC;MACH;MACA,IAAI,KAAK,SAAS,GAAG,eAAe;KACtC;KACA,IAAI;KACJ,IAAI,SAAS,KAAK,cAAc,GAAG;MACjC,MAAM,OAA+B,CAAC;MACtC,KAAK,MAAM,CAAC,OAAO,cAAc,OAAO,QAAQ,KAAK,cAAc,GAAG;OACpE,IAAI,OAAO,cAAc,YAAY,cAAc,MAAM,OAAO,eAAe,KAAA,GAAW;OAC1F,KAAK,SAAS;MAChB;MACA,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,SAAS,GAAG,iBAAiB;KACrD;KACA,SAAS,KAAK;MACZ,IAAI,KAAK;MACT,OAAO,KAAK;MACZ,WAAW,KAAK;MAChB,GAAI,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa;MACrD,GAAI,mBAAmB,KAAA,IAAY,CAAC,IAAI,EAAE,eAAe;KAC3D,CAAC;IACH;IACA,IAAI,SAAS,SAAS,GAAG,QAAQ;GACnC;GACA,OAAO;IAAE;IAAgB;IAAuC,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;GAAG;EAC5G;EAQA,MAAa,mBAAmD;GAC9D,IAAI;GACJ,YAAY;GACZ,gBAAgB;GAEhB,MAAM,KAAyB,QAAmD;IAChF,MAAM,gBAAgB,OAAO,WAAW,eACnC,OAAO,OAAO,eAAe,cAC7B,OAAO,WAAW,kCAAkC,CAAC,CAAC;IAI3D,IAAI,SAAmC;IACvC,IAAI,YAA6C;IACjD,IAAI,MAA+B;IACnC,IAAI;KACF,IAAI,OAAO,sBAAsB,cAAc,OAAO,UAAU,YAAY;MAC1E,MAAM,QAAQ,SAAS,cAAc,QAAQ;MAC7C,MAAM,MAAM,MAAM,WAAW,IAAI;MACjC,IAAI,QAAQ,MAAM;OAChB,SAAS;OACT,YAAY;MACd;KACF;IACF,QAAQ;KACN,SAAS;KACT,YAAY;IACd;IAEA,IAAI,WAAW,QAAQ,cAAc,MAAM;KACzC,OAAO,QAAQ,iBAAiB,IAAI;KACpC,OAAO,YAAY;KACnB,OAAO,MAAM,QAAQ;KACrB,OAAO,MAAM,SAAS;KACtB,OAAO,MAAM,YAAY;KACzB,OAAO,MAAM,gBAAgB;KAC7B,IAAI,UAAU,YAAY,MAAM;IAClC,OAAO;KACL,MAAM,SAAS,cAAc,KAAK;KAClC,IAAI,QAAQ,iBAAiB,IAAI;KACjC,IAAI,MAAM;KACV,IAAI,YAAY;KAChB,IAAI,MAAM,QAAQ;KAClB,IAAI,MAAM,SAAS;KACnB,IAAI,MAAM,YAAY;KACtB,IAAI,MAAM,gBAAgB;KAC1B,IAAI,UAAU,YAAY,GAAG;IAC/B;IAKA,MAAM,2BAAW,IAAI,IAA+C;IACpE,MAAM,aAA8B,CAAC;IAMrC,MAAM,mBAAmB;IACzB,MAAM,aAA0D,CAAC;IACjE,IAAI,eAAe;IAEnB,MAAM,cAAc,OAAO,QAAmD;KAC5E,IAAI;MACF,MAAM,WAAW,MAAM,MAAM,GAAG;MAChC,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,UAAU,SAAS,MAAM;MAC3D,MAAM,SAAS,MAAM,kBAAkB,MAAM,SAAS,KAAK,CAAC;MAC5D,OAAO;OAAE,QAAQ;OAAQ,OAAO,OAAO;OAAO,QAAQ,OAAO;MAAO;KACtE,QAAQ;MAEN,OAAO,MAAM,IAAI,SAAmC,YAAY;OAC9D,IAAI;QACF,MAAM,MAAM,IAAI,MAAM;QACtB,IAAI,eAAqB;SACvB,QAAQ,IAAI,eAAe,IAAI;UAAE,QAAQ;UAAK,OAAO,IAAI;UAAc,QAAQ,IAAI;SAAc,IAAI,KAAA,CAAS;QAChH;QACA,IAAI,gBAAsB,QAAQ,KAAA,CAAS;QAC3C,IAAI,MAAM;OACZ,QAAQ;QACN,QAAQ,KAAA,CAAS;OACnB;MACF,CAAC;KACH;IACF;IAEA,MAAM,mBAAyB;KAC7B,OAAO,eAAe,oBAAoB,WAAW,SAAS,GAAG;MAC/D,MAAM,SAAS,WAAW,MAAM;MAChC,gBAAgB;MAChB,OAAO,QAAQ;KACjB;IACF;IAEA,MAAM,aAAa,KAAa,OAAO,UAA6C;KAIlF,IAAI,MAAM;MACR,MAAM,QAAQ,WAAW,WAAW,WAAW,OAAO,QAAQ,GAAG;MACjE,IAAI,QAAQ,GAAG,WAAW,QAAQ,WAAW,OAAO,OAAO,CAAC,CAAC,CAAC,EAAG;KACnE;KACA,MAAM,SAAS,SAAS,IAAI,GAAG;KAC/B,IAAI,WAAW,KAAA,GAAW,OAAO;KACjC,IAAI;KAEJ,MAAM,MAAyC,IAD9B,SAAe,YAAY;MAAE,UAAU;KAAQ,CACd,CAAC,CAAC,WAAY,WAAW,KAAA,IAAY,YAAY,GAAG,CAAE;KACxG,IAAI,MACD,UAAU;MAAE,IAAI,UAAU,KAAA,GAAW,SAAS,OAAO,GAAG;KAAE,SACrD,SAAS,OAAO,GAAG,CAC3B;KACA,IAAS,cAAc;MACrB,gBAAgB;MAChB,WAAW;KACb,CAAC;KACD,SAAS,IAAI,KAAK,GAAG;KACrB,WAAW,KAAK,IAAI,WAAW,KAAA,SAAiB,KAAA,CAAS,CAAC;KAC1D,MAAM,QAAQ;MAAE;MAAK;KAAQ;KAC7B,IAAI,MAAM,WAAW,QAAQ,KAAK;UAC7B,WAAW,KAAK,KAAK;KAC1B,WAAW;KACX,OAAO;IACT;;;;;;;;;;IAWA,MAAM,iBAAiB;IAEvB,MAAM,iBAAiB,SAAiB,UAAwB;KAC9D,MAAM,MAAM,OAAO,OAAO;KAC1B,IAAI,QAAQ,KAAA,GAAW;KACvB,MAAM,MAAM,KAAK,IAAI,IAAI,OAAO,QAAQ,QAAQ,IAAI,cAAc;KAClE,KAAK,IAAI,QAAQ,QAAQ,GAAG,QAAQ,KAAK,SAAS,GAAG;MACnD,MAAM,MAAM,IAAI,OAAO;MACvB,IAAI,QAAQ,KAAA,GAAW,UAAe,GAAG;KAC3C;IACF;IAEA,IAAI,WAAW;IACf,IAAI;IACJ,IAAI;IACJ,IAAI,QAAgB,OAAO,OAAO;IAClC,IAAI,aAAa;IACjB,IAAI,cAAc,KAAK,IAAI;IAC3B,IAAI;IAIJ,IAAI,WAAmB,OAAO,OAAO;IACrC,IAAI,YAAY;IAChB,IAAI;IAEJ,MAAM,iBAAiB,UAAiC;KACtD,MAAM,SAAS,OAAO,OAAO;KAC7B,IAAI,WAAW,KAAA,GAAW,OAAO;KACjC,MAAM,SAAS,WAAW,OAAO,OAAO,OAAO,WAAW;KAC1D,OAAO,OAAO,OAAO,YAAY,KAAA,IAAY,SAAS;IACxD;;IAGA,MAAM,eAAe,QAAsB;KACzC,IAAI,cAAc,QAAQ,WAAW,MAAM;KAC3C,MAAM,UAAU,EAAE;KAClB,UAAe,KAAK,IAAI,CAAC,CAAC,MAAM,UAAU;MACxC,IAAI,YAAY,UAAU,KAAA,KAAa,YAAY,WAAW;MAC9D,IAAI,iBAAiB,KAAK;MAC1B,eAAe;MAEf,IAAI,OAAO,UAAU,MAAM,SAAS,OAAO,WAAW,MAAM,QAAQ;OAClE,OAAO,QAAQ,MAAM;OACrB,OAAO,SAAS,MAAM;MACxB,OACE,UAAU,UAAU,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;MAEvD,UAAU,UAAU,MAAM,QAA6B,GAAG,CAAC;KAC7D,CAAC,CAAC,CAAC,YAAY,CAAwB,CAAC;IAC1C;IAEA,MAAM,QAAQ,SAAiB,UAAwB;KAErD,MAAM,MADM,OAAO,OAAO,QACX,EAAE,OAAO;KACxB,IAAI,QAAQ,KAAA,GAAW;KACvB,cAAc,SAAS,KAAK;KAC5B,IAAI,QAAQ,MAAM;MAChB,IAAI,IAAI,aAAa,KAAK,MAAM,KAAK,IAAI,MAAM;MAC/C;KACF;KACA,YAAY,GAAG;IACjB;IAEA,MAAM,YAAY,OAAqB;KACrC,IAAI,UAAU;KACd,QAAQ,WAAW,MAAM,EAAE;IAC7B;IAEA,SAAS,OAAa;KACpB,IAAI,UAAU;KACd,MAAM,MAAM,OAAO,OAAO;KAC1B,IAAI,QAAQ,KAAA,KAAa,IAAI,OAAO,WAAW,GAAG;KAClD,cAAc,KAAK,IAAI;KACvB,MAAM,OAAO,aAAa;KAC1B,IAAI,OAAO,IAAI,OAAO,QAAQ;MAC5B,aAAa;MACb,KAAK,OAAO,UAAU;MACtB,SAAS,IAAI,UAAU,eAAe,GAAG;MACzC;KACF;KACA,IAAI,IAAI,MAAM;MACZ,aAAa;MACb,KAAK,OAAO,UAAU;MACtB,SAAS,IAAI,UAAU,eAAe,GAAG;MACzC;KACF;KAMA,MAAM,SAAS,IAAI,aAAa,KAAA,KAAa,OAAO,OAAO,IAAI,cAAc,KAAA,IACxE,IAAI,aAAa,OAAO,OAAO,OAAO,WAAW,IAAI,WACtD;KACJ,IAAI,WAAW,cAAc,IAAI,MAAM,IAAI,CAAC,GAAG,WAAW,KAAA;KAC1D,KAAK,MAAM;IACb;IAEA,SAAS,KAAK,SAAuB;KACnC,IAAI,UAAU;KACd,IAAI,OAAO,OAAO,aAAa,KAAA,GAAW,UAAU;KACpD,IAAI,UAAU,KAAA,GAAW,aAAa,KAAK;KAC3C,QAAQ;KACR,aAAa;KACb,cAAc,KAAK,IAAI;KACvB,KAAK,OAAO,UAAU;KACtB,IAAI,eAAe;KACnB,MAAM,MAAM,OAAO,OAAO;KAC1B,SAAS,IAAI,UAAU,MAAM,GAAG;IAClC;IAEA,MAAM,cAAc,IAAI,MAAM,WAAW,UAAU;KACjD,IAAI,aAAa,KAAA,GAAW;KAC5B,MAAM,SAAS,cAAc,KAAK;KAClC,IAAI,WAAW,OAAO,KAAK,MAAM;IACnC,CAAC;IAID,IAAI,CAAC,eACH,WAAW,kBAAkB;KAC3B,IAAI,UAAU;KACd,MAAM,MAAM,OAAO,OAAO;KAC1B,IAAI,QAAQ,KAAA,KAAa,CAAC,IAAI,MAAM;KACpC,MAAM,YAAY,IAAI,UAAU,eAAe,OAAO;KACtD,IAAI,KAAK,IAAI,IAAI,cAAc,UAAU,KAAK;IAChD,GAAG,WAAW;IAGhB,KAAK,KAAK;IAEV,IAAI,eAAe;IACnB,MAAM,gBAAsB;KAC1B,IAAI,cAAc;KAClB,eAAe;KACf,WAAW;KACX,YAAY;KACZ,IAAI,UAAU,KAAA,GAAW,aAAa,KAAK;KAC3C,IAAI,aAAa,KAAA,GAAW,cAAc,QAAQ;KAIlD,KAAK,MAAM,UAAU,WAAW,OAAO,CAAC,GAAG,OAAO,QAAQ;KAC1D,QAAa,WAAW,UAAU,CAAC,CAAC,WAAW;MAC7C,KAAK,MAAM,OAAO,SAAS,OAAO,GAChC,IAAS,MAAM,UAAU;OACvB,IAAI;QACF,MAAM,cAAc,OAAO,OAAA,EAA+C;QAC1E,IAAI,OAAO,eAAe,cAAc,UAAU,KAAA,GAAW,WAAW,KAAK,MAAM,MAAM;OAC3F,QAAQ,CAAyB;MACnC,CAAC,CAAC,CAAC,YAAY,CAA6B,CAAC;MAE/C,SAAS,MAAM;KACjB,CAAC;KACD,QAAQ,OAAO;KACf,KAAK,OAAO;IACd;IACA,IAAI,UAAU,OAAO;IAErB,OAAO;KACL;KACA,SAAS,MAAgC;MACvC,IAAI,UAAU;MACd,IAAI,SAAS,KAAA,GAAW;OACtB,WAAW,KAAA;OACX,MAAM,SAAS,cAAc,IAAI,MAAM,IAAI,CAAC;OAC5C,IAAI,WAAW,OAAO,KAAK,MAAM;OACjC;MACF;MACA,IAAI,OAAO,OAAO,UAAU,KAAA,GAAW;MACvC,WAAW;MACX,IAAI,SAAS,OAAO,KAAK,IAAI;KAC/B;KACA,aAAa,MAAgC;MAC3C,IAAI,UAAU;MACd,IAAI,SAAS,KAAA,KAAc,OAAO,OAAO,UAAU,KAAA,KAAa,OAAO,OAAO,KAAK,CAAE,MACnF,WAAW,QAAQ,OAAO,OAAO;MAInC,IAAI,aAAa,KAAA,GAAW;OAC1B,MAAM,SAAS,cAAc,IAAI,MAAM,IAAI,CAAC;OAC5C,IAAI,WAAW,OAAO,KAAK,MAAM;MACnC;KACF;KACA,eAAuB;MACrB,OAAO;KACT;IACF;GACF;EACF;;;;;;;;;;;;;;;;;ECvdA,MAAM,OAAO,OAAO,IAAI,4BAA4B;;;;;;;;EAapD,SAAgB,sBAAsB,UAAkC;GACtE,MAAM,OAAO;GACb,KAAK,QAAQ;GACb,aAAa;IACX,IAAI,KAAK,UAAU,UAAU,KAAK,QAAQ,KAAA;GAC5C;EACF;;;;;;EAOA,SAAgB,wBAA8B;GAC5C,MAAM,OAAO;GACb,MAAM,WAAW,KAAK;GACtB,KAAK,QAAQ,KAAA;GACb,WAAW;EACb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EC6BA,SAAgB,mBAAiD,OAAsC;GACrG,MAAM,CAAC,MAAM,YAAA,GAAA,MAAA,SAAA,CAAoB,MAAM,eAAe,IAAI;GAC1D,MAAM,EAAE,OAAO,eAAe;GAC9B,IAAI,CAAC,MAAM,WAAW,OAAO;GAC7B,MAAM,QAAQ,MAAM,EAAE,MAAM,QAAQ;GACpC,MAAM,cAAc,MAAM,EAAE,MAAM,cAAc;GAChD,MAAM,UAAU,CAAC,MAAM,SAAS,MAAM,WAAW,MAAM;GACvD,MAAM,WAAW,eAAe,QAAQ;GACxC,MAAM,YAAY,WAAW,GAAGC,iCAAI,SAAS,GAAGA,iCAAI,SAASA,iCAAI;GAGjE,MAAM,SAAS,eAAe,OAE1B,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAWA,iCAAI;cAApB,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;KAAM,WAAWA,iCAAI;eAArB,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAWA,iCAAI;MAAa;gBAAQ;KAAY,CAAA,GACtD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAWA,iCAAI;MAAa,OAAO;gBAAc,MAAM,mBAAmB;KAAkB,CAAA,CAC9F;QACL,MAAM,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAAM,WAAWA,iCAAI;KAAS,OAAO,MAAM,EAAE,kBAAkB;eAAI,MAAM,EAAE,kBAAkB;IAAQ,CAAA,IAAI,IACrH;QAGL,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;IACE,MAAK;IACL,WAAWA,iCAAI;IACf,iBAAe;IACf,cAAY,GAAG,MAAM,EAAE,OAAO,sBAAsB,iBAAiB,EAAE,IAAI;IAC3E,eAAe;KAAE,QAAQ,CAAC,IAAI;IAAE;cALlC;KAOE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;MAAM,WAAWA,iCAAI;gBAArB,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAAM,WAAWA,iCAAI;OAAa;iBAAQ;MAAY,CAAA,GACtD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAAM,WAAWA,iCAAI;OAAa,OAAO;iBAAc,MAAM,mBAAmB;MAAkB,CAAA,CAC9F;;KACL,MAAM,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAWA,iCAAI;MAAS,OAAO,MAAM,EAAE,kBAAkB;gBAAI,MAAM,EAAE,kBAAkB;KAAQ,CAAA,IAAI;KACxH,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MACE,OAAM;MACN,QAAO;MACP,SAAQ;MACR,MAAK;MACL,OAAM;MACN,WAAW,OAAO,GAAGA,iCAAI,QAAQ,GAAGA,iCAAI,gBAAgBA,iCAAI;gBAE5D,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OACE,GAAE;OACF,MAAK;MACN,CAAA;KACE,CAAA;IACC;;GAMZ,IAAI,CAAC,MAAM,SACT,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,MAAD;IAAI,WAAW;cAAf,CACG,QACA,WAEG,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KAAK,WAAWA,iCAAI;eAClB,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;MAAG,WAAWA,iCAAI;MAAY,MAAK;gBAAU,MAAM,EAAE,qBAAqB;KAAK,CAAA;IAC5E,CAAA,IAEL,IACF;;GAGR,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,MAAD;IAAI,WAAW;cAAf,CACG,QACA,WAEG,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;MACN,MAAM,eAAe,OAClB,OAEJ,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAWA,iCAAI;iBAApB;QACG,MAAM,SAEH,iBAAA,GAAA,kBAAA,KAAA,CAAC,KAAD;SAAG,WAAWA,iCAAI;SAAQ,MAAK;mBAA/B,CACG,MAAM,EAAE,qBAAqB,GAAG,MAAM,eAAe,QAAQ,MAAM,eAAe,EAClF;aAEH;QACJ,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;;KAEF;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;EAEA,MAAM,wCAAwB,IAAI,IAAI,CAAC,iBAAiB,qBAAqB,CAAC;EAE9E,SAAS,eAAwB;GAG/B,OAFoB,OAAO,KAAK,SAAS,KAAK,OACnB,CAAC,CAAC,MAAK,QAAO,IAAI,WAAW,KAAK,KAAK,CAAC,sBAAsB,IAAI,GAAG,CAClF;EAChB;EAOA,MAAM,kBAAkB;;;;;;;;;;EAWxB,SAAgB,YAAY,OAOzB;GACD,MAAM,EAAE,IAAI,SAAS,UAAU;GAC/B,MAAM,CAAC,MAAM,YAAA,GAAA,MAAA,SAAA,CAAoB,KAAK;GACtC,MAAM,CAAC,SAAS,eAAA,GAAA,MAAA,SAAA,CAAuB,KAAK;GAC5C,MAAM,CAAC,OAAO,aAAA,GAAA,MAAA,SAAA,CAAyC,SAAS;GAChE,MAAM,CAAC,aAAa,mBAAA,GAAA,MAAA,SAAA,CAA2B,CAAC;GAChD,MAAM,cAAA,GAAA,MAAA,OAAA,CAA+D,KAAA,CAAS;GAC9E,MAAM,WAAA,GAAA,MAAA,OAAA,CAAwC,IAAI;GAClD,MAAM,YAAA,GAAA,MAAA,OAAA,CAAyC,IAAI;GAEnD,MAAM,qBAAqB;IACzB,MAAM,QAAQ,QAAQ,WAAU,WAAU,OAAO,UAAU,KAAK;IAChE,OAAO,SAAS,IAAI,QAAQ;GAC9B;GAEA,MAAM,SAAA,GAAA,MAAA,YAAA,OAA0B;IAC9B,IAAI,WAAW,YAAY,KAAA,GAAW,aAAa,WAAW,OAAO;IACrE,WAAW,IAAI;IACf,WAAW,UAAU,iBAAiB;KACpC,WAAW,KAAK;KAChB,QAAQ,KAAK;IACf,GAAG,eAAe;GACpB,GAAG,CAAC,CAAC;GAEL,MAAM,kBAAkB;IACtB,IAAI,WAAW,YAAY,KAAA,GAAW,aAAa,WAAW,OAAO;IACrE,eAAe,aAAa,CAAC;IAC7B,SAAS,SAAS;IAClB,WAAW,KAAK;IAChB,QAAQ,IAAI;GACd;GAEA,MAAM,UAAU,UAAkB;IAChC,MAAM,SAAS,QAAQ;IACvB,IAAI,QAAQ,MAAM,OAAO,OAAO,KAAK;IACrC,MAAM;GACR;GAEA,MAAM,uBAAuB;IAC3B,IAAI,MAAM,UAAU;IACpB,IAAI,QAAQ,CAAC,SAAS,MAAM;SACvB,UAAU;GACjB;GAEA,MAAM,aAAa,UAA4C;IAC7D,IAAI,MAAM,UAAU;IACpB,MAAM,QAAQ,QAAQ;IACtB,QAAQ,MAAM,KAAd;KACE,KAAK;KACL,KAAK;KACL,KAAK;KACL,KAAK;MACH,MAAM,eAAe;MACrB,IAAI,CAAC,MACH,UAAU;WACL,IAAI,CAAC,SACV,IAAI,MAAM,QAAQ,aAAa,gBAAe,WAAU,QAAQ,KAAK,KAAK;WACrE,IAAI,MAAM,QAAQ,WAAW,gBAAe,WAAU,QAAQ,IAAI,SAAS,KAAK;WAChF,OAAO,WAAW;MAEzB;KACF,KAAK;MACH,IAAI,MAAM;OACR,MAAM,eAAe;OACrB,MAAM,gBAAgB;OACtB,MAAM;MACR;MACA;KACF,KAAK;MACH,IAAI,MAAM,MAAM;MAChB;IACJ;GACF;GAEA,CAAA,GAAA,MAAA,UAAA,aAAsB;IACpB,IAAI,WAAW,YAAY,KAAA,GAAW,aAAa,WAAW,OAAO;GACvE,GAAG,CAAC,CAAC;GACL,CAAA,GAAA,MAAA,gBAAA,OAAsB;IACpB,IAAI,QAAQ,CAAC,WAAW,UAAU,WAAW;KAC3C,SAAc,SAAS;KACvB,SAAS,MAAM;IACjB;GACF,GAAG;IAAC;IAAM;IAAS;GAAK,CAAC;GAEzB,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,CAAC,MAAM;IACX,MAAM,iBAAiB,UAAwB;KAC7C,MAAM,SAAS,MAAM;KACrB,IAAI,kBAAkB,QAAQ,CAAC,QAAQ,SAAS,SAAS,MAAM,GAAG,MAAM;IAC1E;IACA,SAAS,iBAAiB,eAAe,aAAa;IACtD,aAAa,SAAS,oBAAoB,eAAe,aAAa;GACxE,GAAG,CAAC,MAAM,KAAK,CAAC;GAEhB,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,MAAM,YAAY,MAAM,MAAM;GACpC,GAAG;IAAC,MAAM;IAAU;IAAM;GAAK,CAAC;GAGhC,IAAI,aAAa,GACf,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;IACM;IACJ,WAAWA,iCAAI;IACR;IACP,UAAU,MAAM;IAChB,WAAW,UAAU;KAAE,MAAM,OAAO,MAAM,OAAO,KAAK;IAAE;cAEvD,QAAQ,KAAI,WACX,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;KAA2B,OAAO,OAAO;eAAQ,OAAO;IAAc,GAAzD,OAAO,KAAkD,CACvE;GACK,CAAA;GAIZ,MAAM,QAAQ,QAAQ,MAAK,WAAU,OAAO,UAAU,KAAK,CAAC,EAAE,SAAS;GACvE,MAAM,aAAa,UACf,GAAGA,iCAAI,YAAY,GAAGA,iCAAI,qBAC1B,UAAU,SACR,GAAGA,iCAAI,YAAY,GAAGA,iCAAI,oBAC1BA,iCAAI;GACV,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAWA,iCAAI;IAAY,KAAK;cAArC,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;KACE,MAAK;KACD;KACJ,WAAW,GAAGA,iCAAI,OAAO,GAAGA,iCAAI;KAChC,UAAU,MAAM;KAChB,iBAAc;KACd,iBAAe;KACf,yBAAuB,OAAO,GAAG,GAAG,IAAI,gBAAgB,KAAA;KACxD,gBAAc,MAAM,WAAW,KAAA;KAC/B,SAAS;KACE;eAVb,CAYE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAWA,iCAAI;gBAAc;KAAY,CAAA,GAC/C,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MACE,OAAM;MACN,QAAO;MACP,SAAQ;MACR,MAAK;MACL,OAAM;MACN,WAAW,OAAO,GAAGA,iCAAI,cAAc,GAAGA,iCAAI,sBAAsBA,iCAAI;MACxE,eAAY;gBAEZ,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OACE,GAAE;OACF,MAAK;MACN,CAAA;KACE,CAAA,CACC;QACP,OAEG,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KAAK,WAAW;KAAY,MAAK;KAAU,KAAK;eAC7C,QAAQ,KAAK,QAAQ,UACpB,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAEE,IAAI,GAAG,GAAG,IAAI;MACd,MAAK;MACL,iBAAe,OAAO,UAAU;MAChC,WAAW,GAAGA,iCAAI,eAAe,OAAO,UAAU,QAAQ,IAAIA,iCAAI,yBAAyB,KAAK,UAAU,eAAe,CAAC,UAAU,IAAIA,iCAAI,uBAAuB;MACnK,eAAe;OAAE,OAAO,KAAK;MAAE;gBAE9B,OAAO;KACL,GARE,OAAO,KAQT,CACN;IACE,CAAA,IAEL,IACD;;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,IAAA,CAAC,aAAD;MACE,IAAI,MAAM;MACV,SAAS;OACP;QAAE,OAAO;QAAI,OAAO,MAAM;OAAa;OACvC;QAAE,OAAO;QAAQ,OAAO,MAAM;OAAQ;OACtC;QAAE,OAAO;QAAS,OAAO,MAAM;OAAS;MAC1C;MACA,OAAO,MAAM;MACb,UAAU,MAAM;MAChB,SAAS,MAAM;MACf,QAAQ,MAAM;KACf,CAAA;KACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;MAAG,WAAWA,iCAAI;gBAAO,MAAM;KAAQ,CAAA;IACpC;;EAET;;EAGA,SAAgB,YAAY,OAKzB;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,aAAD;MACE,IAAI,MAAM;MACV,SAAS,CAAC;OAAE,OAAO;OAAI,OAAO,MAAM;MAAa,GAAG,GAAG,MAAM,OAAO;MACpE,OAAO,MAAM;MACb,UAAU,MAAM;MAChB,SAAS,MAAM;MACf,QAAQ,MAAM;KACf,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;;;;EC9ZA,SAAgB,YAAY,OAAe,cAAiC,CAAC,GAAc;GACzF,MAAM,EAAE,UAAU,OAAO,QAAQ;GACjC,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,IAAI,CAAC,OAAO,SAAS,MAAM,GAAG,OAAO,KAAA;KACrC,IAAI,WAAW,CAAC,OAAO,UAAU,MAAM,GAAG,OAAO,KAAA;KACjD,IAAI,QAAQ,KAAA,KAAa,SAAS,KAAK,OAAO,KAAA;KAC9C,OAAO;MAAE,MAAM;MAAO,OAAO;KAAO;IACtC;GACF;EACF;;EAyBA,SAAgB,aAAa,OAA0B;GACrD,OAAO;IACL;IACA,SAAQ,UAAS,OAAO,UAAU,YAAY,OAAO,KAAK,IAAI;IAC9D,QAAQ,SAAS;KACf,MAAM,UAAU,KAAK,KAAK;KAC1B,IAAI,YAAY,IAAI,OAAO,EAAE,MAAM,QAAQ;KAC3C,IAAI,YAAY,QAAQ,OAAO;MAAE,MAAM;MAAO,OAAO;KAAK;KAC1D,IAAI,YAAY,SAAS,OAAO;MAAE,MAAM;MAAO,OAAO;KAAM;IAE9D;GACF;EACF;;EAGA,SAAgB,YAAY,OAAe,SAAuC;GAChF,OAAO;IACL;IACA,SAAQ,UAAS,OAAO,UAAU,YAAY,QAAQ,SAAS,KAAK,IAAI,QAAQ;IAChF,QAAQ,SAAS;KACf,IAAI,SAAS,IAAI,OAAO,EAAE,MAAM,QAAQ;KACxC,OAAO,QAAQ,SAAS,IAAI,IAAI;MAAE,MAAM;MAAO,OAAO;KAAK,IAAI,KAAA;IACjE;GACF;EACF;;;;;;;;;EAUA,IAAa,WAAb,MAAyB;GAaJ;GAZnB;GACA,yBAA0B,IAAI,IAAwB;GACtD,4BAA6B,IAAI,IAAgB;;GAEjD;GACA,WAAmB;GACnB,SAAiB;GACjB,SAAiB;GACjB;;GAGA,YACE,OACA,OACA;IAFiB,KAAA,QAAA;IAGjB,KAAK,QAAQ,IAAI,IAAI,MAAM,KAAI,SAAQ,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC;IAC1D,KAAK,eAAe,MAAM,gBAAgB;KAAE,KAAK,QAAQ;IAAE,CAAC;GAC9D;;;;;GAMA,UAAgB;IACd,IAAI,KAAK,UAAU;IACnB,KAAK,WAAW;IAChB,KAAK,aAAa;IAClB,KAAK,UAAU,MAAM;GACvB;;GAGA,KAAQ,SAAoC;IAC1C,MAAM,QAAQ,oBAAoB,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,SAAS,SAAS,WAAW;KAC7B,UAAU,SAAS;KACnB,OAAO,KAAK,SAAS;KACrB,SAAS,KAAK,MAAK,SAAQ,KAAK,UAAU,KAAA,CAAS;KACnD,QAAQ,KAAK;KACb,QAAQ,KAAK;KACb,GAAG,KAAK,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc,KAAK,aAAa;IAC9E;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,eAAe,KAAA;MACpB,KAAK,QAAQ;KACf;IACF;GACF;;;;;;;;;;;;;;;;;GAkBA,MAAM,OAAsB;IAC1B,MAAM,OAAO,KAAK,KAAK;IACvB,MAAM,QAAQ,KAAK,QAAQ,SAA0D,KAAK,UAAU,KAAA,CAAS;IAC7G,IAAI,KAAK,WAAW,KAAK,KAAK,UAAU,MAAM,WAAW,KAAK,QAAQ;IAItE,MAAM,0BAAU,IAAI,IAAoC;IACxD,KAAK,MAAM,QAAQ,MAAM,QAAQ,IAAI,KAAK,OAAO,KAAK,OAAO,IAAI,KAAK,KAAK,CAAC;IAC5E,KAAK,SAAS;IACd,KAAK,SAAS;IACd,KAAK,eAAe,KAAA;IACpB,KAAK,QAAQ;IAIb,MAAM,MAAgH,MAAM,KAAI,SAAQ,KAAK,GAAG,OAAO,QACnJ;KAAE,IAAI;KAAO,MAAM,CAAC,KAAK,KAAK;KAAG,OAAQ,KAAK,GAA4C;IAAM,IAChG;KAAE,IAAI;KAAS,MAAM,CAAC,KAAK,KAAK;IAAE,CAAC;IACvC,IAAI;IACJ,IAAI;KACF,MAAM,KAAK,MAAM,OAAO,GAAG;IAC7B,SAAS,OAAO;KACd,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IACtE;IAKA,MAAM,SAAS,iBAAiB,KAAA,KAAa,MAAM,OAAM,SAAQ,KAAK,MAAM,CAAC;IAC7E,KAAK,MAAM,CAAC,OAAO,WAAW,SAC5B,IAAI,UAAU,KAAK,OAAO,IAAI,KAAK,MAAM,QAAQ,KAAK,OAAO,OAAO,KAAK;IAE3E,KAAK,SAAS;IACd,KAAK,SAAS,CAAC;IAGf,KAAK,eAAe;IACpB,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,IAAI;QAAE;QAAO,IAAI;OAAQ;OAAG,aAAa,KAAK,YAAY,KAAK;MAAE,CAAC;MAC7G;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,IAAI;OAAE;OAAO,IAAI;MAAQ;MAAG,OAAO,KAAA;KAAU,CAAC;UACrF,IAAI,MAAM,SAAS,SAAS,KAAK,KAAK;MAAE;MAAO,IAAI;OAAE;OAAO,IAAI;MAAQ;MAAG,aAAa,KAAK,YAAY,KAAK;KAAE,CAAC;UACjH,KAAK,KAAK;MAAE;MAAO,IAAI;OAAE;OAAO,IAAI;OAAO,OAAO,MAAM;MAAM;MAAG,aAAa,KAAK,UAAU,OAAO,MAAM,KAAK;KAAE,CAAC;IACzH;IACA,OAAO;GACT;;;;;GAMA,UAAkB,OAAe,OAAyB;IAMxD,IAAI,KAAK,OAAO,KAAK,CAAC,CAAC,QAAQ,OAAO;IACtC,OAAO,KAAK,UAAU,CAAC,GAAG,WAAW;GACvC;;;;;GAMA,YAAoB,OAAwB;IAC1C,OAAO,CAAC,KAAK,OAAO,KAAK;GAC3B;GAEA,MAAc,OAAe,MAAwB;IACnD,KAAK,OAAO,IAAI,OAAO,IAAI;IAC3B,KAAK,SAAS;IACd,KAAK,eAAe,KAAA;IACpB,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;;;;;;;;;;;;;;;;;;;;ECtWA,eAAe,kBAAwC;GACrD,MAAM,WAAW,MAAM,MAAM,eAAe;GAC5C,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,sBAAsB,SAAS,MAAM;GACvE,OAAQ,MAAM,SAAS,KAAK;EAC9B;;EAGA,eAAe,sBAAoD;GACjE,MAAM,WAAW,MAAM,MAAM,sBAAsB;GACnD,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,6BAA6B,SAAS,MAAM;GAE9E,QAAO,MADa,SAAS,KAAK,EAAA,CACtB,eAAe,CAAC;EAC9B;;EAGA,IAAa,4BAAb,MAAuC;GACrC;GACA;GAIA,aAAwC,CAAC;GACzC,4BAA6B,IAAI,IAAoB;GACrD,cAA2C,CAAC;GAC5C,SAAiB;GACjB,WAAmB;GACnB,WAAmB;;GAEnB;;GAGA,YAAY,OAAmC;IAC7C,KAAK,OAAO,IAAI,SAAS,OAAO;KAC9B,aAAa,SAAS;KACtB,aAAa,mBAAmB;KAChC,aAAa,SAAS;KACtB,YAAY,MAAM;KAClB,YAAY,OAAO;KACnB,YAAY,QAAQ;KACpB,YAAY,SAAS,KAAK,UAAU;IACtC,CAAC;IACD,KAAK,QAAQ,KAAK,KAAK,WAAW,KAAK,WAAW,CAAC;IAKnD,KAAK,eAAe,OAAO,iBAAiB;KAC1C,KAAK,eAAe,KAAA;KACpB,IAAI,KAAK,UAAU;KACnB,KAAU,SAAS;KACnB,KAAU,gBAAgB;IAC5B,GAAG,CAAC;GACN;;GAGA,MAAc,kBAAiC;IAC7C,IAAI;KACF,KAAK,cAAc,MAAM,oBAAoB;KAC7C,IAAI,KAAK,UAAU;KACnB,KAAK,MAAM,IAAI,KAAK,WAAW,CAAC;IAClC,QAAQ;KACN,KAAK,cAAc,CAAC;IACtB;GACF;;GAGA,MAAc,WAA0B;IACtC,IAAI,KAAK,UAAU,KAAK,UAAU;IAClC,IAAI;KACF,MAAM,OAAO,MAAM,gBAAgB;KACnC,IAAI,KAAK,UAAU;KACnB,KAAK,WAAW,OAAO,GAAG,KAAK,WAAW,QAAQ,GAAG,KAAK,KAAI,WAAU,OAAO,EAAE,CAAC;KAClF,KAAK,MAAM,UAAU,MAAM,KAAK,UAAU,IAAI,OAAO,IAAI,OAAO,WAAW;KAC3E,KAAK,SAAS;KACd,KAAK,MAAM,IAAI,KAAK,WAAW,CAAC;IAClC,QAAQ;KACN,IAAI,KAAK,UAAU;KACnB,KAAK,YAAY;KACjB,IAAI,KAAK,WAAW,GAClB,KAAK,eAAe,OAAO,iBAAiB;MAC1C,KAAK,eAAe,KAAA;MACpB,IAAI,KAAK,UAAU;MACnB,KAAU,SAAS;KACrB,GAAG,GAAI;IAEX;GACF;GAEA,aAA2C;IACzC,OAAO;KACL,GAAG,KAAK,KAAK,MAAM;KACnB,SAAS,KAAK,KAAK,MAAM,SAAS;KAClC,mBAAmB,KAAK,KAAK,MAAM,mBAAmB;KACtD,SAAS,KAAK,KAAK,MAAM,SAAS;KAClC,MAAM,KAAK,KAAK,MAAM,MAAM;KAC5B,OAAO,KAAK,KAAK,MAAM,OAAO;KAC9B,QAAQ,KAAK,KAAK,MAAM,QAAQ;KAChC,OAAO,KAAK,KAAK,MAAM,OAAO;KAC9B,YAAY,KAAK,WAAW,KAAI,QAAO;MAAE,OAAO;MAAI,OAAO,KAAK,UAAU,IAAI,EAAE,KAAK;KAAG,EAAE;KAC1F,gBAAgB,KAAK;IACvB;GACF;;;;;GAMA,SAA8B;IAC5B,OAAO;KAAE,OAAO,EAAE,iBAAiB,KAAK,MAAM;KAAG,GAAG,KAAK,KAAK,QAAQ;IAAE;GAC1E;;;;;GAMA,UAAgB;IACd,IAAI,KAAK,UAAU;IACnB,KAAK,WAAW;IAChB,IAAI,KAAK,iBAAiB,KAAA,GAAW;KACnC,OAAO,aAAa,KAAK,YAAY;KACrC,KAAK,eAAe,KAAA;IACtB;IACA,KAAK,KAAK,QAAQ;GACpB;EACF;;;;;;EAYA,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;IACjB,YAAA;cAPF;KASE,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,qBAAqB;MAC9B,MAAM,EAAE,yBAAyB;MACjC,cAAc,EAAE,kBAAkB;MAClC,SAAS,EAAE,aAAa;MACxB,UAAU,EAAE,cAAc;MAC1B,GAAI;MACJ,GAAI,MAAM;MACV,SAAS,SAAS;OAAE,MAAM,KAAK,qBAAqB,IAAI;MAAE;MAC1D,eAAe;OAAE,MAAM,WAAW,mBAAmB;MAAE;KACxD,CAAA;KACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAD;MACE,IAAG;MACH,OAAO,EAAE,cAAc;MACvB,MAAM,EAAE,kBAAkB;MAC1B,cAAc,EAAE,kBAAkB;MAClC,GAAI;MACJ,GAAI,MAAM;MACV,SAAS,MAAM;MACf,SAAS,SAAS;OAAE,MAAM,KAAK,SAAS,IAAI;MAAE;MAC9C,eAAe;OAAE,MAAM,WAAW,OAAO;MAAE;KAC5C,CAAA;KACA,MAAM,eAAe,WAAW,IAAI,OACnC,iBAAA,GAAA,kBAAA,KAAA,CAAC,MAAD;MAAI,WAAWC,oCAAW;MAAa,iBAAc;gBAArD,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAAM,WAAWA,oCAAW;iBAAmB,EAAE,2BAA2B;MAAQ,CAAA,GACpF,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD,EAAA,UACG,MAAM,eAAe,KAAK,YAAY,UACrC,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;OAAgB,cAAY,WAAW;iBAAQ,WAAW;MAAY,GAA7D,KAA6D,CACvE,EACC,CAAA,CACF;;KAEN,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;IACiB;;EAExB;;EASA,SAAgB,mBAAmB,OAA2C;GAC5E,MAAM,EAAE,GAAG,oBAAoB,MAAM,SAAS,MAAM,eAAe;GACnE,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;IAAI,WAAWA,oCAAW;cACxB,iBAAA,GAAA,kBAAA,IAAA,CAAC,iBAAD;KAAoB;KAAuB;KAA0B;KAAe;KAAe;KAAkB;IAAa,CAAA;GAChI,CAAA;EAER;;;;EC5UA,MAAa,KAAK;GAChB,YAAY;GACZ,YAAY;GACZ,cAAc;GACd,eAAe;GACf,uBAAuB;GACvB,cAAc;GACd,YAAY;GACZ,oBAAoB;GACpB,oBAAoB;GACpB,oBAAoB;GACpB,sBAAsB;GACtB,sBAAsB;GACtB,sBAAsB;GACtB,sBAAsB;GACtB,sBAAsB;GACtB,sBAAsB;GACtB,cAAc;GACd,cAAc;GACd,qBAAqB;GACrB,mBAAmB;GACnB,4BAA4B;GAC5B,2BAA2B;GAC3B,6BAA6B;GAC7B,0BAA0B;GAC1B,uBAAuB;GAEvB,qBAAqB;GACrB,qBAAqB;GACrB,yBAAyB;GACzB,sBAAsB;GACtB,qBAAqB;GACrB,qBAAqB;GACrB,qBAAqB;GACrB,4BAA4B;GAC5B,qBAAqB;GACrB,oBAAoB;GACpB,6BAA6B;GAC7B,sBAAsB;GACtB,wBAAwB;GACxB,yBAAyB;GACzB,4BAA4B;GAC5B,0BAA0B;GAC1B,4BAA4B;GAC5B,+BAA+B;GAC/B,gCAAgC;GAChC,oBAAoB;GACpB,wBAAwB;GAExB,kBAAkB;GAClB,6BAA6B;GAC7B,wBAAwB;GACxB,gBAAgB;GAChB,oBAAoB;GACpB,oBAAoB;GACpB,wBAAwB;GACxB,uBAAuB;GACvB,2BAA2B;GAC3B,oBAAoB;GACpB,wBAAwB;GACxB,iBAAiB;GACjB,qBAAqB;GACrB,kBAAkB;GAClB,sBAAsB;GACtB,mBAAmB;GACnB,uBAAuB;GACvB,oBAAoB;GACpB,eAAe;GACf,gBAAgB;GAChB,uBAAuB;GACvB,kBAAkB;GAClB,uBAAuB;GACvB,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,oBAAoB;GACpB,oBAAoB;GACpB,oBAAoB;GACpB,sBAAsB;GACtB,sBAAsB;GACtB,sBAAsB;GACtB,sBAAsB;GACtB,sBAAsB;GACtB,sBAAsB;GACtB,cAAc;GACd,cAAc;GACd,qBAAqB;GACrB,mBAAmB;GACnB,4BAA4B;GAC5B,2BAA2B;GAC3B,6BAA6B;GAC7B,0BAA0B;GAC1B,uBAAuB;GAEvB,qBAAqB;GACrB,qBAAqB;GACrB,yBAAyB;GACzB,sBAAsB;GACtB,qBAAqB;GACrB,qBAAqB;GACrB,qBAAqB;GACrB,4BAA4B;GAC5B,qBAAqB;GACrB,oBAAoB;GACpB,6BAA6B;GAC7B,sBAAsB;GACtB,wBAAwB;GACxB,yBAAyB;GACzB,4BAA4B;GAC5B,0BAA0B;GAC1B,4BAA4B;GAC5B,+BAA+B;GAC/B,gCAAgC;GAChC,oBAAoB;GACpB,wBAAwB;GAExB,kBAAkB;GAClB,6BAA6B;GAC7B,wBAAwB;GACxB,gBAAgB;GAChB,oBAAoB;GACpB,oBAAoB;GACpB,wBAAwB;GACxB,uBAAuB;GACvB,2BAA2B;GAC3B,oBAAoB;GACpB,wBAAwB;GACxB,iBAAiB;GACjB,qBAAqB;GACrB,kBAAkB;GAClB,sBAAsB;GACtB,mBAAmB;GACnB,uBAAuB;GACvB,oBAAoB;GACpB,eAAe;GACf,gBAAgB;GAChB,uBAAuB;GACvB,kBAAkB;GAClB,uBAAuB;GACvB,qBAAqB;GACrB,mBAAmB;GACnB,qBAAqB;GACrB,iBAAiB;GACjB,mBAAmB;GACnB,oBAAoB;GACpB,oBAAoB;GACpB,uBAAuB;GACvB,0BAA0B;EAC5B;;;;;;;EAcA,SAAgB,aAAqC;GAEnD,QADa,OAAO,aAAa,cAAc,SAAS,gBAAgB,OAAO,KAAA,CACnE,YAAY,CAAC,CAAC,WAAW,IAAI,IAAI,KAAK;EACpD;;;;;;;;;EAUA,SAAgB,EAAE,KAAa,QAA0C;GACvE,IAAI,OAAgB,WAAW,CAAC,CAA4B,QAAQ;GACpE,IAAI,WAAW,KAAA,GACb,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,MAAM,GAC/C,OAAO,KAAK,WAAW,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC;GAGrD,OAAO;EACT;;;ECzLA,MAAM,cAAc;EACpB,MAAM,iBAAiB;EACvB,MAAM,WAAW;;EAOjB,SAAS,eAAmC;GAC1C,IAAI;IACF,OAAA;GAGF,QAAQ;IACN;GACF;EACF;;EAGA,SAAS,YAA2B;GAClC,IAAI;IACF,MAAM,WAAW,aAAa,QAAQ,WAAW;IACjD,IAAI,YAAY,yBAAyB,KAAK,QAAQ,GAAG,OAAO;IAChE,MAAM,QAAQ,OAAO,WAAW,CAAC,CAAC,WAAW,KAAK,EAAE;IACpD,aAAa,QAAQ,aAAa,KAAK;IACvC,OAAO;GACT,QAAQ;IAEN,OAAO;GACT;EACF;;EAGA,SAAS,aAAa,OAAqB;GACzC,IAAI;IACF,KAAK,IAAI,QAAQ,aAAa,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG;KAChE,MAAM,MAAM,aAAa,IAAI,KAAK;KAClC,IAAI,QAAQ,QAAQ,IAAI,WAAW,cAAc,KAAK,QAAQ,iBAAiB,OAC7E,aAAa,WAAW,GAAG;IAE/B;GACF,QAAQ,CAAoB;EAC9B;;;;;;EAOA,SAAgB,qBAAqB,OAAuC;GAC1E,IAAI;IACF,IAAI,MAAM,WAAW,GAAG;IACxB,MAAM,yBAAQ,IAAI,KAAK,EAAA,CAAE,YAAY,CAAC,CAAC,MAAM,GAAG,EAAE;IAGlD,IAAI,UAAU,WAAW;IACzB,IAAI,aAAa,QAAQ,iBAAiB,KAAK,MAAM,MAAM;IAC3D,MAAM,UAAU,UAAU;IAC1B,IAAI,YAAY,MAAM;IACtB,aAAa,KAAK;IAClB,MAAM,eAAe,MAAM,KAAK,SAAS;KACvC,MAAM,MAA8B,EAAE,MAAM,KAAK,KAAK;KACtD,MAAM,UAAU,KAAK,WAAW,aAAa;KAC7C,IAAI,YAAY,KAAA,GAAW,IAAI,UAAU;KACzC,IAAI,KAAK,YAAY,KAAA,GAAW,IAAI,UAAU,KAAK;KACnD,OAAO;IACT,CAAC;IACD,MAAM,OAAO,KAAK,UAAU;KAAE,MAAM;KAAa;KAAS,OAAO;IAAa,CAAC;IAC/E,MAAW,UAAU;KACnB,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;KAC9C;KACA,WAAW;IACb,CAAC,CAAC,CAAC,MAAM,aAAa;KAGpB,IAAI,SAAS,IAAI,aAAa,QAAQ,iBAAiB,OAAO,GAAG;IACnE,CAAC,CAAC,CAAC,YAAY,CAA0C,CAAC;GAC5D,QAAQ,CAA+C;EACzD;;;;EC1CA,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,SAAS,OAAO,cAAc,SAAS,MAAM;GAE/D,OAAQ,MAAM,SAAS,KAAK;EAC9B;;EAGA,MAAM,SAAqB;GACzB,QAAQ,qBAAqB,SAAS,oBACjC,qBAAqB,KAAA,IAAY,KAAK,cAAc,mBAAmB,gBAAgB,EAAE;GAC9F,YAAY,SAAS,eAAe;GACpC,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;GACzD,SAAS,UAAU,SAAS,oBAAoB,EAAE,MAAM,CAAC;GACzD,UAAU,SAAS,SAAS,qBAAqB,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,KAAK,CAAC;GACnF,gBAAgB,SAAS,SAAS,2BAA2B,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,KAAK,CAAC;GAC/F,kBAAkB,SAAS,SAAS,0BAA0B,EAAE,KAAK,CAAC;GACtE,wBAAwB,SAAS,+BAA+B,CAAC,CAAC;GAClE,cAAc,SAAS,SAAS,yBAAyB,EAAE,KAAK,CAAC;EACnE;;EAGA,MAAM,UAAU;;EAGhB,MAAM,kBAAkB;;EAGxB,MAAa,SAAS;GAAC;GAAS;GAAU;GAAc;GAAiB;GAAU;EAAU;;;;;;;;EA6B7F,MAAM,eAAe,mBAAmB;;;;;;EAOxC,SAAS,iBAAiB,OAA6C;GACrE,MAAM,QAAQ,MAAM,YAAY;GAEhC,OADmB,MAAM,KAAK,MAAM,UAAU,MAAM,OAAO,MAAM,UAAU,IAAI,EAC/D,CAAC,EAAE,UAAU,MAAM;EACrC;EAEA,SAAgB,MAAM,KAA0B;GAG9C,qBAAqB,CAAC,EAAE,MAAM,qBAAqB,CAAC,CAAC;GAErD,IAAI,aAAa;IACf,IAAI;KACF,OAAO,IAAI,OAAO,SAAA,OAAa;MAAE;MAAI;KAAG,CAAC;IAC3C,QAAQ;KACN,aAAa,CAAC;IAChB;GACF,GAAG,mBAAmB;GAItB,2BAA2B,SAAS,cAAc;GAClD,2BAA2B,SAAS,gBAAgB;GAGpD,MAAM,iBADS,IAAI,IAAI,eAAe,KAAK,IAAI,cAAA,CAClB,KAAkB,EAAE,WAAW,gBAAgB,CAAC;GAC7E,MAAM,gBAAyB;IAC7B,MAAM,WAAW,cAAc,YAAY;IAC3C,OAAO,SAAS,WAAW,UACvB,SAAS,OAAO,WAAW,OAC3B,SAAS,WAAW;GAC1B;GAQA,MAAM,cAAc,IAAI,0BAA0B,aAAa;GAG/D,IAAI,MAAM,OAAO,0BAA0B;IACzC,IAAI;KACF,MAAM,aAAa,IAAI,MAAM,SAAS;MACpC,MAAM;MACN,IAAI;MACJ,OAAO;MACP,aAAa,IAAI,OAAO,KAAK,KAAK,CAAC,CAAC,gBAAgB;MACpD,QAAQ;MACR,cAAc,YAAY,OAAO;KACnC,GAAG,kBAAkB;KACrB,aAAa;MACX,WAAW;MACX,YAAY,QAAQ;KACtB;IACF,QAAQ;KACN,aAAa,CAAC;IAChB;GACF,CAAC;GAOD,IAAI;GACJ,IAAI;GACJ,IAAI,SAAS;GACb,MAAM,eAAqB;IACzB,IAAI,QAAQ;IACZ,SAAS;IACT,kBAAkB;IAClB,kBAAkB,KAAA;IAClB,YAAY;IACZ,YAAY,KAAA;GACd;GACA,MAAM,eAAqB;IACzB,IAAI,CAAC,UAAU,QAAQ,KAAK,cAAc,KAAA,GAAW;KAMnD,MAAM,WAA6B,eAAe,CAAC,CAAC,OAAO;KAC3D,MAAM,cAAc,SAAS,QAAQ;KACrC,MAAM,UAAU,SAAS,QAAQ;KACjC,MAAM,WAAW,SAAS,QAAQ;KAClC,MAAM,cAAc,SAAS,QAAQ;KAUrC,MAAM,WAAW,IAAI;KACrB,MAAM,yBAA6C;MACjD,MAAM,UAAU,SAAS,KAAK,YAAY,CAAC,CAAC;MAC5C,OAAO,YAAY,KAAA,IAAY,KAAA,IAAY,OAAO,OAAO;KAC3D;KAMA,IAAI,aAAa;KAKjB,IAAI,WAAW;KACf,MAAM,gBAAsB;MAC1B,IAAI,CAAC,YACH,OAAO,KAAK,CAAC,CAAC,MAAM,SAAS;OAC3B,aAAa;OACb,QAAQ,IAAI;MACd,SAAS,CAET,CAAC;MAEH,MAAM,MAAM,WAAW;MACvB,WAAW;MACX,OAAO,MAAM,iBAAiB,CAAC,CAAC,CAAC,MAAM,aAAa;OAClD,IAAI,QAAQ,UAAU;OACtB,YAAY,QAAQ;MACtB,SAAS;OACP,IAAI,QAAQ,UAAU;OACtB,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;KAKd,MAAM,sBAAsB,IAAI,aAAa;MAI3C,OAHoB,SAAS,KAAK,gBAAgB;OAChD,IAAI,SAAS,oBAAoB,WAAW,QAAQ;MACtD,CACiB;KACnB,GAAG,4BAA4B;KAE/B,MAAM,eAAe,cAA4B;MAE/C,IADa,SAAS,KAAK,YACnB,CAAC,CAAC,KAAa,eAAe,KAAA,GAAW;MACjD,SAAS,KAAK,SAAkB;KAClC;KAEA,MAAM,kBAA+B;MACnC,OAAO;MACP,QAAQ;MACR;MACA,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;MACA,UAAU;OACR,QAAQ,SAAS,OAAO,cAAc,IAAI;OAC1C,UAAU,SAAS,OAAO,QAAQ,IAAI,CAAC,CAAC,MAAM,WAAW;QACvD,IAAI,OAAO,IAAI,QAAQ;QACvB,OAAO;OACT,UAAU;QAAE,IAAI;QAAO,OAAO;OAAY,EAAE;OAE5C,SAAS,OAAO,SAAS;QACvB,IAAI,SAAS,QAAQ,aAAa,MAAM;QACxC,OAAO,OAAO,gBAAgB,IAAI;OACpC;OACA,UAAU,YAAY;QAIpB,IAAI,CAAC,aAAa,MAAM,iBAAiB,QAAQ,CAAC,GAChD,OAAO,EAAE,IAAI,KAAK;QAEpB,OAAO,OAAO,iBAAiB;OACjC;OACA,MAAM,SAAS,OAAO,YAAY,IAAI;MACxC;KACF;KAgBA,sBAAsB;KACtB,KAAK,MAAM,SAAS,MAAM,KAAK,SAAS,iBAAiB,wBAAwB,CAAC,GAChF,MAAM,OAAO;KAEf,MAAM,YAAY,SAAS,cAAc,KAAK;KAC9C,UAAU,QAAQ,aAAa;KAC/B,UAAU,QAAQ,YAAY;KAC9B,SAAS,KAAK,YAAY,SAAS;KACnC,MAAM,WAAA,GAAA,iBAAA,WAAA,CAAqB,SAAS;KAKpC,QAAQ,QAAA,GAAA,MAAA,cAAA,CAAqB,cAAc;MAAE,GAAG,SAAS;MAAG;MAAG,cAAc;KAAU,CAAC,CAAC;KAEzF,IAAI,SAAS;KACb,kBAAkB;MAChB,IAAI,QAAQ;MACZ,SAAS;MACT,kBAAkB;MAClB,kBAAkB,KAAA;MAClB,QAAQ,QAAQ;MAChB,UAAU,OAAO;MACjB,YAAY;MACZ,oBAAoB;MACpB,YAAY,KAAA;KACd;KAIA,kBAAkB,4BAA4B;MAC5C,SAAS;MACT,YAAY;KACd,CAAC;IACH,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,KAAK,cAAc,KAAA,GAAW;KAC3D,UAAU;KACV,YAAY,KAAA;IACd;GACF;GAKA,MAAM,sBAAsB,cAAc,UAAU,MAAM;GAC1D,IAAI,mBACU;IACV,oBAAoB;IACpB,OAAO;GACT,GACA,uBACF;GACA,OAAO;EACT"}