Polish BIM shared viewer controls
This commit is contained in:
parent
fcfa59ec98
commit
f680d6bad2
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
|
|
@ -0,0 +1,13 @@
|
||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<style>
|
||||||
|
.icon-fav-01 { fill: #333334; } /* Тёмный цвет для светлого таб-бара */
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.icon-fav-01 { fill: #FEFEFE; } /* Светлый цвет для тёмного таб-бара */
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
.icon-fav-01 { fill: #000000; } /* Тёмный цвет для светлого таб-бара (для явности) */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<path class="icon-fav-01" d="M17.8738 15.9159L16.0002 18.9718L14.1267 15.9159H17.8738ZM23.6307 12.7864H8.36914L15.9999 25.2308L23.6307 12.7864Z"/>
|
||||||
|
<path class="icon-fav-01" d="M10.9793 19.4872L6.67269 11.5918H25.5344L21.2281 19.4872H25.0581L31.2614 8H0.738281L7.16244 19.4872H10.9793Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 852 B |
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"theme_color": "#eeeff4",
|
||||||
|
"background_color": "#eeeff4",
|
||||||
|
"display": "browser",
|
||||||
|
"scope": "/",
|
||||||
|
"start_url": "/",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "icon-adaptive.svg",
|
||||||
|
"type": "image/svg+xml",
|
||||||
|
"sizes": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "icon-192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "icon-512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "apple-touch-icon.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "180x180"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -1183,6 +1183,74 @@ header {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inspector-color-host {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inspector-color-host .env-color-field {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
height: 40px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 5px 10px 5px 7px;
|
||||||
|
border-radius: 14px;
|
||||||
|
background: var(--nodedc-glass-control-bg);
|
||||||
|
box-shadow: var(--nodedc-glass-control-shadow);
|
||||||
|
backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
|
||||||
|
-webkit-backdrop-filter: blur(24px) saturate(128%) brightness(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inspector-color-host .env-color-field__swatch {
|
||||||
|
position: relative;
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
min-height: 26px;
|
||||||
|
flex: 0 0 26px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 999px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inspector-color-host .env-color-field__preview {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inspector-color-host .env-color-field__swatch input[type="color"] {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
opacity: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inspector-color-host .env-color-field__value {
|
||||||
|
height: 32px;
|
||||||
|
min-height: 32px;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inspector-color-host .env-color-field__value input {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 0;
|
||||||
|
padding: 0 2px;
|
||||||
|
background: transparent;
|
||||||
|
color: rgba(255, 255, 255, 0.66);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 760;
|
||||||
|
letter-spacing: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.nodedc-select {
|
.nodedc-select {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
@ -3102,10 +3170,11 @@ header {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
body[data-viewer-mode="guest"] .bottom-toolbar,
|
body[data-viewer-mode="guest"] .tb-btn:not([data-action="measure"]):not([data-action="section"]):not([data-action="projection"]),
|
||||||
body[data-viewer-mode="guest"] .section-toolbar,
|
body[data-viewer-mode="guest"] #settingsPanel,
|
||||||
body[data-viewer-mode="guest"] .section-overview,
|
body[data-viewer-mode="guest"] #templatesPanel,
|
||||||
body[data-viewer-mode="guest"] .inspector-restore-tray {
|
body[data-viewer-mode="guest"] #projectNameSection,
|
||||||
|
body[data-viewer-mode="guest"] #saveProjectSection {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@ const SDK_VERSION = "2.6.78"; // соответствует package.json
|
||||||
const SDK_URL = "./lib/dc-camera-context.es.js?v=3";
|
const SDK_URL = "./lib/dc-camera-context.es.js?v=3";
|
||||||
|
|
||||||
import { createLogo } from "./src/ui/logo.js";
|
import { createLogo } from "./src/ui/logo.js";
|
||||||
import { applyToolbarVars } from "./src/ui/toolbar.js?v=2";
|
import { applyToolbarVars } from "./src/ui/toolbar.js?v=3";
|
||||||
import { createTemplatesMenu } from "./src/ui/templatesMenu.js?v=3";
|
import { createTemplatesMenu } from "./src/ui/templatesMenu.js?v=3";
|
||||||
import { createSettingsMenu } from "./src/ui/settingsMenu.js?v=12";
|
import { createSettingsMenu } from "./src/ui/settingsMenu.js?v=14";
|
||||||
import { createMeasurementModal } from "./src/ui/measurementModal.js";
|
import { createMeasurementModal } from "./src/ui/measurementModal.js";
|
||||||
import { createGlassSelect } from "./src/ui/glassSelect.js";
|
import { createGlassSelect } from "./src/ui/glassSelect.js";
|
||||||
|
import { createColorControl } from "./src/ui/controls/color.js";
|
||||||
import { createSliderControl } from "./src/ui/controls/slider.js";
|
import { createSliderControl } from "./src/ui/controls/slider.js";
|
||||||
import "./theme.js?v=2";
|
import "./theme.js?v=3";
|
||||||
import { TransformGizmo } from "./transformGizmo.js";
|
import { TransformGizmo } from "./transformGizmo.js";
|
||||||
import { createObjectModal } from "./src/ui/objectModal.js";
|
import { createObjectModal } from "./src/ui/objectModal.js";
|
||||||
|
|
||||||
|
|
@ -73,6 +74,20 @@ const commentThreadList = document.getElementById("commentThreadList");
|
||||||
const commentReplyInput = document.getElementById("commentReplyInput");
|
const commentReplyInput = document.getElementById("commentReplyInput");
|
||||||
const commentReplyAttachButton = document.getElementById("commentReplyAttachButton");
|
const commentReplyAttachButton = document.getElementById("commentReplyAttachButton");
|
||||||
const commentReplyAttachmentInput = document.getElementById("commentReplyAttachmentInput");
|
const commentReplyAttachmentInput = document.getElementById("commentReplyAttachmentInput");
|
||||||
|
|
||||||
|
const DEFAULT_LAUNCHER_URL = "https://hub.nodedc.ru/";
|
||||||
|
|
||||||
|
const getLauncherUrl = () => {
|
||||||
|
const configured = typeof window !== "undefined" ? window.__BIMDC_LAUNCHER_URL__ : "";
|
||||||
|
const raw = typeof configured === "string" && configured.trim()
|
||||||
|
? configured.trim()
|
||||||
|
: DEFAULT_LAUNCHER_URL;
|
||||||
|
try {
|
||||||
|
return new URL(raw, window.location.href).toString();
|
||||||
|
} catch {
|
||||||
|
return DEFAULT_LAUNCHER_URL;
|
||||||
|
}
|
||||||
|
};
|
||||||
const commentReplyAttachmentGrid = document.getElementById("commentReplyAttachmentGrid");
|
const commentReplyAttachmentGrid = document.getElementById("commentReplyAttachmentGrid");
|
||||||
const commentModalError = document.getElementById("commentModalError");
|
const commentModalError = document.getElementById("commentModalError");
|
||||||
const commentCancelButton = document.getElementById("commentCancelButton");
|
const commentCancelButton = document.getElementById("commentCancelButton");
|
||||||
|
|
@ -82,6 +97,9 @@ const commentApplyInlineButton = document.getElementById("commentApplyInlineButt
|
||||||
const treeContainer = document.getElementById("treeContainer");
|
const treeContainer = document.getElementById("treeContainer");
|
||||||
const displayModeControl = document.getElementById("displayModeControl");
|
const displayModeControl = document.getElementById("displayModeControl");
|
||||||
const displayModeSelectHost = document.getElementById("displayModeSelect");
|
const displayModeSelectHost = document.getElementById("displayModeSelect");
|
||||||
|
const customDisplayColorRow = document.getElementById("customDisplayColorRow");
|
||||||
|
const customDisplayColorControlHost = document.getElementById("customDisplayColorControl");
|
||||||
|
const customDisplaySaturationControl = document.getElementById("customDisplaySaturationControl");
|
||||||
const lightingModeControl = document.getElementById("lightingModeControl");
|
const lightingModeControl = document.getElementById("lightingModeControl");
|
||||||
const lightingModeSelectHost = document.getElementById("lightingModeSelect");
|
const lightingModeSelectHost = document.getElementById("lightingModeSelect");
|
||||||
const navigationAxisControl = document.getElementById("navigationAxisControl");
|
const navigationAxisControl = document.getElementById("navigationAxisControl");
|
||||||
|
|
@ -214,11 +232,14 @@ const DISPLAY_MODE_OPTIONS = [
|
||||||
{ value: "source", label: "Исходный" },
|
{ value: "source", label: "Исходный" },
|
||||||
{ value: "clay", label: "Clay" },
|
{ value: "clay", label: "Clay" },
|
||||||
{ value: "white", label: "Белый" },
|
{ value: "white", label: "Белый" },
|
||||||
{ value: "contrast", label: "Контраст" },
|
{ value: "custom", label: "Кастом" },
|
||||||
{ value: "edges", label: "Рёбра" },
|
{ value: "edges", label: "Рёбра" },
|
||||||
{ value: "xray", label: "Рентген" },
|
{ value: "xray", label: "Рентген" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const DEFAULT_CUSTOM_DISPLAY_COLOR = "#ffffff";
|
||||||
|
const DEFAULT_CUSTOM_DISPLAY_SATURATION = 100;
|
||||||
|
|
||||||
const NAVIGATION_AXIS_OPTIONS = [
|
const NAVIGATION_AXIS_OPTIONS = [
|
||||||
{ value: "auto", label: "Авто" },
|
{ value: "auto", label: "Авто" },
|
||||||
{ value: "x-up", label: "X" },
|
{ value: "x-up", label: "X" },
|
||||||
|
|
@ -229,7 +250,6 @@ const NAVIGATION_AXIS_OPTIONS = [
|
||||||
const LIGHTING_MODE_OPTIONS = [
|
const LIGHTING_MODE_OPTIONS = [
|
||||||
{ value: "standard", label: "Стандарт" },
|
{ value: "standard", label: "Стандарт" },
|
||||||
{ value: "soft", label: "Мягкий" },
|
{ value: "soft", label: "Мягкий" },
|
||||||
{ value: "ambient", label: "Без солнца" },
|
|
||||||
{ value: "technical", label: "Тех." },
|
{ value: "technical", label: "Тех." },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -253,6 +273,7 @@ const NAVIGATION_AXIS_PRESETS = {
|
||||||
|
|
||||||
let templatesMenu = null;
|
let templatesMenu = null;
|
||||||
let settingsMenu = null;
|
let settingsMenu = null;
|
||||||
|
let pendingDesignState = null;
|
||||||
let measurementModal = null;
|
let measurementModal = null;
|
||||||
|
|
||||||
applyPanelOffsets();
|
applyPanelOffsets();
|
||||||
|
|
@ -265,6 +286,7 @@ const activeModels = [];
|
||||||
let viewerMode = "standard";
|
let viewerMode = "standard";
|
||||||
let runtimeSessionInfo = { authRequired: false, authenticated: false, canShare: true, user: null };
|
let runtimeSessionInfo = { authRequired: false, authenticated: false, canShare: true, user: null };
|
||||||
let startupSharePayload = null;
|
let startupSharePayload = null;
|
||||||
|
const GUEST_TOOLBAR_ACTIONS = new Set(["measure", "section", "projection"]);
|
||||||
const memoryStore = new Map(); // url -> ArrayBuffer
|
const memoryStore = new Map(); // url -> ArrayBuffer
|
||||||
const blobUrls = [];
|
const blobUrls = [];
|
||||||
let gizmo = null;
|
let gizmo = null;
|
||||||
|
|
@ -273,11 +295,15 @@ const modelTypes = {}; // modelId -> normalized type
|
||||||
const sceneModels = {}; // modelId -> SceneModel
|
const sceneModels = {}; // modelId -> SceneModel
|
||||||
const pointCloudColorSnapshots = new Map(); // modelId -> [{ layer, colors, workingColors }]
|
const pointCloudColorSnapshots = new Map(); // modelId -> [{ layer, colors, workingColors }]
|
||||||
let activeDisplayMode = "source";
|
let activeDisplayMode = "source";
|
||||||
let activeLightingMode = "ambient";
|
let activeLightingMode = "technical";
|
||||||
let activeNavigationAxis = "auto";
|
let activeNavigationAxis = "auto";
|
||||||
let activeZoomSpeed = 20;
|
let activeZoomSpeed = 20;
|
||||||
|
let activeCustomDisplayColor = DEFAULT_CUSTOM_DISPLAY_COLOR;
|
||||||
|
let activeCustomDisplaySaturation = DEFAULT_CUSTOM_DISPLAY_SATURATION;
|
||||||
let inspectorCollapsed = false;
|
let inspectorCollapsed = false;
|
||||||
let displayModeSelect = null;
|
let displayModeSelect = null;
|
||||||
|
let customDisplayColorControl = null;
|
||||||
|
let customDisplaySaturationSlider = null;
|
||||||
let lightingModeSelect = null;
|
let lightingModeSelect = null;
|
||||||
let navigationAxisSelect = null;
|
let navigationAxisSelect = null;
|
||||||
let zoomSpeedSlider = null;
|
let zoomSpeedSlider = null;
|
||||||
|
|
@ -344,6 +370,43 @@ const normalizeZoomSpeed = (value) => {
|
||||||
return Math.min(100, Math.max(5, Math.round(numeric)));
|
return Math.min(100, Math.max(5, Math.round(numeric)));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isHexColor = (value) => /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(String(value || "").trim());
|
||||||
|
|
||||||
|
const normalizeCustomDisplayColor = (value) => {
|
||||||
|
const raw = String(value || "").trim();
|
||||||
|
if (!isHexColor(raw)) return DEFAULT_CUSTOM_DISPLAY_COLOR;
|
||||||
|
if (raw.length === 4) {
|
||||||
|
const [, r, g, b] = raw;
|
||||||
|
return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();
|
||||||
|
}
|
||||||
|
return raw.toLowerCase();
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeCustomDisplaySaturation = (value) => {
|
||||||
|
const numeric = Number(value);
|
||||||
|
if (!Number.isFinite(numeric)) return DEFAULT_CUSTOM_DISPLAY_SATURATION;
|
||||||
|
return Math.min(200, Math.max(0, Math.round(numeric)));
|
||||||
|
};
|
||||||
|
|
||||||
|
const customDisplayHexToRgb01 = (value) => {
|
||||||
|
const hex = normalizeCustomDisplayColor(value).slice(1);
|
||||||
|
return [
|
||||||
|
Number.parseInt(hex.slice(0, 2), 16) / 255,
|
||||||
|
Number.parseInt(hex.slice(2, 4), 16) / 255,
|
||||||
|
Number.parseInt(hex.slice(4, 6), 16) / 255,
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyRgbSaturation = (rgb, saturation) => {
|
||||||
|
const factor = normalizeCustomDisplaySaturation(saturation) / 100;
|
||||||
|
const gray = (rgb[0] * 0.2126) + (rgb[1] * 0.7152) + (rgb[2] * 0.0722);
|
||||||
|
return rgb.map((channel) => Math.min(1, Math.max(0, gray + ((channel - gray) * factor))));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCustomDisplayColorize = () => (
|
||||||
|
applyRgbSaturation(customDisplayHexToRgb01(activeCustomDisplayColor), activeCustomDisplaySaturation)
|
||||||
|
);
|
||||||
|
|
||||||
const configureCameraPanMap = (control) => {
|
const configureCameraPanMap = (control) => {
|
||||||
const input = viewer?.scene?.input;
|
const input = viewer?.scene?.input;
|
||||||
if (!control || !input) return;
|
if (!control || !input) return;
|
||||||
|
|
@ -439,16 +502,36 @@ const normalizeDisplayMode = (mode) => (
|
||||||
);
|
);
|
||||||
|
|
||||||
const normalizeLightingMode = (mode) => (
|
const normalizeLightingMode = (mode) => (
|
||||||
LIGHTING_MODE_OPTIONS.some((option) => option.value === mode) ? mode : "ambient"
|
LIGHTING_MODE_OPTIONS.some((option) => option.value === mode) ? mode : "technical"
|
||||||
);
|
);
|
||||||
|
|
||||||
const normalizeNavigationAxis = (axis) => (
|
const normalizeNavigationAxis = (axis) => (
|
||||||
NAVIGATION_AXIS_OPTIONS.some((option) => option.value === axis) ? axis : "auto"
|
NAVIGATION_AXIS_OPTIONS.some((option) => option.value === axis) ? axis : "auto"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const setCustomDisplaySettings = ({ color, saturation } = {}) => {
|
||||||
|
if (color !== undefined) {
|
||||||
|
activeCustomDisplayColor = normalizeCustomDisplayColor(color);
|
||||||
|
customDisplayColorControl?.setValue?.(activeCustomDisplayColor);
|
||||||
|
}
|
||||||
|
if (saturation !== undefined) {
|
||||||
|
activeCustomDisplaySaturation = normalizeCustomDisplaySaturation(saturation);
|
||||||
|
customDisplaySaturationSlider?.setValue?.(activeCustomDisplaySaturation);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCustomDisplayControlsState = () => {
|
||||||
|
const isCustom = normalizeDisplayMode(activeDisplayMode) === "custom";
|
||||||
|
customDisplayColorRow?.classList.toggle("hidden", !isCustom);
|
||||||
|
customDisplaySaturationControl?.classList.toggle("hidden", !isCustom);
|
||||||
|
customDisplayColorControl?.setValue?.(activeCustomDisplayColor);
|
||||||
|
customDisplaySaturationSlider?.setValue?.(activeCustomDisplaySaturation);
|
||||||
|
};
|
||||||
|
|
||||||
const setDisplayModeControlState = () => {
|
const setDisplayModeControlState = () => {
|
||||||
activeDisplayMode = normalizeDisplayMode(activeDisplayMode);
|
activeDisplayMode = normalizeDisplayMode(activeDisplayMode);
|
||||||
displayModeSelect?.setValue(activeDisplayMode);
|
displayModeSelect?.setValue(activeDisplayMode);
|
||||||
|
setCustomDisplayControlsState();
|
||||||
};
|
};
|
||||||
|
|
||||||
const setLightingModeControlState = () => {
|
const setLightingModeControlState = () => {
|
||||||
|
|
@ -519,6 +602,7 @@ const getPrimaryModelEntry = () => (
|
||||||
const hasModelSettingsTarget = () => !!getModelSettingsSrc(getPrimaryModelEntry());
|
const hasModelSettingsTarget = () => !!getModelSettingsSrc(getPrimaryModelEntry());
|
||||||
|
|
||||||
const isGuestMode = () => viewerMode === "guest";
|
const isGuestMode = () => viewerMode === "guest";
|
||||||
|
const isGuestToolbarAction = (action) => GUEST_TOOLBAR_ACTIONS.has(action);
|
||||||
|
|
||||||
const getShareTokenFromPath = () => {
|
const getShareTokenFromPath = () => {
|
||||||
const match = window.location.pathname.match(/^\/share\/([^/?#]+)/);
|
const match = window.location.pathname.match(/^\/share\/([^/?#]+)/);
|
||||||
|
|
@ -1609,15 +1693,34 @@ const bindFooterButtonAction = (button, handler) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const applyRuntimeMode = () => {
|
const applyRuntimeMode = () => {
|
||||||
|
const guest = isGuestMode();
|
||||||
document.body.dataset.viewerMode = viewerMode;
|
document.body.dataset.viewerMode = viewerMode;
|
||||||
if (bottomToolbar) {
|
if (bottomToolbar) {
|
||||||
bottomToolbar.classList.toggle("hidden", isGuestMode());
|
bottomToolbar.classList.remove("hidden");
|
||||||
|
bottomToolbar.querySelectorAll(".tb-btn").forEach((btn) => {
|
||||||
|
const action = btn.dataset.action || "";
|
||||||
|
const guestHidden = guest && !isGuestToolbarAction(action);
|
||||||
|
btn.classList.toggle("hidden", guestHidden);
|
||||||
|
if (guestHidden) {
|
||||||
|
btn.disabled = true;
|
||||||
|
} else if (!guest && action !== "section" && action !== "comments" && action !== "share") {
|
||||||
|
btn.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
if (settingsButton) settingsButton.disabled = guest;
|
||||||
|
if (templatesButton) templatesButton.disabled = guest;
|
||||||
|
if (measureButton && guest) measureButton.disabled = !measurePlugin || !measureControl || activeModels.length === 0;
|
||||||
|
if (projectionToggle && guest) projectionToggle.disabled = activeModels.length === 0;
|
||||||
if (commentsButton) {
|
if (commentsButton) {
|
||||||
commentsButton.classList.toggle("hidden", isGuestMode());
|
commentsButton.classList.toggle("hidden", guest);
|
||||||
commentsButton.disabled = isGuestMode() || activeModels.length === 0;
|
commentsButton.disabled = guest || activeModels.length === 0;
|
||||||
}
|
}
|
||||||
if (isGuestMode()) {
|
if (guest) {
|
||||||
|
closeSettingsPanel();
|
||||||
|
closeTemplatesPanel();
|
||||||
|
settingsPanel?.classList.add("hidden");
|
||||||
|
templatesPanel?.classList.add("hidden");
|
||||||
commentsModeActive = false;
|
commentsModeActive = false;
|
||||||
commentsCollapsed = false;
|
commentsCollapsed = false;
|
||||||
commentContextTarget = null;
|
commentContextTarget = null;
|
||||||
|
|
@ -1630,15 +1733,12 @@ const applyRuntimeMode = () => {
|
||||||
stopCommentTargetLoop();
|
stopCommentTargetLoop();
|
||||||
}
|
}
|
||||||
if (shareButton) {
|
if (shareButton) {
|
||||||
const canShowShare = !isGuestMode() && runtimeSessionInfo.canShare && activeModels.length > 0;
|
const canShowShare = !guest && runtimeSessionInfo.canShare && activeModels.length > 0;
|
||||||
shareButton.classList.toggle("hidden", !canShowShare);
|
shareButton.classList.toggle("hidden", !canShowShare);
|
||||||
shareButton.disabled = !canShowShare;
|
shareButton.disabled = !canShowShare;
|
||||||
}
|
}
|
||||||
if (dropZone) {
|
if (dropZone) {
|
||||||
dropZone.style.pointerEvents = isGuestMode() ? "none" : "";
|
dropZone.style.pointerEvents = guest ? "none" : "";
|
||||||
}
|
|
||||||
if (sectionToolbar && isGuestMode()) {
|
|
||||||
sectionToolbar.classList.add("hidden");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1646,6 +1746,7 @@ const initializeRuntimeMode = async () => {
|
||||||
const shareToken = getShareTokenFromPath();
|
const shareToken = getShareTokenFromPath();
|
||||||
if (shareToken) {
|
if (shareToken) {
|
||||||
viewerMode = "guest";
|
viewerMode = "guest";
|
||||||
|
inspectorCollapsed = true;
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${SHARES_API}/${encodeURIComponent(shareToken)}`, {
|
const response = await fetch(`${SHARES_API}/${encodeURIComponent(shareToken)}`, {
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
|
|
@ -1755,11 +1856,11 @@ const copyShareLink = async () => {
|
||||||
const getDisplayModePreset = (mode) => {
|
const getDisplayModePreset = (mode) => {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case "clay":
|
case "clay":
|
||||||
return { colorize: [0.86, 0.86, 0.82], opacity: 1, edges: false, xrayed: false };
|
return { colorize: [0.76, 0.35, 0.25], opacity: 1, edges: false, xrayed: false };
|
||||||
case "white":
|
case "white":
|
||||||
return { colorize: [1, 1, 1], opacity: 1, edges: false, xrayed: false };
|
return { colorize: [1, 1, 1], opacity: 1, edges: false, xrayed: false };
|
||||||
case "contrast":
|
case "custom":
|
||||||
return { colorize: [0.78, 0.82, 1], opacity: 1, edges: false, xrayed: false };
|
return { colorize: getCustomDisplayColorize(), opacity: 1, edges: false, xrayed: false };
|
||||||
case "edges":
|
case "edges":
|
||||||
return { colorize: null, opacity: 1, edges: true, xrayed: false };
|
return { colorize: null, opacity: 1, edges: true, xrayed: false };
|
||||||
case "xray":
|
case "xray":
|
||||||
|
|
@ -1960,6 +2061,7 @@ const applyLightingMode = ({ announce = false } = {}) => {
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "technical":
|
case "technical":
|
||||||
|
default:
|
||||||
createSceneLight("ambient", { color: [1.0, 1.0, 1.0], intensity: 0.62 });
|
createSceneLight("ambient", { color: [1.0, 1.0, 1.0], intensity: 0.62 });
|
||||||
createSceneLight("dir", {
|
createSceneLight("dir", {
|
||||||
dir: [0.9, -0.45, -0.35],
|
dir: [0.9, -0.45, -0.35],
|
||||||
|
|
@ -1981,10 +2083,6 @@ const applyLightingMode = ({ announce = false } = {}) => {
|
||||||
scene.sao.numSamples = 20;
|
scene.sao.numSamples = 20;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "ambient":
|
|
||||||
default:
|
|
||||||
createSceneLight("ambient", { color: [1.0, 1.0, 1.0], intensity: 0.92 });
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scene.glRedraw?.();
|
scene.glRedraw?.();
|
||||||
|
|
@ -2706,7 +2804,7 @@ const positionSectionToolbar = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const setSectionModeActive = (active) => {
|
const setSectionModeActive = (active) => {
|
||||||
sectionModeActive = !!active && !!sectionPlanesPlugin && getSectionPlaneIds().length > 0 && !isGuestMode();
|
sectionModeActive = !!active && !!sectionPlanesPlugin && getSectionPlaneIds().length > 0;
|
||||||
if (sectionModeActive) {
|
if (sectionModeActive) {
|
||||||
positionSectionToolbar();
|
positionSectionToolbar();
|
||||||
}
|
}
|
||||||
|
|
@ -2721,7 +2819,7 @@ const setSectionModeActive = (active) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateSectionControls = () => {
|
const updateSectionControls = () => {
|
||||||
const available = !!sectionPlanesPlugin && !isGuestMode() && activeModels.length > 0;
|
const available = !!sectionPlanesPlugin && activeModels.length > 0;
|
||||||
if (sectionButton) {
|
if (sectionButton) {
|
||||||
sectionButton.disabled = !available;
|
sectionButton.disabled = !available;
|
||||||
sectionButton.classList.toggle("disabled", !available);
|
sectionButton.classList.toggle("disabled", !available);
|
||||||
|
|
@ -3386,6 +3484,7 @@ const parseInputNumber = (val) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const persistTransform = (modelId, objectId, transform) => {
|
const persistTransform = (modelId, objectId, transform) => {
|
||||||
|
if (isGuestMode()) return;
|
||||||
if (!modelId || !transform) return;
|
if (!modelId || !transform) return;
|
||||||
if (!transformStore[modelId]) transformStore[modelId] = {};
|
if (!transformStore[modelId]) transformStore[modelId] = {};
|
||||||
const key = objectId || "__model__";
|
const key = objectId || "__model__";
|
||||||
|
|
@ -4299,6 +4398,8 @@ const buildProjectSnapshot = async (name) => {
|
||||||
edges: !!toggleEdges?.checked,
|
edges: !!toggleEdges?.checked,
|
||||||
addMode: !!addMode?.checked,
|
addMode: !!addMode?.checked,
|
||||||
displayMode: activeDisplayMode,
|
displayMode: activeDisplayMode,
|
||||||
|
customDisplayColor: activeCustomDisplayColor,
|
||||||
|
customDisplaySaturation: activeCustomDisplaySaturation,
|
||||||
lightingMode: activeLightingMode,
|
lightingMode: activeLightingMode,
|
||||||
navigationAxis: activeNavigationAxis,
|
navigationAxis: activeNavigationAxis,
|
||||||
zoomSpeed: activeZoomSpeed,
|
zoomSpeed: activeZoomSpeed,
|
||||||
|
|
@ -4329,7 +4430,11 @@ const buildProjectSnapshot = async (name) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const applyDesignState = (design) => {
|
const applyDesignState = (design) => {
|
||||||
if (!design || !settingsMenu?.applyState) return;
|
if (!design || typeof design !== "object") return;
|
||||||
|
if (!settingsMenu?.applyState) {
|
||||||
|
pendingDesignState = { ...design };
|
||||||
|
return;
|
||||||
|
}
|
||||||
settingsMenu.applyState(design);
|
settingsMenu.applyState(design);
|
||||||
applyToolbarVars(settingsMenu?.getState?.());
|
applyToolbarVars(settingsMenu?.getState?.());
|
||||||
updateToolbarDockMode();
|
updateToolbarDockMode();
|
||||||
|
|
@ -4364,6 +4469,8 @@ const buildModelViewerSettings = () => ({
|
||||||
transparent: !!toggleTransparent?.checked,
|
transparent: !!toggleTransparent?.checked,
|
||||||
edges: !!toggleEdges?.checked,
|
edges: !!toggleEdges?.checked,
|
||||||
displayMode: activeDisplayMode,
|
displayMode: activeDisplayMode,
|
||||||
|
customDisplayColor: activeCustomDisplayColor,
|
||||||
|
customDisplaySaturation: activeCustomDisplaySaturation,
|
||||||
lightingMode: activeLightingMode,
|
lightingMode: activeLightingMode,
|
||||||
navigationAxis: activeNavigationAxis,
|
navigationAxis: activeNavigationAxis,
|
||||||
zoomSpeed: activeZoomSpeed
|
zoomSpeed: activeZoomSpeed
|
||||||
|
|
@ -4394,9 +4501,18 @@ const applyModelViewerSettings = (settings, { applyCamera = true, applyDesign =
|
||||||
toggleEdges.checked = viewerState.edges;
|
toggleEdges.checked = viewerState.edges;
|
||||||
suppressEdgeReload = false;
|
suppressEdgeReload = false;
|
||||||
}
|
}
|
||||||
|
setCustomDisplaySettings({
|
||||||
|
color: viewerState.customDisplayColor,
|
||||||
|
saturation: viewerState.customDisplaySaturation,
|
||||||
|
});
|
||||||
if (typeof viewerState.displayMode === "string") {
|
if (typeof viewerState.displayMode === "string") {
|
||||||
activeDisplayMode = viewerState.displayMode;
|
activeDisplayMode = viewerState.displayMode;
|
||||||
applyDisplayMode();
|
applyDisplayMode();
|
||||||
|
} else if (viewerState.customDisplayColor !== undefined || viewerState.customDisplaySaturation !== undefined) {
|
||||||
|
setCustomDisplayControlsState();
|
||||||
|
if (normalizeDisplayMode(activeDisplayMode) === "custom") {
|
||||||
|
applyDisplayMode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (typeof viewerState.lightingMode === "string") {
|
if (typeof viewerState.lightingMode === "string") {
|
||||||
activeLightingMode = viewerState.lightingMode;
|
activeLightingMode = viewerState.lightingMode;
|
||||||
|
|
@ -4491,9 +4607,18 @@ const applyViewerState = (state) => {
|
||||||
if (typeof state.addMode === "boolean" && addMode) {
|
if (typeof state.addMode === "boolean" && addMode) {
|
||||||
addMode.checked = state.addMode;
|
addMode.checked = state.addMode;
|
||||||
}
|
}
|
||||||
|
setCustomDisplaySettings({
|
||||||
|
color: state.customDisplayColor,
|
||||||
|
saturation: state.customDisplaySaturation,
|
||||||
|
});
|
||||||
if (typeof state.displayMode === "string") {
|
if (typeof state.displayMode === "string") {
|
||||||
activeDisplayMode = state.displayMode;
|
activeDisplayMode = state.displayMode;
|
||||||
applyDisplayMode();
|
applyDisplayMode();
|
||||||
|
} else if (state.customDisplayColor !== undefined || state.customDisplaySaturation !== undefined) {
|
||||||
|
setCustomDisplayControlsState();
|
||||||
|
if (normalizeDisplayMode(activeDisplayMode) === "custom") {
|
||||||
|
applyDisplayMode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (typeof state.lightingMode === "string") {
|
if (typeof state.lightingMode === "string") {
|
||||||
activeLightingMode = state.lightingMode;
|
activeLightingMode = state.lightingMode;
|
||||||
|
|
@ -4558,6 +4683,8 @@ const initViewer = async () => {
|
||||||
if (headerEl && homeButton && !homeButton.querySelector(".ue-logo")) {
|
if (headerEl && homeButton && !homeButton.querySelector(".ue-logo")) {
|
||||||
const logoEl = createLogo();
|
const logoEl = createLogo();
|
||||||
homeButton.appendChild(logoEl);
|
homeButton.appendChild(logoEl);
|
||||||
|
homeButton.title = "Открыть NODE.DC Hub";
|
||||||
|
homeButton.setAttribute("aria-label", "Открыть NODE.DC Hub");
|
||||||
if (logoEl.style.width) homeButton.style.width = logoEl.style.width;
|
if (logoEl.style.width) homeButton.style.width = logoEl.style.width;
|
||||||
if (logoEl.style.height) homeButton.style.height = logoEl.style.height;
|
if (logoEl.style.height) homeButton.style.height = logoEl.style.height;
|
||||||
homeButton.style.border = "none";
|
homeButton.style.border = "none";
|
||||||
|
|
@ -5192,8 +5319,15 @@ const initViewer = async () => {
|
||||||
activeDisplayMode = params.get("displayMode") || "source";
|
activeDisplayMode = params.get("displayMode") || "source";
|
||||||
setDisplayModeControlState();
|
setDisplayModeControlState();
|
||||||
}
|
}
|
||||||
|
setCustomDisplaySettings({
|
||||||
|
color: params.has("customDisplayColor") ? params.get("customDisplayColor") : undefined,
|
||||||
|
saturation: params.has("customDisplaySaturation") ? params.get("customDisplaySaturation") : undefined,
|
||||||
|
});
|
||||||
|
if ((params.has("customDisplayColor") || params.has("customDisplaySaturation")) && normalizeDisplayMode(activeDisplayMode) === "custom") {
|
||||||
|
applyDisplayMode();
|
||||||
|
}
|
||||||
if (params.has("lightingMode") || params.has("light")) {
|
if (params.has("lightingMode") || params.has("light")) {
|
||||||
activeLightingMode = params.get("lightingMode") || params.get("light") || "ambient";
|
activeLightingMode = params.get("lightingMode") || params.get("light") || "technical";
|
||||||
applyLightingMode();
|
applyLightingMode();
|
||||||
}
|
}
|
||||||
if (params.has("navigationAxis") || params.has("axis")) {
|
if (params.has("navigationAxis") || params.has("axis")) {
|
||||||
|
|
@ -5582,6 +5716,11 @@ const loadProjectSnapshot = async (project) => {
|
||||||
setToolbarButtonActive(settingsButton, false);
|
setToolbarButtonActive(settingsButton, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (pendingDesignState) {
|
||||||
|
const design = pendingDesignState;
|
||||||
|
pendingDesignState = null;
|
||||||
|
applyDesignState(design);
|
||||||
|
}
|
||||||
// ensure persisted settings are applied to cube/highlight on first load
|
// ensure persisted settings are applied to cube/highlight on first load
|
||||||
applyToolbarVars(settingsMenu?.getState?.());
|
applyToolbarVars(settingsMenu?.getState?.());
|
||||||
updateToolbarDockMode();
|
updateToolbarDockMode();
|
||||||
|
|
@ -5603,6 +5742,40 @@ const loadProjectSnapshot = async (project) => {
|
||||||
});
|
});
|
||||||
setDisplayModeControlState();
|
setDisplayModeControlState();
|
||||||
}
|
}
|
||||||
|
if (customDisplayColorControlHost) {
|
||||||
|
customDisplayColorControl = createColorControl({
|
||||||
|
value: activeCustomDisplayColor,
|
||||||
|
ariaLabel: "Цвет кастомного режима",
|
||||||
|
valueLabel: "HEX цвет кастомного режима",
|
||||||
|
onChange: (value) => {
|
||||||
|
setCustomDisplaySettings({ color: value });
|
||||||
|
if (normalizeDisplayMode(activeDisplayMode) === "custom") {
|
||||||
|
applyDisplayMode();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
customDisplayColorControlHost.replaceChildren(customDisplayColorControl);
|
||||||
|
}
|
||||||
|
if (customDisplaySaturationControl) {
|
||||||
|
customDisplaySaturationSlider = createSliderControl({
|
||||||
|
label: "Насыщенность",
|
||||||
|
value: activeCustomDisplaySaturation,
|
||||||
|
min: 0,
|
||||||
|
max: 200,
|
||||||
|
step: 1,
|
||||||
|
className: "inspector-slider",
|
||||||
|
showValue: true,
|
||||||
|
formatValue: (value) => `${Math.round(value)}%`,
|
||||||
|
onChange: (value) => {
|
||||||
|
setCustomDisplaySettings({ saturation: value });
|
||||||
|
if (normalizeDisplayMode(activeDisplayMode) === "custom") {
|
||||||
|
applyDisplayMode();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
customDisplaySaturationControl.replaceChildren(customDisplaySaturationSlider);
|
||||||
|
}
|
||||||
|
setCustomDisplayControlsState();
|
||||||
if (lightingModeControl && lightingModeSelectHost) {
|
if (lightingModeControl && lightingModeSelectHost) {
|
||||||
lightingModeSelect = createGlassSelect({
|
lightingModeSelect = createGlassSelect({
|
||||||
host: lightingModeSelectHost,
|
host: lightingModeSelectHost,
|
||||||
|
|
@ -5846,23 +6019,7 @@ const loadProjectSnapshot = async (project) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
homeButton.addEventListener("click", () => {
|
homeButton.addEventListener("click", () => {
|
||||||
destroyAllModels();
|
window.location.assign(getLauncherUrl());
|
||||||
dropZone.classList.remove("hidden");
|
|
||||||
setStatus("Готов к загрузке");
|
|
||||||
modelCounter = 0;
|
|
||||||
setLoading(false);
|
|
||||||
memoryStore.clear();
|
|
||||||
currentSelection = null;
|
|
||||||
updateTransformInputs(null);
|
|
||||||
templatesMenu?.hide();
|
|
||||||
setMeasureActive(false);
|
|
||||||
clearMeasurements();
|
|
||||||
closeSaveModal();
|
|
||||||
closeDeleteModal();
|
|
||||||
// Примеры доступны, но сцена чистая
|
|
||||||
gizmo?.clearTarget();
|
|
||||||
updateNavCubeVisibility();
|
|
||||||
updatePanelsVisibility();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (saveProjectButton) {
|
if (saveProjectButton) {
|
||||||
|
|
@ -6190,8 +6347,9 @@ const loadProjectSnapshot = async (project) => {
|
||||||
bottomToolbar.addEventListener("click", (e) => {
|
bottomToolbar.addEventListener("click", (e) => {
|
||||||
const btn = e.target.closest(".tb-btn");
|
const btn = e.target.closest(".tb-btn");
|
||||||
if (!btn) return;
|
if (!btn) return;
|
||||||
if (isGuestMode()) return;
|
if (btn.disabled) return;
|
||||||
const action = btn.dataset.action;
|
const action = btn.dataset.action;
|
||||||
|
if (isGuestMode() && !isGuestToolbarAction(action)) return;
|
||||||
if (action === "settings") {
|
if (action === "settings") {
|
||||||
if (btn.classList.contains("active")) {
|
if (btn.classList.contains("active")) {
|
||||||
closeSettingsPanel();
|
closeSettingsPanel();
|
||||||
|
|
@ -6222,7 +6380,6 @@ const loadProjectSnapshot = async (project) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
sectionToolbar?.addEventListener("click", (e) => {
|
sectionToolbar?.addEventListener("click", (e) => {
|
||||||
if (isGuestMode()) return;
|
|
||||||
const btn = e.target.closest("[data-section-action]");
|
const btn = e.target.closest("[data-section-action]");
|
||||||
if (!btn) return;
|
if (!btn) return;
|
||||||
const action = btn.dataset.sectionAction;
|
const action = btn.dataset.sectionAction;
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
|
|
@ -4,35 +4,40 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>BIM DC Viewer</title>
|
<title>BIM DC Viewer</title>
|
||||||
<link rel="icon" href="/assets/logo.svg">
|
<link rel="icon" type="image/svg+xml" href="/assets/favicon/icon-adaptive.svg">
|
||||||
|
<link rel="alternate icon" href="/assets/favicon/favicon.ico">
|
||||||
|
<link rel="apple-touch-icon" href="/assets/favicon/apple-touch-icon.png">
|
||||||
|
<link rel="manifest" href="/assets/favicon/manifest.webmanifest.json">
|
||||||
<style id="preload-hidden">body{opacity:0;}</style>
|
<style id="preload-hidden">body{opacity:0;}</style>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700&display=swap">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700&display=swap">
|
||||||
<script src="/theme.js?v=2"></script>
|
<script src="/theme.js?v=3"></script>
|
||||||
<script>
|
<script>
|
||||||
// Apply persisted theme ASAP to avoid initial flash of defaults
|
// Apply persisted theme ASAP to avoid initial flash of defaults
|
||||||
(function () {
|
(function () {
|
||||||
const STORAGE_KEY = "bimdc-settings-v3";
|
const STORAGE_KEY = "bimdc-settings-v3";
|
||||||
const LEGACY_KEYS = ["bimdc-settings", "bimdc-settings-v2"];
|
const LEGACY_KEYS = ["bimdc-settings", "bimdc-settings-v2"];
|
||||||
try {
|
try {
|
||||||
LEGACY_KEYS.forEach((k) => localStorage.removeItem(k));
|
|
||||||
|
|
||||||
const project = window.__BIMDC_THEME__ || {};
|
const project = window.__BIMDC_THEME__ || {};
|
||||||
const raw = localStorage.getItem(STORAGE_KEY);
|
const sharedViewer = /^\/share\/[^/]+\/?$/.test(window.location.pathname || "");
|
||||||
let stored = null;
|
let stored = null;
|
||||||
try {
|
if (!sharedViewer) {
|
||||||
stored = raw ? JSON.parse(raw) : null;
|
LEGACY_KEYS.forEach((k) => localStorage.removeItem(k));
|
||||||
} catch (e) {
|
const raw = localStorage.getItem(STORAGE_KEY);
|
||||||
stored = null;
|
try {
|
||||||
|
stored = raw ? JSON.parse(raw) : null;
|
||||||
|
} catch (e) {
|
||||||
|
stored = null;
|
||||||
|
}
|
||||||
|
const storedVersion = stored && stored.themeVersion;
|
||||||
|
const projectVersion = project && project.themeVersion;
|
||||||
|
if (!stored || storedVersion !== projectVersion) {
|
||||||
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(project));
|
||||||
|
stored = { ...project };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const storedVersion = stored && stored.themeVersion;
|
const s = { ...(project || {}), ...(!sharedViewer && stored ? stored : {}) };
|
||||||
const projectVersion = project && project.themeVersion;
|
|
||||||
if (!stored || storedVersion !== projectVersion) {
|
|
||||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(project));
|
|
||||||
stored = { ...project };
|
|
||||||
}
|
|
||||||
const s = { ...(project || {}), ...(stored || {}) };
|
|
||||||
if (!Object.keys(s).length) return;
|
if (!Object.keys(s).length) return;
|
||||||
const root = document.documentElement;
|
const root = document.documentElement;
|
||||||
const set = (k, v) => v !== undefined && root.style.setProperty(k, v);
|
const set = (k, v) => v !== undefined && root.style.setProperty(k, v);
|
||||||
|
|
@ -203,11 +208,11 @@
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<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 href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="/dcViewer.css?v=36">
|
<link rel="stylesheet" href="/dcViewer.css?v=37">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<button class="logo-btn" id="homeButton" title="Вернуться к старту"></button>
|
<button class="logo-btn" id="homeButton" type="button" title="Открыть NODE.DC Hub"></button>
|
||||||
<div class="toolbar"></div>
|
<div class="toolbar"></div>
|
||||||
<div class="hidden-controls" style="display:none">
|
<div class="hidden-controls" style="display:none">
|
||||||
<input type="checkbox" id="toggleEdges" checked>
|
<input type="checkbox" id="toggleEdges" checked>
|
||||||
|
|
@ -263,10 +268,15 @@
|
||||||
<div class="inspector-control-label">Режим отображения</div>
|
<div class="inspector-control-label">Режим отображения</div>
|
||||||
<div class="inspector-select-host" id="displayModeSelect"></div>
|
<div class="inspector-select-host" id="displayModeSelect"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="inspector-control-row inspector-control-row--half hidden" id="customDisplayColorRow">
|
||||||
|
<div class="inspector-control-label">Цвет кастома</div>
|
||||||
|
<div class="inspector-color-host" id="customDisplayColorControl"></div>
|
||||||
|
</div>
|
||||||
<div class="inspector-control-row inspector-control-row--half" id="lightingModeControl">
|
<div class="inspector-control-row inspector-control-row--half" id="lightingModeControl">
|
||||||
<div class="inspector-control-label">Свет</div>
|
<div class="inspector-control-label">Свет</div>
|
||||||
<div class="inspector-select-host" id="lightingModeSelect"></div>
|
<div class="inspector-select-host" id="lightingModeSelect"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="inspector-control-row inspector-control-row--full inspector-control-row--slider hidden" id="customDisplaySaturationControl"></div>
|
||||||
<div class="inspector-control-row inspector-control-row--full" id="navigationAxisControl">
|
<div class="inspector-control-row inspector-control-row--full" id="navigationAxisControl">
|
||||||
<div class="inspector-control-label">Ось навигации</div>
|
<div class="inspector-control-label">Ось навигации</div>
|
||||||
<div class="inspector-select-host" id="navigationAxisSelect"></div>
|
<div class="inspector-select-host" id="navigationAxisSelect"></div>
|
||||||
|
|
@ -641,6 +651,6 @@
|
||||||
<input id="fileInput" class="hidden" type="file"
|
<input id="fileInput" class="hidden" type="file"
|
||||||
accept=".xkt,.glb,.gltf,.bim,.las,.laz,.obj,.stl">
|
accept=".xkt,.glb,.gltf,.bim,.las,.laz,.obj,.stl">
|
||||||
|
|
||||||
<script type="module" src="/dcViewer.js?v=50"></script>
|
<script type="module" src="/dcViewer.js?v=55"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { createSliderControl } from "./controls/slider.js";
|
||||||
import { createColorControl } from "./controls/color.js";
|
import { createColorControl } from "./controls/color.js";
|
||||||
|
|
||||||
const defaultSettings = {
|
const defaultSettings = {
|
||||||
themeVersion: "v6-comments-targets",
|
themeVersion: "v7-toolbar-hub-icon",
|
||||||
bgOuter: "#0f0f0f",
|
bgOuter: "#0f0f0f",
|
||||||
canvasTop: "#1c1c1c",
|
canvasTop: "#1c1c1c",
|
||||||
canvasBottom: "#1c1c1c",
|
canvasBottom: "#1c1c1c",
|
||||||
|
|
@ -25,8 +25,8 @@ const defaultSettings = {
|
||||||
toolbarBorder: "#292929",
|
toolbarBorder: "#292929",
|
||||||
toolbarOutline: "#292929",
|
toolbarOutline: "#292929",
|
||||||
toolbarMinSize: 25,
|
toolbarMinSize: 25,
|
||||||
toolbarMaxSize: 56,
|
toolbarMaxSize: 87,
|
||||||
toolbarLensCount: 7,
|
toolbarLensCount: 5,
|
||||||
toolbarAutoHide: false,
|
toolbarAutoHide: false,
|
||||||
modalBg: "#1c1c1c",
|
modalBg: "#1c1c1c",
|
||||||
modalBgAlpha: 1,
|
modalBgAlpha: 1,
|
||||||
|
|
@ -72,6 +72,14 @@ const defaultSettings = {
|
||||||
|
|
||||||
const STORAGE_KEY = "bimdc-settings-v3";
|
const STORAGE_KEY = "bimdc-settings-v3";
|
||||||
const LEGACY_KEYS = ["bimdc-settings", "bimdc-settings-v2"];
|
const LEGACY_KEYS = ["bimdc-settings", "bimdc-settings-v2"];
|
||||||
|
const isSharedViewerPath = () => (
|
||||||
|
typeof window !== "undefined" &&
|
||||||
|
/^\/share\/[^/]+\/?$/.test(window.location.pathname || "")
|
||||||
|
);
|
||||||
|
const isReadonlySettingsContext = () => (
|
||||||
|
isSharedViewerPath() ||
|
||||||
|
(typeof document !== "undefined" && document.body?.dataset?.viewerMode === "guest")
|
||||||
|
);
|
||||||
const projectSettings = typeof window !== "undefined" && window.__BIMDC_THEME__
|
const projectSettings = typeof window !== "undefined" && window.__BIMDC_THEME__
|
||||||
? { ...defaultSettings, ...window.__BIMDC_THEME__ }
|
? { ...defaultSettings, ...window.__BIMDC_THEME__ }
|
||||||
: { ...defaultSettings };
|
: { ...defaultSettings };
|
||||||
|
|
@ -278,6 +286,7 @@ const applySettings = (s) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveSettings = (s) => {
|
const saveSettings = (s) => {
|
||||||
|
if (isReadonlySettingsContext()) return;
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(s));
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(s));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -286,6 +295,9 @@ const saveSettings = (s) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadSettings = () => {
|
const loadSettings = () => {
|
||||||
|
if (isReadonlySettingsContext()) {
|
||||||
|
return { ...projectSettings };
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// purge legacy keys so старые палитры не мешали
|
// purge legacy keys so старые палитры не мешали
|
||||||
LEGACY_KEYS.forEach((k) => localStorage.removeItem(k));
|
LEGACY_KEYS.forEach((k) => localStorage.removeItem(k));
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
const toolbarConfig = {
|
const toolbarConfig = {
|
||||||
minSize: 25,
|
minSize: 25,
|
||||||
maxSize: 56,
|
maxSize: 87,
|
||||||
lensCount: 7,
|
lensCount: 5,
|
||||||
autoHide: false,
|
autoHide: false,
|
||||||
size: 25,
|
size: 25,
|
||||||
gap: 10,
|
gap: 10,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Базовые цвета/настройки проекта. При очистке localStorage эти значения остаются.
|
// Базовые цвета/настройки проекта. При очистке localStorage эти значения остаются.
|
||||||
// Чтобы изменить палитру по умолчанию — правь этот объект и закоммить изменения.
|
// Чтобы изменить палитру по умолчанию — правь этот объект и закоммить изменения.
|
||||||
const PROJECT_THEME = {
|
const PROJECT_THEME = {
|
||||||
themeVersion: "v6-comments-targets",
|
themeVersion: "v7-toolbar-hub-icon",
|
||||||
bgOuter: "#0f0f0f",
|
bgOuter: "#0f0f0f",
|
||||||
canvasTop: "#1c1c1c",
|
canvasTop: "#1c1c1c",
|
||||||
canvasBottom: "#1c1c1c",
|
canvasBottom: "#1c1c1c",
|
||||||
|
|
@ -18,8 +18,8 @@ const PROJECT_THEME = {
|
||||||
toolbarBorder: "#292929",
|
toolbarBorder: "#292929",
|
||||||
toolbarOutline: "#292929",
|
toolbarOutline: "#292929",
|
||||||
toolbarMinSize: 25,
|
toolbarMinSize: 25,
|
||||||
toolbarMaxSize: 56,
|
toolbarMaxSize: 87,
|
||||||
toolbarLensCount: 7,
|
toolbarLensCount: 5,
|
||||||
toolbarAutoHide: false,
|
toolbarAutoHide: false,
|
||||||
modalBg: "#1c1c1c",
|
modalBg: "#1c1c1c",
|
||||||
modalBgAlpha: 1,
|
modalBgAlpha: 1,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue