@linxin666/dsh-pet 0.2.0 → 0.2.2

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 (59) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +15 -5
  3. package/README.zh.md +15 -5
  4. package/assets/whale/pet.json +1 -1
  5. package/assets/whale-refined/pet.json +7 -0
  6. package/assets/whale-refined/spritesheet.webp +0 -0
  7. package/lib/client.js +257 -53
  8. package/lib/client.js.map +1 -1
  9. package/lib/index.js +235 -83
  10. package/lib/invariant.js +1 -1
  11. package/lib/{state-Ch3f4luZ.js → state-DrMX22GL.js} +35 -5
  12. package/lib/types/chatter.d.ts.map +1 -1
  13. package/lib/types/chatter.js +167 -77
  14. package/lib/types/client/PetDockEntry.d.ts.map +1 -1
  15. package/lib/types/client/PetDockEntry.js +1 -1
  16. package/lib/types/client/PetSprite.d.ts.map +1 -1
  17. package/lib/types/client/PetSprite.js +54 -11
  18. package/lib/types/client/PluginSettingsCard.d.ts +22 -0
  19. package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
  20. package/lib/types/client/PluginSettingsCard.js +142 -3
  21. package/lib/types/client/index.d.ts.map +1 -1
  22. package/lib/types/client/index.js +1 -0
  23. package/lib/types/client/locales.d.ts +4 -0
  24. package/lib/types/client/locales.d.ts.map +1 -1
  25. package/lib/types/client/locales.js +4 -0
  26. package/lib/types/client/spritesheet.d.ts +1 -3
  27. package/lib/types/client/spritesheet.d.ts.map +1 -1
  28. package/lib/types/client/spritesheet.js +1 -5
  29. package/lib/types/loopback.d.ts +25 -0
  30. package/lib/types/loopback.d.ts.map +1 -0
  31. package/lib/types/loopback.js +68 -0
  32. package/lib/types/registry.d.ts +0 -2
  33. package/lib/types/registry.d.ts.map +1 -1
  34. package/lib/types/registry.js +11 -7
  35. package/lib/types/remarks.d.ts.map +1 -1
  36. package/lib/types/remarks.js +30 -0
  37. package/lib/types/routes.d.ts.map +1 -1
  38. package/lib/types/routes.js +14 -0
  39. package/lib/types/service.d.ts.map +1 -1
  40. package/package.json +1 -2
  41. package/src/chatter.ts +167 -77
  42. package/src/client/PetDockEntry.test.tsx +93 -0
  43. package/src/client/PetDockEntry.tsx +1 -0
  44. package/src/client/PetSprite.test.tsx +72 -11
  45. package/src/client/PetSprite.tsx +98 -30
  46. package/src/client/PluginSettingsCard.tsx +219 -18
  47. package/src/client/index.test.tsx +95 -0
  48. package/src/client/index.ts +1 -0
  49. package/src/client/locales.ts +4 -0
  50. package/src/client/pet-css.test.ts +15 -3
  51. package/src/client/pet.module.css +101 -71
  52. package/src/client/settings-card.module.css +90 -1
  53. package/src/client/spritesheet.ts +1 -6
  54. package/src/loopback.ts +63 -0
  55. package/src/registry.test.ts +49 -1
  56. package/src/registry.ts +11 -7
  57. package/src/remarks.ts +30 -0
  58. package/src/routes.ts +11 -0
  59. package/src/service.ts +0 -1
package/lib/client.js CHANGED
@@ -118,7 +118,7 @@ window.__ModuleLoader__.load({
118
118
  * atlas coordinates here would drift each frame by the scale factor and
119
119
  * render torn/overlapping frames.
120
120
  */
121
- function framePosition(cell, columns, row, col, scale = 1) {
121
+ function framePosition(cell, row, col, scale = 1) {
122
122
  return {
123
123
  x: -col * cell.width * scale,
124
124
  y: -row * cell.height * scale
@@ -165,7 +165,7 @@ window.__ModuleLoader__.load({
165
165
  }
166
166
  //#endregion
167
167
  //#region \0dsh-css:packages/dsh-pet/src/client/pet.module.css.mjs
168
- 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;backdrop-filter:blur(8px);letter-spacing:.02em;background:linear-gradient(160deg,#1e293be0,#0f172aeb);border:1px solid #7dd3fc8c;max-width:min(280px,100vw - 24px);animation:.24s ease-out kz2Bea_pet-bubble-in;overflow:hidden;box-shadow:0 4px 14px #02061766,0 0 0 1px #7dd3fc1f,0 0 18px #38bdf82e}.kz2Bea_bubbleWhisper{letter-spacing:.03em;white-space:nowrap;text-overflow:ellipsis;color:#f5d0fe;backdrop-filter:blur(8px);pointer-events:none;background:linear-gradient(150deg,#581c8785,#9d174d70);border:1px dashed #f0abfc8c;border-radius:999px;max-width:min(260px,100vw - 24px);padding:4px 12px;font-size:11.5px;font-style:italic;line-height:1.45;animation:.32s cubic-bezier(.22,1,.36,1) kz2Bea_pet-whisper-in;position:relative;overflow:hidden;box-shadow:0 3px 12px #2e106566,0 0 14px #d946ef29}.kz2Bea_bubbleWhisper:before{content:\"「\";opacity:.75;margin-right:2px}.kz2Bea_bubbleWhisper:after{content:\"」\";opacity:.75;margin-left:2px}.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-in{0%{opacity:0;transform:translateY(6px)scale(.96)}to{opacity:1;transform:translateY(0)scale(1)}}@keyframes kz2Bea_pet-whisper-in{0%{opacity:0;transform:translateY(8px)scale(.9)}60%{transform:translateY(-1px)scale(1.02)}to{opacity:1;transform:translateY(0)scale(1)}}@keyframes kz2Bea_pet-panel-in{0%{opacity:0;transform:translateY(4px)scale(.97)}to{opacity:1;transform:translateY(0)scale(1)}}@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(10px);transform-origin:50% 0;background:linear-gradient(165deg,#1e293bf0,#0f172af5);border:1px solid #94a3b866;border-radius:12px;flex-direction:column;gap:7px;min-width:148px;margin-top:8px;padding:10px 12px;font-size:12px;animation:.2s cubic-bezier(.22,1,.36,1) kz2Bea_pet-panel-in;display:flex;position:absolute;top:100%;box-shadow:0 8px 24px #02061773,0 0 0 1px #7dd3fc14,inset 0 1px #e2e8f014}.kz2Bea_panel:after{content:\"\";height:14px;position:absolute;bottom:100%;left:0;right:0}.kz2Bea_panelAbove{transform-origin:50% 100%;margin-top:0;margin-bottom:8px;top:auto;bottom:100%}.kz2Bea_panelAbove:after{top:100%;bottom:auto}.kz2Bea_rankRow{white-space:nowrap;justify-content:space-between;gap:10px;display:flex}.kz2Bea_nameCell{letter-spacing:.02em;background:linear-gradient(90deg,#7dd3fc,#38bdf8 60%,#818cf8);color:#0000;-webkit-background-clip:text;background-clip:text;font-weight:600}.kz2Bea_statRank{color:#c4b5fd;font-weight:600}.kz2Bea_statTreats{color:#7dd3fc;font-weight:600}.kz2Bea_statPoints{color:#fcd34d;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,transform .12s;box-shadow:0 2px 6px #38bdf840}.kz2Bea_action:hover{filter:brightness(1.08);transform:translateY(-1px);box-shadow:0 4px 10px #38bdf859}.kz2Bea_action:active{filter:brightness(.94);transform:translateY(0);box-shadow:0 1px 4px #38bdf840}.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,.kz2Bea_bubbleStatus,.kz2Bea_bubbleWhisper,.kz2Bea_panel{opacity:1;animation:none}.kz2Bea_action,.kz2Bea_summon{transition:none}}";
168
+ 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:#f4f7ff;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:#4d6bfef2}.kz2Bea_bubbleFeed{background:#0369a1f2}.kz2Bea_bubbleStatus{text-overflow:ellipsis;backdrop-filter:blur(8px);letter-spacing:.02em;background:linear-gradient(160deg,#131c36e6,#070b1af0);border:1px solid #7e98ff73;max-width:min(280px,100vw - 24px);padding:5px 12px;animation:.24s ease-out kz2Bea_pet-bubble-in;overflow:hidden;box-shadow:0 4px 12px #02061759,inset 0 1px #e2e8ff1a,0 0 12px #4d6bfe2e}.kz2Bea_bubbleWhisper{letter-spacing:.03em;animation:.32s cubic-bezier(.22,1,.36,1) kz2Bea_pet-whisper-in}.kz2Bea_bubbleWhisper:before{content:\"「\";opacity:.7;margin-right:2px}.kz2Bea_bubbleWhisper:after{content:\"」\";opacity:.7;margin-left:2px}.kz2Bea_bubbleStack{pointer-events:auto;flex-direction:column-reverse;align-items:center;gap:4px;display:flex;position:absolute;bottom:100%}.kz2Bea_bubbleAnchor{display:inline-flex;position:relative}.kz2Bea_bubbleMore{z-index:1;color:#eef2ff;cursor:pointer;background:linear-gradient(160deg,#4a68f5f2,#2f44b8f2);border:1px solid #7e98ff8c;border-radius:999px;justify-content:center;align-items:center;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:600;line-height:1;transition:filter .12s;display:inline-flex;position:absolute;top:-9px;right:-10px;box-shadow:0 2px 6px #02061766}.kz2Bea_bubbleMore:hover{filter:brightness(1.15)}.kz2Bea_bubbleMore:focus-visible{outline:none;box-shadow:0 0 0 2px #7e98ffe6}.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 #7e98ffe6}@keyframes kz2Bea_pet-bubble-in{0%{opacity:0;transform:translateY(6px)scale(.96)}to{opacity:1;transform:translateY(0)scale(1)}}@keyframes kz2Bea_pet-whisper-in{0%{opacity:0;transform:translateY(8px)scale(.9)}60%{transform:translateY(-1px)scale(1.02)}to{opacity:1;transform:translateY(0)scale(1)}}@keyframes kz2Bea_pet-panel-in{0%{opacity:0;transform:translateY(4px)scale(.97)}to{opacity:1;transform:translateY(0)scale(1)}}@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:#e6ebf8;backdrop-filter:blur(10px);transform-origin:50% 0;background:linear-gradient(165deg,#131c36f2,#070b1af7);border:1px solid #7e98ff4d;border-radius:12px;flex-direction:column;gap:7px;min-width:148px;margin-top:8px;padding:10px 12px;font-size:12px;animation:.2s cubic-bezier(.22,1,.36,1) kz2Bea_pet-panel-in;display:flex;position:absolute;top:100%;box-shadow:0 8px 24px #02061773,0 0 0 1px #4d6bfe1a,inset 0 1px #e2e8ff14}.kz2Bea_panel:after{content:\"\";height:14px;position:absolute;bottom:100%;left:0;right:0}.kz2Bea_panelAbove{transform-origin:50% 100%;margin-top:0;margin-bottom:8px;top:auto;bottom:100%}.kz2Bea_panelAbove:after{top:100%;bottom:auto}.kz2Bea_rankRow{white-space:nowrap;justify-content:space-between;gap:10px;display:flex}.kz2Bea_nameCell{letter-spacing:.02em;background:linear-gradient(90deg,#a9c1ff,#6c8bff 60%,#4d6bfe);color:#0000;-webkit-background-clip:text;background-clip:text;font-weight:600}.kz2Bea_statRank{color:#9db4ff;font-weight:600}.kz2Bea_statTreats{color:#7dd3fc;font-weight:600}.kz2Bea_statPoints{color:#fcd34d;font-weight:600}.kz2Bea_renameRow{align-items:center;gap:6px;display:flex}.kz2Bea_nameInput{color:#e6ebf8;background:#131c36e6;border:1px solid #7e98ff80;border-radius:6px;outline:none;flex:1;min-width:0;padding:3px 6px;font-size:12px}.kz2Bea_nameInput:focus{border-color:#4d6bfe;box-shadow:0 0 0 2px #4d6bfe73}.kz2Bea_actions{gap:6px;display:flex}.kz2Bea_action{cursor:pointer;color:#fff;background:linear-gradient(#4a68f5,#3a55e0);border:none;border-radius:6px;flex:1;padding:4px 8px;font-size:12px;font-weight:600;transition:filter .12s,box-shadow .12s,transform .12s;box-shadow:0 2px 6px #4d6bfe4d}.kz2Bea_action:hover{filter:brightness(1.08);transform:translateY(-1px);box-shadow:0 4px 10px #4d6bfe66}.kz2Bea_action:active{filter:brightness(.94);transform:translateY(0);box-shadow:0 1px 4px #4d6bfe4d}.kz2Bea_action:focus-visible{outline:none;box-shadow:0 0 0 2px #7e98ffe6}.kz2Bea_summon{color:#8ea6ff;cursor:pointer;background:#070b1abf;border:1px dashed #7e98ff99;border-radius:999px;padding:2px 10px;font-size:11px;transition:border-color .12s,color .12s,background .12s,box-shadow .12s}.kz2Bea_summon:hover{color:#c3d3ff;background:#070b1ae6;border-color:#7e98fff2}.kz2Bea_summon:active{color:#8ea6ff;border-color:#7e98ffcc}.kz2Bea_summon:focus-visible{outline:none;box-shadow:0 0 0 2px #7e98ffe6}@media (prefers-reduced-motion:reduce){.kz2Bea_bubble,.kz2Bea_bubbleStatus,.kz2Bea_bubbleWhisper,.kz2Bea_panel{opacity:1;animation:none}.kz2Bea_action,.kz2Bea_summon,.kz2Bea_bubbleMore{transition:none}}";
169
169
  const tagId$2 = "@linxin666/dsh-pet/pet.module.css";
170
170
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
171
171
  const tag = document.createElement("style");
@@ -178,8 +178,10 @@ window.__ModuleLoader__.load({
178
178
  "action": "kz2Bea_action",
179
179
  "actions": "kz2Bea_actions",
180
180
  "bubble": "kz2Bea_bubble",
181
+ "bubbleAnchor": "kz2Bea_bubbleAnchor",
181
182
  "bubbleClickable": "kz2Bea_bubbleClickable",
182
183
  "bubbleFeed": "kz2Bea_bubbleFeed",
184
+ "bubbleMore": "kz2Bea_bubbleMore",
183
185
  "bubblePet": "kz2Bea_bubblePet",
184
186
  "bubbleStack": "kz2Bea_bubbleStack",
185
187
  "bubbleStatus": "kz2Bea_bubbleStatus",
@@ -231,6 +233,8 @@ window.__ModuleLoader__.load({
231
233
  const bubbleRef = (0, react.useRef)(null);
232
234
  const [imageReady, setImageReady] = (0, react.useState)(false);
233
235
  const [hovered, setHovered] = (0, react.useState)(false);
236
+ const [stackPeek, setStackPeek] = (0, react.useState)(false);
237
+ const [stackPinned, setStackPinned] = (0, react.useState)(false);
234
238
  const [renaming, setRenaming] = (0, react.useState)(false);
235
239
  const [panelAbove, setPanelAbove] = (0, react.useState)(false);
236
240
  const [panelLift, setPanelLift] = (0, react.useState)(0);
@@ -273,7 +277,7 @@ window.__ModuleLoader__.load({
273
277
  const row = rowOfTrack(leadAnimation);
274
278
  const track = trimTrack(tracks[leadAnimation], rows[row] ?? tracks[leadAnimation].frames.length);
275
279
  const leadCol = track.frames[0];
276
- const lead = framePosition(cell, columns, row, leadCol, scaleRef.current);
280
+ const lead = framePosition(cell, row, leadCol, scaleRef.current);
277
281
  if (spriteRef.current !== null) spriteRef.current.style.backgroundPosition = lead.x + "px " + lead.y + "px";
278
282
  if (reduceMotion) return;
279
283
  let raf = 0;
@@ -287,7 +291,7 @@ window.__ModuleLoader__.load({
287
291
  const current = sequenceFrameAt(sequence, tracks, sequenceElapsed);
288
292
  const currentRow = rowOfTrack(current.animation);
289
293
  const col = trimTrack(tracks[current.animation], rows[currentRow] ?? tracks[current.animation].frames.length).frames[current.frameIndex];
290
- const pos = framePosition(cell, columns, currentRow, col, scaleRef.current);
294
+ const pos = framePosition(cell, currentRow, col, scaleRef.current);
291
295
  if (spriteRef.current !== null) spriteRef.current.style.backgroundPosition = pos.x + "px " + pos.y + "px";
292
296
  raf = requestAnimationFrame(tick);
293
297
  return;
@@ -309,7 +313,7 @@ window.__ModuleLoader__.load({
309
313
  st.index = 0;
310
314
  } else st.index = maxIndex;
311
315
  const col = track.frames[st.index];
312
- const pos = framePosition(cell, columns, row, col, scaleRef.current);
316
+ const pos = framePosition(cell, row, col, scaleRef.current);
313
317
  if (spriteRef.current !== null) spriteRef.current.style.backgroundPosition = pos.x + "px " + pos.y + "px";
314
318
  raf = requestAnimationFrame(tick);
315
319
  };
@@ -338,6 +342,7 @@ window.__ModuleLoader__.load({
338
342
  hideTimerRef.current = null;
339
343
  }
340
344
  };
345
+ (0, react.useEffect)(() => () => clearHideTimer(), []);
341
346
  const onPointerDown = (e) => {
342
347
  e.preventDefault();
343
348
  e.target.setPointerCapture?.(e.pointerId);
@@ -378,10 +383,15 @@ window.__ModuleLoader__.load({
378
383
  const spriteWidth = Math.round(cell.width * spriteScale);
379
384
  const spriteHeight = Math.round(cell.height * spriteScale);
380
385
  const sessionBubbles = snapshot?.sessions ?? [];
386
+ const stackOpen = stackPeek || stackPinned;
387
+ const visibleSessions = !stackOpen && sessionBubbles.length > 1 ? sessionBubbles.slice(0, 1) : sessionBubbles;
381
388
  const statusBubble = feedback === null && sessionBubbles.length === 0 ? snapshot?.bubble : void 0;
382
389
  const whisper = feedback === null ? snapshot?.whisper : void 0;
383
390
  const bubblePresent = feedback !== null || sessionBubbles.length > 0 || statusBubble !== void 0 || whisper !== void 0;
384
391
  const displayName = snapshot?.name ?? definition.displayName;
392
+ (0, react.useEffect)(() => {
393
+ if (sessionBubbles.length <= 1) setStackPinned(false);
394
+ }, [sessionBubbles.length]);
385
395
  (0, react.useLayoutEffect)(() => {
386
396
  if (!hovered) {
387
397
  setPanelAbove(false);
@@ -408,6 +418,7 @@ window.__ModuleLoader__.load({
408
418
  display.size,
409
419
  bubblePresent,
410
420
  sessionBubbles.length,
421
+ stackOpen,
411
422
  feedback
412
423
  ]);
413
424
  return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -460,27 +471,41 @@ window.__ModuleLoader__.load({
460
471
  feedback === null && (sessionBubbles.length > 0 || statusBubble !== void 0 || whisper !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
461
472
  ref: bubbleRef,
462
473
  className: pet_module_css_default.bubbleStack,
463
- children: [
464
- sessionBubbles.map((session) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
474
+ onPointerEnter: () => setStackPeek(true),
475
+ onPointerLeave: () => setStackPeek(false),
476
+ children: [visibleSessions.map((session, index) => {
477
+ const speaksWhisper = index === 0 && whisper !== void 0;
478
+ const bubble = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
465
479
  type: "button",
466
- className: clsx(pet_module_css_default.bubble, pet_module_css_default.bubbleStatus, pet_module_css_default.bubbleClickable),
480
+ className: clsx(pet_module_css_default.bubble, pet_module_css_default.bubbleStatus, pet_module_css_default.bubbleClickable, speaksWhisper && pet_module_css_default.bubbleWhisper),
467
481
  title: props.t("pet.openSessionHint"),
468
482
  onClick: () => {
469
483
  props.onOpenSession(session.sessionId);
470
484
  },
471
- children: session.bubble
472
- }, session.sessionId)),
473
- sessionBubbles.length === 0 && statusBubble !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
474
- className: clsx(pet_module_css_default.bubble, pet_module_css_default.bubbleStatus),
475
- role: "status",
476
- "aria-live": "polite",
477
- children: statusBubble
478
- }),
479
- whisper !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
480
- className: pet_module_css_default.bubbleWhisper,
481
- children: whisper
482
- }, whisper)
483
- ]
485
+ children: speaksWhisper ? whisper : session.bubble
486
+ }, speaksWhisper ? "whisper:" + whisper : session.sessionId);
487
+ if (index !== 0 || sessionBubbles.length <= 1) return bubble;
488
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
489
+ className: pet_module_css_default.bubbleAnchor,
490
+ children: [bubble, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
491
+ type: "button",
492
+ className: pet_module_css_default.bubbleMore,
493
+ title: stackOpen ? props.t("pet.collapseSessions") : props.t("pet.moreSessions", { n: sessionBubbles.length - 1 }),
494
+ "aria-label": stackOpen ? props.t("pet.collapseSessions") : props.t("pet.moreSessions", { n: sessionBubbles.length - 1 }),
495
+ "aria-expanded": stackOpen,
496
+ onClick: (e) => {
497
+ e.stopPropagation();
498
+ setStackPinned((open) => !open);
499
+ },
500
+ children: stackOpen ? "×" : "+" + String(sessionBubbles.length - 1)
501
+ })]
502
+ }, "primary");
503
+ }), sessionBubbles.length === 0 && (statusBubble !== void 0 || whisper !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
504
+ className: clsx(pet_module_css_default.bubble, pet_module_css_default.bubbleStatus, whisper !== void 0 && pet_module_css_default.bubbleWhisper),
505
+ role: "status",
506
+ "aria-live": "polite",
507
+ children: whisper ?? statusBubble
508
+ }, whisper === void 0 ? "status" : "whisper:" + whisper)]
484
509
  }),
485
510
  hovered && dragRef.current === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
486
511
  ref: panelRef,
@@ -647,12 +672,13 @@ window.__ModuleLoader__.load({
647
672
  },
648
673
  onClick: props.summon,
649
674
  "data-testid": "pet-summon",
675
+ "data-dsh-part": "summon-button",
650
676
  children: props.t("pet.summon", { name: snapshot?.name ?? "" })
651
677
  });
652
678
  }
653
679
  //#endregion
654
680
  //#region \0dsh-css:packages/dsh-pet/src/client/settings-card.module.css.mjs
655
- const css$1 = ".kKk9aW_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.kKk9aW_card:hover{border-color:var(--dsw-alias-label-dimmed)}.kKk9aW_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.kKk9aW_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.kKk9aW_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.kKk9aW_headerStatic{border-radius:12px;align-items:center;gap:12px;width:100%;padding:14px 16px;display:flex}.kKk9aW_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.kKk9aW_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.kKk9aW_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.kKk9aW_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.kKk9aW_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.kKk9aW_chevronOpen{transform:rotate(180deg)}.kKk9aW_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.kKk9aW_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.kKk9aW_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.kKk9aW_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.kKk9aW_failed{min-width:0;color:var(--dsw-alias-label-error);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:12px;line-height:1.5;overflow:hidden}.kKk9aW_discard,.kKk9aW_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.kKk9aW_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.kKk9aW_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.kKk9aW_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.kKk9aW_discard:disabled,.kKk9aW_save:disabled{opacity:.4;cursor:default}.kKk9aW_discard:focus-visible,.kKk9aW_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.kKk9aW_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.kKk9aW_field+.kKk9aW_field{border-top:1px solid var(--dsw-alias-border-l2)}.kKk9aW_head{align-items:center;gap:8px;display:flex}.kKk9aW_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.kKk9aW_badges{align-items:center;gap:8px;display:inline-flex}.kKk9aW_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.kKk9aW_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.kKk9aW_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.kKk9aW_reset:disabled{cursor:default}.kKk9aW_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.kKk9aW_input,.kKk9aW_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.kKk9aW_input:focus-visible,.kKk9aW_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.kKk9aW_input:disabled,.kKk9aW_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.kKk9aW_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.kKk9aW_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-label-error);outline-offset:1px;border-color:var(--dsw-alias-label-error)}.kKk9aW_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.kKk9aW_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.kKk9aW_card,.kKk9aW_header,.kKk9aW_chevron,.kKk9aW_chevronOpen,.kKk9aW_discard,.kKk9aW_save{transition:none}}";
681
+ const css$1 = ".kKk9aW_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.kKk9aW_card:hover{border-color:var(--dsw-alias-label-dimmed)}.kKk9aW_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.kKk9aW_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.kKk9aW_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.kKk9aW_headerStatic{border-radius:12px;align-items:center;gap:12px;width:100%;padding:14px 16px;display:flex}.kKk9aW_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.kKk9aW_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.kKk9aW_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.kKk9aW_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.kKk9aW_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.kKk9aW_chevronOpen{transform:rotate(180deg)}.kKk9aW_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.kKk9aW_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.kKk9aW_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.kKk9aW_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.kKk9aW_failed{min-width:0;color:var(--dsw-alias-label-error);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:12px;line-height:1.5;overflow:hidden}.kKk9aW_discard,.kKk9aW_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.kKk9aW_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.kKk9aW_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.kKk9aW_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.kKk9aW_discard:disabled,.kKk9aW_save:disabled{opacity:.4;cursor:default}.kKk9aW_discard:focus-visible,.kKk9aW_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.kKk9aW_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.kKk9aW_field+.kKk9aW_field{border-top:1px solid var(--dsw-alias-border-l2)}.kKk9aW_head{align-items:center;gap:8px;display:flex}.kKk9aW_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.kKk9aW_badges{align-items:center;gap:8px;display:inline-flex}.kKk9aW_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.kKk9aW_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.kKk9aW_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.kKk9aW_reset:disabled{cursor:default}.kKk9aW_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.kKk9aW_input,.kKk9aW_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.kKk9aW_input:focus-visible,.kKk9aW_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.kKk9aW_input:disabled,.kKk9aW_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.kKk9aW_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.kKk9aW_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-label-error);outline-offset:1px;border-color:var(--dsw-alias-label-error)}.kKk9aW_selectWrap{position:relative}.kKk9aW_selectButton{appearance:none;text-align:left;cursor:pointer;justify-content:space-between;align-items:center;gap:8px;width:100%;display:flex}.kKk9aW_selectLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.kKk9aW_selectChevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.kKk9aW_selectChevronOpen{transform:rotate(180deg)}.kKk9aW_selectPopup{z-index:40;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);max-height:240px;box-shadow:0 8px 24px var(--dsw-alias-bg-mask-2);opacity:0;border-radius:8px;flex-direction:column;padding:4px;transition:opacity .1s,transform .1s;display:flex;position:absolute;top:calc(100% + 4px);left:0;right:0;overflow-y:auto;transform:translateY(-4px)}.kKk9aW_selectPopupOpen{opacity:1;transform:none}.kKk9aW_selectPopupClose{opacity:0;pointer-events:none;transform:translateY(-4px)}.kKk9aW_selectOption{color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;text-overflow:ellipsis;border-radius:6px;padding:6px 10px;font-size:13px;line-height:1.5;overflow:hidden}.kKk9aW_selectOption:hover,.kKk9aW_selectOptionActive{background:var(--dsw-alias-interactive-bg-hover)}.kKk9aW_selectOptionSelected{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 10%, transparent);font-weight:500}.kKk9aW_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.kKk9aW_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.kKk9aW_card,.kKk9aW_header,.kKk9aW_chevron,.kKk9aW_chevronOpen,.kKk9aW_discard,.kKk9aW_save,.kKk9aW_selectChevron,.kKk9aW_selectChevronOpen,.kKk9aW_selectPopup{transition:none}}";
656
682
  const tagId$1 = "@linxin666/dsh-pet/settings-card.module.css";
657
683
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
658
684
  const tag = document.createElement("style");
@@ -689,7 +715,18 @@ window.__ModuleLoader__.load({
689
715
  "readOnly": "kKk9aW_readOnly",
690
716
  "reset": "kKk9aW_reset",
691
717
  "save": "kKk9aW_save",
692
- "select": "kKk9aW_select"
718
+ "select": "kKk9aW_select",
719
+ "selectButton": "kKk9aW_selectButton",
720
+ "selectChevron": "kKk9aW_selectChevron",
721
+ "selectChevronOpen": "kKk9aW_selectChevronOpen",
722
+ "selectLabel": "kKk9aW_selectLabel",
723
+ "selectOption": "kKk9aW_selectOption",
724
+ "selectOptionActive": "kKk9aW_selectOptionActive",
725
+ "selectOptionSelected": "kKk9aW_selectOptionSelected",
726
+ "selectPopup": "kKk9aW_selectPopup",
727
+ "selectPopupClose": "kKk9aW_selectPopupClose",
728
+ "selectPopupOpen": "kKk9aW_selectPopupOpen",
729
+ "selectWrap": "kKk9aW_selectWrap"
693
730
  };
694
731
  //#endregion
695
732
  //#region src/client/PluginSettingsCard.tsx
@@ -868,6 +905,175 @@ window.__ModuleLoader__.load({
868
905
  ]
869
906
  });
870
907
  }
908
+ const NON_SKIN_BODY_MARKERS = /* @__PURE__ */ new Set(["dshSkinCenter", "dshSidebarCollapsed"]);
909
+ function isSkinActive() {
910
+ return Object.keys(document.body.dataset).some((key) => key.startsWith("dsh") && !NON_SKIN_BODY_MARKERS.has(key));
911
+ }
912
+ const SELECT_CLOSE_MS = 100;
913
+ /**
914
+ * The shared dual-mode select control. While an appearance skin is active it
915
+ * renders the legacy native `<select>` untouched, so element-level skin
916
+ * selectors keep working; under the default appearance it renders a
917
+ * self-drawn `role="listbox"` popup whose open/close is transition-animated.
918
+ * Staged cards reach it through BooleanField/ChoiceField; immediate-apply
919
+ * editors (the side-card prefs) bind it directly through onEdit.
920
+ * 双模式下拉框:皮肤激活时用原生 select,默认外观用自绘动画弹层。
921
+ */
922
+ function SelectField(props) {
923
+ const { id, options, value } = props;
924
+ const [open, setOpen] = (0, react.useState)(false);
925
+ const [closing, setClosing] = (0, react.useState)(false);
926
+ const [phase, setPhase] = (0, react.useState)("initial");
927
+ const [activeIndex, setActiveIndex] = (0, react.useState)(0);
928
+ const closeTimer = (0, react.useRef)(void 0);
929
+ const wrapRef = (0, react.useRef)(null);
930
+ const popupRef = (0, react.useRef)(null);
931
+ const currentIndex = () => {
932
+ const index = options.findIndex((option) => option.value === value);
933
+ return index >= 0 ? index : 0;
934
+ };
935
+ const close = (0, react.useCallback)(() => {
936
+ if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
937
+ setClosing(true);
938
+ closeTimer.current = setTimeout(() => {
939
+ setClosing(false);
940
+ setOpen(false);
941
+ }, SELECT_CLOSE_MS);
942
+ }, []);
943
+ const openPopup = () => {
944
+ if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
945
+ setActiveIndex(currentIndex());
946
+ setPhase("initial");
947
+ setClosing(false);
948
+ setOpen(true);
949
+ };
950
+ const commit = (index) => {
951
+ const option = options[index];
952
+ if (option) props.onEdit(option.value);
953
+ close();
954
+ };
955
+ const onTriggerClick = () => {
956
+ if (props.disabled) return;
957
+ if (open && !closing) close();
958
+ else openPopup();
959
+ };
960
+ const onKeyDown = (event) => {
961
+ if (props.disabled) return;
962
+ const count = options.length;
963
+ switch (event.key) {
964
+ case "ArrowDown":
965
+ case "ArrowUp":
966
+ case "Enter":
967
+ case " ":
968
+ event.preventDefault();
969
+ if (!open) openPopup();
970
+ else if (!closing) if (event.key === "ArrowDown") setActiveIndex((index) => (index + 1) % count);
971
+ else if (event.key === "ArrowUp") setActiveIndex((index) => (index - 1 + count) % count);
972
+ else commit(activeIndex);
973
+ break;
974
+ case "Escape":
975
+ if (open) {
976
+ event.preventDefault();
977
+ event.stopPropagation();
978
+ close();
979
+ }
980
+ break;
981
+ case "Tab":
982
+ if (open) close();
983
+ break;
984
+ }
985
+ };
986
+ (0, react.useEffect)(() => () => {
987
+ if (closeTimer.current !== void 0) clearTimeout(closeTimer.current);
988
+ }, []);
989
+ (0, react.useLayoutEffect)(() => {
990
+ if (open && !closing && phase === "initial") {
991
+ popupRef.current?.offsetHeight;
992
+ setPhase("open");
993
+ }
994
+ }, [
995
+ open,
996
+ closing,
997
+ phase
998
+ ]);
999
+ (0, react.useEffect)(() => {
1000
+ if (!open) return;
1001
+ const onPointerDown = (event) => {
1002
+ const target = event.target;
1003
+ if (target instanceof Node && !wrapRef.current?.contains(target)) close();
1004
+ };
1005
+ document.addEventListener("pointerdown", onPointerDown);
1006
+ return () => document.removeEventListener("pointerdown", onPointerDown);
1007
+ }, [open, close]);
1008
+ (0, react.useEffect)(() => {
1009
+ if (props.disabled && open) close();
1010
+ }, [
1011
+ props.disabled,
1012
+ open,
1013
+ close
1014
+ ]);
1015
+ if (isSkinActive()) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
1016
+ id,
1017
+ className: settings_card_module_css_default.select,
1018
+ value,
1019
+ disabled: props.disabled,
1020
+ onChange: (event) => {
1021
+ props.onEdit(event.target.value);
1022
+ },
1023
+ children: options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1024
+ value: option.value,
1025
+ children: option.label
1026
+ }, option.value))
1027
+ });
1028
+ const label = options.find((option) => option.value === value)?.label ?? "";
1029
+ const popupClass = closing ? `${settings_card_module_css_default.selectPopup} ${settings_card_module_css_default.selectPopupClose}` : phase === "open" ? `${settings_card_module_css_default.selectPopup} ${settings_card_module_css_default.selectPopupOpen}` : settings_card_module_css_default.selectPopup;
1030
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1031
+ className: settings_card_module_css_default.selectWrap,
1032
+ ref: wrapRef,
1033
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1034
+ type: "button",
1035
+ id,
1036
+ className: `${settings_card_module_css_default.select} ${settings_card_module_css_default.selectButton}`,
1037
+ disabled: props.disabled,
1038
+ "aria-haspopup": "listbox",
1039
+ "aria-expanded": open,
1040
+ "aria-activedescendant": open ? `${id}-o${activeIndex}` : void 0,
1041
+ "aria-invalid": props.invalid || void 0,
1042
+ onClick: onTriggerClick,
1043
+ onKeyDown,
1044
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1045
+ className: settings_card_module_css_default.selectLabel,
1046
+ children: label
1047
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1048
+ width: "14",
1049
+ height: "14",
1050
+ viewBox: "0 0 14 14",
1051
+ fill: "none",
1052
+ xmlns: "http://www.w3.org/2000/svg",
1053
+ className: open ? `${settings_card_module_css_default.selectChevron} ${settings_card_module_css_default.selectChevronOpen}` : settings_card_module_css_default.selectChevron,
1054
+ "aria-hidden": "true",
1055
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1056
+ d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z",
1057
+ fill: "currentColor"
1058
+ })
1059
+ })]
1060
+ }), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1061
+ className: popupClass,
1062
+ role: "listbox",
1063
+ ref: popupRef,
1064
+ children: options.map((option, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1065
+ id: `${id}-o${index}`,
1066
+ role: "option",
1067
+ "aria-selected": option.value === value,
1068
+ className: `${settings_card_module_css_default.selectOption}${option.value === value ? ` ${settings_card_module_css_default.selectOptionSelected}` : ""}${index === activeIndex && !closing ? ` ${settings_card_module_css_default.selectOptionActive}` : ""}`,
1069
+ onClick: () => {
1070
+ commit(index);
1071
+ },
1072
+ children: option.label
1073
+ }, option.value))
1074
+ }) : null]
1075
+ });
1076
+ }
871
1077
  /** A staged boolean field: 继承 / 开 / 关. */
872
1078
  function BooleanField(props) {
873
1079
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -893,28 +1099,26 @@ window.__ModuleLoader__.load({
893
1099
  })]
894
1100
  }) : null]
895
1101
  }),
896
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1102
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectField, {
897
1103
  id: props.id,
898
- className: settings_card_module_css_default.select,
899
- value: props.text,
900
- disabled: props.disabled,
901
- onChange: (event) => {
902
- props.onEdit(event.target.value);
903
- },
904
- children: [
905
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1104
+ options: [
1105
+ {
906
1106
  value: "",
907
- children: props.inheritLabel
908
- }),
909
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1107
+ label: props.inheritLabel
1108
+ },
1109
+ {
910
1110
  value: "true",
911
- children: props.onLabel
912
- }),
913
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1111
+ label: props.onLabel
1112
+ },
1113
+ {
914
1114
  value: "false",
915
- children: props.offLabel
916
- })
917
- ]
1115
+ label: props.offLabel
1116
+ }
1117
+ ],
1118
+ value: props.text,
1119
+ disabled: props.disabled,
1120
+ invalid: props.invalid,
1121
+ onEdit: props.onEdit
918
1122
  }),
919
1123
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
920
1124
  className: settings_card_module_css_default.hint,
@@ -948,21 +1152,16 @@ window.__ModuleLoader__.load({
948
1152
  })]
949
1153
  }) : null]
950
1154
  }),
951
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1155
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectField, {
952
1156
  id: props.id,
953
- className: settings_card_module_css_default.select,
1157
+ options: [{
1158
+ value: "",
1159
+ label: props.inheritLabel
1160
+ }, ...props.choices],
954
1161
  value: props.text,
955
1162
  disabled: props.disabled,
956
- onChange: (event) => {
957
- props.onEdit(event.target.value);
958
- },
959
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
960
- value: "",
961
- children: props.inheritLabel
962
- }), props.choices.map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
963
- value: choice.value,
964
- children: choice.label
965
- }, choice.value))]
1163
+ invalid: props.invalid,
1164
+ onEdit: props.onEdit
966
1165
  }),
967
1166
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
968
1167
  className: props.invalid ? settings_card_module_css_default.invalid : settings_card_module_css_default.hint,
@@ -1497,6 +1696,8 @@ window.__ModuleLoader__.load({
1497
1696
  "pet.state.loading": "宠物正在赶来…",
1498
1697
  "pet.state.error": "宠物迷路了(连接失败)",
1499
1698
  "pet.openSessionHint": "点击跳转到对应会话",
1699
+ "pet.moreSessions": "展开其余 {n} 个会话的气泡",
1700
+ "pet.collapseSessions": "收起会话气泡",
1500
1701
  "settings.title": "宠物",
1501
1702
  "settings.description": "选择宠物并调整它的显示布局。",
1502
1703
  "settings.pet": "宠物",
@@ -1541,6 +1742,8 @@ window.__ModuleLoader__.load({
1541
1742
  "pet.state.loading": "The pet is on its way…",
1542
1743
  "pet.state.error": "The pet is lost (connection failed)",
1543
1744
  "pet.openSessionHint": "Click to jump to this session",
1745
+ "pet.moreSessions": "Expand {n} more session bubbles",
1746
+ "pet.collapseSessions": "Collapse session bubbles",
1544
1747
  "settings.title": "Pet",
1545
1748
  "settings.description": "Pick a pet and tune its display layout.",
1546
1749
  "settings.pet": "Pet",
@@ -1764,6 +1967,7 @@ window.__ModuleLoader__.load({
1764
1967
  });
1765
1968
  const container = document.createElement("div");
1766
1969
  container.dataset.dshPetRoot = "";
1970
+ container.dataset.dshPlugin = "pet";
1767
1971
  document.body.appendChild(container);
1768
1972
  const petRoot = (0, react_dom_client.createRoot)(container);
1769
1973
  petRoot.render((0, react.createElement)(PetDockEntry, {