Eine Biene als Wappen, an allen vier Stellen

Aus dem Quadrat links in beiden Leisten wird eine Pixelbiene von der Seite:
braune Streifen, schwarze Beine, Fluegel mit Kerbe. Sie steht in genau einem
Raster (tools/biene.py, 16x16 Zeichen); daraus entstehen SVG, PNG und eine
Fassung aus Schriftzeichen.

SVG statt PNG in den Leisten: Die HoneyBar laeuft in fremden Apps, und ob ein
<img> dort laedt, entscheidet deren CSP. Markup kann niemand blockieren.

Im System-Fenster und ueber dem Startlauf steht die Biene aus Schriftzeichen —
beides sind Textflaechen, ein Bild darin waere ein Fremdkoerper.

Das Wappen liegt an drei Stellen (Datei, Modul, HoneyBar-Zeile), weil die
Leiste nichts nachladen kann. TestWappenIstUeberallDasselbe haelt sie gleich.
This commit is contained in:
marc committed 2026-09-02 10:21:03 +02:00
1 parent aec042a8c9
commit 3f8b98b869
14 files changed
+301 -10

No files matched your search

+13
View File
@@ -372,6 +372,19 @@ Zu klären, wenn es soweit ist:
- **In der HoneyBar?** Nur lesend, und auch das erst, wenn alles unter einer - **In der HoneyBar?** Nur lesend, und auch das erst, wenn alles unter einer
Domain liegt — schreiben kann sie von einer fremden Site aus nicht. Domain liegt — schreiben kann sie von einer fremden Site aus nicht.
### Das Wappen — ✅ gebaut (02.09.2026)
Aus dem `■` ist eine Pixelbiene geworden (Marcs Idee, 01.09.2026). Vier Anläufe:
einfarbig 12 × 12 (wirkte durch den angedeuteten Streifen wie ein Grinsen),
farbig 16 × 16 von vorn, als Kasten mit geraden Kanten, und schließlich die
**Seitenansicht** nach einem gedruckten Vorbild — braune Streifen, schwarze
Beine, Flügel mit Kerbe.
Was dabei hängen blieb, steht im [README](README.md): SVG statt PNG wegen der
fremden CSPs, gelb statt mitgefärbt, und die Zeichenfassung fürs System-Fenster.
Eine 3/4-Ansicht (gerechneter Würfel, 2:1) liegt in der Schublade — sie ist
unter 64 px unbrauchbar, taugt aber für ein großes Startbild.
### Eigene App-Logos — verschoben aus 1.3 (29.08.2026) ### Eigene App-Logos — verschoben aus 1.3 (29.08.2026)
**Beschlossen als Planung (25.08.2026), aber nicht als Nächstes.** Je App ein **Beschlossen als Planung (25.08.2026), aber nicht als Nächstes.** Je App ein
+51
View File
@@ -184,6 +184,57 @@ in beiden: Menüleiste, Palette, Einstellungen, App-Liste.
**Tasten:** `M` Betriebsart · `S` Einstellungen · `?` Hilfe · **Tasten:** `M` Betriebsart · `S` Einstellungen · `?` Hilfe ·
`Esc` schließt Startmenü, oberstes Fenster oder Dialog — in dieser Reihenfolge. `Esc` schließt Startmenü, oberstes Fenster oder Dialog — in dieser Reihenfolge.
## Das Wappen
Links in beiden Leisten sitzt eine Pixelbiene — seitlich, mit braunen Streifen,
schwarzen Beinen und Flügeln mit Kerbe. Sie ersetzt das `■`, das dort seit dem
ersten Tag stand.
**Sie steht an genau einer Stelle:** `tools/biene.py`, als 16 × 16 Zeichen, ein
Zeichen je Pixel. Daraus entstehen alle Fassungen:
```
python3 tools/biene.py
```
| Ausgabe | wohin |
|---|---|
| `web/img/biene.svg` | Favicon |
| `web/js/biene.js` | Menüleiste des Schreibtischs (`BIENE_SVG`), System-Fenster und Startlauf (`BIENE_ASCII`) |
| `web/img/biene-256.png`, `-32.png` | Doku und Vorschau |
In die HoneyBar ist dieselbe SVG-Zeile **einkopiert** — sie ist eine einzelne
Datei ohne zweite Anfrage. Ein Test (`TestWappenIstUeberallDasselbe`) hält die
drei Kopien zusammen; wer das Raster ändert, lässt das Skript laufen und
kopiert die Zeile neu.
**Warum SVG und nicht PNG?** Die HoneyBar läuft in *fremden* Apps. Ein `<img>`
holte dort eine Datei von HoneyOS, und ob das erlaubt ist, entscheidet die CSP
der fremden App (`img-src`). Markup kann niemand blockieren, es braucht keine
zweite Anfrage, und es ist mit 2,6 kB kleiner als das PNG.
**Warum bleibt sie gelb?** In der grünen und der eisblauen Röhre wechselt alles
die Farbe mit — die Biene nicht. Ein Zeichen, das die Farbe wechselt, ist ein
Symbol wie *wolke*; eines, das seine Farbe behält, ist ein Wappen. Der eine
warme Fleck liest sich als „das hier ist das System selbst".
### Und aus Schriftzeichen
Im System-Fenster und über dem Startlauf steht dieselbe Biene aus `#`, `:` und
`-`. Beides sind Textflächen — ein Bild darin wäre ein Fremdkörper, wie ein
Foto in einer Tabelle. So wird aus dem System-Fenster ein **neofetch**.
Zwei Dinge stecken in der Umsetzung, die man nur durch Ausprobieren findet: Ein
Zeichen ist etwa halb so breit wie hoch, ein Pixel muss also **zwei Zeichen**
breit sein. Und Blockzeichen (`█ ▓ ▒`) sehen im Editor perfekt aus, hinterlassen
im Browser aber feine Fugen zwischen den Zellen — der Körper wird zur Waffel.
Gewöhnliche Schriftzeichen behaupten gar nicht erst, eine Fläche zu sein.
Dass ausgerechnet die **Seitenansicht** als Text funktioniert, ist kein Zufall:
Ihre Streifen laufen senkrecht, also bleibt jede Textzeile ein
zusammenhängendes Stück Körper. Von vorn zerfällt die Biene in Streifen und
liest sich wie ein Strichcode.
## Widgets ## Widgets
Ein Widget ist eine **Beschreibung**, kein Kasten in der `index.html`: Ein Widget ist eine **Beschreibung**, kein Kasten in der `index.html`:
+1 -1
View File
@@ -52,7 +52,7 @@ func ladeGlyphen() map[string][]string {
// Version ist die einzige Stelle, an der sie steht. build.sh liest sie hier aus // Version ist die einzige Stelle, an der sie steht. build.sh liest sie hier aus
// und vergibt denselben Docker-Tag; die Oberfläche holt sie über /api/public. // und vergibt denselben Docker-Tag; die Oberfläche holt sie über /api/public.
// Vorher stand sie zusätzlich in os.js und lief mit den Image-Tags auseinander. // Vorher stand sie zusätzlich in os.js und lief mit den Image-Tags auseinander.
const Version = "1.2" const Version = "1.3"
type Server struct { type Server struct {
cfg config.Config cfg config.Config
+151
View File
@@ -0,0 +1,151 @@
#!/usr/bin/env python3
"""Erzeugt das HoneyOS-Wappen aus einem Textraster.
Die Biene steht **hier** und nirgends sonst: als 16 × 16 Zeichen, ein Zeichen
je Pixel. Daraus entstehen alle Fassungen, die HoneyOS braucht — SVG für die
beiden Leisten, PNG für Dokumentation und Vorschau, und eine Fassung aus
Schriftzeichen fürs System-Fenster und den Startlauf.
python3 tools/biene.py
Kein Pillow, keine Abhängigkeit: Ein PNG ist ein Kopfteil, ein zlib-Strom aus
Bildzeilen (jede mit einem Filterbyte davor) und eine Prüfsumme — das kann die
Standardbibliothek. Der Vorteil gegenüber einem Bildprogramm ist nicht die
Bequemlichkeit, sondern dass die **Quelle Text bleibt**: Ein Pixel ändern heißt
einen Buchstaben ändern, danach stimmen wieder alle Fassungen überein.
"""
import json
import pathlib
import struct
import zlib
WURZEL = pathlib.Path(__file__).resolve().parent.parent
# Die Farben nach dem Vorbild: Die Streifen sind **braun**. Schwarz sind nur
# Augen, Beine und Fühler — das ist der Unterschied zwischen warm und hart.
FARBEN = {
"Y": (242, 179, 61), # Honiggelb, der Körper
"L": (255, 208, 96), # Oberkante, eine Spur heller
"y": (210, 149, 42), # Unterseite im Schatten
"S": (92, 58, 28), # Streifen
"s": (70, 43, 20), # Streifen an der Unterkante
"E": (24, 20, 16), # Augenfeld und Beine
"B": (99, 199, 192), # der Lichtpunkt im Auge
"D": (59, 36, 18), # Fühler
"W": (245, 242, 231), # Flügel
"w": (214, 209, 190), # Flügel, untere Lage
}
# Von der Seite: Stachel hinten, Kopf in Laufrichtung. Diese Ansicht ist es
# geworden, weil ihre Streifen **senkrecht** laufen — deshalb bleibt sie auch
# als Text lesbar, wo die Vorderansicht in Streifen zerfällt.
BIENE = [
"....WW...WW.....",
"..WWWWW.WWWWW...",
"..WWWWWWWWWWW.D.",
"...WWWWWWWWW.DD.",
"...LLLLLLLLLLL..",
"..YYSSYYSSYYYYY.",
".YYYSSYYSSYYEBY.",
"SYYYSSYYSSYYEEY.",
".YYYSSYYSSYYYYY.",
"..yssyyssyyyyyy.",
"...EE..EE..EE...",
"...EE..EE..EE...",
"................",
"................",
"................",
"................",
]
def svg(raster):
"""Waagerechte Läufe gleicher Farbe werden ein Rechteck — dieselbe Idee wie
bei den Pixelsymbolen in web/js/glyphs.js, nur mit Farbe statt currentColor.
Warum SVG und nicht PNG für die Leisten: Die HoneyBar läuft in *fremden*
Apps. Ein <img> holt dort eine Datei von HoneyOS, und ob das erlaubt ist,
entscheidet die CSP der fremden App. Markup kann niemand blockieren."""
teile = []
for y, zeile in enumerate(raster):
x = 0
while x < len(zeile):
z = zeile[x]
if z not in FARBEN:
x += 1
continue
lauf = 1
while x + lauf < len(zeile) and zeile[x + lauf] == z:
lauf += 1
r, g, b = FARBEN[z]
teile.append(f'<rect x="{x}" y="{y}" width="{lauf}" height="1" fill="#{r:02x}{g:02x}{b:02x}"/>')
x += lauf
return ('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" '
'shape-rendering="crispEdges" aria-hidden="true">' + "".join(teile) + "</svg>")
def png(raster, faktor, ziel):
"""Vergrößert wird um ganze Faktoren, damit jedes Pixel ein Quadrat bleibt."""
n = len(raster)
roh = bytearray()
for zeile in raster:
pixel = bytearray()
for zeichen in zeile:
r, g, b = FARBEN.get(zeichen, (0, 0, 0))
pixel += bytes((r, g, b, 255 if zeichen in FARBEN else 0)) * faktor
for _ in range(faktor):
roh += b"\x00" + pixel # Filterbyte 0: keine Vorhersage
def block(art, daten):
return (struct.pack(">I", len(daten)) + art + daten
+ struct.pack(">I", zlib.crc32(art + daten) & 0xFFFFFFFF))
kopf = struct.pack(">IIBBBBB", n * faktor, n * faktor, 8, 6, 0, 0, 0)
ziel.write_bytes(b"\x89PNG\r\n\x1a\n" + block(b"IHDR", kopf)
+ block(b"IDAT", zlib.compress(bytes(roh), 9)) + block(b"IEND", b""))
# Ein Zeichen ist etwa halb so breit wie hoch — **ein Pixel sind zwei Zeichen**,
# sonst steht die Biene hochkant. Blockzeichen (█ ▓ ▒) wären naheliegend,
# kacheln aber nicht: Zwischen den Zellen bleiben Fugen, und der Körper wird zur
# Waffel. Gewöhnliche Schriftzeichen behaupten gar nicht erst, eine Fläche zu
# sein — deshalb sehen sie besser aus.
ZEICHEN = {"Y": "#", "L": "#", "y": "#", "S": ":", "s": ":", "E": ":", "D": ":", "W": "-", "B": "o"}
def text(raster):
zeilen = ["".join(ZEICHEN.get(c, " ") * 2 for c in z).rstrip() for z in raster]
while zeilen and not zeilen[0]:
zeilen.pop(0)
while zeilen and not zeilen[-1]:
zeilen.pop()
return "\n".join(zeilen)
def main():
bild = svg(BIENE)
zeichen = text(BIENE)
(WURZEL / "web/img/biene.svg").write_text(bild + "\n")
png(BIENE, 16, WURZEL / "web/img/biene-256.png")
png(BIENE, 2, WURZEL / "web/img/biene-32.png")
# Für die Oberfläche als Modul: Das Wappen im Markup (die Leiste holt kein
# Bild nach) und die Zeichenfassung fürs System-Fenster.
(WURZEL / "web/js/biene.js").write_text(
"/* Erzeugt von tools/biene.py — nicht von Hand ändern.\n"
" *\n"
" * Das Wappen als Markup statt als Bilddatei: So kommt es ohne zweite\n"
" * Anfrage aus und lässt sich mit dem Text daneben ausrichten.\n"
" */\n"
# Einfache Anführungszeichen: So steht das Markup wörtlich da und ist
# mit web/img/biene.svg vergleichbar — ein Test hält beides gleich.
f"export const BIENE_SVG = '{bild}';\n\n"
f"export const BIENE_ASCII = {json.dumps(zeichen)};\n"
)
print(text(BIENE))
print(f"\nweb/img/biene.svg ({len(bild)} Zeichen), biene-256.png, biene-32.png, web/js/biene.js")
if __name__ == "__main__":
main()
+18 -1
View File
@@ -202,7 +202,24 @@ body {
white-space: nowrap; white-space: nowrap;
} }
.mb-logo b { font-weight: 400; color: var(--p-mid); } .mb-logo b { font-weight: 400; color: var(--p-mid); }
.mb-logo .hex { font-size: 10px; vertical-align: 2px; } /* Das Wappen bleibt gelb, während der Rest die Röhre wechselt: Ein Zeichen,
das die Farbe mitwechselt, ist ein Symbol wie „wolke" — eines, das seine
Farbe behält, ist ein Wappen. */
.mb-logo .hex { display: inline-block; width: 20px; height: 20px; vertical-align: -6px; }
.mb-logo .hex svg { display: block; width: 100%; height: 100%; }
/* Die Biene aus Schriftzeichen — im System-Fenster und über dem Startlauf.
Ein Zeichen ist halb so breit wie hoch, deshalb ist im Raster jeder Pixel
zwei Zeichen breit (siehe tools/biene.py). */
.biene-text {
margin: 0 0 8px;
color: var(--p);
font-family: inherit;
font-size: 9px;
line-height: 1.1;
overflow-x: auto;
}
.boot-biene { font-size: 11px; margin-bottom: 14px; }
.mb-apps { .mb-apps {
display: flex; display: flex;
+14 -5
View File
@@ -98,6 +98,13 @@
"display:block", "display:block",
"color-scheme:dark", "color-scheme:dark",
].join(";"); ].join(";");
/* Das Wappen. Es steht hier als Markup, weil die HoneyBar in fremden
* Apps läuft: Ein <img> holte eine Datei von HoneyOS, und ob das geht,
* entscheidet die CSP der fremden App. Erzeugt von tools/biene.py —
* dieselbe Zeile steht in web/img/biene.svg (ein Test hält sie gleich).
*/
const WAPPEN = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" shape-rendering="crispEdges" aria-hidden="true"><rect x="4" y="0" width="2" height="1" fill="#f5f2e7"/><rect x="9" y="0" width="2" height="1" fill="#f5f2e7"/><rect x="2" y="1" width="5" height="1" fill="#f5f2e7"/><rect x="8" y="1" width="5" height="1" fill="#f5f2e7"/><rect x="2" y="2" width="11" height="1" fill="#f5f2e7"/><rect x="14" y="2" width="1" height="1" fill="#3b2412"/><rect x="3" y="3" width="9" height="1" fill="#f5f2e7"/><rect x="13" y="3" width="2" height="1" fill="#3b2412"/><rect x="3" y="4" width="11" height="1" fill="#ffd060"/><rect x="2" y="5" width="2" height="1" fill="#f2b33d"/><rect x="4" y="5" width="2" height="1" fill="#5c3a1c"/><rect x="6" y="5" width="2" height="1" fill="#f2b33d"/><rect x="8" y="5" width="2" height="1" fill="#5c3a1c"/><rect x="10" y="5" width="5" height="1" fill="#f2b33d"/><rect x="1" y="6" width="3" height="1" fill="#f2b33d"/><rect x="4" y="6" width="2" height="1" fill="#5c3a1c"/><rect x="6" y="6" width="2" height="1" fill="#f2b33d"/><rect x="8" y="6" width="2" height="1" fill="#5c3a1c"/><rect x="10" y="6" width="2" height="1" fill="#f2b33d"/><rect x="12" y="6" width="1" height="1" fill="#181410"/><rect x="13" y="6" width="1" height="1" fill="#63c7c0"/><rect x="14" y="6" width="1" height="1" fill="#f2b33d"/><rect x="0" y="7" width="1" height="1" fill="#5c3a1c"/><rect x="1" y="7" width="3" height="1" fill="#f2b33d"/><rect x="4" y="7" width="2" height="1" fill="#5c3a1c"/><rect x="6" y="7" width="2" height="1" fill="#f2b33d"/><rect x="8" y="7" width="2" height="1" fill="#5c3a1c"/><rect x="10" y="7" width="2" height="1" fill="#f2b33d"/><rect x="12" y="7" width="2" height="1" fill="#181410"/><rect x="14" y="7" width="1" height="1" fill="#f2b33d"/><rect x="1" y="8" width="3" height="1" fill="#f2b33d"/><rect x="4" y="8" width="2" height="1" fill="#5c3a1c"/><rect x="6" y="8" width="2" height="1" fill="#f2b33d"/><rect x="8" y="8" width="2" height="1" fill="#5c3a1c"/><rect x="10" y="8" width="5" height="1" fill="#f2b33d"/><rect x="2" y="9" width="1" height="1" fill="#d2952a"/><rect x="3" y="9" width="2" height="1" fill="#462b14"/><rect x="5" y="9" width="2" height="1" fill="#d2952a"/><rect x="7" y="9" width="2" height="1" fill="#462b14"/><rect x="9" y="9" width="6" height="1" fill="#d2952a"/><rect x="3" y="10" width="2" height="1" fill="#181410"/><rect x="7" y="10" width="2" height="1" fill="#181410"/><rect x="11" y="10" width="2" height="1" fill="#181410"/><rect x="3" y="11" width="2" height="1" fill="#181410"/><rect x="7" y="11" width="2" height="1" fill="#181410"/><rect x="11" y="11" width="2" height="1" fill="#181410"/></svg>';
const root = host.attachShadow({ mode: "open" }); const root = host.attachShadow({ mode: "open" });
root.innerHTML = ` root.innerHTML = `
@@ -136,10 +143,12 @@
} }
.home { letter-spacing: .14em; color: var(--p); padding-left: 2px; } .home { letter-spacing: .14em; color: var(--p); padding-left: 2px; }
.home b { color: var(--p-mid); font-weight: 400; } .home b { color: var(--p-mid); font-weight: 400; }
/* Dasselbe Quadrat wie in der Menüleiste des Schreibtischs: dort ist es /* Dasselbe Wappen wie in der Menüleiste des Schreibtischs, in derselben
klein und leicht angehoben. Ein Zeichen in voller Größe sah daneben Größe. Es bleibt gelb, während der Rest die Röhre wechselt: Ein
wie ein anderes Logo aus. */ Zeichen, das die Farbe mitwechselt, ist ein Symbol — eines, das seine
.home .hex { font-size: 10px; vertical-align: 2px; } Farbe behält, ist ein Wappen. */
.home .hex { display: inline-block; width: 20px; height: 20px; vertical-align: -6px; }
.home .hex svg { display: block; width: 100%; height: 100%; }
.home:hover { border-color: var(--p-dim); } .home:hover { border-color: var(--p-dim); }
.home[aria-expanded="true"] { border-color: var(--p-dim); background: var(--p-faint); } .home[aria-expanded="true"] { border-color: var(--p-dim); background: var(--p-faint); }
/* Bei vielen Apps schiebt man den Streifen seitwärts. Ohne /* Bei vielen Apps schiebt man den Streifen seitwärts. Ohne
@@ -247,7 +256,7 @@
</style> </style>
<div class="bar"> <div class="bar">
<button class="home" id="home" title="HoneyOS-Menü" aria-haspopup="true" aria-expanded="false"><span class="hex">&#9632;</span> HONEY<b>OS</b></button> <button class="home" id="home" title="HoneyOS-Menü" aria-haspopup="true" aria-expanded="false"><span class="hex">${WAPPEN}</span> HONEY<b>OS</b></button>
<nav class="apps" id="apps"></nav> <nav class="apps" id="apps"></nav>
<div class="right"> <div class="right">
<span class="wx" id="wx" hidden></span> <span class="wx" id="wx" hidden></span>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" shape-rendering="crispEdges" aria-hidden="true"><rect x="4" y="0" width="2" height="1" fill="#f5f2e7"/><rect x="9" y="0" width="2" height="1" fill="#f5f2e7"/><rect x="2" y="1" width="5" height="1" fill="#f5f2e7"/><rect x="8" y="1" width="5" height="1" fill="#f5f2e7"/><rect x="2" y="2" width="11" height="1" fill="#f5f2e7"/><rect x="14" y="2" width="1" height="1" fill="#3b2412"/><rect x="3" y="3" width="9" height="1" fill="#f5f2e7"/><rect x="13" y="3" width="2" height="1" fill="#3b2412"/><rect x="3" y="4" width="11" height="1" fill="#ffd060"/><rect x="2" y="5" width="2" height="1" fill="#f2b33d"/><rect x="4" y="5" width="2" height="1" fill="#5c3a1c"/><rect x="6" y="5" width="2" height="1" fill="#f2b33d"/><rect x="8" y="5" width="2" height="1" fill="#5c3a1c"/><rect x="10" y="5" width="5" height="1" fill="#f2b33d"/><rect x="1" y="6" width="3" height="1" fill="#f2b33d"/><rect x="4" y="6" width="2" height="1" fill="#5c3a1c"/><rect x="6" y="6" width="2" height="1" fill="#f2b33d"/><rect x="8" y="6" width="2" height="1" fill="#5c3a1c"/><rect x="10" y="6" width="2" height="1" fill="#f2b33d"/><rect x="12" y="6" width="1" height="1" fill="#181410"/><rect x="13" y="6" width="1" height="1" fill="#63c7c0"/><rect x="14" y="6" width="1" height="1" fill="#f2b33d"/><rect x="0" y="7" width="1" height="1" fill="#5c3a1c"/><rect x="1" y="7" width="3" height="1" fill="#f2b33d"/><rect x="4" y="7" width="2" height="1" fill="#5c3a1c"/><rect x="6" y="7" width="2" height="1" fill="#f2b33d"/><rect x="8" y="7" width="2" height="1" fill="#5c3a1c"/><rect x="10" y="7" width="2" height="1" fill="#f2b33d"/><rect x="12" y="7" width="2" height="1" fill="#181410"/><rect x="14" y="7" width="1" height="1" fill="#f2b33d"/><rect x="1" y="8" width="3" height="1" fill="#f2b33d"/><rect x="4" y="8" width="2" height="1" fill="#5c3a1c"/><rect x="6" y="8" width="2" height="1" fill="#f2b33d"/><rect x="8" y="8" width="2" height="1" fill="#5c3a1c"/><rect x="10" y="8" width="5" height="1" fill="#f2b33d"/><rect x="2" y="9" width="1" height="1" fill="#d2952a"/><rect x="3" y="9" width="2" height="1" fill="#462b14"/><rect x="5" y="9" width="2" height="1" fill="#d2952a"/><rect x="7" y="9" width="2" height="1" fill="#462b14"/><rect x="9" y="9" width="6" height="1" fill="#d2952a"/><rect x="3" y="10" width="2" height="1" fill="#181410"/><rect x="7" y="10" width="2" height="1" fill="#181410"/><rect x="11" y="10" width="2" height="1" fill="#181410"/><rect x="3" y="11" width="2" height="1" fill="#181410"/><rect x="7" y="11" width="2" height="1" fill="#181410"/><rect x="11" y="11" width="2" height="1" fill="#181410"/></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

+3 -2
View File
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" /> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="theme-color" content="#0a0705" /> <meta name="theme-color" content="#0a0705" />
<title>HoneyOS</title> <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="icon" href="./img/biene.svg" />
<link rel="stylesheet" href="./css/style.css" /> <link rel="stylesheet" href="./css/style.css" />
</head> </head>
<body> <body>
@@ -13,6 +13,7 @@
<div class="crt" id="screen"> <div class="crt" id="screen">
<!-- ── Boot ───────────────────────────────────────────────────────── --> <!-- ── Boot ───────────────────────────────────────────────────────── -->
<div class="boot" id="boot" hidden> <div class="boot" id="boot" hidden>
<pre class="biene-text boot-biene" id="bootBiene" aria-hidden="true"></pre>
<pre class="boot-log" id="bootLog" aria-live="polite"></pre> <pre class="boot-log" id="bootLog" aria-live="polite"></pre>
<p class="boot-hint">[ beliebige Taste = überspringen ]</p> <p class="boot-hint">[ beliebige Taste = überspringen ]</p>
</div> </div>
@@ -33,7 +34,7 @@
<!-- Das Logo ist das Systemmenü — dasselbe wie in der HoneyBar <!-- Das Logo ist das Systemmenü — dasselbe wie in der HoneyBar
fremder Apps, damit HoneyOS von überall gleich erreichbar ist. --> fremder Apps, damit HoneyOS von überall gleich erreichbar ist. -->
<button class="mb-logo" id="btnSystem" title="HoneyOS-Menü" aria-haspopup="true" aria-expanded="false"> <button class="mb-logo" id="btnSystem" title="HoneyOS-Menü" aria-haspopup="true" aria-expanded="false">
<span class="hex">&#9632;</span> HONEY<b>OS</b> <span class="hex" id="logoBiene"></span> HONEY<b>OS</b>
</button> </button>
<nav class="mb-apps" id="mbApps" aria-label="Anwendungen"></nav> <nav class="mb-apps" id="mbApps" aria-label="Anwendungen"></nav>
<div class="mb-right"> <div class="mb-right">
+8
View File
@@ -0,0 +1,8 @@
/* Erzeugt von tools/biene.py — nicht von Hand ändern.
*
* Das Wappen als Markup statt als Bilddatei: So kommt es ohne zweite
* Anfrage aus und lässt sich mit dem Text daneben ausrichten.
*/
export const BIENE_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" shape-rendering="crispEdges" aria-hidden="true"><rect x="4" y="0" width="2" height="1" fill="#f5f2e7"/><rect x="9" y="0" width="2" height="1" fill="#f5f2e7"/><rect x="2" y="1" width="5" height="1" fill="#f5f2e7"/><rect x="8" y="1" width="5" height="1" fill="#f5f2e7"/><rect x="2" y="2" width="11" height="1" fill="#f5f2e7"/><rect x="14" y="2" width="1" height="1" fill="#3b2412"/><rect x="3" y="3" width="9" height="1" fill="#f5f2e7"/><rect x="13" y="3" width="2" height="1" fill="#3b2412"/><rect x="3" y="4" width="11" height="1" fill="#ffd060"/><rect x="2" y="5" width="2" height="1" fill="#f2b33d"/><rect x="4" y="5" width="2" height="1" fill="#5c3a1c"/><rect x="6" y="5" width="2" height="1" fill="#f2b33d"/><rect x="8" y="5" width="2" height="1" fill="#5c3a1c"/><rect x="10" y="5" width="5" height="1" fill="#f2b33d"/><rect x="1" y="6" width="3" height="1" fill="#f2b33d"/><rect x="4" y="6" width="2" height="1" fill="#5c3a1c"/><rect x="6" y="6" width="2" height="1" fill="#f2b33d"/><rect x="8" y="6" width="2" height="1" fill="#5c3a1c"/><rect x="10" y="6" width="2" height="1" fill="#f2b33d"/><rect x="12" y="6" width="1" height="1" fill="#181410"/><rect x="13" y="6" width="1" height="1" fill="#63c7c0"/><rect x="14" y="6" width="1" height="1" fill="#f2b33d"/><rect x="0" y="7" width="1" height="1" fill="#5c3a1c"/><rect x="1" y="7" width="3" height="1" fill="#f2b33d"/><rect x="4" y="7" width="2" height="1" fill="#5c3a1c"/><rect x="6" y="7" width="2" height="1" fill="#f2b33d"/><rect x="8" y="7" width="2" height="1" fill="#5c3a1c"/><rect x="10" y="7" width="2" height="1" fill="#f2b33d"/><rect x="12" y="7" width="2" height="1" fill="#181410"/><rect x="14" y="7" width="1" height="1" fill="#f2b33d"/><rect x="1" y="8" width="3" height="1" fill="#f2b33d"/><rect x="4" y="8" width="2" height="1" fill="#5c3a1c"/><rect x="6" y="8" width="2" height="1" fill="#f2b33d"/><rect x="8" y="8" width="2" height="1" fill="#5c3a1c"/><rect x="10" y="8" width="5" height="1" fill="#f2b33d"/><rect x="2" y="9" width="1" height="1" fill="#d2952a"/><rect x="3" y="9" width="2" height="1" fill="#462b14"/><rect x="5" y="9" width="2" height="1" fill="#d2952a"/><rect x="7" y="9" width="2" height="1" fill="#462b14"/><rect x="9" y="9" width="6" height="1" fill="#d2952a"/><rect x="3" y="10" width="2" height="1" fill="#181410"/><rect x="7" y="10" width="2" height="1" fill="#181410"/><rect x="11" y="10" width="2" height="1" fill="#181410"/><rect x="3" y="11" width="2" height="1" fill="#181410"/><rect x="7" y="11" width="2" height="1" fill="#181410"/><rect x="11" y="11" width="2" height="1" fill="#181410"/></svg>';
export const BIENE_ASCII = " ---- ----\n ---------- ----------\n ---------------------- ::\n ------------------ ::::\n ######################\n ####::::####::::##########\n ######::::####::::####::oo##\n::######::::####::::####::::##\n ######::::####::::##########\n ##::::####::::############\n :::: :::: ::::\n :::: :::: ::::";
+7
View File
@@ -4,6 +4,7 @@ import {
SS_BOOTED, state, SS_BOOTED, state,
} from "./core.js"; } from "./core.js";
import { GLYPHS, glyphSvg } from "./glyphs.js"; import { GLYPHS, glyphSvg } from "./glyphs.js";
import { BIENE_ASCII, BIENE_SVG } from "./biene.js";
import { import {
widgetPlan, widgetPlanSetzen, widgetsLaden, widgetsOrdnen, widgetsStarten, widgetsZeichnen, widgetPlan, widgetPlanSetzen, widgetsLaden, widgetsOrdnen, widgetsStarten, widgetsZeichnen,
} from "./widgets/rahmen.js"; } from "./widgets/rahmen.js";
@@ -79,6 +80,9 @@ function runBoot() {
return new Promise((resolve) => { return new Promise((resolve) => {
const log = $("bootLog"); const log = $("bootLog");
const el = $("boot"); const el = $("boot");
// Über dem Protokoll die Biene — auch hier aus Schriftzeichen, denn der
// Startlauf ist Text und nichts anderes.
$("bootBiene").textContent = BIENE_ASCII;
el.hidden = false; el.hidden = false;
const lines = bootLines(); const lines = bootLines();
let i = 0; let i = 0;
@@ -122,6 +126,9 @@ function runBoot() {
function renderShell() { function renderShell() {
$("desktop").hidden = false; $("desktop").hidden = false;
// Das Wappen steht im Markup, nicht als Bilddatei — dieselbe Zeile wie in
// der HoneyBar fremder Apps, damit beide Leisten wirklich dasselbe zeigen.
$("logoBiene").innerHTML = BIENE_SVG;
renderMenubarApps(); renderMenubarApps();
renderMode(); renderMode();
tickClock(); tickClock();
+7 -1
View File
@@ -1,4 +1,5 @@
import { escapeHtml, state } from "../core.js"; import { escapeHtml, state } from "../core.js";
import { BIENE_ASCII } from "../biene.js";
import { widgetAnmelden } from "./rahmen.js"; import { widgetAnmelden } from "./rahmen.js";
/* ── Systemzustand ───────────────────────────────────────────────────────── /* ── Systemzustand ─────────────────────────────────────────────────────────
@@ -6,6 +7,11 @@ import { widgetAnmelden } from "./rahmen.js";
* Nur im Terminal-Modus ein Widget. Auf dem Schreibtisch gibt es dieselben * Nur im Terminal-Modus ein Widget. Auf dem Schreibtisch gibt es dieselben
* Zahlen als Fenster über das Startmenü — ein Schild, das dauerhaft „modus: * Zahlen als Fenster über das Startmenü — ein Schild, das dauerhaft „modus:
* desktop" behauptet, während man im Desktop-Modus steht, sagt niemandem etwas. * desktop" behauptet, während man im Desktop-Modus steht, sagt niemandem etwas.
*
* Die Biene steht hier **aus Schriftzeichen**, nicht als Bild: Das Fenster
* besteht aus lauter Textzeilen, und ein Bild darin wäre ein Fremdkörper — wie
* ein Foto in einer Tabelle. Als Text gehört sie dazu, und das Ganze wird zu
* dem, was man bei so einem Fenster ohnehin im Kopf hat: einem neofetch.
*/ */
export function statsHtml() { export function statsHtml() {
const up = Object.values(state.probes).filter((s) => s === "up").length; const up = Object.values(state.probes).filter((s) => s === "up").length;
@@ -25,5 +31,5 @@ widgetAnmelden({
id: "system", id: "system",
titel: "system", titel: "system",
wo: ["terminal"], wo: ["terminal"],
inhalt: statsHtml, inhalt: () => `<pre class="biene-text">${BIENE_ASCII}</pre>${statsHtml()}`,
}); });
+27
View File
@@ -177,3 +177,30 @@ func TestNotizNichtUeberDenSchluessel(t *testing.T) {
t.Fatalf("mit Schlüssel: %d, erwartet 401", rec.Code) t.Fatalf("mit Schlüssel: %d, erwartet 401", rec.Code)
} }
} }
// Das Wappen steht an drei Stellen: als Datei (Favicon), als Modul für den
// Schreibtisch und als Zeile in der HoneyBar — die kann keine Datei nachladen,
// weil sie in fremden Apps läuft und deren CSP das entscheidet.
//
// Drei Kopien laufen auseinander, sobald jemand eine davon von Hand anfasst.
// Erzeugt werden alle von tools/biene.py; dieser Test hält sie zusammen.
func TestWappenIstUeberallDasselbe(t *testing.T) {
roh, err := os.ReadFile("web/img/biene.svg")
if err != nil {
t.Fatalf("web/img/biene.svg fehlt: %v", err)
}
wappen := strings.TrimSpace(string(roh))
if !strings.HasPrefix(wappen, "<svg") || len(wappen) < 500 {
t.Fatalf("web/img/biene.svg sieht nicht nach dem Wappen aus (%d Zeichen)", len(wappen))
}
for _, datei := range []string{"web/honeybar/honeybar.js", "web/js/biene.js"} {
b, err := os.ReadFile(datei)
if err != nil {
t.Fatalf("%s: %v", datei, err)
}
if !strings.Contains(string(b), wappen) {
t.Errorf("%s trägt ein anderes Wappen als web/img/biene.svg — tools/biene.py laufen lassen", datei)
}
}
}