feat: HoneyOS – Retro-Schreibtisch und Leiste für die eigenen Apps

Statische Oberfläche vor Aether und InkNote: kein Backend, kein Build,
nginx liefert web/ aus. Jede App bleibt ihre eigene PWA auf ihrer eigenen
Domain – HoneyOS ist Startpunkt und Umschalter, kein iframe-Rahmen.

- zwei Betriebsarten aus einem Datenstand: Terminal (Kacheln, Tastatur)
  und Desktop (Symbole, verschiebbare Fenster, Taskleiste)
- honeybar.js als Drop-in für die Apps, rendert im Shadow DOM, damit
  Tailwind und die InkNote-Palette einander nicht in die Quere kommen
- Konfiguration vollständig aus der .env: das Skript in
  /docker-entrypoint.d/ schreibt config.json beim Containerstart
- optionaler _ME-Pfad pro App zeigt an, wer angemeldet ist; ohne CORS
  bleibt die Anzeige leer, ohne dass etwas kaputtgeht

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-08-24 14:45:55 +02:00
commit 84cc5d619e
17 files changed
+3126

No files matched your search

+26
View File
@@ -0,0 +1,26 @@
{
"user": "marc",
"hostname": "honeyos",
"version": "0.1",
"apps": [
{
"id": "aether",
"name": "AETHER",
"subtitle": "Cloud & Dateien",
"url": "https://cloud.honeyy.duckdns.org",
"glyph": "cloud",
"key": "1",
"me": "/api/v1/auth/me"
},
{
"id": "inknote",
"name": "INKNOTE",
"subtitle": "Notizen & Zeichnen",
"url": "https://notes.honeyy.duckdns.org",
"glyph": "note",
"key": "2",
"me": "/api/auth/me",
"tokenKey": "inknote_token"
}
]
}
+41
View File
@@ -0,0 +1,41 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>HoneyBar — Vorschau in einer fremden App</title>
<style>
/* Bewusst NICHT im HoneyOS-Stil: hier wird geprüft, dass die Leiste
eine ganz andere App nicht anfasst und von ihr nicht angefasst wird. */
body {
margin: 0;
background: #0d1117;
color: #e6edf3;
font: 15px/1.6 system-ui, sans-serif;
}
header.fake {
padding: 14px 20px;
background: #161b22;
border-bottom: 1px solid #30363d;
font-weight: 600;
}
main { padding: 24px 20px; max-width: 620px; }
code { background: #161b22; padding: 2px 5px; border-radius: 4px; font-size: 13px; }
/* So rechnet eine App mit Vollhöhe die Leiste heraus: */
.full { height: calc(100vh - var(--honeybar-h, 0px)); }
</style>
</head>
<body>
<header class="fake">Irgendeine App (dunkles Theme, eigene Schrift)</header>
<main>
<p>Die Leiste oben kommt aus <code>honeybar.js</code> und gehört nicht zu dieser Seite.</p>
<p>
Sie liegt in einem Shadow DOM: nichts von hier färbt auf sie ab, nichts von ihr auf
diese Seite. <code>data-app="inknote"</code> markiert, wo man gerade ist.
</p>
<p>Eine App, die mit <code>100vh</code> rechnet, nimmt <code>--honeybar-h</code> ab.</p>
</main>
<script src="./honeybar.js" data-app="inknote" data-home="../index.html"></script>
</body>
</html>
+290
View File
@@ -0,0 +1,290 @@
/*
* HoneyBar — die HoneyOS-Leiste zum Einhängen in eine fremde App.
*
* <script src="https://os.honeyy.duckdns.org/honeybar/honeybar.js"
* data-app="aether"></script>
*
* Eine Zeile, kein Build, keine Abhängigkeit. Die Leiste rendert in einem
* Shadow DOM — die Stile der Gastgeber-App kommen nicht rein, unsere nicht
* raus. Das ist der ganze Grund für die Konstruktion: Aether ist React +
* Tailwind, InkNote React + eigene Palette, und beide sollen von der Leiste
* nichts merken außer den 32 Pixeln oben.
*
* data-Attribute am <script>:
* data-app (Pflicht) id der App, in der die Leiste steckt
* data-apps URL zu einer config.json; ohne das gilt die Liste unten
* data-home URL des HoneyOS-Desktops
* data-push "body" (Standard) schiebt den Seiteninhalt nach unten,
* "none" legt die Leiste darüber
*
* Wichtig zu wissen: localStorage ist pro Domain. Was auf os.honeyy… steht,
* sieht diese Leiste auf notes.honeyy… nicht — die Leiste ist ein Schalter,
* kein geteilter Zustand. Gemeinsame Sitzung wäre echtes SSO und ist eine
* eigene Baustelle.
*/
(function () {
"use strict";
if (window.__honeyBar) return; // zweimal eingebunden ist kein Fehler
window.__honeyBar = true;
const script = document.currentScript;
const d = (k, fb) => (script && script.dataset[k]) || fb;
const BAR_H = 32;
const LS_HIDDEN = "honeybar.hidden";
const DEFAULT_APPS = [
{ id: "honeyos", name: "DESKTOP", url: "https://os.honeyy.duckdns.org" },
{ id: "aether", name: "AETHER", url: "https://cloud.honeyy.duckdns.org" },
{ id: "inknote", name: "INKNOTE", url: "https://notes.honeyy.duckdns.org" },
];
const current = d("app", "");
const homeUrl = d("home", "https://os.honeyy.duckdns.org");
const pushMode = d("push", "body");
// Ohne data-apps holt die Leiste die Konfiguration neben ihrem eigenen
// Skript — dieselbe Datei, die der Desktop liest.
const appsUrl = d("apps", script ? new URL("../config.json", script.src).href : "");
let apps = DEFAULT_APPS;
/* ── Aufbau ───────────────────────────────────────────────────────────── */
const host = document.createElement("honey-bar");
host.style.cssText = [
"position:fixed", "top:0", "left:0", "right:0",
"height:" + BAR_H + "px",
"z-index:2147483000",
"display:block",
"color-scheme:dark",
].join(";");
const root = host.attachShadow({ mode: "open" });
root.innerHTML = `
<style>
:host, * { box-sizing: border-box; }
.bar {
display: flex; align-items: center; gap: 10px;
height: ${BAR_H}px; padding: 0 8px;
background: #140d07;
border-bottom: 1px solid #6b4718;
color: #ffb641;
font: 12px/1 ui-monospace, "SF Mono", "DejaVu Sans Mono", Menlo, Consolas, monospace;
text-shadow: 0 0 5px rgba(255,182,65,.4);
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);
}
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; }
.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[aria-current="page"] {
background: #ffb641; border-color: #ffb641; color: #140d07; 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[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; }
.menu {
position: absolute; top: ${BAR_H + 2}px; right: 6px; min-width: 190px;
background: #140d07; border: 1px solid #6b4718; 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 .head {
padding: 4px 8px; font-size: 10px; letter-spacing: .18em;
text-transform: uppercase; color: #6b4718;
}
@media (max-width: 560px) { .clock { display: none; } }
</style>
<div class="bar">
<a class="home" id="home" title="HoneyOS">&#9632; HONEY<b>OS</b></a>
<nav class="apps" id="apps"></nav>
<div class="right">
<span class="user" id="user" hidden></span>
<span class="clock" id="clock"></span>
<button class="menu-btn" id="menuBtn" aria-haspopup="true" aria-expanded="false">&#9662;</button>
</div>
</div>
<div class="menu" id="menu" role="menu">
<div class="head">wechseln zu</div>
<div id="menuApps"></div>
<hr />
<button id="hide" role="menuitem">leiste ausblenden</button>
</div>`;
/* ── Verhalten ────────────────────────────────────────────────────────── */
function render() {
const nav = root.getElementById("apps");
const menuApps = root.getElementById("menuApps");
nav.innerHTML = "";
menuApps.innerHTML = "";
apps.forEach((app) => {
if (app.id === "honeyos") return; // steckt schon links im Logo
const a = document.createElement("a");
a.className = "app";
a.href = app.url;
a.textContent = app.name;
if (app.id === current) a.setAttribute("aria-current", "page");
nav.appendChild(a);
const m = document.createElement("a");
m.href = app.url;
m.setAttribute("role", "menuitem");
m.textContent = (app.id === current ? "▸ " : "  ") + app.name;
menuApps.appendChild(m);
});
}
/**
* Zeigt an, wer angemeldet ist. Die Leiste steckt in der App selbst, der
* Aufruf geht also an denselben Ursprung — kein CORS, keine Freigabe nötig.
* Antwortet die App nicht wie erwartet, bleibt das Feld leer und niemand
* merkt etwas: die Anzeige ist eine Zugabe, keine Zusicherung.
*/
async function showUser() {
const me = apps.find((a) => a.id === current);
if (!me || !me.me) return;
try {
const headers = {};
if (me.tokenKey) {
const tok = localStorage.getItem(me.tokenKey);
if (tok) headers.Authorization = "Bearer " + tok;
}
const res = await fetch(url(me, me.me), { credentials: "same-origin", cache: "no-store", headers });
if (!res.ok) return;
const data = await res.json();
const u = (data && (data.user || data.profile)) || data || {};
const name = u.display_name || u.displayName || u.name || u.username || u.preferred_username;
if (!name) return;
const el = root.getElementById("user");
el.textContent = name;
el.hidden = false;
} catch {
/* nicht angemeldet, kein Endpunkt, kein Netz — alles kein Grund zu lärmen */
}
}
function url(app, path) {
if (!path) return app.url;
if (/^https?:\/\//i.test(path)) return path;
return app.url.replace(/\/+$/, "") + (path.startsWith("/") ? path : "/" + path);
}
function clock() {
root.getElementById("clock").textContent = new Date().toLocaleTimeString("de-DE", {
hour: "2-digit",
minute: "2-digit",
});
}
function mount() {
document.body.appendChild(host);
// Der Seite Platz verschaffen. `--honeybar-h` steht bereit, falls eine App
// ihre eigene 100vh-Rechnung anpassen muss (siehe README).
document.documentElement.style.setProperty("--honeybar-h", BAR_H + "px");
if (pushMode === "body") {
const cs = getComputedStyle(document.body);
document.body.style.paddingTop = parseFloat(cs.paddingTop || 0) + BAR_H + "px";
}
render();
clock();
setInterval(clock, 20000);
root.getElementById("home").href = homeUrl;
const menu = root.getElementById("menu");
const btn = root.getElementById("menuBtn");
btn.addEventListener("click", (e) => {
e.stopPropagation();
const open = menu.dataset.open === "1";
menu.dataset.open = open ? "0" : "1";
btn.setAttribute("aria-expanded", String(!open));
});
// Klick daneben schließt — das Ereignis muss die Grenze des Shadow DOM
// in beide Richtungen überleben, deshalb beide Wurzeln.
const close = () => {
menu.dataset.open = "0";
btn.setAttribute("aria-expanded", "false");
};
document.addEventListener("click", close);
root.addEventListener("click", (e) => {
if (!e.composedPath().includes(menu) && e.target !== btn) close();
});
root.getElementById("hide").addEventListener("click", () => {
try {
localStorage.setItem(LS_HIDDEN, "1");
} catch {
/* dann eben nur für diesen Aufruf */
}
unmount();
});
}
function unmount() {
host.remove();
if (pushMode === "body") {
const cs = getComputedStyle(document.body);
document.body.style.paddingTop = Math.max(0, parseFloat(cs.paddingTop || 0) - BAR_H) + "px";
}
document.documentElement.style.removeProperty("--honeybar-h");
}
async function start() {
let hidden = false;
try {
hidden = localStorage.getItem(LS_HIDDEN) === "1";
} catch {
/* kein Speicher, Leiste bleibt sichtbar */
}
if (hidden) return;
if (appsUrl) {
try {
const res = await fetch(appsUrl, { cache: "no-store" });
if (res.ok) {
const cfg = await res.json();
if (Array.isArray(cfg.apps) && cfg.apps.length) {
apps = [{ id: "honeyos", name: "DESKTOP", url: homeUrl }].concat(cfg.apps);
}
}
} catch {
/* Liste nicht erreichbar → eingebaute Liste, Leiste funktioniert trotzdem */
}
}
if (document.body) mount();
else document.addEventListener("DOMContentLoaded", mount, { once: true });
showUser();
}
start();
})();
+86
View File
@@ -0,0 +1,86 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="theme-color" content="#0a0705" />
<title>HoneyOS</title>
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect width='16' height='16' fill='%230a0705'/%3E%3Cpath d='M4 4h2v3h4V4h2v8h-2V9H6v3H4z' fill='%23ffb641'/%3E%3C/svg%3E" />
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<!-- Bildschirm: alles liegt darin, damit Scanlines/Vignette darüber passen -->
<div class="crt" id="screen">
<!-- ── Boot ───────────────────────────────────────────────────────── -->
<div class="boot" id="boot" hidden>
<pre class="boot-log" id="bootLog" aria-live="polite"></pre>
<p class="boot-hint">[ beliebige Taste = überspringen ]</p>
</div>
<!-- ── Desktop ────────────────────────────────────────────────────── -->
<div class="desktop" id="desktop" hidden>
<!-- Menüleiste: dieselbe Anatomie wie honeybar.js in den Apps -->
<header class="menubar">
<button class="mb-logo" id="btnAbout" title="Über HoneyOS">
<span class="hex">&#9632;</span> HONEY<b>OS</b>
</button>
<nav class="mb-apps" id="mbApps" aria-label="Anwendungen"></nav>
<div class="mb-right">
<span class="mb-user" id="mbUser">marc</span>
<span class="mb-clock" id="clock">--:--</span>
<button class="mb-btn" id="btnSettings" title="Einstellungen (S)">&#9881;</button>
</div>
</header>
<main class="stage" id="stage">
<!-- Statusfenster -->
<section class="win win-status">
<div class="win-bar"><span class="win-title">system</span><span class="win-dots">&#9679;&#9679;&#9679;</span></div>
<div class="win-body">
<p class="greet" id="greet">Willkommen zurück.</p>
<dl class="statlist" id="statList"></dl>
</div>
</section>
<!-- App-Kacheln -->
<section class="tiles" id="tiles" aria-label="Apps starten"></section>
</main>
<!-- Desktop-Modus: Symbolfläche, Fenster, Taskleiste -->
<div class="deskspace" id="deskspace" hidden>
<div class="desk-icons" id="deskIcons"></div>
<div class="windows" id="windows"></div>
</div>
<footer class="taskbar" id="taskbar" hidden>
<button class="task-start" id="btnStart">&#9632; START</button>
<div class="task-list" id="taskList"></div>
<span class="task-date" id="taskDate"></span>
</footer>
<footer class="statusbar" id="statusbar">
<span id="hint">[1] [2] app starten &nbsp;·&nbsp; [M] modus &nbsp;·&nbsp; [S] einstellungen &nbsp;·&nbsp; [?] hilfe</span>
<span class="sb-right" id="sbRight"></span>
</footer>
</div>
<!-- ── Dialoge ────────────────────────────────────────────────────── -->
<div class="modal" id="modal" hidden>
<div class="win win-modal" role="dialog" aria-modal="true" aria-labelledby="modalTitle">
<div class="win-bar">
<span class="win-title" id="modalTitle">einstellungen</span>
<button class="win-close" id="btnClose" aria-label="Schließen">&times;</button>
</div>
<div class="win-body" id="modalBody"></div>
</div>
</div>
<!-- CRT-Effekte: rein dekorativ, liegen über allem -->
<div class="scanlines" aria-hidden="true"></div>
<div class="vignette" aria-hidden="true"></div>
<div class="flicker" aria-hidden="true"></div>
</div>
<script src="./os.js"></script>
</body>
</html>
+184
View File
@@ -0,0 +1,184 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>HoneyOS — Look: System 7</title>
<style>
/* ── Mac System 7 ────────────────────────────────────────────────────
Hellgrau, 1 Bit, alles mit 1px schwarzer Kante. Keine Farbe außer
der Auswahl. Schrift klein und eng — Chicago gibt es nicht mehr,
Geneva/Verdana kommt am nächsten. */
* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
font: 12px/1.35 Geneva, Verdana, "DejaVu Sans", sans-serif;
color: #000;
/* der geditherte Schreibtisch: 2px-Schachbrett */
background: repeating-conic-gradient(#8a8a8a 0 25%, #d0d0d0 0 50%) 0 0 / 3px 3px;
overflow: hidden;
-webkit-font-smoothing: none;
}
/* Menüleiste */
.menubar {
display: flex; align-items: center; gap: 18px;
height: 21px; padding: 0 10px;
background: #fff; border-bottom: 1px solid #000;
position: relative; z-index: 10;
}
.menubar .apple { font-weight: 700; letter-spacing: -0.5px; }
.menubar .item { cursor: default; }
.menubar .item:hover { background: #000; color: #fff; }
.menubar .right { margin-left: auto; display: flex; gap: 14px; align-items: center; }
/* Fenster */
.win {
position: absolute;
background: #fff;
border: 1px solid #000;
box-shadow: 2px 2px 0 rgba(0,0,0,.45);
}
.titlebar {
display: flex; align-items: center; gap: 6px;
height: 19px; padding: 0 5px;
border-bottom: 1px solid #000;
/* die sechs Streifen links und rechts des Titels */
background: repeating-linear-gradient(to bottom, #000 0 1px, #fff 1px 2px);
}
.titlebar .box {
width: 11px; height: 11px; background: #fff; border: 1px solid #000; flex: none;
}
.titlebar .name {
background: #fff; padding: 0 7px; font-weight: 700; white-space: nowrap;
margin: 0 auto; letter-spacing: -0.2px;
}
.win-body { padding: 10px 12px; }
/* Statusfenster */
#status { left: 26px; top: 44px; width: 268px; }
#status h1 { font-size: 12px; font-weight: 700; margin: 0 0 8px; }
.rows { display: grid; grid-template-columns: 1fr auto; gap: 2px 10px; }
.rows span:nth-child(odd) { color: #444; }
.rows span:nth-child(even) { font-weight: 700; }
.sep { border: 0; border-top: 1px solid #000; margin: 9px 0; }
.note { color: #555; font-size: 11px; }
/* Fenster mit den Programmen — ein Finder-Fenster, kein Kachelraster */
#apps { left: 330px; top: 44px; width: 372px; }
.shelf { display: flex; gap: 6px; padding: 14px 10px 18px; }
.icon {
width: 104px; padding: 6px 4px 4px; text-align: center; cursor: default;
border: 1px solid transparent;
}
.icon svg { display: block; margin: 0 auto 5px; image-rendering: pixelated; }
.icon .label { display: inline-block; padding: 0 3px; }
.icon:hover .label { background: #000; color: #fff; }
.icon.sel svg { filter: invert(1); }
.icon.sel .label { background: #000; color: #fff; }
.shelf-foot {
border-top: 1px solid #000; padding: 2px 8px; font-size: 11px; color: #333;
display: flex; justify-content: space-between;
}
/* Leiste, wie sie in Aether/InkNote aussähe */
#bar { left: 26px; top: 296px; width: 676px; }
.bardemo {
display: flex; align-items: center; gap: 14px;
height: 21px; padding: 0 8px; background: #fff; border-bottom: 1px solid #000;
font-size: 11px;
}
.bardemo .cur { background: #000; color: #fff; padding: 0 5px; }
.bardemo .right { margin-left: auto; }
.barhost { padding: 12px; color: #333; font-size: 11px; background: #efefef; }
/* Papierkorb unten rechts, weil ohne den ist es kein Mac */
.trash { position: absolute; right: 30px; bottom: 26px; width: 90px; text-align: center; }
.trash .label { background: #d0d0d0; padding: 0 3px; }
.switch {
position: absolute; left: 26px; bottom: 20px;
font-size: 11px; background: #d0d0d0; padding: 2px 6px; border: 1px solid #000;
}
.switch a { color: #000; }
</style>
</head>
<body>
<div class="menubar">
<span class="apple">&#9670;</span>
<span class="item">Ablage</span>
<span class="item">Bearbeiten</span>
<span class="item">Ansicht</span>
<span class="item">Spezial</span>
<span class="right"><span class="item">Di. 02:47</span><span class="item">HoneyOS &#9662;</span></span>
</div>
<!-- Statusfenster -->
<div class="win" id="status">
<div class="titlebar"><span class="box"></span><span class="name">HoneyOS</span></div>
<div class="win-body">
<h1>Willkommen zurück, marc.</h1>
<div class="rows">
<span>Programme</span><span>2</span>
<span>erreichbar</span><span>2 von 2</span>
<span>Version</span><span>0.1</span>
</div>
<hr class="sep" />
<p class="note">Jedes Programm läuft auf seiner eigenen Domain. HoneyOS öffnet sie nur.</p>
</div>
</div>
<!-- Programme -->
<div class="win" id="apps">
<div class="titlebar"><span class="box"></span><span class="name">Programme</span><span class="box"></span></div>
<div class="shelf">
<div class="icon sel">
<svg width="40" height="40" viewBox="0 0 20 20" shape-rendering="crispEdges">
<path fill="#000" d="M6 5h8v1h1v1h1v1h1v6h-1v1H3v-1H2V9h1V8h1V7h1V6h1z"/>
<path fill="#fff" d="M6 6h8v1h1v1h1v5H3V9h1V8h1V7h1z"/>
</svg>
<span class="label">Aether</span>
</div>
<div class="icon">
<svg width="40" height="40" viewBox="0 0 20 20" shape-rendering="crispEdges">
<path fill="#000" d="M4 2h12v16H4z"/>
<path fill="#fff" d="M5 3h10v14H5z"/>
<path fill="#000" d="M7 5h6v1H7zm0 3h6v1H7zm0 3h6v1H7zm0 3h4v1H7z"/>
</svg>
<span class="label">InkNote</span>
</div>
<div class="icon">
<svg width="40" height="40" viewBox="0 0 20 20" shape-rendering="crispEdges">
<path fill="#000" d="M9 4h2v5h5v2h-5v5H9v-5H4V9h5z"/>
</svg>
<span class="label">hinzufügen</span>
</div>
</div>
<div class="shelf-foot"><span>3 Objekte</span><span>alles bereit</span></div>
</div>
<!-- Leiste -->
<div class="win" id="bar">
<div class="titlebar"><span class="box"></span><span class="name">so sähe die Leiste in InkNote aus</span></div>
<div class="bardemo">
<b>&#9670; HoneyOS</b>
<span>Aether</span>
<span class="cur">InkNote</span>
<span class="right">marc &nbsp; 02:47</span>
</div>
<div class="barhost">… hier beginnt die App selbst, mit ihrem eigenen Aussehen.</div>
</div>
<div class="trash">
<svg width="40" height="40" viewBox="0 0 20 20" shape-rendering="crispEdges">
<path fill="#000" d="M7 3h6v1h4v1H3V4h4zm-2 3h10v12H5z"/>
<path fill="#d0d0d0" d="M6 7h8v10H6z"/>
<path fill="#000" d="M8 9h1v6H8zm3 0h1v6h-1z"/>
</svg>
<span class="label">Papierkorb</span>
</div>
<div class="switch">Look: <b>System 7</b> &nbsp;·&nbsp; <a href="../index.html">Amber-CRT</a> &nbsp;·&nbsp; <a href="./win95.html">Windows 95</a></div>
</body>
</html>
+225
View File
@@ -0,0 +1,225 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>HoneyOS — Look: Windows 95</title>
<style>
/* ── Windows 95 ──────────────────────────────────────────────────────
Alles lebt von zwei Kanten: hell oben/links, dunkel unten/rechts.
Das ist der gesamte 3D-Effekt, mehr steckt nicht dahinter. */
* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
font: 11px/1.4 "MS Sans Serif", Tahoma, Verdana, "DejaVu Sans", sans-serif;
color: #000;
background: #008080;
overflow: hidden;
-webkit-font-smoothing: none;
}
.raised {
border: 2px outset;
border-color: #fff #404040 #404040 #fff;
background: #c0c0c0;
box-shadow: inset -1px -1px 0 #808080, inset 1px 1px 0 #dfdfdf;
}
.sunken {
border: 2px solid;
border-color: #808080 #fff #fff #808080;
background: #c0c0c0;
box-shadow: inset 1px 1px 0 #404040, inset -1px -1px 0 #dfdfdf;
}
/* Schreibtisch-Symbole */
.desk { position: absolute; top: 12px; left: 12px; width: 84px; }
.deskicon {
width: 84px; padding: 6px 2px; text-align: center; color: #fff; cursor: default;
margin-bottom: 6px;
}
.deskicon svg { display: block; margin: 0 auto 4px; image-rendering: pixelated; }
.deskicon .label { display: inline-block; padding: 1px 3px; text-shadow: 1px 1px 0 rgba(0,0,0,.6); }
.deskicon.sel .label { background: #000080; text-shadow: none; outline: 1px dotted #fff; }
.deskicon.sel svg { filter: hue-rotate(0deg) brightness(.85); }
/* Fenster */
.win { position: absolute; padding: 3px; }
.titlebar {
display: flex; align-items: center; gap: 4px;
height: 18px; padding: 0 2px 0 3px;
background: linear-gradient(90deg, #000080, #1084d0);
color: #fff; font-weight: 700;
}
.titlebar.inactive { background: linear-gradient(90deg, #808080, #b5b5b5); }
.titlebar .t { flex: 1; }
.tbtn {
width: 16px; height: 14px; font: 700 10px/10px "MS Sans Serif", Tahoma, sans-serif;
display: grid; place-items: center; padding-bottom: 2px;
}
.menustrip { display: flex; gap: 12px; padding: 2px 5px; }
.menustrip span { cursor: default; }
.menustrip span:hover { background: #000080; color: #fff; }
.win-body { padding: 10px; }
#status { left: 120px; top: 30px; width: 292px; }
#status h1 { font-size: 11px; margin: 0 0 8px; font-weight: 700; }
.rows { display: grid; grid-template-columns: 1fr auto; gap: 3px 10px; margin-bottom: 10px; }
.rows span:nth-child(even) { font-weight: 700; }
.field { padding: 6px 8px; color: #000; }
.note { color: #404040; }
.buttons { display: flex; gap: 6px; justify-content: flex-end; margin-top: 12px; }
.btn { padding: 3px 14px; min-width: 72px; text-align: center; cursor: default; }
.btn.default { outline: 1px solid #000; outline-offset: 1px; }
#progs { left: 436px; top: 30px; width: 300px; }
.listview { padding: 4px; height: 148px; background: #fff; }
.row { display: flex; align-items: center; gap: 8px; padding: 3px 4px; }
.row.sel { background: #000080; color: #fff; }
.row .sub { margin-left: auto; color: #606060; }
.row.sel .sub { color: #c0c0c0; }
.statusfields { display: flex; gap: 3px; margin-top: 4px; }
.statusfields div { padding: 2px 6px; }
.statusfields div:first-child { flex: 1; }
/* Leiste */
#bar { left: 120px; top: 236px; width: 616px; }
.bardemo {
display: flex; align-items: center; gap: 4px; padding: 2px 3px;
}
.bardemo .pill { padding: 2px 10px; cursor: default; }
.bardemo .right { margin-left: auto; display: flex; gap: 4px; align-items: center; }
.barhost { margin-top: 3px; padding: 14px 10px; background: #fff; color: #404040; }
/* Taskleiste */
.taskbar {
position: absolute; left: 0; right: 0; bottom: 0; height: 28px;
display: flex; align-items: center; gap: 4px; padding: 2px 3px;
}
.start { display: flex; align-items: center; gap: 4px; padding: 2px 8px 2px 4px; font-weight: 700; cursor: default; }
.divider { width: 2px; height: 20px; border-left: 1px solid #808080; border-right: 1px solid #fff; margin: 0 2px; }
.task { padding: 3px 10px; width: 154px; text-align: left; cursor: default; }
.task.pressed {
border-color: #808080 #fff #fff #808080;
box-shadow: inset 1px 1px 0 #404040;
background: repeating-conic-gradient(#c0c0c0 0 25%, #dfdfdf 0 50%) 0 0/2px 2px;
font-weight: 700;
}
.tray { margin-left: auto; display: flex; align-items: center; gap: 6px; padding: 2px 8px; }
.switch { position: absolute; right: 12px; top: 12px; padding: 4px 8px; }
.switch a { color: #000080; }
</style>
</head>
<body>
<!-- Schreibtisch -->
<div class="desk">
<div class="deskicon sel">
<svg width="32" height="32" viewBox="0 0 16 16" shape-rendering="crispEdges">
<path fill="#000" d="M5 4h6v1h1v1h1v1h1v5H2V8h1V7h1V6h1z"/>
<path fill="#dfe8f5" d="M5 5h6v1h1v1h1v4H3V8h1V7h1z"/>
<path fill="#7fa8d8" d="M4 9h9v2H4z"/>
</svg>
<span class="label">Aether</span>
</div>
<div class="deskicon">
<svg width="32" height="32" viewBox="0 0 16 16" shape-rendering="crispEdges">
<path fill="#000" d="M3 1h10v14H3z"/>
<path fill="#fffbe6" d="M4 2h8v12H4z"/>
<path fill="#c07000" d="M5 4h6v1H5zm0 3h6v1H5zm0 3h4v1H5z"/>
<path fill="#e0b040" d="M10 9h3v1h-3z"/>
</svg>
<span class="label">InkNote</span>
</div>
</div>
<!-- Statusfenster -->
<div class="win raised" id="status">
<div class="titlebar">
<span class="t">HoneyOS</span>
<span class="tbtn raised">_</span><span class="tbtn raised">&#9633;</span><span class="tbtn raised">&times;</span>
</div>
<div class="menustrip"><span><u>D</u>atei</span><span><u>A</u>nsicht</span><span><u>?</u></span></div>
<div class="win-body">
<h1>Willkommen zurück, marc.</h1>
<div class="rows">
<span>Programme</span><span>2</span>
<span>erreichbar</span><span>2 von 2</span>
<span>Version</span><span>0.1</span>
</div>
<div class="field sunken note">Jedes Programm läuft auf seiner eigenen Domain.<br />HoneyOS öffnet sie nur.</div>
<div class="buttons">
<span class="btn raised default">OK</span>
<span class="btn raised">Einstellungen</span>
</div>
</div>
</div>
<!-- Programme -->
<div class="win raised" id="progs">
<div class="titlebar inactive">
<span class="t">Programme</span>
<span class="tbtn raised">_</span><span class="tbtn raised">&#9633;</span><span class="tbtn raised">&times;</span>
</div>
<div class="win-body" style="padding: 4px">
<div class="listview sunken">
<div class="row sel">
<svg width="16" height="16" viewBox="0 0 16 16" shape-rendering="crispEdges">
<path fill="#000" d="M5 4h6v1h1v1h1v1h1v5H2V8h1V7h1V6h1z"/><path fill="#dfe8f5" d="M5 5h6v1h1v1h1v4H3V8h1V7h1z"/>
</svg>
Aether <span class="sub">Cloud &amp; Dateien</span>
</div>
<div class="row">
<svg width="16" height="16" viewBox="0 0 16 16" shape-rendering="crispEdges">
<path fill="#000" d="M3 1h10v14H3z"/><path fill="#fffbe6" d="M4 2h8v12H4z"/><path fill="#c07000" d="M5 4h6v1H5zm0 3h6v1H5z"/>
</svg>
InkNote <span class="sub">Notizen</span>
</div>
<div class="row">
<svg width="16" height="16" viewBox="0 0 16 16" shape-rendering="crispEdges">
<path fill="#404040" d="M7 3h2v4h4v2H9v4H7V9H3V7h4z"/>
</svg>
Programm hinzufügen…
</div>
</div>
<div class="statusfields">
<div class="sunken">3 Objekte</div>
<div class="sunken">alles bereit</div>
</div>
</div>
</div>
<!-- Leiste -->
<div class="win raised" id="bar">
<div class="titlebar inactive"><span class="t">so sähe die Leiste in InkNote aus</span><span class="tbtn raised">&times;</span></div>
<div class="win-body" style="padding: 4px">
<div class="bardemo sunken" style="background:#c0c0c0">
<span class="pill raised"><b>HoneyOS</b></span>
<span class="divider"></span>
<span class="pill raised">Aether</span>
<span class="pill raised task pressed" style="width:auto">InkNote</span>
<span class="right"><span class="pill sunken">marc</span><span class="pill sunken">02:47</span></span>
</div>
<div class="barhost sunken">… hier beginnt die App selbst, mit ihrem eigenen Aussehen.</div>
</div>
</div>
<div class="switch raised">
Look: <b>Windows 95</b> &nbsp;·&nbsp; <a href="../index.html">Amber-CRT</a> &nbsp;·&nbsp; <a href="./system7.html">System 7</a>
</div>
<!-- Taskleiste -->
<div class="taskbar raised">
<span class="start raised">
<svg width="16" height="14" viewBox="0 0 8 7" shape-rendering="crispEdges">
<path fill="#ff2f2f" d="M0 1h3v2H0z"/><path fill="#2fbf2f" d="M4 0h4v3H4z"/>
<path fill="#2f6fff" d="M0 4h3v2H0z"/><path fill="#ffd02f" d="M4 4h4v3H4z"/>
</svg>
Start
</span>
<span class="divider"></span>
<span class="task raised pressed">HoneyOS</span>
<span class="task raised">Programme</span>
<span class="tray sunken">02:47</span>
</div>
</body>
</html>
+1024
View File
File diff suppressed because it is too large. Load diff
+764
View File
@@ -0,0 +1,764 @@
/*
* HoneyOS — Amber-CRT-Desktop.
*
* Farben laufen ausschließlich über die Phosphor-Variablen unten. Wer eine
* neue Röhre will, ergänzt einen [data-phosphor]-Block und fasst sonst nichts an.
*/
/* ── Phosphor ──────────────────────────────────────────────────────────── */
:root,
[data-phosphor="amber"] {
--p: #ffb641; /* Vollton — Text, Rahmen im Fokus */
--p-mid: #c8842a; /* halb — Sekundärtext */
--p-dim: #6b4718; /* schwach — Rahmen, Trennlinien */
--p-faint: #2a1c0c; /* Andeutung — Füllungen */
--bg: #0a0705;
--bg-raised: #140d07;
--glow: 0 0 6px rgba(255, 182, 65, 0.45);
}
[data-phosphor="green"] {
--p: #5cff8f;
--p-mid: #2fa85c;
--p-dim: #17512c;
--p-faint: #08200f;
--bg: #040803;
--bg-raised: #071206;
--glow: 0 0 6px rgba(92, 255, 143, 0.4);
}
[data-phosphor="ice"] {
--p: #cfe8ff;
--p-mid: #7fa6c4;
--p-dim: #395366;
--p-faint: #12202a;
--bg: #04070a;
--bg-raised: #0a1118;
--glow: 0 0 6px rgba(207, 232, 255, 0.35);
}
* {
box-sizing: border-box;
}
/* Boot- und Desktop-Ebene tragen `display:flex` — das schlägt das
hidden-Attribut des Browsers, beide lägen sonst gleichzeitig da. */
[hidden] {
display: none !important;
}
html,
body {
height: 100%;
margin: 0;
}
body {
background: var(--bg);
color: var(--p);
font-family: ui-monospace, "SF Mono", "Cascadia Mono", "DejaVu Sans Mono", Menlo, Consolas, monospace;
font-size: 14px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
overflow: hidden;
}
/* ── Bildschirm + Effekte ──────────────────────────────────────────────── */
.crt {
position: fixed;
inset: 0;
display: flex;
flex-direction: column;
overflow: hidden;
/* Sanftes Glimmen aus der Mitte, wie eine aufgewärmte Röhre */
background:
radial-gradient(120% 90% at 50% 45%, color-mix(in srgb, var(--p) 7%, transparent), transparent 70%),
var(--bg);
text-shadow: var(--glow);
}
.scanlines,
.vignette,
.flicker {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 90;
}
.scanlines {
background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.28) 0 1px, transparent 1px 3px);
opacity: 0.7;
}
.vignette {
background: radial-gradient(115% 105% at 50% 50%, transparent 55%, rgba(0, 0, 0, 0.75) 100%);
}
.flicker {
background: var(--p);
opacity: 0;
mix-blend-mode: overlay;
animation: flicker 7s infinite steps(1);
}
@keyframes flicker {
0%, 96%, 100% { opacity: 0; }
97% { opacity: 0.035; }
98% { opacity: 0; }
99% { opacity: 0.02; }
}
/* Wer die Effekte abschaltet, schaltet sie wirklich ab. */
[data-scanlines="off"] .scanlines,
[data-flicker="off"] .flicker {
display: none;
}
[data-glow="off"] .crt {
text-shadow: none;
}
@media (prefers-reduced-motion: reduce) {
.flicker { animation: none; }
}
/* ── Boot ──────────────────────────────────────────────────────────────── */
.boot {
flex: 1;
padding: clamp(16px, 5vw, 56px);
display: flex;
flex-direction: column;
justify-content: flex-start;
overflow: hidden;
}
.boot-log {
margin: 0;
font: inherit;
white-space: pre-wrap;
color: var(--p);
}
.boot-log .ok { color: var(--p); }
.boot-log .warn { color: var(--p-mid); }
.boot-log b { font-weight: 700; }
.boot-hint {
margin-top: auto;
color: var(--p-dim);
font-size: 12px;
}
.caret::after {
content: "\2588";
animation: blink 1s steps(1) infinite;
}
@keyframes blink {
50% { opacity: 0; }
}
/* ── Desktop ───────────────────────────────────────────────────────────── */
.desktop {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
animation: warmup 0.5s ease-out;
}
@keyframes warmup {
from { opacity: 0; transform: scaleY(0.9); filter: brightness(2.5); }
to { opacity: 1; transform: none; filter: none; }
}
/* Menüleiste — Zwilling von honeybar.js */
.menubar {
display: flex;
align-items: center;
gap: 10px;
padding: 0 10px;
height: 34px;
flex: 0 0 34px;
border-bottom: 1px solid var(--p-dim);
background: var(--bg-raised);
padding-top: env(safe-area-inset-top);
height: calc(34px + env(safe-area-inset-top));
flex-basis: calc(34px + env(safe-area-inset-top));
}
.mb-logo {
background: none;
border: 0;
color: var(--p);
font: inherit;
letter-spacing: 0.14em;
cursor: pointer;
padding: 2px 4px;
white-space: nowrap;
}
.mb-logo b { font-weight: 400; color: var(--p-mid); }
.mb-logo .hex { font-size: 10px; vertical-align: 2px; }
.mb-apps {
display: flex;
gap: 4px;
overflow-x: auto;
scrollbar-width: none;
flex: 1;
}
.mb-apps::-webkit-scrollbar { display: none; }
.mb-app {
background: none;
border: 1px solid transparent;
color: var(--p-mid);
font: inherit;
font-size: 12px;
letter-spacing: 0.08em;
padding: 2px 9px;
cursor: pointer;
white-space: nowrap;
}
.mb-app:hover { color: var(--p); border-color: var(--p-dim); }
.mb-app[aria-current="true"] {
color: var(--bg);
background: var(--p);
border-color: var(--p);
text-shadow: none;
}
.mb-right {
display: flex;
align-items: center;
gap: 10px;
color: var(--p-mid);
font-size: 12px;
}
.mb-user { opacity: 0.9; }
.mb-clock { font-variant-numeric: tabular-nums; color: var(--p); }
.mb-btn {
background: none;
border: 1px solid var(--p-dim);
color: var(--p);
font: inherit;
line-height: 1;
padding: 3px 7px;
cursor: pointer;
}
.mb-btn:hover { background: var(--p-faint); }
/* Bühne */
.stage {
flex: 1;
min-height: 0;
overflow-y: auto;
padding: clamp(14px, 3vw, 32px);
display: grid;
gap: clamp(14px, 3vw, 28px);
grid-template-columns: minmax(0, 1fr);
align-content: start;
max-width: 1100px;
width: 100%;
margin: 0 auto;
}
@media (min-width: 860px) {
.stage {
grid-template-columns: 320px minmax(0, 1fr);
align-items: start;
}
}
/* Fenster */
.win {
border: 1px solid var(--p-dim);
background: var(--bg-raised);
}
.win-bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 3px 8px;
border-bottom: 1px solid var(--p-dim);
background: var(--p-faint);
font-size: 11px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--p-mid);
}
.win-dots { letter-spacing: 2px; opacity: 0.5; }
.win-close {
background: none;
border: 0;
color: var(--p);
font: inherit;
font-size: 16px;
line-height: 1;
cursor: pointer;
padding: 0 4px;
}
.win-body { padding: 14px; }
.greet {
margin: 0 0 12px;
font-size: 15px;
}
.statlist {
margin: 0;
display: grid;
grid-template-columns: auto 1fr;
gap: 3px 12px;
font-size: 12.5px;
}
.statlist dt { color: var(--p-dim); }
.statlist dd { margin: 0; color: var(--p-mid); text-align: right; font-variant-numeric: tabular-nums; }
.statlist dd.up { color: var(--p); }
.statlist dd.down { color: var(--p-mid); opacity: 0.6; }
/* App-Kacheln */
.tiles {
display: grid;
gap: clamp(12px, 2.5vw, 20px);
grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
align-content: start;
}
.tile {
display: flex;
flex-direction: column;
gap: 10px;
padding: 16px;
min-height: 168px;
border: 1px solid var(--p-dim);
background: var(--bg-raised);
color: var(--p);
font: inherit;
text-align: left;
text-decoration: none;
cursor: pointer;
position: relative;
transition: background 0.12s, border-color 0.12s, transform 0.12s;
}
.tile:hover,
.tile:focus-visible {
background: var(--p-faint);
border-color: var(--p);
transform: translateY(-2px);
outline: none;
}
.tile:active { transform: none; }
.tile-key {
position: absolute;
top: 8px;
right: 10px;
font-size: 11px;
color: var(--p-dim);
}
.tile-icon {
width: 46px;
height: 46px;
color: var(--p);
}
.tile-icon svg { display: block; width: 100%; height: 100%; }
.tile-name {
font-size: 16px;
letter-spacing: 0.16em;
}
.tile-sub {
font-size: 12px;
color: var(--p-mid);
margin-top: -6px;
}
.tile-state {
margin-top: auto;
font-size: 11px;
letter-spacing: 0.1em;
color: var(--p-dim);
display: flex;
align-items: center;
gap: 6px;
}
.tile-state .led {
width: 7px;
height: 7px;
background: var(--p-dim);
border-radius: 50%;
}
.tile-state[data-state="up"] { color: var(--p); }
.tile-state[data-state="up"] .led { background: var(--p); box-shadow: var(--glow); }
.tile-state[data-state="down"] { color: var(--p-mid); }
.tile-state[data-state="down"] .led { background: transparent; border: 1px solid var(--p-mid); }
/* leerer Zustand */
.tile-add {
align-items: center;
justify-content: center;
color: var(--p-dim);
border-style: dashed;
min-height: 168px;
}
/* Statuszeile */
.statusbar {
flex: 0 0 auto;
display: flex;
justify-content: space-between;
gap: 12px;
padding: 4px 10px;
padding-bottom: calc(4px + env(safe-area-inset-bottom));
border-top: 1px solid var(--p-dim);
background: var(--bg-raised);
color: var(--p-dim);
font-size: 11px;
letter-spacing: 0.06em;
}
.sb-right { color: var(--p-mid); white-space: nowrap; }
@media (max-width: 620px) {
.statusbar { display: none; }
}
/* ── Modal ─────────────────────────────────────────────────────────────── */
.modal {
position: absolute;
inset: 0;
z-index: 80;
display: grid;
place-items: center;
padding: 16px;
background: rgba(0, 0, 0, 0.65);
}
.modal[hidden] { display: none; }
.win-modal {
width: min(520px, 100%);
max-height: min(78vh, 640px);
display: flex;
flex-direction: column;
box-shadow: 0 0 0 1px var(--p-faint), 0 18px 60px rgba(0, 0, 0, 0.7);
}
.win-modal .win-body { overflow-y: auto; }
.field {
display: block;
margin-bottom: 16px;
}
.field > .label {
display: block;
font-size: 11px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--p-dim);
margin-bottom: 6px;
}
.seg { display: flex; flex-wrap: wrap; gap: 6px; }
.seg button {
background: none;
border: 1px solid var(--p-dim);
color: var(--p-mid);
font: inherit;
font-size: 12px;
padding: 4px 12px;
cursor: pointer;
}
.seg button:hover { border-color: var(--p); color: var(--p); }
.seg button[aria-pressed="true"] {
background: var(--p);
border-color: var(--p);
color: var(--bg);
text-shadow: none;
}
.field input[type="text"],
.field input[type="url"] {
width: 100%;
background: var(--bg);
border: 1px solid var(--p-dim);
color: var(--p);
font: inherit;
font-size: 13px;
padding: 6px 8px;
}
.field input:focus {
outline: none;
border-color: var(--p);
}
.url-row {
display: grid;
grid-template-columns: 88px 1fr;
gap: 8px;
align-items: center;
margin-bottom: 8px;
}
.url-row span { font-size: 12px; color: var(--p-mid); letter-spacing: 0.1em; }
.modal-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-top: 18px;
padding-top: 12px;
border-top: 1px solid var(--p-faint);
}
.btn {
background: none;
border: 1px solid var(--p-dim);
color: var(--p);
font: inherit;
font-size: 12px;
letter-spacing: 0.1em;
padding: 5px 14px;
cursor: pointer;
}
.btn:hover { background: var(--p-faint); border-color: var(--p); }
.btn-primary { background: var(--p); border-color: var(--p); color: var(--bg); text-shadow: none; }
.btn-primary:hover { background: var(--p); filter: brightness(1.1); }
.about p { margin: 0 0 10px; color: var(--p-mid); font-size: 13px; }
.about code {
color: var(--p);
background: var(--p-faint);
padding: 1px 4px;
}
.keys {
display: grid;
grid-template-columns: auto 1fr;
gap: 4px 14px;
font-size: 12.5px;
margin-top: 12px;
}
.keys dt { color: var(--p); }
.keys dd { margin: 0; color: var(--p-mid); }
/* ── Desktop-Modus ─────────────────────────────────────────────────────────
* Dieselbe Röhre, andere Betriebsart: Symbole auf der Fläche, echte Fenster,
* Taskleiste unten. Die Menüleiste oben bleibt in beiden Modi dieselbe — sie
* ist die Klammer, die HoneyOS mit den Apps teilt.
*/
.deskspace {
flex: 1;
min-height: 0;
position: relative;
overflow: hidden;
}
.desk-icons {
position: absolute;
inset: 0;
padding: 12px 8px;
display: flex;
flex-direction: column;
flex-wrap: wrap;
align-content: flex-start;
gap: 2px;
}
.deskicon {
width: 94px;
padding: 8px 4px 6px;
text-align: center;
background: none;
border: 1px solid transparent;
color: var(--p);
font: inherit;
cursor: pointer;
}
.deskicon:hover { border-color: var(--p-dim); }
.deskicon:focus-visible { outline: 1px dotted var(--p); outline-offset: -3px; }
.di-glyph {
/* inline-Elemente ignorieren Breite und Höhe — ohne block wächst das SVG
auf seine Eigengröße und sprengt das Symbol. */
display: block;
width: 34px;
height: 34px;
margin: 0 auto 6px;
color: var(--p);
}
.di-glyph svg { display: block; width: 100%; height: 100%; }
.di-label {
display: inline-block;
font-size: 11.5px;
padding: 1px 4px;
letter-spacing: 0.06em;
}
.deskicon[data-sel="1"] { background: var(--p-faint); border-color: var(--p-dim); }
.deskicon[data-sel="1"] .di-label { background: var(--p); color: var(--bg); text-shadow: none; }
/* Erreichbarkeitslampe am Symbol */
.deskicon[data-app] .di-label::after { content: ""; }
.deskicon[data-state="down"] .di-glyph { opacity: 0.45; }
/* Fenster im Desktop-Modus */
.windows {
position: absolute;
inset: 0;
pointer-events: none;
}
.dwin {
position: absolute;
pointer-events: auto;
border: 1px solid var(--p-dim);
background: var(--bg-raised);
box-shadow: 0 12px 34px rgba(0, 0, 0, 0.55);
display: flex;
flex-direction: column;
max-height: calc(100% - 16px);
}
.dwin[data-focus="1"] { border-color: var(--p); }
.dwin[data-min="1"] { display: none; }
.dwin .win-bar {
cursor: grab;
touch-action: none;
user-select: none;
}
.dwin[data-drag="1"] .win-bar { cursor: grabbing; }
.dwin[data-focus="0"] .win-bar { color: var(--p-dim); }
.dwin .win-body {
overflow: auto;
min-height: 0;
}
.win-btns { display: flex; gap: 2px; }
.win-btn {
background: none;
border: 1px solid var(--p-dim);
color: var(--p);
font: inherit;
font-size: 10px;
line-height: 1;
padding: 1px 5px;
cursor: pointer;
}
.win-btn:hover { background: var(--p-faint); border-color: var(--p); }
/* Programme-Fenster */
.applist { display: flex; flex-direction: column; gap: 2px; }
.applist button {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
background: none;
border: 1px solid transparent;
color: var(--p);
font: inherit;
font-size: 13px;
text-align: left;
padding: 5px 6px;
cursor: pointer;
}
.applist button:hover { background: var(--p-faint); border-color: var(--p-dim); }
.applist .al-glyph { width: 18px; height: 18px; flex: none; }
.applist .al-glyph svg { display: block; width: 100%; height: 100%; }
.applist .al-sub { margin-left: auto; color: var(--p-dim); font-size: 11px; }
/* Taskleiste */
.taskbar {
flex: 0 0 auto;
display: flex;
align-items: center;
gap: 6px;
padding: 3px 6px;
padding-bottom: calc(3px + env(safe-area-inset-bottom));
border-top: 1px solid var(--p-dim);
background: var(--bg-raised);
}
.task-start {
background: var(--p-faint);
border: 1px solid var(--p-dim);
color: var(--p);
font: inherit;
font-size: 11px;
letter-spacing: 0.16em;
padding: 3px 10px;
cursor: pointer;
flex: none;
}
.task-start:hover, .task-start[aria-expanded="true"] { background: var(--p); color: var(--bg); text-shadow: none; }
.task-list {
display: flex;
gap: 4px;
flex: 1;
overflow-x: auto;
scrollbar-width: none;
}
.task-list::-webkit-scrollbar { display: none; }
.task-btn {
background: none;
border: 1px solid var(--p-dim);
color: var(--p-mid);
font: inherit;
font-size: 11px;
padding: 3px 10px;
min-width: 96px;
max-width: 160px;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
}
.task-btn:hover { color: var(--p); }
.task-btn[data-active="1"] { background: var(--p-faint); color: var(--p); border-color: var(--p); }
.task-date { color: var(--p-dim); font-size: 11px; white-space: nowrap; }
/* Startmenü */
.startmenu {
position: absolute;
left: 6px;
bottom: 4px;
min-width: 208px;
z-index: 70;
border: 1px solid var(--p);
background: var(--bg-raised);
padding: 4px;
display: none;
flex-direction: column;
gap: 1px;
box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.6);
}
.startmenu[data-open="1"] { display: flex; }
.startmenu .sm-head {
padding: 4px 8px;
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--p-dim);
}
.startmenu button {
background: none;
border: 0;
color: var(--p-mid);
font: inherit;
font-size: 12.5px;
text-align: left;
padding: 6px 9px;
cursor: pointer;
}
.startmenu button:hover { background: var(--p); color: var(--bg); text-shadow: none; }
.startmenu hr { border: 0; border-top: 1px solid var(--p-faint); margin: 3px 0; }
@media (max-width: 620px) {
.task-btn { min-width: 0; }
.task-date { display: none; }
}