@linxin666/dsh-pet 0.3.4 → 0.3.5
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 +13 -10
- package/README.zh.md +13 -10
- package/contracts/voice-pack-v1.schema.json +23 -28
- package/lib/client.js +23 -15
- package/lib/client.js.map +1 -1
- package/lib/index.js +344 -461
- package/lib/types/chatter.d.ts +68 -46
- package/lib/types/chatter.d.ts.map +1 -1
- package/lib/types/chatter.js +243 -301
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +16 -17
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +26 -9
- package/lib/types/event-projection.d.ts +9 -4
- package/lib/types/event-projection.d.ts.map +1 -1
- package/lib/types/event-projection.js +46 -14
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +8 -3
- package/lib/types/service.d.ts +7 -11
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +27 -19
- package/lib/types/voice-pack.d.ts +6 -7
- package/lib/types/voice-pack.d.ts.map +1 -1
- package/lib/types/voice-pack.js +44 -47
- package/package.json +1 -1
- package/src/chatter.test.ts +68 -38
- package/src/chatter.ts +269 -316
- package/src/client/PetSprite.test.tsx +32 -15
- package/src/client/PetSprite.tsx +21 -24
- package/src/client/index.test.tsx +10 -1
- package/src/client/index.ts +29 -10
- package/src/event-projection.ts +57 -16
- package/src/routes.ts +9 -4
- package/src/service.ts +32 -29
- package/src/voice-pack.test.ts +48 -25
- package/src/voice-pack.ts +50 -47
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: 2501ae7db35246c4348ed08fd169ee58a7569a95
|
|
6
|
+
README.zh.md: a490c37210773c3da3eec1d9c61807864f56b6aa
|
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 | The hover panel sits below the pet (lifted above the status bubbles when there is no room below) and provides 隐藏 (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; success lines rotate by persisted success counts and cooldown lines by persisted rejection counts |
|
|
26
|
-
| Status bubbles | Only
|
|
27
|
-
| Inner whispers | 碎碎念: while
|
|
26
|
+
| Status bubbles | Only one top-level session speaks by default — the session the GUI is currently on when it is reported, otherwise the most recently active one — and the rest collapse behind a +N badge on the main bubble instead of stacking a tall column; hover the bubble (or tap the badge, for touch) to fan every session's bubble out above it and click one to jump to its session; subagent sessions report through their spawning conversation and never occupy a bubble of their own; transient interaction feedback temporarily takes priority. Bubble copy comes from generous rotating pools per scene (waiting / thinking / writing / done / failed...), tool calls map onto per-family witty lines carrying the real argument hint (e.g. 跑跑 npm test), and a long-lived scene re-phrases itself every few seconds |
|
|
27
|
+
| Inner whispers | 碎碎念: while a session streams, the pet occasionally speaks its inner voice through that session's own bubble — a fresh whisper takes over the bubble and marks it with 「」 quotes — sharing the same DeepSeek-blue glass as every status bubble, so stacked bubbles never clash — instead of stacking a second bubble — category lines woken by the SITUATION (thinking / writing / the running tool family) plus outcome lines woken only by structured results (test green from a passed test tool, error from a failed tool result, completion from a completed turn) — never from the model's words, so a discussion that merely mentions a keyword cannot wake a mood, and a whisper never quotes real content (no tool names, paths or model text); paced by cooldowns (9 s category, 5 s outcome) and an 8 s display TTL, the status copy returns after a few seconds |
|
|
28
28
|
| 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 |
|
|
29
29
|
| Voice packs and panel DIY | A per-pet voice.json plus the global $DSH_HOME/pets/.voice.json override replace every bubble word and the hover panel (button labels, stat formats, button visibility); merge precedence per-pet > global > built-in, broken packs warn and never reject a pet |
|
|
30
30
|
|
|
@@ -102,11 +102,14 @@ Every word in the thought bubble (status / tool / whisper copy) and the hover pa
|
|
|
102
102
|
"shell": ["Running {hint}", "Hit enter: {hint}"]
|
|
103
103
|
},
|
|
104
104
|
"toolRemaining": ["{n} helpers still at work"], // {n} allowed
|
|
105
|
-
"whispers": { // murmur pools;
|
|
106
|
-
"
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
"whispers": { // murmur pools; keys replace the built-in pools
|
|
106
|
+
"categories": { // situation pools; an explicit empty array mutes that category
|
|
107
|
+
"thinking": ["Let me think..."],
|
|
108
|
+
"running": ["It's running now"]
|
|
109
|
+
},
|
|
110
|
+
"results": { // outcome pools (test green / error / completion)
|
|
111
|
+
"pass": ["All green!"]
|
|
112
|
+
}
|
|
110
113
|
},
|
|
111
114
|
"panel": { // hover panel; unset slots keep the plugin i18n copy
|
|
112
115
|
"labels": { "feed": "Treat", "hide": "Dive", "rename": "Rename me", "confirm": "Sure" },
|
|
@@ -116,11 +119,11 @@ Every word in the thought bubble (status / tool / whisper copy) and the hover pa
|
|
|
116
119
|
}
|
|
117
120
|
```
|
|
118
121
|
|
|
119
|
-
- Merge precedence (per slot): the pet voice.json > the global .voice.json > built-in copy. status/tools merge per key,
|
|
122
|
+
- Merge precedence (per slot): the pet voice.json > the global .voice.json > built-in copy. status/tools/whispers merge per key, panel merges per slot; any slot a layer misses falls through.
|
|
120
123
|
- Placeholder whitelist: tools accept {tool} / {hint}; toolRemaining accepts {n}; panel.stats accept {rank} / {n} / {points}; status, whisper and panel-label lines accept no placeholders (lines carrying one are dropped with a warning).
|
|
121
|
-
- Caps (warn-and-drop): at most 64 lines per pool and 160 characters per line;
|
|
124
|
+
- Caps (warn-and-drop): at most 64 lines per pool and 160 characters per line; panel labels 40 and stats 80 characters.
|
|
122
125
|
- A broken pack never breaks the pet: voice.json that is not valid JSON or whose root is not an object is ignored with a warning; every other issue drops its slot only. Diagnostics appear under Settings > Pet directory diagnostics. node scripts/dsh-pet validate <dir> fails installs on structure errors and lists content issues as warnings.
|
|
123
|
-
- Semantics: an empty status/tools pool falls back to the built-in copy (a scene line always renders); an explicit empty whisper pool mutes that channel; an empty panel actions array hides all three buttons; uncovered buttons and stats keep the plugin bilingual dictionary.
|
|
126
|
+
- Semantics: an empty status/tools pool falls back to the built-in copy (a scene line always renders); an explicit empty whisper pool mutes that channel; an empty panel actions array hides all three buttons; uncovered buttons and stats keep the plugin bilingual dictionary. Legacy whispers.generic / whispers.rules fields are no longer supported and are ignored with a warning.
|
|
124
127
|
|
|
125
128
|
## Live2D pets (renderer: live2d)
|
|
126
129
|
|
package/README.zh.md
CHANGED
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
| 拖动 | 按住拖动宠物换位置;位置持久化 |
|
|
24
24
|
| 隐藏/召唤 | 悬浮面板位于宠物下方(下方空间不足时上移到状态气泡之上)并提供 隐藏;隐藏后出现 召唤{name} 按钮 |
|
|
25
25
|
| 妙语库 | 内置默认妙语库(每类事件 10 句)+ 宠物自定义台词;成功文案按持久化成功次数轮换,冷却文案按持久化拒绝次数轮换 |
|
|
26
|
-
| 状态气泡 |
|
|
27
|
-
| 碎碎念 |
|
|
26
|
+
| 状态气泡 | 默认只有一个顶层会话说话——上报 GUI 当前会话时优先它,否则是最近活动的一个——其余会话收进主气泡右上角的 +N 角标,不再叠出一长列;悬停气泡(触屏点按角标)即可向上展开所有会话的气泡,点击任一气泡跳转到对应会话;子代理会话借由其发起会话体现,不占用独立气泡;瞬时互动反馈临时优先。气泡文案按场景准备了大量轮换词库(等待 / 思考 / 整理 / 完成 / 失败……),工具调用按工具族映射俏皮文案并带上真实参数(如 跑跑 npm test),同一场景持续数秒会自动换一种说法 |
|
|
27
|
+
| 碎碎念 | 会话流式输出期间,宠物会偶尔借该会话自己的气泡说出内心独白——新鲜的碎碎念接管所属会话的气泡并以「」引号标记——与状态气泡共用同一片 DeepSeek 蓝黑玻璃,气泡栈内颜色统一不再色差——不再叠出第二只气泡——分类台词由「正在干什么」触发(思考 / 写回复 / 正在跑的工具族),结果台词只由结构化结果触发(测试工具通过 → 全绿、工具失败 → 报错、回合完成 → 收工)——决不读模型说的话,讨论里提到关键词也不会误触发心境;碎碎念也从不引用真实内容(无工具名、路径、模型原文);分类冷却 9 秒、结果冷却 5 秒,展示 8 秒后气泡恢复状态文案 |
|
|
28
28
|
| 多会话活动 | 宠物是宿主全局的:最近一次有意义事件驱动精灵动画,同时每个活动的顶层会话用自己的气泡报告各自状态;每个会话(含子代理)完成的轮次都计入亲密度与小鱼干 |
|
|
29
29
|
| 语音包与面板 DIY | 宠物目录 voice.json + 全局 $DSH_HOME/pets/.voice.json 覆盖气泡全部文案与悬浮面板(按钮标签/统计格式/按钮显隐);合并优先级 宠物自带 > 全局 > 内置,坏包警告不拒载 |
|
|
30
30
|
|
|
@@ -102,11 +102,14 @@ node scripts/dsh-pet install <dir> --force # 覆盖同名已安装宠物
|
|
|
102
102
|
"shell": ["跑跑 {hint}", "敲回车!{hint}"]
|
|
103
103
|
},
|
|
104
104
|
"toolRemaining": ["后台还有 {n} 位小助手"], // 允许 {n}
|
|
105
|
-
"whispers": { //
|
|
106
|
-
"
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
"whispers": { // 碎碎念:按键替换内置池
|
|
106
|
+
"categories": { // 分类池;显式空数组 = 静音该分类
|
|
107
|
+
"thinking": ["让我想想……"],
|
|
108
|
+
"running": ["正在跑,我盯着"]
|
|
109
|
+
},
|
|
110
|
+
"results": { // 结果池(测试全绿 / 报错 / 完成)
|
|
111
|
+
"pass": ["全绿!"]
|
|
112
|
+
}
|
|
110
113
|
},
|
|
111
114
|
"panel": { // 悬浮面板(每槽未声明时回落插件 i18n 文案)
|
|
112
115
|
"labels": { "feed": "投喂", "hide": "藏起来", "rename": "起名字", "confirm": "好的" },
|
|
@@ -116,11 +119,11 @@ node scripts/dsh-pet install <dir> --force # 覆盖同名已安装宠物
|
|
|
116
119
|
}
|
|
117
120
|
```
|
|
118
121
|
|
|
119
|
-
- 合并优先级(逐槽):宠物自带 voice.json > 全局 .voice.json > 内置文案。status/tools 逐键合并、
|
|
122
|
+
- 合并优先级(逐槽):宠物自带 voice.json > 全局 .voice.json > 内置文案。status/tools/whispers 逐键合并、panel 逐槽合并,任何层缺失的槽位回落下一层。
|
|
120
123
|
- 占位符白名单:tools 允许 {tool} / {hint};toolRemaining 允许 {n};panel.stats 允许 {rank} / {n} / {points};status、碎碎念与面板标签不允许任何占位符(含非法占位符的行被警告丢弃)。
|
|
121
|
-
- 上限(warn-and-drop):每池 64 行以内、每行 160
|
|
124
|
+
- 上限(warn-and-drop):每池 64 行以内、每行 160 字符以内;面板标签 40 以内、统计 80 字符以内。
|
|
122
125
|
- 坏包不影响宠物:voice.json 不是合法 JSON 或根不是对象 → 警告并整体忽略;其余问题逐槽警告丢弃。诊断显示在设置 → 宠物目录诊断。node scripts/dsh-pet validate <dir> 会把结构错误判为安装失败、内容问题列为警告。
|
|
123
|
-
- 语义细节:status/tools 的空池回落内置文案(场景行始终有话说);whispers 的显式空数组是静音(关掉该通道);面板 actions 为空数组 =
|
|
126
|
+
- 语义细节:status/tools 的空池回落内置文案(场景行始终有话说);whispers 的显式空数组是静音(关掉该通道);面板 actions 为空数组 = 三个按钮全部隐藏;未覆盖的按钮/统计继续使用插件双语字典。旧的 whispers.generic / whispers.rules 字段已不再支持,会被忽略并给出警告。
|
|
124
127
|
|
|
125
128
|
## Live2D 宠物(renderer: live2d)
|
|
126
129
|
|
|
@@ -118,36 +118,31 @@
|
|
|
118
118
|
"whispers": {
|
|
119
119
|
"type": "object",
|
|
120
120
|
"additionalProperties": false,
|
|
121
|
-
"description": "Murmur pools
|
|
121
|
+
"description": "Murmur pools: category pools keyed by the ten WhisperCategory ids plus outcome pools keyed by pass / fail / done. A key replaces that key's built-in pool; an explicit empty array mutes that channel. Legacy generic / rules fields are ignored with a warning.",
|
|
122
122
|
"properties": {
|
|
123
|
-
"
|
|
124
|
-
"
|
|
123
|
+
"categories": {
|
|
124
|
+
"type": "object",
|
|
125
|
+
"additionalProperties": false,
|
|
126
|
+
"properties": {
|
|
127
|
+
"thinking": { "$ref": "#/definitions/pool" },
|
|
128
|
+
"writing": { "$ref": "#/definitions/pool" },
|
|
129
|
+
"reading": { "$ref": "#/definitions/pool" },
|
|
130
|
+
"editing": { "$ref": "#/definitions/pool" },
|
|
131
|
+
"running": { "$ref": "#/definitions/pool" },
|
|
132
|
+
"searching": { "$ref": "#/definitions/pool" },
|
|
133
|
+
"git": { "$ref": "#/definitions/pool" },
|
|
134
|
+
"delegating": { "$ref": "#/definitions/pool" },
|
|
135
|
+
"browsing": { "$ref": "#/definitions/pool" },
|
|
136
|
+
"generic": { "$ref": "#/definitions/pool" }
|
|
137
|
+
}
|
|
125
138
|
},
|
|
126
|
-
"
|
|
127
|
-
"type": "
|
|
128
|
-
"
|
|
129
|
-
"
|
|
130
|
-
"
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
"keywords",
|
|
134
|
-
"pool"
|
|
135
|
-
],
|
|
136
|
-
"properties": {
|
|
137
|
-
"keywords": {
|
|
138
|
-
"type": "array",
|
|
139
|
-
"maxItems": 16,
|
|
140
|
-
"items": {
|
|
141
|
-
"type": "string",
|
|
142
|
-
"maxLength": 40,
|
|
143
|
-
"minLength": 1
|
|
144
|
-
},
|
|
145
|
-
"description": "Lowercase substrings matched against the model's output."
|
|
146
|
-
},
|
|
147
|
-
"pool": {
|
|
148
|
-
"$ref": "#/definitions/pool"
|
|
149
|
-
}
|
|
150
|
-
}
|
|
139
|
+
"results": {
|
|
140
|
+
"type": "object",
|
|
141
|
+
"additionalProperties": false,
|
|
142
|
+
"properties": {
|
|
143
|
+
"pass": { "$ref": "#/definitions/pool" },
|
|
144
|
+
"fail": { "$ref": "#/definitions/pool" },
|
|
145
|
+
"done": { "$ref": "#/definitions/pool" }
|
|
151
146
|
}
|
|
152
147
|
}
|
|
153
148
|
}
|
package/lib/client.js
CHANGED
|
@@ -517,8 +517,7 @@ window.__ModuleLoader__.load({
|
|
|
517
517
|
const stackOpen = stackPeek || stackPinned;
|
|
518
518
|
const visibleSessions = !stackOpen && sessionBubbles.length > 1 ? sessionBubbles.slice(0, 1) : sessionBubbles;
|
|
519
519
|
const statusBubble = feedback === null && sessionBubbles.length === 0 ? snapshot?.bubble : void 0;
|
|
520
|
-
const
|
|
521
|
-
const bubblePresent = feedback !== null || sessionBubbles.length > 0 || statusBubble !== void 0 || whisper !== void 0;
|
|
520
|
+
const bubblePresent = feedback !== null || sessionBubbles.length > 0 || statusBubble !== void 0;
|
|
522
521
|
const displayName = snapshot?.name ?? definition.displayName;
|
|
523
522
|
const decoration = snapshot?.decoration;
|
|
524
523
|
(0, react.useEffect)(() => {
|
|
@@ -615,13 +614,13 @@ window.__ModuleLoader__.load({
|
|
|
615
614
|
className: clsx(pet_module_css_default.bubble, feedback.kind === "feed" ? pet_module_css_default.bubbleFeed : pet_module_css_default.bubblePet),
|
|
616
615
|
children: feedback.text
|
|
617
616
|
}, feedback.at),
|
|
618
|
-
feedback === null && (sessionBubbles.length > 0 || statusBubble !== void 0
|
|
617
|
+
feedback === null && (sessionBubbles.length > 0 || statusBubble !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
619
618
|
ref: bubbleRef,
|
|
620
619
|
className: pet_module_css_default.bubbleStack,
|
|
621
620
|
onPointerEnter: () => setStackPeek(true),
|
|
622
621
|
onPointerLeave: () => setStackPeek(false),
|
|
623
622
|
children: [visibleSessions.map((session, index) => {
|
|
624
|
-
const speaksWhisper =
|
|
623
|
+
const speaksWhisper = session.whisper !== void 0;
|
|
625
624
|
const bubble = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
626
625
|
type: "button",
|
|
627
626
|
className: clsx(pet_module_css_default.bubble, pet_module_css_default.bubbleStatus, pet_module_css_default.bubbleClickable, speaksWhisper && pet_module_css_default.bubbleWhisper),
|
|
@@ -632,8 +631,8 @@ window.__ModuleLoader__.load({
|
|
|
632
631
|
children: [index === 0 && !speaksWhisper && decoration !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusOrnament, {
|
|
633
632
|
decoration,
|
|
634
633
|
phase
|
|
635
|
-
}),
|
|
636
|
-
}, speaksWhisper ? "whisper:" + whisper : session.sessionId);
|
|
634
|
+
}), session.whisper ?? session.bubble]
|
|
635
|
+
}, speaksWhisper ? "whisper:" + session.whisper : session.sessionId);
|
|
637
636
|
if (index !== 0 || sessionBubbles.length <= 1) return bubble;
|
|
638
637
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
639
638
|
className: pet_module_css_default.bubbleAnchor,
|
|
@@ -650,15 +649,15 @@ window.__ModuleLoader__.load({
|
|
|
650
649
|
children: stackOpen ? "×" : "+" + String(sessionBubbles.length - 1)
|
|
651
650
|
})]
|
|
652
651
|
}, "primary");
|
|
653
|
-
}), sessionBubbles.length === 0 &&
|
|
654
|
-
className: clsx(pet_module_css_default.bubble, pet_module_css_default.bubbleStatus
|
|
652
|
+
}), sessionBubbles.length === 0 && statusBubble !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
653
|
+
className: clsx(pet_module_css_default.bubble, pet_module_css_default.bubbleStatus),
|
|
655
654
|
role: "status",
|
|
656
655
|
"aria-live": "polite",
|
|
657
|
-
children: [
|
|
656
|
+
children: [decoration !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusOrnament, {
|
|
658
657
|
decoration,
|
|
659
658
|
phase
|
|
660
|
-
}),
|
|
661
|
-
},
|
|
659
|
+
}), statusBubble]
|
|
660
|
+
}, "status")]
|
|
662
661
|
}),
|
|
663
662
|
hovered && dragRef.current === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
664
663
|
ref: panelRef,
|
|
@@ -3243,7 +3242,7 @@ window.__ModuleLoader__.load({
|
|
|
3243
3242
|
/** The building package's version, when the bundle carries it. */
|
|
3244
3243
|
function bakedVersion() {
|
|
3245
3244
|
try {
|
|
3246
|
-
return "0.3.
|
|
3245
|
+
return "0.3.5";
|
|
3247
3246
|
} catch {
|
|
3248
3247
|
return;
|
|
3249
3248
|
}
|
|
@@ -3319,7 +3318,7 @@ window.__ModuleLoader__.load({
|
|
|
3319
3318
|
}
|
|
3320
3319
|
/** The live host API instance (always defined; failures surface per call). */
|
|
3321
3320
|
const petApi = {
|
|
3322
|
-
state: () => petFetch("/api/pet/state"),
|
|
3321
|
+
state: (currentSessionId) => petFetch("/api/pet/state" + (currentSessionId === void 0 ? "" : "?current=" + encodeURIComponent(currentSessionId))),
|
|
3323
3322
|
pets: () => petFetch("/api/pet/pets"),
|
|
3324
3323
|
interact: (kind) => petFetch("/api/pet/interact", { kind }),
|
|
3325
3324
|
setVisible: (visible) => petFetch("/api/pet/set-visible", { visible }),
|
|
@@ -3406,6 +3405,11 @@ window.__ModuleLoader__.load({
|
|
|
3406
3405
|
const setPets = petStore.actions.setPets;
|
|
3407
3406
|
const setState = petStore.actions.setState;
|
|
3408
3407
|
const setFeedback = petStore.actions.setFeedback;
|
|
3408
|
+
const sessions = ctx.sessions;
|
|
3409
|
+
const currentSessionId = () => {
|
|
3410
|
+
const current = sessions.list.getSnapshot().current;
|
|
3411
|
+
return current === void 0 ? void 0 : String(current);
|
|
3412
|
+
};
|
|
3409
3413
|
let petsLoaded = false;
|
|
3410
3414
|
let stateSeq = 0;
|
|
3411
3415
|
const pollNow = () => {
|
|
@@ -3415,7 +3419,7 @@ window.__ModuleLoader__.load({
|
|
|
3415
3419
|
}, () => {});
|
|
3416
3420
|
const seq = stateSeq + 1;
|
|
3417
3421
|
stateSeq = seq;
|
|
3418
|
-
petApi.state().then((snapshot) => {
|
|
3422
|
+
petApi.state(currentSessionId()).then((snapshot) => {
|
|
3419
3423
|
if (seq !== stateSeq) return;
|
|
3420
3424
|
setSnapshot(snapshot);
|
|
3421
3425
|
}, () => {
|
|
@@ -3447,7 +3451,11 @@ window.__ModuleLoader__.load({
|
|
|
3447
3451
|
document.removeEventListener("visibilitychange", onVisibility);
|
|
3448
3452
|
};
|
|
3449
3453
|
}, "pet: poll");
|
|
3450
|
-
|
|
3454
|
+
ctx.effect(() => {
|
|
3455
|
+
return sessions.list.subscribe(() => {
|
|
3456
|
+
if (document.visibilityState === "visible") pollNow();
|
|
3457
|
+
});
|
|
3458
|
+
}, "pet: current-session watch");
|
|
3451
3459
|
const openSession = (sessionId) => {
|
|
3452
3460
|
if (sessions.list.getSnapshot().byId[sessionId] === void 0) return;
|
|
3453
3461
|
sessions.open(sessionId);
|