@liveroom-tech/react-immersive 2.0.0 → 2.1.1

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/index.js CHANGED
@@ -54,9 +54,11 @@ __export(index_exports, {
54
54
  SimpleModelViewer: () => SimpleModelViewer,
55
55
  material: () => material,
56
56
  patchBindings: () => patchBindings,
57
+ patchSceneConfig: () => patchSceneConfig,
57
58
  useObjectBinding: () => useObjectBinding,
58
59
  useObjectBindingIds: () => useObjectBindingIds,
59
60
  useObjectBindings: () => useObjectBindings,
61
+ useSceneConfig: () => useSceneConfig,
60
62
  useShareableViewerState: () => useShareableViewerState,
61
63
  useViewerActions: () => useViewerActions,
62
64
  useViewerAnimations: () => useViewerAnimations,
@@ -5492,32 +5494,37 @@ function ObjectBindingDataPanel({
5492
5494
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("hr", { className: "left-panel__divider" }),
5493
5495
  objectToRender.actions && objectToRender.actions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
5494
5496
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "left-panel__actions-label", children: "Actions" }),
5495
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "left-panel__actions-list", children: objectToRender.actions.map((action) => {
5497
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "left-panel__actions-list", children: objectToRender.actions.map((action, index) => {
5496
5498
  const isActive = currentAction?.action.id === action.id && (currentAction.binding === objectToRender || currentAction.binding?.id === objectToRender.id || currentAction.binding?.modelObjectId === objectToRender.modelObjectId || currentAction.objectId === objectToRender.id || currentAction.objectId === objectToRender.modelObjectId);
5497
5499
  const extra = renderActionExtra?.(action.id);
5498
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react18.Fragment, { children: [
5499
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5500
- "button",
5501
- {
5502
- onClick: (e) => onAction({
5503
- objectId: objectToRender.modelObjectId || objectToRender.id || "",
5504
- action,
5505
- binding: objectToRender,
5506
- screenX: e.clientX,
5507
- screenY: e.clientY
5508
- }),
5509
- className: [
5510
- "left-panel__action-btn",
5511
- `left-panel__action-btn--${action.id}`,
5512
- isActive ? "left-panel__action-btn--active" : ""
5513
- ].filter(Boolean).join(" "),
5514
- "data-action-id": action.id,
5515
- "data-label": action.label,
5516
- children: action.label
5517
- }
5518
- ),
5519
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ActionExtra, { content: extra })
5520
- ] }, action.id);
5500
+ return (
5501
+ // Keyed by position, not action.id: ids are author-supplied
5502
+ // and not guaranteed unique within a binding, and colliding
5503
+ // keys make React drop one of the buttons.
5504
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react18.Fragment, { children: [
5505
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5506
+ "button",
5507
+ {
5508
+ onClick: (e) => onAction({
5509
+ objectId: objectToRender.modelObjectId || objectToRender.id || "",
5510
+ action,
5511
+ binding: objectToRender,
5512
+ screenX: e.clientX,
5513
+ screenY: e.clientY
5514
+ }),
5515
+ className: [
5516
+ "left-panel__action-btn",
5517
+ `left-panel__action-btn--${action.id}`,
5518
+ isActive ? "left-panel__action-btn--active" : ""
5519
+ ].filter(Boolean).join(" "),
5520
+ "data-action-id": action.id,
5521
+ "data-label": action.label,
5522
+ children: action.label
5523
+ }
5524
+ ),
5525
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ActionExtra, { content: extra })
5526
+ ] }, index)
5527
+ );
5521
5528
  }) })
5522
5529
  ] })
5523
5530
  ] })
@@ -8223,11 +8230,11 @@ function ModelViewer({
8223
8230
  }
8224
8231
 
8225
8232
  // src/components/BindingBuilder/BindingBuilder.tsx
8226
- var import_react39 = require("react");
8233
+ var import_react40 = require("react");
8227
8234
  var import_lucide_react16 = require("lucide-react");
8228
8235
 
8229
8236
  // src/styles/BindingBuilder.css
8230
- styleInject('.bb-root {\n --bb-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --bb-app-bg:\n radial-gradient(\n \n circle at top left,\n rgba(226, 232, 240, 0.8),\n transparent 30% ),\n linear-gradient(\n 135deg,\n #f6f1e8 0%,\n #ebe8df 40%,\n #e9edf2 100%);\n --bb-app-text: #111827;\n --bb-sidebar-bg:\n linear-gradient(\n 180deg,\n rgba(20, 27, 45, 0.98),\n rgba(25, 34, 57, 0.95)),\n #121826;\n --bb-sidebar-text: #f8fafc;\n --bb-sidebar-muted-text: #b3bfd2;\n --bb-sidebar-subtle-text: #94a3b8;\n --bb-sidebar-border: rgba(148, 163, 184, 0.18);\n --bb-sidebar-border-hover: rgba(148, 163, 184, 0.32);\n --bb-sidebar-divider: rgba(148, 163, 184, 0.14);\n --bb-sidebar-surface: rgba(255, 255, 255, 0.04);\n --bb-sidebar-surface-hover: rgba(255, 255, 255, 0.08);\n --bb-sidebar-surface-strong: rgba(255, 255, 255, 0.05);\n --bb-sidebar-eyebrow-bg: rgba(148, 163, 184, 0.16);\n --bb-sidebar-eyebrow-text: #cbd5e1;\n --bb-sidebar-left-shadow: 18px 0 42px rgba(15, 23, 42, 0.12);\n --bb-sidebar-right-shadow: -4px 0 24px rgba(0, 0, 0, 0.35);\n --bb-sidebar-chip-bg: rgba(15, 23, 42, 0.42);\n --bb-sidebar-chip-border: rgba(148, 163, 184, 0.14);\n --bb-sidebar-accent: rgba(99, 102, 241, 0.18);\n --bb-sidebar-accent-border: rgba(99, 102, 241, 0.4);\n --bb-sidebar-accent-text: #f8fafc;\n --bb-menu-bg: #1a1f2e;\n --bb-menu-shadow: 0 16px 32px rgba(15, 23, 42, 0.4);\n display: flex;\n height: 100%;\n min-height: 0;\n width: 100%;\n background: var(--bb-app-bg);\n color: var(--bb-app-text);\n font-family: var(--bb-font-family);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n font-synthesis: none;\n}\n.bb-root * {\n box-sizing: border-box;\n}\n.bb-root input,\n.bb-root select,\n.bb-root textarea,\n.bb-root button {\n font-family: inherit;\n}\n.bb-sidebar {\n width: 400px;\n min-height: 0;\n flex-shrink: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background: var(--bb-sidebar-bg);\n color: var(--bb-sidebar-text);\n transition: width 0.24s ease;\n}\n.bb-sidebar--left {\n border-right: 1px solid var(--bb-sidebar-border);\n box-shadow: var(--bb-sidebar-left-shadow);\n}\n.bb-sidebar--right {\n width: 300px;\n border-left: 1px solid var(--bb-sidebar-border);\n box-shadow: var(--bb-sidebar-right-shadow);\n}\n.bb-sidebar--collapsed {\n width: 36px;\n}\n.bb-sidebar--collapsed .bb-header {\n padding: 5px;\n display: flex;\n justify-content: center;\n}\n.bb-sidebar--collapsed .bb-header--sidebar {\n min-height: 36px;\n}\n.bb-sidebar--collapsed .bb-sidebarToggle {\n width: 26px;\n height: 26px;\n font-size: 12px;\n}\n.bb-header {\n padding: 5px 20px;\n border-bottom: 1px solid var(--bb-sidebar-divider);\n}\n.bb-header--sidebar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n min-height: 55px;\n}\n.bb-eyebrow {\n display: inline-block;\n margin-bottom: 12px;\n padding: 6px 10px;\n border-radius: 999px;\n background: var(--bb-sidebar-eyebrow-bg);\n color: var(--bb-sidebar-eyebrow-text);\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n}\n.bb-title {\n margin: 0 0 0px;\n font-size: 20px;\n line-height: 0.95;\n letter-spacing: -0.04em;\n font-weight: 800;\n color: var(--bb-sidebar-text);\n}\n.bb-sidebarToggle {\n width: 36px;\n height: 36px;\n flex-shrink: 0;\n border-radius: 6px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface-strong);\n color: var(--bb-sidebar-text);\n font-size: 16px;\n font-weight: 800;\n cursor: pointer;\n transition:\n transform 0.18s ease,\n background-color 0.18s ease,\n border-color 0.18s ease;\n}\n.bb-sidebarToggle:hover {\n transform: translateY(-1px);\n background: var(--bb-sidebar-surface-hover);\n border-color: var(--bb-sidebar-border-hover);\n}\n.bb-subtitle {\n margin: 0;\n max-width: 32ch;\n font-size: 15px;\n line-height: 1.7;\n color: var(--bb-sidebar-muted-text);\n}\n.bb-statRow {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 10px;\n margin-top: 22px;\n}\n.bb-statChip {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n padding: 12px 14px;\n border-radius: 8px;\n background: var(--bb-sidebar-chip-bg);\n border: 1px solid var(--bb-sidebar-chip-border);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);\n}\n.bb-statChip strong {\n font-size: 13px;\n color: var(--bb-sidebar-text);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.bb-statLabel {\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--bb-sidebar-subtle-text);\n}\n.bb-tabRow {\n display: flex;\n gap: 6px;\n padding: 10px 20px 10px;\n}\n.bb-tab {\n flex: 1;\n padding: 6px 10px;\n border-radius: 6px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface);\n color: var(--bb-sidebar-muted-text);\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n cursor: pointer;\n transition:\n background-color 0.18s ease,\n border-color 0.18s ease,\n color 0.18s ease;\n}\n.bb-tab:hover {\n background: var(--bb-sidebar-surface-hover);\n border-color: var(--bb-sidebar-border-hover);\n}\n.bb-tab--active {\n background: var(--bb-sidebar-accent);\n border-color: var(--bb-sidebar-accent-border);\n color: var(--bb-sidebar-accent-text);\n}\n.bb-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 10px;\n padding: 8px 20px 8px;\n border-bottom: 1px solid var(--bb-sidebar-divider);\n flex-wrap: wrap;\n}\n.bb-toolbarGroup {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n.bb-toolbarDivider {\n width: 1px;\n align-self: stretch;\n background: var(--bb-sidebar-divider);\n margin: 0;\n}\n.bb-iconButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border-radius: 7px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface);\n color: var(--bb-sidebar-text);\n cursor: pointer;\n transition: background-color 0.18s ease, border-color 0.18s ease;\n}\n.bb-iconButton:hover:not(:disabled) {\n background: var(--bb-sidebar-surface-hover);\n border-color: var(--bb-sidebar-border-hover);\n}\n.bb-iconButton:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.bb-exportMenuWrapper {\n position: relative;\n display: inline-flex;\n}\n.bb-exportMenu {\n position: absolute;\n bottom: calc(100% + 6px);\n right: 0;\n z-index: 20;\n display: flex;\n flex-direction: column;\n min-width: 150px;\n padding: 6px;\n border-radius: 9px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-menu-bg);\n box-shadow: var(--bb-menu-shadow);\n}\n.bb-exportMenuItem {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: var(--bb-sidebar-text);\n font-size: 12px;\n font-weight: 600;\n text-align: left;\n cursor: pointer;\n}\n.bb-exportMenuItem:hover {\n background: var(--bb-sidebar-surface-hover);\n}\n@media (max-width: 640px) {\n .bb-toolbarDivider {\n width: 100%;\n height: 1px;\n }\n}\n.bb-button,\n.bb-fileLabel {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n min-height: 32px;\n padding: 0 12px;\n border-radius: 7px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface);\n color: var(--bb-sidebar-text);\n font-size: 12px;\n font-weight: 700;\n cursor: pointer;\n transition:\n transform 0.18s ease,\n background-color 0.18s ease,\n border-color 0.18s ease,\n box-shadow 0.18s ease;\n}\n.bb-button:hover,\n.bb-fileLabel:hover {\n transform: translateY(-1px);\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.32);\n box-shadow: 0 10px 18px rgba(15, 23, 42, 0.18);\n}\n.bb-button:disabled,\n.bb-fileLabel:has(.bb-hiddenInput:disabled) {\n opacity: 0.55;\n cursor: not-allowed;\n transform: none;\n box-shadow: none;\n}\n.bb-button--primary {\n background: #135bc0;\n border-color: transparent;\n color: #ffffff;\n}\n.bb-button--primary:hover {\n background: #1a68d6;\n}\n.bb-button--ghost {\n background: rgba(248, 250, 252, 0.08);\n}\n.bb-hiddenInput {\n display: none;\n}\n.bb-statusCard {\n margin-top: 4px;\n padding: 14px 16px;\n border-radius: 9px;\n background: rgba(15, 23, 42, 0.44);\n border: 1px solid rgba(148, 163, 184, 0.15);\n}\n.bb-statusCard--error {\n background: rgba(127, 29, 29, 0.32);\n border-color: rgba(248, 113, 113, 0.24);\n}\n.bb-statusCard--success {\n background: rgba(6, 78, 59, 0.34);\n border-color: rgba(52, 211, 153, 0.24);\n}\n.bb-statusTitle {\n margin-bottom: 6px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: #cbd5e1;\n}\n.bb-status {\n font-size: 13px;\n line-height: 1.55;\n color: #e2e8f0;\n}\n.bb-status--error {\n color: #fecaca;\n}\n.bb-status--success {\n color: #bbf7d0;\n}\n.bb-input--error {\n border-color: #dc2626;\n}\n.bb-nodeList {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 0;\n overflow: hidden;\n background: #141824;\n}\n.bb-sectionHead {\n padding: 24px 20px 16px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.07);\n}\n.bb-sectionKicker {\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n color: #64748b;\n}\n.bb-sectionHeading {\n margin-top: 4px;\n font-size: 22px;\n font-weight: 700;\n line-height: 1.2;\n color: #f1f5f9;\n}\n.bb-search {\n padding: 14px 16px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.07);\n}\n.bb-input,\n.bb-select,\n.bb-textarea {\n width: 100%;\n border-radius: 10px;\n border: 1px solid rgba(255, 255, 255, 0.08);\n background: rgba(255, 255, 255, 0.05);\n color: #e2e8f0;\n padding: 10px 14px;\n font-size: 13px;\n line-height: 1.45;\n}\n.bb-input::placeholder,\n.bb-textarea::placeholder {\n color: #475569;\n}\n.bb-input--compact {\n width: 64px;\n max-width: 64px;\n flex: 0 0 auto;\n padding: 7px 4px;\n text-align: center;\n}\n.bb-input:focus,\n.bb-select:focus,\n.bb-textarea:focus {\n outline: none;\n border-color: rgba(255, 255, 255, 0.18);\n background: rgba(255, 255, 255, 0.08);\n box-shadow: none;\n}\n.bb-textarea {\n min-height: 110px;\n resize: vertical;\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n monospace;\n}\n.bb-nodeItems {\n min-height: 0;\n overflow-y: auto;\n padding: 10px 0;\n}\n.bb-nodeButton {\n width: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n padding: 12px 20px;\n margin-bottom: 0;\n text-align: left;\n cursor: pointer;\n transition: background-color 0.12s ease, color 0.12s ease;\n border-bottom: 1px solid rgba(255, 255, 255, 0.04);\n}\n.bb-nodeButton:hover {\n background: rgba(255, 255, 255, 0.05);\n}\n.bb-nodeButton--active {\n background: rgba(255, 255, 255, 0.05);\n box-shadow: none;\n}\n.bb-nodeName {\n display: block;\n margin-bottom: 2px;\n font-size: 14px;\n font-weight: 600;\n color: #e2e8f0;\n}\n.bb-nodeMeta {\n display: block;\n font-size: 11px;\n color: #94a3b8;\n}\n.bb-editor {\n flex: 1 1 auto;\n min-height: 0;\n height: 100%;\n overflow-y: auto;\n padding: 16px 16px 28px;\n overscroll-behavior: contain;\n}\n.bb-editorHeader {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 18px;\n padding: 18px;\n border-radius: 11px;\n background: rgba(255, 255, 255, 0.05);\n border: 1px solid rgba(148, 163, 184, 0.12);\n}\n.bb-editorTitle {\n margin: 8px 0 0;\n font-size: 24px;\n line-height: 1.1;\n color: #f8fafc;\n}\n.bb-editorMeta {\n display: flex;\n flex-wrap: wrap;\n gap: 8px 12px;\n margin-top: 10px;\n font-size: 12px;\n color: #94a3b8;\n}\n.bb-editorBadge {\n flex-shrink: 0;\n padding: 8px 12px;\n border-radius: 999px;\n background: rgba(251, 191, 36, 0.12);\n border: 1px solid rgba(251, 191, 36, 0.2);\n color: #fcd34d;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n}\n.bb-emptyEditor {\n padding: 28px 8px;\n font-size: 14px;\n color: #a7b1c3;\n}\n.bb-section {\n margin-bottom: 10px;\n border-radius: 7px;\n background: rgba(255, 255, 255, 0.03);\n border: 1px solid rgba(148, 163, 184, 0.12);\n overflow: hidden;\n}\n.bb-sectionHeaderButton {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border: none;\n background: rgba(255, 255, 255, 0.03);\n padding: 11px 14px;\n cursor: pointer;\n text-align: left;\n color: inherit;\n}\n.bb-sectionHeaderButton:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.bb-sectionHeaderMain {\n display: flex;\n min-width: 0;\n align-items: center;\n gap: 8px;\n}\n.bb-sectionChevron {\n flex-shrink: 0;\n color: #8b97ab;\n transform: rotate(-90deg);\n transition: transform 0.18s ease;\n}\n.bb-sectionChevron--open {\n transform: rotate(0deg);\n}\n.bb-sectionBody {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 14px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n}\n.bb-sectionTitle {\n margin: 0;\n font-size: 11px;\n font-weight: 700;\n color: #c3cbdb;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n}\n.bb-sectionHelp {\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n margin-left: 2px;\n border-radius: 999px;\n color: #b9c0ca;\n cursor: help;\n transition:\n background 0.18s ease,\n color 0.18s ease,\n transform 0.18s ease;\n}\n.bb-sectionHelp:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #eef2ff;\n transform: translateY(-0.5px);\n}\n.bb-grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 10px;\n}\n.bb-presetGallery {\n display: flex;\n justify-items: center;\n flex-wrap: wrap;\n gap: 4px;\n margin-bottom: 14px;\n}\n.bb-presetSwatch {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 5px;\n width: 64px;\n padding: 6px 4px;\n border-radius: 8px;\n border: 1px solid rgba(148, 163, 184, 0.18);\n background: rgba(148, 163, 184, 0.06);\n cursor: pointer;\n transition: border-color 0.15s, background 0.15s;\n}\n.bb-presetSwatch:hover:not(:disabled) {\n border-color: rgba(148, 163, 184, 0.4);\n background: rgba(148, 163, 184, 0.12);\n}\n.bb-presetSwatch:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.bb-presetSwatchColor {\n width: 32px;\n height: 32px;\n border-radius: 999px;\n border: 1px solid rgba(255, 255, 255, 0.18);\n}\n.bb-presetSwatchLabel {\n font-size: 10px;\n font-weight: 600;\n color: #cbd5e1;\n text-align: center;\n line-height: 1.2;\n}\n.bb-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n}\n.bb-field--full {\n grid-column: 1 / -1;\n}\n.bb-label {\n font-size: 12px;\n font-weight: 700;\n color: #d7dee9;\n}\n.bb-note {\n font-size: 11px;\n color: #8b97ab;\n}\n.bb-tierLock {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 16px 14px;\n border-radius: 7px;\n background: rgba(251, 191, 36, 0.08);\n border: 1px dashed rgba(251, 191, 36, 0.3);\n color: #fcd34d;\n}\n.bb-tierLockMessage {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n}\n.bb-root--licenseChecking,\n.bb-root--licenseInvalid {\n align-items: center;\n justify-content: center;\n}\n.bb-readonly {\n background: rgba(148, 163, 184, 0.08);\n color: #9ca3af;\n}\n.bb-checkboxRow {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n}\n.bb-checkbox {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 6px 5px;\n border-radius: 7px;\n background: rgba(15, 23, 42, 0.22);\n border: 1px solid rgba(148, 163, 184, 0.12);\n font-size: 13px;\n color: #d7dee9;\n}\n.bb-checkbox input {\n accent-color: #f59e0b;\n}\n.bb-sliderField {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.bb-slider {\n flex: 1 1 auto;\n min-width: 0;\n accent-color: #2aa8d8;\n}\n.bb-slider--unset {\n opacity: 0.45;\n}\n.bb-fileField {\n position: relative;\n display: flex;\n align-items: center;\n}\n.bb-fileField-input {\n width: 100%;\n padding-right: 40px;\n}\n.bb-fileFieldButton {\n position: absolute;\n right: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: none;\n border-radius: 5px;\n background: transparent;\n color: #aab4c5;\n cursor: pointer;\n}\n.bb-fileFieldButton:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #f8fafc;\n}\n.bb-materialPanel {\n margin-top: 8px;\n border: 1px solid rgba(148, 163, 184, 0.14);\n border-radius: 6px;\n background: rgba(15, 23, 42, 0.28);\n overflow: hidden;\n}\n.bb-materialPanel:first-of-type {\n margin-top: 0;\n}\n.bb-materialPanelHeader {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border: none;\n padding: 10px 12px;\n background: rgba(255, 255, 255, 0.03);\n cursor: pointer;\n text-align: left;\n color: inherit;\n}\n.bb-materialPanelHeader:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.bb-materialPanelTitleWrap {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n color: #dbe4f0;\n}\n.bb-materialPanelChevron {\n flex-shrink: 0;\n color: #8b97ab;\n transform: rotate(-90deg);\n transition: transform 0.18s ease;\n}\n.bb-materialPanelChevron--open {\n transform: rotate(0deg);\n}\n.bb-materialPanelTitle {\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n}\n.bb-materialPanelHelp {\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n margin-left: 2px;\n border-radius: 999px;\n color: #b9c0ca;\n cursor: help;\n transition:\n background 0.18s ease,\n color 0.18s ease,\n transform 0.18s ease;\n}\n.bb-materialPanelHelp:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #eef2ff;\n transform: translateY(-0.5px);\n}\n.bb-materialPanelBody {\n padding: 12px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.bb-actionList {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-actionCard {\n border: 1px solid rgba(148, 163, 184, 0.14);\n border-radius: 9px;\n padding: 14px;\n background: rgba(15, 23, 42, 0.2);\n}\n.bb-actionCardHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n margin-bottom: 12px;\n}\n.bb-actionCardTitle {\n font-size: 13px;\n font-weight: 700;\n color: #f8fafc;\n}\n.bb-actionCardMeta {\n flex-shrink: 0;\n padding: 5px 9px;\n border-radius: 999px;\n background: rgba(148, 163, 184, 0.12);\n color: #94a3b8;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n}\n.bb-actionGrid {\n display: grid;\n grid-template-columns: 1fr;\n gap: 10px;\n}\n.bb-actionFooter {\n margin-top: 12px;\n}\n.bb-actionRemove {\n width: 100%;\n}\n.bb-previewShell {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n padding: 5px;\n overflow: hidden;\n}\n.bb-previewHeader {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n padding: 0 4px 10px;\n}\n.bb-previewControls {\n display: flex;\n align-items: flex-start;\n justify-content: flex-end;\n gap: 10px;\n flex-wrap: wrap;\n}\n.bb-previewTitle {\n margin: 2px 0 0;\n font-size: 20px;\n line-height: 1.05;\n letter-spacing: -0.03em;\n color: #1f2937;\n}\n.bb-previewToggles {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n}\n.bb-checkbox--compact {\n gap: 5px;\n padding: 0;\n background: transparent;\n border: none;\n font-size: 10px;\n font-weight: 500;\n line-height: 1;\n color: #4b5563;\n}\n.bb-checkbox--compact:hover {\n background: transparent;\n transform: none;\n}\n.bb-checkbox--compact input[type=checkbox] {\n width: 11px;\n height: 11px;\n}\n.bb-previewMeta {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n gap: 8px;\n}\n.bb-removeModelButton {\n background-color: rgb(174, 54, 54);\n min-height: 34px;\n padding: 0 14px;\n}\n.bb-removeModelButton:hover {\n background-color: rgb(163, 20, 20);\n}\n.bb-previewMeta span {\n display: inline-flex;\n align-items: center;\n min-height: 34px;\n padding: 0 12px;\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.7);\n border: 1px solid rgba(148, 163, 184, 0.18);\n color: #475569;\n font-size: 12px;\n font-weight: 700;\n}\n.bb-preview {\n position: relative;\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n border-radius: 14px;\n background:\n radial-gradient(\n circle at top left,\n rgba(255, 255, 255, 0.92),\n rgba(236, 240, 244, 0.82)),\n #f8fafc;\n border: 1px solid rgba(148, 163, 184, 0.24);\n box-shadow: 0 24px 48px rgba(148, 163, 184, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.7);\n}\n.bb-loadingOverlay {\n position: absolute;\n inset: 0;\n z-index: 5;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n padding: 24px;\n text-align: center;\n border-radius: 14px;\n background: rgba(248, 250, 252, 0.82);\n backdrop-filter: blur(2px);\n}\n.bb-loadingSpinner {\n width: 40px;\n height: 40px;\n border-radius: 999px;\n border: 3px solid rgba(148, 163, 184, 0.3);\n border-top-color: #2563eb;\n animation: bb-spin 0.8s linear infinite;\n}\n@keyframes bb-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.bb-loadingMessage {\n max-width: 320px;\n color: #475569;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.5;\n}\n.bb-previewStatus {\n max-width: 280px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bb-previewStatus--success {\n background: rgba(34, 197, 94, 0.12) !important;\n border-color: rgba(34, 197, 94, 0.3) !important;\n color: #15803d !important;\n}\n.bb-previewStatus--error {\n background: rgba(239, 68, 68, 0.12) !important;\n border-color: rgba(239, 68, 68, 0.3) !important;\n color: #b91c1c !important;\n}\n.bb-previewModelName {\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bb-previewPlaceholder {\n position: relative;\n display: flex;\n height: 100%;\n width: 100%;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: #64748b;\n font-size: 15px;\n line-height: 1.7;\n border-radius: 14px;\n border: 2px dashed rgba(148, 163, 184, 0.24);\n background:\n radial-gradient(\n circle at top,\n rgba(251, 191, 36, 0.12),\n transparent 30%),\n linear-gradient(\n 180deg,\n rgba(255, 255, 255, 0.82),\n rgba(238, 242, 248, 0.9));\n transition:\n border-color 0.2s ease,\n background 0.2s ease,\n transform 0.2s ease,\n box-shadow 0.2s ease;\n}\n.bb-previewPlaceholder > div {\n max-width: 420px;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 14px;\n}\n.bb-previewPlaceholderTitle {\n font-size: 28px;\n line-height: 1.05;\n letter-spacing: -0.03em;\n font-weight: 800;\n color: #1f2937;\n}\n.bb-previewPlaceholderButton {\n margin-top: 8px;\n border: 0;\n border-radius: 999px;\n padding: 13px 22px;\n background: #135bc0;\n color: #ffffff;\n font-size: 14px;\n font-weight: 800;\n cursor: pointer;\n transition:\n transform 0.18s ease,\n box-shadow 0.18s ease,\n opacity 0.18s ease;\n}\n.bb-previewPlaceholderButton:hover:not(:disabled) {\n transform: translateY(-1px);\n box-shadow: 0 14px 30px rgba(19, 91, 192, 0.28);\n}\n.bb-previewPlaceholderButton:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n.bb-previewPlaceholder--dragActive {\n border-color: rgba(245, 158, 11, 0.75);\n background:\n radial-gradient(\n circle at top,\n rgba(251, 191, 36, 0.22),\n transparent 32%),\n linear-gradient(\n 180deg,\n rgba(255, 252, 245, 0.95),\n rgba(255, 244, 230, 0.92));\n transform: scale(0.995);\n box-shadow: inset 0 0 0 1px rgba(245, 158, 11, 0.14);\n}\n.bb-sectionHeader {\n cursor: pointer;\n user-select: none;\n}\n.bb-iconButton {\n background: none;\n border: none;\n cursor: pointer;\n font-size: 16px;\n color: var(--bb-sidebar-subtle-text);\n transition: color 0.2s ease;\n padding: 4px 8px;\n border-radius: 4px;\n}\n.bb-iconButton:hover {\n color: var(--bb-sidebar-text);\n background: var(--bb-sidebar-surface-strong);\n}\n.bb-cameraLive,\n.bb-cameraSaved {\n background: rgba(15, 23, 42, 0.44);\n border-radius: 8px;\n padding: 14px;\n margin-bottom: 16px;\n border: 1px solid rgba(148, 163, 184, 0.15);\n transition: all 0.2s ease;\n}\n.bb-cameraLive:hover,\n.bb-cameraSaved:hover {\n border-color: rgba(148, 163, 184, 0.25);\n background: rgba(15, 23, 42, 0.52);\n}\n.bb-cameraLiveHeader,\n.bb-cameraSavedHeader {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n padding-bottom: 8px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.15);\n}\n.bb-cameraLiveBadge {\n background:\n linear-gradient(\n 135deg,\n #10b981,\n #059669);\n color: white;\n font-size: 10px;\n font-weight: 700;\n padding: 3px 8px;\n border-radius: 10px;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n}\n.bb-cameraSavedBadge {\n background:\n linear-gradient(\n 135deg,\n #3b82f6,\n #2563eb);\n color: white;\n font-size: 10px;\n font-weight: 700;\n padding: 3px 8px;\n border-radius: 10px;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n}\n.bb-cameraLiveLabel,\n.bb-cameraSavedLabel {\n font-size: 12px;\n font-weight: 600;\n color: #cbd5e1;\n}\n.bb-cameraValues {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.bb-cameraRow {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n flex-wrap: wrap;\n}\n.bb-cameraLabel {\n font-weight: 600;\n color: #94a3b8;\n min-width: 58px;\n font-size: 11px;\n letter-spacing: 0.02em;\n}\n.bb-cameraCode {\n font-family:\n "Monaco",\n "Menlo",\n "Ubuntu Mono",\n "Consolas",\n monospace;\n font-size: 11px;\n background: rgba(0, 0, 0, 0.3);\n padding: 3px 8px;\n border-radius: 4px;\n color: #fcd34d;\n letter-spacing: 0.02em;\n word-break: break-all;\n}\n.bb-cameraPlaceholder {\n color: #94a3b8;\n font-size: 12px;\n text-align: center;\n padding: 16px;\n font-style: italic;\n}\n.bb-cameraControls {\n display: flex;\n gap: 10px;\n margin-bottom: 14px;\n flex-wrap: wrap;\n}\n.bb-button--primary {\n background: #135bc0;\n border-color: transparent;\n color: #ffffff;\n}\n.bb-button--primary:hover:not(:disabled) {\n background: #1a68d6;\n transform: translateY(-1px);\n}\n.bb-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n}\n.bb-cameraMessage {\n padding: 10px 14px;\n border-radius: 6px;\n margin-bottom: 14px;\n font-size: 12px;\n font-weight: 500;\n animation: slideIn 0.3s ease-out;\n}\n.bb-cameraMessage--success {\n background: rgba(16, 185, 129, 0.15);\n color: #6ee7b7;\n border: 1px solid rgba(16, 185, 129, 0.3);\n}\n.bb-cameraMessage--error {\n background: rgba(239, 68, 68, 0.15);\n color: #ffe5e55c;\n border: 1px solid rgba(255, 248, 248, 0.3);\n}\n@keyframes slideIn {\n from {\n opacity: 0;\n transform: translateY(-8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n.bb-readonly {\n background: rgba(148, 163, 184, 0.08);\n color: #9ca3af;\n cursor: default;\n}\n@media (max-width: 1280px) {\n .bb-cameraRow {\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n }\n .bb-cameraLabel {\n min-width: auto;\n }\n .bb-cameraControls {\n flex-direction: column;\n }\n .bb-cameraControls .bb-button {\n width: 100%;\n }\n}\n@media (max-width: 980px) {\n .bb-sidebar--left {\n width: 100%;\n max-height: 42vh;\n }\n .bb-sidebar--collapsed {\n width: 100%;\n }\n .bb-previewShell {\n padding: 14px;\n }\n .bb-preview {\n min-height: 44vh;\n }\n .bb-previewHeader {\n flex-direction: column;\n align-items: flex-start;\n }\n .bb-previewControls {\n width: 100%;\n justify-content: flex-start;\n }\n .bb-previewMeta {\n justify-content: flex-start;\n }\n .bb-grid,\n .bb-actionGrid,\n .bb-statRow {\n grid-template-columns: 1fr;\n }\n}\n.bb-annotationsInfo {\n margin-top: 8px;\n}\n.bb-emptyAnnotations {\n padding: 32px;\n text-align: center;\n background: #f5f5f5;\n border-radius: 4px;\n color: #666;\n font-size: 14px;\n}\n.bb-annotationsList {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-annotationCard {\n background: #f9f9f9;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n padding: 12px;\n transition: all 0.2s ease;\n}\n.bb-annotationCard:hover {\n background: #ffffff;\n border-color: #4caf50;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n}\n.bb-annotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.bb-annotationBadge {\n background: #4caf50;\n color: white;\n padding: 2px 8px;\n border-radius: 2px;\n font-size: 11px;\n font-weight: 600;\n}\n.bb-annotationActions {\n display: flex;\n gap: 8px;\n}\n.bb-annotationEdit,\n.bb-annotationDelete {\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 4px;\n border-radius: 2px;\n font-size: 14px;\n transition: all 0.2s ease;\n}\n.bb-annotationEdit:hover {\n background: #e8f5e9;\n transform: scale(1.05);\n}\n.bb-annotationDelete:hover {\n background: #ffebee;\n transform: scale(1.05);\n}\n.bb-annotationTitle {\n font-weight: 600;\n font-size: 14px;\n color: #333;\n margin-bottom: 6px;\n}\n.bb-annotationDescription {\n font-size: 13px;\n color: #666;\n margin-bottom: 8px;\n line-height: 1.4;\n}\n.bb-annotationPosition {\n font-size: 11px;\n font-family: monospace;\n color: #999;\n background: #f0f0f0;\n padding: 4px 6px;\n border-radius: 2px;\n display: inline-block;\n}\n.bb-modalOverlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n}\n.bb-modal {\n background: white;\n border-radius: 6px;\n width: 500px;\n max-width: 90%;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n animation: modalSlideIn 0.2s ease;\n}\n@keyframes modalSlideIn {\n from {\n transform: translateY(-20px);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n.bb-modalHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid #e0e0e0;\n}\n.bb-modalHeader h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n}\n.bb-modalClose {\n background: transparent;\n border: none;\n font-size: 20px;\n cursor: pointer;\n color: #999;\n transition: color 0.2s ease;\n}\n.bb-modalClose:hover {\n color: #333;\n}\n.bb-modalBody {\n padding: 20px;\n}\n.bb-modalFooter {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 16px 20px;\n border-top: 1px solid #e0e0e0;\n}\n.bb-annotationsInfo {\n margin-top: 8px;\n}\n.bb-emptyAnnotations {\n padding: 32px;\n text-align: center;\n background: rgba(15, 23, 42, 0.44);\n border-radius: 10px;\n border: 1px solid rgba(148, 163, 184, 0.15);\n color: #94a3b8;\n font-size: 14px;\n}\n.bb-annotationsList {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-annotationCard {\n background: rgba(15, 23, 42, 0.44);\n border-left: 4px solid #22d3ee;\n border-radius: 8px;\n padding: 16px;\n transition: all 0.2s ease;\n border: 1px solid rgba(148, 163, 184, 0.15);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);\n}\n.bb-annotationCard:hover {\n transform: translateX(4px);\n background: rgba(15, 23, 42, 0.6);\n border-color: rgba(148, 163, 184, 0.25);\n box-shadow: 0 4px 12px rgba(15, 23, 42, 0.3);\n}\n.bb-annotationBadge {\n display: inline-block;\n background: #22d3ee;\n color: #06343c;\n padding: 2px 10px;\n border-radius: 10px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.5px;\n margin-bottom: 12px;\n text-transform: uppercase;\n}\n.bb-annotationTitle {\n font-weight: 700;\n font-size: 15px;\n color: #f8fafc;\n margin-bottom: 8px;\n line-height: 1.3;\n}\n.bb-annotationDescription {\n font-size: 13px;\n color: #cbd5e1;\n margin-bottom: 12px;\n line-height: 1.5;\n word-wrap: break-word;\n white-space: pre-wrap;\n}\n.bb-annotationPosition {\n font-size: 11px;\n font-family:\n "Monaco",\n "Menlo",\n "Ubuntu Mono",\n "Consolas",\n monospace;\n color: #94a3b8;\n background: rgba(0, 0, 0, 0.3);\n padding: 4px 8px;\n border-radius: 4px;\n display: inline-block;\n margin-top: 4px;\n letter-spacing: 0.02em;\n}\n.bb-annotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.bb-annotationActions {\n display: flex;\n gap: 4px;\n}\n.bb-annotationAction {\n background: none;\n border: none;\n color: #94a3b8;\n cursor: pointer;\n padding: 4px;\n border-radius: 2px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n}\n.bb-annotationAction--edit:hover {\n border-radius: 50%;\n background: rgba(59, 130, 246, 0.2);\n color: #3b82f6;\n}\n.bb-annotationAction--delete:hover {\n border-radius: 50%;\n background: rgba(239, 68, 68, 0.2);\n color: #ef4444;\n}\n.bb-editSpotOverlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n}\n.bb-editSpotPopup {\n background:\n linear-gradient(\n 180deg,\n rgba(20, 27, 45, 0.98),\n rgba(25, 34, 57, 0.95)),\n #121826;\n border-radius: 8px;\n padding: 24px;\n min-width: 400px;\n max-width: 500px;\n border: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.3);\n}\n.bb-editSpotHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n}\n.bb-editSpotTitle {\n color: #f8fafc;\n font-size: 18px;\n font-weight: 600;\n margin: 0;\n}\n.bb-editSpotClose {\n background: none;\n border: none;\n color: #94a3b8;\n font-size: 24px;\n cursor: pointer;\n padding: 0 4px;\n transition: color 0.2s ease;\n}\n.bb-editSpotClose:hover {\n color: #f8fafc;\n}\n.bb-editSpotBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-editSpotBody .bb-input,\n.bb-editSpotBody .bb-textarea {\n background: rgba(15, 23, 42, 0.4);\n border-color: rgba(148, 163, 184, 0.2);\n color: #f8fafc;\n}\n.bb-editSpotBody .bb-input:focus,\n.bb-editSpotBody .bb-textarea:focus {\n border-color: rgba(251, 191, 36, 0.65);\n box-shadow: 0 0 0 4px rgba(251, 191, 36, 0.12);\n}\n.bb-editSpotBody .bb-label {\n color: #d7dee9;\n}\n.bb-editSpotActions {\n display: flex;\n gap: 8px;\n margin-top: 8px;\n}\n.bb-editSpotActions .bb-button {\n background: rgba(255, 255, 255, 0.04);\n border: 1px solid rgba(148, 163, 184, 0.18);\n color: #e5edf7;\n}\n.bb-editSpotActions .bb-button:hover {\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.32);\n transform: translateY(-1px);\n}\n.bb-button--danger {\n background: #ef4444;\n color: white;\n border: none;\n padding: 8px 16px;\n border-radius: 7px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 700;\n display: flex;\n align-items: center;\n gap: 4px;\n transition: all 0.2s ease;\n}\n.bb-button--danger:hover {\n background: #dc2626;\n transform: translateY(-1px);\n box-shadow: 0 10px 18px rgba(239, 68, 68, 0.25);\n}\n.bb-button--primary {\n background: #135bc0;\n border-color: transparent;\n color: #ffffff;\n}\n.bb-button--primary:hover {\n background: #1a68d6;\n}\n.bb-annotationCard {\n background: rgba(15, 23, 42, 0.44);\n border-left: 4px solid #22d3ee;\n border-radius: 8px;\n padding: 16px;\n transition: all 0.2s ease;\n border: 1px solid rgba(148, 163, 184, 0.15);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);\n}\n.bb-annotationCard:hover {\n transform: translateX(4px);\n background: rgba(15, 23, 42, 0.6);\n border-color: rgba(148, 163, 184, 0.25);\n box-shadow: 0 4px 12px rgba(15, 23, 42, 0.3);\n}\n.bb-annotationBadge {\n display: inline-block;\n background: #22d3ee;\n color: #06343c;\n padding: 2px 10px;\n border-radius: 10px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.5px;\n margin-bottom: 12px;\n text-transform: uppercase;\n}\n.bb-annotationTitle {\n font-weight: 700;\n font-size: 15px;\n color: #f8fafc;\n margin-bottom: 8px;\n line-height: 1.3;\n}\n.bb-annotationDescription {\n font-size: 13px;\n color: #cbd5e1;\n margin-bottom: 12px;\n line-height: 1.5;\n word-wrap: break-word;\n white-space: pre-wrap;\n}\n.bb-annotationPosition {\n font-size: 11px;\n font-family:\n "Monaco",\n "Menlo",\n "Ubuntu Mono",\n "Consolas",\n monospace;\n color: #94a3b8;\n background: rgba(0, 0, 0, 0.3);\n padding: 4px 8px;\n border-radius: 4px;\n display: inline-block;\n margin-top: 4px;\n letter-spacing: 0.02em;\n}\n.bb-emptyAnnotations {\n padding: 32px;\n text-align: center;\n background: rgba(15, 23, 42, 0.44);\n border-radius: 10px;\n border: 1px solid rgba(148, 163, 184, 0.15);\n color: #94a3b8;\n font-size: 14px;\n}\n@media (max-width: 1280px) {\n .bb-cameraRow {\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n }\n .bb-cameraLabel {\n min-width: auto;\n }\n .bb-cameraControls {\n flex-direction: column;\n }\n .bb-cameraControls .bb-button {\n width: 100%;\n }\n}\n@media (max-width: 1120px) {\n .bb-sidebar--left {\n width: 320px;\n }\n .bb-sidebar--right {\n width: 220px;\n }\n .bb-grid {\n grid-template-columns: 1fr;\n }\n .bb-statRow {\n grid-template-columns: 1fr;\n }\n}\n@media (max-width: 980px) {\n .bb-root {\n flex-direction: column;\n height: auto;\n min-height: 0;\n }\n .bb-sidebar--left {\n width: 100% !important;\n max-height: 45vh;\n border-right: none;\n border-bottom: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 18px 42px rgba(15, 23, 42, 0.12);\n }\n .bb-sidebar--right {\n width: 100% !important;\n max-height: 45vh;\n border-left: none;\n border-top: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 -18px 42px rgba(15, 23, 42, 0.12);\n order: 3;\n }\n .bb-sidebar--collapsed {\n width: 100% !important;\n max-height: 72px;\n overflow: hidden;\n }\n .bb-sidebar--collapsed .bb-nodeList,\n .bb-sidebar--collapsed .bb-tabRow,\n .bb-sidebar--collapsed .bb-toolbar {\n display: none;\n }\n .bb-previewShell {\n flex: 1 1 auto;\n min-height: 50vh;\n padding: 14px;\n order: 2;\n }\n .bb-preview {\n min-height: 44vh;\n border-radius: 10px;\n }\n .bb-previewHeader {\n flex-direction: column;\n align-items: flex-start;\n gap: 12px;\n }\n .bb-previewControls {\n width: 100%;\n justify-content: flex-start;\n flex-wrap: wrap;\n }\n .bb-previewMeta {\n justify-content: flex-start;\n }\n .bb-previewMeta span {\n font-size: 11px;\n padding: 0 10px;\n min-height: 30px;\n }\n .bb-grid,\n .bb-actionGrid,\n .bb-statRow {\n grid-template-columns: 1fr;\n }\n .bb-editor {\n padding: 12px 12px 20px;\n }\n .bb-editorHeader {\n flex-direction: column;\n align-items: stretch;\n padding: 14px;\n }\n .bb-editorTitle {\n font-size: 20px;\n }\n .bb-editorBadge {\n align-self: flex-start;\n }\n .bb-toolbar {\n flex-direction: column;\n gap: 12px;\n padding: 16px 20px 18px;\n }\n .bb-toolbarDivider {\n width: 100%;\n height: 1px;\n margin: 0;\n }\n .bb-tabRow {\n padding: 12px 16px 0;\n gap: 6px;\n }\n .bb-tab {\n font-size: 10px;\n padding: 8px 10px;\n }\n .bb-header {\n padding: 14px 16px 12px;\n }\n .bb-header--sidebar {\n min-height: 60px;\n }\n .bb-title {\n font-size: 17px;\n }\n .bb-subtitle {\n font-size: 13px;\n }\n .bb-statChip {\n padding: 10px 12px;\n }\n .bb-statChip strong {\n font-size: 12px;\n }\n .bb-nodeButton {\n padding: 10px 12px;\n }\n .bb-nodeName {\n font-size: 12px;\n }\n .bb-nodeMeta {\n font-size: 10px;\n }\n .bb-section {\n padding: 14px;\n }\n .bb-sectionTitle {\n font-size: 9px;\n }\n .bb-field {\n gap: 4px;\n }\n .bb-label {\n font-size: 11px;\n }\n .bb-input,\n .bb-select,\n .bb-textarea {\n padding: 9px 11px;\n font-size: 12px;\n }\n .bb-textarea {\n min-height: 80px;\n }\n .bb-button,\n .bb-fileLabel {\n min-height: 36px;\n padding: 0 14px;\n font-size: 12px;\n }\n .bb-previewPlaceholderTitle {\n font-size: 22px;\n }\n .bb-previewPlaceholderButton {\n padding: 10px 18px;\n font-size: 13px;\n }\n .bb-editSpotPopup {\n min-width: 90%;\n max-width: 90%;\n padding: 20px;\n }\n .bb-modal {\n width: 95%;\n max-width: 95%;\n }\n .bb-annotationCard {\n padding: 12px;\n }\n .bb-annotationTitle {\n font-size: 14px;\n }\n .bb-annotationDescription {\n font-size: 12px;\n }\n}\n@media (max-width: 640px) {\n .bb-root {\n flex-direction: column;\n height: auto;\n min-height: 0;\n }\n .bb-sidebar--left {\n max-height: 40vh;\n width: 100% !important;\n }\n .bb-sidebar--right {\n max-height: 40vh;\n width: 100% !important;\n }\n .bb-previewShell {\n min-height: 40vh;\n padding: 10px;\n }\n .bb-preview {\n min-height: 35vh;\n border-radius: 8px;\n }\n .bb-previewHeader {\n flex-direction: column;\n align-items: flex-start;\n gap: 8px;\n padding: 0 0 12px;\n }\n .bb-previewTitle {\n font-size: 17px;\n }\n .bb-previewControls {\n gap: 6px;\n }\n .bb-previewMeta span {\n font-size: 10px;\n padding: 0 8px;\n min-height: 26px;\n }\n .bb-editor {\n padding: 10px 10px 16px;\n }\n .bb-editorHeader {\n padding: 12px;\n flex-direction: column;\n align-items: stretch;\n }\n .bb-editorTitle {\n font-size: 18px;\n }\n .bb-editorMeta {\n font-size: 11px;\n gap: 6px 8px;\n }\n .bb-editorBadge {\n font-size: 9px;\n padding: 4px 10px;\n align-self: flex-start;\n }\n .bb-section {\n padding: 12px;\n margin-bottom: 12px;\n }\n .bb-sectionTitle {\n font-size: 9px;\n margin-bottom: 10px;\n }\n .bb-grid {\n gap: 8px;\n }\n .bb-field--full {\n grid-column: 1 / -1;\n }\n .bb-label {\n font-size: 11px;\n }\n .bb-input,\n .bb-select,\n .bb-textarea {\n padding: 8px 10px;\n font-size: 12px;\n border-radius: 6px;\n }\n .bb-textarea {\n min-height: 70px;\n }\n .bb-button,\n .bb-fileLabel {\n min-height: 34px;\n padding: 0 12px;\n font-size: 11px;\n border-radius: 6px;\n }\n .bb-toolbar {\n padding: 12px 14px 14px;\n gap: 10px;\n }\n .bb-tabRow {\n padding: 10px 12px 0;\n gap: 4px;\n }\n .bb-tab {\n font-size: 9px;\n padding: 6px 8px;\n border-radius: 4px;\n }\n .bb-header {\n padding: 10px 12px 8px;\n }\n .bb-header--sidebar {\n min-height: 52px;\n }\n .bb-title {\n font-size: 15px;\n }\n .bb-subtitle {\n font-size: 12px;\n }\n .bb-eyebrow {\n font-size: 9px;\n padding: 4px 8px;\n margin-bottom: 8px;\n }\n .bb-statRow {\n gap: 6px;\n margin-top: 14px;\n }\n .bb-statChip {\n padding: 8px 10px;\n border-radius: 6px;\n }\n .bb-statChip strong {\n font-size: 11px;\n }\n .bb-statLabel {\n font-size: 9px;\n }\n .bb-sidebarToggle {\n width: 30px;\n height: 30px;\n font-size: 14px;\n border-radius: 5px;\n }\n .bb-nodeItems {\n padding: 4px 6px;\n }\n .bb-nodeButton {\n padding: 8px 10px;\n border-radius: 6px;\n margin-bottom: 4px;\n }\n .bb-nodeName {\n font-size: 11px;\n }\n .bb-nodeMeta {\n font-size: 10px;\n }\n .bb-search {\n padding: 8px 10px;\n }\n .bb-statusCard {\n padding: 10px 12px;\n border-radius: 7px;\n }\n .bb-status {\n font-size: 12px;\n }\n .bb-statusTitle {\n font-size: 10px;\n }\n .bb-checkboxRow {\n gap: 6px;\n }\n .bb-checkbox {\n padding: 6px 10px;\n font-size: 12px;\n border-radius: 5px;\n }\n .bb-actionCard {\n padding: 10px;\n border-radius: 7px;\n }\n .bb-actionCardTitle {\n font-size: 12px;\n }\n .bb-actionCardMeta {\n font-size: 9px;\n padding: 3px 7px;\n }\n .bb-previewPlaceholder {\n padding: 24px;\n border-radius: 8px;\n }\n .bb-previewPlaceholderTitle {\n font-size: 18px;\n }\n .bb-previewPlaceholderButton {\n padding: 8px 14px;\n font-size: 12px;\n border-radius: 999px;\n }\n .bb-editSpotPopup {\n min-width: 95%;\n max-width: 95%;\n padding: 16px;\n border-radius: 6px;\n }\n .bb-editSpotTitle {\n font-size: 16px;\n }\n .bb-editSpotActions .bb-button {\n padding: 8px 12px;\n font-size: 12px;\n }\n .bb-modal {\n width: 98%;\n max-width: 98%;\n border-radius: 5px;\n }\n .bb-modalHeader {\n padding: 12px 16px;\n }\n .bb-modalHeader h3 {\n font-size: 16px;\n }\n .bb-modalBody {\n padding: 14px;\n }\n .bb-modalFooter {\n padding: 12px 16px;\n flex-direction: column;\n gap: 8px;\n }\n .bb-modalFooter .bb-button {\n width: 100%;\n justify-content: center;\n }\n .bb-annotationCard {\n padding: 10px;\n border-left-width: 3px;\n border-radius: 6px;\n }\n .bb-annotationTitle {\n font-size: 13px;\n }\n .bb-annotationDescription {\n font-size: 11px;\n }\n .bb-annotationPosition {\n font-size: 10px;\n padding: 3px 6px;\n }\n .bb-annotationBadge {\n font-size: 9px;\n padding: 2px 8px;\n }\n .bb-emptyAnnotations {\n padding: 20px;\n font-size: 12px;\n border-radius: 7px;\n }\n .bb-removeModelButton {\n min-height: 30px;\n padding: 0 10px;\n font-size: 11px;\n }\n .bb-annotationsList {\n gap: 8px;\n }\n .bb-cameraLive,\n .bb-cameraSaved {\n padding: 10px;\n border-radius: 6px;\n }\n .bb-cameraLiveHeader,\n .bb-cameraSavedHeader {\n flex-wrap: wrap;\n }\n .bb-cameraLiveLabel,\n .bb-cameraSavedLabel {\n font-size: 11px;\n }\n .bb-cameraRow {\n font-size: 11px;\n gap: 4px;\n }\n .bb-cameraCode {\n font-size: 10px;\n padding: 2px 6px;\n }\n .bb-cameraControls {\n flex-direction: column;\n gap: 6px;\n }\n .bb-cameraControls .bb-button {\n width: 100%;\n justify-content: center;\n }\n .bb-cameraMessage {\n font-size: 11px;\n padding: 8px 12px;\n }\n .bb-readonly {\n font-size: 11px;\n padding: 6px 8px;\n }\n}\n@media (max-width: 420px) {\n .bb-sidebar--left {\n max-height: 35vh;\n }\n .bb-sidebar--right {\n max-height: 35vh;\n }\n .bb-previewShell {\n min-height: 35vh;\n padding: 6px;\n }\n .bb-preview {\n min-height: 30vh;\n border-radius: 6px;\n }\n .bb-previewTitle {\n font-size: 15px;\n }\n .bb-previewMeta span {\n font-size: 9px;\n padding: 0 6px;\n min-height: 22px;\n }\n .bb-editorTitle {\n font-size: 16px;\n }\n .bb-title {\n font-size: 13px;\n }\n .bb-tab {\n font-size: 8px;\n padding: 5px 6px;\n }\n .bb-button,\n .bb-fileLabel {\n font-size: 10px;\n min-height: 30px;\n padding: 0 10px;\n }\n .bb-input,\n .bb-select,\n .bb-textarea {\n font-size: 11px;\n padding: 6px 8px;\n }\n .bb-statChip {\n padding: 6px 8px;\n }\n .bb-statChip strong {\n font-size: 10px;\n }\n .bb-previewPlaceholderTitle {\n font-size: 15px;\n }\n .bb-previewPlaceholderButton {\n font-size: 11px;\n padding: 6px 12px;\n }\n .bb-editSpotPopup {\n padding: 14px;\n border-radius: 5px;\n }\n .bb-editSpotTitle {\n font-size: 14px;\n }\n .bb-editSpotBody .bb-input,\n .bb-editSpotBody .bb-textarea {\n font-size: 12px;\n padding: 6px 8px;\n }\n .bb-modalHeader h3 {\n font-size: 14px;\n }\n .bb-annotationTitle {\n font-size: 12px;\n }\n}\n@media (max-width: 980px) {\n .bb-sidebar--collapsed .bb-header--sidebar {\n min-height: 52px;\n }\n .bb-sidebar--collapsed .bb-title {\n font-size: 14px;\n }\n .bb-sidebar--collapsed .bb-sidebarToggle {\n width: 28px;\n height: 28px;\n font-size: 12px;\n }\n}\n@media (hover: none) and (pointer: coarse) {\n .bb-nodeButton:hover,\n .bb-button:hover,\n .bb-fileLabel:hover,\n .bb-tab:hover,\n .bb-sidebarToggle:hover,\n .bb-annotationCard:hover {\n transform: none;\n }\n .bb-nodeButton:active,\n .bb-button:active,\n .bb-fileLabel:active,\n .bb-tab:active,\n .bb-sidebarToggle:active {\n transform: scale(0.97);\n transition: transform 0.1s ease;\n }\n}\n@media print {\n .bb-root {\n height: auto;\n min-height: 0;\n background: white;\n }\n .bb-sidebar--left,\n .bb-sidebar--right {\n width: 100% !important;\n max-height: none !important;\n background: white !important;\n color: black !important;\n border: 1px solid #ddd !important;\n box-shadow: none !important;\n }\n .bb-previewShell {\n min-height: auto !important;\n padding: 10px !important;\n }\n .bb-preview {\n min-height: auto !important;\n border-radius: 4px !important;\n border: 1px solid #ddd !important;\n box-shadow: none !important;\n }\n .bb-button,\n .bb-fileLabel,\n .bb-sidebarToggle,\n .bb-tab,\n .bb-annotationAction,\n .bb-iconButton {\n display: none !important;\n }\n .bb-nodeList {\n overflow: visible !important;\n }\n .bb-previewPlaceholderButton {\n display: none !important;\n }\n}\n.bb-checkboxRow {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n align-items: center;\n padding: 4px 0;\n}\n.bb-checkbox {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n border-radius: 7px;\n font-weight: 600;\n font-size: 13px;\n color: #f3eeee;\n cursor: pointer;\n user-select: none;\n transition: all 0.2s ease;\n}\n.bb-checkbox input[type=checkbox] {\n width: 16px;\n height: 16px;\n accent-color: #f59e0b;\n cursor: pointer;\n flex-shrink: 0;\n}\n.bb-editorDivider {\n height: 1px;\n background: rgba(148, 163, 184, 0.14);\n margin: 4px 0 16px 0;\n width: 100%;\n}\n.bb-preview {\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n.ri-viewer {\n width: 100% !important;\n height: 100% !important;\n position: relative !important;\n}\n.ri-buttonContainer {\n position: absolute !important;\n bottom: 20px !important;\n right: 20px !important;\n z-index: 10000 !important;\n display: flex !important;\n gap: 10px !important;\n pointer-events: auto !important;\n}\n.ri-downloadButton {\n display: flex !important;\n align-items: center !important;\n gap: 6px !important;\n padding: 10px 12px !important;\n background: #2f302f74 !important;\n color: white !important;\n border: none !important;\n border-radius: 6px !important;\n cursor: pointer !important;\n font-size: 12px !important;\n font-weight: 500 !important;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important;\n opacity: 1 !important;\n visibility: visible !important;\n}\n.ri-downloadButton svg {\n width: 14px !important;\n height: 14px !important;\n}\n.ri-resetButton {\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n padding: 6px !important;\n background: #2f302f74 !important;\n border: none !important;\n border-radius: 6px !important;\n cursor: pointer !important;\n opacity: 1 !important;\n visibility: visible !important;\n}\n.ri-resetButton svg {\n width: 14px !important;\n height: 14px !important;\n}\n.bb-reset-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n backdrop-filter: blur(4px);\n animation: bb-fade-in 0.2s ease;\n}\n.bb-reset-dialog {\n background: #1a1f2e;\n border-radius: 12px;\n max-width: 480px;\n width: 90%;\n border: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 24px 48px rgba(0, 0, 0, 0.5);\n animation: bb-slide-up 0.25s ease;\n}\n.bb-reset-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px 16px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.1);\n}\n.bb-reset-title {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 0;\n font-size: 18px;\n font-weight: 700;\n color: #f8fafc;\n}\n.bb-reset-title .bb-icon-warning {\n color: #f59e0b;\n flex-shrink: 0;\n}\n.bb-reset-close {\n background: transparent;\n border: none;\n color: #94a3b8;\n cursor: pointer;\n padding: 4px;\n border-radius: 4px;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.bb-reset-close:hover {\n color: #f8fafc;\n background: rgba(255, 255, 255, 0.06);\n}\n.bb-reset-body {\n padding: 20px 24px 24px;\n}\n.bb-reset-message {\n margin: 0 0 12px 0;\n font-size: 15px;\n color: #e2e8f0;\n line-height: 1.5;\n}\n.bb-reset-message strong {\n color: #f8fafc;\n}\n.bb-reset-details {\n color: #94a3b8;\n text-align: center;\n font-size: 13px;\n line-height: 1.6;\n margin: 0 0 12px 0;\n}\n.bb-reset-warning {\n display: flex;\n align-items: center;\n gap: 8px;\n color: #f59e0b;\n font-size: 13px;\n font-weight: 600;\n margin: 4px 0 0 0;\n padding: 10px 12px;\n background: rgba(245, 158, 11, 0.08);\n border-radius: 6px;\n border: 1px solid rgba(245, 158, 11, 0.15);\n}\n.bb-reset-footer {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n padding: 16px 24px 20px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n}\n.bb-reset-btn {\n padding: 8px 20px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n border: 1px solid rgba(148, 163, 184, 0.18);\n background: rgba(255, 255, 255, 0.04);\n color: #e2e8f0;\n}\n.bb-reset-btn:hover {\n background: rgba(255, 255, 255, 0.08);\n transform: translateY(-1px);\n}\n.bb-reset-btn-danger {\n padding: 8px 20px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n border: none;\n background: #dc2626;\n color: white;\n}\n.bb-reset-btn-danger:hover {\n background: #b91c1c;\n transform: translateY(-1px);\n box-shadow: 0 4px 12px rgba(220, 38, 38, 0.3);\n}\n@keyframes bb-fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n@keyframes bb-slide-up {\n from {\n opacity: 0;\n transform: translateY(20px) scale(0.96);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n');
8237
+ styleInject('.bb-root {\n --bb-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --bb-app-bg:\n radial-gradient(\n \n circle at top left,\n rgba(226, 232, 240, 0.8),\n transparent 30% ),\n linear-gradient(\n 135deg,\n #f6f1e8 0%,\n #ebe8df 40%,\n #e9edf2 100%);\n --bb-app-text: #111827;\n --bb-sidebar-bg:\n linear-gradient(\n 180deg,\n rgba(20, 27, 45, 0.98),\n rgba(25, 34, 57, 0.95)),\n #121826;\n --bb-sidebar-text: #f8fafc;\n --bb-sidebar-muted-text: #b3bfd2;\n --bb-sidebar-subtle-text: #94a3b8;\n --bb-sidebar-border: rgba(148, 163, 184, 0.18);\n --bb-sidebar-border-hover: rgba(148, 163, 184, 0.32);\n --bb-sidebar-divider: rgba(148, 163, 184, 0.14);\n --bb-sidebar-surface: rgba(255, 255, 255, 0.04);\n --bb-sidebar-surface-hover: rgba(255, 255, 255, 0.08);\n --bb-sidebar-surface-strong: rgba(255, 255, 255, 0.05);\n --bb-sidebar-eyebrow-bg: rgba(148, 163, 184, 0.16);\n --bb-sidebar-eyebrow-text: #cbd5e1;\n --bb-sidebar-left-shadow: 18px 0 42px rgba(15, 23, 42, 0.12);\n --bb-sidebar-right-shadow: -4px 0 24px rgba(0, 0, 0, 0.35);\n --bb-sidebar-chip-bg: rgba(15, 23, 42, 0.42);\n --bb-sidebar-chip-border: rgba(148, 163, 184, 0.14);\n --bb-sidebar-accent: rgba(99, 102, 241, 0.18);\n --bb-sidebar-accent-border: rgba(99, 102, 241, 0.4);\n --bb-sidebar-accent-text: #f8fafc;\n --bb-menu-bg: #1a1f2e;\n --bb-menu-shadow: 0 16px 32px rgba(15, 23, 42, 0.4);\n display: flex;\n height: 100%;\n min-height: 0;\n width: 100%;\n background: var(--bb-app-bg);\n color: var(--bb-app-text);\n font-family: var(--bb-font-family);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n font-synthesis: none;\n}\n.bb-root * {\n box-sizing: border-box;\n}\n.bb-root input,\n.bb-root select,\n.bb-root textarea,\n.bb-root button {\n font-family: inherit;\n}\n.bb-sidebar {\n width: 400px;\n min-height: 0;\n flex-shrink: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background: var(--bb-sidebar-bg);\n color: var(--bb-sidebar-text);\n transition: width 0.24s ease;\n}\n.bb-sidebar--left {\n border-right: 1px solid var(--bb-sidebar-border);\n box-shadow: var(--bb-sidebar-left-shadow);\n}\n.bb-sidebar--right {\n width: 300px;\n border-left: 1px solid var(--bb-sidebar-border);\n box-shadow: var(--bb-sidebar-right-shadow);\n}\n.bb-sidebar--collapsed {\n width: 36px;\n}\n.bb-sidebar--collapsed .bb-header {\n padding: 5px;\n display: flex;\n justify-content: center;\n}\n.bb-sidebar--collapsed .bb-header--sidebar {\n min-height: 36px;\n}\n.bb-sidebar--collapsed .bb-sidebarToggle {\n width: 26px;\n height: 26px;\n font-size: 12px;\n}\n.bb-header {\n padding: 5px 20px;\n border-bottom: 1px solid var(--bb-sidebar-divider);\n}\n.bb-header--sidebar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n min-height: 55px;\n}\n.bb-eyebrow {\n display: inline-block;\n margin-bottom: 12px;\n padding: 6px 10px;\n border-radius: 999px;\n background: var(--bb-sidebar-eyebrow-bg);\n color: var(--bb-sidebar-eyebrow-text);\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n}\n.bb-title {\n margin: 0 0 0px;\n font-size: 20px;\n line-height: 0.95;\n letter-spacing: -0.04em;\n font-weight: 800;\n color: var(--bb-sidebar-text);\n}\n.bb-sidebarToggle {\n width: 36px;\n height: 36px;\n flex-shrink: 0;\n border-radius: 6px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface-strong);\n color: var(--bb-sidebar-text);\n font-size: 16px;\n font-weight: 800;\n cursor: pointer;\n transition:\n transform 0.18s ease,\n background-color 0.18s ease,\n border-color 0.18s ease;\n}\n.bb-sidebarToggle:hover {\n transform: translateY(-1px);\n background: var(--bb-sidebar-surface-hover);\n border-color: var(--bb-sidebar-border-hover);\n}\n.bb-subtitle {\n margin: 0;\n max-width: 32ch;\n font-size: 15px;\n line-height: 1.7;\n color: var(--bb-sidebar-muted-text);\n}\n.bb-statRow {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 10px;\n margin-top: 22px;\n}\n.bb-statChip {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n padding: 12px 14px;\n border-radius: 8px;\n background: var(--bb-sidebar-chip-bg);\n border: 1px solid var(--bb-sidebar-chip-border);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);\n}\n.bb-statChip strong {\n font-size: 13px;\n color: var(--bb-sidebar-text);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.bb-statLabel {\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--bb-sidebar-subtle-text);\n}\n.bb-tabRow {\n display: flex;\n gap: 6px;\n padding: 10px 20px 10px;\n}\n.bb-tab {\n flex: 1;\n padding: 6px 10px;\n border-radius: 6px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface);\n color: var(--bb-sidebar-muted-text);\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n cursor: pointer;\n transition:\n background-color 0.18s ease,\n border-color 0.18s ease,\n color 0.18s ease;\n}\n.bb-tab:hover {\n background: var(--bb-sidebar-surface-hover);\n border-color: var(--bb-sidebar-border-hover);\n}\n.bb-tab--active {\n background: var(--bb-sidebar-accent);\n border-color: var(--bb-sidebar-accent-border);\n color: var(--bb-sidebar-accent-text);\n}\n.bb-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 10px;\n padding: 8px 20px 8px;\n border-bottom: 1px solid var(--bb-sidebar-divider);\n flex-wrap: wrap;\n}\n.bb-toolbarGroup {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n.bb-toolbarDivider {\n width: 1px;\n align-self: stretch;\n background: var(--bb-sidebar-divider);\n margin: 0;\n}\n.bb-iconButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border-radius: 7px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface);\n color: var(--bb-sidebar-text);\n cursor: pointer;\n transition: background-color 0.18s ease, border-color 0.18s ease;\n}\n.bb-iconButton:hover:not(:disabled) {\n background: var(--bb-sidebar-surface-hover);\n border-color: var(--bb-sidebar-border-hover);\n}\n.bb-iconButton:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.bb-exportMenuWrapper {\n position: relative;\n display: inline-flex;\n}\n.bb-exportMenu {\n position: absolute;\n bottom: calc(100% + 6px);\n right: 0;\n z-index: 20;\n display: flex;\n flex-direction: column;\n min-width: 150px;\n padding: 6px;\n border-radius: 9px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-menu-bg);\n box-shadow: var(--bb-menu-shadow);\n}\n.bb-exportMenuItem {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: var(--bb-sidebar-text);\n font-size: 12px;\n font-weight: 600;\n text-align: left;\n cursor: pointer;\n}\n.bb-exportMenuItem:hover {\n background: var(--bb-sidebar-surface-hover);\n}\n@media (max-width: 640px) {\n .bb-toolbarDivider {\n width: 100%;\n height: 1px;\n }\n}\n.bb-button,\n.bb-fileLabel {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n min-height: 32px;\n padding: 0 12px;\n border-radius: 7px;\n border: 1px solid var(--bb-sidebar-border);\n background: var(--bb-sidebar-surface);\n color: var(--bb-sidebar-text);\n font-size: 12px;\n font-weight: 700;\n cursor: pointer;\n transition:\n transform 0.18s ease,\n background-color 0.18s ease,\n border-color 0.18s ease,\n box-shadow 0.18s ease;\n}\n.bb-button:hover,\n.bb-fileLabel:hover {\n transform: translateY(-1px);\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.32);\n box-shadow: 0 10px 18px rgba(15, 23, 42, 0.18);\n}\n.bb-button:disabled,\n.bb-fileLabel:has(.bb-hiddenInput:disabled) {\n opacity: 0.55;\n cursor: not-allowed;\n transform: none;\n box-shadow: none;\n}\n.bb-button--primary {\n background: #135bc0;\n border-color: transparent;\n color: #ffffff;\n}\n.bb-button--primary:hover {\n background: #1a68d6;\n}\n.bb-button--ghost {\n background: rgba(248, 250, 252, 0.08);\n}\n.bb-hiddenInput {\n display: none;\n}\n.bb-statusCard {\n margin-top: 4px;\n padding: 14px 16px;\n border-radius: 9px;\n background: rgba(15, 23, 42, 0.44);\n border: 1px solid rgba(148, 163, 184, 0.15);\n}\n.bb-statusCard--error {\n background: rgba(127, 29, 29, 0.32);\n border-color: rgba(248, 113, 113, 0.24);\n}\n.bb-statusCard--success {\n background: rgba(6, 78, 59, 0.34);\n border-color: rgba(52, 211, 153, 0.24);\n}\n.bb-statusTitle {\n margin-bottom: 6px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: #cbd5e1;\n}\n.bb-status {\n font-size: 13px;\n line-height: 1.55;\n color: #e2e8f0;\n}\n.bb-status--error {\n color: #fecaca;\n}\n.bb-status--success {\n color: #bbf7d0;\n}\n.bb-input--error {\n border-color: #dc2626;\n}\n.bb-nodeList {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 0;\n overflow: hidden;\n background: #141824;\n}\n.bb-sectionHead {\n padding: 24px 20px 16px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.07);\n}\n.bb-sectionKicker {\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n color: #64748b;\n}\n.bb-sectionHeading {\n margin-top: 4px;\n font-size: 22px;\n font-weight: 700;\n line-height: 1.2;\n color: #f1f5f9;\n}\n.bb-search {\n padding: 14px 16px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.07);\n}\n.bb-input,\n.bb-select,\n.bb-textarea {\n width: 100%;\n border-radius: 10px;\n border: 1px solid rgba(255, 255, 255, 0.08);\n background: rgba(255, 255, 255, 0.05);\n color: #e2e8f0;\n padding: 10px 14px;\n font-size: 13px;\n line-height: 1.45;\n}\n.bb-input::placeholder,\n.bb-textarea::placeholder {\n color: #475569;\n}\n.bb-input--compact {\n width: 64px;\n max-width: 64px;\n flex: 0 0 auto;\n padding: 7px 4px;\n text-align: center;\n}\n.bb-input:focus,\n.bb-select:focus,\n.bb-textarea:focus {\n outline: none;\n border-color: rgba(255, 255, 255, 0.18);\n background: rgba(255, 255, 255, 0.08);\n box-shadow: none;\n}\n.bb-textarea {\n min-height: 110px;\n resize: vertical;\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n monospace;\n}\n.bb-nodeItems {\n min-height: 0;\n overflow-y: auto;\n padding: 10px 0;\n}\n.bb-nodeButton {\n width: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n padding: 12px 20px;\n margin-bottom: 0;\n text-align: left;\n cursor: pointer;\n transition: background-color 0.12s ease, color 0.12s ease;\n border-bottom: 1px solid rgba(255, 255, 255, 0.04);\n}\n.bb-nodeButton:hover {\n background: rgba(255, 255, 255, 0.05);\n}\n.bb-nodeButton--active {\n background: rgba(255, 255, 255, 0.05);\n box-shadow: none;\n}\n.bb-nodeName {\n display: block;\n margin-bottom: 2px;\n font-size: 14px;\n font-weight: 600;\n color: #e2e8f0;\n}\n.bb-nodeMeta {\n display: block;\n font-size: 11px;\n color: #94a3b8;\n}\n.bb-editor {\n flex: 1 1 auto;\n min-height: 0;\n height: 100%;\n overflow-y: auto;\n padding: 16px 16px 28px;\n overscroll-behavior: contain;\n}\n.bb-editorHeader {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 18px;\n padding: 18px;\n border-radius: 11px;\n background: rgba(255, 255, 255, 0.05);\n border: 1px solid rgba(148, 163, 184, 0.12);\n}\n.bb-editorTitle {\n margin: 8px 0 0;\n font-size: 24px;\n line-height: 1.1;\n color: #f8fafc;\n}\n.bb-editorMeta {\n display: flex;\n flex-wrap: wrap;\n gap: 8px 12px;\n margin-top: 10px;\n font-size: 12px;\n color: #94a3b8;\n}\n.bb-editorBadge {\n flex-shrink: 0;\n padding: 8px 12px;\n border-radius: 999px;\n background: rgba(251, 191, 36, 0.12);\n border: 1px solid rgba(251, 191, 36, 0.2);\n color: #fcd34d;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n}\n.bb-emptyEditor {\n padding: 28px 8px;\n font-size: 14px;\n color: #a7b1c3;\n}\n.bb-section {\n margin-bottom: 10px;\n border-radius: 7px;\n background: rgba(255, 255, 255, 0.03);\n border: 1px solid rgba(148, 163, 184, 0.12);\n overflow: hidden;\n}\n.bb-sectionHeaderButton {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border: none;\n background: rgba(255, 255, 255, 0.03);\n padding: 11px 14px;\n cursor: pointer;\n text-align: left;\n color: inherit;\n}\n.bb-sectionHeaderButton:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.bb-sectionHeaderMain {\n display: flex;\n min-width: 0;\n align-items: center;\n gap: 8px;\n}\n.bb-sectionChevron {\n flex-shrink: 0;\n color: #8b97ab;\n transform: rotate(-90deg);\n transition: transform 0.18s ease;\n}\n.bb-sectionChevron--open {\n transform: rotate(0deg);\n}\n.bb-sectionBody {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 14px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n}\n.bb-sectionTitle {\n margin: 0;\n font-size: 11px;\n font-weight: 700;\n color: #c3cbdb;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n}\n.bb-sectionHelp {\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n margin-left: 2px;\n border-radius: 999px;\n color: #b9c0ca;\n cursor: help;\n transition:\n background 0.18s ease,\n color 0.18s ease,\n transform 0.18s ease;\n}\n.bb-sectionHelp:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #eef2ff;\n transform: translateY(-0.5px);\n}\n.bb-grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 10px;\n}\n.bb-presetGallery {\n display: flex;\n justify-items: center;\n flex-wrap: wrap;\n gap: 4px;\n margin-bottom: 14px;\n}\n.bb-presetSwatch {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 5px;\n width: 64px;\n padding: 6px 4px;\n border-radius: 8px;\n border: 1px solid rgba(148, 163, 184, 0.18);\n background: rgba(148, 163, 184, 0.06);\n cursor: pointer;\n transition: border-color 0.15s, background 0.15s;\n}\n.bb-presetSwatch:hover:not(:disabled) {\n border-color: rgba(148, 163, 184, 0.4);\n background: rgba(148, 163, 184, 0.12);\n}\n.bb-presetSwatch:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.bb-presetSwatchColor {\n width: 32px;\n height: 32px;\n border-radius: 999px;\n border: 1px solid rgba(255, 255, 255, 0.18);\n}\n.bb-presetSwatchLabel {\n font-size: 10px;\n font-weight: 600;\n color: #cbd5e1;\n text-align: center;\n line-height: 1.2;\n}\n.bb-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n}\n.bb-field--full {\n grid-column: 1 / -1;\n}\n.bb-label {\n font-size: 12px;\n font-weight: 700;\n color: #d7dee9;\n}\n.bb-note {\n font-size: 11px;\n color: #8b97ab;\n}\n.bb-linkButton {\n padding: 0;\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n font-weight: 700;\n text-decoration: underline;\n cursor: pointer;\n}\n.bb-linkButton:hover {\n color: #f8fafc;\n}\n.bb-tierLock {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 16px 14px;\n border-radius: 7px;\n background: rgba(251, 191, 36, 0.08);\n border: 1px dashed rgba(251, 191, 36, 0.3);\n color: #fcd34d;\n}\n.bb-tierLockMessage {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n}\n.bb-root--licenseChecking,\n.bb-root--licenseInvalid {\n align-items: center;\n justify-content: center;\n}\n.bb-readonly {\n background: rgba(148, 163, 184, 0.08);\n color: #9ca3af;\n}\n.bb-checkboxRow {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n}\n.bb-checkbox {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 6px 5px;\n border-radius: 7px;\n background: rgba(15, 23, 42, 0.22);\n border: 1px solid rgba(148, 163, 184, 0.12);\n font-size: 13px;\n color: #d7dee9;\n}\n.bb-checkbox input {\n accent-color: #f59e0b;\n}\n.bb-sliderField {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.bb-slider {\n flex: 1 1 auto;\n min-width: 0;\n accent-color: #2aa8d8;\n}\n.bb-slider--unset {\n opacity: 0.45;\n}\n.bb-fileField {\n position: relative;\n display: flex;\n align-items: center;\n}\n.bb-fileField-input {\n width: 100%;\n padding-right: 40px;\n}\n.bb-fileFieldButton {\n position: absolute;\n right: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: none;\n border-radius: 5px;\n background: transparent;\n color: #aab4c5;\n cursor: pointer;\n}\n.bb-fileFieldButton:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #f8fafc;\n}\n.bb-materialPanel {\n margin-top: 8px;\n border: 1px solid rgba(148, 163, 184, 0.14);\n border-radius: 6px;\n background: rgba(15, 23, 42, 0.28);\n overflow: hidden;\n}\n.bb-materialPanel:first-of-type {\n margin-top: 0;\n}\n.bb-materialPanelHeader {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border: none;\n padding: 10px 12px;\n background: rgba(255, 255, 255, 0.03);\n cursor: pointer;\n text-align: left;\n color: inherit;\n}\n.bb-materialPanelHeader:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.bb-materialPanelTitleWrap {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n color: #dbe4f0;\n}\n.bb-materialPanelChevron {\n flex-shrink: 0;\n color: #8b97ab;\n transform: rotate(-90deg);\n transition: transform 0.18s ease;\n}\n.bb-materialPanelChevron--open {\n transform: rotate(0deg);\n}\n.bb-materialPanelTitle {\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n}\n.bb-materialPanelHelp {\n display: inline-flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n margin-left: 2px;\n border-radius: 999px;\n color: #b9c0ca;\n cursor: help;\n transition:\n background 0.18s ease,\n color 0.18s ease,\n transform 0.18s ease;\n}\n.bb-materialPanelHelp:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #eef2ff;\n transform: translateY(-0.5px);\n}\n.bb-materialPanelBody {\n padding: 12px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.bb-actionList {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-actionCard {\n border: 1px solid rgba(148, 163, 184, 0.14);\n border-radius: 9px;\n padding: 14px;\n background: rgba(15, 23, 42, 0.2);\n}\n.bb-actionCardHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n margin-bottom: 12px;\n}\n.bb-actionCardTitle {\n font-size: 13px;\n font-weight: 700;\n color: #f8fafc;\n}\n.bb-actionCardMeta {\n flex-shrink: 0;\n padding: 5px 9px;\n border-radius: 999px;\n background: rgba(148, 163, 184, 0.12);\n color: #94a3b8;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n}\n.bb-actionGrid {\n display: grid;\n grid-template-columns: 1fr;\n gap: 10px;\n}\n.bb-actionFooter {\n margin-top: 12px;\n}\n.bb-actionRemove {\n width: 100%;\n}\n.bb-previewShell {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n padding: 5px;\n overflow: hidden;\n}\n.bb-previewHeader {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n padding: 0 4px 10px;\n}\n.bb-previewControls {\n display: flex;\n align-items: flex-start;\n justify-content: flex-end;\n gap: 10px;\n flex-wrap: wrap;\n}\n.bb-previewTitle {\n margin: 2px 0 0;\n font-size: 20px;\n line-height: 1.05;\n letter-spacing: -0.03em;\n color: #1f2937;\n}\n.bb-previewToggles {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n}\n.bb-checkbox--compact {\n gap: 5px;\n padding: 0;\n background: transparent;\n border: none;\n font-size: 10px;\n font-weight: 500;\n line-height: 1;\n color: #4b5563;\n}\n.bb-checkbox--compact:hover {\n background: transparent;\n transform: none;\n}\n.bb-checkbox--compact input[type=checkbox] {\n width: 11px;\n height: 11px;\n}\n.bb-previewMeta {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n gap: 8px;\n}\n.bb-removeModelButton {\n background-color: rgb(174, 54, 54);\n min-height: 34px;\n padding: 0 14px;\n}\n.bb-removeModelButton:hover {\n background-color: rgb(163, 20, 20);\n}\n.bb-previewMeta span {\n display: inline-flex;\n align-items: center;\n min-height: 34px;\n padding: 0 12px;\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.7);\n border: 1px solid rgba(148, 163, 184, 0.18);\n color: #475569;\n font-size: 12px;\n font-weight: 700;\n}\n.bb-preview {\n position: relative;\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n border-radius: 14px;\n background:\n radial-gradient(\n circle at top left,\n rgba(255, 255, 255, 0.92),\n rgba(236, 240, 244, 0.82)),\n #f8fafc;\n border: 1px solid rgba(148, 163, 184, 0.24);\n box-shadow: 0 24px 48px rgba(148, 163, 184, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.7);\n}\n.bb-loadingOverlay {\n position: absolute;\n inset: 0;\n z-index: 5;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n padding: 24px;\n text-align: center;\n border-radius: 14px;\n background: rgba(248, 250, 252, 0.82);\n backdrop-filter: blur(2px);\n}\n.bb-loadingSpinner {\n width: 40px;\n height: 40px;\n border-radius: 999px;\n border: 3px solid rgba(148, 163, 184, 0.3);\n border-top-color: #2563eb;\n animation: bb-spin 0.8s linear infinite;\n}\n@keyframes bb-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.bb-loadingMessage {\n max-width: 320px;\n color: #475569;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.5;\n}\n.bb-previewStatus {\n max-width: 280px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bb-previewStatus--success {\n background: rgba(34, 197, 94, 0.12) !important;\n border-color: rgba(34, 197, 94, 0.3) !important;\n color: #15803d !important;\n}\n.bb-previewStatus--error {\n background: rgba(239, 68, 68, 0.12) !important;\n border-color: rgba(239, 68, 68, 0.3) !important;\n color: #b91c1c !important;\n}\n.bb-previewModelName {\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bb-previewPlaceholder {\n position: relative;\n display: flex;\n height: 100%;\n width: 100%;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: #64748b;\n font-size: 15px;\n line-height: 1.7;\n border-radius: 14px;\n border: 2px dashed rgba(148, 163, 184, 0.24);\n background:\n radial-gradient(\n circle at top,\n rgba(251, 191, 36, 0.12),\n transparent 30%),\n linear-gradient(\n 180deg,\n rgba(255, 255, 255, 0.82),\n rgba(238, 242, 248, 0.9));\n transition:\n border-color 0.2s ease,\n background 0.2s ease,\n transform 0.2s ease,\n box-shadow 0.2s ease;\n}\n.bb-previewPlaceholder > div {\n max-width: 420px;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 14px;\n}\n.bb-previewPlaceholderTitle {\n font-size: 28px;\n line-height: 1.05;\n letter-spacing: -0.03em;\n font-weight: 800;\n color: #1f2937;\n}\n.bb-previewPlaceholderButton {\n margin-top: 8px;\n border: 0;\n border-radius: 999px;\n padding: 13px 22px;\n background: #135bc0;\n color: #ffffff;\n font-size: 14px;\n font-weight: 800;\n cursor: pointer;\n transition:\n transform 0.18s ease,\n box-shadow 0.18s ease,\n opacity 0.18s ease;\n}\n.bb-previewPlaceholderButton:hover:not(:disabled) {\n transform: translateY(-1px);\n box-shadow: 0 14px 30px rgba(19, 91, 192, 0.28);\n}\n.bb-previewPlaceholderButton:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n.bb-previewPlaceholder--dragActive {\n border-color: rgba(245, 158, 11, 0.75);\n background:\n radial-gradient(\n circle at top,\n rgba(251, 191, 36, 0.22),\n transparent 32%),\n linear-gradient(\n 180deg,\n rgba(255, 252, 245, 0.95),\n rgba(255, 244, 230, 0.92));\n transform: scale(0.995);\n box-shadow: inset 0 0 0 1px rgba(245, 158, 11, 0.14);\n}\n.bb-sectionHeader {\n cursor: pointer;\n user-select: none;\n}\n.bb-iconButton {\n background: none;\n border: none;\n cursor: pointer;\n font-size: 16px;\n color: var(--bb-sidebar-subtle-text);\n transition: color 0.2s ease;\n padding: 4px 8px;\n border-radius: 4px;\n}\n.bb-iconButton:hover {\n color: var(--bb-sidebar-text);\n background: var(--bb-sidebar-surface-strong);\n}\n.bb-cameraLive,\n.bb-cameraSaved {\n background: rgba(15, 23, 42, 0.44);\n border-radius: 8px;\n padding: 14px;\n margin-bottom: 16px;\n border: 1px solid rgba(148, 163, 184, 0.15);\n transition: all 0.2s ease;\n}\n.bb-cameraLive:hover,\n.bb-cameraSaved:hover {\n border-color: rgba(148, 163, 184, 0.25);\n background: rgba(15, 23, 42, 0.52);\n}\n.bb-cameraLiveHeader,\n.bb-cameraSavedHeader {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 12px;\n padding-bottom: 8px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.15);\n}\n.bb-cameraLiveBadge {\n background:\n linear-gradient(\n 135deg,\n #10b981,\n #059669);\n color: white;\n font-size: 10px;\n font-weight: 700;\n padding: 3px 8px;\n border-radius: 10px;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n}\n.bb-cameraSavedBadge {\n background:\n linear-gradient(\n 135deg,\n #3b82f6,\n #2563eb);\n color: white;\n font-size: 10px;\n font-weight: 700;\n padding: 3px 8px;\n border-radius: 10px;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n}\n.bb-cameraLiveLabel,\n.bb-cameraSavedLabel {\n font-size: 12px;\n font-weight: 600;\n color: #cbd5e1;\n}\n.bb-cameraValues {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.bb-cameraRow {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n flex-wrap: wrap;\n}\n.bb-cameraLabel {\n font-weight: 600;\n color: #94a3b8;\n min-width: 58px;\n font-size: 11px;\n letter-spacing: 0.02em;\n}\n.bb-cameraCode {\n font-family:\n "Monaco",\n "Menlo",\n "Ubuntu Mono",\n "Consolas",\n monospace;\n font-size: 11px;\n background: rgba(0, 0, 0, 0.3);\n padding: 3px 8px;\n border-radius: 4px;\n color: #fcd34d;\n letter-spacing: 0.02em;\n word-break: break-all;\n}\n.bb-cameraPlaceholder {\n color: #94a3b8;\n font-size: 12px;\n text-align: center;\n padding: 16px;\n font-style: italic;\n}\n.bb-cameraControls {\n display: flex;\n gap: 10px;\n margin-bottom: 14px;\n flex-wrap: wrap;\n}\n.bb-button--primary {\n background: #135bc0;\n border-color: transparent;\n color: #ffffff;\n}\n.bb-button--primary:hover:not(:disabled) {\n background: #1a68d6;\n transform: translateY(-1px);\n}\n.bb-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n}\n.bb-cameraMessage {\n padding: 10px 14px;\n border-radius: 6px;\n margin-bottom: 14px;\n font-size: 12px;\n font-weight: 500;\n animation: slideIn 0.3s ease-out;\n}\n.bb-cameraMessage--success {\n background: rgba(16, 185, 129, 0.15);\n color: #6ee7b7;\n border: 1px solid rgba(16, 185, 129, 0.3);\n}\n.bb-cameraMessage--error {\n background: rgba(239, 68, 68, 0.15);\n color: #ffe5e55c;\n border: 1px solid rgba(255, 248, 248, 0.3);\n}\n@keyframes slideIn {\n from {\n opacity: 0;\n transform: translateY(-8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n.bb-readonly {\n background: rgba(148, 163, 184, 0.08);\n color: #9ca3af;\n cursor: default;\n}\n@media (max-width: 1280px) {\n .bb-cameraRow {\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n }\n .bb-cameraLabel {\n min-width: auto;\n }\n .bb-cameraControls {\n flex-direction: column;\n }\n .bb-cameraControls .bb-button {\n width: 100%;\n }\n}\n@media (max-width: 980px) {\n .bb-sidebar--left {\n width: 100%;\n max-height: 42vh;\n }\n .bb-sidebar--collapsed {\n width: 100%;\n }\n .bb-previewShell {\n padding: 14px;\n }\n .bb-preview {\n min-height: 44vh;\n }\n .bb-previewHeader {\n flex-direction: column;\n align-items: flex-start;\n }\n .bb-previewControls {\n width: 100%;\n justify-content: flex-start;\n }\n .bb-previewMeta {\n justify-content: flex-start;\n }\n .bb-grid,\n .bb-actionGrid,\n .bb-statRow {\n grid-template-columns: 1fr;\n }\n}\n.bb-annotationsInfo {\n margin-top: 8px;\n}\n.bb-emptyAnnotations {\n padding: 32px;\n text-align: center;\n background: #f5f5f5;\n border-radius: 4px;\n color: #666;\n font-size: 14px;\n}\n.bb-annotationsList {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-annotationCard {\n background: #f9f9f9;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n padding: 12px;\n transition: all 0.2s ease;\n}\n.bb-annotationCard:hover {\n background: #ffffff;\n border-color: #4caf50;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n}\n.bb-annotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.bb-annotationBadge {\n background: #4caf50;\n color: white;\n padding: 2px 8px;\n border-radius: 2px;\n font-size: 11px;\n font-weight: 600;\n}\n.bb-annotationActions {\n display: flex;\n gap: 8px;\n}\n.bb-annotationEdit,\n.bb-annotationDelete {\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 4px;\n border-radius: 2px;\n font-size: 14px;\n transition: all 0.2s ease;\n}\n.bb-annotationEdit:hover {\n background: #e8f5e9;\n transform: scale(1.05);\n}\n.bb-annotationDelete:hover {\n background: #ffebee;\n transform: scale(1.05);\n}\n.bb-annotationTitle {\n font-weight: 600;\n font-size: 14px;\n color: #333;\n margin-bottom: 6px;\n}\n.bb-annotationDescription {\n font-size: 13px;\n color: #666;\n margin-bottom: 8px;\n line-height: 1.4;\n}\n.bb-annotationPosition {\n font-size: 11px;\n font-family: monospace;\n color: #999;\n background: #f0f0f0;\n padding: 4px 6px;\n border-radius: 2px;\n display: inline-block;\n}\n.bb-modalOverlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n}\n.bb-modal {\n background: white;\n border-radius: 6px;\n width: 500px;\n max-width: 90%;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n animation: modalSlideIn 0.2s ease;\n}\n@keyframes modalSlideIn {\n from {\n transform: translateY(-20px);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n.bb-modalHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid #e0e0e0;\n}\n.bb-modalHeader h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n}\n.bb-modalClose {\n background: transparent;\n border: none;\n font-size: 20px;\n cursor: pointer;\n color: #999;\n transition: color 0.2s ease;\n}\n.bb-modalClose:hover {\n color: #333;\n}\n.bb-modalBody {\n padding: 20px;\n}\n.bb-modalFooter {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 16px 20px;\n border-top: 1px solid #e0e0e0;\n}\n.bb-annotationsInfo {\n margin-top: 8px;\n}\n.bb-emptyAnnotations {\n padding: 32px;\n text-align: center;\n background: rgba(15, 23, 42, 0.44);\n border-radius: 10px;\n border: 1px solid rgba(148, 163, 184, 0.15);\n color: #94a3b8;\n font-size: 14px;\n}\n.bb-annotationsList {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-annotationCard {\n background: rgba(15, 23, 42, 0.44);\n border-left: 4px solid #22d3ee;\n border-radius: 8px;\n padding: 16px;\n transition: all 0.2s ease;\n border: 1px solid rgba(148, 163, 184, 0.15);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);\n}\n.bb-annotationCard:hover {\n transform: translateX(4px);\n background: rgba(15, 23, 42, 0.6);\n border-color: rgba(148, 163, 184, 0.25);\n box-shadow: 0 4px 12px rgba(15, 23, 42, 0.3);\n}\n.bb-annotationBadge {\n display: inline-block;\n background: #22d3ee;\n color: #06343c;\n padding: 2px 10px;\n border-radius: 10px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.5px;\n margin-bottom: 12px;\n text-transform: uppercase;\n}\n.bb-annotationTitle {\n font-weight: 700;\n font-size: 15px;\n color: #f8fafc;\n margin-bottom: 8px;\n line-height: 1.3;\n}\n.bb-annotationDescription {\n font-size: 13px;\n color: #cbd5e1;\n margin-bottom: 12px;\n line-height: 1.5;\n word-wrap: break-word;\n white-space: pre-wrap;\n}\n.bb-annotationPosition {\n font-size: 11px;\n font-family:\n "Monaco",\n "Menlo",\n "Ubuntu Mono",\n "Consolas",\n monospace;\n color: #94a3b8;\n background: rgba(0, 0, 0, 0.3);\n padding: 4px 8px;\n border-radius: 4px;\n display: inline-block;\n margin-top: 4px;\n letter-spacing: 0.02em;\n}\n.bb-annotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.bb-annotationActions {\n display: flex;\n gap: 4px;\n}\n.bb-annotationAction {\n background: none;\n border: none;\n color: #94a3b8;\n cursor: pointer;\n padding: 4px;\n border-radius: 2px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n}\n.bb-annotationAction--edit:hover {\n border-radius: 50%;\n background: rgba(59, 130, 246, 0.2);\n color: #3b82f6;\n}\n.bb-annotationAction--delete:hover {\n border-radius: 50%;\n background: rgba(239, 68, 68, 0.2);\n color: #ef4444;\n}\n.bb-editSpotOverlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n}\n.bb-editSpotPopup {\n background:\n linear-gradient(\n 180deg,\n rgba(20, 27, 45, 0.98),\n rgba(25, 34, 57, 0.95)),\n #121826;\n border-radius: 8px;\n padding: 24px;\n min-width: 400px;\n max-width: 500px;\n border: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.3);\n}\n.bb-editSpotHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n}\n.bb-editSpotTitle {\n color: #f8fafc;\n font-size: 18px;\n font-weight: 600;\n margin: 0;\n}\n.bb-editSpotClose {\n background: none;\n border: none;\n color: #94a3b8;\n font-size: 24px;\n cursor: pointer;\n padding: 0 4px;\n transition: color 0.2s ease;\n}\n.bb-editSpotClose:hover {\n color: #f8fafc;\n}\n.bb-editSpotBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.bb-editSpotBody .bb-input,\n.bb-editSpotBody .bb-textarea {\n background: rgba(15, 23, 42, 0.4);\n border-color: rgba(148, 163, 184, 0.2);\n color: #f8fafc;\n}\n.bb-editSpotBody .bb-input:focus,\n.bb-editSpotBody .bb-textarea:focus {\n border-color: rgba(251, 191, 36, 0.65);\n box-shadow: 0 0 0 4px rgba(251, 191, 36, 0.12);\n}\n.bb-editSpotBody .bb-label {\n color: #d7dee9;\n}\n.bb-editSpotActions {\n display: flex;\n gap: 8px;\n margin-top: 8px;\n}\n.bb-editSpotActions .bb-button {\n background: rgba(255, 255, 255, 0.04);\n border: 1px solid rgba(148, 163, 184, 0.18);\n color: #e5edf7;\n}\n.bb-editSpotActions .bb-button:hover {\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.32);\n transform: translateY(-1px);\n}\n.bb-button--danger {\n background: #ef4444;\n color: white;\n border: none;\n padding: 8px 16px;\n border-radius: 7px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 700;\n display: flex;\n align-items: center;\n gap: 4px;\n transition: all 0.2s ease;\n}\n.bb-button--danger:hover {\n background: #dc2626;\n transform: translateY(-1px);\n box-shadow: 0 10px 18px rgba(239, 68, 68, 0.25);\n}\n.bb-button--primary {\n background: #135bc0;\n border-color: transparent;\n color: #ffffff;\n}\n.bb-button--primary:hover {\n background: #1a68d6;\n}\n.bb-annotationCard {\n background: rgba(15, 23, 42, 0.44);\n border-left: 4px solid #22d3ee;\n border-radius: 8px;\n padding: 16px;\n transition: all 0.2s ease;\n border: 1px solid rgba(148, 163, 184, 0.15);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);\n}\n.bb-annotationCard:hover {\n transform: translateX(4px);\n background: rgba(15, 23, 42, 0.6);\n border-color: rgba(148, 163, 184, 0.25);\n box-shadow: 0 4px 12px rgba(15, 23, 42, 0.3);\n}\n.bb-annotationBadge {\n display: inline-block;\n background: #22d3ee;\n color: #06343c;\n padding: 2px 10px;\n border-radius: 10px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.5px;\n margin-bottom: 12px;\n text-transform: uppercase;\n}\n.bb-annotationTitle {\n font-weight: 700;\n font-size: 15px;\n color: #f8fafc;\n margin-bottom: 8px;\n line-height: 1.3;\n}\n.bb-annotationDescription {\n font-size: 13px;\n color: #cbd5e1;\n margin-bottom: 12px;\n line-height: 1.5;\n word-wrap: break-word;\n white-space: pre-wrap;\n}\n.bb-annotationPosition {\n font-size: 11px;\n font-family:\n "Monaco",\n "Menlo",\n "Ubuntu Mono",\n "Consolas",\n monospace;\n color: #94a3b8;\n background: rgba(0, 0, 0, 0.3);\n padding: 4px 8px;\n border-radius: 4px;\n display: inline-block;\n margin-top: 4px;\n letter-spacing: 0.02em;\n}\n.bb-emptyAnnotations {\n padding: 32px;\n text-align: center;\n background: rgba(15, 23, 42, 0.44);\n border-radius: 10px;\n border: 1px solid rgba(148, 163, 184, 0.15);\n color: #94a3b8;\n font-size: 14px;\n}\n@media (max-width: 1280px) {\n .bb-cameraRow {\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n }\n .bb-cameraLabel {\n min-width: auto;\n }\n .bb-cameraControls {\n flex-direction: column;\n }\n .bb-cameraControls .bb-button {\n width: 100%;\n }\n}\n@media (max-width: 1120px) {\n .bb-sidebar--left {\n width: 320px;\n }\n .bb-sidebar--right {\n width: 220px;\n }\n .bb-grid {\n grid-template-columns: 1fr;\n }\n .bb-statRow {\n grid-template-columns: 1fr;\n }\n}\n@media (max-width: 980px) {\n .bb-root {\n flex-direction: column;\n height: auto;\n min-height: 0;\n }\n .bb-sidebar--left {\n width: 100% !important;\n max-height: 45vh;\n border-right: none;\n border-bottom: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 18px 42px rgba(15, 23, 42, 0.12);\n }\n .bb-sidebar--right {\n width: 100% !important;\n max-height: 45vh;\n border-left: none;\n border-top: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 -18px 42px rgba(15, 23, 42, 0.12);\n order: 3;\n }\n .bb-sidebar--collapsed {\n width: 100% !important;\n max-height: 72px;\n overflow: hidden;\n }\n .bb-sidebar--collapsed .bb-nodeList,\n .bb-sidebar--collapsed .bb-tabRow,\n .bb-sidebar--collapsed .bb-toolbar {\n display: none;\n }\n .bb-previewShell {\n flex: 1 1 auto;\n min-height: 50vh;\n padding: 14px;\n order: 2;\n }\n .bb-preview {\n min-height: 44vh;\n border-radius: 10px;\n }\n .bb-previewHeader {\n flex-direction: column;\n align-items: flex-start;\n gap: 12px;\n }\n .bb-previewControls {\n width: 100%;\n justify-content: flex-start;\n flex-wrap: wrap;\n }\n .bb-previewMeta {\n justify-content: flex-start;\n }\n .bb-previewMeta span {\n font-size: 11px;\n padding: 0 10px;\n min-height: 30px;\n }\n .bb-grid,\n .bb-actionGrid,\n .bb-statRow {\n grid-template-columns: 1fr;\n }\n .bb-editor {\n padding: 12px 12px 20px;\n }\n .bb-editorHeader {\n flex-direction: column;\n align-items: stretch;\n padding: 14px;\n }\n .bb-editorTitle {\n font-size: 20px;\n }\n .bb-editorBadge {\n align-self: flex-start;\n }\n .bb-toolbar {\n flex-direction: column;\n gap: 12px;\n padding: 16px 20px 18px;\n }\n .bb-toolbarDivider {\n width: 100%;\n height: 1px;\n margin: 0;\n }\n .bb-tabRow {\n padding: 12px 16px 0;\n gap: 6px;\n }\n .bb-tab {\n font-size: 10px;\n padding: 8px 10px;\n }\n .bb-header {\n padding: 14px 16px 12px;\n }\n .bb-header--sidebar {\n min-height: 60px;\n }\n .bb-title {\n font-size: 17px;\n }\n .bb-subtitle {\n font-size: 13px;\n }\n .bb-statChip {\n padding: 10px 12px;\n }\n .bb-statChip strong {\n font-size: 12px;\n }\n .bb-nodeButton {\n padding: 10px 12px;\n }\n .bb-nodeName {\n font-size: 12px;\n }\n .bb-nodeMeta {\n font-size: 10px;\n }\n .bb-section {\n padding: 14px;\n }\n .bb-sectionTitle {\n font-size: 9px;\n }\n .bb-field {\n gap: 4px;\n }\n .bb-label {\n font-size: 11px;\n }\n .bb-input,\n .bb-select,\n .bb-textarea {\n padding: 9px 11px;\n font-size: 12px;\n }\n .bb-textarea {\n min-height: 80px;\n }\n .bb-button,\n .bb-fileLabel {\n min-height: 36px;\n padding: 0 14px;\n font-size: 12px;\n }\n .bb-previewPlaceholderTitle {\n font-size: 22px;\n }\n .bb-previewPlaceholderButton {\n padding: 10px 18px;\n font-size: 13px;\n }\n .bb-editSpotPopup {\n min-width: 90%;\n max-width: 90%;\n padding: 20px;\n }\n .bb-modal {\n width: 95%;\n max-width: 95%;\n }\n .bb-annotationCard {\n padding: 12px;\n }\n .bb-annotationTitle {\n font-size: 14px;\n }\n .bb-annotationDescription {\n font-size: 12px;\n }\n}\n@media (max-width: 640px) {\n .bb-root {\n flex-direction: column;\n height: auto;\n min-height: 0;\n }\n .bb-sidebar--left {\n max-height: 40vh;\n width: 100% !important;\n }\n .bb-sidebar--right {\n max-height: 40vh;\n width: 100% !important;\n }\n .bb-previewShell {\n min-height: 40vh;\n padding: 10px;\n }\n .bb-preview {\n min-height: 35vh;\n border-radius: 8px;\n }\n .bb-previewHeader {\n flex-direction: column;\n align-items: flex-start;\n gap: 8px;\n padding: 0 0 12px;\n }\n .bb-previewTitle {\n font-size: 17px;\n }\n .bb-previewControls {\n gap: 6px;\n }\n .bb-previewMeta span {\n font-size: 10px;\n padding: 0 8px;\n min-height: 26px;\n }\n .bb-editor {\n padding: 10px 10px 16px;\n }\n .bb-editorHeader {\n padding: 12px;\n flex-direction: column;\n align-items: stretch;\n }\n .bb-editorTitle {\n font-size: 18px;\n }\n .bb-editorMeta {\n font-size: 11px;\n gap: 6px 8px;\n }\n .bb-editorBadge {\n font-size: 9px;\n padding: 4px 10px;\n align-self: flex-start;\n }\n .bb-section {\n padding: 12px;\n margin-bottom: 12px;\n }\n .bb-sectionTitle {\n font-size: 9px;\n margin-bottom: 10px;\n }\n .bb-grid {\n gap: 8px;\n }\n .bb-field--full {\n grid-column: 1 / -1;\n }\n .bb-label {\n font-size: 11px;\n }\n .bb-input,\n .bb-select,\n .bb-textarea {\n padding: 8px 10px;\n font-size: 12px;\n border-radius: 6px;\n }\n .bb-textarea {\n min-height: 70px;\n }\n .bb-button,\n .bb-fileLabel {\n min-height: 34px;\n padding: 0 12px;\n font-size: 11px;\n border-radius: 6px;\n }\n .bb-toolbar {\n padding: 12px 14px 14px;\n gap: 10px;\n }\n .bb-tabRow {\n padding: 10px 12px 0;\n gap: 4px;\n }\n .bb-tab {\n font-size: 9px;\n padding: 6px 8px;\n border-radius: 4px;\n }\n .bb-header {\n padding: 10px 12px 8px;\n }\n .bb-header--sidebar {\n min-height: 52px;\n }\n .bb-title {\n font-size: 15px;\n }\n .bb-subtitle {\n font-size: 12px;\n }\n .bb-eyebrow {\n font-size: 9px;\n padding: 4px 8px;\n margin-bottom: 8px;\n }\n .bb-statRow {\n gap: 6px;\n margin-top: 14px;\n }\n .bb-statChip {\n padding: 8px 10px;\n border-radius: 6px;\n }\n .bb-statChip strong {\n font-size: 11px;\n }\n .bb-statLabel {\n font-size: 9px;\n }\n .bb-sidebarToggle {\n width: 30px;\n height: 30px;\n font-size: 14px;\n border-radius: 5px;\n }\n .bb-nodeItems {\n padding: 4px 6px;\n }\n .bb-nodeButton {\n padding: 8px 10px;\n border-radius: 6px;\n margin-bottom: 4px;\n }\n .bb-nodeName {\n font-size: 11px;\n }\n .bb-nodeMeta {\n font-size: 10px;\n }\n .bb-search {\n padding: 8px 10px;\n }\n .bb-statusCard {\n padding: 10px 12px;\n border-radius: 7px;\n }\n .bb-status {\n font-size: 12px;\n }\n .bb-statusTitle {\n font-size: 10px;\n }\n .bb-checkboxRow {\n gap: 6px;\n }\n .bb-checkbox {\n padding: 6px 10px;\n font-size: 12px;\n border-radius: 5px;\n }\n .bb-actionCard {\n padding: 10px;\n border-radius: 7px;\n }\n .bb-actionCardTitle {\n font-size: 12px;\n }\n .bb-actionCardMeta {\n font-size: 9px;\n padding: 3px 7px;\n }\n .bb-previewPlaceholder {\n padding: 24px;\n border-radius: 8px;\n }\n .bb-previewPlaceholderTitle {\n font-size: 18px;\n }\n .bb-previewPlaceholderButton {\n padding: 8px 14px;\n font-size: 12px;\n border-radius: 999px;\n }\n .bb-editSpotPopup {\n min-width: 95%;\n max-width: 95%;\n padding: 16px;\n border-radius: 6px;\n }\n .bb-editSpotTitle {\n font-size: 16px;\n }\n .bb-editSpotActions .bb-button {\n padding: 8px 12px;\n font-size: 12px;\n }\n .bb-modal {\n width: 98%;\n max-width: 98%;\n border-radius: 5px;\n }\n .bb-modalHeader {\n padding: 12px 16px;\n }\n .bb-modalHeader h3 {\n font-size: 16px;\n }\n .bb-modalBody {\n padding: 14px;\n }\n .bb-modalFooter {\n padding: 12px 16px;\n flex-direction: column;\n gap: 8px;\n }\n .bb-modalFooter .bb-button {\n width: 100%;\n justify-content: center;\n }\n .bb-annotationCard {\n padding: 10px;\n border-left-width: 3px;\n border-radius: 6px;\n }\n .bb-annotationTitle {\n font-size: 13px;\n }\n .bb-annotationDescription {\n font-size: 11px;\n }\n .bb-annotationPosition {\n font-size: 10px;\n padding: 3px 6px;\n }\n .bb-annotationBadge {\n font-size: 9px;\n padding: 2px 8px;\n }\n .bb-emptyAnnotations {\n padding: 20px;\n font-size: 12px;\n border-radius: 7px;\n }\n .bb-removeModelButton {\n min-height: 30px;\n padding: 0 10px;\n font-size: 11px;\n }\n .bb-annotationsList {\n gap: 8px;\n }\n .bb-cameraLive,\n .bb-cameraSaved {\n padding: 10px;\n border-radius: 6px;\n }\n .bb-cameraLiveHeader,\n .bb-cameraSavedHeader {\n flex-wrap: wrap;\n }\n .bb-cameraLiveLabel,\n .bb-cameraSavedLabel {\n font-size: 11px;\n }\n .bb-cameraRow {\n font-size: 11px;\n gap: 4px;\n }\n .bb-cameraCode {\n font-size: 10px;\n padding: 2px 6px;\n }\n .bb-cameraControls {\n flex-direction: column;\n gap: 6px;\n }\n .bb-cameraControls .bb-button {\n width: 100%;\n justify-content: center;\n }\n .bb-cameraMessage {\n font-size: 11px;\n padding: 8px 12px;\n }\n .bb-readonly {\n font-size: 11px;\n padding: 6px 8px;\n }\n}\n@media (max-width: 420px) {\n .bb-sidebar--left {\n max-height: 35vh;\n }\n .bb-sidebar--right {\n max-height: 35vh;\n }\n .bb-previewShell {\n min-height: 35vh;\n padding: 6px;\n }\n .bb-preview {\n min-height: 30vh;\n border-radius: 6px;\n }\n .bb-previewTitle {\n font-size: 15px;\n }\n .bb-previewMeta span {\n font-size: 9px;\n padding: 0 6px;\n min-height: 22px;\n }\n .bb-editorTitle {\n font-size: 16px;\n }\n .bb-title {\n font-size: 13px;\n }\n .bb-tab {\n font-size: 8px;\n padding: 5px 6px;\n }\n .bb-button,\n .bb-fileLabel {\n font-size: 10px;\n min-height: 30px;\n padding: 0 10px;\n }\n .bb-input,\n .bb-select,\n .bb-textarea {\n font-size: 11px;\n padding: 6px 8px;\n }\n .bb-statChip {\n padding: 6px 8px;\n }\n .bb-statChip strong {\n font-size: 10px;\n }\n .bb-previewPlaceholderTitle {\n font-size: 15px;\n }\n .bb-previewPlaceholderButton {\n font-size: 11px;\n padding: 6px 12px;\n }\n .bb-editSpotPopup {\n padding: 14px;\n border-radius: 5px;\n }\n .bb-editSpotTitle {\n font-size: 14px;\n }\n .bb-editSpotBody .bb-input,\n .bb-editSpotBody .bb-textarea {\n font-size: 12px;\n padding: 6px 8px;\n }\n .bb-modalHeader h3 {\n font-size: 14px;\n }\n .bb-annotationTitle {\n font-size: 12px;\n }\n}\n@media (max-width: 980px) {\n .bb-sidebar--collapsed .bb-header--sidebar {\n min-height: 52px;\n }\n .bb-sidebar--collapsed .bb-title {\n font-size: 14px;\n }\n .bb-sidebar--collapsed .bb-sidebarToggle {\n width: 28px;\n height: 28px;\n font-size: 12px;\n }\n}\n@media (hover: none) and (pointer: coarse) {\n .bb-nodeButton:hover,\n .bb-button:hover,\n .bb-fileLabel:hover,\n .bb-tab:hover,\n .bb-sidebarToggle:hover,\n .bb-annotationCard:hover {\n transform: none;\n }\n .bb-nodeButton:active,\n .bb-button:active,\n .bb-fileLabel:active,\n .bb-tab:active,\n .bb-sidebarToggle:active {\n transform: scale(0.97);\n transition: transform 0.1s ease;\n }\n}\n@media print {\n .bb-root {\n height: auto;\n min-height: 0;\n background: white;\n }\n .bb-sidebar--left,\n .bb-sidebar--right {\n width: 100% !important;\n max-height: none !important;\n background: white !important;\n color: black !important;\n border: 1px solid #ddd !important;\n box-shadow: none !important;\n }\n .bb-previewShell {\n min-height: auto !important;\n padding: 10px !important;\n }\n .bb-preview {\n min-height: auto !important;\n border-radius: 4px !important;\n border: 1px solid #ddd !important;\n box-shadow: none !important;\n }\n .bb-button,\n .bb-fileLabel,\n .bb-sidebarToggle,\n .bb-tab,\n .bb-annotationAction,\n .bb-iconButton {\n display: none !important;\n }\n .bb-nodeList {\n overflow: visible !important;\n }\n .bb-previewPlaceholderButton {\n display: none !important;\n }\n}\n.bb-checkboxRow {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n align-items: center;\n padding: 4px 0;\n}\n.bb-checkbox {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n border-radius: 7px;\n font-weight: 600;\n font-size: 13px;\n color: #f3eeee;\n cursor: pointer;\n user-select: none;\n transition: all 0.2s ease;\n}\n.bb-checkbox input[type=checkbox] {\n width: 16px;\n height: 16px;\n accent-color: #f59e0b;\n cursor: pointer;\n flex-shrink: 0;\n}\n.bb-editorDivider {\n height: 1px;\n background: rgba(148, 163, 184, 0.14);\n margin: 4px 0 16px 0;\n width: 100%;\n}\n.bb-preview {\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n.ri-viewer {\n width: 100% !important;\n height: 100% !important;\n position: relative !important;\n}\n.ri-buttonContainer {\n position: absolute !important;\n bottom: 20px !important;\n right: 20px !important;\n z-index: 10000 !important;\n display: flex !important;\n gap: 10px !important;\n pointer-events: auto !important;\n}\n.ri-downloadButton {\n display: flex !important;\n align-items: center !important;\n gap: 6px !important;\n padding: 10px 12px !important;\n background: #2f302f74 !important;\n color: white !important;\n border: none !important;\n border-radius: 6px !important;\n cursor: pointer !important;\n font-size: 12px !important;\n font-weight: 500 !important;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important;\n opacity: 1 !important;\n visibility: visible !important;\n}\n.ri-downloadButton svg {\n width: 14px !important;\n height: 14px !important;\n}\n.ri-resetButton {\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n padding: 6px !important;\n background: #2f302f74 !important;\n border: none !important;\n border-radius: 6px !important;\n cursor: pointer !important;\n opacity: 1 !important;\n visibility: visible !important;\n}\n.ri-resetButton svg {\n width: 14px !important;\n height: 14px !important;\n}\n.bb-reset-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n backdrop-filter: blur(4px);\n animation: bb-fade-in 0.2s ease;\n}\n.bb-reset-dialog {\n background: #1a1f2e;\n border-radius: 12px;\n max-width: 480px;\n width: 90%;\n border: 1px solid rgba(148, 163, 184, 0.18);\n box-shadow: 0 24px 48px rgba(0, 0, 0, 0.5);\n animation: bb-slide-up 0.25s ease;\n}\n.bb-reset-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px 16px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.1);\n}\n.bb-reset-title {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 0;\n font-size: 18px;\n font-weight: 700;\n color: #f8fafc;\n}\n.bb-reset-title .bb-icon-warning {\n color: #f59e0b;\n flex-shrink: 0;\n}\n.bb-reset-close {\n background: transparent;\n border: none;\n color: #94a3b8;\n cursor: pointer;\n padding: 4px;\n border-radius: 4px;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.bb-reset-close:hover {\n color: #f8fafc;\n background: rgba(255, 255, 255, 0.06);\n}\n.bb-reset-body {\n padding: 20px 24px 24px;\n}\n.bb-reset-message {\n margin: 0 0 12px 0;\n font-size: 15px;\n color: #e2e8f0;\n line-height: 1.5;\n}\n.bb-reset-message strong {\n color: #f8fafc;\n}\n.bb-reset-details {\n color: #94a3b8;\n text-align: center;\n font-size: 13px;\n line-height: 1.6;\n margin: 0 0 12px 0;\n}\n.bb-reset-warning {\n display: flex;\n align-items: center;\n gap: 8px;\n color: #f59e0b;\n font-size: 13px;\n font-weight: 600;\n margin: 4px 0 0 0;\n padding: 10px 12px;\n background: rgba(245, 158, 11, 0.08);\n border-radius: 6px;\n border: 1px solid rgba(245, 158, 11, 0.15);\n}\n.bb-reset-footer {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n padding: 16px 24px 20px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n}\n.bb-reset-btn {\n padding: 8px 20px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n border: 1px solid rgba(148, 163, 184, 0.18);\n background: rgba(255, 255, 255, 0.04);\n color: #e2e8f0;\n}\n.bb-reset-btn:hover {\n background: rgba(255, 255, 255, 0.08);\n transform: translateY(-1px);\n}\n.bb-reset-btn-danger {\n padding: 8px 20px;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n border: none;\n background: #dc2626;\n color: white;\n}\n.bb-reset-btn-danger:hover {\n background: #b91c1c;\n transform: translateY(-1px);\n box-shadow: 0 4px 12px rgba(220, 38, 38, 0.3);\n}\n@keyframes bb-fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n@keyframes bb-slide-up {\n from {\n opacity: 0;\n transform: translateY(20px) scale(0.96);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n');
8231
8238
 
8232
8239
  // src/hooks/useViewerCamera.ts
8233
8240
  var import_react23 = require("react");
@@ -11722,7 +11729,7 @@ function LightingTab({ config, onChange }) {
11722
11729
  {
11723
11730
  className: "se-segmentedControl",
11724
11731
  style: { gridAutoColumns: "1fr" },
11725
- children: ["shadowCatcher", "bakedAO"].map((m) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11732
+ children: ["shadowCatcher"].map((m) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11726
11733
  "button",
11727
11734
  {
11728
11735
  type: "button",
@@ -14457,8 +14464,99 @@ function BasicsPanel({
14457
14464
  ] });
14458
14465
  }
14459
14466
 
14467
+ // src/components/BindingBuilder/components/ActionsPanel.tsx
14468
+ var import_react37 = require("react");
14469
+
14470
+ // src/components/BindingBuilder/utils/actionCatalog.ts
14471
+ var ACTION_CATALOG = [
14472
+ {
14473
+ id: "change-color",
14474
+ label: "Change Color",
14475
+ handling: "viewer",
14476
+ description: "Opens a color picker docked under the action and commits the result to style.material.baseColor."
14477
+ },
14478
+ {
14479
+ id: "change-material",
14480
+ label: "Change Material",
14481
+ handling: "viewer",
14482
+ description: "Opens the material picker so the viewer can swap the object's texture."
14483
+ },
14484
+ {
14485
+ id: "toggle-visibility",
14486
+ label: "Toggle Visibility",
14487
+ handling: "viewer",
14488
+ description: "Flips the binding's visible flag."
14489
+ },
14490
+ {
14491
+ id: "toggle-light",
14492
+ label: "Toggle Light",
14493
+ handling: "host",
14494
+ description: "Seeded on light bindings. Handle it in your onAction callback."
14495
+ },
14496
+ {
14497
+ id: "change-light-color",
14498
+ label: "Change Light Color",
14499
+ handling: "host",
14500
+ description: "Seeded on light bindings. Handle it in your onAction callback."
14501
+ },
14502
+ {
14503
+ id: "set-brightness",
14504
+ label: "Set Brightness",
14505
+ handling: "host",
14506
+ description: "Seeded on light bindings. Handle it in your onAction callback."
14507
+ }
14508
+ ];
14509
+ var CUSTOM_ACTION_ID = "__custom__";
14510
+ function findCatalogAction(id) {
14511
+ return ACTION_CATALOG.find((entry) => entry.id === id);
14512
+ }
14513
+ var VIEWER_ACTION_IDS = ACTION_CATALOG.filter(
14514
+ (entry) => entry.handling === "viewer"
14515
+ ).map((entry) => entry.id);
14516
+ var AUTO_LABELS = /* @__PURE__ */ new Set([
14517
+ "",
14518
+ "New Action",
14519
+ ...ACTION_CATALOG.map((entry) => entry.label)
14520
+ ]);
14521
+ function isAutoFilledLabel(label) {
14522
+ return AUTO_LABELS.has(label.trim());
14523
+ }
14524
+ function levenshtein(a, b) {
14525
+ let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
14526
+ for (let i = 1; i <= a.length; i += 1) {
14527
+ const current = [i];
14528
+ for (let j = 1; j <= b.length; j += 1) {
14529
+ const substitution = previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1);
14530
+ current[j] = Math.min(current[j - 1] + 1, previous[j] + 1, substitution);
14531
+ }
14532
+ previous = current;
14533
+ }
14534
+ return previous[b.length];
14535
+ }
14536
+ function suggestActionId(id) {
14537
+ if (findCatalogAction(id)) return null;
14538
+ const candidate = id.trim().toLowerCase();
14539
+ if (!candidate) return null;
14540
+ const normalized = candidate.replace(/[^a-z0-9]/g, "");
14541
+ let best = null;
14542
+ for (const entry of ACTION_CATALOG) {
14543
+ const distance = Math.min(
14544
+ levenshtein(candidate, entry.id),
14545
+ levenshtein(normalized, entry.id.replace(/[^a-z0-9]/g, ""))
14546
+ );
14547
+ if (!best || distance < best.distance) {
14548
+ best = { id: entry.id, distance };
14549
+ }
14550
+ }
14551
+ return best && best.distance <= 2 ? best.id : null;
14552
+ }
14553
+
14460
14554
  // src/components/BindingBuilder/components/ActionsPanel.tsx
14461
14555
  var import_jsx_runtime41 = require("react/jsx-runtime");
14556
+ var VIEWER_ACTIONS = ACTION_CATALOG.filter(
14557
+ (entry) => entry.handling === "viewer"
14558
+ );
14559
+ var HOST_ACTIONS = ACTION_CATALOG.filter((entry) => entry.handling === "host");
14462
14560
  function ActionsPanel({
14463
14561
  selectedItem,
14464
14562
  selectedBinding,
@@ -14466,85 +14564,183 @@ function ActionsPanel({
14466
14564
  onToggle,
14467
14565
  updateBinding
14468
14566
  }) {
14567
+ const [customRows, setCustomRows] = (0, import_react37.useState)(/* @__PURE__ */ new Set());
14568
+ (0, import_react37.useEffect)(() => {
14569
+ setCustomRows(/* @__PURE__ */ new Set());
14570
+ }, [selectedItem]);
14571
+ const actions = selectedBinding.actions ?? [];
14572
+ const updateAction = (index, patch) => updateBinding(selectedItem, (binding) => ({
14573
+ ...binding,
14574
+ actions: (binding.actions ?? []).map(
14575
+ (entry, entryIndex) => entryIndex === index ? patch(entry) : entry
14576
+ )
14577
+ }));
14578
+ const removeAction = (index) => {
14579
+ updateBinding(selectedItem, (binding) => ({
14580
+ ...binding,
14581
+ actions: (binding.actions ?? []).filter(
14582
+ (_, entryIndex) => entryIndex !== index
14583
+ )
14584
+ }));
14585
+ setCustomRows((previous) => {
14586
+ const next = /* @__PURE__ */ new Set();
14587
+ previous.forEach((row) => {
14588
+ if (row < index) next.add(row);
14589
+ else if (row > index) next.add(row - 1);
14590
+ });
14591
+ return next;
14592
+ });
14593
+ };
14594
+ const setCustomRow = (index, custom) => setCustomRows((previous) => {
14595
+ const next = new Set(previous);
14596
+ if (custom) next.add(index);
14597
+ else next.delete(index);
14598
+ return next;
14599
+ });
14469
14600
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(BBSection, { title: "Actions", open, onToggle, children: [
14470
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "bb-actionList", children: (selectedBinding.actions ?? []).map((action, index) => (
14471
- // Keyed by position, not action.id: the id is an editable field, so
14472
- // keying on it would remount the card on every keystroke and drop
14473
- // input focus.
14474
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-actionCard", children: [
14475
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-actionCardHeader", children: [
14476
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-actionCardTitle", children: [
14477
- "Action ",
14478
- index + 1
14479
- ] }),
14480
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "bb-actionCardMeta", children: action.type })
14481
- ] }),
14482
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-actionGrid", children: [
14483
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-field", children: [
14484
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("label", { className: "bb-label", children: "Action ID" }),
14485
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
14486
- "input",
14487
- {
14488
- className: "bb-input",
14489
- value: action.id,
14490
- onChange: (event) => updateBinding(selectedItem, (binding) => ({
14491
- ...binding,
14492
- actions: (binding.actions ?? []).map(
14493
- (entry, entryIndex) => entryIndex === index ? { ...entry, id: event.target.value } : entry
14494
- )
14495
- }))
14496
- }
14497
- )
14601
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "bb-actionList", children: actions.map((action, index) => {
14602
+ const catalogEntry = findCatalogAction(action.id);
14603
+ const isCustom = customRows.has(index) || !catalogEntry;
14604
+ const trimmedId = action.id.trim();
14605
+ const suggestion = catalogEntry ? null : suggestActionId(action.id);
14606
+ const isDuplicate = trimmedId.length > 0 && actions.some(
14607
+ (other, otherIndex) => otherIndex !== index && other.id.trim() === trimmedId
14608
+ );
14609
+ return (
14610
+ // Keyed by position, not action.id: the id is an editable field, so
14611
+ // keying on it would remount the card on every keystroke and drop
14612
+ // input focus.
14613
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-actionCard", children: [
14614
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-actionCardHeader", children: [
14615
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-actionCardTitle", children: [
14616
+ "Action ",
14617
+ index + 1
14618
+ ] }),
14619
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "bb-actionCardMeta", children: action.type })
14498
14620
  ] }),
14499
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-field", children: [
14500
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("label", { className: "bb-label", children: "Label" }),
14501
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
14502
- "input",
14503
- {
14504
- className: "bb-input",
14505
- value: action.label,
14506
- onChange: (event) => updateBinding(selectedItem, (binding) => ({
14507
- ...binding,
14508
- actions: (binding.actions ?? []).map(
14509
- (entry, entryIndex) => entryIndex === index ? { ...entry, label: event.target.value } : entry
14510
- )
14511
- }))
14512
- }
14513
- )
14514
- ] })
14515
- ] }),
14516
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "bb-actionFooter", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
14517
- "button",
14518
- {
14519
- type: "button",
14520
- className: "bb-button bb-button--ghost bb-actionRemove",
14521
- onClick: () => updateBinding(selectedItem, (binding) => ({
14522
- ...binding,
14523
- actions: (binding.actions ?? []).filter(
14524
- (_, entryIndex) => entryIndex !== index
14621
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-actionGrid", children: [
14622
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-field", children: [
14623
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("label", { className: "bb-label", children: "Action ID" }),
14624
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
14625
+ "select",
14626
+ {
14627
+ className: "bb-select",
14628
+ value: isCustom ? CUSTOM_ACTION_ID : action.id,
14629
+ onChange: (event) => {
14630
+ const value = event.target.value;
14631
+ if (value === CUSTOM_ACTION_ID) {
14632
+ setCustomRow(index, true);
14633
+ return;
14634
+ }
14635
+ setCustomRow(index, false);
14636
+ const picked = findCatalogAction(value);
14637
+ updateAction(index, (current) => ({
14638
+ ...current,
14639
+ id: value,
14640
+ // Keep wording the author typed themselves.
14641
+ label: picked && isAutoFilledLabel(current.label) ? picked.label : current.label
14642
+ }));
14643
+ },
14644
+ children: [
14645
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("optgroup", { label: "Handled by the viewer", children: VIEWER_ACTIONS.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("option", { value: entry.id, children: entry.id }, entry.id)) }),
14646
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("optgroup", { label: "Handled by your app", children: HOST_ACTIONS.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("option", { value: entry.id, children: entry.id }, entry.id)) }),
14647
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("option", { value: CUSTOM_ACTION_ID, children: "Custom\u2026" })
14648
+ ]
14649
+ }
14525
14650
  )
14526
- })),
14527
- children: "Remove Action"
14528
- }
14529
- ) })
14530
- ] }, index)
14531
- )) }),
14651
+ ] }),
14652
+ isCustom ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-field", children: [
14653
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("label", { className: "bb-label", children: "Custom action ID" }),
14654
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
14655
+ "input",
14656
+ {
14657
+ className: "bb-input",
14658
+ value: action.id,
14659
+ placeholder: "open-datasheet",
14660
+ onChange: (event) => {
14661
+ setCustomRow(index, true);
14662
+ updateAction(index, (entry) => ({
14663
+ ...entry,
14664
+ id: event.target.value
14665
+ }));
14666
+ }
14667
+ }
14668
+ )
14669
+ ] }) : null,
14670
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-field", children: [
14671
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("label", { className: "bb-label", children: "Label" }),
14672
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
14673
+ "input",
14674
+ {
14675
+ className: "bb-input",
14676
+ value: action.label,
14677
+ onChange: (event) => updateAction(index, (entry) => ({
14678
+ ...entry,
14679
+ label: event.target.value
14680
+ }))
14681
+ }
14682
+ )
14683
+ ] }),
14684
+ catalogEntry ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "bb-note", children: catalogEntry.description }) : trimmedId.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "bb-note", style: { color: "#fca5a5" }, children: "An action ID is required \u2014 the button renders but can never be identified in onAction." }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "bb-note", style: { color: "#fcd34d" }, children: [
14685
+ "Unrecognised ID \u2014 the viewer has no built-in behaviour for it, so your app must handle it in onAction.",
14686
+ suggestion ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
14687
+ " Did you mean ",
14688
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
14689
+ "button",
14690
+ {
14691
+ type: "button",
14692
+ className: "bb-linkButton",
14693
+ onClick: () => {
14694
+ setCustomRow(index, false);
14695
+ const picked = findCatalogAction(suggestion);
14696
+ updateAction(index, (current) => ({
14697
+ ...current,
14698
+ id: suggestion,
14699
+ label: picked && isAutoFilledLabel(current.label) ? picked.label : current.label
14700
+ }));
14701
+ },
14702
+ children: suggestion
14703
+ }
14704
+ ),
14705
+ "?"
14706
+ ] }) : null
14707
+ ] }),
14708
+ isDuplicate ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "bb-note", style: { color: "#fcd34d" }, children: "Another action on this binding uses the same ID \u2014 they will render as two identical buttons." }) : null
14709
+ ] }),
14710
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "bb-actionFooter", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
14711
+ "button",
14712
+ {
14713
+ type: "button",
14714
+ className: "bb-button bb-button--ghost bb-actionRemove",
14715
+ onClick: () => removeAction(index),
14716
+ children: "Remove Action"
14717
+ }
14718
+ ) })
14719
+ ] }, index)
14720
+ );
14721
+ }) }),
14532
14722
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { style: { marginTop: 10 }, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
14533
14723
  "button",
14534
14724
  {
14535
14725
  type: "button",
14536
14726
  className: "bb-button",
14537
- onClick: () => updateBinding(selectedItem, (binding) => ({
14538
- ...binding,
14539
- actions: [
14540
- ...binding.actions ?? [],
14541
- {
14542
- id: "new-action",
14543
- label: "New Action",
14544
- type: "command"
14545
- }
14546
- ]
14547
- })),
14727
+ onClick: () => {
14728
+ const unused = VIEWER_ACTIONS.find(
14729
+ (entry) => !actions.some((action) => action.id === entry.id)
14730
+ );
14731
+ updateBinding(selectedItem, (binding) => ({
14732
+ ...binding,
14733
+ actions: [
14734
+ ...binding.actions ?? [],
14735
+ {
14736
+ id: unused?.id ?? "",
14737
+ label: unused?.label ?? "New Action",
14738
+ type: "command"
14739
+ }
14740
+ ]
14741
+ }));
14742
+ if (!unused) setCustomRow(actions.length, true);
14743
+ },
14548
14744
  children: "Add Action"
14549
14745
  }
14550
14746
  ) })
@@ -14658,7 +14854,7 @@ function CameraPositionPanel({
14658
14854
  }
14659
14855
 
14660
14856
  // src/components/BindingBuilder/components/SceneNodesPanel.tsx
14661
- var import_react37 = require("react");
14857
+ var import_react38 = require("react");
14662
14858
 
14663
14859
  // src/components/BindingBuilder/utils/bindingFactory.ts
14664
14860
  function slugify2(value) {
@@ -14747,9 +14943,9 @@ function SceneNodesPanel({
14747
14943
  isOpen = true,
14748
14944
  onToggle
14749
14945
  }) {
14750
- const [search, setSearch] = (0, import_react37.useState)("");
14751
- const nodeKeys = (0, import_react37.useMemo)(() => Object.keys(bindings), [bindings]);
14752
- const filteredNodeKeys = (0, import_react37.useMemo)(() => {
14946
+ const [search, setSearch] = (0, import_react38.useState)("");
14947
+ const nodeKeys = (0, import_react38.useMemo)(() => Object.keys(bindings), [bindings]);
14948
+ const filteredNodeKeys = (0, import_react38.useMemo)(() => {
14753
14949
  const query = search.trim().toLowerCase();
14754
14950
  if (!query) return nodeKeys;
14755
14951
  return nodeKeys.filter((nodeKey) => {
@@ -14892,7 +15088,7 @@ function mergeAnimationConfig(sceneConfig, animationDetails) {
14892
15088
  }
14893
15089
 
14894
15090
  // src/components/BindingBuilder/hooks/useUndoableState.ts
14895
- var import_react38 = require("react");
15091
+ var import_react39 = require("react");
14896
15092
  var MAX_HISTORY = 100;
14897
15093
  function historyReducer(state, action) {
14898
15094
  switch (action.type) {
@@ -14931,13 +15127,13 @@ function historyReducer(state, action) {
14931
15127
  }
14932
15128
  }
14933
15129
  function useUndoableState(initial, coalesceWindowMs = 600) {
14934
- const [state, dispatch] = (0, import_react38.useReducer)(historyReducer, {
15130
+ const [state, dispatch] = (0, import_react39.useReducer)(historyReducer, {
14935
15131
  past: [],
14936
15132
  present: initial,
14937
15133
  future: []
14938
15134
  });
14939
- const lastSetAt = (0, import_react38.useRef)(0);
14940
- const setValue = (0, import_react38.useCallback)(
15135
+ const lastSetAt = (0, import_react39.useRef)(0);
15136
+ const setValue = (0, import_react39.useCallback)(
14941
15137
  (updater) => {
14942
15138
  const now = Date.now();
14943
15139
  const coalesce = now - lastSetAt.current < coalesceWindowMs;
@@ -14946,15 +15142,15 @@ function useUndoableState(initial, coalesceWindowMs = 600) {
14946
15142
  },
14947
15143
  [coalesceWindowMs]
14948
15144
  );
14949
- const undo = (0, import_react38.useCallback)(() => {
15145
+ const undo = (0, import_react39.useCallback)(() => {
14950
15146
  lastSetAt.current = 0;
14951
15147
  dispatch({ type: "undo" });
14952
15148
  }, []);
14953
- const redo = (0, import_react38.useCallback)(() => {
15149
+ const redo = (0, import_react39.useCallback)(() => {
14954
15150
  lastSetAt.current = 0;
14955
15151
  dispatch({ type: "redo" });
14956
15152
  }, []);
14957
- const reset = (0, import_react38.useCallback)((value) => {
15153
+ const reset = (0, import_react39.useCallback)((value) => {
14958
15154
  lastSetAt.current = 0;
14959
15155
  dispatch({ type: "reset", value });
14960
15156
  }, []);
@@ -15200,39 +15396,39 @@ function mergeSceneConfigSection(current, incoming) {
15200
15396
  return next;
15201
15397
  }
15202
15398
  function BindingBuilder({ licenseKey }) {
15203
- const modelInputRef = (0, import_react39.useRef)(null);
15204
- const bindingsInputRef = (0, import_react39.useRef)(null);
15205
- const sceneConfigInputRef = (0, import_react39.useRef)(null);
15206
- const [modelUrl, setModelUrl] = (0, import_react39.useState)(null);
15207
- const [modelName, setModelName] = (0, import_react39.useState)("");
15208
- const [selectedItem, setSelectedItem] = (0, import_react39.useState)(null);
15399
+ const modelInputRef = (0, import_react40.useRef)(null);
15400
+ const bindingsInputRef = (0, import_react40.useRef)(null);
15401
+ const sceneConfigInputRef = (0, import_react40.useRef)(null);
15402
+ const [modelUrl, setModelUrl] = (0, import_react40.useState)(null);
15403
+ const [modelName, setModelName] = (0, import_react40.useState)("");
15404
+ const [selectedItem, setSelectedItem] = (0, import_react40.useState)(null);
15209
15405
  const bindingsHistory = useUndoableState({});
15210
15406
  const bindings = bindingsHistory.value;
15211
15407
  const setBindings = bindingsHistory.setValue;
15212
- const [loading, setLoading] = (0, import_react39.useState)(false);
15213
- const [isExporting, setIsExporting] = (0, import_react39.useState)(false);
15214
- const [statusMessage, setStatusMessage] = (0, import_react39.useState)(
15408
+ const [loading, setLoading] = (0, import_react40.useState)(false);
15409
+ const [isExporting, setIsExporting] = (0, import_react40.useState)(false);
15410
+ const [statusMessage, setStatusMessage] = (0, import_react40.useState)(
15215
15411
  "Load the demo model or upload a GLB/GLTF model to generate bindings."
15216
15412
  );
15217
- const [statusTone, setStatusTone] = (0, import_react39.useState)("default");
15218
- const [statusBannerVisible, setStatusBannerVisible] = (0, import_react39.useState)(false);
15219
- const [metricsDraft, setMetricsDraft] = (0, import_react39.useState)("{}");
15220
- const [metadataDraft, setMetadataDraft] = (0, import_react39.useState)("{}");
15221
- const [metricsError, setMetricsError] = (0, import_react39.useState)(null);
15222
- const [metadataError, setMetadataError] = (0, import_react39.useState)(null);
15223
- const [isPreviewDragActive, setIsPreviewDragActive] = (0, import_react39.useState)(false);
15224
- const [isEditorCollapsed, setIsEditorCollapsed] = (0, import_react39.useState)(false);
15225
- const [isNodesCollapsed, setIsNodesCollapsed] = (0, import_react39.useState)(false);
15226
- const [openObjectSections, setOpenObjectSections] = (0, import_react39.useState)({
15413
+ const [statusTone, setStatusTone] = (0, import_react40.useState)("default");
15414
+ const [statusBannerVisible, setStatusBannerVisible] = (0, import_react40.useState)(false);
15415
+ const [metricsDraft, setMetricsDraft] = (0, import_react40.useState)("{}");
15416
+ const [metadataDraft, setMetadataDraft] = (0, import_react40.useState)("{}");
15417
+ const [metricsError, setMetricsError] = (0, import_react40.useState)(null);
15418
+ const [metadataError, setMetadataError] = (0, import_react40.useState)(null);
15419
+ const [isPreviewDragActive, setIsPreviewDragActive] = (0, import_react40.useState)(false);
15420
+ const [isEditorCollapsed, setIsEditorCollapsed] = (0, import_react40.useState)(false);
15421
+ const [isNodesCollapsed, setIsNodesCollapsed] = (0, import_react40.useState)(false);
15422
+ const [openObjectSections, setOpenObjectSections] = (0, import_react40.useState)({
15227
15423
  identity: true,
15228
15424
  basics: true,
15229
- material: true,
15425
+ material: false,
15230
15426
  actions: false,
15231
15427
  camera: false,
15232
15428
  metrics: false,
15233
15429
  metadata: false
15234
15430
  });
15235
- const [openMaterialPanels, setOpenMaterialPanels] = (0, import_react39.useState)({
15431
+ const [openMaterialPanels, setOpenMaterialPanels] = (0, import_react40.useState)({
15236
15432
  pbr: true,
15237
15433
  roughness: false,
15238
15434
  emission: false,
@@ -15248,27 +15444,27 @@ function BindingBuilder({ licenseKey }) {
15248
15444
  });
15249
15445
  const toggleObjectSection = (key) => setOpenObjectSections((prev) => ({ ...prev, [key]: !prev[key] }));
15250
15446
  const toggleMaterialPanel = (key) => setOpenMaterialPanels((prev) => ({ ...prev, [key]: !prev[key] }));
15251
- const [activeEditorTab, setActiveEditorTab] = (0, import_react39.useState)(
15447
+ const [activeEditorTab, setActiveEditorTab] = (0, import_react40.useState)(
15252
15448
  "object"
15253
15449
  );
15254
- const [activeSubTab, setActiveSubTab] = (0, import_react39.useState)("General");
15255
- const [pbrWorkflow, setPbrWorkflow] = (0, import_react39.useState)("metalness");
15256
- const [materialPanelEnabled, setMaterialPanelEnabled] = (0, import_react39.useState)(createMaterialPanelEnabledState());
15257
- const materialPanelItemRef = (0, import_react39.useRef)(null);
15258
- const [zoomOnSelected, setZoomOnSelected] = (0, import_react39.useState)(false);
15259
- const [highlightOnHover, setHighlightOnHover] = (0, import_react39.useState)(true);
15260
- const [resetDialogOpen, setResetDialogOpen] = (0, import_react39.useState)(false);
15261
- const [resetTarget, setResetTarget] = (0, import_react39.useState)(
15450
+ const [activeSubTab, setActiveSubTab] = (0, import_react40.useState)("General");
15451
+ const [pbrWorkflow, setPbrWorkflow] = (0, import_react40.useState)("metalness");
15452
+ const [materialPanelEnabled, setMaterialPanelEnabled] = (0, import_react40.useState)(createMaterialPanelEnabledState());
15453
+ const materialPanelItemRef = (0, import_react40.useRef)(null);
15454
+ const [zoomOnSelected, setZoomOnSelected] = (0, import_react40.useState)(false);
15455
+ const [highlightOnHover, setHighlightOnHover] = (0, import_react40.useState)(true);
15456
+ const [resetDialogOpen, setResetDialogOpen] = (0, import_react40.useState)(false);
15457
+ const [resetTarget, setResetTarget] = (0, import_react40.useState)(
15262
15458
  null
15263
15459
  );
15264
15460
  const sceneConfigHistory = useUndoableState(DEFAULT_SCENE_CONFIG);
15265
15461
  const sceneConfig = sceneConfigHistory.value;
15266
15462
  const setSceneConfig = sceneConfigHistory.setValue;
15267
- const [activeAnnotation, setActiveAnnotation] = (0, import_react39.useState)(null);
15268
- const [panelHoveredBinding, setPanelHoveredBinding] = (0, import_react39.useState)(null);
15463
+ const [activeAnnotation, setActiveAnnotation] = (0, import_react40.useState)(null);
15464
+ const [panelHoveredBinding, setPanelHoveredBinding] = (0, import_react40.useState)(null);
15269
15465
  const license = useLicense(licenseKey);
15270
15466
  const tier = normalizeTier(license.tier);
15271
- const bindingBuilderContextValue = (0, import_react39.useMemo)(
15467
+ const bindingBuilderContextValue = (0, import_react40.useMemo)(
15272
15468
  () => ({
15273
15469
  isInsideEditor: true,
15274
15470
  activeTab: activeEditorTab,
@@ -15277,11 +15473,11 @@ function BindingBuilder({ licenseKey }) {
15277
15473
  }),
15278
15474
  [activeEditorTab, activeSubTab, tier]
15279
15475
  );
15280
- const uploadedModelRef = (0, import_react39.useRef)(null);
15281
- const previewDragDepthRef = (0, import_react39.useRef)(0);
15282
- const loadRequestRef = (0, import_react39.useRef)(0);
15283
- const pendingLoadRef = (0, import_react39.useRef)(null);
15284
- const loadedSceneRef = (0, import_react39.useRef)(null);
15476
+ const uploadedModelRef = (0, import_react40.useRef)(null);
15477
+ const previewDragDepthRef = (0, import_react40.useRef)(0);
15478
+ const loadRequestRef = (0, import_react40.useRef)(0);
15479
+ const pendingLoadRef = (0, import_react40.useRef)(null);
15480
+ const loadedSceneRef = (0, import_react40.useRef)(null);
15285
15481
  const {
15286
15482
  cameraState,
15287
15483
  resetView,
@@ -15291,7 +15487,7 @@ function BindingBuilder({ licenseKey }) {
15291
15487
  handleCameraChange,
15292
15488
  handleViewerReady
15293
15489
  } = useViewerCamera();
15294
- const handlePreviewWaypoint = (0, import_react39.useCallback)(
15490
+ const handlePreviewWaypoint = (0, import_react40.useCallback)(
15295
15491
  (waypoint) => {
15296
15492
  setCameraState(
15297
15493
  {
@@ -15315,7 +15511,7 @@ function BindingBuilder({ licenseKey }) {
15315
15511
  position: formatVector3(cameraState.position),
15316
15512
  target: formatVector3(cameraState.target)
15317
15513
  } : null;
15318
- const handleAnnotationsChange = (0, import_react39.useCallback)(
15514
+ const handleAnnotationsChange = (0, import_react40.useCallback)(
15319
15515
  (newAnnotations) => {
15320
15516
  setSceneConfig((prev) => {
15321
15517
  const currentIds = prev.annotations.map((a) => a.id).join(",");
@@ -15331,7 +15527,7 @@ function BindingBuilder({ licenseKey }) {
15331
15527
  },
15332
15528
  []
15333
15529
  );
15334
- const handleActiveAnnotationChange = (0, import_react39.useCallback)(
15530
+ const handleActiveAnnotationChange = (0, import_react40.useCallback)(
15335
15531
  (annotation) => {
15336
15532
  setActiveAnnotation(
15337
15533
  annotation ? {
@@ -15341,7 +15537,7 @@ function BindingBuilder({ licenseKey }) {
15341
15537
  },
15342
15538
  []
15343
15539
  );
15344
- (0, import_react39.useEffect)(() => {
15540
+ (0, import_react40.useEffect)(() => {
15345
15541
  if (!selectedBinding) {
15346
15542
  setMetricsDraft("{}");
15347
15543
  setMetadataDraft("{}");
@@ -15354,7 +15550,7 @@ function BindingBuilder({ licenseKey }) {
15354
15550
  setMetricsError(null);
15355
15551
  setMetadataError(null);
15356
15552
  }, [selectedBinding]);
15357
- (0, import_react39.useEffect)(() => {
15553
+ (0, import_react40.useEffect)(() => {
15358
15554
  const derived = createMaterialPanelEnabledState(selectedMaterial);
15359
15555
  if (materialPanelItemRef.current !== selectedItem) {
15360
15556
  materialPanelItemRef.current = selectedItem;
@@ -15373,12 +15569,12 @@ function BindingBuilder({ licenseKey }) {
15373
15569
  return changed ? next : prev;
15374
15570
  });
15375
15571
  }, [selectedItem, selectedMaterial]);
15376
- (0, import_react39.useEffect)(() => {
15572
+ (0, import_react40.useEffect)(() => {
15377
15573
  return () => {
15378
15574
  uploadedModelRef.current?.cleanup();
15379
15575
  };
15380
15576
  }, []);
15381
- (0, import_react39.useEffect)(() => {
15577
+ (0, import_react40.useEffect)(() => {
15382
15578
  const handleKeyDown = (event) => {
15383
15579
  if (event.key.toLowerCase() !== "z" || !(event.metaKey || event.ctrlKey)) {
15384
15580
  return;
@@ -15399,7 +15595,7 @@ function BindingBuilder({ licenseKey }) {
15399
15595
  window.addEventListener("keydown", handleKeyDown);
15400
15596
  return () => window.removeEventListener("keydown", handleKeyDown);
15401
15597
  }, [activeEditorTab, bindingsHistory, sceneConfigHistory]);
15402
- (0, import_react39.useEffect)(() => {
15598
+ (0, import_react40.useEffect)(() => {
15403
15599
  if (loading || statusTone === "default" || statusTone === "success") return;
15404
15600
  setStatusBannerVisible(true);
15405
15601
  const timeout = window.setTimeout(
@@ -15408,8 +15604,8 @@ function BindingBuilder({ licenseKey }) {
15408
15604
  );
15409
15605
  return () => window.clearTimeout(timeout);
15410
15606
  }, [statusMessage, statusTone, loading]);
15411
- const nodeKeys = (0, import_react39.useMemo)(() => Object.keys(bindings), [bindings]);
15412
- const idError = (0, import_react39.useMemo)(() => {
15607
+ const nodeKeys = (0, import_react40.useMemo)(() => Object.keys(bindings), [bindings]);
15608
+ const idError = (0, import_react40.useMemo)(() => {
15413
15609
  if (!selectedItem || !selectedBinding) return null;
15414
15610
  const trimmed = selectedBinding.id.trim();
15415
15611
  if (!trimmed) return "ID is required.";
@@ -15452,7 +15648,7 @@ function BindingBuilder({ licenseKey }) {
15452
15648
  });
15453
15649
  }
15454
15650
  };
15455
- const applyBindingsFromScene = (0, import_react39.useCallback)(
15651
+ const applyBindingsFromScene = (0, import_react40.useCallback)(
15456
15652
  (scene, nextModelName) => {
15457
15653
  const nodeNames = collectNodeNames(scene);
15458
15654
  bindingsHistory.reset(buildBindingsFromNodes(nodeNames));
@@ -15491,7 +15687,7 @@ function BindingBuilder({ licenseKey }) {
15491
15687
  bindingsHistory.reset({});
15492
15688
  setSelectedItem(null);
15493
15689
  };
15494
- const handleViewerModelLoaded = (0, import_react39.useCallback)(
15690
+ const handleViewerModelLoaded = (0, import_react40.useCallback)(
15495
15691
  (scene) => {
15496
15692
  const pending = pendingLoadRef.current;
15497
15693
  if (!pending || pending.requestId !== loadRequestRef.current) return;
@@ -15501,7 +15697,7 @@ function BindingBuilder({ licenseKey }) {
15501
15697
  },
15502
15698
  [applyBindingsFromScene]
15503
15699
  );
15504
- const handleViewerLoadError = (0, import_react39.useCallback)((error) => {
15700
+ const handleViewerLoadError = (0, import_react40.useCallback)((error) => {
15505
15701
  const pending = pendingLoadRef.current;
15506
15702
  if (!pending || pending.requestId !== loadRequestRef.current) return;
15507
15703
  pendingLoadRef.current = null;
@@ -15512,7 +15708,7 @@ function BindingBuilder({ licenseKey }) {
15512
15708
  setStatusTone("error");
15513
15709
  setLoading(false);
15514
15710
  }, []);
15515
- const handleAnimationsReady = (0, import_react39.useCallback)(
15711
+ const handleAnimationsReady = (0, import_react40.useCallback)(
15516
15712
  (controls) => {
15517
15713
  viewerAnimations.handleAnimationsReady(controls);
15518
15714
  const details = controls.clipDetails ?? controls.clips.map((sourceName) => ({ sourceName, duration: 0 }));
@@ -15813,7 +16009,7 @@ function BindingBuilder({ licenseKey }) {
15813
16009
  }));
15814
16010
  setError(null);
15815
16011
  };
15816
- const handleViewerSelect = (0, import_react39.useCallback)(
16012
+ const handleViewerSelect = (0, import_react40.useCallback)(
15817
16013
  (binding) => {
15818
16014
  if (!binding) {
15819
16015
  setSelectedItem(null);
@@ -15826,7 +16022,7 @@ function BindingBuilder({ licenseKey }) {
15826
16022
  },
15827
16023
  [bindings]
15828
16024
  );
15829
- const handleSceneNodeSelect = (0, import_react39.useCallback)(
16025
+ const handleSceneNodeSelect = (0, import_react40.useCallback)(
15830
16026
  async (nodeKey) => {
15831
16027
  setSelectedItem(nodeKey);
15832
16028
  setPanelHoveredBinding(null);
@@ -15834,7 +16030,7 @@ function BindingBuilder({ licenseKey }) {
15834
16030
  },
15835
16031
  [focusObject]
15836
16032
  );
15837
- const saveCameraPositionForSelectedObject = (0, import_react39.useCallback)(() => {
16033
+ const saveCameraPositionForSelectedObject = (0, import_react40.useCallback)(() => {
15838
16034
  if (!selectedItem || !cameraState) {
15839
16035
  return;
15840
16036
  }
@@ -15849,7 +16045,7 @@ function BindingBuilder({ licenseKey }) {
15849
16045
  cameraState: currentCameraState
15850
16046
  }));
15851
16047
  }, [selectedItem, cameraState]);
15852
- const goToSavedCameraPosition = (0, import_react39.useCallback)(() => {
16048
+ const goToSavedCameraPosition = (0, import_react40.useCallback)(() => {
15853
16049
  if (!savedCameraPosition?.position || !savedCameraPosition?.target) return;
15854
16050
  setCameraState(
15855
16051
  {
@@ -15861,28 +16057,28 @@ function BindingBuilder({ licenseKey }) {
15861
16057
  true
15862
16058
  );
15863
16059
  }, [savedCameraPosition, setCameraState, cameraState]);
15864
- const clearSavedCameraPosition = (0, import_react39.useCallback)(() => {
16060
+ const clearSavedCameraPosition = (0, import_react40.useCallback)(() => {
15865
16061
  if (!selectedItem) return;
15866
16062
  updateBinding(selectedItem, (binding) => ({
15867
16063
  ...binding,
15868
16064
  cameraState: void 0
15869
16065
  }));
15870
16066
  }, [selectedItem]);
15871
- const handleResetView = (0, import_react39.useCallback)(async () => {
16067
+ const handleResetView = (0, import_react40.useCallback)(async () => {
15872
16068
  await resetView();
15873
16069
  }, [resetView]);
15874
- const handleFitScene = (0, import_react39.useCallback)(async () => {
16070
+ const handleFitScene = (0, import_react40.useCallback)(async () => {
15875
16071
  await fitScene();
15876
16072
  }, [fitScene]);
15877
- const openResetDialog = (0, import_react39.useCallback)((target) => {
16073
+ const openResetDialog = (0, import_react40.useCallback)((target) => {
15878
16074
  setResetTarget(target);
15879
16075
  setResetDialogOpen(true);
15880
16076
  }, []);
15881
- const closeResetDialog = (0, import_react39.useCallback)(() => {
16077
+ const closeResetDialog = (0, import_react40.useCallback)(() => {
15882
16078
  setResetDialogOpen(false);
15883
16079
  setResetTarget(null);
15884
16080
  }, []);
15885
- const executeReset = (0, import_react39.useCallback)(() => {
16081
+ const executeReset = (0, import_react40.useCallback)(() => {
15886
16082
  if (resetTarget === "bindings") {
15887
16083
  if (!nodeKeys.length) {
15888
16084
  closeResetDialog();
@@ -16440,7 +16636,7 @@ function BindingBuilder({ licenseKey }) {
16440
16636
  }
16441
16637
 
16442
16638
  // src/components/SimpleModelViewer/SimpleModelViewer.tsx
16443
- var import_react40 = require("react");
16639
+ var import_react41 = require("react");
16444
16640
  var import_drei6 = require("@react-three/drei");
16445
16641
  var import_fiber12 = require("@react-three/fiber");
16446
16642
  var import_fiber13 = require("@react-three/fiber");
@@ -16480,7 +16676,7 @@ function applyClippingPlanes(scene, controls) {
16480
16676
  camera.updateProjectionMatrix();
16481
16677
  controls.maxDistance = size * 10;
16482
16678
  }
16483
- var ModelErrorBoundary2 = class extends import_react40.Component {
16679
+ var ModelErrorBoundary2 = class extends import_react41.Component {
16484
16680
  constructor() {
16485
16681
  super(...arguments);
16486
16682
  this.state = { hasError: false };
@@ -16512,12 +16708,12 @@ function InnerModel({
16512
16708
  meshopt: true
16513
16709
  });
16514
16710
  const invalidate = (0, import_fiber12.useThree)((state) => state.invalidate);
16515
- const [hoveredKey, setHoveredKey] = (0, import_react40.useState)(null);
16516
- const scene = (0, import_react40.useMemo)(
16711
+ const [hoveredKey, setHoveredKey] = (0, import_react41.useState)(null);
16712
+ const scene = (0, import_react41.useMemo)(
16517
16713
  () => import_three_stdlib4.SkeletonUtils.clone(rawScene),
16518
16714
  [rawScene]
16519
16715
  );
16520
- const meshMap = (0, import_react40.useMemo)(() => {
16716
+ const meshMap = (0, import_react41.useMemo)(() => {
16521
16717
  const map = {};
16522
16718
  scene.traverse((obj) => {
16523
16719
  if (!isMeshNode2(obj)) return;
@@ -16526,7 +16722,7 @@ function InnerModel({
16526
16722
  });
16527
16723
  return map;
16528
16724
  }, [scene]);
16529
- const perKeyMaterials = (0, import_react40.useMemo)(() => {
16725
+ const perKeyMaterials = (0, import_react41.useMemo)(() => {
16530
16726
  const map = {};
16531
16727
  for (const [key, { ref }] of Object.entries(meshMap)) {
16532
16728
  const original = Array.isArray(ref.material) ? ref.material[0] : ref.material;
@@ -16542,14 +16738,14 @@ function InnerModel({
16542
16738
  }
16543
16739
  return map;
16544
16740
  }, [meshMap]);
16545
- (0, import_react40.useEffect)(
16741
+ (0, import_react41.useEffect)(
16546
16742
  () => () => {
16547
16743
  Object.values(perKeyMaterials).forEach((m) => m.dispose());
16548
16744
  },
16549
16745
  [perKeyMaterials]
16550
16746
  );
16551
- const notifiedUrl = (0, import_react40.useRef)(null);
16552
- (0, import_react40.useEffect)(() => {
16747
+ const notifiedUrl = (0, import_react41.useRef)(null);
16748
+ (0, import_react41.useEffect)(() => {
16553
16749
  if (notifiedUrl.current === url) return;
16554
16750
  notifiedUrl.current = url;
16555
16751
  scene.updateWorldMatrix(true, true);
@@ -16557,7 +16753,7 @@ function InnerModel({
16557
16753
  onMeshesReady(meshMap);
16558
16754
  onModelLoaded(scene);
16559
16755
  }, [url, scene, meshMap, onMeshesReady, onModelLoaded]);
16560
- (0, import_react40.useEffect)(() => {
16756
+ (0, import_react41.useEffect)(() => {
16561
16757
  const controller = new AbortController();
16562
16758
  void buildSceneBVH(scene, controller.signal);
16563
16759
  return () => {
@@ -16565,26 +16761,26 @@ function InnerModel({
16565
16761
  disposeSceneBVH(scene);
16566
16762
  };
16567
16763
  }, [scene]);
16568
- const pointerRaycastEnabled = (0, import_react40.useMemo)(
16764
+ const pointerRaycastEnabled = (0, import_react41.useMemo)(
16569
16765
  () => !exceedsBvhBudget(scene),
16570
16766
  [scene]
16571
16767
  );
16572
- (0, import_react40.useEffect)(() => {
16768
+ (0, import_react41.useEffect)(() => {
16573
16769
  for (const [key, { ref }] of Object.entries(meshMap)) {
16574
16770
  nodeRefsOut.current[key] = ref;
16575
16771
  }
16576
16772
  }, [meshMap, nodeRefsOut]);
16577
- (0, import_react40.useEffect)(() => {
16773
+ (0, import_react41.useEffect)(() => {
16578
16774
  if (!highlightOnHover && hoveredKey !== null) {
16579
16775
  setHoveredKey(null);
16580
16776
  }
16581
16777
  }, [highlightOnHover, hoveredKey]);
16582
- (0, import_react40.useEffect)(() => {
16778
+ (0, import_react41.useEffect)(() => {
16583
16779
  return () => {
16584
16780
  document.body.style.cursor = "default";
16585
16781
  };
16586
16782
  }, []);
16587
- (0, import_react40.useEffect)(() => {
16783
+ (0, import_react41.useEffect)(() => {
16588
16784
  for (const [key, { ref }] of Object.entries(meshMap)) {
16589
16785
  ref.visible = !hiddenKeys.has(key);
16590
16786
  const mat = perKeyMaterials[key];
@@ -16654,11 +16850,11 @@ function CameraFitter({
16654
16850
  refitOnResize,
16655
16851
  onInitialFitComplete
16656
16852
  }) {
16657
- const fitted = (0, import_react40.useRef)(false);
16853
+ const fitted = (0, import_react41.useRef)(false);
16658
16854
  const invalidate = (0, import_fiber12.useThree)((state) => state.invalidate);
16659
16855
  const size = (0, import_fiber12.useThree)((state) => state.size);
16660
- const resizeTimerRef = (0, import_react40.useRef)(null);
16661
- (0, import_react40.useEffect)(() => {
16856
+ const resizeTimerRef = (0, import_react41.useRef)(null);
16857
+ (0, import_react41.useEffect)(() => {
16662
16858
  fitted.current = false;
16663
16859
  invalidate();
16664
16860
  }, [invalidate, scene]);
@@ -16678,7 +16874,7 @@ function CameraFitter({
16678
16874
  }).catch(() => {
16679
16875
  }).finally(onInitialFitComplete);
16680
16876
  });
16681
- (0, import_react40.useEffect)(() => {
16877
+ (0, import_react41.useEffect)(() => {
16682
16878
  if (!refitOnResize || !fitted.current || !scene || !controlsRef.current) {
16683
16879
  return;
16684
16880
  }
@@ -16706,7 +16902,7 @@ function CameraFitter({
16706
16902
  function ExposureController({ exposure }) {
16707
16903
  const gl = (0, import_fiber12.useThree)((state) => state.gl);
16708
16904
  const invalidate = (0, import_fiber12.useThree)((state) => state.invalidate);
16709
- (0, import_react40.useEffect)(() => {
16905
+ (0, import_react41.useEffect)(() => {
16710
16906
  gl.toneMappingExposure = exposure;
16711
16907
  invalidate();
16712
16908
  }, [gl, exposure, invalidate]);
@@ -16814,7 +17010,7 @@ function EnvironmentSection({
16814
17010
  title,
16815
17011
  children
16816
17012
  }) {
16817
- const [collapsed, setCollapsed] = (0, import_react40.useState)(false);
17013
+ const [collapsed, setCollapsed] = (0, import_react41.useState)(false);
16818
17014
  return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "env-section", children: [
16819
17015
  /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
16820
17016
  "button",
@@ -16979,8 +17175,8 @@ function SceneObjectsPanel2({
16979
17175
  onHover,
16980
17176
  onToggleVisibility
16981
17177
  }) {
16982
- const [search, setSearch] = (0, import_react40.useState)("");
16983
- const [rowHovered, setRowHovered] = (0, import_react40.useState)(null);
17178
+ const [search, setSearch] = (0, import_react41.useState)("");
17179
+ const [rowHovered, setRowHovered] = (0, import_react41.useState)(null);
16984
17180
  const entries = Object.values(meshKeys).filter(
16985
17181
  (m) => m.label.toLowerCase().includes(search.toLowerCase())
16986
17182
  );
@@ -17104,35 +17300,35 @@ function SimpleModelViewer({
17104
17300
  onModelLoaded,
17105
17301
  onLoadError
17106
17302
  }) {
17107
- const cameraControlsRef = (0, import_react40.useRef)(null);
17108
- const nodeRefsOut = (0, import_react40.useRef)({});
17109
- const fileInputRef = (0, import_react40.useRef)(null);
17110
- const uploadedModelRef = (0, import_react40.useRef)(null);
17111
- const dragDepthRef = (0, import_react40.useRef)(0);
17112
- const [loadedScene, setLoadedScene] = (0, import_react40.useState)(null);
17113
- const [largeModelTriangles, setLargeModelTriangles] = (0, import_react40.useState)(
17303
+ const cameraControlsRef = (0, import_react41.useRef)(null);
17304
+ const nodeRefsOut = (0, import_react41.useRef)({});
17305
+ const fileInputRef = (0, import_react41.useRef)(null);
17306
+ const uploadedModelRef = (0, import_react41.useRef)(null);
17307
+ const dragDepthRef = (0, import_react41.useRef)(0);
17308
+ const [loadedScene, setLoadedScene] = (0, import_react41.useState)(null);
17309
+ const [largeModelTriangles, setLargeModelTriangles] = (0, import_react41.useState)(
17114
17310
  null
17115
17311
  );
17116
- const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = (0, import_react40.useState)(false);
17117
- const [meshKeys, setMeshKeys] = (0, import_react40.useState)({});
17118
- const [hiddenKeys, setHiddenKeys] = (0, import_react40.useState)(/* @__PURE__ */ new Set());
17119
- const [selectedKey, setSelectedKey] = (0, import_react40.useState)(null);
17120
- const [panelHoveredKey, setPanelHoveredKey] = (0, import_react40.useState)(null);
17121
- const [uploadedModelUrl, setUploadedModelUrl] = (0, import_react40.useState)(null);
17122
- const [uploadedModelName, setUploadedModelName] = (0, import_react40.useState)(
17312
+ const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = (0, import_react41.useState)(false);
17313
+ const [meshKeys, setMeshKeys] = (0, import_react41.useState)({});
17314
+ const [hiddenKeys, setHiddenKeys] = (0, import_react41.useState)(/* @__PURE__ */ new Set());
17315
+ const [selectedKey, setSelectedKey] = (0, import_react41.useState)(null);
17316
+ const [panelHoveredKey, setPanelHoveredKey] = (0, import_react41.useState)(null);
17317
+ const [uploadedModelUrl, setUploadedModelUrl] = (0, import_react41.useState)(null);
17318
+ const [uploadedModelName, setUploadedModelName] = (0, import_react41.useState)(
17123
17319
  null
17124
17320
  );
17125
- const [modelLoadFailed, setModelLoadFailed] = (0, import_react40.useState)(false);
17126
- const [isResolvingUpload, setIsResolvingUpload] = (0, import_react40.useState)(false);
17127
- const [initialFitComplete, setInitialFitComplete] = (0, import_react40.useState)(false);
17128
- const [isDragActive, setIsDragActive] = (0, import_react40.useState)(false);
17129
- const [isPanelOpen, setIsPanelOpen] = (0, import_react40.useState)(
17321
+ const [modelLoadFailed, setModelLoadFailed] = (0, import_react41.useState)(false);
17322
+ const [isResolvingUpload, setIsResolvingUpload] = (0, import_react41.useState)(false);
17323
+ const [initialFitComplete, setInitialFitComplete] = (0, import_react41.useState)(false);
17324
+ const [isDragActive, setIsDragActive] = (0, import_react41.useState)(false);
17325
+ const [isPanelOpen, setIsPanelOpen] = (0, import_react41.useState)(
17130
17326
  () => typeof window === "undefined" ? true : window.innerWidth > 900
17131
17327
  );
17132
- const [isEnvPanelOpen, setIsEnvPanelOpen] = (0, import_react40.useState)(
17328
+ const [isEnvPanelOpen, setIsEnvPanelOpen] = (0, import_react41.useState)(
17133
17329
  () => typeof window === "undefined" ? true : window.innerWidth > 900
17134
17330
  );
17135
- const [envSettings, setEnvSettings] = (0, import_react40.useState)({
17331
+ const [envSettings, setEnvSettings] = (0, import_react41.useState)({
17136
17332
  backgroundEnabled,
17137
17333
  backgroundColor,
17138
17334
  autoRotate,
@@ -17144,7 +17340,7 @@ function SimpleModelViewer({
17144
17340
  directionalColor
17145
17341
  });
17146
17342
  const activeModelUrl = enableModelUpload ? uploadedModelUrl : modelUrl;
17147
- const resetModelState = (0, import_react40.useCallback)(() => {
17343
+ const resetModelState = (0, import_react41.useCallback)(() => {
17148
17344
  setLoadedScene(null);
17149
17345
  setLargeModelTriangles(null);
17150
17346
  setLargeModelNoticeDismissed(false);
@@ -17155,11 +17351,11 @@ function SimpleModelViewer({
17155
17351
  setModelLoadFailed(false);
17156
17352
  setInitialFitComplete(false);
17157
17353
  }, []);
17158
- const revokeUploadedModelUrl = (0, import_react40.useCallback)(() => {
17354
+ const revokeUploadedModelUrl = (0, import_react41.useCallback)(() => {
17159
17355
  uploadedModelRef.current?.cleanup();
17160
17356
  uploadedModelRef.current = null;
17161
17357
  }, []);
17162
- const clearUploadedModel = (0, import_react40.useCallback)(() => {
17358
+ const clearUploadedModel = (0, import_react41.useCallback)(() => {
17163
17359
  revokeUploadedModelUrl();
17164
17360
  setUploadedModelUrl(null);
17165
17361
  setUploadedModelName(null);
@@ -17188,7 +17384,7 @@ function SimpleModelViewer({
17188
17384
  resetModelState,
17189
17385
  revokeUploadedModelUrl
17190
17386
  ]);
17191
- const handleUploadedModel = (0, import_react40.useCallback)(
17387
+ const handleUploadedModel = (0, import_react41.useCallback)(
17192
17388
  async (file) => {
17193
17389
  setIsResolvingUpload(true);
17194
17390
  try {
@@ -17207,7 +17403,7 @@ function SimpleModelViewer({
17207
17403
  },
17208
17404
  [onLoadError, resetModelState, revokeUploadedModelUrl]
17209
17405
  );
17210
- const handleFileSelection = (0, import_react40.useCallback)(
17406
+ const handleFileSelection = (0, import_react41.useCallback)(
17211
17407
  (files) => {
17212
17408
  const file = files?.[0];
17213
17409
  if (!file) return;
@@ -17215,29 +17411,29 @@ function SimpleModelViewer({
17215
17411
  },
17216
17412
  [handleUploadedModel]
17217
17413
  );
17218
- (0, import_react40.useEffect)(() => {
17414
+ (0, import_react41.useEffect)(() => {
17219
17415
  return () => {
17220
17416
  revokeUploadedModelUrl();
17221
17417
  };
17222
17418
  }, [revokeUploadedModelUrl]);
17223
- const handleEnvSettingChange = (0, import_react40.useCallback)(
17419
+ const handleEnvSettingChange = (0, import_react41.useCallback)(
17224
17420
  (key, value) => {
17225
17421
  setEnvSettings((prev) => ({ ...prev, [key]: value }));
17226
17422
  },
17227
17423
  []
17228
17424
  );
17229
- (0, import_react40.useEffect)(() => {
17425
+ (0, import_react41.useEffect)(() => {
17230
17426
  setEnvSettings((prev) => ({ ...prev, backgroundColor }));
17231
17427
  }, [backgroundColor]);
17232
- (0, import_react40.useEffect)(() => {
17428
+ (0, import_react41.useEffect)(() => {
17233
17429
  resetModelState();
17234
17430
  }, [activeModelUrl, resetModelState]);
17235
- const handleMeshesReady = (0, import_react40.useCallback)((meshes) => {
17431
+ const handleMeshesReady = (0, import_react41.useCallback)((meshes) => {
17236
17432
  setMeshKeys(meshes);
17237
17433
  setHiddenKeys(/* @__PURE__ */ new Set());
17238
17434
  setSelectedKey(null);
17239
17435
  }, []);
17240
- const handleModelLoaded = (0, import_react40.useCallback)(
17436
+ const handleModelLoaded = (0, import_react41.useCallback)(
17241
17437
  (scene) => {
17242
17438
  setLoadedScene(scene);
17243
17439
  setModelLoadFailed(false);
@@ -17248,14 +17444,14 @@ function SimpleModelViewer({
17248
17444
  },
17249
17445
  [onModelLoaded]
17250
17446
  );
17251
- const handleLoadError = (0, import_react40.useCallback)(
17447
+ const handleLoadError = (0, import_react41.useCallback)(
17252
17448
  (error) => {
17253
17449
  setModelLoadFailed(true);
17254
17450
  onLoadError?.(error);
17255
17451
  },
17256
17452
  [onLoadError]
17257
17453
  );
17258
- const focusMesh = (0, import_react40.useCallback)(
17454
+ const focusMesh = (0, import_react41.useCallback)(
17259
17455
  (key) => {
17260
17456
  setSelectedKey(key);
17261
17457
  if (!zoomOnSelected) return;
@@ -17271,14 +17467,14 @@ function SimpleModelViewer({
17271
17467
  },
17272
17468
  [zoomOnSelected]
17273
17469
  );
17274
- const handleToggleVisibility = (0, import_react40.useCallback)((key) => {
17470
+ const handleToggleVisibility = (0, import_react41.useCallback)((key) => {
17275
17471
  setHiddenKeys((prev) => {
17276
17472
  const next = new Set(prev);
17277
17473
  next.has(key) ? next.delete(key) : next.add(key);
17278
17474
  return next;
17279
17475
  });
17280
17476
  }, []);
17281
- const handleDragEnter = (0, import_react40.useCallback)(
17477
+ const handleDragEnter = (0, import_react41.useCallback)(
17282
17478
  (event) => {
17283
17479
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
17284
17480
  return;
@@ -17290,7 +17486,7 @@ function SimpleModelViewer({
17290
17486
  },
17291
17487
  [enableModelUpload]
17292
17488
  );
17293
- const handleDragOver = (0, import_react40.useCallback)(
17489
+ const handleDragOver = (0, import_react41.useCallback)(
17294
17490
  (event) => {
17295
17491
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
17296
17492
  return;
@@ -17302,7 +17498,7 @@ function SimpleModelViewer({
17302
17498
  },
17303
17499
  [enableModelUpload]
17304
17500
  );
17305
- const handleDragLeave = (0, import_react40.useCallback)(
17501
+ const handleDragLeave = (0, import_react41.useCallback)(
17306
17502
  (event) => {
17307
17503
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
17308
17504
  return;
@@ -17316,7 +17512,7 @@ function SimpleModelViewer({
17316
17512
  },
17317
17513
  [enableModelUpload]
17318
17514
  );
17319
- const handleDrop = (0, import_react40.useCallback)(
17515
+ const handleDrop = (0, import_react41.useCallback)(
17320
17516
  (event) => {
17321
17517
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
17322
17518
  return;
@@ -17409,7 +17605,7 @@ function SimpleModelViewer({
17409
17605
  raycaster: { firstHitOnly: true },
17410
17606
  style: { background: envSettings.backgroundColor },
17411
17607
  camera: { position: [0, 0, 5], fov: 45, near: 0.1, far: 1e3 },
17412
- children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_react40.Suspense, { fallback: null, children: [
17608
+ children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_react41.Suspense, { fallback: null, children: [
17413
17609
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17414
17610
  "ambientLight",
17415
17611
  {
@@ -17427,7 +17623,7 @@ function SimpleModelViewer({
17427
17623
  }
17428
17624
  ),
17429
17625
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("directionalLight", { position: [-5, 5, 5], intensity: 0.5 }),
17430
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_react40.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17626
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_react41.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17431
17627
  import_drei6.Environment,
17432
17628
  {
17433
17629
  preset: envSettings.envPreset,
@@ -17446,7 +17642,7 @@ function SimpleModelViewer({
17446
17642
  ModelErrorBoundary2,
17447
17643
  {
17448
17644
  onLoadError: handleLoadError,
17449
- children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_react40.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17645
+ children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_react41.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17450
17646
  InnerModel,
17451
17647
  {
17452
17648
  url: activeModelUrl,
@@ -17545,18 +17741,18 @@ function SimpleModelViewer({
17545
17741
  }
17546
17742
 
17547
17743
  // src/hooks/useViewerSelection.ts
17548
- var import_react41 = require("react");
17744
+ var import_react42 = require("react");
17549
17745
  function useViewerSelection(initialSelectedObjectBinding = null) {
17550
- const [selectedObjectBinding, setSelectedObjectBinding] = (0, import_react41.useState)(
17746
+ const [selectedObjectBinding, setSelectedObjectBinding] = (0, import_react42.useState)(
17551
17747
  initialSelectedObjectBinding
17552
17748
  );
17553
- const handleObjectSelect = (0, import_react41.useCallback)(
17749
+ const handleObjectSelect = (0, import_react42.useCallback)(
17554
17750
  (binding) => {
17555
17751
  setSelectedObjectBinding(binding);
17556
17752
  },
17557
17753
  [setSelectedObjectBinding]
17558
17754
  );
17559
- const clearSelection = (0, import_react41.useCallback)(() => {
17755
+ const clearSelection = (0, import_react42.useCallback)(() => {
17560
17756
  setSelectedObjectBinding(null);
17561
17757
  }, []);
17562
17758
  return {
@@ -17568,9 +17764,9 @@ function useViewerSelection(initialSelectedObjectBinding = null) {
17568
17764
  }
17569
17765
 
17570
17766
  // src/hooks/useObjectBinding.ts
17571
- var import_react42 = require("react");
17767
+ var import_react43 = require("react");
17572
17768
  function useObjectBinding(objectBindings, objectId) {
17573
- return (0, import_react42.useMemo)(() => {
17769
+ return (0, import_react43.useMemo)(() => {
17574
17770
  if (!objectId) {
17575
17771
  return null;
17576
17772
  }
@@ -17585,26 +17781,23 @@ function useObjectBinding(objectBindings, objectId) {
17585
17781
  }
17586
17782
 
17587
17783
  // src/hooks/useObjectBindingIds.ts
17588
- var import_react43 = require("react");
17784
+ var import_react44 = require("react");
17589
17785
  function useObjectBindingIds(objectBindings) {
17590
- return (0, import_react43.useMemo)(
17786
+ return (0, import_react44.useMemo)(
17591
17787
  () => Object.values(objectBindings).map((binding) => binding.modelObjectId),
17592
17788
  [objectBindings]
17593
17789
  );
17594
17790
  }
17595
17791
 
17596
17792
  // src/hooks/useObjectBindings.ts
17597
- var import_react44 = require("react");
17793
+ var import_react45 = require("react");
17598
17794
 
17599
- // src/utils/patchBindings.ts
17795
+ // src/utils/deepMerge.ts
17600
17796
  function isPlainObject2(value) {
17601
17797
  if (typeof value !== "object" || value === null) return false;
17602
17798
  const prototype = Object.getPrototypeOf(value);
17603
17799
  return prototype === Object.prototype || prototype === null;
17604
17800
  }
17605
- function isBindingEditArray(value) {
17606
- return Array.isArray(value) && value.length > 0 && typeof value[0] !== "string";
17607
- }
17608
17801
  function deepMerge(base, patch) {
17609
17802
  if (!isPlainObject2(patch)) return patch;
17610
17803
  const target = isPlainObject2(base) ? { ...base } : {};
@@ -17614,6 +17807,11 @@ function deepMerge(base, patch) {
17614
17807
  }
17615
17808
  return target;
17616
17809
  }
17810
+
17811
+ // src/utils/patchBindings.ts
17812
+ function isBindingEditArray(value) {
17813
+ return Array.isArray(value) && value.length > 0 && typeof value[0] !== "string";
17814
+ }
17617
17815
  function patchBindings(bindings, objectIdOrEdits, patch) {
17618
17816
  const edits = isBindingEditArray(objectIdOrEdits) ? objectIdOrEdits : [{ ids: objectIdOrEdits, patch: patch ?? {} }];
17619
17817
  let next = bindings;
@@ -17642,8 +17840,8 @@ function getHiddenObjects(objectBindings) {
17642
17840
  );
17643
17841
  }
17644
17842
  function useObjectBindings(initialBindings) {
17645
- const [objectBindings, setObjectBindings] = (0, import_react44.useState)(initialBindings);
17646
- const patch = (0, import_react44.useCallback)(
17843
+ const [objectBindings, setObjectBindings] = (0, import_react45.useState)(initialBindings);
17844
+ const updateObjectBindings = (0, import_react45.useCallback)(
17647
17845
  (objectIdOrEdits, patchValue) => {
17648
17846
  setObjectBindings(
17649
17847
  (current) => patchValue === void 0 ? patchBindings(current, objectIdOrEdits) : patchBindings(
@@ -17655,34 +17853,34 @@ function useObjectBindings(initialBindings) {
17655
17853
  },
17656
17854
  []
17657
17855
  );
17658
- const setMaterial = (0, import_react44.useCallback)(
17659
- (objectId, value) => patch(objectId, material(value)),
17660
- [patch]
17856
+ const setMaterial = (0, import_react45.useCallback)(
17857
+ (objectId, value) => updateObjectBindings(objectId, material(value)),
17858
+ [updateObjectBindings]
17661
17859
  );
17662
- const setBaseColor = (0, import_react44.useCallback)(
17860
+ const setBaseColor = (0, import_react45.useCallback)(
17663
17861
  (objectId, color) => setMaterial(objectId, { baseColor: color }),
17664
17862
  [setMaterial]
17665
17863
  );
17666
- const resetMaterial = (0, import_react44.useCallback)(
17667
- (objectId) => patch(objectId, { style: { material: void 0 } }),
17668
- [patch]
17864
+ const resetMaterial = (0, import_react45.useCallback)(
17865
+ (objectId) => updateObjectBindings(objectId, { style: { material: void 0 } }),
17866
+ [updateObjectBindings]
17669
17867
  );
17670
- const getMaterial = (0, import_react44.useCallback)(
17868
+ const getMaterial = (0, import_react45.useCallback)(
17671
17869
  (objectId) => {
17672
17870
  const bindingKey = resolveBindingKey(objectBindings, objectId);
17673
17871
  return bindingKey ? objectBindings[bindingKey]?.style?.material ?? null : null;
17674
17872
  },
17675
17873
  [objectBindings]
17676
17874
  );
17677
- const hideObject = (0, import_react44.useCallback)(
17678
- (objectId) => patch(objectId, { visible: false }),
17679
- [patch]
17875
+ const hideObject = (0, import_react45.useCallback)(
17876
+ (objectId) => updateObjectBindings(objectId, { visible: false }),
17877
+ [updateObjectBindings]
17680
17878
  );
17681
- const showObject = (0, import_react44.useCallback)(
17682
- (objectId) => patch(objectId, { visible: true }),
17683
- [patch]
17879
+ const showObject = (0, import_react45.useCallback)(
17880
+ (objectId) => updateObjectBindings(objectId, { visible: true }),
17881
+ [updateObjectBindings]
17684
17882
  );
17685
- const toggleObjectVisibility = (0, import_react44.useCallback)(
17883
+ const toggleObjectVisibility = (0, import_react45.useCallback)(
17686
17884
  (objectId) => {
17687
17885
  setObjectBindings((current) => {
17688
17886
  const objectIds = Array.isArray(objectId) ? objectId : [objectId];
@@ -17701,22 +17899,22 @@ function useObjectBindings(initialBindings) {
17701
17899
  },
17702
17900
  [setObjectBindings]
17703
17901
  );
17704
- const clearHiddenObjects = (0, import_react44.useCallback)(() => {
17902
+ const clearHiddenObjects = (0, import_react45.useCallback)(() => {
17705
17903
  setObjectBindings(
17706
17904
  (current) => patchBindings(current, Object.keys(current), { visible: true })
17707
17905
  );
17708
17906
  }, [setObjectBindings]);
17709
- const hiddenObjects = (0, import_react44.useMemo)(
17907
+ const hiddenObjects = (0, import_react45.useMemo)(
17710
17908
  () => getHiddenObjects(objectBindings),
17711
17909
  [objectBindings]
17712
17910
  );
17713
- const hiddenObjectIds = (0, import_react44.useMemo)(
17911
+ const hiddenObjectIds = (0, import_react45.useMemo)(
17714
17912
  () => Object.keys(hiddenObjects).map(
17715
17913
  (bindingKey) => objectBindings[bindingKey]?.id ?? bindingKey
17716
17914
  ),
17717
17915
  [hiddenObjects, objectBindings]
17718
17916
  );
17719
- const isObjectHidden = (0, import_react44.useCallback)(
17917
+ const isObjectHidden = (0, import_react45.useCallback)(
17720
17918
  (objectId) => {
17721
17919
  const bindingKey = resolveBindingKey(objectBindings, objectId);
17722
17920
  return bindingKey ? objectBindings[bindingKey]?.visible === false : false;
@@ -17726,7 +17924,7 @@ function useObjectBindings(initialBindings) {
17726
17924
  return {
17727
17925
  objectBindings,
17728
17926
  setObjectBindings,
17729
- patch,
17927
+ updateObjectBindings,
17730
17928
  setMaterial,
17731
17929
  setBaseColor,
17732
17930
  resetMaterial,
@@ -17741,11 +17939,45 @@ function useObjectBindings(initialBindings) {
17741
17939
  };
17742
17940
  }
17743
17941
 
17942
+ // src/hooks/useSceneConfig.ts
17943
+ var import_react46 = require("react");
17944
+
17945
+ // src/utils/patchSceneConfig.ts
17946
+ function patchSceneConfig(sceneConfig, patch) {
17947
+ return deepMerge(sceneConfig, patch);
17948
+ }
17949
+
17950
+ // src/hooks/useSceneConfig.ts
17951
+ function useSceneConfig(initialSceneConfig) {
17952
+ const initialSceneConfigRef = (0, import_react46.useRef)(null);
17953
+ const [sceneConfig, setSceneConfig] = (0, import_react46.useState)(() => {
17954
+ const initial = typeof initialSceneConfig === "function" ? initialSceneConfig() : initialSceneConfig;
17955
+ initialSceneConfigRef.current = initial;
17956
+ return initial;
17957
+ });
17958
+ const updateSceneConfig = (0, import_react46.useCallback)((update) => {
17959
+ setSceneConfig((current) => {
17960
+ const patch = typeof update === "function" ? update(current) : update;
17961
+ return patchSceneConfig(current, patch);
17962
+ });
17963
+ }, []);
17964
+ const resetSceneConfig = (0, import_react46.useCallback)(() => {
17965
+ const initial = initialSceneConfigRef.current;
17966
+ if (initial) setSceneConfig(initial);
17967
+ }, []);
17968
+ return {
17969
+ sceneConfig,
17970
+ setSceneConfig,
17971
+ updateSceneConfig,
17972
+ resetSceneConfig
17973
+ };
17974
+ }
17975
+
17744
17976
  // src/hooks/useViewerHover.ts
17745
- var import_react45 = require("react");
17977
+ var import_react47 = require("react");
17746
17978
  function useViewerHover() {
17747
- const [hoveredObjectBinding, setHoveredObjectBinding] = (0, import_react45.useState)(null);
17748
- const handleHoveredObject = (0, import_react45.useCallback)(
17979
+ const [hoveredObjectBinding, setHoveredObjectBinding] = (0, import_react47.useState)(null);
17980
+ const handleHoveredObject = (0, import_react47.useCallback)(
17749
17981
  (binding) => {
17750
17982
  setHoveredObjectBinding(binding);
17751
17983
  },
@@ -17758,18 +17990,18 @@ function useViewerHover() {
17758
17990
  }
17759
17991
 
17760
17992
  // src/hooks/useViewerModel.ts
17761
- var import_react46 = require("react");
17993
+ var import_react48 = require("react");
17762
17994
  var import_three22 = require("three");
17763
17995
  function toTuple2(vector) {
17764
17996
  return [vector.x, vector.y, vector.z];
17765
17997
  }
17766
17998
  function useViewerModel() {
17767
- const [isLoading, setIsLoading] = (0, import_react46.useState)(true);
17768
- const [isReady, setIsReady] = (0, import_react46.useState)(false);
17769
- const [error, setError] = (0, import_react46.useState)(null);
17770
- const [bounds, setBounds] = (0, import_react46.useState)(null);
17771
- const [objectCount, setObjectCount] = (0, import_react46.useState)(0);
17772
- const handleModelLoaded = (0, import_react46.useCallback)((scene) => {
17999
+ const [isLoading, setIsLoading] = (0, import_react48.useState)(true);
18000
+ const [isReady, setIsReady] = (0, import_react48.useState)(false);
18001
+ const [error, setError] = (0, import_react48.useState)(null);
18002
+ const [bounds, setBounds] = (0, import_react48.useState)(null);
18003
+ const [objectCount, setObjectCount] = (0, import_react48.useState)(0);
18004
+ const handleModelLoaded = (0, import_react48.useCallback)((scene) => {
17773
18005
  const box = new import_three22.Box3().setFromObject(scene);
17774
18006
  const min = box.min.clone();
17775
18007
  const max = box.max.clone();
@@ -17794,14 +18026,14 @@ function useViewerModel() {
17794
18026
  setIsLoading(false);
17795
18027
  setIsReady(true);
17796
18028
  }, []);
17797
- const handleLoadError = (0, import_react46.useCallback)((nextError) => {
18029
+ const handleLoadError = (0, import_react48.useCallback)((nextError) => {
17798
18030
  setError(nextError);
17799
18031
  setBounds(null);
17800
18032
  setObjectCount(0);
17801
18033
  setIsLoading(false);
17802
18034
  setIsReady(false);
17803
18035
  }, []);
17804
- const resetModelState = (0, import_react46.useCallback)(() => {
18036
+ const resetModelState = (0, import_react48.useCallback)(() => {
17805
18037
  setIsLoading(true);
17806
18038
  setIsReady(false);
17807
18039
  setError(null);
@@ -17821,10 +18053,10 @@ function useViewerModel() {
17821
18053
  }
17822
18054
 
17823
18055
  // src/hooks/useViewerActions.ts
17824
- var import_react47 = require("react");
18056
+ var import_react49 = require("react");
17825
18057
  function useViewerActions(objectBindings, options = {}) {
17826
18058
  const { onAction, onObjectBindingsChange } = options;
17827
- const getActionsForObject = (0, import_react47.useCallback)(
18059
+ const getActionsForObject = (0, import_react49.useCallback)(
17828
18060
  (objectId) => {
17829
18061
  const bindingKey = resolveBindingKey(objectBindings, objectId);
17830
18062
  if (!bindingKey) {
@@ -17834,7 +18066,7 @@ function useViewerActions(objectBindings, options = {}) {
17834
18066
  },
17835
18067
  [objectBindings]
17836
18068
  );
17837
- const runAction = (0, import_react47.useCallback)(
18069
+ const runAction = (0, import_react49.useCallback)(
17838
18070
  (objectId, actionId) => {
17839
18071
  const bindingKey = resolveBindingKey(objectBindings, objectId);
17840
18072
  if (!bindingKey) {
@@ -17876,7 +18108,7 @@ function useViewerActions(objectBindings, options = {}) {
17876
18108
  }
17877
18109
 
17878
18110
  // src/hooks/useShareableViewerState.ts
17879
- var import_react48 = require("react");
18111
+ var import_react50 = require("react");
17880
18112
  function serializeState(cameraState, objectBindings, selectedObjectBinding) {
17881
18113
  const hidden = Object.values(objectBindings).filter((binding) => binding.visible === false).map((binding) => binding.id);
17882
18114
  return {
@@ -17896,8 +18128,8 @@ function useShareableViewerState({
17896
18128
  debounceMs = 400,
17897
18129
  autoSync = true
17898
18130
  }) {
17899
- const lastWrittenRawRef = (0, import_react48.useRef)(null);
17900
- const buildHash = (0, import_react48.useCallback)(() => {
18131
+ const lastWrittenRawRef = (0, import_react50.useRef)(null);
18132
+ const buildHash = (0, import_react50.useCallback)(() => {
17901
18133
  const state = serializeState(
17902
18134
  cameraState,
17903
18135
  objectBindings,
@@ -17905,12 +18137,12 @@ function useShareableViewerState({
17905
18137
  );
17906
18138
  return `${paramKey}=${encodeURIComponent(JSON.stringify(state))}`;
17907
18139
  }, [cameraState, objectBindings, selectedObjectBinding, paramKey]);
17908
- const getShareUrl = (0, import_react48.useCallback)(() => {
18140
+ const getShareUrl = (0, import_react50.useCallback)(() => {
17909
18141
  const url = new URL(window.location.href);
17910
18142
  url.hash = buildHash();
17911
18143
  return url.toString();
17912
18144
  }, [buildHash]);
17913
- const syncUrl = (0, import_react48.useCallback)(() => {
18145
+ const syncUrl = (0, import_react50.useCallback)(() => {
17914
18146
  const url = new URL(window.location.href);
17915
18147
  url.hash = buildHash();
17916
18148
  window.history.replaceState(null, "", url.toString());
@@ -17918,7 +18150,7 @@ function useShareableViewerState({
17918
18150
  paramKey
17919
18151
  );
17920
18152
  }, [buildHash, paramKey]);
17921
- const latestStateRef = (0, import_react48.useRef)({
18153
+ const latestStateRef = (0, import_react50.useRef)({
17922
18154
  cameraState,
17923
18155
  objectBindings,
17924
18156
  selectedObjectBinding
@@ -17928,9 +18160,9 @@ function useShareableViewerState({
17928
18160
  objectBindings,
17929
18161
  selectedObjectBinding
17930
18162
  };
17931
- const [restoreAttempted, setRestoreAttempted] = (0, import_react48.useState)(false);
17932
- const lastSyncedHashRef = (0, import_react48.useRef)(null);
17933
- (0, import_react48.useEffect)(() => {
18163
+ const [restoreAttempted, setRestoreAttempted] = (0, import_react50.useState)(false);
18164
+ const lastSyncedHashRef = (0, import_react50.useRef)(null);
18165
+ (0, import_react50.useEffect)(() => {
17934
18166
  if (!autoSync || !restoreAttempted) return;
17935
18167
  const tick = () => {
17936
18168
  const current = latestStateRef.current;
@@ -17952,9 +18184,9 @@ function useShareableViewerState({
17952
18184
  const id = window.setInterval(tick, debounceMs);
17953
18185
  return () => window.clearInterval(id);
17954
18186
  }, [autoSync, restoreAttempted, debounceMs, paramKey]);
17955
- const lastRestoredRawRef = (0, import_react48.useRef)(null);
17956
- const restoringRawRef = (0, import_react48.useRef)(null);
17957
- const restoreFromUrl = (0, import_react48.useCallback)(async () => {
18187
+ const lastRestoredRawRef = (0, import_react50.useRef)(null);
18188
+ const restoringRawRef = (0, import_react50.useRef)(null);
18189
+ const restoreFromUrl = (0, import_react50.useCallback)(async () => {
17958
18190
  setRestoreAttempted(true);
17959
18191
  const rawHash = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
17960
18192
  const raw = new URLSearchParams(rawHash).get(paramKey);
@@ -18007,7 +18239,7 @@ function useShareableViewerState({
18007
18239
  setCameraState,
18008
18240
  setSelectedObjectBinding
18009
18241
  ]);
18010
- (0, import_react48.useEffect)(() => {
18242
+ (0, import_react50.useEffect)(() => {
18011
18243
  const handleHashChange = () => {
18012
18244
  void restoreFromUrl();
18013
18245
  };
@@ -18027,9 +18259,11 @@ function useShareableViewerState({
18027
18259
  SimpleModelViewer,
18028
18260
  material,
18029
18261
  patchBindings,
18262
+ patchSceneConfig,
18030
18263
  useObjectBinding,
18031
18264
  useObjectBindingIds,
18032
18265
  useObjectBindings,
18266
+ useSceneConfig,
18033
18267
  useShareableViewerState,
18034
18268
  useViewerActions,
18035
18269
  useViewerAnimations,