Refine BIM glass theming and panel scroll layout
This commit is contained in:
parent
3584359833
commit
ad3a4f3877
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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,68 +250,70 @@
|
|||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="panel-section inspector-main-section">
|
||||
<div id="treeContainer" class="tree"></div>
|
||||
<div class="inspector-controls">
|
||||
<div class="inspector-control-row inspector-control-row--half" id="displayModeControl">
|
||||
<div class="inspector-control-label">Режим отображения</div>
|
||||
<div class="inspector-select-host" id="displayModeSelect"></div>
|
||||
<div class="inspector-panel-body">
|
||||
<div class="panel-section inspector-main-section">
|
||||
<div id="treeContainer" class="tree"></div>
|
||||
<div class="inspector-controls">
|
||||
<div class="inspector-control-row inspector-control-row--half" id="displayModeControl">
|
||||
<div class="inspector-control-label">Режим отображения</div>
|
||||
<div class="inspector-select-host" id="displayModeSelect"></div>
|
||||
</div>
|
||||
<div class="inspector-control-row inspector-control-row--half" id="lightingModeControl">
|
||||
<div class="inspector-control-label">Свет</div>
|
||||
<div class="inspector-select-host" id="lightingModeSelect"></div>
|
||||
</div>
|
||||
<div class="inspector-control-row inspector-control-row--full" id="navigationAxisControl">
|
||||
<div class="inspector-control-label">Ось навигации</div>
|
||||
<div class="inspector-select-host" id="navigationAxisSelect"></div>
|
||||
</div>
|
||||
<div class="inspector-control-row inspector-control-row--full inspector-control-row--slider" id="zoomSpeedControl"></div>
|
||||
</div>
|
||||
<div class="inspector-control-row inspector-control-row--half" id="lightingModeControl">
|
||||
<div class="inspector-control-label">Свет</div>
|
||||
<div class="inspector-select-host" id="lightingModeSelect"></div>
|
||||
</div>
|
||||
<div class="panel-section" id="projectNameSection">
|
||||
<h3>Название проекта</h3>
|
||||
<input type="text" id="projectNameInput" class="project-name-input" placeholder="Проект" autocomplete="off">
|
||||
<div class="error-text inline-error" id="projectNameError"></div>
|
||||
</div>
|
||||
<div class="panel-section">
|
||||
<h3>Глобальные координаты</h3>
|
||||
<div class="muted" style="margin-bottom:6px;">Центр выделения (мировые)</div>
|
||||
<div class="transform-grid">
|
||||
<label>X <input type="text" id="globalX"></label>
|
||||
<label>Y <input type="text" id="globalY"></label>
|
||||
<label>Z <input type="text" id="globalZ"></label>
|
||||
</div>
|
||||
<div class="inspector-control-row inspector-control-row--full" id="navigationAxisControl">
|
||||
<div class="inspector-control-label">Ось навигации</div>
|
||||
<div class="inspector-select-host" id="navigationAxisSelect"></div>
|
||||
</div>
|
||||
<div class="panel-section">
|
||||
<h3>Трансформации</h3>
|
||||
<div class="muted" style="margin-bottom:6px;">Перемещение</div>
|
||||
<div class="transform-grid">
|
||||
<label>X <input type="text" id="posX"></label>
|
||||
<label>Y <input type="text" id="posY"></label>
|
||||
<label>Z <input type="text" id="posZ"></label>
|
||||
</div>
|
||||
<div class="inspector-control-row inspector-control-row--full inspector-control-row--slider" id="zoomSpeedControl"></div>
|
||||
<div class="muted" style="margin:8px 0 6px;">Вращение (deg)</div>
|
||||
<div class="transform-grid">
|
||||
<label>X <input type="text" id="rotX"></label>
|
||||
<label>Y <input type="text" id="rotY"></label>
|
||||
<label>Z <input type="text" id="rotZ"></label>
|
||||
</div>
|
||||
<div class="muted" style="margin:8px 0 6px;">Масштаб</div>
|
||||
<div class="transform-grid">
|
||||
<label>X <input type="text" id="scaleX"></label>
|
||||
<label>Y <input type="text" id="scaleY"></label>
|
||||
<label>Z <input type="text" id="scaleZ"></label>
|
||||
</div>
|
||||
<small id="transformHint" class="muted">Выберите объект в инспекторе или сцене, чтобы редактировать.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-section" id="projectNameSection">
|
||||
<h3>Название проекта</h3>
|
||||
<input type="text" id="projectNameInput" class="project-name-input" placeholder="Проект" autocomplete="off">
|
||||
<div class="error-text inline-error" id="projectNameError"></div>
|
||||
</div>
|
||||
<div class="panel-section">
|
||||
<h3>Глобальные координаты</h3>
|
||||
<div class="muted" style="margin-bottom:6px;">Центр выделения (мировые)</div>
|
||||
<div class="transform-grid">
|
||||
<label>X <input type="text" id="globalX"></label>
|
||||
<label>Y <input type="text" id="globalY"></label>
|
||||
<label>Z <input type="text" id="globalZ"></label>
|
||||
<div class="panel-section">
|
||||
<h3>Журнал</h3>
|
||||
<div id="logList" class="log"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-section">
|
||||
<h3>Трансформации</h3>
|
||||
<div class="muted" style="margin-bottom:6px;">Перемещение</div>
|
||||
<div class="transform-grid">
|
||||
<label>X <input type="text" id="posX"></label>
|
||||
<label>Y <input type="text" id="posY"></label>
|
||||
<label>Z <input type="text" id="posZ"></label>
|
||||
<div class="panel-section" id="saveProjectSection">
|
||||
<button class="secondary-btn model-settings-btn" id="saveModelSettingsButton" type="button">Сохранить настройки</button>
|
||||
<button class="primary-btn" id="saveProjectButton" type="button">Сохранить проект</button>
|
||||
<button class="secondary-btn danger-btn" id="deleteProjectButton" type="button">Удалить проект</button>
|
||||
</div>
|
||||
<div class="muted" style="margin:8px 0 6px;">Вращение (deg)</div>
|
||||
<div class="transform-grid">
|
||||
<label>X <input type="text" id="rotX"></label>
|
||||
<label>Y <input type="text" id="rotY"></label>
|
||||
<label>Z <input type="text" id="rotZ"></label>
|
||||
</div>
|
||||
<div class="muted" style="margin:8px 0 6px;">Масштаб</div>
|
||||
<div class="transform-grid">
|
||||
<label>X <input type="text" id="scaleX"></label>
|
||||
<label>Y <input type="text" id="scaleY"></label>
|
||||
<label>Z <input type="text" id="scaleZ"></label>
|
||||
</div>
|
||||
<small id="transformHint" class="muted">Выберите объект в инспекторе или сцене, чтобы редактировать.</small>
|
||||
</div>
|
||||
<div class="panel-section">
|
||||
<h3>Журнал</h3>
|
||||
<div id="logList" class="log"></div>
|
||||
</div>
|
||||
<div class="panel-section" id="saveProjectSection">
|
||||
<button class="secondary-btn model-settings-btn" id="saveModelSettingsButton" type="button">Сохранить настройки</button>
|
||||
<button class="primary-btn" id="saveProjectButton" type="button">Сохранить проект</button>
|
||||
<button class="secondary-btn danger-btn" id="deleteProjectButton" type="button">Удалить проект</button>
|
||||
</div>
|
||||
</aside>
|
||||
<div class="inspector-restore-tray" id="inspectorRestoreTray" hidden>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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?.();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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?.();
|
||||
|
|
|
|||
Loading…
Reference in New Issue