@liveroom-tech/react-immersive 1.3.2 → 1.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +694 -314
- package/dist/index.mjs +647 -266
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -200,7 +200,7 @@ function styleInject(css, { insertAt } = {}) {
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
// src/styles/ModelViewer.css
|
|
203
|
-
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: var(--ri-databinding-bg, #111318);\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: var(--ri-databinding-text, #f1f5f9);\n --ri-sidepanel-text-emphasis: var(--ri-databinding-text-hover, #cbd5e1);\n --ri-sidepanel-text-muted: var(--ri-databinding-text-muted, #94a3b8);\n --ri-sidepanel-text-subtle: var(--ri-databinding-text-subtle, #64748b);\n --ri-sidepanel-placeholder: var(--ri-databinding-text-faint, #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-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 left: 16px;\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: 8px;\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-mobileHandoffBackdrop {\n position: absolute;\n z-index: 30;\n inset: 0;\n display: grid;\n place-items: center;\n padding: 20px;\n background: rgba(2, 6, 23, 0.68);\n backdrop-filter: blur(5px);\n -webkit-backdrop-filter: blur(5px);\n}\n.ri-mobileHandoffDialog {\n position: relative;\n display: flex;\n width: min(100%, 300px);\n flex-direction: column;\n align-items: center;\n padding: 16px;\n text-align: center;\n color: #e2e8f0;\n background: #0f172a;\n border: 1px solid rgba(148, 163, 184, 0.3);\n border-radius: 16px;\n box-shadow: 0 24px 80px rgba(0, 0, 0, 0.5);\n}\n.ri-mobileHandoffDialog > svg {\n color: #67e8f9;\n}\n.ri-mobileHandoffDialog h2 {\n margin: 12px 0 0;\n font-size: 18px;\n line-height: 1.25;\n color: #fff;\n}\n.ri-mobileHandoffDialog p {\n margin: 8px 0 16px;\n font-size: 13px;\n line-height: 1.5;\n color: #cbd5e1;\n}\n.ri-mobileHandoffQr {\n display: grid;\n place-items: center;\n padding: 10px;\n background: #fff;\n border-radius: 10px;\n}\n.ri-mobileHandoffDialog a {\n margin-top: 16px;\n font-size: 13px;\n font-weight: 600;\n color: #a5f3fc;\n text-decoration: underline;\n text-underline-offset: 3px;\n}\n.ri-mobileHandoffClose {\n position: absolute;\n top: 10px;\n right: 10px;\n display: grid;\n width: 30px;\n height: 30px;\n place-items: center;\n padding: 0;\n color: #cbd5e1;\n background: transparent;\n border: 0;\n border-radius: 6px;\n cursor: pointer;\n}\n.ri-mobileHandoffClose:hover {\n color: #fff;\n background: rgba(255, 255, 255, 0.1);\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-popover--inline {\n position: static;\n z-index: auto;\n width: 100%;\n border-radius: 10px;\n box-shadow: none;\n}\n.ri-popover--inline .react-colorful {\n width: 100%;\n}\n.ri-popover--inline {\n background: var(--ri-databinding-surface, rgba(255, 255, 255, 0.03));\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n color: var(--ri-databinding-text, #f1f5f9);\n}\n.ri-popover--inline .ri-popoverHeader {\n border-bottom-color: var( --ri-databinding-border-strong, rgba(255, 255, 255, 0.08) );\n}\n.ri-popover--inline .ri-popoverLabel {\n color: var(--ri-databinding-text-muted, #94a3b8);\n}\n.ri-popover--inline .ri-colorCode {\n color: var(--ri-databinding-text-muted, #94a3b8);\n}\n.ri-popover--inline .ri-secondaryButton {\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 color: var(--ri-databinding-text, #f1f5f9);\n}\n.ri-popover--inline .ri-secondaryButton:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n.ri-popover--inline .ri-previewFrame {\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n}\n.ri-popover--inline .ri-emptyState {\n border-color: var(--ri-databinding-border-hover, rgba(255, 255, 255, 0.15));\n color: var(--ri-databinding-text-subtle, #64748b);\n}\n.ri-popover--inline .ri-uploadButton {\n background: var(--ri-databinding-accent-gradient, #2563eb);\n color: #ffffff;\n}\n.ri-popover--inline .ri-uploadButton:hover {\n background: var(--ri-databinding-accent-gradient-active, #1d4ed8);\n}\n.ri-popover--inline .ri-removeButton {\n background: transparent;\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n color: var(--ri-databinding-text-muted, #94a3b8);\n}\n.ri-popover--inline .ri-removeButton:hover {\n background: var(--ri-databinding-surface-hover, rgba(255, 255, 255, 0.06));\n}\n.ri-popover--inline .ri-formatHint {\n color: var(--ri-databinding-text-subtle, #64748b);\n}\n.ri-popoverHeader {\n padding: 12px 16px;\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-left: 12px;\n padding-right: 12px;\n padding-bottom: 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 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: 34px;\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 gap: 5px;\n }\n .ri-xrButton {\n height: 34px;\n gap: 5px;\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.ri-largeModelBanner {\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 pointer-events: auto;\n max-width: calc(100% - 32px);\n white-space: normal;\n text-align: center;\n}\n.ri-largeModelBanner--offset {\n top: 64px;\n}\n.ri-largeModelBanner__icon {\n flex-shrink: 0;\n}\n.ri-largeModelBanner__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-largeModelBanner__dismiss:hover {\n color: rgb(251, 191, 36);\n background: rgba(251, 191, 36, 0.12);\n}\n');
|
|
203
|
+
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: var(--ri-databinding-bg, #111318);\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: var(--ri-databinding-text, #f1f5f9);\n --ri-sidepanel-text-emphasis: var(--ri-databinding-text-hover, #cbd5e1);\n --ri-sidepanel-text-muted: var(--ri-databinding-text-muted, #94a3b8);\n --ri-sidepanel-text-subtle: var(--ri-databinding-text-subtle, #64748b);\n --ri-sidepanel-placeholder: var(--ri-databinding-text-faint, #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-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 left: 16px;\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: 8px;\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-mobileHandoffBackdrop {\n position: absolute;\n z-index: 30;\n inset: 0;\n display: grid;\n place-items: center;\n padding: 20px;\n background: rgba(2, 6, 23, 0.68);\n backdrop-filter: blur(5px);\n -webkit-backdrop-filter: blur(5px);\n}\n.ri-mobileHandoffDialog {\n position: relative;\n display: flex;\n width: min(100%, 300px);\n flex-direction: column;\n align-items: center;\n padding: 16px;\n text-align: center;\n color: #e2e8f0;\n background: #0f172a;\n border: 1px solid rgba(148, 163, 184, 0.3);\n border-radius: 16px;\n box-shadow: 0 24px 80px rgba(0, 0, 0, 0.5);\n}\n.ri-mobileHandoffDialog > svg {\n color: #67e8f9;\n}\n.ri-mobileHandoffDialog h2 {\n margin: 12px 0 0;\n font-size: 18px;\n line-height: 1.25;\n color: #fff;\n}\n.ri-mobileHandoffDialog p {\n margin: 8px 0 16px;\n font-size: 13px;\n line-height: 1.5;\n color: #cbd5e1;\n}\n.ri-mobileHandoffQr {\n display: grid;\n place-items: center;\n padding: 10px;\n background: #fff;\n border-radius: 10px;\n}\n.ri-mobileHandoffDialog a {\n margin-top: 16px;\n font-size: 13px;\n font-weight: 600;\n color: #a5f3fc;\n text-decoration: underline;\n text-underline-offset: 3px;\n}\n.ri-mobileHandoffClose {\n position: absolute;\n top: 10px;\n right: 10px;\n display: grid;\n width: 30px;\n height: 30px;\n place-items: center;\n padding: 0;\n color: #cbd5e1;\n background: transparent;\n border: 0;\n border-radius: 6px;\n cursor: pointer;\n}\n.ri-mobileHandoffClose:hover {\n color: #fff;\n background: rgba(255, 255, 255, 0.1);\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-popover--inline {\n position: static;\n z-index: auto;\n width: 100%;\n border-radius: 10px;\n box-shadow: none;\n}\n.ri-popover--inline .react-colorful {\n width: 100%;\n}\n.ri-popover--inline {\n background: var(--ri-databinding-surface, rgba(255, 255, 255, 0.03));\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n color: var(--ri-databinding-text, #f1f5f9);\n}\n.ri-popover--inline .ri-popoverHeader {\n border-bottom-color: var( --ri-databinding-border-strong, rgba(255, 255, 255, 0.08) );\n}\n.ri-popover--inline .ri-popoverLabel {\n color: var(--ri-databinding-text-muted, #94a3b8);\n}\n.ri-popover--inline .ri-colorCode {\n color: var(--ri-databinding-text-muted, #94a3b8);\n}\n.ri-popover--inline .ri-secondaryButton {\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 color: var(--ri-databinding-text, #f1f5f9);\n}\n.ri-popover--inline .ri-secondaryButton:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n.ri-popover--inline .ri-previewFrame {\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n}\n.ri-popover--inline .ri-emptyState {\n border-color: var(--ri-databinding-border-hover, rgba(255, 255, 255, 0.15));\n color: var(--ri-databinding-text-subtle, #64748b);\n}\n.ri-popover--inline .ri-uploadButton {\n background: var(--ri-databinding-accent-gradient, #2563eb);\n color: #ffffff;\n}\n.ri-popover--inline .ri-uploadButton:hover {\n background: var(--ri-databinding-accent-gradient-active, #1d4ed8);\n}\n.ri-popover--inline .ri-removeButton {\n background: transparent;\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n color: var(--ri-databinding-text-muted, #94a3b8);\n}\n.ri-popover--inline .ri-removeButton:hover {\n background: var(--ri-databinding-surface-hover, rgba(255, 255, 255, 0.06));\n}\n.ri-popover--inline .ri-formatHint {\n color: var(--ri-databinding-text-subtle, #64748b);\n}\n.ri-popoverHeader {\n padding: 12px 16px;\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-left: 12px;\n padding-right: 12px;\n padding-bottom: 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 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.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.ri-largeModelBanner {\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 pointer-events: auto;\n max-width: calc(100% - 32px);\n white-space: normal;\n text-align: center;\n}\n.ri-largeModelBanner--offset {\n top: 64px;\n}\n.ri-largeModelBanner__icon {\n flex-shrink: 0;\n}\n.ri-largeModelBanner__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-largeModelBanner__dismiss:hover {\n color: rgb(251, 191, 36);\n background: rgba(251, 191, 36, 0.12);\n}\n@media (max-width: 768px) {\n .ri-xrTools {\n gap: 6px;\n }\n .ri-xrButton {\n height: 34px;\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 gap: 5px;\n }\n .ri-xrButton {\n height: 34px;\n gap: 5px;\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');
|
|
204
204
|
|
|
205
205
|
// src/components/LicenseOverlay.tsx
|
|
206
206
|
import { AlertTriangle } from "lucide-react";
|
|
@@ -600,6 +600,7 @@ var DEFAULT_SCENE_POST_PROCESSING_CONFIG = {
|
|
|
600
600
|
rampEnd: [1, 1],
|
|
601
601
|
startColor: "#000000",
|
|
602
602
|
endColor: "#ffffff",
|
|
603
|
+
opacity: 0.5,
|
|
603
604
|
rampBias: 0.5,
|
|
604
605
|
rampGain: 0.5,
|
|
605
606
|
rampMask: false,
|
|
@@ -1467,7 +1468,10 @@ function PostProcessingEffects({
|
|
|
1467
1468
|
}
|
|
1468
1469
|
),
|
|
1469
1470
|
pp.pixelation.enabled === true && /* @__PURE__ */ jsx4(Pixelation, { granularity: pp.pixelation.granularity }),
|
|
1470
|
-
pp.ramp.enabled === true &&
|
|
1471
|
+
pp.ramp.enabled === true && // Ramp's non-mask shader emits an opaque full-screen gradient.
|
|
1472
|
+
// Blend it with the scene by default instead of replacing the
|
|
1473
|
+
// model completely; mask mode already performs its own mix.
|
|
1474
|
+
/* @__PURE__ */ jsx4(
|
|
1471
1475
|
Ramp,
|
|
1472
1476
|
{
|
|
1473
1477
|
rampType: rampTypeFromName(pp.ramp.rampType),
|
|
@@ -1475,6 +1479,7 @@ function PostProcessingEffects({
|
|
|
1475
1479
|
rampEnd: pp.ramp.rampEnd,
|
|
1476
1480
|
startColor: rampStartColor,
|
|
1477
1481
|
endColor: rampEndColor,
|
|
1482
|
+
opacity: pp.ramp.rampMask ? 1 : pp.ramp.opacity ?? 0.5,
|
|
1478
1483
|
rampBias: pp.ramp.rampBias,
|
|
1479
1484
|
rampGain: pp.ramp.rampGain,
|
|
1480
1485
|
rampMask: pp.ramp.rampMask,
|
|
@@ -4125,6 +4130,7 @@ import {
|
|
|
4125
4130
|
Box3 as Box33,
|
|
4126
4131
|
CatmullRomCurve3,
|
|
4127
4132
|
Sphere,
|
|
4133
|
+
Spherical,
|
|
4128
4134
|
Vector3 as Vector36
|
|
4129
4135
|
} from "three";
|
|
4130
4136
|
var ENTER_BLEND_SECONDS = 1.2;
|
|
@@ -4133,18 +4139,75 @@ function CinematicCamera({
|
|
|
4133
4139
|
sceneRef,
|
|
4134
4140
|
config,
|
|
4135
4141
|
playing,
|
|
4142
|
+
resetOnPlay = false,
|
|
4136
4143
|
onFinished,
|
|
4137
|
-
onUserInteract
|
|
4144
|
+
onUserInteract,
|
|
4145
|
+
onStop
|
|
4138
4146
|
}) {
|
|
4139
4147
|
const invalidate = useThree9((state) => state.invalidate);
|
|
4140
4148
|
const waypoints = useMemo11(() => config.waypoints ?? [], [config.waypoints]);
|
|
4141
4149
|
const hasPath = waypoints.length >= 2;
|
|
4142
4150
|
const loop = config.loop ?? true;
|
|
4143
|
-
const
|
|
4151
|
+
const calculatedDuration = useMemo11(() => {
|
|
4152
|
+
if (waypoints.length < 2) return 24;
|
|
4153
|
+
let total = 0;
|
|
4154
|
+
for (let i = 0; i < waypoints.length - 1; i++) {
|
|
4155
|
+
total += waypoints[i].segmentDuration ?? 3.5;
|
|
4156
|
+
}
|
|
4157
|
+
if (loop && waypoints.length > 1) {
|
|
4158
|
+
total += waypoints[waypoints.length - 1].segmentDuration ?? 3.5;
|
|
4159
|
+
}
|
|
4160
|
+
return Math.max(total, 4);
|
|
4161
|
+
}, [waypoints, loop]);
|
|
4162
|
+
const duration = config.duration ?? calculatedDuration;
|
|
4144
4163
|
const signature = useMemo11(
|
|
4145
|
-
() => waypoints.map(
|
|
4164
|
+
() => waypoints.map(
|
|
4165
|
+
(waypoint) => [
|
|
4166
|
+
...waypoint.position,
|
|
4167
|
+
...waypoint.target,
|
|
4168
|
+
waypoint.azimuthAngle ?? "",
|
|
4169
|
+
waypoint.polarAngle ?? "",
|
|
4170
|
+
waypoint.segmentDuration ?? ""
|
|
4171
|
+
].join(",")
|
|
4172
|
+
).join(";"),
|
|
4146
4173
|
[waypoints]
|
|
4147
4174
|
);
|
|
4175
|
+
const segmentTimes = useMemo11(() => {
|
|
4176
|
+
const times = [];
|
|
4177
|
+
if (waypoints.length < 2) return times;
|
|
4178
|
+
let accumulated = 0;
|
|
4179
|
+
for (let i = 0; i < waypoints.length - 1; i++) {
|
|
4180
|
+
accumulated += waypoints[i].segmentDuration ?? 3.5;
|
|
4181
|
+
times.push(accumulated);
|
|
4182
|
+
}
|
|
4183
|
+
if (loop && waypoints.length > 1) {
|
|
4184
|
+
accumulated += waypoints[waypoints.length - 1].segmentDuration ?? 3.5;
|
|
4185
|
+
times.push(accumulated);
|
|
4186
|
+
}
|
|
4187
|
+
return times;
|
|
4188
|
+
}, [waypoints, loop]);
|
|
4189
|
+
const getCurveParameter = useMemo11(() => {
|
|
4190
|
+
return (elapsed) => {
|
|
4191
|
+
if (waypoints.length < 2 || segmentTimes.length === 0) {
|
|
4192
|
+
return elapsed / duration;
|
|
4193
|
+
}
|
|
4194
|
+
const segmentTotal = segmentTimes[segmentTimes.length - 1];
|
|
4195
|
+
const playbackElapsed = loop ? elapsed % duration : Math.min(elapsed, duration);
|
|
4196
|
+
const normalizedElapsed = duration > 0 ? playbackElapsed / duration * segmentTotal : 0;
|
|
4197
|
+
for (let i = 0; i < segmentTimes.length; i++) {
|
|
4198
|
+
if (normalizedElapsed <= segmentTimes[i]) {
|
|
4199
|
+
const prevTime = i === 0 ? 0 : segmentTimes[i - 1];
|
|
4200
|
+
const segmentDuration = segmentTimes[i] - prevTime;
|
|
4201
|
+
const progress = segmentDuration > 0 ? (normalizedElapsed - prevTime) / segmentDuration : 0;
|
|
4202
|
+
const totalSegments = loop ? waypoints.length : waypoints.length - 1;
|
|
4203
|
+
const baseU = i / totalSegments;
|
|
4204
|
+
const segmentU = 1 / totalSegments;
|
|
4205
|
+
return baseU + progress * segmentU;
|
|
4206
|
+
}
|
|
4207
|
+
}
|
|
4208
|
+
return 1;
|
|
4209
|
+
};
|
|
4210
|
+
}, [waypoints.length, segmentTimes, duration, loop]);
|
|
4148
4211
|
const curves = useMemo11(() => {
|
|
4149
4212
|
if (!hasPath) return null;
|
|
4150
4213
|
return {
|
|
@@ -4162,29 +4225,83 @@ function CinematicCamera({
|
|
|
4162
4225
|
)
|
|
4163
4226
|
};
|
|
4164
4227
|
}, [signature, hasPath, loop]);
|
|
4228
|
+
const orbitPath = useMemo11(() => {
|
|
4229
|
+
if (!hasPath || !waypoints.every(
|
|
4230
|
+
(waypoint) => Number.isFinite(waypoint.azimuthAngle) && Number.isFinite(waypoint.polarAngle)
|
|
4231
|
+
)) {
|
|
4232
|
+
return null;
|
|
4233
|
+
}
|
|
4234
|
+
const points = waypoints.map((waypoint) => ({
|
|
4235
|
+
azimuth: waypoint.azimuthAngle,
|
|
4236
|
+
polar: waypoint.polarAngle,
|
|
4237
|
+
distance: new Vector36(...waypoint.position).distanceTo(
|
|
4238
|
+
new Vector36(...waypoint.target)
|
|
4239
|
+
)
|
|
4240
|
+
}));
|
|
4241
|
+
for (let index = 1; index < points.length; index += 1) {
|
|
4242
|
+
const previous = points[index - 1].azimuth;
|
|
4243
|
+
const current = points[index].azimuth;
|
|
4244
|
+
points[index].azimuth = current + Math.round((previous - current) / (Math.PI * 2)) * Math.PI * 2;
|
|
4245
|
+
}
|
|
4246
|
+
return points;
|
|
4247
|
+
}, [signature, hasPath]);
|
|
4165
4248
|
const elapsedRef = useRef8(0);
|
|
4166
4249
|
const enterElapsedRef = useRef8(0);
|
|
4167
4250
|
const enterPos = useRef8(new Vector36());
|
|
4168
4251
|
const enterTarget = useRef8(new Vector36());
|
|
4169
4252
|
const startedRef = useRef8(false);
|
|
4170
4253
|
const pausedElapsedRef = useRef8(0);
|
|
4254
|
+
const hasFinishedRef = useRef8(false);
|
|
4171
4255
|
const orbitRef = useRef8(null);
|
|
4172
4256
|
const tmpPos = useMemo11(() => new Vector36(), []);
|
|
4173
4257
|
const tmpTarget = useMemo11(() => new Vector36(), []);
|
|
4258
|
+
const tmpSpherical = useMemo11(() => new Spherical(), []);
|
|
4259
|
+
useEffect11(() => {
|
|
4260
|
+
elapsedRef.current = 0;
|
|
4261
|
+
pausedElapsedRef.current = 0;
|
|
4262
|
+
enterElapsedRef.current = 0;
|
|
4263
|
+
hasFinishedRef.current = false;
|
|
4264
|
+
}, [signature, loop, duration]);
|
|
4265
|
+
useEffect11(() => {
|
|
4266
|
+
if (resetOnPlay) {
|
|
4267
|
+
pausedElapsedRef.current = 0;
|
|
4268
|
+
elapsedRef.current = 0;
|
|
4269
|
+
hasFinishedRef.current = false;
|
|
4270
|
+
}
|
|
4271
|
+
}, [resetOnPlay]);
|
|
4174
4272
|
useEffect11(() => {
|
|
4175
4273
|
if (!playing) {
|
|
4176
4274
|
startedRef.current = false;
|
|
4177
|
-
if (elapsedRef.current > 0) {
|
|
4275
|
+
if (elapsedRef.current > 0 && !resetOnPlay) {
|
|
4178
4276
|
pausedElapsedRef.current = elapsedRef.current;
|
|
4277
|
+
} else if (resetOnPlay) {
|
|
4278
|
+
pausedElapsedRef.current = 0;
|
|
4279
|
+
}
|
|
4280
|
+
if (elapsedRef.current > 0 || hasFinishedRef.current) {
|
|
4281
|
+
onStop?.();
|
|
4179
4282
|
}
|
|
4180
4283
|
return;
|
|
4181
4284
|
}
|
|
4285
|
+
if (elapsedRef.current >= duration && !loop) {
|
|
4286
|
+
elapsedRef.current = 0;
|
|
4287
|
+
pausedElapsedRef.current = 0;
|
|
4288
|
+
hasFinishedRef.current = false;
|
|
4289
|
+
}
|
|
4182
4290
|
const controls = controlsRef.current;
|
|
4183
4291
|
if (controls) {
|
|
4184
4292
|
controls.getPosition(enterPos.current);
|
|
4185
4293
|
controls.getTarget(enterTarget.current);
|
|
4186
4294
|
}
|
|
4187
|
-
|
|
4295
|
+
if (resetOnPlay || hasFinishedRef.current || pausedElapsedRef.current === 0) {
|
|
4296
|
+
elapsedRef.current = 0;
|
|
4297
|
+
pausedElapsedRef.current = 0;
|
|
4298
|
+
hasFinishedRef.current = false;
|
|
4299
|
+
} else if (pausedElapsedRef.current > 0 && pausedElapsedRef.current < duration) {
|
|
4300
|
+
elapsedRef.current = pausedElapsedRef.current;
|
|
4301
|
+
} else {
|
|
4302
|
+
elapsedRef.current = 0;
|
|
4303
|
+
pausedElapsedRef.current = 0;
|
|
4304
|
+
}
|
|
4188
4305
|
enterElapsedRef.current = 0;
|
|
4189
4306
|
if (!hasPath) {
|
|
4190
4307
|
const scene = sceneRef.current;
|
|
@@ -4198,12 +4315,26 @@ function CinematicCamera({
|
|
|
4198
4315
|
}
|
|
4199
4316
|
startedRef.current = true;
|
|
4200
4317
|
invalidate();
|
|
4201
|
-
}, [
|
|
4318
|
+
}, [
|
|
4319
|
+
playing,
|
|
4320
|
+
hasPath,
|
|
4321
|
+
controlsRef,
|
|
4322
|
+
sceneRef,
|
|
4323
|
+
invalidate,
|
|
4324
|
+
duration,
|
|
4325
|
+
loop,
|
|
4326
|
+
signature,
|
|
4327
|
+
resetOnPlay,
|
|
4328
|
+
onStop
|
|
4329
|
+
]);
|
|
4202
4330
|
useEffect11(() => {
|
|
4203
4331
|
if (!playing || !onUserInteract) return;
|
|
4204
4332
|
const controls = controlsRef.current;
|
|
4205
4333
|
if (!controls) return;
|
|
4206
|
-
const handle = () =>
|
|
4334
|
+
const handle = () => {
|
|
4335
|
+
if (hasFinishedRef.current) return;
|
|
4336
|
+
onUserInteract();
|
|
4337
|
+
};
|
|
4207
4338
|
controls.addEventListener("controlstart", handle);
|
|
4208
4339
|
return () => controls.removeEventListener("controlstart", handle);
|
|
4209
4340
|
}, [playing, controlsRef, onUserInteract]);
|
|
@@ -4213,12 +4344,33 @@ function CinematicCamera({
|
|
|
4213
4344
|
if (!controls) return;
|
|
4214
4345
|
elapsedRef.current += delta;
|
|
4215
4346
|
enterElapsedRef.current += delta;
|
|
4216
|
-
let u = elapsedRef.current
|
|
4217
|
-
const finished = !loop &&
|
|
4347
|
+
let u = getCurveParameter(elapsedRef.current);
|
|
4348
|
+
const finished = !loop && elapsedRef.current >= duration;
|
|
4218
4349
|
u = loop ? u % 1 : Math.min(u, 1);
|
|
4219
4350
|
if (curves) {
|
|
4220
|
-
curves.
|
|
4221
|
-
|
|
4351
|
+
curves.target.getPoint(u, tmpTarget);
|
|
4352
|
+
if (orbitPath) {
|
|
4353
|
+
const segmentCount = loop ? orbitPath.length : orbitPath.length - 1;
|
|
4354
|
+
const scaled = Math.min(u, 1) * segmentCount;
|
|
4355
|
+
const segment = Math.min(Math.floor(scaled), segmentCount - 1);
|
|
4356
|
+
const next = (segment + 1) % orbitPath.length;
|
|
4357
|
+
const localT = u >= 1 ? 1 : scaled - segment;
|
|
4358
|
+
const eased = localT * localT * (3 - 2 * localT);
|
|
4359
|
+
const start = orbitPath[segment];
|
|
4360
|
+
const end = orbitPath[next];
|
|
4361
|
+
const endAzimuth = end.azimuth + Math.round((start.azimuth - end.azimuth) / (Math.PI * 2)) * Math.PI * 2;
|
|
4362
|
+
const azimuth = start.azimuth + (endAzimuth - start.azimuth) * eased;
|
|
4363
|
+
const polar = start.polar + (end.polar - start.polar) * eased;
|
|
4364
|
+
const distance = start.distance + (end.distance - start.distance) * eased;
|
|
4365
|
+
tmpSpherical.set(
|
|
4366
|
+
Math.max(distance, 1e-6),
|
|
4367
|
+
Math.min(Math.max(polar, 1e-6), Math.PI - 1e-6),
|
|
4368
|
+
azimuth
|
|
4369
|
+
);
|
|
4370
|
+
tmpPos.setFromSpherical(tmpSpherical).add(tmpTarget);
|
|
4371
|
+
} else {
|
|
4372
|
+
curves.position.getPoint(u, tmpPos);
|
|
4373
|
+
}
|
|
4222
4374
|
} else {
|
|
4223
4375
|
const orbit = orbitRef.current;
|
|
4224
4376
|
if (!orbit) return;
|
|
@@ -4248,6 +4400,8 @@ function CinematicCamera({
|
|
|
4248
4400
|
);
|
|
4249
4401
|
if (finished) {
|
|
4250
4402
|
startedRef.current = false;
|
|
4403
|
+
hasFinishedRef.current = true;
|
|
4404
|
+
elapsedRef.current = 0;
|
|
4251
4405
|
pausedElapsedRef.current = 0;
|
|
4252
4406
|
onFinished?.();
|
|
4253
4407
|
return;
|
|
@@ -5648,9 +5802,11 @@ function ModelViewer({
|
|
|
5648
5802
|
return { ...base ?? {}, ...cinematic };
|
|
5649
5803
|
}, [cinematic, resolvedSceneConfig]);
|
|
5650
5804
|
const [cinematicPlaying, setCinematicPlaying] = useState10(false);
|
|
5805
|
+
const [cinematicReset, setCinematicReset] = useState10(false);
|
|
5651
5806
|
const cinematicAutoStartedRef = useRef11(false);
|
|
5652
5807
|
useEffect15(() => {
|
|
5653
5808
|
setCinematicPlaying(false);
|
|
5809
|
+
setCinematicReset(false);
|
|
5654
5810
|
cinematicAutoStartedRef.current = false;
|
|
5655
5811
|
}, [modelUrl]);
|
|
5656
5812
|
useEffect15(() => {
|
|
@@ -5658,6 +5814,7 @@ function ModelViewer({
|
|
|
5658
5814
|
if (modelLoaded && cameraPositioned && !isAutoFraming) {
|
|
5659
5815
|
cinematicAutoStartedRef.current = true;
|
|
5660
5816
|
setCinematicPlaying(true);
|
|
5817
|
+
setCinematicReset(false);
|
|
5661
5818
|
}
|
|
5662
5819
|
}, [cinematicConfig, modelLoaded, cameraPositioned, isAutoFraming]);
|
|
5663
5820
|
const xrStore = useMemo13(() => {
|
|
@@ -6811,6 +6968,20 @@ function ModelViewer({
|
|
|
6811
6968
|
console.error("Reset camera error:", error);
|
|
6812
6969
|
}
|
|
6813
6970
|
}, [onAutoFit, fitScene]);
|
|
6971
|
+
const handleCinematicPlay = useCallback4(() => {
|
|
6972
|
+
setCinematicReset(false);
|
|
6973
|
+
setCinematicPlaying(true);
|
|
6974
|
+
}, []);
|
|
6975
|
+
const handleCinematicPause = useCallback4(() => {
|
|
6976
|
+
setCinematicPlaying(false);
|
|
6977
|
+
}, []);
|
|
6978
|
+
const handleCinematicStop = useCallback4(() => {
|
|
6979
|
+
setCinematicPlaying(false);
|
|
6980
|
+
setCinematicReset(true);
|
|
6981
|
+
setTimeout(() => {
|
|
6982
|
+
handleReset();
|
|
6983
|
+
}, 100);
|
|
6984
|
+
}, [handleReset]);
|
|
6814
6985
|
const handleCameraChangeWithAnnotations = useCallback4(
|
|
6815
6986
|
(cameraValue, controls) => {
|
|
6816
6987
|
handleCameraChangeInternal(cameraValue, controls);
|
|
@@ -7127,8 +7298,14 @@ function ModelViewer({
|
|
|
7127
7298
|
sceneRef,
|
|
7128
7299
|
config: cinematicConfig,
|
|
7129
7300
|
playing: cinematicPlaying,
|
|
7130
|
-
|
|
7131
|
-
|
|
7301
|
+
resetOnPlay: cinematicReset,
|
|
7302
|
+
onFinished: () => {
|
|
7303
|
+
setCinematicPlaying(false);
|
|
7304
|
+
setCinematicReset(false);
|
|
7305
|
+
},
|
|
7306
|
+
onUserInteract: () => {
|
|
7307
|
+
setCinematicPlaying(false);
|
|
7308
|
+
}
|
|
7132
7309
|
}
|
|
7133
7310
|
),
|
|
7134
7311
|
showMeasureTools && /* @__PURE__ */ jsx19(
|
|
@@ -7356,24 +7533,43 @@ function ModelViewer({
|
|
|
7356
7533
|
]
|
|
7357
7534
|
}
|
|
7358
7535
|
),
|
|
7359
|
-
cinematicConfig && /* @__PURE__ */
|
|
7536
|
+
cinematicConfig && /* @__PURE__ */ jsxs15(
|
|
7360
7537
|
"div",
|
|
7361
7538
|
{
|
|
7362
7539
|
className: "ri-cinematicTools",
|
|
7363
7540
|
style: {
|
|
7364
7541
|
top: `${topLeftRow((xrToolsVisible ? 1 : 0) + (showMeasureTools ? 1 : 0) + (showExplodeControls ? 1 : 0))}px`
|
|
7365
7542
|
},
|
|
7366
|
-
children:
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7543
|
+
children: [
|
|
7544
|
+
/* @__PURE__ */ jsx19(
|
|
7545
|
+
"button",
|
|
7546
|
+
{
|
|
7547
|
+
type: "button",
|
|
7548
|
+
className: `ri-measureButton ${cinematicPlaying ? "ri-measureButton--active" : ""}`,
|
|
7549
|
+
onClick: () => {
|
|
7550
|
+
if (cinematicPlaying) {
|
|
7551
|
+
setCinematicPlaying(false);
|
|
7552
|
+
} else {
|
|
7553
|
+
setCinematicReset(false);
|
|
7554
|
+
setCinematicPlaying(true);
|
|
7555
|
+
}
|
|
7556
|
+
},
|
|
7557
|
+
title: cinematicPlaying ? "Pause cinematic" : "Play cinematic",
|
|
7558
|
+
"aria-pressed": cinematicPlaying,
|
|
7559
|
+
children: cinematicPlaying ? /* @__PURE__ */ jsx19(Pause, { size: 16 }) : /* @__PURE__ */ jsx19(Play, { size: 16 })
|
|
7560
|
+
}
|
|
7561
|
+
),
|
|
7562
|
+
cinematicPlaying && /* @__PURE__ */ jsx19(
|
|
7563
|
+
"button",
|
|
7564
|
+
{
|
|
7565
|
+
type: "button",
|
|
7566
|
+
className: "ri-measureButton ri-measureButton--stop",
|
|
7567
|
+
onClick: handleCinematicStop,
|
|
7568
|
+
title: "Stop cinematic and reset camera",
|
|
7569
|
+
children: /* @__PURE__ */ jsx19(Square, { size: 16 })
|
|
7570
|
+
}
|
|
7571
|
+
)
|
|
7572
|
+
]
|
|
7377
7573
|
}
|
|
7378
7574
|
),
|
|
7379
7575
|
(showResetButton || showDownloadButton || showAnnotationNavigation && sortedAnnotations.length > 0) && /* @__PURE__ */ jsxs15("div", { className: "ri-buttonContainer", children: [
|
|
@@ -7613,7 +7809,7 @@ function ModelViewer({
|
|
|
7613
7809
|
}
|
|
7614
7810
|
|
|
7615
7811
|
// src/components/BindingBuilder/BindingBuilder.tsx
|
|
7616
|
-
import { useCallback as
|
|
7812
|
+
import { useCallback as useCallback9, useEffect as useEffect20, useMemo as useMemo16, useRef as useRef18, useState as useState25 } from "react";
|
|
7617
7813
|
import {
|
|
7618
7814
|
Redo2,
|
|
7619
7815
|
Undo2,
|
|
@@ -7660,14 +7856,16 @@ function buildCameraState(camera, controls) {
|
|
|
7660
7856
|
position: toTuple(position),
|
|
7661
7857
|
target: toTuple(target),
|
|
7662
7858
|
fov: "fov" in camera && typeof camera.fov === "number" ? camera.fov : null,
|
|
7663
|
-
zoom: "zoom" in camera && typeof camera.zoom === "number" ? camera.zoom : 1
|
|
7859
|
+
zoom: "zoom" in camera && typeof camera.zoom === "number" ? camera.zoom : 1,
|
|
7860
|
+
azimuthAngle: controls.azimuthAngle,
|
|
7861
|
+
polarAngle: controls.polarAngle
|
|
7664
7862
|
};
|
|
7665
7863
|
}
|
|
7666
7864
|
function isSameCameraState(left, right) {
|
|
7667
7865
|
if (!left) {
|
|
7668
7866
|
return false;
|
|
7669
7867
|
}
|
|
7670
|
-
return left.position[0] === right.position[0] && left.position[1] === right.position[1] && left.position[2] === right.position[2] && left.target[0] === right.target[0] && left.target[1] === right.target[1] && left.target[2] === right.target[2] && left.fov === right.fov && left.zoom === right.zoom;
|
|
7868
|
+
return left.position[0] === right.position[0] && left.position[1] === right.position[1] && left.position[2] === right.position[2] && left.target[0] === right.target[0] && left.target[1] === right.target[1] && left.target[2] === right.target[2] && left.fov === right.fov && left.zoom === right.zoom && left.azimuthAngle === right.azimuthAngle && left.polarAngle === right.polarAngle;
|
|
7671
7869
|
}
|
|
7672
7870
|
async function runSmoothCameraTransition2(controls, action, smoothTime = 0.6) {
|
|
7673
7871
|
const originalSmoothTime = controls.smoothTime;
|
|
@@ -7822,15 +8020,27 @@ function useViewerCamera() {
|
|
|
7822
8020
|
}
|
|
7823
8021
|
try {
|
|
7824
8022
|
markInitialAutoFitOverride(controls);
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
8023
|
+
const transitions = [
|
|
8024
|
+
controls.setLookAt(
|
|
8025
|
+
state.position[0],
|
|
8026
|
+
state.position[1],
|
|
8027
|
+
state.position[2],
|
|
8028
|
+
state.target[0],
|
|
8029
|
+
state.target[1],
|
|
8030
|
+
state.target[2],
|
|
8031
|
+
enableTransition
|
|
8032
|
+
)
|
|
8033
|
+
];
|
|
8034
|
+
if (state.azimuthAngle != null && state.polarAngle != null) {
|
|
8035
|
+
transitions.push(
|
|
8036
|
+
controls.rotateTo(
|
|
8037
|
+
state.azimuthAngle,
|
|
8038
|
+
state.polarAngle,
|
|
8039
|
+
enableTransition
|
|
8040
|
+
)
|
|
8041
|
+
);
|
|
8042
|
+
}
|
|
8043
|
+
await Promise.all(transitions);
|
|
7834
8044
|
const camera = controls.camera;
|
|
7835
8045
|
let projectionChanged = false;
|
|
7836
8046
|
if (state.fov !== null && "fov" in camera && typeof camera.fov === "number") {
|
|
@@ -8138,7 +8348,7 @@ function inferMimeType(path) {
|
|
|
8138
8348
|
}
|
|
8139
8349
|
|
|
8140
8350
|
// src/components/BindingBuilder/SceneSettings/SceneSettingsTab.tsx
|
|
8141
|
-
import { useState as
|
|
8351
|
+
import { useState as useState20, useEffect as useEffect18 } from "react";
|
|
8142
8352
|
|
|
8143
8353
|
// src/components/BindingBuilder/SceneSettings/GeneralSceneSettings.tsx
|
|
8144
8354
|
import { useState as useState14 } from "react";
|
|
@@ -8324,9 +8534,10 @@ function SliderRow({
|
|
|
8324
8534
|
max,
|
|
8325
8535
|
step,
|
|
8326
8536
|
formatValue,
|
|
8327
|
-
onChange
|
|
8537
|
+
onChange,
|
|
8538
|
+
disabled = false
|
|
8328
8539
|
}) {
|
|
8329
|
-
return /* @__PURE__ */ jsxs16("label", { className: "se-field"
|
|
8540
|
+
return /* @__PURE__ */ jsxs16("label", { className: `se-field ${disabled ? "se-field--disabled" : ""}`, children: [
|
|
8330
8541
|
/* @__PURE__ */ jsxs16("span", { className: "se-fieldLabel", children: [
|
|
8331
8542
|
label,
|
|
8332
8543
|
/* @__PURE__ */ jsx20("span", { className: "se-fieldValue", children: formatValue ? formatValue(value) : value })
|
|
@@ -8340,7 +8551,8 @@ function SliderRow({
|
|
|
8340
8551
|
max,
|
|
8341
8552
|
step,
|
|
8342
8553
|
value,
|
|
8343
|
-
onChange: (e) => onChange(Number(e.target.value))
|
|
8554
|
+
onChange: (e) => onChange(Number(e.target.value)),
|
|
8555
|
+
disabled
|
|
8344
8556
|
}
|
|
8345
8557
|
)
|
|
8346
8558
|
] });
|
|
@@ -10386,7 +10598,7 @@ function RampSection({
|
|
|
10386
10598
|
PPSection,
|
|
10387
10599
|
{
|
|
10388
10600
|
title: "Ramp",
|
|
10389
|
-
tooltip: "
|
|
10601
|
+
tooltip: "Adds a screen-space color gradient, or masks effects earlier in the stack.",
|
|
10390
10602
|
open: open.ramp,
|
|
10391
10603
|
onToggle: () => toggle("ramp"),
|
|
10392
10604
|
disabled: !ramp.enabled,
|
|
@@ -10446,6 +10658,17 @@ function RampSection({
|
|
|
10446
10658
|
onChange: (v) => set("ramp", { ...ramp, endColor: v })
|
|
10447
10659
|
}
|
|
10448
10660
|
),
|
|
10661
|
+
!ramp.rampMask && /* @__PURE__ */ jsx25(
|
|
10662
|
+
SliderRow,
|
|
10663
|
+
{
|
|
10664
|
+
label: "Gradient Opacity",
|
|
10665
|
+
value: ramp.opacity ?? 0.5,
|
|
10666
|
+
min: 0,
|
|
10667
|
+
max: 1,
|
|
10668
|
+
step: 0.01,
|
|
10669
|
+
onChange: (v) => set("ramp", { ...ramp, opacity: v })
|
|
10670
|
+
}
|
|
10671
|
+
),
|
|
10449
10672
|
/* @__PURE__ */ jsx25(
|
|
10450
10673
|
SliderRow,
|
|
10451
10674
|
{
|
|
@@ -10471,7 +10694,7 @@ function RampSection({
|
|
|
10471
10694
|
/* @__PURE__ */ jsx25(
|
|
10472
10695
|
ToggleRow,
|
|
10473
10696
|
{
|
|
10474
|
-
label: "Mask",
|
|
10697
|
+
label: "Mask Earlier Effects",
|
|
10475
10698
|
checked: ramp.rampMask,
|
|
10476
10699
|
onChange: (v) => set("ramp", { ...ramp, rampMask: v })
|
|
10477
10700
|
}
|
|
@@ -11745,14 +11968,16 @@ function AnnotationsTab({ config, onChange }) {
|
|
|
11745
11968
|
}
|
|
11746
11969
|
|
|
11747
11970
|
// src/components/BindingBuilder/SceneSettings/CinematicTab.tsx
|
|
11748
|
-
import {
|
|
11749
|
-
import {
|
|
11971
|
+
import { useState as useState19, useCallback as useCallback7 } from "react";
|
|
11972
|
+
import { ArrowDown, ArrowUp, MapPin as MapPin2, Plus as Plus2, Trash2 as Trash24, Settings } from "lucide-react";
|
|
11973
|
+
import { Fragment as Fragment10, jsx as jsx31, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
11750
11974
|
var DEFAULT_CINEMATIC = {
|
|
11751
11975
|
enabled: false,
|
|
11752
11976
|
waypoints: [],
|
|
11753
11977
|
loop: true,
|
|
11754
11978
|
autoPlay: false
|
|
11755
11979
|
};
|
|
11980
|
+
var DEFAULT_SEGMENT_DURATION = 3.5;
|
|
11756
11981
|
function CinematicTab({
|
|
11757
11982
|
config,
|
|
11758
11983
|
onChange,
|
|
@@ -11761,31 +11986,76 @@ function CinematicTab({
|
|
|
11761
11986
|
}) {
|
|
11762
11987
|
const cinematic = config.cinematic ?? DEFAULT_CINEMATIC;
|
|
11763
11988
|
const { waypoints } = cinematic;
|
|
11764
|
-
const
|
|
11989
|
+
const calculateTotalDuration = useCallback7((wps, loop) => {
|
|
11990
|
+
if (wps.length < 2) return 0;
|
|
11991
|
+
let total = 0;
|
|
11992
|
+
for (let i = 0; i < wps.length - 1; i++) {
|
|
11993
|
+
const segDur = wps[i].segmentDuration ?? DEFAULT_SEGMENT_DURATION;
|
|
11994
|
+
total += segDur;
|
|
11995
|
+
}
|
|
11996
|
+
if (loop && wps.length > 1) {
|
|
11997
|
+
total += wps[wps.length - 1].segmentDuration ?? DEFAULT_SEGMENT_DURATION;
|
|
11998
|
+
}
|
|
11999
|
+
return Math.max(total, 0);
|
|
12000
|
+
}, []);
|
|
12001
|
+
const update = useCallback7((patch) => {
|
|
11765
12002
|
onChange({ ...config, cinematic: { ...cinematic, ...patch } });
|
|
11766
|
-
};
|
|
11767
|
-
const addWaypoint = () => {
|
|
12003
|
+
}, [config, cinematic, onChange]);
|
|
12004
|
+
const addWaypoint = useCallback7(() => {
|
|
11768
12005
|
if (!cameraState) return;
|
|
12006
|
+
const newWaypoint = {
|
|
12007
|
+
position: [...cameraState.position],
|
|
12008
|
+
target: [...cameraState.target],
|
|
12009
|
+
...cameraState.azimuthAngle != null ? { azimuthAngle: cameraState.azimuthAngle } : {},
|
|
12010
|
+
...cameraState.polarAngle != null ? { polarAngle: cameraState.polarAngle } : {},
|
|
12011
|
+
segmentDuration: DEFAULT_SEGMENT_DURATION
|
|
12012
|
+
};
|
|
12013
|
+
const newWaypoints = [...waypoints, newWaypoint];
|
|
12014
|
+
const newDuration = calculateTotalDuration(newWaypoints, cinematic.loop);
|
|
11769
12015
|
update({
|
|
11770
|
-
waypoints:
|
|
11771
|
-
|
|
11772
|
-
{
|
|
11773
|
-
position: [...cameraState.position],
|
|
11774
|
-
target: [...cameraState.target]
|
|
11775
|
-
}
|
|
11776
|
-
]
|
|
12016
|
+
waypoints: newWaypoints,
|
|
12017
|
+
duration: newDuration
|
|
11777
12018
|
});
|
|
11778
|
-
};
|
|
11779
|
-
const deleteWaypoint = (index) => {
|
|
11780
|
-
|
|
11781
|
-
|
|
11782
|
-
|
|
12019
|
+
}, [cameraState, waypoints, cinematic.loop, calculateTotalDuration, update]);
|
|
12020
|
+
const deleteWaypoint = useCallback7((index) => {
|
|
12021
|
+
const newWaypoints = waypoints.filter((_, i) => i !== index);
|
|
12022
|
+
const newDuration = calculateTotalDuration(newWaypoints, cinematic.loop);
|
|
12023
|
+
update({
|
|
12024
|
+
waypoints: newWaypoints,
|
|
12025
|
+
duration: newDuration
|
|
12026
|
+
});
|
|
12027
|
+
}, [waypoints, cinematic.loop, calculateTotalDuration, update]);
|
|
12028
|
+
const moveWaypoint = useCallback7((index, delta) => {
|
|
11783
12029
|
const next = index + delta;
|
|
11784
12030
|
if (next < 0 || next >= waypoints.length) return;
|
|
11785
12031
|
const reordered = [...waypoints];
|
|
11786
12032
|
[reordered[index], reordered[next]] = [reordered[next], reordered[index]];
|
|
11787
|
-
|
|
11788
|
-
|
|
12033
|
+
const newDuration = calculateTotalDuration(reordered, cinematic.loop);
|
|
12034
|
+
update({
|
|
12035
|
+
waypoints: reordered,
|
|
12036
|
+
duration: newDuration
|
|
12037
|
+
});
|
|
12038
|
+
}, [waypoints, cinematic.loop, calculateTotalDuration, update]);
|
|
12039
|
+
const updateWaypoint = useCallback7((index, updates) => {
|
|
12040
|
+
const newWaypoints = waypoints.map((wp, i) => {
|
|
12041
|
+
if (i === index) {
|
|
12042
|
+
return { ...wp, ...updates };
|
|
12043
|
+
}
|
|
12044
|
+
return wp;
|
|
12045
|
+
});
|
|
12046
|
+
const newDuration = calculateTotalDuration(newWaypoints, cinematic.loop);
|
|
12047
|
+
update({
|
|
12048
|
+
waypoints: newWaypoints,
|
|
12049
|
+
duration: newDuration
|
|
12050
|
+
});
|
|
12051
|
+
}, [waypoints, cinematic.loop, calculateTotalDuration, update]);
|
|
12052
|
+
const [customizeDurations, setCustomizeDurations] = useState19(false);
|
|
12053
|
+
const hasSegmentDurations = waypoints.some((w) => w.segmentDuration !== void 0);
|
|
12054
|
+
const getDuration = useCallback7((index) => {
|
|
12055
|
+
if (index >= waypoints.length - 1) return DEFAULT_SEGMENT_DURATION;
|
|
12056
|
+
return waypoints[index]?.segmentDuration ?? DEFAULT_SEGMENT_DURATION;
|
|
12057
|
+
}, [waypoints]);
|
|
12058
|
+
const hasEnoughWaypoints = waypoints.length >= 2;
|
|
11789
12059
|
return /* @__PURE__ */ jsxs27(Section, { title: "Cinematic Camera", children: [
|
|
11790
12060
|
/* @__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." }),
|
|
11791
12061
|
/* @__PURE__ */ jsxs27(
|
|
@@ -11802,62 +12072,165 @@ function CinematicTab({
|
|
|
11802
12072
|
]
|
|
11803
12073
|
}
|
|
11804
12074
|
),
|
|
11805
|
-
|
|
11806
|
-
/* @__PURE__ */
|
|
11807
|
-
/* @__PURE__ */ jsxs27("div", { className: "se-waypointBody", children: [
|
|
11808
|
-
/* @__PURE__ */ jsxs27("div", { className: "se-waypointCoords", children: [
|
|
11809
|
-
"pos [",
|
|
11810
|
-
waypoint.position.map((n) => n.toFixed(1)).join(", "),
|
|
11811
|
-
"]"
|
|
11812
|
-
] }),
|
|
11813
|
-
/* @__PURE__ */ jsxs27("div", { className: "se-waypointCoords", children: [
|
|
11814
|
-
"look [",
|
|
11815
|
-
waypoint.target.map((n) => n.toFixed(1)).join(", "),
|
|
11816
|
-
"]"
|
|
11817
|
-
] })
|
|
11818
|
-
] }),
|
|
11819
|
-
/* @__PURE__ */ jsxs27("div", { className: "se-waypointActions", children: [
|
|
12075
|
+
/* @__PURE__ */ jsxs27("div", { className: "se-customizeToggle", children: [
|
|
12076
|
+
/* @__PURE__ */ jsxs27("label", { className: "se-toggleLabel", children: [
|
|
11820
12077
|
/* @__PURE__ */ jsx31(
|
|
11821
|
-
"
|
|
11822
|
-
{
|
|
11823
|
-
className: "se-waypointAction",
|
|
11824
|
-
onClick: () => onPreviewWaypoint?.(waypoint),
|
|
11825
|
-
title: "Fly camera to this waypoint",
|
|
11826
|
-
disabled: !onPreviewWaypoint,
|
|
11827
|
-
children: /* @__PURE__ */ jsx31(MapPin2, { size: 15 })
|
|
11828
|
-
}
|
|
11829
|
-
),
|
|
11830
|
-
/* @__PURE__ */ jsx31(
|
|
11831
|
-
"button",
|
|
11832
|
-
{
|
|
11833
|
-
className: "se-waypointAction",
|
|
11834
|
-
onClick: () => moveWaypoint(index, -1),
|
|
11835
|
-
title: "Move up",
|
|
11836
|
-
disabled: index === 0,
|
|
11837
|
-
children: /* @__PURE__ */ jsx31(ArrowUp, { size: 15 })
|
|
11838
|
-
}
|
|
11839
|
-
),
|
|
11840
|
-
/* @__PURE__ */ jsx31(
|
|
11841
|
-
"button",
|
|
12078
|
+
"input",
|
|
11842
12079
|
{
|
|
11843
|
-
|
|
11844
|
-
|
|
11845
|
-
|
|
11846
|
-
disabled: index === waypoints.length - 1,
|
|
11847
|
-
children: /* @__PURE__ */ jsx31(ArrowDown, { size: 15 })
|
|
12080
|
+
type: "checkbox",
|
|
12081
|
+
checked: customizeDurations,
|
|
12082
|
+
onChange: (e) => setCustomizeDurations(e.target.checked)
|
|
11848
12083
|
}
|
|
11849
12084
|
),
|
|
11850
|
-
/* @__PURE__ */ jsx31(
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
12085
|
+
/* @__PURE__ */ jsx31(Settings, { size: 14 }),
|
|
12086
|
+
/* @__PURE__ */ jsx31("span", { children: "Customize durations between waypoints" })
|
|
12087
|
+
] }),
|
|
12088
|
+
customizeDurations && /* @__PURE__ */ jsxs27("span", { className: "se-toggleHint", children: [
|
|
12089
|
+
"Set duration for each segment between waypoints: 1\u21922, 2\u21923, etc. Default: ",
|
|
12090
|
+
DEFAULT_SEGMENT_DURATION,
|
|
12091
|
+
"s"
|
|
12092
|
+
] })
|
|
12093
|
+
] }),
|
|
12094
|
+
waypoints.length === 0 ? /* @__PURE__ */ jsx31("div", { className: "bb-emptyAnnotations", children: "No waypoints yet. Position the camera and add your first one." }) : /* @__PURE__ */ jsxs27(Fragment10, { children: [
|
|
12095
|
+
/* @__PURE__ */ jsx31("div", { className: "se-waypointList", children: waypoints.map((waypoint, index) => {
|
|
12096
|
+
const dur = getDuration(index);
|
|
12097
|
+
const isLast = index === waypoints.length - 1;
|
|
12098
|
+
const isCustom = waypoint.segmentDuration !== void 0 && waypoint.segmentDuration !== DEFAULT_SEGMENT_DURATION;
|
|
12099
|
+
return /* @__PURE__ */ jsxs27("div", { children: [
|
|
12100
|
+
/* @__PURE__ */ jsxs27("div", { className: "se-waypointCard", children: [
|
|
12101
|
+
/* @__PURE__ */ jsx31("div", { className: "se-waypointBadge", children: index + 1 }),
|
|
12102
|
+
/* @__PURE__ */ jsxs27("div", { className: "se-waypointBody", children: [
|
|
12103
|
+
/* @__PURE__ */ jsxs27("div", { className: "se-waypointCoords", children: [
|
|
12104
|
+
"pos [",
|
|
12105
|
+
waypoint.position.map((n) => n.toFixed(1)).join(", "),
|
|
12106
|
+
"]"
|
|
12107
|
+
] }),
|
|
12108
|
+
/* @__PURE__ */ jsxs27("div", { className: "se-waypointCoords", children: [
|
|
12109
|
+
"look [",
|
|
12110
|
+
waypoint.target.map((n) => n.toFixed(1)).join(", "),
|
|
12111
|
+
"]"
|
|
12112
|
+
] }),
|
|
12113
|
+
customizeDurations && !isLast && /* @__PURE__ */ jsxs27("div", { className: `se-customBadge ${isCustom ? "se-customBadge--custom" : "se-customBadge--default"}`, children: [
|
|
12114
|
+
dur.toFixed(1),
|
|
12115
|
+
"s",
|
|
12116
|
+
!isCustom && " (default)"
|
|
12117
|
+
] })
|
|
12118
|
+
] }),
|
|
12119
|
+
/* @__PURE__ */ jsxs27("div", { className: "se-waypointActions", children: [
|
|
12120
|
+
/* @__PURE__ */ jsx31(
|
|
12121
|
+
"button",
|
|
12122
|
+
{
|
|
12123
|
+
className: "se-waypointAction",
|
|
12124
|
+
onClick: () => onPreviewWaypoint?.(waypoint),
|
|
12125
|
+
title: "Fly camera to this waypoint",
|
|
12126
|
+
disabled: !onPreviewWaypoint,
|
|
12127
|
+
children: /* @__PURE__ */ jsx31(MapPin2, { size: 15 })
|
|
12128
|
+
}
|
|
12129
|
+
),
|
|
12130
|
+
/* @__PURE__ */ jsx31(
|
|
12131
|
+
"button",
|
|
12132
|
+
{
|
|
12133
|
+
className: "se-waypointAction",
|
|
12134
|
+
onClick: () => moveWaypoint(index, -1),
|
|
12135
|
+
title: "Move up",
|
|
12136
|
+
disabled: index === 0,
|
|
12137
|
+
children: /* @__PURE__ */ jsx31(ArrowUp, { size: 15 })
|
|
12138
|
+
}
|
|
12139
|
+
),
|
|
12140
|
+
/* @__PURE__ */ jsx31(
|
|
12141
|
+
"button",
|
|
12142
|
+
{
|
|
12143
|
+
className: "se-waypointAction",
|
|
12144
|
+
onClick: () => moveWaypoint(index, 1),
|
|
12145
|
+
title: "Move down",
|
|
12146
|
+
disabled: isLast,
|
|
12147
|
+
children: /* @__PURE__ */ jsx31(ArrowDown, { size: 15 })
|
|
12148
|
+
}
|
|
12149
|
+
),
|
|
12150
|
+
/* @__PURE__ */ jsx31(
|
|
12151
|
+
"button",
|
|
12152
|
+
{
|
|
12153
|
+
className: "se-waypointAction se-waypointAction--delete",
|
|
12154
|
+
onClick: () => deleteWaypoint(index),
|
|
12155
|
+
title: "Delete waypoint",
|
|
12156
|
+
children: /* @__PURE__ */ jsx31(Trash24, { size: 15 })
|
|
12157
|
+
}
|
|
12158
|
+
)
|
|
12159
|
+
] })
|
|
12160
|
+
] }),
|
|
12161
|
+
customizeDurations && !isLast && /* @__PURE__ */ jsxs27("div", { className: "se-segmentBetween", children: [
|
|
12162
|
+
/* @__PURE__ */ jsxs27("div", { className: "se-segmentLabel", children: [
|
|
12163
|
+
index + 1,
|
|
12164
|
+
" \u2192 ",
|
|
12165
|
+
index + 2,
|
|
12166
|
+
!waypoint.segmentDuration && " (default)"
|
|
12167
|
+
] }),
|
|
12168
|
+
/* @__PURE__ */ jsxs27("div", { className: "se-segmentControls", children: [
|
|
12169
|
+
/* @__PURE__ */ jsx31("span", { className: "se-durationLabel", children: "Duration:" }),
|
|
12170
|
+
/* @__PURE__ */ jsx31(
|
|
12171
|
+
"input",
|
|
12172
|
+
{
|
|
12173
|
+
type: "range",
|
|
12174
|
+
className: "se-segmentSlider",
|
|
12175
|
+
min: 1,
|
|
12176
|
+
max: 20,
|
|
12177
|
+
step: 0.5,
|
|
12178
|
+
value: dur,
|
|
12179
|
+
onChange: (e) => {
|
|
12180
|
+
const val = parseFloat(e.target.value);
|
|
12181
|
+
updateWaypoint(index, { segmentDuration: val });
|
|
12182
|
+
}
|
|
12183
|
+
}
|
|
12184
|
+
),
|
|
12185
|
+
/* @__PURE__ */ jsxs27("span", { className: "se-segmentValue", children: [
|
|
12186
|
+
dur.toFixed(1),
|
|
12187
|
+
"s"
|
|
12188
|
+
] }),
|
|
12189
|
+
/* @__PURE__ */ jsxs27("div", { className: "se-presetButtons", children: [
|
|
12190
|
+
[2, 4, 6, 8].map((preset) => /* @__PURE__ */ jsxs27(
|
|
12191
|
+
"button",
|
|
12192
|
+
{
|
|
12193
|
+
className: "se-presetButton",
|
|
12194
|
+
onClick: () => updateWaypoint(index, { segmentDuration: preset }),
|
|
12195
|
+
children: [
|
|
12196
|
+
preset,
|
|
12197
|
+
"s"
|
|
12198
|
+
]
|
|
12199
|
+
},
|
|
12200
|
+
`preset-${index}-${preset}`
|
|
12201
|
+
)),
|
|
12202
|
+
/* @__PURE__ */ jsx31(
|
|
12203
|
+
"button",
|
|
12204
|
+
{
|
|
12205
|
+
className: "se-presetButton se-presetButton--default",
|
|
12206
|
+
onClick: () => {
|
|
12207
|
+
const newWaypoints = waypoints.map((wp, i) => {
|
|
12208
|
+
if (i === index) {
|
|
12209
|
+
const { segmentDuration, ...rest } = wp;
|
|
12210
|
+
return rest;
|
|
12211
|
+
}
|
|
12212
|
+
return wp;
|
|
12213
|
+
});
|
|
12214
|
+
const newDuration = calculateTotalDuration(newWaypoints, cinematic.loop);
|
|
12215
|
+
update({
|
|
12216
|
+
waypoints: newWaypoints,
|
|
12217
|
+
duration: newDuration
|
|
12218
|
+
});
|
|
12219
|
+
},
|
|
12220
|
+
children: "Reset"
|
|
12221
|
+
}
|
|
12222
|
+
)
|
|
12223
|
+
] })
|
|
12224
|
+
] })
|
|
12225
|
+
] })
|
|
12226
|
+
] }, `waypoint-wrapper-${index}`);
|
|
12227
|
+
}) }),
|
|
12228
|
+
/* @__PURE__ */ jsxs27("div", { className: "se-totalDuration", children: [
|
|
12229
|
+
/* @__PURE__ */ jsx31("span", { className: "se-totalLabel", children: "Total duration:" }),
|
|
12230
|
+
/* @__PURE__ */ jsx31("span", { className: "se-totalValue", children: hasEnoughWaypoints ? (cinematic.duration ?? calculateTotalDuration(waypoints, cinematic.loop)).toFixed(1) + "s" : "-" }),
|
|
12231
|
+
!hasEnoughWaypoints && /* @__PURE__ */ jsx31("span", { className: "se-totalHint", children: "(need at least 2 waypoints)" })
|
|
11859
12232
|
] })
|
|
11860
|
-
] }
|
|
12233
|
+
] }),
|
|
11861
12234
|
/* @__PURE__ */ jsx31(
|
|
11862
12235
|
ToggleRow,
|
|
11863
12236
|
{
|
|
@@ -11871,7 +12244,11 @@ function CinematicTab({
|
|
|
11871
12244
|
{
|
|
11872
12245
|
label: "Loop",
|
|
11873
12246
|
checked: cinematic.loop,
|
|
11874
|
-
onChange: (loop) =>
|
|
12247
|
+
onChange: (loop) => {
|
|
12248
|
+
const newLoop = loop;
|
|
12249
|
+
const newDuration = calculateTotalDuration(waypoints, newLoop);
|
|
12250
|
+
update({ loop: newLoop, duration: newDuration });
|
|
12251
|
+
}
|
|
11875
12252
|
}
|
|
11876
12253
|
),
|
|
11877
12254
|
/* @__PURE__ */ jsx31(
|
|
@@ -11882,11 +12259,11 @@ function CinematicTab({
|
|
|
11882
12259
|
onChange: (autoPlay) => update({ autoPlay })
|
|
11883
12260
|
}
|
|
11884
12261
|
),
|
|
11885
|
-
/* @__PURE__ */ jsx31(
|
|
12262
|
+
!customizeDurations && hasEnoughWaypoints && /* @__PURE__ */ jsx31(
|
|
11886
12263
|
SliderRow,
|
|
11887
12264
|
{
|
|
11888
12265
|
label: "Duration",
|
|
11889
|
-
value: cinematic.duration ??
|
|
12266
|
+
value: cinematic.duration ?? calculateTotalDuration(waypoints, cinematic.loop),
|
|
11890
12267
|
min: 4,
|
|
11891
12268
|
max: 120,
|
|
11892
12269
|
step: 1,
|
|
@@ -11898,7 +12275,7 @@ function CinematicTab({
|
|
|
11898
12275
|
}
|
|
11899
12276
|
|
|
11900
12277
|
// src/styles/SceneSettingsTab.css
|
|
11901
|
-
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');
|
|
12278
|
+
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 12px;\n border: 1px solid rgba(255, 255, 255, 0.08);\n border-radius: 8px;\n background: rgba(255, 255, 255, 0.03);\n}\n.se-waypointBadge {\n flex: none;\n width: 24px;\n height: 24px;\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.5);\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.5);\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.08);\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.se-customBadge {\n display: inline-block;\n padding: 1px 8px;\n border-radius: 3px;\n background: rgba(34, 211, 238, 0.15);\n color: #22d3ee;\n font-size: 10px;\n font-weight: 600;\n margin-top: 4px;\n}\n.se-customizeToggle {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 8px 12px;\n background: rgba(255, 255, 255, 0.03);\n border-radius: 8px;\n border: 1px solid rgba(148, 163, 184, 0.08);\n margin: 8px 0 12px;\n}\n.se-toggleLabel {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n font-weight: 500;\n color: #f8fafc;\n cursor: pointer;\n user-select: none;\n}\n.se-toggleLabel input[type=checkbox] {\n width: 16px;\n height: 16px;\n accent-color: #22d3ee;\n cursor: pointer;\n}\n.se-toggleLabel svg {\n color: #22d3ee;\n width: 14px;\n height: 14px;\n}\n.se-toggleHint {\n font-size: 11px;\n color: rgba(255, 255, 255, 0.4);\n padding-left: 28px;\n}\n.se-segmentBetween {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px 14px;\n margin: 6px 0 6px 32px;\n background: rgba(255, 255, 255, 0.03);\n border-radius: 6px;\n border-left: 3px solid rgba(34, 211, 238, 0.25);\n}\n.se-segmentLabel {\n font-size: 11px;\n font-weight: 500;\n color: rgba(255, 255, 255, 0.45);\n}\n.se-segmentControls {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n.se-durationLabel {\n font-size: 10px;\n color: rgba(255, 255, 255, 0.35);\n font-weight: 500;\n white-space: nowrap;\n}\n.se-segmentSlider {\n flex: 1;\n min-width: 80px;\n max-width: 150px;\n height: 4px;\n -webkit-appearance: none;\n appearance: none;\n background: rgba(255, 255, 255, 0.1);\n border-radius: 2px;\n outline: none;\n}\n.se-segmentSlider::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: #22d3ee;\n cursor: pointer;\n border: 2px solid rgba(255, 255, 255, 0.2);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n}\n.se-segmentSlider::-moz-range-thumb {\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: #22d3ee;\n cursor: pointer;\n border: 2px solid rgba(255, 255, 255, 0.2);\n}\n.se-segmentValue {\n font-size: 12px;\n font-weight: 600;\n color: #22d3ee;\n min-width: 36px;\n text-align: center;\n}\n.se-presetButtons {\n display: flex;\n gap: 4px;\n}\n.se-presetButton {\n padding: 2px 8px;\n background: rgba(255, 255, 255, 0.06);\n border: 1px solid rgba(148, 163, 184, 0.15);\n border-radius: 4px;\n font-size: 10px;\n color: rgba(255, 255, 255, 0.6);\n cursor: pointer;\n transition: all 0.2s ease;\n font-weight: 500;\n}\n.se-presetButton:hover {\n background: #22d3ee;\n color: #06343c;\n border-color: #22d3ee;\n}\n.se-totalDuration {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 4px;\n margin: 8px 0 12px;\n border-top: 1px solid rgba(148, 163, 184, 0.1);\n padding-top: 12px;\n}\n.se-totalLabel {\n font-size: 12px;\n color: rgba(255, 255, 255, 0.5);\n font-weight: 500;\n}\n.se-totalValue {\n font-size: 14px;\n font-weight: 600;\n color: #22d3ee;\n}\n.se-totalHint {\n font-size: 11px;\n color: rgba(255, 255, 255, 0.35);\n font-weight: 400;\n margin-left: 8px;\n}\n');
|
|
11902
12279
|
|
|
11903
12280
|
// src/components/BindingBuilder/SceneSettings/SceneSettingsTab.tsx
|
|
11904
12281
|
import {
|
|
@@ -11944,7 +12321,7 @@ function SceneSettingsTab({
|
|
|
11944
12321
|
animationClipDetails = [],
|
|
11945
12322
|
animationPlayback = FALLBACK_ANIMATION_PLAYBACK
|
|
11946
12323
|
}) {
|
|
11947
|
-
const [internalActiveSubTab, setInternalActiveSubTab] =
|
|
12324
|
+
const [internalActiveSubTab, setInternalActiveSubTab] = useState20("General");
|
|
11948
12325
|
const activeSubTab = controlledActiveSubTab ?? internalActiveSubTab;
|
|
11949
12326
|
const resolvedAnimationPlayback = animationPlayback ?? FALLBACK_ANIMATION_PLAYBACK;
|
|
11950
12327
|
const { tier } = useBindingBuilderContext();
|
|
@@ -12130,7 +12507,7 @@ function EnableToggle2({
|
|
|
12130
12507
|
}
|
|
12131
12508
|
|
|
12132
12509
|
// src/components/BindingBuilder/components/MaterialSection.tsx
|
|
12133
|
-
import { useState as
|
|
12510
|
+
import { useState as useState22 } from "react";
|
|
12134
12511
|
|
|
12135
12512
|
// src/components/ModelViewer/types/objectBinding.ts
|
|
12136
12513
|
var MATERIAL_SIDES = ["front", "back", "double"];
|
|
@@ -12177,7 +12554,7 @@ function MaterialPanelGroup({
|
|
|
12177
12554
|
}
|
|
12178
12555
|
|
|
12179
12556
|
// src/components/BindingBuilder/components/MaterialFields.tsx
|
|
12180
|
-
import { useRef as useRef15, useState as
|
|
12557
|
+
import { useRef as useRef15, useState as useState21 } from "react";
|
|
12181
12558
|
import { HexColorInput as HexColorInput3, HexColorPicker as HexColorPicker4 } from "react-colorful";
|
|
12182
12559
|
import { FileUp } from "lucide-react";
|
|
12183
12560
|
import { jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
@@ -12244,7 +12621,7 @@ function MaterialColorField({
|
|
|
12244
12621
|
fallback = "#ffffff",
|
|
12245
12622
|
onChange
|
|
12246
12623
|
}) {
|
|
12247
|
-
const [open, setOpen] =
|
|
12624
|
+
const [open, setOpen] = useState21(false);
|
|
12248
12625
|
const color = value || fallback;
|
|
12249
12626
|
const anchorRef = useRef15(null);
|
|
12250
12627
|
return /* @__PURE__ */ jsxs30("div", { className: "bb-field", children: [
|
|
@@ -12373,7 +12750,7 @@ function MaterialSliderField({
|
|
|
12373
12750
|
}
|
|
12374
12751
|
|
|
12375
12752
|
// src/components/BindingBuilder/components/MaterialSection.tsx
|
|
12376
|
-
import { Fragment as
|
|
12753
|
+
import { Fragment as Fragment11, jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
12377
12754
|
function MaterialSection({
|
|
12378
12755
|
selectedItem,
|
|
12379
12756
|
selectedMaterial,
|
|
@@ -12398,11 +12775,11 @@ function MaterialSection({
|
|
|
12398
12775
|
tier: "starter",
|
|
12399
12776
|
locked: !canUseStarterFeatures
|
|
12400
12777
|
};
|
|
12401
|
-
const [roughnessMode, setRoughnessMode] =
|
|
12402
|
-
const [anisotropyMode, setAnisotropyMode] =
|
|
12778
|
+
const [roughnessMode, setRoughnessMode] = useState22("roughness");
|
|
12779
|
+
const [anisotropyMode, setAnisotropyMode] = useState22(
|
|
12403
12780
|
"factor"
|
|
12404
12781
|
);
|
|
12405
|
-
return /* @__PURE__ */ jsxs31(
|
|
12782
|
+
return /* @__PURE__ */ jsxs31(Fragment11, { children: [
|
|
12406
12783
|
/* @__PURE__ */ jsx36(
|
|
12407
12784
|
MaterialPanelGroup,
|
|
12408
12785
|
{
|
|
@@ -12441,7 +12818,7 @@ function MaterialSection({
|
|
|
12441
12818
|
}))
|
|
12442
12819
|
}
|
|
12443
12820
|
),
|
|
12444
|
-
pbrWorkflow === "metalness" ? /* @__PURE__ */ jsxs31(
|
|
12821
|
+
pbrWorkflow === "metalness" ? /* @__PURE__ */ jsxs31(Fragment11, { children: [
|
|
12445
12822
|
/* @__PURE__ */ jsx36(
|
|
12446
12823
|
MaterialSliderField,
|
|
12447
12824
|
{
|
|
@@ -12465,7 +12842,7 @@ function MaterialSection({
|
|
|
12465
12842
|
onChange: (value) => setSelectedTexturePath(selectedItem, "metalnessMap", value)
|
|
12466
12843
|
}
|
|
12467
12844
|
)
|
|
12468
|
-
] }) : /* @__PURE__ */ jsxs31(
|
|
12845
|
+
] }) : /* @__PURE__ */ jsxs31(Fragment11, { children: [
|
|
12469
12846
|
/* @__PURE__ */ jsx36(
|
|
12470
12847
|
MaterialSliderField,
|
|
12471
12848
|
{
|
|
@@ -13438,7 +13815,7 @@ function MaterialPresetGallery({
|
|
|
13438
13815
|
}
|
|
13439
13816
|
|
|
13440
13817
|
// src/components/BindingBuilder/components/ToolbarExportMenu.tsx
|
|
13441
|
-
import { useEffect as useEffect19, useRef as useRef16, useState as
|
|
13818
|
+
import { useEffect as useEffect19, useRef as useRef16, useState as useState23 } from "react";
|
|
13442
13819
|
import { ChevronDown as ChevronDown5, Download as Download2, FileCode2, FileJson } from "lucide-react";
|
|
13443
13820
|
import { jsx as jsx38, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
13444
13821
|
function ToolbarExportMenu({
|
|
@@ -13447,7 +13824,7 @@ function ToolbarExportMenu({
|
|
|
13447
13824
|
onExportJson,
|
|
13448
13825
|
onExportTs
|
|
13449
13826
|
}) {
|
|
13450
|
-
const [open, setOpen] =
|
|
13827
|
+
const [open, setOpen] = useState23(false);
|
|
13451
13828
|
const wrapperRef = useRef16(null);
|
|
13452
13829
|
useEffect19(() => {
|
|
13453
13830
|
if (!open) return;
|
|
@@ -13781,7 +14158,7 @@ function ActionsPanel({
|
|
|
13781
14158
|
|
|
13782
14159
|
// src/components/BindingBuilder/components/CameraPositionPanel.tsx
|
|
13783
14160
|
import { MapPin as MapPin3, Trash2 as Trash25 } from "lucide-react";
|
|
13784
|
-
import { Fragment as
|
|
14161
|
+
import { Fragment as Fragment12, jsx as jsx42, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
13785
14162
|
function CameraPositionPanel({
|
|
13786
14163
|
selectedBinding,
|
|
13787
14164
|
open,
|
|
@@ -13800,7 +14177,7 @@ function CameraPositionPanel({
|
|
|
13800
14177
|
/* @__PURE__ */ jsx42("span", { className: "bb-cameraLiveBadge", children: "LIVE" }),
|
|
13801
14178
|
/* @__PURE__ */ jsxs37("div", { className: "se-waypointBody", children: [
|
|
13802
14179
|
/* @__PURE__ */ jsx42("div", { className: "bb-cameraWaypointLabel", children: "Current view" }),
|
|
13803
|
-
cameraState ? /* @__PURE__ */ jsxs37(
|
|
14180
|
+
cameraState ? /* @__PURE__ */ jsxs37(Fragment12, { children: [
|
|
13804
14181
|
/* @__PURE__ */ jsxs37("div", { className: "se-waypointCoords", children: [
|
|
13805
14182
|
"pos ",
|
|
13806
14183
|
formattedCameraState?.position
|
|
@@ -13886,7 +14263,7 @@ function CameraPositionPanel({
|
|
|
13886
14263
|
}
|
|
13887
14264
|
|
|
13888
14265
|
// src/components/BindingBuilder/components/SceneNodesPanel.tsx
|
|
13889
|
-
import { useMemo as useMemo15, useState as
|
|
14266
|
+
import { useMemo as useMemo15, useState as useState24 } from "react";
|
|
13890
14267
|
|
|
13891
14268
|
// src/components/BindingBuilder/utils/bindingFactory.ts
|
|
13892
14269
|
function slugify(value) {
|
|
@@ -13966,7 +14343,7 @@ function collectNodeNames(scene) {
|
|
|
13966
14343
|
}
|
|
13967
14344
|
|
|
13968
14345
|
// src/components/BindingBuilder/components/SceneNodesPanel.tsx
|
|
13969
|
-
import { Fragment as
|
|
14346
|
+
import { Fragment as Fragment13, jsx as jsx43, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
13970
14347
|
function SceneNodesPanel({
|
|
13971
14348
|
bindings,
|
|
13972
14349
|
selectedItem,
|
|
@@ -13975,7 +14352,7 @@ function SceneNodesPanel({
|
|
|
13975
14352
|
isOpen = true,
|
|
13976
14353
|
onToggle
|
|
13977
14354
|
}) {
|
|
13978
|
-
const [search, setSearch] =
|
|
14355
|
+
const [search, setSearch] = useState24("");
|
|
13979
14356
|
const nodeKeys = useMemo15(() => Object.keys(bindings), [bindings]);
|
|
13980
14357
|
const filteredNodeKeys = useMemo15(() => {
|
|
13981
14358
|
const query = search.trim().toLowerCase();
|
|
@@ -14002,7 +14379,7 @@ function SceneNodesPanel({
|
|
|
14002
14379
|
children: "\u2190"
|
|
14003
14380
|
}
|
|
14004
14381
|
),
|
|
14005
|
-
isOpen && /* @__PURE__ */ jsxs38(
|
|
14382
|
+
isOpen && /* @__PURE__ */ jsxs38(Fragment13, { children: [
|
|
14006
14383
|
/* @__PURE__ */ jsxs38("div", { children: [
|
|
14007
14384
|
/* @__PURE__ */ jsx43("div", { className: "bb-sectionKicker", children: "Scene Nodes" }),
|
|
14008
14385
|
/* @__PURE__ */ jsxs38("div", { className: "bb-sectionHeading", children: [
|
|
@@ -14120,7 +14497,7 @@ function mergeAnimationConfig(sceneConfig, animationDetails) {
|
|
|
14120
14497
|
}
|
|
14121
14498
|
|
|
14122
14499
|
// src/components/BindingBuilder/hooks/useUndoableState.ts
|
|
14123
|
-
import { useCallback as
|
|
14500
|
+
import { useCallback as useCallback8, useReducer, useRef as useRef17 } from "react";
|
|
14124
14501
|
var MAX_HISTORY = 100;
|
|
14125
14502
|
function historyReducer(state, action) {
|
|
14126
14503
|
switch (action.type) {
|
|
@@ -14165,7 +14542,7 @@ function useUndoableState(initial, coalesceWindowMs = 600) {
|
|
|
14165
14542
|
future: []
|
|
14166
14543
|
});
|
|
14167
14544
|
const lastSetAt = useRef17(0);
|
|
14168
|
-
const setValue =
|
|
14545
|
+
const setValue = useCallback8(
|
|
14169
14546
|
(updater) => {
|
|
14170
14547
|
const now = Date.now();
|
|
14171
14548
|
const coalesce = now - lastSetAt.current < coalesceWindowMs;
|
|
@@ -14174,15 +14551,15 @@ function useUndoableState(initial, coalesceWindowMs = 600) {
|
|
|
14174
14551
|
},
|
|
14175
14552
|
[coalesceWindowMs]
|
|
14176
14553
|
);
|
|
14177
|
-
const undo =
|
|
14554
|
+
const undo = useCallback8(() => {
|
|
14178
14555
|
lastSetAt.current = 0;
|
|
14179
14556
|
dispatch({ type: "undo" });
|
|
14180
14557
|
}, []);
|
|
14181
|
-
const redo =
|
|
14558
|
+
const redo = useCallback8(() => {
|
|
14182
14559
|
lastSetAt.current = 0;
|
|
14183
14560
|
dispatch({ type: "redo" });
|
|
14184
14561
|
}, []);
|
|
14185
|
-
const reset =
|
|
14562
|
+
const reset = useCallback8((value) => {
|
|
14186
14563
|
lastSetAt.current = 0;
|
|
14187
14564
|
dispatch({ type: "reset", value });
|
|
14188
14565
|
}, []);
|
|
@@ -14411,7 +14788,7 @@ function removeMaterialPanelFields(material, panel) {
|
|
|
14411
14788
|
}
|
|
14412
14789
|
|
|
14413
14790
|
// src/components/BindingBuilder/BindingBuilder.tsx
|
|
14414
|
-
import { Fragment as
|
|
14791
|
+
import { Fragment as Fragment14, jsx as jsx45, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
14415
14792
|
var DEMO_MODEL_URL = "/room.glb";
|
|
14416
14793
|
function isPlainObject(value) {
|
|
14417
14794
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -14431,27 +14808,27 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14431
14808
|
const modelInputRef = useRef18(null);
|
|
14432
14809
|
const bindingsInputRef = useRef18(null);
|
|
14433
14810
|
const sceneConfigInputRef = useRef18(null);
|
|
14434
|
-
const [modelUrl, setModelUrl] =
|
|
14435
|
-
const [modelName, setModelName] =
|
|
14436
|
-
const [selectedItem, setSelectedItem] =
|
|
14811
|
+
const [modelUrl, setModelUrl] = useState25(null);
|
|
14812
|
+
const [modelName, setModelName] = useState25("");
|
|
14813
|
+
const [selectedItem, setSelectedItem] = useState25(null);
|
|
14437
14814
|
const bindingsHistory = useUndoableState({});
|
|
14438
14815
|
const bindings = bindingsHistory.value;
|
|
14439
14816
|
const setBindings = bindingsHistory.setValue;
|
|
14440
|
-
const [loading, setLoading] =
|
|
14441
|
-
const [isExporting, setIsExporting] =
|
|
14442
|
-
const [statusMessage, setStatusMessage] =
|
|
14817
|
+
const [loading, setLoading] = useState25(false);
|
|
14818
|
+
const [isExporting, setIsExporting] = useState25(false);
|
|
14819
|
+
const [statusMessage, setStatusMessage] = useState25(
|
|
14443
14820
|
"Load the demo model or upload a GLB/GLTF model to generate bindings."
|
|
14444
14821
|
);
|
|
14445
|
-
const [statusTone, setStatusTone] =
|
|
14446
|
-
const [statusBannerVisible, setStatusBannerVisible] =
|
|
14447
|
-
const [metricsDraft, setMetricsDraft] =
|
|
14448
|
-
const [metadataDraft, setMetadataDraft] =
|
|
14449
|
-
const [metricsError, setMetricsError] =
|
|
14450
|
-
const [metadataError, setMetadataError] =
|
|
14451
|
-
const [isPreviewDragActive, setIsPreviewDragActive] =
|
|
14452
|
-
const [isEditorCollapsed, setIsEditorCollapsed] =
|
|
14453
|
-
const [isNodesCollapsed, setIsNodesCollapsed] =
|
|
14454
|
-
const [openObjectSections, setOpenObjectSections] =
|
|
14822
|
+
const [statusTone, setStatusTone] = useState25("default");
|
|
14823
|
+
const [statusBannerVisible, setStatusBannerVisible] = useState25(false);
|
|
14824
|
+
const [metricsDraft, setMetricsDraft] = useState25("{}");
|
|
14825
|
+
const [metadataDraft, setMetadataDraft] = useState25("{}");
|
|
14826
|
+
const [metricsError, setMetricsError] = useState25(null);
|
|
14827
|
+
const [metadataError, setMetadataError] = useState25(null);
|
|
14828
|
+
const [isPreviewDragActive, setIsPreviewDragActive] = useState25(false);
|
|
14829
|
+
const [isEditorCollapsed, setIsEditorCollapsed] = useState25(false);
|
|
14830
|
+
const [isNodesCollapsed, setIsNodesCollapsed] = useState25(false);
|
|
14831
|
+
const [openObjectSections, setOpenObjectSections] = useState25({
|
|
14455
14832
|
identity: true,
|
|
14456
14833
|
basics: true,
|
|
14457
14834
|
material: true,
|
|
@@ -14460,7 +14837,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14460
14837
|
metrics: false,
|
|
14461
14838
|
metadata: false
|
|
14462
14839
|
});
|
|
14463
|
-
const [openMaterialPanels, setOpenMaterialPanels] =
|
|
14840
|
+
const [openMaterialPanels, setOpenMaterialPanels] = useState25({
|
|
14464
14841
|
pbr: true,
|
|
14465
14842
|
roughness: false,
|
|
14466
14843
|
emission: false,
|
|
@@ -14476,24 +14853,24 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14476
14853
|
});
|
|
14477
14854
|
const toggleObjectSection = (key) => setOpenObjectSections((prev) => ({ ...prev, [key]: !prev[key] }));
|
|
14478
14855
|
const toggleMaterialPanel = (key) => setOpenMaterialPanels((prev) => ({ ...prev, [key]: !prev[key] }));
|
|
14479
|
-
const [activeEditorTab, setActiveEditorTab] =
|
|
14856
|
+
const [activeEditorTab, setActiveEditorTab] = useState25(
|
|
14480
14857
|
"object"
|
|
14481
14858
|
);
|
|
14482
|
-
const [activeSubTab, setActiveSubTab] =
|
|
14483
|
-
const [pbrWorkflow, setPbrWorkflow] =
|
|
14484
|
-
const [materialPanelEnabled, setMaterialPanelEnabled] =
|
|
14859
|
+
const [activeSubTab, setActiveSubTab] = useState25("General");
|
|
14860
|
+
const [pbrWorkflow, setPbrWorkflow] = useState25("metalness");
|
|
14861
|
+
const [materialPanelEnabled, setMaterialPanelEnabled] = useState25(createMaterialPanelEnabledState());
|
|
14485
14862
|
const materialPanelItemRef = useRef18(null);
|
|
14486
|
-
const [zoomOnSelected, setZoomOnSelected] =
|
|
14487
|
-
const [highlightOnHover, setHighlightOnHover] =
|
|
14488
|
-
const [resetDialogOpen, setResetDialogOpen] =
|
|
14489
|
-
const [resetTarget, setResetTarget] =
|
|
14863
|
+
const [zoomOnSelected, setZoomOnSelected] = useState25(false);
|
|
14864
|
+
const [highlightOnHover, setHighlightOnHover] = useState25(true);
|
|
14865
|
+
const [resetDialogOpen, setResetDialogOpen] = useState25(false);
|
|
14866
|
+
const [resetTarget, setResetTarget] = useState25(
|
|
14490
14867
|
null
|
|
14491
14868
|
);
|
|
14492
14869
|
const sceneConfigHistory = useUndoableState(DEFAULT_SCENE_CONFIG);
|
|
14493
14870
|
const sceneConfig = sceneConfigHistory.value;
|
|
14494
14871
|
const setSceneConfig = sceneConfigHistory.setValue;
|
|
14495
|
-
const [activeAnnotation, setActiveAnnotation] =
|
|
14496
|
-
const [panelHoveredBinding, setPanelHoveredBinding] =
|
|
14872
|
+
const [activeAnnotation, setActiveAnnotation] = useState25(null);
|
|
14873
|
+
const [panelHoveredBinding, setPanelHoveredBinding] = useState25(null);
|
|
14497
14874
|
const license = useLicense(licenseKey);
|
|
14498
14875
|
const tier = normalizeTier(license.tier);
|
|
14499
14876
|
const bindingBuilderContextValue = useMemo16(
|
|
@@ -14519,14 +14896,16 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14519
14896
|
handleCameraChange,
|
|
14520
14897
|
handleViewerReady
|
|
14521
14898
|
} = useViewerCamera();
|
|
14522
|
-
const handlePreviewWaypoint =
|
|
14899
|
+
const handlePreviewWaypoint = useCallback9(
|
|
14523
14900
|
(waypoint) => {
|
|
14524
14901
|
setCameraState(
|
|
14525
14902
|
{
|
|
14526
14903
|
position: waypoint.position,
|
|
14527
14904
|
target: waypoint.target,
|
|
14528
14905
|
fov: cameraState?.fov ?? null,
|
|
14529
|
-
zoom: cameraState?.zoom ?? 1
|
|
14906
|
+
zoom: cameraState?.zoom ?? 1,
|
|
14907
|
+
azimuthAngle: waypoint.azimuthAngle,
|
|
14908
|
+
polarAngle: waypoint.polarAngle
|
|
14530
14909
|
},
|
|
14531
14910
|
true
|
|
14532
14911
|
);
|
|
@@ -14541,7 +14920,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14541
14920
|
position: formatVector3(cameraState.position),
|
|
14542
14921
|
target: formatVector3(cameraState.target)
|
|
14543
14922
|
} : null;
|
|
14544
|
-
const handleAnnotationsChange =
|
|
14923
|
+
const handleAnnotationsChange = useCallback9(
|
|
14545
14924
|
(newAnnotations) => {
|
|
14546
14925
|
setSceneConfig((prev) => {
|
|
14547
14926
|
const currentIds = prev.annotations.map((a) => a.id).join(",");
|
|
@@ -14557,7 +14936,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14557
14936
|
},
|
|
14558
14937
|
[]
|
|
14559
14938
|
);
|
|
14560
|
-
const handleActiveAnnotationChange =
|
|
14939
|
+
const handleActiveAnnotationChange = useCallback9(
|
|
14561
14940
|
(annotation) => {
|
|
14562
14941
|
setActiveAnnotation(
|
|
14563
14942
|
annotation ? {
|
|
@@ -14678,7 +15057,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14678
15057
|
});
|
|
14679
15058
|
}
|
|
14680
15059
|
};
|
|
14681
|
-
const applyBindingsFromScene =
|
|
15060
|
+
const applyBindingsFromScene = useCallback9(
|
|
14682
15061
|
(scene, nextModelName) => {
|
|
14683
15062
|
const nodeNames = collectNodeNames(scene);
|
|
14684
15063
|
bindingsHistory.reset(buildBindingsFromNodes(nodeNames));
|
|
@@ -14717,7 +15096,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14717
15096
|
bindingsHistory.reset({});
|
|
14718
15097
|
setSelectedItem(null);
|
|
14719
15098
|
};
|
|
14720
|
-
const handleViewerModelLoaded =
|
|
15099
|
+
const handleViewerModelLoaded = useCallback9(
|
|
14721
15100
|
(scene) => {
|
|
14722
15101
|
const pending = pendingLoadRef.current;
|
|
14723
15102
|
if (!pending || pending.requestId !== loadRequestRef.current) return;
|
|
@@ -14727,7 +15106,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14727
15106
|
},
|
|
14728
15107
|
[applyBindingsFromScene]
|
|
14729
15108
|
);
|
|
14730
|
-
const handleViewerLoadError =
|
|
15109
|
+
const handleViewerLoadError = useCallback9((error) => {
|
|
14731
15110
|
const pending = pendingLoadRef.current;
|
|
14732
15111
|
if (!pending || pending.requestId !== loadRequestRef.current) return;
|
|
14733
15112
|
pendingLoadRef.current = null;
|
|
@@ -14738,7 +15117,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14738
15117
|
setStatusTone("error");
|
|
14739
15118
|
setLoading(false);
|
|
14740
15119
|
}, []);
|
|
14741
|
-
const handleAnimationsReady =
|
|
15120
|
+
const handleAnimationsReady = useCallback9(
|
|
14742
15121
|
(controls) => {
|
|
14743
15122
|
viewerAnimations.handleAnimationsReady(controls);
|
|
14744
15123
|
const details = controls.clipDetails ?? controls.clips.map((sourceName) => ({ sourceName, duration: 0 }));
|
|
@@ -15039,7 +15418,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15039
15418
|
}));
|
|
15040
15419
|
setError(null);
|
|
15041
15420
|
};
|
|
15042
|
-
const handleViewerSelect =
|
|
15421
|
+
const handleViewerSelect = useCallback9(
|
|
15043
15422
|
(binding) => {
|
|
15044
15423
|
if (!binding) {
|
|
15045
15424
|
setSelectedItem(null);
|
|
@@ -15052,7 +15431,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15052
15431
|
},
|
|
15053
15432
|
[bindings]
|
|
15054
15433
|
);
|
|
15055
|
-
const handleSceneNodeSelect =
|
|
15434
|
+
const handleSceneNodeSelect = useCallback9(
|
|
15056
15435
|
async (nodeKey) => {
|
|
15057
15436
|
setSelectedItem(nodeKey);
|
|
15058
15437
|
setPanelHoveredBinding(null);
|
|
@@ -15060,7 +15439,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15060
15439
|
},
|
|
15061
15440
|
[focusObject]
|
|
15062
15441
|
);
|
|
15063
|
-
const saveCameraPositionForSelectedObject =
|
|
15442
|
+
const saveCameraPositionForSelectedObject = useCallback9(() => {
|
|
15064
15443
|
if (!selectedItem || !cameraState) {
|
|
15065
15444
|
return;
|
|
15066
15445
|
}
|
|
@@ -15075,7 +15454,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15075
15454
|
cameraState: currentCameraState
|
|
15076
15455
|
}));
|
|
15077
15456
|
}, [selectedItem, cameraState]);
|
|
15078
|
-
const goToSavedCameraPosition =
|
|
15457
|
+
const goToSavedCameraPosition = useCallback9(() => {
|
|
15079
15458
|
if (!savedCameraPosition?.position || !savedCameraPosition?.target) return;
|
|
15080
15459
|
setCameraState(
|
|
15081
15460
|
{
|
|
@@ -15087,28 +15466,28 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15087
15466
|
true
|
|
15088
15467
|
);
|
|
15089
15468
|
}, [savedCameraPosition, setCameraState, cameraState]);
|
|
15090
|
-
const clearSavedCameraPosition =
|
|
15469
|
+
const clearSavedCameraPosition = useCallback9(() => {
|
|
15091
15470
|
if (!selectedItem) return;
|
|
15092
15471
|
updateBinding(selectedItem, (binding) => ({
|
|
15093
15472
|
...binding,
|
|
15094
15473
|
cameraState: void 0
|
|
15095
15474
|
}));
|
|
15096
15475
|
}, [selectedItem]);
|
|
15097
|
-
const handleResetView =
|
|
15476
|
+
const handleResetView = useCallback9(async () => {
|
|
15098
15477
|
await resetView();
|
|
15099
15478
|
}, [resetView]);
|
|
15100
|
-
const handleFitScene =
|
|
15479
|
+
const handleFitScene = useCallback9(async () => {
|
|
15101
15480
|
await fitScene();
|
|
15102
15481
|
}, [fitScene]);
|
|
15103
|
-
const openResetDialog =
|
|
15482
|
+
const openResetDialog = useCallback9((target) => {
|
|
15104
15483
|
setResetTarget(target);
|
|
15105
15484
|
setResetDialogOpen(true);
|
|
15106
15485
|
}, []);
|
|
15107
|
-
const closeResetDialog =
|
|
15486
|
+
const closeResetDialog = useCallback9(() => {
|
|
15108
15487
|
setResetDialogOpen(false);
|
|
15109
15488
|
setResetTarget(null);
|
|
15110
15489
|
}, []);
|
|
15111
|
-
const executeReset =
|
|
15490
|
+
const executeReset = useCallback9(() => {
|
|
15112
15491
|
if (resetTarget === "bindings") {
|
|
15113
15492
|
if (!nodeKeys.length) {
|
|
15114
15493
|
closeResetDialog();
|
|
@@ -15205,7 +15584,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15205
15584
|
}
|
|
15206
15585
|
)
|
|
15207
15586
|
] }),
|
|
15208
|
-
!isEditorCollapsed && /* @__PURE__ */ jsxs40(
|
|
15587
|
+
!isEditorCollapsed && /* @__PURE__ */ jsxs40(Fragment14, { children: [
|
|
15209
15588
|
/* @__PURE__ */ jsxs40("div", { className: "bb-tabRow", children: [
|
|
15210
15589
|
/* @__PURE__ */ jsx45(
|
|
15211
15590
|
"button",
|
|
@@ -15226,7 +15605,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15226
15605
|
}
|
|
15227
15606
|
)
|
|
15228
15607
|
] }),
|
|
15229
|
-
/* @__PURE__ */ jsx45("section", { className: "bb-editor", children: activeEditorTab === "scene" ? /* @__PURE__ */ jsx45(
|
|
15608
|
+
/* @__PURE__ */ jsx45("section", { className: "bb-editor", children: activeEditorTab === "scene" ? /* @__PURE__ */ jsx45(Fragment14, { children: modelUrl ? /* @__PURE__ */ jsx45(
|
|
15230
15609
|
SceneSettingsTab,
|
|
15231
15610
|
{
|
|
15232
15611
|
config: sceneConfig,
|
|
@@ -15239,14 +15618,16 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15239
15618
|
// the whole settings panel for no reason.
|
|
15240
15619
|
activeSubTab === "Cinematic" && cameraState ? {
|
|
15241
15620
|
position: cameraState.position,
|
|
15242
|
-
target: cameraState.target
|
|
15621
|
+
target: cameraState.target,
|
|
15622
|
+
azimuthAngle: cameraState.azimuthAngle,
|
|
15623
|
+
polarAngle: cameraState.polarAngle
|
|
15243
15624
|
} : null
|
|
15244
15625
|
),
|
|
15245
15626
|
onPreviewWaypoint: handlePreviewWaypoint,
|
|
15246
15627
|
animationClipDetails: viewerAnimations.clipDetails,
|
|
15247
15628
|
animationPlayback: viewerAnimations
|
|
15248
15629
|
}
|
|
15249
|
-
) : /* @__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(
|
|
15630
|
+
) : /* @__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(Fragment14, { children: [
|
|
15250
15631
|
/* @__PURE__ */ jsx45("div", { className: "bb-editorHeader", children: /* @__PURE__ */ jsxs40("div", { children: [
|
|
15251
15632
|
/* @__PURE__ */ jsx45("div", { className: "bb-sectionKicker", children: "Editing Binding" }),
|
|
15252
15633
|
/* @__PURE__ */ jsx45("h2", { className: "bb-editorTitle", children: selectedBinding.label || selectedItem }),
|
|
@@ -15666,8 +16047,8 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15666
16047
|
// src/components/SimpleModelViewer/SimpleModelViewer.tsx
|
|
15667
16048
|
import {
|
|
15668
16049
|
useRef as useRef19,
|
|
15669
|
-
useState as
|
|
15670
|
-
useCallback as
|
|
16050
|
+
useState as useState26,
|
|
16051
|
+
useCallback as useCallback10,
|
|
15671
16052
|
useEffect as useEffect21,
|
|
15672
16053
|
useMemo as useMemo17,
|
|
15673
16054
|
Component as Component3,
|
|
@@ -15690,14 +16071,14 @@ styleInject('.smv-root {\n --smv-panel-font-family:\n Inter,\n "Segoe UI"
|
|
|
15690
16071
|
import {
|
|
15691
16072
|
X as X3,
|
|
15692
16073
|
Check,
|
|
15693
|
-
Settings,
|
|
16074
|
+
Settings as Settings2,
|
|
15694
16075
|
ChevronDown as ChevronDown6,
|
|
15695
16076
|
Boxes as Boxes2,
|
|
15696
16077
|
Trash2 as Trash26,
|
|
15697
16078
|
Upload as Upload2,
|
|
15698
16079
|
AlertTriangle as AlertTriangle4
|
|
15699
16080
|
} from "lucide-react";
|
|
15700
|
-
import { Fragment as
|
|
16081
|
+
import { Fragment as Fragment15, jsx as jsx46, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
15701
16082
|
var HIGHLIGHT2 = { color: "#93c5fd", intensity: 0.05 };
|
|
15702
16083
|
var ENV_PRESETS = [
|
|
15703
16084
|
"apartment",
|
|
@@ -15757,7 +16138,7 @@ function InnerModel({
|
|
|
15757
16138
|
meshopt: true
|
|
15758
16139
|
});
|
|
15759
16140
|
const invalidate = useThree11((state) => state.invalidate);
|
|
15760
|
-
const [hoveredKey, setHoveredKey] =
|
|
16141
|
+
const [hoveredKey, setHoveredKey] = useState26(null);
|
|
15761
16142
|
const scene = useMemo17(
|
|
15762
16143
|
() => SkeletonUtils2.clone(rawScene),
|
|
15763
16144
|
[rawScene]
|
|
@@ -16059,7 +16440,7 @@ function EnvironmentSection({
|
|
|
16059
16440
|
title,
|
|
16060
16441
|
children
|
|
16061
16442
|
}) {
|
|
16062
|
-
const [collapsed, setCollapsed] =
|
|
16443
|
+
const [collapsed, setCollapsed] = useState26(false);
|
|
16063
16444
|
return /* @__PURE__ */ jsxs41("div", { className: "env-section", children: [
|
|
16064
16445
|
/* @__PURE__ */ jsxs41(
|
|
16065
16446
|
"button",
|
|
@@ -16098,7 +16479,7 @@ function EnvironmentPanel({
|
|
|
16098
16479
|
{
|
|
16099
16480
|
className: "side-panel__open-btn side-panel__open-btn--left",
|
|
16100
16481
|
onClick: () => onToggle(true),
|
|
16101
|
-
children: /* @__PURE__ */ jsx46(
|
|
16482
|
+
children: /* @__PURE__ */ jsx46(Settings2, { size: 20, strokeWidth: 1.8 })
|
|
16102
16483
|
}
|
|
16103
16484
|
),
|
|
16104
16485
|
/* @__PURE__ */ jsxs41(
|
|
@@ -16224,8 +16605,8 @@ function SceneObjectsPanel2({
|
|
|
16224
16605
|
onHover,
|
|
16225
16606
|
onToggleVisibility
|
|
16226
16607
|
}) {
|
|
16227
|
-
const [search, setSearch] =
|
|
16228
|
-
const [rowHovered, setRowHovered] =
|
|
16608
|
+
const [search, setSearch] = useState26("");
|
|
16609
|
+
const [rowHovered, setRowHovered] = useState26(null);
|
|
16229
16610
|
const entries = Object.values(meshKeys).filter(
|
|
16230
16611
|
(m) => m.label.toLowerCase().includes(search.toLowerCase())
|
|
16231
16612
|
);
|
|
@@ -16354,30 +16735,30 @@ function SimpleModelViewer({
|
|
|
16354
16735
|
const fileInputRef = useRef19(null);
|
|
16355
16736
|
const uploadedModelRef = useRef19(null);
|
|
16356
16737
|
const dragDepthRef = useRef19(0);
|
|
16357
|
-
const [loadedScene, setLoadedScene] =
|
|
16358
|
-
const [largeModelTriangles, setLargeModelTriangles] =
|
|
16738
|
+
const [loadedScene, setLoadedScene] = useState26(null);
|
|
16739
|
+
const [largeModelTriangles, setLargeModelTriangles] = useState26(
|
|
16359
16740
|
null
|
|
16360
16741
|
);
|
|
16361
|
-
const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] =
|
|
16362
|
-
const [meshKeys, setMeshKeys] =
|
|
16363
|
-
const [hiddenKeys, setHiddenKeys] =
|
|
16364
|
-
const [selectedKey, setSelectedKey] =
|
|
16365
|
-
const [panelHoveredKey, setPanelHoveredKey] =
|
|
16366
|
-
const [uploadedModelUrl, setUploadedModelUrl] =
|
|
16367
|
-
const [uploadedModelName, setUploadedModelName] =
|
|
16742
|
+
const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = useState26(false);
|
|
16743
|
+
const [meshKeys, setMeshKeys] = useState26({});
|
|
16744
|
+
const [hiddenKeys, setHiddenKeys] = useState26(/* @__PURE__ */ new Set());
|
|
16745
|
+
const [selectedKey, setSelectedKey] = useState26(null);
|
|
16746
|
+
const [panelHoveredKey, setPanelHoveredKey] = useState26(null);
|
|
16747
|
+
const [uploadedModelUrl, setUploadedModelUrl] = useState26(null);
|
|
16748
|
+
const [uploadedModelName, setUploadedModelName] = useState26(
|
|
16368
16749
|
null
|
|
16369
16750
|
);
|
|
16370
|
-
const [modelLoadFailed, setModelLoadFailed] =
|
|
16371
|
-
const [isResolvingUpload, setIsResolvingUpload] =
|
|
16372
|
-
const [initialFitComplete, setInitialFitComplete] =
|
|
16373
|
-
const [isDragActive, setIsDragActive] =
|
|
16374
|
-
const [isPanelOpen, setIsPanelOpen] =
|
|
16751
|
+
const [modelLoadFailed, setModelLoadFailed] = useState26(false);
|
|
16752
|
+
const [isResolvingUpload, setIsResolvingUpload] = useState26(false);
|
|
16753
|
+
const [initialFitComplete, setInitialFitComplete] = useState26(false);
|
|
16754
|
+
const [isDragActive, setIsDragActive] = useState26(false);
|
|
16755
|
+
const [isPanelOpen, setIsPanelOpen] = useState26(
|
|
16375
16756
|
() => typeof window === "undefined" ? true : window.innerWidth > 900
|
|
16376
16757
|
);
|
|
16377
|
-
const [isEnvPanelOpen, setIsEnvPanelOpen] =
|
|
16758
|
+
const [isEnvPanelOpen, setIsEnvPanelOpen] = useState26(
|
|
16378
16759
|
() => typeof window === "undefined" ? true : window.innerWidth > 900
|
|
16379
16760
|
);
|
|
16380
|
-
const [envSettings, setEnvSettings] =
|
|
16761
|
+
const [envSettings, setEnvSettings] = useState26({
|
|
16381
16762
|
backgroundEnabled,
|
|
16382
16763
|
backgroundColor,
|
|
16383
16764
|
autoRotate,
|
|
@@ -16389,7 +16770,7 @@ function SimpleModelViewer({
|
|
|
16389
16770
|
directionalColor
|
|
16390
16771
|
});
|
|
16391
16772
|
const activeModelUrl = enableModelUpload ? uploadedModelUrl : modelUrl;
|
|
16392
|
-
const resetModelState =
|
|
16773
|
+
const resetModelState = useCallback10(() => {
|
|
16393
16774
|
setLoadedScene(null);
|
|
16394
16775
|
setLargeModelTriangles(null);
|
|
16395
16776
|
setLargeModelNoticeDismissed(false);
|
|
@@ -16400,11 +16781,11 @@ function SimpleModelViewer({
|
|
|
16400
16781
|
setModelLoadFailed(false);
|
|
16401
16782
|
setInitialFitComplete(false);
|
|
16402
16783
|
}, []);
|
|
16403
|
-
const revokeUploadedModelUrl =
|
|
16784
|
+
const revokeUploadedModelUrl = useCallback10(() => {
|
|
16404
16785
|
uploadedModelRef.current?.cleanup();
|
|
16405
16786
|
uploadedModelRef.current = null;
|
|
16406
16787
|
}, []);
|
|
16407
|
-
const clearUploadedModel =
|
|
16788
|
+
const clearUploadedModel = useCallback10(() => {
|
|
16408
16789
|
revokeUploadedModelUrl();
|
|
16409
16790
|
setUploadedModelUrl(null);
|
|
16410
16791
|
setUploadedModelName(null);
|
|
@@ -16433,7 +16814,7 @@ function SimpleModelViewer({
|
|
|
16433
16814
|
resetModelState,
|
|
16434
16815
|
revokeUploadedModelUrl
|
|
16435
16816
|
]);
|
|
16436
|
-
const handleUploadedModel =
|
|
16817
|
+
const handleUploadedModel = useCallback10(
|
|
16437
16818
|
async (file) => {
|
|
16438
16819
|
setIsResolvingUpload(true);
|
|
16439
16820
|
try {
|
|
@@ -16452,7 +16833,7 @@ function SimpleModelViewer({
|
|
|
16452
16833
|
},
|
|
16453
16834
|
[onLoadError, resetModelState, revokeUploadedModelUrl]
|
|
16454
16835
|
);
|
|
16455
|
-
const handleFileSelection =
|
|
16836
|
+
const handleFileSelection = useCallback10(
|
|
16456
16837
|
(files) => {
|
|
16457
16838
|
const file = files?.[0];
|
|
16458
16839
|
if (!file) return;
|
|
@@ -16465,7 +16846,7 @@ function SimpleModelViewer({
|
|
|
16465
16846
|
revokeUploadedModelUrl();
|
|
16466
16847
|
};
|
|
16467
16848
|
}, [revokeUploadedModelUrl]);
|
|
16468
|
-
const handleEnvSettingChange =
|
|
16849
|
+
const handleEnvSettingChange = useCallback10(
|
|
16469
16850
|
(key, value) => {
|
|
16470
16851
|
setEnvSettings((prev) => ({ ...prev, [key]: value }));
|
|
16471
16852
|
},
|
|
@@ -16477,12 +16858,12 @@ function SimpleModelViewer({
|
|
|
16477
16858
|
useEffect21(() => {
|
|
16478
16859
|
resetModelState();
|
|
16479
16860
|
}, [activeModelUrl, resetModelState]);
|
|
16480
|
-
const handleMeshesReady =
|
|
16861
|
+
const handleMeshesReady = useCallback10((meshes) => {
|
|
16481
16862
|
setMeshKeys(meshes);
|
|
16482
16863
|
setHiddenKeys(/* @__PURE__ */ new Set());
|
|
16483
16864
|
setSelectedKey(null);
|
|
16484
16865
|
}, []);
|
|
16485
|
-
const handleModelLoaded =
|
|
16866
|
+
const handleModelLoaded = useCallback10(
|
|
16486
16867
|
(scene) => {
|
|
16487
16868
|
setLoadedScene(scene);
|
|
16488
16869
|
setModelLoadFailed(false);
|
|
@@ -16493,14 +16874,14 @@ function SimpleModelViewer({
|
|
|
16493
16874
|
},
|
|
16494
16875
|
[onModelLoaded]
|
|
16495
16876
|
);
|
|
16496
|
-
const handleLoadError =
|
|
16877
|
+
const handleLoadError = useCallback10(
|
|
16497
16878
|
(error) => {
|
|
16498
16879
|
setModelLoadFailed(true);
|
|
16499
16880
|
onLoadError?.(error);
|
|
16500
16881
|
},
|
|
16501
16882
|
[onLoadError]
|
|
16502
16883
|
);
|
|
16503
|
-
const focusMesh =
|
|
16884
|
+
const focusMesh = useCallback10(
|
|
16504
16885
|
(key) => {
|
|
16505
16886
|
setSelectedKey(key);
|
|
16506
16887
|
if (!zoomOnSelected) return;
|
|
@@ -16516,14 +16897,14 @@ function SimpleModelViewer({
|
|
|
16516
16897
|
},
|
|
16517
16898
|
[zoomOnSelected]
|
|
16518
16899
|
);
|
|
16519
|
-
const handleToggleVisibility =
|
|
16900
|
+
const handleToggleVisibility = useCallback10((key) => {
|
|
16520
16901
|
setHiddenKeys((prev) => {
|
|
16521
16902
|
const next = new Set(prev);
|
|
16522
16903
|
next.has(key) ? next.delete(key) : next.add(key);
|
|
16523
16904
|
return next;
|
|
16524
16905
|
});
|
|
16525
16906
|
}, []);
|
|
16526
|
-
const handleDragEnter =
|
|
16907
|
+
const handleDragEnter = useCallback10(
|
|
16527
16908
|
(event) => {
|
|
16528
16909
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
16529
16910
|
return;
|
|
@@ -16535,7 +16916,7 @@ function SimpleModelViewer({
|
|
|
16535
16916
|
},
|
|
16536
16917
|
[enableModelUpload]
|
|
16537
16918
|
);
|
|
16538
|
-
const handleDragOver =
|
|
16919
|
+
const handleDragOver = useCallback10(
|
|
16539
16920
|
(event) => {
|
|
16540
16921
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
16541
16922
|
return;
|
|
@@ -16547,7 +16928,7 @@ function SimpleModelViewer({
|
|
|
16547
16928
|
},
|
|
16548
16929
|
[enableModelUpload]
|
|
16549
16930
|
);
|
|
16550
|
-
const handleDragLeave =
|
|
16931
|
+
const handleDragLeave = useCallback10(
|
|
16551
16932
|
(event) => {
|
|
16552
16933
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
16553
16934
|
return;
|
|
@@ -16561,7 +16942,7 @@ function SimpleModelViewer({
|
|
|
16561
16942
|
},
|
|
16562
16943
|
[enableModelUpload]
|
|
16563
16944
|
);
|
|
16564
|
-
const handleDrop =
|
|
16945
|
+
const handleDrop = useCallback10(
|
|
16565
16946
|
(event) => {
|
|
16566
16947
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
16567
16948
|
return;
|
|
@@ -16615,7 +16996,7 @@ function SimpleModelViewer({
|
|
|
16615
16996
|
}
|
|
16616
16997
|
)
|
|
16617
16998
|
] }),
|
|
16618
|
-
enableModelUpload ? /* @__PURE__ */ jsxs41(
|
|
16999
|
+
enableModelUpload ? /* @__PURE__ */ jsxs41(Fragment15, { children: [
|
|
16619
17000
|
/* @__PURE__ */ jsx46(
|
|
16620
17001
|
"input",
|
|
16621
17002
|
{
|
|
@@ -16629,7 +17010,7 @@ function SimpleModelViewer({
|
|
|
16629
17010
|
}
|
|
16630
17011
|
}
|
|
16631
17012
|
),
|
|
16632
|
-
activeModelUrl ? /* @__PURE__ */ jsx46("div", { className: "smv-upload-toolbar", children: /* @__PURE__ */ jsxs41(
|
|
17013
|
+
activeModelUrl ? /* @__PURE__ */ jsx46("div", { className: "smv-upload-toolbar", children: /* @__PURE__ */ jsxs41(Fragment15, { children: [
|
|
16633
17014
|
/* @__PURE__ */ jsx46("span", { className: "smv-upload-toolbar__name", children: uploadedModelName ?? "Uploaded model" }),
|
|
16634
17015
|
/* @__PURE__ */ jsxs41(
|
|
16635
17016
|
"button",
|
|
@@ -16790,18 +17171,18 @@ function SimpleModelViewer({
|
|
|
16790
17171
|
}
|
|
16791
17172
|
|
|
16792
17173
|
// src/hooks/useViewerSelection.ts
|
|
16793
|
-
import { useCallback as
|
|
17174
|
+
import { useCallback as useCallback11, useState as useState27 } from "react";
|
|
16794
17175
|
function useViewerSelection(initialSelectedObjectBinding = null) {
|
|
16795
|
-
const [selectedObjectBinding, setSelectedObjectBinding] =
|
|
17176
|
+
const [selectedObjectBinding, setSelectedObjectBinding] = useState27(
|
|
16796
17177
|
initialSelectedObjectBinding
|
|
16797
17178
|
);
|
|
16798
|
-
const handleObjectSelect =
|
|
17179
|
+
const handleObjectSelect = useCallback11(
|
|
16799
17180
|
(binding) => {
|
|
16800
17181
|
setSelectedObjectBinding(binding);
|
|
16801
17182
|
},
|
|
16802
17183
|
[setSelectedObjectBinding]
|
|
16803
17184
|
);
|
|
16804
|
-
const clearSelection =
|
|
17185
|
+
const clearSelection = useCallback11(() => {
|
|
16805
17186
|
setSelectedObjectBinding(null);
|
|
16806
17187
|
}, []);
|
|
16807
17188
|
return {
|
|
@@ -16839,14 +17220,14 @@ function useObjectBindingIds(objectBindings) {
|
|
|
16839
17220
|
}
|
|
16840
17221
|
|
|
16841
17222
|
// src/hooks/useObjectVisibility.ts
|
|
16842
|
-
import { useCallback as
|
|
17223
|
+
import { useCallback as useCallback12, useMemo as useMemo20 } from "react";
|
|
16843
17224
|
function getHiddenObjects(objectBindings) {
|
|
16844
17225
|
return Object.fromEntries(
|
|
16845
17226
|
Object.entries(objectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
|
|
16846
17227
|
);
|
|
16847
17228
|
}
|
|
16848
17229
|
function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
16849
|
-
const updateVisibility =
|
|
17230
|
+
const updateVisibility = useCallback12(
|
|
16850
17231
|
(objectId, visible) => {
|
|
16851
17232
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
16852
17233
|
if (!bindingKey) return;
|
|
@@ -16862,15 +17243,15 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
|
16862
17243
|
},
|
|
16863
17244
|
[objectBindings, onObjectBindingsChange]
|
|
16864
17245
|
);
|
|
16865
|
-
const hideObject =
|
|
17246
|
+
const hideObject = useCallback12(
|
|
16866
17247
|
(objectId) => updateVisibility(objectId, false),
|
|
16867
17248
|
[updateVisibility]
|
|
16868
17249
|
);
|
|
16869
|
-
const showObject =
|
|
17250
|
+
const showObject = useCallback12(
|
|
16870
17251
|
(objectId) => updateVisibility(objectId, true),
|
|
16871
17252
|
[updateVisibility]
|
|
16872
17253
|
);
|
|
16873
|
-
const toggleObjectVisibility =
|
|
17254
|
+
const toggleObjectVisibility = useCallback12(
|
|
16874
17255
|
(objectId) => {
|
|
16875
17256
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
16876
17257
|
if (!bindingKey) return;
|
|
@@ -16886,7 +17267,7 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
|
16886
17267
|
},
|
|
16887
17268
|
[objectBindings, onObjectBindingsChange]
|
|
16888
17269
|
);
|
|
16889
|
-
const clearHiddenObjects =
|
|
17270
|
+
const clearHiddenObjects = useCallback12(() => {
|
|
16890
17271
|
onObjectBindingsChange(
|
|
16891
17272
|
Object.fromEntries(
|
|
16892
17273
|
Object.entries(objectBindings).map(([bindingKey, binding]) => [
|
|
@@ -16906,7 +17287,7 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
|
16906
17287
|
),
|
|
16907
17288
|
[hiddenObjects, objectBindings]
|
|
16908
17289
|
);
|
|
16909
|
-
const isObjectHidden =
|
|
17290
|
+
const isObjectHidden = useCallback12(
|
|
16910
17291
|
(objectId) => {
|
|
16911
17292
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
16912
17293
|
return bindingKey ? objectBindings[bindingKey]?.visible === false : false;
|
|
@@ -16925,10 +17306,10 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
|
16925
17306
|
}
|
|
16926
17307
|
|
|
16927
17308
|
// src/hooks/useViewerHover.ts
|
|
16928
|
-
import { useCallback as
|
|
17309
|
+
import { useCallback as useCallback13, useState as useState28 } from "react";
|
|
16929
17310
|
function useViewerHover() {
|
|
16930
|
-
const [hoveredObjectBinding, setHoveredObjectBinding] =
|
|
16931
|
-
const handleHoveredObject =
|
|
17311
|
+
const [hoveredObjectBinding, setHoveredObjectBinding] = useState28(null);
|
|
17312
|
+
const handleHoveredObject = useCallback13(
|
|
16932
17313
|
(binding) => {
|
|
16933
17314
|
setHoveredObjectBinding(binding);
|
|
16934
17315
|
},
|
|
@@ -16941,18 +17322,18 @@ function useViewerHover() {
|
|
|
16941
17322
|
}
|
|
16942
17323
|
|
|
16943
17324
|
// src/hooks/useViewerModel.ts
|
|
16944
|
-
import { useCallback as
|
|
17325
|
+
import { useCallback as useCallback14, useState as useState29 } from "react";
|
|
16945
17326
|
import { Box3 as Box36, Vector3 as Vector310 } from "three";
|
|
16946
17327
|
function toTuple2(vector) {
|
|
16947
17328
|
return [vector.x, vector.y, vector.z];
|
|
16948
17329
|
}
|
|
16949
17330
|
function useViewerModel() {
|
|
16950
|
-
const [isLoading, setIsLoading] =
|
|
16951
|
-
const [isReady, setIsReady] =
|
|
16952
|
-
const [error, setError] =
|
|
16953
|
-
const [bounds, setBounds] =
|
|
16954
|
-
const [objectCount, setObjectCount] =
|
|
16955
|
-
const handleModelLoaded =
|
|
17331
|
+
const [isLoading, setIsLoading] = useState29(true);
|
|
17332
|
+
const [isReady, setIsReady] = useState29(false);
|
|
17333
|
+
const [error, setError] = useState29(null);
|
|
17334
|
+
const [bounds, setBounds] = useState29(null);
|
|
17335
|
+
const [objectCount, setObjectCount] = useState29(0);
|
|
17336
|
+
const handleModelLoaded = useCallback14((scene) => {
|
|
16956
17337
|
const box = new Box36().setFromObject(scene);
|
|
16957
17338
|
const min = box.min.clone();
|
|
16958
17339
|
const max = box.max.clone();
|
|
@@ -16977,14 +17358,14 @@ function useViewerModel() {
|
|
|
16977
17358
|
setIsLoading(false);
|
|
16978
17359
|
setIsReady(true);
|
|
16979
17360
|
}, []);
|
|
16980
|
-
const handleLoadError =
|
|
17361
|
+
const handleLoadError = useCallback14((nextError) => {
|
|
16981
17362
|
setError(nextError);
|
|
16982
17363
|
setBounds(null);
|
|
16983
17364
|
setObjectCount(0);
|
|
16984
17365
|
setIsLoading(false);
|
|
16985
17366
|
setIsReady(false);
|
|
16986
17367
|
}, []);
|
|
16987
|
-
const resetModelState =
|
|
17368
|
+
const resetModelState = useCallback14(() => {
|
|
16988
17369
|
setIsLoading(true);
|
|
16989
17370
|
setIsReady(false);
|
|
16990
17371
|
setError(null);
|
|
@@ -17004,10 +17385,10 @@ function useViewerModel() {
|
|
|
17004
17385
|
}
|
|
17005
17386
|
|
|
17006
17387
|
// src/hooks/useViewerActions.ts
|
|
17007
|
-
import { useCallback as
|
|
17388
|
+
import { useCallback as useCallback15 } from "react";
|
|
17008
17389
|
function useViewerActions(objectBindings, options = {}) {
|
|
17009
17390
|
const { onAction, onObjectBindingsChange } = options;
|
|
17010
|
-
const getActionsForObject =
|
|
17391
|
+
const getActionsForObject = useCallback15(
|
|
17011
17392
|
(objectId) => {
|
|
17012
17393
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
17013
17394
|
if (!bindingKey) {
|
|
@@ -17017,7 +17398,7 @@ function useViewerActions(objectBindings, options = {}) {
|
|
|
17017
17398
|
},
|
|
17018
17399
|
[objectBindings]
|
|
17019
17400
|
);
|
|
17020
|
-
const runAction =
|
|
17401
|
+
const runAction = useCallback15(
|
|
17021
17402
|
(objectId, actionId) => {
|
|
17022
17403
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
17023
17404
|
if (!bindingKey) {
|
|
@@ -17059,7 +17440,7 @@ function useViewerActions(objectBindings, options = {}) {
|
|
|
17059
17440
|
}
|
|
17060
17441
|
|
|
17061
17442
|
// src/hooks/useShareableViewerState.ts
|
|
17062
|
-
import { useCallback as
|
|
17443
|
+
import { useCallback as useCallback16, useEffect as useEffect22, useRef as useRef20, useState as useState30 } from "react";
|
|
17063
17444
|
function serializeState(cameraState, objectBindings, selectedObjectBinding) {
|
|
17064
17445
|
const hidden = Object.values(objectBindings).filter((binding) => binding.visible === false).map((binding) => binding.id);
|
|
17065
17446
|
return {
|
|
@@ -17080,7 +17461,7 @@ function useShareableViewerState({
|
|
|
17080
17461
|
autoSync = true
|
|
17081
17462
|
}) {
|
|
17082
17463
|
const lastWrittenRawRef = useRef20(null);
|
|
17083
|
-
const buildHash =
|
|
17464
|
+
const buildHash = useCallback16(() => {
|
|
17084
17465
|
const state = serializeState(
|
|
17085
17466
|
cameraState,
|
|
17086
17467
|
objectBindings,
|
|
@@ -17088,12 +17469,12 @@ function useShareableViewerState({
|
|
|
17088
17469
|
);
|
|
17089
17470
|
return `${paramKey}=${encodeURIComponent(JSON.stringify(state))}`;
|
|
17090
17471
|
}, [cameraState, objectBindings, selectedObjectBinding, paramKey]);
|
|
17091
|
-
const getShareUrl =
|
|
17472
|
+
const getShareUrl = useCallback16(() => {
|
|
17092
17473
|
const url = new URL(window.location.href);
|
|
17093
17474
|
url.hash = buildHash();
|
|
17094
17475
|
return url.toString();
|
|
17095
17476
|
}, [buildHash]);
|
|
17096
|
-
const syncUrl =
|
|
17477
|
+
const syncUrl = useCallback16(() => {
|
|
17097
17478
|
const url = new URL(window.location.href);
|
|
17098
17479
|
url.hash = buildHash();
|
|
17099
17480
|
window.history.replaceState(null, "", url.toString());
|
|
@@ -17111,7 +17492,7 @@ function useShareableViewerState({
|
|
|
17111
17492
|
objectBindings,
|
|
17112
17493
|
selectedObjectBinding
|
|
17113
17494
|
};
|
|
17114
|
-
const [restoreAttempted, setRestoreAttempted] =
|
|
17495
|
+
const [restoreAttempted, setRestoreAttempted] = useState30(false);
|
|
17115
17496
|
const lastSyncedHashRef = useRef20(null);
|
|
17116
17497
|
useEffect22(() => {
|
|
17117
17498
|
if (!autoSync || !restoreAttempted) return;
|
|
@@ -17137,7 +17518,7 @@ function useShareableViewerState({
|
|
|
17137
17518
|
}, [autoSync, restoreAttempted, debounceMs, paramKey]);
|
|
17138
17519
|
const lastRestoredRawRef = useRef20(null);
|
|
17139
17520
|
const restoringRawRef = useRef20(null);
|
|
17140
|
-
const restoreFromUrl =
|
|
17521
|
+
const restoreFromUrl = useCallback16(async () => {
|
|
17141
17522
|
setRestoreAttempted(true);
|
|
17142
17523
|
const rawHash = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
|
|
17143
17524
|
const raw = new URLSearchParams(rawHash).get(paramKey);
|