@linxin666/dsh-pet 0.3.6 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +4 -0
- package/README.zh.md +4 -0
- package/lib/client.js +187 -88
- package/lib/client.js.map +1 -1
- package/lib/index.js +89 -11
- package/lib/live2d-vendor.js +106 -95
- package/lib/live2d-vendor.js.map +1 -1
- package/lib/types/announce.d.ts +53 -0
- package/lib/types/announce.d.ts.map +1 -0
- package/lib/types/announce.js +80 -0
- package/lib/types/client/PetDockEntry.d.ts +14 -5
- package/lib/types/client/PetDockEntry.d.ts.map +1 -1
- package/lib/types/client/PetDockEntry.js +6 -5
- package/lib/types/client/PetSettingsCard.d.ts +2 -1
- package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PetSprite.d.ts +8 -0
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +29 -4
- package/lib/types/client/index.d.ts +8 -5
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +11 -5
- package/lib/types/client/locales.d.ts +2 -2
- package/lib/types/client/locales.js +2 -2
- package/lib/types/client/pet-store.d.ts +1 -1
- package/lib/types/client/pet-store.d.ts.map +1 -1
- package/lib/types/client/pet-store.js +1 -1
- package/lib/types/client/settings-form.d.ts +35 -43
- package/lib/types/client/settings-form.d.ts.map +1 -1
- package/lib/types/client/settings-form.js +60 -53
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +10 -9
- package/lib/types/service.d.ts +18 -0
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +22 -0
- package/package.json +17 -13
- package/src/announce.ts +103 -0
- package/src/client/PetDockEntry.test.tsx +1 -1
- package/src/client/PetDockEntry.tsx +15 -4
- package/src/client/PetSettingsCard.tsx +2 -1
- package/src/client/PetSprite.test.tsx +18 -0
- package/src/client/PetSprite.tsx +66 -3
- package/src/client/gameplay-hud.test.tsx +1 -1
- package/src/client/index.test.tsx +2 -2
- package/src/client/index.ts +17 -6
- package/src/client/locales.ts +2 -2
- package/src/client/pet-store.ts +2 -2
- package/src/client/pet.module.css +104 -0
- package/src/client/settings-form.ts +78 -92
- package/src/dsh-home.ts +1 -1
- package/src/index.ts +17 -15
- package/src/service.ts +28 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pet announcement-bubble contract (dsh-usage linkage): a sibling plugin
|
|
3
|
+
* pushes one structured announcement through `pet.announce(...)`, the host
|
|
4
|
+
* validates it into a bounded payload, and the browser half renders it as a
|
|
5
|
+
* dedicated, specially styled bubble above the session bubble stack.
|
|
6
|
+
*
|
|
7
|
+
* The validation lives in this pure module so the wire contract has exactly
|
|
8
|
+
* one home and stays testable without the cordis service.
|
|
9
|
+
* @module @linxin666/dsh-pet/announce
|
|
10
|
+
*/
|
|
11
|
+
/** One plugin-authored announcement bubble. */
|
|
12
|
+
export interface PetAnnouncement {
|
|
13
|
+
/** Authoring plugin's source tag (`dsh-usage` for the usage statistics). */
|
|
14
|
+
source: string;
|
|
15
|
+
/** Bubble content kind: a spend estimate, an account balance, or a plan-quota status. */
|
|
16
|
+
kind: 'balance' | 'cost' | 'plan';
|
|
17
|
+
/** Lead text (usually the provider display name). */
|
|
18
|
+
title: string;
|
|
19
|
+
/** Balance or today-spend amount, formatted for display (kinds `balance` and `cost`). */
|
|
20
|
+
amount?: string;
|
|
21
|
+
/** Plan usage percent 0-100 (kind `plan`). */
|
|
22
|
+
percent?: number;
|
|
23
|
+
/** ISO 8601 reset instant (kind `plan`). */
|
|
24
|
+
resetAt?: string;
|
|
25
|
+
/** Short trailing note (plan tier name, peak-period status, currency code, ...). */
|
|
26
|
+
note?: string;
|
|
27
|
+
/** Visual tone; drives the bubble's accent color. */
|
|
28
|
+
tone: 'ok' | 'warn' | 'low';
|
|
29
|
+
/** Freshness window in ms; an expired announcement stops rendering. */
|
|
30
|
+
ttlMs: number;
|
|
31
|
+
/** Epoch ms the announcement arrived. */
|
|
32
|
+
at: number;
|
|
33
|
+
}
|
|
34
|
+
/** Default freshness window. */
|
|
35
|
+
export declare const ANNOUNCE_DEFAULT_TTL_MS = 10000;
|
|
36
|
+
/**
|
|
37
|
+
* Hard TTL ceiling. A repeating announcer (dsh-usage) declares its poll
|
|
38
|
+
* cadence as the TTL, so an `always`-mode bubble stays continuous across
|
|
39
|
+
* polls; the ceiling means a source that dies unmounts its bubble within at
|
|
40
|
+
* most one missed refresh cycle rather than lingering forever.
|
|
41
|
+
*/
|
|
42
|
+
export declare const ANNOUNCE_MAX_TTL_MS = 7200000;
|
|
43
|
+
/**
|
|
44
|
+
* Validate one announce payload. Unknown fields are dropped, oversized text
|
|
45
|
+
* is truncated, and anything structurally wrong resolves to undefined — a
|
|
46
|
+
* malformed announcement never reaches the pet's bubble surface.
|
|
47
|
+
* @param input - the payload a sibling plugin passed to `pet.announce`.
|
|
48
|
+
* @param now - epoch ms the announcement arrived.
|
|
49
|
+
*/
|
|
50
|
+
export declare function parseAnnouncement(input: unknown, now: number): PetAnnouncement | undefined;
|
|
51
|
+
/** Whether an announcement is still fresh at `now`. */
|
|
52
|
+
export declare function announcementFresh(announcement: PetAnnouncement, now: number): boolean;
|
|
53
|
+
//# sourceMappingURL=announce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"announce.d.ts","sourceRoot":"","sources":["../../src/announce.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,+CAA+C;AAC/C,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAA;IACd,yFAAyF;IACzF,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,CAAA;IACjC,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAA;IACb,yFAAyF;IACzF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,qDAAqD;IACrD,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,KAAK,CAAA;IAC3B,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAA;IACb,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAA;CACX;AAED,gCAAgC;AAChC,eAAO,MAAM,uBAAuB,QAAS,CAAA;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,UAAY,CAAA;AAc5C;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAgC1F;AAED,uDAAuD;AACvD,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAErF"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pet announcement-bubble contract (dsh-usage linkage): a sibling plugin
|
|
3
|
+
* pushes one structured announcement through `pet.announce(...)`, the host
|
|
4
|
+
* validates it into a bounded payload, and the browser half renders it as a
|
|
5
|
+
* dedicated, specially styled bubble above the session bubble stack.
|
|
6
|
+
*
|
|
7
|
+
* The validation lives in this pure module so the wire contract has exactly
|
|
8
|
+
* one home and stays testable without the cordis service.
|
|
9
|
+
* @module @linxin666/dsh-pet/announce
|
|
10
|
+
*/
|
|
11
|
+
/** Default freshness window. */
|
|
12
|
+
export const ANNOUNCE_DEFAULT_TTL_MS = 10_000;
|
|
13
|
+
/**
|
|
14
|
+
* Hard TTL ceiling. A repeating announcer (dsh-usage) declares its poll
|
|
15
|
+
* cadence as the TTL, so an `always`-mode bubble stays continuous across
|
|
16
|
+
* polls; the ceiling means a source that dies unmounts its bubble within at
|
|
17
|
+
* most one missed refresh cycle rather than lingering forever.
|
|
18
|
+
*/
|
|
19
|
+
export const ANNOUNCE_MAX_TTL_MS = 7_200_000;
|
|
20
|
+
/** Hard bounds: lengths and the TTL range (1 s .. 60 s). */
|
|
21
|
+
const TITLE_MAX = 80;
|
|
22
|
+
const TEXT_MAX = 120;
|
|
23
|
+
const SOURCE_MAX = 64;
|
|
24
|
+
function boundedString(value, max) {
|
|
25
|
+
if (typeof value !== 'string')
|
|
26
|
+
return undefined;
|
|
27
|
+
const trimmed = value.trim();
|
|
28
|
+
if (trimmed === '')
|
|
29
|
+
return undefined;
|
|
30
|
+
return trimmed.length <= max ? trimmed : trimmed.slice(0, max);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validate one announce payload. Unknown fields are dropped, oversized text
|
|
34
|
+
* is truncated, and anything structurally wrong resolves to undefined — a
|
|
35
|
+
* malformed announcement never reaches the pet's bubble surface.
|
|
36
|
+
* @param input - the payload a sibling plugin passed to `pet.announce`.
|
|
37
|
+
* @param now - epoch ms the announcement arrived.
|
|
38
|
+
*/
|
|
39
|
+
export function parseAnnouncement(input, now) {
|
|
40
|
+
if (typeof input !== 'object' || input === null)
|
|
41
|
+
return undefined;
|
|
42
|
+
const data = input;
|
|
43
|
+
const source = boundedString(data.source, SOURCE_MAX);
|
|
44
|
+
const title = boundedString(data.title, TITLE_MAX);
|
|
45
|
+
if (source === undefined || title === undefined)
|
|
46
|
+
return undefined;
|
|
47
|
+
const kind = data.kind === 'balance' || data.kind === 'cost' || data.kind === 'plan' ? data.kind : undefined;
|
|
48
|
+
if (kind === undefined)
|
|
49
|
+
return undefined;
|
|
50
|
+
const amount = boundedString(data.amount, TEXT_MAX);
|
|
51
|
+
const resetAt = boundedString(data.resetAt, TEXT_MAX);
|
|
52
|
+
const note = boundedString(data.note, TITLE_MAX);
|
|
53
|
+
const percent = typeof data.percent === 'number' && Number.isFinite(data.percent)
|
|
54
|
+
? Math.max(0, Math.min(100, data.percent))
|
|
55
|
+
: undefined;
|
|
56
|
+
if ((kind === 'balance' || kind === 'cost') && amount === undefined)
|
|
57
|
+
return undefined;
|
|
58
|
+
if (kind === 'plan' && percent === undefined)
|
|
59
|
+
return undefined;
|
|
60
|
+
const tone = data.tone === 'warn' || data.tone === 'low' ? data.tone : 'ok';
|
|
61
|
+
const ttlMs = typeof data.ttlMs === 'number' && Number.isFinite(data.ttlMs)
|
|
62
|
+
? Math.max(1000, Math.min(ANNOUNCE_MAX_TTL_MS, data.ttlMs))
|
|
63
|
+
: ANNOUNCE_DEFAULT_TTL_MS;
|
|
64
|
+
return {
|
|
65
|
+
source,
|
|
66
|
+
kind,
|
|
67
|
+
title,
|
|
68
|
+
...(amount !== undefined ? { amount } : {}),
|
|
69
|
+
...(percent !== undefined ? { percent } : {}),
|
|
70
|
+
...(resetAt !== undefined ? { resetAt } : {}),
|
|
71
|
+
...(note !== undefined ? { note } : {}),
|
|
72
|
+
tone,
|
|
73
|
+
ttlMs,
|
|
74
|
+
at: now,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** Whether an announcement is still fresh at `now`. */
|
|
78
|
+
export function announcementFresh(announcement, now) {
|
|
79
|
+
return now - announcement.at < announcement.ttlMs;
|
|
80
|
+
}
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
* it must not ride a session-scoped slot — on the new-conversation screen no
|
|
5
5
|
* session exists to scope a slot by, and the pet would vanish (issue #48).
|
|
6
6
|
* The client half therefore mounts this entry straight onto 'document.body'
|
|
7
|
-
* (see index.ts): while visible it renders the floating PetSprite (a
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* the registry list — no per-pet
|
|
7
|
+
* (see index.ts): while visible it renders the floating PetSprite (a portal
|
|
8
|
+
* into the plugin root, so the root owns the whole surface), while hidden it
|
|
9
|
+
* renders a fixed-position summon button. Which sprite renders is decided by
|
|
10
|
+
* the host snapshot's pet id resolved against the registry list — no per-pet
|
|
11
|
+
* component exists.
|
|
11
12
|
* @module @linxin666/dsh-pet/client/PetDockEntry
|
|
12
13
|
*/
|
|
13
14
|
import { type ReactElement } from 'react';
|
|
@@ -41,7 +42,15 @@ export interface PetInjected {
|
|
|
41
42
|
gameplay: GameplayApi;
|
|
42
43
|
}
|
|
43
44
|
/** Composed props of the global pet entry (locale + injected; no slot runtime share). */
|
|
44
|
-
export type PetDockEntryProps = PetInjected & PropsLocale<typeof NS
|
|
45
|
+
export type PetDockEntryProps = PetInjected & PropsLocale<typeof NS> & {
|
|
46
|
+
/**
|
|
47
|
+
* DOM node the floating sprite chrome portals into. Defaults to
|
|
48
|
+
* document.body; the plugin apply passes its [data-dsh-plugin="pet"]
|
|
49
|
+
* root so root-keyed suppressors (the portrait mobile layer) and skins
|
|
50
|
+
* own the whole pet surface as one unit.
|
|
51
|
+
*/
|
|
52
|
+
portalTarget?: Element;
|
|
53
|
+
};
|
|
45
54
|
/**
|
|
46
55
|
* Dock entry: while the pet is visible, mount the floating PetSprite (it
|
|
47
56
|
* portals itself onto document.body); while hidden, render the summon
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetDockEntry.d.ts","sourceRoot":"","sources":["../../../src/client/PetDockEntry.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"PetDockEntry.d.ts","sourceRoot":"","sources":["../../../src/client/PetDockEntry.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAA2C,KAAK,YAAY,EAAE,MAAM,OAAO,CAAA;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAItD,OAAO,EAAe,KAAK,WAAW,EAAoB,MAAM,oBAAoB,CAAA;AACpF,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,6EAA6E;IAC7E,KAAK,EAAE,gBAAgB,CAAA;IACvB,kFAAkF;IAClF,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,8BAA8B;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAA;IACf,uBAAuB;IACvB,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,uBAAuB;IACvB,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,qCAAqC;IACrC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAChD,uDAAuD;IACvD,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,2DAA2D;IAC3D,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,iCAAiC;IACjC,YAAY,EAAE,MAAM,IAAI,CAAA;IACxB,uGAAuG;IACvG,QAAQ,EAAE,WAAW,CAAA;CACtB;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAC3B,WAAW,GACT,WAAW,CAAC,OAAO,EAAE,CAAC,GACtB;IACA;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAIH;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,CAyFnE"}
|
|
@@ -5,10 +5,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
5
5
|
* it must not ride a session-scoped slot — on the new-conversation screen no
|
|
6
6
|
* session exists to scope a slot by, and the pet would vanish (issue #48).
|
|
7
7
|
* The client half therefore mounts this entry straight onto 'document.body'
|
|
8
|
-
* (see index.ts): while visible it renders the floating PetSprite (a
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* the registry list — no per-pet
|
|
8
|
+
* (see index.ts): while visible it renders the floating PetSprite (a portal
|
|
9
|
+
* into the plugin root, so the root owns the whole surface), while hidden it
|
|
10
|
+
* renders a fixed-position summon button. Which sprite renders is decided by
|
|
11
|
+
* the host snapshot's pet id resolved against the registry list — no per-pet
|
|
12
|
+
* component exists.
|
|
12
13
|
* @module @linxin666/dsh-pet/client/PetDockEntry
|
|
13
14
|
*/
|
|
14
15
|
import { useEffect, useRef, useSyncExternalStore } from 'react';
|
|
@@ -46,7 +47,7 @@ export function PetDockEntry(props) {
|
|
|
46
47
|
if (visible) {
|
|
47
48
|
return (_jsx("span", { "data-pet-dock": true, "data-testid": "pet-dock", children: snapshot === null || definition === null
|
|
48
49
|
? null
|
|
49
|
-
: (_jsx(PetRendererSwitch, { definition: definition, phase: snapshot?.phase ?? 'idle', onPet: props.pet, ...(aux === null ? {} : { drag: aux.drag, bus: aux.bus }), t: props.t, children: _jsx(PetSprite, { snapshot: snapshot, definition: definition, display: snapshot.display, feedback: feedback, onPet: props.pet, onFeed: props.feed, onHide: props.hide, onDragEnd: props.dragEnd, onRename: props.rename, onOpenSession: props.openSession, onFeedbackDone: props.feedbackDone, dragDisabled: snapshot.gameplay?.mode === 'work', ...(gameplay === undefined || aux === null
|
|
50
|
+
: (_jsx(PetRendererSwitch, { definition: definition, phase: snapshot?.phase ?? 'idle', onPet: props.pet, ...(aux === null ? {} : { drag: aux.drag, bus: aux.bus }), t: props.t, children: _jsx(PetSprite, { snapshot: snapshot, definition: definition, display: snapshot.display, feedback: feedback, onPet: props.pet, onFeed: props.feed, onHide: props.hide, onDragEnd: props.dragEnd, onRename: props.rename, onOpenSession: props.openSession, onFeedbackDone: props.feedbackDone, portalTarget: props.portalTarget, dragDisabled: snapshot.gameplay?.mode === 'work', ...(gameplay === undefined || aux === null
|
|
50
51
|
? {}
|
|
51
52
|
: {
|
|
52
53
|
onGameplayTap: (fx, fy) => aux.bus.tap?.(fx, fy),
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { ReactNode } from 'react';
|
|
10
10
|
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
11
|
-
import type { SettingsScope
|
|
11
|
+
import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
12
|
+
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
12
13
|
import { type CardActions, type CardShell, type FieldState as CardFieldState } from './settings-form.ts';
|
|
13
14
|
/** The pet's settings fields this card edits (the namespace's full schema). */
|
|
14
15
|
export interface PetSettings {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PetSettingsCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"PetSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PetSettingsCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4CAA4C,CAAA;AAC/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAIlE,OAAO,EAAoD,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,IAAI,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAG1J,+EAA+E;AAC/E,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6DAA6D;IAC7D,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED,0CAA0C;AAC1C,MAAM,WAAW,oBAAqB,SAAQ,SAAS;IACrD,4BAA4B;IAC5B,OAAO,EAAE,cAAc,CAAA;IACvB,qBAAqB;IACrB,OAAO,EAAE,cAAc,CAAA;IACvB,iBAAiB;IACjB,IAAI,EAAE,cAAc,CAAA;IACpB,mBAAmB;IACnB,KAAK,EAAE,cAAc,CAAA;IACrB,oBAAoB;IACpB,MAAM,EAAE,cAAc,CAAA;IACtB,oBAAoB;IACpB,KAAK,EAAE,cAAc,CAAA;IACrB,uCAAuC;IACvC,iBAAiB,EAAE,cAAc,CAAA;IACjC,wEAAwE;IACxE,UAAU,EAAE,SAAS;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACvD,+EAA+E;IAC/E,cAAc,EAAE,SAAS,iBAAiB,EAAE,CAAA;CAC7C;AAED,gEAAgE;AAChE,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IACtD,KAAK,EAAE;QACL,iEAAiE;QACjE,eAAe,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAA;KACrD,CAAA;CACF;AAQD,0EAA0E;AAC1E,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,OAAO,GAAG,SAAS,CAAA;IAC1B,OAAO,EAAE,MAAM,CAAA;CAChB;AAiBD,2DAA2D;AAC3D,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAuB;IAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAI3D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAe;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,QAAQ,CAAQ;IACxB,oEAAoE;IACpE,OAAO,CAAC,YAAY,CAAoB;IAExC,uEAAuE;gBAC3D,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC;IAuB7C,2EAA2E;YAC7D,eAAe;IAU7B,0EAA0E;YAC5D,QAAQ;IAsBtB,OAAO,CAAC,UAAU;IAelB;;;OAGG;IACH,MAAM,IAAI,mBAAmB;IAI7B;;;OAGG;IACH,OAAO,IAAI,IAAI;CAShB;AAED,0DAA0D;AAC1D,MAAM,MAAM,oBAAoB,GAC9B,WAAW,CAAC,KAAK,CAAC,GAChB,UAAU,CAAC,mBAAmB,CAAC,CAAA;AAEnC;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,+BA6G1D;AAED,8DAA8D;AAC9D,MAAM,MAAM,uBAAuB,GACjC,YAAY,CAAC,kBAAkB,CAAC,GAC9B,WAAW,CAAC,KAAK,CAAC,GAClB,UAAU,CAAC,mBAAmB,CAAC,CAAA;AAEnC,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,SAAS,CAO5E"}
|
|
@@ -68,6 +68,14 @@ export interface PetSpriteProps {
|
|
|
68
68
|
onGameplayMenu?: () => void;
|
|
69
69
|
/** Disable the drag gesture (gameplay work mode blocks dragging). */
|
|
70
70
|
dragDisabled?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* DOM node the floating chrome portals into. Defaults to document.body
|
|
73
|
+
* (the legacy behavior); the plugin apply passes its owning root (the
|
|
74
|
+
* [data-dsh-plugin="pet"] container) so the root owns the whole surface
|
|
75
|
+
* and a root-keyed suppressor (the portrait mobile layer) hides the pet
|
|
76
|
+
* as one unit instead of missing the portaled sprite.
|
|
77
|
+
*/
|
|
78
|
+
portalTarget?: Element;
|
|
71
79
|
/** Locale translate seat (namespace-bound). */
|
|
72
80
|
t: TranslateNS<typeof NS>;
|
|
73
81
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetSprite.d.ts","sourceRoot":"","sources":["../../../src/client/PetSprite.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAkE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAGnH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"PetSprite.d.ts","sourceRoot":"","sources":["../../../src/client/PetSprite.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAkE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAGnH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAIjD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,8EAA8E;IAC9E,UAAU,EAAE,aAAa,CAAA;IACzB,qDAAqD;IACrD,OAAO,EAAE,gBAAgB,CAAA;IACzB,sCAAsC;IACtC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,8BAA8B;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9C,uDAAuD;IACvD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,4DAA4D;IAC5D,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,2DAA2D;IAC3D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;;OAIG;IACH,GAAG,CAAC,EAAE,SAAS,CAAA;IACf;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9D;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;IAC3B,qEAAqE;IACrE,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AA8ID;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,WAAW,CAqjB5D"}
|
|
@@ -12,6 +12,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
12
12
|
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
13
13
|
import { createPortal } from 'react-dom';
|
|
14
14
|
import clsx from 'clsx';
|
|
15
|
+
import { announcementFresh } from "../announce.js";
|
|
15
16
|
import { framePosition, rowOfTrack, trimTrack } from "./spritesheet.js";
|
|
16
17
|
import { sequenceFrameAt } from "./sequences.js";
|
|
17
18
|
import { animationForPhase } from "../state.js";
|
|
@@ -108,6 +109,23 @@ function StatusOrnament(props) {
|
|
|
108
109
|
backgroundPosition: '0px 0px',
|
|
109
110
|
} }));
|
|
110
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* The announcement bubble (dsh-usage linkage): a dedicated, specially
|
|
114
|
+
* designed surface for sibling-plugin facts — a balance or today-spend pill,
|
|
115
|
+
* or a plan-quota card with a tone-tinted accent, a mini meter for percent
|
|
116
|
+
* windows, and the reset instant. It rides the top of the session bubble
|
|
117
|
+
* stack (column-reverse puts the DOM-last child farthest from the sprite)
|
|
118
|
+
* and persists for its TTL instead of the short feedback pop.
|
|
119
|
+
*/
|
|
120
|
+
function UsageAnnouncementBubble(props) {
|
|
121
|
+
const { announcement } = props;
|
|
122
|
+
const tone = announcement.tone === 'low'
|
|
123
|
+
? styles.bubbleUsageLow
|
|
124
|
+
: announcement.tone === 'warn'
|
|
125
|
+
? styles.bubbleUsageWarn
|
|
126
|
+
: styles.bubbleUsageOk;
|
|
127
|
+
return (_jsxs("div", { className: clsx(styles.bubble, styles.bubbleUsage, tone), role: "status", "aria-live": "polite", "data-dsh-pet-announcement": announcement.source, children: [_jsxs("span", { className: styles.bubbleUsageHead, children: [_jsx("span", { className: styles.bubbleUsageTitle, children: announcement.title }), (announcement.kind === 'balance' || announcement.kind === 'cost') && announcement.amount !== undefined && (_jsx("span", { className: styles.bubbleUsageValue, children: announcement.amount })), announcement.kind === 'plan' && announcement.percent !== undefined && (_jsx("span", { className: styles.bubbleUsageValue, children: Math.round(announcement.percent) + '%' }))] }), announcement.kind === 'plan' && announcement.percent !== undefined && (_jsx("span", { className: styles.bubbleUsageMeter, children: _jsx("span", { className: styles.bubbleUsageMeterFill, style: { width: Math.min(100, Math.max(0, announcement.percent)) + '%' } }) })), announcement.note !== undefined && (_jsx("span", { className: styles.bubbleUsageNote, children: announcement.note }))] }));
|
|
128
|
+
}
|
|
111
129
|
/**
|
|
112
130
|
* The floating pet. The spritesheet frame advances on requestAnimationFrame
|
|
113
131
|
* with per-frame durations from the definition's tracks; the atlas image is
|
|
@@ -360,6 +378,13 @@ export function PetSprite(props) {
|
|
|
360
378
|
const statusBubble = feedback === null && sessionBubbles.length === 0
|
|
361
379
|
? snapshot?.bubble
|
|
362
380
|
: undefined;
|
|
381
|
+
// The freshest plugin-authored announcement (dsh-usage linkage): a
|
|
382
|
+
// dedicated, specially styled bubble above the session stack. The host
|
|
383
|
+
// already TTL-filters; this client-side check covers the last poll tick.
|
|
384
|
+
const announcement = snapshot?.announcement;
|
|
385
|
+
const usageAnnouncement = feedback === null && announcement !== undefined && announcementFresh(announcement, Date.now())
|
|
386
|
+
? announcement
|
|
387
|
+
: undefined;
|
|
363
388
|
// Each session's inner whisper (碎碎念) rides its own bubble — short
|
|
364
389
|
// inner-voice copy woken by that session's activity, never the model's or
|
|
365
390
|
// another session's. Instead of a second bubble of its own, a fresh
|
|
@@ -367,7 +392,7 @@ export function PetSprite(props) {
|
|
|
367
392
|
// never wears two voices at once. Interaction feedback takes over the
|
|
368
393
|
// whole bubble area while it plays, so whispers yield to it like status
|
|
369
394
|
// copy.
|
|
370
|
-
const bubblePresent = feedback !== null || sessionBubbles.length > 0 || statusBubble !== undefined;
|
|
395
|
+
const bubblePresent = feedback !== null || sessionBubbles.length > 0 || statusBubble !== undefined || usageAnnouncement !== undefined;
|
|
371
396
|
const displayName = snapshot?.name ?? definition.displayName;
|
|
372
397
|
// The host-served status decoration (M5, #567); absent = text-only bubbles.
|
|
373
398
|
const decoration = snapshot?.decoration;
|
|
@@ -447,7 +472,7 @@ export function PetSprite(props) {
|
|
|
447
472
|
}
|
|
448
473
|
}
|
|
449
474
|
props.onPet();
|
|
450
|
-
}, role: "button", "aria-label": definition.displayName, children: props.visual }) }), props.hud, feedback !== null && (_jsx("div", { ref: bubbleRef, className: clsx(styles.bubble, feedback.kind === 'feed' ? styles.bubbleFeed : styles.bubblePet), children: feedback.text }, feedback.at)), feedback === null && (sessionBubbles.length > 0 || statusBubble !== undefined) && (_jsxs("div", { ref: bubbleRef, className: styles.bubbleStack, onPointerEnter: () => setStackPeek(true), onPointerLeave: () => setStackPeek(false), children: [visibleSessions.map((session, index) => {
|
|
475
|
+
}, role: "button", "aria-label": definition.displayName, children: props.visual }) }), props.hud, feedback !== null && (_jsx("div", { ref: bubbleRef, className: clsx(styles.bubble, feedback.kind === 'feed' ? styles.bubbleFeed : styles.bubblePet), children: feedback.text }, feedback.at)), feedback === null && (sessionBubbles.length > 0 || statusBubble !== undefined || usageAnnouncement !== undefined) && (_jsxs("div", { ref: bubbleRef, className: styles.bubbleStack, onPointerEnter: () => setStackPeek(true), onPointerLeave: () => setStackPeek(false), children: [visibleSessions.map((session, index) => {
|
|
451
476
|
// A session's whisper rides ITS OWN bubble (the stack lead when
|
|
452
477
|
// the current session has one, any bubble when the stack is
|
|
453
478
|
// expanded). The key swap restarts the entrance animation so the
|
|
@@ -466,7 +491,7 @@ export function PetSprite(props) {
|
|
|
466
491
|
e.stopPropagation();
|
|
467
492
|
setStackPinned(open => !open);
|
|
468
493
|
}, children: stackOpen ? '×' : '+' + String(sessionBubbles.length - 1) })] }, "primary"));
|
|
469
|
-
}), sessionBubbles.length === 0 && statusBubble !== undefined && (_jsxs("div", { className: clsx(styles.bubble, styles.bubbleStatus), role: "status", "aria-live": "polite", children: [decoration !== undefined && (_jsx(StatusOrnament, { decoration: decoration, phase: phase })), statusBubble] }, "status"))] })), hovered && dragRef.current === null && (_jsx("div", { ref: panelRef, className: clsx(styles.panel, panelAbove && styles.panelAbove), "data-placement": panelAbove ? 'above' : 'below', style: panelAbove && panelLift > 0
|
|
494
|
+
}), sessionBubbles.length === 0 && statusBubble !== undefined && (_jsxs("div", { className: clsx(styles.bubble, styles.bubbleStatus), role: "status", "aria-live": "polite", children: [decoration !== undefined && (_jsx(StatusOrnament, { decoration: decoration, phase: phase })), statusBubble] }, "status")), usageAnnouncement !== undefined && _jsx(UsageAnnouncementBubble, { announcement: usageAnnouncement })] })), hovered && dragRef.current === null && (_jsx("div", { ref: panelRef, className: clsx(styles.panel, panelAbove && styles.panelAbove), "data-placement": panelAbove ? 'above' : 'below', style: panelAbove && panelLift > 0
|
|
470
495
|
? { marginBottom: panelLift }
|
|
471
496
|
: undefined, onPointerEnter: () => {
|
|
472
497
|
// Reaching the panel (or its bridge) must cancel any hide timer
|
|
@@ -511,5 +536,5 @@ export function PetSprite(props) {
|
|
|
511
536
|
setNameDraft(displayName);
|
|
512
537
|
setRenaming(true);
|
|
513
538
|
}, children: panelLabel('rename', props.t('pet.rename')) })), panelShows('hide') && (_jsx("button", { type: "button", className: styles.action, onClick: props.onHide, children: panelLabel('hide', props.t('pet.hide')) })), props.onGameplayMenu !== undefined && (_jsx("button", { type: "button", className: styles.action, onClick: props.onGameplayMenu, children: props.t('pet.gameplay.menu') }))] })] })) }))] }));
|
|
514
|
-
return createPortal(float, document.body);
|
|
539
|
+
return createPortal(float, props.portalTarget ?? document.body);
|
|
515
540
|
}
|
|
@@ -4,13 +4,16 @@
|
|
|
4
4
|
* endpoints: fetch the registry list once, poll the host snapshot (~2 s),
|
|
5
5
|
* forward interactions, persist drag positions. The pet is host-global (no
|
|
6
6
|
* session dimension), so it mounts directly onto 'document.body' via a
|
|
7
|
-
* single React root
|
|
8
|
-
* new-conversation screen no session exists, and
|
|
9
|
-
* vanish there (issue #48).
|
|
10
|
-
*
|
|
7
|
+
* single React root — the [data-dsh-plugin="pet"] container — rather than a
|
|
8
|
+
* session-scoped slot: on the new-conversation screen no session exists, and
|
|
9
|
+
* a dock-mounted pet would vanish there (issue #48). The sprite chrome
|
|
10
|
+
* portals into that same root, so the root owns the whole surface and a
|
|
11
|
+
* root-keyed suppressor can hide it as one unit. When the pet is hidden the
|
|
12
|
+
* entry becomes a fixed-position summon button.
|
|
11
13
|
* @module @linxin666/dsh-pet/client
|
|
12
14
|
*/
|
|
13
|
-
import type {
|
|
15
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
16
|
+
import type { SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
14
17
|
/** Required services (sessions powers bubble-to-session navigation). */
|
|
15
18
|
export declare const inject: string[];
|
|
16
19
|
/** Re-exported for consumers that type against the injected face. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAGnE,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAA;AA8ElG,wEAAwE;AACxE,eAAO,MAAM,MAAM,UAA2E,CAAA;AAE9F,qEAAqE;AACrE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACxE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC7D,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACtF,YAAY,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AACpE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf;;;;WAIG;QACH,aAAa,CAAC,EAAE;YAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;SAAE,CAAA;KAC1E;CACF;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAoT9C"}
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
* endpoints: fetch the registry list once, poll the host snapshot (~2 s),
|
|
5
5
|
* forward interactions, persist drag positions. The pet is host-global (no
|
|
6
6
|
* session dimension), so it mounts directly onto 'document.body' via a
|
|
7
|
-
* single React root
|
|
8
|
-
* new-conversation screen no session exists, and
|
|
9
|
-
* vanish there (issue #48).
|
|
10
|
-
*
|
|
7
|
+
* single React root — the [data-dsh-plugin="pet"] container — rather than a
|
|
8
|
+
* session-scoped slot: on the new-conversation screen no session exists, and
|
|
9
|
+
* a dock-mounted pet would vanish there (issue #48). The sprite chrome
|
|
10
|
+
* portals into that same root, so the root owns the whole surface and a
|
|
11
|
+
* root-keyed suppressor can hide it as one unit. When the pet is hidden the
|
|
12
|
+
* entry becomes a fixed-position summon button.
|
|
11
13
|
* @module @linxin666/dsh-pet/client
|
|
12
14
|
*/
|
|
13
15
|
import { createElement } from 'react';
|
|
@@ -325,7 +327,11 @@ export function apply(ctx) {
|
|
|
325
327
|
container.dataset.dshPlugin = 'pet';
|
|
326
328
|
document.body.appendChild(container);
|
|
327
329
|
const petRoot = createRoot(container);
|
|
328
|
-
|
|
330
|
+
// The sprite chrome portals into THIS root (not document.body): the
|
|
331
|
+
// root then owns the whole surface, so a root-keyed suppressor (the
|
|
332
|
+
// portrait mobile layer, which hides [data-dsh-plugin="pet"]) really
|
|
333
|
+
// hides the sprite instead of missing the portaled float.
|
|
334
|
+
petRoot.render(createElement(PetDockEntry, { ...injected(), t, portalTarget: container }));
|
|
329
335
|
let uiGone = false;
|
|
330
336
|
disposeUi = () => {
|
|
331
337
|
if (uiGone)
|
|
@@ -72,7 +72,7 @@ export declare const zh: {
|
|
|
72
72
|
readonly 'settings.off': "关";
|
|
73
73
|
readonly 'settings.overridden': "已覆盖";
|
|
74
74
|
readonly 'settings.reset': "恢复默认";
|
|
75
|
-
readonly 'settings.notExposed': "当前 DSH 版本未向设置页暴露本插件的配置命名空间,表单不可用。可编辑
|
|
75
|
+
readonly 'settings.notExposed': "当前 DSH 版本未向设置页暴露本插件的配置命名空间,表单不可用。可编辑 $DSH_HOME/settings.yaml 直接配置,或确认提供该命名空间的插件已挂载其设置域并重启。";
|
|
76
76
|
readonly 'settings.readOnly': "当前部署的设置只读。";
|
|
77
77
|
readonly 'settings.expand': "展开设置";
|
|
78
78
|
readonly 'settings.collapse': "收起设置";
|
|
@@ -151,7 +151,7 @@ export declare const en: {
|
|
|
151
151
|
readonly 'settings.off': "Off";
|
|
152
152
|
readonly 'settings.overridden': "Overridden";
|
|
153
153
|
readonly 'settings.reset': "Reset to default";
|
|
154
|
-
readonly 'settings.notExposed': "This DSH version does not expose this plugin's settings namespace to the configuration page, so the form is unavailable. Edit
|
|
154
|
+
readonly '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.";
|
|
155
155
|
readonly 'settings.readOnly': "This deployment stores settings read-only.";
|
|
156
156
|
readonly 'settings.expand': "Show settings";
|
|
157
157
|
readonly 'settings.collapse': "Hide settings";
|
|
@@ -74,7 +74,7 @@ export const zh = {
|
|
|
74
74
|
'settings.off': '关',
|
|
75
75
|
'settings.overridden': '已覆盖',
|
|
76
76
|
'settings.reset': '恢复默认',
|
|
77
|
-
'settings.notExposed': '当前 DSH 版本未向设置页暴露本插件的配置命名空间,表单不可用。可编辑
|
|
77
|
+
'settings.notExposed': '当前 DSH 版本未向设置页暴露本插件的配置命名空间,表单不可用。可编辑 $DSH_HOME/settings.yaml 直接配置,或确认提供该命名空间的插件已挂载其设置域并重启。',
|
|
78
78
|
'settings.readOnly': '当前部署的设置只读。',
|
|
79
79
|
'settings.expand': '展开设置',
|
|
80
80
|
'settings.collapse': '收起设置',
|
|
@@ -155,7 +155,7 @@ export const en = {
|
|
|
155
155
|
'settings.off': 'Off',
|
|
156
156
|
'settings.overridden': 'Overridden',
|
|
157
157
|
'settings.reset': 'Reset to default',
|
|
158
|
-
'settings.notExposed': 'This DSH version does not expose this plugin\'s settings namespace to the configuration page, so the form is unavailable. Edit
|
|
158
|
+
'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.',
|
|
159
159
|
'settings.readOnly': 'This deployment stores settings read-only.',
|
|
160
160
|
'settings.expand': 'Show settings',
|
|
161
161
|
'settings.collapse': 'Hide settings',
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* only ever read snapshots.
|
|
6
6
|
* @module @linxin666/dsh-pet/client/pet-store
|
|
7
7
|
*/
|
|
8
|
-
import type { EngineStoreHandle, EngineStoreInstance } from '@deepseek-ai/dsh-client-
|
|
8
|
+
import type { EngineStoreHandle, EngineStoreInstance } from '@deepseek-ai/dsh-client-store';
|
|
9
9
|
import type { PetGameplayStateView, PetStateView } from '../service.ts';
|
|
10
10
|
import type { PetInteraction } from '../affinity.ts';
|
|
11
11
|
import type { PetDefinition } from '../registry.ts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pet-store.d.ts","sourceRoot":"","sources":["../../../src/client/pet-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"pet-store.d.ts","sourceRoot":"","sources":["../../../src/client/pet-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAA;AAC3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC1B,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,uDAAuD;IACvD,IAAI,EAAE,cAAc,GAAG,MAAM,CAAA;IAC7B,sDAAsD;IACtD,EAAE,EAAE,MAAM,CAAA;CACX;AAED,wCAAwC;AACxC,MAAM,WAAW,UAAU;IACzB,oEAAoE;IACpE,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,0EAA0E;IAC1E,IAAI,EAAE,aAAa,EAAE,CAAA;IACrB,uBAAuB;IACvB,KAAK,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,CAAA;IACpC,iEAAiE;IACjE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,sCAAsC;IACtC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;CAC7B;AAED,uBAAuB;AACvB,MAAM,MAAM,YAAY,GAAG;IACzB,+CAA+C;IAC/C,WAAW,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAA;IAChE,iCAAiC;IACjC,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,IAAI,CAAA;IAC3D,gCAAgC;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACvF,8BAA8B;IAC9B,WAAW,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAA;IACtE;;;;OAIG;IACH,eAAe,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAA;CACzE,CAAA;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,IAAI,iBAAiB,CAAC,UAAU,EAAE,YAAY,CAAC,CA8B5E;AAED,YAAY,EAAE,cAAc,EAAE,CAAA;AAE9B;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,mBAAmB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAA"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* only ever read snapshots.
|
|
6
6
|
* @module @linxin666/dsh-pet/client/pet-store
|
|
7
7
|
*/
|
|
8
|
-
import { defineStore } from '@deepseek-ai/dsh-client-
|
|
8
|
+
import { defineStore } from '@deepseek-ai/dsh-client-store';
|
|
9
9
|
/** Create the pet store handle (apply world only; never module-level). */
|
|
10
10
|
export function createPetStore() {
|
|
11
11
|
return defineStore({
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* each plugin's client bundle; mirrors the official ui-plugin-config
|
|
7
7
|
* card-store pattern.
|
|
8
8
|
*/
|
|
9
|
-
import type { SettingsScope
|
|
9
|
+
import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
10
|
+
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
10
11
|
/** The write one field's staged text performs when the card is saved. */
|
|
11
12
|
export type FieldWrite = {
|
|
12
13
|
kind: 'set';
|
|
@@ -21,9 +22,11 @@ export interface FieldSpec {
|
|
|
21
22
|
/**
|
|
22
23
|
* Whether the Host treats this field as a secret and redacts its value from
|
|
23
24
|
* the read-back (role('secret') in the section schema). Redacted secrets are
|
|
24
|
-
* never compared against the draft on save
|
|
25
|
-
*
|
|
26
|
-
*
|
|
25
|
+
* never compared against the draft on save: the Host strips them from every
|
|
26
|
+
* wire view layer, so the settled snapshot carries nothing to read back. A
|
|
27
|
+
* staged secret set is judged by the mutation settling; the rest of its
|
|
28
|
+
* batch, when one exists, is still judged by read-back, and the atomic
|
|
29
|
+
* mutation lands every write or none.
|
|
27
30
|
*/
|
|
28
31
|
secret?: boolean;
|
|
29
32
|
/** Render a stored value as draft text; the empty string when the section carries none. */
|
|
@@ -49,8 +52,8 @@ export interface CardShell {
|
|
|
49
52
|
available: boolean;
|
|
50
53
|
/**
|
|
51
54
|
* Whether the namespace is actually served to this client. False when the
|
|
52
|
-
* Host deployment does not expose it (e.g. the
|
|
53
|
-
*
|
|
55
|
+
* Host deployment does not expose it (e.g. the owning plugin's settings
|
|
56
|
+
* domain is not mounted): the card renders an explanation
|
|
54
57
|
* instead of its form, so a missing namespace never looks like a missing
|
|
55
58
|
* plugin.
|
|
56
59
|
*/
|
|
@@ -83,7 +86,7 @@ export interface CardActions {
|
|
|
83
86
|
/** Drop every staged edit. */
|
|
84
87
|
discard: () => void;
|
|
85
88
|
}
|
|
86
|
-
/** One durable write
|
|
89
|
+
/** One durable write inside the save's atomic scope mutation. */
|
|
87
90
|
export interface BatchedWrite {
|
|
88
91
|
/** Field this entry writes. */
|
|
89
92
|
field: string;
|
|
@@ -92,28 +95,6 @@ export interface BatchedWrite {
|
|
|
92
95
|
/** Value for op set (absent for unset). */
|
|
93
96
|
value?: unknown;
|
|
94
97
|
}
|
|
95
|
-
/** Per-field outcome of one batched scope write. */
|
|
96
|
-
export interface BatchedFieldResult {
|
|
97
|
-
/** Field this entry writes. */
|
|
98
|
-
field: string;
|
|
99
|
-
/** Whether the Host accepted this field's write (per the read-back view). */
|
|
100
|
-
landed: boolean;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Result of a batched scope write. The bridge scope posts every planned write
|
|
104
|
-
* in one /mutate so the Host validate hook judges baseURL+model together; a
|
|
105
|
-
* batched refusal fails the whole save rather than per-field.
|
|
106
|
-
*/
|
|
107
|
-
export interface BatchResult {
|
|
108
|
-
/** Whether the whole mutate was accepted. */
|
|
109
|
-
ok: boolean;
|
|
110
|
-
/** Per-field success, in the request order (always present when ok). */
|
|
111
|
-
fields: BatchedFieldResult[];
|
|
112
|
-
/** Host rejection code (mutate refused). */
|
|
113
|
-
code?: string;
|
|
114
|
-
/** Host rejection message (mutate refused). */
|
|
115
|
-
message?: string;
|
|
116
|
-
}
|
|
117
98
|
/** Constraints a numeric field's accepted drafts must satisfy, mirroring the host schema. */
|
|
118
99
|
export interface NumberConstraints {
|
|
119
100
|
/** The accepted value must be a whole number. */
|
|
@@ -128,8 +109,8 @@ export declare function textField(field: string): FieldSpec;
|
|
|
128
109
|
/**
|
|
129
110
|
* A free-text field the Host treats as a secret and redacts from the read-back
|
|
130
111
|
* (role('secret') in the section schema). The card still edits it like text,
|
|
131
|
-
* but a save never compares the redacted value back
|
|
132
|
-
*
|
|
112
|
+
* but a save never compares the redacted value back: the staged set is judged
|
|
113
|
+
* by the mutation settling (see {@link FieldSpec.secret}).
|
|
133
114
|
*/
|
|
134
115
|
export declare function secretField(field: string): FieldSpec;
|
|
135
116
|
/** A boolean field, edited through true/false draft text. */
|
|
@@ -171,19 +152,22 @@ export declare class CardForm<T> {
|
|
|
171
152
|
/** The actions the card's slot registration injects. */
|
|
172
153
|
actions(): CardActions;
|
|
173
154
|
/**
|
|
174
|
-
* Write every staged edit, then re-seed from
|
|
155
|
+
* Write every staged edit in one atomic scope mutation, then re-seed from
|
|
156
|
+
* what the Host accepted.
|
|
175
157
|
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
158
|
+
* The whole batch rides one mutate, so cross-field validate hooks
|
|
159
|
+
* (baseURL+model) judge it as a unit: the Host either applies every write
|
|
160
|
+
* or refuses the batch. The 0.1.2 scope contract never rejects a refused
|
|
161
|
+
* mutation — the scope recovers with a fresh Host view and resolves — so
|
|
162
|
+
* resolution alone proves nothing: the outcome is judged by reading the
|
|
163
|
+
* settled snapshot back, one planned write at a time, and one missed write
|
|
164
|
+
* fails the whole save. A scope that still rejects on refusal (the dsh-web
|
|
165
|
+
* bridge scope) reports through the same failure path with its rejection
|
|
166
|
+
* message. A save that did not land keeps its drafts, so the user can
|
|
167
|
+
* correct them instead of retyping.
|
|
168
|
+
* @returns settlement after the mutation and the read-back.
|
|
183
169
|
*/
|
|
184
170
|
save(): Promise<void>;
|
|
185
|
-
/** The scope's batch surface when it supports one; undefined conservatively otherwise. */
|
|
186
|
-
private batchedScope;
|
|
187
171
|
/**
|
|
188
172
|
* Every staged edit a save would write. An entry whose draft is not a value
|
|
189
173
|
* its field accepts carries no write: the form is still dirty, and the save
|
|
@@ -192,8 +176,16 @@ export declare class CardForm<T> {
|
|
|
192
176
|
* @returns the planned writes, in the order the fields were staged.
|
|
193
177
|
*/
|
|
194
178
|
private plan;
|
|
195
|
-
|
|
196
|
-
|
|
179
|
+
/**
|
|
180
|
+
* Read-back judgment for a planned set: the user layer must hold the
|
|
181
|
+
* intended value once the mutation has settled.
|
|
182
|
+
*/
|
|
183
|
+
private landedSet;
|
|
184
|
+
/**
|
|
185
|
+
* Read-back judgment for a planned unset: the field must be gone from the
|
|
186
|
+
* user layer once the mutation has settled.
|
|
187
|
+
*/
|
|
188
|
+
private landedUnset;
|
|
197
189
|
private stage;
|
|
198
190
|
private specOf;
|
|
199
191
|
private snapshotOf;
|