@liveroom-tech/react-immersive 2.1.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.
Files changed (3) hide show
  1. package/dist/index.js +485 -291
  2. package/dist/index.mjs +392 -198
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5494,32 +5494,37 @@ function ObjectBindingDataPanel({
5494
5494
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("hr", { className: "left-panel__divider" }),
5495
5495
  objectToRender.actions && objectToRender.actions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
5496
5496
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "left-panel__actions-label", children: "Actions" }),
5497
- /* @__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) => {
5498
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);
5499
5499
  const extra = renderActionExtra?.(action.id);
5500
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react18.Fragment, { children: [
5501
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5502
- "button",
5503
- {
5504
- onClick: (e) => onAction({
5505
- objectId: objectToRender.modelObjectId || objectToRender.id || "",
5506
- action,
5507
- binding: objectToRender,
5508
- screenX: e.clientX,
5509
- screenY: e.clientY
5510
- }),
5511
- className: [
5512
- "left-panel__action-btn",
5513
- `left-panel__action-btn--${action.id}`,
5514
- isActive ? "left-panel__action-btn--active" : ""
5515
- ].filter(Boolean).join(" "),
5516
- "data-action-id": action.id,
5517
- "data-label": action.label,
5518
- children: action.label
5519
- }
5520
- ),
5521
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ActionExtra, { content: extra })
5522
- ] }, 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
+ );
5523
5528
  }) })
5524
5529
  ] })
5525
5530
  ] })
@@ -8225,11 +8230,11 @@ function ModelViewer({
8225
8230
  }
8226
8231
 
8227
8232
  // src/components/BindingBuilder/BindingBuilder.tsx
8228
- var import_react39 = require("react");
8233
+ var import_react40 = require("react");
8229
8234
  var import_lucide_react16 = require("lucide-react");
8230
8235
 
8231
8236
  // src/styles/BindingBuilder.css
8232
- 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');
8233
8238
 
8234
8239
  // src/hooks/useViewerCamera.ts
8235
8240
  var import_react23 = require("react");
@@ -11724,7 +11729,7 @@ function LightingTab({ config, onChange }) {
11724
11729
  {
11725
11730
  className: "se-segmentedControl",
11726
11731
  style: { gridAutoColumns: "1fr" },
11727
- children: ["shadowCatcher", "bakedAO"].map((m) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11732
+ children: ["shadowCatcher"].map((m) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11728
11733
  "button",
11729
11734
  {
11730
11735
  type: "button",
@@ -14459,8 +14464,99 @@ function BasicsPanel({
14459
14464
  ] });
14460
14465
  }
14461
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
+
14462
14554
  // src/components/BindingBuilder/components/ActionsPanel.tsx
14463
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");
14464
14560
  function ActionsPanel({
14465
14561
  selectedItem,
14466
14562
  selectedBinding,
@@ -14468,85 +14564,183 @@ function ActionsPanel({
14468
14564
  onToggle,
14469
14565
  updateBinding
14470
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
+ });
14471
14600
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(BBSection, { title: "Actions", open, onToggle, children: [
14472
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "bb-actionList", children: (selectedBinding.actions ?? []).map((action, index) => (
14473
- // Keyed by position, not action.id: the id is an editable field, so
14474
- // keying on it would remount the card on every keystroke and drop
14475
- // input focus.
14476
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-actionCard", children: [
14477
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-actionCardHeader", children: [
14478
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-actionCardTitle", children: [
14479
- "Action ",
14480
- index + 1
14481
- ] }),
14482
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "bb-actionCardMeta", children: action.type })
14483
- ] }),
14484
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-actionGrid", children: [
14485
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-field", children: [
14486
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("label", { className: "bb-label", children: "Action ID" }),
14487
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
14488
- "input",
14489
- {
14490
- className: "bb-input",
14491
- value: action.id,
14492
- onChange: (event) => updateBinding(selectedItem, (binding) => ({
14493
- ...binding,
14494
- actions: (binding.actions ?? []).map(
14495
- (entry, entryIndex) => entryIndex === index ? { ...entry, id: event.target.value } : entry
14496
- )
14497
- }))
14498
- }
14499
- )
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 })
14500
14620
  ] }),
14501
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-field", children: [
14502
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("label", { className: "bb-label", children: "Label" }),
14503
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
14504
- "input",
14505
- {
14506
- className: "bb-input",
14507
- value: action.label,
14508
- onChange: (event) => updateBinding(selectedItem, (binding) => ({
14509
- ...binding,
14510
- actions: (binding.actions ?? []).map(
14511
- (entry, entryIndex) => entryIndex === index ? { ...entry, label: event.target.value } : entry
14512
- )
14513
- }))
14514
- }
14515
- )
14516
- ] })
14517
- ] }),
14518
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "bb-actionFooter", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
14519
- "button",
14520
- {
14521
- type: "button",
14522
- className: "bb-button bb-button--ghost bb-actionRemove",
14523
- onClick: () => updateBinding(selectedItem, (binding) => ({
14524
- ...binding,
14525
- actions: (binding.actions ?? []).filter(
14526
- (_, 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
+ }
14527
14650
  )
14528
- })),
14529
- children: "Remove Action"
14530
- }
14531
- ) })
14532
- ] }, index)
14533
- )) }),
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
+ }) }),
14534
14722
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { style: { marginTop: 10 }, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
14535
14723
  "button",
14536
14724
  {
14537
14725
  type: "button",
14538
14726
  className: "bb-button",
14539
- onClick: () => updateBinding(selectedItem, (binding) => ({
14540
- ...binding,
14541
- actions: [
14542
- ...binding.actions ?? [],
14543
- {
14544
- id: "new-action",
14545
- label: "New Action",
14546
- type: "command"
14547
- }
14548
- ]
14549
- })),
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
+ },
14550
14744
  children: "Add Action"
14551
14745
  }
14552
14746
  ) })
@@ -14660,7 +14854,7 @@ function CameraPositionPanel({
14660
14854
  }
14661
14855
 
14662
14856
  // src/components/BindingBuilder/components/SceneNodesPanel.tsx
14663
- var import_react37 = require("react");
14857
+ var import_react38 = require("react");
14664
14858
 
14665
14859
  // src/components/BindingBuilder/utils/bindingFactory.ts
14666
14860
  function slugify2(value) {
@@ -14749,9 +14943,9 @@ function SceneNodesPanel({
14749
14943
  isOpen = true,
14750
14944
  onToggle
14751
14945
  }) {
14752
- const [search, setSearch] = (0, import_react37.useState)("");
14753
- const nodeKeys = (0, import_react37.useMemo)(() => Object.keys(bindings), [bindings]);
14754
- 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)(() => {
14755
14949
  const query = search.trim().toLowerCase();
14756
14950
  if (!query) return nodeKeys;
14757
14951
  return nodeKeys.filter((nodeKey) => {
@@ -14894,7 +15088,7 @@ function mergeAnimationConfig(sceneConfig, animationDetails) {
14894
15088
  }
14895
15089
 
14896
15090
  // src/components/BindingBuilder/hooks/useUndoableState.ts
14897
- var import_react38 = require("react");
15091
+ var import_react39 = require("react");
14898
15092
  var MAX_HISTORY = 100;
14899
15093
  function historyReducer(state, action) {
14900
15094
  switch (action.type) {
@@ -14933,13 +15127,13 @@ function historyReducer(state, action) {
14933
15127
  }
14934
15128
  }
14935
15129
  function useUndoableState(initial, coalesceWindowMs = 600) {
14936
- const [state, dispatch] = (0, import_react38.useReducer)(historyReducer, {
15130
+ const [state, dispatch] = (0, import_react39.useReducer)(historyReducer, {
14937
15131
  past: [],
14938
15132
  present: initial,
14939
15133
  future: []
14940
15134
  });
14941
- const lastSetAt = (0, import_react38.useRef)(0);
14942
- const setValue = (0, import_react38.useCallback)(
15135
+ const lastSetAt = (0, import_react39.useRef)(0);
15136
+ const setValue = (0, import_react39.useCallback)(
14943
15137
  (updater) => {
14944
15138
  const now = Date.now();
14945
15139
  const coalesce = now - lastSetAt.current < coalesceWindowMs;
@@ -14948,15 +15142,15 @@ function useUndoableState(initial, coalesceWindowMs = 600) {
14948
15142
  },
14949
15143
  [coalesceWindowMs]
14950
15144
  );
14951
- const undo = (0, import_react38.useCallback)(() => {
15145
+ const undo = (0, import_react39.useCallback)(() => {
14952
15146
  lastSetAt.current = 0;
14953
15147
  dispatch({ type: "undo" });
14954
15148
  }, []);
14955
- const redo = (0, import_react38.useCallback)(() => {
15149
+ const redo = (0, import_react39.useCallback)(() => {
14956
15150
  lastSetAt.current = 0;
14957
15151
  dispatch({ type: "redo" });
14958
15152
  }, []);
14959
- const reset = (0, import_react38.useCallback)((value) => {
15153
+ const reset = (0, import_react39.useCallback)((value) => {
14960
15154
  lastSetAt.current = 0;
14961
15155
  dispatch({ type: "reset", value });
14962
15156
  }, []);
@@ -15202,39 +15396,39 @@ function mergeSceneConfigSection(current, incoming) {
15202
15396
  return next;
15203
15397
  }
15204
15398
  function BindingBuilder({ licenseKey }) {
15205
- const modelInputRef = (0, import_react39.useRef)(null);
15206
- const bindingsInputRef = (0, import_react39.useRef)(null);
15207
- const sceneConfigInputRef = (0, import_react39.useRef)(null);
15208
- const [modelUrl, setModelUrl] = (0, import_react39.useState)(null);
15209
- const [modelName, setModelName] = (0, import_react39.useState)("");
15210
- 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);
15211
15405
  const bindingsHistory = useUndoableState({});
15212
15406
  const bindings = bindingsHistory.value;
15213
15407
  const setBindings = bindingsHistory.setValue;
15214
- const [loading, setLoading] = (0, import_react39.useState)(false);
15215
- const [isExporting, setIsExporting] = (0, import_react39.useState)(false);
15216
- 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)(
15217
15411
  "Load the demo model or upload a GLB/GLTF model to generate bindings."
15218
15412
  );
15219
- const [statusTone, setStatusTone] = (0, import_react39.useState)("default");
15220
- const [statusBannerVisible, setStatusBannerVisible] = (0, import_react39.useState)(false);
15221
- const [metricsDraft, setMetricsDraft] = (0, import_react39.useState)("{}");
15222
- const [metadataDraft, setMetadataDraft] = (0, import_react39.useState)("{}");
15223
- const [metricsError, setMetricsError] = (0, import_react39.useState)(null);
15224
- const [metadataError, setMetadataError] = (0, import_react39.useState)(null);
15225
- const [isPreviewDragActive, setIsPreviewDragActive] = (0, import_react39.useState)(false);
15226
- const [isEditorCollapsed, setIsEditorCollapsed] = (0, import_react39.useState)(false);
15227
- const [isNodesCollapsed, setIsNodesCollapsed] = (0, import_react39.useState)(false);
15228
- 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)({
15229
15423
  identity: true,
15230
15424
  basics: true,
15231
- material: true,
15425
+ material: false,
15232
15426
  actions: false,
15233
15427
  camera: false,
15234
15428
  metrics: false,
15235
15429
  metadata: false
15236
15430
  });
15237
- const [openMaterialPanels, setOpenMaterialPanels] = (0, import_react39.useState)({
15431
+ const [openMaterialPanels, setOpenMaterialPanels] = (0, import_react40.useState)({
15238
15432
  pbr: true,
15239
15433
  roughness: false,
15240
15434
  emission: false,
@@ -15250,27 +15444,27 @@ function BindingBuilder({ licenseKey }) {
15250
15444
  });
15251
15445
  const toggleObjectSection = (key) => setOpenObjectSections((prev) => ({ ...prev, [key]: !prev[key] }));
15252
15446
  const toggleMaterialPanel = (key) => setOpenMaterialPanels((prev) => ({ ...prev, [key]: !prev[key] }));
15253
- const [activeEditorTab, setActiveEditorTab] = (0, import_react39.useState)(
15447
+ const [activeEditorTab, setActiveEditorTab] = (0, import_react40.useState)(
15254
15448
  "object"
15255
15449
  );
15256
- const [activeSubTab, setActiveSubTab] = (0, import_react39.useState)("General");
15257
- const [pbrWorkflow, setPbrWorkflow] = (0, import_react39.useState)("metalness");
15258
- const [materialPanelEnabled, setMaterialPanelEnabled] = (0, import_react39.useState)(createMaterialPanelEnabledState());
15259
- const materialPanelItemRef = (0, import_react39.useRef)(null);
15260
- const [zoomOnSelected, setZoomOnSelected] = (0, import_react39.useState)(false);
15261
- const [highlightOnHover, setHighlightOnHover] = (0, import_react39.useState)(true);
15262
- const [resetDialogOpen, setResetDialogOpen] = (0, import_react39.useState)(false);
15263
- 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)(
15264
15458
  null
15265
15459
  );
15266
15460
  const sceneConfigHistory = useUndoableState(DEFAULT_SCENE_CONFIG);
15267
15461
  const sceneConfig = sceneConfigHistory.value;
15268
15462
  const setSceneConfig = sceneConfigHistory.setValue;
15269
- const [activeAnnotation, setActiveAnnotation] = (0, import_react39.useState)(null);
15270
- 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);
15271
15465
  const license = useLicense(licenseKey);
15272
15466
  const tier = normalizeTier(license.tier);
15273
- const bindingBuilderContextValue = (0, import_react39.useMemo)(
15467
+ const bindingBuilderContextValue = (0, import_react40.useMemo)(
15274
15468
  () => ({
15275
15469
  isInsideEditor: true,
15276
15470
  activeTab: activeEditorTab,
@@ -15279,11 +15473,11 @@ function BindingBuilder({ licenseKey }) {
15279
15473
  }),
15280
15474
  [activeEditorTab, activeSubTab, tier]
15281
15475
  );
15282
- const uploadedModelRef = (0, import_react39.useRef)(null);
15283
- const previewDragDepthRef = (0, import_react39.useRef)(0);
15284
- const loadRequestRef = (0, import_react39.useRef)(0);
15285
- const pendingLoadRef = (0, import_react39.useRef)(null);
15286
- 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);
15287
15481
  const {
15288
15482
  cameraState,
15289
15483
  resetView,
@@ -15293,7 +15487,7 @@ function BindingBuilder({ licenseKey }) {
15293
15487
  handleCameraChange,
15294
15488
  handleViewerReady
15295
15489
  } = useViewerCamera();
15296
- const handlePreviewWaypoint = (0, import_react39.useCallback)(
15490
+ const handlePreviewWaypoint = (0, import_react40.useCallback)(
15297
15491
  (waypoint) => {
15298
15492
  setCameraState(
15299
15493
  {
@@ -15317,7 +15511,7 @@ function BindingBuilder({ licenseKey }) {
15317
15511
  position: formatVector3(cameraState.position),
15318
15512
  target: formatVector3(cameraState.target)
15319
15513
  } : null;
15320
- const handleAnnotationsChange = (0, import_react39.useCallback)(
15514
+ const handleAnnotationsChange = (0, import_react40.useCallback)(
15321
15515
  (newAnnotations) => {
15322
15516
  setSceneConfig((prev) => {
15323
15517
  const currentIds = prev.annotations.map((a) => a.id).join(",");
@@ -15333,7 +15527,7 @@ function BindingBuilder({ licenseKey }) {
15333
15527
  },
15334
15528
  []
15335
15529
  );
15336
- const handleActiveAnnotationChange = (0, import_react39.useCallback)(
15530
+ const handleActiveAnnotationChange = (0, import_react40.useCallback)(
15337
15531
  (annotation) => {
15338
15532
  setActiveAnnotation(
15339
15533
  annotation ? {
@@ -15343,7 +15537,7 @@ function BindingBuilder({ licenseKey }) {
15343
15537
  },
15344
15538
  []
15345
15539
  );
15346
- (0, import_react39.useEffect)(() => {
15540
+ (0, import_react40.useEffect)(() => {
15347
15541
  if (!selectedBinding) {
15348
15542
  setMetricsDraft("{}");
15349
15543
  setMetadataDraft("{}");
@@ -15356,7 +15550,7 @@ function BindingBuilder({ licenseKey }) {
15356
15550
  setMetricsError(null);
15357
15551
  setMetadataError(null);
15358
15552
  }, [selectedBinding]);
15359
- (0, import_react39.useEffect)(() => {
15553
+ (0, import_react40.useEffect)(() => {
15360
15554
  const derived = createMaterialPanelEnabledState(selectedMaterial);
15361
15555
  if (materialPanelItemRef.current !== selectedItem) {
15362
15556
  materialPanelItemRef.current = selectedItem;
@@ -15375,12 +15569,12 @@ function BindingBuilder({ licenseKey }) {
15375
15569
  return changed ? next : prev;
15376
15570
  });
15377
15571
  }, [selectedItem, selectedMaterial]);
15378
- (0, import_react39.useEffect)(() => {
15572
+ (0, import_react40.useEffect)(() => {
15379
15573
  return () => {
15380
15574
  uploadedModelRef.current?.cleanup();
15381
15575
  };
15382
15576
  }, []);
15383
- (0, import_react39.useEffect)(() => {
15577
+ (0, import_react40.useEffect)(() => {
15384
15578
  const handleKeyDown = (event) => {
15385
15579
  if (event.key.toLowerCase() !== "z" || !(event.metaKey || event.ctrlKey)) {
15386
15580
  return;
@@ -15401,7 +15595,7 @@ function BindingBuilder({ licenseKey }) {
15401
15595
  window.addEventListener("keydown", handleKeyDown);
15402
15596
  return () => window.removeEventListener("keydown", handleKeyDown);
15403
15597
  }, [activeEditorTab, bindingsHistory, sceneConfigHistory]);
15404
- (0, import_react39.useEffect)(() => {
15598
+ (0, import_react40.useEffect)(() => {
15405
15599
  if (loading || statusTone === "default" || statusTone === "success") return;
15406
15600
  setStatusBannerVisible(true);
15407
15601
  const timeout = window.setTimeout(
@@ -15410,8 +15604,8 @@ function BindingBuilder({ licenseKey }) {
15410
15604
  );
15411
15605
  return () => window.clearTimeout(timeout);
15412
15606
  }, [statusMessage, statusTone, loading]);
15413
- const nodeKeys = (0, import_react39.useMemo)(() => Object.keys(bindings), [bindings]);
15414
- const idError = (0, import_react39.useMemo)(() => {
15607
+ const nodeKeys = (0, import_react40.useMemo)(() => Object.keys(bindings), [bindings]);
15608
+ const idError = (0, import_react40.useMemo)(() => {
15415
15609
  if (!selectedItem || !selectedBinding) return null;
15416
15610
  const trimmed = selectedBinding.id.trim();
15417
15611
  if (!trimmed) return "ID is required.";
@@ -15454,7 +15648,7 @@ function BindingBuilder({ licenseKey }) {
15454
15648
  });
15455
15649
  }
15456
15650
  };
15457
- const applyBindingsFromScene = (0, import_react39.useCallback)(
15651
+ const applyBindingsFromScene = (0, import_react40.useCallback)(
15458
15652
  (scene, nextModelName) => {
15459
15653
  const nodeNames = collectNodeNames(scene);
15460
15654
  bindingsHistory.reset(buildBindingsFromNodes(nodeNames));
@@ -15493,7 +15687,7 @@ function BindingBuilder({ licenseKey }) {
15493
15687
  bindingsHistory.reset({});
15494
15688
  setSelectedItem(null);
15495
15689
  };
15496
- const handleViewerModelLoaded = (0, import_react39.useCallback)(
15690
+ const handleViewerModelLoaded = (0, import_react40.useCallback)(
15497
15691
  (scene) => {
15498
15692
  const pending = pendingLoadRef.current;
15499
15693
  if (!pending || pending.requestId !== loadRequestRef.current) return;
@@ -15503,7 +15697,7 @@ function BindingBuilder({ licenseKey }) {
15503
15697
  },
15504
15698
  [applyBindingsFromScene]
15505
15699
  );
15506
- const handleViewerLoadError = (0, import_react39.useCallback)((error) => {
15700
+ const handleViewerLoadError = (0, import_react40.useCallback)((error) => {
15507
15701
  const pending = pendingLoadRef.current;
15508
15702
  if (!pending || pending.requestId !== loadRequestRef.current) return;
15509
15703
  pendingLoadRef.current = null;
@@ -15514,7 +15708,7 @@ function BindingBuilder({ licenseKey }) {
15514
15708
  setStatusTone("error");
15515
15709
  setLoading(false);
15516
15710
  }, []);
15517
- const handleAnimationsReady = (0, import_react39.useCallback)(
15711
+ const handleAnimationsReady = (0, import_react40.useCallback)(
15518
15712
  (controls) => {
15519
15713
  viewerAnimations.handleAnimationsReady(controls);
15520
15714
  const details = controls.clipDetails ?? controls.clips.map((sourceName) => ({ sourceName, duration: 0 }));
@@ -15815,7 +16009,7 @@ function BindingBuilder({ licenseKey }) {
15815
16009
  }));
15816
16010
  setError(null);
15817
16011
  };
15818
- const handleViewerSelect = (0, import_react39.useCallback)(
16012
+ const handleViewerSelect = (0, import_react40.useCallback)(
15819
16013
  (binding) => {
15820
16014
  if (!binding) {
15821
16015
  setSelectedItem(null);
@@ -15828,7 +16022,7 @@ function BindingBuilder({ licenseKey }) {
15828
16022
  },
15829
16023
  [bindings]
15830
16024
  );
15831
- const handleSceneNodeSelect = (0, import_react39.useCallback)(
16025
+ const handleSceneNodeSelect = (0, import_react40.useCallback)(
15832
16026
  async (nodeKey) => {
15833
16027
  setSelectedItem(nodeKey);
15834
16028
  setPanelHoveredBinding(null);
@@ -15836,7 +16030,7 @@ function BindingBuilder({ licenseKey }) {
15836
16030
  },
15837
16031
  [focusObject]
15838
16032
  );
15839
- const saveCameraPositionForSelectedObject = (0, import_react39.useCallback)(() => {
16033
+ const saveCameraPositionForSelectedObject = (0, import_react40.useCallback)(() => {
15840
16034
  if (!selectedItem || !cameraState) {
15841
16035
  return;
15842
16036
  }
@@ -15851,7 +16045,7 @@ function BindingBuilder({ licenseKey }) {
15851
16045
  cameraState: currentCameraState
15852
16046
  }));
15853
16047
  }, [selectedItem, cameraState]);
15854
- const goToSavedCameraPosition = (0, import_react39.useCallback)(() => {
16048
+ const goToSavedCameraPosition = (0, import_react40.useCallback)(() => {
15855
16049
  if (!savedCameraPosition?.position || !savedCameraPosition?.target) return;
15856
16050
  setCameraState(
15857
16051
  {
@@ -15863,28 +16057,28 @@ function BindingBuilder({ licenseKey }) {
15863
16057
  true
15864
16058
  );
15865
16059
  }, [savedCameraPosition, setCameraState, cameraState]);
15866
- const clearSavedCameraPosition = (0, import_react39.useCallback)(() => {
16060
+ const clearSavedCameraPosition = (0, import_react40.useCallback)(() => {
15867
16061
  if (!selectedItem) return;
15868
16062
  updateBinding(selectedItem, (binding) => ({
15869
16063
  ...binding,
15870
16064
  cameraState: void 0
15871
16065
  }));
15872
16066
  }, [selectedItem]);
15873
- const handleResetView = (0, import_react39.useCallback)(async () => {
16067
+ const handleResetView = (0, import_react40.useCallback)(async () => {
15874
16068
  await resetView();
15875
16069
  }, [resetView]);
15876
- const handleFitScene = (0, import_react39.useCallback)(async () => {
16070
+ const handleFitScene = (0, import_react40.useCallback)(async () => {
15877
16071
  await fitScene();
15878
16072
  }, [fitScene]);
15879
- const openResetDialog = (0, import_react39.useCallback)((target) => {
16073
+ const openResetDialog = (0, import_react40.useCallback)((target) => {
15880
16074
  setResetTarget(target);
15881
16075
  setResetDialogOpen(true);
15882
16076
  }, []);
15883
- const closeResetDialog = (0, import_react39.useCallback)(() => {
16077
+ const closeResetDialog = (0, import_react40.useCallback)(() => {
15884
16078
  setResetDialogOpen(false);
15885
16079
  setResetTarget(null);
15886
16080
  }, []);
15887
- const executeReset = (0, import_react39.useCallback)(() => {
16081
+ const executeReset = (0, import_react40.useCallback)(() => {
15888
16082
  if (resetTarget === "bindings") {
15889
16083
  if (!nodeKeys.length) {
15890
16084
  closeResetDialog();
@@ -16442,7 +16636,7 @@ function BindingBuilder({ licenseKey }) {
16442
16636
  }
16443
16637
 
16444
16638
  // src/components/SimpleModelViewer/SimpleModelViewer.tsx
16445
- var import_react40 = require("react");
16639
+ var import_react41 = require("react");
16446
16640
  var import_drei6 = require("@react-three/drei");
16447
16641
  var import_fiber12 = require("@react-three/fiber");
16448
16642
  var import_fiber13 = require("@react-three/fiber");
@@ -16482,7 +16676,7 @@ function applyClippingPlanes(scene, controls) {
16482
16676
  camera.updateProjectionMatrix();
16483
16677
  controls.maxDistance = size * 10;
16484
16678
  }
16485
- var ModelErrorBoundary2 = class extends import_react40.Component {
16679
+ var ModelErrorBoundary2 = class extends import_react41.Component {
16486
16680
  constructor() {
16487
16681
  super(...arguments);
16488
16682
  this.state = { hasError: false };
@@ -16514,12 +16708,12 @@ function InnerModel({
16514
16708
  meshopt: true
16515
16709
  });
16516
16710
  const invalidate = (0, import_fiber12.useThree)((state) => state.invalidate);
16517
- const [hoveredKey, setHoveredKey] = (0, import_react40.useState)(null);
16518
- const scene = (0, import_react40.useMemo)(
16711
+ const [hoveredKey, setHoveredKey] = (0, import_react41.useState)(null);
16712
+ const scene = (0, import_react41.useMemo)(
16519
16713
  () => import_three_stdlib4.SkeletonUtils.clone(rawScene),
16520
16714
  [rawScene]
16521
16715
  );
16522
- const meshMap = (0, import_react40.useMemo)(() => {
16716
+ const meshMap = (0, import_react41.useMemo)(() => {
16523
16717
  const map = {};
16524
16718
  scene.traverse((obj) => {
16525
16719
  if (!isMeshNode2(obj)) return;
@@ -16528,7 +16722,7 @@ function InnerModel({
16528
16722
  });
16529
16723
  return map;
16530
16724
  }, [scene]);
16531
- const perKeyMaterials = (0, import_react40.useMemo)(() => {
16725
+ const perKeyMaterials = (0, import_react41.useMemo)(() => {
16532
16726
  const map = {};
16533
16727
  for (const [key, { ref }] of Object.entries(meshMap)) {
16534
16728
  const original = Array.isArray(ref.material) ? ref.material[0] : ref.material;
@@ -16544,14 +16738,14 @@ function InnerModel({
16544
16738
  }
16545
16739
  return map;
16546
16740
  }, [meshMap]);
16547
- (0, import_react40.useEffect)(
16741
+ (0, import_react41.useEffect)(
16548
16742
  () => () => {
16549
16743
  Object.values(perKeyMaterials).forEach((m) => m.dispose());
16550
16744
  },
16551
16745
  [perKeyMaterials]
16552
16746
  );
16553
- const notifiedUrl = (0, import_react40.useRef)(null);
16554
- (0, import_react40.useEffect)(() => {
16747
+ const notifiedUrl = (0, import_react41.useRef)(null);
16748
+ (0, import_react41.useEffect)(() => {
16555
16749
  if (notifiedUrl.current === url) return;
16556
16750
  notifiedUrl.current = url;
16557
16751
  scene.updateWorldMatrix(true, true);
@@ -16559,7 +16753,7 @@ function InnerModel({
16559
16753
  onMeshesReady(meshMap);
16560
16754
  onModelLoaded(scene);
16561
16755
  }, [url, scene, meshMap, onMeshesReady, onModelLoaded]);
16562
- (0, import_react40.useEffect)(() => {
16756
+ (0, import_react41.useEffect)(() => {
16563
16757
  const controller = new AbortController();
16564
16758
  void buildSceneBVH(scene, controller.signal);
16565
16759
  return () => {
@@ -16567,26 +16761,26 @@ function InnerModel({
16567
16761
  disposeSceneBVH(scene);
16568
16762
  };
16569
16763
  }, [scene]);
16570
- const pointerRaycastEnabled = (0, import_react40.useMemo)(
16764
+ const pointerRaycastEnabled = (0, import_react41.useMemo)(
16571
16765
  () => !exceedsBvhBudget(scene),
16572
16766
  [scene]
16573
16767
  );
16574
- (0, import_react40.useEffect)(() => {
16768
+ (0, import_react41.useEffect)(() => {
16575
16769
  for (const [key, { ref }] of Object.entries(meshMap)) {
16576
16770
  nodeRefsOut.current[key] = ref;
16577
16771
  }
16578
16772
  }, [meshMap, nodeRefsOut]);
16579
- (0, import_react40.useEffect)(() => {
16773
+ (0, import_react41.useEffect)(() => {
16580
16774
  if (!highlightOnHover && hoveredKey !== null) {
16581
16775
  setHoveredKey(null);
16582
16776
  }
16583
16777
  }, [highlightOnHover, hoveredKey]);
16584
- (0, import_react40.useEffect)(() => {
16778
+ (0, import_react41.useEffect)(() => {
16585
16779
  return () => {
16586
16780
  document.body.style.cursor = "default";
16587
16781
  };
16588
16782
  }, []);
16589
- (0, import_react40.useEffect)(() => {
16783
+ (0, import_react41.useEffect)(() => {
16590
16784
  for (const [key, { ref }] of Object.entries(meshMap)) {
16591
16785
  ref.visible = !hiddenKeys.has(key);
16592
16786
  const mat = perKeyMaterials[key];
@@ -16656,11 +16850,11 @@ function CameraFitter({
16656
16850
  refitOnResize,
16657
16851
  onInitialFitComplete
16658
16852
  }) {
16659
- const fitted = (0, import_react40.useRef)(false);
16853
+ const fitted = (0, import_react41.useRef)(false);
16660
16854
  const invalidate = (0, import_fiber12.useThree)((state) => state.invalidate);
16661
16855
  const size = (0, import_fiber12.useThree)((state) => state.size);
16662
- const resizeTimerRef = (0, import_react40.useRef)(null);
16663
- (0, import_react40.useEffect)(() => {
16856
+ const resizeTimerRef = (0, import_react41.useRef)(null);
16857
+ (0, import_react41.useEffect)(() => {
16664
16858
  fitted.current = false;
16665
16859
  invalidate();
16666
16860
  }, [invalidate, scene]);
@@ -16680,7 +16874,7 @@ function CameraFitter({
16680
16874
  }).catch(() => {
16681
16875
  }).finally(onInitialFitComplete);
16682
16876
  });
16683
- (0, import_react40.useEffect)(() => {
16877
+ (0, import_react41.useEffect)(() => {
16684
16878
  if (!refitOnResize || !fitted.current || !scene || !controlsRef.current) {
16685
16879
  return;
16686
16880
  }
@@ -16708,7 +16902,7 @@ function CameraFitter({
16708
16902
  function ExposureController({ exposure }) {
16709
16903
  const gl = (0, import_fiber12.useThree)((state) => state.gl);
16710
16904
  const invalidate = (0, import_fiber12.useThree)((state) => state.invalidate);
16711
- (0, import_react40.useEffect)(() => {
16905
+ (0, import_react41.useEffect)(() => {
16712
16906
  gl.toneMappingExposure = exposure;
16713
16907
  invalidate();
16714
16908
  }, [gl, exposure, invalidate]);
@@ -16816,7 +17010,7 @@ function EnvironmentSection({
16816
17010
  title,
16817
17011
  children
16818
17012
  }) {
16819
- const [collapsed, setCollapsed] = (0, import_react40.useState)(false);
17013
+ const [collapsed, setCollapsed] = (0, import_react41.useState)(false);
16820
17014
  return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "env-section", children: [
16821
17015
  /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
16822
17016
  "button",
@@ -16981,8 +17175,8 @@ function SceneObjectsPanel2({
16981
17175
  onHover,
16982
17176
  onToggleVisibility
16983
17177
  }) {
16984
- const [search, setSearch] = (0, import_react40.useState)("");
16985
- 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);
16986
17180
  const entries = Object.values(meshKeys).filter(
16987
17181
  (m) => m.label.toLowerCase().includes(search.toLowerCase())
16988
17182
  );
@@ -17106,35 +17300,35 @@ function SimpleModelViewer({
17106
17300
  onModelLoaded,
17107
17301
  onLoadError
17108
17302
  }) {
17109
- const cameraControlsRef = (0, import_react40.useRef)(null);
17110
- const nodeRefsOut = (0, import_react40.useRef)({});
17111
- const fileInputRef = (0, import_react40.useRef)(null);
17112
- const uploadedModelRef = (0, import_react40.useRef)(null);
17113
- const dragDepthRef = (0, import_react40.useRef)(0);
17114
- const [loadedScene, setLoadedScene] = (0, import_react40.useState)(null);
17115
- 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)(
17116
17310
  null
17117
17311
  );
17118
- const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = (0, import_react40.useState)(false);
17119
- const [meshKeys, setMeshKeys] = (0, import_react40.useState)({});
17120
- const [hiddenKeys, setHiddenKeys] = (0, import_react40.useState)(/* @__PURE__ */ new Set());
17121
- const [selectedKey, setSelectedKey] = (0, import_react40.useState)(null);
17122
- const [panelHoveredKey, setPanelHoveredKey] = (0, import_react40.useState)(null);
17123
- const [uploadedModelUrl, setUploadedModelUrl] = (0, import_react40.useState)(null);
17124
- 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)(
17125
17319
  null
17126
17320
  );
17127
- const [modelLoadFailed, setModelLoadFailed] = (0, import_react40.useState)(false);
17128
- const [isResolvingUpload, setIsResolvingUpload] = (0, import_react40.useState)(false);
17129
- const [initialFitComplete, setInitialFitComplete] = (0, import_react40.useState)(false);
17130
- const [isDragActive, setIsDragActive] = (0, import_react40.useState)(false);
17131
- 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)(
17132
17326
  () => typeof window === "undefined" ? true : window.innerWidth > 900
17133
17327
  );
17134
- const [isEnvPanelOpen, setIsEnvPanelOpen] = (0, import_react40.useState)(
17328
+ const [isEnvPanelOpen, setIsEnvPanelOpen] = (0, import_react41.useState)(
17135
17329
  () => typeof window === "undefined" ? true : window.innerWidth > 900
17136
17330
  );
17137
- const [envSettings, setEnvSettings] = (0, import_react40.useState)({
17331
+ const [envSettings, setEnvSettings] = (0, import_react41.useState)({
17138
17332
  backgroundEnabled,
17139
17333
  backgroundColor,
17140
17334
  autoRotate,
@@ -17146,7 +17340,7 @@ function SimpleModelViewer({
17146
17340
  directionalColor
17147
17341
  });
17148
17342
  const activeModelUrl = enableModelUpload ? uploadedModelUrl : modelUrl;
17149
- const resetModelState = (0, import_react40.useCallback)(() => {
17343
+ const resetModelState = (0, import_react41.useCallback)(() => {
17150
17344
  setLoadedScene(null);
17151
17345
  setLargeModelTriangles(null);
17152
17346
  setLargeModelNoticeDismissed(false);
@@ -17157,11 +17351,11 @@ function SimpleModelViewer({
17157
17351
  setModelLoadFailed(false);
17158
17352
  setInitialFitComplete(false);
17159
17353
  }, []);
17160
- const revokeUploadedModelUrl = (0, import_react40.useCallback)(() => {
17354
+ const revokeUploadedModelUrl = (0, import_react41.useCallback)(() => {
17161
17355
  uploadedModelRef.current?.cleanup();
17162
17356
  uploadedModelRef.current = null;
17163
17357
  }, []);
17164
- const clearUploadedModel = (0, import_react40.useCallback)(() => {
17358
+ const clearUploadedModel = (0, import_react41.useCallback)(() => {
17165
17359
  revokeUploadedModelUrl();
17166
17360
  setUploadedModelUrl(null);
17167
17361
  setUploadedModelName(null);
@@ -17190,7 +17384,7 @@ function SimpleModelViewer({
17190
17384
  resetModelState,
17191
17385
  revokeUploadedModelUrl
17192
17386
  ]);
17193
- const handleUploadedModel = (0, import_react40.useCallback)(
17387
+ const handleUploadedModel = (0, import_react41.useCallback)(
17194
17388
  async (file) => {
17195
17389
  setIsResolvingUpload(true);
17196
17390
  try {
@@ -17209,7 +17403,7 @@ function SimpleModelViewer({
17209
17403
  },
17210
17404
  [onLoadError, resetModelState, revokeUploadedModelUrl]
17211
17405
  );
17212
- const handleFileSelection = (0, import_react40.useCallback)(
17406
+ const handleFileSelection = (0, import_react41.useCallback)(
17213
17407
  (files) => {
17214
17408
  const file = files?.[0];
17215
17409
  if (!file) return;
@@ -17217,29 +17411,29 @@ function SimpleModelViewer({
17217
17411
  },
17218
17412
  [handleUploadedModel]
17219
17413
  );
17220
- (0, import_react40.useEffect)(() => {
17414
+ (0, import_react41.useEffect)(() => {
17221
17415
  return () => {
17222
17416
  revokeUploadedModelUrl();
17223
17417
  };
17224
17418
  }, [revokeUploadedModelUrl]);
17225
- const handleEnvSettingChange = (0, import_react40.useCallback)(
17419
+ const handleEnvSettingChange = (0, import_react41.useCallback)(
17226
17420
  (key, value) => {
17227
17421
  setEnvSettings((prev) => ({ ...prev, [key]: value }));
17228
17422
  },
17229
17423
  []
17230
17424
  );
17231
- (0, import_react40.useEffect)(() => {
17425
+ (0, import_react41.useEffect)(() => {
17232
17426
  setEnvSettings((prev) => ({ ...prev, backgroundColor }));
17233
17427
  }, [backgroundColor]);
17234
- (0, import_react40.useEffect)(() => {
17428
+ (0, import_react41.useEffect)(() => {
17235
17429
  resetModelState();
17236
17430
  }, [activeModelUrl, resetModelState]);
17237
- const handleMeshesReady = (0, import_react40.useCallback)((meshes) => {
17431
+ const handleMeshesReady = (0, import_react41.useCallback)((meshes) => {
17238
17432
  setMeshKeys(meshes);
17239
17433
  setHiddenKeys(/* @__PURE__ */ new Set());
17240
17434
  setSelectedKey(null);
17241
17435
  }, []);
17242
- const handleModelLoaded = (0, import_react40.useCallback)(
17436
+ const handleModelLoaded = (0, import_react41.useCallback)(
17243
17437
  (scene) => {
17244
17438
  setLoadedScene(scene);
17245
17439
  setModelLoadFailed(false);
@@ -17250,14 +17444,14 @@ function SimpleModelViewer({
17250
17444
  },
17251
17445
  [onModelLoaded]
17252
17446
  );
17253
- const handleLoadError = (0, import_react40.useCallback)(
17447
+ const handleLoadError = (0, import_react41.useCallback)(
17254
17448
  (error) => {
17255
17449
  setModelLoadFailed(true);
17256
17450
  onLoadError?.(error);
17257
17451
  },
17258
17452
  [onLoadError]
17259
17453
  );
17260
- const focusMesh = (0, import_react40.useCallback)(
17454
+ const focusMesh = (0, import_react41.useCallback)(
17261
17455
  (key) => {
17262
17456
  setSelectedKey(key);
17263
17457
  if (!zoomOnSelected) return;
@@ -17273,14 +17467,14 @@ function SimpleModelViewer({
17273
17467
  },
17274
17468
  [zoomOnSelected]
17275
17469
  );
17276
- const handleToggleVisibility = (0, import_react40.useCallback)((key) => {
17470
+ const handleToggleVisibility = (0, import_react41.useCallback)((key) => {
17277
17471
  setHiddenKeys((prev) => {
17278
17472
  const next = new Set(prev);
17279
17473
  next.has(key) ? next.delete(key) : next.add(key);
17280
17474
  return next;
17281
17475
  });
17282
17476
  }, []);
17283
- const handleDragEnter = (0, import_react40.useCallback)(
17477
+ const handleDragEnter = (0, import_react41.useCallback)(
17284
17478
  (event) => {
17285
17479
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
17286
17480
  return;
@@ -17292,7 +17486,7 @@ function SimpleModelViewer({
17292
17486
  },
17293
17487
  [enableModelUpload]
17294
17488
  );
17295
- const handleDragOver = (0, import_react40.useCallback)(
17489
+ const handleDragOver = (0, import_react41.useCallback)(
17296
17490
  (event) => {
17297
17491
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
17298
17492
  return;
@@ -17304,7 +17498,7 @@ function SimpleModelViewer({
17304
17498
  },
17305
17499
  [enableModelUpload]
17306
17500
  );
17307
- const handleDragLeave = (0, import_react40.useCallback)(
17501
+ const handleDragLeave = (0, import_react41.useCallback)(
17308
17502
  (event) => {
17309
17503
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
17310
17504
  return;
@@ -17318,7 +17512,7 @@ function SimpleModelViewer({
17318
17512
  },
17319
17513
  [enableModelUpload]
17320
17514
  );
17321
- const handleDrop = (0, import_react40.useCallback)(
17515
+ const handleDrop = (0, import_react41.useCallback)(
17322
17516
  (event) => {
17323
17517
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
17324
17518
  return;
@@ -17411,7 +17605,7 @@ function SimpleModelViewer({
17411
17605
  raycaster: { firstHitOnly: true },
17412
17606
  style: { background: envSettings.backgroundColor },
17413
17607
  camera: { position: [0, 0, 5], fov: 45, near: 0.1, far: 1e3 },
17414
- 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: [
17415
17609
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
17416
17610
  "ambientLight",
17417
17611
  {
@@ -17429,7 +17623,7 @@ function SimpleModelViewer({
17429
17623
  }
17430
17624
  ),
17431
17625
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("directionalLight", { position: [-5, 5, 5], intensity: 0.5 }),
17432
- /* @__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)(
17433
17627
  import_drei6.Environment,
17434
17628
  {
17435
17629
  preset: envSettings.envPreset,
@@ -17448,7 +17642,7 @@ function SimpleModelViewer({
17448
17642
  ModelErrorBoundary2,
17449
17643
  {
17450
17644
  onLoadError: handleLoadError,
17451
- 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)(
17452
17646
  InnerModel,
17453
17647
  {
17454
17648
  url: activeModelUrl,
@@ -17547,18 +17741,18 @@ function SimpleModelViewer({
17547
17741
  }
17548
17742
 
17549
17743
  // src/hooks/useViewerSelection.ts
17550
- var import_react41 = require("react");
17744
+ var import_react42 = require("react");
17551
17745
  function useViewerSelection(initialSelectedObjectBinding = null) {
17552
- const [selectedObjectBinding, setSelectedObjectBinding] = (0, import_react41.useState)(
17746
+ const [selectedObjectBinding, setSelectedObjectBinding] = (0, import_react42.useState)(
17553
17747
  initialSelectedObjectBinding
17554
17748
  );
17555
- const handleObjectSelect = (0, import_react41.useCallback)(
17749
+ const handleObjectSelect = (0, import_react42.useCallback)(
17556
17750
  (binding) => {
17557
17751
  setSelectedObjectBinding(binding);
17558
17752
  },
17559
17753
  [setSelectedObjectBinding]
17560
17754
  );
17561
- const clearSelection = (0, import_react41.useCallback)(() => {
17755
+ const clearSelection = (0, import_react42.useCallback)(() => {
17562
17756
  setSelectedObjectBinding(null);
17563
17757
  }, []);
17564
17758
  return {
@@ -17570,9 +17764,9 @@ function useViewerSelection(initialSelectedObjectBinding = null) {
17570
17764
  }
17571
17765
 
17572
17766
  // src/hooks/useObjectBinding.ts
17573
- var import_react42 = require("react");
17767
+ var import_react43 = require("react");
17574
17768
  function useObjectBinding(objectBindings, objectId) {
17575
- return (0, import_react42.useMemo)(() => {
17769
+ return (0, import_react43.useMemo)(() => {
17576
17770
  if (!objectId) {
17577
17771
  return null;
17578
17772
  }
@@ -17587,16 +17781,16 @@ function useObjectBinding(objectBindings, objectId) {
17587
17781
  }
17588
17782
 
17589
17783
  // src/hooks/useObjectBindingIds.ts
17590
- var import_react43 = require("react");
17784
+ var import_react44 = require("react");
17591
17785
  function useObjectBindingIds(objectBindings) {
17592
- return (0, import_react43.useMemo)(
17786
+ return (0, import_react44.useMemo)(
17593
17787
  () => Object.values(objectBindings).map((binding) => binding.modelObjectId),
17594
17788
  [objectBindings]
17595
17789
  );
17596
17790
  }
17597
17791
 
17598
17792
  // src/hooks/useObjectBindings.ts
17599
- var import_react44 = require("react");
17793
+ var import_react45 = require("react");
17600
17794
 
17601
17795
  // src/utils/deepMerge.ts
17602
17796
  function isPlainObject2(value) {
@@ -17646,8 +17840,8 @@ function getHiddenObjects(objectBindings) {
17646
17840
  );
17647
17841
  }
17648
17842
  function useObjectBindings(initialBindings) {
17649
- const [objectBindings, setObjectBindings] = (0, import_react44.useState)(initialBindings);
17650
- const updateObjectBindings = (0, import_react44.useCallback)(
17843
+ const [objectBindings, setObjectBindings] = (0, import_react45.useState)(initialBindings);
17844
+ const updateObjectBindings = (0, import_react45.useCallback)(
17651
17845
  (objectIdOrEdits, patchValue) => {
17652
17846
  setObjectBindings(
17653
17847
  (current) => patchValue === void 0 ? patchBindings(current, objectIdOrEdits) : patchBindings(
@@ -17659,34 +17853,34 @@ function useObjectBindings(initialBindings) {
17659
17853
  },
17660
17854
  []
17661
17855
  );
17662
- const setMaterial = (0, import_react44.useCallback)(
17856
+ const setMaterial = (0, import_react45.useCallback)(
17663
17857
  (objectId, value) => updateObjectBindings(objectId, material(value)),
17664
17858
  [updateObjectBindings]
17665
17859
  );
17666
- const setBaseColor = (0, import_react44.useCallback)(
17860
+ const setBaseColor = (0, import_react45.useCallback)(
17667
17861
  (objectId, color) => setMaterial(objectId, { baseColor: color }),
17668
17862
  [setMaterial]
17669
17863
  );
17670
- const resetMaterial = (0, import_react44.useCallback)(
17864
+ const resetMaterial = (0, import_react45.useCallback)(
17671
17865
  (objectId) => updateObjectBindings(objectId, { style: { material: void 0 } }),
17672
17866
  [updateObjectBindings]
17673
17867
  );
17674
- const getMaterial = (0, import_react44.useCallback)(
17868
+ const getMaterial = (0, import_react45.useCallback)(
17675
17869
  (objectId) => {
17676
17870
  const bindingKey = resolveBindingKey(objectBindings, objectId);
17677
17871
  return bindingKey ? objectBindings[bindingKey]?.style?.material ?? null : null;
17678
17872
  },
17679
17873
  [objectBindings]
17680
17874
  );
17681
- const hideObject = (0, import_react44.useCallback)(
17875
+ const hideObject = (0, import_react45.useCallback)(
17682
17876
  (objectId) => updateObjectBindings(objectId, { visible: false }),
17683
17877
  [updateObjectBindings]
17684
17878
  );
17685
- const showObject = (0, import_react44.useCallback)(
17879
+ const showObject = (0, import_react45.useCallback)(
17686
17880
  (objectId) => updateObjectBindings(objectId, { visible: true }),
17687
17881
  [updateObjectBindings]
17688
17882
  );
17689
- const toggleObjectVisibility = (0, import_react44.useCallback)(
17883
+ const toggleObjectVisibility = (0, import_react45.useCallback)(
17690
17884
  (objectId) => {
17691
17885
  setObjectBindings((current) => {
17692
17886
  const objectIds = Array.isArray(objectId) ? objectId : [objectId];
@@ -17705,22 +17899,22 @@ function useObjectBindings(initialBindings) {
17705
17899
  },
17706
17900
  [setObjectBindings]
17707
17901
  );
17708
- const clearHiddenObjects = (0, import_react44.useCallback)(() => {
17902
+ const clearHiddenObjects = (0, import_react45.useCallback)(() => {
17709
17903
  setObjectBindings(
17710
17904
  (current) => patchBindings(current, Object.keys(current), { visible: true })
17711
17905
  );
17712
17906
  }, [setObjectBindings]);
17713
- const hiddenObjects = (0, import_react44.useMemo)(
17907
+ const hiddenObjects = (0, import_react45.useMemo)(
17714
17908
  () => getHiddenObjects(objectBindings),
17715
17909
  [objectBindings]
17716
17910
  );
17717
- const hiddenObjectIds = (0, import_react44.useMemo)(
17911
+ const hiddenObjectIds = (0, import_react45.useMemo)(
17718
17912
  () => Object.keys(hiddenObjects).map(
17719
17913
  (bindingKey) => objectBindings[bindingKey]?.id ?? bindingKey
17720
17914
  ),
17721
17915
  [hiddenObjects, objectBindings]
17722
17916
  );
17723
- const isObjectHidden = (0, import_react44.useCallback)(
17917
+ const isObjectHidden = (0, import_react45.useCallback)(
17724
17918
  (objectId) => {
17725
17919
  const bindingKey = resolveBindingKey(objectBindings, objectId);
17726
17920
  return bindingKey ? objectBindings[bindingKey]?.visible === false : false;
@@ -17746,7 +17940,7 @@ function useObjectBindings(initialBindings) {
17746
17940
  }
17747
17941
 
17748
17942
  // src/hooks/useSceneConfig.ts
17749
- var import_react45 = require("react");
17943
+ var import_react46 = require("react");
17750
17944
 
17751
17945
  // src/utils/patchSceneConfig.ts
17752
17946
  function patchSceneConfig(sceneConfig, patch) {
@@ -17755,19 +17949,19 @@ function patchSceneConfig(sceneConfig, patch) {
17755
17949
 
17756
17950
  // src/hooks/useSceneConfig.ts
17757
17951
  function useSceneConfig(initialSceneConfig) {
17758
- const initialSceneConfigRef = (0, import_react45.useRef)(null);
17759
- const [sceneConfig, setSceneConfig] = (0, import_react45.useState)(() => {
17952
+ const initialSceneConfigRef = (0, import_react46.useRef)(null);
17953
+ const [sceneConfig, setSceneConfig] = (0, import_react46.useState)(() => {
17760
17954
  const initial = typeof initialSceneConfig === "function" ? initialSceneConfig() : initialSceneConfig;
17761
17955
  initialSceneConfigRef.current = initial;
17762
17956
  return initial;
17763
17957
  });
17764
- const updateSceneConfig = (0, import_react45.useCallback)((update) => {
17958
+ const updateSceneConfig = (0, import_react46.useCallback)((update) => {
17765
17959
  setSceneConfig((current) => {
17766
17960
  const patch = typeof update === "function" ? update(current) : update;
17767
17961
  return patchSceneConfig(current, patch);
17768
17962
  });
17769
17963
  }, []);
17770
- const resetSceneConfig = (0, import_react45.useCallback)(() => {
17964
+ const resetSceneConfig = (0, import_react46.useCallback)(() => {
17771
17965
  const initial = initialSceneConfigRef.current;
17772
17966
  if (initial) setSceneConfig(initial);
17773
17967
  }, []);
@@ -17780,10 +17974,10 @@ function useSceneConfig(initialSceneConfig) {
17780
17974
  }
17781
17975
 
17782
17976
  // src/hooks/useViewerHover.ts
17783
- var import_react46 = require("react");
17977
+ var import_react47 = require("react");
17784
17978
  function useViewerHover() {
17785
- const [hoveredObjectBinding, setHoveredObjectBinding] = (0, import_react46.useState)(null);
17786
- const handleHoveredObject = (0, import_react46.useCallback)(
17979
+ const [hoveredObjectBinding, setHoveredObjectBinding] = (0, import_react47.useState)(null);
17980
+ const handleHoveredObject = (0, import_react47.useCallback)(
17787
17981
  (binding) => {
17788
17982
  setHoveredObjectBinding(binding);
17789
17983
  },
@@ -17796,18 +17990,18 @@ function useViewerHover() {
17796
17990
  }
17797
17991
 
17798
17992
  // src/hooks/useViewerModel.ts
17799
- var import_react47 = require("react");
17993
+ var import_react48 = require("react");
17800
17994
  var import_three22 = require("three");
17801
17995
  function toTuple2(vector) {
17802
17996
  return [vector.x, vector.y, vector.z];
17803
17997
  }
17804
17998
  function useViewerModel() {
17805
- const [isLoading, setIsLoading] = (0, import_react47.useState)(true);
17806
- const [isReady, setIsReady] = (0, import_react47.useState)(false);
17807
- const [error, setError] = (0, import_react47.useState)(null);
17808
- const [bounds, setBounds] = (0, import_react47.useState)(null);
17809
- const [objectCount, setObjectCount] = (0, import_react47.useState)(0);
17810
- const handleModelLoaded = (0, import_react47.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) => {
17811
18005
  const box = new import_three22.Box3().setFromObject(scene);
17812
18006
  const min = box.min.clone();
17813
18007
  const max = box.max.clone();
@@ -17832,14 +18026,14 @@ function useViewerModel() {
17832
18026
  setIsLoading(false);
17833
18027
  setIsReady(true);
17834
18028
  }, []);
17835
- const handleLoadError = (0, import_react47.useCallback)((nextError) => {
18029
+ const handleLoadError = (0, import_react48.useCallback)((nextError) => {
17836
18030
  setError(nextError);
17837
18031
  setBounds(null);
17838
18032
  setObjectCount(0);
17839
18033
  setIsLoading(false);
17840
18034
  setIsReady(false);
17841
18035
  }, []);
17842
- const resetModelState = (0, import_react47.useCallback)(() => {
18036
+ const resetModelState = (0, import_react48.useCallback)(() => {
17843
18037
  setIsLoading(true);
17844
18038
  setIsReady(false);
17845
18039
  setError(null);
@@ -17859,10 +18053,10 @@ function useViewerModel() {
17859
18053
  }
17860
18054
 
17861
18055
  // src/hooks/useViewerActions.ts
17862
- var import_react48 = require("react");
18056
+ var import_react49 = require("react");
17863
18057
  function useViewerActions(objectBindings, options = {}) {
17864
18058
  const { onAction, onObjectBindingsChange } = options;
17865
- const getActionsForObject = (0, import_react48.useCallback)(
18059
+ const getActionsForObject = (0, import_react49.useCallback)(
17866
18060
  (objectId) => {
17867
18061
  const bindingKey = resolveBindingKey(objectBindings, objectId);
17868
18062
  if (!bindingKey) {
@@ -17872,7 +18066,7 @@ function useViewerActions(objectBindings, options = {}) {
17872
18066
  },
17873
18067
  [objectBindings]
17874
18068
  );
17875
- const runAction = (0, import_react48.useCallback)(
18069
+ const runAction = (0, import_react49.useCallback)(
17876
18070
  (objectId, actionId) => {
17877
18071
  const bindingKey = resolveBindingKey(objectBindings, objectId);
17878
18072
  if (!bindingKey) {
@@ -17914,7 +18108,7 @@ function useViewerActions(objectBindings, options = {}) {
17914
18108
  }
17915
18109
 
17916
18110
  // src/hooks/useShareableViewerState.ts
17917
- var import_react49 = require("react");
18111
+ var import_react50 = require("react");
17918
18112
  function serializeState(cameraState, objectBindings, selectedObjectBinding) {
17919
18113
  const hidden = Object.values(objectBindings).filter((binding) => binding.visible === false).map((binding) => binding.id);
17920
18114
  return {
@@ -17934,8 +18128,8 @@ function useShareableViewerState({
17934
18128
  debounceMs = 400,
17935
18129
  autoSync = true
17936
18130
  }) {
17937
- const lastWrittenRawRef = (0, import_react49.useRef)(null);
17938
- const buildHash = (0, import_react49.useCallback)(() => {
18131
+ const lastWrittenRawRef = (0, import_react50.useRef)(null);
18132
+ const buildHash = (0, import_react50.useCallback)(() => {
17939
18133
  const state = serializeState(
17940
18134
  cameraState,
17941
18135
  objectBindings,
@@ -17943,12 +18137,12 @@ function useShareableViewerState({
17943
18137
  );
17944
18138
  return `${paramKey}=${encodeURIComponent(JSON.stringify(state))}`;
17945
18139
  }, [cameraState, objectBindings, selectedObjectBinding, paramKey]);
17946
- const getShareUrl = (0, import_react49.useCallback)(() => {
18140
+ const getShareUrl = (0, import_react50.useCallback)(() => {
17947
18141
  const url = new URL(window.location.href);
17948
18142
  url.hash = buildHash();
17949
18143
  return url.toString();
17950
18144
  }, [buildHash]);
17951
- const syncUrl = (0, import_react49.useCallback)(() => {
18145
+ const syncUrl = (0, import_react50.useCallback)(() => {
17952
18146
  const url = new URL(window.location.href);
17953
18147
  url.hash = buildHash();
17954
18148
  window.history.replaceState(null, "", url.toString());
@@ -17956,7 +18150,7 @@ function useShareableViewerState({
17956
18150
  paramKey
17957
18151
  );
17958
18152
  }, [buildHash, paramKey]);
17959
- const latestStateRef = (0, import_react49.useRef)({
18153
+ const latestStateRef = (0, import_react50.useRef)({
17960
18154
  cameraState,
17961
18155
  objectBindings,
17962
18156
  selectedObjectBinding
@@ -17966,9 +18160,9 @@ function useShareableViewerState({
17966
18160
  objectBindings,
17967
18161
  selectedObjectBinding
17968
18162
  };
17969
- const [restoreAttempted, setRestoreAttempted] = (0, import_react49.useState)(false);
17970
- const lastSyncedHashRef = (0, import_react49.useRef)(null);
17971
- (0, import_react49.useEffect)(() => {
18163
+ const [restoreAttempted, setRestoreAttempted] = (0, import_react50.useState)(false);
18164
+ const lastSyncedHashRef = (0, import_react50.useRef)(null);
18165
+ (0, import_react50.useEffect)(() => {
17972
18166
  if (!autoSync || !restoreAttempted) return;
17973
18167
  const tick = () => {
17974
18168
  const current = latestStateRef.current;
@@ -17990,9 +18184,9 @@ function useShareableViewerState({
17990
18184
  const id = window.setInterval(tick, debounceMs);
17991
18185
  return () => window.clearInterval(id);
17992
18186
  }, [autoSync, restoreAttempted, debounceMs, paramKey]);
17993
- const lastRestoredRawRef = (0, import_react49.useRef)(null);
17994
- const restoringRawRef = (0, import_react49.useRef)(null);
17995
- const restoreFromUrl = (0, import_react49.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 () => {
17996
18190
  setRestoreAttempted(true);
17997
18191
  const rawHash = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
17998
18192
  const raw = new URLSearchParams(rawHash).get(paramKey);
@@ -18045,7 +18239,7 @@ function useShareableViewerState({
18045
18239
  setCameraState,
18046
18240
  setSelectedObjectBinding
18047
18241
  ]);
18048
- (0, import_react49.useEffect)(() => {
18242
+ (0, import_react50.useEffect)(() => {
18049
18243
  const handleHashChange = () => {
18050
18244
  void restoreFromUrl();
18051
18245
  };