dsh-pet-live2d 3.2.0 → 3.3.0

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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # 更新日志
2
2
 
3
+ ## 3.3.0(2026-10-09)
4
+
5
+ ### 新:官方「插件」页里有自己的图标和常用设置
6
+
7
+ 侧栏「插件」页上这张卡片以前是默认插画加包名,现在有自己的图标、中文与英文标题/描述
8
+ (「设置 → 内置插件」那份列表同样受益)。点进详情页多了一节配置区:常用开关(气泡 /
9
+ 提示音 / 摸头 / 摸尾巴 / 转圈 / 记住装扮)、四根手感滑杆(注视满偏、收回倍数、摸鱼静置、
10
+ 提示音音量)和「显示位置」。它写的是与 **设置 → 桌宠** 同一份配置,改哪边都一样;
11
+ 相位台词、池子编辑器、装扮与文案仍在设置页里改。
12
+
3
13
  ## 3.2.0(2026-10-09)
4
14
 
5
15
  ### 新:会话状态提示音
package/README.md CHANGED
@@ -68,10 +68,22 @@ dsh plugin --profile web add "link:./dsh-pet-live2d/dsh-live2d-pet"
68
68
 
69
69
  - **右键点宠物** 呼出面板:「动作」「装扮」两个页签,底部是大小滑杆和「归位」。
70
70
  平时画面上没有常驻 UI。
71
- - **改配置** 去 **DSH 设置页 → 桌宠**(面板只负责"点一下换个样子")。
71
+ - **改配置** 两个入口:**DSH 设置页 → 桌宠**(完整设置),或**侧栏「插件」→ 本插件**(常用开关)。
72
72
 
73
73
  ## 设置说明
74
74
 
75
+ ### 官方「插件」页上的那一节
76
+
77
+ 侧栏点「插件」,「已安装」里那张带图标的卡片就是本插件。点进详情,描述与插件行之间是它
78
+ 自己的配置区:
79
+
80
+ - **常用开关**:气泡、会话提示音、摸头 / 摸尾巴 / 转圈、跨启动记住装扮;
81
+ - **四根滑杆**:注视满偏、收回倍数、静置多久开始摸鱼、提示音音量;
82
+ - **显示位置**:自动 / 页面内 / 桌面。
83
+
84
+ 改的是与设置页**同一份**配置(`%DSH_HOME%\pet-settings.json`),两边随时一致。相位台词、
85
+ 池子编辑器、装扮与台词文案仍在 **设置 → 桌宠** 里改 —— 那里才是完整设置。
86
+
75
87
  ### 摸鱼:每个槽位一张条目表
76
88
 
77
89
  摸鱼 = 静置一会儿之后,宠物自己换个样子。**每个槽位各掷一次骰子**(不是"这次只动一两个"),
package/icon.svg ADDED
@@ -0,0 +1,45 @@
1
+ <!--
2
+ dsh-pet-live2d 的插件图标(原创几何图形,不含模型的任何美术素材 ——
3
+ 模型是 B站@氵六青 的 DS鲸鱼娘,授权与这个图标无关)。
4
+
5
+ 用途:DSH 侧栏「插件」页、设置 → 内置插件列表里这张卡片的图标。
6
+ 宿主的读法(`dsh-app-boot` 的 `readPluginMeta` → `iconOf`):
7
+ package.json 的 `"icon": "./icon.svg"`,或导出的 `./icon` 子路径;
8
+ 必须是**包目录内的相对路径**,SVG / PNG / JPEG / WebP,≤256 KiB。
9
+ 它以 `data:image/svg+xml;base64,…` 塞进 `<img width=36 height=36>`,
10
+ `object-fit:contain` —— 所以画布按 36×36 画、图形留一点边距,
11
+ 且**不能依赖 currentColor**(img 里没有继承),颜色一律写死。
12
+
13
+ 几何约定:所有图形都在 0…36 的 viewBox 内;尾鳍画在身体**之前**,
14
+ 它靠左下那只角(10,20.4)藏在身体里,看起来才是连着的。
15
+ -->
16
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" width="36" height="36" role="img" aria-label="Live2D pet">
17
+ <title>Live2D 桌宠</title>
18
+ <defs>
19
+ <linearGradient id="pet-tile" x1="0" y1="0" x2="0.45" y2="1">
20
+ <stop offset="0" stop-color="#7FB0FF"/>
21
+ <stop offset="1" stop-color="#3B5DD4"/>
22
+ </linearGradient>
23
+ </defs>
24
+
25
+ <!-- 底:一块圆角砖。浅色与深色主题下都要能看见,所以自带底色而不是只描线。 -->
26
+ <rect x="0.6" y="0.6" width="34.8" height="34.8" rx="9.4" fill="url(#pet-tile)"/>
27
+
28
+ <!-- 头顶冒出来的那颗心(对应她的「冒爱心」表情) -->
29
+ <g fill="#FFB4D2">
30
+ <circle cx="12.7" cy="7.7" r="2"/>
31
+ <circle cx="16.5" cy="7.7" r="2"/>
32
+ <path d="M10.6 8.4h8l-4 4.3z"/>
33
+ </g>
34
+
35
+ <!-- 尾鳍(左下角压在身体下面) -->
36
+ <path d="M10 20.4 2.2 12.4l8.6.8z" fill="#D8E6FF"/>
37
+
38
+ <!-- 身体 -->
39
+ <ellipse cx="17.6" cy="21" rx="13" ry="7.4" fill="#FFFFFF"/>
40
+
41
+ <!-- 眼睛 / 嘴 / 腮红 -->
42
+ <circle cx="23.6" cy="19.9" r="1.7" fill="#1E2A4C"/>
43
+ <path d="M21.7 23.6c1 1 2.3 1 3.3 0" fill="none" stroke="#1E2A4C" stroke-width="1" stroke-linecap="round" opacity=".9"/>
44
+ <ellipse cx="18.9" cy="24" rx="1.9" ry="1.1" fill="#FFA6C8"/>
45
+ </svg>
package/lib/client.js CHANGED
@@ -26,6 +26,15 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
26
26
  const { useCallback, useEffect, useRef, useState } = react;
27
27
 
28
28
  const name = "live2d-pet";
29
+ /**
30
+ * 本包的 npm 包名。
31
+ *
32
+ * 官方「插件」页按**包名**派发组合包的配置区(`plugins.bundle.config` 的 key 就是
33
+ * 组合包的包名,见 `dsh-client-ui-plugin-manager` 的 `configLedgerSource`),所以这个
34
+ * 字符串必须与 `package.json` 的 `name` 逐字相同 —— 写错的表现是"注册没报错、
35
+ * 页面上什么都没多出来"(页面按包名查不到这个键就不渲染那一节)。
36
+ */
37
+ const PACKAGE_NAME = "dsh-pet-live2d";
29
38
  // "slots" 是 DSH 客户端界面给插件的扩展点(设置页就是这么挂进去的)。
30
39
  const inject = ["slots"];
31
40
 
@@ -3550,7 +3559,20 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
3550
3559
  + SETTINGS_SEL + " [data-setting=phases]{grid-template-columns:minmax(0,1fr)}",
3551
3560
  "}",
3552
3561
  ];
3553
- const STYLE_TEXT = CSS + "\n" + SETTINGS_CSS.join("\n") + "\n" + PAGE_PHASE_CSS.join("\n");
3562
+ /**
3563
+ * 官方「插件」页那一节的微调(作用域 = 两个属性同时在的那个根节点)。
3564
+ *
3565
+ * 只有两条,都是有理由的:
3566
+ * * 状态行("她现在在哪")本来借的是 `[data-note]` 的灰度 —— 那是给补充说明用的,
3567
+ * 而这一行是这一节**最先要回答**的问题,按 55% 透明度画就太虚了;
3568
+ * * 插件页是窄栏,卡片间距收一档,四张卡不至于要滚半天。
3569
+ */
3570
+ const PLUGIN_PAGE_CSS = [
3571
+ SETTINGS_SEL + "[data-pet-plugin-page]>[data-plugin-status]{font-size:11px;opacity:.85;padding:0 0 9px}",
3572
+ SETTINGS_SEL + "[data-pet-plugin-page] [data-card]{margin-bottom:8px}",
3573
+ ];
3574
+ const STYLE_TEXT = CSS + "\n" + SETTINGS_CSS.join("\n") + "\n" + PLUGIN_PAGE_CSS.join("\n")
3575
+ + "\n" + PAGE_PHASE_CSS.join("\n");
3554
3576
 
3555
3577
  function ensureStyle() {
3556
3578
  if (document.getElementById(STYLE_ID) !== null) return;
@@ -4752,8 +4774,13 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
4752
4774
  function TuningControls(props) {
4753
4775
  useSettings();
4754
4776
  const only = props?.group;
4755
- const fields = TUNING_FIELDS.filter((field) => only === undefined || tuningGroupOf(field) === only);
4756
- return h("div", { "data-settings": "", "data-setting": only ?? "all" },
4777
+ // 也能按**具体键**挑几根滑杆(官方插件页那一节只放最常用的四项,
4778
+ // 不想把八根手感滑杆整套搬过去)。给了 keys 就按 keys 过滤,忽略 group。
4779
+ const keys = Array.isArray(props?.keys) ? props.keys : null;
4780
+ const fields = TUNING_FIELDS.filter((field) => keys === null
4781
+ ? (only === undefined || tuningGroupOf(field) === only)
4782
+ : keys.includes(field.key));
4783
+ return h("div", { "data-settings": "", "data-setting": only ?? (keys === null ? "all" : "subset") },
4757
4784
  fields.map((field) => h("label", {
4758
4785
  key: field.key,
4759
4786
  "data-field": field.key,
@@ -4773,13 +4800,14 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
4773
4800
  h("code", { "data-value": field.key }, String(TUNING[field.key])),
4774
4801
  )),
4775
4802
  // 恢复默认单独一行:它是"这一组"的动作,混在滑杆行里会看着像又一个控件。
4803
+ // 只挑了四项时按钮说"全部" —— 它恢复的确实是整份 TUNING,不含糊。
4776
4804
  only === undefined || only === "feel"
4777
4805
  ? h("div", { "data-add-row": "" },
4778
4806
  h("button", {
4779
4807
  type: "button",
4780
4808
  "data-reset": "tuning",
4781
4809
  onClick: () => applyTuning(Object.assign({}, TUNING_DEFAULTS)),
4782
- }, "恢复默认"))
4810
+ }, keys === null ? "恢复默认" : "全部恢复默认"))
4783
4811
  : null,
4784
4812
  );
4785
4813
  }
@@ -8621,15 +8649,25 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
8621
8649
  );
8622
8650
  }
8623
8651
 
8624
- function PetSettingsBody({ compact = false } = {}) {
8625
- useSettings();
8626
- const card = (key, title, hint, body) => h("div", { key, "data-card": key },
8652
+ /**
8653
+ * 一张设置卡片:标题条(+ 右上角提示)+ 内容区。
8654
+ *
8655
+ * 提到模块作用域是因为现在**两个地方**要画同一种卡片:DSH 设置页那一节,和官方
8656
+ * 「插件」页上这一组合包的配置区。各写一份的下场是两边慢慢长得不一样。
8657
+ */
8658
+ function settingsCard(key, title, hint, body) {
8659
+ return h("div", { key, "data-card": key },
8627
8660
  h("div", { "data-card-head": "" },
8628
8661
  h("span", { "data-card-title": "" }, title),
8629
8662
  hint === null || hint === undefined ? null : h("span", { "data-card-hint": "" }, hint),
8630
8663
  ),
8631
8664
  h("div", { "data-card-body": "" }, body),
8632
8665
  );
8666
+ }
8667
+
8668
+ function PetSettingsBody({ compact = false } = {}) {
8669
+ useSettings();
8670
+ const card = settingsCard;
8633
8671
  return [
8634
8672
  ...TUNING_GROUPS.filter((group) => !TUNING_GROUPS_INLINE.includes(group.id)).map((group) => card(
8635
8673
  "tune-" + group.id, group.label, group.hint, h(TuningControls, { group: group.id }))),
@@ -8680,6 +8718,61 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
8680
8718
  return h("div", { "data-pet-settings": "" }, h(PetSettingsBody, null));
8681
8719
  }
8682
8720
 
8721
+ /**
8722
+ * 侧栏「插件」页上这一组合包的配置区(`plugins.bundle.config`,键 = 包名)。
8723
+ *
8724
+ * 为什么要有它:官方插件页把"插件自带配置"渲染在**组合包详情页**的描述与行之间
8725
+ * (`dsh-client-ui-plugin-manager`;页面只在有人注册了这个键时才画那一节)。我们的
8726
+ * 组合包只有一行(`live2d-pet`),所以"这一行的配置"就是"这只宠物的配置"。
8727
+ *
8728
+ * 它**不是**设置正文的第二个家:相位台词、池子编辑器、装扮与文案仍然只在
8729
+ * 「设置 → 桌宠」里改(正文只写一份,这条是 2.0.0 定下来的)。这里是插件页上最常用的
8730
+ * 那几项 —— 开关、四根滑杆、显示位置 —— 写进去的还是**同一份共享设置**
8731
+ * (`persistShared` → `%DSH_HOME%\pet-settings.json`),所以两个界面永远一致。
8732
+ *
8733
+ * 样式直接复用设置正文那一套(`[data-pet-settings]` 作用域):卡片、行、滑杆、药丸
8734
+ * 按钮的观感与设置页逐字相同,不需要新写一份 CSS。
8735
+ */
8736
+ function PetPluginConfig() {
8737
+ useSettings();
8738
+ const layer = useLayerState();
8739
+ const pet = MANIFEST.current ?? {};
8740
+ const petLabel = typeof pet.label === "string" && pet.label !== ""
8741
+ ? pet.label
8742
+ : (typeof pet.id === "string" && pet.id !== "" ? pet.id : "还没加载");
8743
+ const modeLabel = layer.mode === "inline" ? "页面内" : (layer.mode === "desktop" ? "桌面" : "自动");
8744
+ const ownerLabel = layer.owner === "desktop" ? "现在在桌面上" : "现在在页面里";
8745
+ const flagRow = (key, label, note) => h("label", { key, "data-flag-row": key },
8746
+ h("input", {
8747
+ type: "checkbox",
8748
+ checked: FLAGS[key] === true,
8749
+ "data-flag": key,
8750
+ onChange: (event) => applyFlag(key, event.target.checked),
8751
+ }),
8752
+ h("span", { "data-row-label": "" }, label),
8753
+ note === undefined ? null : h("span", { "data-note-inline": "" }, note),
8754
+ );
8755
+ return h("div", { "data-pet-settings": "", "data-pet-plugin-page": "" },
8756
+ // 第一行先说"这只宠物现在在哪":它比任何开关都更该先回答,
8757
+ // 而且换过显示位置的人回来第一眼就是找这一句。
8758
+ h("div", { "data-plugin-status": "" },
8759
+ "当前宠物:" + petLabel + " · 显示位置:" + modeLabel + "(" + ownerLabel + ")"),
8760
+ settingsCard("plugin-flags", "常用开关", "点一下立刻生效", [
8761
+ flagRow("bubbleEnabled", "显示气泡", FLAGS.bubbleEnabled ? undefined : "已关:任何台词都不弹"),
8762
+ flagRow("soundEnabled", "会话提示音", FLAGS.soundEnabled ? undefined : "默认静音"),
8763
+ flagRow("patEnabled", "摸头有反应", undefined),
8764
+ flagRow("tailEnabled", "摸尾巴有反应", undefined),
8765
+ flagRow("spinEnabled", "鼠标绕着转圈会晕", undefined),
8766
+ flagRow("outfitArchive", "跨启动记住装扮", FLAGS.outfitArchive ? undefined : "已关:也不再记录"),
8767
+ ]),
8768
+ settingsCard("plugin-tuning", "手感", "注视范围 · 摸鱼节奏 · 音量",
8769
+ h(TuningControls, { keys: ["gazeRangePx", "gazeWatchingRatio", "fidgetQuietMs", "soundVolume"] })),
8770
+ settingsCard("plugin-layer", "显示位置", "页面内 / 桌面上", h(LayerControls, null)),
8771
+ h("div", { "data-note": "" },
8772
+ "相位台词、池子编辑器、装扮与台词文案在「设置 → 桌宠」里改。"),
8773
+ );
8774
+ }
8775
+
8683
8776
  function applySettings(ctx) {
8684
8777
  if (ctx === null || ctx === undefined) return;
8685
8778
  const slots = ctx.slots;
@@ -8696,6 +8789,30 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
8696
8789
  }
8697
8790
  }
8698
8791
 
8792
+ /**
8793
+ * 侧栏「插件」页里的配置区。
8794
+ *
8795
+ * 两个容易踩的点:
8796
+ *
8797
+ * * **键必须写包名**(`PACKAGE_NAME`)—— 页面按组合包的包名派发这一格,写错的表现是
8798
+ * "注册成功、页面什么都不多"。
8799
+ * * **`slots.inject` 而不是"现在就注册"**:插件页是懒挂载的面板,它的子 slot 在页面
8800
+ * 注册时才声明;`inject` 会等到那一刻再跑(页面重开/销毁也跟着走)。
8801
+ */
8802
+ function applyPluginPage(ctx) {
8803
+ if (ctx === null || ctx === undefined) return;
8804
+ const slots = ctx.slots;
8805
+ if (slots === undefined || slots === null) return;
8806
+ try {
8807
+ slots.inject("plugins.bundle.config", () => slots.register({
8808
+ name: "plugins.bundle.config",
8809
+ key: PACKAGE_NAME,
8810
+ }, () => h(PetPluginConfig, null)));
8811
+ } catch {
8812
+ /* 老版本 DSH 没有这条 slot:设置页那一节还在,不影响使用 */
8813
+ }
8814
+ }
8815
+
8699
8816
  function apply(ctx) {
8700
8817
  ensureStyle();
8701
8818
  // 先按本地存档起来(宿主不在时这就是全部)。
@@ -8742,6 +8859,7 @@ window.__ModuleLoader__.load({ id: "dsh-pet-live2d", factory: (require) => {
8742
8859
  }, SHARED_POLL_MS);
8743
8860
  }
8744
8861
  applySettings(ctx);
8862
+ applyPluginPage(ctx);
8745
8863
  // Takeover: an earlier instance — a hot reload, or one left behind by a
8746
8864
  // crashed reload — must not leave a second floating pet on the page.
8747
8865
  teardown();
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Live2D Pet",
4
+ "description": "A Live2D pet docked in the DSH interface: she follows the pointer, reacts to clicks and changes expressions with each session phase, with configurable motions, expressions and lines."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Live2D 桌宠",
4
+ "description": "在 DSH 界面里挂一只 Live2D 宠物:会跟着鼠标看、点一下有反应、按会话相位换表情,动作 / 表情 / 台词都能配。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "dsh-pet-live2d",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "description": "Live2D 桌宠插件:为 DSH Web GUI 挂一只可拖动、会跟随鼠标、能换动作与表情的 Live2D 宠物。",
5
+ "icon": "./icon.svg",
5
6
  "type": "module",
6
7
  "main": "lib/index.js",
7
8
  "license": "MIT",
@@ -18,6 +19,8 @@
18
19
  "src",
19
20
  "docs",
20
21
  "pets",
22
+ "locale",
23
+ "icon.svg",
21
24
  "cordis.patch.yml",
22
25
  "screenshots.json",
23
26
  "README.md",
@@ -31,6 +34,7 @@
31
34
  "./client": "./lib/client.js",
32
35
  "./desktop": "./lib/desktop.js",
33
36
  "./cordis.patch.yml": "./cordis.patch.yml",
37
+ "./locale/*.json": "./locale/*.json",
34
38
  "./package.json": "./package.json"
35
39
  },
36
40
  "scripts": {
@@ -49,8 +53,8 @@
49
53
  }
50
54
  },
51
55
  "optionalDependencies": {
52
- "dsh-pet-live2d-desktop-darwin-arm64": "3.2.0",
53
- "dsh-pet-live2d-desktop-win32-x64": "3.2.0"
56
+ "dsh-pet-live2d-desktop-darwin-arm64": "3.3.0",
57
+ "dsh-pet-live2d-desktop-win32-x64": "3.3.0"
54
58
  },
55
59
  "devDependencies": {
56
60
  "esbuild": "^0.25.10",