@liveroom-tech/react-immersive 2.0.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -4
- package/dist/index.d.mts +24 -3
- package/dist/index.d.ts +24 -3
- package/dist/index.js +533 -299
- package/dist/index.mjs +458 -226
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -5531,32 +5531,37 @@ function ObjectBindingDataPanel({
|
|
|
5531
5531
|
/* @__PURE__ */ jsx15("hr", { className: "left-panel__divider" }),
|
|
5532
5532
|
objectToRender.actions && objectToRender.actions.length > 0 && /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
5533
5533
|
/* @__PURE__ */ jsx15("div", { className: "left-panel__actions-label", children: "Actions" }),
|
|
5534
|
-
/* @__PURE__ */ jsx15("div", { className: "left-panel__actions-list", children: objectToRender.actions.map((action) => {
|
|
5534
|
+
/* @__PURE__ */ jsx15("div", { className: "left-panel__actions-list", children: objectToRender.actions.map((action, index) => {
|
|
5535
5535
|
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);
|
|
5536
5536
|
const extra = renderActionExtra?.(action.id);
|
|
5537
|
-
return
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5537
|
+
return (
|
|
5538
|
+
// Keyed by position, not action.id: ids are author-supplied
|
|
5539
|
+
// and not guaranteed unique within a binding, and colliding
|
|
5540
|
+
// keys make React drop one of the buttons.
|
|
5541
|
+
/* @__PURE__ */ jsxs11(Fragment5, { children: [
|
|
5542
|
+
/* @__PURE__ */ jsx15(
|
|
5543
|
+
"button",
|
|
5544
|
+
{
|
|
5545
|
+
onClick: (e) => onAction({
|
|
5546
|
+
objectId: objectToRender.modelObjectId || objectToRender.id || "",
|
|
5547
|
+
action,
|
|
5548
|
+
binding: objectToRender,
|
|
5549
|
+
screenX: e.clientX,
|
|
5550
|
+
screenY: e.clientY
|
|
5551
|
+
}),
|
|
5552
|
+
className: [
|
|
5553
|
+
"left-panel__action-btn",
|
|
5554
|
+
`left-panel__action-btn--${action.id}`,
|
|
5555
|
+
isActive ? "left-panel__action-btn--active" : ""
|
|
5556
|
+
].filter(Boolean).join(" "),
|
|
5557
|
+
"data-action-id": action.id,
|
|
5558
|
+
"data-label": action.label,
|
|
5559
|
+
children: action.label
|
|
5560
|
+
}
|
|
5561
|
+
),
|
|
5562
|
+
/* @__PURE__ */ jsx15(ActionExtra, { content: extra })
|
|
5563
|
+
] }, index)
|
|
5564
|
+
);
|
|
5560
5565
|
}) })
|
|
5561
5566
|
] })
|
|
5562
5567
|
] })
|
|
@@ -8284,7 +8289,7 @@ function ModelViewer({
|
|
|
8284
8289
|
}
|
|
8285
8290
|
|
|
8286
8291
|
// src/components/BindingBuilder/BindingBuilder.tsx
|
|
8287
|
-
import { useCallback as useCallback9, useEffect as
|
|
8292
|
+
import { useCallback as useCallback9, useEffect as useEffect21, useMemo as useMemo16, useRef as useRef18, useState as useState26 } from "react";
|
|
8288
8293
|
import {
|
|
8289
8294
|
Redo2,
|
|
8290
8295
|
Undo2,
|
|
@@ -8295,7 +8300,7 @@ import {
|
|
|
8295
8300
|
} from "lucide-react";
|
|
8296
8301
|
|
|
8297
8302
|
// src/styles/BindingBuilder.css
|
|
8298
|
-
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');
|
|
8303
|
+
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');
|
|
8299
8304
|
|
|
8300
8305
|
// src/hooks/useViewerCamera.ts
|
|
8301
8306
|
import { useCallback as useCallback5, useRef as useRef12, useState as useState11 } from "react";
|
|
@@ -11795,7 +11800,7 @@ function LightingTab({ config, onChange }) {
|
|
|
11795
11800
|
{
|
|
11796
11801
|
className: "se-segmentedControl",
|
|
11797
11802
|
style: { gridAutoColumns: "1fr" },
|
|
11798
|
-
children: ["shadowCatcher"
|
|
11803
|
+
children: ["shadowCatcher"].map((m) => /* @__PURE__ */ jsx28(
|
|
11799
11804
|
"button",
|
|
11800
11805
|
{
|
|
11801
11806
|
type: "button",
|
|
@@ -14538,7 +14543,98 @@ function BasicsPanel({
|
|
|
14538
14543
|
}
|
|
14539
14544
|
|
|
14540
14545
|
// src/components/BindingBuilder/components/ActionsPanel.tsx
|
|
14541
|
-
import {
|
|
14546
|
+
import { useEffect as useEffect20, useState as useState24 } from "react";
|
|
14547
|
+
|
|
14548
|
+
// src/components/BindingBuilder/utils/actionCatalog.ts
|
|
14549
|
+
var ACTION_CATALOG = [
|
|
14550
|
+
{
|
|
14551
|
+
id: "change-color",
|
|
14552
|
+
label: "Change Color",
|
|
14553
|
+
handling: "viewer",
|
|
14554
|
+
description: "Opens a color picker docked under the action and commits the result to style.material.baseColor."
|
|
14555
|
+
},
|
|
14556
|
+
{
|
|
14557
|
+
id: "change-material",
|
|
14558
|
+
label: "Change Material",
|
|
14559
|
+
handling: "viewer",
|
|
14560
|
+
description: "Opens the material picker so the viewer can swap the object's texture."
|
|
14561
|
+
},
|
|
14562
|
+
{
|
|
14563
|
+
id: "toggle-visibility",
|
|
14564
|
+
label: "Toggle Visibility",
|
|
14565
|
+
handling: "viewer",
|
|
14566
|
+
description: "Flips the binding's visible flag."
|
|
14567
|
+
},
|
|
14568
|
+
{
|
|
14569
|
+
id: "toggle-light",
|
|
14570
|
+
label: "Toggle Light",
|
|
14571
|
+
handling: "host",
|
|
14572
|
+
description: "Seeded on light bindings. Handle it in your onAction callback."
|
|
14573
|
+
},
|
|
14574
|
+
{
|
|
14575
|
+
id: "change-light-color",
|
|
14576
|
+
label: "Change Light Color",
|
|
14577
|
+
handling: "host",
|
|
14578
|
+
description: "Seeded on light bindings. Handle it in your onAction callback."
|
|
14579
|
+
},
|
|
14580
|
+
{
|
|
14581
|
+
id: "set-brightness",
|
|
14582
|
+
label: "Set Brightness",
|
|
14583
|
+
handling: "host",
|
|
14584
|
+
description: "Seeded on light bindings. Handle it in your onAction callback."
|
|
14585
|
+
}
|
|
14586
|
+
];
|
|
14587
|
+
var CUSTOM_ACTION_ID = "__custom__";
|
|
14588
|
+
function findCatalogAction(id) {
|
|
14589
|
+
return ACTION_CATALOG.find((entry) => entry.id === id);
|
|
14590
|
+
}
|
|
14591
|
+
var VIEWER_ACTION_IDS = ACTION_CATALOG.filter(
|
|
14592
|
+
(entry) => entry.handling === "viewer"
|
|
14593
|
+
).map((entry) => entry.id);
|
|
14594
|
+
var AUTO_LABELS = /* @__PURE__ */ new Set([
|
|
14595
|
+
"",
|
|
14596
|
+
"New Action",
|
|
14597
|
+
...ACTION_CATALOG.map((entry) => entry.label)
|
|
14598
|
+
]);
|
|
14599
|
+
function isAutoFilledLabel(label) {
|
|
14600
|
+
return AUTO_LABELS.has(label.trim());
|
|
14601
|
+
}
|
|
14602
|
+
function levenshtein(a, b) {
|
|
14603
|
+
let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
|
|
14604
|
+
for (let i = 1; i <= a.length; i += 1) {
|
|
14605
|
+
const current = [i];
|
|
14606
|
+
for (let j = 1; j <= b.length; j += 1) {
|
|
14607
|
+
const substitution = previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1);
|
|
14608
|
+
current[j] = Math.min(current[j - 1] + 1, previous[j] + 1, substitution);
|
|
14609
|
+
}
|
|
14610
|
+
previous = current;
|
|
14611
|
+
}
|
|
14612
|
+
return previous[b.length];
|
|
14613
|
+
}
|
|
14614
|
+
function suggestActionId(id) {
|
|
14615
|
+
if (findCatalogAction(id)) return null;
|
|
14616
|
+
const candidate = id.trim().toLowerCase();
|
|
14617
|
+
if (!candidate) return null;
|
|
14618
|
+
const normalized = candidate.replace(/[^a-z0-9]/g, "");
|
|
14619
|
+
let best = null;
|
|
14620
|
+
for (const entry of ACTION_CATALOG) {
|
|
14621
|
+
const distance = Math.min(
|
|
14622
|
+
levenshtein(candidate, entry.id),
|
|
14623
|
+
levenshtein(normalized, entry.id.replace(/[^a-z0-9]/g, ""))
|
|
14624
|
+
);
|
|
14625
|
+
if (!best || distance < best.distance) {
|
|
14626
|
+
best = { id: entry.id, distance };
|
|
14627
|
+
}
|
|
14628
|
+
}
|
|
14629
|
+
return best && best.distance <= 2 ? best.id : null;
|
|
14630
|
+
}
|
|
14631
|
+
|
|
14632
|
+
// src/components/BindingBuilder/components/ActionsPanel.tsx
|
|
14633
|
+
import { Fragment as Fragment12, jsx as jsx41, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
14634
|
+
var VIEWER_ACTIONS = ACTION_CATALOG.filter(
|
|
14635
|
+
(entry) => entry.handling === "viewer"
|
|
14636
|
+
);
|
|
14637
|
+
var HOST_ACTIONS = ACTION_CATALOG.filter((entry) => entry.handling === "host");
|
|
14542
14638
|
function ActionsPanel({
|
|
14543
14639
|
selectedItem,
|
|
14544
14640
|
selectedBinding,
|
|
@@ -14546,85 +14642,183 @@ function ActionsPanel({
|
|
|
14546
14642
|
onToggle,
|
|
14547
14643
|
updateBinding
|
|
14548
14644
|
}) {
|
|
14645
|
+
const [customRows, setCustomRows] = useState24(/* @__PURE__ */ new Set());
|
|
14646
|
+
useEffect20(() => {
|
|
14647
|
+
setCustomRows(/* @__PURE__ */ new Set());
|
|
14648
|
+
}, [selectedItem]);
|
|
14649
|
+
const actions = selectedBinding.actions ?? [];
|
|
14650
|
+
const updateAction = (index, patch) => updateBinding(selectedItem, (binding) => ({
|
|
14651
|
+
...binding,
|
|
14652
|
+
actions: (binding.actions ?? []).map(
|
|
14653
|
+
(entry, entryIndex) => entryIndex === index ? patch(entry) : entry
|
|
14654
|
+
)
|
|
14655
|
+
}));
|
|
14656
|
+
const removeAction = (index) => {
|
|
14657
|
+
updateBinding(selectedItem, (binding) => ({
|
|
14658
|
+
...binding,
|
|
14659
|
+
actions: (binding.actions ?? []).filter(
|
|
14660
|
+
(_, entryIndex) => entryIndex !== index
|
|
14661
|
+
)
|
|
14662
|
+
}));
|
|
14663
|
+
setCustomRows((previous) => {
|
|
14664
|
+
const next = /* @__PURE__ */ new Set();
|
|
14665
|
+
previous.forEach((row) => {
|
|
14666
|
+
if (row < index) next.add(row);
|
|
14667
|
+
else if (row > index) next.add(row - 1);
|
|
14668
|
+
});
|
|
14669
|
+
return next;
|
|
14670
|
+
});
|
|
14671
|
+
};
|
|
14672
|
+
const setCustomRow = (index, custom) => setCustomRows((previous) => {
|
|
14673
|
+
const next = new Set(previous);
|
|
14674
|
+
if (custom) next.add(index);
|
|
14675
|
+
else next.delete(index);
|
|
14676
|
+
return next;
|
|
14677
|
+
});
|
|
14549
14678
|
return /* @__PURE__ */ jsxs36(BBSection, { title: "Actions", open, onToggle, children: [
|
|
14550
|
-
/* @__PURE__ */ jsx41("div", { className: "bb-actionList", children:
|
|
14551
|
-
|
|
14552
|
-
|
|
14553
|
-
|
|
14554
|
-
|
|
14555
|
-
|
|
14556
|
-
|
|
14557
|
-
|
|
14558
|
-
|
|
14559
|
-
|
|
14560
|
-
|
|
14561
|
-
|
|
14562
|
-
/* @__PURE__ */ jsxs36("div", { className: "bb-
|
|
14563
|
-
/* @__PURE__ */ jsxs36("div", { className: "bb-
|
|
14564
|
-
/* @__PURE__ */
|
|
14565
|
-
|
|
14566
|
-
|
|
14567
|
-
|
|
14568
|
-
|
|
14569
|
-
value: action.id,
|
|
14570
|
-
onChange: (event) => updateBinding(selectedItem, (binding) => ({
|
|
14571
|
-
...binding,
|
|
14572
|
-
actions: (binding.actions ?? []).map(
|
|
14573
|
-
(entry, entryIndex) => entryIndex === index ? { ...entry, id: event.target.value } : entry
|
|
14574
|
-
)
|
|
14575
|
-
}))
|
|
14576
|
-
}
|
|
14577
|
-
)
|
|
14679
|
+
/* @__PURE__ */ jsx41("div", { className: "bb-actionList", children: actions.map((action, index) => {
|
|
14680
|
+
const catalogEntry = findCatalogAction(action.id);
|
|
14681
|
+
const isCustom = customRows.has(index) || !catalogEntry;
|
|
14682
|
+
const trimmedId = action.id.trim();
|
|
14683
|
+
const suggestion = catalogEntry ? null : suggestActionId(action.id);
|
|
14684
|
+
const isDuplicate = trimmedId.length > 0 && actions.some(
|
|
14685
|
+
(other, otherIndex) => otherIndex !== index && other.id.trim() === trimmedId
|
|
14686
|
+
);
|
|
14687
|
+
return (
|
|
14688
|
+
// Keyed by position, not action.id: the id is an editable field, so
|
|
14689
|
+
// keying on it would remount the card on every keystroke and drop
|
|
14690
|
+
// input focus.
|
|
14691
|
+
/* @__PURE__ */ jsxs36("div", { className: "bb-actionCard", children: [
|
|
14692
|
+
/* @__PURE__ */ jsxs36("div", { className: "bb-actionCardHeader", children: [
|
|
14693
|
+
/* @__PURE__ */ jsxs36("div", { className: "bb-actionCardTitle", children: [
|
|
14694
|
+
"Action ",
|
|
14695
|
+
index + 1
|
|
14696
|
+
] }),
|
|
14697
|
+
/* @__PURE__ */ jsx41("div", { className: "bb-actionCardMeta", children: action.type })
|
|
14578
14698
|
] }),
|
|
14579
|
-
/* @__PURE__ */ jsxs36("div", { className: "bb-
|
|
14580
|
-
/* @__PURE__ */
|
|
14581
|
-
|
|
14582
|
-
|
|
14583
|
-
|
|
14584
|
-
|
|
14585
|
-
|
|
14586
|
-
|
|
14587
|
-
|
|
14588
|
-
|
|
14589
|
-
(
|
|
14590
|
-
|
|
14591
|
-
|
|
14592
|
-
|
|
14593
|
-
|
|
14594
|
-
|
|
14595
|
-
|
|
14596
|
-
|
|
14597
|
-
|
|
14598
|
-
|
|
14599
|
-
|
|
14600
|
-
|
|
14601
|
-
|
|
14602
|
-
|
|
14603
|
-
|
|
14604
|
-
|
|
14699
|
+
/* @__PURE__ */ jsxs36("div", { className: "bb-actionGrid", children: [
|
|
14700
|
+
/* @__PURE__ */ jsxs36("div", { className: "bb-field", children: [
|
|
14701
|
+
/* @__PURE__ */ jsx41("label", { className: "bb-label", children: "Action ID" }),
|
|
14702
|
+
/* @__PURE__ */ jsxs36(
|
|
14703
|
+
"select",
|
|
14704
|
+
{
|
|
14705
|
+
className: "bb-select",
|
|
14706
|
+
value: isCustom ? CUSTOM_ACTION_ID : action.id,
|
|
14707
|
+
onChange: (event) => {
|
|
14708
|
+
const value = event.target.value;
|
|
14709
|
+
if (value === CUSTOM_ACTION_ID) {
|
|
14710
|
+
setCustomRow(index, true);
|
|
14711
|
+
return;
|
|
14712
|
+
}
|
|
14713
|
+
setCustomRow(index, false);
|
|
14714
|
+
const picked = findCatalogAction(value);
|
|
14715
|
+
updateAction(index, (current) => ({
|
|
14716
|
+
...current,
|
|
14717
|
+
id: value,
|
|
14718
|
+
// Keep wording the author typed themselves.
|
|
14719
|
+
label: picked && isAutoFilledLabel(current.label) ? picked.label : current.label
|
|
14720
|
+
}));
|
|
14721
|
+
},
|
|
14722
|
+
children: [
|
|
14723
|
+
/* @__PURE__ */ jsx41("optgroup", { label: "Handled by the viewer", children: VIEWER_ACTIONS.map((entry) => /* @__PURE__ */ jsx41("option", { value: entry.id, children: entry.id }, entry.id)) }),
|
|
14724
|
+
/* @__PURE__ */ jsx41("optgroup", { label: "Handled by your app", children: HOST_ACTIONS.map((entry) => /* @__PURE__ */ jsx41("option", { value: entry.id, children: entry.id }, entry.id)) }),
|
|
14725
|
+
/* @__PURE__ */ jsx41("option", { value: CUSTOM_ACTION_ID, children: "Custom\u2026" })
|
|
14726
|
+
]
|
|
14727
|
+
}
|
|
14605
14728
|
)
|
|
14606
|
-
})
|
|
14607
|
-
|
|
14608
|
-
|
|
14609
|
-
|
|
14610
|
-
|
|
14611
|
-
|
|
14729
|
+
] }),
|
|
14730
|
+
isCustom ? /* @__PURE__ */ jsxs36("div", { className: "bb-field", children: [
|
|
14731
|
+
/* @__PURE__ */ jsx41("label", { className: "bb-label", children: "Custom action ID" }),
|
|
14732
|
+
/* @__PURE__ */ jsx41(
|
|
14733
|
+
"input",
|
|
14734
|
+
{
|
|
14735
|
+
className: "bb-input",
|
|
14736
|
+
value: action.id,
|
|
14737
|
+
placeholder: "open-datasheet",
|
|
14738
|
+
onChange: (event) => {
|
|
14739
|
+
setCustomRow(index, true);
|
|
14740
|
+
updateAction(index, (entry) => ({
|
|
14741
|
+
...entry,
|
|
14742
|
+
id: event.target.value
|
|
14743
|
+
}));
|
|
14744
|
+
}
|
|
14745
|
+
}
|
|
14746
|
+
)
|
|
14747
|
+
] }) : null,
|
|
14748
|
+
/* @__PURE__ */ jsxs36("div", { className: "bb-field", children: [
|
|
14749
|
+
/* @__PURE__ */ jsx41("label", { className: "bb-label", children: "Label" }),
|
|
14750
|
+
/* @__PURE__ */ jsx41(
|
|
14751
|
+
"input",
|
|
14752
|
+
{
|
|
14753
|
+
className: "bb-input",
|
|
14754
|
+
value: action.label,
|
|
14755
|
+
onChange: (event) => updateAction(index, (entry) => ({
|
|
14756
|
+
...entry,
|
|
14757
|
+
label: event.target.value
|
|
14758
|
+
}))
|
|
14759
|
+
}
|
|
14760
|
+
)
|
|
14761
|
+
] }),
|
|
14762
|
+
catalogEntry ? /* @__PURE__ */ jsx41("span", { className: "bb-note", children: catalogEntry.description }) : trimmedId.length === 0 ? /* @__PURE__ */ jsx41("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__ */ jsxs36("span", { className: "bb-note", style: { color: "#fcd34d" }, children: [
|
|
14763
|
+
"Unrecognised ID \u2014 the viewer has no built-in behaviour for it, so your app must handle it in onAction.",
|
|
14764
|
+
suggestion ? /* @__PURE__ */ jsxs36(Fragment12, { children: [
|
|
14765
|
+
" Did you mean ",
|
|
14766
|
+
/* @__PURE__ */ jsx41(
|
|
14767
|
+
"button",
|
|
14768
|
+
{
|
|
14769
|
+
type: "button",
|
|
14770
|
+
className: "bb-linkButton",
|
|
14771
|
+
onClick: () => {
|
|
14772
|
+
setCustomRow(index, false);
|
|
14773
|
+
const picked = findCatalogAction(suggestion);
|
|
14774
|
+
updateAction(index, (current) => ({
|
|
14775
|
+
...current,
|
|
14776
|
+
id: suggestion,
|
|
14777
|
+
label: picked && isAutoFilledLabel(current.label) ? picked.label : current.label
|
|
14778
|
+
}));
|
|
14779
|
+
},
|
|
14780
|
+
children: suggestion
|
|
14781
|
+
}
|
|
14782
|
+
),
|
|
14783
|
+
"?"
|
|
14784
|
+
] }) : null
|
|
14785
|
+
] }),
|
|
14786
|
+
isDuplicate ? /* @__PURE__ */ jsx41("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
|
|
14787
|
+
] }),
|
|
14788
|
+
/* @__PURE__ */ jsx41("div", { className: "bb-actionFooter", children: /* @__PURE__ */ jsx41(
|
|
14789
|
+
"button",
|
|
14790
|
+
{
|
|
14791
|
+
type: "button",
|
|
14792
|
+
className: "bb-button bb-button--ghost bb-actionRemove",
|
|
14793
|
+
onClick: () => removeAction(index),
|
|
14794
|
+
children: "Remove Action"
|
|
14795
|
+
}
|
|
14796
|
+
) })
|
|
14797
|
+
] }, index)
|
|
14798
|
+
);
|
|
14799
|
+
}) }),
|
|
14612
14800
|
/* @__PURE__ */ jsx41("div", { style: { marginTop: 10 }, children: /* @__PURE__ */ jsx41(
|
|
14613
14801
|
"button",
|
|
14614
14802
|
{
|
|
14615
14803
|
type: "button",
|
|
14616
14804
|
className: "bb-button",
|
|
14617
|
-
onClick: () =>
|
|
14618
|
-
|
|
14619
|
-
|
|
14620
|
-
|
|
14621
|
-
|
|
14622
|
-
|
|
14623
|
-
|
|
14624
|
-
|
|
14625
|
-
|
|
14626
|
-
|
|
14627
|
-
|
|
14805
|
+
onClick: () => {
|
|
14806
|
+
const unused = VIEWER_ACTIONS.find(
|
|
14807
|
+
(entry) => !actions.some((action) => action.id === entry.id)
|
|
14808
|
+
);
|
|
14809
|
+
updateBinding(selectedItem, (binding) => ({
|
|
14810
|
+
...binding,
|
|
14811
|
+
actions: [
|
|
14812
|
+
...binding.actions ?? [],
|
|
14813
|
+
{
|
|
14814
|
+
id: unused?.id ?? "",
|
|
14815
|
+
label: unused?.label ?? "New Action",
|
|
14816
|
+
type: "command"
|
|
14817
|
+
}
|
|
14818
|
+
]
|
|
14819
|
+
}));
|
|
14820
|
+
if (!unused) setCustomRow(actions.length, true);
|
|
14821
|
+
},
|
|
14628
14822
|
children: "Add Action"
|
|
14629
14823
|
}
|
|
14630
14824
|
) })
|
|
@@ -14633,7 +14827,7 @@ function ActionsPanel({
|
|
|
14633
14827
|
|
|
14634
14828
|
// src/components/BindingBuilder/components/CameraPositionPanel.tsx
|
|
14635
14829
|
import { MapPin as MapPin3, Trash2 as Trash25 } from "lucide-react";
|
|
14636
|
-
import { Fragment as
|
|
14830
|
+
import { Fragment as Fragment13, jsx as jsx42, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
14637
14831
|
function CameraPositionPanel({
|
|
14638
14832
|
selectedBinding,
|
|
14639
14833
|
open,
|
|
@@ -14652,7 +14846,7 @@ function CameraPositionPanel({
|
|
|
14652
14846
|
/* @__PURE__ */ jsx42("span", { className: "bb-cameraLiveBadge", children: "LIVE" }),
|
|
14653
14847
|
/* @__PURE__ */ jsxs37("div", { className: "se-waypointBody", children: [
|
|
14654
14848
|
/* @__PURE__ */ jsx42("div", { className: "bb-cameraWaypointLabel", children: "Current view" }),
|
|
14655
|
-
cameraState ? /* @__PURE__ */ jsxs37(
|
|
14849
|
+
cameraState ? /* @__PURE__ */ jsxs37(Fragment13, { children: [
|
|
14656
14850
|
/* @__PURE__ */ jsxs37("div", { className: "se-waypointCoords", children: [
|
|
14657
14851
|
"pos ",
|
|
14658
14852
|
formattedCameraState?.position
|
|
@@ -14738,7 +14932,7 @@ function CameraPositionPanel({
|
|
|
14738
14932
|
}
|
|
14739
14933
|
|
|
14740
14934
|
// src/components/BindingBuilder/components/SceneNodesPanel.tsx
|
|
14741
|
-
import { useMemo as useMemo15, useState as
|
|
14935
|
+
import { useMemo as useMemo15, useState as useState25 } from "react";
|
|
14742
14936
|
|
|
14743
14937
|
// src/components/BindingBuilder/utils/bindingFactory.ts
|
|
14744
14938
|
function slugify2(value) {
|
|
@@ -14818,7 +15012,7 @@ function collectNodeNames(scene) {
|
|
|
14818
15012
|
}
|
|
14819
15013
|
|
|
14820
15014
|
// src/components/BindingBuilder/components/SceneNodesPanel.tsx
|
|
14821
|
-
import { Fragment as
|
|
15015
|
+
import { Fragment as Fragment14, jsx as jsx43, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
14822
15016
|
function SceneNodesPanel({
|
|
14823
15017
|
bindings,
|
|
14824
15018
|
selectedItem,
|
|
@@ -14827,7 +15021,7 @@ function SceneNodesPanel({
|
|
|
14827
15021
|
isOpen = true,
|
|
14828
15022
|
onToggle
|
|
14829
15023
|
}) {
|
|
14830
|
-
const [search, setSearch] =
|
|
15024
|
+
const [search, setSearch] = useState25("");
|
|
14831
15025
|
const nodeKeys = useMemo15(() => Object.keys(bindings), [bindings]);
|
|
14832
15026
|
const filteredNodeKeys = useMemo15(() => {
|
|
14833
15027
|
const query = search.trim().toLowerCase();
|
|
@@ -14854,7 +15048,7 @@ function SceneNodesPanel({
|
|
|
14854
15048
|
children: "\u2190"
|
|
14855
15049
|
}
|
|
14856
15050
|
),
|
|
14857
|
-
isOpen && /* @__PURE__ */ jsxs38(
|
|
15051
|
+
isOpen && /* @__PURE__ */ jsxs38(Fragment14, { children: [
|
|
14858
15052
|
/* @__PURE__ */ jsxs38("div", { children: [
|
|
14859
15053
|
/* @__PURE__ */ jsx43("div", { className: "bb-sectionKicker", children: "Scene Nodes" }),
|
|
14860
15054
|
/* @__PURE__ */ jsxs38("div", { className: "bb-sectionHeading", children: [
|
|
@@ -15263,7 +15457,7 @@ function removeMaterialPanelFields(material2, panel) {
|
|
|
15263
15457
|
}
|
|
15264
15458
|
|
|
15265
15459
|
// src/components/BindingBuilder/BindingBuilder.tsx
|
|
15266
|
-
import { Fragment as
|
|
15460
|
+
import { Fragment as Fragment15, jsx as jsx45, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
15267
15461
|
var DEMO_MODEL_URL = "/room.glb";
|
|
15268
15462
|
function isPlainObject(value) {
|
|
15269
15463
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -15283,36 +15477,36 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15283
15477
|
const modelInputRef = useRef18(null);
|
|
15284
15478
|
const bindingsInputRef = useRef18(null);
|
|
15285
15479
|
const sceneConfigInputRef = useRef18(null);
|
|
15286
|
-
const [modelUrl, setModelUrl] =
|
|
15287
|
-
const [modelName, setModelName] =
|
|
15288
|
-
const [selectedItem, setSelectedItem] =
|
|
15480
|
+
const [modelUrl, setModelUrl] = useState26(null);
|
|
15481
|
+
const [modelName, setModelName] = useState26("");
|
|
15482
|
+
const [selectedItem, setSelectedItem] = useState26(null);
|
|
15289
15483
|
const bindingsHistory = useUndoableState({});
|
|
15290
15484
|
const bindings = bindingsHistory.value;
|
|
15291
15485
|
const setBindings = bindingsHistory.setValue;
|
|
15292
|
-
const [loading, setLoading] =
|
|
15293
|
-
const [isExporting, setIsExporting] =
|
|
15294
|
-
const [statusMessage, setStatusMessage] =
|
|
15486
|
+
const [loading, setLoading] = useState26(false);
|
|
15487
|
+
const [isExporting, setIsExporting] = useState26(false);
|
|
15488
|
+
const [statusMessage, setStatusMessage] = useState26(
|
|
15295
15489
|
"Load the demo model or upload a GLB/GLTF model to generate bindings."
|
|
15296
15490
|
);
|
|
15297
|
-
const [statusTone, setStatusTone] =
|
|
15298
|
-
const [statusBannerVisible, setStatusBannerVisible] =
|
|
15299
|
-
const [metricsDraft, setMetricsDraft] =
|
|
15300
|
-
const [metadataDraft, setMetadataDraft] =
|
|
15301
|
-
const [metricsError, setMetricsError] =
|
|
15302
|
-
const [metadataError, setMetadataError] =
|
|
15303
|
-
const [isPreviewDragActive, setIsPreviewDragActive] =
|
|
15304
|
-
const [isEditorCollapsed, setIsEditorCollapsed] =
|
|
15305
|
-
const [isNodesCollapsed, setIsNodesCollapsed] =
|
|
15306
|
-
const [openObjectSections, setOpenObjectSections] =
|
|
15491
|
+
const [statusTone, setStatusTone] = useState26("default");
|
|
15492
|
+
const [statusBannerVisible, setStatusBannerVisible] = useState26(false);
|
|
15493
|
+
const [metricsDraft, setMetricsDraft] = useState26("{}");
|
|
15494
|
+
const [metadataDraft, setMetadataDraft] = useState26("{}");
|
|
15495
|
+
const [metricsError, setMetricsError] = useState26(null);
|
|
15496
|
+
const [metadataError, setMetadataError] = useState26(null);
|
|
15497
|
+
const [isPreviewDragActive, setIsPreviewDragActive] = useState26(false);
|
|
15498
|
+
const [isEditorCollapsed, setIsEditorCollapsed] = useState26(false);
|
|
15499
|
+
const [isNodesCollapsed, setIsNodesCollapsed] = useState26(false);
|
|
15500
|
+
const [openObjectSections, setOpenObjectSections] = useState26({
|
|
15307
15501
|
identity: true,
|
|
15308
15502
|
basics: true,
|
|
15309
|
-
material:
|
|
15503
|
+
material: false,
|
|
15310
15504
|
actions: false,
|
|
15311
15505
|
camera: false,
|
|
15312
15506
|
metrics: false,
|
|
15313
15507
|
metadata: false
|
|
15314
15508
|
});
|
|
15315
|
-
const [openMaterialPanels, setOpenMaterialPanels] =
|
|
15509
|
+
const [openMaterialPanels, setOpenMaterialPanels] = useState26({
|
|
15316
15510
|
pbr: true,
|
|
15317
15511
|
roughness: false,
|
|
15318
15512
|
emission: false,
|
|
@@ -15328,24 +15522,24 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15328
15522
|
});
|
|
15329
15523
|
const toggleObjectSection = (key) => setOpenObjectSections((prev) => ({ ...prev, [key]: !prev[key] }));
|
|
15330
15524
|
const toggleMaterialPanel = (key) => setOpenMaterialPanels((prev) => ({ ...prev, [key]: !prev[key] }));
|
|
15331
|
-
const [activeEditorTab, setActiveEditorTab] =
|
|
15525
|
+
const [activeEditorTab, setActiveEditorTab] = useState26(
|
|
15332
15526
|
"object"
|
|
15333
15527
|
);
|
|
15334
|
-
const [activeSubTab, setActiveSubTab] =
|
|
15335
|
-
const [pbrWorkflow, setPbrWorkflow] =
|
|
15336
|
-
const [materialPanelEnabled, setMaterialPanelEnabled] =
|
|
15528
|
+
const [activeSubTab, setActiveSubTab] = useState26("General");
|
|
15529
|
+
const [pbrWorkflow, setPbrWorkflow] = useState26("metalness");
|
|
15530
|
+
const [materialPanelEnabled, setMaterialPanelEnabled] = useState26(createMaterialPanelEnabledState());
|
|
15337
15531
|
const materialPanelItemRef = useRef18(null);
|
|
15338
|
-
const [zoomOnSelected, setZoomOnSelected] =
|
|
15339
|
-
const [highlightOnHover, setHighlightOnHover] =
|
|
15340
|
-
const [resetDialogOpen, setResetDialogOpen] =
|
|
15341
|
-
const [resetTarget, setResetTarget] =
|
|
15532
|
+
const [zoomOnSelected, setZoomOnSelected] = useState26(false);
|
|
15533
|
+
const [highlightOnHover, setHighlightOnHover] = useState26(true);
|
|
15534
|
+
const [resetDialogOpen, setResetDialogOpen] = useState26(false);
|
|
15535
|
+
const [resetTarget, setResetTarget] = useState26(
|
|
15342
15536
|
null
|
|
15343
15537
|
);
|
|
15344
15538
|
const sceneConfigHistory = useUndoableState(DEFAULT_SCENE_CONFIG);
|
|
15345
15539
|
const sceneConfig = sceneConfigHistory.value;
|
|
15346
15540
|
const setSceneConfig = sceneConfigHistory.setValue;
|
|
15347
|
-
const [activeAnnotation, setActiveAnnotation] =
|
|
15348
|
-
const [panelHoveredBinding, setPanelHoveredBinding] =
|
|
15541
|
+
const [activeAnnotation, setActiveAnnotation] = useState26(null);
|
|
15542
|
+
const [panelHoveredBinding, setPanelHoveredBinding] = useState26(null);
|
|
15349
15543
|
const license = useLicense(licenseKey);
|
|
15350
15544
|
const tier = normalizeTier(license.tier);
|
|
15351
15545
|
const bindingBuilderContextValue = useMemo16(
|
|
@@ -15421,7 +15615,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15421
15615
|
},
|
|
15422
15616
|
[]
|
|
15423
15617
|
);
|
|
15424
|
-
|
|
15618
|
+
useEffect21(() => {
|
|
15425
15619
|
if (!selectedBinding) {
|
|
15426
15620
|
setMetricsDraft("{}");
|
|
15427
15621
|
setMetadataDraft("{}");
|
|
@@ -15434,7 +15628,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15434
15628
|
setMetricsError(null);
|
|
15435
15629
|
setMetadataError(null);
|
|
15436
15630
|
}, [selectedBinding]);
|
|
15437
|
-
|
|
15631
|
+
useEffect21(() => {
|
|
15438
15632
|
const derived = createMaterialPanelEnabledState(selectedMaterial);
|
|
15439
15633
|
if (materialPanelItemRef.current !== selectedItem) {
|
|
15440
15634
|
materialPanelItemRef.current = selectedItem;
|
|
@@ -15453,12 +15647,12 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15453
15647
|
return changed ? next : prev;
|
|
15454
15648
|
});
|
|
15455
15649
|
}, [selectedItem, selectedMaterial]);
|
|
15456
|
-
|
|
15650
|
+
useEffect21(() => {
|
|
15457
15651
|
return () => {
|
|
15458
15652
|
uploadedModelRef.current?.cleanup();
|
|
15459
15653
|
};
|
|
15460
15654
|
}, []);
|
|
15461
|
-
|
|
15655
|
+
useEffect21(() => {
|
|
15462
15656
|
const handleKeyDown = (event) => {
|
|
15463
15657
|
if (event.key.toLowerCase() !== "z" || !(event.metaKey || event.ctrlKey)) {
|
|
15464
15658
|
return;
|
|
@@ -15479,7 +15673,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15479
15673
|
window.addEventListener("keydown", handleKeyDown);
|
|
15480
15674
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
15481
15675
|
}, [activeEditorTab, bindingsHistory, sceneConfigHistory]);
|
|
15482
|
-
|
|
15676
|
+
useEffect21(() => {
|
|
15483
15677
|
if (loading || statusTone === "default" || statusTone === "success") return;
|
|
15484
15678
|
setStatusBannerVisible(true);
|
|
15485
15679
|
const timeout = window.setTimeout(
|
|
@@ -16059,7 +16253,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16059
16253
|
}
|
|
16060
16254
|
)
|
|
16061
16255
|
] }),
|
|
16062
|
-
!isEditorCollapsed && /* @__PURE__ */ jsxs40(
|
|
16256
|
+
!isEditorCollapsed && /* @__PURE__ */ jsxs40(Fragment15, { children: [
|
|
16063
16257
|
/* @__PURE__ */ jsxs40("div", { className: "bb-tabRow", children: [
|
|
16064
16258
|
/* @__PURE__ */ jsx45(
|
|
16065
16259
|
"button",
|
|
@@ -16080,7 +16274,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16080
16274
|
}
|
|
16081
16275
|
)
|
|
16082
16276
|
] }),
|
|
16083
|
-
/* @__PURE__ */ jsx45("section", { className: "bb-editor", children: activeEditorTab === "scene" ? /* @__PURE__ */ jsx45(
|
|
16277
|
+
/* @__PURE__ */ jsx45("section", { className: "bb-editor", children: activeEditorTab === "scene" ? /* @__PURE__ */ jsx45(Fragment15, { children: modelUrl ? /* @__PURE__ */ jsx45(
|
|
16084
16278
|
SceneSettingsTab,
|
|
16085
16279
|
{
|
|
16086
16280
|
config: sceneConfig,
|
|
@@ -16102,7 +16296,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16102
16296
|
animationClipDetails: viewerAnimations.clipDetails,
|
|
16103
16297
|
animationPlayback: viewerAnimations
|
|
16104
16298
|
}
|
|
16105
|
-
) : /* @__PURE__ */ jsx45("div", { className: "bb-emptyEditor", children: "Upload a model to edit its scene settings." }) }) : !selectedBinding || !selectedItem ? /* @__PURE__ */ jsx45("div", { className: "bb-emptyEditor", children: "Select a generated node binding to edit its values." }) : /* @__PURE__ */ jsxs40(
|
|
16299
|
+
) : /* @__PURE__ */ jsx45("div", { className: "bb-emptyEditor", children: "Upload a model to edit its scene settings." }) }) : !selectedBinding || !selectedItem ? /* @__PURE__ */ jsx45("div", { className: "bb-emptyEditor", children: "Select a generated node binding to edit its values." }) : /* @__PURE__ */ jsxs40(Fragment15, { children: [
|
|
16106
16300
|
/* @__PURE__ */ jsx45("div", { className: "bb-editorHeader", children: /* @__PURE__ */ jsxs40("div", { children: [
|
|
16107
16301
|
/* @__PURE__ */ jsx45("div", { className: "bb-sectionKicker", children: "Editing Binding" }),
|
|
16108
16302
|
/* @__PURE__ */ jsx45("h2", { className: "bb-editorTitle", children: selectedBinding.label || selectedItem }),
|
|
@@ -16522,9 +16716,9 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16522
16716
|
// src/components/SimpleModelViewer/SimpleModelViewer.tsx
|
|
16523
16717
|
import {
|
|
16524
16718
|
useRef as useRef19,
|
|
16525
|
-
useState as
|
|
16719
|
+
useState as useState27,
|
|
16526
16720
|
useCallback as useCallback10,
|
|
16527
|
-
useEffect as
|
|
16721
|
+
useEffect as useEffect22,
|
|
16528
16722
|
useMemo as useMemo17,
|
|
16529
16723
|
Component as Component3,
|
|
16530
16724
|
Suspense as Suspense3
|
|
@@ -16553,7 +16747,7 @@ import {
|
|
|
16553
16747
|
Upload as Upload2,
|
|
16554
16748
|
AlertTriangle as AlertTriangle4
|
|
16555
16749
|
} from "lucide-react";
|
|
16556
|
-
import { Fragment as
|
|
16750
|
+
import { Fragment as Fragment16, jsx as jsx46, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
16557
16751
|
var HIGHLIGHT2 = { color: "#93c5fd", intensity: 0.05 };
|
|
16558
16752
|
var ENV_PRESETS = [
|
|
16559
16753
|
"apartment",
|
|
@@ -16613,7 +16807,7 @@ function InnerModel({
|
|
|
16613
16807
|
meshopt: true
|
|
16614
16808
|
});
|
|
16615
16809
|
const invalidate = useThree11((state) => state.invalidate);
|
|
16616
|
-
const [hoveredKey, setHoveredKey] =
|
|
16810
|
+
const [hoveredKey, setHoveredKey] = useState27(null);
|
|
16617
16811
|
const scene = useMemo17(
|
|
16618
16812
|
() => SkeletonUtils2.clone(rawScene),
|
|
16619
16813
|
[rawScene]
|
|
@@ -16643,14 +16837,14 @@ function InnerModel({
|
|
|
16643
16837
|
}
|
|
16644
16838
|
return map;
|
|
16645
16839
|
}, [meshMap]);
|
|
16646
|
-
|
|
16840
|
+
useEffect22(
|
|
16647
16841
|
() => () => {
|
|
16648
16842
|
Object.values(perKeyMaterials).forEach((m) => m.dispose());
|
|
16649
16843
|
},
|
|
16650
16844
|
[perKeyMaterials]
|
|
16651
16845
|
);
|
|
16652
16846
|
const notifiedUrl = useRef19(null);
|
|
16653
|
-
|
|
16847
|
+
useEffect22(() => {
|
|
16654
16848
|
if (notifiedUrl.current === url) return;
|
|
16655
16849
|
notifiedUrl.current = url;
|
|
16656
16850
|
scene.updateWorldMatrix(true, true);
|
|
@@ -16658,7 +16852,7 @@ function InnerModel({
|
|
|
16658
16852
|
onMeshesReady(meshMap);
|
|
16659
16853
|
onModelLoaded(scene);
|
|
16660
16854
|
}, [url, scene, meshMap, onMeshesReady, onModelLoaded]);
|
|
16661
|
-
|
|
16855
|
+
useEffect22(() => {
|
|
16662
16856
|
const controller = new AbortController();
|
|
16663
16857
|
void buildSceneBVH(scene, controller.signal);
|
|
16664
16858
|
return () => {
|
|
@@ -16670,22 +16864,22 @@ function InnerModel({
|
|
|
16670
16864
|
() => !exceedsBvhBudget(scene),
|
|
16671
16865
|
[scene]
|
|
16672
16866
|
);
|
|
16673
|
-
|
|
16867
|
+
useEffect22(() => {
|
|
16674
16868
|
for (const [key, { ref }] of Object.entries(meshMap)) {
|
|
16675
16869
|
nodeRefsOut.current[key] = ref;
|
|
16676
16870
|
}
|
|
16677
16871
|
}, [meshMap, nodeRefsOut]);
|
|
16678
|
-
|
|
16872
|
+
useEffect22(() => {
|
|
16679
16873
|
if (!highlightOnHover && hoveredKey !== null) {
|
|
16680
16874
|
setHoveredKey(null);
|
|
16681
16875
|
}
|
|
16682
16876
|
}, [highlightOnHover, hoveredKey]);
|
|
16683
|
-
|
|
16877
|
+
useEffect22(() => {
|
|
16684
16878
|
return () => {
|
|
16685
16879
|
document.body.style.cursor = "default";
|
|
16686
16880
|
};
|
|
16687
16881
|
}, []);
|
|
16688
|
-
|
|
16882
|
+
useEffect22(() => {
|
|
16689
16883
|
for (const [key, { ref }] of Object.entries(meshMap)) {
|
|
16690
16884
|
ref.visible = !hiddenKeys.has(key);
|
|
16691
16885
|
const mat = perKeyMaterials[key];
|
|
@@ -16759,7 +16953,7 @@ function CameraFitter({
|
|
|
16759
16953
|
const invalidate = useThree11((state) => state.invalidate);
|
|
16760
16954
|
const size = useThree11((state) => state.size);
|
|
16761
16955
|
const resizeTimerRef = useRef19(null);
|
|
16762
|
-
|
|
16956
|
+
useEffect22(() => {
|
|
16763
16957
|
fitted.current = false;
|
|
16764
16958
|
invalidate();
|
|
16765
16959
|
}, [invalidate, scene]);
|
|
@@ -16779,7 +16973,7 @@ function CameraFitter({
|
|
|
16779
16973
|
}).catch(() => {
|
|
16780
16974
|
}).finally(onInitialFitComplete);
|
|
16781
16975
|
});
|
|
16782
|
-
|
|
16976
|
+
useEffect22(() => {
|
|
16783
16977
|
if (!refitOnResize || !fitted.current || !scene || !controlsRef.current) {
|
|
16784
16978
|
return;
|
|
16785
16979
|
}
|
|
@@ -16807,7 +17001,7 @@ function CameraFitter({
|
|
|
16807
17001
|
function ExposureController({ exposure }) {
|
|
16808
17002
|
const gl = useThree11((state) => state.gl);
|
|
16809
17003
|
const invalidate = useThree11((state) => state.invalidate);
|
|
16810
|
-
|
|
17004
|
+
useEffect22(() => {
|
|
16811
17005
|
gl.toneMappingExposure = exposure;
|
|
16812
17006
|
invalidate();
|
|
16813
17007
|
}, [gl, exposure, invalidate]);
|
|
@@ -16915,7 +17109,7 @@ function EnvironmentSection({
|
|
|
16915
17109
|
title,
|
|
16916
17110
|
children
|
|
16917
17111
|
}) {
|
|
16918
|
-
const [collapsed, setCollapsed] =
|
|
17112
|
+
const [collapsed, setCollapsed] = useState27(false);
|
|
16919
17113
|
return /* @__PURE__ */ jsxs41("div", { className: "env-section", children: [
|
|
16920
17114
|
/* @__PURE__ */ jsxs41(
|
|
16921
17115
|
"button",
|
|
@@ -17080,8 +17274,8 @@ function SceneObjectsPanel2({
|
|
|
17080
17274
|
onHover,
|
|
17081
17275
|
onToggleVisibility
|
|
17082
17276
|
}) {
|
|
17083
|
-
const [search, setSearch] =
|
|
17084
|
-
const [rowHovered, setRowHovered] =
|
|
17277
|
+
const [search, setSearch] = useState27("");
|
|
17278
|
+
const [rowHovered, setRowHovered] = useState27(null);
|
|
17085
17279
|
const entries = Object.values(meshKeys).filter(
|
|
17086
17280
|
(m) => m.label.toLowerCase().includes(search.toLowerCase())
|
|
17087
17281
|
);
|
|
@@ -17210,30 +17404,30 @@ function SimpleModelViewer({
|
|
|
17210
17404
|
const fileInputRef = useRef19(null);
|
|
17211
17405
|
const uploadedModelRef = useRef19(null);
|
|
17212
17406
|
const dragDepthRef = useRef19(0);
|
|
17213
|
-
const [loadedScene, setLoadedScene] =
|
|
17214
|
-
const [largeModelTriangles, setLargeModelTriangles] =
|
|
17407
|
+
const [loadedScene, setLoadedScene] = useState27(null);
|
|
17408
|
+
const [largeModelTriangles, setLargeModelTriangles] = useState27(
|
|
17215
17409
|
null
|
|
17216
17410
|
);
|
|
17217
|
-
const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] =
|
|
17218
|
-
const [meshKeys, setMeshKeys] =
|
|
17219
|
-
const [hiddenKeys, setHiddenKeys] =
|
|
17220
|
-
const [selectedKey, setSelectedKey] =
|
|
17221
|
-
const [panelHoveredKey, setPanelHoveredKey] =
|
|
17222
|
-
const [uploadedModelUrl, setUploadedModelUrl] =
|
|
17223
|
-
const [uploadedModelName, setUploadedModelName] =
|
|
17411
|
+
const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = useState27(false);
|
|
17412
|
+
const [meshKeys, setMeshKeys] = useState27({});
|
|
17413
|
+
const [hiddenKeys, setHiddenKeys] = useState27(/* @__PURE__ */ new Set());
|
|
17414
|
+
const [selectedKey, setSelectedKey] = useState27(null);
|
|
17415
|
+
const [panelHoveredKey, setPanelHoveredKey] = useState27(null);
|
|
17416
|
+
const [uploadedModelUrl, setUploadedModelUrl] = useState27(null);
|
|
17417
|
+
const [uploadedModelName, setUploadedModelName] = useState27(
|
|
17224
17418
|
null
|
|
17225
17419
|
);
|
|
17226
|
-
const [modelLoadFailed, setModelLoadFailed] =
|
|
17227
|
-
const [isResolvingUpload, setIsResolvingUpload] =
|
|
17228
|
-
const [initialFitComplete, setInitialFitComplete] =
|
|
17229
|
-
const [isDragActive, setIsDragActive] =
|
|
17230
|
-
const [isPanelOpen, setIsPanelOpen] =
|
|
17420
|
+
const [modelLoadFailed, setModelLoadFailed] = useState27(false);
|
|
17421
|
+
const [isResolvingUpload, setIsResolvingUpload] = useState27(false);
|
|
17422
|
+
const [initialFitComplete, setInitialFitComplete] = useState27(false);
|
|
17423
|
+
const [isDragActive, setIsDragActive] = useState27(false);
|
|
17424
|
+
const [isPanelOpen, setIsPanelOpen] = useState27(
|
|
17231
17425
|
() => typeof window === "undefined" ? true : window.innerWidth > 900
|
|
17232
17426
|
);
|
|
17233
|
-
const [isEnvPanelOpen, setIsEnvPanelOpen] =
|
|
17427
|
+
const [isEnvPanelOpen, setIsEnvPanelOpen] = useState27(
|
|
17234
17428
|
() => typeof window === "undefined" ? true : window.innerWidth > 900
|
|
17235
17429
|
);
|
|
17236
|
-
const [envSettings, setEnvSettings] =
|
|
17430
|
+
const [envSettings, setEnvSettings] = useState27({
|
|
17237
17431
|
backgroundEnabled,
|
|
17238
17432
|
backgroundColor,
|
|
17239
17433
|
autoRotate,
|
|
@@ -17316,7 +17510,7 @@ function SimpleModelViewer({
|
|
|
17316
17510
|
},
|
|
17317
17511
|
[handleUploadedModel]
|
|
17318
17512
|
);
|
|
17319
|
-
|
|
17513
|
+
useEffect22(() => {
|
|
17320
17514
|
return () => {
|
|
17321
17515
|
revokeUploadedModelUrl();
|
|
17322
17516
|
};
|
|
@@ -17327,10 +17521,10 @@ function SimpleModelViewer({
|
|
|
17327
17521
|
},
|
|
17328
17522
|
[]
|
|
17329
17523
|
);
|
|
17330
|
-
|
|
17524
|
+
useEffect22(() => {
|
|
17331
17525
|
setEnvSettings((prev) => ({ ...prev, backgroundColor }));
|
|
17332
17526
|
}, [backgroundColor]);
|
|
17333
|
-
|
|
17527
|
+
useEffect22(() => {
|
|
17334
17528
|
resetModelState();
|
|
17335
17529
|
}, [activeModelUrl, resetModelState]);
|
|
17336
17530
|
const handleMeshesReady = useCallback10((meshes) => {
|
|
@@ -17471,7 +17665,7 @@ function SimpleModelViewer({
|
|
|
17471
17665
|
}
|
|
17472
17666
|
)
|
|
17473
17667
|
] }),
|
|
17474
|
-
enableModelUpload ? /* @__PURE__ */ jsxs41(
|
|
17668
|
+
enableModelUpload ? /* @__PURE__ */ jsxs41(Fragment16, { children: [
|
|
17475
17669
|
/* @__PURE__ */ jsx46(
|
|
17476
17670
|
"input",
|
|
17477
17671
|
{
|
|
@@ -17485,7 +17679,7 @@ function SimpleModelViewer({
|
|
|
17485
17679
|
}
|
|
17486
17680
|
}
|
|
17487
17681
|
),
|
|
17488
|
-
activeModelUrl ? /* @__PURE__ */ jsx46("div", { className: "smv-upload-toolbar", children: /* @__PURE__ */ jsxs41(
|
|
17682
|
+
activeModelUrl ? /* @__PURE__ */ jsx46("div", { className: "smv-upload-toolbar", children: /* @__PURE__ */ jsxs41(Fragment16, { children: [
|
|
17489
17683
|
/* @__PURE__ */ jsx46("span", { className: "smv-upload-toolbar__name", children: uploadedModelName ?? "Uploaded model" }),
|
|
17490
17684
|
/* @__PURE__ */ jsxs41(
|
|
17491
17685
|
"button",
|
|
@@ -17646,9 +17840,9 @@ function SimpleModelViewer({
|
|
|
17646
17840
|
}
|
|
17647
17841
|
|
|
17648
17842
|
// src/hooks/useViewerSelection.ts
|
|
17649
|
-
import { useCallback as useCallback11, useState as
|
|
17843
|
+
import { useCallback as useCallback11, useState as useState28 } from "react";
|
|
17650
17844
|
function useViewerSelection(initialSelectedObjectBinding = null) {
|
|
17651
|
-
const [selectedObjectBinding, setSelectedObjectBinding] =
|
|
17845
|
+
const [selectedObjectBinding, setSelectedObjectBinding] = useState28(
|
|
17652
17846
|
initialSelectedObjectBinding
|
|
17653
17847
|
);
|
|
17654
17848
|
const handleObjectSelect = useCallback11(
|
|
@@ -17695,17 +17889,14 @@ function useObjectBindingIds(objectBindings) {
|
|
|
17695
17889
|
}
|
|
17696
17890
|
|
|
17697
17891
|
// src/hooks/useObjectBindings.ts
|
|
17698
|
-
import { useCallback as useCallback12, useMemo as useMemo20, useState as
|
|
17892
|
+
import { useCallback as useCallback12, useMemo as useMemo20, useState as useState29 } from "react";
|
|
17699
17893
|
|
|
17700
|
-
// src/utils/
|
|
17894
|
+
// src/utils/deepMerge.ts
|
|
17701
17895
|
function isPlainObject2(value) {
|
|
17702
17896
|
if (typeof value !== "object" || value === null) return false;
|
|
17703
17897
|
const prototype = Object.getPrototypeOf(value);
|
|
17704
17898
|
return prototype === Object.prototype || prototype === null;
|
|
17705
17899
|
}
|
|
17706
|
-
function isBindingEditArray(value) {
|
|
17707
|
-
return Array.isArray(value) && value.length > 0 && typeof value[0] !== "string";
|
|
17708
|
-
}
|
|
17709
17900
|
function deepMerge(base, patch) {
|
|
17710
17901
|
if (!isPlainObject2(patch)) return patch;
|
|
17711
17902
|
const target = isPlainObject2(base) ? { ...base } : {};
|
|
@@ -17715,6 +17906,11 @@ function deepMerge(base, patch) {
|
|
|
17715
17906
|
}
|
|
17716
17907
|
return target;
|
|
17717
17908
|
}
|
|
17909
|
+
|
|
17910
|
+
// src/utils/patchBindings.ts
|
|
17911
|
+
function isBindingEditArray(value) {
|
|
17912
|
+
return Array.isArray(value) && value.length > 0 && typeof value[0] !== "string";
|
|
17913
|
+
}
|
|
17718
17914
|
function patchBindings(bindings, objectIdOrEdits, patch) {
|
|
17719
17915
|
const edits = isBindingEditArray(objectIdOrEdits) ? objectIdOrEdits : [{ ids: objectIdOrEdits, patch: patch ?? {} }];
|
|
17720
17916
|
let next = bindings;
|
|
@@ -17743,8 +17939,8 @@ function getHiddenObjects(objectBindings) {
|
|
|
17743
17939
|
);
|
|
17744
17940
|
}
|
|
17745
17941
|
function useObjectBindings(initialBindings) {
|
|
17746
|
-
const [objectBindings, setObjectBindings] =
|
|
17747
|
-
const
|
|
17942
|
+
const [objectBindings, setObjectBindings] = useState29(initialBindings);
|
|
17943
|
+
const updateObjectBindings = useCallback12(
|
|
17748
17944
|
(objectIdOrEdits, patchValue) => {
|
|
17749
17945
|
setObjectBindings(
|
|
17750
17946
|
(current) => patchValue === void 0 ? patchBindings(current, objectIdOrEdits) : patchBindings(
|
|
@@ -17757,16 +17953,16 @@ function useObjectBindings(initialBindings) {
|
|
|
17757
17953
|
[]
|
|
17758
17954
|
);
|
|
17759
17955
|
const setMaterial = useCallback12(
|
|
17760
|
-
(objectId, value) =>
|
|
17761
|
-
[
|
|
17956
|
+
(objectId, value) => updateObjectBindings(objectId, material(value)),
|
|
17957
|
+
[updateObjectBindings]
|
|
17762
17958
|
);
|
|
17763
17959
|
const setBaseColor = useCallback12(
|
|
17764
17960
|
(objectId, color) => setMaterial(objectId, { baseColor: color }),
|
|
17765
17961
|
[setMaterial]
|
|
17766
17962
|
);
|
|
17767
17963
|
const resetMaterial = useCallback12(
|
|
17768
|
-
(objectId) =>
|
|
17769
|
-
[
|
|
17964
|
+
(objectId) => updateObjectBindings(objectId, { style: { material: void 0 } }),
|
|
17965
|
+
[updateObjectBindings]
|
|
17770
17966
|
);
|
|
17771
17967
|
const getMaterial = useCallback12(
|
|
17772
17968
|
(objectId) => {
|
|
@@ -17776,12 +17972,12 @@ function useObjectBindings(initialBindings) {
|
|
|
17776
17972
|
[objectBindings]
|
|
17777
17973
|
);
|
|
17778
17974
|
const hideObject = useCallback12(
|
|
17779
|
-
(objectId) =>
|
|
17780
|
-
[
|
|
17975
|
+
(objectId) => updateObjectBindings(objectId, { visible: false }),
|
|
17976
|
+
[updateObjectBindings]
|
|
17781
17977
|
);
|
|
17782
17978
|
const showObject = useCallback12(
|
|
17783
|
-
(objectId) =>
|
|
17784
|
-
[
|
|
17979
|
+
(objectId) => updateObjectBindings(objectId, { visible: true }),
|
|
17980
|
+
[updateObjectBindings]
|
|
17785
17981
|
);
|
|
17786
17982
|
const toggleObjectVisibility = useCallback12(
|
|
17787
17983
|
(objectId) => {
|
|
@@ -17827,7 +18023,7 @@ function useObjectBindings(initialBindings) {
|
|
|
17827
18023
|
return {
|
|
17828
18024
|
objectBindings,
|
|
17829
18025
|
setObjectBindings,
|
|
17830
|
-
|
|
18026
|
+
updateObjectBindings,
|
|
17831
18027
|
setMaterial,
|
|
17832
18028
|
setBaseColor,
|
|
17833
18029
|
resetMaterial,
|
|
@@ -17842,11 +18038,45 @@ function useObjectBindings(initialBindings) {
|
|
|
17842
18038
|
};
|
|
17843
18039
|
}
|
|
17844
18040
|
|
|
18041
|
+
// src/hooks/useSceneConfig.ts
|
|
18042
|
+
import { useCallback as useCallback13, useRef as useRef20, useState as useState30 } from "react";
|
|
18043
|
+
|
|
18044
|
+
// src/utils/patchSceneConfig.ts
|
|
18045
|
+
function patchSceneConfig(sceneConfig, patch) {
|
|
18046
|
+
return deepMerge(sceneConfig, patch);
|
|
18047
|
+
}
|
|
18048
|
+
|
|
18049
|
+
// src/hooks/useSceneConfig.ts
|
|
18050
|
+
function useSceneConfig(initialSceneConfig) {
|
|
18051
|
+
const initialSceneConfigRef = useRef20(null);
|
|
18052
|
+
const [sceneConfig, setSceneConfig] = useState30(() => {
|
|
18053
|
+
const initial = typeof initialSceneConfig === "function" ? initialSceneConfig() : initialSceneConfig;
|
|
18054
|
+
initialSceneConfigRef.current = initial;
|
|
18055
|
+
return initial;
|
|
18056
|
+
});
|
|
18057
|
+
const updateSceneConfig = useCallback13((update) => {
|
|
18058
|
+
setSceneConfig((current) => {
|
|
18059
|
+
const patch = typeof update === "function" ? update(current) : update;
|
|
18060
|
+
return patchSceneConfig(current, patch);
|
|
18061
|
+
});
|
|
18062
|
+
}, []);
|
|
18063
|
+
const resetSceneConfig = useCallback13(() => {
|
|
18064
|
+
const initial = initialSceneConfigRef.current;
|
|
18065
|
+
if (initial) setSceneConfig(initial);
|
|
18066
|
+
}, []);
|
|
18067
|
+
return {
|
|
18068
|
+
sceneConfig,
|
|
18069
|
+
setSceneConfig,
|
|
18070
|
+
updateSceneConfig,
|
|
18071
|
+
resetSceneConfig
|
|
18072
|
+
};
|
|
18073
|
+
}
|
|
18074
|
+
|
|
17845
18075
|
// src/hooks/useViewerHover.ts
|
|
17846
|
-
import { useCallback as
|
|
18076
|
+
import { useCallback as useCallback14, useState as useState31 } from "react";
|
|
17847
18077
|
function useViewerHover() {
|
|
17848
|
-
const [hoveredObjectBinding, setHoveredObjectBinding] =
|
|
17849
|
-
const handleHoveredObject =
|
|
18078
|
+
const [hoveredObjectBinding, setHoveredObjectBinding] = useState31(null);
|
|
18079
|
+
const handleHoveredObject = useCallback14(
|
|
17850
18080
|
(binding) => {
|
|
17851
18081
|
setHoveredObjectBinding(binding);
|
|
17852
18082
|
},
|
|
@@ -17859,18 +18089,18 @@ function useViewerHover() {
|
|
|
17859
18089
|
}
|
|
17860
18090
|
|
|
17861
18091
|
// src/hooks/useViewerModel.ts
|
|
17862
|
-
import { useCallback as
|
|
18092
|
+
import { useCallback as useCallback15, useState as useState32 } from "react";
|
|
17863
18093
|
import { Box3 as Box36, Vector3 as Vector310 } from "three";
|
|
17864
18094
|
function toTuple2(vector) {
|
|
17865
18095
|
return [vector.x, vector.y, vector.z];
|
|
17866
18096
|
}
|
|
17867
18097
|
function useViewerModel() {
|
|
17868
|
-
const [isLoading, setIsLoading] =
|
|
17869
|
-
const [isReady, setIsReady] =
|
|
17870
|
-
const [error, setError] =
|
|
17871
|
-
const [bounds, setBounds] =
|
|
17872
|
-
const [objectCount, setObjectCount] =
|
|
17873
|
-
const handleModelLoaded =
|
|
18098
|
+
const [isLoading, setIsLoading] = useState32(true);
|
|
18099
|
+
const [isReady, setIsReady] = useState32(false);
|
|
18100
|
+
const [error, setError] = useState32(null);
|
|
18101
|
+
const [bounds, setBounds] = useState32(null);
|
|
18102
|
+
const [objectCount, setObjectCount] = useState32(0);
|
|
18103
|
+
const handleModelLoaded = useCallback15((scene) => {
|
|
17874
18104
|
const box = new Box36().setFromObject(scene);
|
|
17875
18105
|
const min = box.min.clone();
|
|
17876
18106
|
const max = box.max.clone();
|
|
@@ -17895,14 +18125,14 @@ function useViewerModel() {
|
|
|
17895
18125
|
setIsLoading(false);
|
|
17896
18126
|
setIsReady(true);
|
|
17897
18127
|
}, []);
|
|
17898
|
-
const handleLoadError =
|
|
18128
|
+
const handleLoadError = useCallback15((nextError) => {
|
|
17899
18129
|
setError(nextError);
|
|
17900
18130
|
setBounds(null);
|
|
17901
18131
|
setObjectCount(0);
|
|
17902
18132
|
setIsLoading(false);
|
|
17903
18133
|
setIsReady(false);
|
|
17904
18134
|
}, []);
|
|
17905
|
-
const resetModelState =
|
|
18135
|
+
const resetModelState = useCallback15(() => {
|
|
17906
18136
|
setIsLoading(true);
|
|
17907
18137
|
setIsReady(false);
|
|
17908
18138
|
setError(null);
|
|
@@ -17922,10 +18152,10 @@ function useViewerModel() {
|
|
|
17922
18152
|
}
|
|
17923
18153
|
|
|
17924
18154
|
// src/hooks/useViewerActions.ts
|
|
17925
|
-
import { useCallback as
|
|
18155
|
+
import { useCallback as useCallback16 } from "react";
|
|
17926
18156
|
function useViewerActions(objectBindings, options = {}) {
|
|
17927
18157
|
const { onAction, onObjectBindingsChange } = options;
|
|
17928
|
-
const getActionsForObject =
|
|
18158
|
+
const getActionsForObject = useCallback16(
|
|
17929
18159
|
(objectId) => {
|
|
17930
18160
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
17931
18161
|
if (!bindingKey) {
|
|
@@ -17935,7 +18165,7 @@ function useViewerActions(objectBindings, options = {}) {
|
|
|
17935
18165
|
},
|
|
17936
18166
|
[objectBindings]
|
|
17937
18167
|
);
|
|
17938
|
-
const runAction =
|
|
18168
|
+
const runAction = useCallback16(
|
|
17939
18169
|
(objectId, actionId) => {
|
|
17940
18170
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
17941
18171
|
if (!bindingKey) {
|
|
@@ -17977,7 +18207,7 @@ function useViewerActions(objectBindings, options = {}) {
|
|
|
17977
18207
|
}
|
|
17978
18208
|
|
|
17979
18209
|
// src/hooks/useShareableViewerState.ts
|
|
17980
|
-
import { useCallback as
|
|
18210
|
+
import { useCallback as useCallback17, useEffect as useEffect23, useRef as useRef21, useState as useState33 } from "react";
|
|
17981
18211
|
function serializeState(cameraState, objectBindings, selectedObjectBinding) {
|
|
17982
18212
|
const hidden = Object.values(objectBindings).filter((binding) => binding.visible === false).map((binding) => binding.id);
|
|
17983
18213
|
return {
|
|
@@ -17997,8 +18227,8 @@ function useShareableViewerState({
|
|
|
17997
18227
|
debounceMs = 400,
|
|
17998
18228
|
autoSync = true
|
|
17999
18229
|
}) {
|
|
18000
|
-
const lastWrittenRawRef =
|
|
18001
|
-
const buildHash =
|
|
18230
|
+
const lastWrittenRawRef = useRef21(null);
|
|
18231
|
+
const buildHash = useCallback17(() => {
|
|
18002
18232
|
const state = serializeState(
|
|
18003
18233
|
cameraState,
|
|
18004
18234
|
objectBindings,
|
|
@@ -18006,12 +18236,12 @@ function useShareableViewerState({
|
|
|
18006
18236
|
);
|
|
18007
18237
|
return `${paramKey}=${encodeURIComponent(JSON.stringify(state))}`;
|
|
18008
18238
|
}, [cameraState, objectBindings, selectedObjectBinding, paramKey]);
|
|
18009
|
-
const getShareUrl =
|
|
18239
|
+
const getShareUrl = useCallback17(() => {
|
|
18010
18240
|
const url = new URL(window.location.href);
|
|
18011
18241
|
url.hash = buildHash();
|
|
18012
18242
|
return url.toString();
|
|
18013
18243
|
}, [buildHash]);
|
|
18014
|
-
const syncUrl =
|
|
18244
|
+
const syncUrl = useCallback17(() => {
|
|
18015
18245
|
const url = new URL(window.location.href);
|
|
18016
18246
|
url.hash = buildHash();
|
|
18017
18247
|
window.history.replaceState(null, "", url.toString());
|
|
@@ -18019,7 +18249,7 @@ function useShareableViewerState({
|
|
|
18019
18249
|
paramKey
|
|
18020
18250
|
);
|
|
18021
18251
|
}, [buildHash, paramKey]);
|
|
18022
|
-
const latestStateRef =
|
|
18252
|
+
const latestStateRef = useRef21({
|
|
18023
18253
|
cameraState,
|
|
18024
18254
|
objectBindings,
|
|
18025
18255
|
selectedObjectBinding
|
|
@@ -18029,9 +18259,9 @@ function useShareableViewerState({
|
|
|
18029
18259
|
objectBindings,
|
|
18030
18260
|
selectedObjectBinding
|
|
18031
18261
|
};
|
|
18032
|
-
const [restoreAttempted, setRestoreAttempted] =
|
|
18033
|
-
const lastSyncedHashRef =
|
|
18034
|
-
|
|
18262
|
+
const [restoreAttempted, setRestoreAttempted] = useState33(false);
|
|
18263
|
+
const lastSyncedHashRef = useRef21(null);
|
|
18264
|
+
useEffect23(() => {
|
|
18035
18265
|
if (!autoSync || !restoreAttempted) return;
|
|
18036
18266
|
const tick = () => {
|
|
18037
18267
|
const current = latestStateRef.current;
|
|
@@ -18053,9 +18283,9 @@ function useShareableViewerState({
|
|
|
18053
18283
|
const id = window.setInterval(tick, debounceMs);
|
|
18054
18284
|
return () => window.clearInterval(id);
|
|
18055
18285
|
}, [autoSync, restoreAttempted, debounceMs, paramKey]);
|
|
18056
|
-
const lastRestoredRawRef =
|
|
18057
|
-
const restoringRawRef =
|
|
18058
|
-
const restoreFromUrl =
|
|
18286
|
+
const lastRestoredRawRef = useRef21(null);
|
|
18287
|
+
const restoringRawRef = useRef21(null);
|
|
18288
|
+
const restoreFromUrl = useCallback17(async () => {
|
|
18059
18289
|
setRestoreAttempted(true);
|
|
18060
18290
|
const rawHash = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
|
|
18061
18291
|
const raw = new URLSearchParams(rawHash).get(paramKey);
|
|
@@ -18108,7 +18338,7 @@ function useShareableViewerState({
|
|
|
18108
18338
|
setCameraState,
|
|
18109
18339
|
setSelectedObjectBinding
|
|
18110
18340
|
]);
|
|
18111
|
-
|
|
18341
|
+
useEffect23(() => {
|
|
18112
18342
|
const handleHashChange = () => {
|
|
18113
18343
|
void restoreFromUrl();
|
|
18114
18344
|
};
|
|
@@ -18127,9 +18357,11 @@ export {
|
|
|
18127
18357
|
SimpleModelViewer,
|
|
18128
18358
|
material,
|
|
18129
18359
|
patchBindings,
|
|
18360
|
+
patchSceneConfig,
|
|
18130
18361
|
useObjectBinding,
|
|
18131
18362
|
useObjectBindingIds,
|
|
18132
18363
|
useObjectBindings,
|
|
18364
|
+
useSceneConfig,
|
|
18133
18365
|
useShareableViewerState,
|
|
18134
18366
|
useViewerActions,
|
|
18135
18367
|
useViewerAnimations,
|