@firetable/project-xiaochun 0.1.15 → 0.1.16
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-CN.md +24 -14
- package/README.md +24 -14
- package/dist/avatar-element.d.ts +10 -4
- package/dist/chunks/{chunk-T6EHFEEZ.js → chunk-HZ2ADKG7.js} +39 -8
- package/dist/chunks/chunk-HZ2ADKG7.js.map +7 -0
- package/dist/chunks/{chunk-KKRZ3SUD.js → chunk-RHQW4QZH.js} +86 -3
- package/dist/chunks/chunk-RHQW4QZH.js.map +7 -0
- package/dist/chunks/{chunk-KQSKHPVF.js → chunk-UWL65W7D.js} +136 -10
- package/dist/chunks/chunk-UWL65W7D.js.map +7 -0
- package/dist/client.d.ts +64 -5
- package/dist/element.cjs +206 -12
- package/dist/element.cjs.map +2 -2
- package/dist/element.js +4 -4
- package/dist/index.cjs +247 -13
- package/dist/index.cjs.map +2 -2
- package/dist/index.js +20 -6
- package/dist/index.js.map +1 -1
- package/dist/loader.global.js +2 -2
- package/dist/loader.global.js.map +3 -3
- package/dist/protocol.cjs +86 -3
- package/dist/protocol.cjs.map +2 -2
- package/dist/protocol.d.ts +104 -3
- package/dist/protocol.js +18 -4
- package/dist/react.cjs +190 -14
- package/dist/react.cjs.map +2 -2
- package/dist/react.d.ts +6 -0
- package/dist/react.js +23 -9
- package/dist/react.js.map +2 -2
- package/package.json +2 -2
- package/dist/chunks/chunk-KKRZ3SUD.js.map +0 -7
- package/dist/chunks/chunk-KQSKHPVF.js.map +0 -7
- package/dist/chunks/chunk-T6EHFEEZ.js.map +0 -7
package/dist/element.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/*! @firetable/project-xiaochun v0.1.
|
|
1
|
+
/*! @firetable/project-xiaochun v0.1.16 | MIT | https://github.com/FireTable/project-xiaochun */
|
|
2
2
|
import {
|
|
3
3
|
XIAOCHUN_ELEMENT_TAG,
|
|
4
4
|
defineXiaochunElement
|
|
5
|
-
} from "./chunks/chunk-
|
|
6
|
-
import "./chunks/chunk-
|
|
7
|
-
import "./chunks/chunk-
|
|
5
|
+
} from "./chunks/chunk-HZ2ADKG7.js";
|
|
6
|
+
import "./chunks/chunk-UWL65W7D.js";
|
|
7
|
+
import "./chunks/chunk-RHQW4QZH.js";
|
|
8
8
|
|
|
9
9
|
// src/element.ts
|
|
10
10
|
defineXiaochunElement();
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @firetable/project-xiaochun v0.1.
|
|
1
|
+
/*! @firetable/project-xiaochun v0.1.16 | MIT | https://github.com/FireTable/project-xiaochun */
|
|
2
2
|
"use strict";
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -21,14 +21,17 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
// src/index.ts
|
|
22
22
|
var src_exports = {};
|
|
23
23
|
__export(src_exports, {
|
|
24
|
+
XC_CAMERA_RANGES: () => XC_CAMERA_RANGES,
|
|
24
25
|
XC_DEFAULT_ORIGIN: () => XC_DEFAULT_ORIGIN,
|
|
25
26
|
XC_DEFAULT_SRC: () => XC_DEFAULT_SRC,
|
|
26
27
|
XC_FRAME_TO_HOST: () => XC_FRAME_TO_HOST,
|
|
27
28
|
XC_HOST_TO_FRAME: () => XC_HOST_TO_FRAME,
|
|
28
29
|
XC_ID_RE: () => XC_ID_RE,
|
|
29
30
|
XC_IMPLEMENTED_COMMANDS: () => XC_IMPLEMENTED_COMMANDS,
|
|
31
|
+
XC_LANGS: () => XC_LANGS,
|
|
30
32
|
XC_PROTOCOL_MAPPING: () => XC_PROTOCOL_MAPPING,
|
|
31
33
|
XC_PROTOCOL_VERSION: () => XC_PROTOCOL_VERSION,
|
|
34
|
+
XC_UI_AUTOHIDE_DEFAULT: () => XC_UI_AUTOHIDE_DEFAULT,
|
|
32
35
|
XC_UI_LEGACY_PARTS: () => XC_UI_LEGACY_PARTS,
|
|
33
36
|
XC_UI_PARTS: () => XC_UI_PARTS,
|
|
34
37
|
XC_UNSUPPORTED_COMMANDS: () => XC_UNSUPPORTED_COMMANDS,
|
|
@@ -40,13 +43,17 @@ __export(src_exports, {
|
|
|
40
43
|
isXcEnvelope: () => isXcEnvelope,
|
|
41
44
|
isXcId: () => isXcId,
|
|
42
45
|
normalizeOrigin: () => normalizeOrigin,
|
|
46
|
+
normalizeXcCamera: () => normalizeXcCamera,
|
|
43
47
|
normalizeXcUiOption: () => normalizeXcUiOption,
|
|
44
48
|
parseProtocolUrl: () => parseProtocolUrl,
|
|
49
|
+
parseXcCameraParams: () => parseXcCameraParams,
|
|
50
|
+
parseXcUiAutoHide: () => parseXcUiAutoHide,
|
|
45
51
|
parseXcUiList: () => parseXcUiList,
|
|
46
52
|
parseXcUiParam: () => parseXcUiParam,
|
|
47
53
|
toProtocolUrl: () => toProtocolUrl,
|
|
48
54
|
xcHasOwn: () => xcHasOwn,
|
|
49
|
-
xcMessage: () => xcMessage
|
|
55
|
+
xcMessage: () => xcMessage,
|
|
56
|
+
xcUiAutoHideActive: () => xcUiAutoHideActive
|
|
50
57
|
});
|
|
51
58
|
module.exports = __toCommonJS(src_exports);
|
|
52
59
|
|
|
@@ -80,6 +87,7 @@ var XC_FRAME_TO_HOST = [
|
|
|
80
87
|
"xc.loaded",
|
|
81
88
|
"xc.outfit-changed",
|
|
82
89
|
"xc.scene-changed",
|
|
90
|
+
"xc.lang-changed",
|
|
83
91
|
"xc.prefetched",
|
|
84
92
|
"xc.state",
|
|
85
93
|
"xc.stt",
|
|
@@ -116,7 +124,7 @@ var XC_PROTOCOL_MAPPING = [
|
|
|
116
124
|
{ xc: "xc.audio.chunk / xc.audio.end", note: "\u6D41\u5F0F PCM", action: "audio", url: null }
|
|
117
125
|
];
|
|
118
126
|
var XC_WINDOW_CORNER_RADIUS = 20;
|
|
119
|
-
var XC_UI_PARTS = ["chat", "bubble", "outfit", "scene"];
|
|
127
|
+
var XC_UI_PARTS = ["chat", "bubble", "outfit", "scene", "lang", "github"];
|
|
120
128
|
var XC_UI_PART_SET = new Set(XC_UI_PARTS);
|
|
121
129
|
var XC_UI_LEGACY_PARTS = ["chat", "bubble"];
|
|
122
130
|
function parseXcUiList(items) {
|
|
@@ -140,9 +148,84 @@ function normalizeXcUiOption(v) {
|
|
|
140
148
|
if (Array.isArray(v)) return parseXcUiList(v);
|
|
141
149
|
return { parts: [], unknown: [], legacy: false };
|
|
142
150
|
}
|
|
151
|
+
var XC_UI_AUTOHIDE_DEFAULT = "transparent";
|
|
152
|
+
function parseXcUiAutoHide(raw) {
|
|
153
|
+
if (raw === true || raw === false || raw === "transparent") return raw;
|
|
154
|
+
if (typeof raw !== "string") return void 0;
|
|
155
|
+
const v = raw.trim().toLowerCase();
|
|
156
|
+
if (v === "1" || v === "true") return true;
|
|
157
|
+
if (v === "0" || v === "false") return false;
|
|
158
|
+
if (v === "transparent") return "transparent";
|
|
159
|
+
return void 0;
|
|
160
|
+
}
|
|
161
|
+
function xcUiAutoHideActive(mode, sceneTransparent) {
|
|
162
|
+
return mode === "transparent" ? sceneTransparent : mode;
|
|
163
|
+
}
|
|
164
|
+
var XC_CAMERA_RANGES = {
|
|
165
|
+
fov: [15, 60],
|
|
166
|
+
distance: [1, 15],
|
|
167
|
+
height: [-1, 1]
|
|
168
|
+
};
|
|
169
|
+
var XC_CAMERA_NUM_KEYS = ["fov", "distance", "height"];
|
|
170
|
+
var XC_CAMERA_KEYS = ["fov", "distance", "height", "intro"];
|
|
171
|
+
function normalizeXcCamera(raw) {
|
|
172
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return { ok: false, error: "camera must be an object" };
|
|
173
|
+
const src = raw;
|
|
174
|
+
for (const k of Object.keys(src)) {
|
|
175
|
+
if (!XC_CAMERA_KEYS.includes(k)) return { ok: false, error: `unknown camera key: ${k} (expected ${XC_CAMERA_KEYS.join(" | ")})` };
|
|
176
|
+
}
|
|
177
|
+
const out = {};
|
|
178
|
+
const clamped = [];
|
|
179
|
+
for (const k of XC_CAMERA_NUM_KEYS) {
|
|
180
|
+
const v = src[k];
|
|
181
|
+
if (v === void 0) continue;
|
|
182
|
+
if (v === null) {
|
|
183
|
+
out[k] = null;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (typeof v !== "number" || !Number.isFinite(v)) return { ok: false, error: `camera.${k} must be a finite number or null` };
|
|
187
|
+
const [lo, hi] = XC_CAMERA_RANGES[k];
|
|
188
|
+
const c = Math.min(hi, Math.max(lo, v));
|
|
189
|
+
if (c !== v) clamped.push(k);
|
|
190
|
+
out[k] = c;
|
|
191
|
+
}
|
|
192
|
+
if (src.intro !== void 0) {
|
|
193
|
+
if (src.intro !== null && typeof src.intro !== "boolean") return { ok: false, error: "camera.intro must be a boolean or null" };
|
|
194
|
+
out.intro = src.intro;
|
|
195
|
+
}
|
|
196
|
+
return { ok: true, camera: out, clamped };
|
|
197
|
+
}
|
|
198
|
+
function parseXcCameraParams(get) {
|
|
199
|
+
const camera = {};
|
|
200
|
+
const warnings = [];
|
|
201
|
+
const names = { fov: "cameraFov", distance: "cameraDistance", height: "cameraHeight" };
|
|
202
|
+
for (const k of XC_CAMERA_NUM_KEYS) {
|
|
203
|
+
const raw = get(names[k]);
|
|
204
|
+
if (raw === null) continue;
|
|
205
|
+
const t = raw.trim();
|
|
206
|
+
const n = t === "" ? NaN : Number(t);
|
|
207
|
+
if (!Number.isFinite(n)) {
|
|
208
|
+
warnings.push(`ignoring invalid ?${names[k]}=${raw}`);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const [lo, hi] = XC_CAMERA_RANGES[k];
|
|
212
|
+
const c = Math.min(hi, Math.max(lo, n));
|
|
213
|
+
if (c !== n) warnings.push(`?${names[k]}=${raw} is out of range [${lo}, ${hi}], clamped to ${c}`);
|
|
214
|
+
camera[k] = c;
|
|
215
|
+
}
|
|
216
|
+
const intro = get("cameraIntro");
|
|
217
|
+
if (intro !== null) {
|
|
218
|
+
const v = intro.trim().toLowerCase();
|
|
219
|
+
if (v === "1" || v === "true") camera.intro = true;
|
|
220
|
+
else if (v === "0" || v === "false") camera.intro = false;
|
|
221
|
+
else warnings.push(`ignoring invalid ?cameraIntro=${intro}`);
|
|
222
|
+
}
|
|
223
|
+
return { camera, warnings };
|
|
224
|
+
}
|
|
143
225
|
var XC_ID_RE = /^[a-z][a-z0-9_]{0,63}$/;
|
|
144
226
|
var isXcId = (v) => typeof v === "string" && XC_ID_RE.test(v);
|
|
145
227
|
var xcHasOwn = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
|
|
228
|
+
var XC_LANGS = ["zh-CN", "en", "ja"];
|
|
146
229
|
function xcMessage(type, payload, id) {
|
|
147
230
|
const m = { type, v: XC_PROTOCOL_VERSION, payload };
|
|
148
231
|
if (id !== void 0) m.id = id;
|
|
@@ -257,7 +340,20 @@ var GestureGate = class {
|
|
|
257
340
|
var FALLBACK_PLACEHOLDER = "data:image/svg+xml;utf8," + encodeURIComponent(
|
|
258
341
|
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 180"><defs><linearGradient id="g" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#f6b8ae"/><stop offset="1" stop-color="#ea8377"/></linearGradient></defs><ellipse cx="60" cy="170" rx="34" ry="6" fill="#000" opacity=".12"/><circle cx="60" cy="48" r="26" fill="url(#g)" opacity=".75"/><path d="M26 160c2-40 18-62 34-62s32 22 34 62z" fill="url(#g)" opacity=".6"/></svg>'
|
|
259
342
|
);
|
|
343
|
+
var noSelect = (st) => {
|
|
344
|
+
st.setProperty("user-select", "none");
|
|
345
|
+
st.setProperty("-webkit-user-select", "none");
|
|
346
|
+
st.setProperty("-webkit-touch-callout", "none");
|
|
347
|
+
st.setProperty("-webkit-tap-highlight-color", "transparent");
|
|
348
|
+
};
|
|
260
349
|
var css = (v, d) => v === void 0 ? d : typeof v === "number" ? `${v}px` : v;
|
|
350
|
+
var XC_DEFAULT_WIDTH = 600;
|
|
351
|
+
var XC_DEFAULT_HEIGHT = 1080;
|
|
352
|
+
var defaultWidthCss = (floating = false) => floating ? `min(${XC_DEFAULT_WIDTH}px, calc(100vw - 2 * var(--xc-offset-x, 16px)))` : `min(${XC_DEFAULT_WIDTH}px, 100vw)`;
|
|
353
|
+
var defaultHeightCss = (floating = false) => {
|
|
354
|
+
const unit = typeof CSS !== "undefined" && typeof CSS.supports === "function" && CSS.supports("height", "1svh") ? "svh" : "vh";
|
|
355
|
+
return floating ? `min(${XC_DEFAULT_HEIGHT}px, calc(100${unit} - 2 * var(--xc-offset-y, 16px)))` : `min(${XC_DEFAULT_HEIGHT}px, 100${unit})`;
|
|
356
|
+
};
|
|
261
357
|
function createXiaochun(options) {
|
|
262
358
|
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
263
359
|
throw new Error("[project-xiaochun] createXiaochun must run in a browser");
|
|
@@ -312,6 +408,16 @@ function createXiaochun(options) {
|
|
|
312
408
|
}
|
|
313
409
|
const uiParse = normalizeXcUiOption(options.ui);
|
|
314
410
|
const uiParts = uiParse.parts;
|
|
411
|
+
const uiAutoHide = parseXcUiAutoHide(options.uiAutoHide);
|
|
412
|
+
if (options.uiAutoHide !== void 0 && uiAutoHide === void 0) console.warn(`[project-xiaochun] ignoring invalid uiAutoHide: ${String(options.uiAutoHide)} (use true | false | 'transparent')`);
|
|
413
|
+
let camera = {};
|
|
414
|
+
if (options.camera !== void 0) {
|
|
415
|
+
const r = normalizeXcCamera(options.camera);
|
|
416
|
+
if (r.ok) {
|
|
417
|
+
camera = r.camera;
|
|
418
|
+
if (r.clamped.length) console.warn(`[project-xiaochun] camera.${r.clamped.join(", camera.")} out of range, clamped (fov ${XC_CAMERA_RANGES.fov.join("\u2013")}, distance ${XC_CAMERA_RANGES.distance.join("\u2013")}, height ${XC_CAMERA_RANGES.height.join("\u2013")})`);
|
|
419
|
+
} else console.warn(`[project-xiaochun] ignoring invalid camera option: ${r.error}`);
|
|
420
|
+
}
|
|
315
421
|
if (uiParse.legacy) console.warn(`[project-xiaochun] option "ui: true" is deprecated, use an array of parts, e.g. ui: ['chat', 'bubble', 'outfit', 'scene'].`);
|
|
316
422
|
if (uiParse.unknown.length) console.warn(`[project-xiaochun] ignoring unknown ui part(s): ${uiParse.unknown.join(", ")} (known: ${XC_UI_PARTS.join(", ")})`);
|
|
317
423
|
const outfitOpt = options.outfit ?? (isXcId(options.model) ? options.model : void 0);
|
|
@@ -326,11 +432,12 @@ function createXiaochun(options) {
|
|
|
326
432
|
wrapper.setAttribute("data-xiaochun", "");
|
|
327
433
|
const ws = wrapper.style;
|
|
328
434
|
ws.position = position === "inline" ? "relative" : "fixed";
|
|
329
|
-
ws.width = css(options.width, "
|
|
330
|
-
ws.height = css(options.height, "
|
|
435
|
+
ws.width = css(options.width, defaultWidthCss(position !== "inline"));
|
|
436
|
+
ws.height = css(options.height, defaultHeightCss(position !== "inline"));
|
|
331
437
|
ws.maxWidth = "100%";
|
|
332
438
|
ws.overflow = "visible";
|
|
333
439
|
ws.contain = "layout style";
|
|
440
|
+
noSelect(ws);
|
|
334
441
|
ws.boxShadow = "var(--xc-shadow, none)";
|
|
335
442
|
const applyWrapperBg = () => {
|
|
336
443
|
ws.background = transparent ? "" : "var(--xc-bg, transparent)";
|
|
@@ -365,8 +472,8 @@ function createXiaochun(options) {
|
|
|
365
472
|
img.src = options.placeholder || FALLBACK_PLACEHOLDER;
|
|
366
473
|
img.alt = "Project XiaoChun";
|
|
367
474
|
img.decoding = "async";
|
|
368
|
-
img.width =
|
|
369
|
-
img.height =
|
|
475
|
+
img.width = XC_DEFAULT_WIDTH;
|
|
476
|
+
img.height = XC_DEFAULT_HEIGHT;
|
|
370
477
|
placeholderEl = img;
|
|
371
478
|
}
|
|
372
479
|
const ps = placeholderEl.style;
|
|
@@ -380,6 +487,7 @@ function createXiaochun(options) {
|
|
|
380
487
|
ps.transition = "opacity .25s ease";
|
|
381
488
|
ps.pointerEvents = "auto";
|
|
382
489
|
ps.cursor = lazy === false ? "default" : "pointer";
|
|
490
|
+
noSelect(ps);
|
|
383
491
|
placeholderEl.addEventListener("click", () => activate());
|
|
384
492
|
wrapper.appendChild(placeholderEl);
|
|
385
493
|
}
|
|
@@ -538,6 +646,11 @@ function createXiaochun(options) {
|
|
|
538
646
|
if (transparent) u.searchParams.set("transparent", "1");
|
|
539
647
|
if (uiParts.length) u.searchParams.set("ui", uiParts.join(","));
|
|
540
648
|
if (options.lang) u.searchParams.set("lang", options.lang);
|
|
649
|
+
if (uiAutoHide !== void 0) u.searchParams.set("uiAutoHide", String(uiAutoHide));
|
|
650
|
+
if (typeof camera.fov === "number") u.searchParams.set("cameraFov", String(camera.fov));
|
|
651
|
+
if (typeof camera.distance === "number") u.searchParams.set("cameraDistance", String(camera.distance));
|
|
652
|
+
if (typeof camera.height === "number") u.searchParams.set("cameraHeight", String(camera.height));
|
|
653
|
+
if (typeof camera.intro === "boolean") u.searchParams.set("cameraIntro", camera.intro ? "1" : "0");
|
|
541
654
|
if (options.heavy) u.searchParams.set("heavy", options.heavy);
|
|
542
655
|
if (options.controls === false) u.searchParams.set("controls", "0");
|
|
543
656
|
if (initialScene && isXcId(initialScene)) u.searchParams.set("scene", initialScene);
|
|
@@ -569,6 +682,7 @@ function createXiaochun(options) {
|
|
|
569
682
|
s.colorScheme = "normal";
|
|
570
683
|
s.borderRadius = `var(--xc-radius, ${radiusValue()})`;
|
|
571
684
|
f.setAttribute("part", "iframe");
|
|
685
|
+
noSelect(s);
|
|
572
686
|
s.opacity = "0";
|
|
573
687
|
s.transition = "opacity .25s ease";
|
|
574
688
|
s.pointerEvents = passthrough ? "none" : "auto";
|
|
@@ -674,6 +788,9 @@ function createXiaochun(options) {
|
|
|
674
788
|
ack(data.id);
|
|
675
789
|
break;
|
|
676
790
|
}
|
|
791
|
+
case "xc.lang-changed":
|
|
792
|
+
emit("lang-changed", data.payload);
|
|
793
|
+
break;
|
|
677
794
|
case "xc.prefetched":
|
|
678
795
|
ack(data.id, data.payload);
|
|
679
796
|
break;
|
|
@@ -918,6 +1035,7 @@ function createXiaochun(options) {
|
|
|
918
1035
|
emit(activeGesture.kind, { phase: "end", ...b });
|
|
919
1036
|
activeGesture = null;
|
|
920
1037
|
lockSelection(false);
|
|
1038
|
+
saveBox();
|
|
921
1039
|
}
|
|
922
1040
|
function onGesture(kind, raw) {
|
|
923
1041
|
if (destroyed || !caps?.gestures) return;
|
|
@@ -946,6 +1064,7 @@ function createXiaochun(options) {
|
|
|
946
1064
|
if (p.phase === "end") {
|
|
947
1065
|
activeGesture = null;
|
|
948
1066
|
lockSelection(false);
|
|
1067
|
+
saveBox();
|
|
949
1068
|
}
|
|
950
1069
|
}
|
|
951
1070
|
function syncGestures() {
|
|
@@ -970,11 +1089,63 @@ function createXiaochun(options) {
|
|
|
970
1089
|
const onWindowResize = () => {
|
|
971
1090
|
if (!userMoved || activeGesture || destroyed) return;
|
|
972
1091
|
const cur = readBox();
|
|
973
|
-
const
|
|
1092
|
+
const vp = viewport();
|
|
1093
|
+
const fit = position === "inline" ? { ...fitBox({ ...cur, top: 0 }, { width: vp.width, height: Number.MAX_SAFE_INTEGER }), top: Math.max(cur.top, -window.scrollY) } : fitBox(cur, vp);
|
|
974
1094
|
if (fit.left === cur.left && fit.top === cur.top && fit.width === cur.width && fit.height === cur.height) return;
|
|
975
1095
|
applyBox(fit, position === "inline" ? { left: cur.left - inlineShift.x, top: cur.top - inlineShift.y } : null, fit.width !== cur.width || fit.height !== cur.height);
|
|
976
1096
|
};
|
|
977
1097
|
window.addEventListener("resize", onWindowResize, { passive: true });
|
|
1098
|
+
const boxKey = options.persistBox === true ? "xiaochun:box" : typeof options.persistBox === "string" && options.persistBox ? `xiaochun:box:${options.persistBox}` : null;
|
|
1099
|
+
const saveBox = () => {
|
|
1100
|
+
if (!boxKey) return;
|
|
1101
|
+
try {
|
|
1102
|
+
const b = readBox();
|
|
1103
|
+
const at = position === "inline" ? inlineShift : { x: b.left, y: b.top };
|
|
1104
|
+
window.localStorage.setItem(boxKey, JSON.stringify({ v: 1, mode: position, x: Math.round(at.x), y: Math.round(at.y), width: Math.round(b.width), height: Math.round(b.height) }));
|
|
1105
|
+
} catch {
|
|
1106
|
+
}
|
|
1107
|
+
};
|
|
1108
|
+
const loadBox = () => {
|
|
1109
|
+
if (!boxKey) return null;
|
|
1110
|
+
try {
|
|
1111
|
+
const raw = window.localStorage.getItem(boxKey);
|
|
1112
|
+
if (raw === null) return null;
|
|
1113
|
+
let o = null;
|
|
1114
|
+
try {
|
|
1115
|
+
o = JSON.parse(raw);
|
|
1116
|
+
} catch {
|
|
1117
|
+
}
|
|
1118
|
+
const ok = (v, lo, hi) => typeof v === "number" && Number.isFinite(v) && v >= lo && v <= hi;
|
|
1119
|
+
if (o && o.v === 1 && o.mode === position && ok(o.x, -1e5, 1e5) && ok(o.y, -1e5, 1e5) && ok(o.width, 1, 1e5) && ok(o.height, 1, 1e5)) {
|
|
1120
|
+
return { x: o.x, y: o.y, width: o.width, height: o.height };
|
|
1121
|
+
}
|
|
1122
|
+
window.localStorage.removeItem(boxKey);
|
|
1123
|
+
} catch {
|
|
1124
|
+
}
|
|
1125
|
+
return null;
|
|
1126
|
+
};
|
|
1127
|
+
function restoreBox() {
|
|
1128
|
+
if (!boxKey || !draggableOn && resizeLimits === null) return;
|
|
1129
|
+
const st = loadBox();
|
|
1130
|
+
if (!st) return;
|
|
1131
|
+
const vp = viewport();
|
|
1132
|
+
const lim = resizeLimits ?? DEFAULT_RESIZE_LIMITS;
|
|
1133
|
+
const cur = readBox();
|
|
1134
|
+
const useW = resizeLimits !== null && options.width === void 0;
|
|
1135
|
+
const useH = resizeLimits !== null && options.height === void 0;
|
|
1136
|
+
const clampN = (v, lo, hi) => Math.min(Math.max(v, lo), Math.max(lo, hi));
|
|
1137
|
+
const width = useW ? clampN(st.width, lim.minWidth, Math.min(lim.maxWidth, vp.width)) : cur.width;
|
|
1138
|
+
const height = useH ? clampN(st.height, lim.minHeight, Math.min(lim.maxHeight, vp.height)) : cur.height;
|
|
1139
|
+
if (position === "inline") {
|
|
1140
|
+
const base = { left: cur.left - inlineShift.x, top: cur.top - inlineShift.y };
|
|
1141
|
+
const left = clampN(base.left + st.x, 0, vp.width - width);
|
|
1142
|
+
const top = Math.max(base.top + st.y, -window.scrollY);
|
|
1143
|
+
applyBox({ left, top, width, height }, base, useW || useH);
|
|
1144
|
+
} else {
|
|
1145
|
+
applyBox(fitBox({ left: st.x, top: st.y, width, height }, vp), null, useW || useH);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
restoreBox();
|
|
978
1149
|
scheduleLazy();
|
|
979
1150
|
const api = {
|
|
980
1151
|
element: wrapper,
|
|
@@ -1031,11 +1202,44 @@ function createXiaochun(options) {
|
|
|
1031
1202
|
setConfig: (cfg) => send("xc.setConfig", cfg),
|
|
1032
1203
|
setSize(width, height) {
|
|
1033
1204
|
if (destroyed) return;
|
|
1034
|
-
ws.width = css(width, "
|
|
1035
|
-
ws.height = css(height, "
|
|
1205
|
+
ws.width = css(width, defaultWidthCss(position !== "inline"));
|
|
1206
|
+
ws.height = css(height, defaultHeightCss(position !== "inline"));
|
|
1207
|
+
if (ws.maxWidth === "none") ws.maxWidth = "100%";
|
|
1036
1208
|
invalidateRect();
|
|
1037
1209
|
},
|
|
1038
1210
|
getBox: () => readBox(),
|
|
1211
|
+
clearPersistedBox(opts) {
|
|
1212
|
+
if (boxKey) {
|
|
1213
|
+
try {
|
|
1214
|
+
window.localStorage.removeItem(boxKey);
|
|
1215
|
+
} catch {
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
if (destroyed || opts?.reset !== true) return;
|
|
1219
|
+
endActiveGesture();
|
|
1220
|
+
ws.width = css(options.width, defaultWidthCss(position !== "inline"));
|
|
1221
|
+
ws.height = css(options.height, defaultHeightCss(position !== "inline"));
|
|
1222
|
+
ws.maxWidth = "100%";
|
|
1223
|
+
if (position === "inline") {
|
|
1224
|
+
inlineShift = { x: 0, y: 0 };
|
|
1225
|
+
ws.removeProperty("translate");
|
|
1226
|
+
} else {
|
|
1227
|
+
ws.left = "";
|
|
1228
|
+
ws.top = "";
|
|
1229
|
+
ws.right = "";
|
|
1230
|
+
ws.bottom = "";
|
|
1231
|
+
if (position === "bottom-right") {
|
|
1232
|
+
ws.right = "var(--xc-offset-x, 16px)";
|
|
1233
|
+
ws.bottom = "var(--xc-offset-y, 16px)";
|
|
1234
|
+
}
|
|
1235
|
+
if (position === "bottom-left") {
|
|
1236
|
+
ws.left = "var(--xc-offset-x, 16px)";
|
|
1237
|
+
ws.bottom = "var(--xc-offset-y, 16px)";
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
userMoved = false;
|
|
1241
|
+
invalidateRect();
|
|
1242
|
+
},
|
|
1039
1243
|
setBorderRadius(radius) {
|
|
1040
1244
|
radiusOpt = radius;
|
|
1041
1245
|
applyRadius();
|
|
@@ -1126,14 +1330,20 @@ var OBSERVED = [
|
|
|
1126
1330
|
"placeholder",
|
|
1127
1331
|
"heavy",
|
|
1128
1332
|
"ui",
|
|
1333
|
+
"ui-autohide",
|
|
1334
|
+
"camera-fov",
|
|
1335
|
+
"camera-distance",
|
|
1336
|
+
"camera-height",
|
|
1337
|
+
"camera-intro",
|
|
1129
1338
|
"controls",
|
|
1130
1339
|
"allowed-origins",
|
|
1131
1340
|
"cross-origin-isolated",
|
|
1132
1341
|
"persist",
|
|
1342
|
+
"persist-box",
|
|
1133
1343
|
"allow-custom-model",
|
|
1134
1344
|
"prefetch"
|
|
1135
1345
|
];
|
|
1136
|
-
var REBUILD = /* @__PURE__ */ new Set(["src", "position", "lazy", "placeholder", "heavy", "ui", "controls", "allowed-origins", "transparent", "cross-origin-isolated", "persist", "allow-custom-model", "prefetch"]);
|
|
1346
|
+
var REBUILD = /* @__PURE__ */ new Set(["src", "position", "lazy", "placeholder", "heavy", "ui", "ui-autohide", "controls", "allowed-origins", "transparent", "cross-origin-isolated", "persist", "persist-box", "allow-custom-model", "prefetch"]);
|
|
1137
1347
|
function radiusAttr(v) {
|
|
1138
1348
|
if (v === null || v.trim() === "") return void 0;
|
|
1139
1349
|
const t = v.trim();
|
|
@@ -1149,22 +1359,37 @@ function prefetchAttr(v) {
|
|
|
1149
1359
|
if (v === "" || v === "true") return true;
|
|
1150
1360
|
return v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1151
1361
|
}
|
|
1362
|
+
function persistBoxAttr(v) {
|
|
1363
|
+
if (v === null || v === "false" || v === "0") return void 0;
|
|
1364
|
+
if (v === "" || v === "true" || v === "1") return true;
|
|
1365
|
+
return v;
|
|
1366
|
+
}
|
|
1152
1367
|
function uiAttr(v) {
|
|
1153
1368
|
if (v === null || v === "false" || v === "0") return void 0;
|
|
1154
1369
|
if (v === "" || v === "true" || v === "1") return true;
|
|
1155
1370
|
return v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1156
1371
|
}
|
|
1372
|
+
function cameraAttrs(el) {
|
|
1373
|
+
const num = (name) => {
|
|
1374
|
+
const v = el.getAttribute(name);
|
|
1375
|
+
if (v === null || v.trim() === "") return null;
|
|
1376
|
+
const n = Number(v);
|
|
1377
|
+
return Number.isFinite(n) ? n : null;
|
|
1378
|
+
};
|
|
1379
|
+
const i = el.getAttribute("camera-intro");
|
|
1380
|
+
return { fov: num("camera-fov"), distance: num("camera-distance"), height: num("camera-height"), intro: i === null ? null : i !== "false" && i !== "0" };
|
|
1381
|
+
}
|
|
1157
1382
|
function parsePx(v) {
|
|
1158
1383
|
const m = v?.trim().match(/^(\d+(?:\.\d+)?)\s*x\s*(\d+(?:\.\d+)?)$/i);
|
|
1159
1384
|
return m ? { w: Number(m[1]), h: Number(m[2]) } : void 0;
|
|
1160
1385
|
}
|
|
1161
1386
|
function parseSize(v) {
|
|
1162
|
-
if (!v) return { width:
|
|
1387
|
+
if (!v) return { width: void 0, height: void 0 };
|
|
1163
1388
|
const m = v.trim().split(/x/i);
|
|
1164
1389
|
const px = (s) => /^\d+(\.\d+)?$/.test(s) ? `${s}px` : s;
|
|
1165
1390
|
if (m.length >= 2) return { width: px(m[0]), height: px(m[1]) };
|
|
1166
1391
|
const w = parseFloat(m[0]);
|
|
1167
|
-
return Number.isFinite(w) && /^\d+(\.\d+)?$/.test(m[0]) ? { width: `${w}px`, height: `${Math.round(w * 1.5)}px` } : { width: m[0], height:
|
|
1392
|
+
return Number.isFinite(w) && /^\d+(\.\d+)?$/.test(m[0]) ? { width: `${w}px`, height: `${Math.round(w * 1.5)}px` } : { width: m[0], height: void 0 };
|
|
1168
1393
|
}
|
|
1169
1394
|
function createElementClass() {
|
|
1170
1395
|
class XiaochunAvatarElementImpl extends HTMLElement {
|
|
@@ -1219,6 +1444,8 @@ function createElementClass() {
|
|
|
1219
1444
|
} else if (name === "border-radius") c.setBorderRadius(radiusAttr(newV));
|
|
1220
1445
|
else if (name === "draggable") c.setDraggable(boolAttr(this, "draggable", false));
|
|
1221
1446
|
else if (name === "resizable" || name === "min-size" || name === "max-size") c.setResizable(this.resizableOption());
|
|
1447
|
+
else if (name.startsWith("camera-")) void c.setConfig({ camera: cameraAttrs(this) }).catch(() => {
|
|
1448
|
+
});
|
|
1222
1449
|
else if (name === "mic") void c.mic(boolAttr(this, "mic", false)).catch(() => {
|
|
1223
1450
|
});
|
|
1224
1451
|
else if (name === "paused") {
|
|
@@ -1251,10 +1478,13 @@ function createElementClass() {
|
|
|
1251
1478
|
outfit: this.getAttribute("outfit") ?? this.getAttribute("model") ?? void 0,
|
|
1252
1479
|
scene: this.getAttribute("scene") ?? void 0,
|
|
1253
1480
|
persist: this.getAttribute("persist") || void 0,
|
|
1481
|
+
persistBox: persistBoxAttr(this.getAttribute("persist-box")),
|
|
1254
1482
|
allowCustomModel: boolAttr(this, "allow-custom-model", false),
|
|
1255
1483
|
prefetch: prefetchAttr(this.getAttribute("prefetch")),
|
|
1256
1484
|
heavy: this.getAttribute("heavy") ?? void 0,
|
|
1257
1485
|
ui: uiAttr(this.getAttribute("ui")),
|
|
1486
|
+
uiAutoHide: parseXcUiAutoHide(this.getAttribute("ui-autohide")),
|
|
1487
|
+
camera: cameraAttrs(this),
|
|
1258
1488
|
controls: boolAttr(this, "controls", true),
|
|
1259
1489
|
crossOriginIsolated: boolAttr(this, "cross-origin-isolated", false)
|
|
1260
1490
|
});
|
|
@@ -1268,6 +1498,7 @@ function createElementClass() {
|
|
|
1268
1498
|
fwd("error", "xc-error");
|
|
1269
1499
|
fwd("outfit-changed", "xc-outfit-changed");
|
|
1270
1500
|
fwd("scene-changed", "xc-scene-changed");
|
|
1501
|
+
fwd("lang-changed", "xc-lang-changed");
|
|
1271
1502
|
fwd("move", "xc-move");
|
|
1272
1503
|
fwd("resize", "xc-resize");
|
|
1273
1504
|
if (boolAttr(this, "paused", false)) c.pause();
|
|
@@ -1307,6 +1538,9 @@ function createElementClass() {
|
|
|
1307
1538
|
getScenes() {
|
|
1308
1539
|
return this.requireClient().getScenes();
|
|
1309
1540
|
}
|
|
1541
|
+
clearPersistedBox(opts) {
|
|
1542
|
+
this.client?.clearPersistedBox(opts);
|
|
1543
|
+
}
|
|
1310
1544
|
prefetch(ids) {
|
|
1311
1545
|
return this.requireClient().prefetch(ids);
|
|
1312
1546
|
}
|