@liveroom-tech/react-immersive 1.3.5 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -59
- package/dist/index.d.mts +49 -8
- package/dist/index.d.ts +49 -8
- package/dist/index.js +576 -331
- package/dist/index.mjs +587 -342
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -200,7 +200,7 @@ function styleInject(css, { insertAt } = {}) {
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
// src/styles/ModelViewer.css
|
|
203
|
-
styleInject('.ri-viewer--license-checking,\n.ri-viewer--license-invalid {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n background-color: #f9f9f9;\n}\n.ri-viewer {\n --ri-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --ri-viewer-bg: #1c1c1d;\n --ri-viewer-text: #111827;\n --ri-panel-bg: #ffffff;\n --ri-panel-border: #e5e7eb;\n --ri-panel-title: #111827;\n --ri-panel-muted-text: #4b5563;\n --ri-panel-subtle-text: #9ca3af;\n --ri-panel-surface: #f9fafb;\n --ri-panel-surface-hover: #f3f4f6;\n --ri-panel-surface-border: #e5e7eb;\n --ri-panel-button-text: #1f2937;\n --ri-panel-accent: #2563eb;\n --ri-panel-accent-contrast: #ffffff;\n --ri-license-overlay-bg: rgba(0, 0, 0, 0.04);\n --ri-license-overlay-border: rgba(0, 0, 0, 0.08);\n --ri-license-overlay-text: #888;\n --ri-license-overlay-error-bg: rgba(192, 57, 43, 0.06);\n --ri-license-overlay-error-border: rgba(192, 57, 43, 0.2);\n --ri-license-overlay-error-text: #c0392b;\n --ri-sidepanel-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --ri-sidepanel-bg: var(--ri-databinding-bg, #111318);\n --ri-sidepanel-open-btn-bg: rgba(20, 20, 20, 0.82);\n --ri-sidepanel-open-btn-bg-hover: #27282b7e;\n --ri-sidepanel-border: rgba(255, 255, 255, 0.06);\n --ri-sidepanel-divider: rgba(255, 255, 255, 0.04);\n --ri-sidepanel-border-strong: rgba(255, 255, 255, 0.08);\n --ri-sidepanel-border-active: rgba(255, 255, 255, 0.18);\n --ri-sidepanel-shadow-left: 4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-sidepanel-shadow-right: -4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-sidepanel-text: var(--ri-databinding-text, #f1f5f9);\n --ri-sidepanel-text-emphasis: var(--ri-databinding-text-hover, #cbd5e1);\n --ri-sidepanel-text-muted: var(--ri-databinding-text-muted, #94a3b8);\n --ri-sidepanel-text-subtle: var(--ri-databinding-text-subtle, #64748b);\n --ri-sidepanel-placeholder: var(--ri-databinding-text-faint, #475569);\n --ri-sidepanel-surface: rgba(255, 255, 255, 0.05);\n --ri-sidepanel-surface-hover: rgba(255, 255, 255, 0.1);\n --ri-sidepanel-accent: #3b82f6;\n --ri-sidepanel-accent-soft: rgba(59, 130, 246, 0.12);\n --ri-databinding-bg: #111318;\n --ri-databinding-border: rgba(255, 255, 255, 0.06);\n --ri-databinding-border-strong: rgba(255, 255, 255, 0.08);\n --ri-databinding-border-hover: rgba(255, 255, 255, 0.15);\n --ri-databinding-shadow: 4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-databinding-font-family:\n "Inter",\n "Segoe UI",\n sans-serif;\n --ri-databinding-mono-font-family:\n "JetBrains Mono",\n "Fira Code",\n "Menlo",\n monospace;\n --ri-databinding-text: #f1f5f9;\n --ri-databinding-text-muted: #94a3b8;\n --ri-databinding-text-subtle: #64748b;\n --ri-databinding-text-faint: #475569;\n --ri-databinding-text-hover: #cbd5e1;\n --ri-databinding-surface: rgba(255, 255, 255, 0.03);\n --ri-databinding-surface-hover: rgba(255, 255, 255, 0.06);\n --ri-databinding-accent: #93c5fd;\n --ri-databinding-accent-glow: rgba(147, 197, 253, 0.8);\n --ri-databinding-accent-border: rgba(96, 165, 250, 0.55);\n --ri-databinding-accent-gradient:\n linear-gradient(\n \n 135deg,\n #1e3a8a 0%,\n #1d4ed8 60%,\n #2563eb 100% );\n --ri-databinding-accent-gradient-active:\n linear-gradient(\n \n 135deg,\n #1e3a8a 0%,\n #1e40af 100% );\n display: flex;\n flex-direction: row;\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n background: var(--ri-viewer-bg);\n position: relative;\n color: var(--ri-viewer-text);\n font-family: var(--ri-font-family);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n font-synthesis: none;\n}\n.ri-viewer * {\n box-sizing: border-box;\n}\n.ri-viewerLeft {\n display: flex;\n flex: 1;\n min-height: 0;\n min-width: 0;\n position: relative;\n}\n.ri-viewerRight {\n display: flex;\n flex-shrink: 0;\n min-width: 0;\n}\n.ri-panel {\n width: 280px;\n min-height: 0;\n background: var(--ri-panel-bg);\n border-right: 1px solid var(--ri-panel-border);\n display: flex;\n flex-direction: column;\n padding: 20px;\n overflow-y: auto;\n flex-shrink: 0;\n transition: margin-left 0.3s ease, transform 0.3s ease;\n}\n.ri-panel--open {\n transform: translateX(0);\n margin-left: 0;\n}\n.ri-panel--closed {\n transform: translateX(-100%);\n margin-left: -280px;\n}\n.ri-panelHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 12px;\n}\n.ri-panelTitle {\n margin: 0;\n font-size: 15px;\n font-weight: 700;\n color: var(--ri-panel-title);\n line-height: 1.4;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ri-closeButton {\n border: 0;\n background: transparent;\n color: var(--ri-panel-subtle-text);\n font-size: 22px;\n line-height: 1;\n padding: 0;\n cursor: pointer;\n flex-shrink: 0;\n transition: color 0.2s ease;\n}\n.ri-closeButton:hover {\n color: var(--ri-panel-muted-text);\n}\n.ri-json {\n margin: 0 0 16px;\n padding: 12px;\n background: var(--ri-panel-surface);\n border: 1px solid var(--ri-panel-surface-border);\n border-radius: 12px;\n font-size: 12px;\n line-height: 1.55;\n color: var(--ri-panel-button-text);\n overflow-x: auto;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.ri-divider {\n border-top: 1px solid var(--ri-panel-border);\n margin-bottom: 12px;\n}\n.ri-sectionLabel {\n margin-bottom: 8px;\n font-size: 11px;\n font-weight: 700;\n color: var(--ri-panel-subtle-text);\n text-transform: uppercase;\n letter-spacing: 0.16em;\n}\n.ri-actionList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.ri-actionButton {\n width: 100%;\n border-radius: 8px;\n border: 1px solid var(--ri-panel-surface-border);\n background: var(--ri-panel-surface);\n color: var(--ri-panel-button-text);\n padding: 10px 12px;\n text-align: left;\n font-size: 14px;\n line-height: 1.4;\n cursor: pointer;\n transition:\n background-color 0.2s ease,\n border-color 0.2s ease,\n color 0.2s ease;\n}\n.ri-actionButton:hover {\n background: var(--ri-panel-surface-hover);\n border-color: var(--ri-panel-border);\n}\n.ri-actionButton--active {\n background: var(--ri-panel-accent);\n border-color: var(--ri-panel-accent);\n color: var(--ri-panel-accent-contrast);\n}\n.ri-actionButton--active:hover {\n background: var(--ri-panel-accent);\n border-color: var(--ri-panel-accent);\n}\n.ri-canvasArea {\n flex: 1 1 auto;\n position: relative;\n min-width: 0;\n}\n.ri-canvasArea--measuring {\n cursor: crosshair;\n}\n.ri-canvasArea canvas {\n opacity: 1;\n transition: opacity 0.18s ease;\n}\n.ri-canvasArea--positioning canvas {\n opacity: 0;\n transition: none;\n}\n.ri-bindingsMismatchBanner {\n position: absolute;\n top: 12px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 20;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 7px 12px 7px 10px;\n border-radius: 8px;\n background: rgba(30, 20, 0, 0.88);\n border: 1px solid rgba(251, 191, 36, 0.35);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n color: rgb(251, 191, 36);\n font-size: 12px;\n font-weight: 500;\n line-height: 1.4;\n white-space: nowrap;\n pointer-events: auto;\n max-width: calc(100% - 32px);\n white-space: normal;\n text-align: center;\n}\n.ri-bindingsMismatchBanner__icon {\n flex-shrink: 0;\n}\n.ri-bindingsMismatchBanner__dismiss {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: 4px;\n padding: 2px;\n background: none;\n border: none;\n border-radius: 4px;\n color: rgba(251, 191, 36, 0.7);\n cursor: pointer;\n transition: color 0.15s ease, background 0.15s ease;\n}\n.ri-bindingsMismatchBanner__dismiss:hover {\n color: rgb(251, 191, 36);\n background: rgba(251, 191, 36, 0.12);\n}\n.ri-measureTools {\n position: absolute;\n top: 16px;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-measureButton {\n width: 34px;\n height: 34px;\n padding: 0;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n color: rgba(255, 255, 255, 0.75);\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n touch-action: manipulation;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n transition:\n background 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease;\n}\n.ri-measureButton:hover {\n background: rgba(40, 40, 40, 0.92);\n color: #fff;\n border-color: rgba(255, 255, 255, 0.25);\n}\n.ri-measureButton:active {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-measureButton--active {\n background: #22d3ee;\n border-color: #22d3ee;\n color: #06343c;\n}\n.ri-measureButton--active:hover {\n background: #38dbf0;\n color: #06343c;\n}\n.ri-measureHint {\n padding: 6px 10px;\n border-radius: 8px;\n font-size: 12px;\n line-height: 1;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n white-space: nowrap;\n}\n.ri-explodeTools {\n position: absolute;\n top: 16px;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-explodeSlider {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 34px;\n padding: 0 12px;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 8px;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n}\n.ri-explodeSlider input[type=range] {\n width: 120px;\n accent-color: #22d3ee;\n cursor: pointer;\n}\n.ri-cinematicTools {\n position: absolute;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-measureDot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: #22d3ee;\n border: 2px solid #fff;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);\n pointer-events: none;\n}\n.ri-measureLabel {\n padding: 3px 7px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n white-space: nowrap;\n color: #06343c;\n background: #22d3ee;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n pointer-events: none;\n user-select: none;\n}\n.ri-xrTools {\n position: absolute;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-xrButton {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n height: 34px;\n padding: 0 10px;\n font-size: 13px;\n font-weight: 500;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.16);\n border-radius: 8px;\n cursor: pointer;\n touch-action: manipulation;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n transition: background 0.15s ease, border-color 0.15s ease;\n}\n.ri-xrButton:hover {\n background: rgba(40, 40, 40, 0.92);\n border-color: rgba(255, 255, 255, 0.3);\n}\n.ri-mobileHandoffBackdrop {\n position: absolute;\n z-index: 30;\n inset: 0;\n display: grid;\n place-items: center;\n padding: 20px;\n background: rgba(2, 6, 23, 0.68);\n backdrop-filter: blur(5px);\n -webkit-backdrop-filter: blur(5px);\n}\n.ri-mobileHandoffDialog {\n position: relative;\n display: flex;\n width: min(100%, 300px);\n flex-direction: column;\n align-items: center;\n padding: 16px;\n text-align: center;\n color: #e2e8f0;\n background: #0f172a;\n border: 1px solid rgba(148, 163, 184, 0.3);\n border-radius: 16px;\n box-shadow: 0 24px 80px rgba(0, 0, 0, 0.5);\n}\n.ri-mobileHandoffDialog > svg {\n color: #67e8f9;\n}\n.ri-mobileHandoffDialog h2 {\n margin: 12px 0 0;\n font-size: 18px;\n line-height: 1.25;\n color: #fff;\n}\n.ri-mobileHandoffDialog p {\n margin: 8px 0 16px;\n font-size: 13px;\n line-height: 1.5;\n color: #cbd5e1;\n}\n.ri-mobileHandoffQr {\n display: grid;\n place-items: center;\n padding: 10px;\n background: #fff;\n border-radius: 10px;\n}\n.ri-mobileHandoffDialog a {\n margin-top: 16px;\n font-size: 13px;\n font-weight: 600;\n color: #a5f3fc;\n text-decoration: underline;\n text-underline-offset: 3px;\n}\n.ri-mobileHandoffClose {\n position: absolute;\n top: 10px;\n right: 10px;\n display: grid;\n width: 30px;\n height: 30px;\n place-items: center;\n padding: 0;\n color: #cbd5e1;\n background: transparent;\n border: 0;\n border-radius: 6px;\n cursor: pointer;\n}\n.ri-mobileHandoffClose:hover {\n color: #fff;\n background: rgba(255, 255, 255, 0.1);\n}\n.ri-arHintOverlay {\n position: absolute;\n top: 18px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n width: min(calc(100% - 32px), 360px);\n pointer-events: none;\n}\n.ri-arHint {\n margin: 0 auto;\n width: fit-content;\n max-width: 100%;\n padding: 6px 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1.2;\n text-align: center;\n color: rgba(255, 255, 255, 0.92);\n background: rgba(20, 20, 20, 0.58);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 999px;\n user-select: none;\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n}\n.ri-arOverlay {\n position: absolute;\n bottom: 24px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n width: min(calc(100% - 32px), 420px);\n pointer-events: none;\n}\n.ri-arControls {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n}\n.ri-arStatus,\n.ri-arActionButton {\n max-width: 100%;\n padding: 8px 16px;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.3;\n text-align: center;\n white-space: nowrap;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.16);\n border-radius: 19px;\n user-select: none;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n}\n.ri-arStatus {\n pointer-events: none;\n}\n.ri-arActionButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n cursor: pointer;\n pointer-events: auto;\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n transition:\n background 0.15s ease,\n border-color 0.15s ease,\n color 0.15s ease,\n transform 0.15s ease;\n}\n.ri-arActionButton svg {\n flex-shrink: 0;\n}\n.ri-arActionButton:hover {\n background: rgba(40, 40, 40, 0.92);\n border-color: rgba(255, 255, 255, 0.3);\n}\n.ri-arActionButton:active {\n transform: scale(0.98);\n}\n.ri-arActionButton--primary {\n color: #06343c;\n background: #22d3ee;\n border-color: transparent;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n}\n.ri-arActionButton--primary:hover {\n background: #67e8f9;\n border-color: transparent;\n}\n.ri-arActionButton.ri-arActionButton--active,\n.ri-arActionButton[aria-pressed=true],\n.ri-arActionButton.ri-arActionButton--active:hover,\n.ri-arActionButton[aria-pressed=true]:hover,\n.ri-arActionButton.ri-arActionButton--active:active,\n.ri-arActionButton[aria-pressed=true]:active {\n color: #06343c;\n background: #22d3ee;\n border-color: transparent;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n}\n.ri-hiddenInput {\n display: none;\n}\n.ri-popover {\n position: absolute;\n z-index: 50;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n border-radius: 16px;\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);\n color: #111827;\n}\n.ri-popover--inline {\n position: static;\n z-index: auto;\n width: 100%;\n border-radius: 10px;\n box-shadow: none;\n}\n.ri-popover--inline .react-colorful {\n width: 100%;\n}\n.ri-popover--inline {\n background: var(--ri-databinding-surface, rgba(255, 255, 255, 0.03));\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n color: var(--ri-databinding-text, #f1f5f9);\n}\n.ri-popover--inline .ri-popoverHeader {\n border-bottom-color: var( --ri-databinding-border-strong, rgba(255, 255, 255, 0.08) );\n}\n.ri-popover--inline .ri-popoverLabel {\n color: var(--ri-databinding-text-muted, #94a3b8);\n}\n.ri-popover--inline .ri-colorCode {\n color: var(--ri-databinding-text-muted, #94a3b8);\n}\n.ri-popover--inline .ri-secondaryButton {\n background: var(--ri-databinding-surface-hover, rgba(255, 255, 255, 0.06));\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n color: var(--ri-databinding-text, #f1f5f9);\n}\n.ri-popover--inline .ri-secondaryButton:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n.ri-popover--inline .ri-previewFrame {\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n}\n.ri-popover--inline .ri-emptyState {\n border-color: var(--ri-databinding-border-hover, rgba(255, 255, 255, 0.15));\n color: var(--ri-databinding-text-subtle, #64748b);\n}\n.ri-popover--inline .ri-uploadButton {\n background: var(--ri-databinding-accent-gradient, #2563eb);\n color: #ffffff;\n}\n.ri-popover--inline .ri-uploadButton:hover {\n background: var(--ri-databinding-accent-gradient-active, #1d4ed8);\n}\n.ri-popover--inline .ri-removeButton {\n background: transparent;\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n color: var(--ri-databinding-text-muted, #94a3b8);\n}\n.ri-popover--inline .ri-removeButton:hover {\n background: var(--ri-databinding-surface-hover, rgba(255, 255, 255, 0.06));\n}\n.ri-popover--inline .ri-formatHint {\n color: var(--ri-databinding-text-subtle, #64748b);\n}\n.ri-popoverHeader {\n padding: 12px 16px;\n}\n.ri-popoverLabel {\n margin: 0 0 8px;\n font-size: 11px;\n font-weight: 700;\n color: #6b7280;\n text-transform: uppercase;\n letter-spacing: 0.16em;\n}\n.ri-inlineRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n margin-top: 8px;\n}\n.ri-colorCode {\n font-size: 12px;\n color: #9ca3af;\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n monospace;\n text-transform: uppercase;\n}\n.ri-secondaryButton {\n border: 1px solid #e5e7eb;\n background: #f3f4f6;\n color: #374151;\n border-radius: 8px;\n padding: 6px 10px;\n font-size: 12px;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-secondaryButton:hover {\n background: #e5e7eb;\n}\n.ri-materialBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding-left: 12px;\n padding-right: 12px;\n padding-bottom: 12px;\n}\n.ri-previewFrame {\n position: relative;\n height: 112px;\n border-radius: 12px;\n overflow: hidden;\n border: 1px solid #e5e7eb;\n}\n.ri-previewImage {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n.ri-previewCaption {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n padding: 4px 8px;\n color: #ffffff;\n font-size: 10px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ri-emptyState {\n height: 112px;\n border-radius: 12px;\n border: 2px dashed #e5e7eb;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n color: #d1d5db;\n text-align: center;\n}\n.ri-uploadButton {\n width: 100%;\n border: 0;\n border-radius: 10px;\n background: #111827;\n color: #ffffff;\n padding: 10px 12px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-uploadButton:hover {\n background: #374151;\n}\n.ri-removeButton {\n width: 100%;\n border-radius: 10px;\n border: 1px solid #e5e7eb;\n background: #ffffff;\n color: #6b7280;\n padding: 8px 12px;\n font-size: 12px;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-removeButton:hover {\n background: #f9fafb;\n}\n.ri-formatHint {\n margin: 0;\n text-align: center;\n font-size: 10px;\n color: #d1d5db;\n}\n.ri-buttonContainer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 8px;\n position: absolute;\n bottom: 12px;\n left: 12px;\n right: 12px;\n z-index: 10;\n}\n.ri-tourControls {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-shrink: 0;\n}\n.ri-rightControls {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-shrink: 0;\n margin-left: auto;\n}\n.ri-rightControls:only-child {\n margin-left: auto;\n}\n.ri-navButtonSingle,\n.ri-tourButton,\n.ri-resetButton,\n.ri-downloadButton {\n height: 34px;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n color: rgba(255, 255, 255, 0.75);\n border-radius: 8px;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n cursor: pointer;\n transition:\n background 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease,\n transform 0.15s ease;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n}\n.ri-navButtonSingle:hover:not(:disabled),\n.ri-tourButton:hover:not(:disabled),\n.ri-resetButton:hover:not(:disabled) {\n background: rgba(40, 40, 40, 0.92);\n color: #fff;\n border-color: rgba(255, 255, 255, 0.25);\n}\n.ri-navButtonSingle:active:not(:disabled),\n.ri-tourButton:active:not(:disabled),\n.ri-resetButton:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-navButtonSingle:disabled,\n.ri-tourButton:disabled,\n.ri-resetButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.ri-navButtonSingle {\n padding: 0 16px;\n font-size: 13px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 8px;\n white-space: nowrap;\n}\n.ri-tourButton {\n padding: 0 12px;\n font-size: 13px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n.ri-tourButton svg {\n width: 15px;\n height: 15px;\n flex-shrink: 0;\n}\n.ri-tourButton--stop {\n border-color: rgba(255, 68, 68, 0.4);\n color: #ff6b6b;\n}\n.ri-tourButton--stop:hover:not(:disabled) {\n background: rgba(255, 68, 68, 0.15);\n border-color: rgba(255, 68, 68, 0.6);\n color: #ff6b6b;\n}\n.ri-tourButton--stop:active:not(:disabled) {\n background: #ff4444;\n color: #fff;\n border-color: #ff4444;\n}\n.ri-tourButton--next {\n border-color: rgba(56, 219, 240, 0.3);\n color: #38dbf0;\n}\n.ri-tourButton--next:hover:not(:disabled) {\n background: rgba(56, 219, 240, 0.12);\n border-color: rgba(56, 219, 240, 0.5);\n color: #38dbf0;\n}\n.ri-tourButton--next:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-resetButton {\n width: 34px;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.ri-resetButton svg {\n width: 15px;\n height: 15px;\n pointer-events: none;\n flex-shrink: 0;\n}\n.ri-resetButton:focus-visible {\n outline: 2px solid rgba(255, 255, 255, 0.5);\n outline-offset: 2px;\n}\n.ri-downloadWrapper {\n position: relative;\n display: inline-flex;\n flex-direction: column;\n align-items: stretch;\n}\n.ri-downloadButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 0 16px;\n font-size: 13px;\n font-weight: 500;\n white-space: nowrap;\n}\n.ri-downloadButton:hover:not(:disabled) {\n background: rgba(0, 0, 0, 0.72);\n border-color: rgba(255, 255, 255, 0.3);\n color: #fff;\n}\n.ri-downloadButton:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-downloadButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.ri-downloadButton__spinner {\n display: inline-block;\n width: 13px;\n height: 13px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: ri-spin 0.7s linear infinite;\n flex-shrink: 0;\n}\n.ri-downloadMenu {\n position: absolute;\n bottom: calc(100% + 6px);\n right: 0;\n min-width: 168px;\n background: rgba(20, 20, 20, 0.92);\n border: 1px solid rgba(255, 255, 255, 0.14);\n border-radius: 10px;\n overflow: hidden;\n z-index: 100;\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n animation: ri-menuReveal 0.12s ease;\n}\n@keyframes ri-menuReveal {\n from {\n opacity: 0;\n transform: translateY(4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n.ri-downloadMenuItem {\n display: flex;\n align-items: center;\n gap: 9px;\n width: 100%;\n padding: 10px 14px;\n font-size: 13px;\n font-weight: 400;\n color: #fff;\n background: transparent;\n border: none;\n cursor: pointer;\n text-align: left;\n transition: background 0.1s ease;\n text-wrap: nowrap;\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.ri-downloadMenuNotice {\n max-width: 220px;\n padding: 8px 14px 11px;\n color: #fca5a5;\n font-size: 11px;\n line-height: 1.4;\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');
|
|
203
|
+
styleInject('.ri-viewer--license-checking,\n.ri-viewer--license-invalid {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n background-color: #f9f9f9;\n}\n.ri-viewer {\n --ri-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --ri-viewer-bg: #1c1c1d;\n --ri-viewer-text: #111827;\n --ri-panel-bg: #ffffff;\n --ri-panel-border: #e5e7eb;\n --ri-panel-title: #111827;\n --ri-panel-muted-text: #4b5563;\n --ri-panel-subtle-text: #9ca3af;\n --ri-panel-surface: #f9fafb;\n --ri-panel-surface-hover: #f3f4f6;\n --ri-panel-surface-border: #e5e7eb;\n --ri-panel-button-text: #1f2937;\n --ri-panel-accent: #2563eb;\n --ri-panel-accent-contrast: #ffffff;\n --ri-license-overlay-bg: rgba(0, 0, 0, 0.04);\n --ri-license-overlay-border: rgba(0, 0, 0, 0.08);\n --ri-license-overlay-text: #888;\n --ri-license-overlay-error-bg: rgba(192, 57, 43, 0.06);\n --ri-license-overlay-error-border: rgba(192, 57, 43, 0.2);\n --ri-license-overlay-error-text: #c0392b;\n --ri-sidepanel-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --ri-sidepanel-bg: var(--ri-databinding-bg, #111318);\n --ri-sidepanel-open-btn-bg: rgba(20, 20, 20, 0.82);\n --ri-sidepanel-open-btn-bg-hover: #27282b7e;\n --ri-sidepanel-border: rgba(255, 255, 255, 0.06);\n --ri-sidepanel-divider: rgba(255, 255, 255, 0.04);\n --ri-sidepanel-border-strong: rgba(255, 255, 255, 0.08);\n --ri-sidepanel-border-active: rgba(255, 255, 255, 0.18);\n --ri-sidepanel-shadow-left: 4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-sidepanel-shadow-right: -4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-sidepanel-text: var(--ri-databinding-text, #f1f5f9);\n --ri-sidepanel-text-emphasis: var(--ri-databinding-text-hover, #cbd5e1);\n --ri-sidepanel-text-muted: var(--ri-databinding-text-muted, #94a3b8);\n --ri-sidepanel-text-subtle: var(--ri-databinding-text-subtle, #64748b);\n --ri-sidepanel-placeholder: var(--ri-databinding-text-faint, #475569);\n --ri-sidepanel-surface: rgba(255, 255, 255, 0.05);\n --ri-sidepanel-surface-hover: rgba(255, 255, 255, 0.1);\n --ri-sidepanel-accent: #3b82f6;\n --ri-sidepanel-accent-soft: rgba(59, 130, 246, 0.12);\n --ri-databinding-bg: #111318;\n --ri-databinding-border: rgba(255, 255, 255, 0.06);\n --ri-databinding-border-strong: rgba(255, 255, 255, 0.08);\n --ri-databinding-border-hover: rgba(255, 255, 255, 0.15);\n --ri-databinding-shadow: 4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-databinding-font-family:\n "Inter",\n "Segoe UI",\n sans-serif;\n --ri-databinding-mono-font-family:\n "JetBrains Mono",\n "Fira Code",\n "Menlo",\n monospace;\n --ri-databinding-text: #f1f5f9;\n --ri-databinding-text-muted: #94a3b8;\n --ri-databinding-text-subtle: #64748b;\n --ri-databinding-text-faint: #475569;\n --ri-databinding-text-hover: #cbd5e1;\n --ri-databinding-surface: rgba(255, 255, 255, 0.03);\n --ri-databinding-surface-hover: rgba(255, 255, 255, 0.06);\n --ri-databinding-accent: #93c5fd;\n --ri-databinding-accent-glow: rgba(147, 197, 253, 0.8);\n --ri-databinding-accent-border: rgba(96, 165, 250, 0.55);\n --ri-databinding-accent-gradient:\n linear-gradient(\n \n 135deg,\n #1e3a8a 0%,\n #1d4ed8 60%,\n #2563eb 100% );\n --ri-databinding-accent-gradient-active:\n linear-gradient(\n \n 135deg,\n #1e3a8a 0%,\n #1e40af 100% );\n display: flex;\n flex-direction: row;\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n background: var(--ri-viewer-bg);\n position: relative;\n color: var(--ri-viewer-text);\n font-family: var(--ri-font-family);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n font-synthesis: none;\n}\n.ri-viewer * {\n box-sizing: border-box;\n}\n.ri-viewerLeft {\n display: flex;\n flex: 1;\n min-height: 0;\n min-width: 0;\n position: relative;\n}\n.ri-viewerRight {\n display: flex;\n flex-shrink: 0;\n min-width: 0;\n}\n.ri-panel {\n width: 280px;\n min-height: 0;\n background: var(--ri-panel-bg);\n border-right: 1px solid var(--ri-panel-border);\n display: flex;\n flex-direction: column;\n padding: 20px;\n overflow-y: auto;\n flex-shrink: 0;\n transition: margin-left 0.3s ease, transform 0.3s ease;\n}\n.ri-panel--open {\n transform: translateX(0);\n margin-left: 0;\n}\n.ri-panel--closed {\n transform: translateX(-100%);\n margin-left: -280px;\n}\n.ri-panelHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 12px;\n}\n.ri-panelTitle {\n margin: 0;\n font-size: 15px;\n font-weight: 700;\n color: var(--ri-panel-title);\n line-height: 1.4;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ri-closeButton {\n border: 0;\n background: transparent;\n color: var(--ri-panel-subtle-text);\n font-size: 22px;\n line-height: 1;\n padding: 0;\n cursor: pointer;\n flex-shrink: 0;\n transition: color 0.2s ease;\n}\n.ri-closeButton:hover {\n color: var(--ri-panel-muted-text);\n}\n.ri-json {\n margin: 0 0 16px;\n padding: 12px;\n background: var(--ri-panel-surface);\n border: 1px solid var(--ri-panel-surface-border);\n border-radius: 12px;\n font-size: 12px;\n line-height: 1.55;\n color: var(--ri-panel-button-text);\n overflow-x: auto;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.ri-divider {\n border-top: 1px solid var(--ri-panel-border);\n margin-bottom: 12px;\n}\n.ri-sectionLabel {\n margin-bottom: 8px;\n font-size: 11px;\n font-weight: 700;\n color: var(--ri-panel-subtle-text);\n text-transform: uppercase;\n letter-spacing: 0.16em;\n}\n.ri-actionList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.ri-actionButton {\n width: 100%;\n border-radius: 8px;\n border: 1px solid var(--ri-panel-surface-border);\n background: var(--ri-panel-surface);\n color: var(--ri-panel-button-text);\n padding: 10px 12px;\n text-align: left;\n font-size: 14px;\n line-height: 1.4;\n cursor: pointer;\n transition:\n background-color 0.2s ease,\n border-color 0.2s ease,\n color 0.2s ease;\n}\n.ri-actionButton:hover {\n background: var(--ri-panel-surface-hover);\n border-color: var(--ri-panel-border);\n}\n.ri-actionButton--active {\n background: var(--ri-panel-accent);\n border-color: var(--ri-panel-accent);\n color: var(--ri-panel-accent-contrast);\n}\n.ri-actionButton--active:hover {\n background: var(--ri-panel-accent);\n border-color: var(--ri-panel-accent);\n}\n.ri-canvasArea {\n flex: 1 1 auto;\n position: relative;\n min-width: 0;\n}\n.ri-canvasArea--measuring {\n cursor: crosshair;\n}\n.ri-canvasArea canvas {\n opacity: 1;\n transition: opacity 0.18s ease;\n}\n.ri-canvasArea--positioning canvas {\n opacity: 0;\n transition: none;\n}\n.ri-bindingsMismatchBanner {\n position: absolute;\n top: 12px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 20;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 7px 12px 7px 10px;\n border-radius: 8px;\n background: rgba(30, 20, 0, 0.88);\n border: 1px solid rgba(251, 191, 36, 0.35);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n color: rgb(251, 191, 36);\n font-size: 12px;\n font-weight: 500;\n line-height: 1.4;\n white-space: nowrap;\n pointer-events: auto;\n max-width: calc(100% - 32px);\n white-space: normal;\n text-align: center;\n}\n.ri-bindingsMismatchBanner__icon {\n flex-shrink: 0;\n}\n.ri-bindingsMismatchBanner__dismiss {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: 4px;\n padding: 2px;\n background: none;\n border: none;\n border-radius: 4px;\n color: rgba(251, 191, 36, 0.7);\n cursor: pointer;\n transition: color 0.15s ease, background 0.15s ease;\n}\n.ri-bindingsMismatchBanner__dismiss:hover {\n color: rgb(251, 191, 36);\n background: rgba(251, 191, 36, 0.12);\n}\n.ri-measureTools,\n.ri-uvCheckerTools {\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 text-wrap: nowrap;\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.ri-downloadMenuNotice {\n max-width: 220px;\n padding: 8px 14px 11px;\n color: #fca5a5;\n font-size: 11px;\n line-height: 1.4;\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.ri-uvCheckerBanner {\n position: absolute;\n top: 12px;\n right: 12px;\n z-index: 20;\n display: flex;\n align-items: center;\n gap: 8px;\n max-width: min(360px, calc(100% - 24px));\n padding: 7px 11px;\n border: 1px solid rgba(244, 114, 182, 0.4);\n border-radius: 8px;\n background: rgba(40, 10, 35, 0.9);\n color: rgb(249, 168, 212);\n font-size: 12px;\n font-weight: 500;\n line-height: 1.4;\n text-align: left;\n pointer-events: none;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n}\n.ri-uvCheckerBanner__icon {\n flex-shrink: 0;\n}\n@media (max-width: 768px) {\n .ri-xrTools {\n gap: 6px;\n }\n .ri-xrButton {\n height: 34px;\n gap: 6px;\n padding: 0 8px;\n font-size: 11px;\n }\n .ri-xrButton svg {\n width: 14px;\n height: 14px;\n }\n .ri-xrButtonPrefix {\n display: none;\n }\n .ri-annotationNavSingle {\n bottom: 100px;\n }\n .ri-navButtonSingle {\n padding: 8px 20px;\n font-size: 13px;\n min-width: 110px;\n }\n .ri-annotationTitleHint {\n padding: 6px 14px;\n font-size: 12px;\n max-width: 250px;\n }\n .ri-viewAnnotationPopup {\n min-width: 160px;\n max-width: 220px;\n padding: 10px 12px;\n }\n .ri-viewAnnotationTitle {\n font-size: 13px;\n }\n .ri-viewAnnotationDescription {\n font-size: 12px;\n }\n .ri-buttonContainer {\n flex-wrap: wrap;\n gap: 6px;\n }\n .ri-tourControls {\n flex-wrap: wrap;\n }\n .ri-rightControls {\n flex-wrap: wrap;\n }\n}\n@media (max-width: 480px) {\n .ri-xrTools {\n gap: 5px;\n }\n .ri-xrButton {\n height: 34px;\n gap: 5px;\n font-size: 10px;\n }\n .ri-xrButton svg {\n width: 13px;\n height: 13px;\n }\n .ri-annotationNavSingle {\n bottom: 80px;\n }\n .ri-navButtonSingle {\n padding: 6px 16px;\n font-size: 12px;\n min-width: 90px;\n gap: 6px;\n }\n .ri-annotationTitleHint {\n padding: 4px 10px;\n font-size: 11px;\n max-width: 200px;\n }\n .ri-navBadge {\n font-size: 10px;\n padding: 1px 6px;\n }\n .ri-tourButton {\n padding: 0 8px;\n font-size: 12px;\n }\n .ri-tourButton span {\n display: none;\n }\n .ri-downloadButton {\n padding: 0 10px;\n font-size: 12px;\n }\n .ri-downloadButton span {\n display: none;\n }\n}\n');
|
|
204
204
|
|
|
205
205
|
// src/components/LicenseOverlay.tsx
|
|
206
206
|
import { AlertTriangle } from "lucide-react";
|
|
@@ -391,9 +391,9 @@ function addTriangleEdges(edges, triangle, indexed) {
|
|
|
391
391
|
addEdge(edges, triangle[1], triangle[2], indexed);
|
|
392
392
|
addEdge(edges, triangle[2], triangle[0], indexed);
|
|
393
393
|
}
|
|
394
|
-
function materialForIndex(
|
|
395
|
-
if (!Array.isArray(
|
|
396
|
-
return
|
|
394
|
+
function materialForIndex(material2, materialIndex) {
|
|
395
|
+
if (!Array.isArray(material2)) return material2;
|
|
396
|
+
return material2[materialIndex] ?? null;
|
|
397
397
|
}
|
|
398
398
|
function collectUvLayouts(scene) {
|
|
399
399
|
const layouts = /* @__PURE__ */ new Map();
|
|
@@ -412,16 +412,16 @@ function collectUvLayouts(scene) {
|
|
|
412
412
|
const groups = geometry.groups.length > 0 ? geometry.groups : [{ start: 0, count: elementCount, materialIndex: 0 }];
|
|
413
413
|
for (const group of groups) {
|
|
414
414
|
const materialIndex = group.materialIndex ?? 0;
|
|
415
|
-
const
|
|
416
|
-
if (!
|
|
417
|
-
let layout = layouts.get(
|
|
415
|
+
const material2 = materialForIndex(mesh.material, materialIndex);
|
|
416
|
+
if (!material2) continue;
|
|
417
|
+
let layout = layouts.get(material2.uuid);
|
|
418
418
|
if (!layout) {
|
|
419
419
|
unnamedMaterialIndex += 1;
|
|
420
420
|
layout = {
|
|
421
|
-
name:
|
|
421
|
+
name: material2.name || `Material ${unnamedMaterialIndex}`,
|
|
422
422
|
layers: /* @__PURE__ */ new Map()
|
|
423
423
|
};
|
|
424
|
-
layouts.set(
|
|
424
|
+
layouts.set(material2.uuid, layout);
|
|
425
425
|
}
|
|
426
426
|
let layer = layout.layers.get(mesh.uuid);
|
|
427
427
|
if (!layer) {
|
|
@@ -607,6 +607,11 @@ var LIGHT_TYPES = [
|
|
|
607
607
|
"point",
|
|
608
608
|
"hemisphere"
|
|
609
609
|
];
|
|
610
|
+
var SCENE_MODEL_RENDERERS = [
|
|
611
|
+
"pbr",
|
|
612
|
+
"matcap",
|
|
613
|
+
"uv-checker"
|
|
614
|
+
];
|
|
610
615
|
var ENVIRONMENT_PRESETS = [
|
|
611
616
|
"apartment",
|
|
612
617
|
"city",
|
|
@@ -1050,6 +1055,7 @@ import {
|
|
|
1050
1055
|
CanvasTexture,
|
|
1051
1056
|
Color as Color2,
|
|
1052
1057
|
NoColorSpace,
|
|
1058
|
+
RepeatWrapping,
|
|
1053
1059
|
SRGBColorSpace,
|
|
1054
1060
|
TextureLoader
|
|
1055
1061
|
} from "three";
|
|
@@ -1194,6 +1200,73 @@ function getMatcapTexture() {
|
|
|
1194
1200
|
sharedMatcapTexture = texture;
|
|
1195
1201
|
return texture;
|
|
1196
1202
|
}
|
|
1203
|
+
var sharedUvCheckerTexture = null;
|
|
1204
|
+
function getUvCheckerTexture() {
|
|
1205
|
+
if (sharedUvCheckerTexture) return sharedUvCheckerTexture;
|
|
1206
|
+
const size = 1024;
|
|
1207
|
+
const cells = 8;
|
|
1208
|
+
const cellSize = size / cells;
|
|
1209
|
+
const colors = ["#ef4444", "#f59e0b", "#22c55e", "#6366f1"];
|
|
1210
|
+
const canvas = document.createElement("canvas");
|
|
1211
|
+
canvas.width = size;
|
|
1212
|
+
canvas.height = size;
|
|
1213
|
+
const ctx = canvas.getContext("2d");
|
|
1214
|
+
ctx.textAlign = "center";
|
|
1215
|
+
ctx.textBaseline = "middle";
|
|
1216
|
+
for (let row = 0; row < cells; row += 1) {
|
|
1217
|
+
for (let column = 0; column < cells; column += 1) {
|
|
1218
|
+
const x = column * cellSize;
|
|
1219
|
+
const y = row * cellSize;
|
|
1220
|
+
const colorIndex = (Math.floor(column / 2) + Math.floor(row / 2)) % 4;
|
|
1221
|
+
ctx.fillStyle = colors[colorIndex];
|
|
1222
|
+
ctx.fillRect(x, y, cellSize, cellSize);
|
|
1223
|
+
ctx.strokeStyle = "rgba(0, 0, 0, 0.28)";
|
|
1224
|
+
ctx.lineWidth = 2;
|
|
1225
|
+
for (let subdivision = 1; subdivision < 4; subdivision += 1) {
|
|
1226
|
+
const offset = subdivision * cellSize / 4;
|
|
1227
|
+
ctx.beginPath();
|
|
1228
|
+
ctx.moveTo(x + offset, y);
|
|
1229
|
+
ctx.lineTo(x + offset, y + cellSize);
|
|
1230
|
+
ctx.moveTo(x, y + offset);
|
|
1231
|
+
ctx.lineTo(x + cellSize, y + offset);
|
|
1232
|
+
ctx.stroke();
|
|
1233
|
+
}
|
|
1234
|
+
ctx.strokeStyle = "#09090b";
|
|
1235
|
+
ctx.lineWidth = 7;
|
|
1236
|
+
ctx.strokeRect(x, y, cellSize, cellSize);
|
|
1237
|
+
const startX = x + cellSize * 0.23;
|
|
1238
|
+
const startY = y + cellSize * 0.72;
|
|
1239
|
+
const endX = x + cellSize * 0.72;
|
|
1240
|
+
const endY = y + cellSize * 0.23;
|
|
1241
|
+
ctx.lineCap = "round";
|
|
1242
|
+
ctx.lineJoin = "round";
|
|
1243
|
+
ctx.beginPath();
|
|
1244
|
+
ctx.moveTo(startX, startY);
|
|
1245
|
+
ctx.lineTo(endX, endY);
|
|
1246
|
+
ctx.lineTo(endX - cellSize * 0.2, endY);
|
|
1247
|
+
ctx.moveTo(endX, endY);
|
|
1248
|
+
ctx.lineTo(endX, endY + cellSize * 0.2);
|
|
1249
|
+
ctx.stroke();
|
|
1250
|
+
const tileNumber = row * cells + column + 1;
|
|
1251
|
+
ctx.font = `700 ${Math.round(cellSize * 0.2)}px ui-monospace, SFMono-Regular, Menlo, monospace`;
|
|
1252
|
+
ctx.fillStyle = "#ffffff";
|
|
1253
|
+
ctx.strokeStyle = "rgba(0, 0, 0, 0.85)";
|
|
1254
|
+
ctx.lineWidth = 6;
|
|
1255
|
+
const label = String(tileNumber).padStart(2, "0");
|
|
1256
|
+
ctx.strokeText(label, x + cellSize / 2, y + cellSize * 0.82);
|
|
1257
|
+
ctx.fillText(label, x + cellSize / 2, y + cellSize * 0.82);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
const texture = new CanvasTexture(canvas);
|
|
1261
|
+
texture.colorSpace = SRGBColorSpace;
|
|
1262
|
+
texture.wrapS = RepeatWrapping;
|
|
1263
|
+
texture.wrapT = RepeatWrapping;
|
|
1264
|
+
texture.flipY = false;
|
|
1265
|
+
texture.anisotropy = 8;
|
|
1266
|
+
texture.needsUpdate = true;
|
|
1267
|
+
sharedUvCheckerTexture = texture;
|
|
1268
|
+
return texture;
|
|
1269
|
+
}
|
|
1197
1270
|
|
|
1198
1271
|
// src/components/viewer/Exposure.tsx
|
|
1199
1272
|
import { forwardRef, useImperativeHandle, useMemo } from "react";
|
|
@@ -2306,14 +2379,14 @@ import {
|
|
|
2306
2379
|
SRGBColorSpace as SRGBColorSpace3,
|
|
2307
2380
|
Vector2 as Vector24
|
|
2308
2381
|
} from "three";
|
|
2309
|
-
function isMeshStandardMaterial(
|
|
2382
|
+
function isMeshStandardMaterial(material2) {
|
|
2310
2383
|
return Boolean(
|
|
2311
|
-
|
|
2384
|
+
material2 && typeof material2 === "object" && "clone" in material2 && "color" in material2 && "metalness" in material2 && "roughness" in material2
|
|
2312
2385
|
);
|
|
2313
2386
|
}
|
|
2314
|
-
function isMeshPhysicalMaterial(
|
|
2387
|
+
function isMeshPhysicalMaterial(material2) {
|
|
2315
2388
|
return Boolean(
|
|
2316
|
-
|
|
2389
|
+
material2 && typeof material2 === "object" && "isMeshPhysicalMaterial" in material2 && material2.isMeshPhysicalMaterial
|
|
2317
2390
|
);
|
|
2318
2391
|
}
|
|
2319
2392
|
function isMeshNode(object) {
|
|
@@ -2349,8 +2422,8 @@ var SRGB_TEXTURE_KEYS = /* @__PURE__ */ new Set([
|
|
|
2349
2422
|
function colorSpaceForTextureKey(key) {
|
|
2350
2423
|
return SRGB_TEXTURE_KEYS.has(key) ? SRGBColorSpace3 : NoColorSpace2;
|
|
2351
2424
|
}
|
|
2352
|
-
function getMaterialTexturePath(
|
|
2353
|
-
return
|
|
2425
|
+
function getMaterialTexturePath(material2, key) {
|
|
2426
|
+
return material2?.[key]?.path;
|
|
2354
2427
|
}
|
|
2355
2428
|
function sideFromConfig(side) {
|
|
2356
2429
|
if (side === "back") return BackSide;
|
|
@@ -2384,10 +2457,10 @@ function getClipLoopMode(clipConfig) {
|
|
|
2384
2457
|
|
|
2385
2458
|
// src/components/model/materialFactory.ts
|
|
2386
2459
|
import { Color as Color6, MeshPhysicalMaterial as MeshPhysicalMaterial2, MeshStandardMaterial as MeshStandardMaterial2, Texture as Texture2 } from "three";
|
|
2387
|
-
function toStandardMaterial(
|
|
2388
|
-
if (isMeshStandardMaterial(
|
|
2389
|
-
if (!
|
|
2390
|
-
const src =
|
|
2460
|
+
function toStandardMaterial(material2) {
|
|
2461
|
+
if (isMeshStandardMaterial(material2)) return material2;
|
|
2462
|
+
if (!material2 || typeof material2 !== "object") return null;
|
|
2463
|
+
const src = material2;
|
|
2391
2464
|
const std = new MeshStandardMaterial2();
|
|
2392
2465
|
if (typeof src.name === "string") std.name = src.name;
|
|
2393
2466
|
std.metalness = 0;
|
|
@@ -2573,69 +2646,69 @@ import {
|
|
|
2573
2646
|
NormalBlending as NormalBlending2,
|
|
2574
2647
|
SRGBColorSpace as SRGBColorSpace4
|
|
2575
2648
|
} from "three";
|
|
2576
|
-
function applyMaterialConfig(
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
toSafeVector2(
|
|
2649
|
+
function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
|
|
2650
|
+
material2.map = material2.userData.baseMap ?? null;
|
|
2651
|
+
material2.color.copy(material2.userData.baseColor);
|
|
2652
|
+
material2.metalness = material2.userData.baseMetalness ?? material2.metalness;
|
|
2653
|
+
material2.metalnessMap = material2.userData.baseMetalnessMap ?? null;
|
|
2654
|
+
material2.roughness = material2.userData.baseRoughness ?? material2.roughness;
|
|
2655
|
+
material2.roughnessMap = material2.userData.baseRoughnessMap ?? null;
|
|
2656
|
+
material2.emissive.copy(material2.userData.baseEmissive ?? new Color7(0, 0, 0));
|
|
2657
|
+
material2.emissiveIntensity = material2.userData.baseEmissiveIntensity ?? material2.emissiveIntensity;
|
|
2658
|
+
material2.emissiveMap = material2.userData.baseEmissiveMap ?? null;
|
|
2659
|
+
material2.opacity = material2.userData.baseOpacity ?? 1;
|
|
2660
|
+
material2.transparent = material2.userData.baseTransparent ?? false;
|
|
2661
|
+
material2.alphaMap = material2.userData.baseAlphaMap ?? null;
|
|
2662
|
+
material2.alphaTest = material2.userData.baseAlphaTest ?? 0;
|
|
2663
|
+
material2.alphaHash = material2.userData.baseAlphaHash ?? false;
|
|
2664
|
+
material2.dithering = material2.userData.baseDithering ?? false;
|
|
2665
|
+
material2.blending = material2.userData.baseBlending ?? NormalBlending2;
|
|
2666
|
+
material2.depthWrite = material2.userData.baseDepthWrite ?? true;
|
|
2667
|
+
material2.side = material2.userData.baseSide ?? FrontSide2;
|
|
2668
|
+
material2.normalMap = material2.userData.baseNormalMap ?? null;
|
|
2669
|
+
material2.normalScale.copy(toSafeVector2(material2.userData.baseNormalScale));
|
|
2670
|
+
material2.bumpMap = material2.userData.baseBumpMap ?? null;
|
|
2671
|
+
material2.bumpScale = material2.userData.baseBumpScale ?? material2.bumpScale;
|
|
2672
|
+
material2.aoMap = material2.userData.baseAoMap ?? null;
|
|
2673
|
+
material2.aoMapIntensity = material2.userData.baseAoMapIntensity ?? material2.aoMapIntensity;
|
|
2674
|
+
material2.displacementMap = material2.userData.baseDisplacementMap ?? null;
|
|
2675
|
+
material2.displacementScale = material2.userData.baseDisplacementScale ?? material2.displacementScale;
|
|
2676
|
+
material2.displacementBias = material2.userData.baseDisplacementBias ?? material2.displacementBias;
|
|
2677
|
+
material2.clearcoat = material2.userData.baseClearcoat ?? material2.clearcoat;
|
|
2678
|
+
material2.clearcoatMap = material2.userData.baseClearcoatMap ?? null;
|
|
2679
|
+
material2.clearcoatRoughness = material2.userData.baseClearcoatRoughness ?? material2.clearcoatRoughness;
|
|
2680
|
+
material2.clearcoatRoughnessMap = material2.userData.baseClearcoatRoughnessMap ?? null;
|
|
2681
|
+
material2.clearcoatNormalMap = material2.userData.baseClearcoatNormalMap ?? null;
|
|
2682
|
+
material2.clearcoatNormalScale.copy(
|
|
2683
|
+
toSafeVector2(material2.userData.baseClearcoatNormalScale)
|
|
2611
2684
|
);
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2685
|
+
material2.sheen = material2.userData.baseSheen ?? material2.sheen;
|
|
2686
|
+
material2.sheenColor.copy(
|
|
2687
|
+
material2.userData.baseSheenColor ?? new Color7(0, 0, 0)
|
|
2615
2688
|
);
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2689
|
+
material2.sheenColorMap = material2.userData.baseSheenColorMap ?? null;
|
|
2690
|
+
material2.sheenRoughness = material2.userData.baseSheenRoughness ?? material2.sheenRoughness;
|
|
2691
|
+
material2.sheenRoughnessMap = material2.userData.baseSheenRoughnessMap ?? null;
|
|
2692
|
+
material2.anisotropy = material2.userData.baseAnisotropy ?? material2.anisotropy;
|
|
2693
|
+
material2.anisotropyRotation = material2.userData.baseAnisotropyRotation ?? material2.anisotropyRotation;
|
|
2694
|
+
material2.anisotropyMap = material2.userData.baseAnisotropyMap ?? null;
|
|
2695
|
+
material2.transmission = material2.userData.baseTransmission ?? material2.transmission;
|
|
2696
|
+
material2.transmissionMap = material2.userData.baseTransmissionMap ?? null;
|
|
2697
|
+
material2.thickness = material2.userData.baseThickness ?? material2.thickness;
|
|
2698
|
+
material2.thicknessMap = material2.userData.baseThicknessMap ?? null;
|
|
2699
|
+
material2.attenuationColor.copy(
|
|
2700
|
+
material2.userData.baseAttenuationColor ?? new Color7("#ffffff")
|
|
2628
2701
|
);
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2702
|
+
material2.attenuationDistance = material2.userData.baseAttenuationDistance ?? material2.attenuationDistance;
|
|
2703
|
+
material2.reflectivity = material2.userData.baseReflectivity ?? material2.reflectivity;
|
|
2704
|
+
material2.specularIntensity = material2.userData.baseSpecularIntensity ?? material2.specularIntensity;
|
|
2705
|
+
material2.specularIntensityMap = material2.userData.baseSpecularIntensityMap ?? null;
|
|
2706
|
+
material2.specularColor.copy(
|
|
2707
|
+
material2.userData.baseSpecularColor ?? new Color7("#ffffff")
|
|
2635
2708
|
);
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2709
|
+
material2.specularColorMap = material2.userData.baseSpecularColorMap ?? null;
|
|
2710
|
+
material2.flatShading = material2.userData.baseFlatShading ?? material2.flatShading;
|
|
2711
|
+
material2.envMapIntensity = material2.userData.baseEnvMapIntensity ?? material2.envMapIntensity;
|
|
2639
2712
|
const texturePath = getMaterialTexturePath(configuredMaterial, "texture");
|
|
2640
2713
|
const metalnessMapPath = getMaterialTexturePath(
|
|
2641
2714
|
configuredMaterial,
|
|
@@ -2698,180 +2771,180 @@ function applyMaterialConfig(material, configuredMaterial, resolvedBaseColor) {
|
|
|
2698
2771
|
"specularColorMap"
|
|
2699
2772
|
);
|
|
2700
2773
|
if (texturePath) {
|
|
2701
|
-
|
|
2702
|
-
|
|
2774
|
+
material2.map = getOrCreateTexture(texturePath, SRGBColorSpace4);
|
|
2775
|
+
material2.color.set(resolvedBaseColor ?? "#ffffff");
|
|
2703
2776
|
} else if (resolvedBaseColor) {
|
|
2704
|
-
|
|
2705
|
-
|
|
2777
|
+
material2.map = null;
|
|
2778
|
+
material2.color.set(resolvedBaseColor);
|
|
2706
2779
|
}
|
|
2707
2780
|
if (metalnessMapPath) {
|
|
2708
|
-
|
|
2781
|
+
material2.metalnessMap = getOrCreateTexture(metalnessMapPath);
|
|
2709
2782
|
}
|
|
2710
2783
|
if (configuredMaterial?.metalness !== void 0) {
|
|
2711
|
-
|
|
2784
|
+
material2.metalness = configuredMaterial.metalness;
|
|
2712
2785
|
}
|
|
2713
2786
|
if (roughnessMapPath) {
|
|
2714
|
-
|
|
2787
|
+
material2.roughnessMap = getOrCreateTexture(roughnessMapPath);
|
|
2715
2788
|
}
|
|
2716
2789
|
if (configuredMaterial?.roughness !== void 0) {
|
|
2717
|
-
|
|
2790
|
+
material2.roughness = configuredMaterial.roughness;
|
|
2718
2791
|
}
|
|
2719
2792
|
if (configuredMaterial?.emissive) {
|
|
2720
|
-
|
|
2793
|
+
material2.emissive.set(configuredMaterial.emissive);
|
|
2721
2794
|
} else if (emissiveMapPath) {
|
|
2722
|
-
|
|
2795
|
+
material2.emissive.set("#ffffff");
|
|
2723
2796
|
}
|
|
2724
2797
|
if (configuredMaterial?.emissiveIntensity !== void 0) {
|
|
2725
|
-
|
|
2798
|
+
material2.emissiveIntensity = configuredMaterial.emissiveIntensity;
|
|
2726
2799
|
}
|
|
2727
2800
|
if (emissiveMapPath) {
|
|
2728
|
-
|
|
2801
|
+
material2.emissiveMap = getOrCreateTexture(emissiveMapPath, SRGBColorSpace4);
|
|
2729
2802
|
}
|
|
2730
2803
|
if (configuredMaterial?.opacity !== void 0) {
|
|
2731
|
-
|
|
2804
|
+
material2.opacity = configuredMaterial.opacity;
|
|
2732
2805
|
}
|
|
2733
2806
|
if (alphaMapPath) {
|
|
2734
|
-
|
|
2807
|
+
material2.alphaMap = getOrCreateTexture(alphaMapPath);
|
|
2735
2808
|
}
|
|
2736
2809
|
if (configuredMaterial?.alphaTest !== void 0) {
|
|
2737
|
-
|
|
2810
|
+
material2.alphaTest = configuredMaterial.alphaTest;
|
|
2738
2811
|
}
|
|
2739
2812
|
if (configuredMaterial?.alphaHash !== void 0) {
|
|
2740
|
-
|
|
2813
|
+
material2.alphaHash = configuredMaterial.alphaHash;
|
|
2741
2814
|
}
|
|
2742
2815
|
if (configuredMaterial?.dithering !== void 0) {
|
|
2743
|
-
|
|
2816
|
+
material2.dithering = configuredMaterial.dithering;
|
|
2744
2817
|
}
|
|
2745
2818
|
if (configuredMaterial?.blendingMode) {
|
|
2746
|
-
|
|
2747
|
-
|
|
2819
|
+
material2.blending = blendingFromConfig(configuredMaterial.blendingMode);
|
|
2820
|
+
material2.depthWrite = configuredMaterial.blendingMode !== "additive";
|
|
2748
2821
|
}
|
|
2749
2822
|
if (configuredMaterial?.side) {
|
|
2750
|
-
|
|
2823
|
+
material2.side = sideFromConfig(configuredMaterial.side);
|
|
2751
2824
|
}
|
|
2752
2825
|
if (normalMapPath) {
|
|
2753
|
-
|
|
2826
|
+
material2.normalMap = getOrCreateTexture(normalMapPath);
|
|
2754
2827
|
}
|
|
2755
2828
|
if (normalMapPath || configuredMaterial?.normalScale !== void 0) {
|
|
2756
|
-
const normalScale = configuredMaterial?.normalScale ?? Math.abs(toSafeVector2(
|
|
2757
|
-
|
|
2829
|
+
const normalScale = configuredMaterial?.normalScale ?? Math.abs(toSafeVector2(material2.userData.baseNormalScale).x);
|
|
2830
|
+
material2.normalScale.set(
|
|
2758
2831
|
normalScale,
|
|
2759
2832
|
configuredMaterial?.flipNormalY ? -normalScale : normalScale
|
|
2760
2833
|
);
|
|
2761
2834
|
}
|
|
2762
2835
|
if (bumpMapPath) {
|
|
2763
|
-
|
|
2836
|
+
material2.bumpMap = getOrCreateTexture(bumpMapPath);
|
|
2764
2837
|
}
|
|
2765
2838
|
if (configuredMaterial?.bumpScale !== void 0) {
|
|
2766
|
-
|
|
2839
|
+
material2.bumpScale = configuredMaterial.bumpScale;
|
|
2767
2840
|
}
|
|
2768
2841
|
if (aoMapPath) {
|
|
2769
|
-
|
|
2842
|
+
material2.aoMap = getOrCreateTexture(aoMapPath);
|
|
2770
2843
|
}
|
|
2771
2844
|
if (configuredMaterial?.aoMapIntensity !== void 0) {
|
|
2772
|
-
|
|
2845
|
+
material2.aoMapIntensity = configuredMaterial.aoMapIntensity;
|
|
2773
2846
|
}
|
|
2774
2847
|
if (displacementMapPath) {
|
|
2775
|
-
|
|
2848
|
+
material2.displacementMap = getOrCreateTexture(displacementMapPath);
|
|
2776
2849
|
}
|
|
2777
2850
|
if (configuredMaterial?.displacementScale !== void 0) {
|
|
2778
|
-
|
|
2851
|
+
material2.displacementScale = configuredMaterial.displacementScale;
|
|
2779
2852
|
}
|
|
2780
2853
|
if (configuredMaterial?.displacementBias !== void 0) {
|
|
2781
|
-
|
|
2854
|
+
material2.displacementBias = configuredMaterial.displacementBias;
|
|
2782
2855
|
}
|
|
2783
2856
|
if (clearcoatMapPath) {
|
|
2784
|
-
|
|
2857
|
+
material2.clearcoatMap = getOrCreateTexture(clearcoatMapPath);
|
|
2785
2858
|
}
|
|
2786
2859
|
if (configuredMaterial?.clearcoat !== void 0) {
|
|
2787
|
-
|
|
2860
|
+
material2.clearcoat = configuredMaterial.clearcoat;
|
|
2788
2861
|
}
|
|
2789
2862
|
if (clearcoatRoughnessMapPath) {
|
|
2790
|
-
|
|
2863
|
+
material2.clearcoatRoughnessMap = getOrCreateTexture(
|
|
2791
2864
|
clearcoatRoughnessMapPath
|
|
2792
2865
|
);
|
|
2793
2866
|
}
|
|
2794
2867
|
if (configuredMaterial?.clearcoatRoughness !== void 0) {
|
|
2795
|
-
|
|
2868
|
+
material2.clearcoatRoughness = configuredMaterial.clearcoatRoughness;
|
|
2796
2869
|
}
|
|
2797
2870
|
if (clearcoatNormalMapPath) {
|
|
2798
|
-
|
|
2871
|
+
material2.clearcoatNormalMap = getOrCreateTexture(clearcoatNormalMapPath);
|
|
2799
2872
|
}
|
|
2800
2873
|
if (clearcoatNormalMapPath || configuredMaterial?.clearcoatNormalScale !== void 0) {
|
|
2801
|
-
const clearcoatNormalScale = configuredMaterial?.clearcoatNormalScale ?? Math.abs(toSafeVector2(
|
|
2802
|
-
|
|
2874
|
+
const clearcoatNormalScale = configuredMaterial?.clearcoatNormalScale ?? Math.abs(toSafeVector2(material2.userData.baseClearcoatNormalScale).x);
|
|
2875
|
+
material2.clearcoatNormalScale.set(
|
|
2803
2876
|
clearcoatNormalScale,
|
|
2804
2877
|
configuredMaterial?.flipClearcoatNormalY ? -clearcoatNormalScale : clearcoatNormalScale
|
|
2805
2878
|
);
|
|
2806
2879
|
}
|
|
2807
2880
|
if (configuredMaterial?.sheen !== void 0) {
|
|
2808
|
-
|
|
2881
|
+
material2.sheen = configuredMaterial.sheen;
|
|
2809
2882
|
}
|
|
2810
2883
|
if (configuredMaterial?.sheenColor) {
|
|
2811
|
-
|
|
2884
|
+
material2.sheenColor.set(configuredMaterial.sheenColor);
|
|
2812
2885
|
}
|
|
2813
2886
|
if (sheenColorMapPath) {
|
|
2814
|
-
|
|
2887
|
+
material2.sheenColorMap = getOrCreateTexture(
|
|
2815
2888
|
sheenColorMapPath,
|
|
2816
2889
|
SRGBColorSpace4
|
|
2817
2890
|
);
|
|
2818
2891
|
}
|
|
2819
2892
|
if (configuredMaterial?.sheenRoughness !== void 0) {
|
|
2820
|
-
|
|
2893
|
+
material2.sheenRoughness = configuredMaterial.sheenRoughness;
|
|
2821
2894
|
}
|
|
2822
2895
|
if (sheenRoughnessMapPath) {
|
|
2823
|
-
|
|
2896
|
+
material2.sheenRoughnessMap = getOrCreateTexture(sheenRoughnessMapPath);
|
|
2824
2897
|
}
|
|
2825
2898
|
if (configuredMaterial?.anisotropy !== void 0) {
|
|
2826
|
-
|
|
2899
|
+
material2.anisotropy = configuredMaterial.anisotropy;
|
|
2827
2900
|
}
|
|
2828
2901
|
if (configuredMaterial?.anisotropyRotation !== void 0) {
|
|
2829
|
-
|
|
2902
|
+
material2.anisotropyRotation = configuredMaterial.anisotropyRotation;
|
|
2830
2903
|
}
|
|
2831
2904
|
if (anisotropyMapPath) {
|
|
2832
|
-
|
|
2905
|
+
material2.anisotropyMap = getOrCreateTexture(anisotropyMapPath);
|
|
2833
2906
|
}
|
|
2834
2907
|
if (configuredMaterial?.transmission !== void 0) {
|
|
2835
|
-
|
|
2908
|
+
material2.transmission = configuredMaterial.transmission;
|
|
2836
2909
|
}
|
|
2837
2910
|
if (transmissionMapPath) {
|
|
2838
|
-
|
|
2911
|
+
material2.transmissionMap = getOrCreateTexture(transmissionMapPath);
|
|
2839
2912
|
}
|
|
2840
2913
|
if (configuredMaterial?.thickness !== void 0) {
|
|
2841
|
-
|
|
2914
|
+
material2.thickness = configuredMaterial.thickness;
|
|
2842
2915
|
}
|
|
2843
2916
|
if (thicknessMapPath) {
|
|
2844
|
-
|
|
2917
|
+
material2.thicknessMap = getOrCreateTexture(thicknessMapPath);
|
|
2845
2918
|
}
|
|
2846
2919
|
if (configuredMaterial?.attenuationColor) {
|
|
2847
|
-
|
|
2920
|
+
material2.attenuationColor.set(configuredMaterial.attenuationColor);
|
|
2848
2921
|
}
|
|
2849
2922
|
if (configuredMaterial?.attenuationDistance !== void 0) {
|
|
2850
|
-
|
|
2923
|
+
material2.attenuationDistance = configuredMaterial.attenuationDistance;
|
|
2851
2924
|
}
|
|
2852
2925
|
if (configuredMaterial?.reflectivity !== void 0) {
|
|
2853
|
-
|
|
2926
|
+
material2.reflectivity = configuredMaterial.reflectivity;
|
|
2854
2927
|
}
|
|
2855
2928
|
if (configuredMaterial?.specularIntensity !== void 0) {
|
|
2856
|
-
|
|
2929
|
+
material2.specularIntensity = configuredMaterial.specularIntensity;
|
|
2857
2930
|
}
|
|
2858
2931
|
if (specularIntensityMapPath) {
|
|
2859
|
-
|
|
2932
|
+
material2.specularIntensityMap = getOrCreateTexture(
|
|
2860
2933
|
specularIntensityMapPath
|
|
2861
2934
|
);
|
|
2862
2935
|
}
|
|
2863
2936
|
if (configuredMaterial?.specularColor) {
|
|
2864
|
-
|
|
2937
|
+
material2.specularColor.set(configuredMaterial.specularColor);
|
|
2865
2938
|
}
|
|
2866
2939
|
if (specularColorMapPath) {
|
|
2867
|
-
|
|
2940
|
+
material2.specularColorMap = getOrCreateTexture(
|
|
2868
2941
|
specularColorMapPath,
|
|
2869
2942
|
SRGBColorSpace4
|
|
2870
2943
|
);
|
|
2871
2944
|
}
|
|
2872
|
-
|
|
2873
|
-
if (!
|
|
2874
|
-
|
|
2945
|
+
material2.transparent = material2.opacity < 1 || material2.transmission > 0 || material2.blending !== NormalBlending2 || Boolean((alphaMapPath || material2.alphaMap) && material2.alphaTest <= 0);
|
|
2946
|
+
if (!material2.transparent) {
|
|
2947
|
+
material2.depthWrite = true;
|
|
2875
2948
|
}
|
|
2876
2949
|
}
|
|
2877
2950
|
|
|
@@ -3014,8 +3087,8 @@ function Model({
|
|
|
3014
3087
|
}, [perNodeMaterials, materialRefsOut]);
|
|
3015
3088
|
useEffect7(() => {
|
|
3016
3089
|
return () => {
|
|
3017
|
-
for (const
|
|
3018
|
-
|
|
3090
|
+
for (const material2 of Object.values(perNodeMaterials)) {
|
|
3091
|
+
material2.dispose();
|
|
3019
3092
|
}
|
|
3020
3093
|
};
|
|
3021
3094
|
}, [perNodeMaterials]);
|
|
@@ -3027,15 +3100,15 @@ function Model({
|
|
|
3027
3100
|
const perNodeBasicMaterials = useMemo7(() => {
|
|
3028
3101
|
const map = {};
|
|
3029
3102
|
if (shadingMode !== "unlit") return map;
|
|
3030
|
-
for (const [nodeName,
|
|
3103
|
+
for (const [nodeName, material2] of Object.entries(perNodeMaterials)) {
|
|
3031
3104
|
const basic = new MeshBasicMaterial();
|
|
3032
|
-
basic.color.copy(
|
|
3033
|
-
basic.map =
|
|
3034
|
-
basic.alphaMap =
|
|
3035
|
-
basic.alphaTest =
|
|
3036
|
-
basic.transparent =
|
|
3037
|
-
basic.opacity =
|
|
3038
|
-
basic.side =
|
|
3105
|
+
basic.color.copy(material2.color);
|
|
3106
|
+
basic.map = material2.map;
|
|
3107
|
+
basic.alphaMap = material2.alphaMap;
|
|
3108
|
+
basic.alphaTest = material2.alphaTest;
|
|
3109
|
+
basic.transparent = material2.transparent;
|
|
3110
|
+
basic.opacity = material2.opacity;
|
|
3111
|
+
basic.side = material2.side;
|
|
3039
3112
|
map[nodeName] = basic;
|
|
3040
3113
|
}
|
|
3041
3114
|
return map;
|
|
@@ -3052,25 +3125,25 @@ function Model({
|
|
|
3052
3125
|
const map = {};
|
|
3053
3126
|
if (shadingMode !== "lit" || rendererMode !== "matcap") return map;
|
|
3054
3127
|
const matcapTexture = getMatcapTexture();
|
|
3055
|
-
for (const [nodeName,
|
|
3128
|
+
for (const [nodeName, material2] of Object.entries(perNodeMaterials)) {
|
|
3056
3129
|
const matcap = new MeshMatcapMaterial();
|
|
3057
3130
|
matcap.matcap = matcapTexture;
|
|
3058
|
-
matcap.color.copy(
|
|
3059
|
-
matcap.alphaMap =
|
|
3060
|
-
matcap.alphaTest =
|
|
3061
|
-
matcap.side =
|
|
3131
|
+
matcap.color.copy(material2.color);
|
|
3132
|
+
matcap.alphaMap = material2.alphaMap;
|
|
3133
|
+
matcap.alphaTest = material2.alphaTest;
|
|
3134
|
+
matcap.side = material2.side;
|
|
3062
3135
|
map[nodeName] = matcap;
|
|
3063
3136
|
}
|
|
3064
3137
|
return map;
|
|
3065
3138
|
}, [perNodeMaterials, shadingMode, rendererMode]);
|
|
3066
3139
|
useEffect7(() => {
|
|
3067
3140
|
return () => {
|
|
3068
|
-
for (const
|
|
3069
|
-
|
|
3070
|
-
for (const
|
|
3071
|
-
|
|
3072
|
-
for (const
|
|
3073
|
-
|
|
3141
|
+
for (const material2 of Object.values(perNodeBasicMaterials))
|
|
3142
|
+
material2.dispose();
|
|
3143
|
+
for (const material2 of Object.values(perNodeNormalMaterials))
|
|
3144
|
+
material2.dispose();
|
|
3145
|
+
for (const material2 of Object.values(perNodeMatcapMaterials))
|
|
3146
|
+
material2.dispose();
|
|
3074
3147
|
};
|
|
3075
3148
|
}, [perNodeBasicMaterials, perNodeNormalMaterials, perNodeMatcapMaterials]);
|
|
3076
3149
|
const materialForNode = (nodeName) => {
|
|
@@ -3116,11 +3189,13 @@ function Model({
|
|
|
3116
3189
|
const node = nodeMap[glbNodeName];
|
|
3117
3190
|
if (!isMeshNode(node)) continue;
|
|
3118
3191
|
node.visible = binding.visible !== false;
|
|
3119
|
-
const
|
|
3120
|
-
if (!
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3192
|
+
const material2 = perNodeMaterials[nodeName];
|
|
3193
|
+
if (!material2) continue;
|
|
3194
|
+
if (rendererMode !== "uv-checker") {
|
|
3195
|
+
const activeMaterial = materialForNode(nodeName);
|
|
3196
|
+
if (activeMaterial) {
|
|
3197
|
+
node.material = activeMaterial;
|
|
3198
|
+
}
|
|
3124
3199
|
}
|
|
3125
3200
|
if (binding.visible === false) continue;
|
|
3126
3201
|
const basicMaterial = perNodeBasicMaterials[nodeName];
|
|
@@ -3152,19 +3227,19 @@ function Model({
|
|
|
3152
3227
|
delete appliedTextureUrls.current[nodeName];
|
|
3153
3228
|
}
|
|
3154
3229
|
const resolvedBaseColor = itemMat?.hex ?? configuredMaterial?.baseColor;
|
|
3155
|
-
applyMaterialConfig(
|
|
3230
|
+
applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor);
|
|
3156
3231
|
if (isHighlightedByHover) {
|
|
3157
|
-
|
|
3232
|
+
material2.emissive?.add(HIGHLIGHT_EMISSIVE_TINT);
|
|
3158
3233
|
}
|
|
3159
|
-
|
|
3234
|
+
material2.needsUpdate = true;
|
|
3160
3235
|
if (basicMaterial) {
|
|
3161
|
-
basicMaterial.color.copy(
|
|
3162
|
-
basicMaterial.map =
|
|
3163
|
-
basicMaterial.alphaMap =
|
|
3164
|
-
basicMaterial.alphaTest =
|
|
3165
|
-
basicMaterial.transparent =
|
|
3166
|
-
basicMaterial.opacity =
|
|
3167
|
-
basicMaterial.side =
|
|
3236
|
+
basicMaterial.color.copy(material2.color);
|
|
3237
|
+
basicMaterial.map = material2.map;
|
|
3238
|
+
basicMaterial.alphaMap = material2.alphaMap;
|
|
3239
|
+
basicMaterial.alphaTest = material2.alphaTest;
|
|
3240
|
+
basicMaterial.transparent = material2.transparent;
|
|
3241
|
+
basicMaterial.opacity = material2.opacity;
|
|
3242
|
+
basicMaterial.side = material2.side;
|
|
3168
3243
|
basicMaterial.needsUpdate = true;
|
|
3169
3244
|
}
|
|
3170
3245
|
if (normalMaterial) normalMaterial.needsUpdate = true;
|
|
@@ -3172,14 +3247,14 @@ function Model({
|
|
|
3172
3247
|
if (isHighlightedByHover || isSelected) {
|
|
3173
3248
|
matcapMaterial.color.set(HIGHLIGHT.color);
|
|
3174
3249
|
} else {
|
|
3175
|
-
matcapMaterial.color.copy(
|
|
3250
|
+
matcapMaterial.color.copy(material2.color);
|
|
3176
3251
|
}
|
|
3177
|
-
matcapMaterial.map =
|
|
3178
|
-
matcapMaterial.alphaMap =
|
|
3179
|
-
matcapMaterial.alphaTest =
|
|
3180
|
-
matcapMaterial.transparent =
|
|
3181
|
-
matcapMaterial.opacity =
|
|
3182
|
-
matcapMaterial.side =
|
|
3252
|
+
matcapMaterial.map = material2.map;
|
|
3253
|
+
matcapMaterial.alphaMap = material2.alphaMap;
|
|
3254
|
+
matcapMaterial.alphaTest = material2.alphaTest;
|
|
3255
|
+
matcapMaterial.transparent = material2.transparent;
|
|
3256
|
+
matcapMaterial.opacity = material2.opacity;
|
|
3257
|
+
matcapMaterial.side = material2.side;
|
|
3183
3258
|
matcapMaterial.needsUpdate = true;
|
|
3184
3259
|
}
|
|
3185
3260
|
}
|
|
@@ -3215,6 +3290,39 @@ function Model({
|
|
|
3215
3290
|
rendererMode,
|
|
3216
3291
|
invalidate
|
|
3217
3292
|
]);
|
|
3293
|
+
useEffect7(() => {
|
|
3294
|
+
if (rendererMode !== "uv-checker") return;
|
|
3295
|
+
const checkerTexture = getUvCheckerTexture();
|
|
3296
|
+
const swaps = [];
|
|
3297
|
+
scene.traverse((object) => {
|
|
3298
|
+
if (!isMeshNode(object)) return;
|
|
3299
|
+
const original = object.material;
|
|
3300
|
+
const sourceMaterials = Array.isArray(original) ? original : [original];
|
|
3301
|
+
const hasUv0 = Boolean(object.geometry.getAttribute("uv"));
|
|
3302
|
+
const diagnosticMaterials = sourceMaterials.map((source, index) => {
|
|
3303
|
+
const material2 = new MeshBasicMaterial({
|
|
3304
|
+
color: hasUv0 ? "#ffffff" : "#ff00ff",
|
|
3305
|
+
map: hasUv0 ? checkerTexture : null,
|
|
3306
|
+
side: source.side
|
|
3307
|
+
});
|
|
3308
|
+
material2.name = `${object.name || "mesh"}-uv-checker-${index}`;
|
|
3309
|
+
material2.toneMapped = false;
|
|
3310
|
+
return material2;
|
|
3311
|
+
});
|
|
3312
|
+
const diagnostic = Array.isArray(original) ? diagnosticMaterials : diagnosticMaterials[0];
|
|
3313
|
+
object.material = diagnostic;
|
|
3314
|
+
swaps.push({ node: object, original, diagnostic });
|
|
3315
|
+
});
|
|
3316
|
+
invalidate();
|
|
3317
|
+
return () => {
|
|
3318
|
+
for (const { node, original, diagnostic } of swaps) {
|
|
3319
|
+
node.material = original;
|
|
3320
|
+
const materials = Array.isArray(diagnostic) ? diagnostic : [diagnostic];
|
|
3321
|
+
for (const material2 of materials) material2.dispose();
|
|
3322
|
+
}
|
|
3323
|
+
invalidate();
|
|
3324
|
+
};
|
|
3325
|
+
}, [scene, rendererMode, invalidate]);
|
|
3218
3326
|
useEffect7(() => {
|
|
3219
3327
|
if (!wireframeEnabled) return;
|
|
3220
3328
|
const added = [];
|
|
@@ -5693,7 +5801,8 @@ import {
|
|
|
5693
5801
|
Pause,
|
|
5694
5802
|
Smartphone,
|
|
5695
5803
|
PencilRuler,
|
|
5696
|
-
RulerDimensionLine
|
|
5804
|
+
RulerDimensionLine,
|
|
5805
|
+
Grid3X3
|
|
5697
5806
|
} from "lucide-react";
|
|
5698
5807
|
|
|
5699
5808
|
// src/components/BindingBuilder/BindingBuilderContext.tsx
|
|
@@ -5848,6 +5957,16 @@ function computeMeasureBox(scene) {
|
|
|
5848
5957
|
size: [size.x, size.y, size.z]
|
|
5849
5958
|
};
|
|
5850
5959
|
}
|
|
5960
|
+
function computeUvMeshStats(scene) {
|
|
5961
|
+
const stats = { total: 0, missing: 0 };
|
|
5962
|
+
scene.traverse((node) => {
|
|
5963
|
+
const mesh = node;
|
|
5964
|
+
if (!mesh.isMesh || !mesh.geometry) return;
|
|
5965
|
+
stats.total += 1;
|
|
5966
|
+
if (!mesh.geometry.getAttribute("uv")) stats.missing += 1;
|
|
5967
|
+
});
|
|
5968
|
+
return stats;
|
|
5969
|
+
}
|
|
5851
5970
|
function computeModelBounds(scene) {
|
|
5852
5971
|
const box = new Box34().setFromObject(scene);
|
|
5853
5972
|
if (box.isEmpty()) return null;
|
|
@@ -5926,6 +6045,7 @@ function ModelViewer({
|
|
|
5926
6045
|
ktx2TranscoderPath,
|
|
5927
6046
|
meshopt = true,
|
|
5928
6047
|
showMeasureTools = false,
|
|
6048
|
+
showUvCheckerButton = false,
|
|
5929
6049
|
showExplodeControls = false,
|
|
5930
6050
|
cinematic,
|
|
5931
6051
|
measurementUnit = "m",
|
|
@@ -5942,6 +6062,24 @@ function ModelViewer({
|
|
|
5942
6062
|
() => sceneConfig ?? DEFAULT_SCENE_CONFIG,
|
|
5943
6063
|
[sceneConfig]
|
|
5944
6064
|
);
|
|
6065
|
+
const [uvCheckerOverride, setUvCheckerOverride] = useState10(
|
|
6066
|
+
null
|
|
6067
|
+
);
|
|
6068
|
+
const configuredUvChecker = resolvedSceneConfig.model.renderer === "uv-checker";
|
|
6069
|
+
const uvCheckerActive = showUvCheckerButton ? uvCheckerOverride ?? configuredUvChecker : configuredUvChecker;
|
|
6070
|
+
const renderedSceneConfig = useMemo13(() => {
|
|
6071
|
+
const renderer = uvCheckerActive ? "uv-checker" : configuredUvChecker ? "pbr" : resolvedSceneConfig.model.renderer;
|
|
6072
|
+
if (renderer === resolvedSceneConfig.model.renderer) {
|
|
6073
|
+
return resolvedSceneConfig;
|
|
6074
|
+
}
|
|
6075
|
+
return {
|
|
6076
|
+
...resolvedSceneConfig,
|
|
6077
|
+
model: { ...resolvedSceneConfig.model, renderer }
|
|
6078
|
+
};
|
|
6079
|
+
}, [configuredUvChecker, resolvedSceneConfig, uvCheckerActive]);
|
|
6080
|
+
useEffect15(() => {
|
|
6081
|
+
setUvCheckerOverride(null);
|
|
6082
|
+
}, [modelUrl, resolvedSceneConfig.model.renderer, showUvCheckerButton]);
|
|
5945
6083
|
const viewerBackdropColor = useMemo13(() => {
|
|
5946
6084
|
if (backgroundColor) return backgroundColor;
|
|
5947
6085
|
const bg = resolvedSceneConfig.background;
|
|
@@ -6013,6 +6151,10 @@ function ModelViewer({
|
|
|
6013
6151
|
null
|
|
6014
6152
|
);
|
|
6015
6153
|
const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = useState10(false);
|
|
6154
|
+
const [uvMeshStats, setUvMeshStats] = useState10({
|
|
6155
|
+
total: 0,
|
|
6156
|
+
missing: 0
|
|
6157
|
+
});
|
|
6016
6158
|
const [loaderMessage, setLoaderMessage] = useState10("Loading model...");
|
|
6017
6159
|
const currentObjectBindingsRef = useRef11({});
|
|
6018
6160
|
const captureInitialCameraState = useCallback4(() => {
|
|
@@ -6893,6 +7035,7 @@ function ModelViewer({
|
|
|
6893
7035
|
setModelLoaded(false);
|
|
6894
7036
|
setModelLoadFailed(false);
|
|
6895
7037
|
setBindingsMismatch(false);
|
|
7038
|
+
setUvMeshStats({ total: 0, missing: 0 });
|
|
6896
7039
|
setLoaderMessage("Loading model...");
|
|
6897
7040
|
if (!modelUrl) {
|
|
6898
7041
|
setIsAutoFraming(false);
|
|
@@ -7082,6 +7225,7 @@ function ModelViewer({
|
|
|
7082
7225
|
sceneRef.current = scene;
|
|
7083
7226
|
setMeasureBox(computeMeasureBox(scene));
|
|
7084
7227
|
setModelBounds(computeModelBounds(scene));
|
|
7228
|
+
setUvMeshStats(computeUvMeshStats(scene));
|
|
7085
7229
|
const triangles = countSceneTriangles(scene);
|
|
7086
7230
|
setLargeModelTriangles(triangles > MAX_BVH_TRIANGLES ? triangles : null);
|
|
7087
7231
|
setLargeModelNoticeDismissed(false);
|
|
@@ -7464,6 +7608,20 @@ function ModelViewer({
|
|
|
7464
7608
|
]
|
|
7465
7609
|
}
|
|
7466
7610
|
),
|
|
7611
|
+
renderedSceneConfig.model.renderer === "uv-checker" && uvMeshStats.missing > 0 && /* @__PURE__ */ jsxs15("div", { className: "ri-uvCheckerBanner", role: "status", children: [
|
|
7612
|
+
/* @__PURE__ */ jsx19(
|
|
7613
|
+
AlertTriangle2,
|
|
7614
|
+
{
|
|
7615
|
+
size: 14,
|
|
7616
|
+
className: "ri-uvCheckerBanner__icon"
|
|
7617
|
+
}
|
|
7618
|
+
),
|
|
7619
|
+
/* @__PURE__ */ jsxs15("span", { children: [
|
|
7620
|
+
uvMeshStats.missing === uvMeshStats.total ? "UV Checker: this model has no UV0 coordinates." : `UV Checker: ${uvMeshStats.missing} of ${uvMeshStats.total} meshes have no UV0 coordinates.`,
|
|
7621
|
+
" ",
|
|
7622
|
+
"Missing UVs are shown in magenta."
|
|
7623
|
+
] })
|
|
7624
|
+
] }),
|
|
7467
7625
|
/* @__PURE__ */ jsx19(
|
|
7468
7626
|
"input",
|
|
7469
7627
|
{
|
|
@@ -7480,7 +7638,7 @@ function ModelViewer({
|
|
|
7480
7638
|
camera,
|
|
7481
7639
|
backgroundColor,
|
|
7482
7640
|
shadows: shadows && resolvedSceneConfig.shadows.enabled,
|
|
7483
|
-
sceneConfig:
|
|
7641
|
+
sceneConfig: renderedSceneConfig,
|
|
7484
7642
|
selectedObjectRef: currentSelectedObjectRef,
|
|
7485
7643
|
hoveredObjectRef: currentHoveredObjectRef,
|
|
7486
7644
|
highlightOnHover,
|
|
@@ -7528,7 +7686,7 @@ function ModelViewer({
|
|
|
7528
7686
|
nodeRefsOut: nodeRefsMap,
|
|
7529
7687
|
materialRefsOut,
|
|
7530
7688
|
panelHoveredItem: panelHoveredObject ?? panelHoveredItem,
|
|
7531
|
-
sceneConfig:
|
|
7689
|
+
sceneConfig: renderedSceneConfig,
|
|
7532
7690
|
zoomOnSelected,
|
|
7533
7691
|
highlightOnHover,
|
|
7534
7692
|
decoderConfig,
|
|
@@ -7720,11 +7878,34 @@ function ModelViewer({
|
|
|
7720
7878
|
)
|
|
7721
7879
|
}
|
|
7722
7880
|
),
|
|
7881
|
+
showUvCheckerButton && /* @__PURE__ */ jsx19(
|
|
7882
|
+
"div",
|
|
7883
|
+
{
|
|
7884
|
+
className: "ri-uvCheckerTools",
|
|
7885
|
+
style: { top: `${topLeftRow(xrToolsVisible ? 1 : 0)}px` },
|
|
7886
|
+
children: /* @__PURE__ */ jsx19(
|
|
7887
|
+
"button",
|
|
7888
|
+
{
|
|
7889
|
+
type: "button",
|
|
7890
|
+
className: `ri-measureButton ${uvCheckerActive ? "ri-measureButton--active" : ""}`,
|
|
7891
|
+
onClick: () => setUvCheckerOverride(
|
|
7892
|
+
(previous) => !(previous ?? configuredUvChecker)
|
|
7893
|
+
),
|
|
7894
|
+
title: uvCheckerActive ? "Disable UV checker" : "Enable UV checker",
|
|
7895
|
+
"aria-label": uvCheckerActive ? "Disable UV checker" : "Enable UV checker",
|
|
7896
|
+
"aria-pressed": uvCheckerActive,
|
|
7897
|
+
children: /* @__PURE__ */ jsx19(Grid3X3, { size: 16 })
|
|
7898
|
+
}
|
|
7899
|
+
)
|
|
7900
|
+
}
|
|
7901
|
+
),
|
|
7723
7902
|
showMeasureTools && /* @__PURE__ */ jsxs15(
|
|
7724
7903
|
"div",
|
|
7725
7904
|
{
|
|
7726
7905
|
className: "ri-measureTools",
|
|
7727
|
-
style: {
|
|
7906
|
+
style: {
|
|
7907
|
+
top: `${topLeftRow((xrToolsVisible ? 1 : 0) + (showUvCheckerButton ? 1 : 0))}px`
|
|
7908
|
+
},
|
|
7728
7909
|
children: [
|
|
7729
7910
|
/* @__PURE__ */ jsx19(
|
|
7730
7911
|
"button",
|
|
@@ -7767,7 +7948,7 @@ function ModelViewer({
|
|
|
7767
7948
|
{
|
|
7768
7949
|
className: "ri-explodeTools",
|
|
7769
7950
|
style: {
|
|
7770
|
-
top: `${topLeftRow((xrToolsVisible ? 1 : 0) + (showMeasureTools ? 1 : 0))}px`
|
|
7951
|
+
top: `${topLeftRow((xrToolsVisible ? 1 : 0) + (showUvCheckerButton ? 1 : 0) + (showMeasureTools ? 1 : 0))}px`
|
|
7771
7952
|
},
|
|
7772
7953
|
children: [
|
|
7773
7954
|
/* @__PURE__ */ jsx19(
|
|
@@ -7807,7 +7988,7 @@ function ModelViewer({
|
|
|
7807
7988
|
{
|
|
7808
7989
|
className: "ri-cinematicTools",
|
|
7809
7990
|
style: {
|
|
7810
|
-
top: `${topLeftRow((xrToolsVisible ? 1 : 0) + (showMeasureTools ? 1 : 0) + (showExplodeControls ? 1 : 0))}px`
|
|
7991
|
+
top: `${topLeftRow((xrToolsVisible ? 1 : 0) + (showUvCheckerButton ? 1 : 0) + (showMeasureTools ? 1 : 0) + (showExplodeControls ? 1 : 0))}px`
|
|
7811
7992
|
},
|
|
7812
7993
|
children: [
|
|
7813
7994
|
/* @__PURE__ */ jsx19(
|
|
@@ -9162,13 +9343,13 @@ function GeneralSceneSettings({ config, onChange }) {
|
|
|
9162
9343
|
),
|
|
9163
9344
|
/* @__PURE__ */ jsxs18("div", { className: "se-field", children: [
|
|
9164
9345
|
/* @__PURE__ */ jsx22("span", { className: "se-fieldLabel", children: "Renderer" }),
|
|
9165
|
-
/* @__PURE__ */ jsx22("div", { className: "se-segmentedControl", children:
|
|
9346
|
+
/* @__PURE__ */ jsx22("div", { className: "se-segmentedControl se-segmentedControl--renderer", children: SCENE_MODEL_RENDERERS.map((option) => /* @__PURE__ */ jsx22(
|
|
9166
9347
|
"button",
|
|
9167
9348
|
{
|
|
9168
9349
|
type: "button",
|
|
9169
9350
|
className: `se-segmentedOption ${model.renderer === option ? "se-segmentedOption--active" : ""}`,
|
|
9170
9351
|
onClick: () => setModel({ ...model, renderer: option }),
|
|
9171
|
-
children: option.toUpperCase()
|
|
9352
|
+
children: option === "uv-checker" ? "UV CHECKER" : option.toUpperCase()
|
|
9172
9353
|
},
|
|
9173
9354
|
option
|
|
9174
9355
|
)) })
|
|
@@ -12569,7 +12750,7 @@ function CinematicTab({
|
|
|
12569
12750
|
}
|
|
12570
12751
|
|
|
12571
12752
|
// src/styles/SceneSettingsTab.css
|
|
12572
|
-
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');
|
|
12753
|
+
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-segmentedControl--renderer {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n}\n.se-segmentedControl--renderer .se-segmentedOption {\n padding-inline: 6px;\n font-size: 12px;\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');
|
|
12573
12754
|
|
|
12574
12755
|
// src/components/BindingBuilder/SceneSettings/SceneSettingsTab.tsx
|
|
12575
12756
|
import {
|
|
@@ -13106,8 +13287,8 @@ function MaterialSection({
|
|
|
13106
13287
|
{
|
|
13107
13288
|
label: pbrWorkflow === "specular" ? "Albedo color" : "Base color",
|
|
13108
13289
|
value: selectedMaterial?.baseColor ?? "#ffffff",
|
|
13109
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13110
|
-
...
|
|
13290
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13291
|
+
...material2,
|
|
13111
13292
|
baseColor: value
|
|
13112
13293
|
}))
|
|
13113
13294
|
}
|
|
@@ -13121,8 +13302,8 @@ function MaterialSection({
|
|
|
13121
13302
|
min: 0,
|
|
13122
13303
|
max: 1,
|
|
13123
13304
|
step: 0.01,
|
|
13124
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13125
|
-
...
|
|
13305
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13306
|
+
...material2,
|
|
13126
13307
|
metalness: value
|
|
13127
13308
|
}))
|
|
13128
13309
|
}
|
|
@@ -13145,8 +13326,8 @@ function MaterialSection({
|
|
|
13145
13326
|
min: 0,
|
|
13146
13327
|
max: 1,
|
|
13147
13328
|
step: 0.01,
|
|
13148
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13149
|
-
...
|
|
13329
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13330
|
+
...material2,
|
|
13150
13331
|
specularIntensity: value
|
|
13151
13332
|
}))
|
|
13152
13333
|
}
|
|
@@ -13156,8 +13337,8 @@ function MaterialSection({
|
|
|
13156
13337
|
{
|
|
13157
13338
|
label: "Specular color",
|
|
13158
13339
|
value: selectedMaterial?.specularColor ?? "#ffffff",
|
|
13159
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13160
|
-
...
|
|
13340
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13341
|
+
...material2,
|
|
13161
13342
|
specularColor: value
|
|
13162
13343
|
}))
|
|
13163
13344
|
}
|
|
@@ -13197,8 +13378,8 @@ function MaterialSection({
|
|
|
13197
13378
|
min: 0,
|
|
13198
13379
|
max: 1,
|
|
13199
13380
|
step: 0.01,
|
|
13200
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13201
|
-
...
|
|
13381
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13382
|
+
...material2,
|
|
13202
13383
|
reflectivity: value
|
|
13203
13384
|
}))
|
|
13204
13385
|
}
|
|
@@ -13209,8 +13390,8 @@ function MaterialSection({
|
|
|
13209
13390
|
{
|
|
13210
13391
|
label: "Base color",
|
|
13211
13392
|
value: selectedMaterial?.baseColor ?? "#ffffff",
|
|
13212
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13213
|
-
...
|
|
13393
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13394
|
+
...material2,
|
|
13214
13395
|
baseColor: value
|
|
13215
13396
|
}))
|
|
13216
13397
|
}
|
|
@@ -13223,8 +13404,8 @@ function MaterialSection({
|
|
|
13223
13404
|
min: 0,
|
|
13224
13405
|
max: 1,
|
|
13225
13406
|
step: 0.01,
|
|
13226
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13227
|
-
...
|
|
13407
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13408
|
+
...material2,
|
|
13228
13409
|
metalness: value
|
|
13229
13410
|
}))
|
|
13230
13411
|
}
|
|
@@ -13267,8 +13448,8 @@ function MaterialSection({
|
|
|
13267
13448
|
min: 0,
|
|
13268
13449
|
max: 1,
|
|
13269
13450
|
step: 0.01,
|
|
13270
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13271
|
-
...
|
|
13451
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13452
|
+
...material2,
|
|
13272
13453
|
roughness: value
|
|
13273
13454
|
}))
|
|
13274
13455
|
}
|
|
@@ -13280,8 +13461,8 @@ function MaterialSection({
|
|
|
13280
13461
|
min: 0,
|
|
13281
13462
|
max: 1,
|
|
13282
13463
|
step: 0.01,
|
|
13283
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13284
|
-
...
|
|
13464
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13465
|
+
...material2,
|
|
13285
13466
|
roughness: value === void 0 ? void 0 : 1 - value
|
|
13286
13467
|
}))
|
|
13287
13468
|
}
|
|
@@ -13313,8 +13494,8 @@ function MaterialSection({
|
|
|
13313
13494
|
label: "Emissive color",
|
|
13314
13495
|
value: selectedMaterial?.emissive ?? "#000000",
|
|
13315
13496
|
fallback: "#000000",
|
|
13316
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13317
|
-
...
|
|
13497
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13498
|
+
...material2,
|
|
13318
13499
|
emissive: value
|
|
13319
13500
|
}))
|
|
13320
13501
|
}
|
|
@@ -13327,8 +13508,8 @@ function MaterialSection({
|
|
|
13327
13508
|
min: 0,
|
|
13328
13509
|
max: 10,
|
|
13329
13510
|
step: 0.1,
|
|
13330
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13331
|
-
...
|
|
13511
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13512
|
+
...material2,
|
|
13332
13513
|
emissiveIntensity: value
|
|
13333
13514
|
}))
|
|
13334
13515
|
}
|
|
@@ -13362,8 +13543,8 @@ function MaterialSection({
|
|
|
13362
13543
|
min: 0,
|
|
13363
13544
|
max: 1,
|
|
13364
13545
|
step: 0.01,
|
|
13365
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13366
|
-
...
|
|
13546
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13547
|
+
...material2,
|
|
13367
13548
|
opacity: value
|
|
13368
13549
|
}))
|
|
13369
13550
|
}
|
|
@@ -13376,8 +13557,8 @@ function MaterialSection({
|
|
|
13376
13557
|
min: 0,
|
|
13377
13558
|
max: 1,
|
|
13378
13559
|
step: 0.01,
|
|
13379
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13380
|
-
...
|
|
13560
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13561
|
+
...material2,
|
|
13381
13562
|
alphaTest: value
|
|
13382
13563
|
}))
|
|
13383
13564
|
}
|
|
@@ -13397,8 +13578,8 @@ function MaterialSection({
|
|
|
13397
13578
|
label: "Blending mode",
|
|
13398
13579
|
value: selectedMaterial?.blendingMode ?? "normal",
|
|
13399
13580
|
options: MATERIAL_BLENDING_MODES,
|
|
13400
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13401
|
-
...
|
|
13581
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13582
|
+
...material2,
|
|
13402
13583
|
blendingMode: value
|
|
13403
13584
|
}))
|
|
13404
13585
|
}
|
|
@@ -13410,8 +13591,8 @@ function MaterialSection({
|
|
|
13410
13591
|
{
|
|
13411
13592
|
label: "Use alpha hash",
|
|
13412
13593
|
checked: selectedMaterial?.alphaHash ?? false,
|
|
13413
|
-
onChange: (checked) => updateSelectedMaterial(selectedItem, (
|
|
13414
|
-
...
|
|
13594
|
+
onChange: (checked) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13595
|
+
...material2,
|
|
13415
13596
|
alphaHash: checked
|
|
13416
13597
|
}))
|
|
13417
13598
|
}
|
|
@@ -13424,8 +13605,8 @@ function MaterialSection({
|
|
|
13424
13605
|
{
|
|
13425
13606
|
label: "Use dithering",
|
|
13426
13607
|
checked: selectedMaterial?.dithering ?? false,
|
|
13427
|
-
onChange: (checked) => updateSelectedMaterial(selectedItem, (
|
|
13428
|
-
...
|
|
13608
|
+
onChange: (checked) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13609
|
+
...material2,
|
|
13429
13610
|
dithering: checked
|
|
13430
13611
|
}))
|
|
13431
13612
|
}
|
|
@@ -13462,8 +13643,8 @@ function MaterialSection({
|
|
|
13462
13643
|
min: 0,
|
|
13463
13644
|
max: 5,
|
|
13464
13645
|
step: 0.01,
|
|
13465
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13466
|
-
...
|
|
13646
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13647
|
+
...material2,
|
|
13467
13648
|
normalScale: value
|
|
13468
13649
|
}))
|
|
13469
13650
|
}
|
|
@@ -13485,8 +13666,8 @@ function MaterialSection({
|
|
|
13485
13666
|
min: 0,
|
|
13486
13667
|
max: 5,
|
|
13487
13668
|
step: 0.01,
|
|
13488
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13489
|
-
...
|
|
13669
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13670
|
+
...material2,
|
|
13490
13671
|
bumpScale: value
|
|
13491
13672
|
}))
|
|
13492
13673
|
}
|
|
@@ -13497,8 +13678,8 @@ function MaterialSection({
|
|
|
13497
13678
|
{
|
|
13498
13679
|
label: "Flip normal Y",
|
|
13499
13680
|
checked: selectedMaterial?.flipNormalY ?? false,
|
|
13500
|
-
onChange: (checked) => updateSelectedMaterial(selectedItem, (
|
|
13501
|
-
...
|
|
13681
|
+
onChange: (checked) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13682
|
+
...material2,
|
|
13502
13683
|
flipNormalY: checked
|
|
13503
13684
|
}))
|
|
13504
13685
|
}
|
|
@@ -13532,8 +13713,8 @@ function MaterialSection({
|
|
|
13532
13713
|
min: -5,
|
|
13533
13714
|
max: 5,
|
|
13534
13715
|
step: 0.01,
|
|
13535
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13536
|
-
...
|
|
13716
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13717
|
+
...material2,
|
|
13537
13718
|
displacementScale: value
|
|
13538
13719
|
}))
|
|
13539
13720
|
}
|
|
@@ -13546,8 +13727,8 @@ function MaterialSection({
|
|
|
13546
13727
|
min: -5,
|
|
13547
13728
|
max: 5,
|
|
13548
13729
|
step: 0.01,
|
|
13549
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13550
|
-
...
|
|
13730
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13731
|
+
...material2,
|
|
13551
13732
|
displacementBias: value
|
|
13552
13733
|
}))
|
|
13553
13734
|
}
|
|
@@ -13581,8 +13762,8 @@ function MaterialSection({
|
|
|
13581
13762
|
min: 0,
|
|
13582
13763
|
max: 5,
|
|
13583
13764
|
step: 0.01,
|
|
13584
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13585
|
-
...
|
|
13765
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13766
|
+
...material2,
|
|
13586
13767
|
aoMapIntensity: value
|
|
13587
13768
|
}))
|
|
13588
13769
|
}
|
|
@@ -13617,8 +13798,8 @@ function MaterialSection({
|
|
|
13617
13798
|
min: 0,
|
|
13618
13799
|
max: 1,
|
|
13619
13800
|
step: 0.01,
|
|
13620
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13621
|
-
...
|
|
13801
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13802
|
+
...material2,
|
|
13622
13803
|
clearcoat: value
|
|
13623
13804
|
}))
|
|
13624
13805
|
}
|
|
@@ -13644,8 +13825,8 @@ function MaterialSection({
|
|
|
13644
13825
|
min: 0,
|
|
13645
13826
|
max: 1,
|
|
13646
13827
|
step: 0.01,
|
|
13647
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13648
|
-
...
|
|
13828
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13829
|
+
...material2,
|
|
13649
13830
|
clearcoatRoughness: value
|
|
13650
13831
|
}))
|
|
13651
13832
|
}
|
|
@@ -13667,8 +13848,8 @@ function MaterialSection({
|
|
|
13667
13848
|
min: 0,
|
|
13668
13849
|
max: 5,
|
|
13669
13850
|
step: 0.01,
|
|
13670
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13671
|
-
...
|
|
13851
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13852
|
+
...material2,
|
|
13672
13853
|
clearcoatNormalScale: value
|
|
13673
13854
|
}))
|
|
13674
13855
|
}
|
|
@@ -13679,8 +13860,8 @@ function MaterialSection({
|
|
|
13679
13860
|
{
|
|
13680
13861
|
label: "Flip clearcoat normal Y",
|
|
13681
13862
|
checked: selectedMaterial?.flipClearcoatNormalY ?? false,
|
|
13682
|
-
onChange: (checked) => updateSelectedMaterial(selectedItem, (
|
|
13683
|
-
...
|
|
13863
|
+
onChange: (checked) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13864
|
+
...material2,
|
|
13684
13865
|
flipClearcoatNormalY: checked
|
|
13685
13866
|
}))
|
|
13686
13867
|
}
|
|
@@ -13705,8 +13886,8 @@ function MaterialSection({
|
|
|
13705
13886
|
min: 0,
|
|
13706
13887
|
max: 1,
|
|
13707
13888
|
step: 0.01,
|
|
13708
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13709
|
-
...
|
|
13889
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13890
|
+
...material2,
|
|
13710
13891
|
sheen: value
|
|
13711
13892
|
}))
|
|
13712
13893
|
}
|
|
@@ -13716,8 +13897,8 @@ function MaterialSection({
|
|
|
13716
13897
|
{
|
|
13717
13898
|
label: "Sheen color",
|
|
13718
13899
|
value: selectedMaterial?.sheenColor ?? "#ffffff",
|
|
13719
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13720
|
-
...
|
|
13900
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13901
|
+
...material2,
|
|
13721
13902
|
sheenColor: value
|
|
13722
13903
|
}))
|
|
13723
13904
|
}
|
|
@@ -13739,8 +13920,8 @@ function MaterialSection({
|
|
|
13739
13920
|
min: 0,
|
|
13740
13921
|
max: 1,
|
|
13741
13922
|
step: 0.01,
|
|
13742
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13743
|
-
...
|
|
13923
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13924
|
+
...material2,
|
|
13744
13925
|
sheenRoughness: value
|
|
13745
13926
|
}))
|
|
13746
13927
|
}
|
|
@@ -13774,8 +13955,8 @@ function MaterialSection({
|
|
|
13774
13955
|
min: 0,
|
|
13775
13956
|
max: 1,
|
|
13776
13957
|
step: 0.01,
|
|
13777
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13778
|
-
...
|
|
13958
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13959
|
+
...material2,
|
|
13779
13960
|
anisotropy: value
|
|
13780
13961
|
}))
|
|
13781
13962
|
}
|
|
@@ -13797,8 +13978,8 @@ function MaterialSection({
|
|
|
13797
13978
|
min: 0,
|
|
13798
13979
|
max: 6.28,
|
|
13799
13980
|
step: 0.01,
|
|
13800
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13801
|
-
...
|
|
13981
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
13982
|
+
...material2,
|
|
13802
13983
|
anisotropyRotation: value
|
|
13803
13984
|
}))
|
|
13804
13985
|
}
|
|
@@ -13832,8 +14013,8 @@ function MaterialSection({
|
|
|
13832
14013
|
min: 0,
|
|
13833
14014
|
max: 1,
|
|
13834
14015
|
step: 0.01,
|
|
13835
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13836
|
-
...
|
|
14016
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
14017
|
+
...material2,
|
|
13837
14018
|
transmission: value
|
|
13838
14019
|
}))
|
|
13839
14020
|
}
|
|
@@ -13855,8 +14036,8 @@ function MaterialSection({
|
|
|
13855
14036
|
min: 0,
|
|
13856
14037
|
max: 5,
|
|
13857
14038
|
step: 0.01,
|
|
13858
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13859
|
-
...
|
|
14039
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
14040
|
+
...material2,
|
|
13860
14041
|
thickness: value
|
|
13861
14042
|
}))
|
|
13862
14043
|
}
|
|
@@ -13875,8 +14056,8 @@ function MaterialSection({
|
|
|
13875
14056
|
{
|
|
13876
14057
|
label: "Volume tint",
|
|
13877
14058
|
value: selectedMaterial?.attenuationColor ?? "#ffffff",
|
|
13878
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13879
|
-
...
|
|
14059
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
14060
|
+
...material2,
|
|
13880
14061
|
attenuationColor: value
|
|
13881
14062
|
}))
|
|
13882
14063
|
}
|
|
@@ -13889,8 +14070,8 @@ function MaterialSection({
|
|
|
13889
14070
|
min: 0,
|
|
13890
14071
|
step: 0.01,
|
|
13891
14072
|
placeholder: "Infinity when unset",
|
|
13892
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13893
|
-
...
|
|
14073
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
14074
|
+
...material2,
|
|
13894
14075
|
attenuationDistance: value
|
|
13895
14076
|
}))
|
|
13896
14077
|
}
|
|
@@ -13912,8 +14093,8 @@ function MaterialSection({
|
|
|
13912
14093
|
label: "Side",
|
|
13913
14094
|
value: selectedMaterial?.side ?? "front",
|
|
13914
14095
|
options: MATERIAL_SIDES,
|
|
13915
|
-
onChange: (value) => updateSelectedMaterial(selectedItem, (
|
|
13916
|
-
...
|
|
14096
|
+
onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
|
|
14097
|
+
...material2,
|
|
13917
14098
|
side: value
|
|
13918
14099
|
}))
|
|
13919
14100
|
}
|
|
@@ -14912,9 +15093,9 @@ function formatVector3(position) {
|
|
|
14912
15093
|
|
|
14913
15094
|
// src/components/BindingBuilder/utils/exportAssets.ts
|
|
14914
15095
|
function collectTexturePaths(binding) {
|
|
14915
|
-
const
|
|
14916
|
-
if (!
|
|
14917
|
-
return Object.values(
|
|
15096
|
+
const material2 = binding.style?.material;
|
|
15097
|
+
if (!material2) return [];
|
|
15098
|
+
return Object.values(material2).filter(
|
|
14918
15099
|
(value) => !!value && typeof value === "object" && typeof value.path === "string" && value.path.trim().length > 0
|
|
14919
15100
|
).map((value) => value.path);
|
|
14920
15101
|
}
|
|
@@ -15032,30 +15213,30 @@ function materialFieldHasValue(value) {
|
|
|
15032
15213
|
}
|
|
15033
15214
|
return false;
|
|
15034
15215
|
}
|
|
15035
|
-
function normalizeMaterialForStorage(
|
|
15036
|
-
if (!
|
|
15037
|
-
return Object.values(
|
|
15216
|
+
function normalizeMaterialForStorage(material2) {
|
|
15217
|
+
if (!material2) return void 0;
|
|
15218
|
+
return Object.values(material2).some(materialFieldHasValue) ? material2 : void 0;
|
|
15038
15219
|
}
|
|
15039
|
-
function isMaterialPanelEnabled(
|
|
15040
|
-
if (!
|
|
15220
|
+
function isMaterialPanelEnabled(material2, panel) {
|
|
15221
|
+
if (!material2) return false;
|
|
15041
15222
|
return MATERIAL_PANEL_FIELDS[panel].some(
|
|
15042
|
-
(field) => materialFieldHasValue(
|
|
15223
|
+
(field) => materialFieldHasValue(material2[field])
|
|
15043
15224
|
);
|
|
15044
15225
|
}
|
|
15045
|
-
function createMaterialPanelEnabledState(
|
|
15226
|
+
function createMaterialPanelEnabledState(material2) {
|
|
15046
15227
|
return {
|
|
15047
|
-
pbr: isMaterialPanelEnabled(
|
|
15048
|
-
roughness: isMaterialPanelEnabled(
|
|
15049
|
-
emission: isMaterialPanelEnabled(
|
|
15050
|
-
opacity: isMaterialPanelEnabled(
|
|
15051
|
-
normalBump: isMaterialPanelEnabled(
|
|
15052
|
-
displacement: isMaterialPanelEnabled(
|
|
15053
|
-
ao: isMaterialPanelEnabled(
|
|
15054
|
-
clearcoat: isMaterialPanelEnabled(
|
|
15055
|
-
sheen: isMaterialPanelEnabled(
|
|
15056
|
-
anisotropy: isMaterialPanelEnabled(
|
|
15057
|
-
transmission: isMaterialPanelEnabled(
|
|
15058
|
-
faces: isMaterialPanelEnabled(
|
|
15228
|
+
pbr: isMaterialPanelEnabled(material2, "pbr"),
|
|
15229
|
+
roughness: isMaterialPanelEnabled(material2, "roughness"),
|
|
15230
|
+
emission: isMaterialPanelEnabled(material2, "emission"),
|
|
15231
|
+
opacity: isMaterialPanelEnabled(material2, "opacity"),
|
|
15232
|
+
normalBump: isMaterialPanelEnabled(material2, "normalBump"),
|
|
15233
|
+
displacement: isMaterialPanelEnabled(material2, "displacement"),
|
|
15234
|
+
ao: isMaterialPanelEnabled(material2, "ao"),
|
|
15235
|
+
clearcoat: isMaterialPanelEnabled(material2, "clearcoat"),
|
|
15236
|
+
sheen: isMaterialPanelEnabled(material2, "sheen"),
|
|
15237
|
+
anisotropy: isMaterialPanelEnabled(material2, "anisotropy"),
|
|
15238
|
+
transmission: isMaterialPanelEnabled(material2, "transmission"),
|
|
15239
|
+
faces: isMaterialPanelEnabled(material2, "faces")
|
|
15059
15240
|
};
|
|
15060
15241
|
}
|
|
15061
15242
|
function withMaterial(binding, nextMaterial) {
|
|
@@ -15072,9 +15253,9 @@ function withMaterial(binding, nextMaterial) {
|
|
|
15072
15253
|
style: Object.keys(rest).length > 0 ? rest : void 0
|
|
15073
15254
|
};
|
|
15074
15255
|
}
|
|
15075
|
-
function removeMaterialPanelFields(
|
|
15076
|
-
if (!
|
|
15077
|
-
const nextMaterial = { ...
|
|
15256
|
+
function removeMaterialPanelFields(material2, panel) {
|
|
15257
|
+
if (!material2) return void 0;
|
|
15258
|
+
const nextMaterial = { ...material2 };
|
|
15078
15259
|
for (const field of MATERIAL_PANEL_FIELDS[panel]) {
|
|
15079
15260
|
delete nextMaterial[field];
|
|
15080
15261
|
}
|
|
@@ -15332,8 +15513,8 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15332
15513
|
});
|
|
15333
15514
|
};
|
|
15334
15515
|
const setSelectedTexturePath = (nodeKey, key, value) => {
|
|
15335
|
-
updateSelectedMaterial(nodeKey, (
|
|
15336
|
-
...
|
|
15516
|
+
updateSelectedMaterial(nodeKey, (material2) => ({
|
|
15517
|
+
...material2,
|
|
15337
15518
|
[key]: value ? { path: value } : void 0
|
|
15338
15519
|
}));
|
|
15339
15520
|
};
|
|
@@ -15963,8 +16144,8 @@ function BindingBuilder({ licenseKey }) {
|
|
|
15963
16144
|
MaterialPresetGallery,
|
|
15964
16145
|
{
|
|
15965
16146
|
disabled: !selectedItem,
|
|
15966
|
-
onApply: (preset) => selectedItem && updateSelectedMaterial(selectedItem, (
|
|
15967
|
-
...
|
|
16147
|
+
onApply: (preset) => selectedItem && updateSelectedMaterial(selectedItem, (material2) => ({
|
|
16148
|
+
...material2,
|
|
15968
16149
|
...preset
|
|
15969
16150
|
}))
|
|
15970
16151
|
}
|
|
@@ -17513,71 +17694,126 @@ function useObjectBindingIds(objectBindings) {
|
|
|
17513
17694
|
);
|
|
17514
17695
|
}
|
|
17515
17696
|
|
|
17516
|
-
// src/hooks/
|
|
17517
|
-
import { useCallback as useCallback12, useMemo as useMemo20 } from "react";
|
|
17697
|
+
// src/hooks/useObjectBindings.ts
|
|
17698
|
+
import { useCallback as useCallback12, useMemo as useMemo20, useState as useState28 } from "react";
|
|
17699
|
+
|
|
17700
|
+
// src/utils/patchBindings.ts
|
|
17701
|
+
function isPlainObject2(value) {
|
|
17702
|
+
if (typeof value !== "object" || value === null) return false;
|
|
17703
|
+
const prototype = Object.getPrototypeOf(value);
|
|
17704
|
+
return prototype === Object.prototype || prototype === null;
|
|
17705
|
+
}
|
|
17706
|
+
function isBindingEditArray(value) {
|
|
17707
|
+
return Array.isArray(value) && value.length > 0 && typeof value[0] !== "string";
|
|
17708
|
+
}
|
|
17709
|
+
function deepMerge(base, patch) {
|
|
17710
|
+
if (!isPlainObject2(patch)) return patch;
|
|
17711
|
+
const target = isPlainObject2(base) ? { ...base } : {};
|
|
17712
|
+
for (const key of Object.keys(patch)) {
|
|
17713
|
+
const value = patch[key];
|
|
17714
|
+
target[key] = isPlainObject2(value) ? deepMerge(target[key], value) : value;
|
|
17715
|
+
}
|
|
17716
|
+
return target;
|
|
17717
|
+
}
|
|
17718
|
+
function patchBindings(bindings, objectIdOrEdits, patch) {
|
|
17719
|
+
const edits = isBindingEditArray(objectIdOrEdits) ? objectIdOrEdits : [{ ids: objectIdOrEdits, patch: patch ?? {} }];
|
|
17720
|
+
let next = bindings;
|
|
17721
|
+
for (const edit of edits) {
|
|
17722
|
+
const ids = Array.isArray(edit.ids) ? edit.ids : [edit.ids];
|
|
17723
|
+
for (const id of ids) {
|
|
17724
|
+
const bindingKey = resolveBindingKey(next, id);
|
|
17725
|
+
if (!bindingKey) continue;
|
|
17726
|
+
const binding = next[bindingKey];
|
|
17727
|
+
if (!binding) continue;
|
|
17728
|
+
const patchedBinding = deepMerge(binding, edit.patch);
|
|
17729
|
+
if (next === bindings) next = { ...bindings };
|
|
17730
|
+
next[bindingKey] = patchedBinding;
|
|
17731
|
+
}
|
|
17732
|
+
}
|
|
17733
|
+
return next;
|
|
17734
|
+
}
|
|
17735
|
+
function material(value) {
|
|
17736
|
+
return { style: { material: value } };
|
|
17737
|
+
}
|
|
17738
|
+
|
|
17739
|
+
// src/hooks/useObjectBindings.ts
|
|
17518
17740
|
function getHiddenObjects(objectBindings) {
|
|
17519
17741
|
return Object.fromEntries(
|
|
17520
17742
|
Object.entries(objectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
|
|
17521
17743
|
);
|
|
17522
17744
|
}
|
|
17523
|
-
function
|
|
17524
|
-
const
|
|
17525
|
-
|
|
17745
|
+
function useObjectBindings(initialBindings) {
|
|
17746
|
+
const [objectBindings, setObjectBindings] = useState28(initialBindings);
|
|
17747
|
+
const patch = useCallback12(
|
|
17748
|
+
(objectIdOrEdits, patchValue) => {
|
|
17749
|
+
setObjectBindings(
|
|
17750
|
+
(current) => patchValue === void 0 ? patchBindings(current, objectIdOrEdits) : patchBindings(
|
|
17751
|
+
current,
|
|
17752
|
+
objectIdOrEdits,
|
|
17753
|
+
patchValue
|
|
17754
|
+
)
|
|
17755
|
+
);
|
|
17756
|
+
},
|
|
17757
|
+
[]
|
|
17758
|
+
);
|
|
17759
|
+
const setMaterial = useCallback12(
|
|
17760
|
+
(objectId, value) => patch(objectId, material(value)),
|
|
17761
|
+
[patch]
|
|
17762
|
+
);
|
|
17763
|
+
const setBaseColor = useCallback12(
|
|
17764
|
+
(objectId, color) => setMaterial(objectId, { baseColor: color }),
|
|
17765
|
+
[setMaterial]
|
|
17766
|
+
);
|
|
17767
|
+
const resetMaterial = useCallback12(
|
|
17768
|
+
(objectId) => patch(objectId, { style: { material: void 0 } }),
|
|
17769
|
+
[patch]
|
|
17770
|
+
);
|
|
17771
|
+
const getMaterial = useCallback12(
|
|
17772
|
+
(objectId) => {
|
|
17526
17773
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
17527
|
-
|
|
17528
|
-
const targetBinding = objectBindings[bindingKey];
|
|
17529
|
-
if (!targetBinding) return;
|
|
17530
|
-
onObjectBindingsChange({
|
|
17531
|
-
...objectBindings,
|
|
17532
|
-
[bindingKey]: {
|
|
17533
|
-
...targetBinding,
|
|
17534
|
-
visible
|
|
17535
|
-
}
|
|
17536
|
-
});
|
|
17774
|
+
return bindingKey ? objectBindings[bindingKey]?.style?.material ?? null : null;
|
|
17537
17775
|
},
|
|
17538
|
-
[objectBindings
|
|
17776
|
+
[objectBindings]
|
|
17539
17777
|
);
|
|
17540
17778
|
const hideObject = useCallback12(
|
|
17541
|
-
(objectId) =>
|
|
17542
|
-
[
|
|
17779
|
+
(objectId) => patch(objectId, { visible: false }),
|
|
17780
|
+
[patch]
|
|
17543
17781
|
);
|
|
17544
17782
|
const showObject = useCallback12(
|
|
17545
|
-
(objectId) =>
|
|
17546
|
-
[
|
|
17783
|
+
(objectId) => patch(objectId, { visible: true }),
|
|
17784
|
+
[patch]
|
|
17547
17785
|
);
|
|
17548
17786
|
const toggleObjectVisibility = useCallback12(
|
|
17549
17787
|
(objectId) => {
|
|
17550
|
-
|
|
17551
|
-
|
|
17552
|
-
|
|
17553
|
-
|
|
17554
|
-
|
|
17555
|
-
|
|
17556
|
-
|
|
17557
|
-
|
|
17558
|
-
|
|
17559
|
-
|
|
17788
|
+
setObjectBindings((current) => {
|
|
17789
|
+
const objectIds = Array.isArray(objectId) ? objectId : [objectId];
|
|
17790
|
+
const edits = objectIds.reduce((acc, id) => {
|
|
17791
|
+
const bindingKey = resolveBindingKey(current, id);
|
|
17792
|
+
const binding = bindingKey ? current[bindingKey] : null;
|
|
17793
|
+
if (!bindingKey || !binding) return acc;
|
|
17794
|
+
acc.push({
|
|
17795
|
+
ids: bindingKey,
|
|
17796
|
+
patch: { visible: binding.visible === false }
|
|
17797
|
+
});
|
|
17798
|
+
return acc;
|
|
17799
|
+
}, []);
|
|
17800
|
+
return patchBindings(current, edits);
|
|
17560
17801
|
});
|
|
17561
17802
|
},
|
|
17562
|
-
[
|
|
17803
|
+
[setObjectBindings]
|
|
17563
17804
|
);
|
|
17564
17805
|
const clearHiddenObjects = useCallback12(() => {
|
|
17565
|
-
|
|
17566
|
-
Object.
|
|
17567
|
-
Object.entries(objectBindings).map(([bindingKey, binding]) => [
|
|
17568
|
-
bindingKey,
|
|
17569
|
-
{ ...binding, visible: true }
|
|
17570
|
-
])
|
|
17571
|
-
)
|
|
17806
|
+
setObjectBindings(
|
|
17807
|
+
(current) => patchBindings(current, Object.keys(current), { visible: true })
|
|
17572
17808
|
);
|
|
17573
|
-
}, [
|
|
17809
|
+
}, [setObjectBindings]);
|
|
17574
17810
|
const hiddenObjects = useMemo20(
|
|
17575
17811
|
() => getHiddenObjects(objectBindings),
|
|
17576
17812
|
[objectBindings]
|
|
17577
17813
|
);
|
|
17578
17814
|
const hiddenObjectIds = useMemo20(
|
|
17579
|
-
() => Object.
|
|
17580
|
-
(
|
|
17815
|
+
() => Object.keys(hiddenObjects).map(
|
|
17816
|
+
(bindingKey) => objectBindings[bindingKey]?.id ?? bindingKey
|
|
17581
17817
|
),
|
|
17582
17818
|
[hiddenObjects, objectBindings]
|
|
17583
17819
|
);
|
|
@@ -17589,6 +17825,13 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
|
17589
17825
|
[objectBindings]
|
|
17590
17826
|
);
|
|
17591
17827
|
return {
|
|
17828
|
+
objectBindings,
|
|
17829
|
+
setObjectBindings,
|
|
17830
|
+
patch,
|
|
17831
|
+
setMaterial,
|
|
17832
|
+
setBaseColor,
|
|
17833
|
+
resetMaterial,
|
|
17834
|
+
getMaterial,
|
|
17592
17835
|
hiddenObjects,
|
|
17593
17836
|
hiddenObjectIds,
|
|
17594
17837
|
hideObject,
|
|
@@ -17600,9 +17843,9 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
|
|
|
17600
17843
|
}
|
|
17601
17844
|
|
|
17602
17845
|
// src/hooks/useViewerHover.ts
|
|
17603
|
-
import { useCallback as useCallback13, useState as
|
|
17846
|
+
import { useCallback as useCallback13, useState as useState29 } from "react";
|
|
17604
17847
|
function useViewerHover() {
|
|
17605
|
-
const [hoveredObjectBinding, setHoveredObjectBinding] =
|
|
17848
|
+
const [hoveredObjectBinding, setHoveredObjectBinding] = useState29(null);
|
|
17606
17849
|
const handleHoveredObject = useCallback13(
|
|
17607
17850
|
(binding) => {
|
|
17608
17851
|
setHoveredObjectBinding(binding);
|
|
@@ -17616,17 +17859,17 @@ function useViewerHover() {
|
|
|
17616
17859
|
}
|
|
17617
17860
|
|
|
17618
17861
|
// src/hooks/useViewerModel.ts
|
|
17619
|
-
import { useCallback as useCallback14, useState as
|
|
17862
|
+
import { useCallback as useCallback14, useState as useState30 } from "react";
|
|
17620
17863
|
import { Box3 as Box36, Vector3 as Vector310 } from "three";
|
|
17621
17864
|
function toTuple2(vector) {
|
|
17622
17865
|
return [vector.x, vector.y, vector.z];
|
|
17623
17866
|
}
|
|
17624
17867
|
function useViewerModel() {
|
|
17625
|
-
const [isLoading, setIsLoading] =
|
|
17626
|
-
const [isReady, setIsReady] =
|
|
17627
|
-
const [error, setError] =
|
|
17628
|
-
const [bounds, setBounds] =
|
|
17629
|
-
const [objectCount, setObjectCount] =
|
|
17868
|
+
const [isLoading, setIsLoading] = useState30(true);
|
|
17869
|
+
const [isReady, setIsReady] = useState30(false);
|
|
17870
|
+
const [error, setError] = useState30(null);
|
|
17871
|
+
const [bounds, setBounds] = useState30(null);
|
|
17872
|
+
const [objectCount, setObjectCount] = useState30(0);
|
|
17630
17873
|
const handleModelLoaded = useCallback14((scene) => {
|
|
17631
17874
|
const box = new Box36().setFromObject(scene);
|
|
17632
17875
|
const min = box.min.clone();
|
|
@@ -17734,7 +17977,7 @@ function useViewerActions(objectBindings, options = {}) {
|
|
|
17734
17977
|
}
|
|
17735
17978
|
|
|
17736
17979
|
// src/hooks/useShareableViewerState.ts
|
|
17737
|
-
import { useCallback as useCallback16, useEffect as useEffect22, useRef as useRef20, useState as
|
|
17980
|
+
import { useCallback as useCallback16, useEffect as useEffect22, useRef as useRef20, useState as useState31 } from "react";
|
|
17738
17981
|
function serializeState(cameraState, objectBindings, selectedObjectBinding) {
|
|
17739
17982
|
const hidden = Object.values(objectBindings).filter((binding) => binding.visible === false).map((binding) => binding.id);
|
|
17740
17983
|
return {
|
|
@@ -17786,7 +18029,7 @@ function useShareableViewerState({
|
|
|
17786
18029
|
objectBindings,
|
|
17787
18030
|
selectedObjectBinding
|
|
17788
18031
|
};
|
|
17789
|
-
const [restoreAttempted, setRestoreAttempted] =
|
|
18032
|
+
const [restoreAttempted, setRestoreAttempted] = useState31(false);
|
|
17790
18033
|
const lastSyncedHashRef = useRef20(null);
|
|
17791
18034
|
useEffect22(() => {
|
|
17792
18035
|
if (!autoSync || !restoreAttempted) return;
|
|
@@ -17882,9 +18125,11 @@ export {
|
|
|
17882
18125
|
MATERIAL_SIDES,
|
|
17883
18126
|
ModelViewer,
|
|
17884
18127
|
SimpleModelViewer,
|
|
18128
|
+
material,
|
|
18129
|
+
patchBindings,
|
|
17885
18130
|
useObjectBinding,
|
|
17886
18131
|
useObjectBindingIds,
|
|
17887
|
-
|
|
18132
|
+
useObjectBindings,
|
|
17888
18133
|
useShareableViewerState,
|
|
17889
18134
|
useViewerActions,
|
|
17890
18135
|
useViewerAnimations,
|