diff --git a/frontend/dcViewer.css b/frontend/dcViewer.css index d526945..05cdbc0 100644 --- a/frontend/dcViewer.css +++ b/frontend/dcViewer.css @@ -27,6 +27,7 @@ --panel-radius: 14px; --toolbar-bg: #292929; --toolbar-bg-active: #c4ff67; + --toolbar-bg-active-contrast: #15170f; --toolbar-border: #292929; --toolbar-outline: #292929; --template-fill: #292929; @@ -36,6 +37,7 @@ --project-btn-border: #292929; --project-btn-hover: #1c1c1c; --modal-btn-primary: #c4ff67; + --modal-btn-primary-contrast: #15170f; --modal-btn-secondary: #292929; --loader-color: #c4ff67; --template-glow-strength: 80%; @@ -576,7 +578,7 @@ header { .primary-btn { background: color-mix(in srgb, var(--modal-btn-primary, var(--accent)) 100%, transparent); - color: #0f0f0f; + color: var(--modal-btn-primary-contrast, #15170f); box-shadow: 0 8px 24px rgba(255, 234, 0, 0.25); } @@ -1182,6 +1184,11 @@ header { .save-project-modal { position: fixed; + --env-control-h: 46px; + --env-button-radius: 17px; + --env-glass-control-bg: var(--nodedc-glass-control-bg); + --env-glass-control-hover: var(--nodedc-glass-control-hover); + --env-glass-control-shadow: var(--nodedc-glass-control-shadow); top: 50%; left: 50%; transform: translate(-50%, -50%); @@ -1242,6 +1249,21 @@ header { box-shadow: 0 0 0 2px rgba(255, 234, 0, 0.08); } +.save-project-modal #shareLinkInput, +.save-project-modal #shareLinkInput:focus, +.save-project-modal #shareLinkInput:focus-visible { + border-color: transparent; + outline: 0; + outline-offset: 0; + box-shadow: none; +} + +.save-project-modal #shareLinkInput { + cursor: default; + caret-color: transparent; + user-select: all; +} + .save-project-modal input[readonly] { cursor: text; color: var(--text); @@ -1920,6 +1942,7 @@ header { .tb-btn.active { background: var(--toolbar-bg-active); + color: var(--toolbar-bg-active-contrast, #15170f); } .tb-btn.disabled, @@ -1983,6 +2006,7 @@ body[data-viewer-mode="guest"] .inspector-restore-tray { .section-tool-btn.active { background: var(--toolbar-bg-active); + color: var(--toolbar-bg-active-contrast, #15170f); } .section-tool-btn:active { diff --git a/frontend/dcViewer.js b/frontend/dcViewer.js index 3c3d525..24d94ac 100644 --- a/frontend/dcViewer.js +++ b/frontend/dcViewer.js @@ -4,7 +4,7 @@ const SDK_URL = "./lib/dc-camera-context.es.js?v=3"; import { createLogo } from "./src/ui/logo.js"; import { toolbarConfig, applyToolbarVars } from "./src/ui/toolbar.js"; import { createTemplatesMenu } from "./src/ui/templatesMenu.js?v=2"; -import { createSettingsMenu } from "./src/ui/settingsMenu.js?v=7"; +import { createSettingsMenu } from "./src/ui/settingsMenu.js?v=8"; import { createMeasurementModal } from "./src/ui/measurementModal.js"; import { createGlassSelect } from "./src/ui/glassSelect.js"; import { createSliderControl } from "./src/ui/controls/slider.js"; diff --git a/frontend/index.html b/frontend/index.html index 1637783..539ad13 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -36,6 +36,34 @@ if (!Object.keys(s).length) return; const root = document.documentElement; const set = (k, v) => v !== undefined && root.style.setProperty(k, v); + const parseColor = (value) => { + if (typeof value !== "string") return null; + const color = value.trim(); + const hex = color.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i); + if (hex) { + const raw = hex[1].length === 3 + ? hex[1].split("").map((c) => c + c).join("") + : hex[1]; + return [ + parseInt(raw.slice(0, 2), 16), + parseInt(raw.slice(2, 4), 16), + parseInt(raw.slice(4, 6), 16), + ]; + } + const rgb = color.match(/^rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)/i); + if (!rgb) return null; + return rgb.slice(1, 4).map((channel) => Math.max(0, Math.min(255, Number(channel)))); + }; + const readableTextColor = (value) => { + const rgb = parseColor(value); + if (!rgb || rgb.some((channel) => !Number.isFinite(channel))) return "#15170f"; + const toLinear = (channel) => { + const value = channel / 255; + return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4; + }; + const lum = 0.2126 * toLinear(rgb[0]) + 0.7152 * toLinear(rgb[1]) + 0.0722 * toLinear(rgb[2]); + return (lum + 0.05) / 0.05 >= 1.05 / (lum + 0.05) ? "#15170f" : "#f5f5fa"; + }; set("--bg-outer", s.bgOuter); set("--canvas-top", s.canvasTop); set("--canvas-bottom", s.canvasBottom); @@ -49,6 +77,7 @@ set("--template-glow-strength", `${s.templateGlow}%`); set("--toolbar-bg", s.toolbarBg); set("--toolbar-bg-active", s.toolbarBgActive); + set("--toolbar-bg-active-contrast", s.toolbarBgActive ? readableTextColor(s.toolbarBgActive) : undefined); set("--toolbar-border", s.toolbarBorder); set("--toolbar-outline", s.toolbarOutline); set("--modal-bg", s.modalBg); @@ -63,6 +92,7 @@ set("--project-btn-border", s.projectBtnBorder); set("--project-btn-hover", s.projectBtnHover); set("--modal-btn-primary", s.modalBtnPrimary); + set("--modal-btn-primary-contrast", s.modalBtnPrimary ? readableTextColor(s.modalBtnPrimary) : undefined); set("--modal-btn-secondary", s.modalBtnSecondary); set("--loader-color", s.loaderColor); set("--border", s.border); @@ -98,7 +128,7 @@ - +
@@ -369,6 +399,6 @@ - + diff --git a/frontend/src/ui/settingsMenu.js b/frontend/src/ui/settingsMenu.js index fcdf9fe..ef5ea82 100644 --- a/frontend/src/ui/settingsMenu.js +++ b/frontend/src/ui/settingsMenu.js @@ -66,6 +66,36 @@ const projectSettings = typeof window !== "undefined" && window.__BIMDC_THEME__ ? { ...defaultSettings, ...window.__BIMDC_THEME__ } : { ...defaultSettings }; +const parseColor = (value) => { + if (typeof value !== "string") return null; + const color = value.trim(); + const hex = color.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i); + if (hex) { + const raw = hex[1].length === 3 + ? hex[1].split("").map((c) => c + c).join("") + : hex[1]; + return [ + parseInt(raw.slice(0, 2), 16), + parseInt(raw.slice(2, 4), 16), + parseInt(raw.slice(4, 6), 16), + ]; + } + const rgb = color.match(/^rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)/i); + if (!rgb) return null; + return rgb.slice(1, 4).map((channel) => Math.max(0, Math.min(255, Number(channel)))); +}; + +const readableTextColor = (value) => { + const rgb = parseColor(value); + if (!rgb || rgb.some((channel) => !Number.isFinite(channel))) return "#15170f"; + const toLinear = (channel) => { + const value = channel / 255; + return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4; + }; + const lum = 0.2126 * toLinear(rgb[0]) + 0.7152 * toLinear(rgb[1]) + 0.0722 * toLinear(rgb[2]); + return (lum + 0.05) / 0.05 >= 1.05 / (lum + 0.05) ? "#15170f" : "#f5f5fa"; +}; + const applySettings = (s) => { const root = document.documentElement; root.style.setProperty("--bg-outer", s.bgOuter); @@ -82,11 +112,13 @@ const applySettings = (s) => { root.style.setProperty("--project-btn-border", s.projectBtnBorder); root.style.setProperty("--project-btn-hover", s.projectBtnHover); root.style.setProperty("--modal-btn-primary", s.modalBtnPrimary); + root.style.setProperty("--modal-btn-primary-contrast", readableTextColor(s.modalBtnPrimary)); root.style.setProperty("--modal-btn-secondary", s.modalBtnSecondary); root.style.setProperty("--loader-color", s.loaderColor); root.style.setProperty("--template-glow-strength", `${s.templateGlow}%`); root.style.setProperty("--toolbar-bg", s.toolbarBg); root.style.setProperty("--toolbar-bg-active", s.toolbarBgActive); + root.style.setProperty("--toolbar-bg-active-contrast", readableTextColor(s.toolbarBgActive)); root.style.setProperty("--toolbar-border", s.toolbarBorder); root.style.setProperty("--toolbar-outline", s.toolbarOutline); const bgAlphaPct = `${(s.modalBgAlpha ?? 1) * 100}%`;