Systemmenue im Logo, in beiden Leisten

Das ■ HONEYOS ist jetzt ein Menue statt eines Links: Schreibtisch,
Einstellungen, HoneyBar einklappen, Abmelden. In fremden Apps sind es
Links auf den Schreibtisch (?einstellungen=1 / ?abmelden=1), weil die
HoneyBar dort nicht schreiben darf — Sitzung und CSRF-Pruefwort ueber-
leben die Site-Grenze nicht, und ein Abmelden per Aufruf koennte jede
Seite ausloesen.

Der Abmeldeknopf in der Menueleiste faellt weg (stand doppelt), und
Abmelden erscheint nur, wenn HoneyOS ueberhaupt anmeldet — dafuer gibt
/api/apps jetzt oidc mit heraus. Nebenbei: restliche 'Leiste' im Text
zu 'HoneyBar' vereinheitlicht.
This commit is contained in:
marc committed 2026-08-29 21:23:11 +02:00
1 parent 52deffa30e
commit 0a5cd94ba0
12 files changed
+244 -51

No files matched your search

+82 -6
View File
@@ -110,7 +110,6 @@ function runBoot() {
function renderShell() {
$("desktop").hidden = false;
$("btnLogout").hidden = !state.oidc;
renderMenubarApps();
renderMode();
tickClock();
@@ -792,6 +791,60 @@ function openAbout() {
/* ── Hilfsmittel ───────────────────────────────────────────────────────── */
/* ── Systemmenü im Logo ────────────────────────────────────────────────────
* Der Zwilling des Menüs in honeybar.js: gleiche Punkte, gleiche Reihenfolge.
* Hier sind es Handlungen, dort Links hierher — denn nur hier ist man
* angemeldet. Wer aus Aether auf „Einstellungen“ klickt, landet auf genau
* demselben Feld, das dieser Punkt aufmacht.
*/
function buildSystemMenu() {
const menu = document.createElement("div");
menu.className = "startmenu sysmenu";
menu.id = "sysmenu";
$("desktop").appendChild(menu);
const btn = $("btnSystem");
btn.addEventListener("click", (e) => {
e.stopPropagation();
if (menu.dataset.open === "1") return closeSystemMenu();
closeStartMenu();
const terminal = state.settings.mode === "terminal";
menu.innerHTML =
`<div class="sm-head">honeyos</div>
<button data-do="about">Über HoneyOS</button>
<button data-do="settings">Einstellungen</button>
<hr />
<button data-do="mode">${terminal ? "▣ Desktop-Modus" : "↩ Terminal-Modus"}</button>` +
(state.oidc ? `<hr /><button data-do="logout">⏻ Abmelden</button>` : "");
menu.querySelectorAll("[data-do]").forEach((b) =>
b.addEventListener("click", () => {
closeSystemMenu();
({
about: openAbout,
settings: openSettings,
mode: () => setMode(terminal ? "desktop" : "terminal"),
logout,
})[b.dataset.do]();
})
);
menu.dataset.open = "1";
btn.setAttribute("aria-expanded", "true");
});
document.addEventListener("click", closeSystemMenu);
}
function closeSystemMenu() {
const menu = $("sysmenu");
if (!menu) return;
menu.dataset.open = "0";
$("btnSystem").setAttribute("aria-expanded", "false");
}
/* ── Startmenü ─────────────────────────────────────────────────────────── */
function buildStartMenu() {
@@ -805,6 +858,10 @@ function buildStartMenu() {
e.stopPropagation();
const open = menu.dataset.open === "1";
if (open) return closeStartMenu();
// Beide Menüs offen ginge, sähe aber aus wie ein Fehler: Der Klick auf den
// Startknopf hält sein Ereignis auf, also erreicht es den Schließer des
// Systemmenüs nicht von selbst.
closeSystemMenu();
menu.innerHTML = `<div class="sm-head">starten</div>` +
state.cfg.apps.map((a) => `<button data-app="${a.id}">${escapeHtml(a.name)}</button>`).join("") +
@@ -812,8 +869,10 @@ function buildStartMenu() {
<button data-win="system">System</button>
<button data-win="settings">Einstellungen</button>
<button data-win="about">Über HoneyOS</button>
<hr /><button data-mode="terminal">↩ Terminal-Modus</button>
<button data-logout="1">⏻ Abmelden</button>`;
<hr /><button data-mode="terminal">↩ Terminal-Modus</button>` +
// Ohne Anbieter gibt es nichts abzumelden — im Systemmenü steht der
// Punkt aus demselben Grund nur bei Anmeldung.
(state.oidc ? `<button data-logout="1">⏻ Abmelden</button>` : "");
menu.querySelectorAll("[data-app]").forEach((b) =>
b.addEventListener("click", () => {
@@ -834,7 +893,7 @@ function buildStartMenu() {
closeStartMenu();
setMode("terminal");
});
menu.querySelector("[data-logout]").addEventListener("click", logout);
menu.querySelector("[data-logout]")?.addEventListener("click", logout);
menu.dataset.open = "1";
btn.setAttribute("aria-expanded", "true");
@@ -922,6 +981,13 @@ async function main() {
loadLocalSettings();
applySettings();
// ?abmelden=1 kommt aus dem Systemmenü der HoneyBar. Es hier zu erledigen
// statt am Server heißt: Die Abmeldung bleibt ein POST von dieser Seite aus,
// und keine fremde Seite kann sie durch ein eingebettetes Bild auslösen.
if (new URLSearchParams(location.search).get("abmelden") === "1") {
return logout();
}
let pub = { oidc: false, buttonLabel: "Anmelden" };
try {
pub = await api("/api/public");
@@ -948,18 +1014,26 @@ async function main() {
renderShell();
buildStartMenu();
buildSystemMenu();
probeApps();
setInterval(tickClock, 15000);
setInterval(probeApps, 120000);
$("btnSettings").addEventListener("click", openSettings);
$("btnAbout").addEventListener("click", openAbout);
$("btnLogout").addEventListener("click", logout);
$("btnClose").addEventListener("click", closeModal);
$("modal").addEventListener("click", (e) => {
if (e.target === $("modal")) closeModal();
});
// Was die HoneyBar aus einer fremden App herüberreicht: Sie darf selbst
// nichts schreiben (siehe honeybar.js), also stellt sie die Frage in der
// Adresse und der Schreibtisch beantwortet sie. Danach kommt die Adresse
// weg — ein Neuladen soll die Einstellungen nicht wieder aufreißen.
if (new URLSearchParams(location.search).get("einstellungen") === "1") {
history.replaceState(null, "", location.pathname);
openSettings();
}
window.addEventListener("keydown", (e) => {
if (e.metaKey || e.ctrlKey || e.altKey) return;
const typing = /^(INPUT|TEXTAREA|SELECT)$/.test(e.target.tagName);
@@ -968,6 +1042,8 @@ async function main() {
// einem gleichzeitig das oberste Fenster weg.
const menu = $("startmenu");
if (menu && menu.dataset.open === "1") return closeStartMenu();
const sys = $("sysmenu");
if (sys && sys.dataset.open === "1") return closeSystemMenu();
return state.settings.mode === "desktop" ? closeTopWindow() : closeModal();
}
if (typing) return;