v0.5.2: elektra (meterkast 3×25 A, schakelaars met letters op hun lampen, wcd, apparaatgroepen incl. perilex, groepenverklaring met fasen/aardlek, leidingen naar de meterkast met strekkende meters per type, PDF-blad + plattegrond, verplaatsbaar door slepen, bouwfase Elektra met installatiebuizen in 3D); ribbon, bouwfasen, kozijnen, kostenmodel en eerdere ongecommitte wijzigingen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8cdfa21f46
commit
e1a688728c
@@ -4,7 +4,7 @@
|
||||
* Alle rechten voorbehouden. Gebruik uitsluitend met schriftelijke toestemming.
|
||||
* Zie LICENTIE.md.
|
||||
*/
|
||||
const APP_VERSION = '0.4.17';
|
||||
const APP_VERSION = '0.5.2';
|
||||
// 0.0.X per kleine iteratie, 0.X.0 per mijlpaal, X.0.0 bij live-gang.
|
||||
|
||||
let tabs = [];
|
||||
@@ -32,15 +32,56 @@ function setWorkspaceEmpty() {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// Ribbon (core/ribbon.js): app-tabbladen Bestand, Beeld en Extra; de
|
||||
// editor voegt er zijn eigen tabbladen aan toe. Knoppen die de huidige
|
||||
// editor niet ondersteunt staan uit.
|
||||
function updateEditMenuState() {
|
||||
const has = (fn) => !!(currentEditor && typeof currentEditor[fn] === 'function');
|
||||
document.getElementById('menu-opslaan-als').classList.toggle('disabled', !has('exportDesign'));
|
||||
document.getElementById('menu-importeren').classList.toggle('disabled', !has('triggerImport'));
|
||||
document.getElementById('menu-mirror-h').classList.toggle('disabled', !has('mirrorHorizontal'));
|
||||
document.getElementById('menu-3d-fullscreen').classList.toggle('disabled', !has('toggleFullscreen3D'));
|
||||
document.getElementById('menu-2d-fullscreen').classList.toggle('disabled', !has('toggleFullscreen2D'));
|
||||
document.getElementById('menu-walk').classList.toggle('disabled', !has('toggleWalk'));
|
||||
document.getElementById('menu-project-props').classList.toggle('disabled', !has('openProjectProperties'));
|
||||
Ribbon.refresh();
|
||||
}
|
||||
|
||||
const editorHas = (fn) => !!(currentEditor && typeof currentEditor[fn] === 'function');
|
||||
const editorCall = (fn) => { if (editorHas(fn)) currentEditor[fn](); };
|
||||
const disabledUnless = (fn) => editorHas(fn) ? '' : 'disabled';
|
||||
|
||||
function appRibbonTabs() {
|
||||
const { big, small } = Ribbon;
|
||||
const wireClicks = (body) => body.querySelectorAll('[data-app]').forEach(b => b.addEventListener('click', () => {
|
||||
const a = b.dataset.app;
|
||||
if (a === 'new') openNewModal();
|
||||
else if (a === 'new-kind') openNew(b.dataset.kind, b.dataset.template);
|
||||
else if (a === 'fullscreen') toggleBrowserFullscreen();
|
||||
else if (a === 'costs') CostSettings.open();
|
||||
else if (a === 'about') document.getElementById('about-overlay').classList.add('open');
|
||||
else editorCall(a);
|
||||
}));
|
||||
return [
|
||||
{ key: 'bestand', label: 'Bestand', order: 0, wire: wireClicks, groups: () => [
|
||||
{ label: 'Document', html:
|
||||
big('Nieuw', 'file-earmark-plus', 'data-app="new" title="Nieuw ontwerp (Ctrl+N)"') +
|
||||
big('Openen', 'folder2-open', `data-app="triggerImport" title="JSON-ontwerp openen" ${disabledUnless('triggerImport')}`) +
|
||||
big('Opslaan', 'save', `data-app="exportDesign" title="Opslaan als JSON + PDF (Ctrl+S)" ${disabledUnless('exportDesign')}`) },
|
||||
{ label: 'Snel nieuw', html:
|
||||
small('Woning', 'house', 'data-app="new-kind" data-kind="woning" data-template="single"') +
|
||||
small('Woning L-vorm', 'bounding-box-circles', 'data-app="new-kind" data-kind="woning" data-template="L"') +
|
||||
small('Los: muur', 'bricks', 'data-app="new-kind" data-kind="muur"') +
|
||||
small('Los: vloer', 'grid-3x3', 'data-app="new-kind" data-kind="vloer"') },
|
||||
{ label: 'Project', html: big('Eigenschappen', 'card-text', `data-app="openProjectProperties" title="Projectgegevens voor de PDF" ${disabledUnless('openProjectProperties')}`) }
|
||||
] },
|
||||
{ key: 'beeld', label: 'Beeld', order: 80, wire: wireClicks, groups: () => [
|
||||
{ label: 'Venster', html:
|
||||
small('Volledig scherm', 'fullscreen', 'data-app="fullscreen" title="F"') +
|
||||
small('3D groot', 'badge-3d', `data-app="toggleFullscreen3D" title="T: standaard → 3D groot → alleen render" ${disabledUnless('toggleFullscreen3D')}`) +
|
||||
small('2D groot', 'aspect-ratio', `data-app="toggleFullscreen2D" title="R" ${disabledUnless('toggleFullscreen2D')}`) },
|
||||
{ label: 'Camera', html:
|
||||
big('Lopen', 'person-walking', `data-app="toggleWalk" title="Shift+T. W/S of pijltjes: vooruit/achteruit · A/D: opzij · pijl links/rechts: draaien · Shift: rennen · slepen: rondkijken" ${disabledUnless('toggleWalk')}`) +
|
||||
small('Bediening', 'mouse', 'title="Rondom: slepen = draaien · rechts slepen = verschuiven · scrollwiel = zoomen. Lopen: Shift+T."') }
|
||||
] },
|
||||
{ key: 'extra', label: 'Extra', order: 90, wire: wireClicks, groups: () => [
|
||||
{ label: 'Bewerken', html: small('Horizontaal spiegelen', 'symmetry-vertical', `data-app="mirrorHorizontal" title="H" ${disabledUnless('mirrorHorizontal')}`) },
|
||||
{ label: 'Instellingen', html: big('Kosten & tijden', 'calculator', 'data-app="costs"') },
|
||||
{ label: 'Info', html: big('Over Tectum', 'info-circle', 'data-app="about"') }
|
||||
] }
|
||||
];
|
||||
}
|
||||
|
||||
function renderTabBar() {
|
||||
@@ -63,6 +104,7 @@ function onTabSaved(tab, filenameWithExt) {
|
||||
function mountEditorForTab(tab) {
|
||||
const ws = document.getElementById('workspace');
|
||||
ws.innerHTML = '';
|
||||
Ribbon.setSource('editor', []);
|
||||
currentEditor = editorModuleFor(tab.kind).mount(ws, { onSaved: (filename) => onTabSaved(tab, filename), template: tab.template, cameraKey: 'tab' + tab.id });
|
||||
if (tab.stateJson && typeof currentEditor.loadStateJson === 'function') {
|
||||
currentEditor.loadStateJson(tab.stateJson);
|
||||
@@ -85,7 +127,7 @@ function switchToTab(tabId) {
|
||||
}
|
||||
|
||||
function openNew(kind, template) {
|
||||
if (!editorModuleFor(kind)) { closeAllMenus(); return; } // 'dak' nog niet beschikbaar
|
||||
if (!editorModuleFor(kind)) return; // 'dak' nog niet beschikbaar
|
||||
|
||||
const oldTab = tabs.find(t => t.id === activeTabId);
|
||||
if (oldTab && currentEditor && typeof currentEditor.getStateJson === 'function') {
|
||||
@@ -100,7 +142,6 @@ function openNew(kind, template) {
|
||||
mountEditorForTab(tab);
|
||||
updateEditMenuState();
|
||||
renderTabBar();
|
||||
closeAllMenus();
|
||||
}
|
||||
|
||||
function isTabDirty(tab) {
|
||||
@@ -140,6 +181,7 @@ function closeTab(tabId) {
|
||||
switchToTab(tabs[tabs.length - 1].id);
|
||||
} else {
|
||||
setWorkspaceEmpty();
|
||||
Ribbon.setSource('editor', []);
|
||||
updateEditMenuState();
|
||||
renderTabBar();
|
||||
}
|
||||
@@ -154,69 +196,6 @@ function wireTabBar() {
|
||||
});
|
||||
}
|
||||
|
||||
function closeAllMenus() {
|
||||
document.querySelectorAll('.menu-dropdown, .submenu').forEach(el => el.classList.remove('open'));
|
||||
document.querySelectorAll('.menu-btn').forEach(el => el.classList.remove('open'));
|
||||
}
|
||||
|
||||
function wireTopLevelMenu(btnId, dropdownId) {
|
||||
const btn = document.getElementById(btnId);
|
||||
const dropdown = document.getElementById(dropdownId);
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const willOpen = !dropdown.classList.contains('open');
|
||||
closeAllMenus();
|
||||
if (willOpen) { dropdown.classList.add('open'); btn.classList.add('open'); }
|
||||
});
|
||||
}
|
||||
|
||||
function wireMenu() {
|
||||
wireTopLevelMenu('menu-bestand-btn', 'menu-bestand-dropdown');
|
||||
wireTopLevelMenu('menu-bewerken-btn', 'menu-bewerken-dropdown');
|
||||
wireTopLevelMenu('menu-beeld-btn', 'menu-beeld-dropdown');
|
||||
|
||||
document.getElementById('menu-nieuw-item').addEventListener('click', (e) => e.stopPropagation());
|
||||
document.querySelectorAll('.menu-nieuw-option').forEach(opt => {
|
||||
opt.addEventListener('click', () => openNew(opt.dataset.kind, opt.dataset.template));
|
||||
});
|
||||
|
||||
document.getElementById('menu-opslaan-als').addEventListener('click', () => {
|
||||
if (currentEditor && typeof currentEditor.exportDesign === 'function') currentEditor.exportDesign();
|
||||
closeAllMenus();
|
||||
});
|
||||
document.getElementById('menu-importeren').addEventListener('click', () => {
|
||||
if (currentEditor && typeof currentEditor.triggerImport === 'function') currentEditor.triggerImport();
|
||||
closeAllMenus();
|
||||
});
|
||||
document.getElementById('menu-mirror-h').addEventListener('click', () => {
|
||||
if (currentEditor && typeof currentEditor.mirrorHorizontal === 'function') currentEditor.mirrorHorizontal();
|
||||
closeAllMenus();
|
||||
});
|
||||
document.getElementById('menu-fullscreen').addEventListener('click', () => {
|
||||
toggleBrowserFullscreen();
|
||||
closeAllMenus();
|
||||
});
|
||||
document.getElementById('menu-3d-fullscreen').addEventListener('click', () => {
|
||||
if (currentEditor && typeof currentEditor.toggleFullscreen3D === 'function') currentEditor.toggleFullscreen3D();
|
||||
closeAllMenus();
|
||||
});
|
||||
document.getElementById('menu-camera-item').addEventListener('click', (e) => e.stopPropagation());
|
||||
document.getElementById('menu-walk').addEventListener('click', () => {
|
||||
if (currentEditor && typeof currentEditor.toggleWalk === 'function') currentEditor.toggleWalk();
|
||||
closeAllMenus();
|
||||
});
|
||||
document.getElementById('menu-2d-fullscreen').addEventListener('click', () => {
|
||||
if (currentEditor && typeof currentEditor.toggleFullscreen2D === 'function') currentEditor.toggleFullscreen2D();
|
||||
closeAllMenus();
|
||||
});
|
||||
document.getElementById('menu-project-props').addEventListener('click', () => {
|
||||
if (currentEditor && typeof currentEditor.openProjectProperties === 'function') currentEditor.openProjectProperties();
|
||||
closeAllMenus();
|
||||
});
|
||||
|
||||
document.addEventListener('click', closeAllMenus);
|
||||
}
|
||||
|
||||
// Minimale markdown-weergave voor de licentietekst (koppen + alinea's).
|
||||
function renderLicenseHtml(md) {
|
||||
const esc = (t) => t.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
@@ -231,10 +210,6 @@ function renderLicenseHtml(md) {
|
||||
function wireAbout() {
|
||||
const overlay = document.getElementById('about-overlay');
|
||||
document.getElementById('about-license').innerHTML = renderLicenseHtml(window.TECTUM_LICENSE_MD || '');
|
||||
document.getElementById('btn-about').addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
overlay.classList.add('open');
|
||||
});
|
||||
document.getElementById('about-close').addEventListener('click', () => overlay.classList.remove('open'));
|
||||
overlay.addEventListener('click', (e) => { if (e.target === overlay) overlay.classList.remove('open'); });
|
||||
}
|
||||
@@ -251,7 +226,6 @@ function wireNewModal() {
|
||||
}
|
||||
|
||||
function openNewModal() {
|
||||
closeAllMenus();
|
||||
document.getElementById('new-overlay').classList.add('open');
|
||||
}
|
||||
|
||||
@@ -274,6 +248,12 @@ function wireHotkeys() {
|
||||
openNewModal();
|
||||
return;
|
||||
}
|
||||
// Easter egg (shooter); e.code omdat Alt/Shift e.key per toetsenbord verandert.
|
||||
if (e.ctrlKey && e.shiftKey && e.altKey && e.code === 'KeyL') {
|
||||
e.preventDefault();
|
||||
if (currentEditor && typeof currentEditor.toggleShooter === 'function') currentEditor.toggleShooter();
|
||||
return;
|
||||
}
|
||||
if (inField || e.ctrlKey || e.metaKey || e.altKey) return;
|
||||
|
||||
if (e.key.toLowerCase() === 'f') {
|
||||
@@ -293,7 +273,8 @@ function wireHotkeys() {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.querySelectorAll('.app-version-slot').forEach(el => { el.textContent = 'v' + APP_VERSION; });
|
||||
document.querySelectorAll('.app-version-number').forEach(el => { el.textContent = APP_VERSION; });
|
||||
wireMenu();
|
||||
Ribbon.init(document.getElementById('ribbon'));
|
||||
Ribbon.setSource('app', appRibbonTabs());
|
||||
wireAbout();
|
||||
wireNewModal();
|
||||
wireHotkeys();
|
||||
|
||||
Reference in New Issue
Block a user