feat: Leiste trägt die Röhre vom Server, Einklappen ist umkehrbar

Zwei Sachen, die beim Benutzen aufgefallen sind.

- "leiste ausblenden" war eine Einbahnstraße: die Leiste verschwand ganz und
  liess sich nur über die Entwicklerkonsole zurückholen. Jetzt bleibt ein
  3px-Strich stehen, ein Klick darauf holt sie wieder. ?honeybar=1 an der
  Adresse ist der Notausgang, falls der Strich übersehen wird.
- Die Leiste hatte ihre Farben fest eingetragen und blieb bernsteinfarben,
  auch wenn der Schreibtisch auf grün stand. Genau dieser Bruch liess sie wie
  etwas Angeklebtes wirken. /api/apps liefert deshalb jetzt {apps, settings};
  die Leiste setzt Röhre, Glühen und Scanlines danach.

Dass das auch über Site-Grenzen geht, war so nicht erwartet: der Schlüssel
für die App-Liste trägt das Aussehen einfach mit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-08-24 22:37:06 +02:00
1 parent 1c3218939b
commit 5ed4e30179
5 files changed
+191 -41

No files matched your search

+122 -25
View File
@@ -53,6 +53,7 @@
const appsKey = d("key", "");
let apps = DEFAULT_APPS;
let settings = null;
/* ── Aufbau ───────────────────────────────────────────────────────────── */
@@ -69,55 +70,66 @@
root.innerHTML = `
<style>
:host, * { box-sizing: border-box; }
/* Dieselben Röhren wie auf dem Schreibtisch. Welche gilt, sagt der
Server; bis seine Antwort da ist, leuchtet Bernstein. */
:host {
--p: #ffb641;
--p-mid: #c8842a;
--p-dim: #6b4718;
--p-faint: #2a1c0c;
--bg: #140d07;
--glow: 0 0 5px rgba(255, 182, 65, .4);
--scan: rgba(0, 0, 0, .13);
}
.bar {
display: flex; align-items: center; gap: 10px;
height: ${BAR_H}px; padding: 0 8px;
background: #140d07;
border-bottom: 1px solid #6b4718;
color: #ffb641;
background: var(--bg);
border-bottom: 1px solid var(--p-dim);
color: var(--p);
font: 12px/1 ui-monospace, "SF Mono", "DejaVu Sans Mono", Menlo, Consolas, monospace;
text-shadow: 0 0 5px rgba(255,182,65,.4);
text-shadow: var(--glow);
user-select: none;
}
.bar::after {
/* Scanlines, damit die Leiste zum Desktop gehört */
content: ""; position: absolute; inset: 0; pointer-events: none;
background: repeating-linear-gradient(to bottom, rgba(0,0,0,.13) 0 1px, transparent 1px 3px);
background: repeating-linear-gradient(to bottom, var(--scan) 0 1px, transparent 1px 3px);
}
a, button {
font: inherit; color: inherit; background: none;
border: 1px solid transparent; cursor: pointer;
text-decoration: none; padding: 3px 9px; white-space: nowrap;
}
.home { letter-spacing: .14em; color: #ffb641; padding-left: 2px; }
.home b { color: #c8842a; font-weight: 400; }
.home:hover { border-color: #6b4718; }
.home { letter-spacing: .14em; color: var(--p); padding-left: 2px; }
.home b { color: var(--p-mid); font-weight: 400; }
.home:hover { border-color: var(--p-dim); }
.apps { display: flex; gap: 4px; flex: 1; overflow-x: auto; scrollbar-width: none; }
.apps::-webkit-scrollbar { display: none; }
.app { color: #c8842a; letter-spacing: .08em; }
.app:hover { color: #ffb641; border-color: #6b4718; }
.app { color: var(--p-mid); letter-spacing: .08em; }
.app:hover { color: var(--p); border-color: var(--p-dim); }
.app[aria-current="page"] {
background: #ffb641; border-color: #ffb641; color: #140d07; text-shadow: none;
background: var(--p); border-color: var(--p); color: var(--bg); text-shadow: none;
}
.right { display: flex; align-items: center; gap: 8px; }
.user { color: #ffb641; max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user { color: var(--p); max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user[hidden] { display: none; }
.clock { color: #c8842a; font-variant-numeric: tabular-nums; }
.menu-btn { border-color: #6b4718; line-height: 1; padding: 3px 7px; }
.menu-btn:hover { background: #2a1c0c; }
.clock { color: var(--p-mid); font-variant-numeric: tabular-nums; }
.menu-btn { border-color: var(--p-dim); line-height: 1; padding: 3px 7px; }
.menu-btn:hover { background: var(--p-faint); }
.menu {
position: absolute; top: ${BAR_H + 2}px; right: 6px; min-width: 190px;
background: #140d07; border: 1px solid #6b4718; padding: 4px;
background: var(--bg); border: 1px solid var(--p-dim); padding: 4px;
display: none; flex-direction: column; gap: 2px;
box-shadow: 0 10px 30px rgba(0,0,0,.6);
}
.menu[data-open="1"] { display: flex; }
.menu a, .menu button { text-align: left; padding: 6px 8px; color: #c8842a; }
.menu a:hover, .menu button:hover { background: #2a1c0c; color: #ffb641; }
.menu hr { border: 0; border-top: 1px solid #2a1c0c; margin: 3px 0; }
.menu a, .menu button { text-align: left; padding: 6px 8px; color: var(--p-mid); }
.menu a:hover, .menu button:hover { background: var(--p-faint); color: var(--p); }
.menu hr { border: 0; border-top: 1px solid var(--p-faint); margin: 3px 0; }
.menu .head {
padding: 4px 8px; font-size: 10px; letter-spacing: .18em;
text-transform: uppercase; color: #6b4718;
text-transform: uppercase; color: var(--p-dim);
}
@media (max-width: 560px) { .clock { display: none; } }
</style>
@@ -136,9 +148,35 @@
<div class="head">wechseln zu</div>
<div id="menuApps"></div>
<hr />
<button id="hide" role="menuitem">leiste ausblenden</button>
<button id="hide" role="menuitem">leiste einklappen</button>
</div>`;
/* ── Aussehen ─────────────────────────────────────────────────────────────
* Dieselben drei Röhren wie in style.css. Sie hier zu wiederholen ist die
* bewusste Entscheidung: die Leiste soll ohne zweite Datei auskommen, damit
* sie in jeder fremden App mit einer Zeile funktioniert.
*/
const ROEHREN = {
amber: { p: "#ffb641", mid: "#c8842a", dim: "#6b4718", faint: "#2a1c0c", bg: "#140d07", glow: "255,182,65" },
green: { p: "#5cff8f", mid: "#2fa85c", dim: "#17512c", faint: "#08200f", bg: "#071206", glow: "92,255,143" },
ice: { p: "#cfe8ff", mid: "#7fa6c4", dim: "#395366", faint: "#12202a", bg: "#0a1118", glow: "207,232,255" },
};
function applySettings(s) {
if (!s) return;
const t = ROEHREN[s.phosphor] || ROEHREN.amber;
const bar = root.querySelector(".bar");
if (!bar) return;
const set = (k, v) => bar.parentNode.host.style.setProperty(k, v);
set("--p", t.p);
set("--p-mid", t.mid);
set("--p-dim", t.dim);
set("--p-faint", t.faint);
set("--bg", t.bg);
set("--glow", s.glow === "off" ? "none" : `0 0 5px rgba(${t.glow},.4)`);
set("--scan", s.scanlines === "off" ? "transparent" : "rgba(0,0,0,.13)");
}
/* ── Verhalten ────────────────────────────────────────────────────────── */
function render() {
@@ -219,6 +257,7 @@
}
render();
applySettings(settings);
clock();
setInterval(clock, 20000);
@@ -250,9 +289,57 @@
/* dann eben nur für diesen Aufruf */
}
unmount();
mountNub();
});
}
/**
* Eingeklappt bleibt ein 3 px hoher Strich am oberen Rand stehen. Er nimmt
* praktisch keinen Platz, liegt über dem Inhalt statt ihn zu schieben — und
* ist der Weg zurück. Ganz verschwinden darf die Leiste nicht: wer sie einmal
* weggeklickt hat, käme sonst nur noch über die Entwicklerkonsole an sie heran.
*/
let nub = null;
function mountNub() {
if (nub) return;
nub = document.createElement("honey-bar-nub");
nub.style.cssText = [
"position:fixed", "top:0", "left:0", "right:0", "height:3px",
"z-index:2147483000", "cursor:pointer", "display:block",
].join(";");
nub.title = "HoneyOS-Leiste einblenden";
const r = nub.attachShadow({ mode: "open" });
r.innerHTML = `
<style>
.strich {
height: 3px;
background: linear-gradient(90deg, #6b4718, #ffb641 50%, #6b4718);
opacity: .55;
transition: opacity .15s, height .15s;
}
:host(:hover) .strich { opacity: 1; height: 3px; }
</style>
<div class="strich"></div>`;
nub.addEventListener("click", () => {
forget();
nub.remove();
nub = null;
mount();
showUser();
});
document.body.appendChild(nub);
}
function forget() {
try {
localStorage.removeItem(LS_HIDDEN);
} catch {
/* dann gilt es nur für diesen Aufruf */
}
}
function unmount() {
host.remove();
if (pushMode === "body") {
@@ -269,7 +356,16 @@
} catch {
/* kein Speicher, Leiste bleibt sichtbar */
}
if (hidden) return;
// ?honeybar=1 holt sie zurück, falls der Strich einmal übersehen wird.
if (hidden && new URLSearchParams(location.search).get("honeybar") === "1") {
hidden = false;
forget();
}
if (hidden) {
if (document.body) mountNub();
else document.addEventListener("DOMContentLoaded", mountNub, { once: true });
return;
}
if (appsUrl) {
try {
@@ -281,10 +377,11 @@
headers: appsKey ? { "X-HoneyOS-Key": appsKey } : {},
});
if (res.ok) {
const list = await res.json();
if (Array.isArray(list) && list.length) {
apps = [{ id: "honeyos", name: "DESKTOP", url: homeUrl }].concat(list);
const data = await res.json();
if (Array.isArray(data.apps) && data.apps.length) {
apps = [{ id: "honeyos", name: "DESKTOP", url: homeUrl }].concat(data.apps);
}
settings = data.settings || null;
}
} catch {
/* nicht angemeldet, kein CORS, kein Netz → eingebaute Liste;