@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.js CHANGED
@@ -52,9 +52,11 @@ __export(index_exports, {
52
52
  MATERIAL_SIDES: () => MATERIAL_SIDES,
53
53
  ModelViewer: () => ModelViewer,
54
54
  SimpleModelViewer: () => SimpleModelViewer,
55
+ material: () => material,
56
+ patchBindings: () => patchBindings,
55
57
  useObjectBinding: () => useObjectBinding,
56
58
  useObjectBindingIds: () => useObjectBindingIds,
57
- useObjectVisibility: () => useObjectVisibility,
59
+ useObjectBindings: () => useObjectBindings,
58
60
  useShareableViewerState: () => useShareableViewerState,
59
61
  useViewerActions: () => useViewerActions,
60
62
  useViewerAnimations: () => useViewerAnimations,
@@ -242,7 +244,7 @@ function styleInject(css, { insertAt } = {}) {
242
244
  }
243
245
 
244
246
  // src/styles/ModelViewer.css
245
- styleInject('.ri-viewer--license-checking,\n.ri-viewer--license-invalid {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n background-color: #f9f9f9;\n}\n.ri-viewer {\n --ri-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --ri-viewer-bg: #1c1c1d;\n --ri-viewer-text: #111827;\n --ri-panel-bg: #ffffff;\n --ri-panel-border: #e5e7eb;\n --ri-panel-title: #111827;\n --ri-panel-muted-text: #4b5563;\n --ri-panel-subtle-text: #9ca3af;\n --ri-panel-surface: #f9fafb;\n --ri-panel-surface-hover: #f3f4f6;\n --ri-panel-surface-border: #e5e7eb;\n --ri-panel-button-text: #1f2937;\n --ri-panel-accent: #2563eb;\n --ri-panel-accent-contrast: #ffffff;\n --ri-license-overlay-bg: rgba(0, 0, 0, 0.04);\n --ri-license-overlay-border: rgba(0, 0, 0, 0.08);\n --ri-license-overlay-text: #888;\n --ri-license-overlay-error-bg: rgba(192, 57, 43, 0.06);\n --ri-license-overlay-error-border: rgba(192, 57, 43, 0.2);\n --ri-license-overlay-error-text: #c0392b;\n --ri-sidepanel-font-family:\n Inter,\n "Segoe UI",\n Helvetica,\n Arial,\n sans-serif;\n --ri-sidepanel-bg: var(--ri-databinding-bg, #111318);\n --ri-sidepanel-open-btn-bg: rgba(20, 20, 20, 0.82);\n --ri-sidepanel-open-btn-bg-hover: #27282b7e;\n --ri-sidepanel-border: rgba(255, 255, 255, 0.06);\n --ri-sidepanel-divider: rgba(255, 255, 255, 0.04);\n --ri-sidepanel-border-strong: rgba(255, 255, 255, 0.08);\n --ri-sidepanel-border-active: rgba(255, 255, 255, 0.18);\n --ri-sidepanel-shadow-left: 4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-sidepanel-shadow-right: -4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-sidepanel-text: var(--ri-databinding-text, #f1f5f9);\n --ri-sidepanel-text-emphasis: var(--ri-databinding-text-hover, #cbd5e1);\n --ri-sidepanel-text-muted: var(--ri-databinding-text-muted, #94a3b8);\n --ri-sidepanel-text-subtle: var(--ri-databinding-text-subtle, #64748b);\n --ri-sidepanel-placeholder: var(--ri-databinding-text-faint, #475569);\n --ri-sidepanel-surface: rgba(255, 255, 255, 0.05);\n --ri-sidepanel-surface-hover: rgba(255, 255, 255, 0.1);\n --ri-sidepanel-accent: #3b82f6;\n --ri-sidepanel-accent-soft: rgba(59, 130, 246, 0.12);\n --ri-databinding-bg: #111318;\n --ri-databinding-border: rgba(255, 255, 255, 0.06);\n --ri-databinding-border-strong: rgba(255, 255, 255, 0.08);\n --ri-databinding-border-hover: rgba(255, 255, 255, 0.15);\n --ri-databinding-shadow: 4px 0 24px rgba(0, 0, 0, 0.35);\n --ri-databinding-font-family:\n "Inter",\n "Segoe UI",\n sans-serif;\n --ri-databinding-mono-font-family:\n "JetBrains Mono",\n "Fira Code",\n "Menlo",\n monospace;\n --ri-databinding-text: #f1f5f9;\n --ri-databinding-text-muted: #94a3b8;\n --ri-databinding-text-subtle: #64748b;\n --ri-databinding-text-faint: #475569;\n --ri-databinding-text-hover: #cbd5e1;\n --ri-databinding-surface: rgba(255, 255, 255, 0.03);\n --ri-databinding-surface-hover: rgba(255, 255, 255, 0.06);\n --ri-databinding-accent: #93c5fd;\n --ri-databinding-accent-glow: rgba(147, 197, 253, 0.8);\n --ri-databinding-accent-border: rgba(96, 165, 250, 0.55);\n --ri-databinding-accent-gradient:\n linear-gradient(\n \n 135deg,\n #1e3a8a 0%,\n #1d4ed8 60%,\n #2563eb 100% );\n --ri-databinding-accent-gradient-active:\n linear-gradient(\n \n 135deg,\n #1e3a8a 0%,\n #1e40af 100% );\n display: flex;\n flex-direction: row;\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n background: var(--ri-viewer-bg);\n position: relative;\n color: var(--ri-viewer-text);\n font-family: var(--ri-font-family);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n font-synthesis: none;\n}\n.ri-viewer * {\n box-sizing: border-box;\n}\n.ri-viewerLeft {\n display: flex;\n flex: 1;\n min-height: 0;\n min-width: 0;\n position: relative;\n}\n.ri-viewerRight {\n display: flex;\n flex-shrink: 0;\n min-width: 0;\n}\n.ri-panel {\n width: 280px;\n min-height: 0;\n background: var(--ri-panel-bg);\n border-right: 1px solid var(--ri-panel-border);\n display: flex;\n flex-direction: column;\n padding: 20px;\n overflow-y: auto;\n flex-shrink: 0;\n transition: margin-left 0.3s ease, transform 0.3s ease;\n}\n.ri-panel--open {\n transform: translateX(0);\n margin-left: 0;\n}\n.ri-panel--closed {\n transform: translateX(-100%);\n margin-left: -280px;\n}\n.ri-panelHeader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 12px;\n}\n.ri-panelTitle {\n margin: 0;\n font-size: 15px;\n font-weight: 700;\n color: var(--ri-panel-title);\n line-height: 1.4;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ri-closeButton {\n border: 0;\n background: transparent;\n color: var(--ri-panel-subtle-text);\n font-size: 22px;\n line-height: 1;\n padding: 0;\n cursor: pointer;\n flex-shrink: 0;\n transition: color 0.2s ease;\n}\n.ri-closeButton:hover {\n color: var(--ri-panel-muted-text);\n}\n.ri-json {\n margin: 0 0 16px;\n padding: 12px;\n background: var(--ri-panel-surface);\n border: 1px solid var(--ri-panel-surface-border);\n border-radius: 12px;\n font-size: 12px;\n line-height: 1.55;\n color: var(--ri-panel-button-text);\n overflow-x: auto;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.ri-divider {\n border-top: 1px solid var(--ri-panel-border);\n margin-bottom: 12px;\n}\n.ri-sectionLabel {\n margin-bottom: 8px;\n font-size: 11px;\n font-weight: 700;\n color: var(--ri-panel-subtle-text);\n text-transform: uppercase;\n letter-spacing: 0.16em;\n}\n.ri-actionList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.ri-actionButton {\n width: 100%;\n border-radius: 8px;\n border: 1px solid var(--ri-panel-surface-border);\n background: var(--ri-panel-surface);\n color: var(--ri-panel-button-text);\n padding: 10px 12px;\n text-align: left;\n font-size: 14px;\n line-height: 1.4;\n cursor: pointer;\n transition:\n background-color 0.2s ease,\n border-color 0.2s ease,\n color 0.2s ease;\n}\n.ri-actionButton:hover {\n background: var(--ri-panel-surface-hover);\n border-color: var(--ri-panel-border);\n}\n.ri-actionButton--active {\n background: var(--ri-panel-accent);\n border-color: var(--ri-panel-accent);\n color: var(--ri-panel-accent-contrast);\n}\n.ri-actionButton--active:hover {\n background: var(--ri-panel-accent);\n border-color: var(--ri-panel-accent);\n}\n.ri-canvasArea {\n flex: 1 1 auto;\n position: relative;\n min-width: 0;\n}\n.ri-canvasArea--measuring {\n cursor: crosshair;\n}\n.ri-canvasArea canvas {\n opacity: 1;\n transition: opacity 0.18s ease;\n}\n.ri-canvasArea--positioning canvas {\n opacity: 0;\n transition: none;\n}\n.ri-bindingsMismatchBanner {\n position: absolute;\n top: 12px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 20;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 7px 12px 7px 10px;\n border-radius: 8px;\n background: rgba(30, 20, 0, 0.88);\n border: 1px solid rgba(251, 191, 36, 0.35);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n color: rgb(251, 191, 36);\n font-size: 12px;\n font-weight: 500;\n line-height: 1.4;\n white-space: nowrap;\n pointer-events: auto;\n max-width: calc(100% - 32px);\n white-space: normal;\n text-align: center;\n}\n.ri-bindingsMismatchBanner__icon {\n flex-shrink: 0;\n}\n.ri-bindingsMismatchBanner__dismiss {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: 4px;\n padding: 2px;\n background: none;\n border: none;\n border-radius: 4px;\n color: rgba(251, 191, 36, 0.7);\n cursor: pointer;\n transition: color 0.15s ease, background 0.15s ease;\n}\n.ri-bindingsMismatchBanner__dismiss:hover {\n color: rgb(251, 191, 36);\n background: rgba(251, 191, 36, 0.12);\n}\n.ri-measureTools {\n position: absolute;\n top: 16px;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-measureButton {\n width: 34px;\n height: 34px;\n padding: 0;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n color: rgba(255, 255, 255, 0.75);\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n touch-action: manipulation;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n transition:\n background 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease;\n}\n.ri-measureButton:hover {\n background: rgba(40, 40, 40, 0.92);\n color: #fff;\n border-color: rgba(255, 255, 255, 0.25);\n}\n.ri-measureButton:active {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-measureButton--active {\n background: #22d3ee;\n border-color: #22d3ee;\n color: #06343c;\n}\n.ri-measureButton--active:hover {\n background: #38dbf0;\n color: #06343c;\n}\n.ri-measureHint {\n padding: 6px 10px;\n border-radius: 8px;\n font-size: 12px;\n line-height: 1;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n white-space: nowrap;\n}\n.ri-explodeTools {\n position: absolute;\n top: 16px;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-explodeSlider {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 34px;\n padding: 0 12px;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 8px;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n}\n.ri-explodeSlider input[type=range] {\n width: 120px;\n accent-color: #22d3ee;\n cursor: pointer;\n}\n.ri-cinematicTools {\n position: absolute;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-measureDot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: #22d3ee;\n border: 2px solid #fff;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);\n pointer-events: none;\n}\n.ri-measureLabel {\n padding: 3px 7px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n white-space: nowrap;\n color: #06343c;\n background: #22d3ee;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n pointer-events: none;\n user-select: none;\n}\n.ri-xrTools {\n position: absolute;\n left: 16px;\n z-index: 10;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.ri-xrButton {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n height: 34px;\n padding: 0 10px;\n font-size: 13px;\n font-weight: 500;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.16);\n border-radius: 8px;\n cursor: pointer;\n touch-action: manipulation;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n transition: background 0.15s ease, border-color 0.15s ease;\n}\n.ri-xrButton:hover {\n background: rgba(40, 40, 40, 0.92);\n border-color: rgba(255, 255, 255, 0.3);\n}\n.ri-mobileHandoffBackdrop {\n position: absolute;\n z-index: 30;\n inset: 0;\n display: grid;\n place-items: center;\n padding: 20px;\n background: rgba(2, 6, 23, 0.68);\n backdrop-filter: blur(5px);\n -webkit-backdrop-filter: blur(5px);\n}\n.ri-mobileHandoffDialog {\n position: relative;\n display: flex;\n width: min(100%, 300px);\n flex-direction: column;\n align-items: center;\n padding: 16px;\n text-align: center;\n color: #e2e8f0;\n background: #0f172a;\n border: 1px solid rgba(148, 163, 184, 0.3);\n border-radius: 16px;\n box-shadow: 0 24px 80px rgba(0, 0, 0, 0.5);\n}\n.ri-mobileHandoffDialog > svg {\n color: #67e8f9;\n}\n.ri-mobileHandoffDialog h2 {\n margin: 12px 0 0;\n font-size: 18px;\n line-height: 1.25;\n color: #fff;\n}\n.ri-mobileHandoffDialog p {\n margin: 8px 0 16px;\n font-size: 13px;\n line-height: 1.5;\n color: #cbd5e1;\n}\n.ri-mobileHandoffQr {\n display: grid;\n place-items: center;\n padding: 10px;\n background: #fff;\n border-radius: 10px;\n}\n.ri-mobileHandoffDialog a {\n margin-top: 16px;\n font-size: 13px;\n font-weight: 600;\n color: #a5f3fc;\n text-decoration: underline;\n text-underline-offset: 3px;\n}\n.ri-mobileHandoffClose {\n position: absolute;\n top: 10px;\n right: 10px;\n display: grid;\n width: 30px;\n height: 30px;\n place-items: center;\n padding: 0;\n color: #cbd5e1;\n background: transparent;\n border: 0;\n border-radius: 6px;\n cursor: pointer;\n}\n.ri-mobileHandoffClose:hover {\n color: #fff;\n background: rgba(255, 255, 255, 0.1);\n}\n.ri-arHintOverlay {\n position: absolute;\n top: 18px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n width: min(calc(100% - 32px), 360px);\n pointer-events: none;\n}\n.ri-arHint {\n margin: 0 auto;\n width: fit-content;\n max-width: 100%;\n padding: 6px 12px;\n font-size: 11px;\n font-weight: 500;\n line-height: 1.2;\n text-align: center;\n color: rgba(255, 255, 255, 0.92);\n background: rgba(20, 20, 20, 0.58);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 999px;\n user-select: none;\n backdrop-filter: blur(4px);\n -webkit-backdrop-filter: blur(4px);\n}\n.ri-arOverlay {\n position: absolute;\n bottom: 24px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10;\n width: min(calc(100% - 32px), 420px);\n pointer-events: none;\n}\n.ri-arControls {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n}\n.ri-arStatus,\n.ri-arActionButton {\n max-width: 100%;\n padding: 8px 16px;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.3;\n text-align: center;\n white-space: nowrap;\n color: #fff;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.16);\n border-radius: 19px;\n user-select: none;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n}\n.ri-arStatus {\n pointer-events: none;\n}\n.ri-arActionButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n cursor: pointer;\n pointer-events: auto;\n touch-action: manipulation;\n -webkit-tap-highlight-color: transparent;\n transition:\n background 0.15s ease,\n border-color 0.15s ease,\n color 0.15s ease,\n transform 0.15s ease;\n}\n.ri-arActionButton svg {\n flex-shrink: 0;\n}\n.ri-arActionButton:hover {\n background: rgba(40, 40, 40, 0.92);\n border-color: rgba(255, 255, 255, 0.3);\n}\n.ri-arActionButton:active {\n transform: scale(0.98);\n}\n.ri-arActionButton--primary {\n color: #06343c;\n background: #22d3ee;\n border-color: transparent;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n}\n.ri-arActionButton--primary:hover {\n background: #67e8f9;\n border-color: transparent;\n}\n.ri-arActionButton.ri-arActionButton--active,\n.ri-arActionButton[aria-pressed=true],\n.ri-arActionButton.ri-arActionButton--active:hover,\n.ri-arActionButton[aria-pressed=true]:hover,\n.ri-arActionButton.ri-arActionButton--active:active,\n.ri-arActionButton[aria-pressed=true]:active {\n color: #06343c;\n background: #22d3ee;\n border-color: transparent;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n}\n.ri-hiddenInput {\n display: none;\n}\n.ri-popover {\n position: absolute;\n z-index: 50;\n background: #ffffff;\n border: 1px solid #e5e7eb;\n border-radius: 16px;\n box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18);\n color: #111827;\n}\n.ri-popover--inline {\n position: static;\n z-index: auto;\n width: 100%;\n border-radius: 10px;\n box-shadow: none;\n}\n.ri-popover--inline .react-colorful {\n width: 100%;\n}\n.ri-popover--inline {\n background: var(--ri-databinding-surface, rgba(255, 255, 255, 0.03));\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n color: var(--ri-databinding-text, #f1f5f9);\n}\n.ri-popover--inline .ri-popoverHeader {\n border-bottom-color: var( --ri-databinding-border-strong, rgba(255, 255, 255, 0.08) );\n}\n.ri-popover--inline .ri-popoverLabel {\n color: var(--ri-databinding-text-muted, #94a3b8);\n}\n.ri-popover--inline .ri-colorCode {\n color: var(--ri-databinding-text-muted, #94a3b8);\n}\n.ri-popover--inline .ri-secondaryButton {\n background: var(--ri-databinding-surface-hover, rgba(255, 255, 255, 0.06));\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n color: var(--ri-databinding-text, #f1f5f9);\n}\n.ri-popover--inline .ri-secondaryButton:hover {\n background: rgba(255, 255, 255, 0.12);\n}\n.ri-popover--inline .ri-previewFrame {\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n}\n.ri-popover--inline .ri-emptyState {\n border-color: var(--ri-databinding-border-hover, rgba(255, 255, 255, 0.15));\n color: var(--ri-databinding-text-subtle, #64748b);\n}\n.ri-popover--inline .ri-uploadButton {\n background: var(--ri-databinding-accent-gradient, #2563eb);\n color: #ffffff;\n}\n.ri-popover--inline .ri-uploadButton:hover {\n background: var(--ri-databinding-accent-gradient-active, #1d4ed8);\n}\n.ri-popover--inline .ri-removeButton {\n background: transparent;\n border-color: var(--ri-databinding-border-strong, rgba(255, 255, 255, 0.08));\n color: var(--ri-databinding-text-muted, #94a3b8);\n}\n.ri-popover--inline .ri-removeButton:hover {\n background: var(--ri-databinding-surface-hover, rgba(255, 255, 255, 0.06));\n}\n.ri-popover--inline .ri-formatHint {\n color: var(--ri-databinding-text-subtle, #64748b);\n}\n.ri-popoverHeader {\n padding: 12px 16px;\n}\n.ri-popoverLabel {\n margin: 0 0 8px;\n font-size: 11px;\n font-weight: 700;\n color: #6b7280;\n text-transform: uppercase;\n letter-spacing: 0.16em;\n}\n.ri-inlineRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n margin-top: 8px;\n}\n.ri-colorCode {\n font-size: 12px;\n color: #9ca3af;\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n monospace;\n text-transform: uppercase;\n}\n.ri-secondaryButton {\n border: 1px solid #e5e7eb;\n background: #f3f4f6;\n color: #374151;\n border-radius: 8px;\n padding: 6px 10px;\n font-size: 12px;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-secondaryButton:hover {\n background: #e5e7eb;\n}\n.ri-materialBody {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding-left: 12px;\n padding-right: 12px;\n padding-bottom: 12px;\n}\n.ri-previewFrame {\n position: relative;\n height: 112px;\n border-radius: 12px;\n overflow: hidden;\n border: 1px solid #e5e7eb;\n}\n.ri-previewImage {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n.ri-previewCaption {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.4);\n padding: 4px 8px;\n color: #ffffff;\n font-size: 10px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ri-emptyState {\n height: 112px;\n border-radius: 12px;\n border: 2px dashed #e5e7eb;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n color: #d1d5db;\n text-align: center;\n}\n.ri-uploadButton {\n width: 100%;\n border: 0;\n border-radius: 10px;\n background: #111827;\n color: #ffffff;\n padding: 10px 12px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-uploadButton:hover {\n background: #374151;\n}\n.ri-removeButton {\n width: 100%;\n border-radius: 10px;\n border: 1px solid #e5e7eb;\n background: #ffffff;\n color: #6b7280;\n padding: 8px 12px;\n font-size: 12px;\n cursor: pointer;\n transition: background-color 0.2s ease;\n}\n.ri-removeButton:hover {\n background: #f9fafb;\n}\n.ri-formatHint {\n margin: 0;\n text-align: center;\n font-size: 10px;\n color: #d1d5db;\n}\n.ri-buttonContainer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 8px;\n position: absolute;\n bottom: 12px;\n left: 12px;\n right: 12px;\n z-index: 10;\n}\n.ri-tourControls {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-shrink: 0;\n}\n.ri-rightControls {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-shrink: 0;\n margin-left: auto;\n}\n.ri-rightControls:only-child {\n margin-left: auto;\n}\n.ri-navButtonSingle,\n.ri-tourButton,\n.ri-resetButton,\n.ri-downloadButton {\n height: 34px;\n background: rgba(20, 20, 20, 0.82);\n border: 1px solid rgba(255, 255, 255, 0.12);\n color: rgba(255, 255, 255, 0.75);\n border-radius: 8px;\n backdrop-filter: blur(6px);\n -webkit-backdrop-filter: blur(6px);\n cursor: pointer;\n transition:\n background 0.15s ease,\n color 0.15s ease,\n border-color 0.15s ease,\n transform 0.15s ease;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n}\n.ri-navButtonSingle:hover:not(:disabled),\n.ri-tourButton:hover:not(:disabled),\n.ri-resetButton:hover:not(:disabled) {\n background: rgba(40, 40, 40, 0.92);\n color: #fff;\n border-color: rgba(255, 255, 255, 0.25);\n}\n.ri-navButtonSingle:active:not(:disabled),\n.ri-tourButton:active:not(:disabled),\n.ri-resetButton:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-navButtonSingle:disabled,\n.ri-tourButton:disabled,\n.ri-resetButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.ri-navButtonSingle {\n padding: 0 16px;\n font-size: 13px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 8px;\n white-space: nowrap;\n}\n.ri-tourButton {\n padding: 0 12px;\n font-size: 13px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 4px;\n white-space: nowrap;\n}\n.ri-tourButton svg {\n width: 15px;\n height: 15px;\n flex-shrink: 0;\n}\n.ri-tourButton--stop {\n border-color: rgba(255, 68, 68, 0.4);\n color: #ff6b6b;\n}\n.ri-tourButton--stop:hover:not(:disabled) {\n background: rgba(255, 68, 68, 0.15);\n border-color: rgba(255, 68, 68, 0.6);\n color: #ff6b6b;\n}\n.ri-tourButton--stop:active:not(:disabled) {\n background: #ff4444;\n color: #fff;\n border-color: #ff4444;\n}\n.ri-tourButton--next {\n border-color: rgba(56, 219, 240, 0.3);\n color: #38dbf0;\n}\n.ri-tourButton--next:hover:not(:disabled) {\n background: rgba(56, 219, 240, 0.12);\n border-color: rgba(56, 219, 240, 0.5);\n color: #38dbf0;\n}\n.ri-tourButton--next:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-resetButton {\n width: 34px;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.ri-resetButton svg {\n width: 15px;\n height: 15px;\n pointer-events: none;\n flex-shrink: 0;\n}\n.ri-resetButton:focus-visible {\n outline: 2px solid rgba(255, 255, 255, 0.5);\n outline-offset: 2px;\n}\n.ri-downloadWrapper {\n position: relative;\n display: inline-flex;\n flex-direction: column;\n align-items: stretch;\n}\n.ri-downloadButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 0 16px;\n font-size: 13px;\n font-weight: 500;\n white-space: nowrap;\n}\n.ri-downloadButton:hover:not(:disabled) {\n background: rgba(0, 0, 0, 0.72);\n border-color: rgba(255, 255, 255, 0.3);\n color: #fff;\n}\n.ri-downloadButton:active:not(:disabled) {\n background: #38dbf0;\n color: #06343c;\n border-color: #38dbf0;\n}\n.ri-downloadButton:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.ri-downloadButton__spinner {\n display: inline-block;\n width: 13px;\n height: 13px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top-color: #fff;\n border-radius: 50%;\n animation: ri-spin 0.7s linear infinite;\n flex-shrink: 0;\n}\n.ri-downloadMenu {\n position: absolute;\n bottom: calc(100% + 6px);\n right: 0;\n min-width: 168px;\n background: rgba(20, 20, 20, 0.92);\n border: 1px solid rgba(255, 255, 255, 0.14);\n border-radius: 10px;\n overflow: hidden;\n z-index: 100;\n backdrop-filter: blur(10px);\n -webkit-backdrop-filter: blur(10px);\n animation: ri-menuReveal 0.12s ease;\n}\n@keyframes ri-menuReveal {\n from {\n opacity: 0;\n transform: translateY(4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n.ri-downloadMenuItem {\n display: flex;\n align-items: center;\n gap: 9px;\n width: 100%;\n padding: 10px 14px;\n font-size: 13px;\n font-weight: 400;\n color: #fff;\n background: transparent;\n border: none;\n cursor: pointer;\n text-align: left;\n transition: background 0.1s ease;\n 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');
247
+ 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');
246
248
 
247
249
  // src/components/LicenseOverlay.tsx
248
250
  var import_lucide_react = require("lucide-react");
@@ -433,9 +435,9 @@ function addTriangleEdges(edges, triangle, indexed) {
433
435
  addEdge(edges, triangle[1], triangle[2], indexed);
434
436
  addEdge(edges, triangle[2], triangle[0], indexed);
435
437
  }
436
- function materialForIndex(material, materialIndex) {
437
- if (!Array.isArray(material)) return material;
438
- return material[materialIndex] ?? null;
438
+ function materialForIndex(material2, materialIndex) {
439
+ if (!Array.isArray(material2)) return material2;
440
+ return material2[materialIndex] ?? null;
439
441
  }
440
442
  function collectUvLayouts(scene) {
441
443
  const layouts = /* @__PURE__ */ new Map();
@@ -454,16 +456,16 @@ function collectUvLayouts(scene) {
454
456
  const groups = geometry.groups.length > 0 ? geometry.groups : [{ start: 0, count: elementCount, materialIndex: 0 }];
455
457
  for (const group of groups) {
456
458
  const materialIndex = group.materialIndex ?? 0;
457
- const material = materialForIndex(mesh.material, materialIndex);
458
- if (!material) continue;
459
- let layout = layouts.get(material.uuid);
459
+ const material2 = materialForIndex(mesh.material, materialIndex);
460
+ if (!material2) continue;
461
+ let layout = layouts.get(material2.uuid);
460
462
  if (!layout) {
461
463
  unnamedMaterialIndex += 1;
462
464
  layout = {
463
- name: material.name || `Material ${unnamedMaterialIndex}`,
465
+ name: material2.name || `Material ${unnamedMaterialIndex}`,
464
466
  layers: /* @__PURE__ */ new Map()
465
467
  };
466
- layouts.set(material.uuid, layout);
468
+ layouts.set(material2.uuid, layout);
467
469
  }
468
470
  let layer = layout.layers.get(mesh.uuid);
469
471
  if (!layer) {
@@ -644,6 +646,11 @@ var LIGHT_TYPES = [
644
646
  "point",
645
647
  "hemisphere"
646
648
  ];
649
+ var SCENE_MODEL_RENDERERS = [
650
+ "pbr",
651
+ "matcap",
652
+ "uv-checker"
653
+ ];
647
654
  var ENVIRONMENT_PRESETS = [
648
655
  "apartment",
649
656
  "city",
@@ -1201,6 +1208,73 @@ function getMatcapTexture() {
1201
1208
  sharedMatcapTexture = texture;
1202
1209
  return texture;
1203
1210
  }
1211
+ var sharedUvCheckerTexture = null;
1212
+ function getUvCheckerTexture() {
1213
+ if (sharedUvCheckerTexture) return sharedUvCheckerTexture;
1214
+ const size = 1024;
1215
+ const cells = 8;
1216
+ const cellSize = size / cells;
1217
+ const colors = ["#ef4444", "#f59e0b", "#22c55e", "#6366f1"];
1218
+ const canvas = document.createElement("canvas");
1219
+ canvas.width = size;
1220
+ canvas.height = size;
1221
+ const ctx = canvas.getContext("2d");
1222
+ ctx.textAlign = "center";
1223
+ ctx.textBaseline = "middle";
1224
+ for (let row = 0; row < cells; row += 1) {
1225
+ for (let column = 0; column < cells; column += 1) {
1226
+ const x = column * cellSize;
1227
+ const y = row * cellSize;
1228
+ const colorIndex = (Math.floor(column / 2) + Math.floor(row / 2)) % 4;
1229
+ ctx.fillStyle = colors[colorIndex];
1230
+ ctx.fillRect(x, y, cellSize, cellSize);
1231
+ ctx.strokeStyle = "rgba(0, 0, 0, 0.28)";
1232
+ ctx.lineWidth = 2;
1233
+ for (let subdivision = 1; subdivision < 4; subdivision += 1) {
1234
+ const offset = subdivision * cellSize / 4;
1235
+ ctx.beginPath();
1236
+ ctx.moveTo(x + offset, y);
1237
+ ctx.lineTo(x + offset, y + cellSize);
1238
+ ctx.moveTo(x, y + offset);
1239
+ ctx.lineTo(x + cellSize, y + offset);
1240
+ ctx.stroke();
1241
+ }
1242
+ ctx.strokeStyle = "#09090b";
1243
+ ctx.lineWidth = 7;
1244
+ ctx.strokeRect(x, y, cellSize, cellSize);
1245
+ const startX = x + cellSize * 0.23;
1246
+ const startY = y + cellSize * 0.72;
1247
+ const endX = x + cellSize * 0.72;
1248
+ const endY = y + cellSize * 0.23;
1249
+ ctx.lineCap = "round";
1250
+ ctx.lineJoin = "round";
1251
+ ctx.beginPath();
1252
+ ctx.moveTo(startX, startY);
1253
+ ctx.lineTo(endX, endY);
1254
+ ctx.lineTo(endX - cellSize * 0.2, endY);
1255
+ ctx.moveTo(endX, endY);
1256
+ ctx.lineTo(endX, endY + cellSize * 0.2);
1257
+ ctx.stroke();
1258
+ const tileNumber = row * cells + column + 1;
1259
+ ctx.font = `700 ${Math.round(cellSize * 0.2)}px ui-monospace, SFMono-Regular, Menlo, monospace`;
1260
+ ctx.fillStyle = "#ffffff";
1261
+ ctx.strokeStyle = "rgba(0, 0, 0, 0.85)";
1262
+ ctx.lineWidth = 6;
1263
+ const label = String(tileNumber).padStart(2, "0");
1264
+ ctx.strokeText(label, x + cellSize / 2, y + cellSize * 0.82);
1265
+ ctx.fillText(label, x + cellSize / 2, y + cellSize * 0.82);
1266
+ }
1267
+ }
1268
+ const texture = new import_three4.CanvasTexture(canvas);
1269
+ texture.colorSpace = import_three4.SRGBColorSpace;
1270
+ texture.wrapS = import_three4.RepeatWrapping;
1271
+ texture.wrapT = import_three4.RepeatWrapping;
1272
+ texture.flipY = false;
1273
+ texture.anisotropy = 8;
1274
+ texture.needsUpdate = true;
1275
+ sharedUvCheckerTexture = texture;
1276
+ return texture;
1277
+ }
1204
1278
 
1205
1279
  // src/components/viewer/Exposure.tsx
1206
1280
  var import_react3 = require("react");
@@ -2290,14 +2364,14 @@ function resolveAnimationClips(clips) {
2290
2364
 
2291
2365
  // src/components/model/materialUtils.ts
2292
2366
  var import_three11 = require("three");
2293
- function isMeshStandardMaterial(material) {
2367
+ function isMeshStandardMaterial(material2) {
2294
2368
  return Boolean(
2295
- material && typeof material === "object" && "clone" in material && "color" in material && "metalness" in material && "roughness" in material
2369
+ material2 && typeof material2 === "object" && "clone" in material2 && "color" in material2 && "metalness" in material2 && "roughness" in material2
2296
2370
  );
2297
2371
  }
2298
- function isMeshPhysicalMaterial(material) {
2372
+ function isMeshPhysicalMaterial(material2) {
2299
2373
  return Boolean(
2300
- material && typeof material === "object" && "isMeshPhysicalMaterial" in material && material.isMeshPhysicalMaterial
2374
+ material2 && typeof material2 === "object" && "isMeshPhysicalMaterial" in material2 && material2.isMeshPhysicalMaterial
2301
2375
  );
2302
2376
  }
2303
2377
  function isMeshNode(object) {
@@ -2333,8 +2407,8 @@ var SRGB_TEXTURE_KEYS = /* @__PURE__ */ new Set([
2333
2407
  function colorSpaceForTextureKey(key) {
2334
2408
  return SRGB_TEXTURE_KEYS.has(key) ? import_three11.SRGBColorSpace : import_three11.NoColorSpace;
2335
2409
  }
2336
- function getMaterialTexturePath(material, key) {
2337
- return material?.[key]?.path;
2410
+ function getMaterialTexturePath(material2, key) {
2411
+ return material2?.[key]?.path;
2338
2412
  }
2339
2413
  function sideFromConfig(side) {
2340
2414
  if (side === "back") return import_three11.BackSide;
@@ -2368,10 +2442,10 @@ function getClipLoopMode(clipConfig) {
2368
2442
 
2369
2443
  // src/components/model/materialFactory.ts
2370
2444
  var import_three12 = require("three");
2371
- function toStandardMaterial(material) {
2372
- if (isMeshStandardMaterial(material)) return material;
2373
- if (!material || typeof material !== "object") return null;
2374
- const src = material;
2445
+ function toStandardMaterial(material2) {
2446
+ if (isMeshStandardMaterial(material2)) return material2;
2447
+ if (!material2 || typeof material2 !== "object") return null;
2448
+ const src = material2;
2375
2449
  const std = new import_three12.MeshStandardMaterial();
2376
2450
  if (typeof src.name === "string") std.name = src.name;
2377
2451
  std.metalness = 0;
@@ -2552,69 +2626,69 @@ function snapshotBaseMaterial(cloned) {
2552
2626
 
2553
2627
  // src/components/model/applyMaterialConfig.ts
2554
2628
  var import_three13 = require("three");
2555
- function applyMaterialConfig(material, configuredMaterial, resolvedBaseColor) {
2556
- material.map = material.userData.baseMap ?? null;
2557
- material.color.copy(material.userData.baseColor);
2558
- material.metalness = material.userData.baseMetalness ?? material.metalness;
2559
- material.metalnessMap = material.userData.baseMetalnessMap ?? null;
2560
- material.roughness = material.userData.baseRoughness ?? material.roughness;
2561
- material.roughnessMap = material.userData.baseRoughnessMap ?? null;
2562
- material.emissive.copy(material.userData.baseEmissive ?? new import_three13.Color(0, 0, 0));
2563
- material.emissiveIntensity = material.userData.baseEmissiveIntensity ?? material.emissiveIntensity;
2564
- material.emissiveMap = material.userData.baseEmissiveMap ?? null;
2565
- material.opacity = material.userData.baseOpacity ?? 1;
2566
- material.transparent = material.userData.baseTransparent ?? false;
2567
- material.alphaMap = material.userData.baseAlphaMap ?? null;
2568
- material.alphaTest = material.userData.baseAlphaTest ?? 0;
2569
- material.alphaHash = material.userData.baseAlphaHash ?? false;
2570
- material.dithering = material.userData.baseDithering ?? false;
2571
- material.blending = material.userData.baseBlending ?? import_three13.NormalBlending;
2572
- material.depthWrite = material.userData.baseDepthWrite ?? true;
2573
- material.side = material.userData.baseSide ?? import_three13.FrontSide;
2574
- material.normalMap = material.userData.baseNormalMap ?? null;
2575
- material.normalScale.copy(toSafeVector2(material.userData.baseNormalScale));
2576
- material.bumpMap = material.userData.baseBumpMap ?? null;
2577
- material.bumpScale = material.userData.baseBumpScale ?? material.bumpScale;
2578
- material.aoMap = material.userData.baseAoMap ?? null;
2579
- material.aoMapIntensity = material.userData.baseAoMapIntensity ?? material.aoMapIntensity;
2580
- material.displacementMap = material.userData.baseDisplacementMap ?? null;
2581
- material.displacementScale = material.userData.baseDisplacementScale ?? material.displacementScale;
2582
- material.displacementBias = material.userData.baseDisplacementBias ?? material.displacementBias;
2583
- material.clearcoat = material.userData.baseClearcoat ?? material.clearcoat;
2584
- material.clearcoatMap = material.userData.baseClearcoatMap ?? null;
2585
- material.clearcoatRoughness = material.userData.baseClearcoatRoughness ?? material.clearcoatRoughness;
2586
- material.clearcoatRoughnessMap = material.userData.baseClearcoatRoughnessMap ?? null;
2587
- material.clearcoatNormalMap = material.userData.baseClearcoatNormalMap ?? null;
2588
- material.clearcoatNormalScale.copy(
2589
- toSafeVector2(material.userData.baseClearcoatNormalScale)
2629
+ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
2630
+ material2.map = material2.userData.baseMap ?? null;
2631
+ material2.color.copy(material2.userData.baseColor);
2632
+ material2.metalness = material2.userData.baseMetalness ?? material2.metalness;
2633
+ material2.metalnessMap = material2.userData.baseMetalnessMap ?? null;
2634
+ material2.roughness = material2.userData.baseRoughness ?? material2.roughness;
2635
+ material2.roughnessMap = material2.userData.baseRoughnessMap ?? null;
2636
+ material2.emissive.copy(material2.userData.baseEmissive ?? new import_three13.Color(0, 0, 0));
2637
+ material2.emissiveIntensity = material2.userData.baseEmissiveIntensity ?? material2.emissiveIntensity;
2638
+ material2.emissiveMap = material2.userData.baseEmissiveMap ?? null;
2639
+ material2.opacity = material2.userData.baseOpacity ?? 1;
2640
+ material2.transparent = material2.userData.baseTransparent ?? false;
2641
+ material2.alphaMap = material2.userData.baseAlphaMap ?? null;
2642
+ material2.alphaTest = material2.userData.baseAlphaTest ?? 0;
2643
+ material2.alphaHash = material2.userData.baseAlphaHash ?? false;
2644
+ material2.dithering = material2.userData.baseDithering ?? false;
2645
+ material2.blending = material2.userData.baseBlending ?? import_three13.NormalBlending;
2646
+ material2.depthWrite = material2.userData.baseDepthWrite ?? true;
2647
+ material2.side = material2.userData.baseSide ?? import_three13.FrontSide;
2648
+ material2.normalMap = material2.userData.baseNormalMap ?? null;
2649
+ material2.normalScale.copy(toSafeVector2(material2.userData.baseNormalScale));
2650
+ material2.bumpMap = material2.userData.baseBumpMap ?? null;
2651
+ material2.bumpScale = material2.userData.baseBumpScale ?? material2.bumpScale;
2652
+ material2.aoMap = material2.userData.baseAoMap ?? null;
2653
+ material2.aoMapIntensity = material2.userData.baseAoMapIntensity ?? material2.aoMapIntensity;
2654
+ material2.displacementMap = material2.userData.baseDisplacementMap ?? null;
2655
+ material2.displacementScale = material2.userData.baseDisplacementScale ?? material2.displacementScale;
2656
+ material2.displacementBias = material2.userData.baseDisplacementBias ?? material2.displacementBias;
2657
+ material2.clearcoat = material2.userData.baseClearcoat ?? material2.clearcoat;
2658
+ material2.clearcoatMap = material2.userData.baseClearcoatMap ?? null;
2659
+ material2.clearcoatRoughness = material2.userData.baseClearcoatRoughness ?? material2.clearcoatRoughness;
2660
+ material2.clearcoatRoughnessMap = material2.userData.baseClearcoatRoughnessMap ?? null;
2661
+ material2.clearcoatNormalMap = material2.userData.baseClearcoatNormalMap ?? null;
2662
+ material2.clearcoatNormalScale.copy(
2663
+ toSafeVector2(material2.userData.baseClearcoatNormalScale)
2590
2664
  );
2591
- material.sheen = material.userData.baseSheen ?? material.sheen;
2592
- material.sheenColor.copy(
2593
- material.userData.baseSheenColor ?? new import_three13.Color(0, 0, 0)
2665
+ material2.sheen = material2.userData.baseSheen ?? material2.sheen;
2666
+ material2.sheenColor.copy(
2667
+ material2.userData.baseSheenColor ?? new import_three13.Color(0, 0, 0)
2594
2668
  );
2595
- material.sheenColorMap = material.userData.baseSheenColorMap ?? null;
2596
- material.sheenRoughness = material.userData.baseSheenRoughness ?? material.sheenRoughness;
2597
- material.sheenRoughnessMap = material.userData.baseSheenRoughnessMap ?? null;
2598
- material.anisotropy = material.userData.baseAnisotropy ?? material.anisotropy;
2599
- material.anisotropyRotation = material.userData.baseAnisotropyRotation ?? material.anisotropyRotation;
2600
- material.anisotropyMap = material.userData.baseAnisotropyMap ?? null;
2601
- material.transmission = material.userData.baseTransmission ?? material.transmission;
2602
- material.transmissionMap = material.userData.baseTransmissionMap ?? null;
2603
- material.thickness = material.userData.baseThickness ?? material.thickness;
2604
- material.thicknessMap = material.userData.baseThicknessMap ?? null;
2605
- material.attenuationColor.copy(
2606
- material.userData.baseAttenuationColor ?? new import_three13.Color("#ffffff")
2669
+ material2.sheenColorMap = material2.userData.baseSheenColorMap ?? null;
2670
+ material2.sheenRoughness = material2.userData.baseSheenRoughness ?? material2.sheenRoughness;
2671
+ material2.sheenRoughnessMap = material2.userData.baseSheenRoughnessMap ?? null;
2672
+ material2.anisotropy = material2.userData.baseAnisotropy ?? material2.anisotropy;
2673
+ material2.anisotropyRotation = material2.userData.baseAnisotropyRotation ?? material2.anisotropyRotation;
2674
+ material2.anisotropyMap = material2.userData.baseAnisotropyMap ?? null;
2675
+ material2.transmission = material2.userData.baseTransmission ?? material2.transmission;
2676
+ material2.transmissionMap = material2.userData.baseTransmissionMap ?? null;
2677
+ material2.thickness = material2.userData.baseThickness ?? material2.thickness;
2678
+ material2.thicknessMap = material2.userData.baseThicknessMap ?? null;
2679
+ material2.attenuationColor.copy(
2680
+ material2.userData.baseAttenuationColor ?? new import_three13.Color("#ffffff")
2607
2681
  );
2608
- material.attenuationDistance = material.userData.baseAttenuationDistance ?? material.attenuationDistance;
2609
- material.reflectivity = material.userData.baseReflectivity ?? material.reflectivity;
2610
- material.specularIntensity = material.userData.baseSpecularIntensity ?? material.specularIntensity;
2611
- material.specularIntensityMap = material.userData.baseSpecularIntensityMap ?? null;
2612
- material.specularColor.copy(
2613
- material.userData.baseSpecularColor ?? new import_three13.Color("#ffffff")
2682
+ material2.attenuationDistance = material2.userData.baseAttenuationDistance ?? material2.attenuationDistance;
2683
+ material2.reflectivity = material2.userData.baseReflectivity ?? material2.reflectivity;
2684
+ material2.specularIntensity = material2.userData.baseSpecularIntensity ?? material2.specularIntensity;
2685
+ material2.specularIntensityMap = material2.userData.baseSpecularIntensityMap ?? null;
2686
+ material2.specularColor.copy(
2687
+ material2.userData.baseSpecularColor ?? new import_three13.Color("#ffffff")
2614
2688
  );
2615
- material.specularColorMap = material.userData.baseSpecularColorMap ?? null;
2616
- material.flatShading = material.userData.baseFlatShading ?? material.flatShading;
2617
- material.envMapIntensity = material.userData.baseEnvMapIntensity ?? material.envMapIntensity;
2689
+ material2.specularColorMap = material2.userData.baseSpecularColorMap ?? null;
2690
+ material2.flatShading = material2.userData.baseFlatShading ?? material2.flatShading;
2691
+ material2.envMapIntensity = material2.userData.baseEnvMapIntensity ?? material2.envMapIntensity;
2618
2692
  const texturePath = getMaterialTexturePath(configuredMaterial, "texture");
2619
2693
  const metalnessMapPath = getMaterialTexturePath(
2620
2694
  configuredMaterial,
@@ -2677,180 +2751,180 @@ function applyMaterialConfig(material, configuredMaterial, resolvedBaseColor) {
2677
2751
  "specularColorMap"
2678
2752
  );
2679
2753
  if (texturePath) {
2680
- material.map = getOrCreateTexture(texturePath, import_three13.SRGBColorSpace);
2681
- material.color.set(resolvedBaseColor ?? "#ffffff");
2754
+ material2.map = getOrCreateTexture(texturePath, import_three13.SRGBColorSpace);
2755
+ material2.color.set(resolvedBaseColor ?? "#ffffff");
2682
2756
  } else if (resolvedBaseColor) {
2683
- material.map = null;
2684
- material.color.set(resolvedBaseColor);
2757
+ material2.map = null;
2758
+ material2.color.set(resolvedBaseColor);
2685
2759
  }
2686
2760
  if (metalnessMapPath) {
2687
- material.metalnessMap = getOrCreateTexture(metalnessMapPath);
2761
+ material2.metalnessMap = getOrCreateTexture(metalnessMapPath);
2688
2762
  }
2689
2763
  if (configuredMaterial?.metalness !== void 0) {
2690
- material.metalness = configuredMaterial.metalness;
2764
+ material2.metalness = configuredMaterial.metalness;
2691
2765
  }
2692
2766
  if (roughnessMapPath) {
2693
- material.roughnessMap = getOrCreateTexture(roughnessMapPath);
2767
+ material2.roughnessMap = getOrCreateTexture(roughnessMapPath);
2694
2768
  }
2695
2769
  if (configuredMaterial?.roughness !== void 0) {
2696
- material.roughness = configuredMaterial.roughness;
2770
+ material2.roughness = configuredMaterial.roughness;
2697
2771
  }
2698
2772
  if (configuredMaterial?.emissive) {
2699
- material.emissive.set(configuredMaterial.emissive);
2773
+ material2.emissive.set(configuredMaterial.emissive);
2700
2774
  } else if (emissiveMapPath) {
2701
- material.emissive.set("#ffffff");
2775
+ material2.emissive.set("#ffffff");
2702
2776
  }
2703
2777
  if (configuredMaterial?.emissiveIntensity !== void 0) {
2704
- material.emissiveIntensity = configuredMaterial.emissiveIntensity;
2778
+ material2.emissiveIntensity = configuredMaterial.emissiveIntensity;
2705
2779
  }
2706
2780
  if (emissiveMapPath) {
2707
- material.emissiveMap = getOrCreateTexture(emissiveMapPath, import_three13.SRGBColorSpace);
2781
+ material2.emissiveMap = getOrCreateTexture(emissiveMapPath, import_three13.SRGBColorSpace);
2708
2782
  }
2709
2783
  if (configuredMaterial?.opacity !== void 0) {
2710
- material.opacity = configuredMaterial.opacity;
2784
+ material2.opacity = configuredMaterial.opacity;
2711
2785
  }
2712
2786
  if (alphaMapPath) {
2713
- material.alphaMap = getOrCreateTexture(alphaMapPath);
2787
+ material2.alphaMap = getOrCreateTexture(alphaMapPath);
2714
2788
  }
2715
2789
  if (configuredMaterial?.alphaTest !== void 0) {
2716
- material.alphaTest = configuredMaterial.alphaTest;
2790
+ material2.alphaTest = configuredMaterial.alphaTest;
2717
2791
  }
2718
2792
  if (configuredMaterial?.alphaHash !== void 0) {
2719
- material.alphaHash = configuredMaterial.alphaHash;
2793
+ material2.alphaHash = configuredMaterial.alphaHash;
2720
2794
  }
2721
2795
  if (configuredMaterial?.dithering !== void 0) {
2722
- material.dithering = configuredMaterial.dithering;
2796
+ material2.dithering = configuredMaterial.dithering;
2723
2797
  }
2724
2798
  if (configuredMaterial?.blendingMode) {
2725
- material.blending = blendingFromConfig(configuredMaterial.blendingMode);
2726
- material.depthWrite = configuredMaterial.blendingMode !== "additive";
2799
+ material2.blending = blendingFromConfig(configuredMaterial.blendingMode);
2800
+ material2.depthWrite = configuredMaterial.blendingMode !== "additive";
2727
2801
  }
2728
2802
  if (configuredMaterial?.side) {
2729
- material.side = sideFromConfig(configuredMaterial.side);
2803
+ material2.side = sideFromConfig(configuredMaterial.side);
2730
2804
  }
2731
2805
  if (normalMapPath) {
2732
- material.normalMap = getOrCreateTexture(normalMapPath);
2806
+ material2.normalMap = getOrCreateTexture(normalMapPath);
2733
2807
  }
2734
2808
  if (normalMapPath || configuredMaterial?.normalScale !== void 0) {
2735
- const normalScale = configuredMaterial?.normalScale ?? Math.abs(toSafeVector2(material.userData.baseNormalScale).x);
2736
- material.normalScale.set(
2809
+ const normalScale = configuredMaterial?.normalScale ?? Math.abs(toSafeVector2(material2.userData.baseNormalScale).x);
2810
+ material2.normalScale.set(
2737
2811
  normalScale,
2738
2812
  configuredMaterial?.flipNormalY ? -normalScale : normalScale
2739
2813
  );
2740
2814
  }
2741
2815
  if (bumpMapPath) {
2742
- material.bumpMap = getOrCreateTexture(bumpMapPath);
2816
+ material2.bumpMap = getOrCreateTexture(bumpMapPath);
2743
2817
  }
2744
2818
  if (configuredMaterial?.bumpScale !== void 0) {
2745
- material.bumpScale = configuredMaterial.bumpScale;
2819
+ material2.bumpScale = configuredMaterial.bumpScale;
2746
2820
  }
2747
2821
  if (aoMapPath) {
2748
- material.aoMap = getOrCreateTexture(aoMapPath);
2822
+ material2.aoMap = getOrCreateTexture(aoMapPath);
2749
2823
  }
2750
2824
  if (configuredMaterial?.aoMapIntensity !== void 0) {
2751
- material.aoMapIntensity = configuredMaterial.aoMapIntensity;
2825
+ material2.aoMapIntensity = configuredMaterial.aoMapIntensity;
2752
2826
  }
2753
2827
  if (displacementMapPath) {
2754
- material.displacementMap = getOrCreateTexture(displacementMapPath);
2828
+ material2.displacementMap = getOrCreateTexture(displacementMapPath);
2755
2829
  }
2756
2830
  if (configuredMaterial?.displacementScale !== void 0) {
2757
- material.displacementScale = configuredMaterial.displacementScale;
2831
+ material2.displacementScale = configuredMaterial.displacementScale;
2758
2832
  }
2759
2833
  if (configuredMaterial?.displacementBias !== void 0) {
2760
- material.displacementBias = configuredMaterial.displacementBias;
2834
+ material2.displacementBias = configuredMaterial.displacementBias;
2761
2835
  }
2762
2836
  if (clearcoatMapPath) {
2763
- material.clearcoatMap = getOrCreateTexture(clearcoatMapPath);
2837
+ material2.clearcoatMap = getOrCreateTexture(clearcoatMapPath);
2764
2838
  }
2765
2839
  if (configuredMaterial?.clearcoat !== void 0) {
2766
- material.clearcoat = configuredMaterial.clearcoat;
2840
+ material2.clearcoat = configuredMaterial.clearcoat;
2767
2841
  }
2768
2842
  if (clearcoatRoughnessMapPath) {
2769
- material.clearcoatRoughnessMap = getOrCreateTexture(
2843
+ material2.clearcoatRoughnessMap = getOrCreateTexture(
2770
2844
  clearcoatRoughnessMapPath
2771
2845
  );
2772
2846
  }
2773
2847
  if (configuredMaterial?.clearcoatRoughness !== void 0) {
2774
- material.clearcoatRoughness = configuredMaterial.clearcoatRoughness;
2848
+ material2.clearcoatRoughness = configuredMaterial.clearcoatRoughness;
2775
2849
  }
2776
2850
  if (clearcoatNormalMapPath) {
2777
- material.clearcoatNormalMap = getOrCreateTexture(clearcoatNormalMapPath);
2851
+ material2.clearcoatNormalMap = getOrCreateTexture(clearcoatNormalMapPath);
2778
2852
  }
2779
2853
  if (clearcoatNormalMapPath || configuredMaterial?.clearcoatNormalScale !== void 0) {
2780
- const clearcoatNormalScale = configuredMaterial?.clearcoatNormalScale ?? Math.abs(toSafeVector2(material.userData.baseClearcoatNormalScale).x);
2781
- material.clearcoatNormalScale.set(
2854
+ const clearcoatNormalScale = configuredMaterial?.clearcoatNormalScale ?? Math.abs(toSafeVector2(material2.userData.baseClearcoatNormalScale).x);
2855
+ material2.clearcoatNormalScale.set(
2782
2856
  clearcoatNormalScale,
2783
2857
  configuredMaterial?.flipClearcoatNormalY ? -clearcoatNormalScale : clearcoatNormalScale
2784
2858
  );
2785
2859
  }
2786
2860
  if (configuredMaterial?.sheen !== void 0) {
2787
- material.sheen = configuredMaterial.sheen;
2861
+ material2.sheen = configuredMaterial.sheen;
2788
2862
  }
2789
2863
  if (configuredMaterial?.sheenColor) {
2790
- material.sheenColor.set(configuredMaterial.sheenColor);
2864
+ material2.sheenColor.set(configuredMaterial.sheenColor);
2791
2865
  }
2792
2866
  if (sheenColorMapPath) {
2793
- material.sheenColorMap = getOrCreateTexture(
2867
+ material2.sheenColorMap = getOrCreateTexture(
2794
2868
  sheenColorMapPath,
2795
2869
  import_three13.SRGBColorSpace
2796
2870
  );
2797
2871
  }
2798
2872
  if (configuredMaterial?.sheenRoughness !== void 0) {
2799
- material.sheenRoughness = configuredMaterial.sheenRoughness;
2873
+ material2.sheenRoughness = configuredMaterial.sheenRoughness;
2800
2874
  }
2801
2875
  if (sheenRoughnessMapPath) {
2802
- material.sheenRoughnessMap = getOrCreateTexture(sheenRoughnessMapPath);
2876
+ material2.sheenRoughnessMap = getOrCreateTexture(sheenRoughnessMapPath);
2803
2877
  }
2804
2878
  if (configuredMaterial?.anisotropy !== void 0) {
2805
- material.anisotropy = configuredMaterial.anisotropy;
2879
+ material2.anisotropy = configuredMaterial.anisotropy;
2806
2880
  }
2807
2881
  if (configuredMaterial?.anisotropyRotation !== void 0) {
2808
- material.anisotropyRotation = configuredMaterial.anisotropyRotation;
2882
+ material2.anisotropyRotation = configuredMaterial.anisotropyRotation;
2809
2883
  }
2810
2884
  if (anisotropyMapPath) {
2811
- material.anisotropyMap = getOrCreateTexture(anisotropyMapPath);
2885
+ material2.anisotropyMap = getOrCreateTexture(anisotropyMapPath);
2812
2886
  }
2813
2887
  if (configuredMaterial?.transmission !== void 0) {
2814
- material.transmission = configuredMaterial.transmission;
2888
+ material2.transmission = configuredMaterial.transmission;
2815
2889
  }
2816
2890
  if (transmissionMapPath) {
2817
- material.transmissionMap = getOrCreateTexture(transmissionMapPath);
2891
+ material2.transmissionMap = getOrCreateTexture(transmissionMapPath);
2818
2892
  }
2819
2893
  if (configuredMaterial?.thickness !== void 0) {
2820
- material.thickness = configuredMaterial.thickness;
2894
+ material2.thickness = configuredMaterial.thickness;
2821
2895
  }
2822
2896
  if (thicknessMapPath) {
2823
- material.thicknessMap = getOrCreateTexture(thicknessMapPath);
2897
+ material2.thicknessMap = getOrCreateTexture(thicknessMapPath);
2824
2898
  }
2825
2899
  if (configuredMaterial?.attenuationColor) {
2826
- material.attenuationColor.set(configuredMaterial.attenuationColor);
2900
+ material2.attenuationColor.set(configuredMaterial.attenuationColor);
2827
2901
  }
2828
2902
  if (configuredMaterial?.attenuationDistance !== void 0) {
2829
- material.attenuationDistance = configuredMaterial.attenuationDistance;
2903
+ material2.attenuationDistance = configuredMaterial.attenuationDistance;
2830
2904
  }
2831
2905
  if (configuredMaterial?.reflectivity !== void 0) {
2832
- material.reflectivity = configuredMaterial.reflectivity;
2906
+ material2.reflectivity = configuredMaterial.reflectivity;
2833
2907
  }
2834
2908
  if (configuredMaterial?.specularIntensity !== void 0) {
2835
- material.specularIntensity = configuredMaterial.specularIntensity;
2909
+ material2.specularIntensity = configuredMaterial.specularIntensity;
2836
2910
  }
2837
2911
  if (specularIntensityMapPath) {
2838
- material.specularIntensityMap = getOrCreateTexture(
2912
+ material2.specularIntensityMap = getOrCreateTexture(
2839
2913
  specularIntensityMapPath
2840
2914
  );
2841
2915
  }
2842
2916
  if (configuredMaterial?.specularColor) {
2843
- material.specularColor.set(configuredMaterial.specularColor);
2917
+ material2.specularColor.set(configuredMaterial.specularColor);
2844
2918
  }
2845
2919
  if (specularColorMapPath) {
2846
- material.specularColorMap = getOrCreateTexture(
2920
+ material2.specularColorMap = getOrCreateTexture(
2847
2921
  specularColorMapPath,
2848
2922
  import_three13.SRGBColorSpace
2849
2923
  );
2850
2924
  }
2851
- material.transparent = material.opacity < 1 || material.transmission > 0 || material.blending !== import_three13.NormalBlending || Boolean((alphaMapPath || material.alphaMap) && material.alphaTest <= 0);
2852
- if (!material.transparent) {
2853
- material.depthWrite = true;
2925
+ material2.transparent = material2.opacity < 1 || material2.transmission > 0 || material2.blending !== import_three13.NormalBlending || Boolean((alphaMapPath || material2.alphaMap) && material2.alphaTest <= 0);
2926
+ if (!material2.transparent) {
2927
+ material2.depthWrite = true;
2854
2928
  }
2855
2929
  }
2856
2930
 
@@ -2993,8 +3067,8 @@ function Model({
2993
3067
  }, [perNodeMaterials, materialRefsOut]);
2994
3068
  (0, import_react11.useEffect)(() => {
2995
3069
  return () => {
2996
- for (const material of Object.values(perNodeMaterials)) {
2997
- material.dispose();
3070
+ for (const material2 of Object.values(perNodeMaterials)) {
3071
+ material2.dispose();
2998
3072
  }
2999
3073
  };
3000
3074
  }, [perNodeMaterials]);
@@ -3006,15 +3080,15 @@ function Model({
3006
3080
  const perNodeBasicMaterials = (0, import_react11.useMemo)(() => {
3007
3081
  const map = {};
3008
3082
  if (shadingMode !== "unlit") return map;
3009
- for (const [nodeName, material] of Object.entries(perNodeMaterials)) {
3083
+ for (const [nodeName, material2] of Object.entries(perNodeMaterials)) {
3010
3084
  const basic = new import_three14.MeshBasicMaterial();
3011
- basic.color.copy(material.color);
3012
- basic.map = material.map;
3013
- basic.alphaMap = material.alphaMap;
3014
- basic.alphaTest = material.alphaTest;
3015
- basic.transparent = material.transparent;
3016
- basic.opacity = material.opacity;
3017
- basic.side = material.side;
3085
+ basic.color.copy(material2.color);
3086
+ basic.map = material2.map;
3087
+ basic.alphaMap = material2.alphaMap;
3088
+ basic.alphaTest = material2.alphaTest;
3089
+ basic.transparent = material2.transparent;
3090
+ basic.opacity = material2.opacity;
3091
+ basic.side = material2.side;
3018
3092
  map[nodeName] = basic;
3019
3093
  }
3020
3094
  return map;
@@ -3031,25 +3105,25 @@ function Model({
3031
3105
  const map = {};
3032
3106
  if (shadingMode !== "lit" || rendererMode !== "matcap") return map;
3033
3107
  const matcapTexture = getMatcapTexture();
3034
- for (const [nodeName, material] of Object.entries(perNodeMaterials)) {
3108
+ for (const [nodeName, material2] of Object.entries(perNodeMaterials)) {
3035
3109
  const matcap = new import_three14.MeshMatcapMaterial();
3036
3110
  matcap.matcap = matcapTexture;
3037
- matcap.color.copy(material.color);
3038
- matcap.alphaMap = material.alphaMap;
3039
- matcap.alphaTest = material.alphaTest;
3040
- matcap.side = material.side;
3111
+ matcap.color.copy(material2.color);
3112
+ matcap.alphaMap = material2.alphaMap;
3113
+ matcap.alphaTest = material2.alphaTest;
3114
+ matcap.side = material2.side;
3041
3115
  map[nodeName] = matcap;
3042
3116
  }
3043
3117
  return map;
3044
3118
  }, [perNodeMaterials, shadingMode, rendererMode]);
3045
3119
  (0, import_react11.useEffect)(() => {
3046
3120
  return () => {
3047
- for (const material of Object.values(perNodeBasicMaterials))
3048
- material.dispose();
3049
- for (const material of Object.values(perNodeNormalMaterials))
3050
- material.dispose();
3051
- for (const material of Object.values(perNodeMatcapMaterials))
3052
- material.dispose();
3121
+ for (const material2 of Object.values(perNodeBasicMaterials))
3122
+ material2.dispose();
3123
+ for (const material2 of Object.values(perNodeNormalMaterials))
3124
+ material2.dispose();
3125
+ for (const material2 of Object.values(perNodeMatcapMaterials))
3126
+ material2.dispose();
3053
3127
  };
3054
3128
  }, [perNodeBasicMaterials, perNodeNormalMaterials, perNodeMatcapMaterials]);
3055
3129
  const materialForNode = (nodeName) => {
@@ -3095,11 +3169,13 @@ function Model({
3095
3169
  const node = nodeMap[glbNodeName];
3096
3170
  if (!isMeshNode(node)) continue;
3097
3171
  node.visible = binding.visible !== false;
3098
- const material = perNodeMaterials[nodeName];
3099
- if (!material) continue;
3100
- const activeMaterial = materialForNode(nodeName);
3101
- if (activeMaterial) {
3102
- node.material = activeMaterial;
3172
+ const material2 = perNodeMaterials[nodeName];
3173
+ if (!material2) continue;
3174
+ if (rendererMode !== "uv-checker") {
3175
+ const activeMaterial = materialForNode(nodeName);
3176
+ if (activeMaterial) {
3177
+ node.material = activeMaterial;
3178
+ }
3103
3179
  }
3104
3180
  if (binding.visible === false) continue;
3105
3181
  const basicMaterial = perNodeBasicMaterials[nodeName];
@@ -3131,19 +3207,19 @@ function Model({
3131
3207
  delete appliedTextureUrls.current[nodeName];
3132
3208
  }
3133
3209
  const resolvedBaseColor = itemMat?.hex ?? configuredMaterial?.baseColor;
3134
- applyMaterialConfig(material, configuredMaterial, resolvedBaseColor);
3210
+ applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor);
3135
3211
  if (isHighlightedByHover) {
3136
- material.emissive?.add(HIGHLIGHT_EMISSIVE_TINT);
3212
+ material2.emissive?.add(HIGHLIGHT_EMISSIVE_TINT);
3137
3213
  }
3138
- material.needsUpdate = true;
3214
+ material2.needsUpdate = true;
3139
3215
  if (basicMaterial) {
3140
- basicMaterial.color.copy(material.color);
3141
- basicMaterial.map = material.map;
3142
- basicMaterial.alphaMap = material.alphaMap;
3143
- basicMaterial.alphaTest = material.alphaTest;
3144
- basicMaterial.transparent = material.transparent;
3145
- basicMaterial.opacity = material.opacity;
3146
- basicMaterial.side = material.side;
3216
+ basicMaterial.color.copy(material2.color);
3217
+ basicMaterial.map = material2.map;
3218
+ basicMaterial.alphaMap = material2.alphaMap;
3219
+ basicMaterial.alphaTest = material2.alphaTest;
3220
+ basicMaterial.transparent = material2.transparent;
3221
+ basicMaterial.opacity = material2.opacity;
3222
+ basicMaterial.side = material2.side;
3147
3223
  basicMaterial.needsUpdate = true;
3148
3224
  }
3149
3225
  if (normalMaterial) normalMaterial.needsUpdate = true;
@@ -3151,14 +3227,14 @@ function Model({
3151
3227
  if (isHighlightedByHover || isSelected) {
3152
3228
  matcapMaterial.color.set(HIGHLIGHT.color);
3153
3229
  } else {
3154
- matcapMaterial.color.copy(material.color);
3230
+ matcapMaterial.color.copy(material2.color);
3155
3231
  }
3156
- matcapMaterial.map = material.map;
3157
- matcapMaterial.alphaMap = material.alphaMap;
3158
- matcapMaterial.alphaTest = material.alphaTest;
3159
- matcapMaterial.transparent = material.transparent;
3160
- matcapMaterial.opacity = material.opacity;
3161
- matcapMaterial.side = material.side;
3232
+ matcapMaterial.map = material2.map;
3233
+ matcapMaterial.alphaMap = material2.alphaMap;
3234
+ matcapMaterial.alphaTest = material2.alphaTest;
3235
+ matcapMaterial.transparent = material2.transparent;
3236
+ matcapMaterial.opacity = material2.opacity;
3237
+ matcapMaterial.side = material2.side;
3162
3238
  matcapMaterial.needsUpdate = true;
3163
3239
  }
3164
3240
  }
@@ -3194,6 +3270,39 @@ function Model({
3194
3270
  rendererMode,
3195
3271
  invalidate
3196
3272
  ]);
3273
+ (0, import_react11.useEffect)(() => {
3274
+ if (rendererMode !== "uv-checker") return;
3275
+ const checkerTexture = getUvCheckerTexture();
3276
+ const swaps = [];
3277
+ scene.traverse((object) => {
3278
+ if (!isMeshNode(object)) return;
3279
+ const original = object.material;
3280
+ const sourceMaterials = Array.isArray(original) ? original : [original];
3281
+ const hasUv0 = Boolean(object.geometry.getAttribute("uv"));
3282
+ const diagnosticMaterials = sourceMaterials.map((source, index) => {
3283
+ const material2 = new import_three14.MeshBasicMaterial({
3284
+ color: hasUv0 ? "#ffffff" : "#ff00ff",
3285
+ map: hasUv0 ? checkerTexture : null,
3286
+ side: source.side
3287
+ });
3288
+ material2.name = `${object.name || "mesh"}-uv-checker-${index}`;
3289
+ material2.toneMapped = false;
3290
+ return material2;
3291
+ });
3292
+ const diagnostic = Array.isArray(original) ? diagnosticMaterials : diagnosticMaterials[0];
3293
+ object.material = diagnostic;
3294
+ swaps.push({ node: object, original, diagnostic });
3295
+ });
3296
+ invalidate();
3297
+ return () => {
3298
+ for (const { node, original, diagnostic } of swaps) {
3299
+ node.material = original;
3300
+ const materials = Array.isArray(diagnostic) ? diagnostic : [diagnostic];
3301
+ for (const material2 of materials) material2.dispose();
3302
+ }
3303
+ invalidate();
3304
+ };
3305
+ }, [scene, rendererMode, invalidate]);
3197
3306
  (0, import_react11.useEffect)(() => {
3198
3307
  if (!wireframeEnabled) return;
3199
3308
  const added = [];
@@ -5787,6 +5896,16 @@ function computeMeasureBox(scene) {
5787
5896
  size: [size.x, size.y, size.z]
5788
5897
  };
5789
5898
  }
5899
+ function computeUvMeshStats(scene) {
5900
+ const stats = { total: 0, missing: 0 };
5901
+ scene.traverse((node) => {
5902
+ const mesh = node;
5903
+ if (!mesh.isMesh || !mesh.geometry) return;
5904
+ stats.total += 1;
5905
+ if (!mesh.geometry.getAttribute("uv")) stats.missing += 1;
5906
+ });
5907
+ return stats;
5908
+ }
5790
5909
  function computeModelBounds(scene) {
5791
5910
  const box = new import_three19.Box3().setFromObject(scene);
5792
5911
  if (box.isEmpty()) return null;
@@ -5865,6 +5984,7 @@ function ModelViewer({
5865
5984
  ktx2TranscoderPath,
5866
5985
  meshopt = true,
5867
5986
  showMeasureTools = false,
5987
+ showUvCheckerButton = false,
5868
5988
  showExplodeControls = false,
5869
5989
  cinematic,
5870
5990
  measurementUnit = "m",
@@ -5881,6 +6001,24 @@ function ModelViewer({
5881
6001
  () => sceneConfig ?? DEFAULT_SCENE_CONFIG,
5882
6002
  [sceneConfig]
5883
6003
  );
6004
+ const [uvCheckerOverride, setUvCheckerOverride] = (0, import_react22.useState)(
6005
+ null
6006
+ );
6007
+ const configuredUvChecker = resolvedSceneConfig.model.renderer === "uv-checker";
6008
+ const uvCheckerActive = showUvCheckerButton ? uvCheckerOverride ?? configuredUvChecker : configuredUvChecker;
6009
+ const renderedSceneConfig = (0, import_react22.useMemo)(() => {
6010
+ const renderer = uvCheckerActive ? "uv-checker" : configuredUvChecker ? "pbr" : resolvedSceneConfig.model.renderer;
6011
+ if (renderer === resolvedSceneConfig.model.renderer) {
6012
+ return resolvedSceneConfig;
6013
+ }
6014
+ return {
6015
+ ...resolvedSceneConfig,
6016
+ model: { ...resolvedSceneConfig.model, renderer }
6017
+ };
6018
+ }, [configuredUvChecker, resolvedSceneConfig, uvCheckerActive]);
6019
+ (0, import_react22.useEffect)(() => {
6020
+ setUvCheckerOverride(null);
6021
+ }, [modelUrl, resolvedSceneConfig.model.renderer, showUvCheckerButton]);
5884
6022
  const viewerBackdropColor = (0, import_react22.useMemo)(() => {
5885
6023
  if (backgroundColor) return backgroundColor;
5886
6024
  const bg = resolvedSceneConfig.background;
@@ -5952,6 +6090,10 @@ function ModelViewer({
5952
6090
  null
5953
6091
  );
5954
6092
  const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = (0, import_react22.useState)(false);
6093
+ const [uvMeshStats, setUvMeshStats] = (0, import_react22.useState)({
6094
+ total: 0,
6095
+ missing: 0
6096
+ });
5955
6097
  const [loaderMessage, setLoaderMessage] = (0, import_react22.useState)("Loading model...");
5956
6098
  const currentObjectBindingsRef = (0, import_react22.useRef)({});
5957
6099
  const captureInitialCameraState = (0, import_react22.useCallback)(() => {
@@ -6832,6 +6974,7 @@ function ModelViewer({
6832
6974
  setModelLoaded(false);
6833
6975
  setModelLoadFailed(false);
6834
6976
  setBindingsMismatch(false);
6977
+ setUvMeshStats({ total: 0, missing: 0 });
6835
6978
  setLoaderMessage("Loading model...");
6836
6979
  if (!modelUrl) {
6837
6980
  setIsAutoFraming(false);
@@ -7021,6 +7164,7 @@ function ModelViewer({
7021
7164
  sceneRef.current = scene;
7022
7165
  setMeasureBox(computeMeasureBox(scene));
7023
7166
  setModelBounds(computeModelBounds(scene));
7167
+ setUvMeshStats(computeUvMeshStats(scene));
7024
7168
  const triangles = countSceneTriangles(scene);
7025
7169
  setLargeModelTriangles(triangles > MAX_BVH_TRIANGLES ? triangles : null);
7026
7170
  setLargeModelNoticeDismissed(false);
@@ -7403,6 +7547,20 @@ function ModelViewer({
7403
7547
  ]
7404
7548
  }
7405
7549
  ),
7550
+ renderedSceneConfig.model.renderer === "uv-checker" && uvMeshStats.missing > 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "ri-uvCheckerBanner", role: "status", children: [
7551
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
7552
+ import_lucide_react3.AlertTriangle,
7553
+ {
7554
+ size: 14,
7555
+ className: "ri-uvCheckerBanner__icon"
7556
+ }
7557
+ ),
7558
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { children: [
7559
+ uvMeshStats.missing === uvMeshStats.total ? "UV Checker: this model has no UV0 coordinates." : `UV Checker: ${uvMeshStats.missing} of ${uvMeshStats.total} meshes have no UV0 coordinates.`,
7560
+ " ",
7561
+ "Missing UVs are shown in magenta."
7562
+ ] })
7563
+ ] }),
7406
7564
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
7407
7565
  "input",
7408
7566
  {
@@ -7419,7 +7577,7 @@ function ModelViewer({
7419
7577
  camera,
7420
7578
  backgroundColor,
7421
7579
  shadows: shadows && resolvedSceneConfig.shadows.enabled,
7422
- sceneConfig: resolvedSceneConfig,
7580
+ sceneConfig: renderedSceneConfig,
7423
7581
  selectedObjectRef: currentSelectedObjectRef,
7424
7582
  hoveredObjectRef: currentHoveredObjectRef,
7425
7583
  highlightOnHover,
@@ -7467,7 +7625,7 @@ function ModelViewer({
7467
7625
  nodeRefsOut: nodeRefsMap,
7468
7626
  materialRefsOut,
7469
7627
  panelHoveredItem: panelHoveredObject ?? panelHoveredItem,
7470
- sceneConfig: resolvedSceneConfig,
7628
+ sceneConfig: renderedSceneConfig,
7471
7629
  zoomOnSelected,
7472
7630
  highlightOnHover,
7473
7631
  decoderConfig,
@@ -7659,11 +7817,34 @@ function ModelViewer({
7659
7817
  )
7660
7818
  }
7661
7819
  ),
7820
+ showUvCheckerButton && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
7821
+ "div",
7822
+ {
7823
+ className: "ri-uvCheckerTools",
7824
+ style: { top: `${topLeftRow(xrToolsVisible ? 1 : 0)}px` },
7825
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
7826
+ "button",
7827
+ {
7828
+ type: "button",
7829
+ className: `ri-measureButton ${uvCheckerActive ? "ri-measureButton--active" : ""}`,
7830
+ onClick: () => setUvCheckerOverride(
7831
+ (previous) => !(previous ?? configuredUvChecker)
7832
+ ),
7833
+ title: uvCheckerActive ? "Disable UV checker" : "Enable UV checker",
7834
+ "aria-label": uvCheckerActive ? "Disable UV checker" : "Enable UV checker",
7835
+ "aria-pressed": uvCheckerActive,
7836
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react3.Grid3X3, { size: 16 })
7837
+ }
7838
+ )
7839
+ }
7840
+ ),
7662
7841
  showMeasureTools && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
7663
7842
  "div",
7664
7843
  {
7665
7844
  className: "ri-measureTools",
7666
- style: { top: `${topLeftRow(xrToolsVisible ? 1 : 0)}px` },
7845
+ style: {
7846
+ top: `${topLeftRow((xrToolsVisible ? 1 : 0) + (showUvCheckerButton ? 1 : 0))}px`
7847
+ },
7667
7848
  children: [
7668
7849
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
7669
7850
  "button",
@@ -7706,7 +7887,7 @@ function ModelViewer({
7706
7887
  {
7707
7888
  className: "ri-explodeTools",
7708
7889
  style: {
7709
- top: `${topLeftRow((xrToolsVisible ? 1 : 0) + (showMeasureTools ? 1 : 0))}px`
7890
+ top: `${topLeftRow((xrToolsVisible ? 1 : 0) + (showUvCheckerButton ? 1 : 0) + (showMeasureTools ? 1 : 0))}px`
7710
7891
  },
7711
7892
  children: [
7712
7893
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
@@ -7746,7 +7927,7 @@ function ModelViewer({
7746
7927
  {
7747
7928
  className: "ri-cinematicTools",
7748
7929
  style: {
7749
- top: `${topLeftRow((xrToolsVisible ? 1 : 0) + (showMeasureTools ? 1 : 0) + (showExplodeControls ? 1 : 0))}px`
7930
+ top: `${topLeftRow((xrToolsVisible ? 1 : 0) + (showUvCheckerButton ? 1 : 0) + (showMeasureTools ? 1 : 0) + (showExplodeControls ? 1 : 0))}px`
7750
7931
  },
7751
7932
  children: [
7752
7933
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
@@ -9089,13 +9270,13 @@ function GeneralSceneSettings({ config, onChange }) {
9089
9270
  ),
9090
9271
  /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "se-field", children: [
9091
9272
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "se-fieldLabel", children: "Renderer" }),
9092
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "se-segmentedControl", children: ["pbr", "matcap"].map((option) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9273
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "se-segmentedControl se-segmentedControl--renderer", children: SCENE_MODEL_RENDERERS.map((option) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
9093
9274
  "button",
9094
9275
  {
9095
9276
  type: "button",
9096
9277
  className: `se-segmentedOption ${model.renderer === option ? "se-segmentedOption--active" : ""}`,
9097
9278
  onClick: () => setModel({ ...model, renderer: option }),
9098
- children: option.toUpperCase()
9279
+ children: option === "uv-checker" ? "UV CHECKER" : option.toUpperCase()
9099
9280
  },
9100
9281
  option
9101
9282
  )) })
@@ -12496,7 +12677,7 @@ function CinematicTab({
12496
12677
  }
12497
12678
 
12498
12679
  // src/styles/SceneSettingsTab.css
12499
- 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');
12680
+ 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');
12500
12681
 
12501
12682
  // src/components/BindingBuilder/SceneSettings/SceneSettingsTab.tsx
12502
12683
  var import_lucide_react11 = require("lucide-react");
@@ -13026,8 +13207,8 @@ function MaterialSection({
13026
13207
  {
13027
13208
  label: pbrWorkflow === "specular" ? "Albedo color" : "Base color",
13028
13209
  value: selectedMaterial?.baseColor ?? "#ffffff",
13029
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13030
- ...material,
13210
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13211
+ ...material2,
13031
13212
  baseColor: value
13032
13213
  }))
13033
13214
  }
@@ -13041,8 +13222,8 @@ function MaterialSection({
13041
13222
  min: 0,
13042
13223
  max: 1,
13043
13224
  step: 0.01,
13044
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13045
- ...material,
13225
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13226
+ ...material2,
13046
13227
  metalness: value
13047
13228
  }))
13048
13229
  }
@@ -13065,8 +13246,8 @@ function MaterialSection({
13065
13246
  min: 0,
13066
13247
  max: 1,
13067
13248
  step: 0.01,
13068
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13069
- ...material,
13249
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13250
+ ...material2,
13070
13251
  specularIntensity: value
13071
13252
  }))
13072
13253
  }
@@ -13076,8 +13257,8 @@ function MaterialSection({
13076
13257
  {
13077
13258
  label: "Specular color",
13078
13259
  value: selectedMaterial?.specularColor ?? "#ffffff",
13079
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13080
- ...material,
13260
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13261
+ ...material2,
13081
13262
  specularColor: value
13082
13263
  }))
13083
13264
  }
@@ -13117,8 +13298,8 @@ function MaterialSection({
13117
13298
  min: 0,
13118
13299
  max: 1,
13119
13300
  step: 0.01,
13120
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13121
- ...material,
13301
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13302
+ ...material2,
13122
13303
  reflectivity: value
13123
13304
  }))
13124
13305
  }
@@ -13129,8 +13310,8 @@ function MaterialSection({
13129
13310
  {
13130
13311
  label: "Base color",
13131
13312
  value: selectedMaterial?.baseColor ?? "#ffffff",
13132
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13133
- ...material,
13313
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13314
+ ...material2,
13134
13315
  baseColor: value
13135
13316
  }))
13136
13317
  }
@@ -13143,8 +13324,8 @@ function MaterialSection({
13143
13324
  min: 0,
13144
13325
  max: 1,
13145
13326
  step: 0.01,
13146
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13147
- ...material,
13327
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13328
+ ...material2,
13148
13329
  metalness: value
13149
13330
  }))
13150
13331
  }
@@ -13187,8 +13368,8 @@ function MaterialSection({
13187
13368
  min: 0,
13188
13369
  max: 1,
13189
13370
  step: 0.01,
13190
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13191
- ...material,
13371
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13372
+ ...material2,
13192
13373
  roughness: value
13193
13374
  }))
13194
13375
  }
@@ -13200,8 +13381,8 @@ function MaterialSection({
13200
13381
  min: 0,
13201
13382
  max: 1,
13202
13383
  step: 0.01,
13203
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13204
- ...material,
13384
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13385
+ ...material2,
13205
13386
  roughness: value === void 0 ? void 0 : 1 - value
13206
13387
  }))
13207
13388
  }
@@ -13233,8 +13414,8 @@ function MaterialSection({
13233
13414
  label: "Emissive color",
13234
13415
  value: selectedMaterial?.emissive ?? "#000000",
13235
13416
  fallback: "#000000",
13236
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13237
- ...material,
13417
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13418
+ ...material2,
13238
13419
  emissive: value
13239
13420
  }))
13240
13421
  }
@@ -13247,8 +13428,8 @@ function MaterialSection({
13247
13428
  min: 0,
13248
13429
  max: 10,
13249
13430
  step: 0.1,
13250
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13251
- ...material,
13431
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13432
+ ...material2,
13252
13433
  emissiveIntensity: value
13253
13434
  }))
13254
13435
  }
@@ -13282,8 +13463,8 @@ function MaterialSection({
13282
13463
  min: 0,
13283
13464
  max: 1,
13284
13465
  step: 0.01,
13285
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13286
- ...material,
13466
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13467
+ ...material2,
13287
13468
  opacity: value
13288
13469
  }))
13289
13470
  }
@@ -13296,8 +13477,8 @@ function MaterialSection({
13296
13477
  min: 0,
13297
13478
  max: 1,
13298
13479
  step: 0.01,
13299
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13300
- ...material,
13480
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13481
+ ...material2,
13301
13482
  alphaTest: value
13302
13483
  }))
13303
13484
  }
@@ -13317,8 +13498,8 @@ function MaterialSection({
13317
13498
  label: "Blending mode",
13318
13499
  value: selectedMaterial?.blendingMode ?? "normal",
13319
13500
  options: MATERIAL_BLENDING_MODES,
13320
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13321
- ...material,
13501
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13502
+ ...material2,
13322
13503
  blendingMode: value
13323
13504
  }))
13324
13505
  }
@@ -13330,8 +13511,8 @@ function MaterialSection({
13330
13511
  {
13331
13512
  label: "Use alpha hash",
13332
13513
  checked: selectedMaterial?.alphaHash ?? false,
13333
- onChange: (checked) => updateSelectedMaterial(selectedItem, (material) => ({
13334
- ...material,
13514
+ onChange: (checked) => updateSelectedMaterial(selectedItem, (material2) => ({
13515
+ ...material2,
13335
13516
  alphaHash: checked
13336
13517
  }))
13337
13518
  }
@@ -13344,8 +13525,8 @@ function MaterialSection({
13344
13525
  {
13345
13526
  label: "Use dithering",
13346
13527
  checked: selectedMaterial?.dithering ?? false,
13347
- onChange: (checked) => updateSelectedMaterial(selectedItem, (material) => ({
13348
- ...material,
13528
+ onChange: (checked) => updateSelectedMaterial(selectedItem, (material2) => ({
13529
+ ...material2,
13349
13530
  dithering: checked
13350
13531
  }))
13351
13532
  }
@@ -13382,8 +13563,8 @@ function MaterialSection({
13382
13563
  min: 0,
13383
13564
  max: 5,
13384
13565
  step: 0.01,
13385
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13386
- ...material,
13566
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13567
+ ...material2,
13387
13568
  normalScale: value
13388
13569
  }))
13389
13570
  }
@@ -13405,8 +13586,8 @@ function MaterialSection({
13405
13586
  min: 0,
13406
13587
  max: 5,
13407
13588
  step: 0.01,
13408
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13409
- ...material,
13589
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13590
+ ...material2,
13410
13591
  bumpScale: value
13411
13592
  }))
13412
13593
  }
@@ -13417,8 +13598,8 @@ function MaterialSection({
13417
13598
  {
13418
13599
  label: "Flip normal Y",
13419
13600
  checked: selectedMaterial?.flipNormalY ?? false,
13420
- onChange: (checked) => updateSelectedMaterial(selectedItem, (material) => ({
13421
- ...material,
13601
+ onChange: (checked) => updateSelectedMaterial(selectedItem, (material2) => ({
13602
+ ...material2,
13422
13603
  flipNormalY: checked
13423
13604
  }))
13424
13605
  }
@@ -13452,8 +13633,8 @@ function MaterialSection({
13452
13633
  min: -5,
13453
13634
  max: 5,
13454
13635
  step: 0.01,
13455
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13456
- ...material,
13636
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13637
+ ...material2,
13457
13638
  displacementScale: value
13458
13639
  }))
13459
13640
  }
@@ -13466,8 +13647,8 @@ function MaterialSection({
13466
13647
  min: -5,
13467
13648
  max: 5,
13468
13649
  step: 0.01,
13469
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13470
- ...material,
13650
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13651
+ ...material2,
13471
13652
  displacementBias: value
13472
13653
  }))
13473
13654
  }
@@ -13501,8 +13682,8 @@ function MaterialSection({
13501
13682
  min: 0,
13502
13683
  max: 5,
13503
13684
  step: 0.01,
13504
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13505
- ...material,
13685
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13686
+ ...material2,
13506
13687
  aoMapIntensity: value
13507
13688
  }))
13508
13689
  }
@@ -13537,8 +13718,8 @@ function MaterialSection({
13537
13718
  min: 0,
13538
13719
  max: 1,
13539
13720
  step: 0.01,
13540
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13541
- ...material,
13721
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13722
+ ...material2,
13542
13723
  clearcoat: value
13543
13724
  }))
13544
13725
  }
@@ -13564,8 +13745,8 @@ function MaterialSection({
13564
13745
  min: 0,
13565
13746
  max: 1,
13566
13747
  step: 0.01,
13567
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13568
- ...material,
13748
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13749
+ ...material2,
13569
13750
  clearcoatRoughness: value
13570
13751
  }))
13571
13752
  }
@@ -13587,8 +13768,8 @@ function MaterialSection({
13587
13768
  min: 0,
13588
13769
  max: 5,
13589
13770
  step: 0.01,
13590
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13591
- ...material,
13771
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13772
+ ...material2,
13592
13773
  clearcoatNormalScale: value
13593
13774
  }))
13594
13775
  }
@@ -13599,8 +13780,8 @@ function MaterialSection({
13599
13780
  {
13600
13781
  label: "Flip clearcoat normal Y",
13601
13782
  checked: selectedMaterial?.flipClearcoatNormalY ?? false,
13602
- onChange: (checked) => updateSelectedMaterial(selectedItem, (material) => ({
13603
- ...material,
13783
+ onChange: (checked) => updateSelectedMaterial(selectedItem, (material2) => ({
13784
+ ...material2,
13604
13785
  flipClearcoatNormalY: checked
13605
13786
  }))
13606
13787
  }
@@ -13625,8 +13806,8 @@ function MaterialSection({
13625
13806
  min: 0,
13626
13807
  max: 1,
13627
13808
  step: 0.01,
13628
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13629
- ...material,
13809
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13810
+ ...material2,
13630
13811
  sheen: value
13631
13812
  }))
13632
13813
  }
@@ -13636,8 +13817,8 @@ function MaterialSection({
13636
13817
  {
13637
13818
  label: "Sheen color",
13638
13819
  value: selectedMaterial?.sheenColor ?? "#ffffff",
13639
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13640
- ...material,
13820
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13821
+ ...material2,
13641
13822
  sheenColor: value
13642
13823
  }))
13643
13824
  }
@@ -13659,8 +13840,8 @@ function MaterialSection({
13659
13840
  min: 0,
13660
13841
  max: 1,
13661
13842
  step: 0.01,
13662
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13663
- ...material,
13843
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13844
+ ...material2,
13664
13845
  sheenRoughness: value
13665
13846
  }))
13666
13847
  }
@@ -13694,8 +13875,8 @@ function MaterialSection({
13694
13875
  min: 0,
13695
13876
  max: 1,
13696
13877
  step: 0.01,
13697
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13698
- ...material,
13878
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13879
+ ...material2,
13699
13880
  anisotropy: value
13700
13881
  }))
13701
13882
  }
@@ -13717,8 +13898,8 @@ function MaterialSection({
13717
13898
  min: 0,
13718
13899
  max: 6.28,
13719
13900
  step: 0.01,
13720
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13721
- ...material,
13901
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13902
+ ...material2,
13722
13903
  anisotropyRotation: value
13723
13904
  }))
13724
13905
  }
@@ -13752,8 +13933,8 @@ function MaterialSection({
13752
13933
  min: 0,
13753
13934
  max: 1,
13754
13935
  step: 0.01,
13755
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13756
- ...material,
13936
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13937
+ ...material2,
13757
13938
  transmission: value
13758
13939
  }))
13759
13940
  }
@@ -13775,8 +13956,8 @@ function MaterialSection({
13775
13956
  min: 0,
13776
13957
  max: 5,
13777
13958
  step: 0.01,
13778
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13779
- ...material,
13959
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13960
+ ...material2,
13780
13961
  thickness: value
13781
13962
  }))
13782
13963
  }
@@ -13795,8 +13976,8 @@ function MaterialSection({
13795
13976
  {
13796
13977
  label: "Volume tint",
13797
13978
  value: selectedMaterial?.attenuationColor ?? "#ffffff",
13798
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13799
- ...material,
13979
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13980
+ ...material2,
13800
13981
  attenuationColor: value
13801
13982
  }))
13802
13983
  }
@@ -13809,8 +13990,8 @@ function MaterialSection({
13809
13990
  min: 0,
13810
13991
  step: 0.01,
13811
13992
  placeholder: "Infinity when unset",
13812
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13813
- ...material,
13993
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
13994
+ ...material2,
13814
13995
  attenuationDistance: value
13815
13996
  }))
13816
13997
  }
@@ -13832,8 +14013,8 @@ function MaterialSection({
13832
14013
  label: "Side",
13833
14014
  value: selectedMaterial?.side ?? "front",
13834
14015
  options: MATERIAL_SIDES,
13835
- onChange: (value) => updateSelectedMaterial(selectedItem, (material) => ({
13836
- ...material,
14016
+ onChange: (value) => updateSelectedMaterial(selectedItem, (material2) => ({
14017
+ ...material2,
13837
14018
  side: value
13838
14019
  }))
13839
14020
  }
@@ -14832,9 +15013,9 @@ function formatVector3(position) {
14832
15013
 
14833
15014
  // src/components/BindingBuilder/utils/exportAssets.ts
14834
15015
  function collectTexturePaths(binding) {
14835
- const material = binding.style?.material;
14836
- if (!material) return [];
14837
- return Object.values(material).filter(
15016
+ const material2 = binding.style?.material;
15017
+ if (!material2) return [];
15018
+ return Object.values(material2).filter(
14838
15019
  (value) => !!value && typeof value === "object" && typeof value.path === "string" && value.path.trim().length > 0
14839
15020
  ).map((value) => value.path);
14840
15021
  }
@@ -14952,30 +15133,30 @@ function materialFieldHasValue(value) {
14952
15133
  }
14953
15134
  return false;
14954
15135
  }
14955
- function normalizeMaterialForStorage(material) {
14956
- if (!material) return void 0;
14957
- return Object.values(material).some(materialFieldHasValue) ? material : void 0;
15136
+ function normalizeMaterialForStorage(material2) {
15137
+ if (!material2) return void 0;
15138
+ return Object.values(material2).some(materialFieldHasValue) ? material2 : void 0;
14958
15139
  }
14959
- function isMaterialPanelEnabled(material, panel) {
14960
- if (!material) return false;
15140
+ function isMaterialPanelEnabled(material2, panel) {
15141
+ if (!material2) return false;
14961
15142
  return MATERIAL_PANEL_FIELDS[panel].some(
14962
- (field) => materialFieldHasValue(material[field])
15143
+ (field) => materialFieldHasValue(material2[field])
14963
15144
  );
14964
15145
  }
14965
- function createMaterialPanelEnabledState(material) {
15146
+ function createMaterialPanelEnabledState(material2) {
14966
15147
  return {
14967
- pbr: isMaterialPanelEnabled(material, "pbr"),
14968
- roughness: isMaterialPanelEnabled(material, "roughness"),
14969
- emission: isMaterialPanelEnabled(material, "emission"),
14970
- opacity: isMaterialPanelEnabled(material, "opacity"),
14971
- normalBump: isMaterialPanelEnabled(material, "normalBump"),
14972
- displacement: isMaterialPanelEnabled(material, "displacement"),
14973
- ao: isMaterialPanelEnabled(material, "ao"),
14974
- clearcoat: isMaterialPanelEnabled(material, "clearcoat"),
14975
- sheen: isMaterialPanelEnabled(material, "sheen"),
14976
- anisotropy: isMaterialPanelEnabled(material, "anisotropy"),
14977
- transmission: isMaterialPanelEnabled(material, "transmission"),
14978
- faces: isMaterialPanelEnabled(material, "faces")
15148
+ pbr: isMaterialPanelEnabled(material2, "pbr"),
15149
+ roughness: isMaterialPanelEnabled(material2, "roughness"),
15150
+ emission: isMaterialPanelEnabled(material2, "emission"),
15151
+ opacity: isMaterialPanelEnabled(material2, "opacity"),
15152
+ normalBump: isMaterialPanelEnabled(material2, "normalBump"),
15153
+ displacement: isMaterialPanelEnabled(material2, "displacement"),
15154
+ ao: isMaterialPanelEnabled(material2, "ao"),
15155
+ clearcoat: isMaterialPanelEnabled(material2, "clearcoat"),
15156
+ sheen: isMaterialPanelEnabled(material2, "sheen"),
15157
+ anisotropy: isMaterialPanelEnabled(material2, "anisotropy"),
15158
+ transmission: isMaterialPanelEnabled(material2, "transmission"),
15159
+ faces: isMaterialPanelEnabled(material2, "faces")
14979
15160
  };
14980
15161
  }
14981
15162
  function withMaterial(binding, nextMaterial) {
@@ -14992,9 +15173,9 @@ function withMaterial(binding, nextMaterial) {
14992
15173
  style: Object.keys(rest).length > 0 ? rest : void 0
14993
15174
  };
14994
15175
  }
14995
- function removeMaterialPanelFields(material, panel) {
14996
- if (!material) return void 0;
14997
- const nextMaterial = { ...material };
15176
+ function removeMaterialPanelFields(material2, panel) {
15177
+ if (!material2) return void 0;
15178
+ const nextMaterial = { ...material2 };
14998
15179
  for (const field of MATERIAL_PANEL_FIELDS[panel]) {
14999
15180
  delete nextMaterial[field];
15000
15181
  }
@@ -15252,8 +15433,8 @@ function BindingBuilder({ licenseKey }) {
15252
15433
  });
15253
15434
  };
15254
15435
  const setSelectedTexturePath = (nodeKey, key, value) => {
15255
- updateSelectedMaterial(nodeKey, (material) => ({
15256
- ...material,
15436
+ updateSelectedMaterial(nodeKey, (material2) => ({
15437
+ ...material2,
15257
15438
  [key]: value ? { path: value } : void 0
15258
15439
  }));
15259
15440
  };
@@ -15883,8 +16064,8 @@ function BindingBuilder({ licenseKey }) {
15883
16064
  MaterialPresetGallery,
15884
16065
  {
15885
16066
  disabled: !selectedItem,
15886
- onApply: (preset) => selectedItem && updateSelectedMaterial(selectedItem, (material) => ({
15887
- ...material,
16067
+ onApply: (preset) => selectedItem && updateSelectedMaterial(selectedItem, (material2) => ({
16068
+ ...material2,
15888
16069
  ...preset
15889
16070
  }))
15890
16071
  }
@@ -17412,71 +17593,126 @@ function useObjectBindingIds(objectBindings) {
17412
17593
  );
17413
17594
  }
17414
17595
 
17415
- // src/hooks/useObjectVisibility.ts
17596
+ // src/hooks/useObjectBindings.ts
17416
17597
  var import_react44 = require("react");
17598
+
17599
+ // src/utils/patchBindings.ts
17600
+ function isPlainObject2(value) {
17601
+ if (typeof value !== "object" || value === null) return false;
17602
+ const prototype = Object.getPrototypeOf(value);
17603
+ return prototype === Object.prototype || prototype === null;
17604
+ }
17605
+ function isBindingEditArray(value) {
17606
+ return Array.isArray(value) && value.length > 0 && typeof value[0] !== "string";
17607
+ }
17608
+ function deepMerge(base, patch) {
17609
+ if (!isPlainObject2(patch)) return patch;
17610
+ const target = isPlainObject2(base) ? { ...base } : {};
17611
+ for (const key of Object.keys(patch)) {
17612
+ const value = patch[key];
17613
+ target[key] = isPlainObject2(value) ? deepMerge(target[key], value) : value;
17614
+ }
17615
+ return target;
17616
+ }
17617
+ function patchBindings(bindings, objectIdOrEdits, patch) {
17618
+ const edits = isBindingEditArray(objectIdOrEdits) ? objectIdOrEdits : [{ ids: objectIdOrEdits, patch: patch ?? {} }];
17619
+ let next = bindings;
17620
+ for (const edit of edits) {
17621
+ const ids = Array.isArray(edit.ids) ? edit.ids : [edit.ids];
17622
+ for (const id of ids) {
17623
+ const bindingKey = resolveBindingKey(next, id);
17624
+ if (!bindingKey) continue;
17625
+ const binding = next[bindingKey];
17626
+ if (!binding) continue;
17627
+ const patchedBinding = deepMerge(binding, edit.patch);
17628
+ if (next === bindings) next = { ...bindings };
17629
+ next[bindingKey] = patchedBinding;
17630
+ }
17631
+ }
17632
+ return next;
17633
+ }
17634
+ function material(value) {
17635
+ return { style: { material: value } };
17636
+ }
17637
+
17638
+ // src/hooks/useObjectBindings.ts
17417
17639
  function getHiddenObjects(objectBindings) {
17418
17640
  return Object.fromEntries(
17419
17641
  Object.entries(objectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
17420
17642
  );
17421
17643
  }
17422
- function useObjectVisibility(objectBindings, onObjectBindingsChange) {
17423
- const updateVisibility = (0, import_react44.useCallback)(
17424
- (objectId, visible) => {
17644
+ function useObjectBindings(initialBindings) {
17645
+ const [objectBindings, setObjectBindings] = (0, import_react44.useState)(initialBindings);
17646
+ const patch = (0, import_react44.useCallback)(
17647
+ (objectIdOrEdits, patchValue) => {
17648
+ setObjectBindings(
17649
+ (current) => patchValue === void 0 ? patchBindings(current, objectIdOrEdits) : patchBindings(
17650
+ current,
17651
+ objectIdOrEdits,
17652
+ patchValue
17653
+ )
17654
+ );
17655
+ },
17656
+ []
17657
+ );
17658
+ const setMaterial = (0, import_react44.useCallback)(
17659
+ (objectId, value) => patch(objectId, material(value)),
17660
+ [patch]
17661
+ );
17662
+ const setBaseColor = (0, import_react44.useCallback)(
17663
+ (objectId, color) => setMaterial(objectId, { baseColor: color }),
17664
+ [setMaterial]
17665
+ );
17666
+ const resetMaterial = (0, import_react44.useCallback)(
17667
+ (objectId) => patch(objectId, { style: { material: void 0 } }),
17668
+ [patch]
17669
+ );
17670
+ const getMaterial = (0, import_react44.useCallback)(
17671
+ (objectId) => {
17425
17672
  const bindingKey = resolveBindingKey(objectBindings, objectId);
17426
- if (!bindingKey) return;
17427
- const targetBinding = objectBindings[bindingKey];
17428
- if (!targetBinding) return;
17429
- onObjectBindingsChange({
17430
- ...objectBindings,
17431
- [bindingKey]: {
17432
- ...targetBinding,
17433
- visible
17434
- }
17435
- });
17673
+ return bindingKey ? objectBindings[bindingKey]?.style?.material ?? null : null;
17436
17674
  },
17437
- [objectBindings, onObjectBindingsChange]
17675
+ [objectBindings]
17438
17676
  );
17439
17677
  const hideObject = (0, import_react44.useCallback)(
17440
- (objectId) => updateVisibility(objectId, false),
17441
- [updateVisibility]
17678
+ (objectId) => patch(objectId, { visible: false }),
17679
+ [patch]
17442
17680
  );
17443
17681
  const showObject = (0, import_react44.useCallback)(
17444
- (objectId) => updateVisibility(objectId, true),
17445
- [updateVisibility]
17682
+ (objectId) => patch(objectId, { visible: true }),
17683
+ [patch]
17446
17684
  );
17447
17685
  const toggleObjectVisibility = (0, import_react44.useCallback)(
17448
17686
  (objectId) => {
17449
- const bindingKey = resolveBindingKey(objectBindings, objectId);
17450
- if (!bindingKey) return;
17451
- const targetBinding = objectBindings[bindingKey];
17452
- if (!targetBinding) return;
17453
- onObjectBindingsChange({
17454
- ...objectBindings,
17455
- [bindingKey]: {
17456
- ...targetBinding,
17457
- visible: targetBinding.visible === false
17458
- }
17687
+ setObjectBindings((current) => {
17688
+ const objectIds = Array.isArray(objectId) ? objectId : [objectId];
17689
+ const edits = objectIds.reduce((acc, id) => {
17690
+ const bindingKey = resolveBindingKey(current, id);
17691
+ const binding = bindingKey ? current[bindingKey] : null;
17692
+ if (!bindingKey || !binding) return acc;
17693
+ acc.push({
17694
+ ids: bindingKey,
17695
+ patch: { visible: binding.visible === false }
17696
+ });
17697
+ return acc;
17698
+ }, []);
17699
+ return patchBindings(current, edits);
17459
17700
  });
17460
17701
  },
17461
- [objectBindings, onObjectBindingsChange]
17702
+ [setObjectBindings]
17462
17703
  );
17463
17704
  const clearHiddenObjects = (0, import_react44.useCallback)(() => {
17464
- onObjectBindingsChange(
17465
- Object.fromEntries(
17466
- Object.entries(objectBindings).map(([bindingKey, binding]) => [
17467
- bindingKey,
17468
- { ...binding, visible: true }
17469
- ])
17470
- )
17705
+ setObjectBindings(
17706
+ (current) => patchBindings(current, Object.keys(current), { visible: true })
17471
17707
  );
17472
- }, [objectBindings, onObjectBindingsChange]);
17708
+ }, [setObjectBindings]);
17473
17709
  const hiddenObjects = (0, import_react44.useMemo)(
17474
17710
  () => getHiddenObjects(objectBindings),
17475
17711
  [objectBindings]
17476
17712
  );
17477
17713
  const hiddenObjectIds = (0, import_react44.useMemo)(
17478
- () => Object.entries(hiddenObjects).map(
17479
- ([bindingKey]) => objectBindings[bindingKey]?.id ?? bindingKey
17714
+ () => Object.keys(hiddenObjects).map(
17715
+ (bindingKey) => objectBindings[bindingKey]?.id ?? bindingKey
17480
17716
  ),
17481
17717
  [hiddenObjects, objectBindings]
17482
17718
  );
@@ -17488,6 +17724,13 @@ function useObjectVisibility(objectBindings, onObjectBindingsChange) {
17488
17724
  [objectBindings]
17489
17725
  );
17490
17726
  return {
17727
+ objectBindings,
17728
+ setObjectBindings,
17729
+ patch,
17730
+ setMaterial,
17731
+ setBaseColor,
17732
+ resetMaterial,
17733
+ getMaterial,
17491
17734
  hiddenObjects,
17492
17735
  hiddenObjectIds,
17493
17736
  hideObject,
@@ -17782,9 +18025,11 @@ function useShareableViewerState({
17782
18025
  MATERIAL_SIDES,
17783
18026
  ModelViewer,
17784
18027
  SimpleModelViewer,
18028
+ material,
18029
+ patchBindings,
17785
18030
  useObjectBinding,
17786
18031
  useObjectBindingIds,
17787
- useObjectVisibility,
18032
+ useObjectBindings,
17788
18033
  useShareableViewerState,
17789
18034
  useViewerActions,
17790
18035
  useViewerAnimations,