@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/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(material, materialIndex) {
395
- if (!Array.isArray(material)) return material;
396
- return material[materialIndex] ?? null;
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 material = materialForIndex(mesh.material, materialIndex);
416
- if (!material) continue;
417
- let layout = layouts.get(material.uuid);
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: material.name || `Material ${unnamedMaterialIndex}`,
421
+ name: material2.name || `Material ${unnamedMaterialIndex}`,
422
422
  layers: /* @__PURE__ */ new Map()
423
423
  };
424
- layouts.set(material.uuid, layout);
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(material) {
2382
+ function isMeshStandardMaterial(material2) {
2310
2383
  return Boolean(
2311
- material && typeof material === "object" && "clone" in material && "color" in material && "metalness" in material && "roughness" in material
2384
+ material2 && typeof material2 === "object" && "clone" in material2 && "color" in material2 && "metalness" in material2 && "roughness" in material2
2312
2385
  );
2313
2386
  }
2314
- function isMeshPhysicalMaterial(material) {
2387
+ function isMeshPhysicalMaterial(material2) {
2315
2388
  return Boolean(
2316
- material && typeof material === "object" && "isMeshPhysicalMaterial" in material && material.isMeshPhysicalMaterial
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(material, key) {
2353
- return material?.[key]?.path;
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(material) {
2388
- if (isMeshStandardMaterial(material)) return material;
2389
- if (!material || typeof material !== "object") return null;
2390
- const src = material;
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(material, configuredMaterial, resolvedBaseColor) {
2577
- material.map = material.userData.baseMap ?? null;
2578
- material.color.copy(material.userData.baseColor);
2579
- material.metalness = material.userData.baseMetalness ?? material.metalness;
2580
- material.metalnessMap = material.userData.baseMetalnessMap ?? null;
2581
- material.roughness = material.userData.baseRoughness ?? material.roughness;
2582
- material.roughnessMap = material.userData.baseRoughnessMap ?? null;
2583
- material.emissive.copy(material.userData.baseEmissive ?? new Color7(0, 0, 0));
2584
- material.emissiveIntensity = material.userData.baseEmissiveIntensity ?? material.emissiveIntensity;
2585
- material.emissiveMap = material.userData.baseEmissiveMap ?? null;
2586
- material.opacity = material.userData.baseOpacity ?? 1;
2587
- material.transparent = material.userData.baseTransparent ?? false;
2588
- material.alphaMap = material.userData.baseAlphaMap ?? null;
2589
- material.alphaTest = material.userData.baseAlphaTest ?? 0;
2590
- material.alphaHash = material.userData.baseAlphaHash ?? false;
2591
- material.dithering = material.userData.baseDithering ?? false;
2592
- material.blending = material.userData.baseBlending ?? NormalBlending2;
2593
- material.depthWrite = material.userData.baseDepthWrite ?? true;
2594
- material.side = material.userData.baseSide ?? FrontSide2;
2595
- material.normalMap = material.userData.baseNormalMap ?? null;
2596
- material.normalScale.copy(toSafeVector2(material.userData.baseNormalScale));
2597
- material.bumpMap = material.userData.baseBumpMap ?? null;
2598
- material.bumpScale = material.userData.baseBumpScale ?? material.bumpScale;
2599
- material.aoMap = material.userData.baseAoMap ?? null;
2600
- material.aoMapIntensity = material.userData.baseAoMapIntensity ?? material.aoMapIntensity;
2601
- material.displacementMap = material.userData.baseDisplacementMap ?? null;
2602
- material.displacementScale = material.userData.baseDisplacementScale ?? material.displacementScale;
2603
- material.displacementBias = material.userData.baseDisplacementBias ?? material.displacementBias;
2604
- material.clearcoat = material.userData.baseClearcoat ?? material.clearcoat;
2605
- material.clearcoatMap = material.userData.baseClearcoatMap ?? null;
2606
- material.clearcoatRoughness = material.userData.baseClearcoatRoughness ?? material.clearcoatRoughness;
2607
- material.clearcoatRoughnessMap = material.userData.baseClearcoatRoughnessMap ?? null;
2608
- material.clearcoatNormalMap = material.userData.baseClearcoatNormalMap ?? null;
2609
- material.clearcoatNormalScale.copy(
2610
- toSafeVector2(material.userData.baseClearcoatNormalScale)
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
- material.sheen = material.userData.baseSheen ?? material.sheen;
2613
- material.sheenColor.copy(
2614
- material.userData.baseSheenColor ?? new Color7(0, 0, 0)
2685
+ material2.sheen = material2.userData.baseSheen ?? material2.sheen;
2686
+ material2.sheenColor.copy(
2687
+ material2.userData.baseSheenColor ?? new Color7(0, 0, 0)
2615
2688
  );
2616
- material.sheenColorMap = material.userData.baseSheenColorMap ?? null;
2617
- material.sheenRoughness = material.userData.baseSheenRoughness ?? material.sheenRoughness;
2618
- material.sheenRoughnessMap = material.userData.baseSheenRoughnessMap ?? null;
2619
- material.anisotropy = material.userData.baseAnisotropy ?? material.anisotropy;
2620
- material.anisotropyRotation = material.userData.baseAnisotropyRotation ?? material.anisotropyRotation;
2621
- material.anisotropyMap = material.userData.baseAnisotropyMap ?? null;
2622
- material.transmission = material.userData.baseTransmission ?? material.transmission;
2623
- material.transmissionMap = material.userData.baseTransmissionMap ?? null;
2624
- material.thickness = material.userData.baseThickness ?? material.thickness;
2625
- material.thicknessMap = material.userData.baseThicknessMap ?? null;
2626
- material.attenuationColor.copy(
2627
- material.userData.baseAttenuationColor ?? new Color7("#ffffff")
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
- material.attenuationDistance = material.userData.baseAttenuationDistance ?? material.attenuationDistance;
2630
- material.reflectivity = material.userData.baseReflectivity ?? material.reflectivity;
2631
- material.specularIntensity = material.userData.baseSpecularIntensity ?? material.specularIntensity;
2632
- material.specularIntensityMap = material.userData.baseSpecularIntensityMap ?? null;
2633
- material.specularColor.copy(
2634
- material.userData.baseSpecularColor ?? new Color7("#ffffff")
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
- material.specularColorMap = material.userData.baseSpecularColorMap ?? null;
2637
- material.flatShading = material.userData.baseFlatShading ?? material.flatShading;
2638
- material.envMapIntensity = material.userData.baseEnvMapIntensity ?? material.envMapIntensity;
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
- material.map = getOrCreateTexture(texturePath, SRGBColorSpace4);
2702
- material.color.set(resolvedBaseColor ?? "#ffffff");
2774
+ material2.map = getOrCreateTexture(texturePath, SRGBColorSpace4);
2775
+ material2.color.set(resolvedBaseColor ?? "#ffffff");
2703
2776
  } else if (resolvedBaseColor) {
2704
- material.map = null;
2705
- material.color.set(resolvedBaseColor);
2777
+ material2.map = null;
2778
+ material2.color.set(resolvedBaseColor);
2706
2779
  }
2707
2780
  if (metalnessMapPath) {
2708
- material.metalnessMap = getOrCreateTexture(metalnessMapPath);
2781
+ material2.metalnessMap = getOrCreateTexture(metalnessMapPath);
2709
2782
  }
2710
2783
  if (configuredMaterial?.metalness !== void 0) {
2711
- material.metalness = configuredMaterial.metalness;
2784
+ material2.metalness = configuredMaterial.metalness;
2712
2785
  }
2713
2786
  if (roughnessMapPath) {
2714
- material.roughnessMap = getOrCreateTexture(roughnessMapPath);
2787
+ material2.roughnessMap = getOrCreateTexture(roughnessMapPath);
2715
2788
  }
2716
2789
  if (configuredMaterial?.roughness !== void 0) {
2717
- material.roughness = configuredMaterial.roughness;
2790
+ material2.roughness = configuredMaterial.roughness;
2718
2791
  }
2719
2792
  if (configuredMaterial?.emissive) {
2720
- material.emissive.set(configuredMaterial.emissive);
2793
+ material2.emissive.set(configuredMaterial.emissive);
2721
2794
  } else if (emissiveMapPath) {
2722
- material.emissive.set("#ffffff");
2795
+ material2.emissive.set("#ffffff");
2723
2796
  }
2724
2797
  if (configuredMaterial?.emissiveIntensity !== void 0) {
2725
- material.emissiveIntensity = configuredMaterial.emissiveIntensity;
2798
+ material2.emissiveIntensity = configuredMaterial.emissiveIntensity;
2726
2799
  }
2727
2800
  if (emissiveMapPath) {
2728
- material.emissiveMap = getOrCreateTexture(emissiveMapPath, SRGBColorSpace4);
2801
+ material2.emissiveMap = getOrCreateTexture(emissiveMapPath, SRGBColorSpace4);
2729
2802
  }
2730
2803
  if (configuredMaterial?.opacity !== void 0) {
2731
- material.opacity = configuredMaterial.opacity;
2804
+ material2.opacity = configuredMaterial.opacity;
2732
2805
  }
2733
2806
  if (alphaMapPath) {
2734
- material.alphaMap = getOrCreateTexture(alphaMapPath);
2807
+ material2.alphaMap = getOrCreateTexture(alphaMapPath);
2735
2808
  }
2736
2809
  if (configuredMaterial?.alphaTest !== void 0) {
2737
- material.alphaTest = configuredMaterial.alphaTest;
2810
+ material2.alphaTest = configuredMaterial.alphaTest;
2738
2811
  }
2739
2812
  if (configuredMaterial?.alphaHash !== void 0) {
2740
- material.alphaHash = configuredMaterial.alphaHash;
2813
+ material2.alphaHash = configuredMaterial.alphaHash;
2741
2814
  }
2742
2815
  if (configuredMaterial?.dithering !== void 0) {
2743
- material.dithering = configuredMaterial.dithering;
2816
+ material2.dithering = configuredMaterial.dithering;
2744
2817
  }
2745
2818
  if (configuredMaterial?.blendingMode) {
2746
- material.blending = blendingFromConfig(configuredMaterial.blendingMode);
2747
- material.depthWrite = configuredMaterial.blendingMode !== "additive";
2819
+ material2.blending = blendingFromConfig(configuredMaterial.blendingMode);
2820
+ material2.depthWrite = configuredMaterial.blendingMode !== "additive";
2748
2821
  }
2749
2822
  if (configuredMaterial?.side) {
2750
- material.side = sideFromConfig(configuredMaterial.side);
2823
+ material2.side = sideFromConfig(configuredMaterial.side);
2751
2824
  }
2752
2825
  if (normalMapPath) {
2753
- material.normalMap = getOrCreateTexture(normalMapPath);
2826
+ material2.normalMap = getOrCreateTexture(normalMapPath);
2754
2827
  }
2755
2828
  if (normalMapPath || configuredMaterial?.normalScale !== void 0) {
2756
- const normalScale = configuredMaterial?.normalScale ?? Math.abs(toSafeVector2(material.userData.baseNormalScale).x);
2757
- material.normalScale.set(
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
- material.bumpMap = getOrCreateTexture(bumpMapPath);
2836
+ material2.bumpMap = getOrCreateTexture(bumpMapPath);
2764
2837
  }
2765
2838
  if (configuredMaterial?.bumpScale !== void 0) {
2766
- material.bumpScale = configuredMaterial.bumpScale;
2839
+ material2.bumpScale = configuredMaterial.bumpScale;
2767
2840
  }
2768
2841
  if (aoMapPath) {
2769
- material.aoMap = getOrCreateTexture(aoMapPath);
2842
+ material2.aoMap = getOrCreateTexture(aoMapPath);
2770
2843
  }
2771
2844
  if (configuredMaterial?.aoMapIntensity !== void 0) {
2772
- material.aoMapIntensity = configuredMaterial.aoMapIntensity;
2845
+ material2.aoMapIntensity = configuredMaterial.aoMapIntensity;
2773
2846
  }
2774
2847
  if (displacementMapPath) {
2775
- material.displacementMap = getOrCreateTexture(displacementMapPath);
2848
+ material2.displacementMap = getOrCreateTexture(displacementMapPath);
2776
2849
  }
2777
2850
  if (configuredMaterial?.displacementScale !== void 0) {
2778
- material.displacementScale = configuredMaterial.displacementScale;
2851
+ material2.displacementScale = configuredMaterial.displacementScale;
2779
2852
  }
2780
2853
  if (configuredMaterial?.displacementBias !== void 0) {
2781
- material.displacementBias = configuredMaterial.displacementBias;
2854
+ material2.displacementBias = configuredMaterial.displacementBias;
2782
2855
  }
2783
2856
  if (clearcoatMapPath) {
2784
- material.clearcoatMap = getOrCreateTexture(clearcoatMapPath);
2857
+ material2.clearcoatMap = getOrCreateTexture(clearcoatMapPath);
2785
2858
  }
2786
2859
  if (configuredMaterial?.clearcoat !== void 0) {
2787
- material.clearcoat = configuredMaterial.clearcoat;
2860
+ material2.clearcoat = configuredMaterial.clearcoat;
2788
2861
  }
2789
2862
  if (clearcoatRoughnessMapPath) {
2790
- material.clearcoatRoughnessMap = getOrCreateTexture(
2863
+ material2.clearcoatRoughnessMap = getOrCreateTexture(
2791
2864
  clearcoatRoughnessMapPath
2792
2865
  );
2793
2866
  }
2794
2867
  if (configuredMaterial?.clearcoatRoughness !== void 0) {
2795
- material.clearcoatRoughness = configuredMaterial.clearcoatRoughness;
2868
+ material2.clearcoatRoughness = configuredMaterial.clearcoatRoughness;
2796
2869
  }
2797
2870
  if (clearcoatNormalMapPath) {
2798
- material.clearcoatNormalMap = getOrCreateTexture(clearcoatNormalMapPath);
2871
+ material2.clearcoatNormalMap = getOrCreateTexture(clearcoatNormalMapPath);
2799
2872
  }
2800
2873
  if (clearcoatNormalMapPath || configuredMaterial?.clearcoatNormalScale !== void 0) {
2801
- const clearcoatNormalScale = configuredMaterial?.clearcoatNormalScale ?? Math.abs(toSafeVector2(material.userData.baseClearcoatNormalScale).x);
2802
- material.clearcoatNormalScale.set(
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
- material.sheen = configuredMaterial.sheen;
2881
+ material2.sheen = configuredMaterial.sheen;
2809
2882
  }
2810
2883
  if (configuredMaterial?.sheenColor) {
2811
- material.sheenColor.set(configuredMaterial.sheenColor);
2884
+ material2.sheenColor.set(configuredMaterial.sheenColor);
2812
2885
  }
2813
2886
  if (sheenColorMapPath) {
2814
- material.sheenColorMap = getOrCreateTexture(
2887
+ material2.sheenColorMap = getOrCreateTexture(
2815
2888
  sheenColorMapPath,
2816
2889
  SRGBColorSpace4
2817
2890
  );
2818
2891
  }
2819
2892
  if (configuredMaterial?.sheenRoughness !== void 0) {
2820
- material.sheenRoughness = configuredMaterial.sheenRoughness;
2893
+ material2.sheenRoughness = configuredMaterial.sheenRoughness;
2821
2894
  }
2822
2895
  if (sheenRoughnessMapPath) {
2823
- material.sheenRoughnessMap = getOrCreateTexture(sheenRoughnessMapPath);
2896
+ material2.sheenRoughnessMap = getOrCreateTexture(sheenRoughnessMapPath);
2824
2897
  }
2825
2898
  if (configuredMaterial?.anisotropy !== void 0) {
2826
- material.anisotropy = configuredMaterial.anisotropy;
2899
+ material2.anisotropy = configuredMaterial.anisotropy;
2827
2900
  }
2828
2901
  if (configuredMaterial?.anisotropyRotation !== void 0) {
2829
- material.anisotropyRotation = configuredMaterial.anisotropyRotation;
2902
+ material2.anisotropyRotation = configuredMaterial.anisotropyRotation;
2830
2903
  }
2831
2904
  if (anisotropyMapPath) {
2832
- material.anisotropyMap = getOrCreateTexture(anisotropyMapPath);
2905
+ material2.anisotropyMap = getOrCreateTexture(anisotropyMapPath);
2833
2906
  }
2834
2907
  if (configuredMaterial?.transmission !== void 0) {
2835
- material.transmission = configuredMaterial.transmission;
2908
+ material2.transmission = configuredMaterial.transmission;
2836
2909
  }
2837
2910
  if (transmissionMapPath) {
2838
- material.transmissionMap = getOrCreateTexture(transmissionMapPath);
2911
+ material2.transmissionMap = getOrCreateTexture(transmissionMapPath);
2839
2912
  }
2840
2913
  if (configuredMaterial?.thickness !== void 0) {
2841
- material.thickness = configuredMaterial.thickness;
2914
+ material2.thickness = configuredMaterial.thickness;
2842
2915
  }
2843
2916
  if (thicknessMapPath) {
2844
- material.thicknessMap = getOrCreateTexture(thicknessMapPath);
2917
+ material2.thicknessMap = getOrCreateTexture(thicknessMapPath);
2845
2918
  }
2846
2919
  if (configuredMaterial?.attenuationColor) {
2847
- material.attenuationColor.set(configuredMaterial.attenuationColor);
2920
+ material2.attenuationColor.set(configuredMaterial.attenuationColor);
2848
2921
  }
2849
2922
  if (configuredMaterial?.attenuationDistance !== void 0) {
2850
- material.attenuationDistance = configuredMaterial.attenuationDistance;
2923
+ material2.attenuationDistance = configuredMaterial.attenuationDistance;
2851
2924
  }
2852
2925
  if (configuredMaterial?.reflectivity !== void 0) {
2853
- material.reflectivity = configuredMaterial.reflectivity;
2926
+ material2.reflectivity = configuredMaterial.reflectivity;
2854
2927
  }
2855
2928
  if (configuredMaterial?.specularIntensity !== void 0) {
2856
- material.specularIntensity = configuredMaterial.specularIntensity;
2929
+ material2.specularIntensity = configuredMaterial.specularIntensity;
2857
2930
  }
2858
2931
  if (specularIntensityMapPath) {
2859
- material.specularIntensityMap = getOrCreateTexture(
2932
+ material2.specularIntensityMap = getOrCreateTexture(
2860
2933
  specularIntensityMapPath
2861
2934
  );
2862
2935
  }
2863
2936
  if (configuredMaterial?.specularColor) {
2864
- material.specularColor.set(configuredMaterial.specularColor);
2937
+ material2.specularColor.set(configuredMaterial.specularColor);
2865
2938
  }
2866
2939
  if (specularColorMapPath) {
2867
- material.specularColorMap = getOrCreateTexture(
2940
+ material2.specularColorMap = getOrCreateTexture(
2868
2941
  specularColorMapPath,
2869
2942
  SRGBColorSpace4
2870
2943
  );
2871
2944
  }
2872
- material.transparent = material.opacity < 1 || material.transmission > 0 || material.blending !== NormalBlending2 || Boolean((alphaMapPath || material.alphaMap) && material.alphaTest <= 0);
2873
- if (!material.transparent) {
2874
- material.depthWrite = true;
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 material of Object.values(perNodeMaterials)) {
3018
- material.dispose();
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, material] of Object.entries(perNodeMaterials)) {
3103
+ for (const [nodeName, material2] of Object.entries(perNodeMaterials)) {
3031
3104
  const basic = new MeshBasicMaterial();
3032
- basic.color.copy(material.color);
3033
- basic.map = material.map;
3034
- basic.alphaMap = material.alphaMap;
3035
- basic.alphaTest = material.alphaTest;
3036
- basic.transparent = material.transparent;
3037
- basic.opacity = material.opacity;
3038
- basic.side = material.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, material] of Object.entries(perNodeMaterials)) {
3128
+ for (const [nodeName, material2] of Object.entries(perNodeMaterials)) {
3056
3129
  const matcap = new MeshMatcapMaterial();
3057
3130
  matcap.matcap = matcapTexture;
3058
- matcap.color.copy(material.color);
3059
- matcap.alphaMap = material.alphaMap;
3060
- matcap.alphaTest = material.alphaTest;
3061
- matcap.side = material.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 material of Object.values(perNodeBasicMaterials))
3069
- material.dispose();
3070
- for (const material of Object.values(perNodeNormalMaterials))
3071
- material.dispose();
3072
- for (const material of Object.values(perNodeMatcapMaterials))
3073
- material.dispose();
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 material = perNodeMaterials[nodeName];
3120
- if (!material) continue;
3121
- const activeMaterial = materialForNode(nodeName);
3122
- if (activeMaterial) {
3123
- node.material = activeMaterial;
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(material, configuredMaterial, resolvedBaseColor);
3230
+ applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor);
3156
3231
  if (isHighlightedByHover) {
3157
- material.emissive?.add(HIGHLIGHT_EMISSIVE_TINT);
3232
+ material2.emissive?.add(HIGHLIGHT_EMISSIVE_TINT);
3158
3233
  }
3159
- material.needsUpdate = true;
3234
+ material2.needsUpdate = true;
3160
3235
  if (basicMaterial) {
3161
- basicMaterial.color.copy(material.color);
3162
- basicMaterial.map = material.map;
3163
- basicMaterial.alphaMap = material.alphaMap;
3164
- basicMaterial.alphaTest = material.alphaTest;
3165
- basicMaterial.transparent = material.transparent;
3166
- basicMaterial.opacity = material.opacity;
3167
- basicMaterial.side = material.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(material.color);
3250
+ matcapMaterial.color.copy(material2.color);
3176
3251
  }
3177
- matcapMaterial.map = material.map;
3178
- matcapMaterial.alphaMap = material.alphaMap;
3179
- matcapMaterial.alphaTest = material.alphaTest;
3180
- matcapMaterial.transparent = material.transparent;
3181
- matcapMaterial.opacity = material.opacity;
3182
- matcapMaterial.side = material.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: resolvedSceneConfig,
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: resolvedSceneConfig,
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: { top: `${topLeftRow(xrToolsVisible ? 1 : 0)}px` },
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: ["pbr", "matcap"].map((option) => /* @__PURE__ */ jsx22(
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, (material) => ({
13110
- ...material,
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, (material) => ({
13125
- ...material,
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, (material) => ({
13149
- ...material,
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, (material) => ({
13160
- ...material,
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, (material) => ({
13201
- ...material,
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, (material) => ({
13213
- ...material,
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, (material) => ({
13227
- ...material,
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, (material) => ({
13271
- ...material,
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, (material) => ({
13284
- ...material,
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, (material) => ({
13317
- ...material,
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, (material) => ({
13331
- ...material,
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, (material) => ({
13366
- ...material,
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, (material) => ({
13380
- ...material,
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, (material) => ({
13401
- ...material,
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, (material) => ({
13414
- ...material,
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, (material) => ({
13428
- ...material,
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, (material) => ({
13466
- ...material,
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, (material) => ({
13489
- ...material,
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, (material) => ({
13501
- ...material,
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, (material) => ({
13536
- ...material,
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, (material) => ({
13550
- ...material,
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, (material) => ({
13585
- ...material,
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, (material) => ({
13621
- ...material,
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, (material) => ({
13648
- ...material,
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, (material) => ({
13671
- ...material,
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, (material) => ({
13683
- ...material,
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, (material) => ({
13709
- ...material,
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, (material) => ({
13720
- ...material,
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, (material) => ({
13743
- ...material,
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, (material) => ({
13778
- ...material,
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, (material) => ({
13801
- ...material,
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, (material) => ({
13836
- ...material,
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, (material) => ({
13859
- ...material,
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, (material) => ({
13879
- ...material,
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, (material) => ({
13893
- ...material,
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, (material) => ({
13916
- ...material,
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 material = binding.style?.material;
14916
- if (!material) return [];
14917
- return Object.values(material).filter(
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(material) {
15036
- if (!material) return void 0;
15037
- return Object.values(material).some(materialFieldHasValue) ? material : void 0;
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(material, panel) {
15040
- if (!material) return false;
15220
+ function isMaterialPanelEnabled(material2, panel) {
15221
+ if (!material2) return false;
15041
15222
  return MATERIAL_PANEL_FIELDS[panel].some(
15042
- (field) => materialFieldHasValue(material[field])
15223
+ (field) => materialFieldHasValue(material2[field])
15043
15224
  );
15044
15225
  }
15045
- function createMaterialPanelEnabledState(material) {
15226
+ function createMaterialPanelEnabledState(material2) {
15046
15227
  return {
15047
- pbr: isMaterialPanelEnabled(material, "pbr"),
15048
- roughness: isMaterialPanelEnabled(material, "roughness"),
15049
- emission: isMaterialPanelEnabled(material, "emission"),
15050
- opacity: isMaterialPanelEnabled(material, "opacity"),
15051
- normalBump: isMaterialPanelEnabled(material, "normalBump"),
15052
- displacement: isMaterialPanelEnabled(material, "displacement"),
15053
- ao: isMaterialPanelEnabled(material, "ao"),
15054
- clearcoat: isMaterialPanelEnabled(material, "clearcoat"),
15055
- sheen: isMaterialPanelEnabled(material, "sheen"),
15056
- anisotropy: isMaterialPanelEnabled(material, "anisotropy"),
15057
- transmission: isMaterialPanelEnabled(material, "transmission"),
15058
- faces: isMaterialPanelEnabled(material, "faces")
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(material, panel) {
15076
- if (!material) return void 0;
15077
- const nextMaterial = { ...material };
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, (material) => ({
15336
- ...material,
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, (material) => ({
15967
- ...material,
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/useObjectVisibility.ts
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 useObjectVisibility(objectBindings, onObjectBindingsChange) {
17524
- const updateVisibility = useCallback12(
17525
- (objectId, visible) => {
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
- if (!bindingKey) return;
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, onObjectBindingsChange]
17776
+ [objectBindings]
17539
17777
  );
17540
17778
  const hideObject = useCallback12(
17541
- (objectId) => updateVisibility(objectId, false),
17542
- [updateVisibility]
17779
+ (objectId) => patch(objectId, { visible: false }),
17780
+ [patch]
17543
17781
  );
17544
17782
  const showObject = useCallback12(
17545
- (objectId) => updateVisibility(objectId, true),
17546
- [updateVisibility]
17783
+ (objectId) => patch(objectId, { visible: true }),
17784
+ [patch]
17547
17785
  );
17548
17786
  const toggleObjectVisibility = useCallback12(
17549
17787
  (objectId) => {
17550
- const bindingKey = resolveBindingKey(objectBindings, objectId);
17551
- if (!bindingKey) return;
17552
- const targetBinding = objectBindings[bindingKey];
17553
- if (!targetBinding) return;
17554
- onObjectBindingsChange({
17555
- ...objectBindings,
17556
- [bindingKey]: {
17557
- ...targetBinding,
17558
- visible: targetBinding.visible === false
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
- [objectBindings, onObjectBindingsChange]
17803
+ [setObjectBindings]
17563
17804
  );
17564
17805
  const clearHiddenObjects = useCallback12(() => {
17565
- onObjectBindingsChange(
17566
- Object.fromEntries(
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
- }, [objectBindings, onObjectBindingsChange]);
17809
+ }, [setObjectBindings]);
17574
17810
  const hiddenObjects = useMemo20(
17575
17811
  () => getHiddenObjects(objectBindings),
17576
17812
  [objectBindings]
17577
17813
  );
17578
17814
  const hiddenObjectIds = useMemo20(
17579
- () => Object.entries(hiddenObjects).map(
17580
- ([bindingKey]) => objectBindings[bindingKey]?.id ?? bindingKey
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 useState28 } from "react";
17846
+ import { useCallback as useCallback13, useState as useState29 } from "react";
17604
17847
  function useViewerHover() {
17605
- const [hoveredObjectBinding, setHoveredObjectBinding] = useState28(null);
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 useState29 } from "react";
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] = useState29(true);
17626
- const [isReady, setIsReady] = useState29(false);
17627
- const [error, setError] = useState29(null);
17628
- const [bounds, setBounds] = useState29(null);
17629
- const [objectCount, setObjectCount] = useState29(0);
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 useState30 } from "react";
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] = useState30(false);
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
- useObjectVisibility,
18132
+ useObjectBindings,
17888
18133
  useShareableViewerState,
17889
18134
  useViewerActions,
17890
18135
  useViewerAnimations,