@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/dist/element.js CHANGED
@@ -1,10 +1,10 @@
1
- /*! @firetable/project-xiaochun v0.1.15 | MIT | https://github.com/FireTable/project-xiaochun */
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-T6EHFEEZ.js";
6
- import "./chunks/chunk-KQSKHPVF.js";
7
- import "./chunks/chunk-KKRZ3SUD.js";
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.15 | MIT | https://github.com/FireTable/project-xiaochun */
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, "320px");
330
- ws.height = css(options.height, "480px");
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 = 320;
369
- img.height = 480;
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 fit = fitBox(cur, viewport());
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, "320px");
1035
- ws.height = css(height, "480px");
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: "320px", height: "480px" };
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: "480px" };
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
  }