@linxin666/dsh-pet 0.1.19 → 0.1.20
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 +3 -3
- package/README.zh.md +3 -3
- package/lib/client.js +54 -22
- package/lib/client.js.map +1 -1
- package/lib/index.js +44 -1
- package/lib/types/affinity.d.ts +18 -18
- package/lib/types/affinity.d.ts.map +1 -1
- package/lib/types/affinity.js +1 -1
- package/lib/types/client/PetDockEntry.d.ts +2 -0
- package/lib/types/client/PetDockEntry.d.ts.map +1 -1
- package/lib/types/client/PetDockEntry.js +2 -2
- package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PetSettingsCard.js +2 -2
- package/lib/types/client/PetSprite.d.ts +2 -0
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +17 -16
- package/lib/types/client/PluginSettingsCard.d.ts +1 -1
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +31 -6
- package/lib/types/client/locales.d.ts +82 -80
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +2 -0
- package/lib/types/client/settings-form.d.ts.map +1 -1
- package/lib/types/client/spritesheet.d.ts +1 -1
- package/lib/types/client/spritesheet.d.ts.map +1 -1
- package/lib/types/client/spritesheet.js +3 -12
- package/lib/types/index.d.ts +2 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +15 -13
- package/lib/types/invariant.js +2 -2
- package/lib/types/ledger.d.ts.map +1 -1
- package/lib/types/ledger.js +3 -3
- package/lib/types/mount-once.d.ts +25 -0
- package/lib/types/mount-once.d.ts.map +1 -0
- package/lib/types/mount-once.js +42 -0
- package/lib/types/persist.js +3 -3
- package/lib/types/registry.js +1 -1
- package/lib/types/remarks.d.ts.map +1 -1
- package/lib/types/routes.js +2 -2
- package/lib/types/service.d.ts +10 -6
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +11 -6
- package/lib/types/state.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/client/PetDockEntry.tsx +3 -0
- package/src/client/PetSprite.test.tsx +56 -3
- package/src/client/PetSprite.tsx +26 -17
- package/src/client/index.ts +26 -3
- package/src/client/locales.ts +2 -0
- package/src/client/pet.module.css +35 -12
- package/src/client/spritesheet.ts +3 -13
- package/src/index.ts +4 -1
- package/src/mount-once.ts +48 -0
- package/src/service.ts +15 -7
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# node scripts/verify-docs.mjs --write <dir>
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: b8705d67cb561b581ecfad2d644e6d70e17cf6b1
|
|
6
|
+
README.zh.md: fb5bc5846a273a667c0f72adac1ecc1c9e0d5130
|
package/README.md
CHANGED
|
@@ -23,8 +23,8 @@ Re-implemented from the pet feature of the Codex desktop app, as an official DSH
|
|
|
23
23
|
| Dragging | Hold and drag the pet to reposition; position persisted |
|
|
24
24
|
| Hide/Summon | Hover panel 隐藏 (Hide); after hiding, a 召唤{name} (Summon {name}) button appears |
|
|
25
25
|
| Witty remarks | Built-in remark library (10 lines per event) plus per-pet custom lines from a manifest remarks block — community PRs give their pet its own voice |
|
|
26
|
-
| Status bubbles | Each concurrently active session gets its own bubble, stacked above the pet (up to 12); transient interaction feedback temporarily takes priority |
|
|
27
|
-
| Multi-session activity | The pet is host-global: the most recent meaningful event drives the sprite animation while every active session reports its own state in a separate bubble; completed turns from every session contribute affinity and treats |
|
|
26
|
+
| Status bubbles | Each concurrently active top-level session gets its own bubble, stacked above the pet (up to 12); subagent sessions report through their spawning conversation and never occupy a bubble of their own; click a bubble to jump to its session; transient interaction feedback temporarily takes priority |
|
|
27
|
+
| Multi-session activity | The pet is host-global: the most recent meaningful event drives the sprite animation while every active top-level session reports its own state in a separate bubble; completed turns from every session (subagents included) contribute affinity and treats |
|
|
28
28
|
|
|
29
29
|
## Pet contract
|
|
30
30
|
|
|
@@ -113,7 +113,7 @@ global React root (createRoot → document.body) <-- polling 2s -- pet-client (b
|
|
|
113
113
|
- **Status source**: the host projects official `turn/start`, `step/start`, `assistant/chunk`, `assistant/message`, `tool/call`, `tool/result`, and `turn/end` events into waiting/thinking/tool/review/done/failed states. Optional legacy `activity/status` events remain a compatibility input.
|
|
114
114
|
- **Registry**: the host normalizes every manifest into a full render definition (geometry, per-row frame counts, per-track durations) and serves it over `/api/pet/pets`; the browser half renders any entry from that definition and carries no per-pet code.
|
|
115
115
|
- **Selection & naming**: `petId` lives in the settings namespace; per-pet names live in `pet.json` under `names`, edited through the hover-panel rename of the active pet. Legacy installs migrate their flat `name` onto the whale girl.
|
|
116
|
-
- **Multi-session semantics**: the API and browser mount are host-global and expose no foreground-session identity. Concurrent sessions each keep their own projected state: the most recent meaningful event drives the sprite animation, while every active session reports its stage in its own bubble (the state view's sessions list, capped at 12 most-recent). Every session's completed turns are still rewarded independently; disposing a session removes its bubble, and disposing the display session falls back to the most recent remaining one.
|
|
116
|
+
- **Multi-session semantics**: the API and browser mount are host-global and expose no foreground-session identity. Concurrent sessions each keep their own projected state: the most recent meaningful event drives the sprite animation, while every active TOP-LEVEL session reports its stage in its own bubble (the state view's sessions list, capped at 12 most-recent). Subagent children are tracked for animation, rewards, and the single display bubble but render no bubble of their own, so N conversations never multiply into an N-plus-subagents stack. Every session's completed turns are still rewarded independently; disposing a session removes its bubble, and disposing the display session falls back to the most recent remaining one.
|
|
117
117
|
- **Mount point**: `document.body` (global React root, always shown: no session / new session / mid-session — the old mount point `conversation.composer.dock` only rendered in an active session, hiding the pet in new sessions); the component uses `createPortal` internally to render the global floating layer.
|
|
118
118
|
- **Rendering**: CSS sprite (background-position) per-frame animation; frame durations come from the served definition's tracks.
|
|
119
119
|
- **Communication**: browser ↔ host over the same-origin `/api/pet/*` JSON endpoints (state/pets/interact/set-visible/set-config/set-name/set-pet); each pet's atlas loads from `/pet/<id>/<spritesheetPath>` — the plugin self-sufficiently provides its own API and assets (the same pattern as dsh-remote-web-ui's `/api/pair`).
|
package/README.zh.md
CHANGED
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
| 拖动 | 按住拖动宠物换位置;位置持久化 |
|
|
24
24
|
| 隐藏/召唤 | 悬浮面板 隐藏;隐藏后出现 召唤{name} 按钮 |
|
|
25
25
|
| 妙语库 | 内置默认妙语库(每类事件 10 句)+ 宠物 manifest 的 remarks 块自定义台词——社区 PR 可以给自家宠物配上专属妙语 |
|
|
26
|
-
| 状态气泡 |
|
|
27
|
-
| 多会话活动 |
|
|
26
|
+
| 状态气泡 | 每个并行活动的顶层会话各自一个气泡,在宠物头顶堆叠(最多 12 个);子代理会话借由其发起会话体现,不占用独立气泡;点击气泡跳转到对应会话;瞬时互动反馈临时优先 |
|
|
27
|
+
| 多会话活动 | 宠物是宿主全局的:最近一次有意义事件驱动精灵动画,同时每个活动的顶层会话用自己的气泡报告各自状态;每个会话(含子代理)完成的轮次都计入亲密度与小鱼干 |
|
|
28
28
|
|
|
29
29
|
## 宠物契约
|
|
30
30
|
|
|
@@ -113,7 +113,7 @@ dsh-pet/
|
|
|
113
113
|
- **状态来源**:宿主把官方 `turn/start`、`step/start`、`assistant/chunk`、`assistant/message`、`tool/call`、`tool/result`、`turn/end` 事件投影为 waiting/thinking/tool/review/done/failed 状态。可选兼容 `activity/status` 事件仍作为输入。
|
|
114
114
|
- **注册表**:宿主把每份 manifest 归一化为完整渲染定义(几何、每行帧数、每轨时长),经 `/api/pet/pets` 下发;浏览器半区用该定义渲染任意条目,不携带任何宠物专属代码。
|
|
115
115
|
- **选择与命名**:`petId` 存于设置命名空间;每只宠物的名字存于 `pet.json` 的 `names`,通过悬浮面板对当前宠物改名编辑。旧版安装的平铺 `name` 自动迁移到鲸鱼娘名下。
|
|
116
|
-
- **多会话语义**:API
|
|
116
|
+
- **多会话语义**:API 与浏览器挂载都是宿主全局的,不暴露前台会话身份。并行会话各自保留投影状态:最近一次有意义事件驱动精灵动画,同时每个活动的顶层会话在独立气泡里报告自己的阶段(state 视图的 sessions 列表,最多保留最近 12 个)。子代理会话仍参与动画、计奖与单一显示气泡,但不占独立气泡位——N 个对话不会变成"N + 子代理数"的气泡堆。每个会话完成的轮次仍独立计奖;销毁会话移除它的气泡,销毁当前显示会话则回退到最近仍在活动的会话。
|
|
117
117
|
- **挂载点**:`document.body`(全局 React 根,始终显示:无会话 / 新会话 / 会话中都可见——旧挂载点 `conversation.composer.dock` 只在活动会话里渲染,新会话里宠物消失);组件内部用 `createPortal` 渲染全局浮层。
|
|
118
118
|
- **渲染**:CSS 精灵(background-position)逐帧动画;帧时长来自下发定义里的轨道表。
|
|
119
119
|
- **通信**:浏览器 ↔ 宿主走同源 `/api/pet/*` JSON 端点(state/pets/interact/set-visible/set-config/set-name/set-pet);每只宠物的图集从 `/pet/<id>/<spritesheetPath>` 加载——插件自给自足地提供自己的 API 与资源(与 dsh-remote-web-ui 的 `/api/pair` 同一模式)。
|
package/lib/client.js
CHANGED
|
@@ -62,21 +62,35 @@ window.__ModuleLoader__.load({
|
|
|
62
62
|
return n;
|
|
63
63
|
}
|
|
64
64
|
//#endregion
|
|
65
|
-
//#region src/
|
|
66
|
-
/**
|
|
67
|
-
function
|
|
65
|
+
//#region src/state.ts
|
|
66
|
+
/** The spritesheet row index for one animation track. */
|
|
67
|
+
function rowOf(animation) {
|
|
68
68
|
return {
|
|
69
|
-
idle: 0,
|
|
69
|
+
"idle": 0,
|
|
70
70
|
"running-right": 1,
|
|
71
71
|
"running-left": 2,
|
|
72
|
-
waving: 3,
|
|
73
|
-
jumping: 4,
|
|
74
|
-
failed: 5,
|
|
75
|
-
waiting: 6,
|
|
76
|
-
running: 7,
|
|
77
|
-
review: 8
|
|
72
|
+
"waving": 3,
|
|
73
|
+
"jumping": 4,
|
|
74
|
+
"failed": 5,
|
|
75
|
+
"waiting": 6,
|
|
76
|
+
"running": 7,
|
|
77
|
+
"review": 8
|
|
78
78
|
}[animation];
|
|
79
79
|
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/client/spritesheet.ts
|
|
82
|
+
/**
|
|
83
|
+
* Spritesheet geometry helpers — parameterized by the pet definition the
|
|
84
|
+
* host serves over '/api/pet/pets', so the browser half renders any registry
|
|
85
|
+
* entry without per-pet code. The per-track tables (frames, durations, loop,
|
|
86
|
+
* fallback) also come from the registry; these helpers only place frames,
|
|
87
|
+
* guard track lengths, and map the fixed 9-row animation contract.
|
|
88
|
+
* @module @linxin666/dsh-pet/client/spritesheet
|
|
89
|
+
*/
|
|
90
|
+
/** Row index of one animation track (the fixed 9-row contract). */
|
|
91
|
+
function rowOfTrack(animation) {
|
|
92
|
+
return rowOf(animation);
|
|
93
|
+
}
|
|
80
94
|
/**
|
|
81
95
|
* Background-position (px) of one frame cell within the scaled atlas.
|
|
82
96
|
* The background image is scaled by `scale` (element size ÷ cell size), and
|
|
@@ -107,7 +121,7 @@ window.__ModuleLoader__.load({
|
|
|
107
121
|
}
|
|
108
122
|
//#endregion
|
|
109
123
|
//#region \0dsh-css:packages/dsh-pet/src/client/pet.module.css.mjs
|
|
110
|
-
const css$2 = ".kz2Bea_float{pointer-events:auto;-webkit-user-select:none;user-select:none;flex-direction:column;align-items:center;display:flex;position:fixed}.kz2Bea_sprite{image-rendering:auto;touch-action:none;position:relative}.kz2Bea_bubble{white-space:nowrap;color:#fff;pointer-events:none;border-radius:999px;margin-bottom:6px;padding:4px 10px;font-size:12px;line-height:1.4;animation:2.6s ease-out forwards kz2Bea_pet-bubble-pop;position:absolute;bottom:100%;box-shadow:0 2px 8px #00000040}.kz2Bea_bubblePet{background:#f472b6eb}.kz2Bea_bubbleFeed{background:#38bdf8eb}.kz2Bea_bubbleStatus{text-overflow:ellipsis;background:#0f172ae6;border:1px solid #7dd3fc80;max-width:min(280px,100vw - 24px);animation:none;overflow:hidden}.kz2Bea_bubbleStack{pointer-events:
|
|
124
|
+
const css$2 = ".kz2Bea_float{pointer-events:auto;-webkit-user-select:none;user-select:none;flex-direction:column;align-items:center;display:flex;position:fixed}.kz2Bea_sprite{image-rendering:auto;touch-action:none;position:relative}.kz2Bea_bubble{white-space:nowrap;color:#fff;pointer-events:none;border-radius:999px;margin-bottom:6px;padding:4px 10px;font-size:12px;line-height:1.4;animation:2.6s ease-out forwards kz2Bea_pet-bubble-pop;position:absolute;bottom:100%;box-shadow:0 2px 8px #00000040}.kz2Bea_bubblePet{background:#f472b6eb}.kz2Bea_bubbleFeed{background:#38bdf8eb}.kz2Bea_bubbleStatus{text-overflow:ellipsis;background:#0f172ae6;border:1px solid #7dd3fc80;max-width:min(280px,100vw - 24px);animation:none;overflow:hidden}.kz2Bea_bubbleStack{pointer-events:auto;flex-direction:column;align-items:center;gap:4px;display:flex;position:absolute;bottom:100%}.kz2Bea_bubbleStack .kz2Bea_bubble{pointer-events:auto;margin-bottom:0;position:relative;bottom:auto}.kz2Bea_bubbleClickable{cursor:pointer;font:inherit;text-align:center;appearance:none;transition:filter .12s,box-shadow .12s}.kz2Bea_bubbleClickable:hover{filter:brightness(1.12)}.kz2Bea_bubbleClickable:focus-visible{outline:none;box-shadow:0 0 0 2px #38bdf8d9}@keyframes kz2Bea_pet-bubble-pop{0%{opacity:0;transform:translateY(6px)scale(.85)}15%{opacity:1;transform:translateY(0)scale(1.05)}25%{transform:translateY(0)scale(1)}75%{opacity:1}to{opacity:0;transform:translateY(-8px)scale(.95)}}.kz2Bea_panel{color:#e2e8f0;backdrop-filter:blur(6px);background:#0f172aeb;border:1px solid #94a3b859;border-radius:10px;flex-direction:column;gap:6px;min-width:132px;margin-right:8px;padding:8px 10px;font-size:12px;display:flex;position:absolute;bottom:0;right:100%;box-shadow:0 4px 16px #00000059}.kz2Bea_panel:after{content:\"\";width:14px;position:absolute;top:0;bottom:0;left:100%}.kz2Bea_rankRow{white-space:nowrap;justify-content:space-between;gap:10px;display:flex}.kz2Bea_nameCell{font-weight:600}.kz2Bea_renameRow{align-items:center;gap:6px;display:flex}.kz2Bea_nameInput{color:#e2e8f0;background:#1e293be6;border:1px solid #7dd3fc80;border-radius:6px;outline:none;flex:1;min-width:0;padding:3px 6px;font-size:12px}.kz2Bea_nameInput:focus{border-color:#38bdf8;box-shadow:0 0 0 2px #38bdf873}.kz2Bea_actions{gap:6px;display:flex}.kz2Bea_action{cursor:pointer;color:#0f172a;background:linear-gradient(#7dd3fc,#38bdf8);border:none;border-radius:6px;flex:1;padding:4px 8px;font-size:12px;transition:filter .12s,box-shadow .12s}.kz2Bea_action:hover{filter:brightness(1.08)}.kz2Bea_action:active{filter:brightness(.94)}.kz2Bea_action:focus-visible{outline:none;box-shadow:0 0 0 2px #38bdf8d9}.kz2Bea_summon{color:#7dd3fc;cursor:pointer;background:#0f172abf;border:1px dashed #7dd3fc99;border-radius:999px;padding:2px 10px;font-size:11px;transition:border-color .12s,color .12s,background .12s,box-shadow .12s}.kz2Bea_summon:hover{color:#bae6fd;background:#0f172ae6;border-color:#7dd3fcf2}.kz2Bea_summon:active{color:#7dd3fc;border-color:#7dd3fccc}.kz2Bea_summon:focus-visible{outline:none;box-shadow:0 0 0 2px #38bdf8d9}@media (prefers-reduced-motion:reduce){.kz2Bea_bubble{opacity:1;animation:none}.kz2Bea_action,.kz2Bea_summon{transition:none}}";
|
|
111
125
|
const tagId$2 = "@linxin666/dsh-pet/pet.module.css";
|
|
112
126
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
113
127
|
const tag = document.createElement("style");
|
|
@@ -120,6 +134,7 @@ window.__ModuleLoader__.load({
|
|
|
120
134
|
"action": "kz2Bea_action",
|
|
121
135
|
"actions": "kz2Bea_actions",
|
|
122
136
|
"bubble": "kz2Bea_bubble",
|
|
137
|
+
"bubbleClickable": "kz2Bea_bubbleClickable",
|
|
123
138
|
"bubbleFeed": "kz2Bea_bubbleFeed",
|
|
124
139
|
"bubblePet": "kz2Bea_bubblePet",
|
|
125
140
|
"bubbleStack": "kz2Bea_bubbleStack",
|
|
@@ -195,7 +210,8 @@ window.__ModuleLoader__.load({
|
|
|
195
210
|
(0, react.useEffect)(() => {
|
|
196
211
|
const reduceMotion = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches === true;
|
|
197
212
|
const row = rowOfTrack(animation);
|
|
198
|
-
const
|
|
213
|
+
const track = trimTrack(tracks[animation], rows[row] ?? tracks[animation].frames.length);
|
|
214
|
+
const leadCol = track.frames[0];
|
|
199
215
|
const lead = framePosition(cell, columns, row, leadCol, scaleRef.current);
|
|
200
216
|
if (spriteRef.current !== null) spriteRef.current.style.backgroundPosition = lead.x + "px " + lead.y + "px";
|
|
201
217
|
if (reduceMotion) return;
|
|
@@ -204,8 +220,6 @@ window.__ModuleLoader__.load({
|
|
|
204
220
|
const tick = (ts) => {
|
|
205
221
|
const delta = ts - last;
|
|
206
222
|
last = ts;
|
|
207
|
-
const row = rowOfTrack(animation);
|
|
208
|
-
const track = trimTrack(tracks[animation], rows[row] ?? tracks[animation].frames.length);
|
|
209
223
|
const st = frameRef.current;
|
|
210
224
|
if (st.track !== animation) {
|
|
211
225
|
st.track = animation;
|
|
@@ -290,7 +304,7 @@ window.__ModuleLoader__.load({
|
|
|
290
304
|
const spriteWidth = Math.round(cell.width * spriteScale);
|
|
291
305
|
const spriteHeight = Math.round(cell.height * spriteScale);
|
|
292
306
|
const sessionBubbles = snapshot?.sessions ?? [];
|
|
293
|
-
const statusBubble = feedback === null &&
|
|
307
|
+
const statusBubble = feedback === null && sessionBubbles.length === 0 ? snapshot?.bubble : void 0;
|
|
294
308
|
const displayName = snapshot?.name ?? definition.displayName;
|
|
295
309
|
return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
296
310
|
ref: floatRef,
|
|
@@ -337,12 +351,15 @@ window.__ModuleLoader__.load({
|
|
|
337
351
|
className: clsx(pet_module_css_default.bubble, feedback.kind === "feed" ? pet_module_css_default.bubbleFeed : pet_module_css_default.bubblePet),
|
|
338
352
|
children: feedback.text
|
|
339
353
|
}, feedback.at),
|
|
340
|
-
feedback === null &&
|
|
354
|
+
feedback === null && sessionBubbles.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
341
355
|
className: pet_module_css_default.bubbleStack,
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
356
|
+
children: sessionBubbles.map((session) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
357
|
+
type: "button",
|
|
358
|
+
className: clsx(pet_module_css_default.bubble, pet_module_css_default.bubbleStatus, pet_module_css_default.bubbleClickable),
|
|
359
|
+
title: props.t("pet.openSessionHint"),
|
|
360
|
+
onClick: () => {
|
|
361
|
+
props.onOpenSession(session.sessionId);
|
|
362
|
+
},
|
|
346
363
|
children: session.bubble
|
|
347
364
|
}, session.sessionId))
|
|
348
365
|
}),
|
|
@@ -481,6 +498,7 @@ window.__ModuleLoader__.load({
|
|
|
481
498
|
onHide: props.hide,
|
|
482
499
|
onDragEnd: props.dragEnd,
|
|
483
500
|
onRename: props.rename,
|
|
501
|
+
onOpenSession: props.openSession,
|
|
484
502
|
onFeedbackDone: props.feedbackDone,
|
|
485
503
|
t: props.t
|
|
486
504
|
})
|
|
@@ -1346,6 +1364,7 @@ window.__ModuleLoader__.load({
|
|
|
1346
1364
|
"pet.treats": "小鱼干 ×{n}",
|
|
1347
1365
|
"pet.state.loading": "宠物正在赶来…",
|
|
1348
1366
|
"pet.state.error": "宠物迷路了(连接失败)",
|
|
1367
|
+
"pet.openSessionHint": "点击跳转到对应会话",
|
|
1349
1368
|
"settings.title": "宠物",
|
|
1350
1369
|
"settings.description": "选择宠物并调整它的显示布局。",
|
|
1351
1370
|
"settings.pet": "宠物",
|
|
@@ -1389,6 +1408,7 @@ window.__ModuleLoader__.load({
|
|
|
1389
1408
|
"pet.treats": "Treats ×{n}",
|
|
1390
1409
|
"pet.state.loading": "The pet is on its way…",
|
|
1391
1410
|
"pet.state.error": "The pet is lost (connection failed)",
|
|
1411
|
+
"pet.openSessionHint": "Click to jump to this session",
|
|
1392
1412
|
"settings.title": "Pet",
|
|
1393
1413
|
"settings.description": "Pick a pet and tune its display layout.",
|
|
1394
1414
|
"settings.pet": "Pet",
|
|
@@ -1467,13 +1487,14 @@ window.__ModuleLoader__.load({
|
|
|
1467
1487
|
const POLL_MS = 2e3;
|
|
1468
1488
|
/** Settings namespace the pet settings card edits (the Host plugin registers it). */
|
|
1469
1489
|
const PET_SETTINGS_NS = "pet";
|
|
1470
|
-
/** Required services. */
|
|
1490
|
+
/** Required services (sessions powers bubble-to-session navigation). */
|
|
1471
1491
|
const inject = [
|
|
1472
1492
|
"slots",
|
|
1473
1493
|
"locale",
|
|
1474
1494
|
"connection",
|
|
1475
1495
|
"settingsScope",
|
|
1476
|
-
"remote"
|
|
1496
|
+
"remote",
|
|
1497
|
+
"sessions"
|
|
1477
1498
|
];
|
|
1478
1499
|
/**
|
|
1479
1500
|
* Client plugin body: register dictionaries, mount the global pet entry and
|
|
@@ -1515,14 +1536,19 @@ window.__ModuleLoader__.load({
|
|
|
1515
1536
|
const setState = petStore.actions.setState;
|
|
1516
1537
|
const setFeedback = petStore.actions.setFeedback;
|
|
1517
1538
|
let petsLoaded = false;
|
|
1539
|
+
let stateSeq = 0;
|
|
1518
1540
|
const pollNow = () => {
|
|
1519
1541
|
if (!petsLoaded) petApi.pets().then((list) => {
|
|
1520
1542
|
petsLoaded = true;
|
|
1521
1543
|
setPets(list);
|
|
1522
1544
|
}, () => {});
|
|
1545
|
+
const seq = stateSeq + 1;
|
|
1546
|
+
stateSeq = seq;
|
|
1523
1547
|
petApi.state().then((snapshot) => {
|
|
1548
|
+
if (seq !== stateSeq) return;
|
|
1524
1549
|
setSnapshot(snapshot);
|
|
1525
1550
|
}, () => {
|
|
1551
|
+
if (seq !== stateSeq) return;
|
|
1526
1552
|
setState("error", "pet.state transport error");
|
|
1527
1553
|
});
|
|
1528
1554
|
};
|
|
@@ -1550,9 +1576,15 @@ window.__ModuleLoader__.load({
|
|
|
1550
1576
|
document.removeEventListener("visibilitychange", onVisibility);
|
|
1551
1577
|
};
|
|
1552
1578
|
}, "pet: poll");
|
|
1579
|
+
const sessions = ctx.sessions;
|
|
1580
|
+
const openSession = (sessionId) => {
|
|
1581
|
+
if (sessions.list.getSnapshot().byId[sessionId] === void 0) return;
|
|
1582
|
+
sessions.open(sessionId);
|
|
1583
|
+
};
|
|
1553
1584
|
const injected = () => ({
|
|
1554
1585
|
store: petStore,
|
|
1555
1586
|
ensure: pollNow,
|
|
1587
|
+
openSession,
|
|
1556
1588
|
pet: () => {
|
|
1557
1589
|
petApi.interact("pet").then((result) => {
|
|
1558
1590
|
setFeedback({
|