Refine BIM glass theming and panel scroll layout

This commit is contained in:
CODEX 2026-06-20 21:55:23 +03:00
parent 3584359833
commit ad3a4f3877
5 changed files with 307 additions and 113 deletions

View File

@ -68,11 +68,11 @@
--comment-target-passive: #f5f5fa;
--comment-target-passive-alpha: 0.58;
--comment-target-passive-size: 22px;
--nodedc-header-dropdown-bg: rgba(11, 11, 14, 0.94);
--nodedc-header-dropdown-item-bg: rgba(42, 42, 46, 0.74);
--nodedc-header-dropdown-item-hover-bg: rgba(42, 42, 46, 0.86);
--nodedc-header-dropdown-item-active-bg: rgba(47, 47, 52, 0.96);
--nodedc-header-dropdown-item-active-color: #ffffff;
--nodedc-header-dropdown-bg: var(--nodedc-glass-panel-bg-strong);
--nodedc-header-dropdown-item-bg: var(--nodedc-glass-control-bg);
--nodedc-header-dropdown-item-hover-bg: var(--nodedc-glass-control-hover);
--nodedc-header-dropdown-item-active-bg: var(--toolbar-bg-active);
--nodedc-header-dropdown-item-active-color: var(--toolbar-bg-active-contrast);
--nodedc-glass-panel-bg: rgba(28, 28, 30, 0.72);
--nodedc-glass-panel-bg-strong: rgba(28, 28, 30, 0.82);
--nodedc-glass-panel-bg-soft: rgba(28, 28, 30, 0.56);
@ -398,17 +398,17 @@ header {
bottom: 12px;
width: 320px;
max-height: calc(100vh - 90px);
overflow: auto;
overflow: hidden;
background: var(--nodedc-glass-panel-surface);
border: 1px solid var(--nodedc-glass-outline);
border-radius: var(--modal-radius, 16px);
padding: 12px;
padding: 0;
box-shadow: var(--nodedc-glass-panel-shadow);
backdrop-filter: var(--nodedc-glass-filter);
-webkit-backdrop-filter: var(--nodedc-glass-filter);
display: flex;
flex-direction: column;
gap: 10px;
gap: 0;
z-index: 11;
display: none;
}
@ -418,8 +418,7 @@ header {
}
.inspector-panel-header {
position: sticky;
top: -12px;
position: relative;
z-index: 5;
flex: 0 0 auto;
display: grid;
@ -428,9 +427,9 @@ header {
gap: 10px;
height: calc(var(--env-control-h) + 20px);
min-height: var(--env-control-h);
margin: -12px -12px 0;
margin: 0;
padding: 12px 12px 8px 16px;
background: var(--nodedc-glass-panel-surface-strong);
background: transparent;
border-radius: var(--modal-radius, 16px) var(--modal-radius, 16px) 0 0;
color: var(--text);
font-weight: 700;
@ -443,6 +442,16 @@ header {
line-height: 1.2;
}
.inspector-panel-body {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
padding: 0 12px 12px;
display: flex;
flex-direction: column;
gap: 10px;
}
.inspector-collapse-btn {
display: grid;
place-items: center;
@ -561,16 +570,16 @@ header {
}
.comments-panel-header {
position: sticky;
top: 0;
position: relative;
z-index: 5;
flex: 0 0 auto;
display: grid;
grid-template-columns: minmax(0, 1fr) var(--env-control-h);
align-items: center;
gap: 10px;
min-height: calc(var(--env-control-h) + 20px);
padding: 12px 12px 8px 16px;
background: var(--nodedc-glass-panel-surface-strong);
background: transparent;
border-radius: var(--modal-radius, 16px) var(--modal-radius, 16px) 0 0;
color: var(--text);
font-weight: 700;
@ -590,6 +599,7 @@ header {
}
.comments-panel-body {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
padding: 0 12px 12px;
@ -1323,13 +1333,14 @@ header {
background: var(--nodedc-glass-panel-surface);
border: 1px solid var(--nodedc-glass-outline);
border-radius: var(--modal-radius, 14px);
padding: 12px;
display: grid;
gap: 10px;
padding: 0;
display: flex;
flex-direction: column;
gap: 0;
width: var(--templates-width, 280px);
min-height: 200px;
max-height: 420px;
overflow: auto;
overflow: hidden;
box-shadow: var(--nodedc-glass-panel-shadow);
z-index: 20;
backdrop-filter: var(--nodedc-glass-filter);
@ -1341,23 +1352,32 @@ header {
}
.templates-panel .templates-header {
position: sticky;
top: -12px;
position: relative;
z-index: 5;
flex: 0 0 auto;
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: 10px;
height: calc(var(--env-control-h) + 20px);
min-height: var(--env-control-h);
margin: -12px -12px 0;
margin: 0;
padding: 12px 12px 8px 16px;
border-radius: var(--modal-radius, 14px) var(--modal-radius, 14px) 0 0;
background: var(--nodedc-glass-panel-surface-strong);
background: transparent;
color: var(--text);
font-weight: 700;
}
.templates-panel .templates-panel-body {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
padding: 0 12px 12px;
display: grid;
gap: 10px;
}
.templates-panel .templates-title {
font-size: 13px;
line-height: 1.2;
@ -1530,6 +1550,8 @@ header {
width: var(--templates-width, 280px);
max-width: min(var(--templates-width, 350px), calc(100vw - 32px));
padding: 12px;
gap: 10px;
overflow: auto;
z-index: 40;
}
@ -2376,13 +2398,14 @@ header {
background: var(--nodedc-glass-panel-surface);
border: 1px solid var(--nodedc-glass-outline);
border-radius: var(--modal-radius, 14px);
padding: 12px;
display: grid;
gap: 10px;
padding: 0;
display: flex;
flex-direction: column;
gap: 0;
width: var(--settings-width, 350px);
min-height: 420px;
max-height: min(720px, calc(100vh - 72px));
overflow: auto;
overflow: hidden;
box-shadow: var(--nodedc-glass-panel-shadow);
z-index: 22;
font-size: 12px;
@ -2391,9 +2414,9 @@ header {
}
.settings-panel .settings-header {
position: sticky;
top: -12px;
position: relative;
z-index: 5;
flex: 0 0 auto;
display: grid;
grid-template-columns: minmax(0, 1fr) var(--env-control-h);
align-items: center;
@ -2402,12 +2425,21 @@ header {
font-weight: 700;
height: calc(var(--env-control-h) + 20px);
min-height: var(--env-control-h);
margin: -12px -12px 0;
margin: 0;
padding: 12px 12px 8px 16px;
background: var(--nodedc-glass-panel-surface-strong);
background: transparent;
border-radius: var(--modal-radius, 14px) var(--modal-radius, 14px) 0 0;
}
.settings-panel .settings-panel-body {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
padding: 0 12px 12px;
display: grid;
gap: 10px;
}
.settings-panel .settings-title {
font-size: 13px;
line-height: 1.2;
@ -3120,13 +3152,14 @@ body[data-viewer-mode="guest"] .inspector-restore-tray {
background: var(--nodedc-glass-panel-surface);
border: 1px solid var(--nodedc-glass-outline);
border-radius: var(--modal-radius, 14px);
padding: 10px;
display: grid;
gap: 10px;
padding: 0;
display: flex;
flex-direction: column;
gap: 0;
width: var(--templates-width, 280px);
min-height: 200px;
max-height: 420px;
overflow: auto;
overflow: hidden;
box-shadow: var(--nodedc-glass-panel-shadow);
z-index: 20;
backdrop-filter: var(--nodedc-glass-filter);

View File

@ -3,8 +3,8 @@ 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=9";
import { createTemplatesMenu } from "./src/ui/templatesMenu.js?v=3";
import { createSettingsMenu } from "./src/ui/settingsMenu.js?v=11";
import { createMeasurementModal } from "./src/ui/measurementModal.js";
import { createGlassSelect } from "./src/ui/glassSelect.js";
import { createSliderControl } from "./src/ui/controls/slider.js";

View File

@ -64,6 +64,69 @@
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 clamp = (value, min, max) => Math.max(min, Math.min(max, value));
const colorParts = (value, fallback) => {
const parsed = parseColor(value);
if (parsed && parsed.every((channel) => Number.isFinite(channel))) return parsed;
const parsedFallback = parseColor(fallback);
if (parsedFallback && parsedFallback.every((channel) => Number.isFinite(channel))) return parsedFallback;
return [28, 28, 28];
};
const alphaValue = (value, fallback) => {
const numeric = Number(value);
return Number.isFinite(numeric) ? clamp(numeric, 0, 1) : fallback;
};
const rgba = (parts, alpha) => {
const [r, g, b] = parts.map((channel) => Math.round(clamp(channel, 0, 255)));
return `rgba(${r}, ${g}, ${b}, ${clamp(alpha, 0, 1).toFixed(3)})`;
};
const applyGlassSettings = () => {
const panelColor = colorParts(s.modalBg, "#1c1c1c");
const focusColor = colorParts(s.modalFocus, "#292929");
const controlColor = colorParts(s.modalElement, s.modalFocus || "#262626");
const outlineColor = colorParts(s.modalBorder, s.modalElementOutline || "#1c1c1c");
const controlOutlineColor = colorParts(s.modalElementOutline, s.modalBorder || "#262626");
const modalBgAlpha = alphaValue(s.modalBgAlpha, 1);
const modalFocusAlpha = alphaValue(s.modalFocusAlpha, 0.35);
const panelAlpha = clamp(0.24 + modalBgAlpha * 0.48, 0.32, 0.88);
const panelStrongAlpha = clamp(panelAlpha + 0.1, 0.42, 0.92);
const panelSoftAlpha = clamp(panelAlpha - 0.16, 0.2, 0.72);
const sectionAlpha = clamp(modalFocusAlpha * 0.6, 0.04, 0.42);
const controlAlpha = clamp(0.12 + modalFocusAlpha * 0.9, 0.16, 0.66);
const controlHoverAlpha = clamp(controlAlpha + 0.08, 0.22, 0.74);
const outlineAlpha = clamp(0.055 + modalFocusAlpha * 0.18, 0.065, 0.2);
const controlOutlineAlpha = clamp(0.045 + modalFocusAlpha * 0.12, 0.055, 0.16);
const panelBg = rgba(panelColor, panelAlpha);
const panelBgStrong = rgba(panelColor, panelStrongAlpha);
const panelBgSoft = rgba(panelColor, panelSoftAlpha);
const sectionBg = rgba(focusColor, sectionAlpha);
const outline = rgba(outlineColor, outlineAlpha);
const outlineBright = rgba(outlineColor, clamp(outlineAlpha + 0.05, 0.08, 0.26));
const outlineMid = rgba(outlineColor, clamp(outlineAlpha + 0.015, 0.07, 0.22));
const outlineDim = rgba(outlineColor, clamp(outlineAlpha * 0.62, 0.04, 0.16));
const controlBg = rgba(controlColor, controlAlpha);
const controlHover = rgba(focusColor, controlHoverAlpha);
const controlShadow = rgba(controlOutlineColor, controlOutlineAlpha);
set("--nodedc-glass-panel-bg", panelBg);
set("--nodedc-glass-panel-bg-strong", panelBgStrong);
set("--nodedc-glass-panel-bg-soft", panelBgSoft);
set("--nodedc-glass-panel-surface", `linear-gradient(180deg, rgba(255, 255, 255, 0.052) 0%, rgba(255, 255, 255, 0.014) 100%), ${panelBg}`);
set("--nodedc-glass-panel-surface-strong", `linear-gradient(180deg, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0.016) 100%), ${panelBgStrong}`);
set("--nodedc-glass-section-bg", `linear-gradient(180deg, rgba(255, 255, 255, 0.038) 0%, rgba(255, 255, 255, 0.01) 100%), ${sectionBg}`);
set("--nodedc-glass-outline", outline);
set("--nodedc-glass-outline-gradient", `linear-gradient(180deg, ${outlineBright} 0%, ${outlineMid} 56%, ${outlineDim} 100%)`);
set("--nodedc-glass-panel-shadow", `inset 0 1px 0 ${outlineDim}, 0 22px 72px rgba(0, 0, 0, 0.42)`);
set("--nodedc-glass-control-bg", `linear-gradient(180deg, rgba(255, 255, 255, 0.052) 0%, rgba(255, 255, 255, 0.018) 100%), ${controlBg}`);
set("--nodedc-glass-control-hover", `linear-gradient(180deg, rgba(255, 255, 255, 0.075) 0%, rgba(255, 255, 255, 0.026) 100%), ${controlHover}`);
set("--nodedc-glass-control-shadow", `inset 0 1px 0 ${controlShadow}`);
set("--nodedc-header-dropdown-bg", panelBgStrong);
set("--nodedc-header-dropdown-item-bg", controlBg);
set("--nodedc-header-dropdown-item-hover-bg", controlHover);
set("--nodedc-header-dropdown-item-active-bg", s.toolbarBgActive);
set("--nodedc-header-dropdown-item-active-color", s.toolbarBgActive ? readableTextColor(s.toolbarBgActive) : undefined);
};
set("--bg-outer", s.bgOuter);
set("--canvas-top", s.canvasTop);
set("--canvas-bottom", s.canvasBottom);
@ -88,6 +151,7 @@
set("--modal-element", s.modalElement);
set("--modal-element-outline", s.modalElementOutline);
set("--modal-radius", s.modalRadius);
applyGlassSettings();
set("--project-btn-fill", s.projectBtnFill);
set("--project-btn-border", s.projectBtnBorder);
set("--project-btn-hover", s.projectBtnHover);
@ -134,7 +198,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="./dcViewer.css?v=30">
<link rel="stylesheet" href="./dcViewer.css?v=32">
</head>
<body>
<header>
@ -186,6 +250,7 @@
</svg>
</button>
</div>
<div class="inspector-panel-body">
<div class="panel-section inspector-main-section">
<div id="treeContainer" class="tree"></div>
<div class="inspector-controls">
@ -249,6 +314,7 @@
<button class="primary-btn" id="saveProjectButton" type="button">Сохранить проект</button>
<button class="secondary-btn danger-btn" id="deleteProjectButton" type="button">Удалить проект</button>
</div>
</div>
</aside>
<div class="inspector-restore-tray" id="inspectorRestoreTray" hidden>
<button class="inspector-restore-button" id="inspectorRestoreButton" type="button">Инспектор</button>
@ -570,6 +636,6 @@
<input id="fileInput" class="hidden" type="file"
accept=".xkt,.glb,.gltf,.bim,.las,.laz,.obj,.stl">
<script type="module" src="./dcViewer.js?v=43"></script>
<script type="module" src="./dcViewer.js?v=45"></script>
</body>
</html>

View File

@ -102,6 +102,92 @@ const readableTextColor = (value) => {
return (lum + 0.05) / 0.05 >= 1.05 / (lum + 0.05) ? "#15170f" : "#f5f5fa";
};
const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
const colorParts = (value, fallback) => {
const parsed = parseColor(value);
if (parsed && parsed.every((channel) => Number.isFinite(channel))) return parsed;
const parsedFallback = parseColor(fallback);
if (parsedFallback && parsedFallback.every((channel) => Number.isFinite(channel))) return parsedFallback;
return [28, 28, 28];
};
const alphaValue = (value, fallback) => {
const numeric = Number(value);
return Number.isFinite(numeric) ? clamp(numeric, 0, 1) : fallback;
};
const rgba = (parts, alpha) => {
const [r, g, b] = parts.map((channel) => Math.round(clamp(channel, 0, 255)));
return `rgba(${r}, ${g}, ${b}, ${clamp(alpha, 0, 1).toFixed(3)})`;
};
const applyGlassSettings = (root, s) => {
const panelColor = colorParts(s.modalBg, defaultSettings.modalBg);
const focusColor = colorParts(s.modalFocus, defaultSettings.modalFocus);
const controlColor = colorParts(s.modalElement, s.modalFocus || defaultSettings.modalElement);
const outlineColor = colorParts(s.modalBorder, s.modalElementOutline || defaultSettings.modalBorder);
const controlOutlineColor = colorParts(s.modalElementOutline, s.modalBorder || defaultSettings.modalElementOutline);
const modalBgAlpha = alphaValue(s.modalBgAlpha, defaultSettings.modalBgAlpha);
const modalFocusAlpha = alphaValue(s.modalFocusAlpha, defaultSettings.modalFocusAlpha);
const panelAlpha = clamp(0.24 + modalBgAlpha * 0.48, 0.32, 0.88);
const panelStrongAlpha = clamp(panelAlpha + 0.1, 0.42, 0.92);
const panelSoftAlpha = clamp(panelAlpha - 0.16, 0.2, 0.72);
const sectionAlpha = clamp(modalFocusAlpha * 0.6, 0.04, 0.42);
const controlAlpha = clamp(0.12 + modalFocusAlpha * 0.9, 0.16, 0.66);
const controlHoverAlpha = clamp(controlAlpha + 0.08, 0.22, 0.74);
const outlineAlpha = clamp(0.055 + modalFocusAlpha * 0.18, 0.065, 0.2);
const controlOutlineAlpha = clamp(0.045 + modalFocusAlpha * 0.12, 0.055, 0.16);
const panelBg = rgba(panelColor, panelAlpha);
const panelBgStrong = rgba(panelColor, panelStrongAlpha);
const panelBgSoft = rgba(panelColor, panelSoftAlpha);
const sectionBg = rgba(focusColor, sectionAlpha);
const outline = rgba(outlineColor, outlineAlpha);
const outlineBright = rgba(outlineColor, clamp(outlineAlpha + 0.05, 0.08, 0.26));
const outlineMid = rgba(outlineColor, clamp(outlineAlpha + 0.015, 0.07, 0.22));
const outlineDim = rgba(outlineColor, clamp(outlineAlpha * 0.62, 0.04, 0.16));
const controlBg = rgba(controlColor, controlAlpha);
const controlHover = rgba(focusColor, controlHoverAlpha);
const controlShadow = rgba(controlOutlineColor, controlOutlineAlpha);
root.style.setProperty("--nodedc-glass-panel-bg", panelBg);
root.style.setProperty("--nodedc-glass-panel-bg-strong", panelBgStrong);
root.style.setProperty("--nodedc-glass-panel-bg-soft", panelBgSoft);
root.style.setProperty(
"--nodedc-glass-panel-surface",
`linear-gradient(180deg, rgba(255, 255, 255, 0.052) 0%, rgba(255, 255, 255, 0.014) 100%), ${panelBg}`
);
root.style.setProperty(
"--nodedc-glass-panel-surface-strong",
`linear-gradient(180deg, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0.016) 100%), ${panelBgStrong}`
);
root.style.setProperty(
"--nodedc-glass-section-bg",
`linear-gradient(180deg, rgba(255, 255, 255, 0.038) 0%, rgba(255, 255, 255, 0.01) 100%), ${sectionBg}`
);
root.style.setProperty("--nodedc-glass-outline", outline);
root.style.setProperty(
"--nodedc-glass-outline-gradient",
`linear-gradient(180deg, ${outlineBright} 0%, ${outlineMid} 56%, ${outlineDim} 100%)`
);
root.style.setProperty("--nodedc-glass-panel-shadow", `inset 0 1px 0 ${outlineDim}, 0 22px 72px rgba(0, 0, 0, 0.42)`);
root.style.setProperty(
"--nodedc-glass-control-bg",
`linear-gradient(180deg, rgba(255, 255, 255, 0.052) 0%, rgba(255, 255, 255, 0.018) 100%), ${controlBg}`
);
root.style.setProperty(
"--nodedc-glass-control-hover",
`linear-gradient(180deg, rgba(255, 255, 255, 0.075) 0%, rgba(255, 255, 255, 0.026) 100%), ${controlHover}`
);
root.style.setProperty("--nodedc-glass-control-shadow", `inset 0 1px 0 ${controlShadow}`);
root.style.setProperty("--nodedc-header-dropdown-bg", panelBgStrong);
root.style.setProperty("--nodedc-header-dropdown-item-bg", controlBg);
root.style.setProperty("--nodedc-header-dropdown-item-hover-bg", controlHover);
root.style.setProperty("--nodedc-header-dropdown-item-active-bg", s.toolbarBgActive);
root.style.setProperty("--nodedc-header-dropdown-item-active-color", readableTextColor(s.toolbarBgActive));
};
const applySettings = (s) => {
const root = document.documentElement;
root.style.setProperty("--bg-outer", s.bgOuter);
@ -137,6 +223,7 @@ const applySettings = (s) => {
root.style.setProperty("--modal-element", s.modalElement);
root.style.setProperty("--modal-element-outline", s.modalElementOutline);
root.style.setProperty("--modal-radius", s.modalRadius);
applyGlassSettings(root, s);
root.style.setProperty("--border", s.border);
root.style.setProperty("--text", s.text);
root.style.setProperty("--panel", s.panel);
@ -259,6 +346,10 @@ export function createSettingsMenu({ panel, controls = {}, onChange = null, onSh
header.appendChild(closeBtn);
panel.appendChild(header);
const body = document.createElement("div");
body.className = "settings-panel-body";
panel.appendChild(body);
const section = (title) => {
const box = document.createElement("div");
box.className = "settings-section";
@ -357,7 +448,7 @@ export function createSettingsMenu({ panel, controls = {}, onChange = null, onSh
addColor(font, "Фон холста низ", "canvasBottom");
addColor(font, "Фон загрузки верх", "loadingTop");
addColor(font, "Фон загрузки низ", "loadingBottom");
panel.appendChild(font);
body.appendChild(font);
const buttons = section("Кнопки");
addColor(buttons, "Плюс", "plus");
@ -374,26 +465,26 @@ export function createSettingsMenu({ panel, controls = {}, onChange = null, onSh
step: 1,
formatValue: (val) => `${Math.round(val)}%`,
});
panel.appendChild(buttons);
body.appendChild(buttons);
const projectButtons = section("Кнопки проектов");
addColor(projectButtons, "Проекты (заливка)", "projectBtnFill");
addColor(projectButtons, "Проекты (бордер)", "projectBtnBorder");
addColor(projectButtons, "Проекты (hover)", "projectBtnHover");
panel.appendChild(projectButtons);
body.appendChild(projectButtons);
const modalButtons = section("Кнопки модалок");
addColor(modalButtons, "Модальная primary", "modalBtnPrimary");
addColor(modalButtons, "Модальная secondary", "modalBtnSecondary");
addColor(modalButtons, "Лоудер", "loaderColor");
panel.appendChild(modalButtons);
body.appendChild(modalButtons);
const toolbar = section("Toolbar");
addColor(toolbar, "Фон", "toolbarBg");
addColor(toolbar, "Актив", "toolbarBgActive");
addColor(toolbar, "Бордер", "toolbarBorder");
addColor(toolbar, "Outline", "toolbarOutline");
panel.appendChild(toolbar);
body.appendChild(toolbar);
const navCube = section("NavCube");
addColor(navCube, "Грани", "navCubeBase");
@ -412,7 +503,7 @@ export function createSettingsMenu({ panel, controls = {}, onChange = null, onSh
setVal("navCubeEdgeAlpha", Math.max(0, Math.min(1, Number(val) / 100)));
},
});
panel.appendChild(navCube);
body.appendChild(navCube);
const sectionCut = section("Разрез / сечение");
addColor(sectionCut, "Ось X", "sectionAxisX");
@ -468,7 +559,7 @@ export function createSettingsMenu({ panel, controls = {}, onChange = null, onSh
setVal("sectionPlaneEdgeAlpha", Math.max(0, Math.min(1, Number(val) / 100)));
},
});
panel.appendChild(sectionCut);
body.appendChild(sectionCut);
const commentTargets = section("Таргеты комментариев");
addColor(commentTargets, "Активный: цвет", "commentTargetActiveColor");
@ -521,14 +612,14 @@ export function createSettingsMenu({ panel, controls = {}, onChange = null, onSh
setVal("commentTargetPassiveSize", Math.max(12, Math.min(48, Math.round(Number(val)))));
},
});
panel.appendChild(commentTargets);
body.appendChild(commentTargets);
const measure = section("Линейка");
addColor(measure, "Линия/бордер", "measureLine");
addColor(measure, "Плашка", "measureLabel");
addColor(measure, "Текст плашки", "measureLabelText");
addColor(measure, "Кружок", "measureDot");
panel.appendChild(measure);
body.appendChild(measure);
const viewport = section("Viewport");
const addCheck = (label, checked, handler) => {
@ -548,7 +639,7 @@ export function createSettingsMenu({ panel, controls = {}, onChange = null, onSh
if (controls.toggleEdges) addCheck("Рёбра", controls.toggleEdges.checked, (v) => { controls.toggleEdges.checked = v; controls.toggleEdges.dispatchEvent(new Event("change")); });
if (controls.toggleTransparent) addCheck("Прозрачный фон", controls.toggleTransparent.checked, (v) => { controls.toggleTransparent.checked = v; controls.toggleTransparent.dispatchEvent(new Event("change")); });
if (controls.addMode) addCheck("Добавлять в сцену", controls.addMode.checked, (v) => { controls.addMode.checked = v; });
panel.appendChild(viewport);
body.appendChild(viewport);
const modals = section("Модальные окна");
addColor(modals, "Основной фон", "modalBg");
@ -598,18 +689,18 @@ export function createSettingsMenu({ panel, controls = {}, onChange = null, onSh
setVal("modalRadius", val);
},
});
panel.appendChild(modals);
body.appendChild(modals);
const cube = section("Выделение объектов");
addColor(cube, "Подсветка объектов", "highlight");
panel.appendChild(cube);
body.appendChild(cube);
};
const show = () => {
render();
if (panel) {
panel.classList.remove("hidden");
panel.style.display = "grid";
panel.style.display = "flex";
}
onShow?.();
};

View File

@ -89,11 +89,15 @@ export function createTemplatesMenu({ panel, fetchProjects, openProject, onShow,
header.appendChild(actions);
panel.appendChild(header);
const body = document.createElement("div");
body.className = "templates-panel-body";
panel.appendChild(body);
if (state.loading) {
const loadingEl = document.createElement("div");
loadingEl.className = "templates-empty";
loadingEl.textContent = "Загрузка списка проектов...";
panel.appendChild(loadingEl);
body.appendChild(loadingEl);
return;
}
@ -102,11 +106,11 @@ export function createTemplatesMenu({ panel, fetchProjects, openProject, onShow,
errorEl.className = "templates-empty";
errorEl.style.color = "var(--error)";
errorEl.textContent = state.error;
panel.appendChild(errorEl);
body.appendChild(errorEl);
}
panel.appendChild(renderSection("Шаблоны", state.templates, "Нет шаблонов"));
panel.appendChild(renderSection("Мои проекты", state.projects, "Нет сохранённых проектов"));
body.appendChild(renderSection("Шаблоны", state.templates, "Нет шаблонов"));
body.appendChild(renderSection("Мои проекты", state.projects, "Нет сохранённых проектов"));
};
const load = async () => {
@ -137,7 +141,7 @@ export function createTemplatesMenu({ panel, fetchProjects, openProject, onShow,
render();
if (panel) {
panel.classList.remove("hidden");
panel.style.display = "grid";
panel.style.display = "flex";
}
load();
onShow?.();