@linxin666/dsh-pet 0.3.5 → 0.3.9

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.
Files changed (88) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +4 -0
  3. package/README.zh.md +4 -0
  4. package/contracts/voice-pack-v1.schema.json +30 -0
  5. package/cordis.patch.yml +1 -1
  6. package/lib/client.js +330 -103
  7. package/lib/client.js.map +1 -1
  8. package/lib/index.js +145 -28
  9. package/lib/invariant.js +1 -1
  10. package/lib/live2d-vendor.js +106 -95
  11. package/lib/live2d-vendor.js.map +1 -1
  12. package/lib/{state-DrMX22GL.js → state-NvDEoCln.js} +5 -2
  13. package/lib/types/access.d.ts +5 -4
  14. package/lib/types/access.d.ts.map +1 -1
  15. package/lib/types/access.js +2 -12
  16. package/lib/types/affinity.d.ts +2 -0
  17. package/lib/types/affinity.d.ts.map +1 -1
  18. package/lib/types/affinity.js +12 -0
  19. package/lib/types/announce.d.ts +53 -0
  20. package/lib/types/announce.d.ts.map +1 -0
  21. package/lib/types/announce.js +80 -0
  22. package/lib/types/client/PetDockEntry.d.ts +14 -5
  23. package/lib/types/client/PetDockEntry.d.ts.map +1 -1
  24. package/lib/types/client/PetDockEntry.js +6 -5
  25. package/lib/types/client/PetSettingsCard.d.ts +2 -1
  26. package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
  27. package/lib/types/client/PetSprite.d.ts +8 -0
  28. package/lib/types/client/PetSprite.d.ts.map +1 -1
  29. package/lib/types/client/PetSprite.js +36 -5
  30. package/lib/types/client/index.d.ts +8 -5
  31. package/lib/types/client/index.d.ts.map +1 -1
  32. package/lib/types/client/index.js +11 -5
  33. package/lib/types/client/locales.d.ts +20 -2
  34. package/lib/types/client/locales.d.ts.map +1 -1
  35. package/lib/types/client/locales.js +20 -2
  36. package/lib/types/client/pet-store.d.ts +1 -1
  37. package/lib/types/client/pet-store.d.ts.map +1 -1
  38. package/lib/types/client/pet-store.js +1 -1
  39. package/lib/types/client/renderers/frames2d.d.ts +12 -0
  40. package/lib/types/client/renderers/frames2d.d.ts.map +1 -1
  41. package/lib/types/client/renderers/frames2d.js +143 -18
  42. package/lib/types/client/settings-form.d.ts +35 -43
  43. package/lib/types/client/settings-form.d.ts.map +1 -1
  44. package/lib/types/client/settings-form.js +60 -53
  45. package/lib/types/index.d.ts.map +1 -1
  46. package/lib/types/index.js +10 -9
  47. package/lib/types/ledger.d.ts +4 -2
  48. package/lib/types/ledger.d.ts.map +1 -1
  49. package/lib/types/ledger.js +4 -4
  50. package/lib/types/pair-access.d.ts +35 -0
  51. package/lib/types/pair-access.d.ts.map +1 -0
  52. package/lib/types/pair-access.js +19 -0
  53. package/lib/types/remarks.d.ts +1 -1
  54. package/lib/types/remarks.d.ts.map +1 -1
  55. package/lib/types/remarks.js +11 -2
  56. package/lib/types/service.d.ts +18 -0
  57. package/lib/types/service.d.ts.map +1 -1
  58. package/lib/types/service.js +26 -1
  59. package/lib/types/voice-pack.d.ts +5 -0
  60. package/lib/types/voice-pack.d.ts.map +1 -1
  61. package/lib/types/voice-pack.js +36 -2
  62. package/package.json +17 -13
  63. package/src/access.ts +7 -27
  64. package/src/affinity.ts +13 -0
  65. package/src/announce.ts +103 -0
  66. package/src/client/PetDockEntry.test.tsx +1 -1
  67. package/src/client/PetDockEntry.tsx +15 -4
  68. package/src/client/PetSettingsCard.tsx +2 -1
  69. package/src/client/PetSprite.test.tsx +36 -0
  70. package/src/client/PetSprite.tsx +75 -4
  71. package/src/client/gameplay-hud.test.tsx +1 -1
  72. package/src/client/index.test.tsx +2 -2
  73. package/src/client/index.ts +17 -6
  74. package/src/client/locales.ts +20 -2
  75. package/src/client/pet-store.ts +2 -2
  76. package/src/client/pet.module.css +104 -0
  77. package/src/client/renderers/frames2d.test.ts +101 -0
  78. package/src/client/renderers/frames2d.ts +138 -18
  79. package/src/client/settings-form.ts +78 -92
  80. package/src/dsh-home.ts +1 -1
  81. package/src/index.ts +17 -15
  82. package/src/ledger.ts +6 -4
  83. package/src/pair-access.ts +49 -0
  84. package/src/remarks.test.ts +10 -0
  85. package/src/remarks.ts +9 -2
  86. package/src/service.ts +32 -2
  87. package/src/voice-pack.test.ts +39 -0
  88. package/src/voice-pack.ts +37 -2
package/lib/index.js CHANGED
@@ -1,5 +1,4 @@
1
- import { _ as RemarkPicker, a as AFFINITY_MAX, c as applyInteraction, d as emptyAffinity, f as rankOf, g as REMARK_LINE_MAX, h as REMARK_LINES_MAX, i as rowOf, l as applyTurnReward, m as REMARK_KINDS, n as animationForPhase, o as AFFINITY_RANKS, p as BUILTIN_REMARKS, r as defaultPetStateConfig, s as affinityViewOf, t as PetStateMachine, u as defaultAffinityConfig, v as builtinRemark, y as normalizePetRemarks } from "./state-DrMX22GL.js";
2
- import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
1
+ import { _ as RemarkPicker, a as AFFINITY_MAX, c as applyInteraction, d as emptyAffinity, f as rankOf, g as REMARK_LINE_MAX, h as REMARK_LINES_MAX, i as rowOf, l as applyTurnReward, m as REMARK_KINDS, n as animationForPhase, o as AFFINITY_RANKS, p as BUILTIN_REMARKS, r as defaultPetStateConfig, s as affinityViewOf, t as PetStateMachine, u as defaultAffinityConfig, v as builtinRemark, y as normalizePetRemarks } from "./state-NvDEoCln.js";
3
2
  import z from "schemastery";
4
3
  import { Service } from "@deepseek-ai/cordis";
5
4
  import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
@@ -8,6 +7,67 @@ import { homedir } from "node:os";
8
7
  import { isAbsolute as isAbsolute$1, join as join$1 } from "node:path/posix";
9
8
  import { fileURLToPath } from "node:url";
10
9
  import { readFile } from "node:fs/promises";
10
+ //#region src/announce.ts
11
+ /** Default freshness window. */
12
+ const ANNOUNCE_DEFAULT_TTL_MS = 1e4;
13
+ /**
14
+ * Hard TTL ceiling. A repeating announcer (dsh-usage) declares its poll
15
+ * cadence as the TTL, so an `always`-mode bubble stays continuous across
16
+ * polls; the ceiling means a source that dies unmounts its bubble within at
17
+ * most one missed refresh cycle rather than lingering forever.
18
+ */
19
+ const ANNOUNCE_MAX_TTL_MS = 72e5;
20
+ /** Hard bounds: lengths and the TTL range (1 s .. 60 s). */
21
+ const TITLE_MAX = 80;
22
+ const TEXT_MAX = 120;
23
+ const SOURCE_MAX = 64;
24
+ function boundedString(value, max) {
25
+ if (typeof value !== "string") return void 0;
26
+ const trimmed = value.trim();
27
+ if (trimmed === "") return void 0;
28
+ return trimmed.length <= max ? trimmed : trimmed.slice(0, max);
29
+ }
30
+ /**
31
+ * Validate one announce payload. Unknown fields are dropped, oversized text
32
+ * is truncated, and anything structurally wrong resolves to undefined — a
33
+ * malformed announcement never reaches the pet's bubble surface.
34
+ * @param input - the payload a sibling plugin passed to `pet.announce`.
35
+ * @param now - epoch ms the announcement arrived.
36
+ */
37
+ function parseAnnouncement(input, now) {
38
+ if (typeof input !== "object" || input === null) return void 0;
39
+ const data = input;
40
+ const source = boundedString(data.source, SOURCE_MAX);
41
+ const title = boundedString(data.title, TITLE_MAX);
42
+ if (source === void 0 || title === void 0) return void 0;
43
+ const kind = data.kind === "balance" || data.kind === "cost" || data.kind === "plan" ? data.kind : void 0;
44
+ if (kind === void 0) return void 0;
45
+ const amount = boundedString(data.amount, TEXT_MAX);
46
+ const resetAt = boundedString(data.resetAt, TEXT_MAX);
47
+ const note = boundedString(data.note, TITLE_MAX);
48
+ const percent = typeof data.percent === "number" && Number.isFinite(data.percent) ? Math.max(0, Math.min(100, data.percent)) : void 0;
49
+ if ((kind === "balance" || kind === "cost") && amount === void 0) return void 0;
50
+ if (kind === "plan" && percent === void 0) return void 0;
51
+ const tone = data.tone === "warn" || data.tone === "low" ? data.tone : "ok";
52
+ const ttlMs = typeof data.ttlMs === "number" && Number.isFinite(data.ttlMs) ? Math.max(1e3, Math.min(ANNOUNCE_MAX_TTL_MS, data.ttlMs)) : ANNOUNCE_DEFAULT_TTL_MS;
53
+ return {
54
+ source,
55
+ kind,
56
+ title,
57
+ ...amount !== void 0 ? { amount } : {},
58
+ ...percent !== void 0 ? { percent } : {},
59
+ ...resetAt !== void 0 ? { resetAt } : {},
60
+ ...note !== void 0 ? { note } : {},
61
+ tone,
62
+ ttlMs,
63
+ at: now
64
+ };
65
+ }
66
+ /** Whether an announcement is still fresh at `now`. */
67
+ function announcementFresh(announcement, now) {
68
+ return now - announcement.at < announcement.ttlMs;
69
+ }
70
+ //#endregion
11
71
  //#region src/chatter.ts
12
72
  /** While a scene persists, its copy advances on this cadence (ms). */
13
73
  const STATUS_ROTATE_MS = 4e3;
@@ -1037,7 +1097,7 @@ var PetLedger = class {
1037
1097
  ...defaultTreatConfig,
1038
1098
  ...config.treats ?? {}
1039
1099
  };
1040
- this.picker = new RemarkPicker(config.remarks);
1100
+ this.picker = new RemarkPicker(config.remarks, config.voiceRemarks);
1041
1101
  this.current = persist;
1042
1102
  }
1043
1103
  /** Affinity cooldown/rank tuning (read-only). */
@@ -1106,10 +1166,10 @@ var PetLedger = class {
1106
1166
  }
1107
1167
  /**
1108
1168
  * Swap the reaction pools to another pet's custom remarks (called on pet
1109
- * selection). Slots the pet does not declare fall back to built-ins.
1169
+ * selection). Slots the pet does not declare fall back to voice packs or built-ins.
1110
1170
  */
1111
- setRemarks(remarks) {
1112
- this.picker = new RemarkPicker(remarks);
1171
+ setRemarks(remarks, voiceRemarks) {
1172
+ this.picker = new RemarkPicker(remarks, voiceRemarks);
1113
1173
  }
1114
1174
  /**
1115
1175
  * Settle the treat economy (work + time output since the last settlement).
@@ -1600,7 +1660,9 @@ const VOICE_PACK_KEYS = /* @__PURE__ */ new Set([
1600
1660
  "tools",
1601
1661
  "toolRemaining",
1602
1662
  "whispers",
1603
- "panel"
1663
+ "panel",
1664
+ "remarks",
1665
+ "ranks"
1604
1666
  ]);
1605
1667
  /** Allowed whisper-section fields (drift-locked in tests). */
1606
1668
  const WHISPER_KEYS = /* @__PURE__ */ new Set(["categories", "results"]);
@@ -1663,10 +1725,22 @@ function normalizeVoicePack(raw, onWarning = () => {}) {
1663
1725
  };
1664
1726
  }
1665
1727
  const panel = raw.panel === void 0 ? void 0 : normalizePanel(raw.panel, onWarning);
1666
- if (overrides.status === void 0 && overrides.tools === void 0 && overrides.toolRemaining === void 0 && overrides.whispers === void 0 && panel === void 0) return;
1728
+ const remarks = raw.remarks === void 0 ? void 0 : normalizePetRemarks(raw.remarks, onWarning);
1729
+ const ranksRaw = raw.ranks;
1730
+ let ranks;
1731
+ if (ranksRaw !== void 0) if (!isRecord$3(ranksRaw)) onWarning("ranks must be an object");
1732
+ else {
1733
+ const out = {};
1734
+ for (const [key, value] of Object.entries(ranksRaw)) if (typeof value === "string" && value.trim() !== "") out[key] = value.trim().slice(0, 80);
1735
+ else onWarning("invalid rank name for " + key);
1736
+ if (Object.keys(out).length > 0) ranks = out;
1737
+ }
1738
+ if (overrides.status === void 0 && overrides.tools === void 0 && overrides.toolRemaining === void 0 && overrides.whispers === void 0 && panel === void 0 && remarks === void 0 && ranks === void 0) return;
1667
1739
  return {
1668
1740
  overrides,
1669
- ...panel === void 0 ? {} : { panel }
1741
+ ...panel === void 0 ? {} : { panel },
1742
+ ...remarks === void 0 ? {} : { remarks },
1743
+ ...ranks === void 0 ? {} : { ranks }
1670
1744
  };
1671
1745
  }
1672
1746
  /**
@@ -1679,6 +1753,8 @@ function mergeVoicePacks(...layers) {
1679
1753
  const overrides = {};
1680
1754
  const labels = {};
1681
1755
  const stats = {};
1756
+ let remarks;
1757
+ let ranks;
1682
1758
  let actions;
1683
1759
  let panelSeen = false;
1684
1760
  let any = false;
@@ -1704,6 +1780,14 @@ function mergeVoicePacks(...layers) {
1704
1780
  if (layer.panel.stats !== void 0) Object.assign(stats, layer.panel.stats);
1705
1781
  if (layer.panel.actions !== void 0) actions = layer.panel.actions;
1706
1782
  }
1783
+ if (layer.remarks !== void 0) remarks = {
1784
+ ...remarks ?? {},
1785
+ ...layer.remarks
1786
+ };
1787
+ if (layer.ranks !== void 0) ranks = {
1788
+ ...ranks ?? {},
1789
+ ...layer.ranks
1790
+ };
1707
1791
  }
1708
1792
  if (!any) return void 0;
1709
1793
  const panel = {
@@ -1714,7 +1798,9 @@ function mergeVoicePacks(...layers) {
1714
1798
  const panelEmpty = panel.labels === void 0 && panel.stats === void 0 && panel.actions === void 0;
1715
1799
  return {
1716
1800
  overrides,
1717
- ...panelSeen && !panelEmpty ? { panel } : {}
1801
+ ...panelSeen && !panelEmpty ? { panel } : {},
1802
+ ...remarks !== void 0 ? { remarks } : {},
1803
+ ...ranks !== void 0 ? { ranks } : {}
1718
1804
  };
1719
1805
  }
1720
1806
  //#endregion
@@ -4064,6 +4150,8 @@ var PetService = class extends Service {
4064
4150
  enabled;
4065
4151
  /** Status-decoration master switch (M5, #567); mirrored from settings. */
4066
4152
  decorationEnabled;
4153
+ /** The freshest plugin-authored announcement (dsh-usage linkage). */
4154
+ announcement;
4067
4155
  disposeActivity;
4068
4156
  /** Session whose most recent meaningful event currently drives the global pet. */
4069
4157
  displaySession;
@@ -4099,10 +4187,12 @@ var PetService = class extends Service {
4099
4187
  petId: this.registry.defaultEntry().id
4100
4188
  };
4101
4189
  const selected = this.registry.byId(persist.petId) ?? this.registry.defaultEntry();
4190
+ const voiceRemarks = mergeVoicePacks(this.registry.globalVoice, selected.voice)?.remarks;
4102
4191
  const ledgerConfig = {
4103
4192
  affinity: config.affinity,
4104
4193
  treats: config.treats,
4105
- remarks: selected.remarks
4194
+ remarks: selected.remarks,
4195
+ voiceRemarks
4106
4196
  };
4107
4197
  this.ledger = new PetLedger(persist, ledgerConfig);
4108
4198
  this.stateConfig = {
@@ -4139,6 +4229,18 @@ var PetService = class extends Service {
4139
4229
  async state(currentSessionId) {
4140
4230
  return this.view(currentSessionId);
4141
4231
  }
4232
+ /**
4233
+ * RPC: one plugin-authored announcement bubble (dsh-usage linkage). The
4234
+ * payload is validated into a bounded PetAnnouncement; a malformed one is
4235
+ * dropped silently — a sibling plugin's bug must never surface as pet
4236
+ * breakage. The announcement is in-memory only.
4237
+ */
4238
+ announce(input) {
4239
+ const parsed = parseAnnouncement(input, Date.now());
4240
+ if (parsed === void 0) return { ok: false };
4241
+ this.announcement = parsed;
4242
+ return { ok: true };
4243
+ }
4142
4244
  /** Current persisted display config (read-only view). */
4143
4245
  display() {
4144
4246
  return { ...this.ledger.snapshot.display };
@@ -4186,7 +4288,8 @@ var PetService = class extends Service {
4186
4288
  error: "unknown-pet"
4187
4289
  };
4188
4290
  this.ledger.setPetId(entry.id);
4189
- this.ledger.setRemarks(entry.remarks);
4291
+ const voiceRemarks = mergeVoicePacks(this.registry.globalVoice, entry.voice)?.remarks;
4292
+ this.ledger.setRemarks(entry.remarks, voiceRemarks);
4190
4293
  this.flush();
4191
4294
  this.syncSettingsFromPet();
4192
4295
  return {
@@ -4617,6 +4720,7 @@ var PetService = class extends Service {
4617
4720
  settleGameplay(state, gameplayDef, Date.now(), { sessionActive: snapshot.sessionActive });
4618
4721
  gameplay = this.gameplayViewOf(state);
4619
4722
  }
4723
+ const announcement = this.announcement !== void 0 && announcementFresh(this.announcement, Date.now()) ? this.announcement : void 0;
4620
4724
  return {
4621
4725
  animation: snapshot.animation,
4622
4726
  ...snapshot.bubble === void 0 ? {} : { bubble: snapshot.bubble },
@@ -4624,6 +4728,7 @@ var PetService = class extends Service {
4624
4728
  sessionActive: snapshot.sessionActive,
4625
4729
  sessions,
4626
4730
  ...decoration === void 0 ? {} : { decoration },
4731
+ ...announcement === void 0 ? {} : { announcement },
4627
4732
  affinity: this.ledger.affinityView(Date.now()),
4628
4733
  display: { ...this.ledger.snapshot.display },
4629
4734
  pet: {
@@ -4691,23 +4796,33 @@ function isLoopbackRequest(request) {
4691
4796
  }
4692
4797
  }
4693
4798
  //#endregion
4694
- //#region src/access.ts
4799
+ //#region src/pair-access.ts
4695
4800
  /**
4696
- * Whether this request may enter any /api/pet or /pet asset route.
4801
+ * Whether this request may enter the plugin's host routes.
4697
4802
  * @param ctx - host context; may expose remoteWebUiPairing.
4698
4803
  * @param request - the incoming HTTP request.
4699
4804
  * @returns true for loopback, or a live paired-device cookie.
4700
4805
  */
4701
- function isPetAllowed(ctx, request) {
4806
+ function isPairedOrLoopbackAllowed(ctx, request) {
4702
4807
  if (isLoopbackRequest(request)) return true;
4703
- const bag = ctx;
4704
- const fromGet = typeof bag.get === "function" ? bag.get("remoteWebUiPairing", false) : void 0;
4705
- return (isPairingAccess(fromGet) ? fromGet : bag.remoteWebUiPairing)?.isPairedDevice(request) === true;
4808
+ const fromGet = typeof ctx.get === "function" ? ctx.get("remoteWebUiPairing", false) : void 0;
4809
+ return (isPairingAccess(fromGet) ? fromGet : ctx.remoteWebUiPairing)?.isPairedDevice(request) === true;
4706
4810
  }
4707
4811
  function isPairingAccess(value) {
4708
4812
  return value !== void 0 && value !== null && typeof value.isPairedDevice === "function";
4709
4813
  }
4710
4814
  //#endregion
4815
+ //#region src/access.ts
4816
+ /**
4817
+ * Whether this request may enter any /api/pet or /pet asset route.
4818
+ * @param ctx - host context; may expose remoteWebUiPairing.
4819
+ * @param request - the incoming HTTP request.
4820
+ * @returns true for loopback, or a live paired-device cookie.
4821
+ */
4822
+ function isPetAllowed(ctx, request) {
4823
+ return isPairedOrLoopbackAllowed(ctx, request);
4824
+ }
4825
+ //#endregion
4711
4826
  //#region src/http.ts
4712
4827
  /** Default body cap for readJsonBody: 64 KiB. */
4713
4828
  const DEFAULT_JSON_BODY_MAX_BYTES = 64 * 1024;
@@ -5427,16 +5542,18 @@ function applyImpl(ctx, config = {}) {
5427
5542
  disposeRoutes = void 0;
5428
5543
  }
5429
5544
  };
5430
- installSettingsSection(ctx, settingsNamespace("pet"), makePetSettingsSchema(service.selectedPetId()), base, {
5431
- setSource: (source) => {
5432
- current = source;
5433
- },
5434
- onChange: () => {
5435
- const section = current();
5436
- service.applySettingsSection(section);
5437
- service.setEnabled(section.enabled ?? true);
5438
- syncRoutes();
5439
- }
5545
+ ctx.inject(["settings"], (settingsCtx) => {
5546
+ settingsCtx.settings.installSection(ctx, "pet", makePetSettingsSchema(service.selectedPetId()), base, {
5547
+ setSource: (source) => {
5548
+ current = source;
5549
+ },
5550
+ onChange: () => {
5551
+ const section = current();
5552
+ service.applySettingsSection(section);
5553
+ service.setEnabled(section.enabled ?? true);
5554
+ syncRoutes();
5555
+ }
5556
+ });
5440
5557
  });
5441
5558
  syncRoutes();
5442
5559
  }
package/lib/invariant.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as AFFINITY_MAX, n as animationForPhase, o as AFFINITY_RANKS, u as defaultAffinityConfig } from "./state-DrMX22GL.js";
1
+ import { a as AFFINITY_MAX, n as animationForPhase, o as AFFINITY_RANKS, u as defaultAffinityConfig } from "./state-NvDEoCln.js";
2
2
  //#region src/invariant.ts
3
3
  /**
4
4
  * Package invariants — cheap structural checks run at import time on the