dsh-pet-live2d 3.1.0 → 3.1.1
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/CHANGELOG.md +26 -0
- package/lib/client.js +60 -9
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# 更新日志
|
|
2
2
|
|
|
3
|
+
## 3.1.1(2026-09-30)
|
|
4
|
+
|
|
5
|
+
### 修:在某些桌面外壳里"宠物整个不出现"(`localStorage` 一访问就抛)
|
|
6
|
+
|
|
7
|
+
有人反馈在 DeepSeek Harness 的**官方桌面端**里用不了(issue #1)。查下来最要命的一处是:
|
|
8
|
+
客户端里有 20 多处**直接读写 `window.localStorage`**,而在**自定义协议 / opaque origin**
|
|
9
|
+
的外壳里,**光是访问 `localStorage` 就会抛 `SecurityError`**(有些壳还会因为隐私设置直接
|
|
10
|
+
禁用 storage)。它抛在组件的第一帧里 ⇒ **宠物和设置页一起白掉**,现象看起来就像"这个插件
|
|
11
|
+
不支持我的桌面端"。
|
|
12
|
+
|
|
13
|
+
现在所有读写都走一层带兜底的封装:
|
|
14
|
+
|
|
15
|
+
- 先试着取一次原生引用(**连这一步都在 `try` 里** —— `typeof window.localStorage` 本身也会抛);
|
|
16
|
+
- 拿不到、或读写中途抛(配额/隐私模式),就退回**内存实现**:本次会话内位置 / 大小 / 装扮
|
|
17
|
+
照常工作,只是关掉窗口不留存。
|
|
18
|
+
|
|
19
|
+
顺带确认了一条:前端**只走相对路径**(`/api/live2d-pet/*`),插件加载走官方机制
|
|
20
|
+
(`dsh.bundle` / `dsh.client` / `slots`)—— 所以换成 `http://127.0.0.1:<端口>` 之外的外壳
|
|
21
|
+
地址也不该受影响。
|
|
22
|
+
|
|
23
|
+
### 改:macOS(arm64)平台子包进入主包声明
|
|
24
|
+
|
|
25
|
+
`dsh-pet-live2d-desktop-darwin-arm64` 已在上一版发上 npm,这一版把主包的
|
|
26
|
+
`optionalDependencies` 补上它 —— mac 用户装插件时会自动带上桌面端(在这之前只能从
|
|
27
|
+
Release 下 zip)。
|
|
28
|
+
|
|
3
29
|
## 3.1.0(2026-09-29)
|
|
4
30
|
|
|
5
31
|
这一轮的主线是**桌面端**:托盘的"没有她"查到底了 —— 根因是本项目自己给产物贴错了
|
package/lib/client.js
CHANGED
|
@@ -31,6 +31,57 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
|
|
|
31
31
|
|
|
32
32
|
const API = "/api/live2d-pet";
|
|
33
33
|
const STORAGE_KEY = "dsh-live2d-pet.state.v1";
|
|
34
|
+
|
|
35
|
+
// ---- 存储:`localStorage` 要裹起来用 ----
|
|
36
|
+
//
|
|
37
|
+
// 为什么不能直接 `window.localStorage.getItem(...)`:**有些桌面外壳里访问它就抛异常**
|
|
38
|
+
// (自定义协议 / opaque origin 下是 `SecurityError: The operation is insecure`,
|
|
39
|
+
// 有的壳还会因为隐私设置直接禁用 storage)。一抛就是**在组件第一帧里**,整个宠物
|
|
40
|
+
// 连带设置页一起白掉 —— 而这种外壳(官方 desktop / 各家 Tauri、Electron 打包版)
|
|
41
|
+
// 恰恰是本插件最常见的运行环境之一。
|
|
42
|
+
//
|
|
43
|
+
// 拿不到就退回内存实现:这一次会话内照样能存(位置、大小、装扮),只是关掉窗口不保留。
|
|
44
|
+
// 注意**不能**用 `typeof window.localStorage` 判断 —— 那个 getter 本身就会抛,
|
|
45
|
+
// 所以连"取一次引用"都要裹在 try 里。
|
|
46
|
+
const nativeStorage = (() => {
|
|
47
|
+
try {
|
|
48
|
+
return window.localStorage;
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
})();
|
|
53
|
+
const memoryStorage = new Map();
|
|
54
|
+
const storage = {
|
|
55
|
+
getItem(key) {
|
|
56
|
+
if (nativeStorage !== null) {
|
|
57
|
+
try {
|
|
58
|
+
return nativeStorage.getItem(key);
|
|
59
|
+
} catch {
|
|
60
|
+
/* 读也抛(配额/隐私模式)⇒ 退回内存那份 */
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return memoryStorage.has(key) ? memoryStorage.get(key) : null;
|
|
64
|
+
},
|
|
65
|
+
setItem(key, value) {
|
|
66
|
+
memoryStorage.set(key, String(value));
|
|
67
|
+
if (nativeStorage === null) return;
|
|
68
|
+
try {
|
|
69
|
+
nativeStorage.setItem(key, value);
|
|
70
|
+
} catch {
|
|
71
|
+
/* 写不进去也别抛:内存那份已经存了 */
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
removeItem(key) {
|
|
75
|
+
memoryStorage.delete(key);
|
|
76
|
+
if (nativeStorage === null) return;
|
|
77
|
+
try {
|
|
78
|
+
nativeStorage.removeItem(key);
|
|
79
|
+
} catch {
|
|
80
|
+
/* 同上 */
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
34
85
|
const ROOT_ATTR = "data-dsh-live2d-pet-root";
|
|
35
86
|
const PET_ATTR = "data-dsh-live2d-pet";
|
|
36
87
|
const LEGACY_ATTR = "data-dsh-live2d-pet-container";
|
|
@@ -3010,7 +3061,7 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
|
|
|
3010
3061
|
|
|
3011
3062
|
function loadStored() {
|
|
3012
3063
|
try {
|
|
3013
|
-
const raw =
|
|
3064
|
+
const raw = storage.getItem(STORAGE_KEY);
|
|
3014
3065
|
if (raw === null) return {};
|
|
3015
3066
|
const parsed = JSON.parse(raw);
|
|
3016
3067
|
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
@@ -3021,7 +3072,7 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
|
|
|
3021
3072
|
|
|
3022
3073
|
function saveStored(patch) {
|
|
3023
3074
|
try {
|
|
3024
|
-
|
|
3075
|
+
storage.setItem(STORAGE_KEY, JSON.stringify(Object.assign(loadStored(), patch)));
|
|
3025
3076
|
} catch {
|
|
3026
3077
|
/* storage is best-effort */
|
|
3027
3078
|
}
|
|
@@ -3637,7 +3688,7 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
|
|
|
3637
3688
|
|
|
3638
3689
|
const readLocal = (key) => {
|
|
3639
3690
|
try {
|
|
3640
|
-
const raw =
|
|
3691
|
+
const raw = storage.getItem(key);
|
|
3641
3692
|
return raw === null ? null : JSON.parse(raw);
|
|
3642
3693
|
} catch {
|
|
3643
3694
|
return null;
|
|
@@ -3645,8 +3696,8 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
|
|
|
3645
3696
|
};
|
|
3646
3697
|
const writeLocal = (key, value) => {
|
|
3647
3698
|
try {
|
|
3648
|
-
if (value === null || value === undefined)
|
|
3649
|
-
else
|
|
3699
|
+
if (value === null || value === undefined) storage.removeItem(key);
|
|
3700
|
+
else storage.setItem(key, JSON.stringify(value));
|
|
3650
3701
|
} catch {
|
|
3651
3702
|
/* 无痕模式之类:这次生效,下次不记得 */
|
|
3652
3703
|
}
|
|
@@ -3912,7 +3963,7 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
|
|
|
3912
3963
|
FLAGS[key] = value === true;
|
|
3913
3964
|
if (key === "outfitArchive" && FLAGS[key] === false) {
|
|
3914
3965
|
try {
|
|
3915
|
-
|
|
3966
|
+
storage.removeItem(OUTFIT_KEY);
|
|
3916
3967
|
} catch {
|
|
3917
3968
|
/* 无痕模式:本来也没存下 */
|
|
3918
3969
|
}
|
|
@@ -3952,7 +4003,7 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
|
|
|
3952
4003
|
let saved = given === undefined ? null : given;
|
|
3953
4004
|
if (saved === undefined || saved === null) {
|
|
3954
4005
|
try {
|
|
3955
|
-
saved = JSON.parse(
|
|
4006
|
+
saved = JSON.parse(storage.getItem(OVERRIDE_KEY) ?? "null");
|
|
3956
4007
|
} catch {
|
|
3957
4008
|
saved = null;
|
|
3958
4009
|
}
|
|
@@ -4304,7 +4355,7 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
|
|
|
4304
4355
|
let saved = given === undefined ? null : given;
|
|
4305
4356
|
if (saved === undefined || saved === null) {
|
|
4306
4357
|
try {
|
|
4307
|
-
saved = JSON.parse(
|
|
4358
|
+
saved = JSON.parse(storage.getItem(TUNING_KEY) ?? "null");
|
|
4308
4359
|
} catch {
|
|
4309
4360
|
saved = null;
|
|
4310
4361
|
}
|
|
@@ -5985,7 +6036,7 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
|
|
|
5985
6036
|
const readOutfit = () => {
|
|
5986
6037
|
if (!FLAGS.outfitArchive) return null;
|
|
5987
6038
|
try {
|
|
5988
|
-
const parsed = JSON.parse(
|
|
6039
|
+
const parsed = JSON.parse(storage.getItem(OUTFIT_KEY) ?? "null");
|
|
5989
6040
|
return parsed !== null && typeof parsed === "object" ? parsed : null;
|
|
5990
6041
|
} catch {
|
|
5991
6042
|
return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-pet-live2d",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Live2D 桌宠插件:为 DSH Web GUI 挂一只可拖动、会跟随鼠标、能换动作与表情的 Live2D 宠物。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -49,7 +49,8 @@
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
"optionalDependencies": {
|
|
52
|
-
"dsh-pet-live2d-desktop-
|
|
52
|
+
"dsh-pet-live2d-desktop-darwin-arm64": "3.1.1",
|
|
53
|
+
"dsh-pet-live2d-desktop-win32-x64": "3.1.1"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
56
|
"esbuild": "^0.25.10",
|