Abmelden nur noch in den Einstellungen, App-Menue statt Zahnrad

Marcs Entscheidung: In der HoneyBar fremder Apps steht kein Abmelden mehr
— es war dort ohnehin nur ein Link auf den Schreibtisch. Als einzige
Schaltflaeche, die eine Sitzung beendet, steht es jetzt an genau einer
Stelle: unten in den Einstellungen, sichtbar nur bei eigener Anmeldung.
Damit faellt auch der ?abmelden=1-Pfad wieder weg und /api/apps braucht
das oidc-Feld nicht mehr.

Das Zahnrad rechts in der Menueleiste ist durch den Pfeil mit der
App-Liste ersetzt — derselbe wie in der HoneyBar. Beide Leisten tragen
damit dieselben vier Dinge: Logo, Apps, Wetter/Uhr, Aufklappmenue.
This commit is contained in:
marc committed 2026-08-29 21:51:00 +02:00
1 parent 0a5cd94ba0
commit 6d75ce78c2
7 files changed
+136 -65

No files matched your search

+10 -2
View File
@@ -41,8 +41,16 @@ Eine Annahme von damals hat sich nicht gehalten: „ohne sie zu verlassen"
schreibt, und die HoneyBar darf in einer fremden App nicht schreiben — die
Sitzung kommt über die Site-Grenze nicht mit, und selbst unter einer Domain
liegt das CSRF-Prüfwort in einem Cookie, das nur HoneyOS' eigene Seiten lesen.
Also reicht die HoneyBar die Frage als `?einstellungen=1` / `?abmelden=1` an den
Schreibtisch weiter. Ein Klick, ein Seitenwechsel — dort, wo man angemeldet ist.
Also reicht die HoneyBar die Frage als `?einstellungen=1` an den Schreibtisch
weiter. Ein Klick, ein Seitenwechsel — dort, wo man angemeldet ist.
**Abmelden ist ganz aus den Leisten verschwunden** und steht unten in den
Einstellungen (Marcs Entscheidung, 29.08.2026). Aus fremden Apps war es ohnehin
nur ein Link auf den Schreibtisch gewesen; als einzige Schaltfläche, die eine
Sitzung beendet, gehört es an eine Stelle statt an drei. Im selben Zug ist das
Zahnrad aus der Menüleiste des Schreibtischs gefallen und durch den Pfeil mit
der App-Liste ersetzt worden — damit tragen beide Leisten wirklich dieselben
vier Dinge: Logo, Apps, Wetter/Uhr, Aufklappmenü.
**Was ohne Seitenwechsel ginge, wenn es je stört:** die reinen Aussehensschalter
(Röhre, Leuchten, Scanlines) direkt im Menü. Sie kämen ohne Prüfwort aus, wenn
+17 -14
View File
@@ -395,25 +395,28 @@ Das `■ HONEYOS` links ist ein Menü — auf dem Schreibtisch wie in jeder App:
| Schreibtisch | Über HoneyOS |
| Einstellungen … | Einstellungen |
| HoneyBar einklappen | Terminal-/Desktop-Modus |
| Abmelden | Abmelden |
Rechts daneben bleibt der Pfeil mit den Apps. Eines auf heißt anderes zu.
Rechts daneben klappt ein Pfeil die App-Liste auf — in beiden Leisten gleich.
Das Zahnrad, das dort früher stand, ist weg: Einstellungen stehen jetzt links
im Systemmenü, wo sie in jeder App auch stehen.
**In den Apps sind es Links auf den Schreibtisch, keine Handlungen** — und das
ist eine Grenze, keine Bequemlichkeit. Einstellungen und Abmelden schreiben, und
**In den Apps sind die Punkte Links auf den Schreibtisch, keine Handlungen** —
und das ist eine Grenze, keine Bequemlichkeit. Einstellungen schreiben, und
dafür braucht der Server die HoneyOS-Sitzung: Aus einer fremden Site schickt der
Browser sie nicht mit, und selbst unter einer Domain liegt das CSRF-Prüfwort in
einem Cookie, das nur HoneyOS' eigene Seiten lesen dürfen. Ein Abmelden, das ein
einfacher Aufruf erledigt, könnte außerdem jede beliebige Seite auslösen.
einem Cookie, das nur HoneyOS' eigene Seiten lesen dürfen. Also stellt die
HoneyBar die Frage in der Adresse (`?einstellungen=1`), und der Schreibtisch
beantwortet sie: Er macht dasselbe Feld auf, das auch `[S]` öffnet, und nimmt
den Anhang danach wieder aus der Adresse.
Also stellt die HoneyBar die Frage in der Adresse (`?einstellungen=1`,
`?abmelden=1`), und der Schreibtisch beantwortet sie: Er macht dasselbe
Einstellungsfeld auf, das auch `[S]` öffnet, und meldet mit einem echten POST
ab. Danach nimmt er den Anhang aus der Adresse wieder heraus.
`Abmelden` steht nur da, wenn HoneyOS überhaupt anmeldet — im offenen Betrieb
oder hinter einem Proxy fehlt der Punkt. Deshalb gibt `/api/apps` neben Liste
und Aussehen auch `oidc` heraus.
**Abmelden steht in den Einstellungen, ganz unten — und sonst nirgends.** Nicht
in der HoneyBar fremder Apps, nicht im Systemmenü, nicht im Startmenü. Es ist
die einzige Schaltfläche, die eine Sitzung beendet statt etwas einzustellen, und
sie gehört dorthin, wo man sich angemeldet hat. Wer es aus einer App heraus
braucht, geht über „Einstellungen …" — ein Klick mehr, dafür kann kein
eingebettetes Bild einer fremden Seite eine Abmeldung auslösen. Der Punkt
erscheint nur, wenn HoneyOS selbst anmeldet; hinter einem Proxy oder im offenen
Betrieb fehlt er.
## Wer angemeldet ist
-3
View File
@@ -316,9 +316,6 @@ func (s *Server) handleApps(w http.ResponseWriter, r *http.Request, sub string)
writeJSON(w, http.StatusOK, map[string]any{
"apps": s.store.Apps(),
"settings": s.store.SettingsFor(sub),
// Fuer das Systemmenue der HoneyBar: ohne Anbieter gibt es nichts
// abzumelden, dann steht der Punkt dort auch nicht.
"oidc": s.auth.Enabled(),
})
}
+19
View File
@@ -777,6 +777,25 @@ body {
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6);
}
.mb-logo[aria-expanded="true"] { background: var(--p-faint); }
/* Das App-Menü hängt am Pfeil rechts — sonst dasselbe Feld. */
.appmenu {
top: calc(32px + env(safe-area-inset-top) + 2px);
bottom: auto;
left: auto;
right: 6px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6);
}
/* Abmelden steht unten in den Einstellungen, abgesetzt vom Rest: Es ist die
einzige Schaltfläche dort, die nicht etwas einstellt, sondern etwas beendet. */
.field-out {
border-top: 1px solid var(--p-faint);
padding-top: 12px;
margin-top: 4px;
margin-bottom: 0;
}
.btn-out { width: 100%; color: var(--p-mid); }
.btn-out:hover { color: var(--bg); background: var(--p); }
.startmenu .sm-head {
padding: 4px 8px;
font-size: 10px;
+14 -25
View File
@@ -56,8 +56,6 @@
let apps = DEFAULT_APPS;
let settings = null;
// Ob HoneyOS ueberhaupt anmeldet — ohne Anbieter gaebe es nichts abzumelden.
let oidc = false;
// Was die Seite oben schon hatte, bevor die HoneyBar sie heruntergeschoben hat.
let urspruenglichesPolster = null;
@@ -222,8 +220,6 @@
<a id="mSettings" role="menuitem">Einstellungen &hellip;</a>
<hr />
<button id="hide" role="menuitem">HoneyBar einklappen</button>
<hr id="mLogoutSep" hidden />
<a id="mLogout" role="menuitem" hidden>Abmelden</a>
</div>
<div class="menu" id="menu" role="menu">
@@ -400,31 +396,25 @@
}
/**
* Die drei Wege ins System. Alle drei sind Links auf den Schreibtisch, keine
* Die zwei Wege ins System. Beides sind Links auf den Schreibtisch, keine
* Aktionen hier — und das ist keine Bequemlichkeit, sondern die Grenze:
*
* Einstellungen und Abmelden **schreiben**. Dafuer braucht der Server die
* HoneyOS-Sitzung, und die schickt der Browser aus einer fremden App nicht
* mit; liegt alles unter einer Domain, kommt sie zwar mit, aber das
* CSRF-Prüfwort steht in einem Cookie der HoneyOS-Adresse, das dieses Skript
* von hier aus nicht lesen darf. Und ein Abmelden, das ein simpler Aufruf
* erledigt, koennte jede fremde Seite ausloesen.
* Einstellungen **schreiben**. Dafuer braucht der Server die HoneyOS-Sitzung,
* und die schickt der Browser aus einer fremden App nicht mit; liegt alles
* unter einer Domain, kommt sie zwar mit, aber das CSRF-Prüfwort steht in
* einem Cookie der HoneyOS-Adresse, das dieses Skript von hier aus nicht
* lesen darf.
*
* Also: die HoneyBar zeigt den Weg, gehandelt wird dort, wo man angemeldet
* ist. Der Schreibtisch sieht die Frage in der Adresse und macht sie auf.
* Abmelden steht hier bewusst gar nicht: Es gehoert dorthin, wo man sich
* angemeldet hat. Wer es aus einer App heraus braucht, geht ueber
* „Einstellungen …" — dort unten steht der Knopf.
*/
function systemZiele() {
const ziel = (q) => {
const u = new URL(homeUrl, location.href);
u.search = q;
return u.href;
};
root.getElementById("mHome").href = new URL(homeUrl, location.href).href;
root.getElementById("mSettings").href = ziel("einstellungen=1");
const ab = root.getElementById("mLogout");
ab.href = ziel("abmelden=1");
ab.hidden = !oidc;
root.getElementById("mLogoutSep").hidden = !oidc;
const heim = new URL(homeUrl, location.href);
root.getElementById("mHome").href = heim.href;
const einst = new URL(homeUrl, location.href);
einst.search = "einstellungen=1";
root.getElementById("mSettings").href = einst.href;
}
/**
@@ -520,7 +510,6 @@
apps = [{ id: "honeyos", name: "DESKTOP", url: homeUrl }].concat(data.apps);
}
settings = data.settings || null;
oidc = !!data.oidc;
}
} catch {
/* nicht angemeldet, kein CORS, kein Netz → eingebaute Liste;
+4 -1
View File
@@ -39,7 +39,10 @@
<div class="mb-right">
<span class="mb-wx" id="mbWx" hidden></span>
<span class="mb-clock" id="clock">--:--</span>
<button class="mb-btn" id="btnSettings" title="Einstellungen (S)">&#9881;</button>
<!-- Derselbe Pfeil wie in der HoneyBar: die Apps zum Aufklappen,
fuer schmale Fenster, in denen der Streifen oben nicht alles
zeigt. Einstellungen stehen im Systemmenü links. -->
<button class="mb-btn" id="btnAppMenu" title="Apps" aria-haspopup="true" aria-expanded="false">&#9662;</button>
</div>
</header>
+72 -20
View File
@@ -590,7 +590,15 @@ function openSettings() {
<p class="login-note" style="margin-top:4px">
Diese Einstellungen liegen auf dem Server, nicht im Browser — auf jedem
Gerät, an dem du angemeldet bist, sieht HoneyOS gleich aus.
</p>`,
</p>` +
// Abmelden steht hier und nur hier: Es ist das Einzige, was man aus einer
// fremden App heraus nicht anklicken können soll (siehe honeybar.js), und
// von HoneyOS aus findet man es dort, wo alles andere auch steht.
(state.oidc
? `<div class="field field-out">
<button class="btn btn-out" id="btnLogout">⏻ Abmelden</button>
</div>`
: ""),
(root) => {
root.querySelectorAll(".seg").forEach((segEl) => {
segEl.addEventListener("click", (e) => {
@@ -662,6 +670,7 @@ function openSettings() {
});
root.querySelector("#btnApps").addEventListener("click", openApps);
root.querySelector("#btnLogout")?.addEventListener("click", logout);
},
420
);
@@ -791,6 +800,58 @@ function openAbout() {
/* ── Hilfsmittel ───────────────────────────────────────────────────────── */
/* ── App-Menü im Pfeil rechts ──────────────────────────────────────────────
* Der Zwilling des Pfeils in honeybar.js. Die Apps stehen zwar schon als
* Streifen in der Leiste, aber der schiebt bei vielen Apps seitwärts — das
* Menü zeigt sie alle untereinander. Das Zahnrad ist dafür weggefallen:
* Einstellungen stehen im Systemmenü links, wie in jeder App.
*/
function buildAppMenu() {
const menu = document.createElement("div");
menu.className = "startmenu appmenu";
menu.id = "appmenu";
$("desktop").appendChild(menu);
const btn = $("btnAppMenu");
btn.addEventListener("click", (e) => {
e.stopPropagation();
if (menu.dataset.open === "1") return closeAppMenu();
closeSystemMenu();
closeStartMenu();
menu.innerHTML =
`<div class="sm-head">wechseln zu</div>` +
(state.cfg.apps.length
? state.cfg.apps.map((a) => `<button data-app="${escapeAttr(a.id)}">${escapeHtml(a.name)}</button>`).join("")
: `<button data-settings="1">keine app eingetragen …</button>`);
menu.querySelectorAll("[data-app]").forEach((b) =>
b.addEventListener("click", () => {
const app = state.cfg.apps.find((a) => a.id === b.dataset.app);
closeAppMenu();
if (app) launch(app);
})
);
menu.querySelector("[data-settings]")?.addEventListener("click", () => {
closeAppMenu();
openApps();
});
menu.dataset.open = "1";
btn.setAttribute("aria-expanded", "true");
});
document.addEventListener("click", closeAppMenu);
}
function closeAppMenu() {
const menu = $("appmenu");
if (!menu) return;
menu.dataset.open = "0";
$("btnAppMenu").setAttribute("aria-expanded", "false");
}
/* ── 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
@@ -809,6 +870,7 @@ function buildSystemMenu() {
e.stopPropagation();
if (menu.dataset.open === "1") return closeSystemMenu();
closeStartMenu();
closeAppMenu();
const terminal = state.settings.mode === "terminal";
menu.innerHTML =
@@ -816,8 +878,7 @@ function buildSystemMenu() {
<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>` : "");
<button data-do="mode">${terminal ? "▣ Desktop-Modus" : "↩ Terminal-Modus"}</button>`;
menu.querySelectorAll("[data-do]").forEach((b) =>
b.addEventListener("click", () => {
@@ -826,7 +887,6 @@ function buildSystemMenu() {
about: openAbout,
settings: openSettings,
mode: () => setMode(terminal ? "desktop" : "terminal"),
logout,
})[b.dataset.do]();
})
);
@@ -858,10 +918,11 @@ 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.
// Zwei offene Menüs sähen aus wie ein Fehler: Der Klick auf den Startknopf
// hält sein Ereignis auf, also erreicht es die Schließer der anderen nicht
// von selbst.
closeSystemMenu();
closeAppMenu();
menu.innerHTML = `<div class="sm-head">starten</div>` +
state.cfg.apps.map((a) => `<button data-app="${a.id}">${escapeHtml(a.name)}</button>`).join("") +
@@ -869,10 +930,7 @@ 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>` +
// 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>` : "");
<hr /><button data-mode="terminal">↩ Terminal-Modus</button>`;
menu.querySelectorAll("[data-app]").forEach((b) =>
b.addEventListener("click", () => {
@@ -893,7 +951,6 @@ function buildStartMenu() {
closeStartMenu();
setMode("terminal");
});
menu.querySelector("[data-logout]")?.addEventListener("click", logout);
menu.dataset.open = "1";
btn.setAttribute("aria-expanded", "true");
@@ -981,13 +1038,6 @@ 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");
@@ -1015,11 +1065,11 @@ async function main() {
renderShell();
buildStartMenu();
buildSystemMenu();
buildAppMenu();
probeApps();
setInterval(tickClock, 15000);
setInterval(probeApps, 120000);
$("btnSettings").addEventListener("click", openSettings);
$("btnClose").addEventListener("click", closeModal);
$("modal").addEventListener("click", (e) => {
if (e.target === $("modal")) closeModal();
@@ -1044,6 +1094,8 @@ async function main() {
if (menu && menu.dataset.open === "1") return closeStartMenu();
const sys = $("sysmenu");
if (sys && sys.dataset.open === "1") return closeSystemMenu();
const apps = $("appmenu");
if (apps && apps.dataset.open === "1") return closeAppMenu();
return state.settings.mode === "desktop" ? closeTopWindow() : closeModal();
}
if (typing) return;