Symbole in beiden Leisten, aus einer Quelle
Die Pixelraster stehen jetzt in web/glyphs.json — einmal. Der Schreibtisch laedt die Datei selbst, die HoneyBar bekommt sie mit der App-Liste mit; sie bleibt damit eine einzelne Datei ohne zweite Anfrage. Die abgetippten Wettersymbole in honeybar.js sind weg, und die Symbolauswahl im Programm-Editor kommt jetzt aus derselben Datei statt aus einer Liste im Quelltext. Neu: Symbole neben den App-Namen im Streifen beider Leisten, mit einem Schalter (Einstellungen -> HoneyBar) fuer beide zugleich. In den Aufklappmenues und auf den Kacheln stehen sie immer.
This commit is contained in:
1 parent
d29b69a426
commit
53ab62d220
9 files changed
+395
-278
No files matched your search
@@ -59,39 +59,29 @@ hält — dieselbe Liste, die heute schon die CORS-Freigabe steuert. Das ist ein
|
|||||||
Sicherheitsentscheidung und keine Kleinigkeit; erst bauen, wenn der Umweg über
|
Sicherheitsentscheidung und keine Kleinigkeit; erst bauen, wenn der Umweg über
|
||||||
den Schreibtisch wirklich nervt.
|
den Schreibtisch wirklich nervt.
|
||||||
|
|
||||||
### 1.3 App-Schublade mit Symbolen
|
### 1.3 App-Schublade mit Symbolen — ✅ halb gebaut (29.08.2026)
|
||||||
|
|
||||||
**Die Idee:** ein Knopf, der eine Übersicht aller Apps aufklappt — mit Symbol.
|
**Gebaut: die geteilte Symbolquelle.** Die Raster liegen in `web/glyphs.json`,
|
||||||
Die Liste kommt aus HoneyOS. Eine neue App trägt man einmal ein und sie taucht
|
der Schreibtisch lädt sie, die HoneyBar bekommt sie mit der App-Liste mit
|
||||||
überall auf, ohne dass eine App angefasst wird.
|
(siehe [README](README.md)). Damit tragen beide Leisten dieselben Symbole neben
|
||||||
|
den App-Namen, und die doppelt abgetippten Wettersymbole sind weg.
|
||||||
|
|
||||||
**Stand:** Die Hälfte steht. `honeybar.js` holt die Liste aus `/api/apps`, bringt
|
Dazu ein Schalter *Einstellungen → HoneyBar*: Symbole im App-Streifen an oder
|
||||||
seine Sitzung mit und hat ein Aufklappmenü. Es fehlt die Darstellung als Raster
|
aus, für beide Leisten zugleich. In den Aufklappmenüs und auf den Kacheln
|
||||||
mit Symbolen — heute sind es Textknöpfe. Bei zwei Apps geht das noch, ab vier
|
stehen sie immer — dort ist Platz, und dort sucht man eine App, statt sie im
|
||||||
wird es eng, am Handy sofort.
|
Vorbeigehen wiederzuerkennen.
|
||||||
|
|
||||||
**Der eigentliche Punkt: die Symbole.** Das Pixelraster lebt in `os.js`
|
**Offen bleibt die Schublade selbst:** ein Raster aus Symbolen statt einer
|
||||||
(`GLYPHS`), die HoneyBar kennt es nicht. Zwei Wege:
|
Liste, wenn es viele Apps werden. Heute reicht der Streifen samt Menü; das ist
|
||||||
|
erst ab sechs, sieben Apps eine echte Frage.
|
||||||
|
|
||||||
- **Pixelglyphen teilen** — `GLYPHS` + `glyphSvg()` in eine eigene Datei, die
|
**Ebenfalls offen: eigene Logos hochladen** (beschlossen als Planung,
|
||||||
Desktop und HoneyBar beide laden. Kein Upload, kein Speicher, passt zum Look.
|
25.08.2026). Je App ein Bild in HoneyOS, die Pixelglyphe als Rückfall, wenn
|
||||||
Nachteil: nur die eingebauten Symbole.
|
keins da ist. Das Logo gehört ausdrücklich auch in die HoneyBar — dort ist die
|
||||||
- **Echte Bilder** — pro App eine Bildadresse oder ein Upload in HoneyOS.
|
Unterscheidung am nötigsten, weil die Knöpfe klein sind und nebeneinanderstehen.
|
||||||
Flexibler, aber: Speicher, Größenbegrenzungen, Auslieferung, und der
|
Der Weg dorthin ist durch die geteilte Quelle jetzt kurz: Beide Seiten holen
|
||||||
Retro-Look bekommt bunte Fremdkörper.
|
ihre Symbole schon aus derselben Antwort, es müsste nur ein Bild an die Stelle
|
||||||
|
des Rasters treten können.
|
||||||
→ **Empfehlung: Pixelglyphen teilen** — als Erstes, weil es sofort trägt.
|
|
||||||
|
|
||||||
**Beschlossen als Planung (25.08.2026): eigene Logos hochladen.** Je App ein
|
|
||||||
Bild in HoneyOS hinterlegen, statt nur aus den eingebauten Glyphen zu wählen.
|
|
||||||
Nicht als Ersatz für die Pixelglyphen, sondern daneben: Glyphe als Rückfall,
|
|
||||||
wenn kein Bild da ist.
|
|
||||||
|
|
||||||
**Das Logo gehört ausdrücklich auch in die HoneyBar**, nicht nur auf den
|
|
||||||
Schreibtisch — dort ist die Unterscheidung am nötigsten, weil die Knöpfe klein
|
|
||||||
sind und nebeneinanderstehen. Damit ist die geteilte Symbolquelle aus 1.3
|
|
||||||
Voraussetzung: Beide Seiten müssen dasselbe Bild bekommen, sonst pflegt man es
|
|
||||||
zweimal.
|
|
||||||
|
|
||||||
Was daran zu bauen ist, in dieser Reihenfolge:
|
Was daran zu bauen ist, in dieser Reihenfolge:
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,31 @@ Gespeichert wird die Liste als Ganzes; der Server prüft sie noch einmal und ant
|
|||||||
dem, was er wirklich behalten hat. Eine Adresse ohne `https://` kommt als Fehlermeldung
|
dem, was er wirklich behalten hat. Eine Adresse ohne `https://` kommt als Fehlermeldung
|
||||||
zurück, nicht als kaputter Eintrag.
|
zurück, nicht als kaputter Eintrag.
|
||||||
|
|
||||||
|
## Ein Satz Symbole für alles
|
||||||
|
|
||||||
|
Die Pixelraster stehen **an genau einer Stelle**: `web/glyphs.json`, 12×12
|
||||||
|
Zeichen je Symbol, ein `#` ist ein Pixel. Der Schreibtisch holt die Datei
|
||||||
|
selbst; die HoneyBar in fremden Apps bekommt die Raster **mit der App-Liste
|
||||||
|
mitgeliefert** — sie soll eine einzelne Datei ohne zweite Anfrage bleiben, und
|
||||||
|
`/api/apps` fragt sie ohnehin. Wer dort ein Symbol ergänzt, kann es sofort im
|
||||||
|
Programm-Editor auswählen; abgetippt wird nichts mehr.
|
||||||
|
|
||||||
|
Dass das nötig war, hatte einen Anlass: Die Wettersymbole standen einmal in
|
||||||
|
`os.js` **und** in `honeybar.js`. Sie liefen auseinander, und niemand merkte es,
|
||||||
|
bis das Wetter in der einen Leiste anders aussah als in der anderen.
|
||||||
|
|
||||||
|
**Wo die Symbole stehen:**
|
||||||
|
|
||||||
|
| | Symbol |
|
||||||
|
|---|---|
|
||||||
|
| App-Streifen in beiden Leisten | nach Schalter *Einstellungen → HoneyBar* |
|
||||||
|
| Aufklappmenü beider Leisten | immer |
|
||||||
|
| Kacheln, Schreibtischsymbole, Startmenü | immer |
|
||||||
|
|
||||||
|
Der Schalter gilt für **beide** Leisten zugleich — es ist dieselbe Leiste, nur
|
||||||
|
an zwei Orten. Er liegt wie alles andere im Server, wirkt also auf jedem Gerät
|
||||||
|
und in jeder App, ohne dass eine App neu gebaut wird.
|
||||||
|
|
||||||
## Zwei Betriebsarten, ein Datenstand
|
## Zwei Betriebsarten, ein Datenstand
|
||||||
|
|
||||||
| | Terminal-Modus | Desktop-Modus |
|
| | Terminal-Modus | Desktop-Modus |
|
||||||
@@ -446,8 +471,9 @@ internal/
|
|||||||
web/
|
web/
|
||||||
index.html
|
index.html
|
||||||
css/style.css
|
css/style.css
|
||||||
|
glyphs.json die Pixelraster — die einzige Quelle, beide Leisten
|
||||||
js/core.js Zustand, Server, Einstellungen, Kleinkram
|
js/core.js Zustand, Server, Einstellungen, Kleinkram
|
||||||
js/glyphs.js die Pixelsymbole
|
js/glyphs.js lädt die Raster und baut SVGs daraus
|
||||||
js/os.js die beiden Ansichten, Fenster, Dialoge
|
js/os.js die beiden Ansichten, Fenster, Dialoge
|
||||||
js/widgets/ ein Widget je Datei
|
js/widgets/ ein Widget je Datei
|
||||||
honeybar/ die HoneyBar für fremde Seiten — bewusst eine Datei
|
honeybar/ die HoneyBar für fremde Seiten — bewusst eine Datei
|
||||||
|
|||||||
@@ -28,6 +28,26 @@ import (
|
|||||||
//go:embed all:web
|
//go:embed all:web
|
||||||
var webFS embed.FS
|
var webFS embed.FS
|
||||||
|
|
||||||
|
// glyphen sind die Pixelraster aus web/glyphs.json. Der Schreibtisch holt sich
|
||||||
|
// dieselbe Datei selbst; die HoneyBar in einer fremden App kann das nicht — sie
|
||||||
|
// ist bewusst eine einzelne Datei ohne zweite Anfrage. Also reist das Raster
|
||||||
|
// mit der App-Liste mit. Eine Quelle, zwei Wege, kein Auseinanderlaufen.
|
||||||
|
var glyphen = ladeGlyphen()
|
||||||
|
|
||||||
|
func ladeGlyphen() map[string][]string {
|
||||||
|
b, err := webFS.ReadFile("web/glyphs.json")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("glyphs.json fehlt: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var g map[string][]string
|
||||||
|
if err := json.Unmarshal(b, &g); err != nil {
|
||||||
|
log.Printf("glyphs.json unlesbar: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -316,6 +336,7 @@ func (s *Server) handleApps(w http.ResponseWriter, r *http.Request, sub string)
|
|||||||
writeJSON(w, http.StatusOK, map[string]any{
|
writeJSON(w, http.StatusOK, map[string]any{
|
||||||
"apps": s.store.Apps(),
|
"apps": s.store.Apps(),
|
||||||
"settings": s.store.SettingsFor(sub),
|
"settings": s.store.SettingsFor(sub),
|
||||||
|
"glyphs": glyphen,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -231,6 +231,13 @@ body {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.mb-app:hover { color: var(--p); border-color: var(--p-dim); }
|
.mb-app:hover { color: var(--p); border-color: var(--p-dim); }
|
||||||
|
/* Symbol und Name in einer Zeile, wie in der HoneyBar. Das Symbol trägt die
|
||||||
|
Textfarbe, also stimmt es auch im umgekehrten Zustand der aktiven App. */
|
||||||
|
.mb-app { display: inline-flex; align-items: center; gap: 6px; }
|
||||||
|
.mb-ico,
|
||||||
|
.sm-ico { display: block; width: 12px; height: 12px; flex: 0 0 auto; }
|
||||||
|
.mb-ico svg,
|
||||||
|
.sm-ico svg { width: 12px; height: 12px; display: block; }
|
||||||
.mb-app[aria-current="true"] {
|
.mb-app[aria-current="true"] {
|
||||||
color: var(--bg);
|
color: var(--bg);
|
||||||
background: var(--p);
|
background: var(--p);
|
||||||
@@ -816,6 +823,10 @@ body {
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 6px 9px;
|
padding: 6px 9px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
/* Symbol links, Name daneben — in den Menüs stehen die Symbole immer. */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
}
|
}
|
||||||
.startmenu button:hover { background: var(--p); color: var(--bg); text-shadow: none; }
|
.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; }
|
.startmenu hr { border: 0; border-top: 1px solid var(--p-faint); margin: 3px 0; }
|
||||||
|
|||||||
+212
@@ -0,0 +1,212 @@
|
|||||||
|
{
|
||||||
|
"sonne": [
|
||||||
|
"............",
|
||||||
|
".....##.....",
|
||||||
|
"..#..##..#..",
|
||||||
|
"...#....#...",
|
||||||
|
"....####....",
|
||||||
|
"..##....##..",
|
||||||
|
"..##....##..",
|
||||||
|
"....####....",
|
||||||
|
"...#....#...",
|
||||||
|
"..#..##..#..",
|
||||||
|
".....##.....",
|
||||||
|
"............"
|
||||||
|
],
|
||||||
|
"heiter": [
|
||||||
|
"............",
|
||||||
|
"...##.......",
|
||||||
|
"..####..#...",
|
||||||
|
"...##..###..",
|
||||||
|
"......#####.",
|
||||||
|
"...####...#.",
|
||||||
|
"..#....##.#.",
|
||||||
|
".##........#",
|
||||||
|
"#..........#",
|
||||||
|
".##########.",
|
||||||
|
"............",
|
||||||
|
"............"
|
||||||
|
],
|
||||||
|
"wolke": [
|
||||||
|
"............",
|
||||||
|
"....####....",
|
||||||
|
"...#....#...",
|
||||||
|
"..#......##.",
|
||||||
|
".##........#",
|
||||||
|
"##.........#",
|
||||||
|
"#..........#",
|
||||||
|
"#..........#",
|
||||||
|
".##########.",
|
||||||
|
"............",
|
||||||
|
"............",
|
||||||
|
"............"
|
||||||
|
],
|
||||||
|
"nebel": [
|
||||||
|
"............",
|
||||||
|
"....####....",
|
||||||
|
"..##....##..",
|
||||||
|
".##.......#.",
|
||||||
|
"#..........#",
|
||||||
|
".##########.",
|
||||||
|
"............",
|
||||||
|
".#########..",
|
||||||
|
"............",
|
||||||
|
"..#########.",
|
||||||
|
"............",
|
||||||
|
".#########.."
|
||||||
|
],
|
||||||
|
"regen": [
|
||||||
|
"............",
|
||||||
|
"....####....",
|
||||||
|
"...#....#...",
|
||||||
|
"..#......##.",
|
||||||
|
".##........#",
|
||||||
|
"##.........#",
|
||||||
|
".##########.",
|
||||||
|
"............",
|
||||||
|
"..#..#..#...",
|
||||||
|
".#..#..#....",
|
||||||
|
"..#..#..#...",
|
||||||
|
".#..#..#...."
|
||||||
|
],
|
||||||
|
"schnee": [
|
||||||
|
"............",
|
||||||
|
"....####....",
|
||||||
|
"...#....#...",
|
||||||
|
"..#......##.",
|
||||||
|
".##........#",
|
||||||
|
"##.........#",
|
||||||
|
".##########.",
|
||||||
|
"............",
|
||||||
|
"..#...#...#.",
|
||||||
|
".#.#.#.#.#..",
|
||||||
|
"..#...#...#.",
|
||||||
|
"............"
|
||||||
|
],
|
||||||
|
"gewitter": [
|
||||||
|
"............",
|
||||||
|
"....####....",
|
||||||
|
"...#....#...",
|
||||||
|
"..#......##.",
|
||||||
|
".##........#",
|
||||||
|
"##.........#",
|
||||||
|
".##########.",
|
||||||
|
"......##....",
|
||||||
|
".....##.....",
|
||||||
|
"....####....",
|
||||||
|
"......##....",
|
||||||
|
".....##....."
|
||||||
|
],
|
||||||
|
"cloud": [
|
||||||
|
"............",
|
||||||
|
"....####....",
|
||||||
|
"...#....#...",
|
||||||
|
"..#......##.",
|
||||||
|
".##........#",
|
||||||
|
"##.........#",
|
||||||
|
"#..........#",
|
||||||
|
"#..........#",
|
||||||
|
".##########.",
|
||||||
|
"............",
|
||||||
|
"............",
|
||||||
|
"............"
|
||||||
|
],
|
||||||
|
"note": [
|
||||||
|
"..########..",
|
||||||
|
"..#......#..",
|
||||||
|
"..#.####.#..",
|
||||||
|
"..#......#..",
|
||||||
|
"..#.####.#..",
|
||||||
|
"..#......#..",
|
||||||
|
"..#.####.#..",
|
||||||
|
"..#......#..",
|
||||||
|
"..#.##...#..",
|
||||||
|
"..#......#..",
|
||||||
|
"..########..",
|
||||||
|
"............"
|
||||||
|
],
|
||||||
|
"box": [
|
||||||
|
"............",
|
||||||
|
".##########.",
|
||||||
|
".#........#.",
|
||||||
|
".#........#.",
|
||||||
|
".#...##...#.",
|
||||||
|
".#..####..#.",
|
||||||
|
".#..####..#.",
|
||||||
|
".#...##...#.",
|
||||||
|
".#........#.",
|
||||||
|
".#........#.",
|
||||||
|
".##########.",
|
||||||
|
"............"
|
||||||
|
],
|
||||||
|
"chip": [
|
||||||
|
"............",
|
||||||
|
"..#.#.#.#...",
|
||||||
|
".##########.",
|
||||||
|
".#........#.",
|
||||||
|
"##.######.##",
|
||||||
|
".#.#....#.#.",
|
||||||
|
"##.#....#.##",
|
||||||
|
".#.######.#.",
|
||||||
|
".#........#.",
|
||||||
|
".##########.",
|
||||||
|
"..#.#.#.#...",
|
||||||
|
"............"
|
||||||
|
],
|
||||||
|
"gear": [
|
||||||
|
"....####....",
|
||||||
|
"..#.####.#..",
|
||||||
|
"..########..",
|
||||||
|
".####..####.",
|
||||||
|
"###......###",
|
||||||
|
"##........##",
|
||||||
|
"##........##",
|
||||||
|
"###......###",
|
||||||
|
".####..####.",
|
||||||
|
"..########..",
|
||||||
|
"..#.####.#..",
|
||||||
|
"....####...."
|
||||||
|
],
|
||||||
|
"globe": [
|
||||||
|
"............",
|
||||||
|
"...######...",
|
||||||
|
"..#..#...#..",
|
||||||
|
".#...#....#.",
|
||||||
|
"############",
|
||||||
|
"#....#.....#",
|
||||||
|
"#....#.....#",
|
||||||
|
"############",
|
||||||
|
".#...#....#.",
|
||||||
|
"..#..#...#..",
|
||||||
|
"...######...",
|
||||||
|
"............"
|
||||||
|
],
|
||||||
|
"disk": [
|
||||||
|
"............",
|
||||||
|
"############",
|
||||||
|
"#..######..#",
|
||||||
|
"#..#....#..#",
|
||||||
|
"#..#....#..#",
|
||||||
|
"#..######..#",
|
||||||
|
"#..........#",
|
||||||
|
"#.########.#",
|
||||||
|
"#.#......#.#",
|
||||||
|
"#.#......#.#",
|
||||||
|
"############",
|
||||||
|
"............"
|
||||||
|
],
|
||||||
|
"folder": [
|
||||||
|
"............",
|
||||||
|
".####.......",
|
||||||
|
"#....#......",
|
||||||
|
"#.....######",
|
||||||
|
"#..........#",
|
||||||
|
"#..........#",
|
||||||
|
"#..........#",
|
||||||
|
"#..........#",
|
||||||
|
"#..........#",
|
||||||
|
"############",
|
||||||
|
"............",
|
||||||
|
"............"
|
||||||
|
]
|
||||||
|
}
|
||||||
+38
-20
@@ -56,6 +56,7 @@
|
|||||||
|
|
||||||
let apps = DEFAULT_APPS;
|
let apps = DEFAULT_APPS;
|
||||||
let settings = null;
|
let settings = null;
|
||||||
|
let glyphs = {};
|
||||||
// Was die Seite oben schon hatte, bevor die HoneyBar sie heruntergeschoben hat.
|
// Was die Seite oben schon hatte, bevor die HoneyBar sie heruntergeschoben hat.
|
||||||
let urspruenglichesPolster = null;
|
let urspruenglichesPolster = null;
|
||||||
|
|
||||||
@@ -69,22 +70,13 @@
|
|||||||
const SAFE = "env(safe-area-inset-top, 0px)";
|
const SAFE = "env(safe-area-inset-top, 0px)";
|
||||||
const HOEHE = "calc(" + BAR_H + "px + " + SAFE + ")";
|
const HOEHE = "calc(" + BAR_H + "px + " + SAFE + ")";
|
||||||
|
|
||||||
/* Wettersymbole als Pixelraster, dieselben Namen wie in weather.go.
|
/* Alle Pixelraster kommen mit der App-Liste vom Server (web/glyphs.json).
|
||||||
Sie stehen auch in os.js — bis die geteilte Symbolquelle (1.3) gebaut ist,
|
Frueher standen die Wettersymbole hier noch einmal abgetippt — genau das
|
||||||
ist das die kleinere Sünde: Die HoneyBar soll eine einzelne Datei bleiben,
|
war schon einmal auseinandergelaufen. Diese Datei bleibt trotzdem eine
|
||||||
die eine fremde App mit einer Zeile einbindet. */
|
einzelne ohne zweite Anfrage: Sie fragt ohnehin nach der App-Liste, und
|
||||||
const WX = {
|
die bringt die Raster einfach mit. */
|
||||||
sonne: "............;.....##.....;..#..##..#..;...#....#...;....####....;..##....##..;..##....##..;....####....;...#....#...;..#..##..#..;.....##.....;............",
|
function rasterSvg(grid) {
|
||||||
heiter: "............;...##.......;..####..#...;...##..###..;......#####.;...####...#.;..#....##.#.;.##........#;#..........#;.##########.;............;............",
|
if (!grid) return "";
|
||||||
wolke: "............;....####....;...#....#...;..#......##.;.##........#;##.........#;#..........#;#..........#;.##########.;............;............;............",
|
|
||||||
nebel: "............;....####....;..##....##..;.##.......#.;#..........#;.##########.;............;.#########..;............;..#########.;............;.#########..",
|
|
||||||
regen: "............;....####....;...#....#...;..#......##.;.##........#;##.........#;.##########.;............;..#..#..#...;.#..#..#....;..#..#..#...;.#..#..#....",
|
|
||||||
schnee: "............;....####....;...#....#...;..#......##.;.##........#;##.........#;.##########.;............;..#...#...#.;.#.#.#.#.#..;..#...#...#.;............",
|
|
||||||
gewitter: "............;....####....;...#....#...;..#......##.;.##........#;##.........#;.##########.;......##....;.....##.....;....####....;......##....;.....##.....",
|
|
||||||
};
|
|
||||||
|
|
||||||
function wxSvg(name) {
|
|
||||||
const grid = (WX[name] || WX.wolke).split(";");
|
|
||||||
let d = "";
|
let d = "";
|
||||||
grid.forEach((row, y) => {
|
grid.forEach((row, y) => {
|
||||||
let run = 0;
|
let run = 0;
|
||||||
@@ -93,7 +85,7 @@
|
|||||||
else if (run) { d += `<rect x="${x - run}" y="${y}" width="${run}" height="1"/>`; run = 0; }
|
else if (run) { d += `<rect x="${x - run}" y="${y}" width="${run}" height="1"/>`; run = 0; }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return `<svg viewBox="0 0 12 12" fill="currentColor" aria-hidden="true">${d}</svg>`;
|
return `<svg viewBox="0 0 12 12" fill="currentColor" shape-rendering="crispEdges" aria-hidden="true">${d}</svg>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const host = document.createElement("honey-bar");
|
const host = document.createElement("honey-bar");
|
||||||
@@ -163,6 +155,11 @@
|
|||||||
}
|
}
|
||||||
.apps::-webkit-scrollbar { display: none; }
|
.apps::-webkit-scrollbar { display: none; }
|
||||||
.app { color: var(--p-mid); letter-spacing: .08em; flex: 0 0 auto; scroll-snap-align: end; }
|
.app { color: var(--p-mid); letter-spacing: .08em; flex: 0 0 auto; scroll-snap-align: end; }
|
||||||
|
/* Symbol und Name in einer Zeile. Das Symbol traegt die Textfarbe, also
|
||||||
|
stimmt es in jeder Roehre und im umgekehrten Zustand von selbst. */
|
||||||
|
.app, .menu a[role="menuitem"] { display: inline-flex; align-items: center; gap: 6px; }
|
||||||
|
.ico { display: block; width: 12px; height: 12px; flex: 0 0 auto; }
|
||||||
|
.ico svg { width: 12px; height: 12px; display: block; }
|
||||||
.app:hover { color: var(--p); border-color: var(--p-dim); }
|
.app:hover { color: var(--p); border-color: var(--p-dim); }
|
||||||
.app[aria-current="page"] {
|
.app[aria-current="page"] {
|
||||||
background: var(--p); border-color: var(--p); color: var(--bg); text-shadow: none;
|
background: var(--p); border-color: var(--p); color: var(--bg); text-shadow: none;
|
||||||
@@ -261,20 +258,40 @@
|
|||||||
nav.innerHTML = "";
|
nav.innerHTML = "";
|
||||||
menuApps.innerHTML = "";
|
menuApps.innerHTML = "";
|
||||||
|
|
||||||
|
// Der Schalter gilt nur fuer den Streifen. Im Aufklappmenue stehen die
|
||||||
|
// Symbole immer: Dort ist Platz, und dort sucht man eine App, statt sie
|
||||||
|
// im Vorbeigehen wiederzuerkennen.
|
||||||
|
const symboleImStreifen = !settings || settings.barGlyphs !== "off";
|
||||||
|
|
||||||
|
const symbol = (app) => {
|
||||||
|
const grid = glyphs[app.glyph] || glyphs.box;
|
||||||
|
if (!grid) return null;
|
||||||
|
const span = document.createElement("span");
|
||||||
|
span.className = "ico";
|
||||||
|
span.innerHTML = rasterSvg(grid);
|
||||||
|
return span;
|
||||||
|
};
|
||||||
|
|
||||||
apps.forEach((app) => {
|
apps.forEach((app) => {
|
||||||
if (app.id === "honeyos") return; // steckt schon links im Logo
|
if (app.id === "honeyos") return; // steckt schon links im Logo
|
||||||
|
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
a.className = "app";
|
a.className = "app";
|
||||||
a.href = app.url;
|
a.href = app.url;
|
||||||
a.textContent = app.name;
|
if (symboleImStreifen) {
|
||||||
|
const ico = symbol(app);
|
||||||
|
if (ico) a.appendChild(ico);
|
||||||
|
}
|
||||||
|
a.appendChild(document.createTextNode(app.name));
|
||||||
if (app.id === current) a.setAttribute("aria-current", "page");
|
if (app.id === current) a.setAttribute("aria-current", "page");
|
||||||
nav.appendChild(a);
|
nav.appendChild(a);
|
||||||
|
|
||||||
const m = document.createElement("a");
|
const m = document.createElement("a");
|
||||||
m.href = app.url;
|
m.href = app.url;
|
||||||
m.setAttribute("role", "menuitem");
|
m.setAttribute("role", "menuitem");
|
||||||
m.textContent = (app.id === current ? "▸ " : " ") + app.name;
|
const ico = symbol(app);
|
||||||
|
if (ico) m.appendChild(ico);
|
||||||
|
m.appendChild(document.createTextNode((app.id === current ? "\u25b8 " : "") + app.name));
|
||||||
menuApps.appendChild(m);
|
menuApps.appendChild(m);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -300,7 +317,7 @@
|
|||||||
return; // offline oder HoneyOS aus — die App geht das nichts an
|
return; // offline oder HoneyOS aus — die App geht das nichts an
|
||||||
}
|
}
|
||||||
const el = root.getElementById("wx");
|
const el = root.getElementById("wx");
|
||||||
el.innerHTML = wxSvg(w.glyph) + `<span class="grad">${Math.round(w.grad)}°</span>`;
|
el.innerHTML = rasterSvg(glyphs[w.glyph] || glyphs.wolke) + `<span class="grad">${Math.round(w.grad)}°</span>`;
|
||||||
el.title = `${w.ort}: ${w.text}, ${Math.round(w.grad)}°`;
|
el.title = `${w.ort}: ${w.text}, ${Math.round(w.grad)}°`;
|
||||||
el.hidden = false;
|
el.hidden = false;
|
||||||
}
|
}
|
||||||
@@ -510,6 +527,7 @@
|
|||||||
apps = [{ id: "honeyos", name: "DESKTOP", url: homeUrl }].concat(data.apps);
|
apps = [{ id: "honeyos", name: "DESKTOP", url: homeUrl }].concat(data.apps);
|
||||||
}
|
}
|
||||||
settings = data.settings || null;
|
settings = data.settings || null;
|
||||||
|
if (data.glyphs) glyphs = data.glyphs;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
/* nicht angemeldet, kein CORS, kein Netz → eingebaute Liste;
|
/* nicht angemeldet, kein CORS, kein Netz → eingebaute Liste;
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ export const DEFAULT_SETTINGS = {
|
|||||||
flicker: "on",
|
flicker: "on",
|
||||||
glow: "on",
|
glow: "on",
|
||||||
openIn: "tab", // tab | same
|
openIn: "tab", // tab | same
|
||||||
|
// Symbole im App-Streifen beider Leisten. Im Aufklappmenü stehen sie immer.
|
||||||
|
barGlyphs: "on",
|
||||||
// Wetter. Zwei Schalter statt einem: Der Schreibtisch und die HoneyBar sind
|
// Wetter. Zwei Schalter statt einem: Der Schreibtisch und die HoneyBar sind
|
||||||
// zwei verschiedene Orte, und wer das Wetter beim Arbeiten in einer App will,
|
// zwei verschiedene Orte, und wer das Wetter beim Arbeiten in einer App will,
|
||||||
// will es nicht zwingend auch auf einer Seite, die er selten aufruft.
|
// will es nicht zwingend auch auf einer Seite, die er selten aufruft.
|
||||||
|
|||||||
+32
-224
@@ -1,235 +1,43 @@
|
|||||||
/* Die Pixelsymbole von HoneyOS.
|
/* Die Pixelsymbole von HoneyOS.
|
||||||
*
|
*
|
||||||
* Eigene Datei, weil sie Daten sind und nicht Verhalten: zwölf mal zwölf
|
* Die Raster selbst stehen in `web/glyphs.json` — **an genau einer Stelle**,
|
||||||
* Zeichen je Symbol, mehr passiert hier nicht. Dieselben Raster stehen noch
|
* absichtlich als Daten statt als Quelltext: Der Schreibtisch holt sie hier,
|
||||||
* einmal in honeybar/honeybar.js — die soll eine einzelne Datei bleiben, die
|
* die HoneyBar in fremden Apps bekommt sie vom Server mit der App-Liste
|
||||||
* eine fremde App mit einer Zeile einbindet (siehe IDEAS 1.3).
|
* mitgeliefert (siehe main.go). Vorher standen sie zweimal da, und die
|
||||||
|
* Wettersymbole waren schon einmal auseinandergelaufen.
|
||||||
|
*
|
||||||
|
* Das Laden geschieht mit `await` auf oberster Ebene: Wer dieses Modul
|
||||||
|
* einbindet, bekommt fertige Symbole und darf sie überall gerade heraus
|
||||||
|
* benutzen — sonst müsste jede Zeichenfunktion asynchron werden.
|
||||||
*/
|
*/
|
||||||
/* ── Pixel-Glyphen ─────────────────────────────────────────────────────────
|
|
||||||
* 12×12-Raster als Zeichenketten. Ein `#` ist ein Pixel. So bleiben die Icons
|
|
||||||
* im Retro-Raster statt aus einer Icon-Bibliothek zu kommen.
|
|
||||||
*/
|
|
||||||
export const GLYPHS = {
|
|
||||||
// Wettersymbole. Dieselben Namen wie in weather.go (wmo) und in der HoneyBar —
|
|
||||||
// solange 1.3 (geteilte Symbolquelle) nicht gebaut ist, stehen sie zweimal da.
|
|
||||||
sonne: [
|
|
||||||
"............",
|
|
||||||
".....##.....",
|
|
||||||
"..#..##..#..",
|
|
||||||
"...#....#...",
|
|
||||||
"....####....",
|
|
||||||
"..##....##..",
|
|
||||||
"..##....##..",
|
|
||||||
"....####....",
|
|
||||||
"...#....#...",
|
|
||||||
"..#..##..#..",
|
|
||||||
".....##.....",
|
|
||||||
"............",
|
|
||||||
],
|
|
||||||
heiter: [
|
|
||||||
"............",
|
|
||||||
"...##.......",
|
|
||||||
"..####..#...",
|
|
||||||
"...##..###..",
|
|
||||||
"......#####.",
|
|
||||||
"...####...#.",
|
|
||||||
"..#....##.#.",
|
|
||||||
".##........#",
|
|
||||||
"#..........#",
|
|
||||||
".##########.",
|
|
||||||
"............",
|
|
||||||
"............",
|
|
||||||
],
|
|
||||||
wolke: [
|
|
||||||
"............",
|
|
||||||
"....####....",
|
|
||||||
"...#....#...",
|
|
||||||
"..#......##.",
|
|
||||||
".##........#",
|
|
||||||
"##.........#",
|
|
||||||
"#..........#",
|
|
||||||
"#..........#",
|
|
||||||
".##########.",
|
|
||||||
"............",
|
|
||||||
"............",
|
|
||||||
"............",
|
|
||||||
],
|
|
||||||
nebel: [
|
|
||||||
"............",
|
|
||||||
"....####....",
|
|
||||||
"..##....##..",
|
|
||||||
".##.......#.",
|
|
||||||
"#..........#",
|
|
||||||
".##########.",
|
|
||||||
"............",
|
|
||||||
".#########..",
|
|
||||||
"............",
|
|
||||||
"..#########.",
|
|
||||||
"............",
|
|
||||||
".#########..",
|
|
||||||
],
|
|
||||||
regen: [
|
|
||||||
"............",
|
|
||||||
"....####....",
|
|
||||||
"...#....#...",
|
|
||||||
"..#......##.",
|
|
||||||
".##........#",
|
|
||||||
"##.........#",
|
|
||||||
".##########.",
|
|
||||||
"............",
|
|
||||||
"..#..#..#...",
|
|
||||||
".#..#..#....",
|
|
||||||
"..#..#..#...",
|
|
||||||
".#..#..#....",
|
|
||||||
],
|
|
||||||
schnee: [
|
|
||||||
"............",
|
|
||||||
"....####....",
|
|
||||||
"...#....#...",
|
|
||||||
"..#......##.",
|
|
||||||
".##........#",
|
|
||||||
"##.........#",
|
|
||||||
".##########.",
|
|
||||||
"............",
|
|
||||||
"..#...#...#.",
|
|
||||||
".#.#.#.#.#..",
|
|
||||||
"..#...#...#.",
|
|
||||||
"............",
|
|
||||||
],
|
|
||||||
gewitter: [
|
|
||||||
"............",
|
|
||||||
"....####....",
|
|
||||||
"...#....#...",
|
|
||||||
"..#......##.",
|
|
||||||
".##........#",
|
|
||||||
"##.........#",
|
|
||||||
".##########.",
|
|
||||||
"......##....",
|
|
||||||
".....##.....",
|
|
||||||
"....####....",
|
|
||||||
"......##....",
|
|
||||||
".....##.....",
|
|
||||||
],
|
|
||||||
|
|
||||||
cloud: [
|
/** 12×12-Raster als Zeichenketten, ein `#` ist ein Pixel. */
|
||||||
"............",
|
export const GLYPHS = await ladeGlyphen();
|
||||||
"....####....",
|
|
||||||
"...#....#...",
|
async function ladeGlyphen() {
|
||||||
"..#......##.",
|
try {
|
||||||
".##........#",
|
const res = await fetch(new URL("../glyphs.json", import.meta.url), { cache: "no-store" });
|
||||||
"##.........#",
|
if (res.ok) return await res.json();
|
||||||
"#..........#",
|
} catch {
|
||||||
"#..........#",
|
/* fällt unten auf ein leeres Raster zurück */
|
||||||
".##########.",
|
}
|
||||||
"............",
|
// Ohne Symbole ist der Schreibtisch hässlich, aber benutzbar — ein leerer
|
||||||
"............",
|
// Bildschirm wäre schlimmer. Passieren kann es nur, wenn der eigene Server
|
||||||
"............",
|
// gerade nicht antwortet; dann ist ohnehin nichts zu holen.
|
||||||
],
|
console.warn("HoneyOS: glyphs.json nicht geladen — Symbole fehlen.");
|
||||||
note: [
|
return {};
|
||||||
"..########..",
|
}
|
||||||
"..#......#..",
|
|
||||||
"..#.####.#..",
|
|
||||||
"..#......#..",
|
|
||||||
"..#.####.#..",
|
|
||||||
"..#......#..",
|
|
||||||
"..#.####.#..",
|
|
||||||
"..#......#..",
|
|
||||||
"..#.##...#..",
|
|
||||||
"..#......#..",
|
|
||||||
"..########..",
|
|
||||||
"............",
|
|
||||||
],
|
|
||||||
box: [
|
|
||||||
"............",
|
|
||||||
".##########.",
|
|
||||||
".#........#.",
|
|
||||||
".#........#.",
|
|
||||||
".#...##...#.",
|
|
||||||
".#..####..#.",
|
|
||||||
".#..####..#.",
|
|
||||||
".#...##...#.",
|
|
||||||
".#........#.",
|
|
||||||
".#........#.",
|
|
||||||
".##########.",
|
|
||||||
"............",
|
|
||||||
],
|
|
||||||
chip: [
|
|
||||||
"............",
|
|
||||||
"..#.#.#.#...",
|
|
||||||
".##########.",
|
|
||||||
".#........#.",
|
|
||||||
"##.######.##",
|
|
||||||
".#.#....#.#.",
|
|
||||||
"##.#....#.##",
|
|
||||||
".#.######.#.",
|
|
||||||
".#........#.",
|
|
||||||
".##########.",
|
|
||||||
"..#.#.#.#...",
|
|
||||||
"............",
|
|
||||||
],
|
|
||||||
gear: [
|
|
||||||
"....####....",
|
|
||||||
"..#.####.#..",
|
|
||||||
"..########..",
|
|
||||||
".####..####.",
|
|
||||||
"###......###",
|
|
||||||
"##........##",
|
|
||||||
"##........##",
|
|
||||||
"###......###",
|
|
||||||
".####..####.",
|
|
||||||
"..########..",
|
|
||||||
"..#.####.#..",
|
|
||||||
"....####....",
|
|
||||||
],
|
|
||||||
globe: [
|
|
||||||
"............",
|
|
||||||
"...######...",
|
|
||||||
"..#..#...#..",
|
|
||||||
".#...#....#.",
|
|
||||||
"############",
|
|
||||||
"#....#.....#",
|
|
||||||
"#....#.....#",
|
|
||||||
"############",
|
|
||||||
".#...#....#.",
|
|
||||||
"..#..#...#..",
|
|
||||||
"...######...",
|
|
||||||
"............",
|
|
||||||
],
|
|
||||||
disk: [
|
|
||||||
"............",
|
|
||||||
"############",
|
|
||||||
"#..######..#",
|
|
||||||
"#..#....#..#",
|
|
||||||
"#..#....#..#",
|
|
||||||
"#..######..#",
|
|
||||||
"#..........#",
|
|
||||||
"#.########.#",
|
|
||||||
"#.#......#.#",
|
|
||||||
"#.#......#.#",
|
|
||||||
"############",
|
|
||||||
"............",
|
|
||||||
],
|
|
||||||
folder: [
|
|
||||||
"............",
|
|
||||||
".####.......",
|
|
||||||
"#....#......",
|
|
||||||
"#.....######",
|
|
||||||
"#..........#",
|
|
||||||
"#..........#",
|
|
||||||
"#..........#",
|
|
||||||
"#..........#",
|
|
||||||
"#..........#",
|
|
||||||
"############",
|
|
||||||
"............",
|
|
||||||
"............",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export function glyphSvg(name) {
|
export function glyphSvg(name) {
|
||||||
const grid = GLYPHS[name] || GLYPHS.box;
|
return rasterSvg(GLYPHS[name] || GLYPHS.box);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Baut aus einem 12×12-Raster ein SVG. Waagerechte Läufe werden
|
||||||
|
* zusammengefasst: weniger Rechtecke, gleiches Bild. */
|
||||||
|
export function rasterSvg(grid) {
|
||||||
|
if (!grid) return "";
|
||||||
const rects = [];
|
const rects = [];
|
||||||
grid.forEach((row, y) => {
|
grid.forEach((row, y) => {
|
||||||
// Waagerechte Läufe zusammenfassen: weniger Rechtecke, gleiches Bild.
|
|
||||||
let run = 0;
|
let run = 0;
|
||||||
for (let x = 0; x <= row.length; x++) {
|
for (let x = 0; x <= row.length; x++) {
|
||||||
if (row[x] === "#") {
|
if (row[x] === "#") {
|
||||||
|
|||||||
+33
-4
@@ -123,10 +123,19 @@ function renderShell() {
|
|||||||
function renderMenubarApps() {
|
function renderMenubarApps() {
|
||||||
const nav = $("mbApps");
|
const nav = $("mbApps");
|
||||||
nav.innerHTML = "";
|
nav.innerHTML = "";
|
||||||
|
// Derselbe Schalter wie in der HoneyBar fremder Apps: Wer die Symbole dort
|
||||||
|
// abstellt, will sie hier auch nicht — es ist dieselbe Leiste.
|
||||||
|
const mitSymbol = state.settings.barGlyphs !== "off";
|
||||||
state.cfg.apps.forEach((app) => {
|
state.cfg.apps.forEach((app) => {
|
||||||
const b = document.createElement("button");
|
const b = document.createElement("button");
|
||||||
b.className = "mb-app";
|
b.className = "mb-app";
|
||||||
b.textContent = app.name;
|
if (mitSymbol) {
|
||||||
|
const ico = document.createElement("span");
|
||||||
|
ico.className = "mb-ico";
|
||||||
|
ico.innerHTML = glyphSvg(app.glyph);
|
||||||
|
b.appendChild(ico);
|
||||||
|
}
|
||||||
|
b.appendChild(document.createTextNode(app.name));
|
||||||
b.title = `${app.name} öffnen — ${app.url}`;
|
b.title = `${app.name} öffnen — ${app.url}`;
|
||||||
b.addEventListener("click", () => launch(app));
|
b.addEventListener("click", () => launch(app));
|
||||||
nav.appendChild(b);
|
nav.appendChild(b);
|
||||||
@@ -564,6 +573,12 @@ function openSettings() {
|
|||||||
${seg("glow", [["on", "glühen an"], ["off", "aus"]], s.glow)}
|
${seg("glow", [["on", "glühen an"], ["off", "aus"]], s.glow)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<span class="label">HoneyBar</span>
|
||||||
|
${seg("barGlyphs", [["on", "symbole an"], ["off", "nur namen"]], s.barGlyphs)}
|
||||||
|
<p class="ae-hint" style="margin-left:0">gilt für den App-Streifen hier und in jeder App</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<span class="label">Apps öffnen</span>
|
<span class="label">Apps öffnen</span>
|
||||||
${seg("openIn", [["tab", "neuer tab"], ["same", "gleiches fenster"]], s.openIn)}
|
${seg("openIn", [["tab", "neuer tab"], ["same", "gleiches fenster"]], s.openIn)}
|
||||||
@@ -619,6 +634,7 @@ function openSettings() {
|
|||||||
renderStats();
|
renderStats();
|
||||||
if (key.startsWith("weather")) loadWeather();
|
if (key.startsWith("weather")) loadWeather();
|
||||||
if (key === "openIn" && state.settings.mode === "terminal") renderTiles();
|
if (key === "openIn" && state.settings.mode === "terminal") renderTiles();
|
||||||
|
if (key === "barGlyphs") renderMenubarApps();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -684,7 +700,11 @@ function openSettings() {
|
|||||||
function openApps() {
|
function openApps() {
|
||||||
let draft = state.cfg.apps.map((a) => ({ ...a }));
|
let draft = state.cfg.apps.map((a) => ({ ...a }));
|
||||||
|
|
||||||
const GLYPHS_LIST = ["cloud", "note", "box", "chip", "folder", "gear", "globe", "disk"];
|
// Die Auswahl kommt aus glyphs.json selbst — wer dort ein Symbol ergänzt,
|
||||||
|
// kann es sofort wählen. Die Wettersymbole liegen in derselben Datei (die
|
||||||
|
// HoneyBar braucht beides in einer Antwort), taugen aber nicht als App-Logo.
|
||||||
|
const WETTER_GLYPHEN = ["sonne", "heiter", "wolke", "nebel", "regen", "schnee", "gewitter"];
|
||||||
|
const GLYPHS_LIST = Object.keys(GLYPHS).filter((g) => !WETTER_GLYPHEN.includes(g));
|
||||||
|
|
||||||
const rowHtml = (a, i) => `
|
const rowHtml = (a, i) => `
|
||||||
<details class="approw" ${a.__neu ? "open" : ""}>
|
<details class="approw" ${a.__neu ? "open" : ""}>
|
||||||
@@ -823,7 +843,14 @@ function buildAppMenu() {
|
|||||||
menu.innerHTML =
|
menu.innerHTML =
|
||||||
`<div class="sm-head">wechseln zu</div>` +
|
`<div class="sm-head">wechseln zu</div>` +
|
||||||
(state.cfg.apps.length
|
(state.cfg.apps.length
|
||||||
? state.cfg.apps.map((a) => `<button data-app="${escapeAttr(a.id)}">${escapeHtml(a.name)}</button>`).join("")
|
? state.cfg.apps
|
||||||
|
.map(
|
||||||
|
(a) =>
|
||||||
|
// Im Menü stehen die Symbole immer — dort ist Platz, und dort
|
||||||
|
// sucht man eine App, statt sie im Vorbeigehen zu erkennen.
|
||||||
|
`<button data-app="${escapeAttr(a.id)}"><span class="sm-ico">${glyphSvg(a.glyph)}</span>${escapeHtml(a.name)}</button>`
|
||||||
|
)
|
||||||
|
.join("")
|
||||||
: `<button data-settings="1">keine app eingetragen …</button>`);
|
: `<button data-settings="1">keine app eingetragen …</button>`);
|
||||||
|
|
||||||
menu.querySelectorAll("[data-app]").forEach((b) =>
|
menu.querySelectorAll("[data-app]").forEach((b) =>
|
||||||
@@ -925,7 +952,9 @@ function buildStartMenu() {
|
|||||||
closeAppMenu();
|
closeAppMenu();
|
||||||
|
|
||||||
menu.innerHTML = `<div class="sm-head">starten</div>` +
|
menu.innerHTML = `<div class="sm-head">starten</div>` +
|
||||||
state.cfg.apps.map((a) => `<button data-app="${a.id}">${escapeHtml(a.name)}</button>`).join("") +
|
state.cfg.apps
|
||||||
|
.map((a) => `<button data-app="${escapeAttr(a.id)}"><span class="sm-ico">${glyphSvg(a.glyph)}</span>${escapeHtml(a.name)}</button>`)
|
||||||
|
.join("") +
|
||||||
`<hr /><button data-win="programme">Programme</button>
|
`<hr /><button data-win="programme">Programme</button>
|
||||||
<button data-win="system">System</button>
|
<button data-win="system">System</button>
|
||||||
<button data-win="settings">Einstellungen</button>
|
<button data-win="settings">Einstellungen</button>
|
||||||
|
|||||||
Reference in new issue
Block a user