@liveroom-tech/react-immersive 1.3.2 → 1.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +694 -314
- package/dist/index.mjs +647 -266
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -242,7 +242,7 @@ function styleInject(css, { insertAt } = {}) {
|
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
// src/styles/ModelViewer.css
|
|
245
|
-
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');
|
|
245
|
+
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');
|
|
246
246
|
|
|
247
247
|
// src/components/LicenseOverlay.tsx
|
|
248
248
|
var import_lucide_react = require("lucide-react");
|
|
@@ -637,6 +637,7 @@ var DEFAULT_SCENE_POST_PROCESSING_CONFIG = {
|
|
|
637
637
|
rampEnd: [1, 1],
|
|
638
638
|
startColor: "#000000",
|
|
639
639
|
endColor: "#ffffff",
|
|
640
|
+
opacity: 0.5,
|
|
640
641
|
rampBias: 0.5,
|
|
641
642
|
rampGain: 0.5,
|
|
642
643
|
rampMask: false,
|
|
@@ -1474,7 +1475,10 @@ function PostProcessingEffects({
|
|
|
1474
1475
|
}
|
|
1475
1476
|
),
|
|
1476
1477
|
pp.pixelation.enabled === true && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_postprocessing5.Pixelation, { granularity: pp.pixelation.granularity }),
|
|
1477
|
-
pp.ramp.enabled === true &&
|
|
1478
|
+
pp.ramp.enabled === true && // Ramp's non-mask shader emits an opaque full-screen gradient.
|
|
1479
|
+
// Blend it with the scene by default instead of replacing the
|
|
1480
|
+
// model completely; mask mode already performs its own mix.
|
|
1481
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1478
1482
|
import_postprocessing5.Ramp,
|
|
1479
1483
|
{
|
|
1480
1484
|
rampType: rampTypeFromName(pp.ramp.rampType),
|
|
@@ -1482,6 +1486,7 @@ function PostProcessingEffects({
|
|
|
1482
1486
|
rampEnd: pp.ramp.rampEnd,
|
|
1483
1487
|
startColor: rampStartColor,
|
|
1484
1488
|
endColor: rampEndColor,
|
|
1489
|
+
opacity: pp.ramp.rampMask ? 1 : pp.ramp.opacity ?? 0.5,
|
|
1485
1490
|
rampBias: pp.ramp.rampBias,
|
|
1486
1491
|
rampGain: pp.ramp.rampGain,
|
|
1487
1492
|
rampMask: pp.ramp.rampMask,
|
|
@@ -4094,18 +4099,75 @@ function CinematicCamera({
|
|
|
4094
4099
|
sceneRef,
|
|
4095
4100
|
config,
|
|
4096
4101
|
playing,
|
|
4102
|
+
resetOnPlay = false,
|
|
4097
4103
|
onFinished,
|
|
4098
|
-
onUserInteract
|
|
4104
|
+
onUserInteract,
|
|
4105
|
+
onStop
|
|
4099
4106
|
}) {
|
|
4100
4107
|
const invalidate = (0, import_fiber10.useThree)((state) => state.invalidate);
|
|
4101
4108
|
const waypoints = (0, import_react15.useMemo)(() => config.waypoints ?? [], [config.waypoints]);
|
|
4102
4109
|
const hasPath = waypoints.length >= 2;
|
|
4103
4110
|
const loop = config.loop ?? true;
|
|
4104
|
-
const
|
|
4111
|
+
const calculatedDuration = (0, import_react15.useMemo)(() => {
|
|
4112
|
+
if (waypoints.length < 2) return 24;
|
|
4113
|
+
let total = 0;
|
|
4114
|
+
for (let i = 0; i < waypoints.length - 1; i++) {
|
|
4115
|
+
total += waypoints[i].segmentDuration ?? 3.5;
|
|
4116
|
+
}
|
|
4117
|
+
if (loop && waypoints.length > 1) {
|
|
4118
|
+
total += waypoints[waypoints.length - 1].segmentDuration ?? 3.5;
|
|
4119
|
+
}
|
|
4120
|
+
return Math.max(total, 4);
|
|
4121
|
+
}, [waypoints, loop]);
|
|
4122
|
+
const duration = config.duration ?? calculatedDuration;
|
|
4105
4123
|
const signature = (0, import_react15.useMemo)(
|
|
4106
|
-
() => waypoints.map(
|
|
4124
|
+
() => waypoints.map(
|
|
4125
|
+
(waypoint) => [
|
|
4126
|
+
...waypoint.position,
|
|
4127
|
+
...waypoint.target,
|
|
4128
|
+
waypoint.azimuthAngle ?? "",
|
|
4129
|
+
waypoint.polarAngle ?? "",
|
|
4130
|
+
waypoint.segmentDuration ?? ""
|
|
4131
|
+
].join(",")
|
|
4132
|
+
).join(";"),
|
|
4107
4133
|
[waypoints]
|
|
4108
4134
|
);
|
|
4135
|
+
const segmentTimes = (0, import_react15.useMemo)(() => {
|
|
4136
|
+
const times = [];
|
|
4137
|
+
if (waypoints.length < 2) return times;
|
|
4138
|
+
let accumulated = 0;
|
|
4139
|
+
for (let i = 0; i < waypoints.length - 1; i++) {
|
|
4140
|
+
accumulated += waypoints[i].segmentDuration ?? 3.5;
|
|
4141
|
+
times.push(accumulated);
|
|
4142
|
+
}
|
|
4143
|
+
if (loop && waypoints.length > 1) {
|
|
4144
|
+
accumulated += waypoints[waypoints.length - 1].segmentDuration ?? 3.5;
|
|
4145
|
+
times.push(accumulated);
|
|
4146
|
+
}
|
|
4147
|
+
return times;
|
|
4148
|
+
}, [waypoints, loop]);
|
|
4149
|
+
const getCurveParameter = (0, import_react15.useMemo)(() => {
|
|
4150
|
+
return (elapsed) => {
|
|
4151
|
+
if (waypoints.length < 2 || segmentTimes.length === 0) {
|
|
4152
|
+
return elapsed / duration;
|
|
4153
|
+
}
|
|
4154
|
+
const segmentTotal = segmentTimes[segmentTimes.length - 1];
|
|
4155
|
+
const playbackElapsed = loop ? elapsed % duration : Math.min(elapsed, duration);
|
|
4156
|
+
const normalizedElapsed = duration > 0 ? playbackElapsed / duration * segmentTotal : 0;
|
|
4157
|
+
for (let i = 0; i < segmentTimes.length; i++) {
|
|
4158
|
+
if (normalizedElapsed <= segmentTimes[i]) {
|
|
4159
|
+
const prevTime = i === 0 ? 0 : segmentTimes[i - 1];
|
|
4160
|
+
const segmentDuration = segmentTimes[i] - prevTime;
|
|
4161
|
+
const progress = segmentDuration > 0 ? (normalizedElapsed - prevTime) / segmentDuration : 0;
|
|
4162
|
+
const totalSegments = loop ? waypoints.length : waypoints.length - 1;
|
|
4163
|
+
const baseU = i / totalSegments;
|
|
4164
|
+
const segmentU = 1 / totalSegments;
|
|
4165
|
+
return baseU + progress * segmentU;
|
|
4166
|
+
}
|
|
4167
|
+
}
|
|
4168
|
+
return 1;
|
|
4169
|
+
};
|
|
4170
|
+
}, [waypoints.length, segmentTimes, duration, loop]);
|
|
4109
4171
|
const curves = (0, import_react15.useMemo)(() => {
|
|
4110
4172
|
if (!hasPath) return null;
|
|
4111
4173
|
return {
|
|
@@ -4123,29 +4185,83 @@ function CinematicCamera({
|
|
|
4123
4185
|
)
|
|
4124
4186
|
};
|
|
4125
4187
|
}, [signature, hasPath, loop]);
|
|
4188
|
+
const orbitPath = (0, import_react15.useMemo)(() => {
|
|
4189
|
+
if (!hasPath || !waypoints.every(
|
|
4190
|
+
(waypoint) => Number.isFinite(waypoint.azimuthAngle) && Number.isFinite(waypoint.polarAngle)
|
|
4191
|
+
)) {
|
|
4192
|
+
return null;
|
|
4193
|
+
}
|
|
4194
|
+
const points = waypoints.map((waypoint) => ({
|
|
4195
|
+
azimuth: waypoint.azimuthAngle,
|
|
4196
|
+
polar: waypoint.polarAngle,
|
|
4197
|
+
distance: new import_three18.Vector3(...waypoint.position).distanceTo(
|
|
4198
|
+
new import_three18.Vector3(...waypoint.target)
|
|
4199
|
+
)
|
|
4200
|
+
}));
|
|
4201
|
+
for (let index = 1; index < points.length; index += 1) {
|
|
4202
|
+
const previous = points[index - 1].azimuth;
|
|
4203
|
+
const current = points[index].azimuth;
|
|
4204
|
+
points[index].azimuth = current + Math.round((previous - current) / (Math.PI * 2)) * Math.PI * 2;
|
|
4205
|
+
}
|
|
4206
|
+
return points;
|
|
4207
|
+
}, [signature, hasPath]);
|
|
4126
4208
|
const elapsedRef = (0, import_react15.useRef)(0);
|
|
4127
4209
|
const enterElapsedRef = (0, import_react15.useRef)(0);
|
|
4128
4210
|
const enterPos = (0, import_react15.useRef)(new import_three18.Vector3());
|
|
4129
4211
|
const enterTarget = (0, import_react15.useRef)(new import_three18.Vector3());
|
|
4130
4212
|
const startedRef = (0, import_react15.useRef)(false);
|
|
4131
4213
|
const pausedElapsedRef = (0, import_react15.useRef)(0);
|
|
4214
|
+
const hasFinishedRef = (0, import_react15.useRef)(false);
|
|
4132
4215
|
const orbitRef = (0, import_react15.useRef)(null);
|
|
4133
4216
|
const tmpPos = (0, import_react15.useMemo)(() => new import_three18.Vector3(), []);
|
|
4134
4217
|
const tmpTarget = (0, import_react15.useMemo)(() => new import_three18.Vector3(), []);
|
|
4218
|
+
const tmpSpherical = (0, import_react15.useMemo)(() => new import_three18.Spherical(), []);
|
|
4219
|
+
(0, import_react15.useEffect)(() => {
|
|
4220
|
+
elapsedRef.current = 0;
|
|
4221
|
+
pausedElapsedRef.current = 0;
|
|
4222
|
+
enterElapsedRef.current = 0;
|
|
4223
|
+
hasFinishedRef.current = false;
|
|
4224
|
+
}, [signature, loop, duration]);
|
|
4225
|
+
(0, import_react15.useEffect)(() => {
|
|
4226
|
+
if (resetOnPlay) {
|
|
4227
|
+
pausedElapsedRef.current = 0;
|
|
4228
|
+
elapsedRef.current = 0;
|
|
4229
|
+
hasFinishedRef.current = false;
|
|
4230
|
+
}
|
|
4231
|
+
}, [resetOnPlay]);
|
|
4135
4232
|
(0, import_react15.useEffect)(() => {
|
|
4136
4233
|
if (!playing) {
|
|
4137
4234
|
startedRef.current = false;
|
|
4138
|
-
if (elapsedRef.current > 0) {
|
|
4235
|
+
if (elapsedRef.current > 0 && !resetOnPlay) {
|
|
4139
4236
|
pausedElapsedRef.current = elapsedRef.current;
|
|
4237
|
+
} else if (resetOnPlay) {
|
|
4238
|
+
pausedElapsedRef.current = 0;
|
|
4239
|
+
}
|
|
4240
|
+
if (elapsedRef.current > 0 || hasFinishedRef.current) {
|
|
4241
|
+
onStop?.();
|
|
4140
4242
|
}
|
|
4141
4243
|
return;
|
|
4142
4244
|
}
|
|
4245
|
+
if (elapsedRef.current >= duration && !loop) {
|
|
4246
|
+
elapsedRef.current = 0;
|
|
4247
|
+
pausedElapsedRef.current = 0;
|
|
4248
|
+
hasFinishedRef.current = false;
|
|
4249
|
+
}
|
|
4143
4250
|
const controls = controlsRef.current;
|
|
4144
4251
|
if (controls) {
|
|
4145
4252
|
controls.getPosition(enterPos.current);
|
|
4146
4253
|
controls.getTarget(enterTarget.current);
|
|
4147
4254
|
}
|
|
4148
|
-
|
|
4255
|
+
if (resetOnPlay || hasFinishedRef.current || pausedElapsedRef.current === 0) {
|
|
4256
|
+
elapsedRef.current = 0;
|
|
4257
|
+
pausedElapsedRef.current = 0;
|
|
4258
|
+
hasFinishedRef.current = false;
|
|
4259
|
+
} else if (pausedElapsedRef.current > 0 && pausedElapsedRef.current < duration) {
|
|
4260
|
+
elapsedRef.current = pausedElapsedRef.current;
|
|
4261
|
+
} else {
|
|
4262
|
+
elapsedRef.current = 0;
|
|
4263
|
+
pausedElapsedRef.current = 0;
|
|
4264
|
+
}
|
|
4149
4265
|
enterElapsedRef.current = 0;
|
|
4150
4266
|
if (!hasPath) {
|
|
4151
4267
|
const scene = sceneRef.current;
|
|
@@ -4159,12 +4275,26 @@ function CinematicCamera({
|
|
|
4159
4275
|
}
|
|
4160
4276
|
startedRef.current = true;
|
|
4161
4277
|
invalidate();
|
|
4162
|
-
}, [
|
|
4278
|
+
}, [
|
|
4279
|
+
playing,
|
|
4280
|
+
hasPath,
|
|
4281
|
+
controlsRef,
|
|
4282
|
+
sceneRef,
|
|
4283
|
+
invalidate,
|
|
4284
|
+
duration,
|
|
4285
|
+
loop,
|
|
4286
|
+
signature,
|
|
4287
|
+
resetOnPlay,
|
|
4288
|
+
onStop
|
|
4289
|
+
]);
|
|
4163
4290
|
(0, import_react15.useEffect)(() => {
|
|
4164
4291
|
if (!playing || !onUserInteract) return;
|
|
4165
4292
|
const controls = controlsRef.current;
|
|
4166
4293
|
if (!controls) return;
|
|
4167
|
-
const handle = () =>
|
|
4294
|
+
const handle = () => {
|
|
4295
|
+
if (hasFinishedRef.current) return;
|
|
4296
|
+
onUserInteract();
|
|
4297
|
+
};
|
|
4168
4298
|
controls.addEventListener("controlstart", handle);
|
|
4169
4299
|
return () => controls.removeEventListener("controlstart", handle);
|
|
4170
4300
|
}, [playing, controlsRef, onUserInteract]);
|
|
@@ -4174,12 +4304,33 @@ function CinematicCamera({
|
|
|
4174
4304
|
if (!controls) return;
|
|
4175
4305
|
elapsedRef.current += delta;
|
|
4176
4306
|
enterElapsedRef.current += delta;
|
|
4177
|
-
let u = elapsedRef.current
|
|
4178
|
-
const finished = !loop &&
|
|
4307
|
+
let u = getCurveParameter(elapsedRef.current);
|
|
4308
|
+
const finished = !loop && elapsedRef.current >= duration;
|
|
4179
4309
|
u = loop ? u % 1 : Math.min(u, 1);
|
|
4180
4310
|
if (curves) {
|
|
4181
|
-
curves.
|
|
4182
|
-
|
|
4311
|
+
curves.target.getPoint(u, tmpTarget);
|
|
4312
|
+
if (orbitPath) {
|
|
4313
|
+
const segmentCount = loop ? orbitPath.length : orbitPath.length - 1;
|
|
4314
|
+
const scaled = Math.min(u, 1) * segmentCount;
|
|
4315
|
+
const segment = Math.min(Math.floor(scaled), segmentCount - 1);
|
|
4316
|
+
const next = (segment + 1) % orbitPath.length;
|
|
4317
|
+
const localT = u >= 1 ? 1 : scaled - segment;
|
|
4318
|
+
const eased = localT * localT * (3 - 2 * localT);
|
|
4319
|
+
const start = orbitPath[segment];
|
|
4320
|
+
const end = orbitPath[next];
|
|
4321
|
+
const endAzimuth = end.azimuth + Math.round((start.azimuth - end.azimuth) / (Math.PI * 2)) * Math.PI * 2;
|
|
4322
|
+
const azimuth = start.azimuth + (endAzimuth - start.azimuth) * eased;
|
|
4323
|
+
const polar = start.polar + (end.polar - start.polar) * eased;
|
|
4324
|
+
const distance = start.distance + (end.distance - start.distance) * eased;
|
|
4325
|
+
tmpSpherical.set(
|
|
4326
|
+
Math.max(distance, 1e-6),
|
|
4327
|
+
Math.min(Math.max(polar, 1e-6), Math.PI - 1e-6),
|
|
4328
|
+
azimuth
|
|
4329
|
+
);
|
|
4330
|
+
tmpPos.setFromSpherical(tmpSpherical).add(tmpTarget);
|
|
4331
|
+
} else {
|
|
4332
|
+
curves.position.getPoint(u, tmpPos);
|
|
4333
|
+
}
|
|
4183
4334
|
} else {
|
|
4184
4335
|
const orbit = orbitRef.current;
|
|
4185
4336
|
if (!orbit) return;
|
|
@@ -4209,6 +4360,8 @@ function CinematicCamera({
|
|
|
4209
4360
|
);
|
|
4210
4361
|
if (finished) {
|
|
4211
4362
|
startedRef.current = false;
|
|
4363
|
+
hasFinishedRef.current = true;
|
|
4364
|
+
elapsedRef.current = 0;
|
|
4212
4365
|
pausedElapsedRef.current = 0;
|
|
4213
4366
|
onFinished?.();
|
|
4214
4367
|
return;
|
|
@@ -5589,9 +5742,11 @@ function ModelViewer({
|
|
|
5589
5742
|
return { ...base ?? {}, ...cinematic };
|
|
5590
5743
|
}, [cinematic, resolvedSceneConfig]);
|
|
5591
5744
|
const [cinematicPlaying, setCinematicPlaying] = (0, import_react22.useState)(false);
|
|
5745
|
+
const [cinematicReset, setCinematicReset] = (0, import_react22.useState)(false);
|
|
5592
5746
|
const cinematicAutoStartedRef = (0, import_react22.useRef)(false);
|
|
5593
5747
|
(0, import_react22.useEffect)(() => {
|
|
5594
5748
|
setCinematicPlaying(false);
|
|
5749
|
+
setCinematicReset(false);
|
|
5595
5750
|
cinematicAutoStartedRef.current = false;
|
|
5596
5751
|
}, [modelUrl]);
|
|
5597
5752
|
(0, import_react22.useEffect)(() => {
|
|
@@ -5599,6 +5754,7 @@ function ModelViewer({
|
|
|
5599
5754
|
if (modelLoaded && cameraPositioned && !isAutoFraming) {
|
|
5600
5755
|
cinematicAutoStartedRef.current = true;
|
|
5601
5756
|
setCinematicPlaying(true);
|
|
5757
|
+
setCinematicReset(false);
|
|
5602
5758
|
}
|
|
5603
5759
|
}, [cinematicConfig, modelLoaded, cameraPositioned, isAutoFraming]);
|
|
5604
5760
|
const xrStore = (0, import_react22.useMemo)(() => {
|
|
@@ -6752,6 +6908,20 @@ function ModelViewer({
|
|
|
6752
6908
|
console.error("Reset camera error:", error);
|
|
6753
6909
|
}
|
|
6754
6910
|
}, [onAutoFit, fitScene]);
|
|
6911
|
+
const handleCinematicPlay = (0, import_react22.useCallback)(() => {
|
|
6912
|
+
setCinematicReset(false);
|
|
6913
|
+
setCinematicPlaying(true);
|
|
6914
|
+
}, []);
|
|
6915
|
+
const handleCinematicPause = (0, import_react22.useCallback)(() => {
|
|
6916
|
+
setCinematicPlaying(false);
|
|
6917
|
+
}, []);
|
|
6918
|
+
const handleCinematicStop = (0, import_react22.useCallback)(() => {
|
|
6919
|
+
setCinematicPlaying(false);
|
|
6920
|
+
setCinematicReset(true);
|
|
6921
|
+
setTimeout(() => {
|
|
6922
|
+
handleReset();
|
|
6923
|
+
}, 100);
|
|
6924
|
+
}, [handleReset]);
|
|
6755
6925
|
const handleCameraChangeWithAnnotations = (0, import_react22.useCallback)(
|
|
6756
6926
|
(cameraValue, controls) => {
|
|
6757
6927
|
handleCameraChangeInternal(cameraValue, controls);
|
|
@@ -7068,8 +7238,14 @@ function ModelViewer({
|
|
|
7068
7238
|
sceneRef,
|
|
7069
7239
|
config: cinematicConfig,
|
|
7070
7240
|
playing: cinematicPlaying,
|
|
7071
|
-
|
|
7072
|
-
|
|
7241
|
+
resetOnPlay: cinematicReset,
|
|
7242
|
+
onFinished: () => {
|
|
7243
|
+
setCinematicPlaying(false);
|
|
7244
|
+
setCinematicReset(false);
|
|
7245
|
+
},
|
|
7246
|
+
onUserInteract: () => {
|
|
7247
|
+
setCinematicPlaying(false);
|
|
7248
|
+
}
|
|
7073
7249
|
}
|
|
7074
7250
|
),
|
|
7075
7251
|
showMeasureTools && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
@@ -7297,24 +7473,43 @@ function ModelViewer({
|
|
|
7297
7473
|
]
|
|
7298
7474
|
}
|
|
7299
7475
|
),
|
|
7300
|
-
cinematicConfig && /* @__PURE__ */ (0, import_jsx_runtime19.
|
|
7476
|
+
cinematicConfig && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
7301
7477
|
"div",
|
|
7302
7478
|
{
|
|
7303
7479
|
className: "ri-cinematicTools",
|
|
7304
7480
|
style: {
|
|
7305
7481
|
top: `${topLeftRow((xrToolsVisible ? 1 : 0) + (showMeasureTools ? 1 : 0) + (showExplodeControls ? 1 : 0))}px`
|
|
7306
7482
|
},
|
|
7307
|
-
children:
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7483
|
+
children: [
|
|
7484
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
7485
|
+
"button",
|
|
7486
|
+
{
|
|
7487
|
+
type: "button",
|
|
7488
|
+
className: `ri-measureButton ${cinematicPlaying ? "ri-measureButton--active" : ""}`,
|
|
7489
|
+
onClick: () => {
|
|
7490
|
+
if (cinematicPlaying) {
|
|
7491
|
+
setCinematicPlaying(false);
|
|
7492
|
+
} else {
|
|
7493
|
+
setCinematicReset(false);
|
|
7494
|
+
setCinematicPlaying(true);
|
|
7495
|
+
}
|
|
7496
|
+
},
|
|
7497
|
+
title: cinematicPlaying ? "Pause cinematic" : "Play cinematic",
|
|
7498
|
+
"aria-pressed": cinematicPlaying,
|
|
7499
|
+
children: cinematicPlaying ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react3.Pause, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react3.Play, { size: 16 })
|
|
7500
|
+
}
|
|
7501
|
+
),
|
|
7502
|
+
cinematicPlaying && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
7503
|
+
"button",
|
|
7504
|
+
{
|
|
7505
|
+
type: "button",
|
|
7506
|
+
className: "ri-measureButton ri-measureButton--stop",
|
|
7507
|
+
onClick: handleCinematicStop,
|
|
7508
|
+
title: "Stop cinematic and reset camera",
|
|
7509
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react3.Square, { size: 16 })
|
|
7510
|
+
}
|
|
7511
|
+
)
|
|
7512
|
+
]
|
|
7318
7513
|
}
|
|
7319
7514
|
),
|
|
7320
7515
|
(showResetButton || showDownloadButton || showAnnotationNavigation && sortedAnnotations.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "ri-buttonContainer", children: [
|
|
@@ -7554,7 +7749,7 @@ function ModelViewer({
|
|
|
7554
7749
|
}
|
|
7555
7750
|
|
|
7556
7751
|
// src/components/BindingBuilder/BindingBuilder.tsx
|
|
7557
|
-
var
|
|
7752
|
+
var import_react39 = require("react");
|
|
7558
7753
|
var import_lucide_react16 = require("lucide-react");
|
|
7559
7754
|
|
|
7560
7755
|
// src/styles/BindingBuilder.css
|
|
@@ -7594,14 +7789,16 @@ function buildCameraState(camera, controls) {
|
|
|
7594
7789
|
position: toTuple(position),
|
|
7595
7790
|
target: toTuple(target),
|
|
7596
7791
|
fov: "fov" in camera && typeof camera.fov === "number" ? camera.fov : null,
|
|
7597
|
-
zoom: "zoom" in camera && typeof camera.zoom === "number" ? camera.zoom : 1
|
|
7792
|
+
zoom: "zoom" in camera && typeof camera.zoom === "number" ? camera.zoom : 1,
|
|
7793
|
+
azimuthAngle: controls.azimuthAngle,
|
|
7794
|
+
polarAngle: controls.polarAngle
|
|
7598
7795
|
};
|
|
7599
7796
|
}
|
|
7600
7797
|
function isSameCameraState(left, right) {
|
|
7601
7798
|
if (!left) {
|
|
7602
7799
|
return false;
|
|
7603
7800
|
}
|
|
7604
|
-
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;
|
|
7801
|
+
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;
|
|
7605
7802
|
}
|
|
7606
7803
|
async function runSmoothCameraTransition2(controls, action, smoothTime = 0.6) {
|
|
7607
7804
|
const originalSmoothTime = controls.smoothTime;
|
|
@@ -7756,15 +7953,27 @@ function useViewerCamera() {
|
|
|
7756
7953
|
}
|
|
7757
7954
|
try {
|
|
7758
7955
|
markInitialAutoFitOverride(controls);
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7956
|
+
const transitions = [
|
|
7957
|
+
controls.setLookAt(
|
|
7958
|
+
state.position[0],
|
|
7959
|
+
state.position[1],
|
|
7960
|
+
state.position[2],
|
|
7961
|
+
state.target[0],
|
|
7962
|
+
state.target[1],
|
|
7963
|
+
state.target[2],
|
|
7964
|
+
enableTransition
|
|
7965
|
+
)
|
|
7966
|
+
];
|
|
7967
|
+
if (state.azimuthAngle != null && state.polarAngle != null) {
|
|
7968
|
+
transitions.push(
|
|
7969
|
+
controls.rotateTo(
|
|
7970
|
+
state.azimuthAngle,
|
|
7971
|
+
state.polarAngle,
|
|
7972
|
+
enableTransition
|
|
7973
|
+
)
|
|
7974
|
+
);
|
|
7975
|
+
}
|
|
7976
|
+
await Promise.all(transitions);
|
|
7768
7977
|
const camera = controls.camera;
|
|
7769
7978
|
let projectionChanged = false;
|
|
7770
7979
|
if (state.fov !== null && "fov" in camera && typeof camera.fov === "number") {
|
|
@@ -8072,7 +8281,7 @@ function inferMimeType(path) {
|
|
|
8072
8281
|
}
|
|
8073
8282
|
|
|
8074
8283
|
// src/components/BindingBuilder/SceneSettings/SceneSettingsTab.tsx
|
|
8075
|
-
var
|
|
8284
|
+
var import_react33 = require("react");
|
|
8076
8285
|
|
|
8077
8286
|
// src/components/BindingBuilder/SceneSettings/GeneralSceneSettings.tsx
|
|
8078
8287
|
var import_react27 = require("react");
|
|
@@ -8253,9 +8462,10 @@ function SliderRow({
|
|
|
8253
8462
|
max,
|
|
8254
8463
|
step,
|
|
8255
8464
|
formatValue,
|
|
8256
|
-
onChange
|
|
8465
|
+
onChange,
|
|
8466
|
+
disabled = false
|
|
8257
8467
|
}) {
|
|
8258
|
-
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("label", { className: "se-field"
|
|
8468
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("label", { className: `se-field ${disabled ? "se-field--disabled" : ""}`, children: [
|
|
8259
8469
|
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "se-fieldLabel", children: [
|
|
8260
8470
|
label,
|
|
8261
8471
|
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "se-fieldValue", children: formatValue ? formatValue(value) : value })
|
|
@@ -8269,7 +8479,8 @@ function SliderRow({
|
|
|
8269
8479
|
max,
|
|
8270
8480
|
step,
|
|
8271
8481
|
value,
|
|
8272
|
-
onChange: (e) => onChange(Number(e.target.value))
|
|
8482
|
+
onChange: (e) => onChange(Number(e.target.value)),
|
|
8483
|
+
disabled
|
|
8273
8484
|
}
|
|
8274
8485
|
)
|
|
8275
8486
|
] });
|
|
@@ -10315,7 +10526,7 @@ function RampSection({
|
|
|
10315
10526
|
PPSection,
|
|
10316
10527
|
{
|
|
10317
10528
|
title: "Ramp",
|
|
10318
|
-
tooltip: "
|
|
10529
|
+
tooltip: "Adds a screen-space color gradient, or masks effects earlier in the stack.",
|
|
10319
10530
|
open: open.ramp,
|
|
10320
10531
|
onToggle: () => toggle("ramp"),
|
|
10321
10532
|
disabled: !ramp.enabled,
|
|
@@ -10375,6 +10586,17 @@ function RampSection({
|
|
|
10375
10586
|
onChange: (v) => set("ramp", { ...ramp, endColor: v })
|
|
10376
10587
|
}
|
|
10377
10588
|
),
|
|
10589
|
+
!ramp.rampMask && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
10590
|
+
SliderRow,
|
|
10591
|
+
{
|
|
10592
|
+
label: "Gradient Opacity",
|
|
10593
|
+
value: ramp.opacity ?? 0.5,
|
|
10594
|
+
min: 0,
|
|
10595
|
+
max: 1,
|
|
10596
|
+
step: 0.01,
|
|
10597
|
+
onChange: (v) => set("ramp", { ...ramp, opacity: v })
|
|
10598
|
+
}
|
|
10599
|
+
),
|
|
10378
10600
|
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
10379
10601
|
SliderRow,
|
|
10380
10602
|
{
|
|
@@ -10400,7 +10622,7 @@ function RampSection({
|
|
|
10400
10622
|
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
10401
10623
|
ToggleRow,
|
|
10402
10624
|
{
|
|
10403
|
-
label: "Mask",
|
|
10625
|
+
label: "Mask Earlier Effects",
|
|
10404
10626
|
checked: ramp.rampMask,
|
|
10405
10627
|
onChange: (v) => set("ramp", { ...ramp, rampMask: v })
|
|
10406
10628
|
}
|
|
@@ -11674,6 +11896,7 @@ function AnnotationsTab({ config, onChange }) {
|
|
|
11674
11896
|
}
|
|
11675
11897
|
|
|
11676
11898
|
// src/components/BindingBuilder/SceneSettings/CinematicTab.tsx
|
|
11899
|
+
var import_react32 = require("react");
|
|
11677
11900
|
var import_lucide_react10 = require("lucide-react");
|
|
11678
11901
|
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
11679
11902
|
var DEFAULT_CINEMATIC = {
|
|
@@ -11682,6 +11905,7 @@ var DEFAULT_CINEMATIC = {
|
|
|
11682
11905
|
loop: true,
|
|
11683
11906
|
autoPlay: false
|
|
11684
11907
|
};
|
|
11908
|
+
var DEFAULT_SEGMENT_DURATION = 3.5;
|
|
11685
11909
|
function CinematicTab({
|
|
11686
11910
|
config,
|
|
11687
11911
|
onChange,
|
|
@@ -11690,31 +11914,76 @@ function CinematicTab({
|
|
|
11690
11914
|
}) {
|
|
11691
11915
|
const cinematic = config.cinematic ?? DEFAULT_CINEMATIC;
|
|
11692
11916
|
const { waypoints } = cinematic;
|
|
11693
|
-
const
|
|
11917
|
+
const calculateTotalDuration = (0, import_react32.useCallback)((wps, loop) => {
|
|
11918
|
+
if (wps.length < 2) return 0;
|
|
11919
|
+
let total = 0;
|
|
11920
|
+
for (let i = 0; i < wps.length - 1; i++) {
|
|
11921
|
+
const segDur = wps[i].segmentDuration ?? DEFAULT_SEGMENT_DURATION;
|
|
11922
|
+
total += segDur;
|
|
11923
|
+
}
|
|
11924
|
+
if (loop && wps.length > 1) {
|
|
11925
|
+
total += wps[wps.length - 1].segmentDuration ?? DEFAULT_SEGMENT_DURATION;
|
|
11926
|
+
}
|
|
11927
|
+
return Math.max(total, 0);
|
|
11928
|
+
}, []);
|
|
11929
|
+
const update = (0, import_react32.useCallback)((patch) => {
|
|
11694
11930
|
onChange({ ...config, cinematic: { ...cinematic, ...patch } });
|
|
11695
|
-
};
|
|
11696
|
-
const addWaypoint = () => {
|
|
11931
|
+
}, [config, cinematic, onChange]);
|
|
11932
|
+
const addWaypoint = (0, import_react32.useCallback)(() => {
|
|
11697
11933
|
if (!cameraState) return;
|
|
11934
|
+
const newWaypoint = {
|
|
11935
|
+
position: [...cameraState.position],
|
|
11936
|
+
target: [...cameraState.target],
|
|
11937
|
+
...cameraState.azimuthAngle != null ? { azimuthAngle: cameraState.azimuthAngle } : {},
|
|
11938
|
+
...cameraState.polarAngle != null ? { polarAngle: cameraState.polarAngle } : {},
|
|
11939
|
+
segmentDuration: DEFAULT_SEGMENT_DURATION
|
|
11940
|
+
};
|
|
11941
|
+
const newWaypoints = [...waypoints, newWaypoint];
|
|
11942
|
+
const newDuration = calculateTotalDuration(newWaypoints, cinematic.loop);
|
|
11698
11943
|
update({
|
|
11699
|
-
waypoints:
|
|
11700
|
-
|
|
11701
|
-
{
|
|
11702
|
-
position: [...cameraState.position],
|
|
11703
|
-
target: [...cameraState.target]
|
|
11704
|
-
}
|
|
11705
|
-
]
|
|
11944
|
+
waypoints: newWaypoints,
|
|
11945
|
+
duration: newDuration
|
|
11706
11946
|
});
|
|
11707
|
-
};
|
|
11708
|
-
const deleteWaypoint = (index) => {
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
|
|
11947
|
+
}, [cameraState, waypoints, cinematic.loop, calculateTotalDuration, update]);
|
|
11948
|
+
const deleteWaypoint = (0, import_react32.useCallback)((index) => {
|
|
11949
|
+
const newWaypoints = waypoints.filter((_, i) => i !== index);
|
|
11950
|
+
const newDuration = calculateTotalDuration(newWaypoints, cinematic.loop);
|
|
11951
|
+
update({
|
|
11952
|
+
waypoints: newWaypoints,
|
|
11953
|
+
duration: newDuration
|
|
11954
|
+
});
|
|
11955
|
+
}, [waypoints, cinematic.loop, calculateTotalDuration, update]);
|
|
11956
|
+
const moveWaypoint = (0, import_react32.useCallback)((index, delta) => {
|
|
11712
11957
|
const next = index + delta;
|
|
11713
11958
|
if (next < 0 || next >= waypoints.length) return;
|
|
11714
11959
|
const reordered = [...waypoints];
|
|
11715
11960
|
[reordered[index], reordered[next]] = [reordered[next], reordered[index]];
|
|
11716
|
-
|
|
11717
|
-
|
|
11961
|
+
const newDuration = calculateTotalDuration(reordered, cinematic.loop);
|
|
11962
|
+
update({
|
|
11963
|
+
waypoints: reordered,
|
|
11964
|
+
duration: newDuration
|
|
11965
|
+
});
|
|
11966
|
+
}, [waypoints, cinematic.loop, calculateTotalDuration, update]);
|
|
11967
|
+
const updateWaypoint = (0, import_react32.useCallback)((index, updates) => {
|
|
11968
|
+
const newWaypoints = waypoints.map((wp, i) => {
|
|
11969
|
+
if (i === index) {
|
|
11970
|
+
return { ...wp, ...updates };
|
|
11971
|
+
}
|
|
11972
|
+
return wp;
|
|
11973
|
+
});
|
|
11974
|
+
const newDuration = calculateTotalDuration(newWaypoints, cinematic.loop);
|
|
11975
|
+
update({
|
|
11976
|
+
waypoints: newWaypoints,
|
|
11977
|
+
duration: newDuration
|
|
11978
|
+
});
|
|
11979
|
+
}, [waypoints, cinematic.loop, calculateTotalDuration, update]);
|
|
11980
|
+
const [customizeDurations, setCustomizeDurations] = (0, import_react32.useState)(false);
|
|
11981
|
+
const hasSegmentDurations = waypoints.some((w) => w.segmentDuration !== void 0);
|
|
11982
|
+
const getDuration = (0, import_react32.useCallback)((index) => {
|
|
11983
|
+
if (index >= waypoints.length - 1) return DEFAULT_SEGMENT_DURATION;
|
|
11984
|
+
return waypoints[index]?.segmentDuration ?? DEFAULT_SEGMENT_DURATION;
|
|
11985
|
+
}, [waypoints]);
|
|
11986
|
+
const hasEnoughWaypoints = waypoints.length >= 2;
|
|
11718
11987
|
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(Section, { title: "Cinematic Camera", children: [
|
|
11719
11988
|
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("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." }),
|
|
11720
11989
|
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
@@ -11731,62 +12000,165 @@ function CinematicTab({
|
|
|
11731
12000
|
]
|
|
11732
12001
|
}
|
|
11733
12002
|
),
|
|
11734
|
-
|
|
11735
|
-
/* @__PURE__ */ (0, import_jsx_runtime31.
|
|
11736
|
-
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "se-waypointBody", children: [
|
|
11737
|
-
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "se-waypointCoords", children: [
|
|
11738
|
-
"pos [",
|
|
11739
|
-
waypoint.position.map((n) => n.toFixed(1)).join(", "),
|
|
11740
|
-
"]"
|
|
11741
|
-
] }),
|
|
11742
|
-
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "se-waypointCoords", children: [
|
|
11743
|
-
"look [",
|
|
11744
|
-
waypoint.target.map((n) => n.toFixed(1)).join(", "),
|
|
11745
|
-
"]"
|
|
11746
|
-
] })
|
|
11747
|
-
] }),
|
|
11748
|
-
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "se-waypointActions", children: [
|
|
11749
|
-
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
11750
|
-
"button",
|
|
11751
|
-
{
|
|
11752
|
-
className: "se-waypointAction",
|
|
11753
|
-
onClick: () => onPreviewWaypoint?.(waypoint),
|
|
11754
|
-
title: "Fly camera to this waypoint",
|
|
11755
|
-
disabled: !onPreviewWaypoint,
|
|
11756
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react10.MapPin, { size: 15 })
|
|
11757
|
-
}
|
|
11758
|
-
),
|
|
11759
|
-
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
11760
|
-
"button",
|
|
11761
|
-
{
|
|
11762
|
-
className: "se-waypointAction",
|
|
11763
|
-
onClick: () => moveWaypoint(index, -1),
|
|
11764
|
-
title: "Move up",
|
|
11765
|
-
disabled: index === 0,
|
|
11766
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react10.ArrowUp, { size: 15 })
|
|
11767
|
-
}
|
|
11768
|
-
),
|
|
12003
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "se-customizeToggle", children: [
|
|
12004
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("label", { className: "se-toggleLabel", children: [
|
|
11769
12005
|
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
11770
|
-
"
|
|
12006
|
+
"input",
|
|
11771
12007
|
{
|
|
11772
|
-
|
|
11773
|
-
|
|
11774
|
-
|
|
11775
|
-
disabled: index === waypoints.length - 1,
|
|
11776
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react10.ArrowDown, { size: 15 })
|
|
12008
|
+
type: "checkbox",
|
|
12009
|
+
checked: customizeDurations,
|
|
12010
|
+
onChange: (e) => setCustomizeDurations(e.target.checked)
|
|
11777
12011
|
}
|
|
11778
12012
|
),
|
|
11779
|
-
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
11780
|
-
|
|
11781
|
-
|
|
11782
|
-
|
|
11783
|
-
|
|
11784
|
-
|
|
11785
|
-
|
|
11786
|
-
}
|
|
11787
|
-
)
|
|
12013
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react10.Settings, { size: 14 }),
|
|
12014
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { children: "Customize durations between waypoints" })
|
|
12015
|
+
] }),
|
|
12016
|
+
customizeDurations && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { className: "se-toggleHint", children: [
|
|
12017
|
+
"Set duration for each segment between waypoints: 1\u21922, 2\u21923, etc. Default: ",
|
|
12018
|
+
DEFAULT_SEGMENT_DURATION,
|
|
12019
|
+
"s"
|
|
11788
12020
|
] })
|
|
11789
|
-
] }
|
|
12021
|
+
] }),
|
|
12022
|
+
waypoints.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "bb-emptyAnnotations", children: "No waypoints yet. Position the camera and add your first one." }) : /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
|
|
12023
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "se-waypointList", children: waypoints.map((waypoint, index) => {
|
|
12024
|
+
const dur = getDuration(index);
|
|
12025
|
+
const isLast = index === waypoints.length - 1;
|
|
12026
|
+
const isCustom = waypoint.segmentDuration !== void 0 && waypoint.segmentDuration !== DEFAULT_SEGMENT_DURATION;
|
|
12027
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { children: [
|
|
12028
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "se-waypointCard", children: [
|
|
12029
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "se-waypointBadge", children: index + 1 }),
|
|
12030
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "se-waypointBody", children: [
|
|
12031
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "se-waypointCoords", children: [
|
|
12032
|
+
"pos [",
|
|
12033
|
+
waypoint.position.map((n) => n.toFixed(1)).join(", "),
|
|
12034
|
+
"]"
|
|
12035
|
+
] }),
|
|
12036
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "se-waypointCoords", children: [
|
|
12037
|
+
"look [",
|
|
12038
|
+
waypoint.target.map((n) => n.toFixed(1)).join(", "),
|
|
12039
|
+
"]"
|
|
12040
|
+
] }),
|
|
12041
|
+
customizeDurations && !isLast && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: `se-customBadge ${isCustom ? "se-customBadge--custom" : "se-customBadge--default"}`, children: [
|
|
12042
|
+
dur.toFixed(1),
|
|
12043
|
+
"s",
|
|
12044
|
+
!isCustom && " (default)"
|
|
12045
|
+
] })
|
|
12046
|
+
] }),
|
|
12047
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "se-waypointActions", children: [
|
|
12048
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
12049
|
+
"button",
|
|
12050
|
+
{
|
|
12051
|
+
className: "se-waypointAction",
|
|
12052
|
+
onClick: () => onPreviewWaypoint?.(waypoint),
|
|
12053
|
+
title: "Fly camera to this waypoint",
|
|
12054
|
+
disabled: !onPreviewWaypoint,
|
|
12055
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react10.MapPin, { size: 15 })
|
|
12056
|
+
}
|
|
12057
|
+
),
|
|
12058
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
12059
|
+
"button",
|
|
12060
|
+
{
|
|
12061
|
+
className: "se-waypointAction",
|
|
12062
|
+
onClick: () => moveWaypoint(index, -1),
|
|
12063
|
+
title: "Move up",
|
|
12064
|
+
disabled: index === 0,
|
|
12065
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react10.ArrowUp, { size: 15 })
|
|
12066
|
+
}
|
|
12067
|
+
),
|
|
12068
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
12069
|
+
"button",
|
|
12070
|
+
{
|
|
12071
|
+
className: "se-waypointAction",
|
|
12072
|
+
onClick: () => moveWaypoint(index, 1),
|
|
12073
|
+
title: "Move down",
|
|
12074
|
+
disabled: isLast,
|
|
12075
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react10.ArrowDown, { size: 15 })
|
|
12076
|
+
}
|
|
12077
|
+
),
|
|
12078
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
12079
|
+
"button",
|
|
12080
|
+
{
|
|
12081
|
+
className: "se-waypointAction se-waypointAction--delete",
|
|
12082
|
+
onClick: () => deleteWaypoint(index),
|
|
12083
|
+
title: "Delete waypoint",
|
|
12084
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react10.Trash2, { size: 15 })
|
|
12085
|
+
}
|
|
12086
|
+
)
|
|
12087
|
+
] })
|
|
12088
|
+
] }),
|
|
12089
|
+
customizeDurations && !isLast && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "se-segmentBetween", children: [
|
|
12090
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "se-segmentLabel", children: [
|
|
12091
|
+
index + 1,
|
|
12092
|
+
" \u2192 ",
|
|
12093
|
+
index + 2,
|
|
12094
|
+
!waypoint.segmentDuration && " (default)"
|
|
12095
|
+
] }),
|
|
12096
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "se-segmentControls", children: [
|
|
12097
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "se-durationLabel", children: "Duration:" }),
|
|
12098
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
12099
|
+
"input",
|
|
12100
|
+
{
|
|
12101
|
+
type: "range",
|
|
12102
|
+
className: "se-segmentSlider",
|
|
12103
|
+
min: 1,
|
|
12104
|
+
max: 20,
|
|
12105
|
+
step: 0.5,
|
|
12106
|
+
value: dur,
|
|
12107
|
+
onChange: (e) => {
|
|
12108
|
+
const val = parseFloat(e.target.value);
|
|
12109
|
+
updateWaypoint(index, { segmentDuration: val });
|
|
12110
|
+
}
|
|
12111
|
+
}
|
|
12112
|
+
),
|
|
12113
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { className: "se-segmentValue", children: [
|
|
12114
|
+
dur.toFixed(1),
|
|
12115
|
+
"s"
|
|
12116
|
+
] }),
|
|
12117
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "se-presetButtons", children: [
|
|
12118
|
+
[2, 4, 6, 8].map((preset) => /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
12119
|
+
"button",
|
|
12120
|
+
{
|
|
12121
|
+
className: "se-presetButton",
|
|
12122
|
+
onClick: () => updateWaypoint(index, { segmentDuration: preset }),
|
|
12123
|
+
children: [
|
|
12124
|
+
preset,
|
|
12125
|
+
"s"
|
|
12126
|
+
]
|
|
12127
|
+
},
|
|
12128
|
+
`preset-${index}-${preset}`
|
|
12129
|
+
)),
|
|
12130
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
12131
|
+
"button",
|
|
12132
|
+
{
|
|
12133
|
+
className: "se-presetButton se-presetButton--default",
|
|
12134
|
+
onClick: () => {
|
|
12135
|
+
const newWaypoints = waypoints.map((wp, i) => {
|
|
12136
|
+
if (i === index) {
|
|
12137
|
+
const { segmentDuration, ...rest } = wp;
|
|
12138
|
+
return rest;
|
|
12139
|
+
}
|
|
12140
|
+
return wp;
|
|
12141
|
+
});
|
|
12142
|
+
const newDuration = calculateTotalDuration(newWaypoints, cinematic.loop);
|
|
12143
|
+
update({
|
|
12144
|
+
waypoints: newWaypoints,
|
|
12145
|
+
duration: newDuration
|
|
12146
|
+
});
|
|
12147
|
+
},
|
|
12148
|
+
children: "Reset"
|
|
12149
|
+
}
|
|
12150
|
+
)
|
|
12151
|
+
] })
|
|
12152
|
+
] })
|
|
12153
|
+
] })
|
|
12154
|
+
] }, `waypoint-wrapper-${index}`);
|
|
12155
|
+
}) }),
|
|
12156
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "se-totalDuration", children: [
|
|
12157
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "se-totalLabel", children: "Total duration:" }),
|
|
12158
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "se-totalValue", children: hasEnoughWaypoints ? (cinematic.duration ?? calculateTotalDuration(waypoints, cinematic.loop)).toFixed(1) + "s" : "-" }),
|
|
12159
|
+
!hasEnoughWaypoints && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "se-totalHint", children: "(need at least 2 waypoints)" })
|
|
12160
|
+
] })
|
|
12161
|
+
] }),
|
|
11790
12162
|
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
11791
12163
|
ToggleRow,
|
|
11792
12164
|
{
|
|
@@ -11800,7 +12172,11 @@ function CinematicTab({
|
|
|
11800
12172
|
{
|
|
11801
12173
|
label: "Loop",
|
|
11802
12174
|
checked: cinematic.loop,
|
|
11803
|
-
onChange: (loop) =>
|
|
12175
|
+
onChange: (loop) => {
|
|
12176
|
+
const newLoop = loop;
|
|
12177
|
+
const newDuration = calculateTotalDuration(waypoints, newLoop);
|
|
12178
|
+
update({ loop: newLoop, duration: newDuration });
|
|
12179
|
+
}
|
|
11804
12180
|
}
|
|
11805
12181
|
),
|
|
11806
12182
|
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
@@ -11811,11 +12187,11 @@ function CinematicTab({
|
|
|
11811
12187
|
onChange: (autoPlay) => update({ autoPlay })
|
|
11812
12188
|
}
|
|
11813
12189
|
),
|
|
11814
|
-
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
12190
|
+
!customizeDurations && hasEnoughWaypoints && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
11815
12191
|
SliderRow,
|
|
11816
12192
|
{
|
|
11817
12193
|
label: "Duration",
|
|
11818
|
-
value: cinematic.duration ??
|
|
12194
|
+
value: cinematic.duration ?? calculateTotalDuration(waypoints, cinematic.loop),
|
|
11819
12195
|
min: 4,
|
|
11820
12196
|
max: 120,
|
|
11821
12197
|
step: 1,
|
|
@@ -11827,7 +12203,7 @@ function CinematicTab({
|
|
|
11827
12203
|
}
|
|
11828
12204
|
|
|
11829
12205
|
// src/styles/SceneSettingsTab.css
|
|
11830
|
-
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');
|
|
12206
|
+
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');
|
|
11831
12207
|
|
|
11832
12208
|
// src/components/BindingBuilder/SceneSettings/SceneSettingsTab.tsx
|
|
11833
12209
|
var import_lucide_react11 = require("lucide-react");
|
|
@@ -11866,13 +12242,13 @@ function SceneSettingsTab({
|
|
|
11866
12242
|
animationClipDetails = [],
|
|
11867
12243
|
animationPlayback = FALLBACK_ANIMATION_PLAYBACK
|
|
11868
12244
|
}) {
|
|
11869
|
-
const [internalActiveSubTab, setInternalActiveSubTab] = (0,
|
|
12245
|
+
const [internalActiveSubTab, setInternalActiveSubTab] = (0, import_react33.useState)("General");
|
|
11870
12246
|
const activeSubTab = controlledActiveSubTab ?? internalActiveSubTab;
|
|
11871
12247
|
const resolvedAnimationPlayback = animationPlayback ?? FALLBACK_ANIMATION_PLAYBACK;
|
|
11872
12248
|
const { tier } = useBindingBuilderContext();
|
|
11873
12249
|
const canUseStarterFeatures = hasTier(tier, "starter");
|
|
11874
12250
|
const canUseGrowthFeatures = hasTier(tier, "growth");
|
|
11875
|
-
(0,
|
|
12251
|
+
(0, import_react33.useEffect)(() => {
|
|
11876
12252
|
if (controlledActiveSubTab !== void 0) {
|
|
11877
12253
|
setInternalActiveSubTab(controlledActiveSubTab);
|
|
11878
12254
|
}
|
|
@@ -12052,7 +12428,7 @@ function EnableToggle2({
|
|
|
12052
12428
|
}
|
|
12053
12429
|
|
|
12054
12430
|
// src/components/BindingBuilder/components/MaterialSection.tsx
|
|
12055
|
-
var
|
|
12431
|
+
var import_react35 = require("react");
|
|
12056
12432
|
|
|
12057
12433
|
// src/components/ModelViewer/types/objectBinding.ts
|
|
12058
12434
|
var MATERIAL_SIDES = ["front", "back", "double"];
|
|
@@ -12099,7 +12475,7 @@ function MaterialPanelGroup({
|
|
|
12099
12475
|
}
|
|
12100
12476
|
|
|
12101
12477
|
// src/components/BindingBuilder/components/MaterialFields.tsx
|
|
12102
|
-
var
|
|
12478
|
+
var import_react34 = require("react");
|
|
12103
12479
|
var import_react_colorful4 = require("react-colorful");
|
|
12104
12480
|
var import_lucide_react13 = require("lucide-react");
|
|
12105
12481
|
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
@@ -12109,7 +12485,7 @@ function MaterialTexturePathField({
|
|
|
12109
12485
|
placeholder,
|
|
12110
12486
|
onChange
|
|
12111
12487
|
}) {
|
|
12112
|
-
const inputRef = (0,
|
|
12488
|
+
const inputRef = (0, import_react34.useRef)(null);
|
|
12113
12489
|
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "bb-field bb-field--full", children: [
|
|
12114
12490
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("label", { className: "bb-label", children: label }),
|
|
12115
12491
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "bb-fileField", children: [
|
|
@@ -12166,9 +12542,9 @@ function MaterialColorField({
|
|
|
12166
12542
|
fallback = "#ffffff",
|
|
12167
12543
|
onChange
|
|
12168
12544
|
}) {
|
|
12169
|
-
const [open, setOpen] = (0,
|
|
12545
|
+
const [open, setOpen] = (0, import_react34.useState)(false);
|
|
12170
12546
|
const color = value || fallback;
|
|
12171
|
-
const anchorRef = (0,
|
|
12547
|
+
const anchorRef = (0, import_react34.useRef)(null);
|
|
12172
12548
|
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "bb-field", children: [
|
|
12173
12549
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "bb-label", children: label }),
|
|
12174
12550
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "se-fieldInline", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { ref: anchorRef, style: { display: "inline-flex" }, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
@@ -12320,8 +12696,8 @@ function MaterialSection({
|
|
|
12320
12696
|
tier: "starter",
|
|
12321
12697
|
locked: !canUseStarterFeatures
|
|
12322
12698
|
};
|
|
12323
|
-
const [roughnessMode, setRoughnessMode] = (0,
|
|
12324
|
-
const [anisotropyMode, setAnisotropyMode] = (0,
|
|
12699
|
+
const [roughnessMode, setRoughnessMode] = (0, import_react35.useState)("roughness");
|
|
12700
|
+
const [anisotropyMode, setAnisotropyMode] = (0, import_react35.useState)(
|
|
12325
12701
|
"factor"
|
|
12326
12702
|
);
|
|
12327
12703
|
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
|
|
@@ -13360,7 +13736,7 @@ function MaterialPresetGallery({
|
|
|
13360
13736
|
}
|
|
13361
13737
|
|
|
13362
13738
|
// src/components/BindingBuilder/components/ToolbarExportMenu.tsx
|
|
13363
|
-
var
|
|
13739
|
+
var import_react36 = require("react");
|
|
13364
13740
|
var import_lucide_react14 = require("lucide-react");
|
|
13365
13741
|
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
13366
13742
|
function ToolbarExportMenu({
|
|
@@ -13369,9 +13745,9 @@ function ToolbarExportMenu({
|
|
|
13369
13745
|
onExportJson,
|
|
13370
13746
|
onExportTs
|
|
13371
13747
|
}) {
|
|
13372
|
-
const [open, setOpen] = (0,
|
|
13373
|
-
const wrapperRef = (0,
|
|
13374
|
-
(0,
|
|
13748
|
+
const [open, setOpen] = (0, import_react36.useState)(false);
|
|
13749
|
+
const wrapperRef = (0, import_react36.useRef)(null);
|
|
13750
|
+
(0, import_react36.useEffect)(() => {
|
|
13375
13751
|
if (!open) return;
|
|
13376
13752
|
const close = (event) => {
|
|
13377
13753
|
if (!wrapperRef.current?.contains(event.target)) {
|
|
@@ -13808,7 +14184,7 @@ function CameraPositionPanel({
|
|
|
13808
14184
|
}
|
|
13809
14185
|
|
|
13810
14186
|
// src/components/BindingBuilder/components/SceneNodesPanel.tsx
|
|
13811
|
-
var
|
|
14187
|
+
var import_react37 = require("react");
|
|
13812
14188
|
|
|
13813
14189
|
// src/components/BindingBuilder/utils/bindingFactory.ts
|
|
13814
14190
|
function slugify(value) {
|
|
@@ -13897,9 +14273,9 @@ function SceneNodesPanel({
|
|
|
13897
14273
|
isOpen = true,
|
|
13898
14274
|
onToggle
|
|
13899
14275
|
}) {
|
|
13900
|
-
const [search, setSearch] = (0,
|
|
13901
|
-
const nodeKeys = (0,
|
|
13902
|
-
const filteredNodeKeys = (0,
|
|
14276
|
+
const [search, setSearch] = (0, import_react37.useState)("");
|
|
14277
|
+
const nodeKeys = (0, import_react37.useMemo)(() => Object.keys(bindings), [bindings]);
|
|
14278
|
+
const filteredNodeKeys = (0, import_react37.useMemo)(() => {
|
|
13903
14279
|
const query = search.trim().toLowerCase();
|
|
13904
14280
|
if (!query) return nodeKeys;
|
|
13905
14281
|
return nodeKeys.filter((nodeKey) => {
|
|
@@ -14042,7 +14418,7 @@ function mergeAnimationConfig(sceneConfig, animationDetails) {
|
|
|
14042
14418
|
}
|
|
14043
14419
|
|
|
14044
14420
|
// src/components/BindingBuilder/hooks/useUndoableState.ts
|
|
14045
|
-
var
|
|
14421
|
+
var import_react38 = require("react");
|
|
14046
14422
|
var MAX_HISTORY = 100;
|
|
14047
14423
|
function historyReducer(state, action) {
|
|
14048
14424
|
switch (action.type) {
|
|
@@ -14081,13 +14457,13 @@ function historyReducer(state, action) {
|
|
|
14081
14457
|
}
|
|
14082
14458
|
}
|
|
14083
14459
|
function useUndoableState(initial, coalesceWindowMs = 600) {
|
|
14084
|
-
const [state, dispatch] = (0,
|
|
14460
|
+
const [state, dispatch] = (0, import_react38.useReducer)(historyReducer, {
|
|
14085
14461
|
past: [],
|
|
14086
14462
|
present: initial,
|
|
14087
14463
|
future: []
|
|
14088
14464
|
});
|
|
14089
|
-
const lastSetAt = (0,
|
|
14090
|
-
const setValue = (0,
|
|
14465
|
+
const lastSetAt = (0, import_react38.useRef)(0);
|
|
14466
|
+
const setValue = (0, import_react38.useCallback)(
|
|
14091
14467
|
(updater) => {
|
|
14092
14468
|
const now = Date.now();
|
|
14093
14469
|
const coalesce = now - lastSetAt.current < coalesceWindowMs;
|
|
@@ -14096,15 +14472,15 @@ function useUndoableState(initial, coalesceWindowMs = 600) {
|
|
|
14096
14472
|
},
|
|
14097
14473
|
[coalesceWindowMs]
|
|
14098
14474
|
);
|
|
14099
|
-
const undo = (0,
|
|
14475
|
+
const undo = (0, import_react38.useCallback)(() => {
|
|
14100
14476
|
lastSetAt.current = 0;
|
|
14101
14477
|
dispatch({ type: "undo" });
|
|
14102
14478
|
}, []);
|
|
14103
|
-
const redo = (0,
|
|
14479
|
+
const redo = (0, import_react38.useCallback)(() => {
|
|
14104
14480
|
lastSetAt.current = 0;
|
|
14105
14481
|
dispatch({ type: "redo" });
|
|
14106
14482
|
}, []);
|
|
14107
|
-
const reset = (0,
|
|
14483
|
+
const reset = (0, import_react38.useCallback)((value) => {
|
|
14108
14484
|
lastSetAt.current = 0;
|
|
14109
14485
|
dispatch({ type: "reset", value });
|
|
14110
14486
|
}, []);
|
|
@@ -14350,30 +14726,30 @@ function mergeSceneConfigSection(current, incoming) {
|
|
|
14350
14726
|
return next;
|
|
14351
14727
|
}
|
|
14352
14728
|
function BindingBuilder({ licenseKey }) {
|
|
14353
|
-
const modelInputRef = (0,
|
|
14354
|
-
const bindingsInputRef = (0,
|
|
14355
|
-
const sceneConfigInputRef = (0,
|
|
14356
|
-
const [modelUrl, setModelUrl] = (0,
|
|
14357
|
-
const [modelName, setModelName] = (0,
|
|
14358
|
-
const [selectedItem, setSelectedItem] = (0,
|
|
14729
|
+
const modelInputRef = (0, import_react39.useRef)(null);
|
|
14730
|
+
const bindingsInputRef = (0, import_react39.useRef)(null);
|
|
14731
|
+
const sceneConfigInputRef = (0, import_react39.useRef)(null);
|
|
14732
|
+
const [modelUrl, setModelUrl] = (0, import_react39.useState)(null);
|
|
14733
|
+
const [modelName, setModelName] = (0, import_react39.useState)("");
|
|
14734
|
+
const [selectedItem, setSelectedItem] = (0, import_react39.useState)(null);
|
|
14359
14735
|
const bindingsHistory = useUndoableState({});
|
|
14360
14736
|
const bindings = bindingsHistory.value;
|
|
14361
14737
|
const setBindings = bindingsHistory.setValue;
|
|
14362
|
-
const [loading, setLoading] = (0,
|
|
14363
|
-
const [isExporting, setIsExporting] = (0,
|
|
14364
|
-
const [statusMessage, setStatusMessage] = (0,
|
|
14738
|
+
const [loading, setLoading] = (0, import_react39.useState)(false);
|
|
14739
|
+
const [isExporting, setIsExporting] = (0, import_react39.useState)(false);
|
|
14740
|
+
const [statusMessage, setStatusMessage] = (0, import_react39.useState)(
|
|
14365
14741
|
"Load the demo model or upload a GLB/GLTF model to generate bindings."
|
|
14366
14742
|
);
|
|
14367
|
-
const [statusTone, setStatusTone] = (0,
|
|
14368
|
-
const [statusBannerVisible, setStatusBannerVisible] = (0,
|
|
14369
|
-
const [metricsDraft, setMetricsDraft] = (0,
|
|
14370
|
-
const [metadataDraft, setMetadataDraft] = (0,
|
|
14371
|
-
const [metricsError, setMetricsError] = (0,
|
|
14372
|
-
const [metadataError, setMetadataError] = (0,
|
|
14373
|
-
const [isPreviewDragActive, setIsPreviewDragActive] = (0,
|
|
14374
|
-
const [isEditorCollapsed, setIsEditorCollapsed] = (0,
|
|
14375
|
-
const [isNodesCollapsed, setIsNodesCollapsed] = (0,
|
|
14376
|
-
const [openObjectSections, setOpenObjectSections] = (0,
|
|
14743
|
+
const [statusTone, setStatusTone] = (0, import_react39.useState)("default");
|
|
14744
|
+
const [statusBannerVisible, setStatusBannerVisible] = (0, import_react39.useState)(false);
|
|
14745
|
+
const [metricsDraft, setMetricsDraft] = (0, import_react39.useState)("{}");
|
|
14746
|
+
const [metadataDraft, setMetadataDraft] = (0, import_react39.useState)("{}");
|
|
14747
|
+
const [metricsError, setMetricsError] = (0, import_react39.useState)(null);
|
|
14748
|
+
const [metadataError, setMetadataError] = (0, import_react39.useState)(null);
|
|
14749
|
+
const [isPreviewDragActive, setIsPreviewDragActive] = (0, import_react39.useState)(false);
|
|
14750
|
+
const [isEditorCollapsed, setIsEditorCollapsed] = (0, import_react39.useState)(false);
|
|
14751
|
+
const [isNodesCollapsed, setIsNodesCollapsed] = (0, import_react39.useState)(false);
|
|
14752
|
+
const [openObjectSections, setOpenObjectSections] = (0, import_react39.useState)({
|
|
14377
14753
|
identity: true,
|
|
14378
14754
|
basics: true,
|
|
14379
14755
|
material: true,
|
|
@@ -14382,7 +14758,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14382
14758
|
metrics: false,
|
|
14383
14759
|
metadata: false
|
|
14384
14760
|
});
|
|
14385
|
-
const [openMaterialPanels, setOpenMaterialPanels] = (0,
|
|
14761
|
+
const [openMaterialPanels, setOpenMaterialPanels] = (0, import_react39.useState)({
|
|
14386
14762
|
pbr: true,
|
|
14387
14763
|
roughness: false,
|
|
14388
14764
|
emission: false,
|
|
@@ -14398,27 +14774,27 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14398
14774
|
});
|
|
14399
14775
|
const toggleObjectSection = (key) => setOpenObjectSections((prev) => ({ ...prev, [key]: !prev[key] }));
|
|
14400
14776
|
const toggleMaterialPanel = (key) => setOpenMaterialPanels((prev) => ({ ...prev, [key]: !prev[key] }));
|
|
14401
|
-
const [activeEditorTab, setActiveEditorTab] = (0,
|
|
14777
|
+
const [activeEditorTab, setActiveEditorTab] = (0, import_react39.useState)(
|
|
14402
14778
|
"object"
|
|
14403
14779
|
);
|
|
14404
|
-
const [activeSubTab, setActiveSubTab] = (0,
|
|
14405
|
-
const [pbrWorkflow, setPbrWorkflow] = (0,
|
|
14406
|
-
const [materialPanelEnabled, setMaterialPanelEnabled] = (0,
|
|
14407
|
-
const materialPanelItemRef = (0,
|
|
14408
|
-
const [zoomOnSelected, setZoomOnSelected] = (0,
|
|
14409
|
-
const [highlightOnHover, setHighlightOnHover] = (0,
|
|
14410
|
-
const [resetDialogOpen, setResetDialogOpen] = (0,
|
|
14411
|
-
const [resetTarget, setResetTarget] = (0,
|
|
14780
|
+
const [activeSubTab, setActiveSubTab] = (0, import_react39.useState)("General");
|
|
14781
|
+
const [pbrWorkflow, setPbrWorkflow] = (0, import_react39.useState)("metalness");
|
|
14782
|
+
const [materialPanelEnabled, setMaterialPanelEnabled] = (0, import_react39.useState)(createMaterialPanelEnabledState());
|
|
14783
|
+
const materialPanelItemRef = (0, import_react39.useRef)(null);
|
|
14784
|
+
const [zoomOnSelected, setZoomOnSelected] = (0, import_react39.useState)(false);
|
|
14785
|
+
const [highlightOnHover, setHighlightOnHover] = (0, import_react39.useState)(true);
|
|
14786
|
+
const [resetDialogOpen, setResetDialogOpen] = (0, import_react39.useState)(false);
|
|
14787
|
+
const [resetTarget, setResetTarget] = (0, import_react39.useState)(
|
|
14412
14788
|
null
|
|
14413
14789
|
);
|
|
14414
14790
|
const sceneConfigHistory = useUndoableState(DEFAULT_SCENE_CONFIG);
|
|
14415
14791
|
const sceneConfig = sceneConfigHistory.value;
|
|
14416
14792
|
const setSceneConfig = sceneConfigHistory.setValue;
|
|
14417
|
-
const [activeAnnotation, setActiveAnnotation] = (0,
|
|
14418
|
-
const [panelHoveredBinding, setPanelHoveredBinding] = (0,
|
|
14793
|
+
const [activeAnnotation, setActiveAnnotation] = (0, import_react39.useState)(null);
|
|
14794
|
+
const [panelHoveredBinding, setPanelHoveredBinding] = (0, import_react39.useState)(null);
|
|
14419
14795
|
const license = useLicense(licenseKey);
|
|
14420
14796
|
const tier = normalizeTier(license.tier);
|
|
14421
|
-
const bindingBuilderContextValue = (0,
|
|
14797
|
+
const bindingBuilderContextValue = (0, import_react39.useMemo)(
|
|
14422
14798
|
() => ({
|
|
14423
14799
|
isInsideEditor: true,
|
|
14424
14800
|
activeTab: activeEditorTab,
|
|
@@ -14427,11 +14803,11 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14427
14803
|
}),
|
|
14428
14804
|
[activeEditorTab, activeSubTab, tier]
|
|
14429
14805
|
);
|
|
14430
|
-
const uploadedModelRef = (0,
|
|
14431
|
-
const previewDragDepthRef = (0,
|
|
14432
|
-
const loadRequestRef = (0,
|
|
14433
|
-
const pendingLoadRef = (0,
|
|
14434
|
-
const loadedSceneRef = (0,
|
|
14806
|
+
const uploadedModelRef = (0, import_react39.useRef)(null);
|
|
14807
|
+
const previewDragDepthRef = (0, import_react39.useRef)(0);
|
|
14808
|
+
const loadRequestRef = (0, import_react39.useRef)(0);
|
|
14809
|
+
const pendingLoadRef = (0, import_react39.useRef)(null);
|
|
14810
|
+
const loadedSceneRef = (0, import_react39.useRef)(null);
|
|
14435
14811
|
const {
|
|
14436
14812
|
cameraState,
|
|
14437
14813
|
resetView,
|
|
@@ -14441,14 +14817,16 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14441
14817
|
handleCameraChange,
|
|
14442
14818
|
handleViewerReady
|
|
14443
14819
|
} = useViewerCamera();
|
|
14444
|
-
const handlePreviewWaypoint = (0,
|
|
14820
|
+
const handlePreviewWaypoint = (0, import_react39.useCallback)(
|
|
14445
14821
|
(waypoint) => {
|
|
14446
14822
|
setCameraState(
|
|
14447
14823
|
{
|
|
14448
14824
|
position: waypoint.position,
|
|
14449
14825
|
target: waypoint.target,
|
|
14450
14826
|
fov: cameraState?.fov ?? null,
|
|
14451
|
-
zoom: cameraState?.zoom ?? 1
|
|
14827
|
+
zoom: cameraState?.zoom ?? 1,
|
|
14828
|
+
azimuthAngle: waypoint.azimuthAngle,
|
|
14829
|
+
polarAngle: waypoint.polarAngle
|
|
14452
14830
|
},
|
|
14453
14831
|
true
|
|
14454
14832
|
);
|
|
@@ -14463,7 +14841,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14463
14841
|
position: formatVector3(cameraState.position),
|
|
14464
14842
|
target: formatVector3(cameraState.target)
|
|
14465
14843
|
} : null;
|
|
14466
|
-
const handleAnnotationsChange = (0,
|
|
14844
|
+
const handleAnnotationsChange = (0, import_react39.useCallback)(
|
|
14467
14845
|
(newAnnotations) => {
|
|
14468
14846
|
setSceneConfig((prev) => {
|
|
14469
14847
|
const currentIds = prev.annotations.map((a) => a.id).join(",");
|
|
@@ -14479,7 +14857,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14479
14857
|
},
|
|
14480
14858
|
[]
|
|
14481
14859
|
);
|
|
14482
|
-
const handleActiveAnnotationChange = (0,
|
|
14860
|
+
const handleActiveAnnotationChange = (0, import_react39.useCallback)(
|
|
14483
14861
|
(annotation) => {
|
|
14484
14862
|
setActiveAnnotation(
|
|
14485
14863
|
annotation ? {
|
|
@@ -14489,7 +14867,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14489
14867
|
},
|
|
14490
14868
|
[]
|
|
14491
14869
|
);
|
|
14492
|
-
(0,
|
|
14870
|
+
(0, import_react39.useEffect)(() => {
|
|
14493
14871
|
if (!selectedBinding) {
|
|
14494
14872
|
setMetricsDraft("{}");
|
|
14495
14873
|
setMetadataDraft("{}");
|
|
@@ -14502,7 +14880,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14502
14880
|
setMetricsError(null);
|
|
14503
14881
|
setMetadataError(null);
|
|
14504
14882
|
}, [selectedBinding]);
|
|
14505
|
-
(0,
|
|
14883
|
+
(0, import_react39.useEffect)(() => {
|
|
14506
14884
|
const derived = createMaterialPanelEnabledState(selectedMaterial);
|
|
14507
14885
|
if (materialPanelItemRef.current !== selectedItem) {
|
|
14508
14886
|
materialPanelItemRef.current = selectedItem;
|
|
@@ -14521,12 +14899,12 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14521
14899
|
return changed ? next : prev;
|
|
14522
14900
|
});
|
|
14523
14901
|
}, [selectedItem, selectedMaterial]);
|
|
14524
|
-
(0,
|
|
14902
|
+
(0, import_react39.useEffect)(() => {
|
|
14525
14903
|
return () => {
|
|
14526
14904
|
uploadedModelRef.current?.cleanup();
|
|
14527
14905
|
};
|
|
14528
14906
|
}, []);
|
|
14529
|
-
(0,
|
|
14907
|
+
(0, import_react39.useEffect)(() => {
|
|
14530
14908
|
const handleKeyDown = (event) => {
|
|
14531
14909
|
if (event.key.toLowerCase() !== "z" || !(event.metaKey || event.ctrlKey)) {
|
|
14532
14910
|
return;
|
|
@@ -14547,7 +14925,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14547
14925
|
window.addEventListener("keydown", handleKeyDown);
|
|
14548
14926
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
14549
14927
|
}, [activeEditorTab, bindingsHistory, sceneConfigHistory]);
|
|
14550
|
-
(0,
|
|
14928
|
+
(0, import_react39.useEffect)(() => {
|
|
14551
14929
|
if (loading || statusTone === "default" || statusTone === "success") return;
|
|
14552
14930
|
setStatusBannerVisible(true);
|
|
14553
14931
|
const timeout = window.setTimeout(
|
|
@@ -14556,8 +14934,8 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14556
14934
|
);
|
|
14557
14935
|
return () => window.clearTimeout(timeout);
|
|
14558
14936
|
}, [statusMessage, statusTone, loading]);
|
|
14559
|
-
const nodeKeys = (0,
|
|
14560
|
-
const idError = (0,
|
|
14937
|
+
const nodeKeys = (0, import_react39.useMemo)(() => Object.keys(bindings), [bindings]);
|
|
14938
|
+
const idError = (0, import_react39.useMemo)(() => {
|
|
14561
14939
|
if (!selectedItem || !selectedBinding) return null;
|
|
14562
14940
|
const trimmed = selectedBinding.id.trim();
|
|
14563
14941
|
if (!trimmed) return "ID is required.";
|
|
@@ -14600,7 +14978,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14600
14978
|
});
|
|
14601
14979
|
}
|
|
14602
14980
|
};
|
|
14603
|
-
const applyBindingsFromScene = (0,
|
|
14981
|
+
const applyBindingsFromScene = (0, import_react39.useCallback)(
|
|
14604
14982
|
(scene, nextModelName) => {
|
|
14605
14983
|
const nodeNames = collectNodeNames(scene);
|
|
14606
14984
|
bindingsHistory.reset(buildBindingsFromNodes(nodeNames));
|
|
@@ -14639,7 +15017,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14639
15017
|
bindingsHistory.reset({});
|
|
14640
15018
|
setSelectedItem(null);
|
|
14641
15019
|
};
|
|
14642
|
-
const handleViewerModelLoaded = (0,
|
|
15020
|
+
const handleViewerModelLoaded = (0, import_react39.useCallback)(
|
|
14643
15021
|
(scene) => {
|
|
14644
15022
|
const pending = pendingLoadRef.current;
|
|
14645
15023
|
if (!pending || pending.requestId !== loadRequestRef.current) return;
|
|
@@ -14649,7 +15027,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14649
15027
|
},
|
|
14650
15028
|
[applyBindingsFromScene]
|
|
14651
15029
|
);
|
|
14652
|
-
const handleViewerLoadError = (0,
|
|
15030
|
+
const handleViewerLoadError = (0, import_react39.useCallback)((error) => {
|
|
14653
15031
|
const pending = pendingLoadRef.current;
|
|
14654
15032
|
if (!pending || pending.requestId !== loadRequestRef.current) return;
|
|
14655
15033
|
pendingLoadRef.current = null;
|
|
@@ -14660,7 +15038,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14660
15038
|
setStatusTone("error");
|
|
14661
15039
|
setLoading(false);
|
|
14662
15040
|
}, []);
|
|
14663
|
-
const handleAnimationsReady = (0,
|
|
15041
|
+
const handleAnimationsReady = (0, import_react39.useCallback)(
|
|
14664
15042
|
(controls) => {
|
|
14665
15043
|
viewerAnimations.handleAnimationsReady(controls);
|
|
14666
15044
|
const details = controls.clipDetails ?? controls.clips.map((sourceName) => ({ sourceName, duration: 0 }));
|
|
@@ -14961,7 +15339,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14961
15339
|
}));
|
|
14962
15340
|
setError(null);
|
|
14963
15341
|
};
|
|
14964
|
-
const handleViewerSelect = (0,
|
|
15342
|
+
const handleViewerSelect = (0, import_react39.useCallback)(
|
|
14965
15343
|
(binding) => {
|
|
14966
15344
|
if (!binding) {
|
|
14967
15345
|
setSelectedItem(null);
|
|
@@ -14974,7 +15352,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14974
15352
|
},
|
|
14975
15353
|
[bindings]
|
|
14976
15354
|
);
|
|
14977
|
-
const handleSceneNodeSelect = (0,
|
|
15355
|
+
const handleSceneNodeSelect = (0, import_react39.useCallback)(
|
|
14978
15356
|
async (nodeKey) => {
|
|
14979
15357
|
setSelectedItem(nodeKey);
|
|
14980
15358
|
setPanelHoveredBinding(null);
|
|
@@ -14982,7 +15360,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14982
15360
|
},
|
|
14983
15361
|
[focusObject]
|
|
14984
15362
|
);
|
|
14985
|
-
const saveCameraPositionForSelectedObject = (0,
|
|
15363
|
+
const saveCameraPositionForSelectedObject = (0, import_react39.useCallback)(() => {
|
|
14986
15364
|
if (!selectedItem || !cameraState) {
|
|
14987
15365
|
return;
|
|
14988
15366
|
}
|
|
@@ -14997,7 +15375,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
14997
15375
|
cameraState: currentCameraState
|
|
14998
15376
|
}));
|
|
14999
15377
|
}, [selectedItem, cameraState]);
|
|
15000
|
-
const goToSavedCameraPosition = (0,
|
|
15378
|
+
const goToSavedCameraPosition = (0, import_react39.useCallback)(() => {
|
|
15001
15379
|
if (!savedCameraPosition?.position || !savedCameraPosition?.target) return;
|
|
15002
15380
|
setCameraState(
|
|
15003
15381
|
{
|
|
@@ -15009,28 +15387,28 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15009
15387
|
true
|
|
15010
15388
|
);
|
|
15011
15389
|
}, [savedCameraPosition, setCameraState, cameraState]);
|
|
15012
|
-
const clearSavedCameraPosition = (0,
|
|
15390
|
+
const clearSavedCameraPosition = (0, import_react39.useCallback)(() => {
|
|
15013
15391
|
if (!selectedItem) return;
|
|
15014
15392
|
updateBinding(selectedItem, (binding) => ({
|
|
15015
15393
|
...binding,
|
|
15016
15394
|
cameraState: void 0
|
|
15017
15395
|
}));
|
|
15018
15396
|
}, [selectedItem]);
|
|
15019
|
-
const handleResetView = (0,
|
|
15397
|
+
const handleResetView = (0, import_react39.useCallback)(async () => {
|
|
15020
15398
|
await resetView();
|
|
15021
15399
|
}, [resetView]);
|
|
15022
|
-
const handleFitScene = (0,
|
|
15400
|
+
const handleFitScene = (0, import_react39.useCallback)(async () => {
|
|
15023
15401
|
await fitScene();
|
|
15024
15402
|
}, [fitScene]);
|
|
15025
|
-
const openResetDialog = (0,
|
|
15403
|
+
const openResetDialog = (0, import_react39.useCallback)((target) => {
|
|
15026
15404
|
setResetTarget(target);
|
|
15027
15405
|
setResetDialogOpen(true);
|
|
15028
15406
|
}, []);
|
|
15029
|
-
const closeResetDialog = (0,
|
|
15407
|
+
const closeResetDialog = (0, import_react39.useCallback)(() => {
|
|
15030
15408
|
setResetDialogOpen(false);
|
|
15031
15409
|
setResetTarget(null);
|
|
15032
15410
|
}, []);
|
|
15033
|
-
const executeReset = (0,
|
|
15411
|
+
const executeReset = (0, import_react39.useCallback)(() => {
|
|
15034
15412
|
if (resetTarget === "bindings") {
|
|
15035
15413
|
if (!nodeKeys.length) {
|
|
15036
15414
|
closeResetDialog();
|
|
@@ -15161,7 +15539,9 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15161
15539
|
// the whole settings panel for no reason.
|
|
15162
15540
|
activeSubTab === "Cinematic" && cameraState ? {
|
|
15163
15541
|
position: cameraState.position,
|
|
15164
|
-
target: cameraState.target
|
|
15542
|
+
target: cameraState.target,
|
|
15543
|
+
azimuthAngle: cameraState.azimuthAngle,
|
|
15544
|
+
polarAngle: cameraState.polarAngle
|
|
15165
15545
|
} : null
|
|
15166
15546
|
),
|
|
15167
15547
|
onPreviewWaypoint: handlePreviewWaypoint,
|
|
@@ -15586,7 +15966,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15586
15966
|
}
|
|
15587
15967
|
|
|
15588
15968
|
// src/components/SimpleModelViewer/SimpleModelViewer.tsx
|
|
15589
|
-
var
|
|
15969
|
+
var import_react40 = require("react");
|
|
15590
15970
|
var import_drei6 = require("@react-three/drei");
|
|
15591
15971
|
var import_fiber12 = require("@react-three/fiber");
|
|
15592
15972
|
var import_fiber13 = require("@react-three/fiber");
|
|
@@ -15626,7 +16006,7 @@ function applyClippingPlanes(scene, controls) {
|
|
|
15626
16006
|
camera.updateProjectionMatrix();
|
|
15627
16007
|
controls.maxDistance = size * 10;
|
|
15628
16008
|
}
|
|
15629
|
-
var ModelErrorBoundary2 = class extends
|
|
16009
|
+
var ModelErrorBoundary2 = class extends import_react40.Component {
|
|
15630
16010
|
constructor() {
|
|
15631
16011
|
super(...arguments);
|
|
15632
16012
|
this.state = { hasError: false };
|
|
@@ -15658,12 +16038,12 @@ function InnerModel({
|
|
|
15658
16038
|
meshopt: true
|
|
15659
16039
|
});
|
|
15660
16040
|
const invalidate = (0, import_fiber12.useThree)((state) => state.invalidate);
|
|
15661
|
-
const [hoveredKey, setHoveredKey] = (0,
|
|
15662
|
-
const scene = (0,
|
|
16041
|
+
const [hoveredKey, setHoveredKey] = (0, import_react40.useState)(null);
|
|
16042
|
+
const scene = (0, import_react40.useMemo)(
|
|
15663
16043
|
() => import_three_stdlib4.SkeletonUtils.clone(rawScene),
|
|
15664
16044
|
[rawScene]
|
|
15665
16045
|
);
|
|
15666
|
-
const meshMap = (0,
|
|
16046
|
+
const meshMap = (0, import_react40.useMemo)(() => {
|
|
15667
16047
|
const map = {};
|
|
15668
16048
|
scene.traverse((obj) => {
|
|
15669
16049
|
if (!isMeshNode2(obj)) return;
|
|
@@ -15672,7 +16052,7 @@ function InnerModel({
|
|
|
15672
16052
|
});
|
|
15673
16053
|
return map;
|
|
15674
16054
|
}, [scene]);
|
|
15675
|
-
const perKeyMaterials = (0,
|
|
16055
|
+
const perKeyMaterials = (0, import_react40.useMemo)(() => {
|
|
15676
16056
|
const map = {};
|
|
15677
16057
|
for (const [key, { ref }] of Object.entries(meshMap)) {
|
|
15678
16058
|
const original = Array.isArray(ref.material) ? ref.material[0] : ref.material;
|
|
@@ -15688,14 +16068,14 @@ function InnerModel({
|
|
|
15688
16068
|
}
|
|
15689
16069
|
return map;
|
|
15690
16070
|
}, [meshMap]);
|
|
15691
|
-
(0,
|
|
16071
|
+
(0, import_react40.useEffect)(
|
|
15692
16072
|
() => () => {
|
|
15693
16073
|
Object.values(perKeyMaterials).forEach((m) => m.dispose());
|
|
15694
16074
|
},
|
|
15695
16075
|
[perKeyMaterials]
|
|
15696
16076
|
);
|
|
15697
|
-
const notifiedUrl = (0,
|
|
15698
|
-
(0,
|
|
16077
|
+
const notifiedUrl = (0, import_react40.useRef)(null);
|
|
16078
|
+
(0, import_react40.useEffect)(() => {
|
|
15699
16079
|
if (notifiedUrl.current === url) return;
|
|
15700
16080
|
notifiedUrl.current = url;
|
|
15701
16081
|
scene.updateWorldMatrix(true, true);
|
|
@@ -15703,7 +16083,7 @@ function InnerModel({
|
|
|
15703
16083
|
onMeshesReady(meshMap);
|
|
15704
16084
|
onModelLoaded(scene);
|
|
15705
16085
|
}, [url, scene, meshMap, onMeshesReady, onModelLoaded]);
|
|
15706
|
-
(0,
|
|
16086
|
+
(0, import_react40.useEffect)(() => {
|
|
15707
16087
|
const controller = new AbortController();
|
|
15708
16088
|
void buildSceneBVH(scene, controller.signal);
|
|
15709
16089
|
return () => {
|
|
@@ -15711,26 +16091,26 @@ function InnerModel({
|
|
|
15711
16091
|
disposeSceneBVH(scene);
|
|
15712
16092
|
};
|
|
15713
16093
|
}, [scene]);
|
|
15714
|
-
const pointerRaycastEnabled = (0,
|
|
16094
|
+
const pointerRaycastEnabled = (0, import_react40.useMemo)(
|
|
15715
16095
|
() => !exceedsBvhBudget(scene),
|
|
15716
16096
|
[scene]
|
|
15717
16097
|
);
|
|
15718
|
-
(0,
|
|
16098
|
+
(0, import_react40.useEffect)(() => {
|
|
15719
16099
|
for (const [key, { ref }] of Object.entries(meshMap)) {
|
|
15720
16100
|
nodeRefsOut.current[key] = ref;
|
|
15721
16101
|
}
|
|
15722
16102
|
}, [meshMap, nodeRefsOut]);
|
|
15723
|
-
(0,
|
|
16103
|
+
(0, import_react40.useEffect)(() => {
|
|
15724
16104
|
if (!highlightOnHover && hoveredKey !== null) {
|
|
15725
16105
|
setHoveredKey(null);
|
|
15726
16106
|
}
|
|
15727
16107
|
}, [highlightOnHover, hoveredKey]);
|
|
15728
|
-
(0,
|
|
16108
|
+
(0, import_react40.useEffect)(() => {
|
|
15729
16109
|
return () => {
|
|
15730
16110
|
document.body.style.cursor = "default";
|
|
15731
16111
|
};
|
|
15732
16112
|
}, []);
|
|
15733
|
-
(0,
|
|
16113
|
+
(0, import_react40.useEffect)(() => {
|
|
15734
16114
|
for (const [key, { ref }] of Object.entries(meshMap)) {
|
|
15735
16115
|
ref.visible = !hiddenKeys.has(key);
|
|
15736
16116
|
const mat = perKeyMaterials[key];
|
|
@@ -15800,11 +16180,11 @@ function CameraFitter({
|
|
|
15800
16180
|
refitOnResize,
|
|
15801
16181
|
onInitialFitComplete
|
|
15802
16182
|
}) {
|
|
15803
|
-
const fitted = (0,
|
|
16183
|
+
const fitted = (0, import_react40.useRef)(false);
|
|
15804
16184
|
const invalidate = (0, import_fiber12.useThree)((state) => state.invalidate);
|
|
15805
16185
|
const size = (0, import_fiber12.useThree)((state) => state.size);
|
|
15806
|
-
const resizeTimerRef = (0,
|
|
15807
|
-
(0,
|
|
16186
|
+
const resizeTimerRef = (0, import_react40.useRef)(null);
|
|
16187
|
+
(0, import_react40.useEffect)(() => {
|
|
15808
16188
|
fitted.current = false;
|
|
15809
16189
|
invalidate();
|
|
15810
16190
|
}, [invalidate, scene]);
|
|
@@ -15824,7 +16204,7 @@ function CameraFitter({
|
|
|
15824
16204
|
}).catch(() => {
|
|
15825
16205
|
}).finally(onInitialFitComplete);
|
|
15826
16206
|
});
|
|
15827
|
-
(0,
|
|
16207
|
+
(0, import_react40.useEffect)(() => {
|
|
15828
16208
|
if (!refitOnResize || !fitted.current || !scene || !controlsRef.current) {
|
|
15829
16209
|
return;
|
|
15830
16210
|
}
|
|
@@ -15852,7 +16232,7 @@ function CameraFitter({
|
|
|
15852
16232
|
function ExposureController({ exposure }) {
|
|
15853
16233
|
const gl = (0, import_fiber12.useThree)((state) => state.gl);
|
|
15854
16234
|
const invalidate = (0, import_fiber12.useThree)((state) => state.invalidate);
|
|
15855
|
-
(0,
|
|
16235
|
+
(0, import_react40.useEffect)(() => {
|
|
15856
16236
|
gl.toneMappingExposure = exposure;
|
|
15857
16237
|
invalidate();
|
|
15858
16238
|
}, [gl, exposure, invalidate]);
|
|
@@ -15960,7 +16340,7 @@ function EnvironmentSection({
|
|
|
15960
16340
|
title,
|
|
15961
16341
|
children
|
|
15962
16342
|
}) {
|
|
15963
|
-
const [collapsed, setCollapsed] = (0,
|
|
16343
|
+
const [collapsed, setCollapsed] = (0, import_react40.useState)(false);
|
|
15964
16344
|
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "env-section", children: [
|
|
15965
16345
|
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
15966
16346
|
"button",
|
|
@@ -16125,8 +16505,8 @@ function SceneObjectsPanel2({
|
|
|
16125
16505
|
onHover,
|
|
16126
16506
|
onToggleVisibility
|
|
16127
16507
|
}) {
|
|
16128
|
-
const [search, setSearch] = (0,
|
|
16129
|
-
const [rowHovered, setRowHovered] = (0,
|
|
16508
|
+
const [search, setSearch] = (0, import_react40.useState)("");
|
|
16509
|
+
const [rowHovered, setRowHovered] = (0, import_react40.useState)(null);
|
|
16130
16510
|
const entries = Object.values(meshKeys).filter(
|
|
16131
16511
|
(m) => m.label.toLowerCase().includes(search.toLowerCase())
|
|
16132
16512
|
);
|
|
@@ -16250,35 +16630,35 @@ function SimpleModelViewer({
|
|
|
16250
16630
|
onModelLoaded,
|
|
16251
16631
|
onLoadError
|
|
16252
16632
|
}) {
|
|
16253
|
-
const cameraControlsRef = (0,
|
|
16254
|
-
const nodeRefsOut = (0,
|
|
16255
|
-
const fileInputRef = (0,
|
|
16256
|
-
const uploadedModelRef = (0,
|
|
16257
|
-
const dragDepthRef = (0,
|
|
16258
|
-
const [loadedScene, setLoadedScene] = (0,
|
|
16259
|
-
const [largeModelTriangles, setLargeModelTriangles] = (0,
|
|
16633
|
+
const cameraControlsRef = (0, import_react40.useRef)(null);
|
|
16634
|
+
const nodeRefsOut = (0, import_react40.useRef)({});
|
|
16635
|
+
const fileInputRef = (0, import_react40.useRef)(null);
|
|
16636
|
+
const uploadedModelRef = (0, import_react40.useRef)(null);
|
|
16637
|
+
const dragDepthRef = (0, import_react40.useRef)(0);
|
|
16638
|
+
const [loadedScene, setLoadedScene] = (0, import_react40.useState)(null);
|
|
16639
|
+
const [largeModelTriangles, setLargeModelTriangles] = (0, import_react40.useState)(
|
|
16260
16640
|
null
|
|
16261
16641
|
);
|
|
16262
|
-
const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = (0,
|
|
16263
|
-
const [meshKeys, setMeshKeys] = (0,
|
|
16264
|
-
const [hiddenKeys, setHiddenKeys] = (0,
|
|
16265
|
-
const [selectedKey, setSelectedKey] = (0,
|
|
16266
|
-
const [panelHoveredKey, setPanelHoveredKey] = (0,
|
|
16267
|
-
const [uploadedModelUrl, setUploadedModelUrl] = (0,
|
|
16268
|
-
const [uploadedModelName, setUploadedModelName] = (0,
|
|
16642
|
+
const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = (0, import_react40.useState)(false);
|
|
16643
|
+
const [meshKeys, setMeshKeys] = (0, import_react40.useState)({});
|
|
16644
|
+
const [hiddenKeys, setHiddenKeys] = (0, import_react40.useState)(/* @__PURE__ */ new Set());
|
|
16645
|
+
const [selectedKey, setSelectedKey] = (0, import_react40.useState)(null);
|
|
16646
|
+
const [panelHoveredKey, setPanelHoveredKey] = (0, import_react40.useState)(null);
|
|
16647
|
+
const [uploadedModelUrl, setUploadedModelUrl] = (0, import_react40.useState)(null);
|
|
16648
|
+
const [uploadedModelName, setUploadedModelName] = (0, import_react40.useState)(
|
|
16269
16649
|
null
|
|
16270
16650
|
);
|
|
16271
|
-
const [modelLoadFailed, setModelLoadFailed] = (0,
|
|
16272
|
-
const [isResolvingUpload, setIsResolvingUpload] = (0,
|
|
16273
|
-
const [initialFitComplete, setInitialFitComplete] = (0,
|
|
16274
|
-
const [isDragActive, setIsDragActive] = (0,
|
|
16275
|
-
const [isPanelOpen, setIsPanelOpen] = (0,
|
|
16651
|
+
const [modelLoadFailed, setModelLoadFailed] = (0, import_react40.useState)(false);
|
|
16652
|
+
const [isResolvingUpload, setIsResolvingUpload] = (0, import_react40.useState)(false);
|
|
16653
|
+
const [initialFitComplete, setInitialFitComplete] = (0, import_react40.useState)(false);
|
|
16654
|
+
const [isDragActive, setIsDragActive] = (0, import_react40.useState)(false);
|
|
16655
|
+
const [isPanelOpen, setIsPanelOpen] = (0, import_react40.useState)(
|
|
16276
16656
|
() => typeof window === "undefined" ? true : window.innerWidth > 900
|
|
16277
16657
|
);
|
|
16278
|
-
const [isEnvPanelOpen, setIsEnvPanelOpen] = (0,
|
|
16658
|
+
const [isEnvPanelOpen, setIsEnvPanelOpen] = (0, import_react40.useState)(
|
|
16279
16659
|
() => typeof window === "undefined" ? true : window.innerWidth > 900
|
|
16280
16660
|
);
|
|
16281
|
-
const [envSettings, setEnvSettings] = (0,
|
|
16661
|
+
const [envSettings, setEnvSettings] = (0, import_react40.useState)({
|
|
16282
16662
|
backgroundEnabled,
|
|
16283
16663
|
backgroundColor,
|
|
16284
16664
|
autoRotate,
|
|
@@ -16290,7 +16670,7 @@ function SimpleModelViewer({
|
|
|
16290
16670
|
directionalColor
|
|
16291
16671
|
});
|
|
16292
16672
|
const activeModelUrl = enableModelUpload ? uploadedModelUrl : modelUrl;
|
|
16293
|
-
const resetModelState = (0,
|
|
16673
|
+
const resetModelState = (0, import_react40.useCallback)(() => {
|
|
16294
16674
|
setLoadedScene(null);
|
|
16295
16675
|
setLargeModelTriangles(null);
|
|
16296
16676
|
setLargeModelNoticeDismissed(false);
|
|
@@ -16301,11 +16681,11 @@ function SimpleModelViewer({
|
|
|
16301
16681
|
setModelLoadFailed(false);
|
|
16302
16682
|
setInitialFitComplete(false);
|
|
16303
16683
|
}, []);
|
|
16304
|
-
const revokeUploadedModelUrl = (0,
|
|
16684
|
+
const revokeUploadedModelUrl = (0, import_react40.useCallback)(() => {
|
|
16305
16685
|
uploadedModelRef.current?.cleanup();
|
|
16306
16686
|
uploadedModelRef.current = null;
|
|
16307
16687
|
}, []);
|
|
16308
|
-
const clearUploadedModel = (0,
|
|
16688
|
+
const clearUploadedModel = (0, import_react40.useCallback)(() => {
|
|
16309
16689
|
revokeUploadedModelUrl();
|
|
16310
16690
|
setUploadedModelUrl(null);
|
|
16311
16691
|
setUploadedModelName(null);
|
|
@@ -16334,7 +16714,7 @@ function SimpleModelViewer({
|
|
|
16334
16714
|
resetModelState,
|
|
16335
16715
|
revokeUploadedModelUrl
|
|
16336
16716
|
]);
|
|
16337
|
-
const handleUploadedModel = (0,
|
|
16717
|
+
const handleUploadedModel = (0, import_react40.useCallback)(
|
|
16338
16718
|
async (file) => {
|
|
16339
16719
|
setIsResolvingUpload(true);
|
|
16340
16720
|
try {
|
|
@@ -16353,7 +16733,7 @@ function SimpleModelViewer({
|
|
|
16353
16733
|
},
|
|
16354
16734
|
[onLoadError, resetModelState, revokeUploadedModelUrl]
|
|
16355
16735
|
);
|
|
16356
|
-
const handleFileSelection = (0,
|
|
16736
|
+
const handleFileSelection = (0, import_react40.useCallback)(
|
|
16357
16737
|
(files) => {
|
|
16358
16738
|
const file = files?.[0];
|
|
16359
16739
|
if (!file) return;
|
|
@@ -16361,29 +16741,29 @@ function SimpleModelViewer({
|
|
|
16361
16741
|
},
|
|
16362
16742
|
[handleUploadedModel]
|
|
16363
16743
|
);
|
|
16364
|
-
(0,
|
|
16744
|
+
(0, import_react40.useEffect)(() => {
|
|
16365
16745
|
return () => {
|
|
16366
16746
|
revokeUploadedModelUrl();
|
|
16367
16747
|
};
|
|
16368
16748
|
}, [revokeUploadedModelUrl]);
|
|
16369
|
-
const handleEnvSettingChange = (0,
|
|
16749
|
+
const handleEnvSettingChange = (0, import_react40.useCallback)(
|
|
16370
16750
|
(key, value) => {
|
|
16371
16751
|
setEnvSettings((prev) => ({ ...prev, [key]: value }));
|
|
16372
16752
|
},
|
|
16373
16753
|
[]
|
|
16374
16754
|
);
|
|
16375
|
-
(0,
|
|
16755
|
+
(0, import_react40.useEffect)(() => {
|
|
16376
16756
|
setEnvSettings((prev) => ({ ...prev, backgroundColor }));
|
|
16377
16757
|
}, [backgroundColor]);
|
|
16378
|
-
(0,
|
|
16758
|
+
(0, import_react40.useEffect)(() => {
|
|
16379
16759
|
resetModelState();
|
|
16380
16760
|
}, [activeModelUrl, resetModelState]);
|
|
16381
|
-
const handleMeshesReady = (0,
|
|
16761
|
+
const handleMeshesReady = (0, import_react40.useCallback)((meshes) => {
|
|
16382
16762
|
setMeshKeys(meshes);
|
|
16383
16763
|
setHiddenKeys(/* @__PURE__ */ new Set());
|
|
16384
16764
|
setSelectedKey(null);
|
|
16385
16765
|
}, []);
|
|
16386
|
-
const handleModelLoaded = (0,
|
|
16766
|
+
const handleModelLoaded = (0, import_react40.useCallback)(
|
|
16387
16767
|
(scene) => {
|
|
16388
16768
|
setLoadedScene(scene);
|
|
16389
16769
|
setModelLoadFailed(false);
|
|
@@ -16394,14 +16774,14 @@ function SimpleModelViewer({
|
|
|
16394
16774
|
},
|
|
16395
16775
|
[onModelLoaded]
|
|
16396
16776
|
);
|
|
16397
|
-
const handleLoadError = (0,
|
|
16777
|
+
const handleLoadError = (0, import_react40.useCallback)(
|
|
16398
16778
|
(error) => {
|
|
16399
16779
|
setModelLoadFailed(true);
|
|
16400
16780
|
onLoadError?.(error);
|
|
16401
16781
|
},
|
|
16402
16782
|
[onLoadError]
|
|
16403
16783
|
);
|
|
16404
|
-
const focusMesh = (0,
|
|
16784
|
+
const focusMesh = (0, import_react40.useCallback)(
|
|
16405
16785
|
(key) => {
|
|
16406
16786
|
setSelectedKey(key);
|
|
16407
16787
|
if (!zoomOnSelected) return;
|
|
@@ -16417,14 +16797,14 @@ function SimpleModelViewer({
|
|
|
16417
16797
|
},
|
|
16418
16798
|
[zoomOnSelected]
|
|
16419
16799
|
);
|
|
16420
|
-
const handleToggleVisibility = (0,
|
|
16800
|
+
const handleToggleVisibility = (0, import_react40.useCallback)((key) => {
|
|
16421
16801
|
setHiddenKeys((prev) => {
|
|
16422
16802
|
const next = new Set(prev);
|
|
16423
16803
|
next.has(key) ? next.delete(key) : next.add(key);
|
|
16424
16804
|
return next;
|
|
16425
16805
|
});
|
|
16426
16806
|
}, []);
|
|
16427
|
-
const handleDragEnter = (0,
|
|
16807
|
+
const handleDragEnter = (0, import_react40.useCallback)(
|
|
16428
16808
|
(event) => {
|
|
16429
16809
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
16430
16810
|
return;
|
|
@@ -16436,7 +16816,7 @@ function SimpleModelViewer({
|
|
|
16436
16816
|
},
|
|
16437
16817
|
[enableModelUpload]
|
|
16438
16818
|
);
|
|
16439
|
-
const handleDragOver = (0,
|
|
16819
|
+
const handleDragOver = (0, import_react40.useCallback)(
|
|
16440
16820
|
(event) => {
|
|
16441
16821
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
16442
16822
|
return;
|
|
@@ -16448,7 +16828,7 @@ function SimpleModelViewer({
|
|
|
16448
16828
|
},
|
|
16449
16829
|
[enableModelUpload]
|
|
16450
16830
|
);
|
|
16451
|
-
const handleDragLeave = (0,
|
|
16831
|
+
const handleDragLeave = (0, import_react40.useCallback)(
|
|
16452
16832
|
(event) => {
|
|
16453
16833
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
16454
16834
|
return;
|
|
@@ -16462,7 +16842,7 @@ function SimpleModelViewer({
|
|
|
16462
16842
|
},
|
|
16463
16843
|
[enableModelUpload]
|
|
16464
16844
|
);
|
|
16465
|
-
const handleDrop = (0,
|
|
16845
|
+
const handleDrop = (0, import_react40.useCallback)(
|
|
16466
16846
|
(event) => {
|
|
16467
16847
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
16468
16848
|
return;
|
|
@@ -16555,7 +16935,7 @@ function SimpleModelViewer({
|
|
|
16555
16935
|
raycaster: { firstHitOnly: true },
|
|
16556
16936
|
style: { background: envSettings.backgroundColor },
|
|
16557
16937
|
camera: { position: [0, 0, 5], fov: 45, near: 0.1, far: 1e3 },
|
|
16558
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
16938
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_react40.Suspense, { fallback: null, children: [
|
|
16559
16939
|
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
16560
16940
|
"ambientLight",
|
|
16561
16941
|
{
|
|
@@ -16573,7 +16953,7 @@ function SimpleModelViewer({
|
|
|
16573
16953
|
}
|
|
16574
16954
|
),
|
|
16575
16955
|
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("directionalLight", { position: [-5, 5, 5], intensity: 0.5 }),
|
|
16576
|
-
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
16956
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_react40.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
16577
16957
|
import_drei6.Environment,
|
|
16578
16958
|
{
|
|
16579
16959
|
preset: envSettings.envPreset,
|
|
@@ -16592,7 +16972,7 @@ function SimpleModelViewer({
|
|
|
16592
16972
|
ModelErrorBoundary2,
|
|
16593
16973
|
{
|
|
16594
16974
|
onLoadError: handleLoadError,
|
|
16595
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
16975
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_react40.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
16596
16976
|
InnerModel,
|
|
16597
16977
|
{
|
|
16598
16978
|
url: activeModelUrl,
|
|
@@ -16691,18 +17071,18 @@ function SimpleModelViewer({
|
|
|
16691
17071
|
}
|
|
16692
17072
|
|
|
16693
17073
|
// src/hooks/useViewerSelection.ts
|
|
16694
|
-
var
|
|
17074
|
+
var import_react41 = require("react");
|
|
16695
17075
|
function useViewerSelection(initialSelectedObjectBinding = null) {
|
|
16696
|
-
const [selectedObjectBinding, setSelectedObjectBinding] = (0,
|
|
17076
|
+
const [selectedObjectBinding, setSelectedObjectBinding] = (0, import_react41.useState)(
|
|
16697
17077
|
initialSelectedObjectBinding
|
|
16698
17078
|
);
|
|
16699
|
-
const handleObjectSelect = (0,
|
|
17079
|
+
const handleObjectSelect = (0, import_react41.useCallback)(
|
|
16700
17080
|
(binding) => {
|
|
16701
17081
|
setSelectedObjectBinding(binding);
|
|
16702
17082
|
},
|
|
16703
17083
|
[setSelectedObjectBinding]
|
|
16704
17084
|
);
|
|
16705
|
-
const clearSelection = (0,
|
|
17085
|
+
const clearSelection = (0, import_react41.useCallback)(() => {
|
|
16706
17086
|
setSelectedObjectBinding(null);
|
|
16707
17087
|
}, []);
|
|
16708
17088
|
return {
|
|
@@ -16714,9 +17094,9 @@ function useViewerSelection(initialSelectedObjectBinding = null) {
|
|
|
16714
17094
|
}
|
|
16715
17095
|
|
|
16716
17096
|
// src/hooks/useObjectBinding.ts
|
|
16717
|
-
var
|
|
17097
|
+
var import_react42 = require("react");
|
|
16718
17098
|
function useObjectBinding(objectBindings, objectId) {
|
|
16719
|
-
return (0,
|
|
17099
|
+
return (0, import_react42.useMemo)(() => {
|
|
16720
17100
|
if (!objectId) {
|
|
16721
17101
|
return null;
|
|
16722
17102
|
}
|
|
@@ -16731,23 +17111,23 @@ function useObjectBinding(objectBindings, objectId) {
|
|
|
16731
17111
|
}
|
|
16732
17112
|
|
|
16733
17113
|
// src/hooks/useObjectBindingIds.ts
|
|
16734
|
-
var
|
|
17114
|
+
var import_react43 = require("react");
|
|
16735
17115
|
function useObjectBindingIds(objectBindings) {
|
|
16736
|
-
return (0,
|
|
17116
|
+
return (0, import_react43.useMemo)(
|
|
16737
17117
|
() => Object.values(objectBindings).map((binding) => binding.modelObjectId),
|
|
16738
17118
|
[objectBindings]
|
|
16739
17119
|
);
|
|
16740
17120
|
}
|
|
16741
17121
|
|
|
16742
17122
|
// src/hooks/useObjectVisibility.ts
|
|
16743
|
-
var
|
|
17123
|
+
var import_react44 = require("react");
|
|
16744
17124
|
function getHiddenObjects(objectBindings) {
|
|
16745
17125
|
return Object.fromEntries(
|
|
16746
17126
|
Object.entries(objectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
|
|
16747
17127
|
);
|
|
16748
17128
|
}
|
|
16749
17129
|
function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
16750
|
-
const updateVisibility = (0,
|
|
17130
|
+
const updateVisibility = (0, import_react44.useCallback)(
|
|
16751
17131
|
(objectId, visible) => {
|
|
16752
17132
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
16753
17133
|
if (!bindingKey) return;
|
|
@@ -16763,15 +17143,15 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
|
16763
17143
|
},
|
|
16764
17144
|
[objectBindings, onObjectBindingsChange]
|
|
16765
17145
|
);
|
|
16766
|
-
const hideObject = (0,
|
|
17146
|
+
const hideObject = (0, import_react44.useCallback)(
|
|
16767
17147
|
(objectId) => updateVisibility(objectId, false),
|
|
16768
17148
|
[updateVisibility]
|
|
16769
17149
|
);
|
|
16770
|
-
const showObject = (0,
|
|
17150
|
+
const showObject = (0, import_react44.useCallback)(
|
|
16771
17151
|
(objectId) => updateVisibility(objectId, true),
|
|
16772
17152
|
[updateVisibility]
|
|
16773
17153
|
);
|
|
16774
|
-
const toggleObjectVisibility = (0,
|
|
17154
|
+
const toggleObjectVisibility = (0, import_react44.useCallback)(
|
|
16775
17155
|
(objectId) => {
|
|
16776
17156
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
16777
17157
|
if (!bindingKey) return;
|
|
@@ -16787,7 +17167,7 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
|
16787
17167
|
},
|
|
16788
17168
|
[objectBindings, onObjectBindingsChange]
|
|
16789
17169
|
);
|
|
16790
|
-
const clearHiddenObjects = (0,
|
|
17170
|
+
const clearHiddenObjects = (0, import_react44.useCallback)(() => {
|
|
16791
17171
|
onObjectBindingsChange(
|
|
16792
17172
|
Object.fromEntries(
|
|
16793
17173
|
Object.entries(objectBindings).map(([bindingKey, binding]) => [
|
|
@@ -16797,17 +17177,17 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
|
16797
17177
|
)
|
|
16798
17178
|
);
|
|
16799
17179
|
}, [objectBindings, onObjectBindingsChange]);
|
|
16800
|
-
const hiddenObjects = (0,
|
|
17180
|
+
const hiddenObjects = (0, import_react44.useMemo)(
|
|
16801
17181
|
() => getHiddenObjects(objectBindings),
|
|
16802
17182
|
[objectBindings]
|
|
16803
17183
|
);
|
|
16804
|
-
const hiddenObjectIds = (0,
|
|
17184
|
+
const hiddenObjectIds = (0, import_react44.useMemo)(
|
|
16805
17185
|
() => Object.entries(hiddenObjects).map(
|
|
16806
17186
|
([bindingKey]) => objectBindings[bindingKey]?.id ?? bindingKey
|
|
16807
17187
|
),
|
|
16808
17188
|
[hiddenObjects, objectBindings]
|
|
16809
17189
|
);
|
|
16810
|
-
const isObjectHidden = (0,
|
|
17190
|
+
const isObjectHidden = (0, import_react44.useCallback)(
|
|
16811
17191
|
(objectId) => {
|
|
16812
17192
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
16813
17193
|
return bindingKey ? objectBindings[bindingKey]?.visible === false : false;
|
|
@@ -16826,10 +17206,10 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
|
16826
17206
|
}
|
|
16827
17207
|
|
|
16828
17208
|
// src/hooks/useViewerHover.ts
|
|
16829
|
-
var
|
|
17209
|
+
var import_react45 = require("react");
|
|
16830
17210
|
function useViewerHover() {
|
|
16831
|
-
const [hoveredObjectBinding, setHoveredObjectBinding] = (0,
|
|
16832
|
-
const handleHoveredObject = (0,
|
|
17211
|
+
const [hoveredObjectBinding, setHoveredObjectBinding] = (0, import_react45.useState)(null);
|
|
17212
|
+
const handleHoveredObject = (0, import_react45.useCallback)(
|
|
16833
17213
|
(binding) => {
|
|
16834
17214
|
setHoveredObjectBinding(binding);
|
|
16835
17215
|
},
|
|
@@ -16842,18 +17222,18 @@ function useViewerHover() {
|
|
|
16842
17222
|
}
|
|
16843
17223
|
|
|
16844
17224
|
// src/hooks/useViewerModel.ts
|
|
16845
|
-
var
|
|
17225
|
+
var import_react46 = require("react");
|
|
16846
17226
|
var import_three22 = require("three");
|
|
16847
17227
|
function toTuple2(vector) {
|
|
16848
17228
|
return [vector.x, vector.y, vector.z];
|
|
16849
17229
|
}
|
|
16850
17230
|
function useViewerModel() {
|
|
16851
|
-
const [isLoading, setIsLoading] = (0,
|
|
16852
|
-
const [isReady, setIsReady] = (0,
|
|
16853
|
-
const [error, setError] = (0,
|
|
16854
|
-
const [bounds, setBounds] = (0,
|
|
16855
|
-
const [objectCount, setObjectCount] = (0,
|
|
16856
|
-
const handleModelLoaded = (0,
|
|
17231
|
+
const [isLoading, setIsLoading] = (0, import_react46.useState)(true);
|
|
17232
|
+
const [isReady, setIsReady] = (0, import_react46.useState)(false);
|
|
17233
|
+
const [error, setError] = (0, import_react46.useState)(null);
|
|
17234
|
+
const [bounds, setBounds] = (0, import_react46.useState)(null);
|
|
17235
|
+
const [objectCount, setObjectCount] = (0, import_react46.useState)(0);
|
|
17236
|
+
const handleModelLoaded = (0, import_react46.useCallback)((scene) => {
|
|
16857
17237
|
const box = new import_three22.Box3().setFromObject(scene);
|
|
16858
17238
|
const min = box.min.clone();
|
|
16859
17239
|
const max = box.max.clone();
|
|
@@ -16878,14 +17258,14 @@ function useViewerModel() {
|
|
|
16878
17258
|
setIsLoading(false);
|
|
16879
17259
|
setIsReady(true);
|
|
16880
17260
|
}, []);
|
|
16881
|
-
const handleLoadError = (0,
|
|
17261
|
+
const handleLoadError = (0, import_react46.useCallback)((nextError) => {
|
|
16882
17262
|
setError(nextError);
|
|
16883
17263
|
setBounds(null);
|
|
16884
17264
|
setObjectCount(0);
|
|
16885
17265
|
setIsLoading(false);
|
|
16886
17266
|
setIsReady(false);
|
|
16887
17267
|
}, []);
|
|
16888
|
-
const resetModelState = (0,
|
|
17268
|
+
const resetModelState = (0, import_react46.useCallback)(() => {
|
|
16889
17269
|
setIsLoading(true);
|
|
16890
17270
|
setIsReady(false);
|
|
16891
17271
|
setError(null);
|
|
@@ -16905,10 +17285,10 @@ function useViewerModel() {
|
|
|
16905
17285
|
}
|
|
16906
17286
|
|
|
16907
17287
|
// src/hooks/useViewerActions.ts
|
|
16908
|
-
var
|
|
17288
|
+
var import_react47 = require("react");
|
|
16909
17289
|
function useViewerActions(objectBindings, options = {}) {
|
|
16910
17290
|
const { onAction, onObjectBindingsChange } = options;
|
|
16911
|
-
const getActionsForObject = (0,
|
|
17291
|
+
const getActionsForObject = (0, import_react47.useCallback)(
|
|
16912
17292
|
(objectId) => {
|
|
16913
17293
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
16914
17294
|
if (!bindingKey) {
|
|
@@ -16918,7 +17298,7 @@ function useViewerActions(objectBindings, options = {}) {
|
|
|
16918
17298
|
},
|
|
16919
17299
|
[objectBindings]
|
|
16920
17300
|
);
|
|
16921
|
-
const runAction = (0,
|
|
17301
|
+
const runAction = (0, import_react47.useCallback)(
|
|
16922
17302
|
(objectId, actionId) => {
|
|
16923
17303
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
16924
17304
|
if (!bindingKey) {
|
|
@@ -16960,7 +17340,7 @@ function useViewerActions(objectBindings, options = {}) {
|
|
|
16960
17340
|
}
|
|
16961
17341
|
|
|
16962
17342
|
// src/hooks/useShareableViewerState.ts
|
|
16963
|
-
var
|
|
17343
|
+
var import_react48 = require("react");
|
|
16964
17344
|
function serializeState(cameraState, objectBindings, selectedObjectBinding) {
|
|
16965
17345
|
const hidden = Object.values(objectBindings).filter((binding) => binding.visible === false).map((binding) => binding.id);
|
|
16966
17346
|
return {
|
|
@@ -16980,8 +17360,8 @@ function useShareableViewerState({
|
|
|
16980
17360
|
debounceMs = 400,
|
|
16981
17361
|
autoSync = true
|
|
16982
17362
|
}) {
|
|
16983
|
-
const lastWrittenRawRef = (0,
|
|
16984
|
-
const buildHash = (0,
|
|
17363
|
+
const lastWrittenRawRef = (0, import_react48.useRef)(null);
|
|
17364
|
+
const buildHash = (0, import_react48.useCallback)(() => {
|
|
16985
17365
|
const state = serializeState(
|
|
16986
17366
|
cameraState,
|
|
16987
17367
|
objectBindings,
|
|
@@ -16989,12 +17369,12 @@ function useShareableViewerState({
|
|
|
16989
17369
|
);
|
|
16990
17370
|
return `${paramKey}=${encodeURIComponent(JSON.stringify(state))}`;
|
|
16991
17371
|
}, [cameraState, objectBindings, selectedObjectBinding, paramKey]);
|
|
16992
|
-
const getShareUrl = (0,
|
|
17372
|
+
const getShareUrl = (0, import_react48.useCallback)(() => {
|
|
16993
17373
|
const url = new URL(window.location.href);
|
|
16994
17374
|
url.hash = buildHash();
|
|
16995
17375
|
return url.toString();
|
|
16996
17376
|
}, [buildHash]);
|
|
16997
|
-
const syncUrl = (0,
|
|
17377
|
+
const syncUrl = (0, import_react48.useCallback)(() => {
|
|
16998
17378
|
const url = new URL(window.location.href);
|
|
16999
17379
|
url.hash = buildHash();
|
|
17000
17380
|
window.history.replaceState(null, "", url.toString());
|
|
@@ -17002,7 +17382,7 @@ function useShareableViewerState({
|
|
|
17002
17382
|
paramKey
|
|
17003
17383
|
);
|
|
17004
17384
|
}, [buildHash, paramKey]);
|
|
17005
|
-
const latestStateRef = (0,
|
|
17385
|
+
const latestStateRef = (0, import_react48.useRef)({
|
|
17006
17386
|
cameraState,
|
|
17007
17387
|
objectBindings,
|
|
17008
17388
|
selectedObjectBinding
|
|
@@ -17012,9 +17392,9 @@ function useShareableViewerState({
|
|
|
17012
17392
|
objectBindings,
|
|
17013
17393
|
selectedObjectBinding
|
|
17014
17394
|
};
|
|
17015
|
-
const [restoreAttempted, setRestoreAttempted] = (0,
|
|
17016
|
-
const lastSyncedHashRef = (0,
|
|
17017
|
-
(0,
|
|
17395
|
+
const [restoreAttempted, setRestoreAttempted] = (0, import_react48.useState)(false);
|
|
17396
|
+
const lastSyncedHashRef = (0, import_react48.useRef)(null);
|
|
17397
|
+
(0, import_react48.useEffect)(() => {
|
|
17018
17398
|
if (!autoSync || !restoreAttempted) return;
|
|
17019
17399
|
const tick = () => {
|
|
17020
17400
|
const current = latestStateRef.current;
|
|
@@ -17036,9 +17416,9 @@ function useShareableViewerState({
|
|
|
17036
17416
|
const id = window.setInterval(tick, debounceMs);
|
|
17037
17417
|
return () => window.clearInterval(id);
|
|
17038
17418
|
}, [autoSync, restoreAttempted, debounceMs, paramKey]);
|
|
17039
|
-
const lastRestoredRawRef = (0,
|
|
17040
|
-
const restoringRawRef = (0,
|
|
17041
|
-
const restoreFromUrl = (0,
|
|
17419
|
+
const lastRestoredRawRef = (0, import_react48.useRef)(null);
|
|
17420
|
+
const restoringRawRef = (0, import_react48.useRef)(null);
|
|
17421
|
+
const restoreFromUrl = (0, import_react48.useCallback)(async () => {
|
|
17042
17422
|
setRestoreAttempted(true);
|
|
17043
17423
|
const rawHash = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
|
|
17044
17424
|
const raw = new URLSearchParams(rawHash).get(paramKey);
|
|
@@ -17091,7 +17471,7 @@ function useShareableViewerState({
|
|
|
17091
17471
|
setCameraState,
|
|
17092
17472
|
setSelectedObjectBinding
|
|
17093
17473
|
]);
|
|
17094
|
-
(0,
|
|
17474
|
+
(0, import_react48.useEffect)(() => {
|
|
17095
17475
|
const handleHashChange = () => {
|
|
17096
17476
|
void restoreFromUrl();
|
|
17097
17477
|
};
|