@linxin666/dsh-pet 0.2.4 → 0.2.6
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 +1 -1
- package/README.zh.md +1 -1
- package/lib/client.js +110 -28
- package/lib/client.js.map +1 -1
- package/lib/index.js +164 -34
- package/lib/types/access.d.ts +16 -0
- package/lib/types/access.d.ts.map +1 -0
- package/lib/types/access.js +20 -0
- package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PetSettingsCard.js +8 -2
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +10 -2
- package/lib/types/client/renderers/live2d/Live2dVisualMount.d.ts.map +1 -1
- package/lib/types/client/renderers/live2d/Live2dVisualMount.js +1 -0
- package/lib/types/client/renderers/live2d/runtime.d.ts +13 -1
- package/lib/types/client/renderers/live2d/runtime.d.ts.map +1 -1
- package/lib/types/client/renderers/live2d.d.ts.map +1 -1
- package/lib/types/client/renderers/live2d.js +120 -24
- package/lib/types/image-dimensions.d.ts +26 -0
- package/lib/types/image-dimensions.d.ts.map +1 -0
- package/lib/types/image-dimensions.js +77 -0
- package/lib/types/index.js +1 -1
- package/lib/types/registry.d.ts.map +1 -1
- package/lib/types/registry.js +43 -1
- package/lib/types/routes.d.ts +6 -1
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +36 -33
- package/package.json +1 -1
- package/src/access.ts +40 -0
- package/src/client/PetSettingsCard.tsx +8 -2
- package/src/client/PetSprite.test.tsx +71 -0
- package/src/client/PetSprite.tsx +10 -2
- package/src/client/renderers/live2d/Live2dVisualMount.test.tsx +72 -0
- package/src/client/renderers/live2d/Live2dVisualMount.tsx +1 -0
- package/src/client/renderers/live2d/runtime.ts +8 -2
- package/src/client/renderers/live2d.test.ts +211 -8
- package/src/client/renderers/live2d.ts +129 -29
- package/src/client/settings-card.module.css +2 -0
- package/src/image-dimensions.test.ts +85 -0
- package/src/image-dimensions.ts +76 -0
- package/src/index.ts +1 -1
- package/src/registry.test.ts +58 -5
- package/src/registry.ts +40 -1
- package/src/routes.ts +38 -34
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: 5b33a914b3a373385771a4526c5129ca91dd71db
|
|
6
|
+
README.zh.md: 4ce92ac1d90ca1a667edc058996ddc9dd7504584
|
package/README.md
CHANGED
|
@@ -288,7 +288,7 @@ The two built-in whale-girl atlases use the same 9-state × 8-column contract: `
|
|
|
288
288
|
|
|
289
289
|
## Security model
|
|
290
290
|
|
|
291
|
-
-
|
|
291
|
+
- Every `/api/pet/*` and `/pet/<id>/*` route is loopback-only by default (the shared plugin-family fence: loopback socket + Host header + browser same-origin markers): unpaired LAN clients get `403 forbidden: loopback-only` before any pet state or atlas is served. When `dsh-remote-web-ui` is also loaded, a live paired-device cookie is an additional allow path (the same cookie `api/gate` already checks); unpaired and revoked devices stay 403. The pet does not depend on the remote plugin.
|
|
292
292
|
- Asset serving resolves both the pet directory and the candidate file through `realpath`; symlink escapes are refused (403). Files are size-capped before being read into memory (manifest 64 KB, imagery 20 MB; over-cap answers 413).
|
|
293
293
|
- Live2D models are served by closure: only the manifest, the declared primary assets, and the files the `.model3.json` references (each screened against traversal, absolute and URL forms).
|
|
294
294
|
- The plugin never downloads executables and never bundles the Live2D Cubism Core.
|
package/README.zh.md
CHANGED
|
@@ -288,7 +288,7 @@ pnpm typecheck # 仅类型检查
|
|
|
288
288
|
|
|
289
289
|
## 安全模型
|
|
290
290
|
|
|
291
|
-
-
|
|
291
|
+
- 全部 `/api/pet/*` 与 `/pet/<id>/*` 路由默认仅限 loopback(插件家族共享围栏:loopback 套接字 + Host 头 + 浏览器同源标记):未配对的局域网客户端在任何宠物状态或图集下发前即收到 `403 forbidden: loopback-only`。同时装了 `dsh-remote-web-ui` 时,有效的已配对设备 cookie 是额外放行路径(与 `api/gate` 检查同一枚 cookie);未配对与已撤销设备仍 403。宠物插件不硬依赖远程插件。
|
|
292
292
|
- 资产服务对宠物目录与目标文件双双做 `realpath` 解析;symlink 越界一律拒绝(403)。文件读入内存前按类限大小(清单 64 KB、图像 20 MB;超限 413)。
|
|
293
293
|
- Live2D 模型按闭包放行:仅清单、声明的主资产与 `.model3.json` 引用到的文件(引用先经穿越/绝对路径/URL 形态筛查)。
|
|
294
294
|
- 插件从不下载可执行文件,也从不内置 Live2D Cubism Core。
|
package/lib/client.js
CHANGED
|
@@ -244,7 +244,7 @@ window.__ModuleLoader__.load({
|
|
|
244
244
|
const position = (index) => -index * frameWidth + "px 0px";
|
|
245
245
|
const reduceMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches === true;
|
|
246
246
|
el.style.backgroundPosition = position(segment.from);
|
|
247
|
-
if (reduceMotion) return;
|
|
247
|
+
if (reduceMotion || segment.from === segment.to) return;
|
|
248
248
|
let raf = 0;
|
|
249
249
|
let index = segment.from;
|
|
250
250
|
let elapsed = 0;
|
|
@@ -258,8 +258,8 @@ window.__ModuleLoader__.load({
|
|
|
258
258
|
elapsed = 0;
|
|
259
259
|
if (index < segment.to) index += 1;
|
|
260
260
|
else if (decoration.loop) index = segment.from;
|
|
261
|
+
el.style.backgroundPosition = position(index);
|
|
261
262
|
}
|
|
262
|
-
el.style.backgroundPosition = position(index);
|
|
263
263
|
if (!decoration.loop && index === segment.to) return;
|
|
264
264
|
raf = requestAnimationFrame(tick);
|
|
265
265
|
};
|
|
@@ -789,6 +789,7 @@ window.__ModuleLoader__.load({
|
|
|
789
789
|
const downRef = (0, react.useRef)(null);
|
|
790
790
|
const [error, setError] = (0, react.useState)(null);
|
|
791
791
|
(0, react.useEffect)(() => {
|
|
792
|
+
setError(null);
|
|
792
793
|
const container = containerRef.current;
|
|
793
794
|
const live2d = props.definition.live2d;
|
|
794
795
|
if (container === null || live2d === void 0) return void 0;
|
|
@@ -1019,6 +1020,25 @@ window.__ModuleLoader__.load({
|
|
|
1019
1020
|
* downsampled atlas only when the effective on-screen scale warrants it.
|
|
1020
1021
|
*/
|
|
1021
1022
|
const TEXTURE_OPTIONS = { lod: "single-auto" };
|
|
1023
|
+
/** Recursively release the activation without invalidating shared texture caches. */
|
|
1024
|
+
const DESTROY_OPTIONS = { children: true };
|
|
1025
|
+
/** Remove only this activation's canvas; `true` would release Pixi globals. */
|
|
1026
|
+
const RENDERER_DESTROY_OPTIONS = { removeView: true };
|
|
1027
|
+
/** Ignore hidden/zero boxes and keep Pixi dimensions stable and integral. */
|
|
1028
|
+
function normalizeRendererSize(width, height) {
|
|
1029
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return void 0;
|
|
1030
|
+
return {
|
|
1031
|
+
width: Math.max(1, Math.round(width)),
|
|
1032
|
+
height: Math.max(1, Math.round(height))
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
/** Fit the model from its unscaled dimensions into the current Pixi screen. */
|
|
1036
|
+
function layoutModel(app, model, sourceSize, config) {
|
|
1037
|
+
const fit = Math.min(app.renderer.width / sourceSize.width, app.renderer.height / sourceSize.height) * .92;
|
|
1038
|
+
model.scale.set(fit * (config.scale ?? 1));
|
|
1039
|
+
model.anchor.set(.5);
|
|
1040
|
+
model.position.set(app.renderer.width / 2 + (config.translate?.x ?? 0), app.renderer.height / 2 + (config.translate?.y ?? 0));
|
|
1041
|
+
}
|
|
1022
1042
|
let vendorConfigured = false;
|
|
1023
1043
|
/** Configure pixi extensions + the Cubism SDK once per page. */
|
|
1024
1044
|
function configureOnce(vendor) {
|
|
@@ -1045,11 +1065,56 @@ window.__ModuleLoader__.load({
|
|
|
1045
1065
|
let disposed = false;
|
|
1046
1066
|
let app;
|
|
1047
1067
|
let model;
|
|
1068
|
+
let modelAttached = false;
|
|
1069
|
+
let modelSourceSize;
|
|
1070
|
+
let resizeObserver;
|
|
1071
|
+
let resizeTracking = false;
|
|
1048
1072
|
let errorListener;
|
|
1049
1073
|
let unsubscribe;
|
|
1050
1074
|
/** The motion group the current phase maps to (resume target after taps). */
|
|
1051
1075
|
let phaseGroup = config.motions.idle;
|
|
1052
1076
|
let tapPlaying = false;
|
|
1077
|
+
const stopResizeTracking = () => {
|
|
1078
|
+
resizeTracking = false;
|
|
1079
|
+
resizeObserver?.disconnect();
|
|
1080
|
+
resizeObserver = void 0;
|
|
1081
|
+
};
|
|
1082
|
+
const resizeRenderer = (pixiApp, width, height) => {
|
|
1083
|
+
const next = normalizeRendererSize(width, height);
|
|
1084
|
+
if (disposed || !resizeTracking || next === void 0) return;
|
|
1085
|
+
if (pixiApp.renderer.width === next.width && pixiApp.renderer.height === next.height) return;
|
|
1086
|
+
pixiApp.renderer.resize(next.width, next.height);
|
|
1087
|
+
if (model !== void 0 && modelSourceSize !== void 0) layoutModel(pixiApp, model, modelSourceSize, config);
|
|
1088
|
+
};
|
|
1089
|
+
const trackContainerSize = (pixiApp) => {
|
|
1090
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
1091
|
+
resizeTracking = true;
|
|
1092
|
+
resizeObserver = new ResizeObserver((entries) => {
|
|
1093
|
+
const entry = entries.find((candidate) => candidate.target === ctx.container);
|
|
1094
|
+
if (entry === void 0) return;
|
|
1095
|
+
resizeRenderer(pixiApp, entry.contentRect.width, entry.contentRect.height);
|
|
1096
|
+
});
|
|
1097
|
+
resizeObserver.observe(ctx.container);
|
|
1098
|
+
resizeRenderer(pixiApp, ctx.container.clientWidth, ctx.container.clientHeight);
|
|
1099
|
+
};
|
|
1100
|
+
/** Release every resource currently owned by this activation exactly once. */
|
|
1101
|
+
const destroyResources = () => {
|
|
1102
|
+
stopResizeTracking();
|
|
1103
|
+
unsubscribe?.();
|
|
1104
|
+
unsubscribe = void 0;
|
|
1105
|
+
const currentApp = app;
|
|
1106
|
+
const currentModel = model;
|
|
1107
|
+
const modelOwnedByApp = currentApp !== void 0 && modelAttached;
|
|
1108
|
+
app = void 0;
|
|
1109
|
+
model = void 0;
|
|
1110
|
+
modelSourceSize = void 0;
|
|
1111
|
+
modelAttached = false;
|
|
1112
|
+
try {
|
|
1113
|
+
if (currentModel !== void 0 && !modelOwnedByApp) currentModel.destroy(DESTROY_OPTIONS);
|
|
1114
|
+
} finally {
|
|
1115
|
+
currentApp?.destroy(RENDERER_DESTROY_OPTIONS, DESTROY_OPTIONS);
|
|
1116
|
+
}
|
|
1117
|
+
};
|
|
1053
1118
|
const playGroup = (group) => {
|
|
1054
1119
|
if (model === void 0) return;
|
|
1055
1120
|
const groups = model.internalModel.settings.motions ?? {};
|
|
@@ -1079,38 +1144,54 @@ window.__ModuleLoader__.load({
|
|
|
1079
1144
|
}
|
|
1080
1145
|
configureOnce(vendor);
|
|
1081
1146
|
const pixiApp = new vendor.Application();
|
|
1082
|
-
|
|
1083
|
-
width:
|
|
1084
|
-
height:
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1147
|
+
const initialSize = normalizeRendererSize(ctx.container.clientWidth, ctx.container.clientHeight) ?? {
|
|
1148
|
+
width: 160,
|
|
1149
|
+
height: 174
|
|
1150
|
+
};
|
|
1151
|
+
try {
|
|
1152
|
+
await pixiApp.init({
|
|
1153
|
+
width: initialSize.width,
|
|
1154
|
+
height: initialSize.height,
|
|
1155
|
+
backgroundAlpha: 0,
|
|
1156
|
+
antialias: true,
|
|
1157
|
+
autoDensity: true,
|
|
1158
|
+
preference: "webgl"
|
|
1159
|
+
});
|
|
1160
|
+
} catch (error) {
|
|
1161
|
+
try {
|
|
1162
|
+
pixiApp.destroy(RENDERER_DESTROY_OPTIONS, DESTROY_OPTIONS);
|
|
1163
|
+
} catch {}
|
|
1164
|
+
throw error;
|
|
1165
|
+
}
|
|
1090
1166
|
if (disposed) {
|
|
1091
|
-
pixiApp.destroy(
|
|
1167
|
+
pixiApp.destroy(RENDERER_DESTROY_OPTIONS, DESTROY_OPTIONS);
|
|
1092
1168
|
return;
|
|
1093
1169
|
}
|
|
1170
|
+
app = pixiApp;
|
|
1094
1171
|
pixiApp.canvas.style.display = "block";
|
|
1095
1172
|
pixiApp.canvas.style.width = "100%";
|
|
1096
1173
|
pixiApp.canvas.style.height = "100%";
|
|
1097
1174
|
ctx.container.appendChild(pixiApp.canvas);
|
|
1175
|
+
trackContainerSize(pixiApp);
|
|
1098
1176
|
const loaded = await vendor.Live2DModel.from(config.modelUrl, {
|
|
1177
|
+
autoUpdate: false,
|
|
1099
1178
|
autoHitTest: false,
|
|
1100
1179
|
autoFocus: false,
|
|
1101
1180
|
textureOptions: TEXTURE_OPTIONS
|
|
1102
1181
|
});
|
|
1182
|
+
model = loaded;
|
|
1103
1183
|
if (disposed) {
|
|
1104
|
-
|
|
1184
|
+
destroyResources();
|
|
1105
1185
|
return;
|
|
1106
1186
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
loaded
|
|
1112
|
-
loaded.position.set(pixiApp.renderer.width / 2 + (config.translate?.x ?? 0), pixiApp.renderer.height / 2 + (config.translate?.y ?? 0));
|
|
1187
|
+
modelSourceSize = {
|
|
1188
|
+
width: Math.max(1, loaded.width),
|
|
1189
|
+
height: Math.max(1, loaded.height)
|
|
1190
|
+
};
|
|
1191
|
+
layoutModel(pixiApp, loaded, modelSourceSize, config);
|
|
1113
1192
|
pixiApp.stage.addChild(loaded);
|
|
1193
|
+
modelAttached = true;
|
|
1194
|
+
loaded.automator.autoUpdate = true;
|
|
1114
1195
|
loaded.on("motionFinish", () => {
|
|
1115
1196
|
if (tapPlaying) {
|
|
1116
1197
|
tapPlaying = false;
|
|
@@ -1121,18 +1202,17 @@ window.__ModuleLoader__.load({
|
|
|
1121
1202
|
unsubscribe = ctx.phase.subscribe(applyPhase);
|
|
1122
1203
|
};
|
|
1123
1204
|
boot().catch(() => {
|
|
1124
|
-
|
|
1205
|
+
try {
|
|
1206
|
+
destroyResources();
|
|
1207
|
+
} finally {
|
|
1208
|
+
if (!disposed) errorListener?.("load-failed");
|
|
1209
|
+
}
|
|
1125
1210
|
});
|
|
1126
1211
|
return {
|
|
1127
1212
|
dispose() {
|
|
1128
1213
|
if (disposed) return;
|
|
1129
1214
|
disposed = true;
|
|
1130
|
-
|
|
1131
|
-
model = void 0;
|
|
1132
|
-
if (app !== void 0) {
|
|
1133
|
-
app.destroy(true, { children: true });
|
|
1134
|
-
app = void 0;
|
|
1135
|
-
}
|
|
1215
|
+
destroyResources();
|
|
1136
1216
|
},
|
|
1137
1217
|
tap(x, y) {
|
|
1138
1218
|
const current = model;
|
|
@@ -1154,7 +1234,7 @@ window.__ModuleLoader__.load({
|
|
|
1154
1234
|
};
|
|
1155
1235
|
//#endregion
|
|
1156
1236
|
//#region \0dsh-css:packages/dsh-pet/src/client/settings-card.module.css.mjs
|
|
1157
|
-
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}}";
|
|
1237
|
+
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;flex-shrink:0;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}}";
|
|
1158
1238
|
const tagId$1 = "@linxin666/dsh-pet/settings-card.module.css";
|
|
1159
1239
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
1160
1240
|
const tag = document.createElement("style");
|
|
@@ -1987,8 +2067,10 @@ window.__ModuleLoader__.load({
|
|
|
1987
2067
|
choiceField("petId", this.petChoices)
|
|
1988
2068
|
]);
|
|
1989
2069
|
this.store = this.form.bind(() => this.projection());
|
|
1990
|
-
|
|
1991
|
-
|
|
2070
|
+
window.setTimeout(() => {
|
|
2071
|
+
this.loadPets();
|
|
2072
|
+
this.loadDiagnostics();
|
|
2073
|
+
}, 0);
|
|
1992
2074
|
}
|
|
1993
2075
|
/** Fetch registry diagnostics once (soft-fail: an empty list on error). */
|
|
1994
2076
|
async loadDiagnostics() {
|