@liveroom-tech/react-immersive 1.3.3 → 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 +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +675 -311
- package/dist/index.mjs +628 -263
- 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";
|
|
@@ -4130,6 +4130,7 @@ import {
|
|
|
4130
4130
|
Box3 as Box33,
|
|
4131
4131
|
CatmullRomCurve3,
|
|
4132
4132
|
Sphere,
|
|
4133
|
+
Spherical,
|
|
4133
4134
|
Vector3 as Vector36
|
|
4134
4135
|
} from "three";
|
|
4135
4136
|
var ENTER_BLEND_SECONDS = 1.2;
|
|
@@ -4138,18 +4139,75 @@ function CinematicCamera({
|
|
|
4138
4139
|
sceneRef,
|
|
4139
4140
|
config,
|
|
4140
4141
|
playing,
|
|
4142
|
+
resetOnPlay = false,
|
|
4141
4143
|
onFinished,
|
|
4142
|
-
onUserInteract
|
|
4144
|
+
onUserInteract,
|
|
4145
|
+
onStop
|
|
4143
4146
|
}) {
|
|
4144
4147
|
const invalidate = useThree9((state) => state.invalidate);
|
|
4145
4148
|
const waypoints = useMemo11(() => config.waypoints ?? [], [config.waypoints]);
|
|
4146
4149
|
const hasPath = waypoints.length >= 2;
|
|
4147
4150
|
const loop = config.loop ?? true;
|
|
4148
|
-
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;
|
|
4149
4163
|
const signature = useMemo11(
|
|
4150
|
-
() => 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(";"),
|
|
4151
4173
|
[waypoints]
|
|
4152
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]);
|
|
4153
4211
|
const curves = useMemo11(() => {
|
|
4154
4212
|
if (!hasPath) return null;
|
|
4155
4213
|
return {
|
|
@@ -4167,29 +4225,83 @@ function CinematicCamera({
|
|
|
4167
4225
|
)
|
|
4168
4226
|
};
|
|
4169
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]);
|
|
4170
4248
|
const elapsedRef = useRef8(0);
|
|
4171
4249
|
const enterElapsedRef = useRef8(0);
|
|
4172
4250
|
const enterPos = useRef8(new Vector36());
|
|
4173
4251
|
const enterTarget = useRef8(new Vector36());
|
|
4174
4252
|
const startedRef = useRef8(false);
|
|
4175
4253
|
const pausedElapsedRef = useRef8(0);
|
|
4254
|
+
const hasFinishedRef = useRef8(false);
|
|
4176
4255
|
const orbitRef = useRef8(null);
|
|
4177
4256
|
const tmpPos = useMemo11(() => new Vector36(), []);
|
|
4178
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]);
|
|
4179
4272
|
useEffect11(() => {
|
|
4180
4273
|
if (!playing) {
|
|
4181
4274
|
startedRef.current = false;
|
|
4182
|
-
if (elapsedRef.current > 0) {
|
|
4275
|
+
if (elapsedRef.current > 0 && !resetOnPlay) {
|
|
4183
4276
|
pausedElapsedRef.current = elapsedRef.current;
|
|
4277
|
+
} else if (resetOnPlay) {
|
|
4278
|
+
pausedElapsedRef.current = 0;
|
|
4279
|
+
}
|
|
4280
|
+
if (elapsedRef.current > 0 || hasFinishedRef.current) {
|
|
4281
|
+
onStop?.();
|
|
4184
4282
|
}
|
|
4185
4283
|
return;
|
|
4186
4284
|
}
|
|
4285
|
+
if (elapsedRef.current >= duration && !loop) {
|
|
4286
|
+
elapsedRef.current = 0;
|
|
4287
|
+
pausedElapsedRef.current = 0;
|
|
4288
|
+
hasFinishedRef.current = false;
|
|
4289
|
+
}
|
|
4187
4290
|
const controls = controlsRef.current;
|
|
4188
4291
|
if (controls) {
|
|
4189
4292
|
controls.getPosition(enterPos.current);
|
|
4190
4293
|
controls.getTarget(enterTarget.current);
|
|
4191
4294
|
}
|
|
4192
|
-
|
|
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
|
+
}
|
|
4193
4305
|
enterElapsedRef.current = 0;
|
|
4194
4306
|
if (!hasPath) {
|
|
4195
4307
|
const scene = sceneRef.current;
|
|
@@ -4203,12 +4315,26 @@ function CinematicCamera({
|
|
|
4203
4315
|
}
|
|
4204
4316
|
startedRef.current = true;
|
|
4205
4317
|
invalidate();
|
|
4206
|
-
}, [
|
|
4318
|
+
}, [
|
|
4319
|
+
playing,
|
|
4320
|
+
hasPath,
|
|
4321
|
+
controlsRef,
|
|
4322
|
+
sceneRef,
|
|
4323
|
+
invalidate,
|
|
4324
|
+
duration,
|
|
4325
|
+
loop,
|
|
4326
|
+
signature,
|
|
4327
|
+
resetOnPlay,
|
|
4328
|
+
onStop
|
|
4329
|
+
]);
|
|
4207
4330
|
useEffect11(() => {
|
|
4208
4331
|
if (!playing || !onUserInteract) return;
|
|
4209
4332
|
const controls = controlsRef.current;
|
|
4210
4333
|
if (!controls) return;
|
|
4211
|
-
const handle = () =>
|
|
4334
|
+
const handle = () => {
|
|
4335
|
+
if (hasFinishedRef.current) return;
|
|
4336
|
+
onUserInteract();
|
|
4337
|
+
};
|
|
4212
4338
|
controls.addEventListener("controlstart", handle);
|
|
4213
4339
|
return () => controls.removeEventListener("controlstart", handle);
|
|
4214
4340
|
}, [playing, controlsRef, onUserInteract]);
|
|
@@ -4218,12 +4344,33 @@ function CinematicCamera({
|
|
|
4218
4344
|
if (!controls) return;
|
|
4219
4345
|
elapsedRef.current += delta;
|
|
4220
4346
|
enterElapsedRef.current += delta;
|
|
4221
|
-
let u = elapsedRef.current
|
|
4222
|
-
const finished = !loop &&
|
|
4347
|
+
let u = getCurveParameter(elapsedRef.current);
|
|
4348
|
+
const finished = !loop && elapsedRef.current >= duration;
|
|
4223
4349
|
u = loop ? u % 1 : Math.min(u, 1);
|
|
4224
4350
|
if (curves) {
|
|
4225
|
-
curves.
|
|
4226
|
-
|
|
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
|
+
}
|
|
4227
4374
|
} else {
|
|
4228
4375
|
const orbit = orbitRef.current;
|
|
4229
4376
|
if (!orbit) return;
|
|
@@ -4253,6 +4400,8 @@ function CinematicCamera({
|
|
|
4253
4400
|
);
|
|
4254
4401
|
if (finished) {
|
|
4255
4402
|
startedRef.current = false;
|
|
4403
|
+
hasFinishedRef.current = true;
|
|
4404
|
+
elapsedRef.current = 0;
|
|
4256
4405
|
pausedElapsedRef.current = 0;
|
|
4257
4406
|
onFinished?.();
|
|
4258
4407
|
return;
|
|
@@ -5653,9 +5802,11 @@ function ModelViewer({
|
|
|
5653
5802
|
return { ...base ?? {}, ...cinematic };
|
|
5654
5803
|
}, [cinematic, resolvedSceneConfig]);
|
|
5655
5804
|
const [cinematicPlaying, setCinematicPlaying] = useState10(false);
|
|
5805
|
+
const [cinematicReset, setCinematicReset] = useState10(false);
|
|
5656
5806
|
const cinematicAutoStartedRef = useRef11(false);
|
|
5657
5807
|
useEffect15(() => {
|
|
5658
5808
|
setCinematicPlaying(false);
|
|
5809
|
+
setCinematicReset(false);
|
|
5659
5810
|
cinematicAutoStartedRef.current = false;
|
|
5660
5811
|
}, [modelUrl]);
|
|
5661
5812
|
useEffect15(() => {
|
|
@@ -5663,6 +5814,7 @@ function ModelViewer({
|
|
|
5663
5814
|
if (modelLoaded && cameraPositioned && !isAutoFraming) {
|
|
5664
5815
|
cinematicAutoStartedRef.current = true;
|
|
5665
5816
|
setCinematicPlaying(true);
|
|
5817
|
+
setCinematicReset(false);
|
|
5666
5818
|
}
|
|
5667
5819
|
}, [cinematicConfig, modelLoaded, cameraPositioned, isAutoFraming]);
|
|
5668
5820
|
const xrStore = useMemo13(() => {
|
|
@@ -6816,6 +6968,20 @@ function ModelViewer({
|
|
|
6816
6968
|
console.error("Reset camera error:", error);
|
|
6817
6969
|
}
|
|
6818
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]);
|
|
6819
6985
|
const handleCameraChangeWithAnnotations = useCallback4(
|
|
6820
6986
|
(cameraValue, controls) => {
|
|
6821
6987
|
handleCameraChangeInternal(cameraValue, controls);
|
|
@@ -7132,8 +7298,14 @@ function ModelViewer({
|
|
|
7132
7298
|
sceneRef,
|
|
7133
7299
|
config: cinematicConfig,
|
|
7134
7300
|
playing: cinematicPlaying,
|
|
7135
|
-
|
|
7136
|
-
|
|
7301
|
+
resetOnPlay: cinematicReset,
|
|
7302
|
+
onFinished: () => {
|
|
7303
|
+
setCinematicPlaying(false);
|
|
7304
|
+
setCinematicReset(false);
|
|
7305
|
+
},
|
|
7306
|
+
onUserInteract: () => {
|
|
7307
|
+
setCinematicPlaying(false);
|
|
7308
|
+
}
|
|
7137
7309
|
}
|
|
7138
7310
|
),
|
|
7139
7311
|
showMeasureTools && /* @__PURE__ */ jsx19(
|
|
@@ -7361,24 +7533,43 @@ function ModelViewer({
|
|
|
7361
7533
|
]
|
|
7362
7534
|
}
|
|
7363
7535
|
),
|
|
7364
|
-
cinematicConfig && /* @__PURE__ */
|
|
7536
|
+
cinematicConfig && /* @__PURE__ */ jsxs15(
|
|
7365
7537
|
"div",
|
|
7366
7538
|
{
|
|
7367
7539
|
className: "ri-cinematicTools",
|
|
7368
7540
|
style: {
|
|
7369
7541
|
top: `${topLeftRow((xrToolsVisible ? 1 : 0) + (showMeasureTools ? 1 : 0) + (showExplodeControls ? 1 : 0))}px`
|
|
7370
7542
|
},
|
|
7371
|
-
children:
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
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
|
+
]
|
|
7382
7573
|
}
|
|
7383
7574
|
),
|
|
7384
7575
|
(showResetButton || showDownloadButton || showAnnotationNavigation && sortedAnnotations.length > 0) && /* @__PURE__ */ jsxs15("div", { className: "ri-buttonContainer", children: [
|
|
@@ -7618,7 +7809,7 @@ function ModelViewer({
|
|
|
7618
7809
|
}
|
|
7619
7810
|
|
|
7620
7811
|
// src/components/BindingBuilder/BindingBuilder.tsx
|
|
7621
|
-
import { useCallback as
|
|
7812
|
+
import { useCallback as useCallback9, useEffect as useEffect20, useMemo as useMemo16, useRef as useRef18, useState as useState25 } from "react";
|
|
7622
7813
|
import {
|
|
7623
7814
|
Redo2,
|
|
7624
7815
|
Undo2,
|
|
@@ -7665,14 +7856,16 @@ function buildCameraState(camera, controls) {
|
|
|
7665
7856
|
position: toTuple(position),
|
|
7666
7857
|
target: toTuple(target),
|
|
7667
7858
|
fov: "fov" in camera && typeof camera.fov === "number" ? camera.fov : null,
|
|
7668
|
-
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
|
|
7669
7862
|
};
|
|
7670
7863
|
}
|
|
7671
7864
|
function isSameCameraState(left, right) {
|
|
7672
7865
|
if (!left) {
|
|
7673
7866
|
return false;
|
|
7674
7867
|
}
|
|
7675
|
-
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;
|
|
7676
7869
|
}
|
|
7677
7870
|
async function runSmoothCameraTransition2(controls, action, smoothTime = 0.6) {
|
|
7678
7871
|
const originalSmoothTime = controls.smoothTime;
|
|
@@ -7827,15 +8020,27 @@ function useViewerCamera() {
|
|
|
7827
8020
|
}
|
|
7828
8021
|
try {
|
|
7829
8022
|
markInitialAutoFitOverride(controls);
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
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);
|
|
7839
8044
|
const camera = controls.camera;
|
|
7840
8045
|
let projectionChanged = false;
|
|
7841
8046
|
if (state.fov !== null && "fov" in camera && typeof camera.fov === "number") {
|
|
@@ -8143,7 +8348,7 @@ function inferMimeType(path) {
|
|
|
8143
8348
|
}
|
|
8144
8349
|
|
|
8145
8350
|
// src/components/BindingBuilder/SceneSettings/SceneSettingsTab.tsx
|
|
8146
|
-
import { useState as
|
|
8351
|
+
import { useState as useState20, useEffect as useEffect18 } from "react";
|
|
8147
8352
|
|
|
8148
8353
|
// src/components/BindingBuilder/SceneSettings/GeneralSceneSettings.tsx
|
|
8149
8354
|
import { useState as useState14 } from "react";
|
|
@@ -8329,9 +8534,10 @@ function SliderRow({
|
|
|
8329
8534
|
max,
|
|
8330
8535
|
step,
|
|
8331
8536
|
formatValue,
|
|
8332
|
-
onChange
|
|
8537
|
+
onChange,
|
|
8538
|
+
disabled = false
|
|
8333
8539
|
}) {
|
|
8334
|
-
return /* @__PURE__ */ jsxs16("label", { className: "se-field"
|
|
8540
|
+
return /* @__PURE__ */ jsxs16("label", { className: `se-field ${disabled ? "se-field--disabled" : ""}`, children: [
|
|
8335
8541
|
/* @__PURE__ */ jsxs16("span", { className: "se-fieldLabel", children: [
|
|
8336
8542
|
label,
|
|
8337
8543
|
/* @__PURE__ */ jsx20("span", { className: "se-fieldValue", children: formatValue ? formatValue(value) : value })
|
|
@@ -8345,7 +8551,8 @@ function SliderRow({
|
|
|
8345
8551
|
max,
|
|
8346
8552
|
step,
|
|
8347
8553
|
value,
|
|
8348
|
-
onChange: (e) => onChange(Number(e.target.value))
|
|
8554
|
+
onChange: (e) => onChange(Number(e.target.value)),
|
|
8555
|
+
disabled
|
|
8349
8556
|
}
|
|
8350
8557
|
)
|
|
8351
8558
|
] });
|
|
@@ -11761,14 +11968,16 @@ function AnnotationsTab({ config, onChange }) {
|
|
|
11761
11968
|
}
|
|
11762
11969
|
|
|
11763
11970
|
// src/components/BindingBuilder/SceneSettings/CinematicTab.tsx
|
|
11764
|
-
import {
|
|
11765
|
-
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";
|
|
11766
11974
|
var DEFAULT_CINEMATIC = {
|
|
11767
11975
|
enabled: false,
|
|
11768
11976
|
waypoints: [],
|
|
11769
11977
|
loop: true,
|
|
11770
11978
|
autoPlay: false
|
|
11771
11979
|
};
|
|
11980
|
+
var DEFAULT_SEGMENT_DURATION = 3.5;
|
|
11772
11981
|
function CinematicTab({
|
|
11773
11982
|
config,
|
|
11774
11983
|
onChange,
|
|
@@ -11777,31 +11986,76 @@ function CinematicTab({
|
|
|
11777
11986
|
}) {
|
|
11778
11987
|
const cinematic = config.cinematic ?? DEFAULT_CINEMATIC;
|
|
11779
11988
|
const { waypoints } = cinematic;
|
|
11780
|
-
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) => {
|
|
11781
12002
|
onChange({ ...config, cinematic: { ...cinematic, ...patch } });
|
|
11782
|
-
};
|
|
11783
|
-
const addWaypoint = () => {
|
|
12003
|
+
}, [config, cinematic, onChange]);
|
|
12004
|
+
const addWaypoint = useCallback7(() => {
|
|
11784
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);
|
|
11785
12015
|
update({
|
|
11786
|
-
waypoints:
|
|
11787
|
-
|
|
11788
|
-
{
|
|
11789
|
-
position: [...cameraState.position],
|
|
11790
|
-
target: [...cameraState.target]
|
|
11791
|
-
}
|
|
11792
|
-
]
|
|
12016
|
+
waypoints: newWaypoints,
|
|
12017
|
+
duration: newDuration
|
|
11793
12018
|
});
|
|
11794
|
-
};
|
|
11795
|
-
const deleteWaypoint = (index) => {
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
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) => {
|
|
11799
12029
|
const next = index + delta;
|
|
11800
12030
|
if (next < 0 || next >= waypoints.length) return;
|
|
11801
12031
|
const reordered = [...waypoints];
|
|
11802
12032
|
[reordered[index], reordered[next]] = [reordered[next], reordered[index]];
|
|
11803
|
-
|
|
11804
|
-
|
|
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;
|
|
11805
12059
|
return /* @__PURE__ */ jsxs27(Section, { title: "Cinematic Camera", children: [
|
|
11806
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." }),
|
|
11807
12061
|
/* @__PURE__ */ jsxs27(
|
|
@@ -11818,62 +12072,165 @@ function CinematicTab({
|
|
|
11818
12072
|
]
|
|
11819
12073
|
}
|
|
11820
12074
|
),
|
|
11821
|
-
|
|
11822
|
-
/* @__PURE__ */
|
|
11823
|
-
/* @__PURE__ */ jsxs27("div", { className: "se-waypointBody", children: [
|
|
11824
|
-
/* @__PURE__ */ jsxs27("div", { className: "se-waypointCoords", children: [
|
|
11825
|
-
"pos [",
|
|
11826
|
-
waypoint.position.map((n) => n.toFixed(1)).join(", "),
|
|
11827
|
-
"]"
|
|
11828
|
-
] }),
|
|
11829
|
-
/* @__PURE__ */ jsxs27("div", { className: "se-waypointCoords", children: [
|
|
11830
|
-
"look [",
|
|
11831
|
-
waypoint.target.map((n) => n.toFixed(1)).join(", "),
|
|
11832
|
-
"]"
|
|
11833
|
-
] })
|
|
11834
|
-
] }),
|
|
11835
|
-
/* @__PURE__ */ jsxs27("div", { className: "se-waypointActions", children: [
|
|
11836
|
-
/* @__PURE__ */ jsx31(
|
|
11837
|
-
"button",
|
|
11838
|
-
{
|
|
11839
|
-
className: "se-waypointAction",
|
|
11840
|
-
onClick: () => onPreviewWaypoint?.(waypoint),
|
|
11841
|
-
title: "Fly camera to this waypoint",
|
|
11842
|
-
disabled: !onPreviewWaypoint,
|
|
11843
|
-
children: /* @__PURE__ */ jsx31(MapPin2, { size: 15 })
|
|
11844
|
-
}
|
|
11845
|
-
),
|
|
11846
|
-
/* @__PURE__ */ jsx31(
|
|
11847
|
-
"button",
|
|
11848
|
-
{
|
|
11849
|
-
className: "se-waypointAction",
|
|
11850
|
-
onClick: () => moveWaypoint(index, -1),
|
|
11851
|
-
title: "Move up",
|
|
11852
|
-
disabled: index === 0,
|
|
11853
|
-
children: /* @__PURE__ */ jsx31(ArrowUp, { size: 15 })
|
|
11854
|
-
}
|
|
11855
|
-
),
|
|
12075
|
+
/* @__PURE__ */ jsxs27("div", { className: "se-customizeToggle", children: [
|
|
12076
|
+
/* @__PURE__ */ jsxs27("label", { className: "se-toggleLabel", children: [
|
|
11856
12077
|
/* @__PURE__ */ jsx31(
|
|
11857
|
-
"
|
|
12078
|
+
"input",
|
|
11858
12079
|
{
|
|
11859
|
-
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
disabled: index === waypoints.length - 1,
|
|
11863
|
-
children: /* @__PURE__ */ jsx31(ArrowDown, { size: 15 })
|
|
12080
|
+
type: "checkbox",
|
|
12081
|
+
checked: customizeDurations,
|
|
12082
|
+
onChange: (e) => setCustomizeDurations(e.target.checked)
|
|
11864
12083
|
}
|
|
11865
12084
|
),
|
|
11866
|
-
/* @__PURE__ */ jsx31(
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
|
|
11873
|
-
}
|
|
11874
|
-
)
|
|
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"
|
|
11875
12092
|
] })
|
|
11876
|
-
] }
|
|
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)" })
|
|
12232
|
+
] })
|
|
12233
|
+
] }),
|
|
11877
12234
|
/* @__PURE__ */ jsx31(
|
|
11878
12235
|
ToggleRow,
|
|
11879
12236
|
{
|
|
@@ -11887,7 +12244,11 @@ function CinematicTab({
|
|
|
11887
12244
|
{
|
|
11888
12245
|
label: "Loop",
|
|
11889
12246
|
checked: cinematic.loop,
|
|
11890
|
-
onChange: (loop) =>
|
|
12247
|
+
onChange: (loop) => {
|
|
12248
|
+
const newLoop = loop;
|
|
12249
|
+
const newDuration = calculateTotalDuration(waypoints, newLoop);
|
|
12250
|
+
update({ loop: newLoop, duration: newDuration });
|
|
12251
|
+
}
|
|
11891
12252
|
}
|
|
11892
12253
|
),
|
|
11893
12254
|
/* @__PURE__ */ jsx31(
|
|
@@ -11898,11 +12259,11 @@ function CinematicTab({
|
|
|
11898
12259
|
onChange: (autoPlay) => update({ autoPlay })
|
|
11899
12260
|
}
|
|
11900
12261
|
),
|
|
11901
|
-
/* @__PURE__ */ jsx31(
|
|
12262
|
+
!customizeDurations && hasEnoughWaypoints && /* @__PURE__ */ jsx31(
|
|
11902
12263
|
SliderRow,
|
|
11903
12264
|
{
|
|
11904
12265
|
label: "Duration",
|
|
11905
|
-
value: cinematic.duration ??
|
|
12266
|
+
value: cinematic.duration ?? calculateTotalDuration(waypoints, cinematic.loop),
|
|
11906
12267
|
min: 4,
|
|
11907
12268
|
max: 120,
|
|
11908
12269
|
step: 1,
|
|
@@ -11914,7 +12275,7 @@ function CinematicTab({
|
|
|
11914
12275
|
}
|
|
11915
12276
|
|
|
11916
12277
|
// src/styles/SceneSettingsTab.css
|
|
11917
|
-
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');
|
|
11918
12279
|
|
|
11919
12280
|
// src/components/BindingBuilder/SceneSettings/SceneSettingsTab.tsx
|
|
11920
12281
|
import {
|
|
@@ -11960,7 +12321,7 @@ function SceneSettingsTab({
|
|
|
11960
12321
|
animationClipDetails = [],
|
|
11961
12322
|
animationPlayback = FALLBACK_ANIMATION_PLAYBACK
|
|
11962
12323
|
}) {
|
|
11963
|
-
const [internalActiveSubTab, setInternalActiveSubTab] =
|
|
12324
|
+
const [internalActiveSubTab, setInternalActiveSubTab] = useState20("General");
|
|
11964
12325
|
const activeSubTab = controlledActiveSubTab ?? internalActiveSubTab;
|
|
11965
12326
|
const resolvedAnimationPlayback = animationPlayback ?? FALLBACK_ANIMATION_PLAYBACK;
|
|
11966
12327
|
const { tier } = useBindingBuilderContext();
|
|
@@ -12146,7 +12507,7 @@ function EnableToggle2({
|
|
|
12146
12507
|
}
|
|
12147
12508
|
|
|
12148
12509
|
// src/components/BindingBuilder/components/MaterialSection.tsx
|
|
12149
|
-
import { useState as
|
|
12510
|
+
import { useState as useState22 } from "react";
|
|
12150
12511
|
|
|
12151
12512
|
// src/components/ModelViewer/types/objectBinding.ts
|
|
12152
12513
|
var MATERIAL_SIDES = ["front", "back", "double"];
|
|
@@ -12193,7 +12554,7 @@ function MaterialPanelGroup({
|
|
|
12193
12554
|
}
|
|
12194
12555
|
|
|
12195
12556
|
// src/components/BindingBuilder/components/MaterialFields.tsx
|
|
12196
|
-
import { useRef as useRef15, useState as
|
|
12557
|
+
import { useRef as useRef15, useState as useState21 } from "react";
|
|
12197
12558
|
import { HexColorInput as HexColorInput3, HexColorPicker as HexColorPicker4 } from "react-colorful";
|
|
12198
12559
|
import { FileUp } from "lucide-react";
|
|
12199
12560
|
import { jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
@@ -12260,7 +12621,7 @@ function MaterialColorField({
|
|
|
12260
12621
|
fallback = "#ffffff",
|
|
12261
12622
|
onChange
|
|
12262
12623
|
}) {
|
|
12263
|
-
const [open, setOpen] =
|
|
12624
|
+
const [open, setOpen] = useState21(false);
|
|
12264
12625
|
const color = value || fallback;
|
|
12265
12626
|
const anchorRef = useRef15(null);
|
|
12266
12627
|
return /* @__PURE__ */ jsxs30("div", { className: "bb-field", children: [
|
|
@@ -12389,7 +12750,7 @@ function MaterialSliderField({
|
|
|
12389
12750
|
}
|
|
12390
12751
|
|
|
12391
12752
|
// src/components/BindingBuilder/components/MaterialSection.tsx
|
|
12392
|
-
import { Fragment as
|
|
12753
|
+
import { Fragment as Fragment11, jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
12393
12754
|
function MaterialSection({
|
|
12394
12755
|
selectedItem,
|
|
12395
12756
|
selectedMaterial,
|
|
@@ -12414,11 +12775,11 @@ function MaterialSection({
|
|
|
12414
12775
|
tier: "starter",
|
|
12415
12776
|
locked: !canUseStarterFeatures
|
|
12416
12777
|
};
|
|
12417
|
-
const [roughnessMode, setRoughnessMode] =
|
|
12418
|
-
const [anisotropyMode, setAnisotropyMode] =
|
|
12778
|
+
const [roughnessMode, setRoughnessMode] = useState22("roughness");
|
|
12779
|
+
const [anisotropyMode, setAnisotropyMode] = useState22(
|
|
12419
12780
|
"factor"
|
|
12420
12781
|
);
|
|
12421
|
-
return /* @__PURE__ */ jsxs31(
|
|
12782
|
+
return /* @__PURE__ */ jsxs31(Fragment11, { children: [
|
|
12422
12783
|
/* @__PURE__ */ jsx36(
|
|
12423
12784
|
MaterialPanelGroup,
|
|
12424
12785
|
{
|
|
@@ -12457,7 +12818,7 @@ function MaterialSection({
|
|
|
12457
12818
|
}))
|
|
12458
12819
|
}
|
|
12459
12820
|
),
|
|
12460
|
-
pbrWorkflow === "metalness" ? /* @__PURE__ */ jsxs31(
|
|
12821
|
+
pbrWorkflow === "metalness" ? /* @__PURE__ */ jsxs31(Fragment11, { children: [
|
|
12461
12822
|
/* @__PURE__ */ jsx36(
|
|
12462
12823
|
MaterialSliderField,
|
|
12463
12824
|
{
|
|
@@ -12481,7 +12842,7 @@ function MaterialSection({
|
|
|
12481
12842
|
onChange: (value) => setSelectedTexturePath(selectedItem, "metalnessMap", value)
|
|
12482
12843
|
}
|
|
12483
12844
|
)
|
|
12484
|
-
] }) : /* @__PURE__ */ jsxs31(
|
|
12845
|
+
] }) : /* @__PURE__ */ jsxs31(Fragment11, { children: [
|
|
12485
12846
|
/* @__PURE__ */ jsx36(
|
|
12486
12847
|
MaterialSliderField,
|
|
12487
12848
|
{
|
|
@@ -13454,7 +13815,7 @@ function MaterialPresetGallery({
|
|
|
13454
13815
|
}
|
|
13455
13816
|
|
|
13456
13817
|
// src/components/BindingBuilder/components/ToolbarExportMenu.tsx
|
|
13457
|
-
import { useEffect as useEffect19, useRef as useRef16, useState as
|
|
13818
|
+
import { useEffect as useEffect19, useRef as useRef16, useState as useState23 } from "react";
|
|
13458
13819
|
import { ChevronDown as ChevronDown5, Download as Download2, FileCode2, FileJson } from "lucide-react";
|
|
13459
13820
|
import { jsx as jsx38, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
13460
13821
|
function ToolbarExportMenu({
|
|
@@ -13463,7 +13824,7 @@ function ToolbarExportMenu({
|
|
|
13463
13824
|
onExportJson,
|
|
13464
13825
|
onExportTs
|
|
13465
13826
|
}) {
|
|
13466
|
-
const [open, setOpen] =
|
|
13827
|
+
const [open, setOpen] = useState23(false);
|
|
13467
13828
|
const wrapperRef = useRef16(null);
|
|
13468
13829
|
useEffect19(() => {
|
|
13469
13830
|
if (!open) return;
|
|
@@ -13797,7 +14158,7 @@ function ActionsPanel({
|
|
|
13797
14158
|
|
|
13798
14159
|
// src/components/BindingBuilder/components/CameraPositionPanel.tsx
|
|
13799
14160
|
import { MapPin as MapPin3, Trash2 as Trash25 } from "lucide-react";
|
|
13800
|
-
import { Fragment as
|
|
14161
|
+
import { Fragment as Fragment12, jsx as jsx42, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
13801
14162
|
function CameraPositionPanel({
|
|
13802
14163
|
selectedBinding,
|
|
13803
14164
|
open,
|
|
@@ -13816,7 +14177,7 @@ function CameraPositionPanel({
|
|
|
13816
14177
|
/* @__PURE__ */ jsx42("span", { className: "bb-cameraLiveBadge", children: "LIVE" }),
|
|
13817
14178
|
/* @__PURE__ */ jsxs37("div", { className: "se-waypointBody", children: [
|
|
13818
14179
|
/* @__PURE__ */ jsx42("div", { className: "bb-cameraWaypointLabel", children: "Current view" }),
|
|
13819
|
-
cameraState ? /* @__PURE__ */ jsxs37(
|
|
14180
|
+
cameraState ? /* @__PURE__ */ jsxs37(Fragment12, { children: [
|
|
13820
14181
|
/* @__PURE__ */ jsxs37("div", { className: "se-waypointCoords", children: [
|
|
13821
14182
|
"pos ",
|
|
13822
14183
|
formattedCameraState?.position
|
|
@@ -13902,7 +14263,7 @@ function CameraPositionPanel({
|
|
|
13902
14263
|
}
|
|
13903
14264
|
|
|
13904
14265
|
// src/components/BindingBuilder/components/SceneNodesPanel.tsx
|
|
13905
|
-
import { useMemo as useMemo15, useState as
|
|
14266
|
+
import { useMemo as useMemo15, useState as useState24 } from "react";
|
|
13906
14267
|
|
|
13907
14268
|
// src/components/BindingBuilder/utils/bindingFactory.ts
|
|
13908
14269
|
function slugify(value) {
|
|
@@ -13982,7 +14343,7 @@ function collectNodeNames(scene) {
|
|
|
13982
14343
|
}
|
|
13983
14344
|
|
|
13984
14345
|
// src/components/BindingBuilder/components/SceneNodesPanel.tsx
|
|
13985
|
-
import { Fragment as
|
|
14346
|
+
import { Fragment as Fragment13, jsx as jsx43, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
13986
14347
|
function SceneNodesPanel({
|
|
13987
14348
|
bindings,
|
|
13988
14349
|
selectedItem,
|
|
@@ -13991,7 +14352,7 @@ function SceneNodesPanel({
|
|
|
13991
14352
|
isOpen = true,
|
|
13992
14353
|
onToggle
|
|
13993
14354
|
}) {
|
|
13994
|
-
const [search, setSearch] =
|
|
14355
|
+
const [search, setSearch] = useState24("");
|
|
13995
14356
|
const nodeKeys = useMemo15(() => Object.keys(bindings), [bindings]);
|
|
13996
14357
|
const filteredNodeKeys = useMemo15(() => {
|
|
13997
14358
|
const query = search.trim().toLowerCase();
|
|
@@ -14018,7 +14379,7 @@ function SceneNodesPanel({
|
|
|
14018
14379
|
children: "\u2190"
|
|
14019
14380
|
}
|
|
14020
14381
|
),
|
|
14021
|
-
isOpen && /* @__PURE__ */ jsxs38(
|
|
14382
|
+
isOpen && /* @__PURE__ */ jsxs38(Fragment13, { children: [
|
|
14022
14383
|
/* @__PURE__ */ jsxs38("div", { children: [
|
|
14023
14384
|
/* @__PURE__ */ jsx43("div", { className: "bb-sectionKicker", children: "Scene Nodes" }),
|
|
14024
14385
|
/* @__PURE__ */ jsxs38("div", { className: "bb-sectionHeading", children: [
|
|
@@ -14136,7 +14497,7 @@ function mergeAnimationConfig(sceneConfig, animationDetails) {
|
|
|
14136
14497
|
}
|
|
14137
14498
|
|
|
14138
14499
|
// src/components/BindingBuilder/hooks/useUndoableState.ts
|
|
14139
|
-
import { useCallback as
|
|
14500
|
+
import { useCallback as useCallback8, useReducer, useRef as useRef17 } from "react";
|
|
14140
14501
|
var MAX_HISTORY = 100;
|
|
14141
14502
|
function historyReducer(state, action) {
|
|
14142
14503
|
switch (action.type) {
|
|
@@ -14181,7 +14542,7 @@ function useUndoableState(initial, coalesceWindowMs = 600) {
|
|
|
14181
14542
|
future: []
|
|
14182
14543
|
});
|
|
14183
14544
|
const lastSetAt = useRef17(0);
|
|
14184
|
-
const setValue =
|
|
14545
|
+
const setValue = useCallback8(
|
|
14185
14546
|
(updater) => {
|
|
14186
14547
|
const now = Date.now();
|
|
14187
14548
|
const coalesce = now - lastSetAt.current < coalesceWindowMs;
|
|
@@ -14190,15 +14551,15 @@ function useUndoableState(initial, coalesceWindowMs = 600) {
|
|
|
14190
14551
|
},
|
|
14191
14552
|
[coalesceWindowMs]
|
|
14192
14553
|
);
|
|
14193
|
-
const undo =
|
|
14554
|
+
const undo = useCallback8(() => {
|
|
14194
14555
|
lastSetAt.current = 0;
|
|
14195
14556
|
dispatch({ type: "undo" });
|
|
14196
14557
|
}, []);
|
|
14197
|
-
const redo =
|
|
14558
|
+
const redo = useCallback8(() => {
|
|
14198
14559
|
lastSetAt.current = 0;
|
|
14199
14560
|
dispatch({ type: "redo" });
|
|
14200
14561
|
}, []);
|
|
14201
|
-
const reset =
|
|
14562
|
+
const reset = useCallback8((value) => {
|
|
14202
14563
|
lastSetAt.current = 0;
|
|
14203
14564
|
dispatch({ type: "reset", value });
|
|
14204
14565
|
}, []);
|
|
@@ -14427,7 +14788,7 @@ function removeMaterialPanelFields(material, panel) {
|
|
|
14427
14788
|
}
|
|
14428
14789
|
|
|
14429
14790
|
// src/components/BindingBuilder/BindingBuilder.tsx
|
|
14430
|
-
import { Fragment as
|
|
14791
|
+
import { Fragment as Fragment14, jsx as jsx45, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
14431
14792
|
var DEMO_MODEL_URL = "/room.glb";
|
|
14432
14793
|
function isPlainObject(value) {
|
|
14433
14794
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -14447,27 +14808,27 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14447
14808
|
const modelInputRef = useRef18(null);
|
|
14448
14809
|
const bindingsInputRef = useRef18(null);
|
|
14449
14810
|
const sceneConfigInputRef = useRef18(null);
|
|
14450
|
-
const [modelUrl, setModelUrl] =
|
|
14451
|
-
const [modelName, setModelName] =
|
|
14452
|
-
const [selectedItem, setSelectedItem] =
|
|
14811
|
+
const [modelUrl, setModelUrl] = useState25(null);
|
|
14812
|
+
const [modelName, setModelName] = useState25("");
|
|
14813
|
+
const [selectedItem, setSelectedItem] = useState25(null);
|
|
14453
14814
|
const bindingsHistory = useUndoableState({});
|
|
14454
14815
|
const bindings = bindingsHistory.value;
|
|
14455
14816
|
const setBindings = bindingsHistory.setValue;
|
|
14456
|
-
const [loading, setLoading] =
|
|
14457
|
-
const [isExporting, setIsExporting] =
|
|
14458
|
-
const [statusMessage, setStatusMessage] =
|
|
14817
|
+
const [loading, setLoading] = useState25(false);
|
|
14818
|
+
const [isExporting, setIsExporting] = useState25(false);
|
|
14819
|
+
const [statusMessage, setStatusMessage] = useState25(
|
|
14459
14820
|
"Load the demo model or upload a GLB/GLTF model to generate bindings."
|
|
14460
14821
|
);
|
|
14461
|
-
const [statusTone, setStatusTone] =
|
|
14462
|
-
const [statusBannerVisible, setStatusBannerVisible] =
|
|
14463
|
-
const [metricsDraft, setMetricsDraft] =
|
|
14464
|
-
const [metadataDraft, setMetadataDraft] =
|
|
14465
|
-
const [metricsError, setMetricsError] =
|
|
14466
|
-
const [metadataError, setMetadataError] =
|
|
14467
|
-
const [isPreviewDragActive, setIsPreviewDragActive] =
|
|
14468
|
-
const [isEditorCollapsed, setIsEditorCollapsed] =
|
|
14469
|
-
const [isNodesCollapsed, setIsNodesCollapsed] =
|
|
14470
|
-
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({
|
|
14471
14832
|
identity: true,
|
|
14472
14833
|
basics: true,
|
|
14473
14834
|
material: true,
|
|
@@ -14476,7 +14837,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14476
14837
|
metrics: false,
|
|
14477
14838
|
metadata: false
|
|
14478
14839
|
});
|
|
14479
|
-
const [openMaterialPanels, setOpenMaterialPanels] =
|
|
14840
|
+
const [openMaterialPanels, setOpenMaterialPanels] = useState25({
|
|
14480
14841
|
pbr: true,
|
|
14481
14842
|
roughness: false,
|
|
14482
14843
|
emission: false,
|
|
@@ -14492,24 +14853,24 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14492
14853
|
});
|
|
14493
14854
|
const toggleObjectSection = (key) => setOpenObjectSections((prev) => ({ ...prev, [key]: !prev[key] }));
|
|
14494
14855
|
const toggleMaterialPanel = (key) => setOpenMaterialPanels((prev) => ({ ...prev, [key]: !prev[key] }));
|
|
14495
|
-
const [activeEditorTab, setActiveEditorTab] =
|
|
14856
|
+
const [activeEditorTab, setActiveEditorTab] = useState25(
|
|
14496
14857
|
"object"
|
|
14497
14858
|
);
|
|
14498
|
-
const [activeSubTab, setActiveSubTab] =
|
|
14499
|
-
const [pbrWorkflow, setPbrWorkflow] =
|
|
14500
|
-
const [materialPanelEnabled, setMaterialPanelEnabled] =
|
|
14859
|
+
const [activeSubTab, setActiveSubTab] = useState25("General");
|
|
14860
|
+
const [pbrWorkflow, setPbrWorkflow] = useState25("metalness");
|
|
14861
|
+
const [materialPanelEnabled, setMaterialPanelEnabled] = useState25(createMaterialPanelEnabledState());
|
|
14501
14862
|
const materialPanelItemRef = useRef18(null);
|
|
14502
|
-
const [zoomOnSelected, setZoomOnSelected] =
|
|
14503
|
-
const [highlightOnHover, setHighlightOnHover] =
|
|
14504
|
-
const [resetDialogOpen, setResetDialogOpen] =
|
|
14505
|
-
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(
|
|
14506
14867
|
null
|
|
14507
14868
|
);
|
|
14508
14869
|
const sceneConfigHistory = useUndoableState(DEFAULT_SCENE_CONFIG);
|
|
14509
14870
|
const sceneConfig = sceneConfigHistory.value;
|
|
14510
14871
|
const setSceneConfig = sceneConfigHistory.setValue;
|
|
14511
|
-
const [activeAnnotation, setActiveAnnotation] =
|
|
14512
|
-
const [panelHoveredBinding, setPanelHoveredBinding] =
|
|
14872
|
+
const [activeAnnotation, setActiveAnnotation] = useState25(null);
|
|
14873
|
+
const [panelHoveredBinding, setPanelHoveredBinding] = useState25(null);
|
|
14513
14874
|
const license = useLicense(licenseKey);
|
|
14514
14875
|
const tier = normalizeTier(license.tier);
|
|
14515
14876
|
const bindingBuilderContextValue = useMemo16(
|
|
@@ -14535,14 +14896,16 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14535
14896
|
handleCameraChange,
|
|
14536
14897
|
handleViewerReady
|
|
14537
14898
|
} = useViewerCamera();
|
|
14538
|
-
const handlePreviewWaypoint =
|
|
14899
|
+
const handlePreviewWaypoint = useCallback9(
|
|
14539
14900
|
(waypoint) => {
|
|
14540
14901
|
setCameraState(
|
|
14541
14902
|
{
|
|
14542
14903
|
position: waypoint.position,
|
|
14543
14904
|
target: waypoint.target,
|
|
14544
14905
|
fov: cameraState?.fov ?? null,
|
|
14545
|
-
zoom: cameraState?.zoom ?? 1
|
|
14906
|
+
zoom: cameraState?.zoom ?? 1,
|
|
14907
|
+
azimuthAngle: waypoint.azimuthAngle,
|
|
14908
|
+
polarAngle: waypoint.polarAngle
|
|
14546
14909
|
},
|
|
14547
14910
|
true
|
|
14548
14911
|
);
|
|
@@ -14557,7 +14920,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14557
14920
|
position: formatVector3(cameraState.position),
|
|
14558
14921
|
target: formatVector3(cameraState.target)
|
|
14559
14922
|
} : null;
|
|
14560
|
-
const handleAnnotationsChange =
|
|
14923
|
+
const handleAnnotationsChange = useCallback9(
|
|
14561
14924
|
(newAnnotations) => {
|
|
14562
14925
|
setSceneConfig((prev) => {
|
|
14563
14926
|
const currentIds = prev.annotations.map((a) => a.id).join(",");
|
|
@@ -14573,7 +14936,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14573
14936
|
},
|
|
14574
14937
|
[]
|
|
14575
14938
|
);
|
|
14576
|
-
const handleActiveAnnotationChange =
|
|
14939
|
+
const handleActiveAnnotationChange = useCallback9(
|
|
14577
14940
|
(annotation) => {
|
|
14578
14941
|
setActiveAnnotation(
|
|
14579
14942
|
annotation ? {
|
|
@@ -14694,7 +15057,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14694
15057
|
});
|
|
14695
15058
|
}
|
|
14696
15059
|
};
|
|
14697
|
-
const applyBindingsFromScene =
|
|
15060
|
+
const applyBindingsFromScene = useCallback9(
|
|
14698
15061
|
(scene, nextModelName) => {
|
|
14699
15062
|
const nodeNames = collectNodeNames(scene);
|
|
14700
15063
|
bindingsHistory.reset(buildBindingsFromNodes(nodeNames));
|
|
@@ -14733,7 +15096,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14733
15096
|
bindingsHistory.reset({});
|
|
14734
15097
|
setSelectedItem(null);
|
|
14735
15098
|
};
|
|
14736
|
-
const handleViewerModelLoaded =
|
|
15099
|
+
const handleViewerModelLoaded = useCallback9(
|
|
14737
15100
|
(scene) => {
|
|
14738
15101
|
const pending = pendingLoadRef.current;
|
|
14739
15102
|
if (!pending || pending.requestId !== loadRequestRef.current) return;
|
|
@@ -14743,7 +15106,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14743
15106
|
},
|
|
14744
15107
|
[applyBindingsFromScene]
|
|
14745
15108
|
);
|
|
14746
|
-
const handleViewerLoadError =
|
|
15109
|
+
const handleViewerLoadError = useCallback9((error) => {
|
|
14747
15110
|
const pending = pendingLoadRef.current;
|
|
14748
15111
|
if (!pending || pending.requestId !== loadRequestRef.current) return;
|
|
14749
15112
|
pendingLoadRef.current = null;
|
|
@@ -14754,7 +15117,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14754
15117
|
setStatusTone("error");
|
|
14755
15118
|
setLoading(false);
|
|
14756
15119
|
}, []);
|
|
14757
|
-
const handleAnimationsReady =
|
|
15120
|
+
const handleAnimationsReady = useCallback9(
|
|
14758
15121
|
(controls) => {
|
|
14759
15122
|
viewerAnimations.handleAnimationsReady(controls);
|
|
14760
15123
|
const details = controls.clipDetails ?? controls.clips.map((sourceName) => ({ sourceName, duration: 0 }));
|
|
@@ -15055,7 +15418,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15055
15418
|
}));
|
|
15056
15419
|
setError(null);
|
|
15057
15420
|
};
|
|
15058
|
-
const handleViewerSelect =
|
|
15421
|
+
const handleViewerSelect = useCallback9(
|
|
15059
15422
|
(binding) => {
|
|
15060
15423
|
if (!binding) {
|
|
15061
15424
|
setSelectedItem(null);
|
|
@@ -15068,7 +15431,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15068
15431
|
},
|
|
15069
15432
|
[bindings]
|
|
15070
15433
|
);
|
|
15071
|
-
const handleSceneNodeSelect =
|
|
15434
|
+
const handleSceneNodeSelect = useCallback9(
|
|
15072
15435
|
async (nodeKey) => {
|
|
15073
15436
|
setSelectedItem(nodeKey);
|
|
15074
15437
|
setPanelHoveredBinding(null);
|
|
@@ -15076,7 +15439,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15076
15439
|
},
|
|
15077
15440
|
[focusObject]
|
|
15078
15441
|
);
|
|
15079
|
-
const saveCameraPositionForSelectedObject =
|
|
15442
|
+
const saveCameraPositionForSelectedObject = useCallback9(() => {
|
|
15080
15443
|
if (!selectedItem || !cameraState) {
|
|
15081
15444
|
return;
|
|
15082
15445
|
}
|
|
@@ -15091,7 +15454,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15091
15454
|
cameraState: currentCameraState
|
|
15092
15455
|
}));
|
|
15093
15456
|
}, [selectedItem, cameraState]);
|
|
15094
|
-
const goToSavedCameraPosition =
|
|
15457
|
+
const goToSavedCameraPosition = useCallback9(() => {
|
|
15095
15458
|
if (!savedCameraPosition?.position || !savedCameraPosition?.target) return;
|
|
15096
15459
|
setCameraState(
|
|
15097
15460
|
{
|
|
@@ -15103,28 +15466,28 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15103
15466
|
true
|
|
15104
15467
|
);
|
|
15105
15468
|
}, [savedCameraPosition, setCameraState, cameraState]);
|
|
15106
|
-
const clearSavedCameraPosition =
|
|
15469
|
+
const clearSavedCameraPosition = useCallback9(() => {
|
|
15107
15470
|
if (!selectedItem) return;
|
|
15108
15471
|
updateBinding(selectedItem, (binding) => ({
|
|
15109
15472
|
...binding,
|
|
15110
15473
|
cameraState: void 0
|
|
15111
15474
|
}));
|
|
15112
15475
|
}, [selectedItem]);
|
|
15113
|
-
const handleResetView =
|
|
15476
|
+
const handleResetView = useCallback9(async () => {
|
|
15114
15477
|
await resetView();
|
|
15115
15478
|
}, [resetView]);
|
|
15116
|
-
const handleFitScene =
|
|
15479
|
+
const handleFitScene = useCallback9(async () => {
|
|
15117
15480
|
await fitScene();
|
|
15118
15481
|
}, [fitScene]);
|
|
15119
|
-
const openResetDialog =
|
|
15482
|
+
const openResetDialog = useCallback9((target) => {
|
|
15120
15483
|
setResetTarget(target);
|
|
15121
15484
|
setResetDialogOpen(true);
|
|
15122
15485
|
}, []);
|
|
15123
|
-
const closeResetDialog =
|
|
15486
|
+
const closeResetDialog = useCallback9(() => {
|
|
15124
15487
|
setResetDialogOpen(false);
|
|
15125
15488
|
setResetTarget(null);
|
|
15126
15489
|
}, []);
|
|
15127
|
-
const executeReset =
|
|
15490
|
+
const executeReset = useCallback9(() => {
|
|
15128
15491
|
if (resetTarget === "bindings") {
|
|
15129
15492
|
if (!nodeKeys.length) {
|
|
15130
15493
|
closeResetDialog();
|
|
@@ -15221,7 +15584,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15221
15584
|
}
|
|
15222
15585
|
)
|
|
15223
15586
|
] }),
|
|
15224
|
-
!isEditorCollapsed && /* @__PURE__ */ jsxs40(
|
|
15587
|
+
!isEditorCollapsed && /* @__PURE__ */ jsxs40(Fragment14, { children: [
|
|
15225
15588
|
/* @__PURE__ */ jsxs40("div", { className: "bb-tabRow", children: [
|
|
15226
15589
|
/* @__PURE__ */ jsx45(
|
|
15227
15590
|
"button",
|
|
@@ -15242,7 +15605,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15242
15605
|
}
|
|
15243
15606
|
)
|
|
15244
15607
|
] }),
|
|
15245
|
-
/* @__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(
|
|
15246
15609
|
SceneSettingsTab,
|
|
15247
15610
|
{
|
|
15248
15611
|
config: sceneConfig,
|
|
@@ -15255,14 +15618,16 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15255
15618
|
// the whole settings panel for no reason.
|
|
15256
15619
|
activeSubTab === "Cinematic" && cameraState ? {
|
|
15257
15620
|
position: cameraState.position,
|
|
15258
|
-
target: cameraState.target
|
|
15621
|
+
target: cameraState.target,
|
|
15622
|
+
azimuthAngle: cameraState.azimuthAngle,
|
|
15623
|
+
polarAngle: cameraState.polarAngle
|
|
15259
15624
|
} : null
|
|
15260
15625
|
),
|
|
15261
15626
|
onPreviewWaypoint: handlePreviewWaypoint,
|
|
15262
15627
|
animationClipDetails: viewerAnimations.clipDetails,
|
|
15263
15628
|
animationPlayback: viewerAnimations
|
|
15264
15629
|
}
|
|
15265
|
-
) : /* @__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: [
|
|
15266
15631
|
/* @__PURE__ */ jsx45("div", { className: "bb-editorHeader", children: /* @__PURE__ */ jsxs40("div", { children: [
|
|
15267
15632
|
/* @__PURE__ */ jsx45("div", { className: "bb-sectionKicker", children: "Editing Binding" }),
|
|
15268
15633
|
/* @__PURE__ */ jsx45("h2", { className: "bb-editorTitle", children: selectedBinding.label || selectedItem }),
|
|
@@ -15682,8 +16047,8 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15682
16047
|
// src/components/SimpleModelViewer/SimpleModelViewer.tsx
|
|
15683
16048
|
import {
|
|
15684
16049
|
useRef as useRef19,
|
|
15685
|
-
useState as
|
|
15686
|
-
useCallback as
|
|
16050
|
+
useState as useState26,
|
|
16051
|
+
useCallback as useCallback10,
|
|
15687
16052
|
useEffect as useEffect21,
|
|
15688
16053
|
useMemo as useMemo17,
|
|
15689
16054
|
Component as Component3,
|
|
@@ -15706,14 +16071,14 @@ styleInject('.smv-root {\n --smv-panel-font-family:\n Inter,\n "Segoe UI"
|
|
|
15706
16071
|
import {
|
|
15707
16072
|
X as X3,
|
|
15708
16073
|
Check,
|
|
15709
|
-
Settings,
|
|
16074
|
+
Settings as Settings2,
|
|
15710
16075
|
ChevronDown as ChevronDown6,
|
|
15711
16076
|
Boxes as Boxes2,
|
|
15712
16077
|
Trash2 as Trash26,
|
|
15713
16078
|
Upload as Upload2,
|
|
15714
16079
|
AlertTriangle as AlertTriangle4
|
|
15715
16080
|
} from "lucide-react";
|
|
15716
|
-
import { Fragment as
|
|
16081
|
+
import { Fragment as Fragment15, jsx as jsx46, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
15717
16082
|
var HIGHLIGHT2 = { color: "#93c5fd", intensity: 0.05 };
|
|
15718
16083
|
var ENV_PRESETS = [
|
|
15719
16084
|
"apartment",
|
|
@@ -15773,7 +16138,7 @@ function InnerModel({
|
|
|
15773
16138
|
meshopt: true
|
|
15774
16139
|
});
|
|
15775
16140
|
const invalidate = useThree11((state) => state.invalidate);
|
|
15776
|
-
const [hoveredKey, setHoveredKey] =
|
|
16141
|
+
const [hoveredKey, setHoveredKey] = useState26(null);
|
|
15777
16142
|
const scene = useMemo17(
|
|
15778
16143
|
() => SkeletonUtils2.clone(rawScene),
|
|
15779
16144
|
[rawScene]
|
|
@@ -16075,7 +16440,7 @@ function EnvironmentSection({
|
|
|
16075
16440
|
title,
|
|
16076
16441
|
children
|
|
16077
16442
|
}) {
|
|
16078
|
-
const [collapsed, setCollapsed] =
|
|
16443
|
+
const [collapsed, setCollapsed] = useState26(false);
|
|
16079
16444
|
return /* @__PURE__ */ jsxs41("div", { className: "env-section", children: [
|
|
16080
16445
|
/* @__PURE__ */ jsxs41(
|
|
16081
16446
|
"button",
|
|
@@ -16114,7 +16479,7 @@ function EnvironmentPanel({
|
|
|
16114
16479
|
{
|
|
16115
16480
|
className: "side-panel__open-btn side-panel__open-btn--left",
|
|
16116
16481
|
onClick: () => onToggle(true),
|
|
16117
|
-
children: /* @__PURE__ */ jsx46(
|
|
16482
|
+
children: /* @__PURE__ */ jsx46(Settings2, { size: 20, strokeWidth: 1.8 })
|
|
16118
16483
|
}
|
|
16119
16484
|
),
|
|
16120
16485
|
/* @__PURE__ */ jsxs41(
|
|
@@ -16240,8 +16605,8 @@ function SceneObjectsPanel2({
|
|
|
16240
16605
|
onHover,
|
|
16241
16606
|
onToggleVisibility
|
|
16242
16607
|
}) {
|
|
16243
|
-
const [search, setSearch] =
|
|
16244
|
-
const [rowHovered, setRowHovered] =
|
|
16608
|
+
const [search, setSearch] = useState26("");
|
|
16609
|
+
const [rowHovered, setRowHovered] = useState26(null);
|
|
16245
16610
|
const entries = Object.values(meshKeys).filter(
|
|
16246
16611
|
(m) => m.label.toLowerCase().includes(search.toLowerCase())
|
|
16247
16612
|
);
|
|
@@ -16370,30 +16735,30 @@ function SimpleModelViewer({
|
|
|
16370
16735
|
const fileInputRef = useRef19(null);
|
|
16371
16736
|
const uploadedModelRef = useRef19(null);
|
|
16372
16737
|
const dragDepthRef = useRef19(0);
|
|
16373
|
-
const [loadedScene, setLoadedScene] =
|
|
16374
|
-
const [largeModelTriangles, setLargeModelTriangles] =
|
|
16738
|
+
const [loadedScene, setLoadedScene] = useState26(null);
|
|
16739
|
+
const [largeModelTriangles, setLargeModelTriangles] = useState26(
|
|
16375
16740
|
null
|
|
16376
16741
|
);
|
|
16377
|
-
const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] =
|
|
16378
|
-
const [meshKeys, setMeshKeys] =
|
|
16379
|
-
const [hiddenKeys, setHiddenKeys] =
|
|
16380
|
-
const [selectedKey, setSelectedKey] =
|
|
16381
|
-
const [panelHoveredKey, setPanelHoveredKey] =
|
|
16382
|
-
const [uploadedModelUrl, setUploadedModelUrl] =
|
|
16383
|
-
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(
|
|
16384
16749
|
null
|
|
16385
16750
|
);
|
|
16386
|
-
const [modelLoadFailed, setModelLoadFailed] =
|
|
16387
|
-
const [isResolvingUpload, setIsResolvingUpload] =
|
|
16388
|
-
const [initialFitComplete, setInitialFitComplete] =
|
|
16389
|
-
const [isDragActive, setIsDragActive] =
|
|
16390
|
-
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(
|
|
16391
16756
|
() => typeof window === "undefined" ? true : window.innerWidth > 900
|
|
16392
16757
|
);
|
|
16393
|
-
const [isEnvPanelOpen, setIsEnvPanelOpen] =
|
|
16758
|
+
const [isEnvPanelOpen, setIsEnvPanelOpen] = useState26(
|
|
16394
16759
|
() => typeof window === "undefined" ? true : window.innerWidth > 900
|
|
16395
16760
|
);
|
|
16396
|
-
const [envSettings, setEnvSettings] =
|
|
16761
|
+
const [envSettings, setEnvSettings] = useState26({
|
|
16397
16762
|
backgroundEnabled,
|
|
16398
16763
|
backgroundColor,
|
|
16399
16764
|
autoRotate,
|
|
@@ -16405,7 +16770,7 @@ function SimpleModelViewer({
|
|
|
16405
16770
|
directionalColor
|
|
16406
16771
|
});
|
|
16407
16772
|
const activeModelUrl = enableModelUpload ? uploadedModelUrl : modelUrl;
|
|
16408
|
-
const resetModelState =
|
|
16773
|
+
const resetModelState = useCallback10(() => {
|
|
16409
16774
|
setLoadedScene(null);
|
|
16410
16775
|
setLargeModelTriangles(null);
|
|
16411
16776
|
setLargeModelNoticeDismissed(false);
|
|
@@ -16416,11 +16781,11 @@ function SimpleModelViewer({
|
|
|
16416
16781
|
setModelLoadFailed(false);
|
|
16417
16782
|
setInitialFitComplete(false);
|
|
16418
16783
|
}, []);
|
|
16419
|
-
const revokeUploadedModelUrl =
|
|
16784
|
+
const revokeUploadedModelUrl = useCallback10(() => {
|
|
16420
16785
|
uploadedModelRef.current?.cleanup();
|
|
16421
16786
|
uploadedModelRef.current = null;
|
|
16422
16787
|
}, []);
|
|
16423
|
-
const clearUploadedModel =
|
|
16788
|
+
const clearUploadedModel = useCallback10(() => {
|
|
16424
16789
|
revokeUploadedModelUrl();
|
|
16425
16790
|
setUploadedModelUrl(null);
|
|
16426
16791
|
setUploadedModelName(null);
|
|
@@ -16449,7 +16814,7 @@ function SimpleModelViewer({
|
|
|
16449
16814
|
resetModelState,
|
|
16450
16815
|
revokeUploadedModelUrl
|
|
16451
16816
|
]);
|
|
16452
|
-
const handleUploadedModel =
|
|
16817
|
+
const handleUploadedModel = useCallback10(
|
|
16453
16818
|
async (file) => {
|
|
16454
16819
|
setIsResolvingUpload(true);
|
|
16455
16820
|
try {
|
|
@@ -16468,7 +16833,7 @@ function SimpleModelViewer({
|
|
|
16468
16833
|
},
|
|
16469
16834
|
[onLoadError, resetModelState, revokeUploadedModelUrl]
|
|
16470
16835
|
);
|
|
16471
|
-
const handleFileSelection =
|
|
16836
|
+
const handleFileSelection = useCallback10(
|
|
16472
16837
|
(files) => {
|
|
16473
16838
|
const file = files?.[0];
|
|
16474
16839
|
if (!file) return;
|
|
@@ -16481,7 +16846,7 @@ function SimpleModelViewer({
|
|
|
16481
16846
|
revokeUploadedModelUrl();
|
|
16482
16847
|
};
|
|
16483
16848
|
}, [revokeUploadedModelUrl]);
|
|
16484
|
-
const handleEnvSettingChange =
|
|
16849
|
+
const handleEnvSettingChange = useCallback10(
|
|
16485
16850
|
(key, value) => {
|
|
16486
16851
|
setEnvSettings((prev) => ({ ...prev, [key]: value }));
|
|
16487
16852
|
},
|
|
@@ -16493,12 +16858,12 @@ function SimpleModelViewer({
|
|
|
16493
16858
|
useEffect21(() => {
|
|
16494
16859
|
resetModelState();
|
|
16495
16860
|
}, [activeModelUrl, resetModelState]);
|
|
16496
|
-
const handleMeshesReady =
|
|
16861
|
+
const handleMeshesReady = useCallback10((meshes) => {
|
|
16497
16862
|
setMeshKeys(meshes);
|
|
16498
16863
|
setHiddenKeys(/* @__PURE__ */ new Set());
|
|
16499
16864
|
setSelectedKey(null);
|
|
16500
16865
|
}, []);
|
|
16501
|
-
const handleModelLoaded =
|
|
16866
|
+
const handleModelLoaded = useCallback10(
|
|
16502
16867
|
(scene) => {
|
|
16503
16868
|
setLoadedScene(scene);
|
|
16504
16869
|
setModelLoadFailed(false);
|
|
@@ -16509,14 +16874,14 @@ function SimpleModelViewer({
|
|
|
16509
16874
|
},
|
|
16510
16875
|
[onModelLoaded]
|
|
16511
16876
|
);
|
|
16512
|
-
const handleLoadError =
|
|
16877
|
+
const handleLoadError = useCallback10(
|
|
16513
16878
|
(error) => {
|
|
16514
16879
|
setModelLoadFailed(true);
|
|
16515
16880
|
onLoadError?.(error);
|
|
16516
16881
|
},
|
|
16517
16882
|
[onLoadError]
|
|
16518
16883
|
);
|
|
16519
|
-
const focusMesh =
|
|
16884
|
+
const focusMesh = useCallback10(
|
|
16520
16885
|
(key) => {
|
|
16521
16886
|
setSelectedKey(key);
|
|
16522
16887
|
if (!zoomOnSelected) return;
|
|
@@ -16532,14 +16897,14 @@ function SimpleModelViewer({
|
|
|
16532
16897
|
},
|
|
16533
16898
|
[zoomOnSelected]
|
|
16534
16899
|
);
|
|
16535
|
-
const handleToggleVisibility =
|
|
16900
|
+
const handleToggleVisibility = useCallback10((key) => {
|
|
16536
16901
|
setHiddenKeys((prev) => {
|
|
16537
16902
|
const next = new Set(prev);
|
|
16538
16903
|
next.has(key) ? next.delete(key) : next.add(key);
|
|
16539
16904
|
return next;
|
|
16540
16905
|
});
|
|
16541
16906
|
}, []);
|
|
16542
|
-
const handleDragEnter =
|
|
16907
|
+
const handleDragEnter = useCallback10(
|
|
16543
16908
|
(event) => {
|
|
16544
16909
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
16545
16910
|
return;
|
|
@@ -16551,7 +16916,7 @@ function SimpleModelViewer({
|
|
|
16551
16916
|
},
|
|
16552
16917
|
[enableModelUpload]
|
|
16553
16918
|
);
|
|
16554
|
-
const handleDragOver =
|
|
16919
|
+
const handleDragOver = useCallback10(
|
|
16555
16920
|
(event) => {
|
|
16556
16921
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
16557
16922
|
return;
|
|
@@ -16563,7 +16928,7 @@ function SimpleModelViewer({
|
|
|
16563
16928
|
},
|
|
16564
16929
|
[enableModelUpload]
|
|
16565
16930
|
);
|
|
16566
|
-
const handleDragLeave =
|
|
16931
|
+
const handleDragLeave = useCallback10(
|
|
16567
16932
|
(event) => {
|
|
16568
16933
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
16569
16934
|
return;
|
|
@@ -16577,7 +16942,7 @@ function SimpleModelViewer({
|
|
|
16577
16942
|
},
|
|
16578
16943
|
[enableModelUpload]
|
|
16579
16944
|
);
|
|
16580
|
-
const handleDrop =
|
|
16945
|
+
const handleDrop = useCallback10(
|
|
16581
16946
|
(event) => {
|
|
16582
16947
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
16583
16948
|
return;
|
|
@@ -16631,7 +16996,7 @@ function SimpleModelViewer({
|
|
|
16631
16996
|
}
|
|
16632
16997
|
)
|
|
16633
16998
|
] }),
|
|
16634
|
-
enableModelUpload ? /* @__PURE__ */ jsxs41(
|
|
16999
|
+
enableModelUpload ? /* @__PURE__ */ jsxs41(Fragment15, { children: [
|
|
16635
17000
|
/* @__PURE__ */ jsx46(
|
|
16636
17001
|
"input",
|
|
16637
17002
|
{
|
|
@@ -16645,7 +17010,7 @@ function SimpleModelViewer({
|
|
|
16645
17010
|
}
|
|
16646
17011
|
}
|
|
16647
17012
|
),
|
|
16648
|
-
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: [
|
|
16649
17014
|
/* @__PURE__ */ jsx46("span", { className: "smv-upload-toolbar__name", children: uploadedModelName ?? "Uploaded model" }),
|
|
16650
17015
|
/* @__PURE__ */ jsxs41(
|
|
16651
17016
|
"button",
|
|
@@ -16806,18 +17171,18 @@ function SimpleModelViewer({
|
|
|
16806
17171
|
}
|
|
16807
17172
|
|
|
16808
17173
|
// src/hooks/useViewerSelection.ts
|
|
16809
|
-
import { useCallback as
|
|
17174
|
+
import { useCallback as useCallback11, useState as useState27 } from "react";
|
|
16810
17175
|
function useViewerSelection(initialSelectedObjectBinding = null) {
|
|
16811
|
-
const [selectedObjectBinding, setSelectedObjectBinding] =
|
|
17176
|
+
const [selectedObjectBinding, setSelectedObjectBinding] = useState27(
|
|
16812
17177
|
initialSelectedObjectBinding
|
|
16813
17178
|
);
|
|
16814
|
-
const handleObjectSelect =
|
|
17179
|
+
const handleObjectSelect = useCallback11(
|
|
16815
17180
|
(binding) => {
|
|
16816
17181
|
setSelectedObjectBinding(binding);
|
|
16817
17182
|
},
|
|
16818
17183
|
[setSelectedObjectBinding]
|
|
16819
17184
|
);
|
|
16820
|
-
const clearSelection =
|
|
17185
|
+
const clearSelection = useCallback11(() => {
|
|
16821
17186
|
setSelectedObjectBinding(null);
|
|
16822
17187
|
}, []);
|
|
16823
17188
|
return {
|
|
@@ -16855,14 +17220,14 @@ function useObjectBindingIds(objectBindings) {
|
|
|
16855
17220
|
}
|
|
16856
17221
|
|
|
16857
17222
|
// src/hooks/useObjectVisibility.ts
|
|
16858
|
-
import { useCallback as
|
|
17223
|
+
import { useCallback as useCallback12, useMemo as useMemo20 } from "react";
|
|
16859
17224
|
function getHiddenObjects(objectBindings) {
|
|
16860
17225
|
return Object.fromEntries(
|
|
16861
17226
|
Object.entries(objectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
|
|
16862
17227
|
);
|
|
16863
17228
|
}
|
|
16864
17229
|
function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
16865
|
-
const updateVisibility =
|
|
17230
|
+
const updateVisibility = useCallback12(
|
|
16866
17231
|
(objectId, visible) => {
|
|
16867
17232
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
16868
17233
|
if (!bindingKey) return;
|
|
@@ -16878,15 +17243,15 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
|
16878
17243
|
},
|
|
16879
17244
|
[objectBindings, onObjectBindingsChange]
|
|
16880
17245
|
);
|
|
16881
|
-
const hideObject =
|
|
17246
|
+
const hideObject = useCallback12(
|
|
16882
17247
|
(objectId) => updateVisibility(objectId, false),
|
|
16883
17248
|
[updateVisibility]
|
|
16884
17249
|
);
|
|
16885
|
-
const showObject =
|
|
17250
|
+
const showObject = useCallback12(
|
|
16886
17251
|
(objectId) => updateVisibility(objectId, true),
|
|
16887
17252
|
[updateVisibility]
|
|
16888
17253
|
);
|
|
16889
|
-
const toggleObjectVisibility =
|
|
17254
|
+
const toggleObjectVisibility = useCallback12(
|
|
16890
17255
|
(objectId) => {
|
|
16891
17256
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
16892
17257
|
if (!bindingKey) return;
|
|
@@ -16902,7 +17267,7 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
|
16902
17267
|
},
|
|
16903
17268
|
[objectBindings, onObjectBindingsChange]
|
|
16904
17269
|
);
|
|
16905
|
-
const clearHiddenObjects =
|
|
17270
|
+
const clearHiddenObjects = useCallback12(() => {
|
|
16906
17271
|
onObjectBindingsChange(
|
|
16907
17272
|
Object.fromEntries(
|
|
16908
17273
|
Object.entries(objectBindings).map(([bindingKey, binding]) => [
|
|
@@ -16922,7 +17287,7 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
|
16922
17287
|
),
|
|
16923
17288
|
[hiddenObjects, objectBindings]
|
|
16924
17289
|
);
|
|
16925
|
-
const isObjectHidden =
|
|
17290
|
+
const isObjectHidden = useCallback12(
|
|
16926
17291
|
(objectId) => {
|
|
16927
17292
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
16928
17293
|
return bindingKey ? objectBindings[bindingKey]?.visible === false : false;
|
|
@@ -16941,10 +17306,10 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
|
16941
17306
|
}
|
|
16942
17307
|
|
|
16943
17308
|
// src/hooks/useViewerHover.ts
|
|
16944
|
-
import { useCallback as
|
|
17309
|
+
import { useCallback as useCallback13, useState as useState28 } from "react";
|
|
16945
17310
|
function useViewerHover() {
|
|
16946
|
-
const [hoveredObjectBinding, setHoveredObjectBinding] =
|
|
16947
|
-
const handleHoveredObject =
|
|
17311
|
+
const [hoveredObjectBinding, setHoveredObjectBinding] = useState28(null);
|
|
17312
|
+
const handleHoveredObject = useCallback13(
|
|
16948
17313
|
(binding) => {
|
|
16949
17314
|
setHoveredObjectBinding(binding);
|
|
16950
17315
|
},
|
|
@@ -16957,18 +17322,18 @@ function useViewerHover() {
|
|
|
16957
17322
|
}
|
|
16958
17323
|
|
|
16959
17324
|
// src/hooks/useViewerModel.ts
|
|
16960
|
-
import { useCallback as
|
|
17325
|
+
import { useCallback as useCallback14, useState as useState29 } from "react";
|
|
16961
17326
|
import { Box3 as Box36, Vector3 as Vector310 } from "three";
|
|
16962
17327
|
function toTuple2(vector) {
|
|
16963
17328
|
return [vector.x, vector.y, vector.z];
|
|
16964
17329
|
}
|
|
16965
17330
|
function useViewerModel() {
|
|
16966
|
-
const [isLoading, setIsLoading] =
|
|
16967
|
-
const [isReady, setIsReady] =
|
|
16968
|
-
const [error, setError] =
|
|
16969
|
-
const [bounds, setBounds] =
|
|
16970
|
-
const [objectCount, setObjectCount] =
|
|
16971
|
-
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) => {
|
|
16972
17337
|
const box = new Box36().setFromObject(scene);
|
|
16973
17338
|
const min = box.min.clone();
|
|
16974
17339
|
const max = box.max.clone();
|
|
@@ -16993,14 +17358,14 @@ function useViewerModel() {
|
|
|
16993
17358
|
setIsLoading(false);
|
|
16994
17359
|
setIsReady(true);
|
|
16995
17360
|
}, []);
|
|
16996
|
-
const handleLoadError =
|
|
17361
|
+
const handleLoadError = useCallback14((nextError) => {
|
|
16997
17362
|
setError(nextError);
|
|
16998
17363
|
setBounds(null);
|
|
16999
17364
|
setObjectCount(0);
|
|
17000
17365
|
setIsLoading(false);
|
|
17001
17366
|
setIsReady(false);
|
|
17002
17367
|
}, []);
|
|
17003
|
-
const resetModelState =
|
|
17368
|
+
const resetModelState = useCallback14(() => {
|
|
17004
17369
|
setIsLoading(true);
|
|
17005
17370
|
setIsReady(false);
|
|
17006
17371
|
setError(null);
|
|
@@ -17020,10 +17385,10 @@ function useViewerModel() {
|
|
|
17020
17385
|
}
|
|
17021
17386
|
|
|
17022
17387
|
// src/hooks/useViewerActions.ts
|
|
17023
|
-
import { useCallback as
|
|
17388
|
+
import { useCallback as useCallback15 } from "react";
|
|
17024
17389
|
function useViewerActions(objectBindings, options = {}) {
|
|
17025
17390
|
const { onAction, onObjectBindingsChange } = options;
|
|
17026
|
-
const getActionsForObject =
|
|
17391
|
+
const getActionsForObject = useCallback15(
|
|
17027
17392
|
(objectId) => {
|
|
17028
17393
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
17029
17394
|
if (!bindingKey) {
|
|
@@ -17033,7 +17398,7 @@ function useViewerActions(objectBindings, options = {}) {
|
|
|
17033
17398
|
},
|
|
17034
17399
|
[objectBindings]
|
|
17035
17400
|
);
|
|
17036
|
-
const runAction =
|
|
17401
|
+
const runAction = useCallback15(
|
|
17037
17402
|
(objectId, actionId) => {
|
|
17038
17403
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
17039
17404
|
if (!bindingKey) {
|
|
@@ -17075,7 +17440,7 @@ function useViewerActions(objectBindings, options = {}) {
|
|
|
17075
17440
|
}
|
|
17076
17441
|
|
|
17077
17442
|
// src/hooks/useShareableViewerState.ts
|
|
17078
|
-
import { useCallback as
|
|
17443
|
+
import { useCallback as useCallback16, useEffect as useEffect22, useRef as useRef20, useState as useState30 } from "react";
|
|
17079
17444
|
function serializeState(cameraState, objectBindings, selectedObjectBinding) {
|
|
17080
17445
|
const hidden = Object.values(objectBindings).filter((binding) => binding.visible === false).map((binding) => binding.id);
|
|
17081
17446
|
return {
|
|
@@ -17096,7 +17461,7 @@ function useShareableViewerState({
|
|
|
17096
17461
|
autoSync = true
|
|
17097
17462
|
}) {
|
|
17098
17463
|
const lastWrittenRawRef = useRef20(null);
|
|
17099
|
-
const buildHash =
|
|
17464
|
+
const buildHash = useCallback16(() => {
|
|
17100
17465
|
const state = serializeState(
|
|
17101
17466
|
cameraState,
|
|
17102
17467
|
objectBindings,
|
|
@@ -17104,12 +17469,12 @@ function useShareableViewerState({
|
|
|
17104
17469
|
);
|
|
17105
17470
|
return `${paramKey}=${encodeURIComponent(JSON.stringify(state))}`;
|
|
17106
17471
|
}, [cameraState, objectBindings, selectedObjectBinding, paramKey]);
|
|
17107
|
-
const getShareUrl =
|
|
17472
|
+
const getShareUrl = useCallback16(() => {
|
|
17108
17473
|
const url = new URL(window.location.href);
|
|
17109
17474
|
url.hash = buildHash();
|
|
17110
17475
|
return url.toString();
|
|
17111
17476
|
}, [buildHash]);
|
|
17112
|
-
const syncUrl =
|
|
17477
|
+
const syncUrl = useCallback16(() => {
|
|
17113
17478
|
const url = new URL(window.location.href);
|
|
17114
17479
|
url.hash = buildHash();
|
|
17115
17480
|
window.history.replaceState(null, "", url.toString());
|
|
@@ -17127,7 +17492,7 @@ function useShareableViewerState({
|
|
|
17127
17492
|
objectBindings,
|
|
17128
17493
|
selectedObjectBinding
|
|
17129
17494
|
};
|
|
17130
|
-
const [restoreAttempted, setRestoreAttempted] =
|
|
17495
|
+
const [restoreAttempted, setRestoreAttempted] = useState30(false);
|
|
17131
17496
|
const lastSyncedHashRef = useRef20(null);
|
|
17132
17497
|
useEffect22(() => {
|
|
17133
17498
|
if (!autoSync || !restoreAttempted) return;
|
|
@@ -17153,7 +17518,7 @@ function useShareableViewerState({
|
|
|
17153
17518
|
}, [autoSync, restoreAttempted, debounceMs, paramKey]);
|
|
17154
17519
|
const lastRestoredRawRef = useRef20(null);
|
|
17155
17520
|
const restoringRawRef = useRef20(null);
|
|
17156
|
-
const restoreFromUrl =
|
|
17521
|
+
const restoreFromUrl = useCallback16(async () => {
|
|
17157
17522
|
setRestoreAttempted(true);
|
|
17158
17523
|
const rawHash = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
|
|
17159
17524
|
const raw = new URLSearchParams(rawHash).get(paramKey);
|