@liveroom-tech/react-immersive 1.0.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -2
- package/dist/index.d.mts +26 -2
- package/dist/index.d.ts +26 -2
- package/dist/index.js +1362 -957
- package/dist/index.mjs +1119 -706
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
// src/components/ModelViewer/ModelViewer.tsx
|
|
2
2
|
import {
|
|
3
|
-
useRef as
|
|
3
|
+
useRef as useRef10,
|
|
4
4
|
useState as useState10,
|
|
5
|
-
useEffect as
|
|
5
|
+
useEffect as useEffect14,
|
|
6
6
|
useCallback as useCallback4,
|
|
7
|
-
useMemo as
|
|
7
|
+
useMemo as useMemo13,
|
|
8
8
|
Suspense as Suspense2
|
|
9
9
|
} from "react";
|
|
10
10
|
import { createPortal } from "react-dom";
|
|
11
|
-
import { useFrame as
|
|
11
|
+
import { useFrame as useFrame8, useThree as useThree10 } from "@react-three/fiber";
|
|
12
12
|
import { createXRStore } from "@react-three/xr";
|
|
13
13
|
import {
|
|
14
|
-
Box3 as
|
|
14
|
+
Box3 as Box33,
|
|
15
15
|
PerspectiveCamera as PerspectiveCamera3,
|
|
16
16
|
Raycaster,
|
|
17
|
-
Sphere,
|
|
17
|
+
Sphere as Sphere2,
|
|
18
18
|
Vector2 as Vector25,
|
|
19
|
-
Vector3 as
|
|
19
|
+
Vector3 as Vector37
|
|
20
20
|
} from "three";
|
|
21
21
|
|
|
22
22
|
// #style-inject:#style-inject
|
|
@@ -42,7 +42,7 @@ function styleInject(css, { insertAt } = {}) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// src/styles/ModelViewer.css
|
|
45
|
-
styleInject('.ri-viewer--license-checking,\n.ri-viewer--license-invalid {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n background-color: #f9f9f9;\n}\n.ri-viewer {\n --ri-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --ri-viewer-bg: #1c1c1d;\n --ri-viewer-text: #111827;\n --ri-panel-bg: #ffffff;\n --ri-panel-border: #e5e7eb;\n --ri-panel-title: #111827;\n --ri-panel-muted-text: #4b5563;\n --ri-panel-subtle-text: #9ca3af;\n --ri-panel-surface: #f9fafb;\n --ri-panel-surface-hover: #f3f4f6;\n --ri-panel-surface-border: #e5e7eb;\n --ri-panel-button-text: #1f2937;\n --ri-panel-accent: #2563eb;\n --ri-panel-accent-contrast: #ffffff;\n --ri-license-overlay-bg: rgba(0, 0, 0, 0.04);\n --ri-license-overlay-border: rgba(0, 0, 0, 0.08);\n --ri-license-overlay-text: #888;\n --ri-license-overlay-error-bg: rgba(192, 57, 43, 0.06);\n --ri-license-overlay-error-border: rgba(192, 57, 43, 0.2);\n --ri-license-overlay-error-text: #c0392b;\n --ri-sidepanel-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --ri-sidepanel-bg: #141824;\n --ri-sidepanel-open-btn-bg: rgba(20, 20, 20, 0.82);\n --ri-sidepanel-open-btn-bg-hover: #27282b7e;\n --ri-sidepanel-border: rgba(255, 255, 255, 0.06);\n --ri-sidepanel-divider: rgba(255, 255, 255, 0.04);\n --ri-sidepanel-border-strong: rgba(255, 255, 255, 0.08);\n --ri-sidepanel-border-active: rgba(255, 255, 255, 0.18);\n --ri-sidepanel-shadow-left: 4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-sidepanel-shadow-right: -4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-sidepanel-text: #f1f5f9;\n --ri-sidepanel-text-emphasis: #ffffff;\n --ri-sidepanel-text-muted: #94a3b8;\n --ri-sidepanel-text-subtle: #64748b;\n --ri-sidepanel-placeholder: #475569;\n --ri-sidepanel-surface: rgba(255, 255, 255, 0.05);\n --ri-sidepanel-surface-hover: rgba(255, 255, 255, 0.1);\n --ri-sidepanel-accent: #3b82f6;\n --ri-sidepanel-accent-soft: rgba(59, 130, 246, 0.12);\n --ri-databinding-bg: #111318;\n --ri-databinding-border: rgba(255, 255, 255, 0.06);\n --ri-databinding-border-strong: rgba(255, 255, 255, 0.08);\n --ri-databinding-border-hover: rgba(255, 255, 255, 0.15);\n --ri-databinding-shadow: 4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-databinding-font-family:\n "Inter",\n "Segoe UI",\n sans-serif;\n --ri-databinding-mono-font-family:\n "JetBrains Mono",\n "Fira Code",\n "Menlo",\n monospace;\n --ri-databinding-text: #f1f5f9;\n --ri-databinding-text-muted: #94a3b8;\n --ri-databinding-text-subtle: #64748b;\n --ri-databinding-text-faint: #475569;\n --ri-databinding-text-hover: #cbd5e1;\n --ri-databinding-surface: rgba(255, 255, 255, 0.03);\n --ri-databinding-surface-hover: rgba(255, 255, 255, 0.06);\n --ri-databinding-accent: #93c5fd;\n --ri-databinding-accent-glow: rgba(147, 197, 253, 0.8);\n --ri-databinding-accent-border: rgba(96, 165, 250, 0.55);\n --ri-databinding-accent-gradient:\n linear-gradient(\n \n 135deg,\n #1e3a8a 0%,\n #1d4ed8 60%,\n #2563eb 100% );\n --ri-databinding-accent-gradient-active:\n linear-gradient(\n \n 135deg,\n #1e3a8a 0%,\n #1e40af 100% );\n display: flex;\n flex-direction: row;\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n background: var(--ri-viewer-bg);\n position: relative;\n color: var(--ri-viewer-text);\n font-family: var(--ri-font-family);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n font-synthesis: none;\n}\n.ri-viewer * {\n box-sizing: border-box;\n}\n.ri-viewerLeft {\n display: flex;\n flex: 1;\n min-height: 0;\n min-width: 0;\n position: relative;\n}\n.ri-viewerRight {\n display: flex;\n flex-shrink: 0;\n min-width: 0;\n}\n.ri-panel {\n width: 280px;\n min-height: 0;\n background: var(--ri-panel-bg);\n border-right: 1px solid var(--ri-panel-border);\n display: flex;\n flex-direction: column;\n padding: 20px;\n overflow-y: auto;\n flex-shrink: 0;\n transition: margin-left 0.3s ease, transform 0.3s ease;\n}\n.ri-panel--open {\n transform: translateX(0);\n margin-left: 0;\n}\n.ri-panel--closed {\n transform: translateX(-100%);\n margin-left: -280px;\n}\n.ri-panelHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 12px;\n}\n.ri-panelTitle {\n margin: 0;\n font-size: 15px;\n font-weight: 700;\n color: var(--ri-panel-title);\n line-height: 1.4;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ri-closeButton {\n border: 0;\n background: transparent;\n color: var(--ri-panel-subtle-text);\n font-size: 22px;\n line-height: 1;\n padding: 0;\n cursor: pointer;\n flex-shrink: 0;\n transition: color 0.2s ease;\n}\n.ri-closeButton:hover {\n color: var(--ri-panel-muted-text);\n}\n.ri-json {\n margin: 0 0 16px;\n padding: 12px;\n background: var(--ri-panel-surface);\n border: 1px solid var(--ri-panel-surface-border);\n border-radius: 12px;\n font-size: 12px;\n line-height: 1.55;\n color: var(--ri-panel-button-text);\n overflow-x: auto;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.ri-divider {\n border-top: 1px solid var(--ri-panel-border);\n margin-bottom: 12px;\n}\n.ri-sectionLabel {\n margin-bottom: 8px;\n font-size: 11px;\n font-weight: 700;\n color: var(--ri-panel-subtle-text);\n text-transform: uppercase;\n letter-spacing: 0.16em;\n}\n.ri-actionList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.ri-actionButton {\n width: 100%;\n border-radius: 8px;\n border: 1px solid var(--ri-panel-surface-border);\n background: var(--ri-panel-surface);\n color: var(--ri-panel-button-text);\n padding: 10px 12px;\n text-align: left;\n font-size: 14px;\n line-height: 1.4;\n cursor: pointer;\n transition:\n background-color 0.2s ease,\n border-color 0.2s ease,\n color 0.2s ease;\n}\n.ri-actionButton:hover {\n background: var(--ri-panel-surface-hover);\n border-color: var(--ri-panel-border);\n}\n.ri-actionButton--active {\n background: var(--ri-panel-accent);\n border-color: var(--ri-panel-accent);\n color: var(--ri-panel-accent-contrast);\n}\n.ri-actionButton--active:hover {\n background: var(--ri-panel-accent);\n border-color: var(--ri-panel-accent);\n}\n.ri-canvasArea {\n flex: 1 1 auto;\n position: relative;\n min-width: 0;\n}\n.ri-canvasArea--measuring {\n cursor: crosshair;\n}\n.ri-canvasArea canvas {\n opacity: 1;\n transition: opacity 0.18s ease;\n}\n.ri-canvasArea--positioning canvas {\n opacity: 0;\n transition: none;\n}\n.ri-bindingsMismatchBanner {\n position: absolute;\n top: 12px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 20;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 7px 12px 7px 10px;\n border-radius: 8px;\n background: rgba(30, 20, 0, 0.88);\n border: 1px solid rgba(251, 191, 36, 0.35);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n color: rgb(251, 191, 36);\n font-size: 12px;\n font-weight: 500;\n line-height: 1.4;\n white-space: nowrap;\n pointer-events: auto;\n max-width: calc(100% - 32px);\n white-space: normal;\n text-align: center;\n}\n.ri-bindingsMismatchBanner__icon {\n flex-shrink: 0;\n}\n.ri-bindingsMismatchBanner__dismiss {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: 4px;\n padding: 2px;\n background: none;\n border: none;\n border-radius: 4px;\n color: rgba(251, 191, 36, 0.7);\n cursor: pointer;\n transition: color 0.15s ease, background 0.15s ease;\n}\n.ri-bindingsMismatchBanner__dismiss:hover {\n color: rgb(251, 191, 36);\n background: rgba(251, 191, 36, 0.12);\n}\n.ri-measureTools {\n position: absolute;\n top: 16px;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-measureButton {\n width: 34px;\n height: 34px;\n padding: 0;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n color: rgba(255, 255, 255, 0.75);\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n touch-action: manipulation;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n transition:\n background 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease;\n}\n.ri-measureButton:hover {\n background: rgba(40, 40, 40, 0.92);\n color: #fff;\n border-color: rgba(255, 255, 255, 0.25);\n}\n.ri-measureButton:active {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-measureButton--active {\n background: #22d3ee;\n border-color: #22d3ee;\n color: #06343c;\n}\n.ri-measureButton--active:hover {\n background: #38dbf0;\n color: #06343c;\n}\n.ri-measureHint {\n padding: 6px 10px;\n border-radius: 8px;\n font-size: 12px;\n line-height: 1;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n white-space: nowrap;\n}\n.ri-explodeTools {\n position: absolute;\n top: 16px;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-explodeTools--stacked {\n top: 58px;\n}\n.ri-explodeSlider {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 34px;\n padding: 0 12px;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 8px;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n}\n.ri-explodeSlider input[type=range] {\n width: 120px;\n accent-color: #22d3ee;\n cursor: pointer;\n}\n.ri-measureDot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: #22d3ee;\n border: 2px solid #fff;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);\n pointer-events: none;\n}\n.ri-measureLabel {\n padding: 3px 7px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n white-space: nowrap;\n color: #06343c;\n background: #22d3ee;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n pointer-events: none;\n user-select: none;\n}\n.ri-xrTools {\n position: absolute;\n top: 16px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-xrButton {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n height: 34px;\n padding: 0 10px;\n font-size: 13px;\n font-weight: 500;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.16);\n border-radius: 10px;\n cursor: pointer;\n touch-action: manipulation;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n transition: background 0.15s ease, border-color 0.15s ease;\n}\n.ri-xrButton:hover {\n background: rgba(40, 40, 40, 0.92);\n border-color: rgba(255, 255, 255, 0.3);\n}\n.ri-arHintOverlay {\n position: absolute;\n top: 18px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n width: min(calc(100% - 32px), 360px);\n pointer-events: none;\n}\n.ri-arHint {\n margin: 0 auto;\n width: fit-content;\n max-width: 100%;\n padding: 6px 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1.2;\n text-align: center;\n color: rgba(255, 255, 255, 0.92);\n background: rgba(20, 20, 20, 0.58);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 999px;\n user-select: none;\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n}\n.ri-arOverlay {\n position: absolute;\n bottom: 24px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n width: min(calc(100% - 32px), 420px);\n pointer-events: none;\n}\n.ri-arControls {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n}\n.ri-arStatus,\n.ri-arActionButton {\n max-width: 100%;\n padding: 8px 16px;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.3;\n text-align: center;\n white-space: nowrap;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.16);\n border-radius: 19px;\n user-select: none;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n}\n.ri-arStatus {\n pointer-events: none;\n}\n.ri-arActionButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n cursor: pointer;\n pointer-events: auto;\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n transition:\n background 0.15s ease,\n border-color 0.15s ease,\n color 0.15s ease,\n transform 0.15s ease;\n}\n.ri-arActionButton svg {\n flex-shrink: 0;\n}\n.ri-arActionButton:hover {\n background: rgba(40, 40, 40, 0.92);\n border-color: rgba(255, 255, 255, 0.3);\n}\n.ri-arActionButton:active {\n transform: scale(0.98);\n}\n.ri-arActionButton--primary {\n color: #06343c;\n background: #22d3ee;\n border-color: transparent;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n}\n.ri-arActionButton--primary:hover {\n background: #67e8f9;\n border-color: transparent;\n}\n.ri-arActionButton.ri-arActionButton--active,\n.ri-arActionButton[aria-pressed=true],\n.ri-arActionButton.ri-arActionButton--active:hover,\n.ri-arActionButton[aria-pressed=true]:hover,\n.ri-arActionButton.ri-arActionButton--active:active,\n.ri-arActionButton[aria-pressed=true]:active {\n color: #06343c;\n background: #22d3ee;\n border-color: transparent;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n}\n.ri-hiddenInput {\n display: none;\n}\n.ri-popover {\n position: absolute;\n z-index: 50;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n border-radius: 16px;\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);\n color: #111827;\n}\n.ri-popoverHeader {\n padding: 12px 16px;\n border-bottom: 1px solid #f3f4f6;\n}\n.ri-popoverLabel {\n margin: 0 0 8px;\n font-size: 11px;\n font-weight: 700;\n color: #6b7280;\n text-transform: uppercase;\n letter-spacing: 0.16em;\n}\n.ri-inlineRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n margin-top: 8px;\n}\n.ri-colorCode {\n font-size: 12px;\n color: #9ca3af;\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n monospace;\n text-transform: uppercase;\n}\n.ri-secondaryButton {\n border: 1px solid #e5e7eb;\n background: #f3f4f6;\n color: #374151;\n border-radius: 8px;\n padding: 6px 10px;\n font-size: 12px;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-secondaryButton:hover {\n background: #e5e7eb;\n}\n.ri-materialBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px;\n}\n.ri-previewFrame {\n position: relative;\n height: 112px;\n border-radius: 12px;\n overflow: hidden;\n border: 1px solid #e5e7eb;\n}\n.ri-previewImage {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n.ri-previewCaption {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n padding: 4px 8px;\n color: #ffffff;\n font-size: 10px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ri-emptyState {\n height: 112px;\n border-radius: 12px;\n border: 2px dashed #e5e7eb;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n color: #d1d5db;\n text-align: center;\n}\n.ri-uploadButton {\n width: 100%;\n border: 0;\n border-radius: 10px;\n background: #111827;\n color: #ffffff;\n padding: 10px 12px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-uploadButton:hover {\n background: #374151;\n}\n.ri-removeButton {\n width: 100%;\n border-radius: 10px;\n border: 1px solid #e5e7eb;\n background: #ffffff;\n color: #6b7280;\n padding: 8px 12px;\n font-size: 12px;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-removeButton:hover {\n background: #f9fafb;\n}\n.ri-formatHint {\n margin: 0;\n text-align: center;\n font-size: 10px;\n color: #d1d5db;\n}\n.ri-buttonContainer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n gap: 8px;\n position: absolute;\n bottom: 12px;\n left: 12px;\n right: 12px;\n z-index: 10;\n}\n.ri-tourControls {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-shrink: 0;\n}\n.ri-rightControls {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-shrink: 0;\n margin-left: auto;\n}\n.ri-rightControls:only-child {\n margin-left: auto;\n}\n.ri-navButtonSingle,\n.ri-tourButton,\n.ri-resetButton,\n.ri-downloadButton {\n height: 34px;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n color: rgba(255, 255, 255, 0.75);\n border-radius: 8px;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n cursor: pointer;\n transition:\n background 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease,\n transform 0.15s ease;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n}\n.ri-navButtonSingle:hover:not(:disabled),\n.ri-tourButton:hover:not(:disabled),\n.ri-resetButton:hover:not(:disabled) {\n background: rgba(40, 40, 40, 0.92);\n color: #fff;\n border-color: rgba(255, 255, 255, 0.25);\n}\n.ri-navButtonSingle:active:not(:disabled),\n.ri-tourButton:active:not(:disabled),\n.ri-resetButton:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-navButtonSingle:disabled,\n.ri-tourButton:disabled,\n.ri-resetButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.ri-navButtonSingle {\n padding: 0 16px;\n font-size: 13px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 8px;\n white-space: nowrap;\n}\n.ri-tourButton {\n padding: 0 12px;\n font-size: 13px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n.ri-tourButton svg {\n width: 15px;\n height: 15px;\n flex-shrink: 0;\n}\n.ri-tourButton--stop {\n border-color: rgba(255, 68, 68, 0.4);\n color: #ff6b6b;\n}\n.ri-tourButton--stop:hover:not(:disabled) {\n background: rgba(255, 68, 68, 0.15);\n border-color: rgba(255, 68, 68, 0.6);\n color: #ff6b6b;\n}\n.ri-tourButton--stop:active:not(:disabled) {\n background: #ff4444;\n color: #fff;\n border-color: #ff4444;\n}\n.ri-tourButton--next {\n border-color: rgba(56, 219, 240, 0.3);\n color: #38dbf0;\n}\n.ri-tourButton--next:hover:not(:disabled) {\n background: rgba(56, 219, 240, 0.12);\n border-color: rgba(56, 219, 240, 0.5);\n color: #38dbf0;\n}\n.ri-tourButton--next:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-resetButton {\n width: 34px;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.ri-resetButton svg {\n width: 15px;\n height: 15px;\n pointer-events: none;\n flex-shrink: 0;\n}\n.ri-resetButton:focus-visible {\n outline: 2px solid rgba(255, 255, 255, 0.5);\n outline-offset: 2px;\n}\n.ri-downloadWrapper {\n position: relative;\n display: inline-flex;\n flex-direction: column;\n align-items: stretch;\n}\n.ri-downloadButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 0 16px;\n font-size: 13px;\n font-weight: 500;\n white-space: nowrap;\n}\n.ri-downloadButton:hover:not(:disabled) {\n background: rgba(0, 0, 0, 0.72);\n border-color: rgba(255, 255, 255, 0.3);\n color: #fff;\n}\n.ri-downloadButton:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-downloadButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.ri-downloadButton__spinner {\n display: inline-block;\n width: 13px;\n height: 13px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: ri-spin 0.7s linear infinite;\n flex-shrink: 0;\n}\n.ri-downloadMenu {\n position: absolute;\n bottom: calc(100% + 6px);\n right: 0;\n min-width: 168px;\n background: rgba(20, 20, 20, 0.92);\n border: 1px solid rgba(255, 255, 255, 0.14);\n border-radius: 10px;\n overflow: hidden;\n z-index: 100;\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n animation: ri-menuReveal 0.12s ease;\n}\n@keyframes ri-menuReveal {\n from {\n opacity: 0;\n transform: translateY(4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n.ri-downloadMenuItem {\n display: flex;\n align-items: center;\n gap: 9px;\n width: 100%;\n padding: 10px 14px;\n font-size: 13px;\n font-weight: 400;\n color: #fff;\n background: transparent;\n border: none;\n cursor: pointer;\n text-align: left;\n transition: background 0.1s ease;\n}\n.ri-downloadMenuItem:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.1);\n}\n.ri-downloadMenuItem:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.ri-downloadMenuDivider {\n height: 1px;\n background: rgba(255, 255, 255, 0.1);\n margin: 0 10px;\n}\n@keyframes ri-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.ri-annotationMarker {\n position: relative;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: rgba(20, 20, 20, 0.82);\n border: 2px solid rgba(255, 255, 255, 0.9);\n color: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n font-weight: 700;\n cursor: pointer;\n z-index: 20;\n user-select: none;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);\n pointer-events: auto;\n transition: opacity 0.3s ease, transform 0.3s ease;\n}\n.ri-annotationMarker:hover {\n transform: scale(1.08);\n box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18);\n background: rgba(40, 40, 40, 0.92);\n}\n.ri-annotationPopup {\n position: absolute;\n width: min(270px, calc(100vw - 32px));\n background: rgba(20, 20, 20, 0.85);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 16px;\n padding: 16px;\n z-index: 30;\n display: flex;\n flex-direction: column;\n gap: 12px;\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n}\n.ri-annotationInput,\n.ri-annotationTextarea {\n width: 100%;\n border: 1px solid rgba(255, 255, 255, 0.12);\n background: rgba(255, 255, 255, 0.05);\n color: #e5e7eb;\n border-radius: 10px;\n padding: 10px 12px;\n outline: none;\n font-family: inherit;\n transition: border-color 0.2s ease, box-shadow 0.2s ease;\n}\n.ri-annotationInput {\n font-size: 14px;\n font-weight: 500;\n}\n.ri-annotationTextarea {\n font-size: 13px;\n resize: vertical;\n min-height: 110px;\n}\n.ri-annotationInput:focus,\n.ri-annotationTextarea:focus {\n border-color: #38dbf0;\n box-shadow: 0 0 0 3px rgba(56, 219, 240, 0.12);\n}\n.ri-annotationActions {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.ri-annotationOkBtn {\n border: 0;\n border-radius: 8px;\n background: rgba(56, 219, 240, 0.8);\n color: #06343c;\n padding: 8px 18px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.2s ease, opacity 0.2s ease;\n}\n.ri-annotationOkBtn:hover:not(:disabled) {\n background: #38dbf0;\n}\n.ri-annotationOkBtn:disabled,\n.ri-annotationOkBtn.disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.ri-annotationCancelBtn {\n border: 1px solid rgba(255, 255, 255, 0.12);\n background: transparent;\n color: #9ca3af;\n border-radius: 8px;\n padding: 8px 12px;\n font-size: 13px;\n cursor: pointer;\n transition: background-color 0.2s ease, border-color 0.2s ease;\n}\n.ri-annotationCancelBtn:hover {\n background: rgba(255, 255, 255, 0.05);\n border-color: rgba(255, 255, 255, 0.2);\n}\n.ri-annotationIcons {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 12px;\n color: #9ca3af;\n}\n.ri-annotationIcons svg {\n flex-shrink: 0;\n}\n.ri-viewAnnotationPopup {\n position: relative;\n background: rgba(20, 20, 20, 0.85);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 8px;\n padding: 12px 16px;\n min-width: 200px;\n max-width: min(280px, calc(100vw - 48px));\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);\n pointer-events: auto;\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n animation: ri-viewAnnotationFadeIn 0.15s ease-out;\n margin-left: 24px;\n margin-top: -20px;\n isolation: isolate;\n z-index: 1000;\n}\n.ri-viewAnnotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.ri-viewAnnotationTitle {\n margin: 0;\n color: #e2e8f0;\n font-size: 14px;\n font-weight: 600;\n}\n.ri-viewAnnotationClose {\n background: transparent;\n border: none;\n color: #b7bdc6;\n cursor: pointer;\n font-size: 18px;\n padding: 0 4px;\n transition: color 0.2s;\n line-height: 1;\n}\n.ri-viewAnnotationClose:hover {\n color: #e2e8f0;\n}\n.ri-viewAnnotationDescription {\n margin: 0;\n color: #cbd5e1;\n font-size: 13px;\n line-height: 1.4;\n word-wrap: break-word;\n}\n@keyframes ri-viewAnnotationFadeIn {\n from {\n opacity: 0;\n transform: translateY(-6px) scale(0.96);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n.ri-annotationNavSingle {\n position: absolute;\n bottom: 120px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n pointer-events: none;\n}\n.ri-annotationNavSingle > * {\n pointer-events: auto;\n}\n.ri-navBadge {\n background: rgba(255, 255, 255, 0.1);\n padding: 2px 10px;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 400;\n color: rgba(255, 255, 255, 0.7);\n}\n.ri-annotationTitleHint {\n background: rgba(0, 0, 0, 0.7);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n color: white;\n padding: 8px 20px;\n border-radius: 8px;\n font-size: 13px;\n border: 1px solid rgba(255, 255, 255, 0.08);\n text-align: center;\n max-width: 350px;\n animation: slideUp 0.3s ease;\n}\n.ri-annotationHintTitle {\n font-weight: 600;\n color: #e5e7eb;\n}\n.ri-annotationHintDesc {\n font-size: 12px;\n opacity: 0.6;\n margin-top: 2px;\n color: #d1d5db;\n}\n@keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n@media (max-width: 768px) {\n .ri-xrTools {\n gap: 6px;\n }\n .ri-xrButton {\n height: 30px;\n gap: 6px;\n padding: 0 8px;\n font-size: 11px;\n }\n .ri-xrButton svg {\n width: 14px;\n height: 14px;\n }\n .ri-xrButtonPrefix {\n display: none;\n }\n .ri-annotationNavSingle {\n bottom: 100px;\n }\n .ri-navButtonSingle {\n padding: 8px 20px;\n font-size: 13px;\n min-width: 110px;\n }\n .ri-annotationTitleHint {\n padding: 6px 14px;\n font-size: 12px;\n max-width: 250px;\n }\n .ri-viewAnnotationPopup {\n min-width: 160px;\n max-width: 220px;\n padding: 10px 12px;\n }\n .ri-viewAnnotationTitle {\n font-size: 13px;\n }\n .ri-viewAnnotationDescription {\n font-size: 12px;\n }\n .ri-buttonContainer {\n flex-wrap: wrap;\n gap: 6px;\n }\n .ri-tourControls {\n flex-wrap: wrap;\n }\n .ri-rightControls {\n flex-wrap: wrap;\n }\n}\n@media (max-width: 480px) {\n .ri-xrTools {\n top: 12px;\n gap: 5px;\n }\n .ri-xrButton {\n height: 28px;\n gap: 5px;\n padding: 0 7px;\n font-size: 10px;\n }\n .ri-xrButton svg {\n width: 13px;\n height: 13px;\n }\n .ri-annotationNavSingle {\n bottom: 80px;\n }\n .ri-navButtonSingle {\n padding: 6px 16px;\n font-size: 12px;\n min-width: 90px;\n gap: 6px;\n }\n .ri-annotationTitleHint {\n padding: 4px 10px;\n font-size: 11px;\n max-width: 200px;\n }\n .ri-navBadge {\n font-size: 10px;\n padding: 1px 6px;\n }\n .ri-tourButton {\n padding: 0 8px;\n font-size: 12px;\n }\n .ri-tourButton span {\n display: none;\n }\n .ri-downloadButton {\n padding: 0 10px;\n font-size: 12px;\n }\n .ri-downloadButton span {\n display: none;\n }\n}\n.ri-canvasArea .bb-loadingOverlay {\n position: absolute;\n inset: 0;\n z-index: 12;\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(15, 23, 42, 0.46);\n backdrop-filter: blur(1px);\n}\n.ri-canvasArea .bb-loadingSpinner {\n width: 40px;\n height: 40px;\n border-radius: 999px;\n border: 3px solid rgba(226, 232, 240, 0.24);\n border-top-color: #93c5fd;\n animation: bb-spin 0.8s linear infinite;\n}\n.ri-canvasArea .bb-loadingMessage {\n max-width: 320px;\n color: #f7f8f9;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.5;\n}\n@keyframes bb-spin {\n to {\n transform: rotate(360deg);\n }\n}\n');
|
|
45
|
+
styleInject('.ri-viewer--license-checking,\n.ri-viewer--license-invalid {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n background-color: #f9f9f9;\n}\n.ri-viewer {\n --ri-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --ri-viewer-bg: #1c1c1d;\n --ri-viewer-text: #111827;\n --ri-panel-bg: #ffffff;\n --ri-panel-border: #e5e7eb;\n --ri-panel-title: #111827;\n --ri-panel-muted-text: #4b5563;\n --ri-panel-subtle-text: #9ca3af;\n --ri-panel-surface: #f9fafb;\n --ri-panel-surface-hover: #f3f4f6;\n --ri-panel-surface-border: #e5e7eb;\n --ri-panel-button-text: #1f2937;\n --ri-panel-accent: #2563eb;\n --ri-panel-accent-contrast: #ffffff;\n --ri-license-overlay-bg: rgba(0, 0, 0, 0.04);\n --ri-license-overlay-border: rgba(0, 0, 0, 0.08);\n --ri-license-overlay-text: #888;\n --ri-license-overlay-error-bg: rgba(192, 57, 43, 0.06);\n --ri-license-overlay-error-border: rgba(192, 57, 43, 0.2);\n --ri-license-overlay-error-text: #c0392b;\n --ri-sidepanel-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --ri-sidepanel-bg: #141824;\n --ri-sidepanel-open-btn-bg: rgba(20, 20, 20, 0.82);\n --ri-sidepanel-open-btn-bg-hover: #27282b7e;\n --ri-sidepanel-border: rgba(255, 255, 255, 0.06);\n --ri-sidepanel-divider: rgba(255, 255, 255, 0.04);\n --ri-sidepanel-border-strong: rgba(255, 255, 255, 0.08);\n --ri-sidepanel-border-active: rgba(255, 255, 255, 0.18);\n --ri-sidepanel-shadow-left: 4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-sidepanel-shadow-right: -4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-sidepanel-text: #f1f5f9;\n --ri-sidepanel-text-emphasis: #ffffff;\n --ri-sidepanel-text-muted: #94a3b8;\n --ri-sidepanel-text-subtle: #64748b;\n --ri-sidepanel-placeholder: #475569;\n --ri-sidepanel-surface: rgba(255, 255, 255, 0.05);\n --ri-sidepanel-surface-hover: rgba(255, 255, 255, 0.1);\n --ri-sidepanel-accent: #3b82f6;\n --ri-sidepanel-accent-soft: rgba(59, 130, 246, 0.12);\n --ri-databinding-bg: #111318;\n --ri-databinding-border: rgba(255, 255, 255, 0.06);\n --ri-databinding-border-strong: rgba(255, 255, 255, 0.08);\n --ri-databinding-border-hover: rgba(255, 255, 255, 0.15);\n --ri-databinding-shadow: 4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-databinding-font-family:\n "Inter",\n "Segoe UI",\n sans-serif;\n --ri-databinding-mono-font-family:\n "JetBrains Mono",\n "Fira Code",\n "Menlo",\n monospace;\n --ri-databinding-text: #f1f5f9;\n --ri-databinding-text-muted: #94a3b8;\n --ri-databinding-text-subtle: #64748b;\n --ri-databinding-text-faint: #475569;\n --ri-databinding-text-hover: #cbd5e1;\n --ri-databinding-surface: rgba(255, 255, 255, 0.03);\n --ri-databinding-surface-hover: rgba(255, 255, 255, 0.06);\n --ri-databinding-accent: #93c5fd;\n --ri-databinding-accent-glow: rgba(147, 197, 253, 0.8);\n --ri-databinding-accent-border: rgba(96, 165, 250, 0.55);\n --ri-databinding-accent-gradient:\n linear-gradient(\n \n 135deg,\n #1e3a8a 0%,\n #1d4ed8 60%,\n #2563eb 100% );\n --ri-databinding-accent-gradient-active:\n linear-gradient(\n \n 135deg,\n #1e3a8a 0%,\n #1e40af 100% );\n display: flex;\n flex-direction: row;\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n background: var(--ri-viewer-bg);\n position: relative;\n color: var(--ri-viewer-text);\n font-family: var(--ri-font-family);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n font-synthesis: none;\n}\n.ri-viewer * {\n box-sizing: border-box;\n}\n.ri-viewerLeft {\n display: flex;\n flex: 1;\n min-height: 0;\n min-width: 0;\n position: relative;\n}\n.ri-viewerRight {\n display: flex;\n flex-shrink: 0;\n min-width: 0;\n}\n.ri-panel {\n width: 280px;\n min-height: 0;\n background: var(--ri-panel-bg);\n border-right: 1px solid var(--ri-panel-border);\n display: flex;\n flex-direction: column;\n padding: 20px;\n overflow-y: auto;\n flex-shrink: 0;\n transition: margin-left 0.3s ease, transform 0.3s ease;\n}\n.ri-panel--open {\n transform: translateX(0);\n margin-left: 0;\n}\n.ri-panel--closed {\n transform: translateX(-100%);\n margin-left: -280px;\n}\n.ri-panelHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 12px;\n}\n.ri-panelTitle {\n margin: 0;\n font-size: 15px;\n font-weight: 700;\n color: var(--ri-panel-title);\n line-height: 1.4;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ri-closeButton {\n border: 0;\n background: transparent;\n color: var(--ri-panel-subtle-text);\n font-size: 22px;\n line-height: 1;\n padding: 0;\n cursor: pointer;\n flex-shrink: 0;\n transition: color 0.2s ease;\n}\n.ri-closeButton:hover {\n color: var(--ri-panel-muted-text);\n}\n.ri-json {\n margin: 0 0 16px;\n padding: 12px;\n background: var(--ri-panel-surface);\n border: 1px solid var(--ri-panel-surface-border);\n border-radius: 12px;\n font-size: 12px;\n line-height: 1.55;\n color: var(--ri-panel-button-text);\n overflow-x: auto;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.ri-divider {\n border-top: 1px solid var(--ri-panel-border);\n margin-bottom: 12px;\n}\n.ri-sectionLabel {\n margin-bottom: 8px;\n font-size: 11px;\n font-weight: 700;\n color: var(--ri-panel-subtle-text);\n text-transform: uppercase;\n letter-spacing: 0.16em;\n}\n.ri-actionList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.ri-actionButton {\n width: 100%;\n border-radius: 8px;\n border: 1px solid var(--ri-panel-surface-border);\n background: var(--ri-panel-surface);\n color: var(--ri-panel-button-text);\n padding: 10px 12px;\n text-align: left;\n font-size: 14px;\n line-height: 1.4;\n cursor: pointer;\n transition:\n background-color 0.2s ease,\n border-color 0.2s ease,\n color 0.2s ease;\n}\n.ri-actionButton:hover {\n background: var(--ri-panel-surface-hover);\n border-color: var(--ri-panel-border);\n}\n.ri-actionButton--active {\n background: var(--ri-panel-accent);\n border-color: var(--ri-panel-accent);\n color: var(--ri-panel-accent-contrast);\n}\n.ri-actionButton--active:hover {\n background: var(--ri-panel-accent);\n border-color: var(--ri-panel-accent);\n}\n.ri-canvasArea {\n flex: 1 1 auto;\n position: relative;\n min-width: 0;\n}\n.ri-canvasArea--measuring {\n cursor: crosshair;\n}\n.ri-canvasArea canvas {\n opacity: 1;\n transition: opacity 0.18s ease;\n}\n.ri-canvasArea--positioning canvas {\n opacity: 0;\n transition: none;\n}\n.ri-bindingsMismatchBanner {\n position: absolute;\n top: 12px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 20;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 7px 12px 7px 10px;\n border-radius: 8px;\n background: rgba(30, 20, 0, 0.88);\n border: 1px solid rgba(251, 191, 36, 0.35);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n color: rgb(251, 191, 36);\n font-size: 12px;\n font-weight: 500;\n line-height: 1.4;\n white-space: nowrap;\n pointer-events: auto;\n max-width: calc(100% - 32px);\n white-space: normal;\n text-align: center;\n}\n.ri-bindingsMismatchBanner__icon {\n flex-shrink: 0;\n}\n.ri-bindingsMismatchBanner__dismiss {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: 4px;\n padding: 2px;\n background: none;\n border: none;\n border-radius: 4px;\n color: rgba(251, 191, 36, 0.7);\n cursor: pointer;\n transition: color 0.15s ease, background 0.15s ease;\n}\n.ri-bindingsMismatchBanner__dismiss:hover {\n color: rgb(251, 191, 36);\n background: rgba(251, 191, 36, 0.12);\n}\n.ri-measureTools {\n position: absolute;\n top: 16px;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-measureButton {\n width: 34px;\n height: 34px;\n padding: 0;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n color: rgba(255, 255, 255, 0.75);\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n touch-action: manipulation;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n transition:\n background 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease;\n}\n.ri-measureButton:hover {\n background: rgba(40, 40, 40, 0.92);\n color: #fff;\n border-color: rgba(255, 255, 255, 0.25);\n}\n.ri-measureButton:active {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-measureButton--active {\n background: #22d3ee;\n border-color: #22d3ee;\n color: #06343c;\n}\n.ri-measureButton--active:hover {\n background: #38dbf0;\n color: #06343c;\n}\n.ri-measureHint {\n padding: 6px 10px;\n border-radius: 8px;\n font-size: 12px;\n line-height: 1;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n white-space: nowrap;\n}\n.ri-explodeTools {\n position: absolute;\n top: 16px;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-explodeTools--stacked {\n top: 58px;\n}\n.ri-explodeSlider {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 34px;\n padding: 0 12px;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 8px;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n}\n.ri-explodeSlider input[type=range] {\n width: 120px;\n accent-color: #22d3ee;\n cursor: pointer;\n}\n.ri-cinematicTools {\n position: absolute;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-measureDot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: #22d3ee;\n border: 2px solid #fff;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);\n pointer-events: none;\n}\n.ri-measureLabel {\n padding: 3px 7px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n white-space: nowrap;\n color: #06343c;\n background: #22d3ee;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n pointer-events: none;\n user-select: none;\n}\n.ri-xrTools {\n position: absolute;\n top: 16px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-xrButton {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n height: 34px;\n padding: 0 10px;\n font-size: 13px;\n font-weight: 500;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.16);\n border-radius: 10px;\n cursor: pointer;\n touch-action: manipulation;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n transition: background 0.15s ease, border-color 0.15s ease;\n}\n.ri-xrButton:hover {\n background: rgba(40, 40, 40, 0.92);\n border-color: rgba(255, 255, 255, 0.3);\n}\n.ri-arHintOverlay {\n position: absolute;\n top: 18px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n width: min(calc(100% - 32px), 360px);\n pointer-events: none;\n}\n.ri-arHint {\n margin: 0 auto;\n width: fit-content;\n max-width: 100%;\n padding: 6px 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1.2;\n text-align: center;\n color: rgba(255, 255, 255, 0.92);\n background: rgba(20, 20, 20, 0.58);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 999px;\n user-select: none;\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n}\n.ri-arOverlay {\n position: absolute;\n bottom: 24px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n width: min(calc(100% - 32px), 420px);\n pointer-events: none;\n}\n.ri-arControls {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n}\n.ri-arStatus,\n.ri-arActionButton {\n max-width: 100%;\n padding: 8px 16px;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.3;\n text-align: center;\n white-space: nowrap;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.16);\n border-radius: 19px;\n user-select: none;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n}\n.ri-arStatus {\n pointer-events: none;\n}\n.ri-arActionButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n cursor: pointer;\n pointer-events: auto;\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n transition:\n background 0.15s ease,\n border-color 0.15s ease,\n color 0.15s ease,\n transform 0.15s ease;\n}\n.ri-arActionButton svg {\n flex-shrink: 0;\n}\n.ri-arActionButton:hover {\n background: rgba(40, 40, 40, 0.92);\n border-color: rgba(255, 255, 255, 0.3);\n}\n.ri-arActionButton:active {\n transform: scale(0.98);\n}\n.ri-arActionButton--primary {\n color: #06343c;\n background: #22d3ee;\n border-color: transparent;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n}\n.ri-arActionButton--primary:hover {\n background: #67e8f9;\n border-color: transparent;\n}\n.ri-arActionButton.ri-arActionButton--active,\n.ri-arActionButton[aria-pressed=true],\n.ri-arActionButton.ri-arActionButton--active:hover,\n.ri-arActionButton[aria-pressed=true]:hover,\n.ri-arActionButton.ri-arActionButton--active:active,\n.ri-arActionButton[aria-pressed=true]:active {\n color: #06343c;\n background: #22d3ee;\n border-color: transparent;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n}\n.ri-hiddenInput {\n display: none;\n}\n.ri-popover {\n position: absolute;\n z-index: 50;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n border-radius: 16px;\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);\n color: #111827;\n}\n.ri-popoverHeader {\n padding: 12px 16px;\n border-bottom: 1px solid #f3f4f6;\n}\n.ri-popoverLabel {\n margin: 0 0 8px;\n font-size: 11px;\n font-weight: 700;\n color: #6b7280;\n text-transform: uppercase;\n letter-spacing: 0.16em;\n}\n.ri-inlineRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n margin-top: 8px;\n}\n.ri-colorCode {\n font-size: 12px;\n color: #9ca3af;\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n monospace;\n text-transform: uppercase;\n}\n.ri-secondaryButton {\n border: 1px solid #e5e7eb;\n background: #f3f4f6;\n color: #374151;\n border-radius: 8px;\n padding: 6px 10px;\n font-size: 12px;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-secondaryButton:hover {\n background: #e5e7eb;\n}\n.ri-materialBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px;\n}\n.ri-previewFrame {\n position: relative;\n height: 112px;\n border-radius: 12px;\n overflow: hidden;\n border: 1px solid #e5e7eb;\n}\n.ri-previewImage {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n.ri-previewCaption {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n padding: 4px 8px;\n color: #ffffff;\n font-size: 10px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ri-emptyState {\n height: 112px;\n border-radius: 12px;\n border: 2px dashed #e5e7eb;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n color: #d1d5db;\n text-align: center;\n}\n.ri-uploadButton {\n width: 100%;\n border: 0;\n border-radius: 10px;\n background: #111827;\n color: #ffffff;\n padding: 10px 12px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-uploadButton:hover {\n background: #374151;\n}\n.ri-removeButton {\n width: 100%;\n border-radius: 10px;\n border: 1px solid #e5e7eb;\n background: #ffffff;\n color: #6b7280;\n padding: 8px 12px;\n font-size: 12px;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-removeButton:hover {\n background: #f9fafb;\n}\n.ri-formatHint {\n margin: 0;\n text-align: center;\n font-size: 10px;\n color: #d1d5db;\n}\n.ri-buttonContainer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n gap: 8px;\n position: absolute;\n bottom: 12px;\n left: 12px;\n right: 12px;\n z-index: 10;\n}\n.ri-tourControls {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-shrink: 0;\n}\n.ri-rightControls {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-shrink: 0;\n margin-left: auto;\n}\n.ri-rightControls:only-child {\n margin-left: auto;\n}\n.ri-navButtonSingle,\n.ri-tourButton,\n.ri-resetButton,\n.ri-downloadButton {\n height: 34px;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n color: rgba(255, 255, 255, 0.75);\n border-radius: 8px;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n cursor: pointer;\n transition:\n background 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease,\n transform 0.15s ease;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n}\n.ri-navButtonSingle:hover:not(:disabled),\n.ri-tourButton:hover:not(:disabled),\n.ri-resetButton:hover:not(:disabled) {\n background: rgba(40, 40, 40, 0.92);\n color: #fff;\n border-color: rgba(255, 255, 255, 0.25);\n}\n.ri-navButtonSingle:active:not(:disabled),\n.ri-tourButton:active:not(:disabled),\n.ri-resetButton:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-navButtonSingle:disabled,\n.ri-tourButton:disabled,\n.ri-resetButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.ri-navButtonSingle {\n padding: 0 16px;\n font-size: 13px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 8px;\n white-space: nowrap;\n}\n.ri-tourButton {\n padding: 0 12px;\n font-size: 13px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n.ri-tourButton svg {\n width: 15px;\n height: 15px;\n flex-shrink: 0;\n}\n.ri-tourButton--stop {\n border-color: rgba(255, 68, 68, 0.4);\n color: #ff6b6b;\n}\n.ri-tourButton--stop:hover:not(:disabled) {\n background: rgba(255, 68, 68, 0.15);\n border-color: rgba(255, 68, 68, 0.6);\n color: #ff6b6b;\n}\n.ri-tourButton--stop:active:not(:disabled) {\n background: #ff4444;\n color: #fff;\n border-color: #ff4444;\n}\n.ri-tourButton--next {\n border-color: rgba(56, 219, 240, 0.3);\n color: #38dbf0;\n}\n.ri-tourButton--next:hover:not(:disabled) {\n background: rgba(56, 219, 240, 0.12);\n border-color: rgba(56, 219, 240, 0.5);\n color: #38dbf0;\n}\n.ri-tourButton--next:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-resetButton {\n width: 34px;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.ri-resetButton svg {\n width: 15px;\n height: 15px;\n pointer-events: none;\n flex-shrink: 0;\n}\n.ri-resetButton:focus-visible {\n outline: 2px solid rgba(255, 255, 255, 0.5);\n outline-offset: 2px;\n}\n.ri-downloadWrapper {\n position: relative;\n display: inline-flex;\n flex-direction: column;\n align-items: stretch;\n}\n.ri-downloadButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 0 16px;\n font-size: 13px;\n font-weight: 500;\n white-space: nowrap;\n}\n.ri-downloadButton:hover:not(:disabled) {\n background: rgba(0, 0, 0, 0.72);\n border-color: rgba(255, 255, 255, 0.3);\n color: #fff;\n}\n.ri-downloadButton:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-downloadButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.ri-downloadButton__spinner {\n display: inline-block;\n width: 13px;\n height: 13px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: ri-spin 0.7s linear infinite;\n flex-shrink: 0;\n}\n.ri-downloadMenu {\n position: absolute;\n bottom: calc(100% + 6px);\n right: 0;\n min-width: 168px;\n background: rgba(20, 20, 20, 0.92);\n border: 1px solid rgba(255, 255, 255, 0.14);\n border-radius: 10px;\n overflow: hidden;\n z-index: 100;\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n animation: ri-menuReveal 0.12s ease;\n}\n@keyframes ri-menuReveal {\n from {\n opacity: 0;\n transform: translateY(4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n.ri-downloadMenuItem {\n display: flex;\n align-items: center;\n gap: 9px;\n width: 100%;\n padding: 10px 14px;\n font-size: 13px;\n font-weight: 400;\n color: #fff;\n background: transparent;\n border: none;\n cursor: pointer;\n text-align: left;\n transition: background 0.1s ease;\n}\n.ri-downloadMenuItem:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.1);\n}\n.ri-downloadMenuItem:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.ri-downloadMenuDivider {\n height: 1px;\n background: rgba(255, 255, 255, 0.1);\n margin: 0 10px;\n}\n@keyframes ri-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.ri-annotationMarker {\n position: relative;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: rgba(20, 20, 20, 0.82);\n border: 2px solid rgba(255, 255, 255, 0.9);\n color: #fff;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n font-weight: 700;\n cursor: pointer;\n z-index: 20;\n user-select: none;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);\n pointer-events: auto;\n transition: opacity 0.3s ease, transform 0.3s ease;\n}\n.ri-annotationMarker:hover {\n transform: scale(1.08);\n box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18);\n background: rgba(40, 40, 40, 0.92);\n}\n.ri-annotationPopup {\n position: absolute;\n width: min(270px, calc(100vw - 32px));\n background: rgba(20, 20, 20, 0.85);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 16px;\n padding: 16px;\n z-index: 30;\n display: flex;\n flex-direction: column;\n gap: 12px;\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n}\n.ri-annotationInput,\n.ri-annotationTextarea {\n width: 100%;\n border: 1px solid rgba(255, 255, 255, 0.12);\n background: rgba(255, 255, 255, 0.05);\n color: #e5e7eb;\n border-radius: 10px;\n padding: 10px 12px;\n outline: none;\n font-family: inherit;\n transition: border-color 0.2s ease, box-shadow 0.2s ease;\n}\n.ri-annotationInput {\n font-size: 14px;\n font-weight: 500;\n}\n.ri-annotationTextarea {\n font-size: 13px;\n resize: vertical;\n min-height: 110px;\n}\n.ri-annotationInput:focus,\n.ri-annotationTextarea:focus {\n border-color: #38dbf0;\n box-shadow: 0 0 0 3px rgba(56, 219, 240, 0.12);\n}\n.ri-annotationActions {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.ri-annotationOkBtn {\n border: 0;\n border-radius: 8px;\n background: rgba(56, 219, 240, 0.8);\n color: #06343c;\n padding: 8px 18px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.2s ease, opacity 0.2s ease;\n}\n.ri-annotationOkBtn:hover:not(:disabled) {\n background: #38dbf0;\n}\n.ri-annotationOkBtn:disabled,\n.ri-annotationOkBtn.disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.ri-annotationCancelBtn {\n border: 1px solid rgba(255, 255, 255, 0.12);\n background: transparent;\n color: #9ca3af;\n border-radius: 8px;\n padding: 8px 12px;\n font-size: 13px;\n cursor: pointer;\n transition: background-color 0.2s ease, border-color 0.2s ease;\n}\n.ri-annotationCancelBtn:hover {\n background: rgba(255, 255, 255, 0.05);\n border-color: rgba(255, 255, 255, 0.2);\n}\n.ri-annotationIcons {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 12px;\n color: #9ca3af;\n}\n.ri-annotationIcons svg {\n flex-shrink: 0;\n}\n.ri-viewAnnotationPopup {\n position: relative;\n background: rgba(20, 20, 20, 0.85);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 8px;\n padding: 12px 16px;\n min-width: 200px;\n max-width: min(280px, calc(100vw - 48px));\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);\n pointer-events: auto;\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n animation: ri-viewAnnotationFadeIn 0.15s ease-out;\n margin-left: 24px;\n margin-top: -20px;\n isolation: isolate;\n z-index: 1000;\n}\n.ri-viewAnnotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.ri-viewAnnotationTitle {\n margin: 0;\n color: #e2e8f0;\n font-size: 14px;\n font-weight: 600;\n}\n.ri-viewAnnotationClose {\n background: transparent;\n border: none;\n color: #b7bdc6;\n cursor: pointer;\n font-size: 18px;\n padding: 0 4px;\n transition: color 0.2s;\n line-height: 1;\n}\n.ri-viewAnnotationClose:hover {\n color: #e2e8f0;\n}\n.ri-viewAnnotationDescription {\n margin: 0;\n color: #cbd5e1;\n font-size: 13px;\n line-height: 1.4;\n word-wrap: break-word;\n}\n@keyframes ri-viewAnnotationFadeIn {\n from {\n opacity: 0;\n transform: translateY(-6px) scale(0.96);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n.ri-annotationNavSingle {\n position: absolute;\n bottom: 120px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n pointer-events: none;\n}\n.ri-annotationNavSingle > * {\n pointer-events: auto;\n}\n.ri-navBadge {\n background: rgba(255, 255, 255, 0.1);\n padding: 2px 10px;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 400;\n color: rgba(255, 255, 255, 0.7);\n}\n.ri-annotationTitleHint {\n background: rgba(0, 0, 0, 0.7);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n color: white;\n padding: 8px 20px;\n border-radius: 8px;\n font-size: 13px;\n border: 1px solid rgba(255, 255, 255, 0.08);\n text-align: center;\n max-width: 350px;\n animation: slideUp 0.3s ease;\n}\n.ri-annotationHintTitle {\n font-weight: 600;\n color: #e5e7eb;\n}\n.ri-annotationHintDesc {\n font-size: 12px;\n opacity: 0.6;\n margin-top: 2px;\n color: #d1d5db;\n}\n@keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n@media (max-width: 768px) {\n .ri-xrTools {\n gap: 6px;\n }\n .ri-xrButton {\n height: 30px;\n gap: 6px;\n padding: 0 8px;\n font-size: 11px;\n }\n .ri-xrButton svg {\n width: 14px;\n height: 14px;\n }\n .ri-xrButtonPrefix {\n display: none;\n }\n .ri-annotationNavSingle {\n bottom: 100px;\n }\n .ri-navButtonSingle {\n padding: 8px 20px;\n font-size: 13px;\n min-width: 110px;\n }\n .ri-annotationTitleHint {\n padding: 6px 14px;\n font-size: 12px;\n max-width: 250px;\n }\n .ri-viewAnnotationPopup {\n min-width: 160px;\n max-width: 220px;\n padding: 10px 12px;\n }\n .ri-viewAnnotationTitle {\n font-size: 13px;\n }\n .ri-viewAnnotationDescription {\n font-size: 12px;\n }\n .ri-buttonContainer {\n flex-wrap: wrap;\n gap: 6px;\n }\n .ri-tourControls {\n flex-wrap: wrap;\n }\n .ri-rightControls {\n flex-wrap: wrap;\n }\n}\n@media (max-width: 480px) {\n .ri-xrTools {\n top: 12px;\n gap: 5px;\n }\n .ri-xrButton {\n height: 28px;\n gap: 5px;\n padding: 0 7px;\n font-size: 10px;\n }\n .ri-xrButton svg {\n width: 13px;\n height: 13px;\n }\n .ri-annotationNavSingle {\n bottom: 80px;\n }\n .ri-navButtonSingle {\n padding: 6px 16px;\n font-size: 12px;\n min-width: 90px;\n gap: 6px;\n }\n .ri-annotationTitleHint {\n padding: 4px 10px;\n font-size: 11px;\n max-width: 200px;\n }\n .ri-navBadge {\n font-size: 10px;\n padding: 1px 6px;\n }\n .ri-tourButton {\n padding: 0 8px;\n font-size: 12px;\n }\n .ri-tourButton span {\n display: none;\n }\n .ri-downloadButton {\n padding: 0 10px;\n font-size: 12px;\n }\n .ri-downloadButton span {\n display: none;\n }\n}\n.ri-canvasArea .bb-loadingOverlay {\n position: absolute;\n inset: 0;\n z-index: 12;\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(15, 23, 42, 0.46);\n backdrop-filter: blur(1px);\n}\n.ri-canvasArea .bb-loadingSpinner {\n width: 40px;\n height: 40px;\n border-radius: 999px;\n border: 3px solid rgba(226, 232, 240, 0.24);\n border-top-color: #93c5fd;\n animation: bb-spin 0.8s linear infinite;\n}\n.ri-canvasArea .bb-loadingMessage {\n max-width: 320px;\n color: #f7f8f9;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.5;\n}\n@keyframes bb-spin {\n to {\n transform: rotate(360deg);\n }\n}\n');
|
|
46
46
|
|
|
47
47
|
// src/components/LicenseOverlay.tsx
|
|
48
48
|
import { AlertTriangle } from "lucide-react";
|
|
@@ -3902,8 +3902,142 @@ var XRModelPresenter = forwardRef2(
|
|
|
3902
3902
|
);
|
|
3903
3903
|
var XRModelPresenter_default = XRModelPresenter;
|
|
3904
3904
|
|
|
3905
|
+
// src/components/ModelViewer/CinematicCamera.tsx
|
|
3906
|
+
import { useEffect as useEffect10, useMemo as useMemo11, useRef as useRef8 } from "react";
|
|
3907
|
+
import { useFrame as useFrame7, useThree as useThree9 } from "@react-three/fiber";
|
|
3908
|
+
import {
|
|
3909
|
+
Box3 as Box32,
|
|
3910
|
+
CatmullRomCurve3,
|
|
3911
|
+
Sphere,
|
|
3912
|
+
Vector3 as Vector36
|
|
3913
|
+
} from "three";
|
|
3914
|
+
var ENTER_BLEND_SECONDS = 1.2;
|
|
3915
|
+
function CinematicCamera({
|
|
3916
|
+
controlsRef,
|
|
3917
|
+
sceneRef,
|
|
3918
|
+
config,
|
|
3919
|
+
playing,
|
|
3920
|
+
onFinished,
|
|
3921
|
+
onUserInteract
|
|
3922
|
+
}) {
|
|
3923
|
+
const invalidate = useThree9((state) => state.invalidate);
|
|
3924
|
+
const waypoints = useMemo11(() => config.waypoints ?? [], [config.waypoints]);
|
|
3925
|
+
const hasPath = waypoints.length >= 2;
|
|
3926
|
+
const loop = config.loop ?? true;
|
|
3927
|
+
const duration = config.duration ?? (hasPath ? Math.max(waypoints.length * 3.5, 8) : 24);
|
|
3928
|
+
const signature = useMemo11(
|
|
3929
|
+
() => waypoints.map((w) => [...w.position, ...w.target].join(",")).join(";"),
|
|
3930
|
+
[waypoints]
|
|
3931
|
+
);
|
|
3932
|
+
const curves = useMemo11(() => {
|
|
3933
|
+
if (!hasPath) return null;
|
|
3934
|
+
return {
|
|
3935
|
+
position: new CatmullRomCurve3(
|
|
3936
|
+
waypoints.map((w) => new Vector36(...w.position)),
|
|
3937
|
+
loop,
|
|
3938
|
+
"catmullrom",
|
|
3939
|
+
0.5
|
|
3940
|
+
),
|
|
3941
|
+
target: new CatmullRomCurve3(
|
|
3942
|
+
waypoints.map((w) => new Vector36(...w.target)),
|
|
3943
|
+
loop,
|
|
3944
|
+
"catmullrom",
|
|
3945
|
+
0.5
|
|
3946
|
+
)
|
|
3947
|
+
};
|
|
3948
|
+
}, [signature, hasPath, loop]);
|
|
3949
|
+
const elapsedRef = useRef8(0);
|
|
3950
|
+
const enterElapsedRef = useRef8(0);
|
|
3951
|
+
const enterPos = useRef8(new Vector36());
|
|
3952
|
+
const enterTarget = useRef8(new Vector36());
|
|
3953
|
+
const startedRef = useRef8(false);
|
|
3954
|
+
const orbitRef = useRef8(null);
|
|
3955
|
+
const tmpPos = useMemo11(() => new Vector36(), []);
|
|
3956
|
+
const tmpTarget = useMemo11(() => new Vector36(), []);
|
|
3957
|
+
useEffect10(() => {
|
|
3958
|
+
if (!playing) {
|
|
3959
|
+
startedRef.current = false;
|
|
3960
|
+
return;
|
|
3961
|
+
}
|
|
3962
|
+
const controls = controlsRef.current;
|
|
3963
|
+
if (controls) {
|
|
3964
|
+
controls.getPosition(enterPos.current);
|
|
3965
|
+
controls.getTarget(enterTarget.current);
|
|
3966
|
+
}
|
|
3967
|
+
elapsedRef.current = 0;
|
|
3968
|
+
enterElapsedRef.current = 0;
|
|
3969
|
+
if (!hasPath) {
|
|
3970
|
+
const scene = sceneRef.current;
|
|
3971
|
+
if (scene) {
|
|
3972
|
+
const sphere = new Box32().setFromObject(scene).getBoundingSphere(new Sphere());
|
|
3973
|
+
orbitRef.current = {
|
|
3974
|
+
center: sphere.center.clone(),
|
|
3975
|
+
radius: sphere.radius > 0 ? sphere.radius : 1
|
|
3976
|
+
};
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
startedRef.current = true;
|
|
3980
|
+
invalidate();
|
|
3981
|
+
}, [playing, hasPath, controlsRef, sceneRef, invalidate]);
|
|
3982
|
+
useEffect10(() => {
|
|
3983
|
+
if (!playing || !onUserInteract) return;
|
|
3984
|
+
const controls = controlsRef.current;
|
|
3985
|
+
if (!controls) return;
|
|
3986
|
+
const handle = () => onUserInteract();
|
|
3987
|
+
controls.addEventListener("controlstart", handle);
|
|
3988
|
+
return () => controls.removeEventListener("controlstart", handle);
|
|
3989
|
+
}, [playing, controlsRef, onUserInteract]);
|
|
3990
|
+
useFrame7((_, delta) => {
|
|
3991
|
+
if (!playing || !startedRef.current) return;
|
|
3992
|
+
const controls = controlsRef.current;
|
|
3993
|
+
if (!controls) return;
|
|
3994
|
+
elapsedRef.current += delta;
|
|
3995
|
+
enterElapsedRef.current += delta;
|
|
3996
|
+
let u = elapsedRef.current / duration;
|
|
3997
|
+
const finished = !loop && u >= 1;
|
|
3998
|
+
u = loop ? u % 1 : Math.min(u, 1);
|
|
3999
|
+
if (curves) {
|
|
4000
|
+
curves.position.getPointAt(u, tmpPos);
|
|
4001
|
+
curves.target.getPointAt(u, tmpTarget);
|
|
4002
|
+
} else {
|
|
4003
|
+
const orbit = orbitRef.current;
|
|
4004
|
+
if (!orbit) return;
|
|
4005
|
+
const angle = u * Math.PI * 2;
|
|
4006
|
+
const dist = orbit.radius * 2.2;
|
|
4007
|
+
tmpPos.set(
|
|
4008
|
+
orbit.center.x + Math.cos(angle) * dist,
|
|
4009
|
+
orbit.center.y + orbit.radius * 0.6,
|
|
4010
|
+
orbit.center.z + Math.sin(angle) * dist
|
|
4011
|
+
);
|
|
4012
|
+
tmpTarget.copy(orbit.center);
|
|
4013
|
+
}
|
|
4014
|
+
const blend = Math.min(enterElapsedRef.current / ENTER_BLEND_SECONDS, 1);
|
|
4015
|
+
if (blend < 1) {
|
|
4016
|
+
const eased = blend * blend * (3 - 2 * blend);
|
|
4017
|
+
tmpPos.lerpVectors(enterPos.current, tmpPos, eased);
|
|
4018
|
+
tmpTarget.lerpVectors(enterTarget.current, tmpTarget, eased);
|
|
4019
|
+
}
|
|
4020
|
+
controls.setLookAt(
|
|
4021
|
+
tmpPos.x,
|
|
4022
|
+
tmpPos.y,
|
|
4023
|
+
tmpPos.z,
|
|
4024
|
+
tmpTarget.x,
|
|
4025
|
+
tmpTarget.y,
|
|
4026
|
+
tmpTarget.z,
|
|
4027
|
+
false
|
|
4028
|
+
);
|
|
4029
|
+
if (finished) {
|
|
4030
|
+
startedRef.current = false;
|
|
4031
|
+
onFinished?.();
|
|
4032
|
+
return;
|
|
4033
|
+
}
|
|
4034
|
+
invalidate();
|
|
4035
|
+
});
|
|
4036
|
+
return null;
|
|
4037
|
+
}
|
|
4038
|
+
|
|
3905
4039
|
// src/hooks/useLicense.ts
|
|
3906
|
-
import { useEffect as
|
|
4040
|
+
import { useEffect as useEffect11, useState as useState5 } from "react";
|
|
3907
4041
|
var LOCAL_HOSTNAMES = ["localhost", "127.0.0.1", "0.0.0.0"];
|
|
3908
4042
|
var isLocalHostname = (hostname) => LOCAL_HOSTNAMES.includes(hostname) || hostname.endsWith(".local") || hostname.endsWith(".localhost");
|
|
3909
4043
|
var LICENSE_SERVER = "https://react-immersive.liveroom.dev/api/validate-license";
|
|
@@ -4078,7 +4212,7 @@ var useLicense = (licenseKey) => {
|
|
|
4078
4212
|
// window isn't available during SSR — useEffect below recomputes this on mount
|
|
4079
4213
|
isLocalDomain: false
|
|
4080
4214
|
});
|
|
4081
|
-
|
|
4215
|
+
useEffect11(() => {
|
|
4082
4216
|
if (!canUseBrowserAPI()) {
|
|
4083
4217
|
return;
|
|
4084
4218
|
}
|
|
@@ -4129,7 +4263,7 @@ var useLicense = (licenseKey) => {
|
|
|
4129
4263
|
};
|
|
4130
4264
|
|
|
4131
4265
|
// src/components/MouseController.tsx
|
|
4132
|
-
import { useRef as
|
|
4266
|
+
import { useRef as useRef9, useState as useState6, useCallback as useCallback3, useEffect as useEffect12 } from "react";
|
|
4133
4267
|
|
|
4134
4268
|
// src/styles/MouseController.css
|
|
4135
4269
|
styleInject(".mc__wrapper {\n position: fixed;\n bottom: -10px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n flex-direction: column;\n align-items: center;\n z-index: 100;\n}\n.mc__dpad {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n margin-bottom: 20px;\n}\n.mc__middle-row {\n display: flex;\n align-items: center;\n gap: 2px;\n}\n.mc__joystick {\n width: 60px;\n height: 60px;\n flex-shrink: 0;\n background:\n radial-gradient(\n circle at 40% 35%,\n rgba(255, 255, 255, 0.07),\n rgba(0, 0, 0, 0.55));\n border-radius: 50%;\n border: 1.5px solid rgba(255, 255, 255, 0.15);\n position: relative;\n cursor: grab;\n touch-action: none;\n transition: border-color 0.2s ease, box-shadow 0.2s ease;\n margin: 4px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5), 0 0 0 4px rgba(255, 255, 255, 0.03);\n}\n.mc__joystick:hover {\n border-color: rgba(255, 255, 255, 0.28);\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.55), 0 0 0 4px rgba(66, 133, 244, 0.08);\n}\n.mc__joystick:active {\n cursor: grabbing;\n border-color: rgba(66, 133, 244, 0.6);\n box-shadow: 0 2px 12px rgba(0, 0, 0, 0.5), 0 0 4px rgba(66, 133, 244, 0.15);\n}\n.mc__knob {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 34px;\n height: 34px;\n margin-left: -17px;\n margin-top: -17px;\n background:\n linear-gradient(\n 145deg,\n #5a9bf5,\n #1a66d9);\n border-radius: 50%;\n cursor: grab;\n box-shadow: 0 3px 10px rgba(26, 102, 217, 0.5), 0 1px 0 rgba(255, 255, 255, 0.25) inset;\n transition: transform 0.04s linear, box-shadow 0.1s ease;\n border: 1.5px solid rgba(255, 255, 255, 0.45);\n}\n.mc__joystick:active .mc__knob {\n box-shadow: 0 2px 16px rgba(66, 133, 244, 0.7), 0 1px 0 rgba(255, 255, 255, 0.25) inset;\n animation: pulse 0.6s ease infinite;\n}\n.mc__arrow {\n padding: 0;\n margin: 0;\n background: transparent;\n border: none;\n border-radius: 0;\n box-shadow: none;\n outline: none;\n -webkit-appearance: none;\n appearance: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n touch-action: manipulation;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n transition: transform 0.1s ease, filter 0.15s ease;\n overflow: visible;\n}\n.mc__arrow svg {\n display: block !important;\n pointer-events: none !important;\n user-select: none !important;\n -webkit-user-select: none !important;\n overflow: visible !important;\n}\n.mc__arrow--up {\n margin-bottom: 2px;\n}\n.mc__arrow--up:hover {\n transform: scale(1.15) translateY(-2px);\n filter: drop-shadow(0 4px 12px rgba(66, 133, 244, 0.8));\n}\n.mc__arrow--up:active {\n transform: scale(0.92) translateY(2px);\n}\n.mc__arrow--down {\n margin-top: 2px;\n}\n.mc__arrow--down:hover {\n transform: scale(1.15) translateY(2px);\n filter: drop-shadow(0 -4px 12px rgba(66, 133, 244, 0.8));\n}\n.mc__arrow--down:active {\n transform: scale(0.92) translateY(-2px);\n}\n.mc__arrow--left {\n margin-right: 2px;\n}\n.mc__arrow--left:hover {\n transform: scale(1.15) translateX(-2px);\n filter: drop-shadow(4px 0 12px rgba(66, 133, 244, 0.8));\n}\n.mc__arrow--left:active {\n transform: scale(0.92) translateX(2px);\n}\n.mc__arrow--right {\n margin-left: 2px;\n}\n.mc__arrow--right:hover {\n transform: scale(1.15) translateX(2px);\n filter: drop-shadow(-4px 0 12px rgba(66, 133, 244, 0.8));\n}\n.mc__arrow--right:active {\n transform: scale(0.92) translateX(-2px);\n}\n@keyframes pulse {\n 0%, 100% {\n box-shadow: 0 2px 16px rgba(66, 133, 244, 0.6), 0 1px 0 rgba(255, 255, 255, 0.25) inset;\n opacity: 0.85;\n }\n 50% {\n box-shadow: 0 4px 24px rgba(66, 133, 244, 0.95), 0 1px 0 rgba(255, 255, 255, 0.25) inset;\n opacity: 1;\n }\n}\n@media (max-width: 768px) {\n .mc__wrapper {\n bottom: -5px;\n }\n .mc__joystick {\n width: 68px;\n height: 68px;\n }\n .mc__knob {\n width: 30px;\n height: 30px;\n margin-left: -15px;\n margin-top: -15px;\n }\n .mc__arrow--up:hover {\n transform: scale(1.1) translateY(-1px);\n }\n .mc__arrow--down:hover {\n transform: scale(1.1) translateY(1px);\n }\n .mc__arrow--left:hover {\n transform: scale(1.1) translateX(-1px);\n }\n .mc__arrow--right:hover {\n transform: scale(1.1) translateX(1px);\n }\n}\n@media (max-width: 480px) {\n .mc__joystick {\n width: 56px;\n height: 56px;\n }\n .mc__knob {\n width: 26px;\n height: 26px;\n margin-left: -13px;\n margin-top: -13px;\n }\n .mc__arrow--up {\n margin-bottom: 1px;\n }\n .mc__arrow--down {\n margin-top: 1px;\n }\n .mc__arrow--left {\n margin-right: 1px;\n }\n .mc__arrow--right {\n margin-left: 1px;\n }\n}\n@media (hover: none) {\n .mc__arrow:hover {\n transform: none !important;\n filter: none !important;\n }\n .mc__arrow:active {\n transform: scale(0.92) !important;\n }\n}\n");
|
|
@@ -4214,14 +4348,14 @@ var MouseController = ({
|
|
|
4214
4348
|
rotateSensitivity = 0.01,
|
|
4215
4349
|
position = "center-bottom"
|
|
4216
4350
|
}) => {
|
|
4217
|
-
const joystickBaseRef =
|
|
4351
|
+
const joystickBaseRef = useRef9(null);
|
|
4218
4352
|
const [knobPosition, setKnobPosition] = useState6({ x: 0, y: 0 });
|
|
4219
|
-
const isDragging =
|
|
4220
|
-
const rotateDirection =
|
|
4221
|
-
const animationFrame =
|
|
4222
|
-
const repeatAnimationRef =
|
|
4223
|
-
const activeRepeatActionRef =
|
|
4224
|
-
const lastRepeatTimeRef =
|
|
4353
|
+
const isDragging = useRef9(false);
|
|
4354
|
+
const rotateDirection = useRef9({ x: 0, y: 0 });
|
|
4355
|
+
const animationFrame = useRef9();
|
|
4356
|
+
const repeatAnimationRef = useRef9();
|
|
4357
|
+
const activeRepeatActionRef = useRef9(null);
|
|
4358
|
+
const lastRepeatTimeRef = useRef9(0);
|
|
4225
4359
|
const getControls = () => controlsRef.current;
|
|
4226
4360
|
const clampToCircle = (dx, dy, maxR) => {
|
|
4227
4361
|
const dist = Math.hypot(dx, dy);
|
|
@@ -4333,7 +4467,7 @@ var MouseController = ({
|
|
|
4333
4467
|
lastRepeatTimeRef.current = 0;
|
|
4334
4468
|
}
|
|
4335
4469
|
}, []);
|
|
4336
|
-
|
|
4470
|
+
useEffect12(() => {
|
|
4337
4471
|
window.addEventListener("mousemove", onJoystickMove);
|
|
4338
4472
|
window.addEventListener("mouseup", onJoystickEnd);
|
|
4339
4473
|
window.addEventListener("touchmove", onJoystickMove, { passive: false });
|
|
@@ -4562,7 +4696,7 @@ var MaterialPicker = ({
|
|
|
4562
4696
|
};
|
|
4563
4697
|
|
|
4564
4698
|
// src/components/ModelViewer/ObjectBindingDataPanel.tsx
|
|
4565
|
-
import { useEffect as
|
|
4699
|
+
import { useEffect as useEffect13, useState as useState7 } from "react";
|
|
4566
4700
|
|
|
4567
4701
|
// src/styles/ObjectBindingDataPanel.css
|
|
4568
4702
|
styleInject('.left-panel {\n width: 280px;\n min-height: 0;\n background: var(--ri-databinding-bg, #111318);\n border-right: 1px solid var(--ri-databinding-border, rgba(255, 255, 255, 0.06));\n display: flex;\n flex-direction: column;\n padding: 24px 18px;\n overflow-y: auto;\n font-family: var( --ri-databinding-font-family, "Inter", "Segoe UI", sans-serif );\n flex-shrink: 0;\n transition: margin-left 0.45s cubic-bezier(0.4, 0, 0.2, 1);\n box-shadow: var(--ri-databinding-shadow, 4px 0 24px rgba(0, 0, 0, 0.35));\n}\n.left-panel--open {\n margin-left: 0;\n}\n.left-panel--closed {\n margin-left: -280px;\n}\n.left-panel::-webkit-scrollbar {\n width: 4px;\n}\n.left-panel::-webkit-scrollbar-track {\n background: transparent;\n}\n.left-panel::-webkit-scrollbar-thumb {\n background: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n border-radius: 4px;\n}\n.left-panel__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 16px;\n padding-bottom: 14px;\n border-bottom: 1px solid var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n}\n.left-panel__title {\n font-size: 14px;\n font-weight: 600;\n color: var(--ri-databinding-text, #f1f5f9);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n padding-right: 8px;\n margin: 0;\n letter-spacing: 0.02em;\n}\n.left-panel__close-btn {\n width: 26px;\n height: 26px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--ri-databinding-surface, rgba(255, 255, 255, 0.03));\n border: 1px solid var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n border-radius: 6px;\n color: var(--ri-databinding-text-subtle, #64748b);\n font-size: 13px;\n flex-shrink: 0;\n cursor: pointer;\n transition:\n background 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease;\n}\n.left-panel__close-btn:hover {\n background: var(--ri-databinding-surface-hover, rgba(255, 255, 255, 0.06));\n color: var(--ri-databinding-text-hover, #cbd5e1);\n border-color: var(--ri-databinding-border-hover, rgba(255, 255, 255, 0.15));\n}\n.left-panel__data {\n padding: 12px 6px;\n background: var(--ri-databinding-surface, rgba(255, 255, 255, 0.03));\n border: 1px solid var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n border-radius: 10px;\n font-size: 11px;\n font-family: var( --ri-databinding-mono-font-family, "JetBrains Mono", "Fira Code", "Menlo", monospace );\n color: var(--ri-databinding-text-muted, #94a3b8);\n line-height: 1.7;\n overflow-x: auto;\n margin-bottom: 18px;\n}\n.left-panel__divider {\n border: none;\n border-top: 1px solid var(--ri-databinding-border, rgba(255, 255, 255, 0.06));\n margin-bottom: 16px;\n}\n.left-panel__actions-label {\n font-size: 10px;\n font-weight: 600;\n color: var(--ri-databinding-text-faint, #475569);\n text-transform: uppercase;\n letter-spacing: 0.12em;\n margin-bottom: 10px;\n}\n.left-panel__actions-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.left-panel__action-btn {\n position: relative;\n padding: 9px 14px;\n border-radius: 8px;\n text-align: left;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n overflow: hidden;\n border: 1px solid var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n background: var(--ri-databinding-surface, rgba(255, 255, 255, 0.03));\n color: var(--ri-databinding-text-muted, #94a3b8);\n letter-spacing: 0.01em;\n}\n.left-panel__action-btn:active {\n transform: scale(0.97);\n}\n.left-panel__action-btn--active::before {\n content: "";\n position: absolute;\n left: 0;\n top: 20%;\n bottom: 20%;\n width: 3px;\n background: var(--ri-databinding-accent, #93c5fd);\n border-radius: 0 3px 3px 0;\n box-shadow: 0 0 8px var(--ri-databinding-accent-glow, rgba(147, 197, 253, 0.8));\n}\n.left-panel__action-btn--active:hover {\n background: var( --ri-databinding-accent-gradient, linear-gradient(135deg, #1e3a8a 0%, #1d4ed8 60%, #2563eb 100%) );\n border-color: var(--ri-databinding-accent-border, rgba(96, 165, 250, 0.55));\n}\n.left-panel__action-btn--active:active {\n transform: scale(0.97);\n background: var( --ri-databinding-accent-gradient-active, linear-gradient(135deg, #1e3a8a 0%, #1e40af 100%) );\n}\n.left-panel__data {\n display: flex;\n flex-direction: column;\n gap: 2px;\n margin-bottom: 18px;\n}\n.left-panel__data-row {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 10px;\n padding: 7px 10px;\n border-radius: 7px;\n background: var(--ri-databinding-surface, rgba(255, 255, 255, 0.03));\n border: 1px solid transparent;\n transition: background 0.15s ease, border-color 0.15s ease;\n}\n.left-panel__data-row:hover {\n background: var(--ri-databinding-surface-hover, rgba(255, 255, 255, 0.06));\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n}\n.left-panel__data-key {\n font-size: 11px;\n font-weight: 600;\n color: var(--ri-databinding-text-faint, #475569);\n text-transform: uppercase;\n letter-spacing: 0.07em;\n white-space: nowrap;\n flex-shrink: 0;\n}\n.left-panel__data-value {\n font-size: 12px;\n font-family: var( --ri-databinding-mono-font-family, "JetBrains Mono", "Fira Code", "Menlo", monospace );\n text-align: right;\n word-break: break-all;\n max-width: 150px;\n}\n@media (max-width: 900px) {\n .left-panel {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n width: min(280px, 85vw);\n z-index: 200;\n min-height: 0;\n margin-left: 0;\n transition: transform 0.45s cubic-bezier(0.4, 0, 0.2, 1);\n }\n .left-panel--open {\n margin-left: 0;\n transform: translateX(0);\n }\n .left-panel--closed {\n margin-left: 0;\n transform: translateX(-100%);\n }\n}\n');
|
|
@@ -4577,7 +4711,7 @@ function ObjectBindingDataPanel({
|
|
|
4577
4711
|
onAction
|
|
4578
4712
|
}) {
|
|
4579
4713
|
const [displayedObject, setDisplayedObject] = useState7(selectedObject);
|
|
4580
|
-
|
|
4714
|
+
useEffect13(() => {
|
|
4581
4715
|
if (selectedObject) setDisplayedObject(selectedObject);
|
|
4582
4716
|
}, [selectedObject]);
|
|
4583
4717
|
const objectToRender = selectedObject ?? displayedObject;
|
|
@@ -4643,7 +4777,7 @@ function ObjectBindingDataPanel({
|
|
|
4643
4777
|
}
|
|
4644
4778
|
|
|
4645
4779
|
// src/components/ModelViewer/SceneObjectsPanel.tsx
|
|
4646
|
-
import { useMemo as
|
|
4780
|
+
import { useMemo as useMemo12, useState as useState9 } from "react";
|
|
4647
4781
|
import { Boxes } from "lucide-react";
|
|
4648
4782
|
|
|
4649
4783
|
// src/components/shared/SearchableSidePanel.tsx
|
|
@@ -4768,7 +4902,7 @@ function SceneObjectsPanel({
|
|
|
4768
4902
|
}) {
|
|
4769
4903
|
const [hoveredNode, setHoveredNode] = useState9(null);
|
|
4770
4904
|
const [searchQuery, setSearchQuery] = useState9("");
|
|
4771
|
-
const entries =
|
|
4905
|
+
const entries = useMemo12(
|
|
4772
4906
|
() => Object.entries(objectBindings).filter(([nodeName, binding]) => {
|
|
4773
4907
|
const q = searchQuery.toLowerCase();
|
|
4774
4908
|
return nodeName.toLowerCase().includes(q) || (binding.label ?? "").toLowerCase().includes(q);
|
|
@@ -4873,7 +5007,9 @@ import {
|
|
|
4873
5007
|
ChevronRight,
|
|
4874
5008
|
RectangleGoggles,
|
|
4875
5009
|
X,
|
|
4876
|
-
Expand
|
|
5010
|
+
Expand,
|
|
5011
|
+
Play,
|
|
5012
|
+
Pause
|
|
4877
5013
|
} from "lucide-react";
|
|
4878
5014
|
|
|
4879
5015
|
// src/components/BindingBuilder/BindingBuilderContext.tsx
|
|
@@ -4928,7 +5064,7 @@ function findInfluencingBone(hit) {
|
|
|
4928
5064
|
if (!skinIndex || !skinWeight || !position) return null;
|
|
4929
5065
|
const localHit = mesh.worldToLocal(hit.point.clone());
|
|
4930
5066
|
const vertices = [hit.face.a, hit.face.b, hit.face.c];
|
|
4931
|
-
const vertex = new
|
|
5067
|
+
const vertex = new Vector37();
|
|
4932
5068
|
let nearest = vertices[0];
|
|
4933
5069
|
let nearestDist = Infinity;
|
|
4934
5070
|
for (const vi of vertices) {
|
|
@@ -4975,19 +5111,19 @@ function GuidedTourCameraFollow({
|
|
|
4975
5111
|
controlsRef,
|
|
4976
5112
|
offsetRef
|
|
4977
5113
|
}) {
|
|
4978
|
-
const scene =
|
|
4979
|
-
const nodeRef =
|
|
4980
|
-
const hasPrev =
|
|
4981
|
-
const live =
|
|
4982
|
-
const prev =
|
|
4983
|
-
const goal =
|
|
5114
|
+
const scene = useThree10((state) => state.scene);
|
|
5115
|
+
const nodeRef = useRef10(null);
|
|
5116
|
+
const hasPrev = useRef10(false);
|
|
5117
|
+
const live = useMemo13(() => new Vector37(), []);
|
|
5118
|
+
const prev = useMemo13(() => new Vector37(), []);
|
|
5119
|
+
const goal = useMemo13(() => new Vector37(), []);
|
|
4984
5120
|
const nodeName = annotation?.anchorNodeName;
|
|
4985
5121
|
const localPosition = annotation?.anchorLocalPosition;
|
|
4986
|
-
|
|
5122
|
+
useEffect14(() => {
|
|
4987
5123
|
nodeRef.current = null;
|
|
4988
5124
|
hasPrev.current = false;
|
|
4989
5125
|
}, [nodeName, annotation?.id]);
|
|
4990
|
-
|
|
5126
|
+
useFrame8(() => {
|
|
4991
5127
|
const controls = controlsRef.current;
|
|
4992
5128
|
const offset = offsetRef.current;
|
|
4993
5129
|
if (!enabled || !controls || !offset || !nodeName || !localPosition) {
|
|
@@ -5019,8 +5155,8 @@ var DEFAULT_AR_OVERLAY_STATE = {
|
|
|
5019
5155
|
isModelPlaced: false
|
|
5020
5156
|
};
|
|
5021
5157
|
function computeMeasureBox(scene) {
|
|
5022
|
-
const box = new
|
|
5023
|
-
const size = new
|
|
5158
|
+
const box = new Box33().setFromObject(scene);
|
|
5159
|
+
const size = new Vector37();
|
|
5024
5160
|
box.getSize(size);
|
|
5025
5161
|
return {
|
|
5026
5162
|
min: [box.min.x, box.min.y, box.min.z],
|
|
@@ -5031,8 +5167,8 @@ function computeMeasureBox(scene) {
|
|
|
5031
5167
|
function fitClippingPlanesToScene(scene, controls) {
|
|
5032
5168
|
const camera = controls?.camera;
|
|
5033
5169
|
if (!(camera instanceof PerspectiveCamera3)) return;
|
|
5034
|
-
const box = new
|
|
5035
|
-
const size = box.getSize(new
|
|
5170
|
+
const box = new Box33().setFromObject(scene);
|
|
5171
|
+
const size = box.getSize(new Vector37()).length();
|
|
5036
5172
|
if (!Number.isFinite(size) || size <= 0) return;
|
|
5037
5173
|
camera.near = size / 100;
|
|
5038
5174
|
camera.far = size * 100;
|
|
@@ -5100,6 +5236,7 @@ function ModelViewer({
|
|
|
5100
5236
|
meshopt = true,
|
|
5101
5237
|
showMeasureTools = false,
|
|
5102
5238
|
showExplodeControls = false,
|
|
5239
|
+
cinematic,
|
|
5103
5240
|
measurementUnit = "m",
|
|
5104
5241
|
enableXR = false,
|
|
5105
5242
|
usdzUrl,
|
|
@@ -5107,48 +5244,48 @@ function ModelViewer({
|
|
|
5107
5244
|
}) {
|
|
5108
5245
|
const { activeTab, activeSubTab } = useBindingBuilderContext();
|
|
5109
5246
|
const license = useLicense(licenseKey);
|
|
5110
|
-
const resolvedSceneConfig =
|
|
5247
|
+
const resolvedSceneConfig = useMemo13(
|
|
5111
5248
|
() => sceneConfig ?? DEFAULT_SCENE_CONFIG,
|
|
5112
5249
|
[sceneConfig]
|
|
5113
5250
|
);
|
|
5114
|
-
const viewerBackdropColor =
|
|
5251
|
+
const viewerBackdropColor = useMemo13(() => {
|
|
5115
5252
|
if (backgroundColor) return backgroundColor;
|
|
5116
5253
|
const bg = resolvedSceneConfig.background;
|
|
5117
5254
|
if (bg.enabled && bg.type === "color") return bg.color;
|
|
5118
5255
|
return "#1c1c1d";
|
|
5119
5256
|
}, [backgroundColor, resolvedSceneConfig]);
|
|
5120
|
-
const decoderConfig =
|
|
5257
|
+
const decoderConfig = useMemo13(
|
|
5121
5258
|
() => ({ dracoDecoderPath, ktx2TranscoderPath, meshopt }),
|
|
5122
5259
|
[dracoDecoderPath, ktx2TranscoderPath, meshopt]
|
|
5123
5260
|
);
|
|
5124
|
-
const reduceGpuLoad =
|
|
5261
|
+
const reduceGpuLoad = useMemo13(
|
|
5125
5262
|
() => shouldReduceGpuLoad(performanceProfile),
|
|
5126
5263
|
[performanceProfile]
|
|
5127
5264
|
);
|
|
5128
|
-
const controlsRef =
|
|
5129
|
-
|
|
5265
|
+
const controlsRef = useRef10(null);
|
|
5266
|
+
useEffect14(() => {
|
|
5130
5267
|
if (!controlsRef.current) return;
|
|
5131
5268
|
controlsRef.current.dollySpeed = Math.max(zoomSensitivity, 0);
|
|
5132
5269
|
}, [zoomSensitivity]);
|
|
5133
|
-
const tourCameraOffsetRef =
|
|
5134
|
-
const annotationWorldPositionsRef =
|
|
5135
|
-
const fileInputRef =
|
|
5136
|
-
const pendingObjectId =
|
|
5137
|
-
const rendererCaptureRef =
|
|
5138
|
-
const createdBlobUrls =
|
|
5139
|
-
|
|
5270
|
+
const tourCameraOffsetRef = useRef10(null);
|
|
5271
|
+
const annotationWorldPositionsRef = useRef10(/* @__PURE__ */ new Map());
|
|
5272
|
+
const fileInputRef = useRef10(null);
|
|
5273
|
+
const pendingObjectId = useRef10(null);
|
|
5274
|
+
const rendererCaptureRef = useRef10(null);
|
|
5275
|
+
const createdBlobUrls = useRef10({});
|
|
5276
|
+
useEffect14(() => {
|
|
5140
5277
|
return () => {
|
|
5141
5278
|
for (const url of Object.values(createdBlobUrls.current)) {
|
|
5142
5279
|
URL.revokeObjectURL(url);
|
|
5143
5280
|
}
|
|
5144
5281
|
};
|
|
5145
5282
|
}, []);
|
|
5146
|
-
const nodeRefsMap =
|
|
5147
|
-
const materialRefsOut =
|
|
5148
|
-
const viewerLeftRef =
|
|
5149
|
-
const canvasAreaRef =
|
|
5150
|
-
const xrPresenterRef =
|
|
5151
|
-
const viewAnnotationPopupRef =
|
|
5283
|
+
const nodeRefsMap = useRef10({});
|
|
5284
|
+
const materialRefsOut = useRef10({});
|
|
5285
|
+
const viewerLeftRef = useRef10(null);
|
|
5286
|
+
const canvasAreaRef = useRef10(null);
|
|
5287
|
+
const xrPresenterRef = useRef10(null);
|
|
5288
|
+
const viewAnnotationPopupRef = useRef10(null);
|
|
5152
5289
|
const [internalObjectBindings, setInternalObjectBindings] = useState10(objectBindings);
|
|
5153
5290
|
const [itemMaterials, setItemMaterials] = useState10({});
|
|
5154
5291
|
const [currentAction, setCurrentAction] = useState10(
|
|
@@ -5162,12 +5299,12 @@ function ModelViewer({
|
|
|
5162
5299
|
null
|
|
5163
5300
|
);
|
|
5164
5301
|
const [isDownloading, setIsDownloading] = useState10(false);
|
|
5165
|
-
const sceneRef =
|
|
5166
|
-
const fitAttemptedRef =
|
|
5167
|
-
const fitInFlightRef =
|
|
5168
|
-
const cameraProvidedRef =
|
|
5302
|
+
const sceneRef = useRef10(null);
|
|
5303
|
+
const fitAttemptedRef = useRef10(false);
|
|
5304
|
+
const fitInFlightRef = useRef10(false);
|
|
5305
|
+
const cameraProvidedRef = useRef10(Boolean(camera));
|
|
5169
5306
|
cameraProvidedRef.current = Boolean(camera);
|
|
5170
|
-
const initialCameraStateRef =
|
|
5307
|
+
const initialCameraStateRef = useRef10(null);
|
|
5171
5308
|
const [isAutoFraming, setIsAutoFraming] = useState10(Boolean(modelUrl));
|
|
5172
5309
|
const [cameraPositioned, setCameraPositioned] = useState10(
|
|
5173
5310
|
!modelUrl || Boolean(onAutoFit) || Boolean(camera)
|
|
@@ -5177,14 +5314,14 @@ function ModelViewer({
|
|
|
5177
5314
|
const [modelLoadFailed, setModelLoadFailed] = useState10(false);
|
|
5178
5315
|
const [bindingsMismatch, setBindingsMismatch] = useState10(false);
|
|
5179
5316
|
const [mismatchDismissed, setMismatchDismissed] = useState10(false);
|
|
5180
|
-
const currentObjectBindingsRef =
|
|
5317
|
+
const currentObjectBindingsRef = useRef10({});
|
|
5181
5318
|
const captureInitialCameraState = useCallback4(() => {
|
|
5182
5319
|
const controls = controlsRef.current;
|
|
5183
5320
|
if (!controls || initialCameraStateRef.current) {
|
|
5184
5321
|
return;
|
|
5185
5322
|
}
|
|
5186
|
-
const position = controls.getPosition(new
|
|
5187
|
-
const target = controls.getTarget(new
|
|
5323
|
+
const position = controls.getPosition(new Vector37());
|
|
5324
|
+
const target = controls.getTarget(new Vector37());
|
|
5188
5325
|
initialCameraStateRef.current = {
|
|
5189
5326
|
position: [position.x, position.y, position.z],
|
|
5190
5327
|
target: [target.x, target.y, target.z]
|
|
@@ -5195,17 +5332,45 @@ function ModelViewer({
|
|
|
5195
5332
|
const [measurePoints, setMeasurePoints] = useState10([]);
|
|
5196
5333
|
const [showDimensions, setShowDimensions] = useState10(false);
|
|
5197
5334
|
const [measureBox, setMeasureBox] = useState10(null);
|
|
5198
|
-
const measureModeRef =
|
|
5335
|
+
const measureModeRef = useRef10(false);
|
|
5199
5336
|
measureModeRef.current = measureMode;
|
|
5200
|
-
const measurePointCounter =
|
|
5201
|
-
const measureDownRef =
|
|
5337
|
+
const measurePointCounter = useRef10(0);
|
|
5338
|
+
const measureDownRef = useRef10(null);
|
|
5202
5339
|
const [explodeFactor, setExplodeFactor] = useState10(0);
|
|
5203
5340
|
const [explodeOpen, setExplodeOpen] = useState10(false);
|
|
5204
|
-
|
|
5341
|
+
useEffect14(() => {
|
|
5205
5342
|
setExplodeFactor(0);
|
|
5206
5343
|
setExplodeOpen(false);
|
|
5207
5344
|
}, [modelUrl]);
|
|
5208
|
-
const
|
|
5345
|
+
const cinematicConfig = useMemo13(() => {
|
|
5346
|
+
const sc = resolvedSceneConfig.cinematic;
|
|
5347
|
+
const base = sc?.enabled ? {
|
|
5348
|
+
waypoints: sc.waypoints,
|
|
5349
|
+
duration: sc.duration,
|
|
5350
|
+
loop: sc.loop,
|
|
5351
|
+
autoPlay: sc.autoPlay
|
|
5352
|
+
} : null;
|
|
5353
|
+
if (cinematic === void 0) {
|
|
5354
|
+
return base && (base.waypoints?.length ?? 0) >= 2 ? base : null;
|
|
5355
|
+
}
|
|
5356
|
+
if (cinematic === false) return null;
|
|
5357
|
+
if (cinematic === true) return base ?? {};
|
|
5358
|
+
return { ...base ?? {}, ...cinematic };
|
|
5359
|
+
}, [cinematic, resolvedSceneConfig]);
|
|
5360
|
+
const [cinematicPlaying, setCinematicPlaying] = useState10(false);
|
|
5361
|
+
const cinematicAutoStartedRef = useRef10(false);
|
|
5362
|
+
useEffect14(() => {
|
|
5363
|
+
setCinematicPlaying(false);
|
|
5364
|
+
cinematicAutoStartedRef.current = false;
|
|
5365
|
+
}, [modelUrl]);
|
|
5366
|
+
useEffect14(() => {
|
|
5367
|
+
if (!cinematicConfig?.autoPlay || cinematicAutoStartedRef.current) return;
|
|
5368
|
+
if (modelLoaded && cameraPositioned && !isAutoFraming) {
|
|
5369
|
+
cinematicAutoStartedRef.current = true;
|
|
5370
|
+
setCinematicPlaying(true);
|
|
5371
|
+
}
|
|
5372
|
+
}, [cinematicConfig, modelLoaded, cameraPositioned, isAutoFraming]);
|
|
5373
|
+
const xrStore = useMemo13(() => {
|
|
5209
5374
|
if (!enableXR) return null;
|
|
5210
5375
|
const xr = navigator.xr;
|
|
5211
5376
|
if (!xr || typeof xr.isSessionSupported !== "function" || typeof xr.addEventListener !== "function") {
|
|
@@ -5219,7 +5384,7 @@ function ModelViewer({
|
|
|
5219
5384
|
}, [enableXR]);
|
|
5220
5385
|
const [arSupported, setArSupported] = useState10(false);
|
|
5221
5386
|
const [vrSupported, setVrSupported] = useState10(false);
|
|
5222
|
-
const quickLookSupported =
|
|
5387
|
+
const quickLookSupported = useMemo13(
|
|
5223
5388
|
() => enableXR && Boolean(usdzUrl?.trim()) && isAppleHandheldDevice(),
|
|
5224
5389
|
[enableXR, usdzUrl]
|
|
5225
5390
|
);
|
|
@@ -5228,17 +5393,17 @@ function ModelViewer({
|
|
|
5228
5393
|
DEFAULT_AR_OVERLAY_STATE
|
|
5229
5394
|
);
|
|
5230
5395
|
const [relocatePressed, setRelocatePressed] = useState10(false);
|
|
5231
|
-
|
|
5396
|
+
useEffect14(() => {
|
|
5232
5397
|
if (!xrStore) return;
|
|
5233
5398
|
return xrStore.subscribe(
|
|
5234
5399
|
(state) => setInARSession(state.session != null && state.mode === "immersive-ar")
|
|
5235
5400
|
);
|
|
5236
5401
|
}, [xrStore]);
|
|
5237
|
-
|
|
5402
|
+
useEffect14(() => {
|
|
5238
5403
|
setRelocatePressed(false);
|
|
5239
5404
|
setArOverlayState(DEFAULT_AR_OVERLAY_STATE);
|
|
5240
5405
|
}, [inARSession]);
|
|
5241
|
-
|
|
5406
|
+
useEffect14(() => {
|
|
5242
5407
|
if (!enableXR) return;
|
|
5243
5408
|
const xr = navigator.xr;
|
|
5244
5409
|
if (!xr?.isSessionSupported) return;
|
|
@@ -5273,7 +5438,7 @@ function ModelViewer({
|
|
|
5273
5438
|
setRelocatePressed(true);
|
|
5274
5439
|
xrPresenterRef.current?.startPlacementMode();
|
|
5275
5440
|
}, []);
|
|
5276
|
-
|
|
5441
|
+
useEffect14(() => {
|
|
5277
5442
|
if (!relocatePressed) return;
|
|
5278
5443
|
const onPointerUp = () => finishRelocation(true);
|
|
5279
5444
|
const onPointerCancel = () => finishRelocation(false);
|
|
@@ -5336,7 +5501,7 @@ function ModelViewer({
|
|
|
5336
5501
|
const [annotations, setAnnotations] = useState10(
|
|
5337
5502
|
() => sceneConfig?.annotations ?? []
|
|
5338
5503
|
);
|
|
5339
|
-
|
|
5504
|
+
useEffect14(() => {
|
|
5340
5505
|
if (annotations.length > 0) {
|
|
5341
5506
|
const maxId = annotations.reduce((max, ann) => Math.max(max, ann.id), 0);
|
|
5342
5507
|
annotationCounterRef.current = maxId;
|
|
@@ -5349,15 +5514,15 @@ function ModelViewer({
|
|
|
5349
5514
|
title: "",
|
|
5350
5515
|
description: ""
|
|
5351
5516
|
});
|
|
5352
|
-
const annotationCounterRef =
|
|
5517
|
+
const annotationCounterRef = useRef10(0);
|
|
5353
5518
|
const [internalViewingAnnotation, setInternalViewingAnnotation] = useState10(null);
|
|
5354
5519
|
const [currentAnnotationIndex, setCurrentAnnotationIndex] = useState10(-1);
|
|
5355
5520
|
const [isNavigating, setIsNavigating] = useState10(false);
|
|
5356
|
-
const annotationSmoothRestoreTimeoutRef =
|
|
5357
|
-
const sortedAnnotations =
|
|
5521
|
+
const annotationSmoothRestoreTimeoutRef = useRef10(null);
|
|
5522
|
+
const sortedAnnotations = useMemo13(() => {
|
|
5358
5523
|
return [...annotations].sort((a, b) => a.id - b.id);
|
|
5359
5524
|
}, [annotations]);
|
|
5360
|
-
const currentAnnotation =
|
|
5525
|
+
const currentAnnotation = useMemo13(() => {
|
|
5361
5526
|
if (currentAnnotationIndex < 0 || currentAnnotationIndex >= sortedAnnotations.length) {
|
|
5362
5527
|
return null;
|
|
5363
5528
|
}
|
|
@@ -5378,7 +5543,7 @@ function ModelViewer({
|
|
|
5378
5543
|
const controls = controlsRef.current;
|
|
5379
5544
|
const scene = sceneRef.current;
|
|
5380
5545
|
if (!controls) return;
|
|
5381
|
-
const target = new
|
|
5546
|
+
const target = new Vector37();
|
|
5382
5547
|
const livePosition = annotationWorldPositionsRef.current.get(
|
|
5383
5548
|
annotation.id
|
|
5384
5549
|
);
|
|
@@ -5396,15 +5561,15 @@ function ModelViewer({
|
|
|
5396
5561
|
controls.smoothTime = 0.6;
|
|
5397
5562
|
const storedNormal = annotation.worldNormal;
|
|
5398
5563
|
if (scene) {
|
|
5399
|
-
const box = new
|
|
5400
|
-
const modelCenter = new
|
|
5564
|
+
const box = new Box33().setFromObject(scene);
|
|
5565
|
+
const modelCenter = new Vector37();
|
|
5401
5566
|
box.getCenter(modelCenter);
|
|
5402
|
-
const modelSize = new
|
|
5567
|
+
const modelSize = new Vector37();
|
|
5403
5568
|
box.getSize(modelSize);
|
|
5404
5569
|
const modelRadius = modelSize.length() * 0.2;
|
|
5405
5570
|
let outwardDir;
|
|
5406
5571
|
if (storedNormal) {
|
|
5407
|
-
outwardDir = new
|
|
5572
|
+
outwardDir = new Vector37(
|
|
5408
5573
|
storedNormal[0],
|
|
5409
5574
|
storedNormal[1],
|
|
5410
5575
|
storedNormal[2]
|
|
@@ -5502,23 +5667,23 @@ function ModelViewer({
|
|
|
5502
5667
|
);
|
|
5503
5668
|
}
|
|
5504
5669
|
}, [setActiveAnnotation]);
|
|
5505
|
-
|
|
5670
|
+
useEffect14(() => {
|
|
5506
5671
|
setCurrentAnnotationIndex(-1);
|
|
5507
5672
|
setIsGuidedTourActive(false);
|
|
5508
5673
|
}, [sortedAnnotations.length]);
|
|
5509
|
-
|
|
5674
|
+
useEffect14(() => {
|
|
5510
5675
|
return () => {
|
|
5511
5676
|
if (annotationSmoothRestoreTimeoutRef.current) {
|
|
5512
5677
|
clearTimeout(annotationSmoothRestoreTimeoutRef.current);
|
|
5513
5678
|
}
|
|
5514
5679
|
};
|
|
5515
5680
|
}, []);
|
|
5516
|
-
|
|
5681
|
+
useEffect14(() => {
|
|
5517
5682
|
if (currentAnnotation && isGuidedTourActive) {
|
|
5518
5683
|
setActiveAnnotation(currentAnnotation);
|
|
5519
5684
|
}
|
|
5520
5685
|
}, [currentAnnotation, setActiveAnnotation, isGuidedTourActive]);
|
|
5521
|
-
|
|
5686
|
+
useEffect14(() => {
|
|
5522
5687
|
if (sceneConfig?.annotations === void 0) {
|
|
5523
5688
|
return;
|
|
5524
5689
|
}
|
|
@@ -5542,7 +5707,7 @@ function ModelViewer({
|
|
|
5542
5707
|
setInternalViewingAnnotation(null);
|
|
5543
5708
|
}, [controlledActiveAnnotation, onActiveAnnotationChange]);
|
|
5544
5709
|
const activeAnnotation = controlledActiveAnnotation !== void 0 ? controlledActiveAnnotation : internalViewingAnnotation?.annotation ?? null;
|
|
5545
|
-
const activeViewingAnnotation =
|
|
5710
|
+
const activeViewingAnnotation = useMemo13(() => {
|
|
5546
5711
|
if (!activeAnnotation) {
|
|
5547
5712
|
return null;
|
|
5548
5713
|
}
|
|
@@ -5551,7 +5716,7 @@ function ModelViewer({
|
|
|
5551
5716
|
);
|
|
5552
5717
|
return annotation ?? null;
|
|
5553
5718
|
}, [activeAnnotation, annotations]);
|
|
5554
|
-
|
|
5719
|
+
useEffect14(() => {
|
|
5555
5720
|
if (activeAnnotation && !activeViewingAnnotation) {
|
|
5556
5721
|
closeViewingAnnotation();
|
|
5557
5722
|
}
|
|
@@ -5559,7 +5724,7 @@ function ModelViewer({
|
|
|
5559
5724
|
const isControlledBindings = onObjectBindingsChange !== void 0;
|
|
5560
5725
|
const currentObjectBindings = isControlledBindings ? objectBindings : internalObjectBindings;
|
|
5561
5726
|
currentObjectBindingsRef.current = currentObjectBindings;
|
|
5562
|
-
const currentHiddenObjects =
|
|
5727
|
+
const currentHiddenObjects = useMemo13(
|
|
5563
5728
|
() => Object.fromEntries(
|
|
5564
5729
|
Object.entries(currentObjectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
|
|
5565
5730
|
),
|
|
@@ -5599,12 +5764,12 @@ function ModelViewer({
|
|
|
5599
5764
|
);
|
|
5600
5765
|
const isPanelOpen = showObjectBindingDataPanel && currentSelectedObject !== null && currentSelectedObjectKey !== null;
|
|
5601
5766
|
const currentPanelObject = (currentSelectedObjectKey ? currentObjectBindings[currentSelectedObjectKey] : null) ?? currentSelectedObject;
|
|
5602
|
-
|
|
5767
|
+
useEffect14(() => {
|
|
5603
5768
|
if (!isControlledBindings) {
|
|
5604
5769
|
setInternalObjectBindings(objectBindings);
|
|
5605
5770
|
}
|
|
5606
5771
|
}, [isControlledBindings, objectBindings]);
|
|
5607
|
-
|
|
5772
|
+
useEffect14(() => {
|
|
5608
5773
|
onHiddenObjectsChange?.(currentHiddenObjects);
|
|
5609
5774
|
}, [currentHiddenObjects, onHiddenObjectsChange]);
|
|
5610
5775
|
const applySelection = useCallback4(
|
|
@@ -5711,8 +5876,8 @@ function ModelViewer({
|
|
|
5711
5876
|
};
|
|
5712
5877
|
if (canvasContainer && cameraControls && object) {
|
|
5713
5878
|
const canvasRect = canvasContainer.getBoundingClientRect();
|
|
5714
|
-
const objectCenter = new
|
|
5715
|
-
new
|
|
5879
|
+
const objectCenter = new Vector37();
|
|
5880
|
+
new Box33().setFromObject(object).getCenter(objectCenter);
|
|
5716
5881
|
objectCenter.project(cameraControls);
|
|
5717
5882
|
const projectedX = (objectCenter.x + 1) / 2 * canvasRect.width + (canvasRect.left - rect.left);
|
|
5718
5883
|
const projectedY = (1 - objectCenter.y) / 2 * canvasRect.height + (canvasRect.top - rect.top);
|
|
@@ -5797,24 +5962,24 @@ function ModelViewer({
|
|
|
5797
5962
|
resolveObjectKey
|
|
5798
5963
|
]
|
|
5799
5964
|
);
|
|
5800
|
-
|
|
5965
|
+
useEffect14(() => {
|
|
5801
5966
|
if (!colorPicker) return;
|
|
5802
5967
|
window.addEventListener("pointerdown", closeColorPicker);
|
|
5803
5968
|
return () => window.removeEventListener("pointerdown", closeColorPicker);
|
|
5804
5969
|
}, [colorPicker, closeColorPicker]);
|
|
5805
|
-
|
|
5970
|
+
useEffect14(() => {
|
|
5806
5971
|
if (!materialPopup) return;
|
|
5807
5972
|
const close = () => setMaterialPopup(null);
|
|
5808
5973
|
window.addEventListener("pointerdown", close);
|
|
5809
5974
|
return () => window.removeEventListener("pointerdown", close);
|
|
5810
5975
|
}, [materialPopup]);
|
|
5811
|
-
|
|
5976
|
+
useEffect14(() => {
|
|
5812
5977
|
if (!downloadMenuOpen) return;
|
|
5813
5978
|
const close = () => setDownloadMenuOpen(false);
|
|
5814
5979
|
window.addEventListener("pointerdown", close);
|
|
5815
5980
|
return () => window.removeEventListener("pointerdown", close);
|
|
5816
5981
|
}, [downloadMenuOpen]);
|
|
5817
|
-
|
|
5982
|
+
useEffect14(() => {
|
|
5818
5983
|
if (!activeViewingAnnotation) return;
|
|
5819
5984
|
const handleClickOutside = (event) => {
|
|
5820
5985
|
if (viewAnnotationPopupRef.current && !viewAnnotationPopupRef.current.contains(event.target)) {
|
|
@@ -5832,7 +5997,7 @@ function ModelViewer({
|
|
|
5832
5997
|
document.removeEventListener("touchstart", handleClickOutside);
|
|
5833
5998
|
};
|
|
5834
5999
|
}, [activeViewingAnnotation, closeViewingAnnotation]);
|
|
5835
|
-
|
|
6000
|
+
useEffect14(() => {
|
|
5836
6001
|
if (!pendingAnnotation) return;
|
|
5837
6002
|
const handleClickOutside = (event) => {
|
|
5838
6003
|
const target = event.target;
|
|
@@ -5913,7 +6078,7 @@ function ModelViewer({
|
|
|
5913
6078
|
delete createdBlobUrls.current[targetKey];
|
|
5914
6079
|
}
|
|
5915
6080
|
};
|
|
5916
|
-
const onViewerReadyRef =
|
|
6081
|
+
const onViewerReadyRef = useRef10(onViewerReady);
|
|
5917
6082
|
onViewerReadyRef.current = onViewerReady;
|
|
5918
6083
|
const notifyViewerReady = useCallback4(() => {
|
|
5919
6084
|
const controls = controlsRef.current;
|
|
@@ -5938,14 +6103,14 @@ function ModelViewer({
|
|
|
5938
6103
|
}
|
|
5939
6104
|
try {
|
|
5940
6105
|
if (flyIn) {
|
|
5941
|
-
const target = controls.getTarget(new
|
|
5942
|
-
const position = controls.getPosition(new
|
|
6106
|
+
const target = controls.getTarget(new Vector37());
|
|
6107
|
+
const position = controls.getPosition(new Vector37());
|
|
5943
6108
|
const direction = position.clone().sub(target);
|
|
5944
6109
|
if (direction.lengthSq() < 1e-6) {
|
|
5945
6110
|
direction.set(0, 0, 1);
|
|
5946
6111
|
}
|
|
5947
6112
|
direction.normalize();
|
|
5948
|
-
const sphere = new
|
|
6113
|
+
const sphere = new Box33().setFromObject(scene).getBoundingSphere(new Sphere2());
|
|
5949
6114
|
const farDistance = Math.max(sphere.radius * 10, 20);
|
|
5950
6115
|
const farPosition = target.clone().add(direction.multiplyScalar(farDistance));
|
|
5951
6116
|
await controls.setLookAt(
|
|
@@ -5979,7 +6144,7 @@ function ModelViewer({
|
|
|
5979
6144
|
},
|
|
5980
6145
|
[captureInitialCameraState, notifyViewerReady]
|
|
5981
6146
|
);
|
|
5982
|
-
|
|
6147
|
+
useEffect14(() => {
|
|
5983
6148
|
fitAttemptedRef.current = false;
|
|
5984
6149
|
fitInFlightRef.current = false;
|
|
5985
6150
|
initialCameraStateRef.current = null;
|
|
@@ -6051,7 +6216,7 @@ function ModelViewer({
|
|
|
6051
6216
|
notifyViewerReady,
|
|
6052
6217
|
onAutoFit
|
|
6053
6218
|
]);
|
|
6054
|
-
|
|
6219
|
+
useEffect14(() => {
|
|
6055
6220
|
if (!refitOnResize) {
|
|
6056
6221
|
return;
|
|
6057
6222
|
}
|
|
@@ -6082,6 +6247,7 @@ function ModelViewer({
|
|
|
6082
6247
|
const handleNodeClick = useCallback4(
|
|
6083
6248
|
(binding) => {
|
|
6084
6249
|
if (measureModeRef.current) return;
|
|
6250
|
+
setCinematicPlaying(false);
|
|
6085
6251
|
closeColorPicker();
|
|
6086
6252
|
setMaterialPopup(null);
|
|
6087
6253
|
setCurrentAction(null);
|
|
@@ -6159,7 +6325,7 @@ function ModelViewer({
|
|
|
6159
6325
|
},
|
|
6160
6326
|
[applySelection, resolveBindingKey2, disableZoom]
|
|
6161
6327
|
);
|
|
6162
|
-
|
|
6328
|
+
useEffect14(() => {
|
|
6163
6329
|
notifyViewerReady();
|
|
6164
6330
|
}, [notifyViewerReady]);
|
|
6165
6331
|
const handleCameraChangeInternal = useCallback4(
|
|
@@ -6190,7 +6356,7 @@ function ModelViewer({
|
|
|
6190
6356
|
},
|
|
6191
6357
|
[onLoadError]
|
|
6192
6358
|
);
|
|
6193
|
-
|
|
6359
|
+
useEffect14(() => {
|
|
6194
6360
|
const scene = sceneRef.current;
|
|
6195
6361
|
if (!modelLoaded || !scene) {
|
|
6196
6362
|
setBindingsMismatch(false);
|
|
@@ -6251,7 +6417,7 @@ function ModelViewer({
|
|
|
6251
6417
|
},
|
|
6252
6418
|
[]
|
|
6253
6419
|
);
|
|
6254
|
-
|
|
6420
|
+
useEffect14(() => {
|
|
6255
6421
|
setMeasurePoints([]);
|
|
6256
6422
|
}, [modelUrl, resolvedSceneConfig.model.rotation]);
|
|
6257
6423
|
const handleDownloadMesh = useCallback4(() => {
|
|
@@ -6560,6 +6726,17 @@ function ModelViewer({
|
|
|
6560
6726
|
offsetRef: tourCameraOffsetRef
|
|
6561
6727
|
}
|
|
6562
6728
|
),
|
|
6729
|
+
cinematicConfig && /* @__PURE__ */ jsx19(
|
|
6730
|
+
CinematicCamera,
|
|
6731
|
+
{
|
|
6732
|
+
controlsRef,
|
|
6733
|
+
sceneRef,
|
|
6734
|
+
config: cinematicConfig,
|
|
6735
|
+
playing: cinematicPlaying,
|
|
6736
|
+
onFinished: () => setCinematicPlaying(false),
|
|
6737
|
+
onUserInteract: () => setCinematicPlaying(false)
|
|
6738
|
+
}
|
|
6739
|
+
),
|
|
6563
6740
|
showMeasureTools && /* @__PURE__ */ jsx19(
|
|
6564
6741
|
MeasurementLayer,
|
|
6565
6742
|
{
|
|
@@ -6665,6 +6842,26 @@ function ModelViewer({
|
|
|
6665
6842
|
]
|
|
6666
6843
|
}
|
|
6667
6844
|
),
|
|
6845
|
+
cinematicConfig && /* @__PURE__ */ jsx19(
|
|
6846
|
+
"div",
|
|
6847
|
+
{
|
|
6848
|
+
className: "ri-cinematicTools",
|
|
6849
|
+
style: {
|
|
6850
|
+
top: `${16 + ((showMeasureTools ? 1 : 0) + (showExplodeControls ? 1 : 0)) * 42}px`
|
|
6851
|
+
},
|
|
6852
|
+
children: /* @__PURE__ */ jsx19(
|
|
6853
|
+
"button",
|
|
6854
|
+
{
|
|
6855
|
+
type: "button",
|
|
6856
|
+
className: `ri-measureButton ${cinematicPlaying ? "ri-measureButton--active" : ""}`,
|
|
6857
|
+
onClick: () => setCinematicPlaying((prev) => !prev),
|
|
6858
|
+
title: cinematicPlaying ? "Pause cinematic" : "Play cinematic",
|
|
6859
|
+
"aria-pressed": cinematicPlaying,
|
|
6860
|
+
children: cinematicPlaying ? /* @__PURE__ */ jsx19(Pause, { size: 16 }) : /* @__PURE__ */ jsx19(Play, { size: 16 })
|
|
6861
|
+
}
|
|
6862
|
+
)
|
|
6863
|
+
}
|
|
6864
|
+
),
|
|
6668
6865
|
enableXR && (xrStore && (arSupported || vrSupported) || quickLookSupported) && /* @__PURE__ */ jsxs15("div", { className: "ri-xrTools", children: [
|
|
6669
6866
|
!arSupported && quickLookSupported && /* @__PURE__ */ jsxs15(
|
|
6670
6867
|
"button",
|
|
@@ -6950,7 +7147,7 @@ function ModelViewer({
|
|
|
6950
7147
|
}
|
|
6951
7148
|
|
|
6952
7149
|
// src/components/BindingBuilder/BindingBuilder.tsx
|
|
6953
|
-
import { useCallback as useCallback8, useEffect as
|
|
7150
|
+
import { useCallback as useCallback8, useEffect as useEffect19, useMemo as useMemo16, useRef as useRef17, useState as useState24 } from "react";
|
|
6954
7151
|
import {
|
|
6955
7152
|
Redo2,
|
|
6956
7153
|
Undo2,
|
|
@@ -6961,11 +7158,11 @@ import {
|
|
|
6961
7158
|
} from "lucide-react";
|
|
6962
7159
|
|
|
6963
7160
|
// src/styles/BindingBuilder.css
|
|
6964
|
-
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:\n linear-gradient(\n 135deg,\n #f59e0b,\n #fb7185);\n border-color: transparent;\n color: #111827;\n}\n.bb-button--primary:hover {\n background:\n linear-gradient(\n 135deg,\n #fbbf24,\n #fb7185);\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:\n linear-gradient(\n 135deg,\n #f59e0b,\n #fb7185);\n color: #111827;\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(251, 113, 133, 0.24);\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:\n linear-gradient(\n 135deg,\n #135bc0,\n #2c6e9a);\n border-color: transparent;\n color: #ffffff;\n}\n.bb-button--primary:hover:not(:disabled) {\n background:\n linear-gradient(\n 135deg,\n #135bc0,\n #2c6e9a);\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 #f59e0b;\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:\n linear-gradient(\n 135deg,\n #f59e0b,\n #fb7185);\n color: #111827;\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:\n linear-gradient(\n 135deg,\n #f59e0b,\n #fb7185);\n border-color: transparent;\n color: #111827;\n}\n.bb-button--primary:hover {\n background:\n linear-gradient(\n 135deg,\n #fbbf24,\n #fb7185);\n}\n.bb-annotationCard {\n background: rgba(15, 23, 42, 0.44);\n border-left: 4px solid #f59e0b;\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:\n linear-gradient(\n 135deg,\n #f59e0b,\n #fb7185);\n color: #111827;\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');
|
|
7161
|
+
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');
|
|
6965
7162
|
|
|
6966
7163
|
// src/hooks/useViewerCamera.ts
|
|
6967
|
-
import { useCallback as useCallback5, useRef as
|
|
6968
|
-
import { Vector3 as
|
|
7164
|
+
import { useCallback as useCallback5, useRef as useRef11, useState as useState11 } from "react";
|
|
7165
|
+
import { Vector3 as Vector38 } from "three";
|
|
6969
7166
|
|
|
6970
7167
|
// src/utils/resolveBindingKey.ts
|
|
6971
7168
|
function resolveBindingKey(objectBindings, objectId) {
|
|
@@ -6989,8 +7186,8 @@ function toTuple(vector) {
|
|
|
6989
7186
|
return [vector.x, vector.y, vector.z];
|
|
6990
7187
|
}
|
|
6991
7188
|
function buildCameraState(camera, controls) {
|
|
6992
|
-
const position = new
|
|
6993
|
-
const target = new
|
|
7189
|
+
const position = new Vector38();
|
|
7190
|
+
const target = new Vector38();
|
|
6994
7191
|
controls.getPosition(position);
|
|
6995
7192
|
controls.getTarget(target);
|
|
6996
7193
|
return {
|
|
@@ -7016,7 +7213,7 @@ async function runSmoothCameraTransition2(controls, action, smoothTime = 0.6) {
|
|
|
7016
7213
|
}
|
|
7017
7214
|
}
|
|
7018
7215
|
function useViewerCamera() {
|
|
7019
|
-
const viewerRef =
|
|
7216
|
+
const viewerRef = useRef11(null);
|
|
7020
7217
|
const [cameraState, setCameraStateValue] = useState11(
|
|
7021
7218
|
null
|
|
7022
7219
|
);
|
|
@@ -7031,8 +7228,8 @@ function useViewerCamera() {
|
|
|
7031
7228
|
},
|
|
7032
7229
|
[updateCameraState]
|
|
7033
7230
|
);
|
|
7034
|
-
const homeCameraStateRef =
|
|
7035
|
-
const homeSceneRef =
|
|
7231
|
+
const homeCameraStateRef = useRef11(null);
|
|
7232
|
+
const homeSceneRef = useRef11(null);
|
|
7036
7233
|
const handleViewerReady = useCallback5((viewer) => {
|
|
7037
7234
|
viewerRef.current = viewer;
|
|
7038
7235
|
updateCameraState(buildCameraState(viewer.controls.camera, viewer.controls));
|
|
@@ -7202,18 +7399,18 @@ function useViewerCamera() {
|
|
|
7202
7399
|
}
|
|
7203
7400
|
|
|
7204
7401
|
// src/hooks/useViewerAnimations.ts
|
|
7205
|
-
import { useCallback as useCallback6, useEffect as
|
|
7402
|
+
import { useCallback as useCallback6, useEffect as useEffect15, useRef as useRef12, useState as useState12 } from "react";
|
|
7206
7403
|
function useViewerAnimations() {
|
|
7207
|
-
const controlsRef =
|
|
7404
|
+
const controlsRef = useRef12(null);
|
|
7208
7405
|
const [clips, setClips] = useState12([]);
|
|
7209
|
-
const unsubscribeRef =
|
|
7406
|
+
const unsubscribeRef = useRef12(null);
|
|
7210
7407
|
const [clipDetails, setClipDetails] = useState12([]);
|
|
7211
7408
|
const [currentClip, setCurrentClip] = useState12(null);
|
|
7212
7409
|
const [isPlaying, setIsPlaying] = useState12(false);
|
|
7213
7410
|
const [speed, setSpeedState] = useState12(1);
|
|
7214
7411
|
const [time, setTime] = useState12(0);
|
|
7215
7412
|
const [duration, setDuration] = useState12(0);
|
|
7216
|
-
|
|
7413
|
+
useEffect15(
|
|
7217
7414
|
() => () => {
|
|
7218
7415
|
unsubscribeRef.current?.();
|
|
7219
7416
|
},
|
|
@@ -7475,7 +7672,7 @@ function inferMimeType(path) {
|
|
|
7475
7672
|
}
|
|
7476
7673
|
|
|
7477
7674
|
// src/components/BindingBuilder/SceneSettings/SceneSettingsTab.tsx
|
|
7478
|
-
import { useState as useState19, useEffect as
|
|
7675
|
+
import { useState as useState19, useEffect as useEffect17 } from "react";
|
|
7479
7676
|
|
|
7480
7677
|
// src/components/BindingBuilder/SceneSettings/GeneralSceneSettings.tsx
|
|
7481
7678
|
import { useState as useState14 } from "react";
|
|
@@ -7488,7 +7685,7 @@ import {
|
|
|
7488
7685
|
import { HexColorInput as HexColorInput2, HexColorPicker as HexColorPicker3 } from "react-colorful";
|
|
7489
7686
|
|
|
7490
7687
|
// src/components/BindingBuilder/SceneSettings/SceneControls.tsx
|
|
7491
|
-
import { useLayoutEffect as useLayoutEffect2, useRef as
|
|
7688
|
+
import { useLayoutEffect as useLayoutEffect2, useRef as useRef13, useState as useState13 } from "react";
|
|
7492
7689
|
import { createPortal as createPortal2 } from "react-dom";
|
|
7493
7690
|
import { ChevronDown, Info } from "lucide-react";
|
|
7494
7691
|
import { HexColorInput, HexColorPicker as HexColorPicker2 } from "react-colorful";
|
|
@@ -7503,9 +7700,9 @@ function getContrastTextColor(hex) {
|
|
|
7503
7700
|
}
|
|
7504
7701
|
|
|
7505
7702
|
// src/hooks/useClickOutside.ts
|
|
7506
|
-
import { useEffect as
|
|
7703
|
+
import { useEffect as useEffect16 } from "react";
|
|
7507
7704
|
function useClickOutside(refs, onOutside, active) {
|
|
7508
|
-
|
|
7705
|
+
useEffect16(() => {
|
|
7509
7706
|
if (!active) return;
|
|
7510
7707
|
const refList = Array.isArray(refs) ? refs : [refs];
|
|
7511
7708
|
const handlePointerDown = (event) => {
|
|
@@ -7529,7 +7726,7 @@ function ColorPopover({
|
|
|
7529
7726
|
onClose,
|
|
7530
7727
|
children
|
|
7531
7728
|
}) {
|
|
7532
|
-
const popoverRef =
|
|
7729
|
+
const popoverRef = useRef13(null);
|
|
7533
7730
|
const [position, setPosition] = useState13(null);
|
|
7534
7731
|
useClickOutside([popoverRef, anchorRef], onClose, true);
|
|
7535
7732
|
useLayoutEffect2(() => {
|
|
@@ -7725,7 +7922,7 @@ function ColorRow({
|
|
|
7725
7922
|
onChange
|
|
7726
7923
|
}) {
|
|
7727
7924
|
const [open, setOpen] = useState13(false);
|
|
7728
|
-
const anchorRef =
|
|
7925
|
+
const anchorRef = useRef13(null);
|
|
7729
7926
|
return /* @__PURE__ */ jsxs16("div", { className: "se-field", children: [
|
|
7730
7927
|
/* @__PURE__ */ jsxs16("div", { className: "se-fieldInline", children: [
|
|
7731
7928
|
/* @__PURE__ */ jsx20("span", { className: "se-fieldLabel", children: label }),
|
|
@@ -10582,8 +10779,8 @@ function LightingTab({ config, onChange }) {
|
|
|
10582
10779
|
}
|
|
10583
10780
|
|
|
10584
10781
|
// src/components/BindingBuilder/SceneSettings/AnimationsTab.tsx
|
|
10585
|
-
import { useMemo as
|
|
10586
|
-
import { ChevronDown as ChevronDown3, Pause, Play, Square as Square2, Trash2 as Trash22 } from "lucide-react";
|
|
10782
|
+
import { useMemo as useMemo14, useState as useState17 } from "react";
|
|
10783
|
+
import { ChevronDown as ChevronDown3, Pause as Pause2, Play as Play2, Square as Square2, Trash2 as Trash22 } from "lucide-react";
|
|
10587
10784
|
import { jsx as jsx29, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
10588
10785
|
function formatDuration(duration) {
|
|
10589
10786
|
if (!Number.isFinite(duration) || duration <= 0) return "Unknown duration";
|
|
@@ -10618,7 +10815,7 @@ function AnimationsTab({
|
|
|
10618
10815
|
seek
|
|
10619
10816
|
}) {
|
|
10620
10817
|
const [showRemoved, setShowRemoved] = useState17(false);
|
|
10621
|
-
const durationBySourceName =
|
|
10818
|
+
const durationBySourceName = useMemo14(
|
|
10622
10819
|
() => new Map(
|
|
10623
10820
|
clipDetails.map((clipDetail) => [
|
|
10624
10821
|
clipDetail.sourceName,
|
|
@@ -10759,7 +10956,7 @@ function AnimationsTab({
|
|
|
10759
10956
|
onClick: () => isActivePlaying ? pause() : play(clip.sourceName),
|
|
10760
10957
|
"aria-label": isActivePlaying ? "Pause" : "Play",
|
|
10761
10958
|
title: isActivePlaying ? "Pause" : "Play",
|
|
10762
|
-
children: isActivePlaying ? /* @__PURE__ */ jsx29(
|
|
10959
|
+
children: isActivePlaying ? /* @__PURE__ */ jsx29(Pause2, { size: 16 }) : /* @__PURE__ */ jsx29(Play2, { size: 16 })
|
|
10763
10960
|
}
|
|
10764
10961
|
);
|
|
10765
10962
|
})(),
|
|
@@ -11081,8 +11278,161 @@ function AnnotationsTab({ config, onChange }) {
|
|
|
11081
11278
|
] });
|
|
11082
11279
|
}
|
|
11083
11280
|
|
|
11281
|
+
// src/components/BindingBuilder/SceneSettings/CinematicTab.tsx
|
|
11282
|
+
import { ArrowDown, ArrowUp, MapPin as MapPin2, Plus as Plus2, Trash2 as Trash24 } from "lucide-react";
|
|
11283
|
+
import { jsx as jsx31, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
11284
|
+
var DEFAULT_CINEMATIC = {
|
|
11285
|
+
enabled: false,
|
|
11286
|
+
waypoints: [],
|
|
11287
|
+
loop: true,
|
|
11288
|
+
autoPlay: false
|
|
11289
|
+
};
|
|
11290
|
+
function CinematicTab({
|
|
11291
|
+
config,
|
|
11292
|
+
onChange,
|
|
11293
|
+
cameraState,
|
|
11294
|
+
onPreviewWaypoint
|
|
11295
|
+
}) {
|
|
11296
|
+
const cinematic = config.cinematic ?? DEFAULT_CINEMATIC;
|
|
11297
|
+
const { waypoints } = cinematic;
|
|
11298
|
+
const update = (patch) => {
|
|
11299
|
+
onChange({ ...config, cinematic: { ...cinematic, ...patch } });
|
|
11300
|
+
};
|
|
11301
|
+
const addWaypoint = () => {
|
|
11302
|
+
if (!cameraState) return;
|
|
11303
|
+
update({
|
|
11304
|
+
waypoints: [
|
|
11305
|
+
...waypoints,
|
|
11306
|
+
{
|
|
11307
|
+
position: [...cameraState.position],
|
|
11308
|
+
target: [...cameraState.target]
|
|
11309
|
+
}
|
|
11310
|
+
]
|
|
11311
|
+
});
|
|
11312
|
+
};
|
|
11313
|
+
const deleteWaypoint = (index) => {
|
|
11314
|
+
update({ waypoints: waypoints.filter((_, i) => i !== index) });
|
|
11315
|
+
};
|
|
11316
|
+
const moveWaypoint = (index, delta) => {
|
|
11317
|
+
const next = index + delta;
|
|
11318
|
+
if (next < 0 || next >= waypoints.length) return;
|
|
11319
|
+
const reordered = [...waypoints];
|
|
11320
|
+
[reordered[index], reordered[next]] = [reordered[next], reordered[index]];
|
|
11321
|
+
update({ waypoints: reordered });
|
|
11322
|
+
};
|
|
11323
|
+
return /* @__PURE__ */ jsxs27(Section, { title: "Cinematic Camera", children: [
|
|
11324
|
+
/* @__PURE__ */ jsx31("p", { className: "se-cinematicHint", children: "Frame the model in the viewer, then capture it as a waypoint. The camera glides through the waypoints in order \u2014 enable and press play in the viewer to preview." }),
|
|
11325
|
+
/* @__PURE__ */ jsxs27(
|
|
11326
|
+
"button",
|
|
11327
|
+
{
|
|
11328
|
+
type: "button",
|
|
11329
|
+
className: "bb-button bb-button--primary se-cinematicAdd",
|
|
11330
|
+
onClick: addWaypoint,
|
|
11331
|
+
disabled: !cameraState,
|
|
11332
|
+
title: "Capture the current camera view as a waypoint",
|
|
11333
|
+
children: [
|
|
11334
|
+
/* @__PURE__ */ jsx31(Plus2, { size: 15 }),
|
|
11335
|
+
"Add waypoint from current view"
|
|
11336
|
+
]
|
|
11337
|
+
}
|
|
11338
|
+
),
|
|
11339
|
+
waypoints.length === 0 ? /* @__PURE__ */ jsx31("div", { className: "bb-emptyAnnotations", children: "No waypoints yet. Position the camera and add your first one." }) : /* @__PURE__ */ jsx31("div", { className: "se-waypointList", children: waypoints.map((waypoint, index) => /* @__PURE__ */ jsxs27("div", { className: "se-waypointCard", children: [
|
|
11340
|
+
/* @__PURE__ */ jsx31("div", { className: "se-waypointBadge", children: index + 1 }),
|
|
11341
|
+
/* @__PURE__ */ jsxs27("div", { className: "se-waypointBody", children: [
|
|
11342
|
+
/* @__PURE__ */ jsxs27("div", { className: "se-waypointCoords", children: [
|
|
11343
|
+
"pos [",
|
|
11344
|
+
waypoint.position.map((n) => n.toFixed(1)).join(", "),
|
|
11345
|
+
"]"
|
|
11346
|
+
] }),
|
|
11347
|
+
/* @__PURE__ */ jsxs27("div", { className: "se-waypointCoords", children: [
|
|
11348
|
+
"look [",
|
|
11349
|
+
waypoint.target.map((n) => n.toFixed(1)).join(", "),
|
|
11350
|
+
"]"
|
|
11351
|
+
] })
|
|
11352
|
+
] }),
|
|
11353
|
+
/* @__PURE__ */ jsxs27("div", { className: "se-waypointActions", children: [
|
|
11354
|
+
/* @__PURE__ */ jsx31(
|
|
11355
|
+
"button",
|
|
11356
|
+
{
|
|
11357
|
+
className: "se-waypointAction",
|
|
11358
|
+
onClick: () => onPreviewWaypoint?.(waypoint),
|
|
11359
|
+
title: "Fly camera to this waypoint",
|
|
11360
|
+
disabled: !onPreviewWaypoint,
|
|
11361
|
+
children: /* @__PURE__ */ jsx31(MapPin2, { size: 15 })
|
|
11362
|
+
}
|
|
11363
|
+
),
|
|
11364
|
+
/* @__PURE__ */ jsx31(
|
|
11365
|
+
"button",
|
|
11366
|
+
{
|
|
11367
|
+
className: "se-waypointAction",
|
|
11368
|
+
onClick: () => moveWaypoint(index, -1),
|
|
11369
|
+
title: "Move up",
|
|
11370
|
+
disabled: index === 0,
|
|
11371
|
+
children: /* @__PURE__ */ jsx31(ArrowUp, { size: 15 })
|
|
11372
|
+
}
|
|
11373
|
+
),
|
|
11374
|
+
/* @__PURE__ */ jsx31(
|
|
11375
|
+
"button",
|
|
11376
|
+
{
|
|
11377
|
+
className: "se-waypointAction",
|
|
11378
|
+
onClick: () => moveWaypoint(index, 1),
|
|
11379
|
+
title: "Move down",
|
|
11380
|
+
disabled: index === waypoints.length - 1,
|
|
11381
|
+
children: /* @__PURE__ */ jsx31(ArrowDown, { size: 15 })
|
|
11382
|
+
}
|
|
11383
|
+
),
|
|
11384
|
+
/* @__PURE__ */ jsx31(
|
|
11385
|
+
"button",
|
|
11386
|
+
{
|
|
11387
|
+
className: "se-waypointAction se-waypointAction--delete",
|
|
11388
|
+
onClick: () => deleteWaypoint(index),
|
|
11389
|
+
title: "Delete waypoint",
|
|
11390
|
+
children: /* @__PURE__ */ jsx31(Trash24, { size: 15 })
|
|
11391
|
+
}
|
|
11392
|
+
)
|
|
11393
|
+
] })
|
|
11394
|
+
] }, index)) }),
|
|
11395
|
+
/* @__PURE__ */ jsx31(
|
|
11396
|
+
ToggleRow,
|
|
11397
|
+
{
|
|
11398
|
+
label: "Enable cinematic camera",
|
|
11399
|
+
checked: cinematic.enabled,
|
|
11400
|
+
onChange: (enabled) => update({ enabled })
|
|
11401
|
+
}
|
|
11402
|
+
),
|
|
11403
|
+
/* @__PURE__ */ jsx31(
|
|
11404
|
+
ToggleRow,
|
|
11405
|
+
{
|
|
11406
|
+
label: "Loop",
|
|
11407
|
+
checked: cinematic.loop,
|
|
11408
|
+
onChange: (loop) => update({ loop })
|
|
11409
|
+
}
|
|
11410
|
+
),
|
|
11411
|
+
/* @__PURE__ */ jsx31(
|
|
11412
|
+
ToggleRow,
|
|
11413
|
+
{
|
|
11414
|
+
label: "Auto-play on load",
|
|
11415
|
+
checked: cinematic.autoPlay,
|
|
11416
|
+
onChange: (autoPlay) => update({ autoPlay })
|
|
11417
|
+
}
|
|
11418
|
+
),
|
|
11419
|
+
/* @__PURE__ */ jsx31(
|
|
11420
|
+
SliderRow,
|
|
11421
|
+
{
|
|
11422
|
+
label: "Duration",
|
|
11423
|
+
value: cinematic.duration ?? Math.max(waypoints.length * 3.5, 8),
|
|
11424
|
+
min: 4,
|
|
11425
|
+
max: 120,
|
|
11426
|
+
step: 1,
|
|
11427
|
+
formatValue: (v) => `${v}s`,
|
|
11428
|
+
onChange: (duration) => update({ duration })
|
|
11429
|
+
}
|
|
11430
|
+
)
|
|
11431
|
+
] });
|
|
11432
|
+
}
|
|
11433
|
+
|
|
11084
11434
|
// src/styles/SceneSettingsTab.css
|
|
11085
|
-
styleInject('.se-tabContent {\n display: flex;\n flex-direction: column;\n}\n.se-tabContent * {\n box-sizing: border-box;\n}\n.se-subTabRow {\n display: flex;\n justify-content: space-between;\n gap: 6px;\n padding-bottom: 16px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.1);\n margin-bottom: 4px;\n}\n.se-subTab {\n padding: 8px 12px;\n display: flex;\n justify-items: center;\n border-radius: 10px;\n border: 1px solid rgba(148, 163, 184, 0.18);\n background: rgba(255, 255, 255, 0.04);\n color: #b3bfd2;\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.se-subTab:hover {\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.32);\n}\n.se-subTab--active {\n background: rgba(99, 102, 241, 0.18);\n border-color: rgba(99, 102, 241, 0.4);\n color: #f8fafc;\n}\n.se-generalContent {\n display: flex;\n flex-direction: column;\n padding-top: 0;\n}\n.se-placeholder {\n font-size: 13px;\n color: #93a4c0;\n}\n.se-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 padding: 0;\n}\n.se-section:last-child {\n margin-bottom: 0;\n}\n.se-sectionTitle {\n margin: 0;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: #c3cbdb;\n padding: 11px 14px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.1);\n}\n.se-sectionBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 14px;\n}\n.se-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n}\n.se-fieldInline {\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n.se-fieldLabel {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n font-size: 12px;\n font-weight: 700;\n color: #d7dee9;\n}\n.se-fieldValue {\n font-variant-numeric: tabular-nums;\n font-size: 12px;\n color: #93a4c0;\n}\n.se-slider {\n width: 100%;\n accent-color: #2aa8d8;\n cursor: pointer;\n}\n.se-checkbox {\n width: 18px;\n height: 18px;\n accent-color: #2aa8d8;\n cursor: pointer;\n}\n.se-select,\n.se-textInput,\n.se-numberInput {\n background: rgba(15, 23, 42, 0.28);\n border: 1px solid rgba(148, 163, 184, 0.2);\n border-radius: 7px;\n color: #f8fafc;\n font-size: 13px;\n padding: 11px 13px;\n outline: none;\n}\n.se-select:focus,\n.se-textInput:focus,\n.se-numberInput:focus {\n outline: none;\n border-color: rgba(42, 168, 216, 0.65);\n box-shadow: 0 0 0 4px rgba(42, 168, 216, 0.12);\n}\n.se-select {\n min-width: 130px;\n text-transform: capitalize;\n}\n.se-textInput {\n width: 100%;\n}\n.se-numberInput {\n width: 100%;\n}\n.se-swatch {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n min-width: 96px;\n height: 30px;\n padding: 0 8px;\n border-radius: 8px;\n border: 1px solid rgba(148, 163, 184, 0.35);\n cursor: pointer;\n box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.15);\n}\n.se-swatchCode {\n font-size: 11px;\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n color: #fff;\n mix-blend-mode: difference;\n}\n.se-colorPicker {\n margin-top: 6px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n width: fit-content;\n}\n.se-colorPicker .react-colorful {\n width: 100%;\n height: 140px;\n}\n.se-inlineColorPicker .react-colorful {\n width: 100%;\n height: 240px;\n}\n.se-colorPickerPopover {\n z-index: 1000;\n padding: 10px;\n border-radius: 8px;\n background: #1b2333;\n border: 1px solid rgba(148, 163, 184, 0.25);\n box-shadow: 0 12px 28px rgba(0, 0, 0, 0.4);\n}\n.se-colorPickerPopover .react-colorful {\n width: 200px;\n height: 160px;\n}\n.se-vectorRow {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 8px;\n}\n.se-vectorAxis {\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: 11px;\n color: #93a4c0;\n}\n.se-panel {\n margin-bottom: 10px;\n overflow: hidden;\n border: 1px solid rgba(148, 163, 184, 0.12);\n border-radius: 7px;\n background: rgba(255, 255, 255, 0.03);\n}\n.se-panel:last-child {\n margin-bottom: 0;\n}\n.se-panelHeader {\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 color: #e5e7eb;\n cursor: pointer;\n text-align: left;\n}\n.se-panelHeader:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.se-panelHeaderMain {\n display: flex;\n min-width: 0;\n align-items: center;\n gap: 8px;\n}\n.se-panelChevron {\n flex-shrink: 0;\n color: #8b97ab;\n transform: rotate(-90deg);\n transition: transform 0.18s ease;\n}\n.se-panelChevron--open {\n transform: rotate(0deg);\n}\n.se-panelTitle {\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: #c3cbdb;\n}\n.se-panelHelp {\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.se-panelHelp:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #eef2ff;\n transform: translateY(-0.5px);\n}\n.se-panelTrailing {\n display: flex;\n align-items: center;\n}\n.se-panelBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 14px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n background: transparent;\n}\n.se-iconButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n border: none;\n border-radius: 8px;\n background: rgba(255, 255, 255, 0.08);\n color: #d7dde8;\n cursor: pointer;\n}\n.se-iconButton:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n.se-axisStepperGrid {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 12px;\n}\n.se-axisStepper {\n display: grid;\n grid-template-columns: 40px 1fr 40px;\n align-items: center;\n min-height: 30px;\n border-radius: 10px;\n background: rgba(17, 24, 39, 0.62);\n border: 1px solid rgba(255, 255, 255, 0.05);\n}\n.se-axisStepperButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n border: none;\n background: transparent;\n color: #f3f4f6;\n cursor: pointer;\n}\n.se-axisStepperButton:hover {\n background: rgba(255, 255, 255, 0.04);\n}\n.se-axisStepperLabel {\n text-align: center;\n font-size: 15px;\n color: #f3f4f6;\n}\n.se-segmentedControl {\n display: grid;\n grid-auto-flow: column;\n overflow: hidden;\n border-radius: 10px;\n background: rgba(17, 24, 39, 0.62);\n}\n.se-segmentedOption {\n min-height: 35px;\n border: none;\n background: transparent;\n color: #d1d5db;\n font-size: 14px;\n text-transform: uppercase;\n cursor: pointer;\n}\n.se-segmentedOption--active {\n background: #2aa8d8;\n color: #fff;\n}\n.se-helperCard {\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.se-helperCard strong {\n font-size: 14px;\n}\n.se-helperCard span {\n font-size: 13px;\n line-height: 1.4;\n color: #c7d0df;\n}\n.se-switch {\n position: relative;\n display: inline-flex;\n align-items: center;\n}\n.se-switch input[type=checkbox] {\n position: absolute;\n left: 0;\n top: 0;\n z-index: 10;\n width: 100%;\n height: 100%;\n cursor: pointer;\n opacity: 0;\n}\n.se-switchTrack {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.se-switchTrack::before {\n content: "";\n width: 36px;\n height: 20px;\n background: #4b5563;\n display: inline-block;\n border-radius: 20px;\n transition: background-color 0.2s ease-in;\n}\n.se-switchThumb {\n position: absolute;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n left: 2px;\n top: 2px;\n z-index: 2;\n background: #fff;\n box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);\n transition: 0.2s ease-in;\n}\n.se-switch input[type=checkbox]:hover + .se-switchTrack .se-switchThumb {\n box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.2), 0 3px 8px 0 rgba(0, 0, 0, 0.1);\n}\n.se-switch input[type=checkbox]:checked + .se-switchTrack::before {\n background: #376fcb;\n}\n.se-switch input[type=checkbox]:checked + .se-switchTrack .se-switchThumb {\n background: #4285f4;\n left: 18px;\n}\n.se-switchLabel {\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 0.06em;\n color: #e5e7eb;\n}\n.se-switch input[type=checkbox]:checked ~ .se-switchTrack .se-switchLabel {\n color: #2aa8d8;\n}\n.se-lightList {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.se-lightRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid rgba(148, 163, 184, 0.1);\n background: rgba(255, 255, 255, 0.03);\n cursor: pointer;\n transition: background-color 0.15s ease, border-color 0.15s ease;\n}\n.se-lightRow:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.se-lightRow--active {\n background: rgba(42, 168, 216, 0.12);\n border-color: rgba(42, 168, 216, 0.35);\n}\n.se-lightRowLabel {\n font-size: 13px;\n color: #d8e0ee;\n}\n.se-lightActions {\n display: flex;\n gap: 8px;\n}\n.se-lightProps {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding-top: 4px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n}\n.se-animationList {\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n.se-animationCard {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 16px;\n border-radius: 14px;\n border: 1px solid rgba(148, 163, 184, 0.16);\n background: rgba(15, 23, 42, 0.48);\n}\n.se-animationCard--active {\n border-color: rgba(42, 168, 216, 0.45);\n box-shadow: inset 0 0 0 1px rgba(42, 168, 216, 0.16);\n}\n.se-animationHeader {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n}\n.se-animationHeading {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: 6px;\n}\n.se-animationTitleRow {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n}\n.se-animationStatus,\n.se-animationBadge {\n display: inline-flex;\n align-items: center;\n min-height: 22px;\n padding: 0 8px;\n border-radius: 999px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n}\n.se-animationStatus {\n background: rgba(42, 168, 216, 0.16);\n color: #9fe8ff;\n}\n.se-animationBadge {\n background: rgba(99, 102, 241, 0.18);\n color: #d9ddff;\n}\n.se-animationSource {\n font-size: 12px;\n color: #93a4c0;\n word-break: break-word;\n}\n.se-animationMeta {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n.se-animationMeta span {\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.06);\n padding: 4px 9px;\n font-size: 12px;\n color: #c6d0e2;\n}\n.se-animationDangerButton,\n.se-animationButton,\n.se-animationAutoplayButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n border: 1px solid rgba(148, 163, 184, 0.2);\n border-radius: 10px;\n background: rgba(255, 255, 255, 0.04);\n color: #e2e8f0;\n cursor: pointer;\n transition:\n background-color 0.18s ease,\n border-color 0.18s ease,\n color 0.18s ease;\n}\n.se-animationButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.se-animationDangerButton {\n width: 36px;\n height: 36px;\n color: #fca5a5;\n}\n.se-animationDangerButton:hover,\n.se-animationButton:hover,\n.se-animationAutoplayButton:hover {\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.34);\n}\n.se-animationButtonRow {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n}\n.se-animationButton {\n min-height: 36px;\n padding: 0 14px;\n font-size: 13px;\n font-weight: 600;\n}\n.se-animationButton--icon {\n flex: 1;\n min-width: 0;\n padding: 0;\n height: 38px;\n}\n.se-animationSeekRow {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.se-animationSeek {\n flex: 1;\n min-width: 0;\n}\n.se-animationSeekTime {\n flex-shrink: 0;\n min-width: 86px;\n text-align: right;\n font-variant-numeric: tabular-nums;\n font-size: 12px;\n color: #97a3b6;\n}\n.se-animationGrid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n}\n.se-animationSpeedRow {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 88px;\n gap: 10px;\n align-items: center;\n}\n.se-animationSpeedInput {\n text-align: right;\n}\n.se-animationAutoplayButton {\n align-self: flex-start;\n min-height: 36px;\n padding: 0 14px;\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n}\n.se-animationAutoplayButton--active {\n background: rgba(42, 168, 216, 0.18);\n border-color: rgba(42, 168, 216, 0.42);\n color: #dff8ff;\n}\n.se-removedAnimations {\n overflow: hidden;\n border-radius: 12px;\n border: 1px solid rgba(148, 163, 184, 0.14);\n background: rgba(255, 255, 255, 0.03);\n}\n.se-removedAnimationList {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px;\n}\n.se-removedAnimationRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border-radius: 10px;\n background: rgba(15, 23, 42, 0.42);\n padding: 12px;\n}\n.se-removedAnimationName {\n font-size: 13px;\n font-weight: 600;\n color: #e2e8f0;\n}\n@media (max-width: 900px) {\n .se-animationGrid {\n grid-template-columns: 1fr;\n }\n .se-animationSpeedRow {\n grid-template-columns: 1fr;\n }\n}\n.bb-viewSpotOverlay {\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-fadeIn 0.2s ease;\n}\n.bb-viewSpotPopup {\n background: #1e1e20;\n border-radius: 12px;\n max-width: 480px;\n width: 90%;\n max-height: 80vh;\n overflow-y: auto;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);\n border: 1px solid #333335;\n animation: bb-slideUp 0.25s ease;\n}\n.bb-viewSpotHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n border-bottom: 1px solid #2a2a2c;\n}\n.bb-viewSpotTitle {\n font-size: 18px;\n font-weight: 600;\n color: #e8e8ea;\n margin: 0;\n}\n.bb-viewSpotClose {\n background: none;\n border: none;\n color: #888;\n font-size: 24px;\n cursor: pointer;\n padding: 0 4px;\n line-height: 1;\n transition: color 0.2s;\n}\n.bb-viewSpotClose:hover {\n color: #e8e8ea;\n}\n.bb-viewSpotBody {\n padding: 24px;\n}\n.bb-viewSpotDescription {\n color: #c8c8ca;\n font-size: 14px;\n line-height: 1.6;\n margin-bottom: 20px;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.bb-viewSpotPosition {\n background: #141416;\n border-radius: 8px;\n padding: 12px 16px;\n margin-bottom: 24px;\n font-size: 13px;\n color: #a0a0a2;\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n.bb-viewSpotLabel {\n font-weight: 500;\n color: #888;\n}\n.bb-viewSpotCode {\n background: #0a0a0c;\n padding: 4px 10px;\n border-radius: 4px;\n font-family:\n "Monaco",\n "Menlo",\n monospace;\n font-size: 12px;\n color: #8be9fd;\n}\n.bb-viewSpotActions {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n padding-top: 4px;\n}\n.bb-editAnnotationOverlay {\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-editAnnotationPopup {\n background: #0f141e;\n border-radius: 16px;\n padding: 24px;\n min-width: 200px;\n max-width: 400px;\n width: 90%;\n border: 1px solid rgba(148, 163, 184, 0.18);\n}\n.bb-editAnnotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n padding-bottom: 12px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.12);\n}\n.bb-editAnnotationTitle {\n color: #f8fafc;\n font-size: 18px;\n font-weight: 600;\n margin: 0;\n}\n.bb-editAnnotationClose {\n background: none;\n border: none;\n color: #94a3b8;\n font-size: 24px;\n cursor: pointer;\n padding: 0 4px;\n}\n.bb-editAnnotationClose:hover {\n color: #f8fafc;\n}\n.bb-editAnnotationBody {\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n.bb-editAnnotationActions {\n display: flex;\n gap: 8px;\n margin-top: 8px;\n}\n');
|
|
11435
|
+
styleInject('.se-tabContent {\n display: flex;\n flex-direction: column;\n}\n.se-tabContent * {\n box-sizing: border-box;\n}\n.se-subTabRow {\n display: flex;\n justify-content: space-between;\n gap: 6px;\n padding-bottom: 16px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.1);\n margin-bottom: 4px;\n}\n.se-subTab {\n padding: 8px 12px;\n display: flex;\n justify-items: center;\n border-radius: 10px;\n border: 1px solid rgba(148, 163, 184, 0.18);\n background: rgba(255, 255, 255, 0.04);\n color: #b3bfd2;\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.se-subTab:hover {\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.32);\n}\n.se-subTab--active {\n background: rgba(99, 102, 241, 0.18);\n border-color: rgba(99, 102, 241, 0.4);\n color: #f8fafc;\n}\n.se-generalContent {\n display: flex;\n flex-direction: column;\n padding-top: 0;\n}\n.se-placeholder {\n font-size: 13px;\n color: #93a4c0;\n}\n.se-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 padding: 0;\n}\n.se-section:last-child {\n margin-bottom: 0;\n}\n.se-sectionTitle {\n margin: 0;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: #c3cbdb;\n padding: 11px 14px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.1);\n}\n.se-sectionBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 14px;\n}\n.se-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n}\n.se-fieldInline {\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n.se-fieldLabel {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n font-size: 12px;\n font-weight: 700;\n color: #d7dee9;\n}\n.se-fieldValue {\n font-variant-numeric: tabular-nums;\n font-size: 12px;\n color: #93a4c0;\n}\n.se-slider {\n width: 100%;\n accent-color: #2aa8d8;\n cursor: pointer;\n}\n.se-checkbox {\n width: 18px;\n height: 18px;\n accent-color: #2aa8d8;\n cursor: pointer;\n}\n.se-select,\n.se-textInput,\n.se-numberInput {\n background: rgba(15, 23, 42, 0.28);\n border: 1px solid rgba(148, 163, 184, 0.2);\n border-radius: 7px;\n color: #f8fafc;\n font-size: 13px;\n padding: 11px 13px;\n outline: none;\n}\n.se-select:focus,\n.se-textInput:focus,\n.se-numberInput:focus {\n outline: none;\n border-color: rgba(42, 168, 216, 0.65);\n box-shadow: 0 0 0 4px rgba(42, 168, 216, 0.12);\n}\n.se-select {\n min-width: 130px;\n text-transform: capitalize;\n}\n.se-textInput {\n width: 100%;\n}\n.se-numberInput {\n width: 100%;\n}\n.se-swatch {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n min-width: 96px;\n height: 30px;\n padding: 0 8px;\n border-radius: 8px;\n border: 1px solid rgba(148, 163, 184, 0.35);\n cursor: pointer;\n box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.15);\n}\n.se-swatchCode {\n font-size: 11px;\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n color: #fff;\n mix-blend-mode: difference;\n}\n.se-colorPicker {\n margin-top: 6px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n width: fit-content;\n}\n.se-colorPicker .react-colorful {\n width: 100%;\n height: 140px;\n}\n.se-inlineColorPicker .react-colorful {\n width: 100%;\n height: 240px;\n}\n.se-colorPickerPopover {\n z-index: 1000;\n padding: 10px;\n border-radius: 8px;\n background: #1b2333;\n border: 1px solid rgba(148, 163, 184, 0.25);\n box-shadow: 0 12px 28px rgba(0, 0, 0, 0.4);\n}\n.se-colorPickerPopover .react-colorful {\n width: 200px;\n height: 160px;\n}\n.se-vectorRow {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 8px;\n}\n.se-vectorAxis {\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: 11px;\n color: #93a4c0;\n}\n.se-panel {\n margin-bottom: 10px;\n overflow: hidden;\n border: 1px solid rgba(148, 163, 184, 0.12);\n border-radius: 7px;\n background: rgba(255, 255, 255, 0.03);\n}\n.se-panel:last-child {\n margin-bottom: 0;\n}\n.se-panelHeader {\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 color: #e5e7eb;\n cursor: pointer;\n text-align: left;\n}\n.se-panelHeader:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.se-panelHeaderMain {\n display: flex;\n min-width: 0;\n align-items: center;\n gap: 8px;\n}\n.se-panelChevron {\n flex-shrink: 0;\n color: #8b97ab;\n transform: rotate(-90deg);\n transition: transform 0.18s ease;\n}\n.se-panelChevron--open {\n transform: rotate(0deg);\n}\n.se-panelTitle {\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: #c3cbdb;\n}\n.se-panelHelp {\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.se-panelHelp:hover {\n background: rgba(255, 255, 255, 0.08);\n color: #eef2ff;\n transform: translateY(-0.5px);\n}\n.se-panelTrailing {\n display: flex;\n align-items: center;\n}\n.se-panelBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 14px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n background: transparent;\n}\n.se-iconButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n border: none;\n border-radius: 8px;\n background: rgba(255, 255, 255, 0.08);\n color: #d7dde8;\n cursor: pointer;\n}\n.se-iconButton:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n.se-axisStepperGrid {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 12px;\n}\n.se-axisStepper {\n display: grid;\n grid-template-columns: 40px 1fr 40px;\n align-items: center;\n min-height: 30px;\n border-radius: 10px;\n background: rgba(17, 24, 39, 0.62);\n border: 1px solid rgba(255, 255, 255, 0.05);\n}\n.se-axisStepperButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n border: none;\n background: transparent;\n color: #f3f4f6;\n cursor: pointer;\n}\n.se-axisStepperButton:hover {\n background: rgba(255, 255, 255, 0.04);\n}\n.se-axisStepperLabel {\n text-align: center;\n font-size: 15px;\n color: #f3f4f6;\n}\n.se-segmentedControl {\n display: grid;\n grid-auto-flow: column;\n overflow: hidden;\n border-radius: 10px;\n background: rgba(17, 24, 39, 0.62);\n}\n.se-segmentedOption {\n min-height: 35px;\n border: none;\n background: transparent;\n color: #d1d5db;\n font-size: 14px;\n text-transform: uppercase;\n cursor: pointer;\n}\n.se-segmentedOption--active {\n background: #2aa8d8;\n color: #fff;\n}\n.se-helperCard {\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.se-helperCard strong {\n font-size: 14px;\n}\n.se-helperCard span {\n font-size: 13px;\n line-height: 1.4;\n color: #c7d0df;\n}\n.se-switch {\n position: relative;\n display: inline-flex;\n align-items: center;\n}\n.se-switch input[type=checkbox] {\n position: absolute;\n left: 0;\n top: 0;\n z-index: 10;\n width: 100%;\n height: 100%;\n cursor: pointer;\n opacity: 0;\n}\n.se-switchTrack {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.se-switchTrack::before {\n content: "";\n width: 36px;\n height: 20px;\n background: #4b5563;\n display: inline-block;\n border-radius: 20px;\n transition: background-color 0.2s ease-in;\n}\n.se-switchThumb {\n position: absolute;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n left: 2px;\n top: 2px;\n z-index: 2;\n background: #fff;\n box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);\n transition: 0.2s ease-in;\n}\n.se-switch input[type=checkbox]:hover + .se-switchTrack .se-switchThumb {\n box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.2), 0 3px 8px 0 rgba(0, 0, 0, 0.1);\n}\n.se-switch input[type=checkbox]:checked + .se-switchTrack::before {\n background: #376fcb;\n}\n.se-switch input[type=checkbox]:checked + .se-switchTrack .se-switchThumb {\n background: #4285f4;\n left: 18px;\n}\n.se-switchLabel {\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 0.06em;\n color: #e5e7eb;\n}\n.se-switch input[type=checkbox]:checked ~ .se-switchTrack .se-switchLabel {\n color: #2aa8d8;\n}\n.se-lightList {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.se-lightRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid rgba(148, 163, 184, 0.1);\n background: rgba(255, 255, 255, 0.03);\n cursor: pointer;\n transition: background-color 0.15s ease, border-color 0.15s ease;\n}\n.se-lightRow:hover {\n background: rgba(255, 255, 255, 0.06);\n}\n.se-lightRow--active {\n background: rgba(42, 168, 216, 0.12);\n border-color: rgba(42, 168, 216, 0.35);\n}\n.se-lightRowLabel {\n font-size: 13px;\n color: #d8e0ee;\n}\n.se-lightActions {\n display: flex;\n gap: 8px;\n}\n.se-lightProps {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding-top: 4px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n}\n.se-animationList {\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n.se-animationCard {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 16px;\n border-radius: 14px;\n border: 1px solid rgba(148, 163, 184, 0.16);\n background: rgba(15, 23, 42, 0.48);\n}\n.se-animationCard--active {\n border-color: rgba(42, 168, 216, 0.45);\n box-shadow: inset 0 0 0 1px rgba(42, 168, 216, 0.16);\n}\n.se-animationHeader {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n}\n.se-animationHeading {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: 6px;\n}\n.se-animationTitleRow {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n}\n.se-animationStatus,\n.se-animationBadge {\n display: inline-flex;\n align-items: center;\n min-height: 22px;\n padding: 0 8px;\n border-radius: 999px;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n}\n.se-animationStatus {\n background: rgba(42, 168, 216, 0.16);\n color: #9fe8ff;\n}\n.se-animationBadge {\n background: rgba(99, 102, 241, 0.18);\n color: #d9ddff;\n}\n.se-animationSource {\n font-size: 12px;\n color: #93a4c0;\n word-break: break-word;\n}\n.se-animationMeta {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n.se-animationMeta span {\n border-radius: 999px;\n background: rgba(255, 255, 255, 0.06);\n padding: 4px 9px;\n font-size: 12px;\n color: #c6d0e2;\n}\n.se-animationDangerButton,\n.se-animationButton,\n.se-animationAutoplayButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n border: 1px solid rgba(148, 163, 184, 0.2);\n border-radius: 10px;\n background: rgba(255, 255, 255, 0.04);\n color: #e2e8f0;\n cursor: pointer;\n transition:\n background-color 0.18s ease,\n border-color 0.18s ease,\n color 0.18s ease;\n}\n.se-animationButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.se-animationDangerButton {\n width: 36px;\n height: 36px;\n color: #fca5a5;\n}\n.se-animationDangerButton:hover,\n.se-animationButton:hover,\n.se-animationAutoplayButton:hover {\n background: rgba(255, 255, 255, 0.08);\n border-color: rgba(148, 163, 184, 0.34);\n}\n.se-animationButtonRow {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n}\n.se-animationButton {\n min-height: 36px;\n padding: 0 14px;\n font-size: 13px;\n font-weight: 600;\n}\n.se-animationButton--icon {\n flex: 1;\n min-width: 0;\n padding: 0;\n height: 38px;\n}\n.se-animationSeekRow {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.se-animationSeek {\n flex: 1;\n min-width: 0;\n}\n.se-animationSeekTime {\n flex-shrink: 0;\n min-width: 86px;\n text-align: right;\n font-variant-numeric: tabular-nums;\n font-size: 12px;\n color: #97a3b6;\n}\n.se-animationGrid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n}\n.se-animationSpeedRow {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 88px;\n gap: 10px;\n align-items: center;\n}\n.se-animationSpeedInput {\n text-align: right;\n}\n.se-animationAutoplayButton {\n align-self: flex-start;\n min-height: 36px;\n padding: 0 14px;\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n}\n.se-animationAutoplayButton--active {\n background: rgba(42, 168, 216, 0.18);\n border-color: rgba(42, 168, 216, 0.42);\n color: #dff8ff;\n}\n.se-removedAnimations {\n overflow: hidden;\n border-radius: 12px;\n border: 1px solid rgba(148, 163, 184, 0.14);\n background: rgba(255, 255, 255, 0.03);\n}\n.se-removedAnimationList {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px;\n}\n.se-removedAnimationRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n border-radius: 10px;\n background: rgba(15, 23, 42, 0.42);\n padding: 12px;\n}\n.se-removedAnimationName {\n font-size: 13px;\n font-weight: 600;\n color: #e2e8f0;\n}\n@media (max-width: 900px) {\n .se-animationGrid {\n grid-template-columns: 1fr;\n }\n .se-animationSpeedRow {\n grid-template-columns: 1fr;\n }\n}\n.bb-viewSpotOverlay {\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-fadeIn 0.2s ease;\n}\n.bb-viewSpotPopup {\n background: #1e1e20;\n border-radius: 12px;\n max-width: 480px;\n width: 90%;\n max-height: 80vh;\n overflow-y: auto;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);\n border: 1px solid #333335;\n animation: bb-slideUp 0.25s ease;\n}\n.bb-viewSpotHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n border-bottom: 1px solid #2a2a2c;\n}\n.bb-viewSpotTitle {\n font-size: 18px;\n font-weight: 600;\n color: #e8e8ea;\n margin: 0;\n}\n.bb-viewSpotClose {\n background: none;\n border: none;\n color: #888;\n font-size: 24px;\n cursor: pointer;\n padding: 0 4px;\n line-height: 1;\n transition: color 0.2s;\n}\n.bb-viewSpotClose:hover {\n color: #e8e8ea;\n}\n.bb-viewSpotBody {\n padding: 24px;\n}\n.bb-viewSpotDescription {\n color: #c8c8ca;\n font-size: 14px;\n line-height: 1.6;\n margin-bottom: 20px;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.bb-viewSpotPosition {\n background: #141416;\n border-radius: 8px;\n padding: 12px 16px;\n margin-bottom: 24px;\n font-size: 13px;\n color: #a0a0a2;\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n.bb-viewSpotLabel {\n font-weight: 500;\n color: #888;\n}\n.bb-viewSpotCode {\n background: #0a0a0c;\n padding: 4px 10px;\n border-radius: 4px;\n font-family:\n "Monaco",\n "Menlo",\n monospace;\n font-size: 12px;\n color: #8be9fd;\n}\n.bb-viewSpotActions {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n padding-top: 4px;\n}\n.bb-editAnnotationOverlay {\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-editAnnotationPopup {\n background: #0f141e;\n border-radius: 16px;\n padding: 24px;\n min-width: 200px;\n max-width: 400px;\n width: 90%;\n border: 1px solid rgba(148, 163, 184, 0.18);\n}\n.bb-editAnnotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n padding-bottom: 12px;\n border-bottom: 1px solid rgba(148, 163, 184, 0.12);\n}\n.bb-editAnnotationTitle {\n color: #f8fafc;\n font-size: 18px;\n font-weight: 600;\n margin: 0;\n}\n.bb-editAnnotationClose {\n background: none;\n border: none;\n color: #94a3b8;\n font-size: 24px;\n cursor: pointer;\n padding: 0 4px;\n}\n.bb-editAnnotationClose:hover {\n color: #f8fafc;\n}\n.bb-editAnnotationBody {\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n.bb-editAnnotationActions {\n display: flex;\n gap: 8px;\n margin-top: 8px;\n}\n.se-cinematicHint {\n margin: 0 0 10px;\n font-size: 12px;\n line-height: 1.5;\n color: rgba(255, 255, 255, 0.55);\n}\n.bb-button.se-cinematicAdd {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n width: 100%;\n margin-bottom: 12px;\n background: #135bc0;\n color: white;\n}\n.bb-button.se-cinematicAdd:hover:not(:disabled) {\n background: #1a68d6;\n}\n.se-waypointList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 14px;\n}\n.se-waypointCard {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 8px;\n background: rgba(255, 255, 255, 0.03);\n}\n.se-waypointBadge {\n flex: none;\n width: 22px;\n height: 22px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 600;\n color: #06343c;\n background: #22d3ee;\n}\n.se-waypointBody {\n flex: 1;\n min-width: 0;\n}\n.se-waypointCoords {\n font-size: 11px;\n line-height: 1.4;\n color: rgba(255, 255, 255, 0.6);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.se-waypointActions {\n flex: none;\n display: flex;\n gap: 2px;\n}\n.se-waypointAction {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: rgba(255, 255, 255, 0.6);\n cursor: pointer;\n transition: background 0.15s ease, color 0.15s ease;\n}\n.se-waypointAction:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.1);\n color: #fff;\n}\n.se-waypointAction:disabled {\n opacity: 0.35;\n cursor: default;\n}\n.se-waypointAction--delete:hover:not(:disabled) {\n background: rgba(248, 113, 113, 0.16);\n color: #f87171;\n}\n.bb-cameraWaypoint {\n align-items: flex-start;\n margin-bottom: 8px;\n}\n.bb-cameraWaypoint .bb-cameraLiveBadge,\n.bb-cameraWaypoint .bb-cameraSavedBadge {\n flex: none;\n}\n.bb-cameraWaypointLabel {\n font-size: 11px;\n font-weight: 600;\n color: rgba(255, 255, 255, 0.8);\n margin-bottom: 2px;\n}\n.bb-cameraControls--centered {\n justify-content: center;\n margin: 12px 0;\n}\n');
|
|
11086
11436
|
|
|
11087
11437
|
// src/components/BindingBuilder/SceneSettings/SceneSettingsTab.tsx
|
|
11088
11438
|
import {
|
|
@@ -11090,15 +11440,17 @@ import {
|
|
|
11090
11440
|
Clapperboard,
|
|
11091
11441
|
MessageSquareText,
|
|
11092
11442
|
Sparkles,
|
|
11093
|
-
Spotlight
|
|
11443
|
+
Spotlight,
|
|
11444
|
+
Video
|
|
11094
11445
|
} from "lucide-react";
|
|
11095
|
-
import { jsx as
|
|
11446
|
+
import { jsx as jsx32, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
11096
11447
|
var SCENE_SUB_TABS = [
|
|
11097
|
-
{ label: "General", icon: /* @__PURE__ */
|
|
11098
|
-
{ label: "Lighting", icon: /* @__PURE__ */
|
|
11099
|
-
{ label: "Animations", icon: /* @__PURE__ */
|
|
11100
|
-
{ label: "Post Processing", icon: /* @__PURE__ */
|
|
11101
|
-
{ label: "Annotations", icon: /* @__PURE__ */
|
|
11448
|
+
{ label: "General", icon: /* @__PURE__ */ jsx32(Box2, {}) },
|
|
11449
|
+
{ label: "Lighting", icon: /* @__PURE__ */ jsx32(Spotlight, {}) },
|
|
11450
|
+
{ label: "Animations", icon: /* @__PURE__ */ jsx32(Clapperboard, {}) },
|
|
11451
|
+
{ label: "Post Processing", icon: /* @__PURE__ */ jsx32(Sparkles, {}) },
|
|
11452
|
+
{ label: "Annotations", icon: /* @__PURE__ */ jsx32(MessageSquareText, {}) },
|
|
11453
|
+
{ label: "Cinematic", icon: /* @__PURE__ */ jsx32(Video, {}) }
|
|
11102
11454
|
];
|
|
11103
11455
|
var FALLBACK_ANIMATION_PLAYBACK = {
|
|
11104
11456
|
currentClip: null,
|
|
@@ -11121,6 +11473,8 @@ function SceneSettingsTab({
|
|
|
11121
11473
|
onChange,
|
|
11122
11474
|
activeSubTab: controlledActiveSubTab,
|
|
11123
11475
|
onActiveSubTabChange,
|
|
11476
|
+
cameraState = null,
|
|
11477
|
+
onPreviewWaypoint,
|
|
11124
11478
|
animationClipDetails = [],
|
|
11125
11479
|
animationPlayback = FALLBACK_ANIMATION_PLAYBACK
|
|
11126
11480
|
}) {
|
|
@@ -11130,7 +11484,7 @@ function SceneSettingsTab({
|
|
|
11130
11484
|
const { tier } = useBindingBuilderContext();
|
|
11131
11485
|
const canUseStarterFeatures = hasTier(tier, "starter");
|
|
11132
11486
|
const canUseGrowthFeatures = hasTier(tier, "growth");
|
|
11133
|
-
|
|
11487
|
+
useEffect17(() => {
|
|
11134
11488
|
if (controlledActiveSubTab !== void 0) {
|
|
11135
11489
|
setInternalActiveSubTab(controlledActiveSubTab);
|
|
11136
11490
|
}
|
|
@@ -11141,8 +11495,8 @@ function SceneSettingsTab({
|
|
|
11141
11495
|
}
|
|
11142
11496
|
onActiveSubTabChange?.(subTab);
|
|
11143
11497
|
};
|
|
11144
|
-
return /* @__PURE__ */
|
|
11145
|
-
/* @__PURE__ */
|
|
11498
|
+
return /* @__PURE__ */ jsxs28("div", { className: "se-tabContent", children: [
|
|
11499
|
+
/* @__PURE__ */ jsx32("div", { className: "se-subTabRow", children: SCENE_SUB_TABS.map((tab) => /* @__PURE__ */ jsx32(
|
|
11146
11500
|
"button",
|
|
11147
11501
|
{
|
|
11148
11502
|
type: "button",
|
|
@@ -11153,9 +11507,9 @@ function SceneSettingsTab({
|
|
|
11153
11507
|
},
|
|
11154
11508
|
tab.label
|
|
11155
11509
|
)) }),
|
|
11156
|
-
activeSubTab === "General" && /* @__PURE__ */
|
|
11157
|
-
activeSubTab === "Lighting" && /* @__PURE__ */
|
|
11158
|
-
activeSubTab === "Animations" && (canUseStarterFeatures ? /* @__PURE__ */
|
|
11510
|
+
activeSubTab === "General" && /* @__PURE__ */ jsx32(GeneralSceneSettings, { config, onChange }),
|
|
11511
|
+
activeSubTab === "Lighting" && /* @__PURE__ */ jsx32(LightingTab, { config, onChange }),
|
|
11512
|
+
activeSubTab === "Animations" && (canUseStarterFeatures ? /* @__PURE__ */ jsx32(
|
|
11159
11513
|
AnimationsTab,
|
|
11160
11514
|
{
|
|
11161
11515
|
config,
|
|
@@ -11171,15 +11525,24 @@ function SceneSettingsTab({
|
|
|
11171
11525
|
setSpeed: resolvedAnimationPlayback.setSpeed,
|
|
11172
11526
|
seek: resolvedAnimationPlayback.seek
|
|
11173
11527
|
}
|
|
11174
|
-
) : /* @__PURE__ */
|
|
11175
|
-
activeSubTab === "Post Processing" && (canUseGrowthFeatures ? /* @__PURE__ */
|
|
11176
|
-
activeSubTab === "Annotations" && (canUseStarterFeatures ? /* @__PURE__ */
|
|
11528
|
+
) : /* @__PURE__ */ jsx32(TierLock, { requiredTier: "starter", feature: "Animation configuration" })),
|
|
11529
|
+
activeSubTab === "Post Processing" && (canUseGrowthFeatures ? /* @__PURE__ */ jsx32(PostProcessingTab, { config, onChange }) : /* @__PURE__ */ jsx32(TierLock, { requiredTier: "growth", feature: "Post-processing effects" })),
|
|
11530
|
+
activeSubTab === "Annotations" && (canUseStarterFeatures ? /* @__PURE__ */ jsx32(AnnotationsTab, { config, onChange }) : /* @__PURE__ */ jsx32(TierLock, { requiredTier: "starter", feature: "Annotations" })),
|
|
11531
|
+
activeSubTab === "Cinematic" && (canUseStarterFeatures ? /* @__PURE__ */ jsx32(
|
|
11532
|
+
CinematicTab,
|
|
11533
|
+
{
|
|
11534
|
+
config,
|
|
11535
|
+
onChange,
|
|
11536
|
+
cameraState,
|
|
11537
|
+
onPreviewWaypoint
|
|
11538
|
+
}
|
|
11539
|
+
) : /* @__PURE__ */ jsx32(TierLock, { requiredTier: "starter", feature: "Cinematic camera" }))
|
|
11177
11540
|
] });
|
|
11178
11541
|
}
|
|
11179
11542
|
|
|
11180
11543
|
// src/components/BindingBuilder/components/Layout.tsx
|
|
11181
11544
|
import { ChevronDown as ChevronDown4, Info as Info2 } from "lucide-react";
|
|
11182
|
-
import { jsx as
|
|
11545
|
+
import { jsx as jsx33, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
11183
11546
|
function MaterialPanel({
|
|
11184
11547
|
title,
|
|
11185
11548
|
trailing,
|
|
@@ -11190,8 +11553,8 @@ function MaterialPanel({
|
|
|
11190
11553
|
tooltip
|
|
11191
11554
|
}) {
|
|
11192
11555
|
const headerTitle = tooltip ? `${tooltip}` : title;
|
|
11193
|
-
return /* @__PURE__ */
|
|
11194
|
-
/* @__PURE__ */
|
|
11556
|
+
return /* @__PURE__ */ jsxs29("div", { className: "bb-materialPanel", children: [
|
|
11557
|
+
/* @__PURE__ */ jsxs29(
|
|
11195
11558
|
"button",
|
|
11196
11559
|
{
|
|
11197
11560
|
type: "button",
|
|
@@ -11200,16 +11563,16 @@ function MaterialPanel({
|
|
|
11200
11563
|
"aria-expanded": open,
|
|
11201
11564
|
title: headerTitle,
|
|
11202
11565
|
children: [
|
|
11203
|
-
/* @__PURE__ */
|
|
11204
|
-
/* @__PURE__ */
|
|
11566
|
+
/* @__PURE__ */ jsxs29("div", { className: "bb-materialPanelTitleWrap", children: [
|
|
11567
|
+
/* @__PURE__ */ jsx33(
|
|
11205
11568
|
ChevronDown4,
|
|
11206
11569
|
{
|
|
11207
11570
|
size: 14,
|
|
11208
11571
|
className: `bb-materialPanelChevron ${open ? "bb-materialPanelChevron--open" : ""}`
|
|
11209
11572
|
}
|
|
11210
11573
|
),
|
|
11211
|
-
/* @__PURE__ */
|
|
11212
|
-
tooltip ? /* @__PURE__ */
|
|
11574
|
+
/* @__PURE__ */ jsx33("span", { className: "bb-materialPanelTitle", children: title }),
|
|
11575
|
+
tooltip ? /* @__PURE__ */ jsx33(
|
|
11213
11576
|
"span",
|
|
11214
11577
|
{
|
|
11215
11578
|
className: "bb-materialPanelHelp",
|
|
@@ -11217,15 +11580,15 @@ function MaterialPanel({
|
|
|
11217
11580
|
"aria-label": headerTitle,
|
|
11218
11581
|
role: "img",
|
|
11219
11582
|
onClick: (event) => event.stopPropagation(),
|
|
11220
|
-
children: /* @__PURE__ */
|
|
11583
|
+
children: /* @__PURE__ */ jsx33(Info2, { size: 12 })
|
|
11221
11584
|
}
|
|
11222
11585
|
) : null
|
|
11223
11586
|
] }),
|
|
11224
|
-
trailing ? /* @__PURE__ */
|
|
11587
|
+
trailing ? /* @__PURE__ */ jsx33("div", { onClick: (event) => event.stopPropagation(), children: trailing }) : null
|
|
11225
11588
|
]
|
|
11226
11589
|
}
|
|
11227
11590
|
),
|
|
11228
|
-
open ? /* @__PURE__ */
|
|
11591
|
+
open ? /* @__PURE__ */ jsx33(
|
|
11229
11592
|
"div",
|
|
11230
11593
|
{
|
|
11231
11594
|
className: "bb-materialPanelBody",
|
|
@@ -11244,8 +11607,8 @@ function BBSection({
|
|
|
11244
11607
|
tooltip
|
|
11245
11608
|
}) {
|
|
11246
11609
|
const headerTitle = tooltip ? `${title}: ${tooltip}` : title;
|
|
11247
|
-
return /* @__PURE__ */
|
|
11248
|
-
/* @__PURE__ */
|
|
11610
|
+
return /* @__PURE__ */ jsxs29("div", { className: "bb-section", children: [
|
|
11611
|
+
/* @__PURE__ */ jsxs29(
|
|
11249
11612
|
"button",
|
|
11250
11613
|
{
|
|
11251
11614
|
type: "button",
|
|
@@ -11254,16 +11617,16 @@ function BBSection({
|
|
|
11254
11617
|
"aria-expanded": open,
|
|
11255
11618
|
title: headerTitle,
|
|
11256
11619
|
children: [
|
|
11257
|
-
/* @__PURE__ */
|
|
11258
|
-
/* @__PURE__ */
|
|
11620
|
+
/* @__PURE__ */ jsxs29("span", { className: "bb-sectionHeaderMain", children: [
|
|
11621
|
+
/* @__PURE__ */ jsx33(
|
|
11259
11622
|
ChevronDown4,
|
|
11260
11623
|
{
|
|
11261
11624
|
size: 15,
|
|
11262
11625
|
className: `bb-sectionChevron ${open ? "bb-sectionChevron--open" : ""}`
|
|
11263
11626
|
}
|
|
11264
11627
|
),
|
|
11265
|
-
/* @__PURE__ */
|
|
11266
|
-
tooltip ? /* @__PURE__ */
|
|
11628
|
+
/* @__PURE__ */ jsx33("h2", { className: "bb-sectionTitle", children: title }),
|
|
11629
|
+
tooltip ? /* @__PURE__ */ jsx33(
|
|
11267
11630
|
"span",
|
|
11268
11631
|
{
|
|
11269
11632
|
className: "bb-sectionHelp",
|
|
@@ -11271,15 +11634,15 @@ function BBSection({
|
|
|
11271
11634
|
"aria-label": headerTitle,
|
|
11272
11635
|
role: "img",
|
|
11273
11636
|
onClick: (event) => event.stopPropagation(),
|
|
11274
|
-
children: /* @__PURE__ */
|
|
11637
|
+
children: /* @__PURE__ */ jsx33(Info2, { size: 13 })
|
|
11275
11638
|
}
|
|
11276
11639
|
) : null
|
|
11277
11640
|
] }),
|
|
11278
|
-
trailing ? /* @__PURE__ */
|
|
11641
|
+
trailing ? /* @__PURE__ */ jsx33("span", { onClick: (event) => event.stopPropagation(), children: trailing }) : null
|
|
11279
11642
|
]
|
|
11280
11643
|
}
|
|
11281
11644
|
),
|
|
11282
|
-
open ? /* @__PURE__ */
|
|
11645
|
+
open ? /* @__PURE__ */ jsx33("div", { className: "bb-sectionBody", children }) : null
|
|
11283
11646
|
] });
|
|
11284
11647
|
}
|
|
11285
11648
|
function EnableToggle2({
|
|
@@ -11287,8 +11650,8 @@ function EnableToggle2({
|
|
|
11287
11650
|
onChange,
|
|
11288
11651
|
label
|
|
11289
11652
|
}) {
|
|
11290
|
-
return /* @__PURE__ */
|
|
11291
|
-
/* @__PURE__ */
|
|
11653
|
+
return /* @__PURE__ */ jsxs29("label", { className: "se-switch", "aria-label": label, children: [
|
|
11654
|
+
/* @__PURE__ */ jsx33(
|
|
11292
11655
|
"input",
|
|
11293
11656
|
{
|
|
11294
11657
|
type: "checkbox",
|
|
@@ -11296,7 +11659,7 @@ function EnableToggle2({
|
|
|
11296
11659
|
onChange: (event) => onChange(event.target.checked)
|
|
11297
11660
|
}
|
|
11298
11661
|
),
|
|
11299
|
-
/* @__PURE__ */
|
|
11662
|
+
/* @__PURE__ */ jsx33("span", { className: "se-switchTrack", children: /* @__PURE__ */ jsx33("span", { className: "se-switchThumb" }) })
|
|
11300
11663
|
] });
|
|
11301
11664
|
}
|
|
11302
11665
|
|
|
@@ -11308,7 +11671,7 @@ var MATERIAL_SIDES = ["front", "back", "double"];
|
|
|
11308
11671
|
var MATERIAL_BLENDING_MODES = ["normal", "additive"];
|
|
11309
11672
|
|
|
11310
11673
|
// src/components/BindingBuilder/components/MaterialPanelGroup.tsx
|
|
11311
|
-
import { jsx as
|
|
11674
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
11312
11675
|
function MaterialPanelGroup({
|
|
11313
11676
|
panelKey,
|
|
11314
11677
|
title,
|
|
@@ -11325,7 +11688,7 @@ function MaterialPanelGroup({
|
|
|
11325
11688
|
onSetMaterialPanelState
|
|
11326
11689
|
} = controls;
|
|
11327
11690
|
const locked = requiredTier?.locked ?? false;
|
|
11328
|
-
return /* @__PURE__ */
|
|
11691
|
+
return /* @__PURE__ */ jsx34(
|
|
11329
11692
|
MaterialPanel,
|
|
11330
11693
|
{
|
|
11331
11694
|
title,
|
|
@@ -11333,7 +11696,7 @@ function MaterialPanelGroup({
|
|
|
11333
11696
|
disabled: locked || !materialPanelEnabled[panelKey],
|
|
11334
11697
|
open: openMaterialPanels[panelKey],
|
|
11335
11698
|
onToggle: () => onToggleMaterialPanel(panelKey),
|
|
11336
|
-
trailing: /* @__PURE__ */
|
|
11699
|
+
trailing: /* @__PURE__ */ jsx34(
|
|
11337
11700
|
EnableToggle2,
|
|
11338
11701
|
{
|
|
11339
11702
|
checked: !locked && materialPanelEnabled[panelKey],
|
|
@@ -11342,27 +11705,27 @@ function MaterialPanelGroup({
|
|
|
11342
11705
|
label: toggleLabel ?? `Toggle ${title}`
|
|
11343
11706
|
}
|
|
11344
11707
|
),
|
|
11345
|
-
children: locked && requiredTier ? /* @__PURE__ */
|
|
11708
|
+
children: locked && requiredTier ? /* @__PURE__ */ jsx34(TierLock, { requiredTier: requiredTier.tier, feature: title }) : children
|
|
11346
11709
|
}
|
|
11347
11710
|
);
|
|
11348
11711
|
}
|
|
11349
11712
|
|
|
11350
11713
|
// src/components/BindingBuilder/components/MaterialFields.tsx
|
|
11351
|
-
import { useRef as
|
|
11714
|
+
import { useRef as useRef14, useState as useState20 } from "react";
|
|
11352
11715
|
import { HexColorInput as HexColorInput3, HexColorPicker as HexColorPicker4 } from "react-colorful";
|
|
11353
11716
|
import { FileUp } from "lucide-react";
|
|
11354
|
-
import { jsx as
|
|
11717
|
+
import { jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
11355
11718
|
function MaterialTexturePathField({
|
|
11356
11719
|
label,
|
|
11357
11720
|
value,
|
|
11358
11721
|
placeholder,
|
|
11359
11722
|
onChange
|
|
11360
11723
|
}) {
|
|
11361
|
-
const inputRef =
|
|
11362
|
-
return /* @__PURE__ */
|
|
11363
|
-
/* @__PURE__ */
|
|
11364
|
-
/* @__PURE__ */
|
|
11365
|
-
/* @__PURE__ */
|
|
11724
|
+
const inputRef = useRef14(null);
|
|
11725
|
+
return /* @__PURE__ */ jsxs30("div", { className: "bb-field bb-field--full", children: [
|
|
11726
|
+
/* @__PURE__ */ jsx35("label", { className: "bb-label", children: label }),
|
|
11727
|
+
/* @__PURE__ */ jsxs30("div", { className: "bb-fileField", children: [
|
|
11728
|
+
/* @__PURE__ */ jsx35(
|
|
11366
11729
|
"input",
|
|
11367
11730
|
{
|
|
11368
11731
|
className: "bb-input bb-fileField-input",
|
|
@@ -11375,7 +11738,7 @@ function MaterialTexturePathField({
|
|
|
11375
11738
|
}
|
|
11376
11739
|
}
|
|
11377
11740
|
),
|
|
11378
|
-
/* @__PURE__ */
|
|
11741
|
+
/* @__PURE__ */ jsx35(
|
|
11379
11742
|
"button",
|
|
11380
11743
|
{
|
|
11381
11744
|
type: "button",
|
|
@@ -11383,10 +11746,10 @@ function MaterialTexturePathField({
|
|
|
11383
11746
|
onClick: () => inputRef.current?.click(),
|
|
11384
11747
|
"aria-label": `Select file for ${label}`,
|
|
11385
11748
|
title: `Select file for ${label}`,
|
|
11386
|
-
children: /* @__PURE__ */
|
|
11749
|
+
children: /* @__PURE__ */ jsx35(FileUp, { size: 20 })
|
|
11387
11750
|
}
|
|
11388
11751
|
),
|
|
11389
|
-
/* @__PURE__ */
|
|
11752
|
+
/* @__PURE__ */ jsx35(
|
|
11390
11753
|
"input",
|
|
11391
11754
|
{
|
|
11392
11755
|
ref: inputRef,
|
|
@@ -11417,10 +11780,10 @@ function MaterialColorField({
|
|
|
11417
11780
|
}) {
|
|
11418
11781
|
const [open, setOpen] = useState20(false);
|
|
11419
11782
|
const color = value || fallback;
|
|
11420
|
-
const anchorRef =
|
|
11421
|
-
return /* @__PURE__ */
|
|
11422
|
-
/* @__PURE__ */
|
|
11423
|
-
/* @__PURE__ */
|
|
11783
|
+
const anchorRef = useRef14(null);
|
|
11784
|
+
return /* @__PURE__ */ jsxs30("div", { className: "bb-field", children: [
|
|
11785
|
+
/* @__PURE__ */ jsx35("span", { className: "bb-label", children: label }),
|
|
11786
|
+
/* @__PURE__ */ jsx35("div", { className: "se-fieldInline", children: /* @__PURE__ */ jsx35("span", { ref: anchorRef, style: { display: "inline-flex" }, children: /* @__PURE__ */ jsx35(
|
|
11424
11787
|
HexColorInput3,
|
|
11425
11788
|
{
|
|
11426
11789
|
prefixed: true,
|
|
@@ -11434,7 +11797,7 @@ function MaterialColorField({
|
|
|
11434
11797
|
onChange
|
|
11435
11798
|
}
|
|
11436
11799
|
) }) }),
|
|
11437
|
-
open ? /* @__PURE__ */
|
|
11800
|
+
open ? /* @__PURE__ */ jsx35(ColorPopover, { anchorRef, onClose: () => setOpen(false), children: /* @__PURE__ */ jsx35(HexColorPicker4, { color, onChange }) }) : null
|
|
11438
11801
|
] });
|
|
11439
11802
|
}
|
|
11440
11803
|
function MaterialCheckboxField({
|
|
@@ -11442,8 +11805,8 @@ function MaterialCheckboxField({
|
|
|
11442
11805
|
checked,
|
|
11443
11806
|
onChange
|
|
11444
11807
|
}) {
|
|
11445
|
-
return /* @__PURE__ */
|
|
11446
|
-
/* @__PURE__ */
|
|
11808
|
+
return /* @__PURE__ */ jsxs30("label", { className: "bb-checkbox", children: [
|
|
11809
|
+
/* @__PURE__ */ jsx35(
|
|
11447
11810
|
"input",
|
|
11448
11811
|
{
|
|
11449
11812
|
type: "checkbox",
|
|
@@ -11463,9 +11826,9 @@ function MaterialNumberField({
|
|
|
11463
11826
|
placeholder,
|
|
11464
11827
|
onChange
|
|
11465
11828
|
}) {
|
|
11466
|
-
return /* @__PURE__ */
|
|
11467
|
-
/* @__PURE__ */
|
|
11468
|
-
/* @__PURE__ */
|
|
11829
|
+
return /* @__PURE__ */ jsxs30("div", { className: "bb-field", children: [
|
|
11830
|
+
/* @__PURE__ */ jsx35("label", { className: "bb-label", children: label }),
|
|
11831
|
+
/* @__PURE__ */ jsx35(
|
|
11469
11832
|
"input",
|
|
11470
11833
|
{
|
|
11471
11834
|
className: "bb-input",
|
|
@@ -11488,15 +11851,15 @@ function MaterialSelectField({
|
|
|
11488
11851
|
options,
|
|
11489
11852
|
onChange
|
|
11490
11853
|
}) {
|
|
11491
|
-
return /* @__PURE__ */
|
|
11492
|
-
/* @__PURE__ */
|
|
11493
|
-
/* @__PURE__ */
|
|
11854
|
+
return /* @__PURE__ */ jsxs30("div", { className: "bb-field", children: [
|
|
11855
|
+
/* @__PURE__ */ jsx35("label", { className: "bb-label", children: label }),
|
|
11856
|
+
/* @__PURE__ */ jsx35(
|
|
11494
11857
|
"select",
|
|
11495
11858
|
{
|
|
11496
11859
|
className: "bb-select",
|
|
11497
11860
|
value,
|
|
11498
11861
|
onChange: (event) => onChange(event.target.value),
|
|
11499
|
-
children: options.map((option) => /* @__PURE__ */
|
|
11862
|
+
children: options.map((option) => /* @__PURE__ */ jsx35("option", { value: option, children: option }, option))
|
|
11500
11863
|
}
|
|
11501
11864
|
)
|
|
11502
11865
|
] });
|
|
@@ -11510,10 +11873,10 @@ function MaterialSliderField({
|
|
|
11510
11873
|
onChange
|
|
11511
11874
|
}) {
|
|
11512
11875
|
const displayValue = value ?? min;
|
|
11513
|
-
return /* @__PURE__ */
|
|
11514
|
-
/* @__PURE__ */
|
|
11515
|
-
/* @__PURE__ */
|
|
11516
|
-
/* @__PURE__ */
|
|
11876
|
+
return /* @__PURE__ */ jsxs30("div", { className: "bb-field bb-field--full", children: [
|
|
11877
|
+
/* @__PURE__ */ jsx35("label", { className: "bb-label", children: label }),
|
|
11878
|
+
/* @__PURE__ */ jsxs30("div", { className: "bb-sliderField", children: [
|
|
11879
|
+
/* @__PURE__ */ jsx35(
|
|
11517
11880
|
"input",
|
|
11518
11881
|
{
|
|
11519
11882
|
className: `bb-slider ${value === void 0 ? "bb-slider--unset" : ""}`,
|
|
@@ -11525,7 +11888,7 @@ function MaterialSliderField({
|
|
|
11525
11888
|
onChange: (event) => onChange(Number(event.target.value))
|
|
11526
11889
|
}
|
|
11527
11890
|
),
|
|
11528
|
-
/* @__PURE__ */
|
|
11891
|
+
/* @__PURE__ */ jsx35(
|
|
11529
11892
|
"input",
|
|
11530
11893
|
{
|
|
11531
11894
|
className: "bb-input bb-input--compact",
|
|
@@ -11544,7 +11907,7 @@ function MaterialSliderField({
|
|
|
11544
11907
|
}
|
|
11545
11908
|
|
|
11546
11909
|
// src/components/BindingBuilder/components/MaterialSection.tsx
|
|
11547
|
-
import { Fragment as Fragment9, jsx as
|
|
11910
|
+
import { Fragment as Fragment9, jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
11548
11911
|
function MaterialSection({
|
|
11549
11912
|
selectedItem,
|
|
11550
11913
|
selectedMaterial,
|
|
@@ -11573,16 +11936,16 @@ function MaterialSection({
|
|
|
11573
11936
|
const [anisotropyMode, setAnisotropyMode] = useState21(
|
|
11574
11937
|
"factor"
|
|
11575
11938
|
);
|
|
11576
|
-
return /* @__PURE__ */
|
|
11577
|
-
/* @__PURE__ */
|
|
11939
|
+
return /* @__PURE__ */ jsxs31(Fragment9, { children: [
|
|
11940
|
+
/* @__PURE__ */ jsx36(
|
|
11578
11941
|
MaterialPanelGroup,
|
|
11579
11942
|
{
|
|
11580
11943
|
panelKey: "pbr",
|
|
11581
11944
|
title: "PBR Maps",
|
|
11582
11945
|
tooltip: "Controls the base color and workflow used for physically based shading.",
|
|
11583
11946
|
controls: panelControls,
|
|
11584
|
-
children: canUseStarterFeatures ? /* @__PURE__ */
|
|
11585
|
-
/* @__PURE__ */
|
|
11947
|
+
children: canUseStarterFeatures ? /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
|
|
11948
|
+
/* @__PURE__ */ jsx36(
|
|
11586
11949
|
MaterialSelectField,
|
|
11587
11950
|
{
|
|
11588
11951
|
label: "Workflow",
|
|
@@ -11591,8 +11954,8 @@ function MaterialSection({
|
|
|
11591
11954
|
onChange: onPbrWorkflowChange
|
|
11592
11955
|
}
|
|
11593
11956
|
),
|
|
11594
|
-
/* @__PURE__ */
|
|
11595
|
-
/* @__PURE__ */
|
|
11957
|
+
/* @__PURE__ */ jsx36("div", { className: "bb-field bb-field--full", children: /* @__PURE__ */ jsx36("p", { className: "bb-note", style: { margin: 0 }, children: pbrWorkflow === "metalness" ? "Metalness workflow maps directly to the underlying physical material." : "Specular workflow exposes the same physical material with non-metal specular controls; it does not switch to a separate spec/gloss shader." }) }),
|
|
11958
|
+
/* @__PURE__ */ jsx36(
|
|
11596
11959
|
MaterialTexturePathField,
|
|
11597
11960
|
{
|
|
11598
11961
|
label: pbrWorkflow === "specular" ? "Albedo texture path" : "Base color texture path",
|
|
@@ -11601,7 +11964,7 @@ function MaterialSection({
|
|
|
11601
11964
|
onChange: (value) => setSelectedTexturePath(selectedItem, "texture", value)
|
|
11602
11965
|
}
|
|
11603
11966
|
),
|
|
11604
|
-
/* @__PURE__ */
|
|
11967
|
+
/* @__PURE__ */ jsx36(
|
|
11605
11968
|
MaterialColorField,
|
|
11606
11969
|
{
|
|
11607
11970
|
label: pbrWorkflow === "specular" ? "Albedo color" : "Base color",
|
|
@@ -11612,8 +11975,8 @@ function MaterialSection({
|
|
|
11612
11975
|
}))
|
|
11613
11976
|
}
|
|
11614
11977
|
),
|
|
11615
|
-
pbrWorkflow === "metalness" ? /* @__PURE__ */
|
|
11616
|
-
/* @__PURE__ */
|
|
11978
|
+
pbrWorkflow === "metalness" ? /* @__PURE__ */ jsxs31(Fragment9, { children: [
|
|
11979
|
+
/* @__PURE__ */ jsx36(
|
|
11617
11980
|
MaterialSliderField,
|
|
11618
11981
|
{
|
|
11619
11982
|
label: "Metalness",
|
|
@@ -11627,7 +11990,7 @@ function MaterialSection({
|
|
|
11627
11990
|
}))
|
|
11628
11991
|
}
|
|
11629
11992
|
),
|
|
11630
|
-
/* @__PURE__ */
|
|
11993
|
+
/* @__PURE__ */ jsx36(
|
|
11631
11994
|
MaterialTexturePathField,
|
|
11632
11995
|
{
|
|
11633
11996
|
label: "Metalness map path",
|
|
@@ -11636,8 +11999,8 @@ function MaterialSection({
|
|
|
11636
11999
|
onChange: (value) => setSelectedTexturePath(selectedItem, "metalnessMap", value)
|
|
11637
12000
|
}
|
|
11638
12001
|
)
|
|
11639
|
-
] }) : /* @__PURE__ */
|
|
11640
|
-
/* @__PURE__ */
|
|
12002
|
+
] }) : /* @__PURE__ */ jsxs31(Fragment9, { children: [
|
|
12003
|
+
/* @__PURE__ */ jsx36(
|
|
11641
12004
|
MaterialSliderField,
|
|
11642
12005
|
{
|
|
11643
12006
|
label: "Specular intensity",
|
|
@@ -11651,7 +12014,7 @@ function MaterialSection({
|
|
|
11651
12014
|
}))
|
|
11652
12015
|
}
|
|
11653
12016
|
),
|
|
11654
|
-
/* @__PURE__ */
|
|
12017
|
+
/* @__PURE__ */ jsx36(
|
|
11655
12018
|
MaterialColorField,
|
|
11656
12019
|
{
|
|
11657
12020
|
label: "Specular color",
|
|
@@ -11662,7 +12025,7 @@ function MaterialSection({
|
|
|
11662
12025
|
}))
|
|
11663
12026
|
}
|
|
11664
12027
|
),
|
|
11665
|
-
/* @__PURE__ */
|
|
12028
|
+
/* @__PURE__ */ jsx36(
|
|
11666
12029
|
MaterialTexturePathField,
|
|
11667
12030
|
{
|
|
11668
12031
|
label: "Specular intensity map path",
|
|
@@ -11675,7 +12038,7 @@ function MaterialSection({
|
|
|
11675
12038
|
)
|
|
11676
12039
|
}
|
|
11677
12040
|
),
|
|
11678
|
-
/* @__PURE__ */
|
|
12041
|
+
/* @__PURE__ */ jsx36(
|
|
11679
12042
|
MaterialTexturePathField,
|
|
11680
12043
|
{
|
|
11681
12044
|
label: "Specular color map path",
|
|
@@ -11689,7 +12052,7 @@ function MaterialSection({
|
|
|
11689
12052
|
}
|
|
11690
12053
|
)
|
|
11691
12054
|
] }),
|
|
11692
|
-
/* @__PURE__ */
|
|
12055
|
+
/* @__PURE__ */ jsx36(
|
|
11693
12056
|
MaterialSliderField,
|
|
11694
12057
|
{
|
|
11695
12058
|
label: "Specular F0 / reflectivity",
|
|
@@ -11703,8 +12066,8 @@ function MaterialSection({
|
|
|
11703
12066
|
}))
|
|
11704
12067
|
}
|
|
11705
12068
|
)
|
|
11706
|
-
] }) : /* @__PURE__ */
|
|
11707
|
-
/* @__PURE__ */
|
|
12069
|
+
] }) : /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
|
|
12070
|
+
/* @__PURE__ */ jsx36(
|
|
11708
12071
|
MaterialColorField,
|
|
11709
12072
|
{
|
|
11710
12073
|
label: "Base color",
|
|
@@ -11715,7 +12078,7 @@ function MaterialSection({
|
|
|
11715
12078
|
}))
|
|
11716
12079
|
}
|
|
11717
12080
|
),
|
|
11718
|
-
/* @__PURE__ */
|
|
12081
|
+
/* @__PURE__ */ jsx36(
|
|
11719
12082
|
MaterialSliderField,
|
|
11720
12083
|
{
|
|
11721
12084
|
label: "Metalness",
|
|
@@ -11729,7 +12092,7 @@ function MaterialSection({
|
|
|
11729
12092
|
}))
|
|
11730
12093
|
}
|
|
11731
12094
|
),
|
|
11732
|
-
/* @__PURE__ */
|
|
12095
|
+
/* @__PURE__ */ jsx36("div", { className: "bb-field bb-field--full", children: /* @__PURE__ */ jsx36(
|
|
11733
12096
|
TierLock,
|
|
11734
12097
|
{
|
|
11735
12098
|
requiredTier: "starter",
|
|
@@ -11739,7 +12102,7 @@ function MaterialSection({
|
|
|
11739
12102
|
] })
|
|
11740
12103
|
}
|
|
11741
12104
|
),
|
|
11742
|
-
/* @__PURE__ */
|
|
12105
|
+
/* @__PURE__ */ jsx36(
|
|
11743
12106
|
MaterialPanelGroup,
|
|
11744
12107
|
{
|
|
11745
12108
|
panelKey: "roughness",
|
|
@@ -11748,8 +12111,8 @@ function MaterialSection({
|
|
|
11748
12111
|
tooltip: "Adjusts how matte or shiny the surface appears.",
|
|
11749
12112
|
toggleLabel: "Toggle Roughness and Glossiness",
|
|
11750
12113
|
controls: panelControls,
|
|
11751
|
-
children: /* @__PURE__ */
|
|
11752
|
-
/* @__PURE__ */
|
|
12114
|
+
children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
|
|
12115
|
+
/* @__PURE__ */ jsx36(
|
|
11753
12116
|
MaterialSelectField,
|
|
11754
12117
|
{
|
|
11755
12118
|
label: "Mode",
|
|
@@ -11758,8 +12121,8 @@ function MaterialSection({
|
|
|
11758
12121
|
onChange: setRoughnessMode
|
|
11759
12122
|
}
|
|
11760
12123
|
),
|
|
11761
|
-
/* @__PURE__ */
|
|
11762
|
-
roughnessMode === "roughness" ? /* @__PURE__ */
|
|
12124
|
+
/* @__PURE__ */ jsx36("div", { className: "bb-field bb-field--full", children: /* @__PURE__ */ jsx36("p", { className: "bb-note", style: { margin: 0 }, children: roughnessMode === "roughness" ? "Roughness: 0 is glossy, 1 is matte." : "Glossiness is the inverse of roughness (glossiness = 1 \u2212 roughness)" }) }),
|
|
12125
|
+
roughnessMode === "roughness" ? /* @__PURE__ */ jsx36(
|
|
11763
12126
|
MaterialSliderField,
|
|
11764
12127
|
{
|
|
11765
12128
|
label: "Roughness",
|
|
@@ -11772,7 +12135,7 @@ function MaterialSection({
|
|
|
11772
12135
|
roughness: value
|
|
11773
12136
|
}))
|
|
11774
12137
|
}
|
|
11775
|
-
) : /* @__PURE__ */
|
|
12138
|
+
) : /* @__PURE__ */ jsx36(
|
|
11776
12139
|
MaterialSliderField,
|
|
11777
12140
|
{
|
|
11778
12141
|
label: "Glossiness",
|
|
@@ -11786,7 +12149,7 @@ function MaterialSection({
|
|
|
11786
12149
|
}))
|
|
11787
12150
|
}
|
|
11788
12151
|
),
|
|
11789
|
-
/* @__PURE__ */
|
|
12152
|
+
/* @__PURE__ */ jsx36(
|
|
11790
12153
|
MaterialTexturePathField,
|
|
11791
12154
|
{
|
|
11792
12155
|
label: roughnessMode === "glossiness" ? "Glossiness map path (applied as roughness)" : "Roughness map path",
|
|
@@ -11798,7 +12161,7 @@ function MaterialSection({
|
|
|
11798
12161
|
] })
|
|
11799
12162
|
}
|
|
11800
12163
|
),
|
|
11801
|
-
/* @__PURE__ */
|
|
12164
|
+
/* @__PURE__ */ jsx36(
|
|
11802
12165
|
MaterialPanelGroup,
|
|
11803
12166
|
{
|
|
11804
12167
|
panelKey: "emission",
|
|
@@ -11806,8 +12169,8 @@ function MaterialSection({
|
|
|
11806
12169
|
title: "Emission",
|
|
11807
12170
|
tooltip: "Makes the material self-illuminated with emissive color and intensity.",
|
|
11808
12171
|
controls: panelControls,
|
|
11809
|
-
children: /* @__PURE__ */
|
|
11810
|
-
/* @__PURE__ */
|
|
12172
|
+
children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
|
|
12173
|
+
/* @__PURE__ */ jsx36(
|
|
11811
12174
|
MaterialColorField,
|
|
11812
12175
|
{
|
|
11813
12176
|
label: "Emissive color",
|
|
@@ -11819,7 +12182,7 @@ function MaterialSection({
|
|
|
11819
12182
|
}))
|
|
11820
12183
|
}
|
|
11821
12184
|
),
|
|
11822
|
-
/* @__PURE__ */
|
|
12185
|
+
/* @__PURE__ */ jsx36(
|
|
11823
12186
|
MaterialSliderField,
|
|
11824
12187
|
{
|
|
11825
12188
|
label: "Emissive intensity",
|
|
@@ -11833,7 +12196,7 @@ function MaterialSection({
|
|
|
11833
12196
|
}))
|
|
11834
12197
|
}
|
|
11835
12198
|
),
|
|
11836
|
-
/* @__PURE__ */
|
|
12199
|
+
/* @__PURE__ */ jsx36(
|
|
11837
12200
|
MaterialTexturePathField,
|
|
11838
12201
|
{
|
|
11839
12202
|
label: "Emissive map path",
|
|
@@ -11845,7 +12208,7 @@ function MaterialSection({
|
|
|
11845
12208
|
] })
|
|
11846
12209
|
}
|
|
11847
12210
|
),
|
|
11848
|
-
/* @__PURE__ */
|
|
12211
|
+
/* @__PURE__ */ jsx36(
|
|
11849
12212
|
MaterialPanelGroup,
|
|
11850
12213
|
{
|
|
11851
12214
|
panelKey: "opacity",
|
|
@@ -11853,8 +12216,8 @@ function MaterialSection({
|
|
|
11853
12216
|
title: "Opacity",
|
|
11854
12217
|
tooltip: "Controls transparency, alpha testing, and blending behavior.",
|
|
11855
12218
|
controls: panelControls,
|
|
11856
|
-
children: /* @__PURE__ */
|
|
11857
|
-
/* @__PURE__ */
|
|
12219
|
+
children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
|
|
12220
|
+
/* @__PURE__ */ jsx36(
|
|
11858
12221
|
MaterialSliderField,
|
|
11859
12222
|
{
|
|
11860
12223
|
label: "Opacity",
|
|
@@ -11868,7 +12231,7 @@ function MaterialSection({
|
|
|
11868
12231
|
}))
|
|
11869
12232
|
}
|
|
11870
12233
|
),
|
|
11871
|
-
/* @__PURE__ */
|
|
12234
|
+
/* @__PURE__ */ jsx36(
|
|
11872
12235
|
MaterialSliderField,
|
|
11873
12236
|
{
|
|
11874
12237
|
label: "Alpha test",
|
|
@@ -11882,7 +12245,7 @@ function MaterialSection({
|
|
|
11882
12245
|
}))
|
|
11883
12246
|
}
|
|
11884
12247
|
),
|
|
11885
|
-
/* @__PURE__ */
|
|
12248
|
+
/* @__PURE__ */ jsx36(
|
|
11886
12249
|
MaterialTexturePathField,
|
|
11887
12250
|
{
|
|
11888
12251
|
label: "Alpha map path",
|
|
@@ -11891,7 +12254,7 @@ function MaterialSection({
|
|
|
11891
12254
|
onChange: (value) => setSelectedTexturePath(selectedItem, "alphaMap", value)
|
|
11892
12255
|
}
|
|
11893
12256
|
),
|
|
11894
|
-
/* @__PURE__ */
|
|
12257
|
+
/* @__PURE__ */ jsx36(
|
|
11895
12258
|
MaterialSelectField,
|
|
11896
12259
|
{
|
|
11897
12260
|
label: "Blending mode",
|
|
@@ -11903,9 +12266,9 @@ function MaterialSection({
|
|
|
11903
12266
|
}))
|
|
11904
12267
|
}
|
|
11905
12268
|
),
|
|
11906
|
-
/* @__PURE__ */
|
|
11907
|
-
/* @__PURE__ */
|
|
11908
|
-
/* @__PURE__ */
|
|
12269
|
+
/* @__PURE__ */ jsxs31("div", { className: "bb-field", children: [
|
|
12270
|
+
/* @__PURE__ */ jsx36("label", { className: "bb-label", children: "Alpha hash" }),
|
|
12271
|
+
/* @__PURE__ */ jsx36("div", { className: "bb-checkboxRow", children: /* @__PURE__ */ jsx36(
|
|
11909
12272
|
MaterialCheckboxField,
|
|
11910
12273
|
{
|
|
11911
12274
|
label: "Use alpha hash",
|
|
@@ -11917,9 +12280,9 @@ function MaterialSection({
|
|
|
11917
12280
|
}
|
|
11918
12281
|
) })
|
|
11919
12282
|
] }),
|
|
11920
|
-
/* @__PURE__ */
|
|
11921
|
-
/* @__PURE__ */
|
|
11922
|
-
/* @__PURE__ */
|
|
12283
|
+
/* @__PURE__ */ jsxs31("div", { className: "bb-field", children: [
|
|
12284
|
+
/* @__PURE__ */ jsx36("label", { className: "bb-label", children: "Dithering" }),
|
|
12285
|
+
/* @__PURE__ */ jsx36("div", { className: "bb-checkboxRow", children: /* @__PURE__ */ jsx36(
|
|
11923
12286
|
MaterialCheckboxField,
|
|
11924
12287
|
{
|
|
11925
12288
|
label: "Use dithering",
|
|
@@ -11934,7 +12297,7 @@ function MaterialSection({
|
|
|
11934
12297
|
] })
|
|
11935
12298
|
}
|
|
11936
12299
|
),
|
|
11937
|
-
/* @__PURE__ */
|
|
12300
|
+
/* @__PURE__ */ jsxs31(
|
|
11938
12301
|
MaterialPanelGroup,
|
|
11939
12302
|
{
|
|
11940
12303
|
panelKey: "normalBump",
|
|
@@ -11944,8 +12307,8 @@ function MaterialSection({
|
|
|
11944
12307
|
toggleLabel: "Toggle Normal and Bump",
|
|
11945
12308
|
controls: panelControls,
|
|
11946
12309
|
children: [
|
|
11947
|
-
/* @__PURE__ */
|
|
11948
|
-
/* @__PURE__ */
|
|
12310
|
+
/* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
|
|
12311
|
+
/* @__PURE__ */ jsx36(
|
|
11949
12312
|
MaterialTexturePathField,
|
|
11950
12313
|
{
|
|
11951
12314
|
label: "Normal map path",
|
|
@@ -11954,7 +12317,7 @@ function MaterialSection({
|
|
|
11954
12317
|
onChange: (value) => setSelectedTexturePath(selectedItem, "normalMap", value)
|
|
11955
12318
|
}
|
|
11956
12319
|
),
|
|
11957
|
-
/* @__PURE__ */
|
|
12320
|
+
/* @__PURE__ */ jsx36(
|
|
11958
12321
|
MaterialSliderField,
|
|
11959
12322
|
{
|
|
11960
12323
|
label: "Normal scale",
|
|
@@ -11968,7 +12331,7 @@ function MaterialSection({
|
|
|
11968
12331
|
}))
|
|
11969
12332
|
}
|
|
11970
12333
|
),
|
|
11971
|
-
/* @__PURE__ */
|
|
12334
|
+
/* @__PURE__ */ jsx36(
|
|
11972
12335
|
MaterialTexturePathField,
|
|
11973
12336
|
{
|
|
11974
12337
|
label: "Bump map path",
|
|
@@ -11977,7 +12340,7 @@ function MaterialSection({
|
|
|
11977
12340
|
onChange: (value) => setSelectedTexturePath(selectedItem, "bumpMap", value)
|
|
11978
12341
|
}
|
|
11979
12342
|
),
|
|
11980
|
-
/* @__PURE__ */
|
|
12343
|
+
/* @__PURE__ */ jsx36(
|
|
11981
12344
|
MaterialSliderField,
|
|
11982
12345
|
{
|
|
11983
12346
|
label: "Bump scale",
|
|
@@ -11992,7 +12355,7 @@ function MaterialSection({
|
|
|
11992
12355
|
}
|
|
11993
12356
|
)
|
|
11994
12357
|
] }),
|
|
11995
|
-
/* @__PURE__ */
|
|
12358
|
+
/* @__PURE__ */ jsx36("div", { className: "bb-checkboxRow", style: { marginTop: 12 }, children: /* @__PURE__ */ jsx36(
|
|
11996
12359
|
MaterialCheckboxField,
|
|
11997
12360
|
{
|
|
11998
12361
|
label: "Flip normal Y",
|
|
@@ -12006,7 +12369,7 @@ function MaterialSection({
|
|
|
12006
12369
|
]
|
|
12007
12370
|
}
|
|
12008
12371
|
),
|
|
12009
|
-
/* @__PURE__ */
|
|
12372
|
+
/* @__PURE__ */ jsx36(
|
|
12010
12373
|
MaterialPanelGroup,
|
|
12011
12374
|
{
|
|
12012
12375
|
panelKey: "displacement",
|
|
@@ -12014,8 +12377,8 @@ function MaterialSection({
|
|
|
12014
12377
|
title: "Displacement",
|
|
12015
12378
|
tooltip: "Offsets geometry using a texture to create real surface height.",
|
|
12016
12379
|
controls: panelControls,
|
|
12017
|
-
children: /* @__PURE__ */
|
|
12018
|
-
/* @__PURE__ */
|
|
12380
|
+
children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
|
|
12381
|
+
/* @__PURE__ */ jsx36(
|
|
12019
12382
|
MaterialTexturePathField,
|
|
12020
12383
|
{
|
|
12021
12384
|
label: "Displacement map path",
|
|
@@ -12024,7 +12387,7 @@ function MaterialSection({
|
|
|
12024
12387
|
onChange: (value) => setSelectedTexturePath(selectedItem, "displacementMap", value)
|
|
12025
12388
|
}
|
|
12026
12389
|
),
|
|
12027
|
-
/* @__PURE__ */
|
|
12390
|
+
/* @__PURE__ */ jsx36(
|
|
12028
12391
|
MaterialSliderField,
|
|
12029
12392
|
{
|
|
12030
12393
|
label: "Displacement scale",
|
|
@@ -12038,7 +12401,7 @@ function MaterialSection({
|
|
|
12038
12401
|
}))
|
|
12039
12402
|
}
|
|
12040
12403
|
),
|
|
12041
|
-
/* @__PURE__ */
|
|
12404
|
+
/* @__PURE__ */ jsx36(
|
|
12042
12405
|
MaterialSliderField,
|
|
12043
12406
|
{
|
|
12044
12407
|
label: "Displacement bias",
|
|
@@ -12055,7 +12418,7 @@ function MaterialSection({
|
|
|
12055
12418
|
] })
|
|
12056
12419
|
}
|
|
12057
12420
|
),
|
|
12058
|
-
/* @__PURE__ */
|
|
12421
|
+
/* @__PURE__ */ jsx36(
|
|
12059
12422
|
MaterialPanelGroup,
|
|
12060
12423
|
{
|
|
12061
12424
|
panelKey: "ao",
|
|
@@ -12063,8 +12426,8 @@ function MaterialSection({
|
|
|
12063
12426
|
title: "Ambient Occlusion",
|
|
12064
12427
|
tooltip: "Darkens creases and contact areas to add depth.",
|
|
12065
12428
|
controls: panelControls,
|
|
12066
|
-
children: /* @__PURE__ */
|
|
12067
|
-
/* @__PURE__ */
|
|
12429
|
+
children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
|
|
12430
|
+
/* @__PURE__ */ jsx36(
|
|
12068
12431
|
MaterialTexturePathField,
|
|
12069
12432
|
{
|
|
12070
12433
|
label: "AO map path",
|
|
@@ -12073,7 +12436,7 @@ function MaterialSection({
|
|
|
12073
12436
|
onChange: (value) => setSelectedTexturePath(selectedItem, "aoMap", value)
|
|
12074
12437
|
}
|
|
12075
12438
|
),
|
|
12076
|
-
/* @__PURE__ */
|
|
12439
|
+
/* @__PURE__ */ jsx36(
|
|
12077
12440
|
MaterialSliderField,
|
|
12078
12441
|
{
|
|
12079
12442
|
label: "AO intensity",
|
|
@@ -12090,7 +12453,7 @@ function MaterialSection({
|
|
|
12090
12453
|
] })
|
|
12091
12454
|
}
|
|
12092
12455
|
),
|
|
12093
|
-
/* @__PURE__ */
|
|
12456
|
+
/* @__PURE__ */ jsxs31(
|
|
12094
12457
|
MaterialPanelGroup,
|
|
12095
12458
|
{
|
|
12096
12459
|
panelKey: "clearcoat",
|
|
@@ -12099,8 +12462,8 @@ function MaterialSection({
|
|
|
12099
12462
|
tooltip: "Adds a glossy protective layer on top of the base material.",
|
|
12100
12463
|
controls: panelControls,
|
|
12101
12464
|
children: [
|
|
12102
|
-
/* @__PURE__ */
|
|
12103
|
-
/* @__PURE__ */
|
|
12465
|
+
/* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
|
|
12466
|
+
/* @__PURE__ */ jsx36(
|
|
12104
12467
|
MaterialTexturePathField,
|
|
12105
12468
|
{
|
|
12106
12469
|
label: "Clearcoat map path",
|
|
@@ -12109,7 +12472,7 @@ function MaterialSection({
|
|
|
12109
12472
|
onChange: (value) => setSelectedTexturePath(selectedItem, "clearcoatMap", value)
|
|
12110
12473
|
}
|
|
12111
12474
|
),
|
|
12112
|
-
/* @__PURE__ */
|
|
12475
|
+
/* @__PURE__ */ jsx36(
|
|
12113
12476
|
MaterialSliderField,
|
|
12114
12477
|
{
|
|
12115
12478
|
label: "Clearcoat",
|
|
@@ -12123,7 +12486,7 @@ function MaterialSection({
|
|
|
12123
12486
|
}))
|
|
12124
12487
|
}
|
|
12125
12488
|
),
|
|
12126
|
-
/* @__PURE__ */
|
|
12489
|
+
/* @__PURE__ */ jsx36(
|
|
12127
12490
|
MaterialTexturePathField,
|
|
12128
12491
|
{
|
|
12129
12492
|
label: "Clearcoat roughness map path",
|
|
@@ -12136,7 +12499,7 @@ function MaterialSection({
|
|
|
12136
12499
|
)
|
|
12137
12500
|
}
|
|
12138
12501
|
),
|
|
12139
|
-
/* @__PURE__ */
|
|
12502
|
+
/* @__PURE__ */ jsx36(
|
|
12140
12503
|
MaterialSliderField,
|
|
12141
12504
|
{
|
|
12142
12505
|
label: "Clearcoat roughness",
|
|
@@ -12150,7 +12513,7 @@ function MaterialSection({
|
|
|
12150
12513
|
}))
|
|
12151
12514
|
}
|
|
12152
12515
|
),
|
|
12153
|
-
/* @__PURE__ */
|
|
12516
|
+
/* @__PURE__ */ jsx36(
|
|
12154
12517
|
MaterialTexturePathField,
|
|
12155
12518
|
{
|
|
12156
12519
|
label: "Clearcoat normal map path",
|
|
@@ -12159,7 +12522,7 @@ function MaterialSection({
|
|
|
12159
12522
|
onChange: (value) => setSelectedTexturePath(selectedItem, "clearcoatNormalMap", value)
|
|
12160
12523
|
}
|
|
12161
12524
|
),
|
|
12162
|
-
/* @__PURE__ */
|
|
12525
|
+
/* @__PURE__ */ jsx36(
|
|
12163
12526
|
MaterialSliderField,
|
|
12164
12527
|
{
|
|
12165
12528
|
label: "Clearcoat normal scale",
|
|
@@ -12174,7 +12537,7 @@ function MaterialSection({
|
|
|
12174
12537
|
}
|
|
12175
12538
|
)
|
|
12176
12539
|
] }),
|
|
12177
|
-
/* @__PURE__ */
|
|
12540
|
+
/* @__PURE__ */ jsx36("div", { className: "bb-checkboxRow", style: { marginTop: 12 }, children: /* @__PURE__ */ jsx36(
|
|
12178
12541
|
MaterialCheckboxField,
|
|
12179
12542
|
{
|
|
12180
12543
|
label: "Flip clearcoat normal Y",
|
|
@@ -12188,7 +12551,7 @@ function MaterialSection({
|
|
|
12188
12551
|
]
|
|
12189
12552
|
}
|
|
12190
12553
|
),
|
|
12191
|
-
/* @__PURE__ */
|
|
12554
|
+
/* @__PURE__ */ jsx36(
|
|
12192
12555
|
MaterialPanelGroup,
|
|
12193
12556
|
{
|
|
12194
12557
|
panelKey: "sheen",
|
|
@@ -12196,8 +12559,8 @@ function MaterialSection({
|
|
|
12196
12559
|
title: "Sheen",
|
|
12197
12560
|
tooltip: "Adds a soft fabric-like sheen on grazing angles.",
|
|
12198
12561
|
controls: panelControls,
|
|
12199
|
-
children: /* @__PURE__ */
|
|
12200
|
-
/* @__PURE__ */
|
|
12562
|
+
children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
|
|
12563
|
+
/* @__PURE__ */ jsx36(
|
|
12201
12564
|
MaterialSliderField,
|
|
12202
12565
|
{
|
|
12203
12566
|
label: "Sheen",
|
|
@@ -12211,7 +12574,7 @@ function MaterialSection({
|
|
|
12211
12574
|
}))
|
|
12212
12575
|
}
|
|
12213
12576
|
),
|
|
12214
|
-
/* @__PURE__ */
|
|
12577
|
+
/* @__PURE__ */ jsx36(
|
|
12215
12578
|
MaterialColorField,
|
|
12216
12579
|
{
|
|
12217
12580
|
label: "Sheen color",
|
|
@@ -12222,7 +12585,7 @@ function MaterialSection({
|
|
|
12222
12585
|
}))
|
|
12223
12586
|
}
|
|
12224
12587
|
),
|
|
12225
|
-
/* @__PURE__ */
|
|
12588
|
+
/* @__PURE__ */ jsx36(
|
|
12226
12589
|
MaterialTexturePathField,
|
|
12227
12590
|
{
|
|
12228
12591
|
label: "Sheen color map path",
|
|
@@ -12231,7 +12594,7 @@ function MaterialSection({
|
|
|
12231
12594
|
onChange: (value) => setSelectedTexturePath(selectedItem, "sheenColorMap", value)
|
|
12232
12595
|
}
|
|
12233
12596
|
),
|
|
12234
|
-
/* @__PURE__ */
|
|
12597
|
+
/* @__PURE__ */ jsx36(
|
|
12235
12598
|
MaterialSliderField,
|
|
12236
12599
|
{
|
|
12237
12600
|
label: "Sheen roughness",
|
|
@@ -12245,7 +12608,7 @@ function MaterialSection({
|
|
|
12245
12608
|
}))
|
|
12246
12609
|
}
|
|
12247
12610
|
),
|
|
12248
|
-
/* @__PURE__ */
|
|
12611
|
+
/* @__PURE__ */ jsx36(
|
|
12249
12612
|
MaterialTexturePathField,
|
|
12250
12613
|
{
|
|
12251
12614
|
label: "Sheen roughness map path",
|
|
@@ -12257,7 +12620,7 @@ function MaterialSection({
|
|
|
12257
12620
|
] })
|
|
12258
12621
|
}
|
|
12259
12622
|
),
|
|
12260
|
-
/* @__PURE__ */
|
|
12623
|
+
/* @__PURE__ */ jsx36(
|
|
12261
12624
|
MaterialPanelGroup,
|
|
12262
12625
|
{
|
|
12263
12626
|
panelKey: "anisotropy",
|
|
@@ -12265,8 +12628,8 @@ function MaterialSection({
|
|
|
12265
12628
|
title: "Anisotropy",
|
|
12266
12629
|
tooltip: "Stretches reflections in one direction for brushed or fiber-like surfaces.",
|
|
12267
12630
|
controls: panelControls,
|
|
12268
|
-
children: /* @__PURE__ */
|
|
12269
|
-
/* @__PURE__ */
|
|
12631
|
+
children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
|
|
12632
|
+
/* @__PURE__ */ jsx36(
|
|
12270
12633
|
MaterialSliderField,
|
|
12271
12634
|
{
|
|
12272
12635
|
label: "Intensity",
|
|
@@ -12280,7 +12643,7 @@ function MaterialSection({
|
|
|
12280
12643
|
}))
|
|
12281
12644
|
}
|
|
12282
12645
|
),
|
|
12283
|
-
/* @__PURE__ */
|
|
12646
|
+
/* @__PURE__ */ jsx36(
|
|
12284
12647
|
MaterialSelectField,
|
|
12285
12648
|
{
|
|
12286
12649
|
label: "Tilt direction",
|
|
@@ -12289,7 +12652,7 @@ function MaterialSection({
|
|
|
12289
12652
|
onChange: setAnisotropyMode
|
|
12290
12653
|
}
|
|
12291
12654
|
),
|
|
12292
|
-
anisotropyMode === "factor" ? /* @__PURE__ */
|
|
12655
|
+
anisotropyMode === "factor" ? /* @__PURE__ */ jsx36(
|
|
12293
12656
|
MaterialSliderField,
|
|
12294
12657
|
{
|
|
12295
12658
|
label: "Rotation (rad)",
|
|
@@ -12302,7 +12665,7 @@ function MaterialSection({
|
|
|
12302
12665
|
anisotropyRotation: value
|
|
12303
12666
|
}))
|
|
12304
12667
|
}
|
|
12305
|
-
) : /* @__PURE__ */
|
|
12668
|
+
) : /* @__PURE__ */ jsx36(
|
|
12306
12669
|
MaterialTexturePathField,
|
|
12307
12670
|
{
|
|
12308
12671
|
label: "Anisotropy map path",
|
|
@@ -12314,7 +12677,7 @@ function MaterialSection({
|
|
|
12314
12677
|
] })
|
|
12315
12678
|
}
|
|
12316
12679
|
),
|
|
12317
|
-
/* @__PURE__ */
|
|
12680
|
+
/* @__PURE__ */ jsx36(
|
|
12318
12681
|
MaterialPanelGroup,
|
|
12319
12682
|
{
|
|
12320
12683
|
panelKey: "transmission",
|
|
@@ -12323,8 +12686,8 @@ function MaterialSection({
|
|
|
12323
12686
|
tooltip: "Controls glass-like transmission and how light travels through volume.",
|
|
12324
12687
|
toggleLabel: "Toggle Transmission and Volume",
|
|
12325
12688
|
controls: panelControls,
|
|
12326
|
-
children: /* @__PURE__ */
|
|
12327
|
-
/* @__PURE__ */
|
|
12689
|
+
children: /* @__PURE__ */ jsxs31("div", { className: "bb-grid", children: [
|
|
12690
|
+
/* @__PURE__ */ jsx36(
|
|
12328
12691
|
MaterialSliderField,
|
|
12329
12692
|
{
|
|
12330
12693
|
label: "Transmission",
|
|
@@ -12338,7 +12701,7 @@ function MaterialSection({
|
|
|
12338
12701
|
}))
|
|
12339
12702
|
}
|
|
12340
12703
|
),
|
|
12341
|
-
/* @__PURE__ */
|
|
12704
|
+
/* @__PURE__ */ jsx36(
|
|
12342
12705
|
MaterialTexturePathField,
|
|
12343
12706
|
{
|
|
12344
12707
|
label: "Transmission map path",
|
|
@@ -12347,7 +12710,7 @@ function MaterialSection({
|
|
|
12347
12710
|
onChange: (value) => setSelectedTexturePath(selectedItem, "transmissionMap", value)
|
|
12348
12711
|
}
|
|
12349
12712
|
),
|
|
12350
|
-
/* @__PURE__ */
|
|
12713
|
+
/* @__PURE__ */ jsx36(
|
|
12351
12714
|
MaterialSliderField,
|
|
12352
12715
|
{
|
|
12353
12716
|
label: "Thickness",
|
|
@@ -12361,7 +12724,7 @@ function MaterialSection({
|
|
|
12361
12724
|
}))
|
|
12362
12725
|
}
|
|
12363
12726
|
),
|
|
12364
|
-
/* @__PURE__ */
|
|
12727
|
+
/* @__PURE__ */ jsx36(
|
|
12365
12728
|
MaterialTexturePathField,
|
|
12366
12729
|
{
|
|
12367
12730
|
label: "Thickness map path",
|
|
@@ -12370,7 +12733,7 @@ function MaterialSection({
|
|
|
12370
12733
|
onChange: (value) => setSelectedTexturePath(selectedItem, "thicknessMap", value)
|
|
12371
12734
|
}
|
|
12372
12735
|
),
|
|
12373
|
-
/* @__PURE__ */
|
|
12736
|
+
/* @__PURE__ */ jsx36(
|
|
12374
12737
|
MaterialColorField,
|
|
12375
12738
|
{
|
|
12376
12739
|
label: "Volume tint",
|
|
@@ -12381,7 +12744,7 @@ function MaterialSection({
|
|
|
12381
12744
|
}))
|
|
12382
12745
|
}
|
|
12383
12746
|
),
|
|
12384
|
-
/* @__PURE__ */
|
|
12747
|
+
/* @__PURE__ */ jsx36(
|
|
12385
12748
|
MaterialNumberField,
|
|
12386
12749
|
{
|
|
12387
12750
|
label: "Attenuation distance",
|
|
@@ -12398,7 +12761,7 @@ function MaterialSection({
|
|
|
12398
12761
|
] })
|
|
12399
12762
|
}
|
|
12400
12763
|
),
|
|
12401
|
-
/* @__PURE__ */
|
|
12764
|
+
/* @__PURE__ */ jsx36(
|
|
12402
12765
|
MaterialPanelGroup,
|
|
12403
12766
|
{
|
|
12404
12767
|
panelKey: "faces",
|
|
@@ -12406,7 +12769,7 @@ function MaterialSection({
|
|
|
12406
12769
|
title: "Faces Rendering",
|
|
12407
12770
|
tooltip: "Chooses which side of the mesh renders and how backfaces are handled.",
|
|
12408
12771
|
controls: panelControls,
|
|
12409
|
-
children: /* @__PURE__ */
|
|
12772
|
+
children: /* @__PURE__ */ jsx36("div", { className: "bb-grid", children: /* @__PURE__ */ jsx36(
|
|
12410
12773
|
MaterialSelectField,
|
|
12411
12774
|
{
|
|
12412
12775
|
label: "Side",
|
|
@@ -12580,12 +12943,12 @@ var MATERIAL_PRESETS = [
|
|
|
12580
12943
|
];
|
|
12581
12944
|
|
|
12582
12945
|
// src/components/BindingBuilder/components/MaterialPresetGallery.tsx
|
|
12583
|
-
import { jsx as
|
|
12946
|
+
import { jsx as jsx37, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
12584
12947
|
function MaterialPresetGallery({
|
|
12585
12948
|
disabled,
|
|
12586
12949
|
onApply
|
|
12587
12950
|
}) {
|
|
12588
|
-
return /* @__PURE__ */
|
|
12951
|
+
return /* @__PURE__ */ jsx37("div", { className: "bb-presetGallery", children: MATERIAL_PRESETS.map((preset) => /* @__PURE__ */ jsxs32(
|
|
12589
12952
|
"button",
|
|
12590
12953
|
{
|
|
12591
12954
|
type: "button",
|
|
@@ -12594,14 +12957,14 @@ function MaterialPresetGallery({
|
|
|
12594
12957
|
title: `Apply ${preset.label} preset`,
|
|
12595
12958
|
onClick: () => onApply(preset.material),
|
|
12596
12959
|
children: [
|
|
12597
|
-
/* @__PURE__ */
|
|
12960
|
+
/* @__PURE__ */ jsx37(
|
|
12598
12961
|
"span",
|
|
12599
12962
|
{
|
|
12600
12963
|
className: "bb-presetSwatchColor",
|
|
12601
12964
|
style: { background: preset.swatch }
|
|
12602
12965
|
}
|
|
12603
12966
|
),
|
|
12604
|
-
/* @__PURE__ */
|
|
12967
|
+
/* @__PURE__ */ jsx37("span", { className: "bb-presetSwatchLabel", children: preset.label })
|
|
12605
12968
|
]
|
|
12606
12969
|
},
|
|
12607
12970
|
preset.id
|
|
@@ -12609,9 +12972,9 @@ function MaterialPresetGallery({
|
|
|
12609
12972
|
}
|
|
12610
12973
|
|
|
12611
12974
|
// src/components/BindingBuilder/components/ToolbarExportMenu.tsx
|
|
12612
|
-
import { useEffect as
|
|
12975
|
+
import { useEffect as useEffect18, useRef as useRef15, useState as useState22 } from "react";
|
|
12613
12976
|
import { ChevronDown as ChevronDown5, Download as Download2, FileCode2, FileJson } from "lucide-react";
|
|
12614
|
-
import { jsx as
|
|
12977
|
+
import { jsx as jsx38, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
12615
12978
|
function ToolbarExportMenu({
|
|
12616
12979
|
disabled,
|
|
12617
12980
|
exporting,
|
|
@@ -12619,8 +12982,8 @@ function ToolbarExportMenu({
|
|
|
12619
12982
|
onExportTs
|
|
12620
12983
|
}) {
|
|
12621
12984
|
const [open, setOpen] = useState22(false);
|
|
12622
|
-
const wrapperRef =
|
|
12623
|
-
|
|
12985
|
+
const wrapperRef = useRef15(null);
|
|
12986
|
+
useEffect18(() => {
|
|
12624
12987
|
if (!open) return;
|
|
12625
12988
|
const close = (event) => {
|
|
12626
12989
|
if (!wrapperRef.current?.contains(event.target)) {
|
|
@@ -12630,8 +12993,8 @@ function ToolbarExportMenu({
|
|
|
12630
12993
|
window.addEventListener("pointerdown", close);
|
|
12631
12994
|
return () => window.removeEventListener("pointerdown", close);
|
|
12632
12995
|
}, [open]);
|
|
12633
|
-
return /* @__PURE__ */
|
|
12634
|
-
/* @__PURE__ */
|
|
12996
|
+
return /* @__PURE__ */ jsxs33("div", { className: "bb-exportMenuWrapper", ref: wrapperRef, children: [
|
|
12997
|
+
/* @__PURE__ */ jsxs33(
|
|
12635
12998
|
"button",
|
|
12636
12999
|
{
|
|
12637
13000
|
type: "button",
|
|
@@ -12640,14 +13003,14 @@ function ToolbarExportMenu({
|
|
|
12640
13003
|
onClick: () => setOpen((prev) => !prev),
|
|
12641
13004
|
title: "Export",
|
|
12642
13005
|
children: [
|
|
12643
|
-
/* @__PURE__ */
|
|
13006
|
+
/* @__PURE__ */ jsx38(Download2, { size: 14 }),
|
|
12644
13007
|
exporting ? "Exporting\u2026" : "Export",
|
|
12645
|
-
/* @__PURE__ */
|
|
13008
|
+
/* @__PURE__ */ jsx38(ChevronDown5, { size: 12 })
|
|
12646
13009
|
]
|
|
12647
13010
|
}
|
|
12648
13011
|
),
|
|
12649
|
-
open && /* @__PURE__ */
|
|
12650
|
-
/* @__PURE__ */
|
|
13012
|
+
open && /* @__PURE__ */ jsxs33("div", { className: "bb-exportMenu", children: [
|
|
13013
|
+
/* @__PURE__ */ jsxs33(
|
|
12651
13014
|
"button",
|
|
12652
13015
|
{
|
|
12653
13016
|
type: "button",
|
|
@@ -12657,12 +13020,12 @@ function ToolbarExportMenu({
|
|
|
12657
13020
|
void onExportJson();
|
|
12658
13021
|
},
|
|
12659
13022
|
children: [
|
|
12660
|
-
/* @__PURE__ */
|
|
13023
|
+
/* @__PURE__ */ jsx38(FileJson, { size: 14 }),
|
|
12661
13024
|
"JSON"
|
|
12662
13025
|
]
|
|
12663
13026
|
}
|
|
12664
13027
|
),
|
|
12665
|
-
/* @__PURE__ */
|
|
13028
|
+
/* @__PURE__ */ jsxs33(
|
|
12666
13029
|
"button",
|
|
12667
13030
|
{
|
|
12668
13031
|
type: "button",
|
|
@@ -12672,7 +13035,7 @@ function ToolbarExportMenu({
|
|
|
12672
13035
|
void onExportTs();
|
|
12673
13036
|
},
|
|
12674
13037
|
children: [
|
|
12675
|
-
/* @__PURE__ */
|
|
13038
|
+
/* @__PURE__ */ jsx38(FileCode2, { size: 14 }),
|
|
12676
13039
|
"TypeScript"
|
|
12677
13040
|
]
|
|
12678
13041
|
}
|
|
@@ -12682,7 +13045,7 @@ function ToolbarExportMenu({
|
|
|
12682
13045
|
}
|
|
12683
13046
|
|
|
12684
13047
|
// src/components/BindingBuilder/components/IdentityPanel.tsx
|
|
12685
|
-
import { jsx as
|
|
13048
|
+
import { jsx as jsx39, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
12686
13049
|
function IdentityPanel({
|
|
12687
13050
|
selectedItem,
|
|
12688
13051
|
selectedBinding,
|
|
@@ -12691,11 +13054,11 @@ function IdentityPanel({
|
|
|
12691
13054
|
updateBinding,
|
|
12692
13055
|
idError
|
|
12693
13056
|
}) {
|
|
12694
|
-
return /* @__PURE__ */
|
|
12695
|
-
/* @__PURE__ */
|
|
12696
|
-
/* @__PURE__ */
|
|
12697
|
-
/* @__PURE__ */
|
|
12698
|
-
/* @__PURE__ */
|
|
13057
|
+
return /* @__PURE__ */ jsx39(BBSection, { title: "Identity", open, onToggle, children: /* @__PURE__ */ jsxs34("div", { className: "bb-grid", children: [
|
|
13058
|
+
/* @__PURE__ */ jsxs34("div", { className: "bb-field bb-field--full", children: [
|
|
13059
|
+
/* @__PURE__ */ jsx39("label", { className: "bb-label", children: "Model Node ID" }),
|
|
13060
|
+
/* @__PURE__ */ jsx39("span", { className: "bb-note", children: "This is the exact mesh name from the imported model and stays locked." }),
|
|
13061
|
+
/* @__PURE__ */ jsx39(
|
|
12699
13062
|
"input",
|
|
12700
13063
|
{
|
|
12701
13064
|
className: "bb-input bb-readonly",
|
|
@@ -12704,9 +13067,9 @@ function IdentityPanel({
|
|
|
12704
13067
|
}
|
|
12705
13068
|
)
|
|
12706
13069
|
] }),
|
|
12707
|
-
/* @__PURE__ */
|
|
12708
|
-
/* @__PURE__ */
|
|
12709
|
-
/* @__PURE__ */
|
|
13070
|
+
/* @__PURE__ */ jsxs34("div", { className: "bb-field bb-field--full", children: [
|
|
13071
|
+
/* @__PURE__ */ jsx39("label", { className: "bb-label", children: "ID" }),
|
|
13072
|
+
/* @__PURE__ */ jsx39(
|
|
12710
13073
|
"input",
|
|
12711
13074
|
{
|
|
12712
13075
|
className: `bb-input ${idError ? "bb-input--error" : ""}`,
|
|
@@ -12717,7 +13080,7 @@ function IdentityPanel({
|
|
|
12717
13080
|
}))
|
|
12718
13081
|
}
|
|
12719
13082
|
),
|
|
12720
|
-
idError ? /* @__PURE__ */
|
|
13083
|
+
idError ? /* @__PURE__ */ jsx39("span", { className: "bb-status bb-status--error", children: idError }) : null
|
|
12721
13084
|
] })
|
|
12722
13085
|
] }) });
|
|
12723
13086
|
}
|
|
@@ -12753,7 +13116,7 @@ var statusOptions = [
|
|
|
12753
13116
|
];
|
|
12754
13117
|
|
|
12755
13118
|
// src/components/BindingBuilder/components/BasicsPanel.tsx
|
|
12756
|
-
import { jsx as
|
|
13119
|
+
import { jsx as jsx40, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
12757
13120
|
function BasicsPanel({
|
|
12758
13121
|
selectedItem,
|
|
12759
13122
|
selectedBinding,
|
|
@@ -12761,11 +13124,11 @@ function BasicsPanel({
|
|
|
12761
13124
|
onToggle,
|
|
12762
13125
|
updateBinding
|
|
12763
13126
|
}) {
|
|
12764
|
-
return /* @__PURE__ */
|
|
12765
|
-
/* @__PURE__ */
|
|
12766
|
-
/* @__PURE__ */
|
|
12767
|
-
/* @__PURE__ */
|
|
12768
|
-
/* @__PURE__ */
|
|
13127
|
+
return /* @__PURE__ */ jsxs35(BBSection, { title: "Basics", open, onToggle, children: [
|
|
13128
|
+
/* @__PURE__ */ jsxs35("div", { className: "bb-grid", children: [
|
|
13129
|
+
/* @__PURE__ */ jsxs35("div", { className: "bb-field", children: [
|
|
13130
|
+
/* @__PURE__ */ jsx40("label", { className: "bb-label", children: "Label" }),
|
|
13131
|
+
/* @__PURE__ */ jsx40(
|
|
12769
13132
|
"input",
|
|
12770
13133
|
{
|
|
12771
13134
|
className: "bb-input",
|
|
@@ -12777,9 +13140,9 @@ function BasicsPanel({
|
|
|
12777
13140
|
}
|
|
12778
13141
|
)
|
|
12779
13142
|
] }),
|
|
12780
|
-
/* @__PURE__ */
|
|
12781
|
-
/* @__PURE__ */
|
|
12782
|
-
/* @__PURE__ */
|
|
13143
|
+
/* @__PURE__ */ jsxs35("div", { className: "bb-field", children: [
|
|
13144
|
+
/* @__PURE__ */ jsx40("label", { className: "bb-label", children: "Type" }),
|
|
13145
|
+
/* @__PURE__ */ jsx40(
|
|
12783
13146
|
"input",
|
|
12784
13147
|
{
|
|
12785
13148
|
className: "bb-input",
|
|
@@ -12791,11 +13154,11 @@ function BasicsPanel({
|
|
|
12791
13154
|
}))
|
|
12792
13155
|
}
|
|
12793
13156
|
),
|
|
12794
|
-
/* @__PURE__ */
|
|
13157
|
+
/* @__PURE__ */ jsx40("datalist", { id: "binding-type-suggestions", children: typeSuggestions.map((option) => /* @__PURE__ */ jsx40("option", { value: option }, option)) })
|
|
12795
13158
|
] }),
|
|
12796
|
-
/* @__PURE__ */
|
|
12797
|
-
/* @__PURE__ */
|
|
12798
|
-
/* @__PURE__ */
|
|
13159
|
+
/* @__PURE__ */ jsxs35("div", { className: "bb-field", children: [
|
|
13160
|
+
/* @__PURE__ */ jsx40("label", { className: "bb-label", children: "Status" }),
|
|
13161
|
+
/* @__PURE__ */ jsx40(
|
|
12799
13162
|
"select",
|
|
12800
13163
|
{
|
|
12801
13164
|
className: "bb-select",
|
|
@@ -12804,14 +13167,14 @@ function BasicsPanel({
|
|
|
12804
13167
|
...binding,
|
|
12805
13168
|
status: event.target.value
|
|
12806
13169
|
})),
|
|
12807
|
-
children: statusOptions.map((option) => /* @__PURE__ */
|
|
13170
|
+
children: statusOptions.map((option) => /* @__PURE__ */ jsx40("option", { value: option, children: option }, option))
|
|
12808
13171
|
}
|
|
12809
13172
|
)
|
|
12810
13173
|
] })
|
|
12811
13174
|
] }),
|
|
12812
|
-
/* @__PURE__ */
|
|
12813
|
-
/* @__PURE__ */
|
|
12814
|
-
/* @__PURE__ */
|
|
13175
|
+
/* @__PURE__ */ jsxs35("div", { className: "bb-checkboxRow", style: { marginTop: 12 }, children: [
|
|
13176
|
+
/* @__PURE__ */ jsxs35("label", { className: "bb-checkbox", children: [
|
|
13177
|
+
/* @__PURE__ */ jsx40(
|
|
12815
13178
|
"input",
|
|
12816
13179
|
{
|
|
12817
13180
|
type: "checkbox",
|
|
@@ -12824,8 +13187,8 @@ function BasicsPanel({
|
|
|
12824
13187
|
),
|
|
12825
13188
|
"selectable"
|
|
12826
13189
|
] }),
|
|
12827
|
-
/* @__PURE__ */
|
|
12828
|
-
/* @__PURE__ */
|
|
13190
|
+
/* @__PURE__ */ jsxs35("label", { className: "bb-checkbox", children: [
|
|
13191
|
+
/* @__PURE__ */ jsx40(
|
|
12829
13192
|
"input",
|
|
12830
13193
|
{
|
|
12831
13194
|
type: "checkbox",
|
|
@@ -12838,8 +13201,8 @@ function BasicsPanel({
|
|
|
12838
13201
|
),
|
|
12839
13202
|
"hoverable"
|
|
12840
13203
|
] }),
|
|
12841
|
-
/* @__PURE__ */
|
|
12842
|
-
/* @__PURE__ */
|
|
13204
|
+
/* @__PURE__ */ jsxs35("label", { className: "bb-checkbox", children: [
|
|
13205
|
+
/* @__PURE__ */ jsx40(
|
|
12843
13206
|
"input",
|
|
12844
13207
|
{
|
|
12845
13208
|
type: "checkbox",
|
|
@@ -12857,7 +13220,7 @@ function BasicsPanel({
|
|
|
12857
13220
|
}
|
|
12858
13221
|
|
|
12859
13222
|
// src/components/BindingBuilder/components/ActionsPanel.tsx
|
|
12860
|
-
import { jsx as
|
|
13223
|
+
import { jsx as jsx41, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
12861
13224
|
function ActionsPanel({
|
|
12862
13225
|
selectedItem,
|
|
12863
13226
|
selectedBinding,
|
|
@@ -12865,23 +13228,23 @@ function ActionsPanel({
|
|
|
12865
13228
|
onToggle,
|
|
12866
13229
|
updateBinding
|
|
12867
13230
|
}) {
|
|
12868
|
-
return /* @__PURE__ */
|
|
12869
|
-
/* @__PURE__ */
|
|
13231
|
+
return /* @__PURE__ */ jsxs36(BBSection, { title: "Actions", open, onToggle, children: [
|
|
13232
|
+
/* @__PURE__ */ jsx41("div", { className: "bb-actionList", children: (selectedBinding.actions ?? []).map((action, index) => (
|
|
12870
13233
|
// Keyed by position, not action.id: the id is an editable field, so
|
|
12871
13234
|
// keying on it would remount the card on every keystroke and drop
|
|
12872
13235
|
// input focus.
|
|
12873
|
-
/* @__PURE__ */
|
|
12874
|
-
/* @__PURE__ */
|
|
12875
|
-
/* @__PURE__ */
|
|
13236
|
+
/* @__PURE__ */ jsxs36("div", { className: "bb-actionCard", children: [
|
|
13237
|
+
/* @__PURE__ */ jsxs36("div", { className: "bb-actionCardHeader", children: [
|
|
13238
|
+
/* @__PURE__ */ jsxs36("div", { className: "bb-actionCardTitle", children: [
|
|
12876
13239
|
"Action ",
|
|
12877
13240
|
index + 1
|
|
12878
13241
|
] }),
|
|
12879
|
-
/* @__PURE__ */
|
|
13242
|
+
/* @__PURE__ */ jsx41("div", { className: "bb-actionCardMeta", children: action.type })
|
|
12880
13243
|
] }),
|
|
12881
|
-
/* @__PURE__ */
|
|
12882
|
-
/* @__PURE__ */
|
|
12883
|
-
/* @__PURE__ */
|
|
12884
|
-
/* @__PURE__ */
|
|
13244
|
+
/* @__PURE__ */ jsxs36("div", { className: "bb-actionGrid", children: [
|
|
13245
|
+
/* @__PURE__ */ jsxs36("div", { className: "bb-field", children: [
|
|
13246
|
+
/* @__PURE__ */ jsx41("label", { className: "bb-label", children: "Action ID" }),
|
|
13247
|
+
/* @__PURE__ */ jsx41(
|
|
12885
13248
|
"input",
|
|
12886
13249
|
{
|
|
12887
13250
|
className: "bb-input",
|
|
@@ -12895,9 +13258,9 @@ function ActionsPanel({
|
|
|
12895
13258
|
}
|
|
12896
13259
|
)
|
|
12897
13260
|
] }),
|
|
12898
|
-
/* @__PURE__ */
|
|
12899
|
-
/* @__PURE__ */
|
|
12900
|
-
/* @__PURE__ */
|
|
13261
|
+
/* @__PURE__ */ jsxs36("div", { className: "bb-field", children: [
|
|
13262
|
+
/* @__PURE__ */ jsx41("label", { className: "bb-label", children: "Label" }),
|
|
13263
|
+
/* @__PURE__ */ jsx41(
|
|
12901
13264
|
"input",
|
|
12902
13265
|
{
|
|
12903
13266
|
className: "bb-input",
|
|
@@ -12912,7 +13275,7 @@ function ActionsPanel({
|
|
|
12912
13275
|
)
|
|
12913
13276
|
] })
|
|
12914
13277
|
] }),
|
|
12915
|
-
/* @__PURE__ */
|
|
13278
|
+
/* @__PURE__ */ jsx41("div", { className: "bb-actionFooter", children: /* @__PURE__ */ jsx41(
|
|
12916
13279
|
"button",
|
|
12917
13280
|
{
|
|
12918
13281
|
type: "button",
|
|
@@ -12928,7 +13291,7 @@ function ActionsPanel({
|
|
|
12928
13291
|
) })
|
|
12929
13292
|
] }, index)
|
|
12930
13293
|
)) }),
|
|
12931
|
-
/* @__PURE__ */
|
|
13294
|
+
/* @__PURE__ */ jsx41("div", { style: { marginTop: 10 }, children: /* @__PURE__ */ jsx41(
|
|
12932
13295
|
"button",
|
|
12933
13296
|
{
|
|
12934
13297
|
type: "button",
|
|
@@ -12951,7 +13314,8 @@ function ActionsPanel({
|
|
|
12951
13314
|
}
|
|
12952
13315
|
|
|
12953
13316
|
// src/components/BindingBuilder/components/CameraPositionPanel.tsx
|
|
12954
|
-
import {
|
|
13317
|
+
import { MapPin as MapPin3, Trash2 as Trash25 } from "lucide-react";
|
|
13318
|
+
import { Fragment as Fragment10, jsx as jsx42, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
12955
13319
|
function CameraPositionPanel({
|
|
12956
13320
|
selectedBinding,
|
|
12957
13321
|
open,
|
|
@@ -12960,67 +13324,30 @@ function CameraPositionPanel({
|
|
|
12960
13324
|
formattedCameraState,
|
|
12961
13325
|
savedCameraPosition,
|
|
12962
13326
|
onSaveCameraPosition,
|
|
13327
|
+
onGoToSavedCamera,
|
|
13328
|
+
onClearSavedCamera,
|
|
12963
13329
|
onResetView,
|
|
12964
13330
|
onFitScene
|
|
12965
13331
|
}) {
|
|
12966
|
-
return /* @__PURE__ */
|
|
12967
|
-
/* @__PURE__ */
|
|
12968
|
-
/* @__PURE__ */
|
|
12969
|
-
|
|
12970
|
-
/* @__PURE__ */
|
|
12971
|
-
|
|
12972
|
-
|
|
12973
|
-
|
|
12974
|
-
|
|
12975
|
-
|
|
12976
|
-
|
|
12977
|
-
|
|
12978
|
-
|
|
12979
|
-
/* @__PURE__ */ jsx41("code", { className: "bb-cameraCode", children: formattedCameraState?.target })
|
|
12980
|
-
] })
|
|
12981
|
-
] }) : /* @__PURE__ */ jsx41("div", { className: "bb-cameraPlaceholder", children: "Move the camera to see real-time position updates" })
|
|
12982
|
-
] }),
|
|
12983
|
-
savedCameraPosition?.position && savedCameraPosition?.target && /* @__PURE__ */ jsxs36("div", { className: "bb-cameraSaved", children: [
|
|
12984
|
-
/* @__PURE__ */ jsxs36("div", { className: "bb-cameraSavedHeader", children: [
|
|
12985
|
-
/* @__PURE__ */ jsx41("span", { className: "bb-cameraSavedBadge", children: "SAVED" }),
|
|
12986
|
-
/* @__PURE__ */ jsxs36("span", { className: "bb-cameraSavedLabel", children: [
|
|
12987
|
-
"For ",
|
|
12988
|
-
selectedBinding.label
|
|
12989
|
-
] })
|
|
12990
|
-
] }),
|
|
12991
|
-
/* @__PURE__ */ jsxs36("div", { className: "bb-cameraValues", children: [
|
|
12992
|
-
/* @__PURE__ */ jsxs36("div", { className: "bb-cameraRow", children: [
|
|
12993
|
-
/* @__PURE__ */ jsx41("span", { className: "bb-cameraLabel", children: "Position:" }),
|
|
12994
|
-
/* @__PURE__ */ jsxs36("code", { className: "bb-cameraCode", children: [
|
|
12995
|
-
"[",
|
|
12996
|
-
savedCameraPosition.position[0].toFixed(3),
|
|
12997
|
-
",",
|
|
12998
|
-
" ",
|
|
12999
|
-
savedCameraPosition.position[1].toFixed(3),
|
|
13000
|
-
",",
|
|
13001
|
-
" ",
|
|
13002
|
-
savedCameraPosition.position[2].toFixed(3),
|
|
13003
|
-
"]"
|
|
13004
|
-
] })
|
|
13005
|
-
] }),
|
|
13006
|
-
/* @__PURE__ */ jsxs36("div", { className: "bb-cameraRow", children: [
|
|
13007
|
-
/* @__PURE__ */ jsx41("span", { className: "bb-cameraLabel", children: "Target:" }),
|
|
13008
|
-
/* @__PURE__ */ jsxs36("code", { className: "bb-cameraCode", children: [
|
|
13009
|
-
"[",
|
|
13010
|
-
savedCameraPosition.target[0].toFixed(3),
|
|
13011
|
-
",",
|
|
13012
|
-
" ",
|
|
13013
|
-
savedCameraPosition.target[1].toFixed(3),
|
|
13014
|
-
",",
|
|
13015
|
-
" ",
|
|
13016
|
-
savedCameraPosition.target[2].toFixed(3),
|
|
13017
|
-
"]"
|
|
13332
|
+
return /* @__PURE__ */ jsxs37(BBSection, { title: "Camera Position", open, onToggle, children: [
|
|
13333
|
+
/* @__PURE__ */ jsxs37("div", { className: "se-waypointCard bb-cameraWaypoint", children: [
|
|
13334
|
+
/* @__PURE__ */ jsx42("span", { className: "bb-cameraLiveBadge", children: "LIVE" }),
|
|
13335
|
+
/* @__PURE__ */ jsxs37("div", { className: "se-waypointBody", children: [
|
|
13336
|
+
/* @__PURE__ */ jsx42("div", { className: "bb-cameraWaypointLabel", children: "Current view" }),
|
|
13337
|
+
cameraState ? /* @__PURE__ */ jsxs37(Fragment10, { children: [
|
|
13338
|
+
/* @__PURE__ */ jsxs37("div", { className: "se-waypointCoords", children: [
|
|
13339
|
+
"pos ",
|
|
13340
|
+
formattedCameraState?.position
|
|
13341
|
+
] }),
|
|
13342
|
+
/* @__PURE__ */ jsxs37("div", { className: "se-waypointCoords", children: [
|
|
13343
|
+
"look ",
|
|
13344
|
+
formattedCameraState?.target
|
|
13018
13345
|
] })
|
|
13019
|
-
] })
|
|
13346
|
+
] }) : /* @__PURE__ */ jsx42("div", { className: "se-waypointCoords", children: "Move the camera to see live position" })
|
|
13020
13347
|
] })
|
|
13021
13348
|
] }),
|
|
13022
|
-
/* @__PURE__ */
|
|
13023
|
-
/* @__PURE__ */
|
|
13349
|
+
/* @__PURE__ */ jsxs37("div", { className: "bb-cameraControls bb-cameraControls--centered", children: [
|
|
13350
|
+
/* @__PURE__ */ jsx42(
|
|
13024
13351
|
"button",
|
|
13025
13352
|
{
|
|
13026
13353
|
type: "button",
|
|
@@ -13030,7 +13357,7 @@ function CameraPositionPanel({
|
|
|
13030
13357
|
children: "Save"
|
|
13031
13358
|
}
|
|
13032
13359
|
),
|
|
13033
|
-
/* @__PURE__ */
|
|
13360
|
+
/* @__PURE__ */ jsx42(
|
|
13034
13361
|
"button",
|
|
13035
13362
|
{
|
|
13036
13363
|
type: "button",
|
|
@@ -13039,7 +13366,7 @@ function CameraPositionPanel({
|
|
|
13039
13366
|
children: "Reset View"
|
|
13040
13367
|
}
|
|
13041
13368
|
),
|
|
13042
|
-
/* @__PURE__ */
|
|
13369
|
+
/* @__PURE__ */ jsx42(
|
|
13043
13370
|
"button",
|
|
13044
13371
|
{
|
|
13045
13372
|
type: "button",
|
|
@@ -13049,12 +13376,51 @@ function CameraPositionPanel({
|
|
|
13049
13376
|
}
|
|
13050
13377
|
)
|
|
13051
13378
|
] }),
|
|
13052
|
-
/* @__PURE__ */
|
|
13379
|
+
savedCameraPosition?.position && savedCameraPosition?.target && /* @__PURE__ */ jsxs37("div", { className: "se-waypointCard bb-cameraWaypoint", children: [
|
|
13380
|
+
/* @__PURE__ */ jsx42("span", { className: "bb-cameraSavedBadge", children: "SAVED" }),
|
|
13381
|
+
/* @__PURE__ */ jsxs37("div", { className: "se-waypointBody", children: [
|
|
13382
|
+
/* @__PURE__ */ jsxs37("div", { className: "bb-cameraWaypointLabel", children: [
|
|
13383
|
+
"For ",
|
|
13384
|
+
selectedBinding.label
|
|
13385
|
+
] }),
|
|
13386
|
+
/* @__PURE__ */ jsxs37("div", { className: "se-waypointCoords", children: [
|
|
13387
|
+
"pos [",
|
|
13388
|
+
savedCameraPosition.position.map((n) => n.toFixed(1)).join(", "),
|
|
13389
|
+
"]"
|
|
13390
|
+
] }),
|
|
13391
|
+
/* @__PURE__ */ jsxs37("div", { className: "se-waypointCoords", children: [
|
|
13392
|
+
"look [",
|
|
13393
|
+
savedCameraPosition.target.map((n) => n.toFixed(1)).join(", "),
|
|
13394
|
+
"]"
|
|
13395
|
+
] })
|
|
13396
|
+
] }),
|
|
13397
|
+
/* @__PURE__ */ jsxs37("div", { className: "se-waypointActions", children: [
|
|
13398
|
+
/* @__PURE__ */ jsx42(
|
|
13399
|
+
"button",
|
|
13400
|
+
{
|
|
13401
|
+
className: "se-waypointAction",
|
|
13402
|
+
onClick: onGoToSavedCamera,
|
|
13403
|
+
title: "Fly camera to the saved position",
|
|
13404
|
+
children: /* @__PURE__ */ jsx42(MapPin3, { size: 15 })
|
|
13405
|
+
}
|
|
13406
|
+
),
|
|
13407
|
+
/* @__PURE__ */ jsx42(
|
|
13408
|
+
"button",
|
|
13409
|
+
{
|
|
13410
|
+
className: "se-waypointAction se-waypointAction--delete",
|
|
13411
|
+
onClick: onClearSavedCamera,
|
|
13412
|
+
title: "Delete saved position",
|
|
13413
|
+
children: /* @__PURE__ */ jsx42(Trash25, { size: 15 })
|
|
13414
|
+
}
|
|
13415
|
+
)
|
|
13416
|
+
] })
|
|
13417
|
+
] }),
|
|
13418
|
+
/* @__PURE__ */ jsx42("span", { className: "bb-note", children: 'Tip: Position the camera how you want, then click "Save" to store this position for the selected object. The camera position updates in real-time as you navigate.' })
|
|
13053
13419
|
] });
|
|
13054
13420
|
}
|
|
13055
13421
|
|
|
13056
13422
|
// src/components/BindingBuilder/components/SceneNodesPanel.tsx
|
|
13057
|
-
import { useMemo as
|
|
13423
|
+
import { useMemo as useMemo15, useState as useState23 } from "react";
|
|
13058
13424
|
|
|
13059
13425
|
// src/components/BindingBuilder/utils/bindingFactory.ts
|
|
13060
13426
|
import { GLTFLoader } from "three-stdlib";
|
|
@@ -13172,7 +13538,7 @@ function collectModelMetadata(modelUrl) {
|
|
|
13172
13538
|
}
|
|
13173
13539
|
|
|
13174
13540
|
// src/components/BindingBuilder/components/SceneNodesPanel.tsx
|
|
13175
|
-
import { Fragment as
|
|
13541
|
+
import { Fragment as Fragment11, jsx as jsx43, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
13176
13542
|
function SceneNodesPanel({
|
|
13177
13543
|
bindings,
|
|
13178
13544
|
selectedItem,
|
|
@@ -13182,8 +13548,8 @@ function SceneNodesPanel({
|
|
|
13182
13548
|
onToggle
|
|
13183
13549
|
}) {
|
|
13184
13550
|
const [search, setSearch] = useState23("");
|
|
13185
|
-
const nodeKeys =
|
|
13186
|
-
const filteredNodeKeys =
|
|
13551
|
+
const nodeKeys = useMemo15(() => Object.keys(bindings), [bindings]);
|
|
13552
|
+
const filteredNodeKeys = useMemo15(() => {
|
|
13187
13553
|
const query = search.trim().toLowerCase();
|
|
13188
13554
|
if (!query) return nodeKeys;
|
|
13189
13555
|
return nodeKeys.filter((nodeKey) => {
|
|
@@ -13191,13 +13557,13 @@ function SceneNodesPanel({
|
|
|
13191
13557
|
return nodeKey.toLowerCase().includes(query) || binding.label?.toLowerCase().includes(query) || binding.type.toLowerCase().includes(query);
|
|
13192
13558
|
});
|
|
13193
13559
|
}, [bindings, nodeKeys, search]);
|
|
13194
|
-
return /* @__PURE__ */
|
|
13560
|
+
return /* @__PURE__ */ jsxs38(
|
|
13195
13561
|
"aside",
|
|
13196
13562
|
{
|
|
13197
13563
|
className: `bb-sidebar bb-sidebar--right ${isOpen ? "" : "bb-sidebar--collapsed"}`,
|
|
13198
13564
|
children: [
|
|
13199
|
-
/* @__PURE__ */
|
|
13200
|
-
!isOpen && /* @__PURE__ */
|
|
13565
|
+
/* @__PURE__ */ jsxs38("div", { className: "bb-header bb-header--sidebar", children: [
|
|
13566
|
+
!isOpen && /* @__PURE__ */ jsx43(
|
|
13201
13567
|
"button",
|
|
13202
13568
|
{
|
|
13203
13569
|
type: "button",
|
|
@@ -13208,15 +13574,15 @@ function SceneNodesPanel({
|
|
|
13208
13574
|
children: "\u2190"
|
|
13209
13575
|
}
|
|
13210
13576
|
),
|
|
13211
|
-
isOpen && /* @__PURE__ */
|
|
13212
|
-
/* @__PURE__ */
|
|
13213
|
-
/* @__PURE__ */
|
|
13214
|
-
/* @__PURE__ */
|
|
13577
|
+
isOpen && /* @__PURE__ */ jsxs38(Fragment11, { children: [
|
|
13578
|
+
/* @__PURE__ */ jsxs38("div", { children: [
|
|
13579
|
+
/* @__PURE__ */ jsx43("div", { className: "bb-sectionKicker", children: "Scene Nodes" }),
|
|
13580
|
+
/* @__PURE__ */ jsxs38("div", { className: "bb-sectionHeading", children: [
|
|
13215
13581
|
filteredNodeKeys.length,
|
|
13216
13582
|
" visible entries"
|
|
13217
13583
|
] })
|
|
13218
13584
|
] }),
|
|
13219
|
-
/* @__PURE__ */
|
|
13585
|
+
/* @__PURE__ */ jsx43(
|
|
13220
13586
|
"button",
|
|
13221
13587
|
{
|
|
13222
13588
|
type: "button",
|
|
@@ -13229,8 +13595,8 @@ function SceneNodesPanel({
|
|
|
13229
13595
|
)
|
|
13230
13596
|
] })
|
|
13231
13597
|
] }),
|
|
13232
|
-
isOpen && /* @__PURE__ */
|
|
13233
|
-
/* @__PURE__ */
|
|
13598
|
+
isOpen && /* @__PURE__ */ jsxs38("section", { className: "bb-nodeList", children: [
|
|
13599
|
+
/* @__PURE__ */ jsx43("div", { className: "bb-search", children: /* @__PURE__ */ jsx43(
|
|
13234
13600
|
"input",
|
|
13235
13601
|
{
|
|
13236
13602
|
className: "bb-input",
|
|
@@ -13239,10 +13605,10 @@ function SceneNodesPanel({
|
|
|
13239
13605
|
onChange: (event) => setSearch(event.target.value)
|
|
13240
13606
|
}
|
|
13241
13607
|
) }),
|
|
13242
|
-
/* @__PURE__ */
|
|
13608
|
+
/* @__PURE__ */ jsx43("div", { className: "bb-nodeItems", children: filteredNodeKeys.map((nodeKey) => {
|
|
13243
13609
|
const binding = bindings[nodeKey];
|
|
13244
13610
|
const isActive = nodeKey === selectedItem;
|
|
13245
|
-
return /* @__PURE__ */
|
|
13611
|
+
return /* @__PURE__ */ jsxs38(
|
|
13246
13612
|
"button",
|
|
13247
13613
|
{
|
|
13248
13614
|
type: "button",
|
|
@@ -13251,8 +13617,8 @@ function SceneNodesPanel({
|
|
|
13251
13617
|
onMouseEnter: () => onHover?.(nodeKey),
|
|
13252
13618
|
onMouseLeave: () => onHover?.(null),
|
|
13253
13619
|
children: [
|
|
13254
|
-
/* @__PURE__ */
|
|
13255
|
-
/* @__PURE__ */
|
|
13620
|
+
/* @__PURE__ */ jsx43("span", { className: "bb-nodeName", children: nodeKey }),
|
|
13621
|
+
/* @__PURE__ */ jsxs38("span", { className: "bb-nodeMeta", children: [
|
|
13256
13622
|
binding.type,
|
|
13257
13623
|
binding.label === humanizeNodeName(nodeKey) ? "" : binding.label || "\xB7 Untitled"
|
|
13258
13624
|
] })
|
|
@@ -13268,7 +13634,7 @@ function SceneNodesPanel({
|
|
|
13268
13634
|
}
|
|
13269
13635
|
|
|
13270
13636
|
// src/components/BindingBuilder/components/JsonPanel.tsx
|
|
13271
|
-
import { jsx as
|
|
13637
|
+
import { jsx as jsx44, jsxs as jsxs39 } from "react/jsx-runtime";
|
|
13272
13638
|
function JsonPanel({
|
|
13273
13639
|
title,
|
|
13274
13640
|
label,
|
|
@@ -13280,9 +13646,9 @@ function JsonPanel({
|
|
|
13280
13646
|
onDraftChange,
|
|
13281
13647
|
onCommit
|
|
13282
13648
|
}) {
|
|
13283
|
-
return /* @__PURE__ */
|
|
13284
|
-
/* @__PURE__ */
|
|
13285
|
-
/* @__PURE__ */
|
|
13649
|
+
return /* @__PURE__ */ jsx44(BBSection, { title, open, onToggle, children: /* @__PURE__ */ jsxs39("div", { className: "bb-field", children: [
|
|
13650
|
+
/* @__PURE__ */ jsx44("label", { className: "bb-label", children: label }),
|
|
13651
|
+
/* @__PURE__ */ jsx44(
|
|
13286
13652
|
"textarea",
|
|
13287
13653
|
{
|
|
13288
13654
|
className: "bb-textarea",
|
|
@@ -13291,7 +13657,7 @@ function JsonPanel({
|
|
|
13291
13657
|
onBlur: onCommit
|
|
13292
13658
|
}
|
|
13293
13659
|
),
|
|
13294
|
-
error ? /* @__PURE__ */
|
|
13660
|
+
error ? /* @__PURE__ */ jsx44("span", { className: "bb-status bb-status--error", children: error }) : /* @__PURE__ */ jsxs39("span", { className: "bb-note", children: [
|
|
13295
13661
|
"Use a JSON object like ",
|
|
13296
13662
|
example,
|
|
13297
13663
|
"."
|
|
@@ -13326,7 +13692,7 @@ function mergeAnimationConfig(sceneConfig, animationDetails) {
|
|
|
13326
13692
|
}
|
|
13327
13693
|
|
|
13328
13694
|
// src/components/BindingBuilder/hooks/useUndoableState.ts
|
|
13329
|
-
import { useCallback as useCallback7, useReducer, useRef as
|
|
13695
|
+
import { useCallback as useCallback7, useReducer, useRef as useRef16 } from "react";
|
|
13330
13696
|
var MAX_HISTORY = 100;
|
|
13331
13697
|
function historyReducer(state, action) {
|
|
13332
13698
|
switch (action.type) {
|
|
@@ -13370,7 +13736,7 @@ function useUndoableState(initial, coalesceWindowMs = 600) {
|
|
|
13370
13736
|
present: initial,
|
|
13371
13737
|
future: []
|
|
13372
13738
|
});
|
|
13373
|
-
const lastSetAt =
|
|
13739
|
+
const lastSetAt = useRef16(0);
|
|
13374
13740
|
const setValue = useCallback7(
|
|
13375
13741
|
(updater) => {
|
|
13376
13742
|
const now = Date.now();
|
|
@@ -13617,7 +13983,7 @@ function removeMaterialPanelFields(material, panel) {
|
|
|
13617
13983
|
}
|
|
13618
13984
|
|
|
13619
13985
|
// src/components/BindingBuilder/BindingBuilder.tsx
|
|
13620
|
-
import { Fragment as
|
|
13986
|
+
import { Fragment as Fragment12, jsx as jsx45, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
13621
13987
|
var DEMO_MODEL_URL = "/room.glb";
|
|
13622
13988
|
function isPlainObject(value) {
|
|
13623
13989
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -13634,9 +14000,9 @@ function mergeSceneConfigSection(current, incoming) {
|
|
|
13634
14000
|
return next;
|
|
13635
14001
|
}
|
|
13636
14002
|
function BindingBuilder({ licenseKey }) {
|
|
13637
|
-
const modelInputRef =
|
|
13638
|
-
const bindingsInputRef =
|
|
13639
|
-
const sceneConfigInputRef =
|
|
14003
|
+
const modelInputRef = useRef17(null);
|
|
14004
|
+
const bindingsInputRef = useRef17(null);
|
|
14005
|
+
const sceneConfigInputRef = useRef17(null);
|
|
13640
14006
|
const [modelUrl, setModelUrl] = useState24(null);
|
|
13641
14007
|
const [modelName, setModelName] = useState24("");
|
|
13642
14008
|
const [selectedItem, setSelectedItem] = useState24(null);
|
|
@@ -13688,7 +14054,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
13688
14054
|
const [activeSubTab, setActiveSubTab] = useState24("General");
|
|
13689
14055
|
const [pbrWorkflow, setPbrWorkflow] = useState24("metalness");
|
|
13690
14056
|
const [materialPanelEnabled, setMaterialPanelEnabled] = useState24(createMaterialPanelEnabledState());
|
|
13691
|
-
const materialPanelItemRef =
|
|
14057
|
+
const materialPanelItemRef = useRef17(null);
|
|
13692
14058
|
const [zoomOnSelected, setZoomOnSelected] = useState24(false);
|
|
13693
14059
|
const [highlightOnHover, setHighlightOnHover] = useState24(true);
|
|
13694
14060
|
const [resetDialogOpen, setResetDialogOpen] = useState24(false);
|
|
@@ -13702,7 +14068,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
13702
14068
|
const [panelHoveredBinding, setPanelHoveredBinding] = useState24(null);
|
|
13703
14069
|
const license = useLicense(licenseKey);
|
|
13704
14070
|
const tier = normalizeTier(license.tier);
|
|
13705
|
-
const bindingBuilderContextValue =
|
|
14071
|
+
const bindingBuilderContextValue = useMemo16(
|
|
13706
14072
|
() => ({
|
|
13707
14073
|
isInsideEditor: true,
|
|
13708
14074
|
activeTab: activeEditorTab,
|
|
@@ -13711,17 +14077,32 @@ function BindingBuilder({ licenseKey }) {
|
|
|
13711
14077
|
}),
|
|
13712
14078
|
[activeEditorTab, activeSubTab, tier]
|
|
13713
14079
|
);
|
|
13714
|
-
const uploadedModelRef =
|
|
13715
|
-
const previewDragDepthRef =
|
|
13716
|
-
const loadRequestRef =
|
|
14080
|
+
const uploadedModelRef = useRef17(null);
|
|
14081
|
+
const previewDragDepthRef = useRef17(0);
|
|
14082
|
+
const loadRequestRef = useRef17(0);
|
|
13717
14083
|
const {
|
|
13718
14084
|
cameraState,
|
|
13719
14085
|
resetView,
|
|
13720
14086
|
focusObject,
|
|
13721
14087
|
fitScene,
|
|
14088
|
+
setCameraState,
|
|
13722
14089
|
handleCameraChange,
|
|
13723
14090
|
handleViewerReady
|
|
13724
14091
|
} = useViewerCamera();
|
|
14092
|
+
const handlePreviewWaypoint = useCallback8(
|
|
14093
|
+
(waypoint) => {
|
|
14094
|
+
setCameraState(
|
|
14095
|
+
{
|
|
14096
|
+
position: waypoint.position,
|
|
14097
|
+
target: waypoint.target,
|
|
14098
|
+
fov: cameraState?.fov ?? null,
|
|
14099
|
+
zoom: cameraState?.zoom ?? 1
|
|
14100
|
+
},
|
|
14101
|
+
true
|
|
14102
|
+
);
|
|
14103
|
+
},
|
|
14104
|
+
[setCameraState, cameraState]
|
|
14105
|
+
);
|
|
13725
14106
|
const viewerAnimations = useViewerAnimations();
|
|
13726
14107
|
const selectedBinding = selectedItem ? bindings[selectedItem] ?? null : null;
|
|
13727
14108
|
const selectedMaterial = selectedBinding?.style?.material;
|
|
@@ -13756,7 +14137,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
13756
14137
|
},
|
|
13757
14138
|
[]
|
|
13758
14139
|
);
|
|
13759
|
-
|
|
14140
|
+
useEffect19(() => {
|
|
13760
14141
|
if (!selectedBinding) {
|
|
13761
14142
|
setMetricsDraft("{}");
|
|
13762
14143
|
setMetadataDraft("{}");
|
|
@@ -13769,7 +14150,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
13769
14150
|
setMetricsError(null);
|
|
13770
14151
|
setMetadataError(null);
|
|
13771
14152
|
}, [selectedBinding]);
|
|
13772
|
-
|
|
14153
|
+
useEffect19(() => {
|
|
13773
14154
|
const derived = createMaterialPanelEnabledState(selectedMaterial);
|
|
13774
14155
|
if (materialPanelItemRef.current !== selectedItem) {
|
|
13775
14156
|
materialPanelItemRef.current = selectedItem;
|
|
@@ -13788,12 +14169,12 @@ function BindingBuilder({ licenseKey }) {
|
|
|
13788
14169
|
return changed ? next : prev;
|
|
13789
14170
|
});
|
|
13790
14171
|
}, [selectedItem, selectedMaterial]);
|
|
13791
|
-
|
|
14172
|
+
useEffect19(() => {
|
|
13792
14173
|
return () => {
|
|
13793
14174
|
uploadedModelRef.current?.cleanup();
|
|
13794
14175
|
};
|
|
13795
14176
|
}, []);
|
|
13796
|
-
|
|
14177
|
+
useEffect19(() => {
|
|
13797
14178
|
const handleKeyDown = (event) => {
|
|
13798
14179
|
if (event.key.toLowerCase() !== "z" || !(event.metaKey || event.ctrlKey)) {
|
|
13799
14180
|
return;
|
|
@@ -13814,7 +14195,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
13814
14195
|
window.addEventListener("keydown", handleKeyDown);
|
|
13815
14196
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
13816
14197
|
}, [activeEditorTab, bindingsHistory, sceneConfigHistory]);
|
|
13817
|
-
|
|
14198
|
+
useEffect19(() => {
|
|
13818
14199
|
if (loading || statusTone === "default" || statusTone === "success") return;
|
|
13819
14200
|
setStatusBannerVisible(true);
|
|
13820
14201
|
const timeout = window.setTimeout(
|
|
@@ -13823,8 +14204,8 @@ function BindingBuilder({ licenseKey }) {
|
|
|
13823
14204
|
);
|
|
13824
14205
|
return () => window.clearTimeout(timeout);
|
|
13825
14206
|
}, [statusMessage, statusTone, loading]);
|
|
13826
|
-
const nodeKeys =
|
|
13827
|
-
const idError =
|
|
14207
|
+
const nodeKeys = useMemo16(() => Object.keys(bindings), [bindings]);
|
|
14208
|
+
const idError = useMemo16(() => {
|
|
13828
14209
|
if (!selectedItem || !selectedBinding) return null;
|
|
13829
14210
|
const trimmed = selectedBinding.id.trim();
|
|
13830
14211
|
if (!trimmed) return "ID is required.";
|
|
@@ -14091,7 +14472,8 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14091
14472
|
"groundShadows",
|
|
14092
14473
|
"postProcessing",
|
|
14093
14474
|
"animations",
|
|
14094
|
-
"annotations"
|
|
14475
|
+
"annotations",
|
|
14476
|
+
"cinematic"
|
|
14095
14477
|
];
|
|
14096
14478
|
const incoming = parsed;
|
|
14097
14479
|
const matchedKeys = sceneConfigKeys.filter(
|
|
@@ -14222,6 +14604,25 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14222
14604
|
cameraState: currentCameraState
|
|
14223
14605
|
}));
|
|
14224
14606
|
}, [selectedItem, cameraState]);
|
|
14607
|
+
const goToSavedCameraPosition = useCallback8(() => {
|
|
14608
|
+
if (!savedCameraPosition?.position || !savedCameraPosition?.target) return;
|
|
14609
|
+
setCameraState(
|
|
14610
|
+
{
|
|
14611
|
+
position: savedCameraPosition.position,
|
|
14612
|
+
target: savedCameraPosition.target,
|
|
14613
|
+
fov: savedCameraPosition.fov ?? cameraState?.fov ?? null,
|
|
14614
|
+
zoom: savedCameraPosition.zoom ?? cameraState?.zoom ?? 1
|
|
14615
|
+
},
|
|
14616
|
+
true
|
|
14617
|
+
);
|
|
14618
|
+
}, [savedCameraPosition, setCameraState, cameraState]);
|
|
14619
|
+
const clearSavedCameraPosition = useCallback8(() => {
|
|
14620
|
+
if (!selectedItem) return;
|
|
14621
|
+
updateBinding(selectedItem, (binding) => ({
|
|
14622
|
+
...binding,
|
|
14623
|
+
cameraState: void 0
|
|
14624
|
+
}));
|
|
14625
|
+
}, [selectedItem]);
|
|
14225
14626
|
const handleResetView = useCallback8(async () => {
|
|
14226
14627
|
await resetView();
|
|
14227
14628
|
}, [resetView]);
|
|
@@ -14261,7 +14662,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14261
14662
|
closeResetDialog
|
|
14262
14663
|
]);
|
|
14263
14664
|
if (license.status === "checking") {
|
|
14264
|
-
return /* @__PURE__ */
|
|
14665
|
+
return /* @__PURE__ */ jsx45("div", { className: "bb-root bb-root--licenseChecking", children: /* @__PURE__ */ jsx45(
|
|
14265
14666
|
LicenseOverlay,
|
|
14266
14667
|
{
|
|
14267
14668
|
title: "Verifying License",
|
|
@@ -14270,7 +14671,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14270
14671
|
) });
|
|
14271
14672
|
}
|
|
14272
14673
|
if (license.status === "invalid") {
|
|
14273
|
-
return /* @__PURE__ */
|
|
14674
|
+
return /* @__PURE__ */ jsx45("div", { className: "bb-root bb-root--licenseInvalid", children: /* @__PURE__ */ jsx45(
|
|
14274
14675
|
LicenseOverlay,
|
|
14275
14676
|
{
|
|
14276
14677
|
tone: "error",
|
|
@@ -14279,9 +14680,9 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14279
14680
|
}
|
|
14280
14681
|
) });
|
|
14281
14682
|
}
|
|
14282
|
-
return /* @__PURE__ */
|
|
14283
|
-
/* @__PURE__ */
|
|
14284
|
-
/* @__PURE__ */
|
|
14683
|
+
return /* @__PURE__ */ jsxs40(BindingBuilderContext.Provider, { value: bindingBuilderContextValue, children: [
|
|
14684
|
+
/* @__PURE__ */ jsxs40("div", { className: "bb-root", children: [
|
|
14685
|
+
/* @__PURE__ */ jsx45(
|
|
14285
14686
|
"input",
|
|
14286
14687
|
{
|
|
14287
14688
|
ref: modelInputRef,
|
|
@@ -14292,7 +14693,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14292
14693
|
disabled: loading
|
|
14293
14694
|
}
|
|
14294
14695
|
),
|
|
14295
|
-
/* @__PURE__ */
|
|
14696
|
+
/* @__PURE__ */ jsx45(
|
|
14296
14697
|
"input",
|
|
14297
14698
|
{
|
|
14298
14699
|
ref: bindingsInputRef,
|
|
@@ -14303,7 +14704,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14303
14704
|
disabled: loading
|
|
14304
14705
|
}
|
|
14305
14706
|
),
|
|
14306
|
-
/* @__PURE__ */
|
|
14707
|
+
/* @__PURE__ */ jsx45(
|
|
14307
14708
|
"input",
|
|
14308
14709
|
{
|
|
14309
14710
|
ref: sceneConfigInputRef,
|
|
@@ -14314,14 +14715,14 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14314
14715
|
disabled: loading
|
|
14315
14716
|
}
|
|
14316
14717
|
),
|
|
14317
|
-
/* @__PURE__ */
|
|
14718
|
+
/* @__PURE__ */ jsxs40(
|
|
14318
14719
|
"aside",
|
|
14319
14720
|
{
|
|
14320
14721
|
className: `bb-sidebar bb-sidebar--left ${isEditorCollapsed ? "bb-sidebar--collapsed" : ""}`,
|
|
14321
14722
|
children: [
|
|
14322
|
-
/* @__PURE__ */
|
|
14323
|
-
!isEditorCollapsed && /* @__PURE__ */
|
|
14324
|
-
/* @__PURE__ */
|
|
14723
|
+
/* @__PURE__ */ jsxs40("div", { className: "bb-header bb-header--sidebar", children: [
|
|
14724
|
+
!isEditorCollapsed && /* @__PURE__ */ jsx45("h1", { className: "bb-title", children: "Binding Builder" }),
|
|
14725
|
+
/* @__PURE__ */ jsx45(
|
|
14325
14726
|
"button",
|
|
14326
14727
|
{
|
|
14327
14728
|
type: "button",
|
|
@@ -14333,9 +14734,9 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14333
14734
|
}
|
|
14334
14735
|
)
|
|
14335
14736
|
] }),
|
|
14336
|
-
!isEditorCollapsed && /* @__PURE__ */
|
|
14337
|
-
/* @__PURE__ */
|
|
14338
|
-
/* @__PURE__ */
|
|
14737
|
+
!isEditorCollapsed && /* @__PURE__ */ jsxs40(Fragment12, { children: [
|
|
14738
|
+
/* @__PURE__ */ jsxs40("div", { className: "bb-tabRow", children: [
|
|
14739
|
+
/* @__PURE__ */ jsx45(
|
|
14339
14740
|
"button",
|
|
14340
14741
|
{
|
|
14341
14742
|
type: "button",
|
|
@@ -14344,7 +14745,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14344
14745
|
children: "Object"
|
|
14345
14746
|
}
|
|
14346
14747
|
),
|
|
14347
|
-
/* @__PURE__ */
|
|
14748
|
+
/* @__PURE__ */ jsx45(
|
|
14348
14749
|
"button",
|
|
14349
14750
|
{
|
|
14350
14751
|
type: "button",
|
|
@@ -14354,26 +14755,36 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14354
14755
|
}
|
|
14355
14756
|
)
|
|
14356
14757
|
] }),
|
|
14357
|
-
/* @__PURE__ */
|
|
14758
|
+
/* @__PURE__ */ jsx45("section", { className: "bb-editor", children: activeEditorTab === "scene" ? /* @__PURE__ */ jsx45(Fragment12, { children: modelUrl ? /* @__PURE__ */ jsx45(
|
|
14358
14759
|
SceneSettingsTab,
|
|
14359
14760
|
{
|
|
14360
14761
|
config: sceneConfig,
|
|
14361
14762
|
onChange: setSceneConfig,
|
|
14362
14763
|
activeSubTab,
|
|
14363
14764
|
onActiveSubTabChange: setActiveSubTab,
|
|
14765
|
+
cameraState: (
|
|
14766
|
+
// Only feed the live camera while the Cinematic tab is
|
|
14767
|
+
// open — otherwise every orbit frame would re-render
|
|
14768
|
+
// the whole settings panel for no reason.
|
|
14769
|
+
activeSubTab === "Cinematic" && cameraState ? {
|
|
14770
|
+
position: cameraState.position,
|
|
14771
|
+
target: cameraState.target
|
|
14772
|
+
} : null
|
|
14773
|
+
),
|
|
14774
|
+
onPreviewWaypoint: handlePreviewWaypoint,
|
|
14364
14775
|
animationClipDetails: viewerAnimations.clipDetails,
|
|
14365
14776
|
animationPlayback: viewerAnimations
|
|
14366
14777
|
}
|
|
14367
|
-
) : /* @__PURE__ */
|
|
14368
|
-
/* @__PURE__ */
|
|
14369
|
-
/* @__PURE__ */
|
|
14370
|
-
/* @__PURE__ */
|
|
14371
|
-
/* @__PURE__ */
|
|
14372
|
-
/* @__PURE__ */
|
|
14373
|
-
/* @__PURE__ */
|
|
14778
|
+
) : /* @__PURE__ */ jsx45("div", { className: "bb-emptyEditor", children: "Upload a model to edit its scene settings." }) }) : !selectedBinding || !selectedItem ? /* @__PURE__ */ jsx45("div", { className: "bb-emptyEditor", children: "Select a generated node binding to edit its values." }) : /* @__PURE__ */ jsxs40(Fragment12, { children: [
|
|
14779
|
+
/* @__PURE__ */ jsx45("div", { className: "bb-editorHeader", children: /* @__PURE__ */ jsxs40("div", { children: [
|
|
14780
|
+
/* @__PURE__ */ jsx45("div", { className: "bb-sectionKicker", children: "Editing Binding" }),
|
|
14781
|
+
/* @__PURE__ */ jsx45("h2", { className: "bb-editorTitle", children: selectedBinding.label || selectedItem }),
|
|
14782
|
+
/* @__PURE__ */ jsxs40("div", { className: "bb-editorMeta", children: [
|
|
14783
|
+
/* @__PURE__ */ jsx45("span", { children: selectedBinding.type }),
|
|
14784
|
+
/* @__PURE__ */ jsx45("span", { children: selectedBinding.modelObjectId })
|
|
14374
14785
|
] })
|
|
14375
14786
|
] }) }),
|
|
14376
|
-
/* @__PURE__ */
|
|
14787
|
+
/* @__PURE__ */ jsx45(
|
|
14377
14788
|
IdentityPanel,
|
|
14378
14789
|
{
|
|
14379
14790
|
selectedItem,
|
|
@@ -14384,7 +14795,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14384
14795
|
idError
|
|
14385
14796
|
}
|
|
14386
14797
|
),
|
|
14387
|
-
/* @__PURE__ */
|
|
14798
|
+
/* @__PURE__ */ jsx45(
|
|
14388
14799
|
BasicsPanel,
|
|
14389
14800
|
{
|
|
14390
14801
|
selectedItem,
|
|
@@ -14394,7 +14805,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14394
14805
|
updateBinding
|
|
14395
14806
|
}
|
|
14396
14807
|
),
|
|
14397
|
-
/* @__PURE__ */
|
|
14808
|
+
/* @__PURE__ */ jsxs40(
|
|
14398
14809
|
BBSection,
|
|
14399
14810
|
{
|
|
14400
14811
|
title: "Material",
|
|
@@ -14402,7 +14813,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14402
14813
|
onToggle: () => toggleObjectSection("material"),
|
|
14403
14814
|
tooltip: "Edit the selected object's base material, textures, and surface properties.",
|
|
14404
14815
|
children: [
|
|
14405
|
-
/* @__PURE__ */
|
|
14816
|
+
/* @__PURE__ */ jsx45(
|
|
14406
14817
|
MaterialPresetGallery,
|
|
14407
14818
|
{
|
|
14408
14819
|
disabled: !selectedItem,
|
|
@@ -14412,7 +14823,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14412
14823
|
}))
|
|
14413
14824
|
}
|
|
14414
14825
|
),
|
|
14415
|
-
/* @__PURE__ */
|
|
14826
|
+
/* @__PURE__ */ jsx45(
|
|
14416
14827
|
MaterialSection,
|
|
14417
14828
|
{
|
|
14418
14829
|
selectedItem,
|
|
@@ -14430,7 +14841,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14430
14841
|
]
|
|
14431
14842
|
}
|
|
14432
14843
|
),
|
|
14433
|
-
/* @__PURE__ */
|
|
14844
|
+
/* @__PURE__ */ jsx45(
|
|
14434
14845
|
ActionsPanel,
|
|
14435
14846
|
{
|
|
14436
14847
|
selectedItem,
|
|
@@ -14440,7 +14851,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14440
14851
|
updateBinding
|
|
14441
14852
|
}
|
|
14442
14853
|
),
|
|
14443
|
-
/* @__PURE__ */
|
|
14854
|
+
/* @__PURE__ */ jsx45(
|
|
14444
14855
|
CameraPositionPanel,
|
|
14445
14856
|
{
|
|
14446
14857
|
selectedBinding,
|
|
@@ -14450,11 +14861,13 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14450
14861
|
formattedCameraState,
|
|
14451
14862
|
savedCameraPosition,
|
|
14452
14863
|
onSaveCameraPosition: saveCameraPositionForSelectedObject,
|
|
14864
|
+
onGoToSavedCamera: goToSavedCameraPosition,
|
|
14865
|
+
onClearSavedCamera: clearSavedCameraPosition,
|
|
14453
14866
|
onResetView: handleResetView,
|
|
14454
14867
|
onFitScene: handleFitScene
|
|
14455
14868
|
}
|
|
14456
14869
|
),
|
|
14457
|
-
/* @__PURE__ */
|
|
14870
|
+
/* @__PURE__ */ jsx45(
|
|
14458
14871
|
JsonPanel,
|
|
14459
14872
|
{
|
|
14460
14873
|
title: "Metrics",
|
|
@@ -14468,7 +14881,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14468
14881
|
onCommit: () => applyJsonDraft("metrics", metricsDraft, setMetricsError)
|
|
14469
14882
|
}
|
|
14470
14883
|
),
|
|
14471
|
-
/* @__PURE__ */
|
|
14884
|
+
/* @__PURE__ */ jsx45(
|
|
14472
14885
|
JsonPanel,
|
|
14473
14886
|
{
|
|
14474
14887
|
title: "Metadata",
|
|
@@ -14487,9 +14900,9 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14487
14900
|
}
|
|
14488
14901
|
)
|
|
14489
14902
|
] }) }),
|
|
14490
|
-
activeEditorTab === "object" && /* @__PURE__ */
|
|
14491
|
-
/* @__PURE__ */
|
|
14492
|
-
/* @__PURE__ */
|
|
14903
|
+
activeEditorTab === "object" && /* @__PURE__ */ jsxs40("div", { className: "bb-toolbar", children: [
|
|
14904
|
+
/* @__PURE__ */ jsxs40("div", { className: "bb-toolbarGroup", children: [
|
|
14905
|
+
/* @__PURE__ */ jsx45(
|
|
14493
14906
|
"button",
|
|
14494
14907
|
{
|
|
14495
14908
|
type: "button",
|
|
@@ -14497,10 +14910,10 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14497
14910
|
disabled: !bindingsHistory.canUndo,
|
|
14498
14911
|
onClick: bindingsHistory.undo,
|
|
14499
14912
|
title: "Undo (Cmd/Ctrl+Z)",
|
|
14500
|
-
children: /* @__PURE__ */
|
|
14913
|
+
children: /* @__PURE__ */ jsx45(Undo2, { size: 16 })
|
|
14501
14914
|
}
|
|
14502
14915
|
),
|
|
14503
|
-
/* @__PURE__ */
|
|
14916
|
+
/* @__PURE__ */ jsx45(
|
|
14504
14917
|
"button",
|
|
14505
14918
|
{
|
|
14506
14919
|
type: "button",
|
|
@@ -14508,10 +14921,10 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14508
14921
|
disabled: !nodeKeys.length,
|
|
14509
14922
|
onClick: () => openResetDialog("bindings"),
|
|
14510
14923
|
title: "Reset object bindings to default",
|
|
14511
|
-
children: /* @__PURE__ */
|
|
14924
|
+
children: /* @__PURE__ */ jsx45(ListRestart, { size: 16 })
|
|
14512
14925
|
}
|
|
14513
14926
|
),
|
|
14514
|
-
/* @__PURE__ */
|
|
14927
|
+
/* @__PURE__ */ jsx45(
|
|
14515
14928
|
"button",
|
|
14516
14929
|
{
|
|
14517
14930
|
type: "button",
|
|
@@ -14519,12 +14932,12 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14519
14932
|
disabled: !bindingsHistory.canRedo,
|
|
14520
14933
|
onClick: bindingsHistory.redo,
|
|
14521
14934
|
title: "Redo (Cmd/Ctrl+Shift+Z)",
|
|
14522
|
-
children: /* @__PURE__ */
|
|
14935
|
+
children: /* @__PURE__ */ jsx45(Redo2, { size: 16 })
|
|
14523
14936
|
}
|
|
14524
14937
|
)
|
|
14525
14938
|
] }),
|
|
14526
|
-
/* @__PURE__ */
|
|
14527
|
-
/* @__PURE__ */
|
|
14939
|
+
/* @__PURE__ */ jsx45("div", { className: "bb-toolbarDivider" }),
|
|
14940
|
+
/* @__PURE__ */ jsxs40(
|
|
14528
14941
|
"button",
|
|
14529
14942
|
{
|
|
14530
14943
|
type: "button",
|
|
@@ -14533,13 +14946,13 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14533
14946
|
onClick: openBindingsImportPicker,
|
|
14534
14947
|
title: "Re-apply a previously exported objectBindings.json onto this model",
|
|
14535
14948
|
children: [
|
|
14536
|
-
/* @__PURE__ */
|
|
14949
|
+
/* @__PURE__ */ jsx45(Upload, { size: 14 }),
|
|
14537
14950
|
"Import"
|
|
14538
14951
|
]
|
|
14539
14952
|
}
|
|
14540
14953
|
),
|
|
14541
|
-
/* @__PURE__ */
|
|
14542
|
-
/* @__PURE__ */
|
|
14954
|
+
/* @__PURE__ */ jsx45("div", { className: "bb-toolbarDivider" }),
|
|
14955
|
+
/* @__PURE__ */ jsx45(
|
|
14543
14956
|
ToolbarExportMenu,
|
|
14544
14957
|
{
|
|
14545
14958
|
disabled: !nodeKeys.length || isExporting,
|
|
@@ -14549,9 +14962,9 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14549
14962
|
}
|
|
14550
14963
|
)
|
|
14551
14964
|
] }),
|
|
14552
|
-
activeEditorTab === "scene" && /* @__PURE__ */
|
|
14553
|
-
/* @__PURE__ */
|
|
14554
|
-
/* @__PURE__ */
|
|
14965
|
+
activeEditorTab === "scene" && /* @__PURE__ */ jsxs40("div", { className: "bb-toolbar", children: [
|
|
14966
|
+
/* @__PURE__ */ jsxs40("div", { className: "bb-toolbarGroup", children: [
|
|
14967
|
+
/* @__PURE__ */ jsx45(
|
|
14555
14968
|
"button",
|
|
14556
14969
|
{
|
|
14557
14970
|
type: "button",
|
|
@@ -14559,20 +14972,20 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14559
14972
|
disabled: !sceneConfigHistory.canUndo,
|
|
14560
14973
|
onClick: sceneConfigHistory.undo,
|
|
14561
14974
|
title: "Undo (Cmd/Ctrl+Z)",
|
|
14562
|
-
children: /* @__PURE__ */
|
|
14975
|
+
children: /* @__PURE__ */ jsx45(Undo2, { size: 16 })
|
|
14563
14976
|
}
|
|
14564
14977
|
),
|
|
14565
|
-
/* @__PURE__ */
|
|
14978
|
+
/* @__PURE__ */ jsx45(
|
|
14566
14979
|
"button",
|
|
14567
14980
|
{
|
|
14568
14981
|
type: "button",
|
|
14569
14982
|
className: "bb-iconButton",
|
|
14570
14983
|
onClick: () => openResetDialog("scene"),
|
|
14571
14984
|
title: "Reset scene config to default",
|
|
14572
|
-
children: /* @__PURE__ */
|
|
14985
|
+
children: /* @__PURE__ */ jsx45(ListRestart, { size: 16 })
|
|
14573
14986
|
}
|
|
14574
14987
|
),
|
|
14575
|
-
/* @__PURE__ */
|
|
14988
|
+
/* @__PURE__ */ jsx45(
|
|
14576
14989
|
"button",
|
|
14577
14990
|
{
|
|
14578
14991
|
type: "button",
|
|
@@ -14580,12 +14993,12 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14580
14993
|
disabled: !sceneConfigHistory.canRedo,
|
|
14581
14994
|
onClick: sceneConfigHistory.redo,
|
|
14582
14995
|
title: "Redo (Cmd/Ctrl+Shift+Z)",
|
|
14583
|
-
children: /* @__PURE__ */
|
|
14996
|
+
children: /* @__PURE__ */ jsx45(Redo2, { size: 16 })
|
|
14584
14997
|
}
|
|
14585
14998
|
)
|
|
14586
14999
|
] }),
|
|
14587
|
-
/* @__PURE__ */
|
|
14588
|
-
/* @__PURE__ */
|
|
15000
|
+
/* @__PURE__ */ jsx45("div", { className: "bb-toolbarDivider" }),
|
|
15001
|
+
/* @__PURE__ */ jsxs40(
|
|
14589
15002
|
"button",
|
|
14590
15003
|
{
|
|
14591
15004
|
type: "button",
|
|
@@ -14594,13 +15007,13 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14594
15007
|
onClick: openSceneConfigImportPicker,
|
|
14595
15008
|
title: "Re-apply a previously exported sceneConfig.json",
|
|
14596
15009
|
children: [
|
|
14597
|
-
/* @__PURE__ */
|
|
15010
|
+
/* @__PURE__ */ jsx45(Upload, { size: 14 }),
|
|
14598
15011
|
"Import"
|
|
14599
15012
|
]
|
|
14600
15013
|
}
|
|
14601
15014
|
),
|
|
14602
|
-
/* @__PURE__ */
|
|
14603
|
-
/* @__PURE__ */
|
|
15015
|
+
/* @__PURE__ */ jsx45("div", { className: "bb-toolbarDivider" }),
|
|
15016
|
+
/* @__PURE__ */ jsx45(
|
|
14604
15017
|
ToolbarExportMenu,
|
|
14605
15018
|
{
|
|
14606
15019
|
disabled: !nodeKeys.length || loading,
|
|
@@ -14621,16 +15034,16 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14621
15034
|
]
|
|
14622
15035
|
}
|
|
14623
15036
|
),
|
|
14624
|
-
/* @__PURE__ */
|
|
14625
|
-
/* @__PURE__ */
|
|
14626
|
-
/* @__PURE__ */
|
|
14627
|
-
/* @__PURE__ */
|
|
15037
|
+
/* @__PURE__ */ jsxs40("main", { className: "bb-previewShell", children: [
|
|
15038
|
+
/* @__PURE__ */ jsxs40("div", { className: "bb-previewHeader", children: [
|
|
15039
|
+
/* @__PURE__ */ jsxs40("div", { className: "bb-previewToggles", children: [
|
|
15040
|
+
/* @__PURE__ */ jsxs40(
|
|
14628
15041
|
"label",
|
|
14629
15042
|
{
|
|
14630
15043
|
className: "bb-checkbox bb-checkbox--compact",
|
|
14631
15044
|
style: { color: "black" },
|
|
14632
15045
|
children: [
|
|
14633
|
-
/* @__PURE__ */
|
|
15046
|
+
/* @__PURE__ */ jsx45(
|
|
14634
15047
|
"input",
|
|
14635
15048
|
{
|
|
14636
15049
|
type: "checkbox",
|
|
@@ -14642,13 +15055,13 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14642
15055
|
]
|
|
14643
15056
|
}
|
|
14644
15057
|
),
|
|
14645
|
-
/* @__PURE__ */
|
|
15058
|
+
/* @__PURE__ */ jsxs40(
|
|
14646
15059
|
"label",
|
|
14647
15060
|
{
|
|
14648
15061
|
className: "bb-checkbox bb-checkbox--compact",
|
|
14649
15062
|
style: { color: "black" },
|
|
14650
15063
|
children: [
|
|
14651
|
-
/* @__PURE__ */
|
|
15064
|
+
/* @__PURE__ */ jsx45(
|
|
14652
15065
|
"input",
|
|
14653
15066
|
{
|
|
14654
15067
|
type: "checkbox",
|
|
@@ -14661,9 +15074,9 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14661
15074
|
}
|
|
14662
15075
|
)
|
|
14663
15076
|
] }),
|
|
14664
|
-
/* @__PURE__ */
|
|
14665
|
-
/* @__PURE__ */
|
|
14666
|
-
modelUrl && /* @__PURE__ */
|
|
15077
|
+
/* @__PURE__ */ jsxs40("div", { className: "bb-previewControls", children: [
|
|
15078
|
+
/* @__PURE__ */ jsx45("div", { className: "bb-previewMeta", children: /* @__PURE__ */ jsx45("span", { children: selectedItem ? `Focused: ${selectedItem}` : "Select a node to inspect it" }) }),
|
|
15079
|
+
modelUrl && /* @__PURE__ */ jsx45(
|
|
14667
15080
|
"button",
|
|
14668
15081
|
{
|
|
14669
15082
|
type: "button",
|
|
@@ -14674,8 +15087,8 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14674
15087
|
)
|
|
14675
15088
|
] })
|
|
14676
15089
|
] }),
|
|
14677
|
-
/* @__PURE__ */
|
|
14678
|
-
/* @__PURE__ */
|
|
15090
|
+
/* @__PURE__ */ jsxs40("div", { className: "bb-preview", children: [
|
|
15091
|
+
/* @__PURE__ */ jsx45(
|
|
14679
15092
|
LoadingOverlay,
|
|
14680
15093
|
{
|
|
14681
15094
|
visible: loading || statusBannerVisible,
|
|
@@ -14684,7 +15097,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14684
15097
|
spinner: loading
|
|
14685
15098
|
}
|
|
14686
15099
|
),
|
|
14687
|
-
modelUrl ? /* @__PURE__ */
|
|
15100
|
+
modelUrl ? /* @__PURE__ */ jsx45(
|
|
14688
15101
|
ModelViewer,
|
|
14689
15102
|
{
|
|
14690
15103
|
modelUrl,
|
|
@@ -14707,7 +15120,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14707
15120
|
panelHoveredObject: highlightOnHover ? panelHoveredBinding : null,
|
|
14708
15121
|
showExplodeControls: true
|
|
14709
15122
|
}
|
|
14710
|
-
) : /* @__PURE__ */
|
|
15123
|
+
) : /* @__PURE__ */ jsx45(
|
|
14711
15124
|
"div",
|
|
14712
15125
|
{
|
|
14713
15126
|
className: `bb-previewPlaceholder ${isPreviewDragActive ? "bb-previewPlaceholder--dragActive" : ""}`,
|
|
@@ -14715,9 +15128,9 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14715
15128
|
onDragOver: handlePreviewDragOver,
|
|
14716
15129
|
onDragLeave: handlePreviewDragLeave,
|
|
14717
15130
|
onDrop: handlePreviewDrop,
|
|
14718
|
-
children: /* @__PURE__ */
|
|
14719
|
-
/* @__PURE__ */
|
|
14720
|
-
/* @__PURE__ */
|
|
15131
|
+
children: /* @__PURE__ */ jsxs40("div", { children: [
|
|
15132
|
+
/* @__PURE__ */ jsx45("div", { children: "Upload a GLB/GLTF model or drag a .glb, .gltf, or .zip file here to generate editable object bindings." }),
|
|
15133
|
+
/* @__PURE__ */ jsx45(
|
|
14721
15134
|
"button",
|
|
14722
15135
|
{
|
|
14723
15136
|
type: "button",
|
|
@@ -14732,7 +15145,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14732
15145
|
)
|
|
14733
15146
|
] })
|
|
14734
15147
|
] }),
|
|
14735
|
-
/* @__PURE__ */
|
|
15148
|
+
/* @__PURE__ */ jsx45(
|
|
14736
15149
|
SceneNodesPanel,
|
|
14737
15150
|
{
|
|
14738
15151
|
bindings,
|
|
@@ -14750,27 +15163,27 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14750
15163
|
}
|
|
14751
15164
|
)
|
|
14752
15165
|
] }),
|
|
14753
|
-
resetDialogOpen && /* @__PURE__ */
|
|
14754
|
-
/* @__PURE__ */
|
|
14755
|
-
/* @__PURE__ */
|
|
14756
|
-
/* @__PURE__ */
|
|
15166
|
+
resetDialogOpen && /* @__PURE__ */ jsx45("div", { className: "bb-reset-overlay", onClick: closeResetDialog, children: /* @__PURE__ */ jsxs40("div", { className: "bb-reset-dialog", onClick: (e) => e.stopPropagation(), children: [
|
|
15167
|
+
/* @__PURE__ */ jsxs40("div", { className: "bb-reset-header", children: [
|
|
15168
|
+
/* @__PURE__ */ jsxs40("h3", { className: "bb-reset-title", children: [
|
|
15169
|
+
/* @__PURE__ */ jsx45(AlertTriangle3, { size: 20, className: "bb-icon-warning" }),
|
|
14757
15170
|
"Confirm Reset"
|
|
14758
15171
|
] }),
|
|
14759
|
-
/* @__PURE__ */
|
|
15172
|
+
/* @__PURE__ */ jsx45("button", { className: "bb-reset-close", onClick: closeResetDialog, children: /* @__PURE__ */ jsx45(X2, { size: 20 }) })
|
|
14760
15173
|
] }),
|
|
14761
|
-
/* @__PURE__ */
|
|
14762
|
-
/* @__PURE__ */
|
|
15174
|
+
/* @__PURE__ */ jsxs40("div", { className: "bb-reset-body", children: [
|
|
15175
|
+
/* @__PURE__ */ jsxs40("p", { className: "bb-reset-message", children: [
|
|
14763
15176
|
"Are you sure you want to reset",
|
|
14764
15177
|
" ",
|
|
14765
|
-
/* @__PURE__ */
|
|
15178
|
+
/* @__PURE__ */ jsx45("strong", { children: resetTarget === "bindings" ? "Object Bindings" : "Scene Config" }),
|
|
14766
15179
|
"?"
|
|
14767
15180
|
] }),
|
|
14768
|
-
/* @__PURE__ */
|
|
14769
|
-
/* @__PURE__ */
|
|
15181
|
+
/* @__PURE__ */ jsx45("p", { className: "bb-reset-details", children: resetTarget === "bindings" ? "This will remove all your custom changes to object properties, materials, actions, camera positions, metrics, and metadata." : "This will restore all scene settings (camera, lighting, environment, background, animations, annotations, etc.) to their default values." }),
|
|
15182
|
+
/* @__PURE__ */ jsx45("p", { className: "bb-reset-warning", children: "This action cannot be undone!" })
|
|
14770
15183
|
] }),
|
|
14771
|
-
/* @__PURE__ */
|
|
14772
|
-
/* @__PURE__ */
|
|
14773
|
-
/* @__PURE__ */
|
|
15184
|
+
/* @__PURE__ */ jsxs40("div", { className: "bb-reset-footer", children: [
|
|
15185
|
+
/* @__PURE__ */ jsx45("button", { className: "bb-reset-btn", onClick: closeResetDialog, children: "Cancel" }),
|
|
15186
|
+
/* @__PURE__ */ jsx45("button", { className: "bb-reset-btn-danger", onClick: executeReset, children: "Yes, Reset" })
|
|
14774
15187
|
] })
|
|
14775
15188
|
] }) })
|
|
14776
15189
|
] });
|
|
@@ -14778,21 +15191,21 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14778
15191
|
|
|
14779
15192
|
// src/components/SimpleModelViewer/SimpleModelViewer.tsx
|
|
14780
15193
|
import {
|
|
14781
|
-
useRef as
|
|
15194
|
+
useRef as useRef18,
|
|
14782
15195
|
useState as useState25,
|
|
14783
15196
|
useCallback as useCallback9,
|
|
14784
|
-
useEffect as
|
|
14785
|
-
useMemo as
|
|
15197
|
+
useEffect as useEffect20,
|
|
15198
|
+
useMemo as useMemo17,
|
|
14786
15199
|
Component as Component3,
|
|
14787
15200
|
Suspense as Suspense3
|
|
14788
15201
|
} from "react";
|
|
14789
15202
|
import { CameraControls as CameraControls2, Environment as Environment2 } from "@react-three/drei";
|
|
14790
|
-
import { useFrame as
|
|
15203
|
+
import { useFrame as useFrame9, useThree as useThree11 } from "@react-three/fiber";
|
|
14791
15204
|
import { Canvas as Canvas3 } from "@react-three/fiber";
|
|
14792
15205
|
import {
|
|
14793
|
-
Box3 as
|
|
15206
|
+
Box3 as Box34,
|
|
14794
15207
|
PerspectiveCamera as PerspectiveCamera4,
|
|
14795
|
-
Vector3 as
|
|
15208
|
+
Vector3 as Vector39
|
|
14796
15209
|
} from "three";
|
|
14797
15210
|
import { SkeletonUtils as SkeletonUtils2 } from "three-stdlib";
|
|
14798
15211
|
|
|
@@ -14805,11 +15218,11 @@ import {
|
|
|
14805
15218
|
Check,
|
|
14806
15219
|
Settings,
|
|
14807
15220
|
ChevronDown as ChevronDown6,
|
|
14808
|
-
Boxes as
|
|
14809
|
-
Trash2 as
|
|
15221
|
+
Boxes as Boxes2,
|
|
15222
|
+
Trash2 as Trash26,
|
|
14810
15223
|
Upload as Upload2
|
|
14811
15224
|
} from "lucide-react";
|
|
14812
|
-
import { Fragment as
|
|
15225
|
+
import { Fragment as Fragment13, jsx as jsx46, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
14813
15226
|
var HIGHLIGHT2 = { color: "#93c5fd", intensity: 0.05 };
|
|
14814
15227
|
var ENV_PRESETS = [
|
|
14815
15228
|
"apartment",
|
|
@@ -14840,7 +15253,7 @@ function applyClippingPlanes(scene, controls) {
|
|
|
14840
15253
|
if (!controls) return;
|
|
14841
15254
|
const camera = controls.camera;
|
|
14842
15255
|
if (!(camera instanceof PerspectiveCamera4)) return;
|
|
14843
|
-
const size = new
|
|
15256
|
+
const size = new Box34().setFromObject(scene).getSize(new Vector39()).length();
|
|
14844
15257
|
if (!Number.isFinite(size) || size <= 0) return;
|
|
14845
15258
|
camera.near = size / 100;
|
|
14846
15259
|
camera.far = size * 100;
|
|
@@ -14878,13 +15291,13 @@ function InnerModel({
|
|
|
14878
15291
|
ktx2TranscoderPath: void 0,
|
|
14879
15292
|
meshopt: true
|
|
14880
15293
|
});
|
|
14881
|
-
const invalidate =
|
|
15294
|
+
const invalidate = useThree11((state) => state.invalidate);
|
|
14882
15295
|
const [hoveredKey, setHoveredKey] = useState25(null);
|
|
14883
|
-
const scene =
|
|
15296
|
+
const scene = useMemo17(
|
|
14884
15297
|
() => SkeletonUtils2.clone(rawScene),
|
|
14885
15298
|
[rawScene]
|
|
14886
15299
|
);
|
|
14887
|
-
const meshMap =
|
|
15300
|
+
const meshMap = useMemo17(() => {
|
|
14888
15301
|
const map = {};
|
|
14889
15302
|
scene.traverse((obj) => {
|
|
14890
15303
|
if (!isMeshNode2(obj)) return;
|
|
@@ -14893,7 +15306,7 @@ function InnerModel({
|
|
|
14893
15306
|
});
|
|
14894
15307
|
return map;
|
|
14895
15308
|
}, [scene]);
|
|
14896
|
-
const perKeyMaterials =
|
|
15309
|
+
const perKeyMaterials = useMemo17(() => {
|
|
14897
15310
|
const map = {};
|
|
14898
15311
|
for (const [key, { ref }] of Object.entries(meshMap)) {
|
|
14899
15312
|
const original = Array.isArray(ref.material) ? ref.material[0] : ref.material;
|
|
@@ -14909,14 +15322,14 @@ function InnerModel({
|
|
|
14909
15322
|
}
|
|
14910
15323
|
return map;
|
|
14911
15324
|
}, [meshMap]);
|
|
14912
|
-
|
|
15325
|
+
useEffect20(
|
|
14913
15326
|
() => () => {
|
|
14914
15327
|
Object.values(perKeyMaterials).forEach((m) => m.dispose());
|
|
14915
15328
|
},
|
|
14916
15329
|
[perKeyMaterials]
|
|
14917
15330
|
);
|
|
14918
|
-
const notifiedUrl =
|
|
14919
|
-
|
|
15331
|
+
const notifiedUrl = useRef18(null);
|
|
15332
|
+
useEffect20(() => {
|
|
14920
15333
|
if (notifiedUrl.current === url) return;
|
|
14921
15334
|
notifiedUrl.current = url;
|
|
14922
15335
|
scene.updateWorldMatrix(true, true);
|
|
@@ -14924,22 +15337,22 @@ function InnerModel({
|
|
|
14924
15337
|
onMeshesReady(meshMap);
|
|
14925
15338
|
onModelLoaded(scene);
|
|
14926
15339
|
}, [url, scene, meshMap, onMeshesReady, onModelLoaded]);
|
|
14927
|
-
|
|
15340
|
+
useEffect20(() => {
|
|
14928
15341
|
for (const [key, { ref }] of Object.entries(meshMap)) {
|
|
14929
15342
|
nodeRefsOut.current[key] = ref;
|
|
14930
15343
|
}
|
|
14931
15344
|
}, [meshMap, nodeRefsOut]);
|
|
14932
|
-
|
|
15345
|
+
useEffect20(() => {
|
|
14933
15346
|
if (!highlightOnHover && hoveredKey !== null) {
|
|
14934
15347
|
setHoveredKey(null);
|
|
14935
15348
|
}
|
|
14936
15349
|
}, [highlightOnHover, hoveredKey]);
|
|
14937
|
-
|
|
15350
|
+
useEffect20(() => {
|
|
14938
15351
|
return () => {
|
|
14939
15352
|
document.body.style.cursor = "default";
|
|
14940
15353
|
};
|
|
14941
15354
|
}, []);
|
|
14942
|
-
|
|
15355
|
+
useEffect20(() => {
|
|
14943
15356
|
for (const [key, { ref }] of Object.entries(meshMap)) {
|
|
14944
15357
|
ref.visible = !hiddenKeys.has(key);
|
|
14945
15358
|
const mat = perKeyMaterials[key];
|
|
@@ -14979,7 +15392,7 @@ function InnerModel({
|
|
|
14979
15392
|
}
|
|
14980
15393
|
return null;
|
|
14981
15394
|
};
|
|
14982
|
-
return /* @__PURE__ */
|
|
15395
|
+
return /* @__PURE__ */ jsx46(
|
|
14983
15396
|
"primitive",
|
|
14984
15397
|
{
|
|
14985
15398
|
object: scene,
|
|
@@ -15012,15 +15425,15 @@ function CameraFitter({
|
|
|
15012
15425
|
refitOnResize,
|
|
15013
15426
|
onInitialFitComplete
|
|
15014
15427
|
}) {
|
|
15015
|
-
const fitted =
|
|
15016
|
-
const invalidate =
|
|
15017
|
-
const size =
|
|
15018
|
-
const resizeTimerRef =
|
|
15019
|
-
|
|
15428
|
+
const fitted = useRef18(false);
|
|
15429
|
+
const invalidate = useThree11((state) => state.invalidate);
|
|
15430
|
+
const size = useThree11((state) => state.size);
|
|
15431
|
+
const resizeTimerRef = useRef18(null);
|
|
15432
|
+
useEffect20(() => {
|
|
15020
15433
|
fitted.current = false;
|
|
15021
15434
|
invalidate();
|
|
15022
15435
|
}, [invalidate, scene]);
|
|
15023
|
-
|
|
15436
|
+
useFrame9(() => {
|
|
15024
15437
|
if (fitted.current) return;
|
|
15025
15438
|
if (!scene || !controlsRef.current) {
|
|
15026
15439
|
invalidate();
|
|
@@ -15036,7 +15449,7 @@ function CameraFitter({
|
|
|
15036
15449
|
}).catch(() => {
|
|
15037
15450
|
}).finally(onInitialFitComplete);
|
|
15038
15451
|
});
|
|
15039
|
-
|
|
15452
|
+
useEffect20(() => {
|
|
15040
15453
|
if (!refitOnResize || !fitted.current || !scene || !controlsRef.current) {
|
|
15041
15454
|
return;
|
|
15042
15455
|
}
|
|
@@ -15062,9 +15475,9 @@ function CameraFitter({
|
|
|
15062
15475
|
return null;
|
|
15063
15476
|
}
|
|
15064
15477
|
function ExposureController({ exposure }) {
|
|
15065
|
-
const gl =
|
|
15066
|
-
const invalidate =
|
|
15067
|
-
|
|
15478
|
+
const gl = useThree11((state) => state.gl);
|
|
15479
|
+
const invalidate = useThree11((state) => state.invalidate);
|
|
15480
|
+
useEffect20(() => {
|
|
15068
15481
|
gl.toneMappingExposure = exposure;
|
|
15069
15482
|
invalidate();
|
|
15070
15483
|
}, [gl, exposure, invalidate]);
|
|
@@ -15074,8 +15487,8 @@ function AutoRotator({
|
|
|
15074
15487
|
enabled,
|
|
15075
15488
|
controlsRef
|
|
15076
15489
|
}) {
|
|
15077
|
-
const invalidate =
|
|
15078
|
-
|
|
15490
|
+
const invalidate = useThree11((state) => state.invalidate);
|
|
15491
|
+
useFrame9((_, delta) => {
|
|
15079
15492
|
if (!enabled || !controlsRef.current) return;
|
|
15080
15493
|
controlsRef.current.rotate(delta * 0.2, 0, false);
|
|
15081
15494
|
invalidate();
|
|
@@ -15090,13 +15503,13 @@ function ToggleRow2({
|
|
|
15090
15503
|
checked,
|
|
15091
15504
|
onChange
|
|
15092
15505
|
}) {
|
|
15093
|
-
return /* @__PURE__ */
|
|
15094
|
-
/* @__PURE__ */
|
|
15095
|
-
/* @__PURE__ */
|
|
15506
|
+
return /* @__PURE__ */ jsxs41("div", { className: "env-row", children: [
|
|
15507
|
+
/* @__PURE__ */ jsx46("span", { className: "env-row__label", children: label }),
|
|
15508
|
+
/* @__PURE__ */ jsx46("div", { className: "env-row__value", children: /* @__PURE__ */ jsx46("button", { className: "env-checkbox-btn", onClick: () => onChange(!checked), children: /* @__PURE__ */ jsx46(
|
|
15096
15509
|
"span",
|
|
15097
15510
|
{
|
|
15098
15511
|
className: `env-checkbox${checked ? " env-checkbox--checked" : ""}`,
|
|
15099
|
-
children: checked && /* @__PURE__ */
|
|
15512
|
+
children: checked && /* @__PURE__ */ jsx46(Check, { size: 9, strokeWidth: 2, color: "white" })
|
|
15100
15513
|
}
|
|
15101
15514
|
) }) })
|
|
15102
15515
|
] });
|
|
@@ -15106,11 +15519,11 @@ function ColorRow2({
|
|
|
15106
15519
|
value,
|
|
15107
15520
|
onChange
|
|
15108
15521
|
}) {
|
|
15109
|
-
return /* @__PURE__ */
|
|
15110
|
-
/* @__PURE__ */
|
|
15111
|
-
/* @__PURE__ */
|
|
15112
|
-
/* @__PURE__ */
|
|
15113
|
-
/* @__PURE__ */
|
|
15522
|
+
return /* @__PURE__ */ jsxs41("div", { className: "env-row", children: [
|
|
15523
|
+
/* @__PURE__ */ jsx46("span", { className: "env-row__label", children: label }),
|
|
15524
|
+
/* @__PURE__ */ jsx46("div", { className: "env-row__value", children: /* @__PURE__ */ jsxs41("div", { className: "env-color-swatch", style: { background: value }, children: [
|
|
15525
|
+
/* @__PURE__ */ jsx46("span", { className: "env-color-swatch__hex", children: value }),
|
|
15526
|
+
/* @__PURE__ */ jsx46(
|
|
15114
15527
|
"input",
|
|
15115
15528
|
{
|
|
15116
15529
|
type: "color",
|
|
@@ -15130,10 +15543,10 @@ function SliderRow2({
|
|
|
15130
15543
|
step,
|
|
15131
15544
|
onChange
|
|
15132
15545
|
}) {
|
|
15133
|
-
return /* @__PURE__ */
|
|
15134
|
-
/* @__PURE__ */
|
|
15135
|
-
/* @__PURE__ */
|
|
15136
|
-
/* @__PURE__ */
|
|
15546
|
+
return /* @__PURE__ */ jsxs41("div", { className: "env-row", children: [
|
|
15547
|
+
/* @__PURE__ */ jsx46("span", { className: "env-row__label", children: label }),
|
|
15548
|
+
/* @__PURE__ */ jsxs41("div", { className: "env-row__value env-row__value--slider", children: [
|
|
15549
|
+
/* @__PURE__ */ jsx46(
|
|
15137
15550
|
"input",
|
|
15138
15551
|
{
|
|
15139
15552
|
type: "range",
|
|
@@ -15145,7 +15558,7 @@ function SliderRow2({
|
|
|
15145
15558
|
onChange: (e) => onChange(Number(e.target.value))
|
|
15146
15559
|
}
|
|
15147
15560
|
),
|
|
15148
|
-
/* @__PURE__ */
|
|
15561
|
+
/* @__PURE__ */ jsx46("span", { className: "env-slider__value", children: value.toFixed(2) })
|
|
15149
15562
|
] })
|
|
15150
15563
|
] });
|
|
15151
15564
|
}
|
|
@@ -15155,15 +15568,15 @@ function SelectRow2({
|
|
|
15155
15568
|
options,
|
|
15156
15569
|
onChange
|
|
15157
15570
|
}) {
|
|
15158
|
-
return /* @__PURE__ */
|
|
15159
|
-
/* @__PURE__ */
|
|
15160
|
-
/* @__PURE__ */
|
|
15571
|
+
return /* @__PURE__ */ jsxs41("div", { className: "env-row", children: [
|
|
15572
|
+
/* @__PURE__ */ jsx46("span", { className: "env-row__label", children: label }),
|
|
15573
|
+
/* @__PURE__ */ jsx46("div", { className: "env-row__value", children: /* @__PURE__ */ jsx46(
|
|
15161
15574
|
"select",
|
|
15162
15575
|
{
|
|
15163
15576
|
className: "env-select",
|
|
15164
15577
|
value,
|
|
15165
15578
|
onChange: (e) => onChange(e.target.value),
|
|
15166
|
-
children: options.map((option) => /* @__PURE__ */
|
|
15579
|
+
children: options.map((option) => /* @__PURE__ */ jsx46("option", { value: option, children: option }, option))
|
|
15167
15580
|
}
|
|
15168
15581
|
) })
|
|
15169
15582
|
] });
|
|
@@ -15173,14 +15586,14 @@ function EnvironmentSection({
|
|
|
15173
15586
|
children
|
|
15174
15587
|
}) {
|
|
15175
15588
|
const [collapsed, setCollapsed] = useState25(false);
|
|
15176
|
-
return /* @__PURE__ */
|
|
15177
|
-
/* @__PURE__ */
|
|
15589
|
+
return /* @__PURE__ */ jsxs41("div", { className: "env-section", children: [
|
|
15590
|
+
/* @__PURE__ */ jsxs41(
|
|
15178
15591
|
"button",
|
|
15179
15592
|
{
|
|
15180
15593
|
className: "env-section__header",
|
|
15181
15594
|
onClick: () => setCollapsed((c) => !c),
|
|
15182
15595
|
children: [
|
|
15183
|
-
/* @__PURE__ */
|
|
15596
|
+
/* @__PURE__ */ jsx46(
|
|
15184
15597
|
ChevronDown6,
|
|
15185
15598
|
{
|
|
15186
15599
|
size: 12,
|
|
@@ -15188,11 +15601,11 @@ function EnvironmentSection({
|
|
|
15188
15601
|
className: `env-section__chevron${collapsed ? " env-section__chevron--collapsed" : ""}`
|
|
15189
15602
|
}
|
|
15190
15603
|
),
|
|
15191
|
-
/* @__PURE__ */
|
|
15604
|
+
/* @__PURE__ */ jsx46("span", { children: title })
|
|
15192
15605
|
]
|
|
15193
15606
|
}
|
|
15194
15607
|
),
|
|
15195
|
-
!collapsed && /* @__PURE__ */
|
|
15608
|
+
!collapsed && /* @__PURE__ */ jsx46("div", { className: "env-section__body", children })
|
|
15196
15609
|
] });
|
|
15197
15610
|
}
|
|
15198
15611
|
function EnvironmentPanel({
|
|
@@ -15201,40 +15614,40 @@ function EnvironmentPanel({
|
|
|
15201
15614
|
settings,
|
|
15202
15615
|
onChange
|
|
15203
15616
|
}) {
|
|
15204
|
-
return /* @__PURE__ */
|
|
15617
|
+
return /* @__PURE__ */ jsxs41(
|
|
15205
15618
|
"div",
|
|
15206
15619
|
{
|
|
15207
15620
|
className: `left-panel-wrapper ${isOpen ? "left-panel-wrapper--open" : "left-panel-wrapper--closed"}`,
|
|
15208
15621
|
children: [
|
|
15209
|
-
!isOpen && /* @__PURE__ */
|
|
15622
|
+
!isOpen && /* @__PURE__ */ jsx46(
|
|
15210
15623
|
"button",
|
|
15211
15624
|
{
|
|
15212
15625
|
className: "side-panel__open-btn side-panel__open-btn--left",
|
|
15213
15626
|
onClick: () => onToggle(true),
|
|
15214
|
-
children: /* @__PURE__ */
|
|
15627
|
+
children: /* @__PURE__ */ jsx46(Settings, { size: 20, strokeWidth: 1.8 })
|
|
15215
15628
|
}
|
|
15216
15629
|
),
|
|
15217
|
-
/* @__PURE__ */
|
|
15630
|
+
/* @__PURE__ */ jsxs41(
|
|
15218
15631
|
"div",
|
|
15219
15632
|
{
|
|
15220
15633
|
className: `side-panel side-panel--left ${isOpen ? "side-panel--open-left" : "side-panel--closed-left"}`,
|
|
15221
15634
|
"aria-hidden": !isOpen,
|
|
15222
15635
|
children: [
|
|
15223
|
-
/* @__PURE__ */
|
|
15224
|
-
/* @__PURE__ */
|
|
15225
|
-
/* @__PURE__ */
|
|
15636
|
+
/* @__PURE__ */ jsx46("div", { className: "side-panel__header", children: /* @__PURE__ */ jsxs41("div", { className: "side-panel__header-top", children: [
|
|
15637
|
+
/* @__PURE__ */ jsx46("span", { className: "side-panel__eyebrow", children: "Scene Settings" }),
|
|
15638
|
+
/* @__PURE__ */ jsx46(
|
|
15226
15639
|
"button",
|
|
15227
15640
|
{
|
|
15228
15641
|
className: "side-panel__close-btn",
|
|
15229
15642
|
onClick: () => onToggle(false),
|
|
15230
15643
|
title: "Close",
|
|
15231
|
-
children: /* @__PURE__ */
|
|
15644
|
+
children: /* @__PURE__ */ jsx46(X3, { size: 12, strokeWidth: 1.6 })
|
|
15232
15645
|
}
|
|
15233
15646
|
)
|
|
15234
15647
|
] }) }),
|
|
15235
|
-
/* @__PURE__ */
|
|
15236
|
-
/* @__PURE__ */
|
|
15237
|
-
/* @__PURE__ */
|
|
15648
|
+
/* @__PURE__ */ jsxs41("div", { className: "env-controls", children: [
|
|
15649
|
+
/* @__PURE__ */ jsxs41(EnvironmentSection, { title: "Background", children: [
|
|
15650
|
+
/* @__PURE__ */ jsx46(
|
|
15238
15651
|
ToggleRow2,
|
|
15239
15652
|
{
|
|
15240
15653
|
label: "Background",
|
|
@@ -15242,7 +15655,7 @@ function EnvironmentPanel({
|
|
|
15242
15655
|
onChange: (v) => onChange("backgroundEnabled", v)
|
|
15243
15656
|
}
|
|
15244
15657
|
),
|
|
15245
|
-
/* @__PURE__ */
|
|
15658
|
+
/* @__PURE__ */ jsx46(
|
|
15246
15659
|
ColorRow2,
|
|
15247
15660
|
{
|
|
15248
15661
|
label: "Background Color",
|
|
@@ -15250,7 +15663,7 @@ function EnvironmentPanel({
|
|
|
15250
15663
|
onChange: (v) => onChange("backgroundColor", v)
|
|
15251
15664
|
}
|
|
15252
15665
|
),
|
|
15253
|
-
/* @__PURE__ */
|
|
15666
|
+
/* @__PURE__ */ jsx46(
|
|
15254
15667
|
SelectRow2,
|
|
15255
15668
|
{
|
|
15256
15669
|
label: "Environment",
|
|
@@ -15259,7 +15672,7 @@ function EnvironmentPanel({
|
|
|
15259
15672
|
onChange: (v) => onChange("envPreset", v)
|
|
15260
15673
|
}
|
|
15261
15674
|
),
|
|
15262
|
-
/* @__PURE__ */
|
|
15675
|
+
/* @__PURE__ */ jsx46(
|
|
15263
15676
|
ToggleRow2,
|
|
15264
15677
|
{
|
|
15265
15678
|
label: "Auto Rotate",
|
|
@@ -15268,8 +15681,8 @@ function EnvironmentPanel({
|
|
|
15268
15681
|
}
|
|
15269
15682
|
)
|
|
15270
15683
|
] }),
|
|
15271
|
-
/* @__PURE__ */
|
|
15272
|
-
/* @__PURE__ */
|
|
15684
|
+
/* @__PURE__ */ jsxs41(EnvironmentSection, { title: "Lighting", children: [
|
|
15685
|
+
/* @__PURE__ */ jsx46(
|
|
15273
15686
|
SliderRow2,
|
|
15274
15687
|
{
|
|
15275
15688
|
label: "Exposure",
|
|
@@ -15280,7 +15693,7 @@ function EnvironmentPanel({
|
|
|
15280
15693
|
onChange: (v) => onChange("exposure", v)
|
|
15281
15694
|
}
|
|
15282
15695
|
),
|
|
15283
|
-
/* @__PURE__ */
|
|
15696
|
+
/* @__PURE__ */ jsx46(
|
|
15284
15697
|
SliderRow2,
|
|
15285
15698
|
{
|
|
15286
15699
|
label: "Ambient Light Intensity",
|
|
@@ -15291,7 +15704,7 @@ function EnvironmentPanel({
|
|
|
15291
15704
|
onChange: (v) => onChange("ambientIntensity", v)
|
|
15292
15705
|
}
|
|
15293
15706
|
),
|
|
15294
|
-
/* @__PURE__ */
|
|
15707
|
+
/* @__PURE__ */ jsx46(
|
|
15295
15708
|
ColorRow2,
|
|
15296
15709
|
{
|
|
15297
15710
|
label: "Ambient Light Color",
|
|
@@ -15299,7 +15712,7 @@ function EnvironmentPanel({
|
|
|
15299
15712
|
onChange: (v) => onChange("ambientColor", v)
|
|
15300
15713
|
}
|
|
15301
15714
|
),
|
|
15302
|
-
/* @__PURE__ */
|
|
15715
|
+
/* @__PURE__ */ jsx46(
|
|
15303
15716
|
SliderRow2,
|
|
15304
15717
|
{
|
|
15305
15718
|
label: "Direct Light Intensity",
|
|
@@ -15310,7 +15723,7 @@ function EnvironmentPanel({
|
|
|
15310
15723
|
onChange: (v) => onChange("directionalIntensity", v)
|
|
15311
15724
|
}
|
|
15312
15725
|
),
|
|
15313
|
-
/* @__PURE__ */
|
|
15726
|
+
/* @__PURE__ */ jsx46(
|
|
15314
15727
|
ColorRow2,
|
|
15315
15728
|
{
|
|
15316
15729
|
label: "Direct Light Color",
|
|
@@ -15342,37 +15755,37 @@ function SceneObjectsPanel2({
|
|
|
15342
15755
|
const entries = Object.values(meshKeys).filter(
|
|
15343
15756
|
(m) => m.label.toLowerCase().includes(search.toLowerCase())
|
|
15344
15757
|
);
|
|
15345
|
-
return /* @__PURE__ */
|
|
15758
|
+
return /* @__PURE__ */ jsxs41(
|
|
15346
15759
|
"div",
|
|
15347
15760
|
{
|
|
15348
15761
|
className: `right-panel-wrapper ${isOpen ? "right-panel-wrapper--open" : "right-panel-wrapper--closed"}`,
|
|
15349
15762
|
children: [
|
|
15350
|
-
!isOpen && /* @__PURE__ */
|
|
15351
|
-
/* @__PURE__ */
|
|
15763
|
+
!isOpen && /* @__PURE__ */ jsx46("button", { className: "side-panel__open-btn", onClick: () => onToggle(true), children: /* @__PURE__ */ jsx46(Boxes2, { size: 20, strokeWidth: 1.8 }) }),
|
|
15764
|
+
/* @__PURE__ */ jsxs41(
|
|
15352
15765
|
"div",
|
|
15353
15766
|
{
|
|
15354
15767
|
className: `side-panel ${isOpen ? "side-panel--open" : "side-panel--closed"}`,
|
|
15355
15768
|
"aria-hidden": !isOpen,
|
|
15356
15769
|
children: [
|
|
15357
|
-
/* @__PURE__ */
|
|
15358
|
-
/* @__PURE__ */
|
|
15359
|
-
/* @__PURE__ */
|
|
15360
|
-
/* @__PURE__ */
|
|
15770
|
+
/* @__PURE__ */ jsxs41("div", { className: "side-panel__header", children: [
|
|
15771
|
+
/* @__PURE__ */ jsxs41("div", { className: "side-panel__header-top", children: [
|
|
15772
|
+
/* @__PURE__ */ jsx46("span", { className: "side-panel__eyebrow", children: "SCENE OBJECTS" }),
|
|
15773
|
+
/* @__PURE__ */ jsx46(
|
|
15361
15774
|
"button",
|
|
15362
15775
|
{
|
|
15363
15776
|
className: "side-panel__close-btn",
|
|
15364
15777
|
onClick: () => onToggle(false),
|
|
15365
15778
|
title: "Close",
|
|
15366
|
-
children: /* @__PURE__ */
|
|
15779
|
+
children: /* @__PURE__ */ jsx46(X3, { size: 12, strokeWidth: 1.6 })
|
|
15367
15780
|
}
|
|
15368
15781
|
)
|
|
15369
15782
|
] }),
|
|
15370
|
-
/* @__PURE__ */
|
|
15783
|
+
/* @__PURE__ */ jsxs41("p", { className: "side-panel__title", children: [
|
|
15371
15784
|
entries.length,
|
|
15372
15785
|
" objects"
|
|
15373
15786
|
] })
|
|
15374
15787
|
] }),
|
|
15375
|
-
/* @__PURE__ */
|
|
15788
|
+
/* @__PURE__ */ jsx46("div", { className: "side-panel__search-wrap", children: /* @__PURE__ */ jsx46(
|
|
15376
15789
|
"input",
|
|
15377
15790
|
{
|
|
15378
15791
|
className: "side-panel__search",
|
|
@@ -15382,11 +15795,11 @@ function SceneObjectsPanel2({
|
|
|
15382
15795
|
onChange: (e) => setSearch(e.target.value)
|
|
15383
15796
|
}
|
|
15384
15797
|
) }),
|
|
15385
|
-
/* @__PURE__ */
|
|
15798
|
+
/* @__PURE__ */ jsx46("div", { className: "side-panel__list", children: entries.map(({ key, label }) => {
|
|
15386
15799
|
const isVisible = !hiddenKeys.has(key);
|
|
15387
15800
|
const isSelected = selectedKey === key;
|
|
15388
15801
|
const isHovered = rowHovered === key;
|
|
15389
|
-
return /* @__PURE__ */
|
|
15802
|
+
return /* @__PURE__ */ jsxs41(
|
|
15390
15803
|
"div",
|
|
15391
15804
|
{
|
|
15392
15805
|
onClick: () => onFocus(key),
|
|
@@ -15404,14 +15817,14 @@ function SceneObjectsPanel2({
|
|
|
15404
15817
|
isSelected ? "side-panel__row--selected" : ""
|
|
15405
15818
|
].filter(Boolean).join(" "),
|
|
15406
15819
|
children: [
|
|
15407
|
-
/* @__PURE__ */
|
|
15820
|
+
/* @__PURE__ */ jsx46(
|
|
15408
15821
|
"span",
|
|
15409
15822
|
{
|
|
15410
15823
|
className: `side-panel__label${isHovered || isSelected ? " side-panel__label--hovered" : ""}`,
|
|
15411
15824
|
children: label
|
|
15412
15825
|
}
|
|
15413
15826
|
),
|
|
15414
|
-
/* @__PURE__ */
|
|
15827
|
+
/* @__PURE__ */ jsx46(
|
|
15415
15828
|
"button",
|
|
15416
15829
|
{
|
|
15417
15830
|
className: "side-panel__vis-btn",
|
|
@@ -15420,11 +15833,11 @@ function SceneObjectsPanel2({
|
|
|
15420
15833
|
e.stopPropagation();
|
|
15421
15834
|
onToggleVisibility(key);
|
|
15422
15835
|
},
|
|
15423
|
-
children: /* @__PURE__ */
|
|
15836
|
+
children: /* @__PURE__ */ jsx46(
|
|
15424
15837
|
"span",
|
|
15425
15838
|
{
|
|
15426
15839
|
className: `side-panel__checkbox${isVisible ? " side-panel__checkbox--checked" : ""}`,
|
|
15427
|
-
children: isVisible && /* @__PURE__ */
|
|
15840
|
+
children: isVisible && /* @__PURE__ */ jsx46(Check, { size: 9, strokeWidth: 5, color: "white" })
|
|
15428
15841
|
}
|
|
15429
15842
|
)
|
|
15430
15843
|
}
|
|
@@ -15462,11 +15875,11 @@ function SimpleModelViewer({
|
|
|
15462
15875
|
onModelLoaded,
|
|
15463
15876
|
onLoadError
|
|
15464
15877
|
}) {
|
|
15465
|
-
const cameraControlsRef =
|
|
15466
|
-
const nodeRefsOut =
|
|
15467
|
-
const fileInputRef =
|
|
15468
|
-
const uploadedModelRef =
|
|
15469
|
-
const dragDepthRef =
|
|
15878
|
+
const cameraControlsRef = useRef18(null);
|
|
15879
|
+
const nodeRefsOut = useRef18({});
|
|
15880
|
+
const fileInputRef = useRef18(null);
|
|
15881
|
+
const uploadedModelRef = useRef18(null);
|
|
15882
|
+
const dragDepthRef = useRef18(0);
|
|
15470
15883
|
const [loadedScene, setLoadedScene] = useState25(null);
|
|
15471
15884
|
const [meshKeys, setMeshKeys] = useState25({});
|
|
15472
15885
|
const [hiddenKeys, setHiddenKeys] = useState25(/* @__PURE__ */ new Set());
|
|
@@ -15544,7 +15957,7 @@ function SimpleModelViewer({
|
|
|
15544
15957
|
},
|
|
15545
15958
|
[handleUploadedModel]
|
|
15546
15959
|
);
|
|
15547
|
-
|
|
15960
|
+
useEffect20(() => {
|
|
15548
15961
|
return () => {
|
|
15549
15962
|
revokeUploadedModelUrl();
|
|
15550
15963
|
};
|
|
@@ -15555,10 +15968,10 @@ function SimpleModelViewer({
|
|
|
15555
15968
|
},
|
|
15556
15969
|
[]
|
|
15557
15970
|
);
|
|
15558
|
-
|
|
15971
|
+
useEffect20(() => {
|
|
15559
15972
|
setEnvSettings((prev) => ({ ...prev, backgroundColor }));
|
|
15560
15973
|
}, [backgroundColor]);
|
|
15561
|
-
|
|
15974
|
+
useEffect20(() => {
|
|
15562
15975
|
resetModelState();
|
|
15563
15976
|
}, [activeModelUrl, resetModelState]);
|
|
15564
15977
|
const handleMeshesReady = useCallback9((meshes) => {
|
|
@@ -15658,7 +16071,7 @@ function SimpleModelViewer({
|
|
|
15658
16071
|
const showModelLoader = showLoadingOverlay && (isResolvingUpload || Boolean(
|
|
15659
16072
|
activeModelUrl && !modelLoadFailed && (!loadedScene || !initialFitComplete)
|
|
15660
16073
|
));
|
|
15661
|
-
return /* @__PURE__ */
|
|
16074
|
+
return /* @__PURE__ */ jsxs41(
|
|
15662
16075
|
"div",
|
|
15663
16076
|
{
|
|
15664
16077
|
className: "smv-root",
|
|
@@ -15668,7 +16081,7 @@ function SimpleModelViewer({
|
|
|
15668
16081
|
onDragLeave: handleDragLeave,
|
|
15669
16082
|
onDrop: handleDrop,
|
|
15670
16083
|
children: [
|
|
15671
|
-
showSceneSettingsPanel && (modelUrl || activeModelUrl) && /* @__PURE__ */
|
|
16084
|
+
showSceneSettingsPanel && (modelUrl || activeModelUrl) && /* @__PURE__ */ jsx46(
|
|
15672
16085
|
EnvironmentPanel,
|
|
15673
16086
|
{
|
|
15674
16087
|
isOpen: isEnvPanelOpen,
|
|
@@ -15677,9 +16090,9 @@ function SimpleModelViewer({
|
|
|
15677
16090
|
onChange: handleEnvSettingChange
|
|
15678
16091
|
}
|
|
15679
16092
|
),
|
|
15680
|
-
/* @__PURE__ */
|
|
15681
|
-
enableModelUpload ? /* @__PURE__ */
|
|
15682
|
-
/* @__PURE__ */
|
|
16093
|
+
/* @__PURE__ */ jsxs41("div", { className: "smv-canvasArea", children: [
|
|
16094
|
+
enableModelUpload ? /* @__PURE__ */ jsxs41(Fragment13, { children: [
|
|
16095
|
+
/* @__PURE__ */ jsx46(
|
|
15683
16096
|
"input",
|
|
15684
16097
|
{
|
|
15685
16098
|
ref: fileInputRef,
|
|
@@ -15692,38 +16105,38 @@ function SimpleModelViewer({
|
|
|
15692
16105
|
}
|
|
15693
16106
|
}
|
|
15694
16107
|
),
|
|
15695
|
-
activeModelUrl ? /* @__PURE__ */
|
|
15696
|
-
/* @__PURE__ */
|
|
15697
|
-
/* @__PURE__ */
|
|
16108
|
+
activeModelUrl ? /* @__PURE__ */ jsx46("div", { className: "smv-upload-toolbar", children: /* @__PURE__ */ jsxs41(Fragment13, { children: [
|
|
16109
|
+
/* @__PURE__ */ jsx46("span", { className: "smv-upload-toolbar__name", children: uploadedModelName ?? "Uploaded model" }),
|
|
16110
|
+
/* @__PURE__ */ jsxs41(
|
|
15698
16111
|
"button",
|
|
15699
16112
|
{
|
|
15700
16113
|
type: "button",
|
|
15701
16114
|
className: "smv-upload-toolbar__button smv-upload-toolbar__button--ghost",
|
|
15702
16115
|
onClick: clearUploadedModel,
|
|
15703
16116
|
children: [
|
|
15704
|
-
/* @__PURE__ */
|
|
16117
|
+
/* @__PURE__ */ jsx46(Trash26, { size: 14 }),
|
|
15705
16118
|
"Clear"
|
|
15706
16119
|
]
|
|
15707
16120
|
}
|
|
15708
16121
|
)
|
|
15709
16122
|
] }) }) : null
|
|
15710
16123
|
] }) : null,
|
|
15711
|
-
activeModelUrl ? /* @__PURE__ */
|
|
16124
|
+
activeModelUrl ? /* @__PURE__ */ jsx46(
|
|
15712
16125
|
Canvas3,
|
|
15713
16126
|
{
|
|
15714
16127
|
dpr: [1, 2],
|
|
15715
16128
|
resize: { debounce: 0, scroll: false },
|
|
15716
16129
|
style: { background: envSettings.backgroundColor },
|
|
15717
16130
|
camera: { position: [0, 0, 5], fov: 45, near: 0.1, far: 1e3 },
|
|
15718
|
-
children: /* @__PURE__ */
|
|
15719
|
-
/* @__PURE__ */
|
|
16131
|
+
children: /* @__PURE__ */ jsxs41(Suspense3, { fallback: null, children: [
|
|
16132
|
+
/* @__PURE__ */ jsx46(
|
|
15720
16133
|
"ambientLight",
|
|
15721
16134
|
{
|
|
15722
16135
|
intensity: envSettings.ambientIntensity,
|
|
15723
16136
|
color: envSettings.ambientColor
|
|
15724
16137
|
}
|
|
15725
16138
|
),
|
|
15726
|
-
/* @__PURE__ */
|
|
16139
|
+
/* @__PURE__ */ jsx46(
|
|
15727
16140
|
"directionalLight",
|
|
15728
16141
|
{
|
|
15729
16142
|
position: [-4, 5, 7],
|
|
@@ -15732,27 +16145,27 @@ function SimpleModelViewer({
|
|
|
15732
16145
|
castShadow: true
|
|
15733
16146
|
}
|
|
15734
16147
|
),
|
|
15735
|
-
/* @__PURE__ */
|
|
15736
|
-
/* @__PURE__ */
|
|
16148
|
+
/* @__PURE__ */ jsx46("directionalLight", { position: [-5, 5, 5], intensity: 0.5 }),
|
|
16149
|
+
/* @__PURE__ */ jsx46(Suspense3, { fallback: null, children: /* @__PURE__ */ jsx46(
|
|
15737
16150
|
Environment2,
|
|
15738
16151
|
{
|
|
15739
16152
|
preset: envSettings.envPreset,
|
|
15740
16153
|
background: envSettings.backgroundEnabled
|
|
15741
16154
|
}
|
|
15742
16155
|
) }),
|
|
15743
|
-
/* @__PURE__ */
|
|
15744
|
-
/* @__PURE__ */
|
|
16156
|
+
/* @__PURE__ */ jsx46(ExposureController, { exposure: envSettings.exposure }),
|
|
16157
|
+
/* @__PURE__ */ jsx46(
|
|
15745
16158
|
AutoRotator,
|
|
15746
16159
|
{
|
|
15747
16160
|
enabled: envSettings.autoRotate,
|
|
15748
16161
|
controlsRef: cameraControlsRef
|
|
15749
16162
|
}
|
|
15750
16163
|
),
|
|
15751
|
-
/* @__PURE__ */
|
|
16164
|
+
/* @__PURE__ */ jsx46(
|
|
15752
16165
|
ModelErrorBoundary2,
|
|
15753
16166
|
{
|
|
15754
16167
|
onLoadError: handleLoadError,
|
|
15755
|
-
children: /* @__PURE__ */
|
|
16168
|
+
children: /* @__PURE__ */ jsx46(Suspense3, { fallback: null, children: /* @__PURE__ */ jsx46(
|
|
15756
16169
|
InnerModel,
|
|
15757
16170
|
{
|
|
15758
16171
|
url: activeModelUrl,
|
|
@@ -15769,7 +16182,7 @@ function SimpleModelViewer({
|
|
|
15769
16182
|
},
|
|
15770
16183
|
activeModelUrl
|
|
15771
16184
|
),
|
|
15772
|
-
/* @__PURE__ */
|
|
16185
|
+
/* @__PURE__ */ jsx46(
|
|
15773
16186
|
CameraFitter,
|
|
15774
16187
|
{
|
|
15775
16188
|
scene: loadedScene,
|
|
@@ -15778,7 +16191,7 @@ function SimpleModelViewer({
|
|
|
15778
16191
|
onInitialFitComplete: () => setInitialFitComplete(true)
|
|
15779
16192
|
}
|
|
15780
16193
|
),
|
|
15781
|
-
/* @__PURE__ */
|
|
16194
|
+
/* @__PURE__ */ jsx46(
|
|
15782
16195
|
CameraControls2,
|
|
15783
16196
|
{
|
|
15784
16197
|
ref: cameraControlsRef,
|
|
@@ -15788,31 +16201,31 @@ function SimpleModelViewer({
|
|
|
15788
16201
|
)
|
|
15789
16202
|
] })
|
|
15790
16203
|
}
|
|
15791
|
-
) : /* @__PURE__ */
|
|
16204
|
+
) : /* @__PURE__ */ jsx46(
|
|
15792
16205
|
"div",
|
|
15793
16206
|
{
|
|
15794
16207
|
className: "smv-upload-empty-state",
|
|
15795
16208
|
style: { background: envSettings.backgroundColor },
|
|
15796
|
-
children: /* @__PURE__ */
|
|
15797
|
-
/* @__PURE__ */
|
|
15798
|
-
/* @__PURE__ */
|
|
16209
|
+
children: /* @__PURE__ */ jsxs41("div", { className: "smv-upload-empty-state__card", children: [
|
|
16210
|
+
/* @__PURE__ */ jsx46("div", { className: "smv-upload-empty-state__icon", children: /* @__PURE__ */ jsx46(Upload2, { size: 24 }) }),
|
|
16211
|
+
/* @__PURE__ */ jsxs41("p", { className: "smv-upload-empty-state__body", children: [
|
|
15799
16212
|
"Drag and drop ",
|
|
15800
|
-
/* @__PURE__ */
|
|
16213
|
+
/* @__PURE__ */ jsx46("code", { children: ".glb" }),
|
|
15801
16214
|
", ",
|
|
15802
|
-
/* @__PURE__ */
|
|
16215
|
+
/* @__PURE__ */ jsx46("code", { children: ".gltf" }),
|
|
15803
16216
|
", or",
|
|
15804
16217
|
" ",
|
|
15805
|
-
/* @__PURE__ */
|
|
16218
|
+
/* @__PURE__ */ jsx46("code", { children: ".zip" }),
|
|
15806
16219
|
" file here"
|
|
15807
16220
|
] }),
|
|
15808
|
-
/* @__PURE__ */
|
|
16221
|
+
/* @__PURE__ */ jsxs41(
|
|
15809
16222
|
"button",
|
|
15810
16223
|
{
|
|
15811
16224
|
type: "button",
|
|
15812
16225
|
className: "smv-upload-empty-state__button",
|
|
15813
16226
|
onClick: () => fileInputRef.current?.click(),
|
|
15814
16227
|
children: [
|
|
15815
|
-
/* @__PURE__ */
|
|
16228
|
+
/* @__PURE__ */ jsx46(Upload2, { size: 15 }),
|
|
15816
16229
|
"Choose model"
|
|
15817
16230
|
]
|
|
15818
16231
|
}
|
|
@@ -15820,19 +16233,19 @@ function SimpleModelViewer({
|
|
|
15820
16233
|
] })
|
|
15821
16234
|
}
|
|
15822
16235
|
),
|
|
15823
|
-
/* @__PURE__ */
|
|
16236
|
+
/* @__PURE__ */ jsx46(
|
|
15824
16237
|
LoadingOverlay,
|
|
15825
16238
|
{
|
|
15826
16239
|
visible: showModelLoader,
|
|
15827
16240
|
message: isResolvingUpload ? "Preparing model..." : "Loading model..."
|
|
15828
16241
|
}
|
|
15829
16242
|
),
|
|
15830
|
-
enableModelUpload && isDragActive ? /* @__PURE__ */
|
|
15831
|
-
/* @__PURE__ */
|
|
15832
|
-
/* @__PURE__ */
|
|
16243
|
+
enableModelUpload && isDragActive ? /* @__PURE__ */ jsx46("div", { className: "smv-upload-drop-overlay", children: /* @__PURE__ */ jsxs41("div", { className: "smv-upload-drop-overlay__card", children: [
|
|
16244
|
+
/* @__PURE__ */ jsx46(Upload2, { size: 20 }),
|
|
16245
|
+
/* @__PURE__ */ jsx46("span", { children: "Release to load your model" })
|
|
15833
16246
|
] }) }) : null
|
|
15834
16247
|
] }),
|
|
15835
|
-
showSceneObjectsPanel && activeModelUrl && /* @__PURE__ */
|
|
16248
|
+
showSceneObjectsPanel && activeModelUrl && /* @__PURE__ */ jsx46(
|
|
15836
16249
|
SceneObjectsPanel2,
|
|
15837
16250
|
{
|
|
15838
16251
|
meshKeys,
|
|
@@ -15874,9 +16287,9 @@ function useViewerSelection(initialSelectedObjectBinding = null) {
|
|
|
15874
16287
|
}
|
|
15875
16288
|
|
|
15876
16289
|
// src/hooks/useObjectBinding.ts
|
|
15877
|
-
import { useMemo as
|
|
16290
|
+
import { useMemo as useMemo18 } from "react";
|
|
15878
16291
|
function useObjectBinding(objectBindings, objectId) {
|
|
15879
|
-
return
|
|
16292
|
+
return useMemo18(() => {
|
|
15880
16293
|
if (!objectId) {
|
|
15881
16294
|
return null;
|
|
15882
16295
|
}
|
|
@@ -15891,16 +16304,16 @@ function useObjectBinding(objectBindings, objectId) {
|
|
|
15891
16304
|
}
|
|
15892
16305
|
|
|
15893
16306
|
// src/hooks/useObjectBindingIds.ts
|
|
15894
|
-
import { useMemo as
|
|
16307
|
+
import { useMemo as useMemo19 } from "react";
|
|
15895
16308
|
function useObjectBindingIds(objectBindings) {
|
|
15896
|
-
return
|
|
16309
|
+
return useMemo19(
|
|
15897
16310
|
() => Object.values(objectBindings).map((binding) => binding.modelObjectId),
|
|
15898
16311
|
[objectBindings]
|
|
15899
16312
|
);
|
|
15900
16313
|
}
|
|
15901
16314
|
|
|
15902
16315
|
// src/hooks/useObjectVisibility.ts
|
|
15903
|
-
import { useCallback as useCallback11, useMemo as
|
|
16316
|
+
import { useCallback as useCallback11, useMemo as useMemo20 } from "react";
|
|
15904
16317
|
function getHiddenObjects(objectBindings) {
|
|
15905
16318
|
return Object.fromEntries(
|
|
15906
16319
|
Object.entries(objectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
|
|
@@ -15957,11 +16370,11 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
|
15957
16370
|
)
|
|
15958
16371
|
);
|
|
15959
16372
|
}, [objectBindings, onObjectBindingsChange]);
|
|
15960
|
-
const hiddenObjects =
|
|
16373
|
+
const hiddenObjects = useMemo20(
|
|
15961
16374
|
() => getHiddenObjects(objectBindings),
|
|
15962
16375
|
[objectBindings]
|
|
15963
16376
|
);
|
|
15964
|
-
const hiddenObjectIds =
|
|
16377
|
+
const hiddenObjectIds = useMemo20(
|
|
15965
16378
|
() => Object.entries(hiddenObjects).map(
|
|
15966
16379
|
([bindingKey]) => objectBindings[bindingKey]?.id ?? bindingKey
|
|
15967
16380
|
),
|
|
@@ -16003,7 +16416,7 @@ function useViewerHover() {
|
|
|
16003
16416
|
|
|
16004
16417
|
// src/hooks/useViewerModel.ts
|
|
16005
16418
|
import { useCallback as useCallback13, useState as useState28 } from "react";
|
|
16006
|
-
import { Box3 as
|
|
16419
|
+
import { Box3 as Box35, Vector3 as Vector310 } from "three";
|
|
16007
16420
|
function toTuple2(vector) {
|
|
16008
16421
|
return [vector.x, vector.y, vector.z];
|
|
16009
16422
|
}
|
|
@@ -16014,11 +16427,11 @@ function useViewerModel() {
|
|
|
16014
16427
|
const [bounds, setBounds] = useState28(null);
|
|
16015
16428
|
const [objectCount, setObjectCount] = useState28(0);
|
|
16016
16429
|
const handleModelLoaded = useCallback13((scene) => {
|
|
16017
|
-
const box = new
|
|
16430
|
+
const box = new Box35().setFromObject(scene);
|
|
16018
16431
|
const min = box.min.clone();
|
|
16019
16432
|
const max = box.max.clone();
|
|
16020
|
-
const center = new
|
|
16021
|
-
const size = new
|
|
16433
|
+
const center = new Vector310();
|
|
16434
|
+
const size = new Vector310();
|
|
16022
16435
|
let nextObjectCount = 0;
|
|
16023
16436
|
scene.traverse((child) => {
|
|
16024
16437
|
if ("isMesh" in child && child.isMesh) {
|
|
@@ -16120,7 +16533,7 @@ function useViewerActions(objectBindings, options = {}) {
|
|
|
16120
16533
|
}
|
|
16121
16534
|
|
|
16122
16535
|
// src/hooks/useShareableViewerState.ts
|
|
16123
|
-
import { useCallback as useCallback15, useEffect as
|
|
16536
|
+
import { useCallback as useCallback15, useEffect as useEffect21, useRef as useRef19, useState as useState29 } from "react";
|
|
16124
16537
|
function serializeState(cameraState, objectBindings, selectedObjectBinding) {
|
|
16125
16538
|
const hidden = Object.values(objectBindings).filter((binding) => binding.visible === false).map((binding) => binding.id);
|
|
16126
16539
|
return {
|
|
@@ -16140,7 +16553,7 @@ function useShareableViewerState({
|
|
|
16140
16553
|
debounceMs = 400,
|
|
16141
16554
|
autoSync = true
|
|
16142
16555
|
}) {
|
|
16143
|
-
const lastWrittenRawRef =
|
|
16556
|
+
const lastWrittenRawRef = useRef19(null);
|
|
16144
16557
|
const buildHash = useCallback15(() => {
|
|
16145
16558
|
const state = serializeState(
|
|
16146
16559
|
cameraState,
|
|
@@ -16162,7 +16575,7 @@ function useShareableViewerState({
|
|
|
16162
16575
|
paramKey
|
|
16163
16576
|
);
|
|
16164
16577
|
}, [buildHash, paramKey]);
|
|
16165
|
-
const latestStateRef =
|
|
16578
|
+
const latestStateRef = useRef19({
|
|
16166
16579
|
cameraState,
|
|
16167
16580
|
objectBindings,
|
|
16168
16581
|
selectedObjectBinding
|
|
@@ -16173,8 +16586,8 @@ function useShareableViewerState({
|
|
|
16173
16586
|
selectedObjectBinding
|
|
16174
16587
|
};
|
|
16175
16588
|
const [restoreAttempted, setRestoreAttempted] = useState29(false);
|
|
16176
|
-
const lastSyncedHashRef =
|
|
16177
|
-
|
|
16589
|
+
const lastSyncedHashRef = useRef19(null);
|
|
16590
|
+
useEffect21(() => {
|
|
16178
16591
|
if (!autoSync || !restoreAttempted) return;
|
|
16179
16592
|
const tick = () => {
|
|
16180
16593
|
const current = latestStateRef.current;
|
|
@@ -16196,8 +16609,8 @@ function useShareableViewerState({
|
|
|
16196
16609
|
const id = window.setInterval(tick, debounceMs);
|
|
16197
16610
|
return () => window.clearInterval(id);
|
|
16198
16611
|
}, [autoSync, restoreAttempted, debounceMs, paramKey]);
|
|
16199
|
-
const lastRestoredRawRef =
|
|
16200
|
-
const restoringRawRef =
|
|
16612
|
+
const lastRestoredRawRef = useRef19(null);
|
|
16613
|
+
const restoringRawRef = useRef19(null);
|
|
16201
16614
|
const restoreFromUrl = useCallback15(async () => {
|
|
16202
16615
|
setRestoreAttempted(true);
|
|
16203
16616
|
const rawHash = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
|
|
@@ -16251,7 +16664,7 @@ function useShareableViewerState({
|
|
|
16251
16664
|
setCameraState,
|
|
16252
16665
|
setSelectedObjectBinding
|
|
16253
16666
|
]);
|
|
16254
|
-
|
|
16667
|
+
useEffect21(() => {
|
|
16255
16668
|
const handleHashChange = () => {
|
|
16256
16669
|
void restoreFromUrl();
|
|
16257
16670
|
};
|