Zwei Widgets: Zettel mit kleinem Markdown, Bild je Nutzer

Zettel: ein Textfeld, mehr nicht — InkNote gibt es schon. Gelesen wird
gesetzt (Ueberschriften, Haken, Aufzaehlungen, fett/kursiv/code, Strich),
geschrieben wird roh. Haken lassen sich ohne Bearbeiten setzen und
aendern die Quelle. Gespeichert wird 800 ms nach dem letzten Anschlag,
sofort beim Verlassen und beim Schliessen des Tabs (keepalive statt
sendBeacon: der kann nur POST und traegt keine Kopfzeilen). Grenze 4000
Zeichen, vom Server durchgesetzt.

Bild: genau eines je Nutzer, hochgeladen oder verlinkt. Die erste Datei,
die HoneyOS ablegt — in bilder/ neben honeyos.json, benannt nach einem
Fingerabdruck der Nutzerkennung. Ein neues Bild ersetzt das alte samt
Datei, die Adresse traegt einen Versionsanteil gegen den Zwischen-
speicher. SVG kommt nicht herein (Text, kann Skripte tragen), und der
Typ wird an den Bytes erkannt, nicht an der Angabe des Browsers.

Der Rahmen kann jetzt Widgets mit eigenem DOM: 'stabil' haelt den Inhalt,
'montiert' verdrahtet ihn. Ohne das haette das Anklopfen bei den Apps
alle zwei Minuten den Text unter den Fingern ausgetauscht.

Nebenbei: Der Aufrufhelfer setzte bei jedem Schreibzugriff
Content-Type: application/json — auch fuer Bildbytes.
This commit is contained in:
marc committed 2026-08-31 00:44:20 +02:00
1 parent b7cc06b7c1
commit c0abfbb491
9 files changed
+857 -20

No files matched your search

+55 -18
View File
@@ -114,9 +114,59 @@ function zustandHtml(w) {
/**
* Zeichnet alle Widgets in die Ansicht, die gerade gilt.
*
* Beide Container werden bei jedem Durchgang neu gefüllt statt einzeln
* nachgeführt: Bei einer Handvoll Widgets ist das billiger als Buchführung
* darüber, was sich geändert hat — und es kann nicht auseinanderlaufen.
* Jedes Widget bekommt seinen eigenen Knoten, der stehen bleibt, solange es
* gebraucht wird. Der erste Entwurf hat beide Behälter bei jedem Durchgang neu
* gefüllt — kürzer, aber falsch, sobald ein Widget etwas im DOM hält: Der
* Zettel wird alle zwei Minuten mitgezeichnet (die Apps werden dann angeklopft),
* und wer gerade tippte, verlor Text und Schreibmarke.
*
* Deshalb zwei Zusagen an die Widgets:
* · `stabil: true` — dein Inhalt wird nicht neu gesetzt, solange sich dein
* Zustand nicht ändert. Du führst ihn selbst nach.
* · `montiert(el)` — wird nach jedem Neusetzen gerufen, zum Verdrahten.
*/
function baue(behaelter, dran, alsFenster) {
const alt = new Map([...behaelter.children].map((el) => [el.dataset.widget, el]));
dran.forEach((w) => {
let el = alt.get(w.id);
alt.delete(w.id);
if (!el) {
el = document.createElement(alsFenster ? "section" : "div");
el.dataset.widget = w.id;
el.className = alsFenster ? "win win-widget" : "deskwidget";
if (alsFenster) {
el.innerHTML =
`<div class="win-bar"><span class="win-title">${w.titel}</span>` +
`<span class="win-dots">&#9679;&#9679;&#9679;</span></div><div class="win-body"></div>`;
}
behaelter.appendChild(el);
}
const koerper = alsFenster ? el.querySelector(".win-body") : el;
const frisch = el.dataset.zustand !== zustand[w.id];
if (frisch || !w.stabil) {
koerper.innerHTML = zustandHtml(w);
el.dataset.zustand = zustand[w.id];
if (w.montiert && zustand[w.id] === "fertig") w.montiert(koerper);
}
});
alt.forEach((el) => el.remove());
// Reihenfolge nur anfassen, wenn sie nicht stimmt: Einen Knoten zu verschieben
// heißt, ihn kurz aus dem Dokument zu nehmen — ein Textfeld verliert dabei
// die Schreibmarke.
const soll = dran.map((w) => w.id).join(",");
const ist = [...behaelter.children].map((el) => el.dataset.widget).join(",");
if (soll !== ist) {
dran.forEach((w) => behaelter.appendChild(behaelter.querySelector(`[data-widget="${w.id}"]`)));
}
}
/**
* Wählt aus, was gerade dran ist, und lässt es zeichnen.
*/
export function widgetsZeichnen() {
const desktop = state.settings.mode === "desktop";
@@ -133,19 +183,6 @@ export function widgetsZeichnen() {
return !(zustand[w.id] === "leer" && w.leer === "verbergen");
});
spalte.innerHTML = desktop
? ""
: dran
.map(
(w) => `
<section class="win win-widget" data-widget="${w.id}">
<div class="win-bar"><span class="win-title">${w.titel}</span><span class="win-dots">&#9679;&#9679;&#9679;</span></div>
<div class="win-body">${zustandHtml(w)}</div>
</section>`
)
.join("");
schilder.innerHTML = desktop
? dran.map((w) => `<div class="deskwidget" data-widget="${w.id}">${zustandHtml(w)}</div>`).join("")
: "";
baue(spalte, desktop ? [] : dran, true);
baue(schilder, desktop ? dran : [], false);
}