@liveroom-tech/react-immersive 1.3.5 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -59
- package/dist/index.d.mts +49 -8
- package/dist/index.d.ts +49 -8
- package/dist/index.js +576 -331
- package/dist/index.mjs +587 -342
- package/package.json +1 -1
package/dist/index.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
|
-
|
|
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(
|
|
437
|
-
if (!Array.isArray(
|
|
438
|
-
return
|
|
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
|
|
458
|
-
if (!
|
|
459
|
-
let layout = layouts.get(
|
|
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:
|
|
465
|
+
name: material2.name || `Material ${unnamedMaterialIndex}`,
|
|
464
466
|
layers: /* @__PURE__ */ new Map()
|
|
465
467
|
};
|
|
466
|
-
layouts.set(
|
|
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(
|
|
2367
|
+
function isMeshStandardMaterial(material2) {
|
|
2294
2368
|
return Boolean(
|
|
2295
|
-
|
|
2369
|
+
material2 && typeof material2 === "object" && "clone" in material2 && "color" in material2 && "metalness" in material2 && "roughness" in material2
|
|
2296
2370
|
);
|
|
2297
2371
|
}
|
|
2298
|
-
function isMeshPhysicalMaterial(
|
|
2372
|
+
function isMeshPhysicalMaterial(material2) {
|
|
2299
2373
|
return Boolean(
|
|
2300
|
-
|
|
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(
|
|
2337
|
-
return
|
|
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(
|
|
2372
|
-
if (isMeshStandardMaterial(
|
|
2373
|
-
if (!
|
|
2374
|
-
const src =
|
|
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(
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
toSafeVector2(
|
|
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
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
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
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
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
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
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
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
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
|
-
|
|
2681
|
-
|
|
2754
|
+
material2.map = getOrCreateTexture(texturePath, import_three13.SRGBColorSpace);
|
|
2755
|
+
material2.color.set(resolvedBaseColor ?? "#ffffff");
|
|
2682
2756
|
} else if (resolvedBaseColor) {
|
|
2683
|
-
|
|
2684
|
-
|
|
2757
|
+
material2.map = null;
|
|
2758
|
+
material2.color.set(resolvedBaseColor);
|
|
2685
2759
|
}
|
|
2686
2760
|
if (metalnessMapPath) {
|
|
2687
|
-
|
|
2761
|
+
material2.metalnessMap = getOrCreateTexture(metalnessMapPath);
|
|
2688
2762
|
}
|
|
2689
2763
|
if (configuredMaterial?.metalness !== void 0) {
|
|
2690
|
-
|
|
2764
|
+
material2.metalness = configuredMaterial.metalness;
|
|
2691
2765
|
}
|
|
2692
2766
|
if (roughnessMapPath) {
|
|
2693
|
-
|
|
2767
|
+
material2.roughnessMap = getOrCreateTexture(roughnessMapPath);
|
|
2694
2768
|
}
|
|
2695
2769
|
if (configuredMaterial?.roughness !== void 0) {
|
|
2696
|
-
|
|
2770
|
+
material2.roughness = configuredMaterial.roughness;
|
|
2697
2771
|
}
|
|
2698
2772
|
if (configuredMaterial?.emissive) {
|
|
2699
|
-
|
|
2773
|
+
material2.emissive.set(configuredMaterial.emissive);
|
|
2700
2774
|
} else if (emissiveMapPath) {
|
|
2701
|
-
|
|
2775
|
+
material2.emissive.set("#ffffff");
|
|
2702
2776
|
}
|
|
2703
2777
|
if (configuredMaterial?.emissiveIntensity !== void 0) {
|
|
2704
|
-
|
|
2778
|
+
material2.emissiveIntensity = configuredMaterial.emissiveIntensity;
|
|
2705
2779
|
}
|
|
2706
2780
|
if (emissiveMapPath) {
|
|
2707
|
-
|
|
2781
|
+
material2.emissiveMap = getOrCreateTexture(emissiveMapPath, import_three13.SRGBColorSpace);
|
|
2708
2782
|
}
|
|
2709
2783
|
if (configuredMaterial?.opacity !== void 0) {
|
|
2710
|
-
|
|
2784
|
+
material2.opacity = configuredMaterial.opacity;
|
|
2711
2785
|
}
|
|
2712
2786
|
if (alphaMapPath) {
|
|
2713
|
-
|
|
2787
|
+
material2.alphaMap = getOrCreateTexture(alphaMapPath);
|
|
2714
2788
|
}
|
|
2715
2789
|
if (configuredMaterial?.alphaTest !== void 0) {
|
|
2716
|
-
|
|
2790
|
+
material2.alphaTest = configuredMaterial.alphaTest;
|
|
2717
2791
|
}
|
|
2718
2792
|
if (configuredMaterial?.alphaHash !== void 0) {
|
|
2719
|
-
|
|
2793
|
+
material2.alphaHash = configuredMaterial.alphaHash;
|
|
2720
2794
|
}
|
|
2721
2795
|
if (configuredMaterial?.dithering !== void 0) {
|
|
2722
|
-
|
|
2796
|
+
material2.dithering = configuredMaterial.dithering;
|
|
2723
2797
|
}
|
|
2724
2798
|
if (configuredMaterial?.blendingMode) {
|
|
2725
|
-
|
|
2726
|
-
|
|
2799
|
+
material2.blending = blendingFromConfig(configuredMaterial.blendingMode);
|
|
2800
|
+
material2.depthWrite = configuredMaterial.blendingMode !== "additive";
|
|
2727
2801
|
}
|
|
2728
2802
|
if (configuredMaterial?.side) {
|
|
2729
|
-
|
|
2803
|
+
material2.side = sideFromConfig(configuredMaterial.side);
|
|
2730
2804
|
}
|
|
2731
2805
|
if (normalMapPath) {
|
|
2732
|
-
|
|
2806
|
+
material2.normalMap = getOrCreateTexture(normalMapPath);
|
|
2733
2807
|
}
|
|
2734
2808
|
if (normalMapPath || configuredMaterial?.normalScale !== void 0) {
|
|
2735
|
-
const normalScale = configuredMaterial?.normalScale ?? Math.abs(toSafeVector2(
|
|
2736
|
-
|
|
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
|
-
|
|
2816
|
+
material2.bumpMap = getOrCreateTexture(bumpMapPath);
|
|
2743
2817
|
}
|
|
2744
2818
|
if (configuredMaterial?.bumpScale !== void 0) {
|
|
2745
|
-
|
|
2819
|
+
material2.bumpScale = configuredMaterial.bumpScale;
|
|
2746
2820
|
}
|
|
2747
2821
|
if (aoMapPath) {
|
|
2748
|
-
|
|
2822
|
+
material2.aoMap = getOrCreateTexture(aoMapPath);
|
|
2749
2823
|
}
|
|
2750
2824
|
if (configuredMaterial?.aoMapIntensity !== void 0) {
|
|
2751
|
-
|
|
2825
|
+
material2.aoMapIntensity = configuredMaterial.aoMapIntensity;
|
|
2752
2826
|
}
|
|
2753
2827
|
if (displacementMapPath) {
|
|
2754
|
-
|
|
2828
|
+
material2.displacementMap = getOrCreateTexture(displacementMapPath);
|
|
2755
2829
|
}
|
|
2756
2830
|
if (configuredMaterial?.displacementScale !== void 0) {
|
|
2757
|
-
|
|
2831
|
+
material2.displacementScale = configuredMaterial.displacementScale;
|
|
2758
2832
|
}
|
|
2759
2833
|
if (configuredMaterial?.displacementBias !== void 0) {
|
|
2760
|
-
|
|
2834
|
+
material2.displacementBias = configuredMaterial.displacementBias;
|
|
2761
2835
|
}
|
|
2762
2836
|
if (clearcoatMapPath) {
|
|
2763
|
-
|
|
2837
|
+
material2.clearcoatMap = getOrCreateTexture(clearcoatMapPath);
|
|
2764
2838
|
}
|
|
2765
2839
|
if (configuredMaterial?.clearcoat !== void 0) {
|
|
2766
|
-
|
|
2840
|
+
material2.clearcoat = configuredMaterial.clearcoat;
|
|
2767
2841
|
}
|
|
2768
2842
|
if (clearcoatRoughnessMapPath) {
|
|
2769
|
-
|
|
2843
|
+
material2.clearcoatRoughnessMap = getOrCreateTexture(
|
|
2770
2844
|
clearcoatRoughnessMapPath
|
|
2771
2845
|
);
|
|
2772
2846
|
}
|
|
2773
2847
|
if (configuredMaterial?.clearcoatRoughness !== void 0) {
|
|
2774
|
-
|
|
2848
|
+
material2.clearcoatRoughness = configuredMaterial.clearcoatRoughness;
|
|
2775
2849
|
}
|
|
2776
2850
|
if (clearcoatNormalMapPath) {
|
|
2777
|
-
|
|
2851
|
+
material2.clearcoatNormalMap = getOrCreateTexture(clearcoatNormalMapPath);
|
|
2778
2852
|
}
|
|
2779
2853
|
if (clearcoatNormalMapPath || configuredMaterial?.clearcoatNormalScale !== void 0) {
|
|
2780
|
-
const clearcoatNormalScale = configuredMaterial?.clearcoatNormalScale ?? Math.abs(toSafeVector2(
|
|
2781
|
-
|
|
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
|
-
|
|
2861
|
+
material2.sheen = configuredMaterial.sheen;
|
|
2788
2862
|
}
|
|
2789
2863
|
if (configuredMaterial?.sheenColor) {
|
|
2790
|
-
|
|
2864
|
+
material2.sheenColor.set(configuredMaterial.sheenColor);
|
|
2791
2865
|
}
|
|
2792
2866
|
if (sheenColorMapPath) {
|
|
2793
|
-
|
|
2867
|
+
material2.sheenColorMap = getOrCreateTexture(
|
|
2794
2868
|
sheenColorMapPath,
|
|
2795
2869
|
import_three13.SRGBColorSpace
|
|
2796
2870
|
);
|
|
2797
2871
|
}
|
|
2798
2872
|
if (configuredMaterial?.sheenRoughness !== void 0) {
|
|
2799
|
-
|
|
2873
|
+
material2.sheenRoughness = configuredMaterial.sheenRoughness;
|
|
2800
2874
|
}
|
|
2801
2875
|
if (sheenRoughnessMapPath) {
|
|
2802
|
-
|
|
2876
|
+
material2.sheenRoughnessMap = getOrCreateTexture(sheenRoughnessMapPath);
|
|
2803
2877
|
}
|
|
2804
2878
|
if (configuredMaterial?.anisotropy !== void 0) {
|
|
2805
|
-
|
|
2879
|
+
material2.anisotropy = configuredMaterial.anisotropy;
|
|
2806
2880
|
}
|
|
2807
2881
|
if (configuredMaterial?.anisotropyRotation !== void 0) {
|
|
2808
|
-
|
|
2882
|
+
material2.anisotropyRotation = configuredMaterial.anisotropyRotation;
|
|
2809
2883
|
}
|
|
2810
2884
|
if (anisotropyMapPath) {
|
|
2811
|
-
|
|
2885
|
+
material2.anisotropyMap = getOrCreateTexture(anisotropyMapPath);
|
|
2812
2886
|
}
|
|
2813
2887
|
if (configuredMaterial?.transmission !== void 0) {
|
|
2814
|
-
|
|
2888
|
+
material2.transmission = configuredMaterial.transmission;
|
|
2815
2889
|
}
|
|
2816
2890
|
if (transmissionMapPath) {
|
|
2817
|
-
|
|
2891
|
+
material2.transmissionMap = getOrCreateTexture(transmissionMapPath);
|
|
2818
2892
|
}
|
|
2819
2893
|
if (configuredMaterial?.thickness !== void 0) {
|
|
2820
|
-
|
|
2894
|
+
material2.thickness = configuredMaterial.thickness;
|
|
2821
2895
|
}
|
|
2822
2896
|
if (thicknessMapPath) {
|
|
2823
|
-
|
|
2897
|
+
material2.thicknessMap = getOrCreateTexture(thicknessMapPath);
|
|
2824
2898
|
}
|
|
2825
2899
|
if (configuredMaterial?.attenuationColor) {
|
|
2826
|
-
|
|
2900
|
+
material2.attenuationColor.set(configuredMaterial.attenuationColor);
|
|
2827
2901
|
}
|
|
2828
2902
|
if (configuredMaterial?.attenuationDistance !== void 0) {
|
|
2829
|
-
|
|
2903
|
+
material2.attenuationDistance = configuredMaterial.attenuationDistance;
|
|
2830
2904
|
}
|
|
2831
2905
|
if (configuredMaterial?.reflectivity !== void 0) {
|
|
2832
|
-
|
|
2906
|
+
material2.reflectivity = configuredMaterial.reflectivity;
|
|
2833
2907
|
}
|
|
2834
2908
|
if (configuredMaterial?.specularIntensity !== void 0) {
|
|
2835
|
-
|
|
2909
|
+
material2.specularIntensity = configuredMaterial.specularIntensity;
|
|
2836
2910
|
}
|
|
2837
2911
|
if (specularIntensityMapPath) {
|
|
2838
|
-
|
|
2912
|
+
material2.specularIntensityMap = getOrCreateTexture(
|
|
2839
2913
|
specularIntensityMapPath
|
|
2840
2914
|
);
|
|
2841
2915
|
}
|
|
2842
2916
|
if (configuredMaterial?.specularColor) {
|
|
2843
|
-
|
|
2917
|
+
material2.specularColor.set(configuredMaterial.specularColor);
|
|
2844
2918
|
}
|
|
2845
2919
|
if (specularColorMapPath) {
|
|
2846
|
-
|
|
2920
|
+
material2.specularColorMap = getOrCreateTexture(
|
|
2847
2921
|
specularColorMapPath,
|
|
2848
2922
|
import_three13.SRGBColorSpace
|
|
2849
2923
|
);
|
|
2850
2924
|
}
|
|
2851
|
-
|
|
2852
|
-
if (!
|
|
2853
|
-
|
|
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
|
|
2997
|
-
|
|
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,
|
|
3083
|
+
for (const [nodeName, material2] of Object.entries(perNodeMaterials)) {
|
|
3010
3084
|
const basic = new import_three14.MeshBasicMaterial();
|
|
3011
|
-
basic.color.copy(
|
|
3012
|
-
basic.map =
|
|
3013
|
-
basic.alphaMap =
|
|
3014
|
-
basic.alphaTest =
|
|
3015
|
-
basic.transparent =
|
|
3016
|
-
basic.opacity =
|
|
3017
|
-
basic.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,
|
|
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(
|
|
3038
|
-
matcap.alphaMap =
|
|
3039
|
-
matcap.alphaTest =
|
|
3040
|
-
matcap.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
|
|
3048
|
-
|
|
3049
|
-
for (const
|
|
3050
|
-
|
|
3051
|
-
for (const
|
|
3052
|
-
|
|
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
|
|
3099
|
-
if (!
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
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(
|
|
3210
|
+
applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor);
|
|
3135
3211
|
if (isHighlightedByHover) {
|
|
3136
|
-
|
|
3212
|
+
material2.emissive?.add(HIGHLIGHT_EMISSIVE_TINT);
|
|
3137
3213
|
}
|
|
3138
|
-
|
|
3214
|
+
material2.needsUpdate = true;
|
|
3139
3215
|
if (basicMaterial) {
|
|
3140
|
-
basicMaterial.color.copy(
|
|
3141
|
-
basicMaterial.map =
|
|
3142
|
-
basicMaterial.alphaMap =
|
|
3143
|
-
basicMaterial.alphaTest =
|
|
3144
|
-
basicMaterial.transparent =
|
|
3145
|
-
basicMaterial.opacity =
|
|
3146
|
-
basicMaterial.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(
|
|
3230
|
+
matcapMaterial.color.copy(material2.color);
|
|
3155
3231
|
}
|
|
3156
|
-
matcapMaterial.map =
|
|
3157
|
-
matcapMaterial.alphaMap =
|
|
3158
|
-
matcapMaterial.alphaTest =
|
|
3159
|
-
matcapMaterial.transparent =
|
|
3160
|
-
matcapMaterial.opacity =
|
|
3161
|
-
matcapMaterial.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:
|
|
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:
|
|
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: {
|
|
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:
|
|
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, (
|
|
13030
|
-
...
|
|
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, (
|
|
13045
|
-
...
|
|
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, (
|
|
13069
|
-
...
|
|
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, (
|
|
13080
|
-
...
|
|
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, (
|
|
13121
|
-
...
|
|
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, (
|
|
13133
|
-
...
|
|
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, (
|
|
13147
|
-
...
|
|
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, (
|
|
13191
|
-
...
|
|
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, (
|
|
13204
|
-
...
|
|
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, (
|
|
13237
|
-
...
|
|
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, (
|
|
13251
|
-
...
|
|
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, (
|
|
13286
|
-
...
|
|
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, (
|
|
13300
|
-
...
|
|
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, (
|
|
13321
|
-
...
|
|
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, (
|
|
13334
|
-
...
|
|
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, (
|
|
13348
|
-
...
|
|
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, (
|
|
13386
|
-
...
|
|
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, (
|
|
13409
|
-
...
|
|
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, (
|
|
13421
|
-
...
|
|
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, (
|
|
13456
|
-
...
|
|
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, (
|
|
13470
|
-
...
|
|
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, (
|
|
13505
|
-
...
|
|
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, (
|
|
13541
|
-
...
|
|
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, (
|
|
13568
|
-
...
|
|
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, (
|
|
13591
|
-
...
|
|
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, (
|
|
13603
|
-
...
|
|
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, (
|
|
13629
|
-
...
|
|
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, (
|
|
13640
|
-
...
|
|
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, (
|
|
13663
|
-
...
|
|
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, (
|
|
13698
|
-
...
|
|
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, (
|
|
13721
|
-
...
|
|
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, (
|
|
13756
|
-
...
|
|
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, (
|
|
13779
|
-
...
|
|
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, (
|
|
13799
|
-
...
|
|
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, (
|
|
13813
|
-
...
|
|
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, (
|
|
13836
|
-
...
|
|
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
|
|
14836
|
-
if (!
|
|
14837
|
-
return Object.values(
|
|
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(
|
|
14956
|
-
if (!
|
|
14957
|
-
return Object.values(
|
|
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(
|
|
14960
|
-
if (!
|
|
15140
|
+
function isMaterialPanelEnabled(material2, panel) {
|
|
15141
|
+
if (!material2) return false;
|
|
14961
15142
|
return MATERIAL_PANEL_FIELDS[panel].some(
|
|
14962
|
-
(field) => materialFieldHasValue(
|
|
15143
|
+
(field) => materialFieldHasValue(material2[field])
|
|
14963
15144
|
);
|
|
14964
15145
|
}
|
|
14965
|
-
function createMaterialPanelEnabledState(
|
|
15146
|
+
function createMaterialPanelEnabledState(material2) {
|
|
14966
15147
|
return {
|
|
14967
|
-
pbr: isMaterialPanelEnabled(
|
|
14968
|
-
roughness: isMaterialPanelEnabled(
|
|
14969
|
-
emission: isMaterialPanelEnabled(
|
|
14970
|
-
opacity: isMaterialPanelEnabled(
|
|
14971
|
-
normalBump: isMaterialPanelEnabled(
|
|
14972
|
-
displacement: isMaterialPanelEnabled(
|
|
14973
|
-
ao: isMaterialPanelEnabled(
|
|
14974
|
-
clearcoat: isMaterialPanelEnabled(
|
|
14975
|
-
sheen: isMaterialPanelEnabled(
|
|
14976
|
-
anisotropy: isMaterialPanelEnabled(
|
|
14977
|
-
transmission: isMaterialPanelEnabled(
|
|
14978
|
-
faces: isMaterialPanelEnabled(
|
|
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(
|
|
14996
|
-
if (!
|
|
14997
|
-
const nextMaterial = { ...
|
|
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, (
|
|
15256
|
-
...
|
|
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, (
|
|
15887
|
-
...
|
|
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/
|
|
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
|
|
17423
|
-
const
|
|
17424
|
-
|
|
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
|
-
|
|
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
|
|
17675
|
+
[objectBindings]
|
|
17438
17676
|
);
|
|
17439
17677
|
const hideObject = (0, import_react44.useCallback)(
|
|
17440
|
-
(objectId) =>
|
|
17441
|
-
[
|
|
17678
|
+
(objectId) => patch(objectId, { visible: false }),
|
|
17679
|
+
[patch]
|
|
17442
17680
|
);
|
|
17443
17681
|
const showObject = (0, import_react44.useCallback)(
|
|
17444
|
-
(objectId) =>
|
|
17445
|
-
[
|
|
17682
|
+
(objectId) => patch(objectId, { visible: true }),
|
|
17683
|
+
[patch]
|
|
17446
17684
|
);
|
|
17447
17685
|
const toggleObjectVisibility = (0, import_react44.useCallback)(
|
|
17448
17686
|
(objectId) => {
|
|
17449
|
-
|
|
17450
|
-
|
|
17451
|
-
|
|
17452
|
-
|
|
17453
|
-
|
|
17454
|
-
|
|
17455
|
-
|
|
17456
|
-
|
|
17457
|
-
|
|
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
|
-
[
|
|
17702
|
+
[setObjectBindings]
|
|
17462
17703
|
);
|
|
17463
17704
|
const clearHiddenObjects = (0, import_react44.useCallback)(() => {
|
|
17464
|
-
|
|
17465
|
-
Object.
|
|
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
|
-
}, [
|
|
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.
|
|
17479
|
-
(
|
|
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
|
-
|
|
18032
|
+
useObjectBindings,
|
|
17788
18033
|
useShareableViewerState,
|
|
17789
18034
|
useViewerActions,
|
|
17790
18035
|
useViewerAnimations,
|