feat: standardize Beam viewer environment settings

This commit is contained in:
CODEX 2026-06-05 17:04:11 +03:00
parent be2294d3fa
commit ef5b0a42a6
12 changed files with 556 additions and 194 deletions

View File

@ -6,8 +6,12 @@
--loading-top: #1c1c1c;
--loading-bottom: #1c1c1c;
--modal-bg: #1c1c1c;
--modal-bg-alpha: 100%;
--modal-focus: #292929;
--modal-border: #292929;
--modal-focus-alpha: 35%;
--modal-border: #1c1c1c;
--modal-element: #262626;
--modal-element-outline: #262626;
--modal-radius: 14px;
--panel: #1c1c1c;
--panel-2: #292929;
@ -21,27 +25,34 @@
--shadow: 0 18px 48px rgba(0, 0, 0, 0.35);
--panel-alpha: 1;
--panel-radius: 14px;
--toolbar-bg: #1c1c1c;
--toolbar-bg-active: #ffea00;
--toolbar-border: #233044;
--template-fill: #1c1c1c;
--template-outline: #233044;
--template-outline-hover: rgba(255, 234, 0, 0.7);
--toolbar-bg: #292929;
--toolbar-bg-active: #c4ff67;
--toolbar-border: #292929;
--toolbar-outline: #292929;
--template-fill: #292929;
--template-outline: #292929;
--template-outline-hover: #1c1c1c;
--project-btn-fill: #292929;
--project-btn-border: #292929;
--project-btn-hover: #1c1c1c;
--modal-btn-primary: #c4ff67;
--modal-btn-secondary: #292929;
--loader-color: #c4ff67;
--template-glow-strength: 80%;
--highlight-color: #ffea00;
--highlight-color: #c4ff67;
--cube-text: #242424;
--cube-edges: #e0e7ff;
--cube-base: #ffea00;
--cube-base: #ffffff;
--cube-hover: #ffffff;
--cube-edge-color: #e0e7ff;
--cube-edge-alpha: 0.6;
--plus-color: #ffea00;
--measure-line: #ffea00;
--measure-label: #ffea00;
--plus-color: #c4ff67;
--measure-line: #c4ff67;
--measure-label: #c4ff67;
--measure-label-text: #292929;
--measure-dot: #ffea00;
--nodedc-glass-control-bg: var(--modal-focus);
--nodedc-glass-control-hover: color-mix(in srgb, var(--modal-focus) 86%, white 8%);
--measure-dot: #c4ff67;
--nodedc-glass-control-bg: var(--modal-element);
--nodedc-glass-control-hover: color-mix(in srgb, var(--modal-element) 86%, white 8%);
--nodedc-glass-control-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.01);
--tb-size: 25px;
--tb-gap: 10px;
@ -341,6 +352,12 @@ header {
.side-panel {
position: fixed;
--env-control-h: 46px;
--env-button-radius: 17px;
--env-pill-radius: 999px;
--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: 170px;
right: 12px;
bottom: 12px;
@ -350,11 +367,11 @@ header {
background: color-mix(in srgb, var(--modal-bg) var(--modal-bg-alpha, 100%), transparent);
border: 1px solid var(--modal-border);
border-radius: var(--modal-radius, 16px);
padding: 14px 14px 10px;
padding: 12px;
box-shadow: var(--shadow);
display: flex;
flex-direction: column;
gap: 12px;
gap: 10px;
z-index: 11;
display: none;
}
@ -364,52 +381,60 @@ header {
}
.inspector-panel-header {
display: flex;
position: sticky;
top: -12px;
z-index: 5;
flex: 0 0 auto;
display: grid;
grid-template-columns: minmax(0, 1fr) var(--env-control-h);
align-items: center;
justify-content: space-between;
min-height: 28px;
margin: 0 0 8px;
gap: 10px;
height: calc(var(--env-control-h) + 20px);
min-height: var(--env-control-h);
margin: -12px -12px 0;
padding: 12px 12px 8px 16px;
background: color-mix(in srgb, var(--modal-bg) var(--modal-bg-alpha, 100%), transparent);
border-radius: var(--modal-radius, 16px) var(--modal-radius, 16px) 0 0;
color: var(--text);
font-weight: 700;
}
.inspector-panel-header h3 {
margin: 0;
min-width: 0;
font-size: 13px;
line-height: 1.2;
}
.inspector-collapse-btn {
position: relative;
display: grid;
place-items: center;
width: 28px;
height: 28px;
margin: -2px -2px 0 8px;
width: var(--env-control-h);
height: var(--env-control-h);
margin: 0;
padding: 0;
border: 0;
border-radius: 10px;
background: transparent;
color: var(--modal-btn-primary, var(--accent));
border-radius: var(--env-button-radius);
background: var(--env-glass-control-bg);
color: rgba(245, 245, 250, 0.78);
box-shadow: var(--env-glass-control-shadow);
cursor: pointer;
font: inherit;
font-size: 0;
font-weight: 900;
line-height: 1;
outline: none;
transition: background 140ms ease, transform 100ms ease;
backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
-webkit-backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
transition: background 140ms ease, color 140ms ease, transform 100ms ease;
}
.inspector-collapse-btn::before {
content: "";
position: absolute;
left: 50%;
top: 38%;
width: 11px;
height: 2px;
border-radius: 999px;
background: currentColor;
transform: translate(-50%, -50%);
.inspector-collapse-btn svg {
display: block;
width: 16px;
height: 16px;
}
.inspector-collapse-btn:hover {
background: var(--nodedc-glass-control-hover);
background: var(--env-glass-control-hover);
color: #fff;
}
.inspector-collapse-btn:active {
@ -502,7 +527,7 @@ header {
padding: 10px 12px;
border-radius: var(--modal-radius, 10px);
border: 1px solid var(--modal-border);
background: color-mix(in srgb, var(--modal-focus) var(--modal-focus-alpha, 100%), transparent);
background: var(--modal-element);
color: var(--text);
font-size: 14px;
outline: none;
@ -625,7 +650,7 @@ header {
}
.tree {
background: color-mix(in srgb, var(--modal-element) var(--modal-focus-alpha, 100%), transparent);
background: var(--modal-element);
border-radius: 10px;
border: 1px solid var(--modal-element-outline, var(--modal-element));
height: 260px;
@ -722,7 +747,7 @@ header {
vertical-align: -1px;
border: 0;
border-radius: 4px;
background: color-mix(in srgb, var(--modal-element) var(--modal-focus-alpha, 100%), transparent);
background: var(--modal-element);
box-shadow: inset 0 0 0 1px var(--modal-element-outline, var(--modal-element));
cursor: pointer;
transition:
@ -731,7 +756,7 @@ header {
}
.tree li > input[type="checkbox"]:checked {
background: var(--highlight-color, var(--accent));
background: var(--modal-btn-primary, var(--accent));
box-shadow: none;
}
@ -740,7 +765,7 @@ header {
}
.tree li > input[type="checkbox"]:checked:hover {
background: color-mix(in srgb, var(--highlight-color, var(--accent)) 86%, white 14%);
background: color-mix(in srgb, var(--modal-btn-primary, var(--accent)) 86%, white 14%);
}
.tree li > input[type="checkbox"]:focus,
@ -914,7 +939,7 @@ header {
}
.nodedc-dropdown-option.nodedc-select__option[data-selected="true"] {
background: var(--modal-focus);
background: var(--modal-element);
color: rgba(255, 255, 255, 0.96);
}
@ -935,12 +960,17 @@ header {
}
.templates-panel {
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);
left: var(--templates-left, 72px);
bottom: var(--templates-bottom, 12px);
background: color-mix(in srgb, var(--modal-bg) var(--modal-bg-alpha, 100%), transparent);
border: 1px solid var(--modal-border);
border-radius: var(--modal-radius, 14px);
padding: 10px;
padding: 12px;
display: grid;
gap: 10px;
width: var(--templates-width, 280px);
@ -957,28 +987,71 @@ header {
}
.templates-panel .templates-header {
display: flex;
position: sticky;
top: -12px;
z-index: 5;
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
justify-content: space-between;
padding: 6px 6px 0 6px;
gap: 10px;
height: calc(var(--env-control-h) + 20px);
min-height: var(--env-control-h);
margin: -12px -12px 0;
padding: 12px 12px 8px 16px;
border-radius: var(--modal-radius, 14px) var(--modal-radius, 14px) 0 0;
background: color-mix(in srgb, var(--modal-bg) var(--modal-bg-alpha, 100%), transparent);
color: var(--text);
font-weight: 700;
}
.templates-panel .templates-title {
font-size: 14px;
font-size: 13px;
line-height: 1.2;
}
.templates-panel .templates-actions {
display: flex;
gap: 6px;
}
.templates-panel .templates-close {
width: 26px;
height: 26px;
border-radius: 8px;
border: 1px solid var(--template-outline);
background: color-mix(in srgb, var(--template-fill) var(--modal-focus-alpha, 100%), transparent);
color: var(--text);
cursor: pointer;
display: grid;
place-items: center;
width: var(--env-control-h);
height: var(--env-control-h);
padding: 0;
border: 0;
border-radius: var(--env-button-radius);
background: var(--env-glass-control-bg);
color: rgba(245, 245, 250, 0.78);
box-shadow: var(--env-glass-control-shadow);
cursor: pointer;
line-height: 1;
outline: none;
backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
-webkit-backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
transition: background 140ms ease, color 140ms ease, transform 100ms ease;
}
.templates-panel .templates-close svg {
display: block;
width: 16px;
height: 16px;
}
.templates-panel .templates-close:hover {
background: var(--env-glass-control-hover);
color: #fff;
}
.templates-panel .templates-close:active {
transform: translateY(1px);
}
.templates-panel .templates-close:focus,
.templates-panel .templates-close:focus-visible {
outline: none;
box-shadow: none;
}
.templates-panel .template-list {
@ -1155,7 +1228,7 @@ header {
padding: 12px;
border-radius: var(--modal-radius, 10px);
border: 1px solid var(--modal-border);
background: color-mix(in srgb, var(--modal-focus) var(--modal-focus-alpha, 1), transparent);
background: var(--modal-element);
color: var(--text);
font-size: 14px;
outline: none;
@ -1220,14 +1293,21 @@ header {
}
.settings-panel .settings-header {
position: sticky;
top: -12px;
z-index: 5;
display: grid;
grid-template-columns: minmax(0, 1fr) var(--env-control-h);
align-items: center;
gap: 10px;
color: var(--text);
font-weight: 700;
height: calc(var(--env-control-h) + 20px);
min-height: var(--env-control-h);
padding: 0 0 0 4px;
margin: -12px -12px 0;
padding: 12px 12px 8px 16px;
background: color-mix(in srgb, var(--modal-bg) var(--modal-bg-alpha, 100%), transparent);
border-radius: var(--modal-radius, 14px) var(--modal-radius, 14px) 0 0;
}
.settings-panel .settings-title {
@ -1290,6 +1370,63 @@ header {
margin-bottom: 2px;
}
.settings-panel .checkbox-row {
display: grid;
grid-template-columns: minmax(0, 1fr) 26px;
align-items: center;
gap: 10px;
min-height: var(--env-control-h);
padding: 5px 7px 5px 18px;
border-radius: var(--env-pill-radius);
background: var(--env-glass-control-bg);
box-shadow: var(--env-glass-control-shadow);
cursor: pointer;
backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
-webkit-backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
}
.settings-panel .checkbox-row__label {
min-width: 0;
overflow: hidden;
color: rgba(255, 255, 255, 0.9);
font-size: 12px;
font-weight: 760;
line-height: 1;
text-overflow: ellipsis;
white-space: nowrap;
}
.settings-panel .checkbox-row input[type="checkbox"] {
appearance: none;
-webkit-appearance: none;
width: 26px;
height: 26px;
margin: 0;
border: 0;
border-radius: 999px;
background: var(--modal-bg);
cursor: pointer;
transition: background 140ms ease, transform 100ms ease;
}
.settings-panel .checkbox-row input[type="checkbox"]:checked {
background: var(--modal-btn-primary, var(--accent));
}
.settings-panel .checkbox-row:hover {
background: var(--env-glass-control-hover);
}
.settings-panel .checkbox-row input[type="checkbox"]:active {
transform: scale(0.94);
}
.settings-panel .checkbox-row input[type="checkbox"]:focus,
.settings-panel .checkbox-row input[type="checkbox"]:focus-visible {
outline: none;
box-shadow: none;
}
.settings-panel .color-row {
display: grid;
grid-template-columns: minmax(110px, 1fr) minmax(150px, 168px);
@ -1407,7 +1544,7 @@ header {
top: 50%;
z-index: 2;
font-size: 12px;
font-weight: 760;
font-weight: 500;
line-height: 1;
transform: translateY(-50%);
pointer-events: none;
@ -1659,13 +1796,47 @@ header {
.settings-panel input[type="number"] {
width: 100%;
max-width: 86px;
background: color-mix(in srgb, var(--modal-element) var(--modal-focus-alpha, 100%), transparent);
background: var(--modal-element);
border: 1px solid var(--modal-element-outline, var(--modal-element));
border-radius: 8px;
color: var(--text);
padding: 5px 6px;
}
.settings-panel input.env-number-field[type="number"] {
width: 100%;
max-width: none;
height: var(--env-control-h);
border: 0;
border-radius: var(--env-pill-radius);
padding: 0 18px;
background: var(--env-glass-control-bg);
box-shadow: var(--env-glass-control-shadow);
color: rgba(255, 255, 255, 0.86);
font-size: 12px;
font-weight: 500;
line-height: 1;
text-align: center;
outline: none;
appearance: textfield;
-moz-appearance: textfield;
backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
-webkit-backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
}
.settings-panel input.env-number-field[type="number"]::-webkit-outer-spin-button,
.settings-panel input.env-number-field[type="number"]::-webkit-inner-spin-button {
margin: 0;
appearance: none;
-webkit-appearance: none;
}
.settings-panel input.env-number-field[type="number"]:focus,
.settings-panel input.env-number-field[type="number"]:focus-visible {
border: 0;
box-shadow: var(--env-glass-control-shadow);
}
.settings-panel .env-color-field .env-color-field__value input[type="text"] {
width: 100%;
max-width: none;
@ -1689,7 +1860,7 @@ header {
}
.log {
background: color-mix(in srgb, var(--modal-element) var(--modal-focus-alpha, 100%), transparent);
background: var(--modal-element);
border: 1px solid var(--modal-element-outline, var(--modal-element));
}
@ -1810,7 +1981,7 @@ header {
.transform-grid input {
flex: 1;
background: color-mix(in srgb, var(--modal-element) var(--modal-focus-alpha, 100%), transparent);
background: var(--modal-element);
border: 1px solid var(--modal-element-outline, var(--modal-element));
border-radius: 8px;
padding: 6px 8px;

View File

@ -3,8 +3,8 @@ const SDK_URL = "./lib/dc-camera-context.es.js";
import { createLogo } from "./src/ui/logo.js";
import { toolbarConfig, applyToolbarVars } from "./src/ui/toolbar.js";
import { createTemplatesMenu } from "./src/ui/templatesMenu.js";
import { createSettingsMenu } from "./src/ui/settingsMenu.js?v=2";
import { createTemplatesMenu } from "./src/ui/templatesMenu.js?v=2";
import { createSettingsMenu } from "./src/ui/settingsMenu.js?v=5";
import { createMeasurementModal } from "./src/ui/measurementModal.js";
import { createGlassSelect } from "./src/ui/glassSelect.js";
import { createSliderControl } from "./src/ui/controls/slider.js";

View File

@ -52,7 +52,9 @@
set("--toolbar-border", s.toolbarBorder);
set("--toolbar-outline", s.toolbarOutline);
set("--modal-bg", s.modalBg);
set("--modal-bg-alpha", s.modalBgAlpha !== undefined ? `${s.modalBgAlpha * 100}%` : undefined);
set("--modal-focus", s.modalFocus);
set("--modal-focus-alpha", s.modalFocusAlpha !== undefined ? `${s.modalFocusAlpha * 100}%` : undefined);
set("--modal-border", s.modalBorder);
set("--modal-element", s.modalElement);
set("--modal-element-outline", s.modalElementOutline);
@ -87,7 +89,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=12">
<link rel="stylesheet" href="./dcViewer.css?v=16">
</head>
<body>
<header>
@ -125,7 +127,6 @@
</section>
<aside class="side-panel" id="viewerInspector">
<div class="panel-section">
<div class="inspector-panel-header">
<h3>Инспектор</h3>
<button
@ -134,8 +135,13 @@
type="button"
title="Свернуть инспектор"
aria-label="Свернуть инспектор"
>▁</button>
>
<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">
<path d="M4.25 4.25 11.75 11.75M11.75 4.25 4.25 11.75" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path>
</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">
@ -251,6 +257,6 @@
<input id="fileInput" class="hidden" type="file"
accept=".xkt,.glb,.gltf,.bim,.las,.laz,.obj,.stl">
<script type="module" src="./dcViewer.js?v=17"></script>
<script type="module" src="./dcViewer.js?v=21"></script>
</body>
</html>

View File

@ -2,7 +2,7 @@ import { createSliderControl } from "./controls/slider.js";
import { createColorControl } from "./controls/color.js";
const defaultSettings = {
themeVersion: "v4-beam-lime",
themeVersion: "v5-drone-lime",
bgOuter: "#0f0f0f",
canvasTop: "#1c1c1c",
canvasBottom: "#1c1c1c",
@ -10,27 +10,27 @@ const defaultSettings = {
loadingBottom: "#1c1c1c",
accent: "#ffea00",
accent2: "#ffea00",
templateFill: "#1c1c1c",
templateOutline: "#233044",
templateOutlineHover: "rgba(255,234,0,0.7)",
projectBtnFill: "#1c1c1c",
projectBtnBorder: "#233044",
projectBtnHover: "rgba(255,234,0,0.7)",
templateFill: "#292929",
templateOutline: "#292929",
templateOutlineHover: "#1c1c1c",
projectBtnFill: "#292929",
projectBtnBorder: "#292929",
projectBtnHover: "#1c1c1c",
modalBtnPrimary: "#c4ff67",
modalBtnSecondary: "#292929",
loaderColor: "#c4ff67",
templateGlow: 80,
toolbarBg: "#1c1c1c",
toolbarBg: "#292929",
toolbarBgActive: "#c4ff67",
toolbarBorder: "#233044",
toolbarOutline: "#c4ff67",
toolbarBorder: "#292929",
toolbarOutline: "#292929",
modalBg: "#1c1c1c",
modalBgAlpha: 1,
modalFocus: "#292929",
modalFocusAlpha: 1,
modalBorder: "#292929",
modalElement: "#292929",
modalElementOutline: "#2b2b2b",
modalFocusAlpha: 0.35,
modalBorder: "#1c1c1c",
modalElement: "#262626",
modalElementOutline: "#262626",
modalRadius: "14px",
border: "#233044",
text: "#e6edf3",
@ -255,6 +255,40 @@ export function createSettingsMenu({ panel, controls = {}, onChange = null, onSh
return slider;
};
const addNumber = ({
parent,
label,
value,
min = 0,
max,
step = 1,
onChange,
}) => {
const row = document.createElement("div");
row.className = "color-row";
const lbl = document.createElement("div");
lbl.textContent = label;
const input = document.createElement("input");
input.className = "env-number-field";
input.type = "number";
input.value = value;
input.min = min;
input.step = step;
if (max !== undefined) {
input.max = max;
}
input.addEventListener("input", () => {
const nextValue = Number(input.value);
if (Number.isFinite(nextValue)) {
onChange?.(nextValue);
}
});
row.appendChild(lbl);
row.appendChild(input);
parent.appendChild(row);
return input;
};
const font = section("Фон");
addColor(font, "Фон внешний", "bgOuter");
addColor(font, "Фон холста верх", "canvasTop");
@ -327,16 +361,17 @@ export function createSettingsMenu({ panel, controls = {}, onChange = null, onSh
const viewport = section("Viewport");
const addCheck = (label, checked, handler) => {
const row = document.createElement("div");
const row = document.createElement("label");
row.className = "checkbox-row";
const text = document.createElement("span");
text.className = "checkbox-row__label";
text.textContent = label;
const chk = document.createElement("input");
chk.type = "checkbox";
chk.checked = checked;
chk.addEventListener("change", () => handler(chk.checked));
const lbl = document.createElement("label");
lbl.textContent = label;
row.appendChild(text);
row.appendChild(chk);
row.appendChild(lbl);
viewport.appendChild(row);
};
if (controls.toggleEdges) addCheck("Рёбра", controls.toggleEdges.checked, (v) => { controls.toggleEdges.checked = v; controls.toggleEdges.dispatchEvent(new Event("change")); });
@ -378,24 +413,20 @@ export function createSettingsMenu({ panel, controls = {}, onChange = null, onSh
});
addColor(modals, "Аутлайн", "modalBorder");
addColor(modals, "Фон элемента", "modalElement");
addColor(modals, "Контролы", "modalElement");
addColor(modals, "Аутлайн элемента", "modalElementOutline");
const radiusRow = document.createElement("div");
radiusRow.className = "color-row";
const radiusLbl = document.createElement("label");
radiusLbl.textContent = "Радиус";
const radiusInput = document.createElement("input");
radiusInput.type = "number";
radiusInput.value = parseInt(getComputedStyle(document.documentElement).getPropertyValue("--modal-radius")) || 14;
radiusInput.addEventListener("change", () => {
const val = `${radiusInput.value}px`;
document.documentElement.style.setProperty("--modal-radius", val);
addNumber({
parent: modals,
label: "Радиус",
value: parseInt(getComputedStyle(document.documentElement).getPropertyValue("--modal-radius")) || 14,
min: 0,
max: 48,
step: 1,
onChange: (nextValue) => {
const val = `${Math.max(0, Math.min(48, Math.round(nextValue)))}px`;
setVal("modalRadius", val);
},
});
radiusRow.appendChild(radiusLbl);
radiusRow.appendChild(radiusInput);
radiusRow.appendChild(document.createElement("div"));
modals.appendChild(radiusRow);
panel.appendChild(modals);
const cube = section("Выделение объектов");

View File

@ -62,16 +62,27 @@ export function createTemplatesMenu({ panel, fetchProjects, openProject, onShow,
header.className = "templates-header";
header.innerHTML = `<span class="templates-title">Проекты и шаблоны</span>`;
const actions = document.createElement("div");
actions.style.display = "flex";
actions.style.gap = "6px";
actions.className = "templates-actions";
const refresh = document.createElement("button");
refresh.className = "templates-close";
refresh.type = "button";
refresh.title = "Обновить список";
refresh.textContent = "↻";
refresh.setAttribute("aria-label", "Обновить список");
refresh.innerHTML = `
<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">
<path d="M12.2 5.3A4.4 4.4 0 0 0 4.7 3.4L3.6 4.5M3.7 2.1v2.4h2.4M3.8 10.7a4.4 4.4 0 0 0 7.5 1.9l1.1-1.1M12.3 13.9v-2.4H9.9" fill="none" stroke="currentColor" stroke-width="1.35" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
`;
refresh.addEventListener("click", () => load());
const closeBtn = document.createElement("button");
closeBtn.className = "templates-close";
closeBtn.textContent = "×";
closeBtn.type = "button";
closeBtn.setAttribute("aria-label", "Закрыть проекты и шаблоны");
closeBtn.innerHTML = `
<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">
<path d="M4.25 4.25 11.75 11.75M11.75 4.25 4.25 11.75" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"></path>
</svg>
`;
closeBtn.addEventListener("click", hide);
actions.appendChild(refresh);
actions.appendChild(closeBtn);

View File

@ -1,7 +1,7 @@
// Базовые цвета/настройки проекта. При очистке localStorage эти значения остаются.
// Чтобы изменить палитру по умолчанию — правь этот объект и закоммить изменения.
const PROJECT_THEME = {
themeVersion: "v4-beam-lime",
themeVersion: "v5-drone-lime",
bgOuter: "#0f0f0f",
canvasTop: "#1c1c1c",
canvasBottom: "#1c1c1c",
@ -9,25 +9,25 @@ const PROJECT_THEME = {
loadingBottom: "#1c1c1c",
accent: "#ffea00",
accent2: "#ffea00",
templateFill: "#1c1c1c",
templateOutline: "#233044",
templateOutlineHover: "rgba(255,234,0,0.7)",
templateFill: "#292929",
templateOutline: "#292929",
templateOutlineHover: "#1c1c1c",
templateGlow: 80,
toolbarBg: "#1c1c1c",
toolbarBgActive: "#ffea00",
toolbarBorder: "#233044",
toolbarOutline: "#ffea00",
toolbarBg: "#292929",
toolbarBgActive: "#c4ff67",
toolbarBorder: "#292929",
toolbarOutline: "#292929",
modalBg: "#1c1c1c",
modalBgAlpha: 1,
modalFocus: "#292929",
modalFocusAlpha: 1,
modalBorder: "#292929",
modalElement: "#292929",
modalElementOutline: "#2b2b2b",
modalFocusAlpha: 0.35,
modalBorder: "#1c1c1c",
modalElement: "#262626",
modalElementOutline: "#262626",
modalRadius: "14px",
projectBtnFill: "#1c1c1c",
projectBtnBorder: "#233044",
projectBtnHover: "rgba(255,234,0,0.7)",
projectBtnFill: "#292929",
projectBtnBorder: "#292929",
projectBtnHover: "#1c1c1c",
modalBtnPrimary: "#c4ff67",
modalBtnSecondary: "#292929",
loaderColor: "#c4ff67",

View File

@ -1,4 +1,12 @@
[
{
"id": "proj_0a284bb5-1c82-48b1-a636-e204578a570a",
"name": "DRONE",
"type": "project",
"ownerId": null,
"createdAt": "2026-06-05T13:33:23.556Z",
"updatedAt": "2026-06-05T13:33:23.556Z"
},
{
"id": "proj_0c1b26f3-020c-4ee9-926c-fedf08003304",
"name": "TrackEnsemble_6074_v11",

View File

@ -0,0 +1,135 @@
{
"id": "proj_0a284bb5-1c82-48b1-a636-e204578a570a",
"name": "DRONE",
"type": "project",
"createdAt": "2026-06-05T13:33:23.556Z",
"updatedAt": "2026-06-05T13:33:23.556Z",
"ownerId": null,
"shares": [],
"models": [
{
"id": "model",
"type": "xkt",
"meta": {
"label": "Final Assembly.STEP",
"metadataPath": null,
"settingsSrc": "uploads/nodedc_c2f0a08f-3f98-446a-94e9-c7b91ee95611_32e82d59-f15f-49b1-ba36-de2f8cc9ebc2/Final_Assembly.STEP",
"sourceSrc": "uploads/nodedc_c2f0a08f-3f98-446a-94e9-c7b91ee95611_32e82d59-f15f-49b1-ba36-de2f8cc9ebc2/Final_Assembly.STEP",
"artifactSrc": "uploads/nodedc_c2f0a08f-3f98-446a-94e9-c7b91ee95611_32e82d59-f15f-49b1-ba36-de2f8cc9ebc2/Final_Assembly.xkt"
},
"visible": true,
"src": "http://localhost:8080/uploads/nodedc_c2f0a08f-3f98-446a-94e9-c7b91ee95611_32e82d59-f15f-49b1-ba36-de2f8cc9ebc2/Final_Assembly.xkt?v=2026-06-04T18%3A09%3A13.204700%2B00%3A00"
}
],
"viewerState": {
"camera": {
"eye": [
0.00010965801538165287,
0.010159958762113674,
1.2885417934859191
],
"look": [
0.00010965801538165287,
0.010159958762113674,
-0.4406459172923821
],
"up": [
0,
1,
0
],
"projection": "perspective"
},
"transparent": true,
"edges": true,
"addMode": true,
"displayMode": "white",
"lightingMode": "standard",
"navigationAxis": "auto",
"zoomSpeed": 52,
"selection": {
"ids": [],
"modelId": null
},
"models": [
{
"id": "model",
"visible": true
}
],
"transforms": {
"model": {
"__model__": {
"position": [
0,
0,
0
],
"rotation": [
0,
0,
0
],
"scale": [
1,
1,
1
]
}
}
}
},
"design": {
"themeVersion": "v5-drone-lime",
"bgOuter": "#0f0f0f",
"canvasTop": "#1c1c1c",
"canvasBottom": "#1c1c1c",
"loadingTop": "#1c1c1c",
"loadingBottom": "#1c1c1c",
"accent": "#ffea00",
"accent2": "#ffea00",
"templateFill": "#292929",
"templateOutline": "#292929",
"templateOutlineHover": "#1c1c1c",
"projectBtnFill": "#292929",
"projectBtnBorder": "#292929",
"projectBtnHover": "#1c1c1c",
"modalBtnPrimary": "#c4ff67",
"modalBtnSecondary": "#292929",
"loaderColor": "#c4ff67",
"templateGlow": 80,
"toolbarBg": "#292929",
"toolbarBgActive": "#c4ff67",
"toolbarBorder": "#292929",
"toolbarOutline": "#292929",
"modalBg": "#1c1c1c",
"modalBgAlpha": 1,
"modalFocus": "#292929",
"modalFocusAlpha": 0.35,
"modalBorder": "#1c1c1c",
"modalElement": "#262626",
"modalElementOutline": "#262626",
"modalRadius": "14px",
"border": "#233044",
"text": "#e6edf3",
"panel": "#1c1c1c",
"panel2": "#292929",
"highlight": "#c4ff67",
"cubeText": "#242424",
"cubeEdges": "#e0e7ff",
"plus": "#c4ff67",
"navCubeBase": "#ffffff",
"navCubeHover": "#ffffff",
"navCubeText": "#242424",
"navCubeEdge": "#e0e7ff",
"navCubeEdgeAlpha": 0.6,
"measureLine": "#c4ff67",
"measureLabel": "#c4ff67",
"measureLabelText": "#292929",
"measureDot": "#c4ff67"
},
"meta": {
"description": "",
"tags": []
}
}

View File

@ -78,7 +78,7 @@
}
},
"design": {
"themeVersion": "v4-beam-lime",
"themeVersion": "v5-drone-lime",
"bgOuter": "#0f0f0f",
"canvasTop": "#1c1c1c",
"canvasBottom": "#1c1c1c",
@ -86,27 +86,27 @@
"loadingBottom": "#1c1c1c",
"accent": "#ffea00",
"accent2": "#ffea00",
"templateFill": "#1c1c1c",
"templateOutline": "#233044",
"templateOutlineHover": "rgba(255,234,0,0.7)",
"projectBtnFill": "#1c1c1c",
"projectBtnBorder": "#233044",
"projectBtnHover": "rgba(255,234,0,0.7)",
"templateFill": "#292929",
"templateOutline": "#292929",
"templateOutlineHover": "#1c1c1c",
"projectBtnFill": "#292929",
"projectBtnBorder": "#292929",
"projectBtnHover": "#1c1c1c",
"modalBtnPrimary": "#c4ff67",
"modalBtnSecondary": "#292929",
"loaderColor": "#c4ff67",
"templateGlow": 80,
"toolbarBg": "#1c1c1c",
"toolbarBg": "#292929",
"toolbarBgActive": "#c4ff67",
"toolbarBorder": "#233044",
"toolbarOutline": "#c4ff67",
"toolbarBorder": "#292929",
"toolbarOutline": "#292929",
"modalBg": "#1c1c1c",
"modalBgAlpha": 1,
"modalFocus": "#292929",
"modalFocusAlpha": 1,
"modalBorder": "#292929",
"modalElement": "#292929",
"modalElementOutline": "#2b2b2b",
"modalFocusAlpha": 0.35,
"modalBorder": "#1c1c1c",
"modalElement": "#262626",
"modalElementOutline": "#262626",
"modalRadius": "14px",
"border": "#233044",
"text": "#e6edf3",

View File

@ -802,7 +802,7 @@
}
},
"design": {
"themeVersion": "v4-beam-lime",
"themeVersion": "v5-drone-lime",
"bgOuter": "#0f0f0f",
"canvasTop": "#1c1c1c",
"canvasBottom": "#1c1c1c",
@ -810,27 +810,27 @@
"loadingBottom": "#1c1c1c",
"accent": "#ffea00",
"accent2": "#ffea00",
"templateFill": "#1c1c1c",
"templateOutline": "#233044",
"templateOutlineHover": "rgba(255,234,0,0.7)",
"projectBtnFill": "#1c1c1c",
"projectBtnBorder": "#233044",
"projectBtnHover": "rgba(255,234,0,0.7)",
"templateFill": "#292929",
"templateOutline": "#292929",
"templateOutlineHover": "#1c1c1c",
"projectBtnFill": "#292929",
"projectBtnBorder": "#292929",
"projectBtnHover": "#1c1c1c",
"modalBtnPrimary": "#c4ff67",
"modalBtnSecondary": "#292929",
"loaderColor": "#c4ff67",
"templateGlow": 80,
"toolbarBg": "#1c1c1c",
"toolbarBg": "#292929",
"toolbarBgActive": "#c4ff67",
"toolbarBorder": "#233044",
"toolbarOutline": "#c4ff67",
"toolbarBorder": "#292929",
"toolbarOutline": "#292929",
"modalBg": "#1c1c1c",
"modalBgAlpha": 1,
"modalFocus": "#292929",
"modalFocusAlpha": 1,
"modalBorder": "#292929",
"modalElement": "#292929",
"modalElementOutline": "#2b2b2b",
"modalFocusAlpha": 0.35,
"modalBorder": "#1c1c1c",
"modalElement": "#262626",
"modalElementOutline": "#262626",
"modalRadius": "14px",
"border": "#233044",
"text": "#e6edf3",

View File

@ -73,7 +73,7 @@
}
},
"design": {
"themeVersion": "v4-beam-lime",
"themeVersion": "v5-drone-lime",
"bgOuter": "#0f0f0f",
"canvasTop": "#1c1c1c",
"canvasBottom": "#1c1c1c",
@ -81,27 +81,27 @@
"loadingBottom": "#1c1c1c",
"accent": "#ffea00",
"accent2": "#ffea00",
"templateFill": "#1c1c1c",
"templateOutline": "#233044",
"templateOutlineHover": "rgba(255,234,0,0.7)",
"projectBtnFill": "#1c1c1c",
"projectBtnBorder": "#233044",
"projectBtnHover": "rgba(255,234,0,0.7)",
"templateFill": "#292929",
"templateOutline": "#292929",
"templateOutlineHover": "#1c1c1c",
"projectBtnFill": "#292929",
"projectBtnBorder": "#292929",
"projectBtnHover": "#1c1c1c",
"modalBtnPrimary": "#c4ff67",
"modalBtnSecondary": "#292929",
"loaderColor": "#c4ff67",
"templateGlow": 80,
"toolbarBg": "#1c1c1c",
"toolbarBg": "#292929",
"toolbarBgActive": "#c4ff67",
"toolbarBorder": "#233044",
"toolbarOutline": "#c4ff67",
"toolbarBorder": "#292929",
"toolbarOutline": "#292929",
"modalBg": "#1c1c1c",
"modalBgAlpha": 1,
"modalFocus": "#292929",
"modalFocusAlpha": 1,
"modalBorder": "#292929",
"modalElement": "#292929",
"modalElementOutline": "#2b2b2b",
"modalFocusAlpha": 0.35,
"modalBorder": "#1c1c1c",
"modalElement": "#262626",
"modalElementOutline": "#262626",
"modalRadius": "14px",
"border": "#233044",
"text": "#e6edf3",

View File

@ -92,7 +92,7 @@
}
},
"design": {
"themeVersion": "v4-beam-lime",
"themeVersion": "v5-drone-lime",
"bgOuter": "#0f0f0f",
"canvasTop": "#1c1c1c",
"canvasBottom": "#1c1c1c",
@ -100,27 +100,27 @@
"loadingBottom": "#1c1c1c",
"accent": "#ffea00",
"accent2": "#ffea00",
"templateFill": "#1c1c1c",
"templateOutline": "#233044",
"templateOutlineHover": "rgba(255,234,0,0.7)",
"projectBtnFill": "#1c1c1c",
"projectBtnBorder": "#233044",
"projectBtnHover": "rgba(255,234,0,0.7)",
"templateFill": "#292929",
"templateOutline": "#292929",
"templateOutlineHover": "#1c1c1c",
"projectBtnFill": "#292929",
"projectBtnBorder": "#292929",
"projectBtnHover": "#1c1c1c",
"modalBtnPrimary": "#c4ff67",
"modalBtnSecondary": "#292929",
"loaderColor": "#c4ff67",
"templateGlow": 80,
"toolbarBg": "#1c1c1c",
"toolbarBg": "#292929",
"toolbarBgActive": "#c4ff67",
"toolbarBorder": "#233044",
"toolbarOutline": "#c4ff67",
"toolbarBorder": "#292929",
"toolbarOutline": "#292929",
"modalBg": "#1c1c1c",
"modalBgAlpha": 1,
"modalFocus": "#292929",
"modalFocusAlpha": 1,
"modalBorder": "#292929",
"modalElement": "#292929",
"modalElementOutline": "#2b2b2b",
"modalFocusAlpha": 0.35,
"modalBorder": "#1c1c1c",
"modalElement": "#262626",
"modalElementOutline": "#262626",
"modalRadius": "14px",
"border": "#233044",
"text": "#e6edf3",