From d73d5a31179f62a2c98d806e24e873ca435b0d31 Mon Sep 17 00:00:00 2001 From: "manuel.sowada" Date: Wed, 22 Apr 2026 12:25:45 +0200 Subject: [PATCH] update os logic --- public/helpers/fileHelper.js | 8 +++++ public/javascript/main.js | 59 +++++++++++++++++++++++++------- public/javascript/os.js | 11 ++++-- public/styles/os.css | 12 ++++--- public/views/partials/child.hbs | 7 ++-- public/views/partials/window.hbs | 11 +++--- 6 files changed, 82 insertions(+), 26 deletions(-) diff --git a/public/helpers/fileHelper.js b/public/helpers/fileHelper.js index aafcd54..8e607e1 100644 --- a/public/helpers/fileHelper.js +++ b/public/helpers/fileHelper.js @@ -1,4 +1,12 @@ module.exports = { + renderPartial: function(app, view, data = {}) { + return new Promise((resolve, reject) => { + app.render(view, data, (err, html) => { + if (err) return reject(err); + resolve(html); + }); + }); + }, // objectTree: function(context, options) { // let ret = ''; diff --git a/public/javascript/main.js b/public/javascript/main.js index 8148f27..f8c3029 100644 --- a/public/javascript/main.js +++ b/public/javascript/main.js @@ -1476,19 +1476,54 @@ function createTd(tr, text, options = {}) { }; //#endregion + //#region Tabs -function createTab(tabSelector, name, onclick = null) { - const tabElement = document.createElement('div'); - tabElement.className = 'tab'; - tabElement.dataset.tab = name; - tabElement.textContent = name; - tabSelector.appendChild(tabElement); - tabElement.addEventListener('click', async () => { - Array.from(document.querySelectorAll('.tab')).forEach(t => t.classList.remove('active')); - tabElement.classList.add('active'); - if(typeof onclick === 'function') { - onclick(); +function createTab(tabSelector, name, options = {}) { + const { + id = null, + hidden = false, + classes = [], + styles = null, + attributes = {}, + onclick = null + } = options; + + const tabElement = document.createElement('div'); + tabElement.className = 'tab'; + tabElement.dataset.tab = name; + tabElement.textContent = name; + tabSelector.appendChild(tabElement); + + if(id !== null) { + tabElement.id = id; } - }); + + // Standard-Stile & Attribute + tabElement.hidden = hidden; + + classes.forEach(c => tabElement.classList.add(c)); + + if (styles && typeof styles === 'object') { + Object.entries(styles).forEach(([prop, value]) => { + tabElement.style[prop] = value; + }); + } + + // Data attributes + if (attributes && typeof attributes === 'object') { + Object.entries(attributes).forEach(([key, value]) => { + tabElement.setAttribute(key, value); + }); + } + + tabElement.addEventListener('click', async (evt) => { + Array.from(tabSelector.querySelectorAll('.tab')).forEach(t => t.classList.remove('active')); + tabElement.classList.add('active'); + if(typeof onclick === 'function') { + onclick(evt); + } + }); + + return tabElement; } //#endregion \ No newline at end of file diff --git a/public/javascript/os.js b/public/javascript/os.js index 1a7c8ea..77f57f7 100644 --- a/public/javascript/os.js +++ b/public/javascript/os.js @@ -378,6 +378,7 @@ function applyMaximized(win) { win.classList.add('max'); win.dataset.state = 'maximized'; + } @@ -600,16 +601,18 @@ function wireWindowControls(win, taskbarBtn = null) { if (taskbarBtn !== null) { titlebar.addEventListener('dblclick', (evt) => { toggleMaximize(); + titlebar.querySelector('.maximize').innerHTML = win.dataset.state === 'maximized' ? 'πŸ——' : 'β–’'; saveOpenWindows() }); } + titlebar.addEventListener('touchend', (ev) => { const current = Date.now(); const delta = current - lastTap; - if (delta < 300 && delta > 0) { // Double tap β†’ maximize / restore + titlebar.querySelector('.maximize').innerHTML = win.dataset.state === 'maximized' ? 'πŸ——' : 'β–’'; toggleMaximize(); } lastTap = current; @@ -668,7 +671,7 @@ function destroyWindow(win) { // πŸ”₯ VOR dem Maximieren speichern storeNormalGeometry(win); -applyMaximized(win); + applyMaximized(win); } else { const prev = JSON.parse(win.dataset.normalGeometry || '{}'); @@ -838,6 +841,7 @@ function makeDraggableWithSnap(win) { } else if (ev.clientY <= snapThreshold) { // Snap top (maximize) applySnap(win, 'top'); + win.querySelector('.maximize').innerHTML = 'πŸ——'; snapped = 'top'; } else if (snapped && ev.clientX > snapThreshold * 2 && ev.clientX < screenW - snapThreshold * 2) { // Wegziehen vom Rand β†’ Restore @@ -865,6 +869,7 @@ function makeDraggableWithSnap(win) { } win.classList.add('max'); + if (side === 'left') { win.style.left = '0'; @@ -908,6 +913,8 @@ function makeDraggableWithSnap(win) { win.style.height = height + 'px'; win.classList.remove('max'); + win.querySelector('.maximize').innerHTML = 'β–’'; + win.dataset.state = 'normal'; win.dataset.snapped = ''; } diff --git a/public/styles/os.css b/public/styles/os.css index a31b004..f022206 100644 --- a/public/styles/os.css +++ b/public/styles/os.css @@ -3,11 +3,15 @@ body, html { margin:0; padding:0; height:100%; overflow: hidden; font-family: va #desktop { position:relative; height:100vh; overflow:hidden; background-size:var(--theme-desktop-background-size); background-repeat: no-repeat; background-position: center; touch-action: none; } #windows { z-index: 1; position:absolute; inset:0; padding:8px; box-sizing:border-box; } -.window { position:absolute; width: 800px; height: 600px; border-radius:6px; box-shadow: 0 6px 20px rgba(0,0,0,0.4); overflow:hidden; top:50px; left:50px; display:flex; flex-direction:column; resize:both; } -.window-titlebar { padding: 0 0 1px 0; height: 28px; display:flex; justify-content:space-between; align-items:center; } -.window-icon { height: 20px; background-size:contain; transform: translate(1px, -1px); background-repeat: no-repeat; background-position: left; background-color: rgb(144, 179, 144); padding:4px;border-radius: 8px;} +.window { min-width:250px; min-height:250px; position:absolute; width: 800px; height: 600px; border-radius:6px; box-shadow: 0 6px 20px rgba(0,0,0,0.4); overflow:hidden; top:50px; left:50px; display:flex; flex-direction:column; resize:both; } +.window-titlebar { padding: 0 0 1px 0; height: auto; display:flex; justify-content:space-between; align-items:center; } +.window-titlebar .title { display: flex; align-items: center; min-width: 0; flex: 1; overflow: hidden; } +.window-titlebar .window-title { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; margin-left:8px; } + +.window-icon { height: 10px; background-size:contain; transform: translate(1px, -1px); background-repeat: no-repeat; background-position: left; background-color: rgb(144, 179, 144); padding:4px;border-radius: 8px;} +.window .controls { display: flex; flex-shrink: 0;} +.window .controls button { transition: background-color .3s, color .3s; padding: 2px 10px; border:none;} .window-content { display: flex; flex-direction: column; flex:1; padding:8px; overflow: auto; } -.window .controls button { transition: background-color .3s, color .3s; padding: 6px 10px; border:none; } .window[class="max"] .window-resize-handle { display: none; } .window-resize-handle { position:absolute; right:0; bottom:0; width:12px; height:12px; cursor:se-resize; z-index: 10; } .window-resize-n { top: -4px; left: 0; right: 0; height: 8px; cursor: n-resize; } diff --git a/public/views/partials/child.hbs b/public/views/partials/child.hbs index 44efd2a..7c3ac3e 100644 --- a/public/views/partials/child.hbs +++ b/public/views/partials/child.hbs @@ -1,6 +1,8 @@
-
{{{viewLabel}}}
+
+
{{viewLabel}}
+
{{#if this.printable}} @@ -12,5 +14,4 @@ {{{contentHtml}}}
-
- \ No newline at end of file +
\ No newline at end of file diff --git a/public/views/partials/window.hbs b/public/views/partials/window.hbs index 3fb002c..26f2dd4 100644 --- a/public/views/partials/window.hbs +++ b/public/views/partials/window.hbs @@ -1,13 +1,15 @@
-
{{appname}} [{{label}}]
+
+
{{appname}} [{{label}}]
+
{{#if tutorial}} {{/if}} - - + +
@@ -15,5 +17,4 @@ {{{contentHtml}}}
- - \ No newline at end of file + \ No newline at end of file