diff --git a/IDEAS.md b/IDEAS.md
index 040b198..5089210 100644
--- a/IDEAS.md
+++ b/IDEAS.md
@@ -326,6 +326,12 @@ Zu klären, wenn es soweit ist:
- Passt zum Aufnehmen eigener App-Logos (1.3): dieselbe Ablage, dasselbe
Ausliefern. Zusammen bauen spart die Hälfte.
+**Nachtrag 31.08.2026.** Hochladen, Verlinken und Entfernen standen erst im
+Widget selbst und sind in die Einstellungen gewandert (Marcs Einwand): Das
+Widget zeigt, die Einstellungen stellen ein — wie beim Wetter. Damit gilt für
+alle Widgets dieselbe Regel, und der Schreibtisch bleibt eine Anzeige statt
+einer Bedienoberfläche.
+
### Notiz-Widget — ✅ gebaut (30.08.2026), Sofort-Ansicht (31.08.2026)
Ein Zettel auf dem Schreibtisch: hineintippen, fertig. Serverseitig je Nutzer,
diff --git a/README.md b/README.md
index 63696c7..f78bfce 100644
--- a/README.md
+++ b/README.md
@@ -279,7 +279,10 @@ gewinnt.
### Das Bild
Genau eines je Nutzer, hochgeladen oder verlinkt; ein neues ersetzt das alte
-samt Datei. Erlaubt sind PNG, JPEG, WebP und GIF — der Typ wird aus den
+samt Datei. **Eingestellt wird es unter *Einstellungen → Bild*, nicht im Widget**
+— wie beim Wetter, dessen Ort auch nicht in der Wetterkachel gesucht wird. Ein
+Schild auf dem Schreibtisch, das zu drei Vierteln aus Knöpfen besteht, zeigt vor
+allem sich selbst. Erlaubt sind PNG, JPEG, WebP und GIF — der Typ wird aus den
**Bytes** bestimmt, nicht aus dem, was der Browser behauptet. SVG ist bewusst
nicht dabei: Es kann Skripte enthalten. Ausgeliefert wird mit einem
Versionsanteil in der Adresse, sonst bliebe beim Austausch das alte Bild im
diff --git a/web/css/style.css b/web/css/style.css
index bfdecb6..d9e5dca 100644
--- a/web/css/style.css
+++ b/web/css/style.css
@@ -1307,6 +1307,26 @@ body {
.bildw-bild { width: 100%; height: 100%; object-fit: cover; display: block; }
.bildw .w-zustand { padding: 0 12px; text-align: center; }
+/* Die Knöpfe stehen in den Einstellungen, nicht im Widget: links eine kleine
+ Vorschau, rechts was man damit tun kann. */
+.bildset { display: flex; gap: 10px; align-items: flex-start; }
+.bildset-vorschau {
+ flex: none;
+ width: 84px;
+ height: 56px;
+ border: 1px solid var(--p-faint);
+ background: var(--bg);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+}
+.bildset-bild { width: 100%; height: 100%; object-fit: cover; display: block; }
+.bildset-leer { color: var(--p-dim); font-size: 11px; }
+.bildset-rechts { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
+.bildset-knoepfe { display: flex; gap: 4px; flex-wrap: wrap; }
+.bildset-knoepfe .btn { font-size: 12px; padding: 3px 9px; }
+
.bildw-linkzeile { display: flex; gap: 6px; margin-top: 8px; }
.bildw-linkzeile input {
flex: 1;
@@ -1319,7 +1339,6 @@ body {
padding: 4px 6px;
}
.bildw-linkzeile input:focus { outline: none; border-color: var(--p); }
-.bildw-knoepfe { display: flex; gap: 4px; flex-wrap: wrap; justify-content: flex-end; }
/* Einspaltig (Handy, schmales Fenster) sollen die Widgets so breit sein wie das
Systemfenster. Drei verschieden breite Kästen untereinander sehen aus wie ein
diff --git a/web/js/os.js b/web/js/os.js
index c68e1ef..67a392c 100644
--- a/web/js/os.js
+++ b/web/js/os.js
@@ -11,7 +11,7 @@ import "./widgets/user.js";
import { statsHtml } from "./widgets/system.js";
import { loadWeather, renderWeatherBar } from "./widgets/weather.js";
import "./widgets/notiz.js";
-import "./widgets/bild.js";
+import { bildFormularHtml, bildFormularMontieren } from "./widgets/bild.js";
// Die Reihenfolge steht hier und nicht in den Importzeilen darüber: Module
// werden nicht in deren Reihenfolge ausgewertet, sobald eines auf oberster
@@ -590,6 +590,12 @@ function openSettings() {
`
Reihenfolge und An/Aus liegen bei deinem Konto, nicht am Gerät.
Genau ein Bild je Konto; ein neues ersetzt das alte.
+ Zu sehen ist es im Widget „bild".
`
+ ) +
sektion(
"Wetter",
`
@@ -638,6 +644,7 @@ function openSettings() {
});
widgetListeZeichnen(root.querySelector("#wlListe"));
+ bildFormularMontieren(root);
const zeigeOrt = () => {
root.querySelector("#wxCurrent").textContent = state.settings.weatherCity
diff --git a/web/js/widgets/bild.js b/web/js/widgets/bild.js
index 5078694..6f20dfc 100644
--- a/web/js/widgets/bild.js
+++ b/web/js/widgets/bild.js
@@ -10,92 +10,142 @@ import { widgetAnmelden, widgetsLaden, widgetsZeichnen } from "./rahmen.js";
* Hochgeladen wird zum eigenen Server; verlinkt wird auf eine fremde Adresse.
* Der Unterschied ist keiner der Bequemlichkeit: Ein Link holt bei jedem
* Aufbau des Schreibtischs etwas von woanders, ein Upload nie.
+ *
+ * DAS WIDGET ZEIGT, DIE EINSTELLUNGEN STELLEN EIN.
+ *
+ * Hochladen, Verlinken und Entfernen standen erst im Widget selbst. Das ist die
+ * Ausnahme gewesen, nicht die Regel: Der Ort des Wetters wird auch nicht in der
+ * Wetterkachel gesucht. Ein Schild auf dem Schreibtisch, das zu drei Vierteln
+ * aus Knöpfen besteht, zeigt vor allem sich selbst.
+ *
+ * Also: Das Widget zeigt nur noch das Bild. Die Knöpfe stehen unter
+ * *Einstellungen → Bild* — dieselbe Datei, aber ein zweiter Ausgang
+ * (`bildFormularHtml` / `bildFormularMontieren`), den os.js einhängt.
*/
-const LEER = "Kein Bild — hochladen oder Adresse einfügen.";
+const LEER = "Kein Bild — unter Einstellungen → Bild hochladen oder verlinken.";
+const FORM_ID = "bildForm";
let art = ""; // "datei" | "link" | ""
let quelle = "";
-let stand = ""; // Meldung unter dem Bild
+let stand = ""; // Meldung im Einstellungsfeld
let linkAuf = false;
+let kaputt = false; // die Adresse liefert kein Bild
widgetAnmelden({
id: "bild",
titel: "bild",
- // Hält ein Dateifeld und ein Eingabefeld: Der Rahmen darf den Inhalt nicht
- // unter den Fingern austauschen.
+ // Das Bild soll beim Neuzeichnen nicht neu geladen werden — ein Bild, das
+ // alle zwei Minuten kurz weiß aufblitzt, wäre schlechter als keines.
stabil: true,
async laden() {
const d = await api("/api/image/info");
art = (d && d.art) || "";
quelle = (d && d.url) || "";
+ kaputt = false;
},
inhalt() {
- const bild = quelle
- ? ``
- : `