@linxin666/dsh-pet 0.1.16 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +93 -74
- package/lib/client.js.map +1 -1
- package/lib/types/client/PetSettingsCard.d.ts +11 -5
- package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PetSettingsCard.js +7 -1
- package/lib/types/client/PluginSettingsCard.d.ts +11 -0
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PluginSettingsCard.js +11 -5
- package/lib/types/client/index.d.ts +3 -22
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +12 -11
- package/lib/types/client/locales.js +2 -2
- package/package.json +1 -1
- package/src/client/PetSettingsCard.tsx +27 -6
- package/src/client/PluginSettingsCard.tsx +50 -47
- package/src/client/index.ts +13 -29
- package/src/client/locales.ts +2 -2
- package/src/client/settings-card.module.css +9 -0
- package/src/client/settings-section.module.css +5 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PetSettingsCard.tsx"],"names":[],"mappings":"AAAA
|
|
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,wCAAwC,CAAA;AAI1F,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;CACf;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,wEAAwE;IACxE,UAAU,EAAE,SAAS;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CACxD;AAED,gEAAgE;AAChE,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IACtD,KAAK,EAAE;QACL,iEAAiE;QACjE,eAAe,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAA;KACrD,CAAA;CACF;AAeD,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,MAAM,CAAQ;IACtB,OAAO,CAAC,QAAQ,CAAI;IAEpB,uEAAuE;gBAC3D,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC;IAa7C,0EAA0E;YAC5D,QAAQ;IAgBtB,OAAO,CAAC,UAAU;IAalB;;;OAGG;IACH,MAAM,IAAI,mBAAmB;CAG9B;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,+BAuF1D;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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { PluginSettingsCard, ValueField, BooleanField, ChoiceField } from "./PluginSettingsCard.js";
|
|
3
3
|
import { CardForm, booleanField, choiceField, numberField } from "./settings-form.js";
|
|
4
|
+
import sectionCss from './settings-section.module.css';
|
|
4
5
|
/** Fetch the registry list (the same data the sprite renders from). */
|
|
5
6
|
async function fetchPetChoices() {
|
|
6
7
|
const response = await fetch('/api/pet/pets');
|
|
@@ -86,5 +87,10 @@ export function PetSettingsCard(props) {
|
|
|
86
87
|
invalidLabel: t('settings.invalidNumber'),
|
|
87
88
|
disabled,
|
|
88
89
|
};
|
|
89
|
-
return (_jsxs(PluginSettingsCard, { t: t, titleKey: "settings.title", descriptionKey: "settings.description", state: state, onSave: props.save, onDiscard: props.discard, children: [_jsx(BooleanField, { id: "settings-pet-enabled", label: t('settings.enabled'), hint: t('settings.enabledHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.enabled, onEdit: (text) => { props.edit('enabled', text); }, onReset: () => { props.resetField('enabled'); } }), _jsx(ChoiceField, { id: "settings-pet-pet", label: t('settings.pet'), hint: t('settings.petHint'), inheritLabel: t('settings.inherit'), ...fieldProps, ...state.petId, choices: state.petChoices, onEdit: (text) => { props.edit('petId', text); }, onReset: () => { props.resetField('petId'); } }), _jsx(BooleanField, { id: "settings-pet-visible", label: t('settings.visible'), hint: t('settings.visibleHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.visible, onEdit: (text) => { props.edit('visible', text); }, onReset: () => { props.resetField('visible'); } }), _jsx(ValueField, { id: "settings-pet-size", label: t('settings.size'), hint: t('settings.sizeHint'), numeric: true, ...fieldProps, ...state.size, onEdit: (text) => { props.edit('size', text); }, onReset: () => { props.resetField('size'); } }), _jsx(ValueField, { id: "settings-pet-right", label: t('settings.right'), hint: t('settings.rightHint'), numeric: true, ...fieldProps, ...state.right, onEdit: (text) => { props.edit('right', text); }, onReset: () => { props.resetField('right'); } }), _jsx(ValueField, { id: "settings-pet-bottom", label: t('settings.bottom'), hint: t('settings.bottomHint'), numeric: true, ...fieldProps, ...state.bottom, onEdit: (text) => { props.edit('bottom', text); }, onReset: () => { props.resetField('bottom'); } })] }));
|
|
90
|
+
return (_jsxs(PluginSettingsCard, { t: t, titleKey: "settings.title", descriptionKey: "settings.description", state: state, onSave: props.save, onDiscard: props.discard, alwaysOpen: true, children: [_jsx(BooleanField, { id: "settings-pet-enabled", label: t('settings.enabled'), hint: t('settings.enabledHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.enabled, onEdit: (text) => { props.edit('enabled', text); }, onReset: () => { props.resetField('enabled'); } }), _jsx(ChoiceField, { id: "settings-pet-pet", label: t('settings.pet'), hint: t('settings.petHint'), inheritLabel: t('settings.inherit'), ...fieldProps, ...state.petId, choices: state.petChoices, onEdit: (text) => { props.edit('petId', text); }, onReset: () => { props.resetField('petId'); } }), _jsx(BooleanField, { id: "settings-pet-visible", label: t('settings.visible'), hint: t('settings.visibleHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.visible, onEdit: (text) => { props.edit('visible', text); }, onReset: () => { props.resetField('visible'); } }), _jsx(ValueField, { id: "settings-pet-size", label: t('settings.size'), hint: t('settings.sizeHint'), numeric: true, ...fieldProps, ...state.size, onEdit: (text) => { props.edit('size', text); }, onReset: () => { props.resetField('size'); } }), _jsx(ValueField, { id: "settings-pet-right", label: t('settings.right'), hint: t('settings.rightHint'), numeric: true, ...fieldProps, ...state.right, onEdit: (text) => { props.edit('right', text); }, onReset: () => { props.resetField('right'); } }), _jsx(ValueField, { id: "settings-pet-bottom", label: t('settings.bottom'), hint: t('settings.bottomHint'), numeric: true, ...fieldProps, ...state.bottom, onEdit: (text) => { props.edit('bottom', text); }, onReset: () => { props.resetField('bottom'); } })] }));
|
|
91
|
+
}
|
|
92
|
+
/** Render the pet settings card as a first-level settings page. */
|
|
93
|
+
export function PetSettingsSection(props) {
|
|
94
|
+
const { t, usePetSettingsCard, save, discard, edit, resetField } = props;
|
|
95
|
+
return (_jsx("ul", { className: sectionCss.sectionList, children: _jsx(PetSettingsCard, { t: t, usePetSettingsCard: usePetSettingsCard, save: save, discard: discard, edit: edit, resetField: resetField }) }));
|
|
90
96
|
}
|
|
@@ -28,6 +28,17 @@ export interface PluginSettingsCardProps<TKey extends string = string> {
|
|
|
28
28
|
onSave: () => void;
|
|
29
29
|
/** Drop every staged edit. */
|
|
30
30
|
onDiscard: () => void;
|
|
31
|
+
/**
|
|
32
|
+
* Render the controls expanded on arrival (still collapsible). Defaults to
|
|
33
|
+
* true: promoted first-level sections show their settings immediately.
|
|
34
|
+
*/
|
|
35
|
+
defaultOpen?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Render without the collapse affordance: a static header and an always
|
|
38
|
+
* visible body. Used by first-level settings sections whose nav entry
|
|
39
|
+
* already provides the selection.
|
|
40
|
+
*/
|
|
41
|
+
alwaysOpen?: boolean;
|
|
31
42
|
/** The plugin's controls. */
|
|
32
43
|
children: ReactNode;
|
|
33
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluginSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PluginSettingsCard.tsx"],"names":[],"mappings":"AACA;;;;;;;GAOG;AAEH,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAGnD,oGAAoG;AACpG,eAAO,MAAM,cAAc,kMAUjB,CAAA;AAEV,6CAA6C;AAC7C,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAEzD,oEAAoE;AACpE,MAAM,MAAM,eAAe,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,IAAI,IAAI,GAAG,WAAW,CAAA;AAE9E,wDAAwD;AACxD,MAAM,WAAW,uBAAuB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IACnE,0CAA0C;IAC1C,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,MAAM,CAAA;IACnF,uCAAuC;IACvC,QAAQ,EAAE,IAAI,CAAA;IACd,4EAA4E;IAC5E,cAAc,EAAE,IAAI,CAAA;IACpB,kFAAkF;IAClF,KAAK,EAAE,SAAS,CAAA;IAChB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,6BAA6B;IAC7B,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,uBAAuB,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"PluginSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PluginSettingsCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -28,24 +28,30 @@ export const CARD_COPY_KEYS = [
|
|
|
28
28
|
* @returns the card, or nothing while the namespace is still loading.
|
|
29
29
|
*/
|
|
30
30
|
export function PluginSettingsCard(props) {
|
|
31
|
-
const [open, setOpen] = useState(
|
|
32
|
-
const { state } = props;
|
|
31
|
+
const [open, setOpen] = useState(props.defaultOpen ?? true);
|
|
32
|
+
const { state, alwaysOpen } = props;
|
|
33
33
|
if (!state.available)
|
|
34
34
|
return null;
|
|
35
35
|
const title = props.t(props.titleKey);
|
|
36
36
|
const description = props.t(props.descriptionKey);
|
|
37
37
|
const blocked = !state.dirty || state.invalid || state.saving;
|
|
38
|
-
const
|
|
38
|
+
const expanded = alwaysOpen === true || open;
|
|
39
|
+
const cardClass = expanded ? `${css.cardOpen} ${css.card}` : css.card;
|
|
40
|
+
// With alwaysOpen the nav entry already provides the selection, so the
|
|
41
|
+
// header is a static title row instead of a disclosure button.
|
|
42
|
+
const header = alwaysOpen === true
|
|
43
|
+
? (_jsxs("div", { className: css.headerStatic, children: [_jsxs("span", { className: css.headText, children: [_jsx("span", { className: css.name, title: title, children: title }), _jsx("span", { className: css.description, title: description, children: description })] }), state.dirty ? _jsx("span", { className: css.pending, title: props.t('settings.unsaved'), children: props.t('settings.unsaved') }) : null] }))
|
|
44
|
+
: (_jsxs("button", { type: "button", className: css.header, "aria-expanded": open, "aria-label": `${props.t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`, onClick: () => { setOpen(!open); }, children: [_jsxs("span", { className: css.headText, children: [_jsx("span", { className: css.name, title: title, children: title }), _jsx("span", { className: css.description, title: description, children: description })] }), state.dirty ? _jsx("span", { className: css.pending, title: props.t('settings.unsaved'), children: props.t('settings.unsaved') }) : null, _jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: open ? `${css.chevron} ${css.chevronOpen}` : css.chevron, children: _jsx("path", { 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", fill: "currentColor" }) })] }));
|
|
39
45
|
// The namespace exists but the Host does not serve it to this client (the
|
|
40
46
|
// official settings allowlist omits third-party namespaces): show a card
|
|
41
47
|
// that explains the gap instead of vanishing, so a missing card never
|
|
42
48
|
// reads as a missing plugin.
|
|
43
49
|
if (!state.exposed) {
|
|
44
|
-
return (_jsxs("li", { className: cardClass, children: [
|
|
50
|
+
return (_jsxs("li", { className: cardClass, children: [header, expanded
|
|
45
51
|
? (_jsx("div", { className: css.body, children: _jsx("p", { className: css.notExposed, role: "status", children: props.t('settings.notExposed') }) }))
|
|
46
52
|
: null] }));
|
|
47
53
|
}
|
|
48
|
-
return (_jsxs("li", { className: cardClass, children: [
|
|
54
|
+
return (_jsxs("li", { className: cardClass, children: [header, expanded
|
|
49
55
|
? (_jsxs("div", { className: css.body, children: [!state.writable ? _jsx("p", { className: css.readOnly, role: "status", children: props.t('settings.readOnly') }) : null, props.children, _jsxs("div", { className: css.footer, children: [state.failed
|
|
50
56
|
? (_jsxs("p", { className: css.failed, role: "status", children: [props.t('settings.saveFailed'), state.failedReason ? ' - ' + state.failedReason : ''] }))
|
|
51
57
|
: null, _jsx("button", { type: "button", className: css.discard, disabled: !state.dirty || state.saving, onClick: props.onDiscard, children: props.t('settings.discard') }), _jsx("button", { type: "button", className: css.save, disabled: blocked, onClick: props.onSave, children: props.t(!state.saving ? 'settings.save' : 'settings.saving') })] })] }))
|
|
@@ -18,27 +18,8 @@ export type { PetInjected, PetDockEntryProps } from './PetDockEntry.tsx';
|
|
|
18
18
|
export type { PetSpriteProps } from './PetSprite.tsx';
|
|
19
19
|
export type { PetUiState, PetFeedback } from './pet-store.ts';
|
|
20
20
|
export type { PetSettingsCardFace, PetSettingsCardState } from './PetSettingsCard.tsx';
|
|
21
|
+
export type { PetSettingsSectionProps } from './PetSettingsCard.tsx';
|
|
21
22
|
export type { PetDefinition } from '../registry.ts';
|
|
22
|
-
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
23
|
-
interface SlotMap {
|
|
24
|
-
/**
|
|
25
|
-
* The child slot the Web UI plugin group declares; this card registers
|
|
26
|
-
* into the group instead of the top-level 'settings.plugin.item' list.
|
|
27
|
-
* Spelled here with the same shape so this package can register without
|
|
28
|
-
* depending on the sibling UI package.
|
|
29
|
-
*/
|
|
30
|
-
'web-ui.plugin.item': {
|
|
31
|
-
kind: 'list';
|
|
32
|
-
scope: 'root';
|
|
33
|
-
owner: SettingsPluginItemOwnerProps;
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
/** Owner share of a plugin card (the group card supplies nothing). */
|
|
38
|
-
export interface SettingsPluginItemOwnerProps {
|
|
39
|
-
/** Marker field: card owner props are intentionally empty. */
|
|
40
|
-
children?: never;
|
|
41
|
-
}
|
|
42
23
|
declare module '@deepseek-ai/cordis' {
|
|
43
24
|
interface Context {
|
|
44
25
|
/**
|
|
@@ -53,8 +34,8 @@ declare module '@deepseek-ai/cordis' {
|
|
|
53
34
|
}
|
|
54
35
|
/**
|
|
55
36
|
* Client plugin body: register dictionaries, mount the global pet entry and
|
|
56
|
-
* poll loop while the plugin is enabled, and seat the settings card
|
|
57
|
-
*
|
|
37
|
+
* poll loop while the plugin is enabled, and seat the settings card as a
|
|
38
|
+
* first-level settings section.
|
|
58
39
|
* @param ctx - client root context.
|
|
59
40
|
*/
|
|
60
41
|
export declare function apply(ctx: ClientContext): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AA6D7G,yBAAyB;AACzB,eAAO,MAAM,MAAM,UAA+D,CAAA;AAElF,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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AA6D7G,yBAAyB;AACzB,eAAO,MAAM,MAAM,UAA+D,CAAA;AAElF,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,CAoL9C"}
|
|
@@ -14,7 +14,7 @@ import { createElement } from 'react';
|
|
|
14
14
|
import { createRoot } from 'react-dom/client';
|
|
15
15
|
import { createPetStore } from "./pet-store.js";
|
|
16
16
|
import { PetDockEntry } from "./PetDockEntry.js";
|
|
17
|
-
import {
|
|
17
|
+
import { PetSettingsSection, PetSettingsCardController } from "./PetSettingsCard.js";
|
|
18
18
|
import { NS, en, zh, t } from "./locales.js";
|
|
19
19
|
/** Same-origin JSON fetch helper (GET without body, POST with JSON body). */
|
|
20
20
|
async function petFetch(path, body) {
|
|
@@ -48,8 +48,8 @@ const PET_SETTINGS_NS = 'pet';
|
|
|
48
48
|
export const inject = ['slots', 'locale', 'connection', 'settingsScope', 'remote'];
|
|
49
49
|
/**
|
|
50
50
|
* Client plugin body: register dictionaries, mount the global pet entry and
|
|
51
|
-
* poll loop while the plugin is enabled, and seat the settings card
|
|
52
|
-
*
|
|
51
|
+
* poll loop while the plugin is enabled, and seat the settings card as a
|
|
52
|
+
* first-level settings section.
|
|
53
53
|
* @param ctx - client root context.
|
|
54
54
|
*/
|
|
55
55
|
export function apply(ctx) {
|
|
@@ -62,17 +62,18 @@ export function apply(ctx) {
|
|
|
62
62
|
? snapshot.value?.enabled ?? true
|
|
63
63
|
: snapshot.status === 'unavailable';
|
|
64
64
|
};
|
|
65
|
-
//
|
|
66
|
-
// namespace,
|
|
65
|
+
// First-level settings section: one staged form over the 'pet' settings
|
|
66
|
+
// namespace, registered as a top-level settings page. The controller loads
|
|
67
67
|
// the petId choices from the registry endpoint itself.
|
|
68
68
|
const petSettings = new PetSettingsCardController(settingsScope);
|
|
69
|
-
ctx.slots.inject('
|
|
70
|
-
name: '
|
|
71
|
-
id: 'pet
|
|
72
|
-
order:
|
|
73
|
-
|
|
69
|
+
ctx.slots.inject('settings.section', () => ctx.slots.register({
|
|
70
|
+
name: 'settings.section',
|
|
71
|
+
id: 'pet',
|
|
72
|
+
order: 130,
|
|
73
|
+
label: () => ctx.locale.bind('pet')('settings.title'),
|
|
74
|
+
locale: 'pet',
|
|
74
75
|
inject: () => petSettings.inject(),
|
|
75
|
-
},
|
|
76
|
+
}, PetSettingsSection));
|
|
76
77
|
// The global pet entry, its store, and the poll loop live while the plugin
|
|
77
78
|
// is enabled; toggling the setting off hides the pet and stops polling.
|
|
78
79
|
let disposeUi;
|
|
@@ -17,7 +17,7 @@ export const zh = {
|
|
|
17
17
|
'pet.treats': '小鱼干 ×{n}',
|
|
18
18
|
'pet.state.loading': '宠物正在赶来…',
|
|
19
19
|
'pet.state.error': '宠物迷路了(连接失败)',
|
|
20
|
-
//
|
|
20
|
+
// 一级设置页(settings.section 席位)。
|
|
21
21
|
'settings.title': '宠物',
|
|
22
22
|
'settings.description': '选择宠物并调整它的显示布局。',
|
|
23
23
|
'settings.pet': '宠物',
|
|
@@ -61,7 +61,7 @@ export const en = {
|
|
|
61
61
|
'pet.treats': 'Treats ×{n}',
|
|
62
62
|
'pet.state.loading': 'The pet is on its way…',
|
|
63
63
|
'pet.state.error': 'The pet is lost (connection failed)',
|
|
64
|
-
//
|
|
64
|
+
// First-level settings section (the `settings.section` seat).
|
|
65
65
|
'settings.title': 'Pet',
|
|
66
66
|
'settings.description': 'Pick a pet and tune its display layout.',
|
|
67
67
|
'settings.pet': 'Pet',
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linxin666/dsh-pet",
|
|
3
3
|
"description": "Multi-pet companion plugin for the dsh web GUI: a registry-driven floating pet that reacts to model activity, with per-pet naming, petting/feeding interactions and an affinity score",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.18",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The pet settings card: pet selection plus display layout, bound to the
|
|
3
|
-
* 'pet' settings namespace the host plugin registers.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* 'pet' settings namespace the host plugin registers. Rendered as an
|
|
4
|
+
* always-open first-level settings page; the section wrapper below mounts it
|
|
5
|
+
* as the content of the top-level 'settings.section' nav entry. The petId
|
|
6
|
+
* choices come from the registry endpoint ('/api/pet/pets') — the same list
|
|
7
|
+
* the sprite renders from — so the card carries no per-pet knowledge.
|
|
7
8
|
*/
|
|
8
9
|
|
|
10
|
+
import type { ReactNode } from 'react'
|
|
9
11
|
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
|
10
12
|
import type { SettingsScope, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'
|
|
13
|
+
// Type-only: pulls the settings-surface SlotMap merge (the 'settings.section' entry).
|
|
14
|
+
import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
|
|
11
15
|
import { PluginSettingsCard, ValueField, BooleanField, ChoiceField } from './PluginSettingsCard.tsx'
|
|
12
16
|
import { CardForm, booleanField, choiceField, numberField, type CardActions, type CardShell, type FieldState as CardFieldState } from './settings-form.ts'
|
|
17
|
+
import sectionCss from './settings-section.module.css'
|
|
13
18
|
|
|
14
19
|
/** The pet's settings fields this card edits (the namespace's full schema). */
|
|
15
20
|
export interface PetSettings {
|
|
@@ -133,8 +138,7 @@ export class PetSettingsCardController {
|
|
|
133
138
|
|
|
134
139
|
/** Props the renderer binds for the pet settings card. */
|
|
135
140
|
export type PetSettingsCardProps =
|
|
136
|
-
|
|
137
|
-
& PropsLocale<'pet'>
|
|
141
|
+
PropsLocale<'pet'>
|
|
138
142
|
& InjectFace<PetSettingsCardFace>
|
|
139
143
|
|
|
140
144
|
/**
|
|
@@ -160,6 +164,7 @@ export function PetSettingsCard(props: PetSettingsCardProps) {
|
|
|
160
164
|
state={state}
|
|
161
165
|
onSave={props.save}
|
|
162
166
|
onDiscard={props.discard}
|
|
167
|
+
alwaysOpen
|
|
163
168
|
>
|
|
164
169
|
<BooleanField
|
|
165
170
|
id="settings-pet-enabled"
|
|
@@ -229,3 +234,19 @@ export function PetSettingsCard(props: PetSettingsCardProps) {
|
|
|
229
234
|
</PluginSettingsCard>
|
|
230
235
|
)
|
|
231
236
|
}
|
|
237
|
+
|
|
238
|
+
/** Props the settings section binds for the pet card page. */
|
|
239
|
+
export type PetSettingsSectionProps =
|
|
240
|
+
PropsRuntime<'settings.section'>
|
|
241
|
+
& PropsLocale<'pet'>
|
|
242
|
+
& InjectFace<PetSettingsCardFace>
|
|
243
|
+
|
|
244
|
+
/** Render the pet settings card as a first-level settings page. */
|
|
245
|
+
export function PetSettingsSection(props: PetSettingsSectionProps): ReactNode {
|
|
246
|
+
const { t, usePetSettingsCard, save, discard, edit, resetField } = props
|
|
247
|
+
return (
|
|
248
|
+
<ul className={sectionCss.sectionList}>
|
|
249
|
+
<PetSettingsCard t={t} usePetSettingsCard={usePetSettingsCard} save={save} discard={discard} edit={edit} resetField={resetField} />
|
|
250
|
+
</ul>
|
|
251
|
+
)
|
|
252
|
+
}
|
|
@@ -45,6 +45,17 @@ export interface PluginSettingsCardProps<TKey extends string = string> {
|
|
|
45
45
|
onSave: () => void
|
|
46
46
|
/** Drop every staged edit. */
|
|
47
47
|
onDiscard: () => void
|
|
48
|
+
/**
|
|
49
|
+
* Render the controls expanded on arrival (still collapsible). Defaults to
|
|
50
|
+
* true: promoted first-level sections show their settings immediately.
|
|
51
|
+
*/
|
|
52
|
+
defaultOpen?: boolean
|
|
53
|
+
/**
|
|
54
|
+
* Render without the collapse affordance: a static header and an always
|
|
55
|
+
* visible body. Used by first-level settings sections whose nav entry
|
|
56
|
+
* already provides the selection.
|
|
57
|
+
*/
|
|
58
|
+
alwaysOpen?: boolean
|
|
48
59
|
/** The plugin's controls. */
|
|
49
60
|
children: ReactNode
|
|
50
61
|
}
|
|
@@ -55,57 +66,27 @@ export interface PluginSettingsCardProps<TKey extends string = string> {
|
|
|
55
66
|
* @returns the card, or nothing while the namespace is still loading.
|
|
56
67
|
*/
|
|
57
68
|
export function PluginSettingsCard<TKey extends string = string>(props: PluginSettingsCardProps<TKey>) {
|
|
58
|
-
const [open, setOpen] = useState(
|
|
59
|
-
const { state } = props
|
|
69
|
+
const [open, setOpen] = useState(props.defaultOpen ?? true)
|
|
70
|
+
const { state, alwaysOpen } = props
|
|
60
71
|
if (!state.available) return null
|
|
61
72
|
const title = props.t(props.titleKey)
|
|
62
73
|
const description = props.t(props.descriptionKey)
|
|
63
74
|
const blocked = !state.dirty || state.invalid || state.saving
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
onClick={() => { setOpen(!open) }}
|
|
78
|
-
>
|
|
79
|
-
<span className={css.headText}>
|
|
80
|
-
<span className={css.name} title={title}>{title}</span>
|
|
81
|
-
<span className={css.description} title={description}>{description}</span>
|
|
82
|
-
</span>
|
|
83
|
-
<svg
|
|
84
|
-
width="14"
|
|
85
|
-
height="14"
|
|
86
|
-
viewBox="0 0 14 14"
|
|
87
|
-
fill="none"
|
|
88
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
89
|
-
className={open ? `${css.chevron} ${css.chevronOpen}` : css.chevron}
|
|
90
|
-
>
|
|
91
|
-
<path
|
|
92
|
-
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"
|
|
93
|
-
fill="currentColor"
|
|
94
|
-
/>
|
|
95
|
-
</svg>
|
|
96
|
-
</button>
|
|
97
|
-
{open
|
|
98
|
-
? (
|
|
99
|
-
<div className={css.body}>
|
|
100
|
-
<p className={css.notExposed} role="status">{props.t('settings.notExposed')}</p>
|
|
101
|
-
</div>
|
|
102
|
-
)
|
|
103
|
-
: null}
|
|
104
|
-
</li>
|
|
75
|
+
const expanded = alwaysOpen === true || open
|
|
76
|
+
const cardClass = expanded ? `${css.cardOpen} ${css.card}` : css.card
|
|
77
|
+
// With alwaysOpen the nav entry already provides the selection, so the
|
|
78
|
+
// header is a static title row instead of a disclosure button.
|
|
79
|
+
const header = alwaysOpen === true
|
|
80
|
+
? (
|
|
81
|
+
<div className={css.headerStatic}>
|
|
82
|
+
<span className={css.headText}>
|
|
83
|
+
<span className={css.name} title={title}>{title}</span>
|
|
84
|
+
<span className={css.description} title={description}>{description}</span>
|
|
85
|
+
</span>
|
|
86
|
+
{state.dirty ? <span className={css.pending} title={props.t('settings.unsaved')}>{props.t('settings.unsaved')}</span> : null}
|
|
87
|
+
</div>
|
|
105
88
|
)
|
|
106
|
-
|
|
107
|
-
return (
|
|
108
|
-
<li className={cardClass}>
|
|
89
|
+
: (
|
|
109
90
|
<button
|
|
110
91
|
type="button"
|
|
111
92
|
className={css.header}
|
|
@@ -132,7 +113,29 @@ export function PluginSettingsCard<TKey extends string = string>(props: PluginSe
|
|
|
132
113
|
/>
|
|
133
114
|
</svg>
|
|
134
115
|
</button>
|
|
135
|
-
|
|
116
|
+
)
|
|
117
|
+
// The namespace exists but the Host does not serve it to this client (the
|
|
118
|
+
// official settings allowlist omits third-party namespaces): show a card
|
|
119
|
+
// that explains the gap instead of vanishing, so a missing card never
|
|
120
|
+
// reads as a missing plugin.
|
|
121
|
+
if (!state.exposed) {
|
|
122
|
+
return (
|
|
123
|
+
<li className={cardClass}>
|
|
124
|
+
{header}
|
|
125
|
+
{expanded
|
|
126
|
+
? (
|
|
127
|
+
<div className={css.body}>
|
|
128
|
+
<p className={css.notExposed} role="status">{props.t('settings.notExposed')}</p>
|
|
129
|
+
</div>
|
|
130
|
+
)
|
|
131
|
+
: null}
|
|
132
|
+
</li>
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
return (
|
|
136
|
+
<li className={cardClass}>
|
|
137
|
+
{header}
|
|
138
|
+
{expanded
|
|
136
139
|
? (
|
|
137
140
|
<div className={css.body}>
|
|
138
141
|
{!state.writable ? <p className={css.readOnly} role="status">{props.t('settings.readOnly')}</p> : null}
|
package/src/client/index.ts
CHANGED
|
@@ -26,7 +26,7 @@ import { createElement } from 'react'
|
|
|
26
26
|
import { createRoot } from 'react-dom/client'
|
|
27
27
|
import { createPetStore, type PetStoreInstance } from './pet-store.ts'
|
|
28
28
|
import { PetDockEntry, type PetInjected } from './PetDockEntry.tsx'
|
|
29
|
-
import {
|
|
29
|
+
import { PetSettingsSection, PetSettingsCardController, type PetSettings } from './PetSettingsCard.tsx'
|
|
30
30
|
import { NS, en, zh, t } from './locales.ts'
|
|
31
31
|
|
|
32
32
|
/** The host pet API as the browser sees it (same-origin JSON endpoints). */
|
|
@@ -80,26 +80,9 @@ export type { PetInjected, PetDockEntryProps } from './PetDockEntry.tsx'
|
|
|
80
80
|
export type { PetSpriteProps } from './PetSprite.tsx'
|
|
81
81
|
export type { PetUiState, PetFeedback } from './pet-store.ts'
|
|
82
82
|
export type { PetSettingsCardFace, PetSettingsCardState } from './PetSettingsCard.tsx'
|
|
83
|
+
export type { PetSettingsSectionProps } from './PetSettingsCard.tsx'
|
|
83
84
|
export type { PetDefinition } from '../registry.ts'
|
|
84
85
|
|
|
85
|
-
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
86
|
-
interface SlotMap {
|
|
87
|
-
/**
|
|
88
|
-
* The child slot the Web UI plugin group declares; this card registers
|
|
89
|
-
* into the group instead of the top-level 'settings.plugin.item' list.
|
|
90
|
-
* Spelled here with the same shape so this package can register without
|
|
91
|
-
* depending on the sibling UI package.
|
|
92
|
-
*/
|
|
93
|
-
'web-ui.plugin.item': { kind: 'list'; scope: 'root'; owner: SettingsPluginItemOwnerProps }
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** Owner share of a plugin card (the group card supplies nothing). */
|
|
98
|
-
export interface SettingsPluginItemOwnerProps {
|
|
99
|
-
/** Marker field: card owner props are intentionally empty. */
|
|
100
|
-
children?: never
|
|
101
|
-
}
|
|
102
|
-
|
|
103
86
|
declare module '@deepseek-ai/cordis' {
|
|
104
87
|
interface Context {
|
|
105
88
|
/**
|
|
@@ -113,8 +96,8 @@ declare module '@deepseek-ai/cordis' {
|
|
|
113
96
|
|
|
114
97
|
/**
|
|
115
98
|
* Client plugin body: register dictionaries, mount the global pet entry and
|
|
116
|
-
* poll loop while the plugin is enabled, and seat the settings card
|
|
117
|
-
*
|
|
99
|
+
* poll loop while the plugin is enabled, and seat the settings card as a
|
|
100
|
+
* first-level settings section.
|
|
118
101
|
* @param ctx - client root context.
|
|
119
102
|
*/
|
|
120
103
|
export function apply(ctx: ClientContext): void {
|
|
@@ -129,17 +112,18 @@ export function apply(ctx: ClientContext): void {
|
|
|
129
112
|
: snapshot.status === 'unavailable'
|
|
130
113
|
}
|
|
131
114
|
|
|
132
|
-
//
|
|
133
|
-
// namespace,
|
|
115
|
+
// First-level settings section: one staged form over the 'pet' settings
|
|
116
|
+
// namespace, registered as a top-level settings page. The controller loads
|
|
134
117
|
// the petId choices from the registry endpoint itself.
|
|
135
118
|
const petSettings = new PetSettingsCardController(settingsScope)
|
|
136
|
-
ctx.slots.inject('
|
|
137
|
-
name: '
|
|
138
|
-
id: 'pet
|
|
139
|
-
order:
|
|
140
|
-
|
|
119
|
+
ctx.slots.inject('settings.section', () => ctx.slots.register({
|
|
120
|
+
name: 'settings.section',
|
|
121
|
+
id: 'pet',
|
|
122
|
+
order: 130,
|
|
123
|
+
label: () => ctx.locale.bind('pet')('settings.title'),
|
|
124
|
+
locale: 'pet',
|
|
141
125
|
inject: () => petSettings.inject(),
|
|
142
|
-
},
|
|
126
|
+
}, PetSettingsSection))
|
|
143
127
|
|
|
144
128
|
// The global pet entry, its store, and the poll loop live while the plugin
|
|
145
129
|
// is enabled; toggling the setting off hides the pet and stops polling.
|
package/src/client/locales.ts
CHANGED
|
@@ -19,7 +19,7 @@ export const zh = {
|
|
|
19
19
|
'pet.treats': '小鱼干 ×{n}',
|
|
20
20
|
'pet.state.loading': '宠物正在赶来…',
|
|
21
21
|
'pet.state.error': '宠物迷路了(连接失败)',
|
|
22
|
-
//
|
|
22
|
+
// 一级设置页(settings.section 席位)。
|
|
23
23
|
'settings.title': '宠物',
|
|
24
24
|
'settings.description': '选择宠物并调整它的显示布局。',
|
|
25
25
|
'settings.pet': '宠物',
|
|
@@ -64,7 +64,7 @@ export const en = {
|
|
|
64
64
|
'pet.treats': 'Treats ×{n}',
|
|
65
65
|
'pet.state.loading': 'The pet is on its way…',
|
|
66
66
|
'pet.state.error': 'The pet is lost (connection failed)',
|
|
67
|
-
//
|
|
67
|
+
// First-level settings section (the `settings.section` seat).
|
|
68
68
|
'settings.title': 'Pet',
|
|
69
69
|
'settings.description': 'Pick a pet and tune its display layout.',
|
|
70
70
|
'settings.pet': 'Pet',
|