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