Der Notizblock -- und eine Pruefung, die zwoelf Kacheln nie angesehen hat
Filipe: „fuer die modis, rechte und linke hand und dogfather eine
kachel hinzufuegst, sie soll: Notizen, heissen. ich will dass du das
auch wie ein notizblock erstellst. ich will dass es uebelst geil und
einzigartig ist."
=================================================================
TEIL 1: DIE FARBE -- UND WAS DABEI AUFFIEL
=================================================================
Fuer die neue Kachel braucht es einen Ton. Beim Suchen fiel auf, dass
tools/kachelton-entzerren.mjs zwoelf der 45 Farben fuer FREI hielt.
Sie sind es nicht. bereicheFuer() gibt fuer die fuenf Rollen des
Agenturhauses null zurueck -- das heisst „nimm die Liste aus dem
Browser", und die steht in workspace/assets/js/bereiche.js. Dort
stehen die Toene 1 bis 22 und 44: Dashboard, Zahlen, Team-Lage,
Scout-Pipeline. Kacheln, die jeden Tag jemand ansieht.
pruef-kachelfarben hatte denselben blinden Fleck. Sie meldete seit
Wochen „33 benutzte Toene" und war gruen. Was sie dadurch NICHT sah:
* Der Farblauf von gestern Nacht hat zwoelf dieser Kacheln
verschoben und drei davon unter die Buntheitsgrenze gedrueckt.
Gruen geblieben.
* Ton 7, 16 und 22 lagen SEIT JEHER unter der Grenze (0,112 / 0,116
/ 0,068). Nie gemeldet.
Das ist die Sorte gruener Haken, vor der die Hausregel warnt: Er sagt
nur, dass die Bedingung erfuellt war -- nicht, dass sie das Richtige
angesehen hat.
BEHOBEN, und zwar an der Wurzel: Die REGELN (Kontrast, Buntheit)
gelten jetzt fuer jeden Ton, der irgendwo an einer Kachel steht --
gelesen aus denselben fuenf Dateien, die auch pruef-kachel-universum
liest. Dazu die Namen der Kacheln, damit „Ton 1 traegt Dashboard"
ueberhaupt pruefbar ist.
DIE PALETTE WURDE NEU GERECHNET, mit dem dritten Verfahren an einem
Tag -- die ersten zwei stehen als Fehler im Kopf des Werkzeugs:
1. Alle 45 neu verteilt. Lief ueber zwei ausdrueckliche Wuensche
hinweg (#ff1a1a, #a8d8ff).
2. Nur die Kollisionen, aber immer nur EINEN der beiden bewegt.
Ergebnis: Ton 1 sprang vom Tuerkis ins Altrosa, 0,197 weit.
3. BEIDE duerfen sich bewegen, und zwar beide nur ein bisschen.
Zwei Toene, die 0,02 auseinanderstehen und 0,09 brauchen, teilen
sich das -- jeder rueckt 0,045, und beide bleiben, was sie waren.
kleinster Abstand 0,0154 -> 0,0905
zu blass 4 -> 0
sichtbar veraendert 7 Kacheln (ueber 0,05)
kaum zu sehen 28 (13 zwischen 0,02 und 0,05, 15 darunter)
EINE AUSNAHME MIT ZAHL, keine mit Achselzucken: Ton 1 (Dashboard)
bleibt acht Tausendstel unter der Buntheitsgrenze. Gemessen ueber ALLE
16,7 Millionen sRGB-Farben ist die naechste, die alle Regeln haelt,
#7a75c8 -- ein Blauviolett, 0,113 entfernt. Tuerkis erreicht in sRGB
schlicht keine hoehere Buntheit, und das schmale Band teilen sich
schon sieben Toene. Eine Kachel, die ihre Farbfamilie behaelt, ist die
bessere Antwort auf „richtig geil und speziell" als eine, die acht
Tausendstel bunter ist und niemand wiedererkennt. blassBis sagt jetzt
bei jeder Ausnahme, WIE WEIT sie reicht -- vorher hiess blassErlaubt
schlicht „hier wird weggesehen".
DIE NEUE FARBE IST GRUEN UND WOLLTE BERNSTEIN SEIN. Gesucht war die
Farbe von Papier. Gemessen gibt es sie nicht mehr: Der beste Bernstein
im ganzen Farbraum haelt 0,0799 Abstand -- unter der Hausgrenze. Und
Platz schaffen hilft nicht: Setzt man ihn fest, muss ein warmer Ton
das Band verlassen (Ton 45 waere 0,212 weit ins Magenta gewandert).
Die groesste wirklich freie Luecke liegt im Gruen, bei 0,0917. Ein
linierter Block in Gruen ist Papier, seit es Papier gibt.
=================================================================
TEIL 2: DER BLOCK
=================================================================
DREI ENTSCHEIDUNGEN, AUS DENEN DER REST FOLGT:
1. ES GIBT KEINEN SPEICHERN-KNOPF. Nirgends. Wer einen Block
aufschlaegt und lostippt, drueckt hinterher nicht auf „sichern";
er klappt ihn zu. Gesichert wird 800 ms nach dem letzten
Tastendruck. Geht das nicht, bleibt der Text im Browser liegen und
wird nachgereicht -- und der Fuss sagt ehrlich „noch nicht
gesichert", statt einen Verlust zu melden, den man gerade nicht
verhindern kann.
2. DAS BLATT IST EIN SCHREIBFELD MIT EINER DECKSCHICHT. Das Feld
traegt Text und Cursor und ist unsichtbar; darueber zeichnet eine
zweite Schicht denselben Text noch einmal -- mit Kaestchen,
Ueberschriften, Strichen und Links.
DARAUS FOLGT EINE EISERNE REGEL, und sie steht dreimal im Code:
KEINE Auszeichnung darf die BREITE eines Zeichens aendern. Kein
Fettdruck, keine andere Groesse, keine Sperrung. Erlaubt sind nur
Farbe, Flaeche, Rahmen und Durchstreichen. Ein einziges
font-weight: 700 verschoebe den Umbruch, und ab der zweiten Zeile
stuende der sichtbare Text neben dem Cursor.
pruef-notizen misst das am echten Umbruch: eine Probe mit allen
Auszeichnungen und einer Zeile, die umbrechen MUSS. Sind beide
Schichten verschieden hoch, sitzt der Umbruch woanders. Mit
Gegenprobe -- Fettdruck auf der Deckschicht bricht die Deckung um
genau eine Zeile (30 px), und die Zeile wird rot.
3. DIE TASTATUR FUEHRT DIE LISTE FORT, NICHT EINE LEISTE. Ein
Spiegelstrich und Enter macht den naechsten Punkt, ein leeres
Kaestchen und Enter das naechste Kaestchen -- und ein GESETZTER
Haken wird dabei nicht mitgenommen, die naechste Aufgabe ist ja
noch nicht erledigt. Zweimal Enter beendet die Liste. Ein Klick
aufs Kaestchen hakt ab. Es gibt keine Werkzeugleiste, weil man beim
Schreiben nie den Stift wechselt.
UND: NIEMAND SIEHT DIE NOTIZEN EINES ANDEREN. Auch DogFather nicht.
Es gibt in workspace-notizen.js keinen siehtAlles()-Zweig, keinen
Umschalter, keine fremde Liste -- jede Abfrage hat person_id = ? fest
eingebaut. Die Kachel steht deshalb in „Fuer dich" und nicht in
„Taeglich": Die Gruppe sagt die wichtigste Eigenschaft, bevor man sie
anfasst.
NUR AUF DER TEAM-ADRESSE. Auf der Agenturadresse ist die Seite ein
404 -- auch fuer DogFather. Das ist dieselbe Trennung wie bei Chat,
Kalender und Aufgaben, und sie steht an zwei Stellen: in
GEHOERT_ZU_ADRESSE (die Datei) und in der Schnittstelle (die Daten).
Die Seite ist nur HTML; die Daten sind die Sache.
Dazu: sechs Papierfarben, Anheften, Suche mit Hervorhebung im Text,
Papierkorb mit dreissig Tagen und einem Eintrag in der
Aufbewahrungsliste (ohne den waere die Frist ein Satz in einem
Kommentar -- genau das ist dem Support am 24.09. passiert).
=================================================================
EIN FUND BEIM BAUEN, DER ALLEN GEHOERT
=================================================================
DELETE /workspace/api/notizen/:id gab es schon -- in
workspace-aufgaben.js, fuer die Notizen AN einer Aufgabe. Weil jener
Router frueher eingehaengt ist, hat er jeden Loeschversuch des Blocks
abgefangen und mit 404 beantwortet.
Von aussen sah das aus wie ein Fehler im neuen Modul: Anlegen ging,
Aendern ging, Loeschen nicht. Man sucht dann im eigenen Code, und dort
ist nichts. Express meldet so etwas nicht -- es nimmt die erste Route,
die passt, und schweigt ueber die zweite.
Der Block liegt jetzt unter /workspace/api/notizblock. Und
pruef-notizen geht seitdem den Routenbaum von express durch und meldet
jedes Paar aus Methode und Pfad, das zweimal vergeben ist. Gemessen:
307 Wege, keine Dublette. Die Pruefung gilt fuers ganze Haus, auch
wenn sie in dieser Datei steht -- hier ist sie gefunden worden.
Nebenbei berichtigt: pruef-crew-adresse verlangte von jedem Eintrag
der Haustafel HTTP 200 mit Inhalt. Das stimmte, solange dort nur
oeffentliche Dateien standen. notizen.html ist die erste Seite hinter
der Anmeldung -- sie antwortet mit 302, und das ist richtig. Gefragt
wird jetzt, was die Tafel wirklich meint: GIBT es das hier.
GEMESSEN, alles nach den Aenderungen:
pruef-notizen 76 Punkte, 0 Fehler (neu)
pruef-kachelfarben 31 Punkte, 0 Fehler (vorher 26)
pruef-kachel-universum 13 Punkte, 0 Fehler
pruef-crew-adresse 157 Punkte, 0 Fehler
pruef-buehne 230 Punkte, 0 Fehler -- notizen.html neu in
der Liste, schlechtester Kontrast 6,73:1,
also 50 % ueber der Grenze
pruef-rechtetafel, -haus-seiten, -struktur, -css-klassen,
-jeder-hat-eine-seite, -workspace-seiten, -kachelraster,
-rueckmeldung alle 0 Fehler
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -376,6 +376,16 @@ export function istWorkspacePfad(pfad) {
|
|||||||
export const GEHOERT_ZU_ADRESSE = [
|
export const GEHOERT_ZU_ADRESSE = [
|
||||||
{ datei: "/workspace/crew-index.html", zuhause: istCrewAdresse },
|
{ datei: "/workspace/crew-index.html", zuhause: istCrewAdresse },
|
||||||
{ datei: "/workspace/assets/css/crew-haus.css", zuhause: istCrewAdresse },
|
{ datei: "/workspace/assets/css/crew-haus.css", zuhause: istCrewAdresse },
|
||||||
|
/* DER NOTIZBLOCK (26.09.2026). Er gehoert zum Haus von Team Dogi,
|
||||||
|
und zwar vollstaendig: Auf der Agenturadresse gibt es die Seite
|
||||||
|
nicht -- auch nicht fuer DogFather, der in beiden Haeusern
|
||||||
|
arbeitet. Das ist dieselbe Trennung wie bei Chat, Kalender und
|
||||||
|
Aufgaben; ohne diesen Eintrag waere die Seite dort einfach
|
||||||
|
abrufbar, weil express.static jede Datei ausliefert, die man
|
||||||
|
beim Namen nennt. Die Schnittstelle prueft es ein zweites Mal
|
||||||
|
(nurTeamDogi in workspace-notizen.js) -- die Seite ist nur HTML,
|
||||||
|
die Daten sind die Sache. */
|
||||||
|
{ datei: "/workspace/notizen.html", zuhause: istCrewAdresse },
|
||||||
];
|
];
|
||||||
|
|
||||||
/** Wird hier eine Wand verlangt, die zu einer ANDEREN Tuer gehoert? */
|
/** Wird hier eine Wand verlangt, die zu einer ANDEREN Tuer gehoert? */
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import { uebernahmeRouter } from "./workspace-uebernahme.js";
|
|||||||
import { befindenRouter } from "./workspace-befinden.js";
|
import { befindenRouter } from "./workspace-befinden.js";
|
||||||
import { hilfeRouter } from "./workspace-hilfe.js";
|
import { hilfeRouter } from "./workspace-hilfe.js";
|
||||||
import { supportRouter } from "./workspace-support.js";
|
import { supportRouter } from "./workspace-support.js";
|
||||||
|
import { notizenRouter } from "./workspace-notizen.js";
|
||||||
import { unterstuetzungRouter } from "./workspace-unterstuetzung.js";
|
import { unterstuetzungRouter } from "./workspace-unterstuetzung.js";
|
||||||
import { reportRouter } from "./workspace-reports.js";
|
import { reportRouter } from "./workspace-reports.js";
|
||||||
import { scoutRouter } from "./workspace-scouts.js";
|
import { scoutRouter } from "./workspace-scouts.js";
|
||||||
@@ -312,6 +313,10 @@ app.use(uebernahmeRouter);
|
|||||||
app.use(befindenRouter);
|
app.use(befindenRouter);
|
||||||
app.use(hilfeRouter);
|
app.use(hilfeRouter);
|
||||||
app.use(supportRouter);
|
app.use(supportRouter);
|
||||||
|
/* Der Notizblock (26.09.2026): vier Rollen des Teamhauses, und jeder
|
||||||
|
sieht nur seinen eigenen -- auch DogFather. Begruendung im Kopf von
|
||||||
|
notiz-tabellen.js. */
|
||||||
|
app.use(notizenRouter);
|
||||||
/* Unterstützen (24.09.2026): die Wege, auf denen die Community etwas
|
/* Unterstützen (24.09.2026): die Wege, auf denen die Community etwas
|
||||||
beitragen kann – lesen alle, ändern nur DogFather. */
|
beitragen kann – lesen alle, ändern nur DogFather. */
|
||||||
app.use(unterstuetzungRouter);
|
app.use(unterstuetzungRouter);
|
||||||
@@ -569,3 +574,17 @@ process.on("uncaughtException", (fehler) => console.error("[uncaughtException]",
|
|||||||
app.listen(PORT, "127.0.0.1", () => {
|
app.listen(PORT, "127.0.0.1", () => {
|
||||||
console.log(`DogFather Universe – Server läuft auf http://127.0.0.1:${PORT}`);
|
console.log(`DogFather Universe – Server läuft auf http://127.0.0.1:${PORT}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
DIE ANWENDUNG NACH AUSSEN (26.09.2026)
|
||||||
|
|
||||||
|
Nur fuer Pruefungen. `pruef-notizen` geht damit den Routenbaum von
|
||||||
|
express durch und sucht Wege, die zweimal vergeben sind -- genau so
|
||||||
|
ein Fall hat beim Bau des Notizblocks eine Stunde gekostet
|
||||||
|
(DELETE /workspace/api/notizen/:id gab es schon in
|
||||||
|
workspace-aufgaben.js, und express nimmt schweigend die erste).
|
||||||
|
|
||||||
|
Der Export aendert am Betrieb nichts: Wer diese Datei importiert,
|
||||||
|
startet den Server ohnehin -- das tut sie beim Laden. Er macht nur
|
||||||
|
sichtbar, was vorher nur im Speicher stand. */
|
||||||
|
export default app;
|
||||||
|
|||||||
@@ -0,0 +1,241 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
WIE SIEHT DER NOTIZBLOCK AUS? (26.09.2026)
|
||||||
|
|
||||||
|
Ein Messwerkzeug, keine Pruefung: Es meldet nichts, es macht
|
||||||
|
Bilder. Rechner und Handy, leeres Blatt und beschriebenes.
|
||||||
|
|
||||||
|
node server/mess-notizblock.mjs
|
||||||
|
|
||||||
|
Filipe: „für die modis, rechte und linke hand und dogfather eine
|
||||||
|
kachel hinzufügst, sie soll: Notizen, heißen. ich will dass du das
|
||||||
|
auch wie ein notizblock erstellst."
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
DREI DINGE WERDEN GEPRÜFT, UND ZWEI DAVON HAT NIEMAND BESTELLT
|
||||||
|
|
||||||
|
1. WER DARF ÜBERHAUPT. Genau die vier Rollen, und niemand sonst —
|
||||||
|
auch nicht über die Schnittstelle, auch nicht von der anderen
|
||||||
|
Adresse aus. Die Rollenmenge wird dabei nicht abgeschrieben,
|
||||||
|
sondern aus dem Programm gelesen und gegen die Rechtetafel
|
||||||
|
gehalten: Zwei Listen für dieselbe Frage laufen auseinander.
|
||||||
|
|
||||||
|
2. NIEMAND SIEHT DIE NOTIZEN EINES ANDEREN. Auch DogFather nicht.
|
||||||
|
Das ist die Zusage, die auf der Kachel steht („nur du siehst
|
||||||
|
ihn"), und die einzige, deren Bruch man nicht bemerken würde:
|
||||||
|
Eine Notiz, die jemand Fremdes liest, sieht für den Schreiber
|
||||||
|
genauso aus wie eine, die niemand liest.
|
||||||
|
|
||||||
|
3. DIE DECKSCHICHT LIEGT AUF DEN PIXEL GENAU. Das Blatt besteht
|
||||||
|
aus einem unsichtbaren Schreibfeld und einer sichtbaren
|
||||||
|
Deckschicht darüber. Ändert eine Auszeichnung die BREITE eines
|
||||||
|
Zeichens (Fettdruck, andere Schriftgröße, Sperrung), brechen
|
||||||
|
beide Schichten an verschiedenen Stellen um — und der Cursor
|
||||||
|
steht neben dem Text. Das sieht aus wie ein Browserfehler und
|
||||||
|
wäre einer in notizen.css. Gemessen wird deshalb am echten
|
||||||
|
Umbruch, nicht an der Absicht.
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
WARUM DIESE PRÜFUNG ÜBER https SPRICHT
|
||||||
|
|
||||||
|
Derselbe Grund wie bei pruef-handy-teamdogi: `dogfather-universe.com`
|
||||||
|
steht in der fest eingebauten HSTS-Liste von Chromium, und ohne die
|
||||||
|
Team-Adresse gibt es diese Seite gar nicht — auf einer Prüfadresse
|
||||||
|
(127.0.0.1) ist `haus` weder „crew" noch „agentur", und der Block
|
||||||
|
antwortet zu Recht mit 404.
|
||||||
|
|
||||||
|
Aufrufen mit: node server/pruef-notizen.mjs
|
||||||
|
===================================================================== */
|
||||||
|
import { mkdtempSync, rmSync, readFileSync } from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { request } from "node:http";
|
||||||
|
|
||||||
|
const ordner = mkdtempSync(join(tmpdir(), "ws-notiz-"));
|
||||||
|
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
||||||
|
|
||||||
|
import { notbremse } from "./helfer-notbremse.mjs";
|
||||||
|
|
||||||
|
/* Eine feste Nummer -- `eigenerPort` leitet seine aus der Stelle im
|
||||||
|
Alphabet der PRUEFdateien ab, und dieses Werkzeug ist keine.
|
||||||
|
Geprueft wird trotzdem, ob sie frei ist: Sonst misst man an einem
|
||||||
|
fremden Server und bekommt ein Bild von einem anderen Stand. */
|
||||||
|
const PORT = 5461;
|
||||||
|
{
|
||||||
|
const netz = await import("node:net");
|
||||||
|
const frei = await new Promise((fertig) => {
|
||||||
|
const s = netz.createServer();
|
||||||
|
s.once("error", () => fertig(false));
|
||||||
|
s.once("listening", () => s.close(() => fertig(true)));
|
||||||
|
s.listen(PORT, "127.0.0.1");
|
||||||
|
});
|
||||||
|
if (!frei) { console.error(`Port ${PORT} ist belegt. Abgebrochen.`); process.exit(3); }
|
||||||
|
}
|
||||||
|
|
||||||
|
process.env.PORT = `${PORT}`;
|
||||||
|
process.env.SITE_ACCESS_SECRET = "lokaler-test";
|
||||||
|
process.env.SITE_PUBLIC_LAUNCH_AT = "2020-01-01T00:00:00+01:00";
|
||||||
|
|
||||||
|
const express = (await import("express")).default;
|
||||||
|
const ec = express.response.cookie;
|
||||||
|
express.response.cookie = function (n, w, o) { return ec.call(this, n, w, { ...(o || {}), secure: false }); };
|
||||||
|
await import("./index.js");
|
||||||
|
notbremse(200_000, "mess-notizblock");
|
||||||
|
await new Promise((r) => setTimeout(r, 900));
|
||||||
|
|
||||||
|
let fehler = 0, geprueft = 0;
|
||||||
|
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
||||||
|
const melde = (t) => console.log(t);
|
||||||
|
process.on("exit", () => { try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ } });
|
||||||
|
|
||||||
|
const CREW = "crew.dogfather-universe.com";
|
||||||
|
const AGENTUR = "workspace.dogfather-universe.com";
|
||||||
|
|
||||||
|
/* ---------- Zugänge anlegen -------------------------------------------- */
|
||||||
|
const { DatabaseSync } = await import("node:sqlite");
|
||||||
|
const { scryptSync, randomBytes, createHmac } = await import("node:crypto");
|
||||||
|
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
||||||
|
const nun = new Date().toISOString();
|
||||||
|
const kennSchluessel = randomBytes(32).toString("hex");
|
||||||
|
d.prepare("INSERT INTO einstellungen (schluessel, wert, geaendert, von)"
|
||||||
|
+ " VALUES (?,?,?,NULL) ON CONFLICT(schluessel) DO NOTHING")
|
||||||
|
.run("code_kennung_schluessel", kennSchluessel, nun);
|
||||||
|
function anlegen(name, rolle, code) {
|
||||||
|
const salt = randomBytes(16).toString("hex");
|
||||||
|
const hash = scryptSync(code, salt, 64,
|
||||||
|
{ N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
|
||||||
|
d.prepare("INSERT INTO personen (name, rolle, code_hash, code_salt, code_n,"
|
||||||
|
+ " code_kennung, aktiv, erstellt) VALUES (?,?,?,?,?,?,1,?)")
|
||||||
|
.run(name, rolle, hash, salt, 32768,
|
||||||
|
createHmac("sha256", kennSchluessel).update(code).digest("hex"), nun);
|
||||||
|
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
|
||||||
|
}
|
||||||
|
const ROLLEN = [["admin", "Filipe"], ["hand", "VanVan"], ["linke", "Funny"],
|
||||||
|
["modi", "Miss"], ["gast", "Zuschauerin"], ["creator", "Luna"]];
|
||||||
|
const CODES = {};
|
||||||
|
const IDS = {};
|
||||||
|
for (const [i, [rolle, name]] of ROLLEN.entries()) {
|
||||||
|
CODES[rolle] = `CODE-N${String(i).padStart(2, "0")}-000001`;
|
||||||
|
IDS[rolle] = anlegen(name, rolle, CODES[rolle]);
|
||||||
|
}
|
||||||
|
d.close();
|
||||||
|
|
||||||
|
/* ---------- Sprechen mit Hausadresse ------------------------------------
|
||||||
|
`fetch` kann die Host-Kopfzeile nicht setzen (verbotene Kopfzeile),
|
||||||
|
und an ihr entscheidet das Haus alles. Deshalb node:http. */
|
||||||
|
function roh(host, pfad, { method = "GET", keks = "", rumpf = null, origin } = {}) {
|
||||||
|
return new Promise((fertig, schief) => {
|
||||||
|
const daten = rumpf === null ? null : Buffer.from(JSON.stringify(rumpf));
|
||||||
|
const a = request({
|
||||||
|
host: "127.0.0.1", port: PORT, path: pfad, method,
|
||||||
|
headers: {
|
||||||
|
Host: host,
|
||||||
|
Origin: origin === undefined ? `https://${host}` : origin,
|
||||||
|
...(keks ? { Cookie: keks } : {}),
|
||||||
|
...(daten ? { "Content-Type": "application/json", "Content-Length": daten.length } : {}),
|
||||||
|
},
|
||||||
|
}, (antwort) => {
|
||||||
|
const teile = [];
|
||||||
|
antwort.on("data", (s) => teile.push(s));
|
||||||
|
antwort.on("end", () => {
|
||||||
|
const text = Buffer.concat(teile).toString("utf8");
|
||||||
|
let j = null; try { j = JSON.parse(text); } catch { /* kein JSON */ }
|
||||||
|
fertig({ code: antwort.statusCode, text, j, kopf: antwort.headers });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
a.on("error", schief);
|
||||||
|
if (daten) a.write(daten);
|
||||||
|
a.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function anmelden(rolle, host) {
|
||||||
|
const a = await roh(host, "/workspace/api/anmelden", {
|
||||||
|
method: "POST", rumpf: { rolle, code: CODES[rolle] },
|
||||||
|
});
|
||||||
|
const keks = []
|
||||||
|
.concat(a.kopf["set-cookie"] || [])
|
||||||
|
.map((z) => z.split(";")[0]).join("; ");
|
||||||
|
return a.code === 200 && keks ? keks : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const kekse = { modi: await anmelden("modi", CREW) };
|
||||||
|
if (!kekse.modi) { console.error("Anmeldung ging nicht"); process.exit(1); }
|
||||||
|
|
||||||
|
const PROBE = [
|
||||||
|
"# Sendung am Freitag",
|
||||||
|
"",
|
||||||
|
"[x] Gaesteliste an VanVan",
|
||||||
|
"[ ] Technik durchgehen -- Ton am Mischpult",
|
||||||
|
"[ ] Ansage schreiben",
|
||||||
|
"",
|
||||||
|
"- Idee: Zuschauerfragen in der letzten halben Stunde",
|
||||||
|
"- Idee: Gewinnspiel, aber ohne Punkte",
|
||||||
|
"",
|
||||||
|
"> Nicht vergessen: Casper braucht um 19 Uhr seine Runde.",
|
||||||
|
"",
|
||||||
|
"Unterlagen: https://crew.dogfather-universe.com/workspace/dateien.html",
|
||||||
|
].join(String.fromCharCode(10));
|
||||||
|
|
||||||
|
const pw = await import(
|
||||||
|
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
||||||
|
const { execFileSync } = await import("node:child_process");
|
||||||
|
const { createServer: httpsServer } = await import("node:https");
|
||||||
|
const { request: httpAnfrage } = await import("node:http");
|
||||||
|
const schluesselDatei = join(ordner, "m.key");
|
||||||
|
const zertDatei = join(ordner, "m.crt");
|
||||||
|
execFileSync("openssl", ["req", "-x509", "-newkey", "rsa:2048", "-nodes",
|
||||||
|
"-keyout", schluesselDatei, "-out", zertDatei, "-days", "2",
|
||||||
|
"-subj", `/CN=${CREW}`, "-addext", `subjectAltName=DNS:${CREW}`], { stdio: "ignore" });
|
||||||
|
const HTTPS_PORT = PORT + 1;
|
||||||
|
const vorbau = httpsServer({ key: readFileSync(schluesselDatei), cert: readFileSync(zertDatei) },
|
||||||
|
(von, nach) => {
|
||||||
|
const weiter = httpAnfrage({ host: "127.0.0.1", port: PORT, path: von.url,
|
||||||
|
method: von.method, headers: { ...von.headers, host: `${CREW}:${HTTPS_PORT}` } },
|
||||||
|
(antwort) => { nach.writeHead(antwort.statusCode || 502, antwort.headers); antwort.pipe(nach); });
|
||||||
|
weiter.on("error", () => { try { nach.writeHead(502); nach.end(); } catch { /* egal */ } });
|
||||||
|
von.pipe(weiter);
|
||||||
|
});
|
||||||
|
await new Promise((r) => vorbau.listen(HTTPS_PORT, "127.0.0.1", r));
|
||||||
|
const BASIS = `https://${CREW}:${HTTPS_PORT}`;
|
||||||
|
|
||||||
|
/* Drei Blaetter, damit der Stapel etwas zeigt. */
|
||||||
|
const BR = String.fromCharCode(10);
|
||||||
|
await roh(CREW, "/workspace/api/notizblock", { method: "POST", keks: kekse.modi,
|
||||||
|
rumpf: { text: ["Einkauf fuers Studio", "[ ] Kabel", "[ ] Kaffee"].join(BR) } });
|
||||||
|
await roh(CREW, "/workspace/api/notizblock", { method: "POST", keks: kekse.modi,
|
||||||
|
rumpf: { text: ["Woerter, die ich nicht mehr sagen will", "- eigentlich", "- quasi"].join(BR),
|
||||||
|
farbe: "rosa" } });
|
||||||
|
const haupt = await roh(CREW, "/workspace/api/notizblock", { method: "POST", keks: kekse.modi,
|
||||||
|
rumpf: { text: PROBE, farbe: "sonne" } });
|
||||||
|
await roh(CREW, `/workspace/api/notizblock/${haupt.j.notiz.id}`, { method: "PATCH",
|
||||||
|
keks: kekse.modi, rumpf: { angeheftet: true } });
|
||||||
|
|
||||||
|
const browser = await pw.chromium.launch({
|
||||||
|
args: [`--host-resolver-rules=MAP ${CREW} 127.0.0.1`, "--ignore-certificate-errors"] });
|
||||||
|
const kontext = await browser.newContext({ viewport: { width: 1440, height: 980 },
|
||||||
|
ignoreHTTPSErrors: true, deviceScaleFactor: 2 });
|
||||||
|
await kontext.addCookies(kekse.modi.split("; ").filter(Boolean).map((k) => {
|
||||||
|
const [n, ...r] = k.split("=");
|
||||||
|
return { name: n, value: r.join("="), domain: CREW, path: "/" };
|
||||||
|
}));
|
||||||
|
|
||||||
|
const seite = await kontext.newPage();
|
||||||
|
await seite.goto(`${BASIS}/workspace/notizen.html`, { waitUntil: "networkidle" });
|
||||||
|
await seite.waitForTimeout(1200);
|
||||||
|
await seite.screenshot({ path: "server/mess-notizblock-rechner.png" });
|
||||||
|
console.log("server/mess-notizblock-rechner.png");
|
||||||
|
|
||||||
|
const handy = await kontext.newPage();
|
||||||
|
await handy.setViewportSize({ width: 390, height: 844 });
|
||||||
|
await handy.goto(`${BASIS}/workspace/notizen.html`, { waitUntil: "networkidle" });
|
||||||
|
await handy.waitForTimeout(900);
|
||||||
|
await handy.screenshot({ path: "server/mess-notizblock-handy-liste.png" });
|
||||||
|
await handy.locator(".n-zettel").first().click();
|
||||||
|
await handy.waitForTimeout(600);
|
||||||
|
await handy.screenshot({ path: "server/mess-notizblock-handy-blatt.png" });
|
||||||
|
console.log("server/mess-notizblock-handy-liste.png");
|
||||||
|
console.log("server/mess-notizblock-handy-blatt.png");
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
await new Promise((r) => vorbau.close(r));
|
||||||
|
process.exit(0);
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
DER NOTIZBLOCK — die Tabelle und die Regeln dahinter (26.09.2026)
|
||||||
|
=====================================================================
|
||||||
|
|
||||||
|
Filipe: „für die modis, rechte und linke hand und dogfather eine
|
||||||
|
kachel hinzufügst, sie soll: Notizen, heißen. ich will dass du das
|
||||||
|
auch wie ein notizblock erstellst."
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
DIE EINE ENTSCHEIDUNG, DIE ALLES ANDERE NACH SICH ZIEHT
|
||||||
|
|
||||||
|
EIN NOTIZBLOCK GEHÖRT EINEM MENSCHEN. Nicht dem Team, nicht dem
|
||||||
|
Haus, nicht der Leitung. Wer seine Gedanken aufschreibt, schreibt
|
||||||
|
anders, wenn jemand mitlesen könnte — und dann ist es kein Block
|
||||||
|
mehr, sondern ein Bericht.
|
||||||
|
|
||||||
|
Deshalb gibt es hier KEINE Rolle, die mehr sieht. `siehtAlles()`
|
||||||
|
wird in diesem Modul nicht gefragt, und das ist Absicht: DogFather
|
||||||
|
sieht die Notizen eines Modis so wenig wie umgekehrt. Jede Abfrage
|
||||||
|
hat `person_id = ?` fest eingebaut, nicht als Bedingung, die man
|
||||||
|
weglassen könnte.
|
||||||
|
|
||||||
|
Das ist dieselbe Linie wie bei `befinden.html` („Ein paar Fragen
|
||||||
|
über dich, nur für dich. Niemand aus dem Team sieht deine
|
||||||
|
Antworten") — dort steht sie schon im Haus, hier wird sie
|
||||||
|
fortgesetzt.
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
WARUM ON DELETE CASCADE UND NICHT SET NULL
|
||||||
|
|
||||||
|
Bei Aufgaben ist es richtig, den Namen zu entfernen und die Sache
|
||||||
|
stehen zu lassen: Sie gehört dem Projekt. Eine Notiz gehört
|
||||||
|
niemandem sonst. Wird ein Zugang gelöscht, hat die Zeile keinen
|
||||||
|
Leser mehr — sie wäre ein Text ohne Menschen, den irgendwann jemand
|
||||||
|
in der Datenbank findet. Also geht sie mit.
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
DER PAPIERKORB IST EIN DATUM, KEINE ZWEITE TABELLE
|
||||||
|
|
||||||
|
`weg_am` gesetzt heißt „liegt im Papierkorb, seit …". Eine zweite
|
||||||
|
Tabelle wäre dieselbe Struktur noch einmal, und beim nächsten neuen
|
||||||
|
Feld hätte eine davon es nicht. Geräumt wird über die
|
||||||
|
Aufbewahrungsliste (workspace-aufbewahrung.js) — dort steht die
|
||||||
|
Frist, dort wird sie eingelöst. Ein Versprechen ohne Eintrag dort
|
||||||
|
ist kein Versprechen; genau das ist dem Support am 24.09. passiert.
|
||||||
|
===================================================================== */
|
||||||
|
|
||||||
|
/** Wie lange etwas im Papierkorb liegt, bevor es wirklich weg ist.
|
||||||
|
*
|
||||||
|
* Dreißig Tage. Kürzer wäre eine Falle für den, der drei Wochen im
|
||||||
|
* Urlaub war; länger würde aus dem Papierkorb ein zweites Archiv,
|
||||||
|
* und dann räumt ihn niemand mehr aus. */
|
||||||
|
export const PAPIERKORB_TAGE = 30;
|
||||||
|
|
||||||
|
/** Wie lang eine einzelne Notiz werden darf.
|
||||||
|
*
|
||||||
|
* Zwanzigtausend Zeichen sind rund acht eng beschriebene Seiten.
|
||||||
|
* Die Zahl steht nicht gegen den Benutzer, sondern gegen ein
|
||||||
|
* hängendes Skript: Ohne Grenze schreibt ein Fehler in einer Schleife
|
||||||
|
* die Platte voll, und das merkt man erst, wenn nichts mehr geht. */
|
||||||
|
export const TEXT_MAX = 20000;
|
||||||
|
|
||||||
|
/** Wie viele Blätter ein Block hat.
|
||||||
|
*
|
||||||
|
* Dreihundert. Wer so viele hat, sucht ohnehin über die Suche und
|
||||||
|
* nicht über die Liste — und auch diese Zahl steht gegen ein Skript,
|
||||||
|
* nicht gegen einen Menschen. */
|
||||||
|
export const BLAETTER_MAX = 300;
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
DIE PAPIERFARBEN
|
||||||
|
|
||||||
|
Sechs, und keine davon ist ein Papierweiß. Auf einer dunklen Wand
|
||||||
|
wäre ein cremefarbenes Blatt eine Lampe — die Hausregel heißt
|
||||||
|
augenschonend, und sie gilt auch für ein Blatt Papier.
|
||||||
|
|
||||||
|
Die Farbe ist deshalb die TINTE: Rand, Linien und Kante des Blattes
|
||||||
|
tragen sie, die Fläche bleibt dunkel. Das ist näher an einer
|
||||||
|
schwarzen Kladde mit farbigem Lesebändchen als an einem Klebezettel
|
||||||
|
— und man kann sechs Blätter nebeneinander auseinanderhalten, ohne
|
||||||
|
dass eines blendet.
|
||||||
|
|
||||||
|
ALLE SECHS HALTEN 4,5:1 GEGEN DEN DUNKLEN GRUND (#0a121e) —
|
||||||
|
gemessen, nicht geschätzt, und nachgeprüft in pruef-notizen. Sie
|
||||||
|
tragen ja auch Text.
|
||||||
|
|
||||||
|
WARUM NICHT DIE 46 KACHELFARBEN: Die sind gegeneinander
|
||||||
|
auseinandergerechnet, damit man 46 Kacheln unterscheidet. Hier
|
||||||
|
müssen sechs auseinandergehen, und dafür sind größere, ruhigere
|
||||||
|
Abstände besser als sechs Nachbarn aus einer vollen Palette. */
|
||||||
|
export const FARBEN = [
|
||||||
|
{ name: "block", wort: "Block", hex: "#9cde90" },
|
||||||
|
{ name: "sonne", wort: "Sonne", hex: "#e8bd63" },
|
||||||
|
{ name: "rosa", wort: "Rosa", hex: "#f2a0c0" },
|
||||||
|
{ name: "himmel", wort: "Himmel", hex: "#86c5f5" },
|
||||||
|
{ name: "flieder", wort: "Flieder", hex: "#c3a6f2" },
|
||||||
|
{ name: "minze", wort: "Minze", hex: "#7ee0c4" },
|
||||||
|
];
|
||||||
|
export const FARB_NAMEN = FARBEN.map((f) => f.name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legt die Tabelle an. Wird aus workspace.js gerufen, nachdem
|
||||||
|
* `personen` steht — die Notiz hängt daran.
|
||||||
|
*
|
||||||
|
* ACHTUNG, EIN GEGEN-APOSTROPH IN DIESEM TEXT BEENDET DAS
|
||||||
|
* TEMPLATE-LITERAL. Das hat in diesem Haus schon zweimal den Start
|
||||||
|
* gekostet (support-tabellen.js, workspace.js). Deshalb steht in den
|
||||||
|
* Kommentaren hier drin kein einziger.
|
||||||
|
*/
|
||||||
|
export function notizTabellen(d) {
|
||||||
|
d.exec(`
|
||||||
|
CREATE TABLE IF NOT EXISTS notizen (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
/* CASCADE: Eine Notiz ohne ihren Menschen ist ein Text, den
|
||||||
|
niemand lesen darf und niemand vermisst. */
|
||||||
|
person_id INTEGER NOT NULL REFERENCES personen(id) ON DELETE CASCADE,
|
||||||
|
/* DER GANZE TEXT IN EINEM FELD -- Titel, Haken, Absaetze.
|
||||||
|
|
||||||
|
Eine eigene Titelspalte waere die naheliegende Loesung und
|
||||||
|
die falsche: Dann muesste die Oberflaeche zwei Eingabefelder
|
||||||
|
haben, und ein Notizblock mit Formular ist kein Notizblock
|
||||||
|
mehr. Die erste Zeile IST der Titel, und zwar dort, wo sie
|
||||||
|
hingehoert -- im Text. */
|
||||||
|
text TEXT NOT NULL DEFAULT '',
|
||||||
|
farbe TEXT NOT NULL DEFAULT 'block',
|
||||||
|
/* Angeheftet: bleibt oben. Als Zahl und nicht als Datum -- hier
|
||||||
|
geht es um ein Ja oder Nein, und ein Datum waere ein Feld,
|
||||||
|
das etwas anderes verspricht, als es haelt. */
|
||||||
|
angeheftet INTEGER NOT NULL DEFAULT 0,
|
||||||
|
erstellt TEXT NOT NULL,
|
||||||
|
geaendert TEXT NOT NULL,
|
||||||
|
/* Im Papierkorb seit. NULL heisst: auf dem Block. */
|
||||||
|
weg_am TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Die eine Frage, die dieses Modul stellt: „meine Blaetter, die
|
||||||
|
angehefteten zuerst, danach das zuletzt Angefasste". Der Index
|
||||||
|
beantwortet sie in genau dieser Reihenfolge. */
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_notizen_person
|
||||||
|
ON notizen (person_id, weg_am, angeheftet DESC, geaendert DESC);
|
||||||
|
/* Und die Frage des Aufraeumers: „was liegt lange genug im
|
||||||
|
Papierkorb?" */
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_notizen_weg
|
||||||
|
ON notizen (weg_am);
|
||||||
|
`);
|
||||||
|
|
||||||
|
/* DIE FARBPRUEFUNG STEHT NICHT IN DER TABELLE.
|
||||||
|
|
||||||
|
Ein CHECK (farbe IN (...)) waere die naheliegende Stelle -- und
|
||||||
|
die, an der die Liste ein zweites Mal steht. Waechst FARBEN oben
|
||||||
|
um eine Farbe, muesste die Tabelle umgebaut werden, damit ein
|
||||||
|
Wert erlaubt wird, der im Programm laengst gilt. Genau diese
|
||||||
|
Sorte Abschrift hat am 11.09. drei Spalten gekostet.
|
||||||
|
|
||||||
|
Geprueft wird deshalb an der Route, gegen FARB_NAMEN -- eine
|
||||||
|
Liste, die niemand pflegen muss, weil sie abgeleitet ist. */
|
||||||
|
}
|
||||||
@@ -73,6 +73,10 @@ const ALLE_SEITEN = [
|
|||||||
/* Neu am 21.09.2026 -- sie traegt eine Buehne und viel Text. */
|
/* Neu am 21.09.2026 -- sie traegt eine Buehne und viel Text. */
|
||||||
"willkommen.html",
|
"willkommen.html",
|
||||||
"unsere-seiten.html",
|
"unsere-seiten.html",
|
||||||
|
/* Neu am 26.09.2026: der Notizblock. Er traegt eine Buehne, und
|
||||||
|
auf ihm steht die leiseste Schrift des Hauses direkt ueber
|
||||||
|
einem Foto -- genau der Fall, fuer den diese Datei gebaut ist. */
|
||||||
|
"notizen.html",
|
||||||
];
|
];
|
||||||
const SEITEN = process.env.BUEHNE_SEITEN
|
const SEITEN = process.env.BUEHNE_SEITEN
|
||||||
? process.env.BUEHNE_SEITEN.split(",").map((s) => s.trim()).filter(Boolean)
|
? process.env.BUEHNE_SEITEN.split(",").map((s) => s.trim()).filter(Boolean)
|
||||||
|
|||||||
@@ -645,11 +645,36 @@ for (const eintrag of GEHOERT_ZU_ADRESSE) {
|
|||||||
ok((draussen.text || "").length < 200,
|
ok((draussen.text || "").length < 200,
|
||||||
`workspace.: und es kommt auch kein Inhalt mit (${(draussen.text || "").length} Zeichen)`);
|
`workspace.: und es kommt auch kein Inhalt mit (${(draussen.text || "").length} Zeichen)`);
|
||||||
|
|
||||||
|
/* ==== DRINNEN HEISST NICHT IMMER 200 (26.09.2026) ==============
|
||||||
|
|
||||||
|
Hier stand „muss 200 sein, und mehr als 200 Zeichen lang".
|
||||||
|
Das stimmte, solange in der Tafel nur OEFFENTLICHE Dateien
|
||||||
|
standen -- die Eingangswand und eine Stilvorlage.
|
||||||
|
|
||||||
|
Mit notizen.html steht dort zum ersten Mal eine Seite HINTER
|
||||||
|
der Anmeldung. Ohne Sitzung antwortet sie mit 302 auf die
|
||||||
|
Anmeldung, und das ist richtig so. Die Pruefung meldete
|
||||||
|
trotzdem einen Fehler -- nicht am Haus, sondern an ihrer
|
||||||
|
eigenen, zu engen Erwartung.
|
||||||
|
|
||||||
|
Die Frage dieser Tafel ist nicht „liefert es Inhalt aus?",
|
||||||
|
sondern „GIBT es das hier?". Genau das wird jetzt gemessen:
|
||||||
|
drinnen alles ausser 404/410, draussen nur 404/410. Beide
|
||||||
|
Richtungen bleiben, nur die Innenseite ist nicht mehr auf eine
|
||||||
|
einzige Zahl festgelegt. */
|
||||||
const drinnen = await hole(weg, { host: CREW });
|
const drinnen = await hole(weg, { host: CREW });
|
||||||
ok(drinnen.status === 200,
|
ok(drinnen.status !== 404 && drinnen.status !== 410,
|
||||||
`crew.: ${weg} kommt dort an (${drinnen.status})`);
|
`crew.: ${weg} gibt es dort (${drinnen.status})`);
|
||||||
|
/* Und eine offene Datei kommt dort auch wirklich an. Bei einer
|
||||||
|
Seite hinter der Anmeldung waere „mehr als 200 Zeichen" eine
|
||||||
|
Aussage ueber die Weiterleitung, nicht ueber die Seite. */
|
||||||
|
if (drinnen.status === 200) {
|
||||||
ok((drinnen.text || "").length > 200,
|
ok((drinnen.text || "").length > 200,
|
||||||
`crew.: und zwar mit Inhalt (${(drinnen.text || "").length} Zeichen)`);
|
`crew.: und zwar mit Inhalt (${(drinnen.text || "").length} Zeichen)`);
|
||||||
|
} else {
|
||||||
|
ok(drinnen.status === 302 || drinnen.status === 401 || drinnen.status === 403,
|
||||||
|
`crew.: und schickt ohne Anmeldung zur Anmeldung (${drinnen.status})`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* DIE UMBIEGUNG DARF DIE SPERRE NICHT MITNEHMEN.
|
/* DIE UMBIEGUNG DARF DIE SPERRE NICHT MITNEHMEN.
|
||||||
|
|||||||
@@ -133,6 +133,63 @@ for (const r of rollen) {
|
|||||||
|
|
||||||
/* DIE ZAHL GEHÖRT IN DIE BEDINGUNG. Fände diese Prüfung null benutzte
|
/* DIE ZAHL GEHÖRT IN DIE BEDINGUNG. Fände diese Prüfung null benutzte
|
||||||
Töne, wäre alles Folgende trivial grün — und bliebe es für immer. */
|
Töne, wäre alles Folgende trivial grün — und bliebe es für immer. */
|
||||||
|
/* =====================================================================
|
||||||
|
DIE ZWEITE QUELLE -- UND DER BLINDE FLECK, DEN SIE SCHLIESST
|
||||||
|
===================================================================== (26.09.2026)
|
||||||
|
|
||||||
|
`benutzt` oben entsteht aus bereicheFuer() je Rolle. Das ist die
|
||||||
|
richtige Quelle fuer die Frage „welche Kachel traegt welchen Ton
|
||||||
|
in welcher Ansicht" -- und es ist die falsche fuer die Frage
|
||||||
|
„welche Toene sind ueberhaupt in Gebrauch".
|
||||||
|
|
||||||
|
Denn fuer die fuenf Rollen des AGENTURHAUSES gibt bereicheFuer()
|
||||||
|
`null` zurueck. Das heisst „nimm die Liste aus dem Browser", und
|
||||||
|
die steht in workspace/assets/js/bereiche.js. Dort stehen die
|
||||||
|
Toene 1 bis 22 und 44 -- zweiundzwanzig Stueck, und keiner davon
|
||||||
|
ist hier je angekommen.
|
||||||
|
|
||||||
|
WAS DAS GEKOSTET HAT: Diese Pruefung meldete „33 benutzte Toene"
|
||||||
|
und war gruen. Am 25.09. hat ein Farblauf daraufhin zwoelf
|
||||||
|
vermeintlich freie Toene verschoben und drei davon unter die
|
||||||
|
Buntheitsgrenze gedrueckt -- darunter „Dashboard", „Zahlen" und
|
||||||
|
„Scout-Pipeline", Kacheln, die jeden Tag jemand ansieht. Die
|
||||||
|
Pruefung blieb gruen. Ausserdem lagen Ton 7, 16 und 22 SEIT JEHER
|
||||||
|
unter der Grenze, ohne dass es je gemeldet wurde.
|
||||||
|
|
||||||
|
Das ist die Sorte gruener Haken, vor der die Hausregel warnt: Er
|
||||||
|
sagt nur, dass die Bedingung erfuellt war -- nicht, dass sie das
|
||||||
|
Richtige angesehen hat.
|
||||||
|
|
||||||
|
Ab hier gelten die REGELN (Kontrast, Buntheit) fuer jeden Ton, der
|
||||||
|
irgendwo an einer Kachel steht. Gelesen werden dieselben fuenf
|
||||||
|
Quellen wie in pruef-kachel-universum. Die Fragen nach ANSICHTEN
|
||||||
|
(zwei gleiche nebeneinander, die Willkommenskachel) bleiben bei
|
||||||
|
`benutzt` -- dort geht es wirklich um das, was der Server
|
||||||
|
ausgibt. */
|
||||||
|
const QUELLEN = ["workspace.js", "../workspace/assets/js/bereiche.js",
|
||||||
|
"../workspace/assets/js/start.js", "../workspace/assets/js/kopf.js",
|
||||||
|
"../workspace/assets/js/befinden.js"];
|
||||||
|
const IN_GEBRAUCH = new Set();
|
||||||
|
/* Und WELCHE Kachel einen Ton traegt -- auch das aus den Quellen,
|
||||||
|
nicht nur aus dem, was der Server ausgibt. Gesucht wird der Name
|
||||||
|
VOR der Tonnummer im selben Objekt; `[^{}]*?` sorgt dafuer, dass
|
||||||
|
die Suche an der naechsten geschweiften Klammer endet und nicht
|
||||||
|
den Namen der vorigen Kachel einsammelt. */
|
||||||
|
const TRAEGT = new Map();
|
||||||
|
for (const q of QUELLEN) {
|
||||||
|
const text = readFileSync(join(HIER, q), "utf8");
|
||||||
|
for (const m of text.matchAll(/ton:\s*(\d+)/g)) IN_GEBRAUCH.add(Number(m[1]));
|
||||||
|
for (const m of text.matchAll(/name:\s*["']([^"']+)["'][^{}]*?ton:\s*(\d+)/g)) {
|
||||||
|
const n = Number(m[2]);
|
||||||
|
if (!TRAEGT.has(n)) TRAEGT.set(n, new Set());
|
||||||
|
TRAEGT.get(n).add(m[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pruef(`${IN_GEBRAUCH.size} Toene stehen an einer Kachel -- gelesen aus `
|
||||||
|
+ `${QUELLEN.length} Dateien, nicht nur aus dem, was der Server ausgibt`,
|
||||||
|
IN_GEBRAUCH.size >= benutzt.size,
|
||||||
|
`nur ${IN_GEBRAUCH.size} gefunden, der Server gibt schon ${benutzt.size} aus`);
|
||||||
|
|
||||||
pruef(`es gibt benutzte Kachelfarben (${benutzt.size} Toene, ${ansichten.length} Ansichten)`,
|
pruef(`es gibt benutzte Kachelfarben (${benutzt.size} Toene, ${ansichten.length} Ansichten)`,
|
||||||
benutzt.size >= 20 && ansichten.length >= 5,
|
benutzt.size >= 20 && ansichten.length >= 5,
|
||||||
`nur ${benutzt.size} Toene in ${ansichten.length} Ansichten gefunden`);
|
`nur ${benutzt.size} Toene in ${ansichten.length} Ansichten gefunden`);
|
||||||
@@ -243,7 +300,13 @@ console.log("\n=== Keine Ansicht zeigt zwei gleiche ===");
|
|||||||
/* ---- 3. Festgelegt -------------------------------------------------- */
|
/* ---- 3. Festgelegt -------------------------------------------------- */
|
||||||
console.log("\n=== Was festgelegt ist, bleibt ===");
|
console.log("\n=== Was festgelegt ist, bleibt ===");
|
||||||
for (const f of FESTGELEGT) {
|
for (const f of FESTGELEGT) {
|
||||||
const traeger = [...(benutzt.get(f.ton) || [])];
|
/* AUS BEIDEN QUELLEN. Bis zum 26.09. stand hier nur `benutzt` --
|
||||||
|
die Kacheln, die der Server ausgibt. Ton 1 traegt „Dashboard"
|
||||||
|
im Agenturhaus, und das steht im Browser; die Zeile meldete
|
||||||
|
deshalb „traegt stattdessen: nichts" fuer eine Kachel, die es
|
||||||
|
gibt. */
|
||||||
|
const traeger = [...new Set([...(benutzt.get(f.ton) || []),
|
||||||
|
...(TRAEGT.get(f.ton) || [])])];
|
||||||
pruef(`Ton ${f.ton} traegt "${f.kachel}" (${f.warum})`,
|
pruef(`Ton ${f.ton} traegt "${f.kachel}" (${f.warum})`,
|
||||||
traeger.includes(f.kachel),
|
traeger.includes(f.kachel),
|
||||||
`Ton ${f.ton} traegt stattdessen: ${traeger.join(", ") || "nichts"}`);
|
`Ton ${f.ton} traegt stattdessen: ${traeger.join(", ") || "nichts"}`);
|
||||||
@@ -263,15 +326,23 @@ console.log("\n=== Lesbar und kraeftig ===");
|
|||||||
beim naechsten Mal vergessen wird. */
|
beim naechsten Mal vergessen wird. */
|
||||||
const darfBlassSein = new Map(
|
const darfBlassSein = new Map(
|
||||||
FESTGELEGT.filter((f) => f.blassErlaubt).map((f) => [f.ton, f.blassErlaubt]));
|
FESTGELEGT.filter((f) => f.blassErlaubt).map((f) => [f.ton, f.blassErlaubt]));
|
||||||
for (const n of benutzt.keys()) {
|
/* WIE BLASS GENAU -- eine Ausnahme mit Zahl statt eines Freibriefs
|
||||||
|
(26.09.2026). Vorher hiess `blassErlaubt` schlicht „diese
|
||||||
|
Pruefung sieht hier weg"; ein Ton haette danach beliebig
|
||||||
|
verblassen koennen, ohne dass es auffaellt. Jetzt steht neben
|
||||||
|
jeder Ausnahme, wie weit sie reicht. */
|
||||||
|
const blassBis = new Map(
|
||||||
|
FESTGELEGT.filter((f) => f.blassBis).map((f) => [f.ton, f.blassBis]));
|
||||||
|
for (const n of IN_GEBRAUCH) {
|
||||||
const hex = farben.get(n);
|
const hex = farben.get(n);
|
||||||
if (!hex) continue;
|
if (!hex) continue;
|
||||||
if (kontrast(hex) < 4.5) dunkel.push(`Ton ${n} ${hex} (${kontrast(hex).toFixed(2)}:1)`);
|
if (kontrast(hex) < 4.5) dunkel.push(`Ton ${n} ${hex} (${kontrast(hex).toFixed(2)}:1)`);
|
||||||
if (buntheit(hex) < MINDEST_BUNTHEIT && !darfBlassSein.has(n)) {
|
const grenze = blassBis.get(n) ?? MINDEST_BUNTHEIT;
|
||||||
blass.push(`Ton ${n} ${hex} (${buntheit(hex).toFixed(3)})`);
|
if (buntheit(hex) < grenze) {
|
||||||
|
blass.push(`Ton ${n} ${hex} (${buntheit(hex).toFixed(3)}, erlaubt ab ${grenze})`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pruef(`alle ${benutzt.size} benutzten Toene erreichen 4,5:1 gegen den Grund`,
|
pruef(`alle ${IN_GEBRAUCH.size} Toene an Kacheln erreichen 4,5:1 gegen den Grund`,
|
||||||
dunkel.length === 0, dunkel.join(", "));
|
dunkel.length === 0, dunkel.join(", "));
|
||||||
pruef(`keiner ist blasser als ${MINDEST_BUNTHEIT}`
|
pruef(`keiner ist blasser als ${MINDEST_BUNTHEIT}`
|
||||||
+ (darfBlassSein.size ? ` (ausser ${darfBlassSein.size} benannten)` : ""),
|
+ (darfBlassSein.size ? ` (ausser ${darfBlassSein.size} benannten)` : ""),
|
||||||
@@ -287,7 +358,7 @@ console.log("\n=== Lesbar und kraeftig ===");
|
|||||||
if (!hex) { pruef(`Ton ${ton} ist festgelegt, steht aber nicht in start.css`, false); continue; }
|
if (!hex) { pruef(`Ton ${ton} ist festgelegt, steht aber nicht in start.css`, false); continue; }
|
||||||
let naechste = Infinity, wer = "-";
|
let naechste = Infinity, wer = "-";
|
||||||
for (const [m, h2] of farben) {
|
for (const [m, h2] of farben) {
|
||||||
if (m === ton || !benutzt.has(m)) continue;
|
if (m === ton || !IN_GEBRAUCH.has(m)) continue;
|
||||||
const d = abstand(oklab(hex), oklab(h2));
|
const d = abstand(oklab(hex), oklab(h2));
|
||||||
if (d < naechste) { naechste = d; wer = `Ton ${m}`; }
|
if (d < naechste) { naechste = d; wer = `Ton ${m}`; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,690 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
DER NOTIZBLOCK (26.09.2026)
|
||||||
|
|
||||||
|
Filipe: „für die modis, rechte und linke hand und dogfather eine
|
||||||
|
kachel hinzufügst, sie soll: Notizen, heißen. ich will dass du das
|
||||||
|
auch wie ein notizblock erstellst."
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
DREI DINGE WERDEN GEPRÜFT, UND ZWEI DAVON HAT NIEMAND BESTELLT
|
||||||
|
|
||||||
|
1. WER DARF ÜBERHAUPT. Genau die vier Rollen, und niemand sonst —
|
||||||
|
auch nicht über die Schnittstelle, auch nicht von der anderen
|
||||||
|
Adresse aus. Die Rollenmenge wird dabei nicht abgeschrieben,
|
||||||
|
sondern aus dem Programm gelesen und gegen die Rechtetafel
|
||||||
|
gehalten: Zwei Listen für dieselbe Frage laufen auseinander.
|
||||||
|
|
||||||
|
2. NIEMAND SIEHT DIE NOTIZEN EINES ANDEREN. Auch DogFather nicht.
|
||||||
|
Das ist die Zusage, die auf der Kachel steht („nur du siehst
|
||||||
|
ihn"), und die einzige, deren Bruch man nicht bemerken würde:
|
||||||
|
Eine Notiz, die jemand Fremdes liest, sieht für den Schreiber
|
||||||
|
genauso aus wie eine, die niemand liest.
|
||||||
|
|
||||||
|
3. DIE DECKSCHICHT LIEGT AUF DEN PIXEL GENAU. Das Blatt besteht
|
||||||
|
aus einem unsichtbaren Schreibfeld und einer sichtbaren
|
||||||
|
Deckschicht darüber. Ändert eine Auszeichnung die BREITE eines
|
||||||
|
Zeichens (Fettdruck, andere Schriftgröße, Sperrung), brechen
|
||||||
|
beide Schichten an verschiedenen Stellen um — und der Cursor
|
||||||
|
steht neben dem Text. Das sieht aus wie ein Browserfehler und
|
||||||
|
wäre einer in notizen.css. Gemessen wird deshalb am echten
|
||||||
|
Umbruch, nicht an der Absicht.
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
WARUM DIESE PRÜFUNG ÜBER https SPRICHT
|
||||||
|
|
||||||
|
Derselbe Grund wie bei pruef-handy-teamdogi: `dogfather-universe.com`
|
||||||
|
steht in der fest eingebauten HSTS-Liste von Chromium, und ohne die
|
||||||
|
Team-Adresse gibt es diese Seite gar nicht — auf einer Prüfadresse
|
||||||
|
(127.0.0.1) ist `haus` weder „crew" noch „agentur", und der Block
|
||||||
|
antwortet zu Recht mit 404.
|
||||||
|
|
||||||
|
Aufrufen mit: node server/pruef-notizen.mjs
|
||||||
|
===================================================================== */
|
||||||
|
import { mkdtempSync, rmSync, readFileSync } from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { request } from "node:http";
|
||||||
|
|
||||||
|
const ordner = mkdtempSync(join(tmpdir(), "ws-notiz-"));
|
||||||
|
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
||||||
|
|
||||||
|
import { notbremse } from "./helfer-notbremse.mjs";
|
||||||
|
const { eigenerPort } = await import("./helfer-port.mjs");
|
||||||
|
const PORT = await eigenerPort(import.meta, "die Notizblockpruefung");
|
||||||
|
|
||||||
|
process.env.PORT = `${PORT}`;
|
||||||
|
process.env.SITE_ACCESS_SECRET = "lokaler-test";
|
||||||
|
process.env.SITE_PUBLIC_LAUNCH_AT = "2020-01-01T00:00:00+01:00";
|
||||||
|
|
||||||
|
const express = (await import("express")).default;
|
||||||
|
const ec = express.response.cookie;
|
||||||
|
express.response.cookie = function (n, w, o) { return ec.call(this, n, w, { ...(o || {}), secure: false }); };
|
||||||
|
await import("./index.js");
|
||||||
|
notbremse(260_000, "pruef-notizen");
|
||||||
|
await new Promise((r) => setTimeout(r, 900));
|
||||||
|
|
||||||
|
let fehler = 0, geprueft = 0;
|
||||||
|
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
||||||
|
const melde = (t) => console.log(t);
|
||||||
|
process.on("exit", () => { try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ } });
|
||||||
|
|
||||||
|
const CREW = "crew.dogfather-universe.com";
|
||||||
|
const AGENTUR = "workspace.dogfather-universe.com";
|
||||||
|
|
||||||
|
/* ---------- Zugänge anlegen -------------------------------------------- */
|
||||||
|
const { DatabaseSync } = await import("node:sqlite");
|
||||||
|
const { scryptSync, randomBytes, createHmac } = await import("node:crypto");
|
||||||
|
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
||||||
|
const nun = new Date().toISOString();
|
||||||
|
const kennSchluessel = randomBytes(32).toString("hex");
|
||||||
|
d.prepare("INSERT INTO einstellungen (schluessel, wert, geaendert, von)"
|
||||||
|
+ " VALUES (?,?,?,NULL) ON CONFLICT(schluessel) DO NOTHING")
|
||||||
|
.run("code_kennung_schluessel", kennSchluessel, nun);
|
||||||
|
function anlegen(name, rolle, code) {
|
||||||
|
const salt = randomBytes(16).toString("hex");
|
||||||
|
const hash = scryptSync(code, salt, 64,
|
||||||
|
{ N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
|
||||||
|
d.prepare("INSERT INTO personen (name, rolle, code_hash, code_salt, code_n,"
|
||||||
|
+ " code_kennung, aktiv, erstellt) VALUES (?,?,?,?,?,?,1,?)")
|
||||||
|
.run(name, rolle, hash, salt, 32768,
|
||||||
|
createHmac("sha256", kennSchluessel).update(code).digest("hex"), nun);
|
||||||
|
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
|
||||||
|
}
|
||||||
|
const ROLLEN = [["admin", "Filipe"], ["hand", "VanVan"], ["linke", "Funny"],
|
||||||
|
["modi", "Miss"], ["gast", "Zuschauerin"], ["creator", "Luna"]];
|
||||||
|
const CODES = {};
|
||||||
|
const IDS = {};
|
||||||
|
for (const [i, [rolle, name]] of ROLLEN.entries()) {
|
||||||
|
CODES[rolle] = `CODE-N${String(i).padStart(2, "0")}-000001`;
|
||||||
|
IDS[rolle] = anlegen(name, rolle, CODES[rolle]);
|
||||||
|
}
|
||||||
|
d.close();
|
||||||
|
|
||||||
|
/* ---------- Sprechen mit Hausadresse ------------------------------------
|
||||||
|
`fetch` kann die Host-Kopfzeile nicht setzen (verbotene Kopfzeile),
|
||||||
|
und an ihr entscheidet das Haus alles. Deshalb node:http. */
|
||||||
|
function roh(host, pfad, { method = "GET", keks = "", rumpf = null, origin } = {}) {
|
||||||
|
return new Promise((fertig, schief) => {
|
||||||
|
const daten = rumpf === null ? null : Buffer.from(JSON.stringify(rumpf));
|
||||||
|
const a = request({
|
||||||
|
host: "127.0.0.1", port: PORT, path: pfad, method,
|
||||||
|
headers: {
|
||||||
|
Host: host,
|
||||||
|
Origin: origin === undefined ? `https://${host}` : origin,
|
||||||
|
...(keks ? { Cookie: keks } : {}),
|
||||||
|
...(daten ? { "Content-Type": "application/json", "Content-Length": daten.length } : {}),
|
||||||
|
},
|
||||||
|
}, (antwort) => {
|
||||||
|
const teile = [];
|
||||||
|
antwort.on("data", (s) => teile.push(s));
|
||||||
|
antwort.on("end", () => {
|
||||||
|
const text = Buffer.concat(teile).toString("utf8");
|
||||||
|
let j = null; try { j = JSON.parse(text); } catch { /* kein JSON */ }
|
||||||
|
fertig({ code: antwort.statusCode, text, j, kopf: antwort.headers });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
a.on("error", schief);
|
||||||
|
if (daten) a.write(daten);
|
||||||
|
a.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function anmelden(rolle, host) {
|
||||||
|
const a = await roh(host, "/workspace/api/anmelden", {
|
||||||
|
method: "POST", rumpf: { rolle, code: CODES[rolle] },
|
||||||
|
});
|
||||||
|
const keks = []
|
||||||
|
.concat(a.kopf["set-cookie"] || [])
|
||||||
|
.map((z) => z.split(";")[0]).join("; ");
|
||||||
|
return a.code === 200 && keks ? keks : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =======================================================================
|
||||||
|
1. WER DARF — und woher die Liste kommt
|
||||||
|
======================================================================= */
|
||||||
|
melde("=== 1. Wer einen Block hat ===");
|
||||||
|
|
||||||
|
const { ROLLEN_MIT_BLOCK } = await import("./workspace-notizen.js");
|
||||||
|
const { SEITEN } = await import("./rechte.js");
|
||||||
|
{
|
||||||
|
const ausTafel = [...(SEITEN["/workspace/notizen.html"] || [])].sort();
|
||||||
|
const ausProgramm = [...ROLLEN_MIT_BLOCK].sort();
|
||||||
|
ok(ausTafel.length === 4,
|
||||||
|
`die Rechtetafel nennt vier Rollen (${ausTafel.join(", ") || "keine"})`);
|
||||||
|
/* BEIDE SEITEN GEGENEINANDER. Die Tafel bewacht die Seite, die
|
||||||
|
Menge im Modul die Daten. Wer nur eine pflegt, baut entweder eine
|
||||||
|
Tuer ohne Raum oder einen Raum ohne Tuer -- und beides faellt
|
||||||
|
niemandem auf. */
|
||||||
|
ok(JSON.stringify(ausTafel) === JSON.stringify(ausProgramm),
|
||||||
|
`Tafel und Schnittstelle nennen dieselben Rollen (${ausProgramm.join(", ")})`,
|
||||||
|
);
|
||||||
|
ok(ausTafel.includes("modi") && ausTafel.includes("hand")
|
||||||
|
&& ausTafel.includes("linke") && ausTafel.includes("admin"),
|
||||||
|
"und es sind genau die vier aus dem Auftrag");
|
||||||
|
}
|
||||||
|
|
||||||
|
const kekse = {};
|
||||||
|
for (const [rolle] of ROLLEN) kekse[rolle] = await anmelden(rolle, CREW);
|
||||||
|
ok(["admin", "hand", "linke", "modi"].every((r) => kekse[r]),
|
||||||
|
"die vier melden sich auf der Team-Adresse an");
|
||||||
|
|
||||||
|
melde("");
|
||||||
|
melde("=== 2. Und wer nicht ===");
|
||||||
|
for (const rolle of ["gast", "creator"]) {
|
||||||
|
if (!kekse[rolle]) { ok(true, `${rolle}: kommt auf dieser Adresse gar nicht herein`); continue; }
|
||||||
|
const a = await roh(CREW, "/workspace/api/notizblock", { keks: kekse[rolle] });
|
||||||
|
ok(a.code === 404, `${rolle}: bekommt 404 an der Schnittstelle (${a.code})`);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
/* DIE ANDERE ADRESSE. DogFather arbeitet in beiden Haeusern -- und
|
||||||
|
genau deshalb ist er hier der richtige Pruefstein: Bei ihm
|
||||||
|
scheitert es nicht an der Rolle, sondern an der Adresse. */
|
||||||
|
const keksAgentur = await anmelden("admin", AGENTUR);
|
||||||
|
ok(!!keksAgentur, "DogFather meldet sich auch auf der Agenturadresse an");
|
||||||
|
const a = await roh(AGENTUR, "/workspace/api/notizblock", { keks: keksAgentur });
|
||||||
|
ok(a.code === 404, `und bekommt dort 404 auf seinen Block (${a.code})`);
|
||||||
|
const s = await roh(AGENTUR, "/workspace/notizen.html", { keks: keksAgentur });
|
||||||
|
ok(s.code === 404, `auch die Seite selbst gibt es dort nicht (${s.code})`);
|
||||||
|
const s2 = await roh(CREW, "/workspace/notizen.html", { keks: kekse.admin });
|
||||||
|
ok(s2.code === 200, `auf der Team-Adresse schon (${s2.code})`);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const ohne = await roh(CREW, "/workspace/api/notizblock");
|
||||||
|
ok(ohne.code === 401, `ohne Anmeldung: 401 (${ohne.code})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =======================================================================
|
||||||
|
3. SCHREIBEN, LESEN, ÄNDERN
|
||||||
|
======================================================================= */
|
||||||
|
melde("");
|
||||||
|
melde("=== 3. Ein Blatt ===");
|
||||||
|
let blattId = null;
|
||||||
|
{
|
||||||
|
const leer = await roh(CREW, "/workspace/api/notizblock", { keks: kekse.modi });
|
||||||
|
ok(leer.code === 200 && Array.isArray(leer.j?.notizen) && leer.j.notizen.length === 0,
|
||||||
|
`am Anfang ist der Block leer (${leer.j?.notizen?.length})`);
|
||||||
|
ok(Array.isArray(leer.j?.farben) && leer.j.farben.length >= 4,
|
||||||
|
`die Papierfarben kommen vom Server (${leer.j?.farben?.length})`);
|
||||||
|
|
||||||
|
const neu = await roh(CREW, "/workspace/api/notizblock", {
|
||||||
|
method: "POST", keks: kekse.modi, rumpf: { text: "Erste Zeile\nZweite Zeile" },
|
||||||
|
});
|
||||||
|
blattId = neu.j?.notiz?.id;
|
||||||
|
ok(neu.code === 201 && blattId > 0, `ein Blatt anlegen geht (${neu.code})`);
|
||||||
|
ok(neu.j?.notiz?.titel === "Erste Zeile",
|
||||||
|
`der Titel wird aus der ersten Zeile abgeleitet ("${neu.j?.notiz?.titel}")`);
|
||||||
|
|
||||||
|
const aend = await roh(CREW, `/workspace/api/notizblock/${blattId}`, {
|
||||||
|
method: "PATCH", keks: kekse.modi, rumpf: { text: "# Einkauf\n[ ] Milch\n[x] Brot" },
|
||||||
|
});
|
||||||
|
ok(aend.code === 200 && typeof aend.j?.geaendert === "string",
|
||||||
|
`aendern geht und gibt den Zeitstempel zurueck (${aend.code})`);
|
||||||
|
|
||||||
|
const nach = await roh(CREW, "/workspace/api/notizblock", { keks: kekse.modi });
|
||||||
|
ok(nach.j?.notizen?.[0]?.text?.includes("[x] Brot"), "und der Text steht drin");
|
||||||
|
ok(nach.j?.notizen?.[0]?.titel === "Einkauf",
|
||||||
|
`die Raute zaehlt nicht zum Titel ("${nach.j?.notizen?.[0]?.titel}")`);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const falsch = await roh(CREW, `/workspace/api/notizblock/${blattId}`, {
|
||||||
|
method: "PATCH", keks: kekse.modi, rumpf: { farbe: "knallorange" },
|
||||||
|
});
|
||||||
|
ok(falsch.code === 400, `eine erfundene Papierfarbe wird abgelehnt (${falsch.code})`);
|
||||||
|
const echt = await roh(CREW, `/workspace/api/notizblock/${blattId}`, {
|
||||||
|
method: "PATCH", keks: kekse.modi, rumpf: { farbe: "rosa" },
|
||||||
|
});
|
||||||
|
ok(echt.code === 200, `eine echte nicht (${echt.code})`);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const lang = "x".repeat(30000);
|
||||||
|
const a = await roh(CREW, `/workspace/api/notizblock/${blattId}`, {
|
||||||
|
method: "PATCH", keks: kekse.modi, rumpf: { text: lang },
|
||||||
|
});
|
||||||
|
const nach = await roh(CREW, "/workspace/api/notizblock", { keks: kekse.modi });
|
||||||
|
const drin = nach.j?.notizen?.find((n) => n.id === blattId)?.text || "";
|
||||||
|
/* ABGESCHNITTEN UND NICHT ABGELEHNT. Wer zwanzigtausend Zeichen
|
||||||
|
getippt hat, soll nicht alles verlieren, weil er hundert zu viel
|
||||||
|
hatte -- er soll merken, dass hier Schluss ist. */
|
||||||
|
ok(a.code === 200 && drin.length === nach.j.text_max,
|
||||||
|
`zu langer Text wird gekuerzt statt abgelehnt (${drin.length} von ${nach.j?.text_max})`);
|
||||||
|
await roh(CREW, `/workspace/api/notizblock/${blattId}`, {
|
||||||
|
method: "PATCH", keks: kekse.modi, rumpf: { text: "# Einkauf\n[ ] Milch\n[x] Brot" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const fremd = await roh(CREW, `/workspace/api/notizblock/${blattId}`, {
|
||||||
|
method: "PATCH", keks: kekse.modi, rumpf: { text: "x" }, origin: "https://boese.example",
|
||||||
|
});
|
||||||
|
ok(fremd.code === 403, `eine Anfrage von einer fremden Seite wird abgewiesen (${fremd.code})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =======================================================================
|
||||||
|
4. NIEMAND SIEHT DIE NOTIZEN EINES ANDEREN
|
||||||
|
|
||||||
|
Die Zusage steht auf der Kachel. Sie ist auch die einzige, deren
|
||||||
|
Bruch der Schreiber nicht bemerken würde.
|
||||||
|
======================================================================= */
|
||||||
|
melde("");
|
||||||
|
melde("=== 4. Niemand sonst ===");
|
||||||
|
for (const rolle of ["admin", "hand", "linke"]) {
|
||||||
|
const liste = await roh(CREW, "/workspace/api/notizblock", { keks: kekse[rolle] });
|
||||||
|
const fremdeDrin = (liste.j?.notizen || []).some((n) => n.id === blattId);
|
||||||
|
ok(liste.code === 200 && !fremdeDrin,
|
||||||
|
`${rolle}: sieht das Blatt des Modis nicht in seiner Liste`);
|
||||||
|
const holen = await roh(CREW, `/workspace/api/notizblock/${blattId}`, {
|
||||||
|
method: "PATCH", keks: kekse[rolle], rumpf: { text: "hier war ich" },
|
||||||
|
});
|
||||||
|
ok(holen.code === 404, `${rolle}: kann es auch nicht aendern (${holen.code})`);
|
||||||
|
const weg = await roh(CREW, `/workspace/api/notizblock/${blattId}`, {
|
||||||
|
method: "DELETE", keks: kekse[rolle],
|
||||||
|
});
|
||||||
|
ok(weg.code === 404, `${rolle}: und nicht wegwerfen (${weg.code})`);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
/* GEGENPROBE. Ohne sie wuerde die Messung oben auch dann gruen, wenn
|
||||||
|
das Blatt gar nicht existiert. */
|
||||||
|
const eigen = await roh(CREW, `/workspace/api/notizblock/${blattId}`, {
|
||||||
|
method: "PATCH", keks: kekse.modi, rumpf: { angeheftet: true },
|
||||||
|
});
|
||||||
|
ok(eigen.code === 200, `Gegenprobe: dem Modi selbst gelingt genau dasselbe (${eigen.code})`);
|
||||||
|
const nach = await roh(CREW, "/workspace/api/notizblock", { keks: kekse.modi });
|
||||||
|
ok(nach.j?.notizen?.[0]?.angeheftet === true, "und das Anheften kommt an");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =======================================================================
|
||||||
|
5. DER PAPIERKORB
|
||||||
|
======================================================================= */
|
||||||
|
melde("");
|
||||||
|
melde("=== 5. Der Papierkorb ===");
|
||||||
|
{
|
||||||
|
const weg = await roh(CREW, `/workspace/api/notizblock/${blattId}`, {
|
||||||
|
method: "DELETE", keks: kekse.modi,
|
||||||
|
});
|
||||||
|
ok(weg.code === 200, `wegtun geht (${weg.code})`);
|
||||||
|
const block = await roh(CREW, "/workspace/api/notizblock", { keks: kekse.modi });
|
||||||
|
ok(!(block.j?.notizen || []).some((n) => n.id === blattId), "und es ist vom Block verschwunden");
|
||||||
|
ok(block.j?.papierkorb === 1, `der Papierkorb zaehlt es (${block.j?.papierkorb})`);
|
||||||
|
|
||||||
|
const korb = await roh(CREW, "/workspace/api/notizblock/papierkorb", { keks: kekse.modi });
|
||||||
|
const drin = (korb.j?.notizen || []).find((n) => n.id === blattId);
|
||||||
|
ok(!!drin, "es liegt im Papierkorb");
|
||||||
|
ok(typeof drin?.endgueltig_am === "string"
|
||||||
|
&& Date.parse(drin.endgueltig_am) > Date.now(),
|
||||||
|
`und es steht dabei, wann es von selbst geht (${drin?.endgueltig_am?.slice(0, 10)})`);
|
||||||
|
|
||||||
|
const zurueck = await roh(CREW, `/workspace/api/notizblock/${blattId}/zurueck`, {
|
||||||
|
method: "POST", keks: kekse.modi,
|
||||||
|
});
|
||||||
|
ok(zurueck.code === 200, `zurueckholen geht (${zurueck.code})`);
|
||||||
|
const block2 = await roh(CREW, "/workspace/api/notizblock", { keks: kekse.modi });
|
||||||
|
ok((block2.j?.notizen || []).some((n) => n.id === blattId), "und es ist wieder da");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
/* ENDGUELTIG NUR AUS DEM PAPIERKORB. Vom Block aus gibt es diesen
|
||||||
|
Weg nicht -- wer danebentippt, soll nicht alles verlieren. */
|
||||||
|
const zuFrueh = await roh(CREW, `/workspace/api/notizblock/${blattId}/endgueltig`, {
|
||||||
|
method: "DELETE", keks: kekse.modi,
|
||||||
|
});
|
||||||
|
ok(zuFrueh.code === 404,
|
||||||
|
`endgueltig loeschen geht vom Block aus NICHT (${zuFrueh.code})`);
|
||||||
|
await roh(CREW, `/workspace/api/notizblock/${blattId}`, { method: "DELETE", keks: kekse.modi });
|
||||||
|
const jetzt = await roh(CREW, `/workspace/api/notizblock/${blattId}/endgueltig`, {
|
||||||
|
method: "DELETE", keks: kekse.modi,
|
||||||
|
});
|
||||||
|
ok(jetzt.code === 200, `aus dem Papierkorb schon (${jetzt.code})`);
|
||||||
|
const korb = await roh(CREW, "/workspace/api/notizblock/papierkorb", { keks: kekse.modi });
|
||||||
|
ok((korb.j?.notizen || []).length === 0, "und dann ist es wirklich weg");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =======================================================================
|
||||||
|
6. DAS AUFRÄUMEN HÄLT SEINE FRIST
|
||||||
|
|
||||||
|
Eine Frist ohne Werkzeug ist ein Satz in einem Kommentar. Geprüft
|
||||||
|
wird deshalb an echten Daten, in beide Richtungen.
|
||||||
|
======================================================================= */
|
||||||
|
melde("");
|
||||||
|
melde("=== 6. Dreissig Tage ===");
|
||||||
|
{
|
||||||
|
const { notizenAufraeumen } = await import("./workspace-notizen.js");
|
||||||
|
const { PAPIERKORB_TAGE } = await import("./notiz-tabellen.js");
|
||||||
|
const { db } = await import("./workspace.js");
|
||||||
|
const alt = new Date(Date.now() - (PAPIERKORB_TAGE + 2) * 86400000).toISOString();
|
||||||
|
const frisch = new Date(Date.now() - 86400000).toISOString();
|
||||||
|
const einfuegen = (weg) => db().prepare(
|
||||||
|
"INSERT INTO notizen (person_id, text, farbe, angeheftet, erstellt, geaendert, weg_am)"
|
||||||
|
+ " VALUES (?,?,?,0,?,?,?) RETURNING id")
|
||||||
|
.get(IDS.modi, "Probe", "block", nun, nun, weg).id;
|
||||||
|
const idAlt = einfuegen(alt);
|
||||||
|
const idNeu = einfuegen(frisch);
|
||||||
|
const idOffen = db().prepare(
|
||||||
|
"INSERT INTO notizen (person_id, text, farbe, angeheftet, erstellt, geaendert)"
|
||||||
|
+ " VALUES (?,?,?,0,?,?) RETURNING id").get(IDS.modi, "Auf dem Block", "block", nun, nun).id;
|
||||||
|
|
||||||
|
const weg = notizenAufraeumen();
|
||||||
|
const da = (id) => !!db().prepare("SELECT 1 FROM notizen WHERE id = ?").get(id);
|
||||||
|
ok(weg === 1, `genau eines wird geraeumt (${weg})`);
|
||||||
|
ok(!da(idAlt), `das alte ist weg (nach ${PAPIERKORB_TAGE} Tagen)`);
|
||||||
|
ok(da(idNeu), "das von gestern liegt noch da");
|
||||||
|
/* DIE WICHTIGERE HAELFTE: Was auf dem Block liegt, wird NIE
|
||||||
|
geraeumt. Ohne diese Zeile waere ein Aufraeumer, der alles
|
||||||
|
loescht, hier gruen. */
|
||||||
|
ok(da(idOffen), "und was gar nicht weggelegt wurde, bleibt unberuehrt");
|
||||||
|
db().prepare("DELETE FROM notizen WHERE id IN (?,?)").run(idNeu, idOffen);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
/* Steht die Frist auch in der Aufbewahrungsliste? Sie ist die
|
||||||
|
einzige Stelle im Haus, die wirklich raeumt -- am 24.09. war
|
||||||
|
genau dieser Eintrag beim Support vergessen worden, und die
|
||||||
|
Meldungen waeren fuer immer liegen geblieben. */
|
||||||
|
const { FRISTEN } = await import("./workspace-aufbewahrung.js");
|
||||||
|
const eintrag = (FRISTEN || []).find((e) => e.schluessel === "notizen_papierkorb");
|
||||||
|
ok(!!eintrag, "der Papierkorb steht in der Aufbewahrungsliste");
|
||||||
|
ok(typeof eintrag?.raeumen === "function",
|
||||||
|
"und hat ein Werkzeug, das ihn wirklich raeumt");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =======================================================================
|
||||||
|
7. DAS BLATT AUF DEM BILDSCHIRM
|
||||||
|
|
||||||
|
Jetzt der Teil, den keine Schnittstelle beantworten kann.
|
||||||
|
======================================================================= */
|
||||||
|
melde("");
|
||||||
|
melde("=== 7. Das Blatt auf dem Bildschirm ===");
|
||||||
|
|
||||||
|
const { execFileSync } = await import("node:child_process");
|
||||||
|
const { createServer: httpsServer } = await import("node:https");
|
||||||
|
const { request: httpAnfrage } = await import("node:http");
|
||||||
|
|
||||||
|
const schluesselDatei = join(ordner, "pruef.key");
|
||||||
|
const zertDatei = join(ordner, "pruef.crt");
|
||||||
|
execFileSync("openssl", ["req", "-x509", "-newkey", "rsa:2048", "-nodes",
|
||||||
|
"-keyout", schluesselDatei, "-out", zertDatei, "-days", "2",
|
||||||
|
"-subj", `/CN=${CREW}`, "-addext", `subjectAltName=DNS:${CREW}`],
|
||||||
|
{ stdio: "ignore" });
|
||||||
|
|
||||||
|
const HTTPS_PORT = PORT + 1;
|
||||||
|
const vorbau = httpsServer({
|
||||||
|
key: readFileSync(schluesselDatei), cert: readFileSync(zertDatei),
|
||||||
|
}, (von, nach) => {
|
||||||
|
const weiter = httpAnfrage({
|
||||||
|
host: "127.0.0.1", port: PORT, path: von.url, method: von.method,
|
||||||
|
headers: { ...von.headers, host: `${CREW}:${HTTPS_PORT}` },
|
||||||
|
}, (antwort) => {
|
||||||
|
nach.writeHead(antwort.statusCode || 502, antwort.headers);
|
||||||
|
antwort.pipe(nach);
|
||||||
|
});
|
||||||
|
weiter.on("error", () => { try { nach.writeHead(502); nach.end(); } catch { /* egal */ } });
|
||||||
|
von.pipe(weiter);
|
||||||
|
});
|
||||||
|
await new Promise((r) => vorbau.listen(HTTPS_PORT, "127.0.0.1", r));
|
||||||
|
const BASIS = `https://${CREW}:${HTTPS_PORT}`;
|
||||||
|
|
||||||
|
const pw = await import(
|
||||||
|
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
||||||
|
const browser = await pw.chromium.launch({
|
||||||
|
args: [`--host-resolver-rules=MAP ${CREW} 127.0.0.1`,
|
||||||
|
"--ignore-certificate-errors"],
|
||||||
|
});
|
||||||
|
const kontext = await browser.newContext({
|
||||||
|
viewport: { width: 1440, height: 900 }, ignoreHTTPSErrors: true,
|
||||||
|
});
|
||||||
|
await kontext.addCookies((kekse.modi || "").split("; ").filter(Boolean).map((k) => {
|
||||||
|
const [n, ...r] = k.split("=");
|
||||||
|
return { name: n, value: r.join("="), domain: CREW, path: "/" };
|
||||||
|
}));
|
||||||
|
const seite = await kontext.newPage();
|
||||||
|
const konsole = [];
|
||||||
|
seite.on("pageerror", (f) => konsole.push(String(f?.message || f)));
|
||||||
|
seite.on("console", (m) => { if (m.type() === "error") konsole.push(m.text()); });
|
||||||
|
|
||||||
|
await seite.goto(`${BASIS}/workspace/notizen.html`, { waitUntil: "networkidle" });
|
||||||
|
await seite.waitForTimeout(700);
|
||||||
|
|
||||||
|
ok(await seite.locator("#block").count() === 1, "die Seite kommt an");
|
||||||
|
|
||||||
|
/* Ein Blatt anlegen und Text hineinschreiben, der alle Auszeichnungen
|
||||||
|
enthaelt -- und eine SEHR lange Zeile, die umbrechen MUSS. Genau an
|
||||||
|
ihr faellt eine Breitenaenderung auf: Brechen die zwei Schichten an
|
||||||
|
verschiedenen Stellen um, sind sie verschieden hoch. */
|
||||||
|
const PROBE = [
|
||||||
|
"# Eine Ueberschrift",
|
||||||
|
"[ ] Offener Punkt",
|
||||||
|
"[x] Erledigter Punkt",
|
||||||
|
"- Ein Aufzaehlungspunkt",
|
||||||
|
"> Ein Zitat",
|
||||||
|
"Ein Link: https://example.org/pfad",
|
||||||
|
"Eine sehr lange Zeile, die auf jeden Fall umbrechen muss, damit man sieht, "
|
||||||
|
+ "ob beide Schichten an derselben Stelle umbrechen, und deshalb ist sie so "
|
||||||
|
+ "lang wie sie ist, mit vielen Woertern hintereinander ohne Punkt und Komma "
|
||||||
|
+ "damit der Umbruch wirklich mitten im Satz passiert und nicht am Ende.",
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
await seite.click("#neu");
|
||||||
|
await seite.waitForTimeout(400);
|
||||||
|
await seite.fill("#feld", PROBE);
|
||||||
|
await seite.waitForTimeout(400);
|
||||||
|
|
||||||
|
{
|
||||||
|
const m = await seite.evaluate(() => {
|
||||||
|
const f = document.getElementById("feld");
|
||||||
|
const dk = document.getElementById("deck");
|
||||||
|
const cf = getComputedStyle(f), cd = getComputedStyle(dk);
|
||||||
|
const gleich = (n) => cf[n] === cd[n];
|
||||||
|
return {
|
||||||
|
hoeheFeld: f.scrollHeight, hoeheDeck: dk.scrollHeight,
|
||||||
|
breiteFeld: f.clientWidth, breiteDeck: dk.clientWidth,
|
||||||
|
schriftGleich: ["fontFamily", "fontSize", "fontWeight", "fontStyle",
|
||||||
|
"letterSpacing", "wordSpacing", "lineHeight", "whiteSpace",
|
||||||
|
"overflowWrap", "tabSize", "paddingLeft", "paddingRight",
|
||||||
|
"paddingTop"].filter((n) => !gleich(n)),
|
||||||
|
/* Und dasselbe fuer JEDES ausgezeichnete Stueck im Deck: Wenn
|
||||||
|
eine Auszeichnung die Schrift veraendert, steht es hier. */
|
||||||
|
stuecke: [...dk.querySelectorAll("span, a")].map((s) => {
|
||||||
|
const c = getComputedStyle(s);
|
||||||
|
return ["fontFamily", "fontSize", "fontWeight", "fontStyle",
|
||||||
|
"letterSpacing", "wordSpacing"]
|
||||||
|
.filter((n) => c[n] !== cf[n])
|
||||||
|
.map((n) => `${s.className || s.tagName}.${n}=${c[n]}`);
|
||||||
|
}).flat(),
|
||||||
|
haken: dk.querySelectorAll(".n-hk").length,
|
||||||
|
hakenAn: dk.querySelectorAll('.n-hk[data-an="ja"]').length,
|
||||||
|
links: dk.querySelectorAll("a.n-link").length,
|
||||||
|
ueber: dk.querySelectorAll(".n-h").length,
|
||||||
|
text: dk.textContent,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
ok(m.schriftGleich.length === 0,
|
||||||
|
`beide Schichten schreiben gleich (${m.schriftGleich.join(", ") || "alles gleich"})`);
|
||||||
|
ok(m.stuecke.length === 0,
|
||||||
|
`keine Auszeichnung aendert die Schrift (${m.stuecke.slice(0, 3).join(", ") || "keine"})`);
|
||||||
|
ok(m.breiteFeld === m.breiteDeck,
|
||||||
|
`beide sind gleich breit (${m.breiteFeld} / ${m.breiteDeck})`);
|
||||||
|
/* DIE EIGENTLICHE MESSUNG. Zwei Bildpunkte Spielraum fuers Runden;
|
||||||
|
ein verschobener Umbruch kostet eine ganze Zeile (30 px). */
|
||||||
|
ok(Math.abs(m.hoeheFeld - m.hoeheDeck) <= 2,
|
||||||
|
`und gleich hoch -- der Umbruch sitzt an derselben Stelle `
|
||||||
|
+ `(${m.hoeheFeld} / ${m.hoeheDeck})`);
|
||||||
|
ok(m.text.replace(/\s+$/, "") === PROBE,
|
||||||
|
"die Deckschicht zeigt genau den Text, Zeichen fuer Zeichen");
|
||||||
|
ok(m.haken === 2 && m.hakenAn === 1,
|
||||||
|
`zwei Kaestchen, eines angehakt (${m.haken}/${m.hakenAn})`);
|
||||||
|
ok(m.links === 1, `die Adresse ist anklickbar (${m.links})`);
|
||||||
|
ok(m.ueber === 1, `die Ueberschrift ist ausgezeichnet (${m.ueber})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GEGENPROBE ZUR HOEHENMESSUNG. Ohne sie wuerde die Zeile oben auch
|
||||||
|
dann gruen, wenn die Messung gar nichts ansieht. Ein Fettdruck auf
|
||||||
|
der Deckschicht MUSS sie rot machen. */
|
||||||
|
{
|
||||||
|
const unterschied = await seite.evaluate(() => {
|
||||||
|
const f = document.getElementById("feld");
|
||||||
|
const dk = document.getElementById("deck");
|
||||||
|
const vorher = Math.abs(f.scrollHeight - dk.scrollHeight);
|
||||||
|
dk.style.fontWeight = "800";
|
||||||
|
dk.style.letterSpacing = "0.4px";
|
||||||
|
const nachher = Math.abs(f.scrollHeight - dk.scrollHeight);
|
||||||
|
dk.style.fontWeight = ""; dk.style.letterSpacing = "";
|
||||||
|
return { vorher, nachher };
|
||||||
|
});
|
||||||
|
ok(unterschied.nachher > 2,
|
||||||
|
`Gegenprobe: Fettdruck auf der Deckschicht bricht die Deckung `
|
||||||
|
+ `(${unterschied.vorher} -> ${unterschied.nachher} px Unterschied)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ein Klick auf ein Kaestchen schaltet es um. */
|
||||||
|
{
|
||||||
|
const vorher = await seite.inputValue("#feld");
|
||||||
|
await seite.locator("#deck .n-hk").first().click();
|
||||||
|
await seite.waitForTimeout(250);
|
||||||
|
const nachher = await seite.inputValue("#feld");
|
||||||
|
ok(vorher.includes("[ ] Offener Punkt") && nachher.includes("[x] Offener Punkt"),
|
||||||
|
"ein Klick auf das Kaestchen setzt den Haken");
|
||||||
|
ok(nachher.length === vorher.length,
|
||||||
|
`und der Text bleibt zeichengenau gleich lang (${nachher.length}/${vorher.length})`);
|
||||||
|
await seite.locator("#deck .n-hk").first().click();
|
||||||
|
await seite.waitForTimeout(250);
|
||||||
|
ok((await seite.inputValue("#feld")).includes("[ ] Offener Punkt"),
|
||||||
|
"noch ein Klick nimmt ihn wieder weg");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Die Tastatur fuehrt die Liste fort. */
|
||||||
|
{
|
||||||
|
await seite.fill("#feld", "- Erster Punkt");
|
||||||
|
await seite.click("#feld");
|
||||||
|
await seite.keyboard.press("Control+End");
|
||||||
|
await seite.keyboard.press("Enter");
|
||||||
|
await seite.keyboard.type("Zweiter");
|
||||||
|
await seite.waitForTimeout(200);
|
||||||
|
const t = await seite.inputValue("#feld");
|
||||||
|
ok(t === "- Erster Punkt\n- Zweiter",
|
||||||
|
`Enter setzt die Aufzaehlung fort ("${t.replace(/\n/g, "\\n")}")`);
|
||||||
|
|
||||||
|
await seite.fill("#feld", "[x] Fertig");
|
||||||
|
await seite.click("#feld");
|
||||||
|
await seite.keyboard.press("Control+End");
|
||||||
|
await seite.keyboard.press("Enter");
|
||||||
|
await seite.keyboard.type("Noch offen");
|
||||||
|
await seite.waitForTimeout(200);
|
||||||
|
const t2 = await seite.inputValue("#feld");
|
||||||
|
/* DER HAKEN WIRD NICHT MITGENOMMEN -- die naechste Aufgabe ist noch
|
||||||
|
nicht erledigt. Das ist die Sorte Kleinigkeit, die den
|
||||||
|
Unterschied macht zwischen „funktioniert" und „denkt mit". */
|
||||||
|
ok(t2 === "[x] Fertig\n[ ] Noch offen",
|
||||||
|
`und uebernimmt einen gesetzten Haken NICHT ("${t2.replace(/\n/g, "\\n")}")`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Es sichert sich von selbst -- gemessen am Neuladen, nicht an der
|
||||||
|
Anzeige. Eine Anzeige, die „gesichert" sagt, ist eine Behauptung. */
|
||||||
|
{
|
||||||
|
const wort = `Selbstgesichert-${Date.now()}`;
|
||||||
|
await seite.fill("#feld", wort);
|
||||||
|
await seite.waitForTimeout(1400);
|
||||||
|
const stand = (await seite.locator("#stand").textContent() || "").trim();
|
||||||
|
/* GEFRAGT WIRD DER SERVER, nicht die Seite nach dem Neuladen.
|
||||||
|
|
||||||
|
Der erste Anlauf lud neu und sah ins Schreibfeld -- und wurde
|
||||||
|
rot, obwohl alles richtig war: Die Seite schlaegt nach dem Laden
|
||||||
|
das OBERSTE Blatt auf, und das war ein anderes (ein angeheftetes
|
||||||
|
aus Abschnitt 4). Gemessen wurde also ein Blatt, in das nie
|
||||||
|
jemand getippt hat. Eine Messung, die das Falsche ansieht, ist
|
||||||
|
schlimmer als keine -- sie schickt einen los, einen Fehler zu
|
||||||
|
suchen, den es nicht gibt. */
|
||||||
|
const drueben = await roh(CREW, "/workspace/api/notizblock", { keks: kekse.modi });
|
||||||
|
const gefunden = (drueben.j?.notizen || []).some((n) => String(n.text).includes(wort));
|
||||||
|
ok(gefunden, `der Text ist ohne Knopfdruck beim Server angekommen `
|
||||||
|
+ `(Anzeige sagte "${stand}")`);
|
||||||
|
ok(stand === "gesichert", `und die Anzeige sagt es auch ("${stand}")`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Und auf dem Handy: Das Blatt legt sich ueber die Liste, der
|
||||||
|
Zurueck-Knopf holt sie wieder. */
|
||||||
|
{
|
||||||
|
const handy = await kontext.newPage();
|
||||||
|
await handy.setViewportSize({ width: 390, height: 780 });
|
||||||
|
await handy.goto(`${BASIS}/workspace/notizen.html`, { waitUntil: "networkidle" });
|
||||||
|
await handy.waitForTimeout(700);
|
||||||
|
const listeDa = await handy.locator(".n-zettel").count();
|
||||||
|
ok(listeDa >= 1, `auf dem Handy steht zuerst der Stapel (${listeDa} Blaetter)`);
|
||||||
|
ok(await handy.locator("#blatt").isHidden(),
|
||||||
|
"und kein Blatt liegt davor -- sonst kaeme man nie an die Liste");
|
||||||
|
await handy.locator(".n-zettel").first().click();
|
||||||
|
await handy.waitForTimeout(400);
|
||||||
|
ok(await handy.locator("#blatt").isVisible(), "ein Tipp schlaegt das Blatt auf");
|
||||||
|
ok(await handy.locator("#zurueck").isVisible(),
|
||||||
|
"und der Zurueck-Knopf erscheint");
|
||||||
|
/* KEIN WAAGERECHTES SCHIEBEN. Die Hausgrenze ist die Breite des
|
||||||
|
Schirms; ein Blatt, das darueber hinausragt, macht die ganze
|
||||||
|
Seite schiebbar. */
|
||||||
|
const breit = await handy.evaluate(() =>
|
||||||
|
document.documentElement.scrollWidth - document.documentElement.clientWidth);
|
||||||
|
ok(breit <= 1, `nichts ragt seitlich hinaus (${breit} px)`);
|
||||||
|
await handy.locator("#zurueck").click();
|
||||||
|
await handy.waitForTimeout(400);
|
||||||
|
ok(await handy.locator("#blatt").isHidden(), "der Zurueck-Knopf schliesst es wieder");
|
||||||
|
await handy.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =======================================================================
|
||||||
|
8. ZWEI WEGE MIT DEMSELBEN NAMEN -- EINE HAUSWEITE PRUEFUNG
|
||||||
|
|
||||||
|
Sie steht in dieser Datei, weil sie hier gefunden wurde, und sie
|
||||||
|
gilt fuer das ganze Haus.
|
||||||
|
|
||||||
|
BEIM BAUEN DIESES BLOCKS: Die Route DELETE /workspace/api/notizen/:id
|
||||||
|
gab es schon -- in workspace-aufgaben.js, fuer die Notizen AN einer
|
||||||
|
Aufgabe. Weil aufgabenRouter frueher eingehaengt wird, hat sie jeden
|
||||||
|
Loeschversuch des Blocks abgefangen und mit 404 beantwortet.
|
||||||
|
|
||||||
|
VON AUSSEN SAH DAS AUS WIE EIN FEHLER IM NEUEN MODUL: Anlegen ging,
|
||||||
|
Aendern ging, Loeschen nicht. Man sucht dann im eigenen Code, und
|
||||||
|
dort ist nichts. Express meldet so etwas nicht -- es nimmt die
|
||||||
|
erste Route, die passt, und schweigt ueber die zweite.
|
||||||
|
|
||||||
|
Geprueft wird der Routenbaum von express selbst, nicht eine Liste.
|
||||||
|
Findet sich ein Paar aus Methode und Pfad zweimal, steht hier, wo.
|
||||||
|
======================================================================= */
|
||||||
|
melde("");
|
||||||
|
melde("=== 8. Kein Weg heisst zweimal gleich ===");
|
||||||
|
{
|
||||||
|
const app = (await import("./index.js")).default;
|
||||||
|
const wege = new Map();
|
||||||
|
const doppelt = [];
|
||||||
|
const sammeln = (schicht, vorn = "") => {
|
||||||
|
if (schicht.route) {
|
||||||
|
const pfad = vorn + (schicht.route.path || "");
|
||||||
|
for (const m of Object.keys(schicht.route.methods || {})) {
|
||||||
|
if (m === "_all") continue;
|
||||||
|
const name = `${m.toUpperCase()} ${pfad}`;
|
||||||
|
if (wege.has(name)) doppelt.push(name); else wege.set(name, true);
|
||||||
|
}
|
||||||
|
} else if (schicht.handle?.stack) {
|
||||||
|
for (const s of schicht.handle.stack) sammeln(s, vorn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const stapel = app?._router?.stack || app?.router?.stack || [];
|
||||||
|
for (const s of stapel) sammeln(s);
|
||||||
|
/* DIE ZAHL GEHOERT IN DIE BEDINGUNG. Faende diese Messung gar keine
|
||||||
|
Wege (weil express seinen Baum eines Tages anders ablegt), waere
|
||||||
|
„keine Dubletten" trivial wahr -- und bliebe es fuer immer. */
|
||||||
|
ok(wege.size > 200, `${wege.size} Wege im Baum gefunden`);
|
||||||
|
ok(doppelt.length === 0,
|
||||||
|
doppelt.length ? `doppelt vergeben: ${[...new Set(doppelt)].join(", ")}`
|
||||||
|
: "kein Paar aus Methode und Pfad kommt zweimal vor");
|
||||||
|
/* GEGENPROBE: Die Messung MUSS eine Dublette finden koennen. */
|
||||||
|
const probe = new Map(); const gefunden = [];
|
||||||
|
for (const n of ["GET /x", "GET /x"]) {
|
||||||
|
if (probe.has(n)) gefunden.push(n); else probe.set(n, true);
|
||||||
|
}
|
||||||
|
ok(gefunden.length === 1, "Gegenprobe: zwei gleiche Wege wuerden auffallen");
|
||||||
|
}
|
||||||
|
|
||||||
|
ok(konsole.length === 0, `keine Fehler in der Konsole (${konsole.slice(0, 2).join(" | ") || "keine"})`);
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
await new Promise((r) => vorbau.close(r));
|
||||||
|
|
||||||
|
melde("");
|
||||||
|
melde(`${geprueft} Pruefungen, ${fehler} Fehler`);
|
||||||
|
melde(fehler ? "FEHLGESCHLAGEN" : "ALLES IN ORDNUNG");
|
||||||
|
process.exit(fehler ? 1 : 0);
|
||||||
@@ -503,6 +503,28 @@ export const SEITEN = {
|
|||||||
Weiterhin ausdruecklich NICHT fuer die Agentur und nicht fuer die
|
Weiterhin ausdruecklich NICHT fuer die Agentur und nicht fuer die
|
||||||
Modis: Die Seite ist eine vollstaendige Karte dieses Hauses -- jede
|
Modis: Die Seite ist eine vollstaendige Karte dieses Hauses -- jede
|
||||||
Seite, jede Rolle. Wer sie hat, weiss, wo er es versuchen muesste. */
|
Seite, jede Rolle. Wer sie hat, weiss, wo er es versuchen muesste. */
|
||||||
|
/* DER NOTIZBLOCK (26.09.2026).
|
||||||
|
|
||||||
|
Filipe: „fuer die modis, rechte und linke hand und dogfather
|
||||||
|
eine kachel hinzufuegst, sie soll: Notizen, heissen."
|
||||||
|
|
||||||
|
GENAU DIESE VIER, und die linke Hand steht ausdruecklich dabei
|
||||||
|
statt sich aus der Schleife weiter unten zu ergeben: Sie ist
|
||||||
|
hier namentlich genannt, und eine namentliche Nennung gehoert
|
||||||
|
in die Tafel, die man liest, wenn man wissen will, wer wohin
|
||||||
|
darf.
|
||||||
|
|
||||||
|
WAS AUF DER SEITE ZU SEHEN IST, entscheidet nicht diese Zeile:
|
||||||
|
In workspace-notizen.js hat jede Abfrage `person_id = ?` fest
|
||||||
|
eingebaut. Es gibt keine Rolle, die mehr sieht -- auch
|
||||||
|
DogFather nicht. Diese Tafel beantwortet nur, wer die Tuer
|
||||||
|
aufbekommt.
|
||||||
|
|
||||||
|
DIE AGENTUR STEHT NICHT DABEI, und die Adresse auch nicht: Die
|
||||||
|
Seite gehoert zum Haus von Team Dogi und ist auf der
|
||||||
|
Agenturadresse ein 404 (GEHOERT_ZU_ADRESSE in crew-adresse.js). */
|
||||||
|
"/workspace/notizen.html": ["admin", "hand", "linke", "modi"],
|
||||||
|
|
||||||
"/workspace/rechte.html": ["admin", "hand"],
|
"/workspace/rechte.html": ["admin", "hand"],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ import express from "express";
|
|||||||
import { AUFBEWAHRUNG_TAGE as HILFE_TAGE, FALL_STAND } from "./hilfe-tabellen.js";
|
import { AUFBEWAHRUNG_TAGE as HILFE_TAGE, FALL_STAND } from "./hilfe-tabellen.js";
|
||||||
import { AUFBEWAHRUNG_TAGE as SUPPORT_TAGE } from "./support-tabellen.js";
|
import { AUFBEWAHRUNG_TAGE as SUPPORT_TAGE } from "./support-tabellen.js";
|
||||||
import { supportAufraeumen } from "./workspace-support.js";
|
import { supportAufraeumen } from "./workspace-support.js";
|
||||||
|
import { notizenAufraeumen } from "./workspace-notizen.js";
|
||||||
|
import { PAPIERKORB_TAGE as NOTIZ_TAGE } from "./notiz-tabellen.js";
|
||||||
import { ZAEHLUNG_TAGE } from "./unterstuetzung-tabellen.js";
|
import { ZAEHLUNG_TAGE } from "./unterstuetzung-tabellen.js";
|
||||||
import { unterstuetzungAufraeumen } from "./workspace-unterstuetzung.js";
|
import { unterstuetzungAufraeumen } from "./workspace-unterstuetzung.js";
|
||||||
import {
|
import {
|
||||||
@@ -299,6 +301,28 @@ export const FRISTEN = [
|
|||||||
lassen — und ein Bild ohne Zeile findet danach niemand mehr. */
|
lassen — und ein Bild ohne Zeile findet danach niemand mehr. */
|
||||||
raeumen: () => supportAufraeumen(),
|
raeumen: () => supportAufraeumen(),
|
||||||
},
|
},
|
||||||
|
/* ==== DER PAPIERKORB DES NOTIZBLOCKS (26.09.2026) ==============
|
||||||
|
|
||||||
|
Der Eintrag entsteht ZUSAMMEN mit der Frist und nicht danach.
|
||||||
|
Am 24.09. ist genau das schiefgegangen: `supportAufraeumen()`
|
||||||
|
war geschrieben, exportiert und dokumentiert -- und wurde
|
||||||
|
nirgends aufgerufen. Im Kopf der Datei stand „90 Tage", und
|
||||||
|
geräumt wurde nie. */
|
||||||
|
{
|
||||||
|
schluessel: "notizen_papierkorb",
|
||||||
|
was: "Weggelegte Notizblätter",
|
||||||
|
art: "raeumen",
|
||||||
|
frist: `${NOTIZ_TAGE} Tage im Papierkorb`,
|
||||||
|
zweck: "Ein versehentlich weggetipptes Blatt zurückholen können.",
|
||||||
|
grundlage: "Art. 5 Abs. 1 lit. e DSGVO – Speicherbegrenzung",
|
||||||
|
wirkung: `Das Blatt verschwindet ganz. Was auf dem Block liegt, wird `
|
||||||
|
+ `nie geräumt – nur was jemand selbst weggelegt hat, und erst `
|
||||||
|
+ `${NOTIZ_TAGE} Tage danach.`,
|
||||||
|
offen: (d) => zaehle(d,
|
||||||
|
"SELECT COUNT(*) AS n FROM notizen WHERE weg_am IS NOT NULL AND weg_am < ?",
|
||||||
|
vorTagen(NOTIZ_TAGE)),
|
||||||
|
raeumen: () => notizenAufraeumen(),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
schluessel: "unterstuetzung_striche",
|
schluessel: "unterstuetzung_striche",
|
||||||
was: "Tagesstriche der Unterstützen-Seite",
|
was: "Tagesstriche der Unterstützen-Seite",
|
||||||
|
|||||||
@@ -0,0 +1,402 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
DER NOTIZBLOCK — die Wege (26.09.2026)
|
||||||
|
|
||||||
|
Die Regeln und ihre Begründung stehen im Kopf von
|
||||||
|
notiz-tabellen.js. Hier sind die Routen — und die vier Stellen, an
|
||||||
|
denen etwas schiefgehen kann:
|
||||||
|
|
||||||
|
1. EINE NOTIZ DARF NIE BEIM FALSCHEN LANDEN. Jede Abfrage hat
|
||||||
|
`person_id = ?` fest eingebaut. Es gibt keine Rolle, die mehr
|
||||||
|
sieht, und deshalb auch keinen Zweig, der die Bedingung
|
||||||
|
weglassen könnte. `blattFuer()` gibt den Datensatz zurück oder
|
||||||
|
`null` — nie `true`/`false`. So kann keine Route erst „darf
|
||||||
|
sehen" beantworten und danach trotzdem das falsche Blatt laden.
|
||||||
|
|
||||||
|
2. NUR AUF DER TEAM-ADRESSE. Der Block gehört zum Haus von Team
|
||||||
|
Dogi. Auf der Agenturadresse ist er ein 404 — auch für
|
||||||
|
DogFather, der in beiden Häusern arbeitet. Das ist dieselbe
|
||||||
|
Trennung wie bei Chat, Kalender und Aufgaben, und sie wird hier
|
||||||
|
geprüft und nicht nur in der Rechtetafel: Die Tafel beantwortet,
|
||||||
|
wer die SEITE öffnen darf, nicht von welcher Adresse aus.
|
||||||
|
|
||||||
|
3. GESPEICHERT WIRD OHNE KNOPF. Die Oberfläche schickt beim Tippen
|
||||||
|
alle 800 ms. Das ist viel Verkehr für wenig Inhalt — deshalb
|
||||||
|
antwortet PATCH knapp (nur Zeitstempel), und es gibt eine
|
||||||
|
Schreibbremse, die ein hängendes Skript stoppt, ohne einen
|
||||||
|
schnell Tippenden zu treffen.
|
||||||
|
|
||||||
|
4. ENDGÜLTIG IST ENDGÜLTIG. Löschen legt ins Papierkorbfach;
|
||||||
|
erst das zweite Löschen entfernt wirklich, und nur von dort.
|
||||||
|
Ein direkter Weg von „auf dem Block" zu „weg" existiert nicht.
|
||||||
|
===================================================================== */
|
||||||
|
|
||||||
|
import express from "express";
|
||||||
|
import { db, sitzungLesen, protokolliere, echteIp } from "./workspace.js";
|
||||||
|
import { PAPIERKORB_TAGE, TEXT_MAX, BLAETTER_MAX, FARBEN, FARB_NAMEN }
|
||||||
|
from "./notiz-tabellen.js";
|
||||||
|
|
||||||
|
export const notizenRouter = express.Router();
|
||||||
|
|
||||||
|
/** Wer einen Block hat.
|
||||||
|
*
|
||||||
|
* Genau die vier Rollen aus Filipes Satz. Die Liste steht hier UND
|
||||||
|
* in rechte.js, und das ist kein Versehen: Die Tafel bewacht die
|
||||||
|
* Seite, diese Menge die Daten. Wer nur die Tafel prüfte, hätte eine
|
||||||
|
* Schnittstelle, die jeder Angemeldete aufrufen kann — die Seite ist
|
||||||
|
* nur HTML, die Daten sind die Sache.
|
||||||
|
*
|
||||||
|
* pruef-notizen hält beide gegeneinander und wird rot, wenn sie
|
||||||
|
* auseinanderlaufen. */
|
||||||
|
const MIT_BLOCK = new Set(["admin", "hand", "linke", "modi"]);
|
||||||
|
|
||||||
|
const jetzt = () => new Date().toISOString();
|
||||||
|
|
||||||
|
/* ---------- Schranken --------------------------------------------------- */
|
||||||
|
|
||||||
|
function angemeldet(req, res, next) {
|
||||||
|
const person = sitzungLesen(req);
|
||||||
|
if (!person) return res.status(401).json({ fehler: "nicht_angemeldet" });
|
||||||
|
req.person = person;
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Team-Adresse und passende Rolle — sonst gibt es diese Seite nicht.
|
||||||
|
*
|
||||||
|
* 404 UND NICHT 403: Ein „verboten" verrät, dass es hier etwas gibt.
|
||||||
|
* Auf der Agenturadresse soll der Block nicht einmal als verschlossene
|
||||||
|
* Tür sichtbar sein. */
|
||||||
|
function nurTeamDogi(req, res, next) {
|
||||||
|
if (req.person?.haus !== "crew") return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||||
|
if (!MIT_BLOCK.has(req.person?.rolle)) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Schutz gegen Anfragen von fremden Seiten -- wortgleich zu den
|
||||||
|
übrigen Modulen. Fehlt der Kopf ganz (curl), ist es kein
|
||||||
|
Browser-Angriff über eine fremde Seite; dann zählt allein das
|
||||||
|
Cookie. */
|
||||||
|
function gleicheHerkunft(req, res, next) {
|
||||||
|
const herkunft = req.get("origin");
|
||||||
|
if (!herkunft) return next();
|
||||||
|
let erlaubt;
|
||||||
|
try { erlaubt = new URL(herkunft).host === req.get("host"); } catch { erlaubt = false; }
|
||||||
|
if (!erlaubt) return res.status(403).json({ fehler: "fremde_herkunft" });
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==== DIE SCHREIBBREMSE ================================================
|
||||||
|
|
||||||
|
Automatisches Speichern heißt: Bei jedem Tippen kann eine Anfrage
|
||||||
|
kommen. Die Oberfläche wartet 800 ms Ruhe ab, ein schneller
|
||||||
|
Schreiber löst also höchstens rund 75 Anfragen je Minute aus, und
|
||||||
|
das auch nur, wenn er die ganze Minute durchtippt.
|
||||||
|
|
||||||
|
DIE GRENZE IST DAS VIERFACHE DAVON. Sie steht nicht gegen den
|
||||||
|
Menschen, sondern gegen ein Skript, das in einer Schleife hängt --
|
||||||
|
und sie ist absichtlich so hoch, dass niemand sie im Alltag je
|
||||||
|
sieht. Eine Bremse, die beim normalen Arbeiten greift, wird
|
||||||
|
umgangen und ist ab da wirkungslos (siehe die Hausregel dazu).
|
||||||
|
|
||||||
|
Im Speicher und nicht in der Datenbank: Ein Neustart setzt sie
|
||||||
|
zurück, und das ist richtig -- sie schützt vor einem laufenden
|
||||||
|
Fehler, nicht vor einem Angreifer mit Geduld. */
|
||||||
|
const SCHREIB_FENSTER_MS = 60_000;
|
||||||
|
const SCHREIB_MAX = 300;
|
||||||
|
const schreibZaehler = new Map(); // personId -> [Zeitpunkte]
|
||||||
|
|
||||||
|
function schreibBremse(req, res, next) {
|
||||||
|
const id = req.person?.id;
|
||||||
|
if (!id) return next();
|
||||||
|
const jetztMs = Date.now();
|
||||||
|
const liste = (schreibZaehler.get(id) || []).filter((t) => jetztMs - t < SCHREIB_FENSTER_MS);
|
||||||
|
if (liste.length >= SCHREIB_MAX) {
|
||||||
|
return res.status(429).json({
|
||||||
|
fehler: "Zu viele Änderungen in kurzer Zeit. Warte einen Moment — "
|
||||||
|
+ "dein Text ist nicht verloren, er wird gleich erneut gesichert.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
liste.push(jetztMs);
|
||||||
|
schreibZaehler.set(id, liste);
|
||||||
|
/* Damit die Karte nicht ewig wächst: Wer lange nichts geschrieben
|
||||||
|
hat, fliegt beim nächsten Durchgang heraus. */
|
||||||
|
if (schreibZaehler.size > 500) {
|
||||||
|
for (const [k, v] of schreibZaehler) {
|
||||||
|
if (!v.some((t) => jetztMs - t < SCHREIB_FENSTER_MS)) schreibZaehler.delete(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
notizenRouter.use("/workspace/api/notizblock", angemeldet, nurTeamDogi);
|
||||||
|
|
||||||
|
/* ---------- Hilfen ------------------------------------------------------ */
|
||||||
|
|
||||||
|
/** Ein Blatt dieser Person — oder null.
|
||||||
|
*
|
||||||
|
* Gibt den DATENSATZ zurück und nicht ein Ja/Nein: Eine Route, die
|
||||||
|
* erst fragt „darf ich?" und danach selbst lädt, kann die zweite
|
||||||
|
* Abfrage anders stellen als die erste. So kann sie es nicht. */
|
||||||
|
function blattFuer(person, id, { ausPapierkorb = false } = {}) {
|
||||||
|
const n = Number(id);
|
||||||
|
if (!Number.isInteger(n) || n <= 0) return null;
|
||||||
|
const zeile = db().prepare("SELECT * FROM notizen WHERE id = ? AND person_id = ?")
|
||||||
|
.get(n, person.id);
|
||||||
|
if (!zeile) return null;
|
||||||
|
if (ausPapierkorb ? !zeile.weg_am : zeile.weg_am) return null;
|
||||||
|
return zeile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Mehrzeiliger Text: Absätze bleiben, Leerzeilenwüsten nicht.
|
||||||
|
*
|
||||||
|
* Kein `trim()` am Anfang jeder Zeile — Einrückung ist in einer
|
||||||
|
* Notiz Inhalt („ - Unterpunkt"). Entfernt werden nur Wagenrückläufe
|
||||||
|
* (damit die Länge überall dieselbe ist) und mehr als zwei leere
|
||||||
|
* Zeilen am Stück. */
|
||||||
|
function textSauber(s) {
|
||||||
|
return String(s ?? "")
|
||||||
|
.replace(/\r\n?/g, "\n")
|
||||||
|
.replace(/\n{4,}/g, "\n\n\n")
|
||||||
|
.slice(0, TEXT_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Die erste nicht leere Zeile, ohne ihre Auszeichnung.
|
||||||
|
*
|
||||||
|
* Wird NUR für die Liste gebraucht (und für die Suche). Der Titel
|
||||||
|
* wird nicht gespeichert: Zwei Orte für dieselbe Sache laufen
|
||||||
|
* auseinander, sobald jemand die erste Zeile ändert und das Feld
|
||||||
|
* nicht. Hier wird er jedes Mal aus dem Text abgeleitet und ist
|
||||||
|
* damit immer der, der wirklich oben steht. */
|
||||||
|
function titelVon(text) {
|
||||||
|
const zeile = String(text || "").split("\n").find((z) => z.trim().length) || "";
|
||||||
|
return zeile
|
||||||
|
.replace(/^\s*#{1,3}\s*/, "")
|
||||||
|
.replace(/^\s*[-*]\s+/, "")
|
||||||
|
.replace(/^\s*\[[ xX]\]\s*/, "")
|
||||||
|
.trim()
|
||||||
|
.slice(0, 120);
|
||||||
|
}
|
||||||
|
|
||||||
|
function nachDraussen(z) {
|
||||||
|
return {
|
||||||
|
id: z.id,
|
||||||
|
text: z.text,
|
||||||
|
titel: titelVon(z.text),
|
||||||
|
farbe: z.farbe,
|
||||||
|
angeheftet: !!z.angeheftet,
|
||||||
|
erstellt: z.erstellt,
|
||||||
|
geaendert: z.geaendert,
|
||||||
|
weg_am: z.weg_am || null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Lesen -------------------------------------------------------- */
|
||||||
|
|
||||||
|
/** Der Block: alle eigenen Blätter, angeheftete zuerst. */
|
||||||
|
notizenRouter.get("/workspace/api/notizblock", (req, res) => {
|
||||||
|
try {
|
||||||
|
const zeilen = db().prepare(`
|
||||||
|
SELECT * FROM notizen
|
||||||
|
WHERE person_id = ? AND weg_am IS NULL
|
||||||
|
ORDER BY angeheftet DESC, geaendert DESC
|
||||||
|
LIMIT ?`).all(req.person.id, BLAETTER_MAX);
|
||||||
|
/* DIE ZAHL KOMMT AUS DER DATENBANK, nicht aus der Liste oben: Die
|
||||||
|
ist bei BLAETTER_MAX abgeschnitten, und eine Zählung darauf
|
||||||
|
wäre ab dem 301. Blatt still falsch. */
|
||||||
|
const imKorb = db().prepare(`SELECT COUNT(*) AS n FROM notizen
|
||||||
|
WHERE person_id = ? AND weg_am IS NOT NULL`).get(req.person.id).n;
|
||||||
|
res.json({
|
||||||
|
notizen: zeilen.map(nachDraussen),
|
||||||
|
papierkorb: imKorb,
|
||||||
|
text_max: TEXT_MAX,
|
||||||
|
blaetter_max: BLAETTER_MAX,
|
||||||
|
papierkorb_tage: PAPIERKORB_TAGE,
|
||||||
|
farben: FARBEN,
|
||||||
|
});
|
||||||
|
} catch (fehler) {
|
||||||
|
console.error("[notizen] Liste:", fehler?.message);
|
||||||
|
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Was im Papierkorb liegt — mit dem Tag, an dem es von selbst geht. */
|
||||||
|
notizenRouter.get("/workspace/api/notizblock/papierkorb", (req, res) => {
|
||||||
|
try {
|
||||||
|
const zeilen = db().prepare(`
|
||||||
|
SELECT * FROM notizen
|
||||||
|
WHERE person_id = ? AND weg_am IS NOT NULL
|
||||||
|
ORDER BY weg_am DESC LIMIT ?`).all(req.person.id, BLAETTER_MAX);
|
||||||
|
res.json({
|
||||||
|
notizen: zeilen.map((z) => ({
|
||||||
|
...nachDraussen(z),
|
||||||
|
/* WANN ES WIRKLICH WEG IST -- gerechnet und nicht „in 30
|
||||||
|
Tagen" geschrieben. Wer gestern geloescht hat, will wissen,
|
||||||
|
wie viel Zeit er noch hat, und nicht, wie lang die Frist
|
||||||
|
ist. */
|
||||||
|
endgueltig_am: new Date(Date.parse(z.weg_am) + PAPIERKORB_TAGE * 86400000)
|
||||||
|
.toISOString(),
|
||||||
|
})),
|
||||||
|
papierkorb_tage: PAPIERKORB_TAGE,
|
||||||
|
});
|
||||||
|
} catch (fehler) {
|
||||||
|
console.error("[notizen] Papierkorb:", fehler?.message);
|
||||||
|
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ---------- Schreiben ----------------------------------------------------- */
|
||||||
|
|
||||||
|
/** Ein neues Blatt. */
|
||||||
|
notizenRouter.post("/workspace/api/notizblock", gleicheHerkunft, schreibBremse,
|
||||||
|
express.json({ limit: "256kb" }), (req, res) => {
|
||||||
|
try {
|
||||||
|
const offen = db().prepare(`SELECT COUNT(*) AS n FROM notizen
|
||||||
|
WHERE person_id = ? AND weg_am IS NULL`).get(req.person.id).n;
|
||||||
|
if (offen >= BLAETTER_MAX) {
|
||||||
|
return res.status(429).json({
|
||||||
|
fehler: `Dein Block hat ${BLAETTER_MAX} Blätter — mehr gehen nicht. `
|
||||||
|
+ "Räum ein paar in den Papierkorb, dann geht es weiter.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const text = textSauber(req.body?.text);
|
||||||
|
const farbe = FARB_NAMEN.includes(req.body?.farbe) ? req.body.farbe : FARB_NAMEN[0];
|
||||||
|
const n = jetzt();
|
||||||
|
const { lastInsertRowid } = db().prepare(`
|
||||||
|
INSERT INTO notizen (person_id, text, farbe, angeheftet, erstellt, geaendert)
|
||||||
|
VALUES (?,?,?,0,?,?)`).run(req.person.id, text, farbe, n, n);
|
||||||
|
const frisch = db().prepare("SELECT * FROM notizen WHERE id = ?").get(lastInsertRowid);
|
||||||
|
res.status(201).json({ notiz: nachDraussen(frisch) });
|
||||||
|
} catch (fehler) {
|
||||||
|
console.error("[notizen] Anlegen:", fehler?.message);
|
||||||
|
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Ändern: Text, Farbe, angeheftet. Einzeln oder zusammen.
|
||||||
|
*
|
||||||
|
* ANTWORTET KNAPP. Diese Route läuft beim Tippen alle 800 ms; das
|
||||||
|
* ganze Blatt zurückzuschicken wäre bei einer langen Notiz
|
||||||
|
* zwanzigtausend Zeichen je Tastendruckpause, für eine Information,
|
||||||
|
* die der Browser schon hat. Zurück kommt der Zeitstempel — das
|
||||||
|
* Einzige, was er NICHT weiß. */
|
||||||
|
notizenRouter.patch("/workspace/api/notizblock/:id(\\d+)", gleicheHerkunft, schreibBremse,
|
||||||
|
express.json({ limit: "256kb" }), (req, res) => {
|
||||||
|
try {
|
||||||
|
const z = blattFuer(req.person, req.params.id);
|
||||||
|
if (!z) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||||
|
|
||||||
|
const felder = [], werte = [];
|
||||||
|
if (req.body?.text !== undefined) {
|
||||||
|
felder.push("text = ?"); werte.push(textSauber(req.body.text));
|
||||||
|
}
|
||||||
|
if (req.body?.farbe !== undefined) {
|
||||||
|
if (!FARB_NAMEN.includes(req.body.farbe)) {
|
||||||
|
return res.status(400).json({ fehler: "Diese Papierfarbe gibt es nicht." });
|
||||||
|
}
|
||||||
|
felder.push("farbe = ?"); werte.push(req.body.farbe);
|
||||||
|
}
|
||||||
|
if (req.body?.angeheftet !== undefined) {
|
||||||
|
felder.push("angeheftet = ?"); werte.push(req.body.angeheftet ? 1 : 0);
|
||||||
|
}
|
||||||
|
if (!felder.length) return res.status(400).json({ fehler: "Nichts zu ändern." });
|
||||||
|
|
||||||
|
const n = jetzt();
|
||||||
|
felder.push("geaendert = ?"); werte.push(n);
|
||||||
|
werte.push(z.id, req.person.id);
|
||||||
|
db().prepare(`UPDATE notizen SET ${felder.join(", ")}
|
||||||
|
WHERE id = ? AND person_id = ?`).run(...werte);
|
||||||
|
res.json({ geaendert: n });
|
||||||
|
} catch (fehler) {
|
||||||
|
console.error("[notizen] Aendern:", fehler?.message);
|
||||||
|
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/** In den Papierkorb. */
|
||||||
|
notizenRouter.delete("/workspace/api/notizblock/:id(\\d+)", gleicheHerkunft, schreibBremse,
|
||||||
|
(req, res) => {
|
||||||
|
try {
|
||||||
|
const z = blattFuer(req.person, req.params.id);
|
||||||
|
if (!z) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||||
|
const n = jetzt();
|
||||||
|
db().prepare("UPDATE notizen SET weg_am = ?, angeheftet = 0 WHERE id = ? AND person_id = ?")
|
||||||
|
.run(n, z.id, req.person.id);
|
||||||
|
res.json({ weg_am: n, papierkorb_tage: PAPIERKORB_TAGE });
|
||||||
|
} catch (fehler) {
|
||||||
|
console.error("[notizen] Wegtun:", fehler?.message);
|
||||||
|
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Wieder heraus. */
|
||||||
|
notizenRouter.post("/workspace/api/notizblock/:id(\\d+)/zurueck", gleicheHerkunft, schreibBremse,
|
||||||
|
(req, res) => {
|
||||||
|
try {
|
||||||
|
const z = blattFuer(req.person, req.params.id, { ausPapierkorb: true });
|
||||||
|
if (!z) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||||
|
const offen = db().prepare(`SELECT COUNT(*) AS n FROM notizen
|
||||||
|
WHERE person_id = ? AND weg_am IS NULL`).get(req.person.id).n;
|
||||||
|
if (offen >= BLAETTER_MAX) {
|
||||||
|
return res.status(429).json({
|
||||||
|
fehler: `Dein Block ist voll (${BLAETTER_MAX} Blätter). `
|
||||||
|
+ "Tu erst eines weg, dann kommt dieses zurück.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
db().prepare("UPDATE notizen SET weg_am = NULL, geaendert = ? WHERE id = ? AND person_id = ?")
|
||||||
|
.run(jetzt(), z.id, req.person.id);
|
||||||
|
const frisch = db().prepare("SELECT * FROM notizen WHERE id = ?").get(z.id);
|
||||||
|
res.json({ notiz: nachDraussen(frisch) });
|
||||||
|
} catch (fehler) {
|
||||||
|
console.error("[notizen] Zurueck:", fehler?.message);
|
||||||
|
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Endgültig — und nur aus dem Papierkorb.
|
||||||
|
*
|
||||||
|
* Es gibt keinen Weg von „auf dem Block" direkt zu „weg". Wer
|
||||||
|
* danebentippt, landet im Papierkorb; erst wer dort noch einmal
|
||||||
|
* drückt, meint es. */
|
||||||
|
notizenRouter.delete("/workspace/api/notizblock/:id(\\d+)/endgueltig", gleicheHerkunft,
|
||||||
|
schreibBremse, (req, res) => {
|
||||||
|
try {
|
||||||
|
const z = blattFuer(req.person, req.params.id, { ausPapierkorb: true });
|
||||||
|
if (!z) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||||
|
db().prepare("DELETE FROM notizen WHERE id = ? AND person_id = ?")
|
||||||
|
.run(z.id, req.person.id);
|
||||||
|
/* PROTOKOLLIERT WIRD NUR DAS UNWIDERRUFLICHE -- und ohne eine
|
||||||
|
Silbe des Inhalts. Jede Textänderung zu protokollieren würde
|
||||||
|
aus einem privaten Block eine Aktivitätsspur machen, die
|
||||||
|
andere lesen können; genau das soll er nicht sein. Dass etwas
|
||||||
|
endgültig verschwunden ist, gehört dagegen in die Ablage:
|
||||||
|
Sonst sucht jemand eines Tages eine Zeile, die niemand
|
||||||
|
vermisst hat. */
|
||||||
|
protokolliere("notiz_endgueltig_weg", {
|
||||||
|
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
|
||||||
|
detail: `#${z.id}`,
|
||||||
|
});
|
||||||
|
res.json({ weg: true });
|
||||||
|
} catch (fehler) {
|
||||||
|
console.error("[notizen] Endgueltig:", fehler?.message);
|
||||||
|
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
AUFRAEUMEN
|
||||||
|
|
||||||
|
Wird aus der Aufbewahrungsliste gerufen (workspace-aufbewahrung.js)
|
||||||
|
und nirgends sonst. Das ist die einzige Stelle im Haus, die
|
||||||
|
wirklich räumt — eine Frist ohne Eintrag dort ist kein Versprechen,
|
||||||
|
sondern ein Satz in einem Kommentar.
|
||||||
|
===================================================================== */
|
||||||
|
export function notizenAufraeumen() {
|
||||||
|
const grenze = new Date(Date.now() - PAPIERKORB_TAGE * 86400000).toISOString();
|
||||||
|
const weg = db().prepare("DELETE FROM notizen WHERE weg_am IS NOT NULL AND weg_am < ?")
|
||||||
|
.run(grenze);
|
||||||
|
return Number(weg?.changes || 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Für die Prüfung: dieselbe Menge, die `nurTeamDogi` benutzt. Sie
|
||||||
|
wird exportiert, damit pruef-notizen sie gegen die Rechtetafel
|
||||||
|
halten kann, statt sie abzuschreiben. */
|
||||||
|
export const ROLLEN_MIT_BLOCK = MIT_BLOCK;
|
||||||
@@ -140,6 +140,9 @@ const LANG = {
|
|||||||
"hilfe.html":
|
"hilfe.html":
|
||||||
"Wenn etwas vorgefallen ist, das niemand mitlesen soll. Nur DogFather "
|
"Wenn etwas vorgefallen ist, das niemand mitlesen soll. Nur DogFather "
|
||||||
+ "und die rechte Hand sehen diese Meldungen.",
|
+ "und die rechte Hand sehen diese Meldungen.",
|
||||||
|
"notizen.html":
|
||||||
|
"Dein Block. Schreib hinein, was dir einfällt — es speichert sich von "
|
||||||
|
+ "selbst, und niemand außer dir sieht es. Auch DogFather nicht.",
|
||||||
"support.html":
|
"support.html":
|
||||||
"Etwas an der Seite funktioniert nicht oder sieht falsch aus? Schreib "
|
"Etwas an der Seite funktioniert nicht oder sieht falsch aus? Schreib "
|
||||||
+ "einen Satz dazu und häng ein Bildschirmfoto an — DogFather und die "
|
+ "einen Satz dazu und häng ein Bildschirmfoto an — DogFather und die "
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import { darfSeite, OHNE_ANMELDUNG, seitenFuer } from "./rechte.js";
|
|||||||
import { treffTabellen, treffSperreLesen, MINDESTALTER } from "./treff-tabellen.js";
|
import { treffTabellen, treffSperreLesen, MINDESTALTER } from "./treff-tabellen.js";
|
||||||
import { hilfeTabellen } from "./hilfe-tabellen.js";
|
import { hilfeTabellen } from "./hilfe-tabellen.js";
|
||||||
import { supportTabellen } from "./support-tabellen.js";
|
import { supportTabellen } from "./support-tabellen.js";
|
||||||
|
import { notizTabellen } from "./notiz-tabellen.js";
|
||||||
import { unterstuetzungTabellen } from "./unterstuetzung-tabellen.js";
|
import { unterstuetzungTabellen } from "./unterstuetzung-tabellen.js";
|
||||||
import { sitzungPasstZurAdresse, istOhneModiAdresse, istCrewAdresse, istPruefAdresse, AUSSEN_ROLLEN,
|
import { sitzungPasstZurAdresse, istOhneModiAdresse, istCrewAdresse, istPruefAdresse, AUSSEN_ROLLEN,
|
||||||
TEAM_DOGI_ROLLEN, CREW_ADRESSE, WORKSPACE_ADRESSE, AGENTUR_ROLLEN, BEIDE_HAEUSER_ROLLEN,
|
TEAM_DOGI_ROLLEN, CREW_ADRESSE, WORKSPACE_ADRESSE, AGENTUR_ROLLEN, BEIDE_HAEUSER_ROLLEN,
|
||||||
@@ -1049,6 +1050,49 @@ const MODI_BEREICHE = [
|
|||||||
{ gruppe: "Für dich", gruppeUnter: "Deine Seite im Team",
|
{ gruppe: "Für dich", gruppeUnter: "Deine Seite im Team",
|
||||||
name: "Mein Steckbrief", unter: "Dein Bild und deine Kanäle", zeichen: "steckbrief",
|
name: "Mein Steckbrief", unter: "Dein Bild und deine Kanäle", zeichen: "steckbrief",
|
||||||
ton: 10, ziel: "steckbrief.html", szene: "halle" },
|
ton: 10, ziel: "steckbrief.html", szene: "halle" },
|
||||||
|
|
||||||
|
/* =================================================================
|
||||||
|
DER NOTIZBLOCK (26.09.2026)
|
||||||
|
|
||||||
|
Filipe: „fuer die modis, rechte und linke hand und dogfather
|
||||||
|
eine kachel hinzufuegst, sie soll: Notizen, heissen. ich will
|
||||||
|
dass du das auch wie ein notizblock erstellst."
|
||||||
|
|
||||||
|
GENAU DIESE VIER ROLLEN, und dafuer muss hier nichts aufgezaehlt
|
||||||
|
werden: MODI_BEREICHE ist die Grundliste, aus der HAND_BEREICHE
|
||||||
|
durch Erweitern entsteht und LINKE_MIT_TREFF durch Wegnehmen
|
||||||
|
einer einzigen Kachel. Wer hier eintraegt, trifft Modi, rechte
|
||||||
|
Hand, linke Hand und DogFather auf der Team-Adresse -- und
|
||||||
|
sonst niemanden. Eine eigene Liste waere die, die beim naechsten
|
||||||
|
Rollenzuschnitt auseinanderlaeuft.
|
||||||
|
|
||||||
|
ER GEHOERT ZU „FUER DICH" UND NICHT ZU „TAEGLICH", obwohl man
|
||||||
|
ihn taeglich aufmacht. Die Gruppe sagt die wichtigste
|
||||||
|
Eigenschaft, bevor man die Kachel anfasst: Diese Notizen sieht
|
||||||
|
NIEMAND ausser dem, der sie schreibt -- auch DogFather nicht.
|
||||||
|
Stuende die Kachel zwischen Chat und Kalender, waere die erste
|
||||||
|
Frage „wer liest das mit?", und die Antwort stuende erst auf der
|
||||||
|
Seite.
|
||||||
|
|
||||||
|
NUR AUF DER TEAM-ADRESSE. Die Seite steht in
|
||||||
|
GEHOERT_ZU_ADRESSE (crew-adresse.js) und ist auf der
|
||||||
|
Agenturadresse ein 404 -- auch fuer DogFather, der in beiden
|
||||||
|
Haeusern arbeitet. Das ist dieselbe Trennung wie bei Chat,
|
||||||
|
Kalender und Aufgaben: Auf jeder Adresse sieht er nur deren
|
||||||
|
Bestand.
|
||||||
|
|
||||||
|
TON 46 UND DIE OBERFLAECHE `papier`. Der Ton ist die groesste
|
||||||
|
freie Luecke der Palette; warum es kein Bernstein wurde, steht
|
||||||
|
mit allen Messungen in tools/kachelton-regeln.mjs. Das
|
||||||
|
Papiergefuehl traegt nicht die Farbe, sondern die OBERFLAECHE --
|
||||||
|
Linien und ein perforierter Rand, so wie `silber` bei den
|
||||||
|
Aufgaben und `regenbogen` bei Willkommen. Faellt sie aus (alter
|
||||||
|
Zwischenspeicher, Kontrastmodus), bleibt die Kachel in ihrer
|
||||||
|
Farbe und damit unterscheidbar. */
|
||||||
|
{ gruppe: "Für dich", gruppeUnter: "Deine Seite im Team",
|
||||||
|
name: "Notizen", unter: "Dein Block – nur du siehst ihn",
|
||||||
|
zeichen: "notizen", ton: 46, papier: true,
|
||||||
|
ziel: "notizen.html", szene: "halle" },
|
||||||
/* ==== DIE WISSENS-KACHEL IST HIER RAUS (24.09.2026) =============
|
/* ==== DIE WISSENS-KACHEL IST HIER RAUS (24.09.2026) =============
|
||||||
|
|
||||||
Filipe, zur Frage, wohin die neun Dokumente gehören: „diese
|
Filipe, zur Frage, wohin die neun Dokumente gehören: „diese
|
||||||
@@ -5990,6 +6034,9 @@ export function db() {
|
|||||||
kommt deshalb hierher, nicht in den Bauplan darueber. */
|
kommt deshalb hierher, nicht in den Bauplan darueber. */
|
||||||
hilfeTabellen(d);
|
hilfeTabellen(d);
|
||||||
supportTabellen(d);
|
supportTabellen(d);
|
||||||
|
/* Der Notizblock (26.09.2026). Haengt an personen (CASCADE) und
|
||||||
|
kommt deshalb hierher, nicht in den Bauplan darueber. */
|
||||||
|
notizTabellen(d);
|
||||||
/* Die Wege zum Unterstützen (24.09.2026). Sie hängen an personen
|
/* Die Wege zum Unterstützen (24.09.2026). Sie hängen an personen
|
||||||
(geaendert_von) und kommen deshalb hierher, nicht in den Bauplan
|
(geaendert_von) und kommen deshalb hierher, nicht in den Bauplan
|
||||||
darüber. */
|
darüber. */
|
||||||
|
|||||||
+202
-194
@@ -1,35 +1,55 @@
|
|||||||
/* =====================================================================
|
/* =====================================================================
|
||||||
NUR DIE FARBEN BEWEGEN, DIE SICH WIRKLICH IN DIE QUERE KOMMEN
|
DIE KACHELFARBEN AUSEINANDERZIEHEN -- SO WENIG WIE MOEGLICH
|
||||||
(25.09.2026, zweiter Anlauf)
|
(25./26.09.2026)
|
||||||
|
|
||||||
Der erste Anlauf hat ALLE 45 Farben neu verteilt (die Rechnung
|
Filipe, 17.09.: „ich will das jede kachel eine andere farbe hat, es
|
||||||
dazu steht in der Geschichte des Verzeichnisses, Commit d7bb0f7e).
|
soll keine die gleiche farben haben bitte und auch keine die sich
|
||||||
Das Ergebnis war rechnerisch besser und praktisch falsch: Er ist
|
irgendwie aehnlich sind ... die farben sollen auch richtig geil und
|
||||||
dabei ueber zwei ausdrueckliche Wuensche gelaufen (das knallrote
|
speziell sein."
|
||||||
#ff1a1a fuer "Vertraulich melden", das Babyblau #a8d8ff fuer
|
|
||||||
"Support") und hat drei Toene unter die Buntheitsgrenze von 0,12
|
|
||||||
gedrueckt. pruef-kachelfarben hat alles fuenf gefunden.
|
|
||||||
|
|
||||||
DIE LEHRE IST NICHT "die Rechnung war falsch", sondern: Eine
|
Gemessen lagen acht Paare zwischen 0,015 und 0,054 OKLab-Abstand.
|
||||||
Gesamtloesung fuer ein LOKALES Problem bezahlt man mit allem, was
|
#ff8fb4 gegen #fe8ebe ist mit blossem Auge DIESELBE Farbe.
|
||||||
an den nicht betroffenen Stellen schon richtig war. Acht Paare
|
|
||||||
standen zu eng -- also werden acht Paare auseinandergezogen und
|
|
||||||
sonst nichts angefasst.
|
|
||||||
|
|
||||||
VORGEHEN. Solange irgendein Paar unter dem Mindestabstand liegt:
|
---------------------------------------------------------------------
|
||||||
das engste nehmen, den beweglicheren der beiden auf die naechste
|
ZWEI ANLAEUFE HAT DAS GEKOSTET, UND BEIDE FEHLER STEHEN HIER
|
||||||
erlaubte Farbe schieben, die alle Bedingungen haelt. Festgelegte
|
|
||||||
Toene sind unbeweglich.
|
|
||||||
|
|
||||||
ALLE SCHWELLEN KOMMEN AUS tools/kachelton-regeln.mjs. Keine Zahl
|
ERSTER ANLAUF: alle 45 Farben neu verteilt. Rechnerisch besser,
|
||||||
wird hier abgeschrieben -- genau dafuer gibt es die Datei.
|
praktisch falsch -- er lief ueber zwei ausdrueckliche Wuensche
|
||||||
|
hinweg (das knallrote #ff1a1a, das Babyblau #a8d8ff) und drueckte
|
||||||
|
drei Toene unter die Buntheitsgrenze.
|
||||||
|
|
||||||
|
ZWEITER ANLAUF: nur die Kollisionen angefasst, dafuer immer nur
|
||||||
|
EINEN der beiden Toene bewegt -- den, dessen Ausweichen billiger
|
||||||
|
war. Das sieht sparsam aus und ist es nicht: Ist die Nachbarschaft
|
||||||
|
voll, muss dieser eine quer durch den Farbraum. Gemessen wanderte
|
||||||
|
Ton 1 von einem Tuerkis (#0c86af) auf ein Altrosa (#b4649c), 0,197
|
||||||
|
weit. Eine Kachel, die von Tuerkis auf Rosa springt, erkennt
|
||||||
|
niemand wieder -- und der Zweck der ganzen Uebung war ja, dass man
|
||||||
|
sie auseinanderhaelt.
|
||||||
|
|
||||||
|
DRITTER ANLAUF, und der steht hier: BEIDE duerfen sich bewegen, und
|
||||||
|
zwar beide nur ein bisschen. Zwei Toene, die 0,02 auseinanderliegen
|
||||||
|
und 0,09 brauchen, koennen sich das teilen -- jeder rueckt 0,045,
|
||||||
|
und beide bleiben, was sie waren.
|
||||||
|
|
||||||
|
Umgesetzt als wachsender Spielraum: Erst darf jeder Ton 0,02 von
|
||||||
|
seiner Farbe abweichen, dann 0,03, dann 0,04 ... Es wird immer die
|
||||||
|
KLEINSTE Abweichung genommen, mit der es aufgeht. Wer gar nicht
|
||||||
|
abweichen muss, bleibt unberuehrt.
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
ALLE SCHWELLEN KOMMEN AUS tools/kachelton-regeln.mjs
|
||||||
|
|
||||||
|
Keine Zahl wird hier abgeschrieben -- genau dafuer gibt es die
|
||||||
|
Datei. Und die FESTGELEGTEN Toene stehen ebenfalls dort: Sie werden
|
||||||
|
nie bewegt, auch nicht um einen Bildpunkt.
|
||||||
|
|
||||||
node tools/kachelton-entzerren.mjs rechnen und zeigen
|
node tools/kachelton-entzerren.mjs rechnen und zeigen
|
||||||
node tools/kachelton-entzerren.mjs schreiben start.css aendern
|
node tools/kachelton-entzerren.mjs schreiben start.css aendern
|
||||||
|
|
||||||
|
Umgebung: ABSTAND=0.09 (Ziel), SCHRITT=3 (Rasterweite des Suchfelds)
|
||||||
===================================================================== */
|
===================================================================== */
|
||||||
import { readFileSync, writeFileSync, mkdtempSync } from "node:fs";
|
import { readFileSync, writeFileSync } from "node:fs";
|
||||||
import { tmpdir } from "node:os";
|
|
||||||
import { join } from "node:path";
|
|
||||||
import {
|
import {
|
||||||
GRUND, MINDEST_ABSTAND, MINDEST_BUNTHEIT, HOECHST_BUNTHEIT,
|
GRUND, MINDEST_ABSTAND, MINDEST_BUNTHEIT, HOECHST_BUNTHEIT,
|
||||||
MINDEST_KONTRAST, FESTGELEGT,
|
MINDEST_KONTRAST, FESTGELEGT,
|
||||||
@@ -40,251 +60,239 @@ const DATEI = "workspace/assets/css/start.css";
|
|||||||
/* Etwas Luft ueber jeder Schwelle -- gerundet wird auf ganze
|
/* Etwas Luft ueber jeder Schwelle -- gerundet wird auf ganze
|
||||||
Bildpunkte, und eine Farbe, die genau auf der Grenze steht, faellt
|
Bildpunkte, und eine Farbe, die genau auf der Grenze steht, faellt
|
||||||
beim naechsten Runden darunter. */
|
beim naechsten Runden darunter. */
|
||||||
const ZIEL_ABSTAND = MINDEST_ABSTAND + 0.004;
|
const ZIEL_ABSTAND = Number(process.env.ABSTAND || MINDEST_ABSTAND) + 0.0005;
|
||||||
const ZIEL_KONTRAST = MINDEST_KONTRAST + 0.15;
|
const ZIEL_KONTRAST = MINDEST_KONTRAST + 0.15;
|
||||||
const ZIEL_BUNT = MINDEST_BUNTHEIT + 0.004;
|
const ZIEL_BUNT = MINDEST_BUNTHEIT + 0.004;
|
||||||
|
const SCHRITT = Number(process.env.SCHRITT || 3);
|
||||||
|
|
||||||
|
/* ---------- Farbrechnung ---------------------------------------------- */
|
||||||
const lin = (c) => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);
|
const lin = (c) => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);
|
||||||
const srgb = (h) => [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16) / 255);
|
const srgb = (h) => [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16) / 255);
|
||||||
function oklab(h) {
|
function labVon(r, g, b) {
|
||||||
const [r, g, b] = srgb(h).map(lin);
|
const rr = lin(r / 255), gg = lin(g / 255), bb = lin(b / 255);
|
||||||
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
const l = Math.cbrt(0.4122214708 * rr + 0.5363325363 * gg + 0.0514459929 * bb);
|
||||||
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
const m = Math.cbrt(0.2119034982 * rr + 0.6806995451 * gg + 0.1073969566 * bb);
|
||||||
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
const s = Math.cbrt(0.0883024619 * rr + 0.2817188376 * gg + 0.6299787005 * bb);
|
||||||
return [0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,
|
return [0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,
|
||||||
1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,
|
1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,
|
||||||
0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s];
|
0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s];
|
||||||
}
|
}
|
||||||
const leuchte = (h) => { const [r, g, b] = srgb(h).map(lin); return 0.2126 * r + 0.7152 * g + 0.0722 * b; };
|
const labHex = (h) => labVon(...[1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16)));
|
||||||
const kontrast = (h) => (leuchte(h) + 0.05) / (leuchte(GRUND) + 0.05);
|
const leuchteVon = (r, g, b) => 0.2126 * lin(r / 255) + 0.7152 * lin(g / 255) + 0.0722 * lin(b / 255);
|
||||||
const bunt = (h) => { const [, a, b] = oklab(h); return Math.hypot(a, b); };
|
const kontrastVon = (y) => (y + 0.05) / (leuchteVon(...[1, 3, 5].map((i) => parseInt(GRUND.slice(i, i + 2), 16))) + 0.05);
|
||||||
const abst = (a, b) => { const x = oklab(a), y = oklab(b); return Math.hypot(x[0] - y[0], x[1] - y[1], x[2] - y[2]); };
|
const buntVon = (L) => Math.hypot(L[1], L[2]);
|
||||||
|
const d2 = (a, b) => (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2;
|
||||||
const hexVon = (r, g, b) => "#" + [r, g, b].map((c) => c.toString(16).padStart(2, "0")).join("");
|
const hexVon = (r, g, b) => "#" + [r, g, b].map((c) => c.toString(16).padStart(2, "0")).join("");
|
||||||
|
|
||||||
/* ---------- Die Farben aus der Datei --------------------------------- */
|
/* ---------- Die Farben aus der Datei ----------------------------------- */
|
||||||
const css = readFileSync(DATEI, "utf8");
|
const css = readFileSync(DATEI, "utf8");
|
||||||
const TOENE = [...css.matchAll(/\[data-ton="(\d+)"\]\s*\{\s*--ton:\s*(#[0-9a-fA-F]{6})/g)]
|
const TOENE = [...css.matchAll(/\[data-ton="(\d+)"\]\s*\{\s*--ton:\s*(#[0-9a-fA-F]{6})/g)]
|
||||||
.map((m) => ({ n: Number(m[1]), alt: m[2].toLowerCase(), hex: m[2].toLowerCase() }));
|
.map((m) => ({ n: Number(m[1]), alt: m[2].toLowerCase(), hex: m[2].toLowerCase() }));
|
||||||
if (TOENE.length < 20) { console.error("Muster passt nicht mehr"); process.exit(3); }
|
if (TOENE.length < 20) { console.error("Muster passt nicht mehr"); process.exit(3); }
|
||||||
|
for (const t of TOENE) t.altLab = labHex(t.alt);
|
||||||
|
|
||||||
/* ---------- Wer darf sich nicht bewegen, wer darf blass sein --------- */
|
|
||||||
const FEST = new Map(FESTGELEGT.filter((f) => f.hex).map((f) => [f.ton, f.hex.toLowerCase()]));
|
const FEST = new Map(FESTGELEGT.filter((f) => f.hex).map((f) => [f.ton, f.hex.toLowerCase()]));
|
||||||
const BLASS_ERLAUBT = new Map(FESTGELEGT.filter((f) => f.blassErlaubt).map((f) => [f.ton, f.blassErlaubt]));
|
const BLASS_ERLAUBT = new Map(FESTGELEGT.filter((f) => f.blassErlaubt).map((f) => [f.ton, f.blassErlaubt]));
|
||||||
|
|
||||||
for (const t of TOENE) {
|
for (const t of TOENE) {
|
||||||
if (FEST.has(t.n) && t.hex !== FEST.get(t.n)) {
|
if (FEST.has(t.n) && t.hex !== FEST.get(t.n)) {
|
||||||
console.log(` Ton ${t.n} steht auf ${t.hex}, festgelegt ist ${FEST.get(t.n)} -- wird zurueckgesetzt`);
|
console.log(` Ton ${t.n} stand auf ${t.hex}, festgelegt ist ${FEST.get(t.n)} -- zurueckgesetzt`);
|
||||||
t.hex = FEST.get(t.n);
|
t.hex = FEST.get(t.n);
|
||||||
}
|
}
|
||||||
|
t.lab = labHex(t.hex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- WELCHE TOENE WERDEN UEBERHAUPT BENUTZT? ------------------
|
/* ---------- WELCHE TOENE WERDEN BENUTZT? -------------------------------
|
||||||
|
|
||||||
Die Buntheitsgrenze gilt nur fuer BENUTZTE Toene -- so steht es in
|
ALLE FUENFUNDVIERZIG. Und das ist der Befund, an dem der zweite
|
||||||
pruef-kachelfarben, und das ist der Grund, aus dem die Reparatur
|
Anlauf gescheitert ist.
|
||||||
ueberhaupt aufgeht: Mit Buntheit >= 0,12 fuer ALLE 45 ist ein
|
|
||||||
Mindestabstand von 0,09 rechnerisch unmoeglich (gemessene Decke:
|
|
||||||
0,0853). Die zwoelf unbenutzten duerfen blasser sein und nehmen
|
|
||||||
damit Druck aus dem engen Blau-Tuerkis-Bereich.
|
|
||||||
|
|
||||||
Gefragt wird das Programm selbst, nicht eine Liste. */
|
Wer das PROGRAMM fragt (bereicheFuer() je Rolle), bekommt 33. Die
|
||||||
process.env.WORKSPACE_DB = process.env.WORKSPACE_DB
|
uebrigen zwoelf sehen dann aus wie freie Reserve -- sie duerfen
|
||||||
|| join(mkdtempSync(join(tmpdir(), "toene-")), "workspace.db");
|
blasser werden und weiter wandern. Sie sind keine Reserve: Fuer die
|
||||||
const w = await import("../server/workspace.js");
|
fuenf Rollen des Agenturhauses gibt bereicheFuer() `null` zurueck,
|
||||||
|
und das heisst „nimm die Liste aus dem Browser". Die steht in
|
||||||
|
workspace/assets/js/bereiche.js, und dort stehen die Toene 1 bis 22
|
||||||
|
und 44 -- genau die zwoelf.
|
||||||
|
|
||||||
|
Gemessen: Ton 1 traegt „Dashboard", Ton 2 „Zahlen", Ton 21
|
||||||
|
„Scout-Pipeline", Ton 22 „Team-Lage". Alles Kacheln, die jeden Tag
|
||||||
|
jemand ansieht.
|
||||||
|
|
||||||
|
Gezaehlt wird deshalb ueber dieselben Quellen, die auch
|
||||||
|
pruef-kachel-universum liest. Die Frage lautet nicht „wem gibt der
|
||||||
|
Server eine Kachel", sondern „steht diese Nummer irgendwo an einer
|
||||||
|
Kachel". */
|
||||||
|
const QUELLEN = ["../server/workspace.js", "../workspace/assets/js/bereiche.js",
|
||||||
|
"../workspace/assets/js/start.js", "../workspace/assets/js/kopf.js",
|
||||||
|
"../workspace/assets/js/befinden.js"];
|
||||||
const BENUTZT = new Set();
|
const BENUTZT = new Set();
|
||||||
for (const r of ["admin", "hand", "linke", "modi", "gast", "manager", "scout", "creator", "spicy"]) {
|
for (const q of QUELLEN) {
|
||||||
for (const haus of ["crew", "agentur"]) {
|
for (const m of readFileSync(new URL(q, import.meta.url), "utf8").matchAll(/ton:\s*(\d+)/g)) {
|
||||||
let k = [];
|
BENUTZT.add(Number(m[1]));
|
||||||
try { k = [...(w.bereicheFuer({ id: 1, rolle: r, name: "X" }, haus) || []),
|
|
||||||
...(w.zusatzBereicheFuer({ id: 1, rolle: r, name: "X" }, haus) || [])]; }
|
|
||||||
catch { continue; }
|
|
||||||
for (const kachel of k) if (kachel?.ton != null) BENUTZT.add(Number(kachel.ton));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (BENUTZT.size < 20) { console.error(`nur ${BENUTZT.size} benutzte Toene gefunden`); process.exit(5); }
|
if (BENUTZT.size < 20) { console.error(`nur ${BENUTZT.size} benutzte Toene gefunden`); process.exit(5); }
|
||||||
console.log(`${BENUTZT.size} von ${TOENE.length} Toenen werden benutzt: ${[...BENUTZT].sort((a, b) => a - b).join(", ")}`);
|
console.log(`${BENUTZT.size} von ${TOENE.length} Toenen werden benutzt`);
|
||||||
/* Auch die unbenutzten sollen so bunt bleiben, wie es noch passt --
|
|
||||||
sonst steht beim naechsten Gebrauch ein blasser Ton bereit und die
|
|
||||||
Pruefung wird rot, obwohl niemand etwas falsch gemacht hat. */
|
|
||||||
const UNBENUTZT_BUNT = Number(process.env.UNBUNT || 0.045);
|
|
||||||
const buntNoetig = (n) => (BENUTZT.has(n) && !BLASS_ERLAUBT.has(n) ? ZIEL_BUNT
|
|
||||||
: Math.min(UNBENUTZT_BUNT, bunt(TOENE.find((t) => t.n === n).alt)));
|
|
||||||
|
|
||||||
/* Der verlangte Mindestabstand kann je Ton hoeher sein: Wer blass sein
|
/* Wer blass sein darf, muss das beim Abstand ausgleichen -- so steht es
|
||||||
darf, muss das beim Abstand ausgleichen (steht so in
|
mit Messung in kachelton-regeln.mjs. */
|
||||||
pruef-kachelfarben und ist dort begruendet). */
|
const abstandFuer = (n) => Math.max(ZIEL_ABSTAND, (BLASS_ERLAUBT.get(n) ?? 0) + 0.0005);
|
||||||
const abstandFuer = (n) => Math.max(ZIEL_ABSTAND, (BLASS_ERLAUBT.get(n) ?? 0) + 0.004);
|
const BLASS_BIS = new Map(FESTGELEGT.filter((f) => f.blassBis).map((f) => [f.ton, f.blassBis]));
|
||||||
|
const buntFuer = (n) => (BLASS_BIS.has(n) ? BLASS_BIS.get(n) + 0.002 : ZIEL_BUNT);
|
||||||
|
|
||||||
/* ---------- Alle erlaubten Farben ------------------------------------ */
|
/* ---------- Das Suchfeld ----------------------------------------------- */
|
||||||
const ALLE = [];
|
const KANDIDATEN = [];
|
||||||
for (let r = 0; r <= 255; r += 2) {
|
for (let r = 0; r <= 255; r += SCHRITT) {
|
||||||
for (let g = 0; g <= 255; g += 2) {
|
for (let g = 0; g <= 255; g += SCHRITT) {
|
||||||
for (let b = 0; b <= 255; b += 2) {
|
for (let b = 0; b <= 255; b += SCHRITT) {
|
||||||
const h = hexVon(r, g, b);
|
const rr = Math.min(255, r), gg = Math.min(255, g), bb = Math.min(255, b);
|
||||||
if (kontrast(h) < ZIEL_KONTRAST) continue;
|
if (kontrastVon(leuchteVon(rr, gg, bb)) < ZIEL_KONTRAST) continue;
|
||||||
const c = bunt(h);
|
const L = labVon(rr, gg, bb);
|
||||||
|
const c = buntVon(L);
|
||||||
if (c > HOECHST_BUNTHEIT) continue;
|
if (c > HOECHST_BUNTHEIT) continue;
|
||||||
ALLE.push({ hex: h, bunt: c });
|
KANDIDATEN.push({ hex: hexVon(rr, gg, bb), L, bunt: c });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const kandidatenFuer = (n) => ALLE.filter((x) => x.bunt >= buntNoetig(n)).map((x) => x.hex);
|
console.log(`${KANDIDATEN.length} Farben im Suchfeld (Kontrast >= ${ZIEL_KONTRAST.toFixed(2)}, `
|
||||||
console.log(`${ALLE.length} erlaubte Farben (Kontrast >= ${ZIEL_KONTRAST.toFixed(2)}, `
|
+ `Buntheit bis ${HOECHST_BUNTHEIT}, Raster ${SCHRITT})`);
|
||||||
+ `Buntheit bis ${HOECHST_BUNTHEIT}; benutzte Toene ab ${ZIEL_BUNT.toFixed(3)})`);
|
|
||||||
|
|
||||||
/* ---------- Was steht zu eng? ---------------------------------------- */
|
/* ---------- Messen ------------------------------------------------------ */
|
||||||
const engstes = () => {
|
function engstes() {
|
||||||
let m = Infinity, wo = null;
|
let fehl = 0, klein = Infinity, wo = null;
|
||||||
for (let i = 0; i < TOENE.length; i++) {
|
for (let i = 0; i < TOENE.length; i++) {
|
||||||
for (let j = i + 1; j < TOENE.length; j++) {
|
for (let j = i + 1; j < TOENE.length; j++) {
|
||||||
const noetig = Math.max(abstandFuer(TOENE[i].n), abstandFuer(TOENE[j].n));
|
const noetig = Math.max(abstandFuer(TOENE[i].n), abstandFuer(TOENE[j].n));
|
||||||
const d = abst(TOENE[i].hex, TOENE[j].hex);
|
const d = Math.sqrt(d2(TOENE[i].lab, TOENE[j].lab));
|
||||||
if (d < noetig && d - noetig < m) { m = d - noetig; wo = { i, j, d, noetig }; }
|
if (d < noetig) fehl++;
|
||||||
|
if (d < klein) { klein = d; wo = [TOENE[i], TOENE[j]]; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return wo;
|
return { fehl, klein, wo };
|
||||||
};
|
}
|
||||||
|
const zuBlass = () => TOENE.filter((t) =>
|
||||||
|
buntVon(t.lab) < (BLASS_BIS.get(t.n) ?? MINDEST_BUNTHEIT));
|
||||||
|
|
||||||
const naechsterZu = (h, ausser) => {
|
/** Wie gut steht dieser Ton gerade? (Abstand zum naechsten, in Einheiten
|
||||||
|
* dessen, was er braucht -- >= 1 heisst „in Ordnung".) */
|
||||||
|
function guete(lab, selbst) {
|
||||||
let m = Infinity;
|
let m = Infinity;
|
||||||
for (const t of TOENE) { if (t === ausser) continue; m = Math.min(m, abst(h, t.hex)); }
|
|
||||||
return m;
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log("");
|
|
||||||
let bewegt = 0;
|
|
||||||
for (let runde = 0; runde < 200; runde++) {
|
|
||||||
const p = engstes();
|
|
||||||
if (!p) break;
|
|
||||||
const a = TOENE[p.i], b = TOENE[p.j];
|
|
||||||
|
|
||||||
/* Wer wird bewegt? Nicht der Festgelegte. Sonst der, fuer den die
|
|
||||||
naechste erlaubte Stelle naeher an seinem eigenen Ton liegt. */
|
|
||||||
const kandidatFuer = (t) => {
|
|
||||||
if (FEST.has(t.n)) return null;
|
|
||||||
let best = null, bestWeg = Infinity;
|
|
||||||
const noetig = abstandFuer(t.n);
|
|
||||||
for (const h of kandidatenFuer(t.n)) {
|
|
||||||
const weg = abst(t.alt, h);
|
|
||||||
if (weg >= bestWeg) continue;
|
|
||||||
let ok = true;
|
|
||||||
for (const u of TOENE) {
|
for (const u of TOENE) {
|
||||||
if (u === t) continue;
|
if (u === selbst) continue;
|
||||||
if (abst(h, u.hex) < Math.max(noetig, abstandFuer(u.n))) { ok = false; break; }
|
const noetig = Math.max(abstandFuer(selbst.n), abstandFuer(u.n));
|
||||||
|
const v = Math.sqrt(d2(lab, u.lab)) / noetig;
|
||||||
|
if (v < m) m = v;
|
||||||
}
|
}
|
||||||
if (ok) { best = h; bestWeg = weg; }
|
return m;
|
||||||
}
|
|
||||||
return best ? { hex: best, weg: bestWeg } : null;
|
|
||||||
};
|
|
||||||
const ka = kandidatFuer(a), kb = kandidatFuer(b);
|
|
||||||
if (!ka && !kb) {
|
|
||||||
console.error(`Fuer ${a.hex} (Ton ${a.n}) und ${b.hex} (Ton ${b.n}) gibt es keinen Platz.`);
|
|
||||||
process.exit(4);
|
|
||||||
}
|
|
||||||
/* WER WEICHT AUS? ZUERST DER, DEN NIEMAND SIEHT.
|
|
||||||
|
|
||||||
Gemessen: In JEDEM der acht zu engen Paare ist mindestens einer
|
|
||||||
der beiden Toene gar nicht in Gebrauch -- 33 der 45 tragen eine
|
|
||||||
Kachel, zwoelf stehen nur bereit. Wer den unbenutzten verschiebt,
|
|
||||||
loest dasselbe Problem, ohne dass sich fuer irgendjemanden auch
|
|
||||||
nur eine Kachel veraendert.
|
|
||||||
|
|
||||||
Das ist mehr als Bequemlichkeit: Eine Kachel, die ihre Farbe
|
|
||||||
wechselt, muss neu gelernt werden. Wer zwei Jahre lang „das
|
|
||||||
Tuerkise" angesteuert hat, sucht danach. Diese Kosten entstehen
|
|
||||||
hier schlicht nicht.
|
|
||||||
|
|
||||||
Erst wenn beide benutzt sind (oder beide nicht), entscheidet der
|
|
||||||
kuerzere Weg. */
|
|
||||||
const frei = (t, k) => k && !BENUTZT.has(t.n);
|
|
||||||
const nimm = frei(a, ka) && !frei(b, kb) ? { t: a, k: ka }
|
|
||||||
: frei(b, kb) && !frei(a, ka) ? { t: b, k: kb }
|
|
||||||
: !ka ? { t: b, k: kb } : !kb ? { t: a, k: ka }
|
|
||||||
: (ka.weg <= kb.weg ? { t: a, k: ka } : { t: b, k: kb });
|
|
||||||
console.log(` ${p.d.toFixed(4)} (noetig ${p.noetig.toFixed(3)}) Ton ${a.n} ${a.hex} <-> `
|
|
||||||
+ `Ton ${b.n} ${b.hex} -> Ton ${nimm.t.n} wandert nach ${nimm.k.hex} `
|
|
||||||
+ `(${nimm.k.weg.toFixed(3)})`);
|
|
||||||
nimm.t.hex = nimm.k.hex;
|
|
||||||
bewegt++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- NACHZIEHEN -----------------------------------------------
|
/* ---------- Der wachsende Spielraum ------------------------------------- */
|
||||||
|
const anfang = engstes();
|
||||||
|
console.log(`vorher: kleinster Abstand ${anfang.klein.toFixed(4)}, `
|
||||||
|
+ `${anfang.fehl} Paare unter dem Soll, ${zuBlass().length} zu blass`);
|
||||||
|
console.log("");
|
||||||
|
|
||||||
Der Durchgang oben arbeitet sich vom engsten Paar nach vorn und
|
/* Der Spielraum waechst in jeder Runde ein wenig. Er MUSS wachsen,
|
||||||
entscheidet dabei immer nur mit dem, was GERADE dasteht. Wer
|
auch wenn sich gerade etwas bewegt hat: Sonst haengt ein Ton, der
|
||||||
frueh ausweichen muss, weicht deshalb manchmal weiter aus als
|
mehr Platz braucht als die anderen, fuer immer fest, waehrend die
|
||||||
noetig -- die Stelle, die ihm gepasst haette, wird erst zwei
|
uebrigen noch hin und her rutschen -- gemessen blieben so vier zu
|
||||||
Schritte spaeter frei.
|
blasse Toene stehen, obwohl das Verfahren noch lange nicht am
|
||||||
|
Ende war. */
|
||||||
|
let spielraum = 0.015;
|
||||||
|
for (let runde = 0; runde < 300; runde++) {
|
||||||
|
const lage = engstes();
|
||||||
|
const blass = zuBlass();
|
||||||
|
if (!lage.fehl && !blass.length) break;
|
||||||
|
|
||||||
Gemessen war das kein Schoenheitsfehler: Ton 10 (#ff6877, ein
|
let bewegt = 0;
|
||||||
Korallrot) landete beim ersten Lauf auf #a86cb4, einem Violett --
|
for (const t of TOENE) {
|
||||||
0,178 entfernt, und niemand haette die Kachel wiedererkannt.
|
if (FEST.has(t.n)) continue;
|
||||||
|
const jetztGuete = Math.min(guete(t.lab, t), 1);
|
||||||
|
const jetztBunt = buntVon(t.lab);
|
||||||
|
const bunkOk = jetztBunt >= (BLASS_BIS.get(t.n) ?? MINDEST_BUNTHEIT);
|
||||||
|
if (jetztGuete >= 1 && bunkOk) continue; // dieser ist fertig
|
||||||
|
|
||||||
Also am Ende noch einmal von vorn: Jeder bewegte Ton darf so weit
|
const grenze2 = spielraum * spielraum;
|
||||||
zu seiner alten Farbe zurueck, wie die Bedingungen es zulassen.
|
const mindBunt = buntFuer(t.n);
|
||||||
Das aendert am Mindestabstand nichts -- es wird nur geprueft, ob
|
/* IST DIE BUNTHEIT DAS PROBLEM, ZAEHLT DIE JETZIGE LAGE NICHT.
|
||||||
es NAEHER geht. */
|
|
||||||
|
Hier stand `bestGuete = jetztGuete`. Fuer einen Ton, der weit
|
||||||
|
genug von allen wegliegt (Guete 1) und nur zu blass ist, konnte
|
||||||
|
damit KEIN Kandidat gewinnen -- kein Wert ist groesser als 1,
|
||||||
|
wenn bei 1 gedeckelt wird. Vier Toene blieben deshalb blass
|
||||||
|
stehen, obwohl das Suchfeld voller passender Farben war.
|
||||||
|
|
||||||
|
Mit -1 als Ausgangswert gewinnt jeder Kandidat, der ueberhaupt
|
||||||
|
bunt genug ist -- und unter denen wieder der naechste. */
|
||||||
|
let best = null, bestGuete = bunkOk ? jetztGuete : -1, bestWeg = 0;
|
||||||
|
for (const k of KANDIDATEN) {
|
||||||
|
if (k.bunt < mindBunt) continue;
|
||||||
|
const weg2 = d2(t.altLab, k.L);
|
||||||
|
if (weg2 > grenze2) continue;
|
||||||
|
/* GEDECKELT BEI 1. „Weiter weg als noetig" ist kein Gewinn --
|
||||||
|
ohne den Deckel sucht das Verfahren das Maximum und wandert
|
||||||
|
dafuer quer durch den Farbraum. Mit ihm gewinnt unter allen
|
||||||
|
Farben, die reichen, die NAECHSTE. */
|
||||||
|
const g = Math.min(guete(k.L, t), 1);
|
||||||
|
if (g > bestGuete + 1e-9 || (g > bestGuete - 1e-9 && best && weg2 < bestWeg)) {
|
||||||
|
best = k; bestGuete = g; bestWeg = weg2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (best && best.hex !== t.hex) {
|
||||||
|
t.hex = best.hex; t.lab = best.L; bewegt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spielraum += 0.0015;
|
||||||
|
if (spielraum > 0.26) { console.error("Spielraum ausgeschoepft -- es geht nicht auf."); break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Nachziehen: so weit zurueck wie moeglich --------------------- */
|
||||||
for (let runde = 0; runde < 12; runde++) {
|
for (let runde = 0; runde < 12; runde++) {
|
||||||
let geruckt = false;
|
let geruckt = false;
|
||||||
for (const t of TOENE) {
|
for (const t of TOENE) {
|
||||||
if (FEST.has(t.n) || t.hex === t.alt) continue;
|
if (FEST.has(t.n) || t.hex === t.alt) continue;
|
||||||
const jetzt = abst(t.alt, t.hex);
|
const jetztWeg = d2(t.altLab, t.lab);
|
||||||
const noetig = abstandFuer(t.n);
|
const mindBunt = buntFuer(t.n);
|
||||||
let best = null, bestWeg = jetzt;
|
let best = null, bestWeg = jetztWeg;
|
||||||
for (const h of kandidatenFuer(t.n)) {
|
for (const k of KANDIDATEN) {
|
||||||
const weg = abst(t.alt, h);
|
if (k.bunt < mindBunt) continue;
|
||||||
if (weg >= bestWeg) continue;
|
const weg2 = d2(t.altLab, k.L);
|
||||||
let ok = true;
|
if (weg2 >= bestWeg) continue;
|
||||||
for (const u of TOENE) {
|
if (guete(k.L, t) < 1) continue;
|
||||||
if (u === t) continue;
|
best = k; bestWeg = weg2;
|
||||||
if (abst(h, u.hex) < Math.max(noetig, abstandFuer(u.n))) { ok = false; break; }
|
|
||||||
}
|
|
||||||
if (ok) { best = h; bestWeg = weg; }
|
|
||||||
}
|
|
||||||
if (best) {
|
|
||||||
console.log(` nachgezogen: Ton ${t.n} ${t.hex} -> ${best} `
|
|
||||||
+ `(${jetzt.toFixed(3)} -> ${bestWeg.toFixed(3)} vom alten Ton)`);
|
|
||||||
t.hex = best; geruckt = true;
|
|
||||||
}
|
}
|
||||||
|
if (best) { t.hex = best.hex; t.lab = best.L; geruckt = true; }
|
||||||
}
|
}
|
||||||
if (!geruckt) break;
|
if (!geruckt) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- Nachmessen ------------------------------------------------ */
|
/* ---------- Ergebnis ----------------------------------------------------- */
|
||||||
let klein = Infinity, wo = null, unterSoll = 0;
|
const ende = engstes();
|
||||||
for (let i = 0; i < TOENE.length; i++) {
|
const blassEnde = zuBlass();
|
||||||
for (let j = i + 1; j < TOENE.length; j++) {
|
|
||||||
const d = abst(TOENE[i].hex, TOENE[j].hex);
|
|
||||||
const noetig = Math.max(abstandFuer(TOENE[i].n), abstandFuer(TOENE[j].n));
|
|
||||||
if (d < noetig) unterSoll++;
|
|
||||||
if (d < klein) { klein = d; wo = [TOENE[i], TOENE[j]]; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const geaendert = TOENE.filter((t) => t.hex !== t.alt);
|
const geaendert = TOENE.filter((t) => t.hex !== t.alt);
|
||||||
const zuBlass = TOENE.filter((t) => BENUTZT.has(t.n)
|
|
||||||
&& bunt(t.hex) < MINDEST_BUNTHEIT && !BLASS_ERLAUBT.has(t.n));
|
|
||||||
const zuDunkel = TOENE.filter((t) => kontrast(t.hex) < MINDEST_KONTRAST);
|
|
||||||
const doppelt = TOENE.length - new Set(TOENE.map((t) => t.hex)).size;
|
const doppelt = TOENE.length - new Set(TOENE.map((t) => t.hex)).size;
|
||||||
|
|
||||||
console.log("");
|
|
||||||
console.log(`geaendert ${geaendert.length} von ${TOENE.length}`);
|
console.log(`geaendert ${geaendert.length} von ${TOENE.length}`);
|
||||||
for (const t of geaendert) {
|
for (const t of geaendert.sort((a, b) => d2(b.altLab, b.lab) - d2(a.altLab, a.lab))) {
|
||||||
console.log(` Ton ${String(t.n).padStart(2)} ${t.alt} -> ${t.hex} `
|
console.log(` Ton ${String(t.n).padStart(2)} ${t.alt} -> ${t.hex} `
|
||||||
+ `verschoben ${abst(t.alt, t.hex).toFixed(3)} Buntheit ${bunt(t.hex).toFixed(3)} `
|
+ `verschoben ${Math.sqrt(d2(t.altLab, t.lab)).toFixed(3)} `
|
||||||
+ `Kontrast ${kontrast(t.hex).toFixed(2)}:1`);
|
+ `Buntheit ${buntVon(t.lab).toFixed(3)} `
|
||||||
|
+ `Kontrast ${kontrastVon(leuchteVon(...[1, 3, 5].map((i) => parseInt(t.hex.slice(i, i + 2), 16)))).toFixed(2)}:1`);
|
||||||
}
|
}
|
||||||
console.log(`kleinster Abstand ${klein.toFixed(4)} (Ton ${wo[0].n} ${wo[0].hex} / Ton ${wo[1].n} ${wo[1].hex})`);
|
console.log(`kleinster Abstand ${ende.klein.toFixed(4)} `
|
||||||
console.log(`unter dem Soll ${unterSoll}`);
|
+ `(Ton ${ende.wo[0].n} ${ende.wo[0].hex} / Ton ${ende.wo[1].n} ${ende.wo[1].hex})`);
|
||||||
console.log(`zu blass ${zuBlass.map((t) => `Ton ${t.n} ${bunt(t.hex).toFixed(3)}`).join(", ") || "keiner"}`);
|
console.log(`unter dem Soll ${ende.fehl}`);
|
||||||
console.log(`zu dunkel ${zuDunkel.map((t) => `Ton ${t.n}`).join(", ") || "keiner"}`);
|
console.log(`zu blass ${blassEnde.map((t) => `Ton ${t.n} ${buntVon(t.lab).toFixed(3)}`).join(", ") || "keiner"}`);
|
||||||
console.log(`doppelt ${doppelt}`);
|
console.log(`doppelt ${doppelt}`);
|
||||||
|
console.log(`groesste Aenderung ${geaendert.length ? Math.max(...geaendert.map((t) => Math.sqrt(d2(t.altLab, t.lab)))).toFixed(3) : "0"}`);
|
||||||
for (const [n, h] of FEST) {
|
for (const [n, h] of FEST) {
|
||||||
const t = TOENE.find((x) => x.n === n);
|
const t = TOENE.find((x) => x.n === n);
|
||||||
console.log(`festgelegt Ton ${n} ${t.hex} ${t.hex === h ? "(unveraendert)" : "*** ABWEICHUNG ***"}`);
|
console.log(`festgelegt Ton ${n} ${t.hex} ${t.hex === h ? "(unveraendert)" : "*** ABWEICHUNG ***"}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.argv[2] === "schreiben") {
|
if (process.argv[2] === "schreiben") {
|
||||||
if (unterSoll || zuBlass.length || zuDunkel.length || doppelt
|
if (ende.fehl || blassEnde.length || doppelt
|
||||||
|| [...FEST].some(([n, h]) => TOENE.find((x) => x.n === n).hex !== h)) {
|
|| [...FEST].some(([n, h]) => TOENE.find((x) => x.n === n).hex !== h)) {
|
||||||
console.error("\nNICHT geschrieben -- das Ergebnis haelt die Bedingungen nicht.");
|
console.error("\nNICHT geschrieben -- das Ergebnis haelt die Bedingungen nicht.");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
@@ -108,6 +108,70 @@ const FESTGELEGT = [
|
|||||||
Haus ohnehin steht. Die Bedingung dazu steht unten bei der
|
Haus ohnehin steht. Die Bedingung dazu steht unten bei der
|
||||||
Pruefung und ist echt: Faellt der Abstand unter 0,10, wird diese
|
Pruefung und ist echt: Faellt der Abstand unter 0,10, wird diese
|
||||||
Zeile rot. */
|
Zeile rot. */
|
||||||
{ ton: 44, kachel: "Support", hex: "#a8d8ff", blassErlaubt: 0.10,
|
{ ton: 44, kachel: "Support", hex: "#a8d8ff", blassErlaubt: 0.10, blassBis: 0.070,
|
||||||
warum: "Babyblau auf Wunsch -- blass, dafuer mit gemessenem Sicherheitsabstand" },
|
warum: "Babyblau auf Wunsch -- blass, dafuer mit gemessenem Sicherheitsabstand" },
|
||||||
|
|
||||||
|
/* ==== TUERKIS GEHT NICHT BUNTER (26.09.2026) =====================
|
||||||
|
|
||||||
|
Ton 1 traegt „Dashboard" im Agenturhaus und liegt bei einer
|
||||||
|
Buntheit von 0,112 -- acht Tausendstel unter der Hausgrenze.
|
||||||
|
Aufgefallen ist das erst, als diese Pruefung endlich AUCH die
|
||||||
|
Toene des Agenturhauses ansah; vorher kannte sie nur 33 von 45.
|
||||||
|
|
||||||
|
DIE NAHELIEGENDE ANTWORT WAERE, IHN BUNTER ZU MACHEN. Gemessen
|
||||||
|
geht das nicht: Durchsucht man ALLE 16,7 Millionen sRGB-Farben
|
||||||
|
nach der naechstgelegenen, die gleichzeitig Buntheit >= 0,124,
|
||||||
|
Kontrast >= 4,65 und 0,09 Abstand zu allen 44 anderen haelt,
|
||||||
|
kommt #7a75c8 heraus -- ein Blauviolett, 0,113 entfernt. Aus
|
||||||
|
dem Tuerkis des Dashboards wuerde also Flieder.
|
||||||
|
|
||||||
|
Der Grund ist der Farbraum selbst: Tuerkis erreicht in sRGB nur
|
||||||
|
eine geringe Buntheit, und dieses schmale Band teilen sich
|
||||||
|
bereits die Toene 15, 16, 23, 26, 31, 32 und 43. Es ist voll.
|
||||||
|
|
||||||
|
ALSO EINE AUSNAHME MIT ZAHL, KEIN FREIBRIEF: `blassBis` sagt,
|
||||||
|
wie blass er hoechstens sein darf -- blasser als heute darf er
|
||||||
|
nicht werden. Und `blassErlaubt` fordert weiterhin den vollen
|
||||||
|
Abstand ein. Eine Kachel, die ihre Farbfamilie behaelt, ist die
|
||||||
|
bessere Antwort auf „richtig geil und speziell" als eine, die
|
||||||
|
acht Tausendstel bunter ist und niemand wiedererkennt. */
|
||||||
|
{ ton: 1, kachel: "Dashboard", blassErlaubt: 0.09, blassBis: 0.108,
|
||||||
|
warum: "Tuerkis -- in diesem schmalen Band gibt es keine buntere Farbe mit Abstand" },
|
||||||
|
|
||||||
|
/* ==== DER SECHSUNDVIERZIGSTE TON (26.09.2026) ====================
|
||||||
|
|
||||||
|
Filipe: „fuer die modis, rechte und linke hand und dogfather
|
||||||
|
eine kachel ... sie soll: Notizen, heissen."
|
||||||
|
|
||||||
|
GESUCHT WAR BERNSTEIN -- die Farbe von Papier. Gemessen gibt es
|
||||||
|
sie nicht mehr: Durchsucht man alle sRGB-Farben im Bernsteinband
|
||||||
|
(Farbwinkel 55 bis 100 Grad) nach der, die am weitesten von
|
||||||
|
allen 45 vorhandenen wegliegt, kommt #d0ac48 heraus -- mit
|
||||||
|
0,0799 Abstand, also UNTER der Hausgrenze von 0,09.
|
||||||
|
|
||||||
|
Und Platz zu schaffen hilft nicht: Setzt man diesen Bernstein
|
||||||
|
fest und laesst die Nachbarn ausweichen, geht es zwar auf --
|
||||||
|
aber ein warmer Ton muss dafuer das Band verlassen. Gemessen
|
||||||
|
wanderte Ton 45 (Orange) 0,212 weit ins Magenta; haelt man auch
|
||||||
|
den fest, trifft es Ton 40 mit 0,246. Das warme Band ist voll,
|
||||||
|
und jede neue Farbe darin kostet eine vorhandene Kachel ihre
|
||||||
|
Identitaet. Das ist ein schlechter Tausch.
|
||||||
|
|
||||||
|
DIE FREIEN LUECKEN, alle gemessen:
|
||||||
|
|
||||||
|
Gruen #9cde90 0,0917 <- genommen
|
||||||
|
Violett #ca50ac 0,0913
|
||||||
|
Blau #746afc 0,0890
|
||||||
|
Tuerkis #66e2bc 0,0821
|
||||||
|
Bernstein #d0ac48 0,0799
|
||||||
|
|
||||||
|
Gruen passt zur Sache besser, als es zunaechst klingt: Das
|
||||||
|
Notizblatt im Haus traegt seine Linien in dieser Farbe, und ein
|
||||||
|
linierter Block in Gruen ist Papier, seit es Papier gibt.
|
||||||
|
|
||||||
|
ER IST FESTGELEGT, weil die Kachel ihre eigene Oberflaeche hat
|
||||||
|
(siehe `papier` in start.css): Ein Farblauf, der ihn verschiebt,
|
||||||
|
wuerde Linien und Flaeche auseinanderlaufen lassen. */
|
||||||
|
{ ton: 46, kachel: "Notizen", hex: "#9cde90",
|
||||||
|
warum: "die groesste freie Luecke der Palette -- Bernstein gibt es nicht mehr" },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
verdeckt. Gefunden hat es pruef-struktur.
|
verdeckt. Gefunden hat es pruef-struktur.
|
||||||
|
|
||||||
Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. -->
|
Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. -->
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
<!-- Ohne diese drei nimmt ein iPhone einen Bildschirmausschnitt als
|
<!-- Ohne diese drei nimmt ein iPhone einen Bildschirmausschnitt als
|
||||||
Symbol, Android zeichnet die Leiste weiss, und die Seite oeffnet
|
Symbol, Android zeichnet die Leiste weiss, und die Seite oeffnet
|
||||||
ausserhalb der App -- auf einer Seite, die genau erklaeren soll,
|
ausserhalb der App -- auf einer Seite, die genau erklaeren soll,
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
schreibt jede Seite app.webmanifest -- auch die hier. -->
|
schreibt jede Seite app.webmanifest -- auch die hier. -->
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<style>
|
<style>
|
||||||
/* Bewusst in der Seite und nicht in einer eigenen Datei: Diese Seite
|
/* Bewusst in der Seite und nicht in einer eigenen Datei: Diese Seite
|
||||||
soll auch dann noch etwas anzeigen, wenn genau das schiefgeht,
|
soll auch dann noch etwas anzeigen, wenn genau das schiefgeht,
|
||||||
@@ -135,7 +135,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script src="assets/js/anruf-probe.js?v=202609252349" defer></script>
|
<script src="assets/js/anruf-probe.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -23,25 +23,25 @@
|
|||||||
},
|
},
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/workspace-192.png?v=202609252349",
|
"src": "/assets/img/app-symbole/workspace-192.png?v=202609261240",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/workspace-512.png?v=202609252349",
|
"src": "/assets/img/app-symbole/workspace-512.png?v=202609261240",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609252349",
|
"src": "/assets/img/app-symbole/workspace-192-maskable.png?v=202609261240",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable"
|
"purpose": "maskable"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609252349",
|
"src": "/assets/img/app-symbole/workspace-512-maskable.png?v=202609261240",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable"
|
"purpose": "maskable"
|
||||||
|
|||||||
@@ -0,0 +1,543 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
DER NOTIZBLOCK (26.09.2026)
|
||||||
|
=====================================================================
|
||||||
|
|
||||||
|
Filipe: „ich will dass du das auch wie ein notizblock erstellst.
|
||||||
|
ich will dass es übelst geil und einzigartig ist. mach wirklich das
|
||||||
|
krasseste und geilste notizblock auf der welt."
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
DIE EINE REGEL, AN DER HIER ALLES HÄNGT
|
||||||
|
|
||||||
|
Das Blatt besteht aus ZWEI Schichten, die exakt aufeinanderliegen:
|
||||||
|
ein Schreibfeld (trägt Text und Cursor, ist aber unsichtbar) und
|
||||||
|
eine Deckschicht darüber (zeichnet denselben Text noch einmal, mit
|
||||||
|
Haken, Überschriften und Strichen).
|
||||||
|
|
||||||
|
Damit das aufgeht, muss jedes Zeichen in beiden Schichten an
|
||||||
|
derselben Stelle landen. Deshalb gilt in dieser Datei:
|
||||||
|
|
||||||
|
JEDE AUSZEICHNUNG DARF FARBE UND FLÄCHE ÄNDERN — NIE DIE BREITE.
|
||||||
|
|
||||||
|
Erlaubt: color, background, box-shadow, text-decoration, opacity,
|
||||||
|
text-shadow, border-radius.
|
||||||
|
Verboten: font-weight, font-size, font-family, letter-spacing,
|
||||||
|
word-spacing, font-style, text-transform, padding und margin an
|
||||||
|
allem, was IM Textfluss liegt.
|
||||||
|
|
||||||
|
Ein einziges `font-weight: 700` auf einer Überschrift verschiebt
|
||||||
|
die ganze Zeile um ein paar Pixel, und ab der zweiten Zeile steht
|
||||||
|
der sichtbare Text neben dem Cursor. Das sieht aus wie ein
|
||||||
|
Browserfehler und ist einer in dieser Datei. `pruef-notizen` misst
|
||||||
|
die Deckung deshalb an echten Bildpunkten nach.
|
||||||
|
|
||||||
|
Fettdruck gibt es trotzdem — aber im KOPF des Blattes, außerhalb
|
||||||
|
des Textflusses: Die erste Zeile steht dort groß als Titel. So
|
||||||
|
bekommt man die Wirkung, ohne die Deckung zu verlieren.
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
WARUM DAS PAPIER DUNKEL IST
|
||||||
|
|
||||||
|
Ein cremeweißes Blatt auf einer dunklen Seite ist eine Lampe. Die
|
||||||
|
Hausregel heißt augenschonend, und sie gilt auch für Papier. Das
|
||||||
|
Papiergefühl trägt hier die STRUKTUR — Linien im Zeilenabstand, ein
|
||||||
|
Randstrich, die Perforation oben —, nicht die Helligkeit. Eine
|
||||||
|
schwarze Kladde ist auch ein Notizbuch.
|
||||||
|
|
||||||
|
Die gewählte Papierfarbe ist die TINTE: Sie färbt Linien, Rand,
|
||||||
|
Kante und Haken. Sechs Farben, alle über 4,5:1 gegen den Grund
|
||||||
|
(siehe notiz-tabellen.js), damit sie auch Text tragen können.
|
||||||
|
===================================================================== */
|
||||||
|
|
||||||
|
.notizblock {
|
||||||
|
/* Der Zeilenabstand ist die Maßeinheit dieser Seite: Er bestimmt die
|
||||||
|
Zeilenhöhe im Text UND den Abstand der Linien auf dem Papier. Eine
|
||||||
|
zweite Zahl wäre die, die beim nächsten Umbau auseinanderläuft --
|
||||||
|
und man sähe es sofort, weil die Schrift dann zwischen den Linien
|
||||||
|
schwimmt. */
|
||||||
|
--n-zeile: 30px;
|
||||||
|
--n-schrift: 15.5px;
|
||||||
|
--n-oben: 26px; /* Innenabstand oben, im Feld wie im Deck */
|
||||||
|
--n-links: 66px; /* bis zum Text -- links davon der Rand */
|
||||||
|
--n-rechts: 24px;
|
||||||
|
--n-randstrich: 46px; /* wo der senkrechte Randstrich sitzt */
|
||||||
|
--n-tinte: var(--ton, var(--akzent));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------
|
||||||
|
Kopfzeile der Seite
|
||||||
|
--------------------------------------------------------------------- */
|
||||||
|
.n-kopf { align-items: flex-end; gap: 16px; flex-wrap: wrap; }
|
||||||
|
.n-kopf__griffe { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
||||||
|
|
||||||
|
.n-suche {
|
||||||
|
display: flex; align-items: center; gap: 8px;
|
||||||
|
padding: 0 12px; height: 40px;
|
||||||
|
background: var(--glas); border: 1px solid var(--rand);
|
||||||
|
border-radius: 999px;
|
||||||
|
}
|
||||||
|
.n-suche:focus-within { border-color: var(--akzent); }
|
||||||
|
.n-suche__zeichen {
|
||||||
|
width: 17px; height: 17px; flex: none;
|
||||||
|
fill: none; stroke: var(--text-leise); stroke-width: 1.9;
|
||||||
|
stroke-linecap: round;
|
||||||
|
}
|
||||||
|
.n-suche__feld {
|
||||||
|
border: 0; background: none; color: var(--text);
|
||||||
|
font: inherit; font-size: .92rem; width: 11ch; min-width: 0;
|
||||||
|
padding: 0; outline: none;
|
||||||
|
}
|
||||||
|
.n-suche__feld::placeholder { color: var(--text-still); }
|
||||||
|
/* Das X, das Chromium in ein Suchfeld setzt, ist auf Dunkel fast
|
||||||
|
unsichtbar und verschiebt beim Erscheinen den Text. Weg damit --
|
||||||
|
geleert wird mit der Escape-Taste, und das steht im Titel. */
|
||||||
|
.n-suche__feld::-webkit-search-cancel-button { display: none; }
|
||||||
|
|
||||||
|
.n-neu { white-space: nowrap; }
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------
|
||||||
|
Der Raum: Stapel links, Blatt rechts
|
||||||
|
--------------------------------------------------------------------- */
|
||||||
|
.n-raum {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(240px, 300px) minmax(0, 1fr);
|
||||||
|
gap: 18px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------
|
||||||
|
Der Stapel
|
||||||
|
--------------------------------------------------------------------- */
|
||||||
|
.n-stapel {
|
||||||
|
display: flex; flex-direction: column; gap: 10px;
|
||||||
|
position: sticky; top: 14px;
|
||||||
|
max-height: calc(100vh - 120px);
|
||||||
|
}
|
||||||
|
.n-stapel__kopf { display: flex; align-items: baseline; gap: 10px; }
|
||||||
|
.n-stapel__titel {
|
||||||
|
margin: 0; font-size: .78rem; font-weight: 700;
|
||||||
|
letter-spacing: .06em; text-transform: uppercase;
|
||||||
|
color: var(--text-still);
|
||||||
|
}
|
||||||
|
.n-zahl {
|
||||||
|
display: inline-block; min-width: 20px; padding: 1px 7px;
|
||||||
|
text-align: center; font-size: .74rem; font-weight: 700;
|
||||||
|
color: var(--text-leise);
|
||||||
|
background: rgba(255, 255, 255, .06); border-radius: 999px;
|
||||||
|
}
|
||||||
|
.n-korb-knopf {
|
||||||
|
margin-left: auto; display: flex; align-items: center; gap: 6px;
|
||||||
|
border: 0; background: none; color: var(--text-still);
|
||||||
|
font: inherit; font-size: .78rem; cursor: pointer; padding: 2px 4px;
|
||||||
|
}
|
||||||
|
.n-korb-knopf:hover { color: var(--text-leise); }
|
||||||
|
|
||||||
|
.n-liste {
|
||||||
|
list-style: none; margin: 0; padding: 0 2px 4px 0;
|
||||||
|
display: flex; flex-direction: column; gap: 8px;
|
||||||
|
overflow-y: auto; min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* EIN BLATT IM STAPEL. Es sieht aus wie ein Zettel mit angeknickter
|
||||||
|
Ecke -- die Ecke ist die einzige Form, die man bei dieser Größe
|
||||||
|
noch als „Papier" liest. */
|
||||||
|
.n-zettel {
|
||||||
|
position: relative; width: 100%; text-align: left;
|
||||||
|
padding: 11px 13px 10px; border: 0; cursor: pointer;
|
||||||
|
font: inherit; color: inherit;
|
||||||
|
background: color-mix(in srgb, var(--n-tinte) 7%, var(--glas));
|
||||||
|
border-radius: 10px;
|
||||||
|
/* Der farbige Streifen links ist die Papierfarbe des Blattes. */
|
||||||
|
box-shadow:
|
||||||
|
inset 3px 0 0 color-mix(in srgb, var(--n-tinte) 70%, transparent),
|
||||||
|
inset 0 0 0 1px var(--rand);
|
||||||
|
transition: background-color .14s ease, box-shadow .14s ease;
|
||||||
|
}
|
||||||
|
.n-zettel:hover {
|
||||||
|
background: color-mix(in srgb, var(--n-tinte) 13%, var(--glas));
|
||||||
|
}
|
||||||
|
.n-zettel[aria-current="true"] {
|
||||||
|
background: color-mix(in srgb, var(--n-tinte) 17%, var(--glas));
|
||||||
|
box-shadow:
|
||||||
|
inset 3px 0 0 var(--n-tinte),
|
||||||
|
inset 0 0 0 1px color-mix(in srgb, var(--n-tinte) 40%, transparent);
|
||||||
|
}
|
||||||
|
/* Die angeknickte Ecke oben rechts. */
|
||||||
|
.n-zettel::after {
|
||||||
|
content: ""; position: absolute; top: 0; right: 0;
|
||||||
|
width: 14px; height: 14px; border-radius: 0 10px 0 6px;
|
||||||
|
background: linear-gradient(225deg,
|
||||||
|
color-mix(in srgb, var(--n-tinte) 30%, transparent) 50%, transparent 50%);
|
||||||
|
}
|
||||||
|
.n-zettel__titel {
|
||||||
|
display: block; font-size: .94rem; color: var(--text);
|
||||||
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||||
|
padding-right: 16px;
|
||||||
|
}
|
||||||
|
.n-zettel__vor {
|
||||||
|
display: block; margin-top: 3px; font-size: .8rem; color: var(--text-still);
|
||||||
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||||
|
}
|
||||||
|
.n-zettel__fuss {
|
||||||
|
display: flex; align-items: center; gap: 8px; margin-top: 7px;
|
||||||
|
font-size: .72rem; color: var(--text-still);
|
||||||
|
}
|
||||||
|
.n-zettel__nadel { color: var(--n-tinte); }
|
||||||
|
.n-zettel__haken {
|
||||||
|
margin-left: auto; padding: 1px 6px; border-radius: 999px;
|
||||||
|
background: color-mix(in srgb, var(--n-tinte) 16%, transparent);
|
||||||
|
color: var(--text-leise);
|
||||||
|
}
|
||||||
|
.n-zettel__haken[data-fertig="ja"] {
|
||||||
|
background: color-mix(in srgb, var(--n-tinte) 30%, transparent);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-leer { margin: 6px 2px; color: var(--text-still); font-size: .88rem; }
|
||||||
|
.n-leer__knopf {
|
||||||
|
border: 0; background: none; padding: 0; font: inherit;
|
||||||
|
color: var(--akzent); text-decoration: underline; cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
DAS BLATT
|
||||||
|
===================================================================== */
|
||||||
|
.n-blatt {
|
||||||
|
position: relative; display: flex; flex-direction: column;
|
||||||
|
min-height: 62vh;
|
||||||
|
background:
|
||||||
|
linear-gradient(to bottom,
|
||||||
|
color-mix(in srgb, var(--n-tinte) 6%, var(--glas-tief)),
|
||||||
|
var(--glas-tief) 180px);
|
||||||
|
border: 1px solid color-mix(in srgb, var(--n-tinte) 22%, var(--rand));
|
||||||
|
border-radius: 14px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DIE PERFORATION. Halbkreise, die in den oberen Rand gestanzt sind --
|
||||||
|
das Einzige an dieser Seite, das sofort „Block" sagt, bevor man ein
|
||||||
|
Wort gelesen hat. Sie sitzt IN der Kante und nicht darunter: Ein
|
||||||
|
Streifen mit Löchern darunter wäre eine Verzierung, eine gestanzte
|
||||||
|
Kante ist ein Blatt. */
|
||||||
|
.n-blatt__perforation {
|
||||||
|
position: absolute; top: 0; left: 0; right: 0; height: 11px; z-index: 4;
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 50% 0,
|
||||||
|
var(--tinte) 0 3.4px, transparent 3.8px) repeat-x 0 0 / 16px 11px,
|
||||||
|
linear-gradient(to bottom,
|
||||||
|
color-mix(in srgb, var(--n-tinte) 34%, transparent),
|
||||||
|
transparent);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-blatt__kopf {
|
||||||
|
display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap;
|
||||||
|
padding: 18px 20px 12px;
|
||||||
|
border-bottom: 1px solid color-mix(in srgb, var(--n-tinte) 16%, transparent);
|
||||||
|
}
|
||||||
|
/* HIER steht der Fettdruck — außerhalb des Textflusses, deshalb
|
||||||
|
unbedenklich (siehe der Kopf dieser Datei). Der Titel ist die erste
|
||||||
|
Zeile des Blattes, live abgeleitet. */
|
||||||
|
.n-blatt__titel {
|
||||||
|
margin: 0; flex: 1 1 14ch; min-width: 0;
|
||||||
|
font-size: 1.32rem; font-weight: 700; line-height: 1.25;
|
||||||
|
color: var(--text);
|
||||||
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||||
|
}
|
||||||
|
.n-blatt__titel[data-leer="ja"] { color: var(--text-still); }
|
||||||
|
.n-blatt__griffe { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||||
|
|
||||||
|
.n-haken-stand {
|
||||||
|
padding: 3px 9px; border-radius: 999px; font-size: .76rem;
|
||||||
|
color: var(--text-leise);
|
||||||
|
background: color-mix(in srgb, var(--n-tinte) 15%, transparent);
|
||||||
|
}
|
||||||
|
.n-haken-stand[data-fertig="ja"] {
|
||||||
|
color: var(--text);
|
||||||
|
background: color-mix(in srgb, var(--n-tinte) 32%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-griff {
|
||||||
|
display: inline-flex; align-items: center; gap: 6px;
|
||||||
|
min-height: 34px; padding: 0 11px;
|
||||||
|
background: none; border: 1px solid var(--rand); border-radius: 9px;
|
||||||
|
color: var(--text-leise); font: inherit; font-size: .82rem; cursor: pointer;
|
||||||
|
transition: border-color .14s ease, color .14s ease, background-color .14s ease;
|
||||||
|
}
|
||||||
|
.n-griff svg {
|
||||||
|
width: 16px; height: 16px; flex: none;
|
||||||
|
fill: none; stroke: currentColor; stroke-width: 1.8;
|
||||||
|
stroke-linecap: round; stroke-linejoin: round;
|
||||||
|
}
|
||||||
|
.n-griff:hover { color: var(--text); border-color: var(--rand-hell); }
|
||||||
|
.n-griff[aria-pressed="true"] {
|
||||||
|
color: var(--n-tinte);
|
||||||
|
border-color: color-mix(in srgb, var(--n-tinte) 55%, transparent);
|
||||||
|
background: color-mix(in srgb, var(--n-tinte) 12%, transparent);
|
||||||
|
}
|
||||||
|
.n-griff--weg:hover { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 50%, transparent); }
|
||||||
|
|
||||||
|
/* Die sechs Papierfarben. Kreise, nicht Kästchen: Ein Kästchen sieht
|
||||||
|
aus wie etwas, das man ankreuzt. */
|
||||||
|
.n-farben { display: flex; align-items: center; gap: 5px; }
|
||||||
|
/* `flex: none` ist nicht Zierde: In einer Reihe mit `align-items:
|
||||||
|
center` dehnt sich ein Knopf sonst auf die Hoehe der Reihe -- im
|
||||||
|
Bildschirmfoto standen statt sechs Kreisen sechs Ovale da. */
|
||||||
|
.n-farbe {
|
||||||
|
flex: none; width: 22px; height: 22px; padding: 0; cursor: pointer;
|
||||||
|
border: 1px solid rgba(255, 255, 255, .16); border-radius: 50%;
|
||||||
|
background: var(--f);
|
||||||
|
transition: transform .12s ease, box-shadow .12s ease;
|
||||||
|
}
|
||||||
|
.n-farbe:hover { transform: scale(1.12); }
|
||||||
|
.n-farbe[aria-pressed="true"] {
|
||||||
|
box-shadow: 0 0 0 2px var(--tinte), 0 0 0 3.6px var(--f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
DAS PAPIER — die zwei Schichten
|
||||||
|
|
||||||
|
ALLES, WAS HIER DOPPELT STEHT, MUSS DOPPELT GLEICH SEIN. Die
|
||||||
|
gemeinsamen Werte stehen deshalb in EINER Regel, die beide
|
||||||
|
Schichten trifft; was nur eine von beiden braucht (Cursorfarbe,
|
||||||
|
Zeigedurchlass), steht einzeln darunter.
|
||||||
|
===================================================================== */
|
||||||
|
.n-papier {
|
||||||
|
position: relative; flex: 1 1 auto; min-height: 320px;
|
||||||
|
overflow: auto;
|
||||||
|
/* Die Liniatur und der Randstrich. `local` sorgt dafür, dass sie
|
||||||
|
MIT dem Text scrollen -- ohne das bliebe das Papier stehen und
|
||||||
|
die Schrift liefe darüber hinweg. */
|
||||||
|
background-image:
|
||||||
|
repeating-linear-gradient(to bottom,
|
||||||
|
transparent 0 calc(var(--n-zeile) - 1px),
|
||||||
|
color-mix(in srgb, var(--n-tinte) 15%, transparent)
|
||||||
|
calc(var(--n-zeile) - 1px) var(--n-zeile)),
|
||||||
|
linear-gradient(to right,
|
||||||
|
transparent 0 var(--n-randstrich),
|
||||||
|
color-mix(in srgb, var(--n-tinte) 34%, transparent) var(--n-randstrich),
|
||||||
|
transparent calc(var(--n-randstrich) + 1px));
|
||||||
|
background-position: 0 calc(var(--n-oben) + 0.72 * var(--n-zeile)), 0 0;
|
||||||
|
background-attachment: local, local;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Die gemeinsame Schreibweise beider Schichten. */
|
||||||
|
.n-papier__feld,
|
||||||
|
.n-papier__deck {
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: var(--n-schrift);
|
||||||
|
line-height: var(--n-zeile);
|
||||||
|
letter-spacing: normal;
|
||||||
|
word-spacing: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
padding: var(--n-oben) var(--n-rechts) 60px var(--n-links);
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-break: normal;
|
||||||
|
tab-size: 2;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-papier__feld {
|
||||||
|
position: relative; z-index: 2;
|
||||||
|
display: block; width: 100%; min-height: 100%;
|
||||||
|
resize: none; overflow: hidden;
|
||||||
|
background: transparent;
|
||||||
|
/* Der Text ist unsichtbar, der Cursor nicht -- gezeichnet wird von
|
||||||
|
der Deckschicht. */
|
||||||
|
color: transparent;
|
||||||
|
caret-color: var(--text);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
/* Die Markierung muss sichtbar bleiben, obwohl die Schrift es nicht
|
||||||
|
ist: Sie liegt UNTER der Deckschicht und färbt deren Grund. */
|
||||||
|
.n-papier__feld::selection {
|
||||||
|
background: color-mix(in srgb, var(--n-tinte) 34%, transparent);
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.n-papier__deck {
|
||||||
|
position: absolute; inset: 0 0 auto 0; z-index: 3;
|
||||||
|
color: var(--text);
|
||||||
|
/* Die Schicht liegt OBEN, lässt aber alles durch -- sonst käme kein
|
||||||
|
Klick mehr ins Schreibfeld. Nur die Haken fangen ihn ab (unten). */
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Die Auszeichnungen -- Farbe und Fläche, nie Breite ------ */
|
||||||
|
|
||||||
|
/* ÜBERSCHRIFT (`# `). Kein Fettdruck, keine größere Schrift -- beides
|
||||||
|
würde die Zeile verschieben. Stattdessen: die volle Textfarbe, ein
|
||||||
|
feiner Schein und eine Linie darunter. Auf dunklem Grund liest sich
|
||||||
|
das genauso als Überschrift, und die Deckung bleibt. */
|
||||||
|
.n-h {
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 0 10px color-mix(in srgb, var(--n-tinte) 55%, transparent);
|
||||||
|
box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--n-tinte) 45%, transparent);
|
||||||
|
}
|
||||||
|
.n-marke { color: color-mix(in srgb, var(--n-tinte) 70%, var(--text-still)); }
|
||||||
|
|
||||||
|
/* HAKEN. Die eckigen Klammern bleiben als Zeichen stehen (sonst
|
||||||
|
verschöbe sich die Zeile), werden aber unsichtbar gefärbt; darüber
|
||||||
|
liegt ein gezeichnetes Kästchen aus einem Rahmen, der keine Fläche
|
||||||
|
beansprucht. */
|
||||||
|
/* DAS KAESTCHEN WIRD GEZEICHNET, NICHT GERAHMT.
|
||||||
|
|
||||||
|
Der erste Anlauf legte einen Innenrahmen (`box-shadow: inset`) um
|
||||||
|
das Stueck Text. Das nimmt die Form des STUECKS an -- und ein
|
||||||
|
Stueck Text ist so hoch wie die Zeile (30 px) und so breit wie
|
||||||
|
drei Zeichen. Im Bildschirmfoto standen deshalb hochkant stehende
|
||||||
|
Pillen da, keine Kaestchen.
|
||||||
|
|
||||||
|
Jetzt zeichnet ein Pseudo-Element ein echtes Quadrat, mittig ueber
|
||||||
|
dem Stueck. Es ist absolut gesetzt und beansprucht damit keinen
|
||||||
|
Platz -- die Deckung mit dem Schreibfeld bleibt unberuehrt, genau
|
||||||
|
wie die Regel im Kopf dieser Datei es verlangt. */
|
||||||
|
.n-hk {
|
||||||
|
position: relative;
|
||||||
|
color: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
.n-hk::before {
|
||||||
|
content: ""; position: absolute;
|
||||||
|
left: 50%; top: 50%; transform: translate(-50%, -50%);
|
||||||
|
width: 16px; height: 16px;
|
||||||
|
border: 1.7px solid color-mix(in srgb, var(--n-tinte) 78%, transparent);
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: border-color .12s ease, background-color .12s ease;
|
||||||
|
}
|
||||||
|
.n-hk:hover::before { border-color: var(--n-tinte); }
|
||||||
|
.n-hk[data-an="ja"]::before {
|
||||||
|
background: var(--n-tinte);
|
||||||
|
border-color: var(--n-tinte);
|
||||||
|
}
|
||||||
|
/* Der Haken selbst -- ebenfalls absolut. */
|
||||||
|
.n-hk[data-an="ja"]::after {
|
||||||
|
content: ""; position: absolute;
|
||||||
|
left: 50%; top: 50%;
|
||||||
|
width: 4px; height: 8.5px;
|
||||||
|
margin: -1px 0 0 -2px;
|
||||||
|
border: solid #07080d; border-width: 0 2.2px 2.2px 0;
|
||||||
|
transform: translate(-50%, -60%) rotate(42deg);
|
||||||
|
}
|
||||||
|
/* Eine erledigte Zeile wird leiser und durchgestrichen. */
|
||||||
|
.n-erledigt { color: var(--text-still); text-decoration: line-through; }
|
||||||
|
|
||||||
|
/* AUFZÄHLUNG, ZITAT, LINK */
|
||||||
|
.n-punkt { color: var(--n-tinte); }
|
||||||
|
.n-zitat {
|
||||||
|
color: var(--text-leise);
|
||||||
|
box-shadow: inset 2px 0 0 color-mix(in srgb, var(--n-tinte) 60%, transparent);
|
||||||
|
}
|
||||||
|
.n-link {
|
||||||
|
color: var(--akzent);
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-color: color-mix(in srgb, var(--akzent) 55%, transparent);
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
pointer-events: auto; cursor: pointer;
|
||||||
|
}
|
||||||
|
/* Der Treffer der Suche. Nur Fläche -- Breite unverändert. */
|
||||||
|
.n-treffer {
|
||||||
|
background: color-mix(in srgb, var(--warn) 40%, transparent);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Der Fuß des Blattes -------------------------------------- */
|
||||||
|
.n-blatt__fuss {
|
||||||
|
display: flex; align-items: center; gap: 12px;
|
||||||
|
padding: 9px 20px 11px;
|
||||||
|
border-top: 1px solid color-mix(in srgb, var(--n-tinte) 14%, transparent);
|
||||||
|
font-size: .78rem; color: var(--text-still);
|
||||||
|
}
|
||||||
|
.n-wann { margin-left: auto; }
|
||||||
|
.n-stand { display: inline-flex; align-items: center; gap: 7px; }
|
||||||
|
/* Der Punkt vor dem Wort. Er ist die eigentliche Auskunft -- man liest
|
||||||
|
die Farbe, bevor man das Wort liest. */
|
||||||
|
.n-stand::before {
|
||||||
|
content: ""; width: 7px; height: 7px; border-radius: 50%;
|
||||||
|
background: color-mix(in srgb, var(--n-tinte) 70%, transparent);
|
||||||
|
}
|
||||||
|
.n-stand[data-stand="sichert"]::before { background: var(--akzent); }
|
||||||
|
.n-stand[data-stand="offen"]::before { background: var(--warn); }
|
||||||
|
.n-stand[data-stand="offen"] { color: var(--warn); }
|
||||||
|
|
||||||
|
/* ---------- Der Papierkorb ------------------------------------------- */
|
||||||
|
.n-korb {
|
||||||
|
background: var(--glas-tief); border: 1px solid var(--rand);
|
||||||
|
border-radius: 14px; padding: 18px 20px 20px;
|
||||||
|
}
|
||||||
|
.n-korb__kopf { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
|
||||||
|
.n-korb__kopf .knopf-still { margin-left: auto; }
|
||||||
|
.n-korb__satz { margin: 8px 0 14px; color: var(--text-still); font-size: .86rem; }
|
||||||
|
.n-liste--korb { max-height: none; }
|
||||||
|
.n-korb-zeile {
|
||||||
|
display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
|
||||||
|
padding: 11px 13px;
|
||||||
|
background: var(--glas); border: 1px solid var(--rand); border-radius: 10px;
|
||||||
|
}
|
||||||
|
.n-korb-zeile__text { flex: 1 1 16ch; min-width: 0; }
|
||||||
|
.n-korb-zeile__titel {
|
||||||
|
display: block; color: var(--text-leise);
|
||||||
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||||
|
}
|
||||||
|
.n-korb-zeile__wann { display: block; font-size: .76rem; color: var(--text-still); }
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
HANDY: eine Spalte, das Blatt legt sich darüber
|
||||||
|
=====================================================================
|
||||||
|
|
||||||
|
Gemessen wird das Haus bei 320, 390 und 430 px. Zwei Spalten
|
||||||
|
bräuchten dort jede rund 160 px -- ein Blatt Papier, auf das keine
|
||||||
|
Zeile passt. Also: Der Stapel IST die Seite, und ein Blatt legt sich
|
||||||
|
davor. Zurück geht es mit dem Knopf der Kopfleiste, den es auf jeder
|
||||||
|
Seite des Hauses gibt.
|
||||||
|
===================================================================== */
|
||||||
|
@media (max-width: 860px) {
|
||||||
|
.notizblock { --n-links: 52px; --n-randstrich: 34px; --n-rechts: 16px; }
|
||||||
|
.n-raum { grid-template-columns: minmax(0, 1fr); }
|
||||||
|
.n-stapel { position: static; max-height: none; }
|
||||||
|
.n-blatt, .n-korb {
|
||||||
|
position: fixed; inset: 0; z-index: 60;
|
||||||
|
border-radius: 0; border-left: 0; border-right: 0;
|
||||||
|
min-height: 0; overflow: auto;
|
||||||
|
/* Unter der Kopfleiste beginnen und über der Fußzeile enden --
|
||||||
|
sonst liegt das Blatt über dem Zurück-Knopf, und man kommt aus
|
||||||
|
ihm nicht mehr heraus. */
|
||||||
|
padding-top: calc(env(safe-area-inset-top, 0px) + 58px);
|
||||||
|
padding-bottom: env(safe-area-inset-bottom, 0px);
|
||||||
|
background: var(--tinte);
|
||||||
|
}
|
||||||
|
.n-blatt { display: flex; }
|
||||||
|
.n-blatt__perforation { top: calc(env(safe-area-inset-top, 0px) + 58px); }
|
||||||
|
.n-blatt__kopf { padding: 14px 16px 10px; }
|
||||||
|
.n-blatt__titel { font-size: 1.15rem; }
|
||||||
|
.n-griff__text { display: none; }
|
||||||
|
.n-griff { padding: 0 9px; }
|
||||||
|
/* Das Suchfeld nimmt, was uebrig ist. Mit einer festen Breite von
|
||||||
|
acht Zeichen stand im Bildschirmfoto „Suche" mit abgeschnittenem
|
||||||
|
n da -- ein Feld, das seinen eigenen Namen nicht fasst. */
|
||||||
|
.n-suche { flex: 1 1 150px; min-width: 0; }
|
||||||
|
.n-suche__feld { width: 100%; }
|
||||||
|
.n-kopf__griffe { width: 100%; }
|
||||||
|
/* Die Tastatur des Handys verdeckt sonst die letzte Zeile. */
|
||||||
|
.n-papier__feld, .n-papier__deck { padding-bottom: 45vh; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wer weniger Bewegung möchte, bekommt keine. */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.n-zettel, .n-griff, .n-farbe { transition: none; }
|
||||||
|
.n-farbe:hover { transform: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Im Kontrastmodus trägt allein die Kante. Verläufe und
|
||||||
|
Farbmischungen würden dort zu grauen Flächen. */
|
||||||
|
@media (forced-colors: active) {
|
||||||
|
.n-papier { background-image: none; }
|
||||||
|
.n-blatt__perforation { display: none; }
|
||||||
|
.n-hk { box-shadow: none; border: 1px solid CanvasText; color: CanvasText; }
|
||||||
|
.n-hk[data-an="ja"] { background: Highlight; }
|
||||||
|
}
|
||||||
+216
-138
@@ -888,10 +888,10 @@ body.start > main { position: relative; z-index: 1; }
|
|||||||
Unterschied), und jede Farbe haelt 4,5:1 gegen den dunklen Grund --
|
Unterschied), und jede Farbe haelt 4,5:1 gegen den dunklen Grund --
|
||||||
sie traegt auch Text. Wer sie neu rechnet: Das Werkzeug bricht ab,
|
sie traegt auch Text. Wer sie neu rechnet: Das Werkzeug bricht ab,
|
||||||
wenn das Ergebnis schlechter waere als der alte Stand. */
|
wenn das Ergebnis schlechter waere als der alte Stand. */
|
||||||
[data-ton="1"] { --ton: #aa6c90; } /* Dashboard */
|
[data-ton="1"] { --ton: #0093b1; } /* Dashboard */
|
||||||
[data-ton="2"] { --ton: #d050a4; } /* Zahlen */
|
[data-ton="2"] { --ton: #e76c9f; } /* Zahlen */
|
||||||
[data-ton="3"] { --ton: #18b803; } /* LIVE */
|
[data-ton="3"] { --ton: #18b803; } /* LIVE */
|
||||||
[data-ton="4"] { --ton: #c16302; } /* Chat */
|
[data-ton="4"] { --ton: #b46f1e; } /* Chat */
|
||||||
/* TON 5 IST VON HAND GESETZT (09.09.2026, screen4).
|
/* TON 5 IST VON HAND GESETZT (09.09.2026, screen4).
|
||||||
|
|
||||||
Filipe: "ich will dass die kategorie eine farbe bekommt die extrem
|
Filipe: "ich will dass die kategorie eine farbe bekommt die extrem
|
||||||
@@ -925,22 +925,22 @@ body.start > main { position: relative; z-index: 1; }
|
|||||||
gewünscht ("alle kacheln auf der ganzen website sollen sein wie auf
|
gewünscht ("alle kacheln auf der ganzen website sollen sein wie auf
|
||||||
der startseite"). Was sie heraushebt, ist die Farbe, nicht eine
|
der startseite"). Was sie heraushebt, ist die Farbe, nicht eine
|
||||||
Sonderform. */
|
Sonderform. */
|
||||||
[data-ton="5"] { --ton: #fe04d8; } /* Schutz & Regeln -- Wunsch: extrem auffällig */
|
[data-ton="5"] { --ton: #fc33fc; } /* Schutz & Regeln -- Wunsch: extrem auffällig */
|
||||||
[data-ton="6"] { --ton: #b699fe; } /* Community */
|
[data-ton="6"] { --ton: #c6a5ff; } /* Community */
|
||||||
[data-ton="7"] { --ton: #098f71; } /* Reports */
|
[data-ton="7"] { --ton: #009369; } /* Reports */
|
||||||
[data-ton="8"] { --ton: #ab68ff; } /* Kalender */
|
[data-ton="8"] { --ton: #ab60ff; } /* Kalender */
|
||||||
[data-ton="9"] { --ton: #ceae6e; } /* Bibliothek */
|
[data-ton="9"] { --ton: #e703c6; } /* Bibliothek */
|
||||||
[data-ton="10"] { --ton: #fe667e; } /* Steckbrief */
|
[data-ton="10"] { --ton: #ff7269; } /* Steckbrief */
|
||||||
[data-ton="11"] { --ton: #da8a06; } /* Personen */
|
[data-ton="11"] { --ton: #db8a0c; } /* Personen */
|
||||||
[data-ton="12"] { --ton: #7eca8c; } /* Content */
|
[data-ton="12"] { --ton: #0fd88a; } /* Content */
|
||||||
[data-ton="13"] { --ton: #12b37e; } /* Aufgaben */
|
[data-ton="13"] { --ton: #12b37e; } /* Aufgaben */
|
||||||
[data-ton="14"] { --ton: #ffbd4a; } /* Technik */
|
[data-ton="14"] { --ton: #ffb742; } /* Technik */
|
||||||
[data-ton="15"] { --ton: #9ce6bc; } /* Calls */
|
[data-ton="15"] { --ton: #00d5fc; } /* Calls */
|
||||||
[data-ton="16"] { --ton: #28aeba; } /* Start-Check */
|
[data-ton="16"] { --ton: #00b1d5; } /* Start-Check */
|
||||||
[data-ton="17"] { --ton: #cace02; } /* Agentur */
|
[data-ton="17"] { --ton: #cace02; } /* Agentur */
|
||||||
[data-ton="18"] { --ton: #9a86ca; } /* Automationen */
|
[data-ton="18"] { --ton: #a28aff; } /* Automationen */
|
||||||
[data-ton="19"] { --ton: #08eb9f; } /* Dateien */
|
[data-ton="19"] { --ton: #00f9a8; } /* Dateien */
|
||||||
[data-ton="20"] { --ton: #d48a92; } /* Creator-Profile */
|
[data-ton="20"] { --ton: #ba7bc3; } /* Creator-Profile */
|
||||||
/* TON 21 IST VON HAND GESETZT (09.09.2026, screen6).
|
/* TON 21 IST VON HAND GESETZT (09.09.2026, screen6).
|
||||||
|
|
||||||
Filipe: "die farbe von dieser kategorie soll knall rot sein."
|
Filipe: "die farbe von dieser kategorie soll knall rot sein."
|
||||||
@@ -958,7 +958,7 @@ body.start > main { position: relative; z-index: 1; }
|
|||||||
Kontrast auf dem dunklen Grund: 5,01:1 (noetig 4,5). Von sechs
|
Kontrast auf dem dunklen Grund: 5,01:1 (noetig 4,5). Von sechs
|
||||||
geprueften Rottoenen hat dieser den groessten Abstand UND genug
|
geprueften Rottoenen hat dieser den groessten Abstand UND genug
|
||||||
Kontrast -- deshalb dieser und nicht der erstbeste. */
|
Kontrast -- deshalb dieser und nicht der erstbeste. */
|
||||||
[data-ton="21"] { --ton: #d6525e; } /* Scout-Pipeline -- Wunsch: knallrot */
|
[data-ton="21"] { --ton: #db4b66; } /* Scout-Pipeline -- Wunsch: knallrot */
|
||||||
|
|
||||||
/* TON 22 -- TEAM-LAGE (09.09.2026).
|
/* TON 22 -- TEAM-LAGE (09.09.2026).
|
||||||
|
|
||||||
@@ -975,13 +975,13 @@ body.start > main { position: relative; z-index: 1; }
|
|||||||
Warm und hell ist kein Zufall: Diese eine Kachel gehoert der
|
Warm und hell ist kein Zufall: Diese eine Kachel gehoert der
|
||||||
Leitung. Zwischen den kuehlen Toenen der Arbeitsbereiche faellt ein
|
Leitung. Zwischen den kuehlen Toenen der Arbeitsbereiche faellt ein
|
||||||
warmer auf, ohne lauter zu sein. */
|
warmer auf, ohne lauter zu sein. */
|
||||||
[data-ton="22"] { --ton: #fee0b2; } /* Team-Lage */
|
[data-ton="22"] { --ton: #ffde7b; } /* Team-Lage */
|
||||||
/* Ton 23 (10.09.2026, Ideen-Board). NICHT nach Gefuehl gewaehlt: Die
|
/* Ton 23 (10.09.2026, Ideen-Board). NICHT nach Gefuehl gewaehlt: Die
|
||||||
22 vorhandenen Toene sind dicht besetzt, deshalb wurde der Abstand zu
|
22 vorhandenen Toene sind dicht besetzt, deshalb wurde der Abstand zu
|
||||||
allen ausgerechnet. #5f8a9f hat mit 127 den zweitgroessten -- der
|
allen ausgerechnet. #5f8a9f hat mit 127 den zweitgroessten -- der
|
||||||
groesste war ein Graubraun, und eine graue Kachel zwischen bunten
|
groesste war ein Graubraun, und eine graue Kachel zwischen bunten
|
||||||
sieht abgeschaltet aus. Gedeckt statt grell, wie alles hier. */
|
sieht abgeschaltet aus. Gedeckt statt grell, wie alles hier. */
|
||||||
[data-ton="23"] { --ton: #08b5fc; } /* Ideen-Board */
|
[data-ton="23"] { --ton: #75b7ff; } /* Ideen-Board */
|
||||||
|
|
||||||
/* Ton 24 (10.09.2026, Eingang). Wieder gerechnet statt gewaehlt, und bei
|
/* Ton 24 (10.09.2026, Eingang). Wieder gerechnet statt gewaehlt, und bei
|
||||||
23 belegten Toenen wird es eng: Der Abstand im CIELAB-Raum zum
|
23 belegten Toenen wird es eng: Der Abstand im CIELAB-Raum zum
|
||||||
@@ -994,7 +994,7 @@ body.start > main { position: relative; z-index: 1; }
|
|||||||
schlechtesten zu fokussieren. Kraeftiges Gruen kam auf 39,5, hat aber
|
schlechtesten zu fokussieren. Kraeftiges Gruen kam auf 39,5, hat aber
|
||||||
L* 81 und wuerde leuchten. Dieser Ton hat eine Leuchtdichte von 0,109
|
L* 81 und wuerde leuchten. Dieser Ton hat eine Leuchtdichte von 0,109
|
||||||
-- dunkel genug, um neben den anderen ruhig zu bleiben. */
|
-- dunkel genug, um neben den anderen ruhig zu bleiben. */
|
||||||
[data-ton="24"] { --ton: #d21afd; } /* Eingang */
|
[data-ton="24"] { --ton: #cf1bfc; } /* Eingang */
|
||||||
|
|
||||||
/* TON 25 -- Rueckmeldung (10.09.2026).
|
/* TON 25 -- Rueckmeldung (10.09.2026).
|
||||||
|
|
||||||
@@ -1032,8 +1032,8 @@ body.start > main { position: relative; z-index: 1; }
|
|||||||
Wenn eine achtundzwanzigste Kachel kommt, ist das der Zeitpunkt, an
|
Wenn eine achtundzwanzigste Kachel kommt, ist das der Zeitpunkt, an
|
||||||
dem die Kacheln Gruppenfarben brauchen statt Einzelfarben -- nicht
|
dem die Kacheln Gruppenfarben brauchen statt Einzelfarben -- nicht
|
||||||
der, an dem man den naechsten Ton dazwischenquetscht. */
|
der, an dem man den naechsten Ton dazwischenquetscht. */
|
||||||
[data-ton="26"] { --ton: #07fff1; } /* Eure Aufgaben (bis 19.09. "Entwicklung") */
|
[data-ton="26"] { --ton: #0cfff0; } /* Eure Aufgaben (bis 19.09. "Entwicklung") */
|
||||||
[data-ton="27"] { --ton: #e871ff; } /* Talente */
|
[data-ton="27"] { --ton: #e472ff; } /* Talente */
|
||||||
|
|
||||||
/* =====================================================================
|
/* =====================================================================
|
||||||
DIE SIEBEN TOENE DES TREFFS (11.09.2026)
|
DIE SIEBEN TOENE DES TREFFS (11.09.2026)
|
||||||
@@ -1059,12 +1059,12 @@ body.start > main { position: relative; z-index: 1; }
|
|||||||
Hand gewaehlte Farbfamilie im selben Blau bei 12,6. Die Rechnung war
|
Hand gewaehlte Farbfamilie im selben Blau bei 12,6. Die Rechnung war
|
||||||
also nicht Genauigkeitsspielerei, sondern der Unterschied zwischen
|
also nicht Genauigkeitsspielerei, sondern der Unterschied zwischen
|
||||||
unterscheidbar und nicht. */
|
unterscheidbar und nicht. */
|
||||||
[data-ton="28"] { --ton: #069511; } /* Regeln & Hilfe */
|
[data-ton="28"] { --ton: #03960f; } /* Regeln & Hilfe */
|
||||||
[data-ton="29"] { --ton: #f10778; } /* Highlights */
|
[data-ton="29"] { --ton: #f60684; } /* Highlights */
|
||||||
[data-ton="30"] { --ton: #a7ff98; } /* Wunschliste */
|
[data-ton="30"] { --ton: #abff96; } /* Wunschliste */
|
||||||
[data-ton="31"] { --ton: #0ed1d0; } /* Was ansteht */
|
[data-ton="31"] { --ton: #2dc9ba; } /* Was ansteht */
|
||||||
[data-ton="32"] { --ton: #0687bc; } /* Mitmachen */
|
[data-ton="32"] { --ton: #577ecc; } /* Mitmachen */
|
||||||
[data-ton="33"] { --ton: #f4acff; } /* Anschlagbrett */
|
[data-ton="33"] { --ton: #ffabff; } /* Anschlagbrett */
|
||||||
[data-ton="34"] { --ton: #a2af10; } /* Der Treff */
|
[data-ton="34"] { --ton: #a2af10; } /* Der Treff */
|
||||||
|
|
||||||
/* DER 35. TON (11.09.2026) — für „Wer sieht was".
|
/* DER 35. TON (11.09.2026) — für „Wer sieht was".
|
||||||
@@ -1091,7 +1091,7 @@ body.start > main { position: relative; z-index: 1; }
|
|||||||
mit einer anderen geteilt. Zwei Dinge in einer Farbe heißt, dass die
|
mit einer anderen geteilt. Zwei Dinge in einer Farbe heißt, dass die
|
||||||
Farbe nichts mehr sagt — und pruef-start-ansicht zählt genau das
|
Farbe nichts mehr sagt — und pruef-start-ansicht zählt genau das
|
||||||
nach ("34 Farben auf 35 Kacheln" war ihre Meldung). */
|
nach ("34 Farben auf 35 Kacheln" war ihre Meldung). */
|
||||||
[data-ton="35"] { --ton: #fe8ebe; } /* Wer sieht was */
|
[data-ton="35"] { --ton: #ff90c0; } /* Wer sieht was */
|
||||||
/* Der 36., nach derselben Rechnung: #7c2771, Abstand 29,7 zu den beiden
|
/* Der 36., nach derselben Rechnung: #7c2771, Abstand 29,7 zu den beiden
|
||||||
nächsten (#8e5b77 „Highlights", #2f2f7f „Wer sieht was"). Auch das
|
nächsten (#8e5b77 „Highlights", #2f2f7f „Wer sieht was"). Auch das
|
||||||
liegt über dem schwächsten Paar der vorhandenen Töne (24,9). */
|
liegt über dem schwächsten Paar der vorhandenen Töne (24,9). */
|
||||||
@@ -1131,11 +1131,11 @@ body.start > main { position: relative; z-index: 1; }
|
|||||||
sind. Mehr ist im lesbaren Bereich nicht zu holen; wer den naechsten
|
sind. Mehr ist im lesbaren Bereich nicht zu holen; wer den naechsten
|
||||||
hinzufuegt, sollte das wissen und notfalls einen alten verschieben,
|
hinzufuegt, sollte das wissen und notfalls einen alten verschieben,
|
||||||
statt die Schwelle stillschweigend zu senken. */
|
statt die Schwelle stillschweigend zu senken. */
|
||||||
[data-ton="39"] { --ton: #ff57ba; } /* Vertraulich melden */
|
[data-ton="39"] { --ton: #ff51c9; } /* Vertraulich melden */
|
||||||
[data-ton="40"] { --ton: #ff9d82; } /* Entwicklung (die Auswertung) */
|
[data-ton="40"] { --ton: #ff9f7b; } /* Entwicklung (die Auswertung) */
|
||||||
[data-ton="41"] { --ton: #6c8efe; } /* Treff-Chat */
|
[data-ton="41"] { --ton: #5499ff; } /* Treff-Chat */
|
||||||
[data-ton="42"] { --ton: #918a01; } /* Material (Platzhalter, wird gerechnet) */
|
[data-ton="42"] { --ton: #918a01; } /* Material (Platzhalter, wird gerechnet) */
|
||||||
[data-ton="43"] { --ton: #027afb; } /* Calls & Protokolle (gerechnet 22.09.2026) */
|
[data-ton="43"] { --ton: #007bff; } /* Calls & Protokolle (gerechnet 22.09.2026) */
|
||||||
/* GEWUENSCHT UND NACHGEMESSEN (25.09.2026)
|
/* GEWUENSCHT UND NACHGEMESSEN (25.09.2026)
|
||||||
|
|
||||||
Filipe zum Bildschirmfoto der Support-Seite: „ich will dass du bitte
|
Filipe zum Bildschirmfoto der Support-Seite: „ich will dass du bitte
|
||||||
@@ -1178,7 +1178,8 @@ body.start > main { position: relative; z-index: 1; }
|
|||||||
(vorher hielt es eine zu blasse Farbe, weil ihr Abstand gut war).
|
(vorher hielt es eine zu blasse Farbe, weil ihr Abstand gut war).
|
||||||
Wer diesen Ton spaeter aufhellen will, muss den Abstand neu rechnen
|
Wer diesen Ton spaeter aufhellen will, muss den Abstand neu rechnen
|
||||||
-- nicht nach Gefuehl nachbessern. */
|
-- nicht nach Gefuehl nachbessern. */
|
||||||
[data-ton="45"] { --ton: #fe660e; } /* Unterstützen (gerechnet 24.09.2026) */
|
[data-ton="45"] { --ton: #e76006; } /* Unterstützen (gerechnet 24.09.2026) */
|
||||||
|
[data-ton="46"] { --ton: #9cde90; } /* Notizen */
|
||||||
/* WARUM ES KEINEN TON 46 GIBT (25.09.2026)
|
/* WARUM ES KEINEN TON 46 GIBT (25.09.2026)
|
||||||
|
|
||||||
Die Tuer ins andere Haus trug bis heute Ton 40 -- dieselbe Nummer
|
Die Tuer ins andere Haus trug bis heute Ton 40 -- dieselbe Nummer
|
||||||
@@ -8533,8 +8534,8 @@ body.start .k-leiste .feldschild::after {
|
|||||||
|
|
||||||
/* ---- ANFANG GERECHNET: kachel-regenbogen.mjs ---- */
|
/* ---- ANFANG GERECHNET: kachel-regenbogen.mjs ---- */
|
||||||
/* ERZEUGT VON tools/kachel-regenbogen.mjs — NICHT VON HAND ÄNDERN.
|
/* ERZEUGT VON tools/kachel-regenbogen.mjs — NICHT VON HAND ÄNDERN.
|
||||||
99 Punkte in drei Größen, jede Größe einmal alle
|
102 Punkte in drei Größen, jede Größe einmal alle
|
||||||
33 benutzten Kacheltöne. Die Liste ist aus den
|
34 benutzten Kacheltöne. Die Liste ist aus den
|
||||||
Kachellisten abgeleitet; wer eine Kachel hinzufügt, lässt das
|
Kachellisten abgeleitet; wer eine Kachel hinzufügt, lässt das
|
||||||
Werkzeug erneut laufen. pruef-kachelfarben zählt nach, ob beides
|
Werkzeug erneut laufen. pruef-kachelfarben zählt nach, ob beides
|
||||||
noch zusammenpasst. */
|
noch zusammenpasst. */
|
||||||
@@ -8562,113 +8563,116 @@ body.start .k-leiste .feldschild::after {
|
|||||||
/* 3. DIE GROSSEN PUNKTE, jeder mit Hof — das ist der Unterschied
|
/* 3. DIE GROSSEN PUNKTE, jeder mit Hof — das ist der Unterschied
|
||||||
zwischen einem Licht und einem gestanzten Kreis. Dieselbe
|
zwischen einem Licht und einem gestanzten Kreis. Dieselbe
|
||||||
Bauart wie die „nahen Sterne" auf jeder anderen Kachel. */
|
Bauart wie die „nahen Sterne" auf jeder anderen Kachel. */
|
||||||
radial-gradient(4.82px 4.82px at 21.3px 22.8px, color-mix(in srgb, #07fff1 88%, #ffffff), color-mix(in srgb, #07fff1 88%, #ffffff) 34%, color-mix(in srgb, #07fff1 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.82px 4.82px at 21.3px 22.8px, color-mix(in srgb, #2dc9ba 88%, #ffffff), color-mix(in srgb, #2dc9ba 88%, #ffffff) 34%, color-mix(in srgb, #2dc9ba 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(5.48px 5.48px at 50.6px 7.0px, color-mix(in srgb, #fe8ebe 88%, #ffffff), color-mix(in srgb, #fe8ebe 88%, #ffffff) 34%, color-mix(in srgb, #fe8ebe 55%, transparent) 52%, transparent 100%),
|
radial-gradient(5.48px 5.48px at 50.6px 7.0px, color-mix(in srgb, #ffabff 88%, #ffffff), color-mix(in srgb, #ffabff 88%, #ffffff) 34%, color-mix(in srgb, #ffabff 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(6.57px 6.57px at 81.2px 6.7px, color-mix(in srgb, #72d512 88%, #ffffff), color-mix(in srgb, #72d512 88%, #ffffff) 34%, color-mix(in srgb, #72d512 55%, transparent) 52%, transparent 100%),
|
radial-gradient(6.57px 6.57px at 81.2px 6.7px, color-mix(in srgb, #918a01 88%, #ffffff), color-mix(in srgb, #918a01 88%, #ffffff) 34%, color-mix(in srgb, #918a01 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.71px 4.71px at 110.3px 23.0px, color-mix(in srgb, #6c8efe 88%, #ffffff), color-mix(in srgb, #6c8efe 88%, #ffffff) 34%, color-mix(in srgb, #6c8efe 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.71px 4.71px at 110.3px 23.0px, color-mix(in srgb, #12b37e 88%, #ffffff), color-mix(in srgb, #12b37e 88%, #ffffff) 34%, color-mix(in srgb, #12b37e 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.88px 4.88px at 154.3px 8.0px, color-mix(in srgb, #c16302 88%, #ffffff), color-mix(in srgb, #c16302 88%, #ffffff) 34%, color-mix(in srgb, #c16302 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.88px 4.88px at 154.3px 8.0px, color-mix(in srgb, #e472ff 88%, #ffffff), color-mix(in srgb, #e472ff 88%, #ffffff) 34%, color-mix(in srgb, #e472ff 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.83px 4.83px at 185.7px 11.5px, color-mix(in srgb, #12b37e 88%, #ffffff), color-mix(in srgb, #12b37e 88%, #ffffff) 34%, color-mix(in srgb, #12b37e 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.83px 4.83px at 185.7px 11.5px, color-mix(in srgb, #ffb742 88%, #ffffff), color-mix(in srgb, #ffb742 88%, #ffffff) 34%, color-mix(in srgb, #ffb742 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.33px 4.33px at 16.5px 42.0px, color-mix(in srgb, #ff57ba 88%, #ffffff), color-mix(in srgb, #ff57ba 88%, #ffffff) 34%, color-mix(in srgb, #ff57ba 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.33px 4.33px at 16.5px 42.0px, color-mix(in srgb, #00f9a8 88%, #ffffff), color-mix(in srgb, #00f9a8 88%, #ffffff) 34%, color-mix(in srgb, #00f9a8 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(5.17px 5.17px at 49.7px 52.7px, color-mix(in srgb, #a2af10 88%, #ffffff), color-mix(in srgb, #a2af10 88%, #ffffff) 34%, color-mix(in srgb, #a2af10 55%, transparent) 52%, transparent 100%),
|
radial-gradient(5.17px 5.17px at 49.7px 52.7px, color-mix(in srgb, #cf1bfc 88%, #ffffff), color-mix(in srgb, #cf1bfc 88%, #ffffff) 34%, color-mix(in srgb, #cf1bfc 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.63px 4.63px at 82.5px 47.6px, color-mix(in srgb, #027afb 88%, #ffffff), color-mix(in srgb, #027afb 88%, #ffffff) 34%, color-mix(in srgb, #027afb 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.63px 4.63px at 82.5px 47.6px, color-mix(in srgb, #db8a0c 88%, #ffffff), color-mix(in srgb, #db8a0c 88%, #ffffff) 34%, color-mix(in srgb, #db8a0c 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.55px 4.55px at 105.1px 54.1px, color-mix(in srgb, #fe660e 88%, #ffffff), color-mix(in srgb, #fe660e 88%, #ffffff) 34%, color-mix(in srgb, #fe660e 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.55px 4.55px at 105.1px 54.1px, color-mix(in srgb, #03960f 88%, #ffffff), color-mix(in srgb, #03960f 88%, #ffffff) 34%, color-mix(in srgb, #03960f 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(6.07px 6.07px at 149.6px 56.3px, color-mix(in srgb, #08eb9f 88%, #ffffff), color-mix(in srgb, #08eb9f 88%, #ffffff) 34%, color-mix(in srgb, #08eb9f 55%, transparent) 52%, transparent 100%),
|
radial-gradient(6.07px 6.07px at 149.6px 56.3px, color-mix(in srgb, #ab60ff 88%, #ffffff), color-mix(in srgb, #ab60ff 88%, #ffffff) 34%, color-mix(in srgb, #ab60ff 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.34px 4.34px at 182.8px 53.3px, color-mix(in srgb, #f4acff 88%, #ffffff), color-mix(in srgb, #f4acff 88%, #ffffff) 34%, color-mix(in srgb, #f4acff 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.34px 4.34px at 182.8px 53.3px, color-mix(in srgb, #b46f1e 88%, #ffffff), color-mix(in srgb, #b46f1e 88%, #ffffff) 34%, color-mix(in srgb, #b46f1e 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(5.58px 5.58px at 14.4px 89.8px, color-mix(in srgb, #cace02 88%, #ffffff), color-mix(in srgb, #cace02 88%, #ffffff) 34%, color-mix(in srgb, #cace02 55%, transparent) 52%, transparent 100%),
|
radial-gradient(5.58px 5.58px at 14.4px 89.8px, color-mix(in srgb, #18b803 88%, #ffffff), color-mix(in srgb, #18b803 88%, #ffffff) 34%, color-mix(in srgb, #18b803 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.55px 4.55px at 56.4px 81.9px, color-mix(in srgb, #a8d8ff 88%, #ffffff), color-mix(in srgb, #a8d8ff 88%, #ffffff) 34%, color-mix(in srgb, #a8d8ff 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.55px 4.55px at 56.4px 81.9px, color-mix(in srgb, #c6a5ff 88%, #ffffff), color-mix(in srgb, #c6a5ff 88%, #ffffff) 34%, color-mix(in srgb, #c6a5ff 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(5.65px 5.65px at 78.8px 88.9px, color-mix(in srgb, #ff9d82 88%, #ffffff), color-mix(in srgb, #ff9d82 88%, #ffffff) 34%, color-mix(in srgb, #ff9d82 55%, transparent) 52%, transparent 100%),
|
radial-gradient(5.65px 5.65px at 78.8px 88.9px, color-mix(in srgb, #e76006 88%, #ffffff), color-mix(in srgb, #e76006 88%, #ffffff) 34%, color-mix(in srgb, #e76006 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.85px 4.85px at 113.3px 78.4px, color-mix(in srgb, #069511 88%, #ffffff), color-mix(in srgb, #069511 88%, #ffffff) 34%, color-mix(in srgb, #069511 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.85px 4.85px at 113.3px 78.4px, color-mix(in srgb, #9cde90 88%, #ffffff), color-mix(in srgb, #9cde90 88%, #ffffff) 34%, color-mix(in srgb, #9cde90 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.40px 4.40px at 142.4px 90.0px, color-mix(in srgb, #e871ff 88%, #ffffff), color-mix(in srgb, #e871ff 88%, #ffffff) 34%, color-mix(in srgb, #e871ff 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.40px 4.40px at 142.4px 90.0px, color-mix(in srgb, #577ecc 88%, #ffffff), color-mix(in srgb, #577ecc 88%, #ffffff) 34%, color-mix(in srgb, #577ecc 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.65px 4.65px at 190.5px 85.8px, color-mix(in srgb, #eeef08 88%, #ffffff), color-mix(in srgb, #eeef08 88%, #ffffff) 34%, color-mix(in srgb, #eeef08 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.65px 4.65px at 190.5px 85.8px, color-mix(in srgb, #ff9f7b 88%, #ffffff), color-mix(in srgb, #ff9f7b 88%, #ffffff) 34%, color-mix(in srgb, #ff9f7b 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(6.19px 6.19px at 20.7px 123.9px, color-mix(in srgb, #08b5fc 88%, #ffffff), color-mix(in srgb, #08b5fc 88%, #ffffff) 34%, color-mix(in srgb, #08b5fc 55%, transparent) 52%, transparent 100%),
|
radial-gradient(6.19px 6.19px at 20.7px 123.9px, color-mix(in srgb, #abff96 88%, #ffffff), color-mix(in srgb, #abff96 88%, #ffffff) 34%, color-mix(in srgb, #abff96 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(5.44px 5.44px at 56.7px 116.6px, color-mix(in srgb, #ff1a1a 88%, #ffffff), color-mix(in srgb, #ff1a1a 88%, #ffffff) 34%, color-mix(in srgb, #ff1a1a 55%, transparent) 52%, transparent 100%),
|
radial-gradient(5.44px 5.44px at 56.7px 116.6px, color-mix(in srgb, #5499ff 88%, #ffffff), color-mix(in srgb, #5499ff 88%, #ffffff) 34%, color-mix(in srgb, #5499ff 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(6.28px 6.28px at 89.2px 122.9px, color-mix(in srgb, #18b803 88%, #ffffff), color-mix(in srgb, #18b803 88%, #ffffff) 34%, color-mix(in srgb, #18b803 55%, transparent) 52%, transparent 100%),
|
radial-gradient(6.28px 6.28px at 89.2px 122.9px, color-mix(in srgb, #ff1a1a 88%, #ffffff), color-mix(in srgb, #ff1a1a 88%, #ffffff) 34%, color-mix(in srgb, #ff1a1a 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.52px 4.52px at 105.6px 108.9px, color-mix(in srgb, #d21afd 88%, #ffffff), color-mix(in srgb, #d21afd 88%, #ffffff) 34%, color-mix(in srgb, #d21afd 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.52px 4.52px at 105.6px 108.9px, color-mix(in srgb, #61ff09 88%, #ffffff), color-mix(in srgb, #61ff09 88%, #ffffff) 34%, color-mix(in srgb, #61ff09 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.31px 4.31px at 150.4px 108.8px, color-mix(in srgb, #918a01 88%, #ffffff), color-mix(in srgb, #918a01 88%, #ffffff) 34%, color-mix(in srgb, #918a01 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.31px 4.31px at 150.4px 108.8px, color-mix(in srgb, #007bff 88%, #ffffff), color-mix(in srgb, #007bff 88%, #ffffff) 34%, color-mix(in srgb, #007bff 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(5.83px 5.83px at 189.4px 117.4px, color-mix(in srgb, #0687bc 88%, #ffffff), color-mix(in srgb, #0687bc 88%, #ffffff) 34%, color-mix(in srgb, #0687bc 55%, transparent) 52%, transparent 100%),
|
radial-gradient(5.83px 5.83px at 189.4px 117.4px, color-mix(in srgb, #ff7269 88%, #ffffff), color-mix(in srgb, #ff7269 88%, #ffffff) 34%, color-mix(in srgb, #ff7269 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.38px 4.38px at 10.9px 139.1px, color-mix(in srgb, #fe667e 88%, #ffffff), color-mix(in srgb, #fe667e 88%, #ffffff) 34%, color-mix(in srgb, #fe667e 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.38px 4.38px at 10.9px 139.1px, color-mix(in srgb, #72d512 88%, #ffffff), color-mix(in srgb, #72d512 88%, #ffffff) 34%, color-mix(in srgb, #72d512 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(6.29px 6.29px at 54.7px 155.9px, color-mix(in srgb, #a7ff98 88%, #ffffff), color-mix(in srgb, #a7ff98 88%, #ffffff) 34%, color-mix(in srgb, #a7ff98 55%, transparent) 52%, transparent 100%),
|
radial-gradient(6.29px 6.29px at 54.7px 155.9px, color-mix(in srgb, #75b7ff 88%, #ffffff), color-mix(in srgb, #75b7ff 88%, #ffffff) 34%, color-mix(in srgb, #75b7ff 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(6.12px 6.12px at 75.1px 154.0px, color-mix(in srgb, #ab68ff 88%, #ffffff), color-mix(in srgb, #ab68ff 88%, #ffffff) 34%, color-mix(in srgb, #ab68ff 55%, transparent) 52%, transparent 100%),
|
radial-gradient(6.12px 6.12px at 75.1px 154.0px, color-mix(in srgb, #f60684 88%, #ffffff), color-mix(in srgb, #f60684 88%, #ffffff) 34%, color-mix(in srgb, #f60684 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(6.08px 6.08px at 123.3px 154.7px, color-mix(in srgb, #ffbd4a 88%, #ffffff), color-mix(in srgb, #ffbd4a 88%, #ffffff) 34%, color-mix(in srgb, #ffbd4a 55%, transparent) 52%, transparent 100%),
|
radial-gradient(6.08px 6.08px at 123.3px 154.7px, color-mix(in srgb, #a2af10 88%, #ffffff), color-mix(in srgb, #a2af10 88%, #ffffff) 34%, color-mix(in srgb, #a2af10 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(5.81px 5.81px at 141.2px 142.7px, color-mix(in srgb, #0ed1d0 88%, #ffffff), color-mix(in srgb, #0ed1d0 88%, #ffffff) 34%, color-mix(in srgb, #0ed1d0 55%, transparent) 52%, transparent 100%),
|
radial-gradient(5.81px 5.81px at 141.2px 142.7px, color-mix(in srgb, #a8d8ff 88%, #ffffff), color-mix(in srgb, #a8d8ff 88%, #ffffff) 34%, color-mix(in srgb, #a8d8ff 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.42px 4.42px at 177.4px 144.5px, color-mix(in srgb, #f10778 88%, #ffffff), color-mix(in srgb, #f10778 88%, #ffffff) 34%, color-mix(in srgb, #f10778 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.42px 4.42px at 177.4px 144.5px, color-mix(in srgb, #ff90c0 88%, #ffffff), color-mix(in srgb, #ff90c0 88%, #ffffff) 34%, color-mix(in srgb, #ff90c0 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(5.16px 5.16px at 17.7px 183.1px, color-mix(in srgb, #61ff09 88%, #ffffff), color-mix(in srgb, #61ff09 88%, #ffffff) 34%, color-mix(in srgb, #61ff09 55%, transparent) 52%, transparent 100%),
|
radial-gradient(5.16px 5.16px at 17.7px 183.1px, color-mix(in srgb, #cace02 88%, #ffffff), color-mix(in srgb, #cace02 88%, #ffffff) 34%, color-mix(in srgb, #cace02 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(4.80px 4.80px at 46.6px 182.2px, color-mix(in srgb, #b699fe 88%, #ffffff), color-mix(in srgb, #b699fe 88%, #ffffff) 34%, color-mix(in srgb, #b699fe 55%, transparent) 52%, transparent 100%),
|
radial-gradient(4.80px 4.80px at 46.6px 182.2px, color-mix(in srgb, #0cfff0 88%, #ffffff), color-mix(in srgb, #0cfff0 88%, #ffffff) 34%, color-mix(in srgb, #0cfff0 55%, transparent) 52%, transparent 100%),
|
||||||
radial-gradient(5.00px 5.00px at 80.1px 179.7px, color-mix(in srgb, #da8a06 88%, #ffffff), color-mix(in srgb, #da8a06 88%, #ffffff) 34%, color-mix(in srgb, #da8a06 55%, transparent) 52%, transparent 100%),
|
radial-gradient(5.00px 5.00px at 80.1px 179.7px, color-mix(in srgb, #ff51c9 88%, #ffffff), color-mix(in srgb, #ff51c9 88%, #ffffff) 34%, color-mix(in srgb, #ff51c9 55%, transparent) 52%, transparent 100%),
|
||||||
|
radial-gradient(6.47px 6.47px at 116.3px 173.0px, color-mix(in srgb, #eeef08 88%, #ffffff), color-mix(in srgb, #eeef08 88%, #ffffff) 34%, color-mix(in srgb, #eeef08 55%, transparent) 52%, transparent 100%),
|
||||||
|
|
||||||
/* 4. DIE MITTLEREN — die Masse des Feldes. */
|
/* 4. DIE MITTLEREN — die Masse des Feldes. */
|
||||||
radial-gradient(3.21px 3.21px at 5.1px 5.6px, color-mix(in srgb, #07fff1 82%, #ffffff), color-mix(in srgb, #07fff1 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(3.21px 3.21px at 5.1px 5.6px, color-mix(in srgb, #2dc9ba 82%, #ffffff), color-mix(in srgb, #2dc9ba 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(3.60px 3.60px at 30.9px 16.4px, color-mix(in srgb, #ab68ff 82%, #ffffff), color-mix(in srgb, #ab68ff 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(3.60px 3.60px at 30.9px 16.4px, color-mix(in srgb, #cf1bfc 82%, #ffffff), color-mix(in srgb, #cf1bfc 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(3.00px 3.00px at 52.4px 11.5px, color-mix(in srgb, #ff1a1a 82%, #ffffff), color-mix(in srgb, #ff1a1a 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(3.00px 3.00px at 52.4px 11.5px, color-mix(in srgb, #e76006 82%, #ffffff), color-mix(in srgb, #e76006 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.86px 2.86px at 82.4px 14.9px, color-mix(in srgb, #cace02 82%, #ffffff), color-mix(in srgb, #cace02 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.86px 2.86px at 82.4px 14.9px, color-mix(in srgb, #61ff09 82%, #ffffff), color-mix(in srgb, #61ff09 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.96px 2.96px at 98.8px 9.3px, color-mix(in srgb, #12b37e 82%, #ffffff), color-mix(in srgb, #12b37e 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.96px 2.96px at 98.8px 9.3px, color-mix(in srgb, #a8d8ff 82%, #ffffff), color-mix(in srgb, #a8d8ff 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.65px 2.65px at 121.0px 14.2px, color-mix(in srgb, #b699fe 82%, #ffffff), color-mix(in srgb, #b699fe 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.65px 2.65px at 121.0px 14.2px, color-mix(in srgb, #ffabff 82%, #ffffff), color-mix(in srgb, #ffabff 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.49px 2.49px at 11.8px 26.8px, color-mix(in srgb, #fe667e 82%, #ffffff), color-mix(in srgb, #fe667e 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.49px 2.49px at 11.8px 26.8px, color-mix(in srgb, #db8a0c 82%, #ffffff), color-mix(in srgb, #db8a0c 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.92px 2.92px at 33.8px 37.3px, color-mix(in srgb, #eeef08 82%, #ffffff), color-mix(in srgb, #eeef08 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.92px 2.92px at 33.8px 37.3px, color-mix(in srgb, #9cde90 82%, #ffffff), color-mix(in srgb, #9cde90 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.47px 2.47px at 55.5px 38.6px, color-mix(in srgb, #08eb9f 82%, #ffffff), color-mix(in srgb, #08eb9f 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.47px 2.47px at 55.5px 38.6px, color-mix(in srgb, #007bff 82%, #ffffff), color-mix(in srgb, #007bff 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.55px 2.55px at 74.8px 28.0px, color-mix(in srgb, #6c8efe 82%, #ffffff), color-mix(in srgb, #6c8efe 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.55px 2.55px at 74.8px 28.0px, color-mix(in srgb, #ff90c0 82%, #ffffff), color-mix(in srgb, #ff90c0 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.43px 2.43px at 103.9px 38.4px, color-mix(in srgb, #f10778 82%, #ffffff), color-mix(in srgb, #f10778 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.43px 2.43px at 103.9px 38.4px, color-mix(in srgb, #918a01 82%, #ffffff), color-mix(in srgb, #918a01 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.62px 2.62px at 124.3px 27.3px, color-mix(in srgb, #918a01 82%, #ffffff), color-mix(in srgb, #918a01 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.62px 2.62px at 124.3px 27.3px, color-mix(in srgb, #03960f 82%, #ffffff), color-mix(in srgb, #03960f 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.61px 2.61px at 6.8px 55.0px, color-mix(in srgb, #069511 82%, #ffffff), color-mix(in srgb, #069511 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.61px 2.61px at 6.8px 55.0px, color-mix(in srgb, #577ecc 82%, #ffffff), color-mix(in srgb, #577ecc 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(3.19px 3.19px at 36.1px 60.1px, color-mix(in srgb, #027afb 82%, #ffffff), color-mix(in srgb, #027afb 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(3.19px 3.19px at 36.1px 60.1px, color-mix(in srgb, #ff7269 82%, #ffffff), color-mix(in srgb, #ff7269 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(3.63px 3.63px at 56.8px 55.5px, color-mix(in srgb, #fe8ebe 82%, #ffffff), color-mix(in srgb, #fe8ebe 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(3.63px 3.63px at 56.8px 55.5px, color-mix(in srgb, #cace02 82%, #ffffff), color-mix(in srgb, #cace02 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.62px 2.62px at 82.6px 57.2px, color-mix(in srgb, #ffbd4a 82%, #ffffff), color-mix(in srgb, #ffbd4a 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.62px 2.62px at 82.6px 57.2px, color-mix(in srgb, #12b37e 82%, #ffffff), color-mix(in srgb, #12b37e 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.89px 2.89px at 97.7px 59.0px, color-mix(in srgb, #18b803 82%, #ffffff), color-mix(in srgb, #18b803 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.89px 2.89px at 97.7px 59.0px, color-mix(in srgb, #ab60ff 82%, #ffffff), color-mix(in srgb, #ab60ff 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(3.29px 3.29px at 125.8px 57.6px, color-mix(in srgb, #a8d8ff 82%, #ffffff), color-mix(in srgb, #a8d8ff 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(3.29px 3.29px at 125.8px 57.6px, color-mix(in srgb, #ff9f7b 82%, #ffffff), color-mix(in srgb, #ff9f7b 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.52px 2.52px at 10.9px 83.0px, color-mix(in srgb, #ff57ba 82%, #ffffff), color-mix(in srgb, #ff57ba 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.52px 2.52px at 10.9px 83.0px, color-mix(in srgb, #72d512 82%, #ffffff), color-mix(in srgb, #72d512 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.37px 2.37px at 33.1px 72.6px, color-mix(in srgb, #da8a06 82%, #ffffff), color-mix(in srgb, #da8a06 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.37px 2.37px at 33.1px 72.6px, color-mix(in srgb, #0cfff0 82%, #ffffff), color-mix(in srgb, #0cfff0 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(3.21px 3.21px at 54.6px 78.8px, color-mix(in srgb, #a7ff98 82%, #ffffff), color-mix(in srgb, #a7ff98 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(3.21px 3.21px at 54.6px 78.8px, color-mix(in srgb, #e472ff 82%, #ffffff), color-mix(in srgb, #e472ff 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(3.43px 3.43px at 71.0px 71.9px, color-mix(in srgb, #08b5fc 82%, #ffffff), color-mix(in srgb, #08b5fc 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(3.43px 3.43px at 71.0px 71.9px, color-mix(in srgb, #b46f1e 82%, #ffffff), color-mix(in srgb, #b46f1e 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(3.16px 3.16px at 92.6px 70.6px, color-mix(in srgb, #f4acff 82%, #ffffff), color-mix(in srgb, #f4acff 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(3.16px 3.16px at 92.6px 70.6px, color-mix(in srgb, #abff96 82%, #ffffff), color-mix(in srgb, #abff96 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(3.22px 3.22px at 118.9px 78.1px, color-mix(in srgb, #c16302 82%, #ffffff), color-mix(in srgb, #c16302 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(3.22px 3.22px at 118.9px 78.1px, color-mix(in srgb, #75b7ff 82%, #ffffff), color-mix(in srgb, #75b7ff 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.50px 2.50px at 16.0px 101.8px, color-mix(in srgb, #61ff09 82%, #ffffff), color-mix(in srgb, #61ff09 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.50px 2.50px at 16.0px 101.8px, color-mix(in srgb, #ff51c9 82%, #ffffff), color-mix(in srgb, #ff51c9 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.80px 2.80px at 33.8px 102.3px, color-mix(in srgb, #0687bc 82%, #ffffff), color-mix(in srgb, #0687bc 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.80px 2.80px at 33.8px 102.3px, color-mix(in srgb, #ffb742 82%, #ffffff), color-mix(in srgb, #ffb742 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.52px 2.52px at 49.8px 93.9px, color-mix(in srgb, #e871ff 82%, #ffffff), color-mix(in srgb, #e871ff 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.52px 2.52px at 49.8px 93.9px, color-mix(in srgb, #18b803 82%, #ffffff), color-mix(in srgb, #18b803 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.87px 2.87px at 77.0px 95.8px, color-mix(in srgb, #fe660e 82%, #ffffff), color-mix(in srgb, #fe660e 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.87px 2.87px at 77.0px 95.8px, color-mix(in srgb, #5499ff 82%, #ffffff), color-mix(in srgb, #5499ff 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.37px 2.37px at 101.9px 95.3px, color-mix(in srgb, #72d512 82%, #ffffff), color-mix(in srgb, #72d512 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.37px 2.37px at 101.9px 95.3px, color-mix(in srgb, #f60684 82%, #ffffff), color-mix(in srgb, #f60684 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(3.42px 3.42px at 113.5px 91.8px, color-mix(in srgb, #0ed1d0 82%, #ffffff), color-mix(in srgb, #0ed1d0 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(3.42px 3.42px at 113.5px 91.8px, color-mix(in srgb, #eeef08 82%, #ffffff), color-mix(in srgb, #eeef08 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(3.22px 3.22px at 12.7px 116.5px, color-mix(in srgb, #d21afd 82%, #ffffff), color-mix(in srgb, #d21afd 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(3.22px 3.22px at 12.7px 116.5px, color-mix(in srgb, #00f9a8 82%, #ffffff), color-mix(in srgb, #00f9a8 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.35px 2.35px at 26.5px 120.2px, color-mix(in srgb, #ff9d82 82%, #ffffff), color-mix(in srgb, #ff9d82 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.35px 2.35px at 26.5px 120.2px, color-mix(in srgb, #c6a5ff 82%, #ffffff), color-mix(in srgb, #c6a5ff 82%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(3.34px 3.34px at 53.1px 119.0px, color-mix(in srgb, #a2af10 82%, #ffffff), color-mix(in srgb, #a2af10 82%, #ffffff) 34%, transparent 100%),
|
radial-gradient(3.34px 3.34px at 53.1px 119.0px, color-mix(in srgb, #ff1a1a 82%, #ffffff), color-mix(in srgb, #ff1a1a 82%, #ffffff) 34%, transparent 100%),
|
||||||
|
radial-gradient(2.84px 2.84px at 72.3px 120.1px, color-mix(in srgb, #a2af10 82%, #ffffff), color-mix(in srgb, #a2af10 82%, #ffffff) 34%, transparent 100%),
|
||||||
|
|
||||||
/* 5. DER FEINE STAUB. Er füllt die Lücken: Ein Feld aus nur
|
/* 5. DER FEINE STAUB. Er füllt die Lücken: Ein Feld aus nur
|
||||||
großen Punkten sieht aus wie aufgeklebt. */
|
großen Punkten sieht aus wie aufgeklebt. */
|
||||||
radial-gradient(1.70px 1.70px at 6.2px 11.0px, color-mix(in srgb, #07fff1 74%, #ffffff), color-mix(in srgb, #07fff1 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.70px 1.70px at 6.2px 11.0px, color-mix(in srgb, #2dc9ba 74%, #ffffff), color-mix(in srgb, #2dc9ba 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.00px 2.00px at 19.0px 4.4px, color-mix(in srgb, #b699fe 74%, #ffffff), color-mix(in srgb, #b699fe 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.00px 2.00px at 19.0px 4.4px, color-mix(in srgb, #c6a5ff 74%, #ffffff), color-mix(in srgb, #c6a5ff 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.88px 1.88px at 38.0px 4.6px, color-mix(in srgb, #f10778 74%, #ffffff), color-mix(in srgb, #f10778 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.88px 1.88px at 38.0px 4.6px, color-mix(in srgb, #f60684 74%, #ffffff), color-mix(in srgb, #f60684 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.78px 1.78px at 49.4px 2.8px, color-mix(in srgb, #ffbd4a 74%, #ffffff), color-mix(in srgb, #ffbd4a 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.78px 1.78px at 49.4px 2.8px, color-mix(in srgb, #ffb742 74%, #ffffff), color-mix(in srgb, #ffb742 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.68px 1.68px at 65.7px 3.2px, color-mix(in srgb, #a7ff98 74%, #ffffff), color-mix(in srgb, #a7ff98 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.68px 1.68px at 65.7px 3.2px, color-mix(in srgb, #abff96 74%, #ffffff), color-mix(in srgb, #abff96 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.75px 1.75px at 77.7px 7.7px, color-mix(in srgb, #0687bc 74%, #ffffff), color-mix(in srgb, #0687bc 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.75px 1.75px at 77.7px 7.7px, color-mix(in srgb, #0cfff0 74%, #ffffff), color-mix(in srgb, #0cfff0 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.44px 1.44px at 3.7px 23.9px, color-mix(in srgb, #d21afd 74%, #ffffff), color-mix(in srgb, #d21afd 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.44px 1.44px at 3.7px 23.9px, color-mix(in srgb, #ab60ff 74%, #ffffff), color-mix(in srgb, #ab60ff 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.64px 1.64px at 23.7px 21.2px, color-mix(in srgb, #ff1a1a 74%, #ffffff), color-mix(in srgb, #ff1a1a 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.64px 1.64px at 23.7px 21.2px, color-mix(in srgb, #ff7269 74%, #ffffff), color-mix(in srgb, #ff7269 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.53px 1.53px at 30.8px 17.1px, color-mix(in srgb, #eeef08 74%, #ffffff), color-mix(in srgb, #eeef08 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.53px 1.53px at 30.8px 17.1px, color-mix(in srgb, #918a01 74%, #ffffff), color-mix(in srgb, #918a01 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.69px 1.69px at 46.6px 21.7px, color-mix(in srgb, #069511 74%, #ffffff), color-mix(in srgb, #069511 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.69px 1.69px at 46.6px 21.7px, color-mix(in srgb, #9cde90 74%, #ffffff), color-mix(in srgb, #9cde90 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(2.03px 2.03px at 64.3px 18.5px, color-mix(in srgb, #a8d8ff 74%, #ffffff), color-mix(in srgb, #a8d8ff 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(2.03px 2.03px at 64.3px 18.5px, color-mix(in srgb, #a8d8ff 74%, #ffffff), color-mix(in srgb, #a8d8ff 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.36px 1.36px at 77.2px 20.7px, color-mix(in srgb, #f4acff 74%, #ffffff), color-mix(in srgb, #f4acff 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.36px 1.36px at 77.2px 20.7px, color-mix(in srgb, #cf1bfc 74%, #ffffff), color-mix(in srgb, #cf1bfc 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.40px 1.40px at 3.8px 31.4px, color-mix(in srgb, #fe660e 74%, #ffffff), color-mix(in srgb, #fe660e 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.40px 1.40px at 3.8px 31.4px, color-mix(in srgb, #ff1a1a 74%, #ffffff), color-mix(in srgb, #ff1a1a 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.36px 1.36px at 17.0px 32.1px, color-mix(in srgb, #a2af10 74%, #ffffff), color-mix(in srgb, #a2af10 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.36px 1.36px at 17.0px 32.1px, color-mix(in srgb, #eeef08 74%, #ffffff), color-mix(in srgb, #eeef08 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.84px 1.84px at 32.1px 35.9px, color-mix(in srgb, #12b37e 74%, #ffffff), color-mix(in srgb, #12b37e 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.84px 1.84px at 32.1px 35.9px, color-mix(in srgb, #18b803 74%, #ffffff), color-mix(in srgb, #18b803 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.63px 1.63px at 48.3px 34.6px, color-mix(in srgb, #6c8efe 74%, #ffffff), color-mix(in srgb, #6c8efe 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.63px 1.63px at 48.3px 34.6px, color-mix(in srgb, #75b7ff 74%, #ffffff), color-mix(in srgb, #75b7ff 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.47px 1.47px at 61.7px 38.8px, color-mix(in srgb, #fe8ebe 74%, #ffffff), color-mix(in srgb, #fe8ebe 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.47px 1.47px at 61.7px 38.8px, color-mix(in srgb, #e472ff 74%, #ffffff), color-mix(in srgb, #e472ff 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.59px 1.59px at 73.2px 36.6px, color-mix(in srgb, #da8a06 74%, #ffffff), color-mix(in srgb, #da8a06 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.59px 1.59px at 73.2px 36.6px, color-mix(in srgb, #ff9f7b 74%, #ffffff), color-mix(in srgb, #ff9f7b 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.34px 1.34px at 2.9px 51.3px, color-mix(in srgb, #61ff09 74%, #ffffff), color-mix(in srgb, #61ff09 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.34px 1.34px at 2.9px 51.3px, color-mix(in srgb, #cace02 74%, #ffffff), color-mix(in srgb, #cace02 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.78px 1.78px at 17.6px 52.6px, color-mix(in srgb, #0ed1d0 74%, #ffffff), color-mix(in srgb, #0ed1d0 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.78px 1.78px at 17.6px 52.6px, color-mix(in srgb, #03960f 74%, #ffffff), color-mix(in srgb, #03960f 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.72px 1.72px at 35.6px 46.1px, color-mix(in srgb, #ab68ff 74%, #ffffff), color-mix(in srgb, #ab68ff 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.72px 1.72px at 35.6px 46.1px, color-mix(in srgb, #007bff 74%, #ffffff), color-mix(in srgb, #007bff 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.87px 1.87px at 50.9px 49.3px, color-mix(in srgb, #fe667e 74%, #ffffff), color-mix(in srgb, #fe667e 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.87px 1.87px at 50.9px 49.3px, color-mix(in srgb, #ffabff 74%, #ffffff), color-mix(in srgb, #ffabff 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.44px 1.44px at 59.6px 52.2px, color-mix(in srgb, #918a01 74%, #ffffff), color-mix(in srgb, #918a01 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.44px 1.44px at 59.6px 52.2px, color-mix(in srgb, #e76006 74%, #ffffff), color-mix(in srgb, #e76006 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.77px 1.77px at 73.3px 49.9px, color-mix(in srgb, #18b803 74%, #ffffff), color-mix(in srgb, #18b803 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.77px 1.77px at 73.3px 49.9px, color-mix(in srgb, #a2af10 74%, #ffffff), color-mix(in srgb, #a2af10 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.76px 1.76px at 3.5px 65.0px, color-mix(in srgb, #08b5fc 74%, #ffffff), color-mix(in srgb, #08b5fc 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.76px 1.76px at 3.5px 65.0px, color-mix(in srgb, #00f9a8 74%, #ffffff), color-mix(in srgb, #00f9a8 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.72px 1.72px at 25.0px 58.0px, color-mix(in srgb, #e871ff 74%, #ffffff), color-mix(in srgb, #e871ff 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.72px 1.72px at 25.0px 58.0px, color-mix(in srgb, #5499ff 74%, #ffffff), color-mix(in srgb, #5499ff 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.77px 1.77px at 30.9px 65.9px, color-mix(in srgb, #ff9d82 74%, #ffffff), color-mix(in srgb, #ff9d82 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.77px 1.77px at 30.9px 65.9px, color-mix(in srgb, #ff51c9 74%, #ffffff), color-mix(in srgb, #ff51c9 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.87px 1.87px at 51.0px 65.4px, color-mix(in srgb, #cace02 74%, #ffffff), color-mix(in srgb, #cace02 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.87px 1.87px at 51.0px 65.4px, color-mix(in srgb, #b46f1e 74%, #ffffff), color-mix(in srgb, #b46f1e 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.38px 1.38px at 61.3px 65.1px, color-mix(in srgb, #08eb9f 74%, #ffffff), color-mix(in srgb, #08eb9f 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.38px 1.38px at 61.3px 65.1px, color-mix(in srgb, #72d512 74%, #ffffff), color-mix(in srgb, #72d512 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.79px 1.79px at 78.3px 63.7px, color-mix(in srgb, #027afb 74%, #ffffff), color-mix(in srgb, #027afb 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.79px 1.79px at 78.3px 63.7px, color-mix(in srgb, #12b37e 74%, #ffffff), color-mix(in srgb, #12b37e 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.34px 1.34px at 9.8px 74.9px, color-mix(in srgb, #ff57ba 74%, #ffffff), color-mix(in srgb, #ff57ba 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.34px 1.34px at 9.8px 74.9px, color-mix(in srgb, #577ecc 74%, #ffffff), color-mix(in srgb, #577ecc 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.54px 1.54px at 20.7px 75.9px, color-mix(in srgb, #c16302 74%, #ffffff), color-mix(in srgb, #c16302 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.54px 1.54px at 20.7px 75.9px, color-mix(in srgb, #ff90c0 74%, #ffffff), color-mix(in srgb, #ff90c0 74%, #ffffff) 34%, transparent 100%),
|
||||||
radial-gradient(1.61px 1.61px at 31.6px 74.4px, color-mix(in srgb, #72d512 74%, #ffffff), color-mix(in srgb, #72d512 74%, #ffffff) 34%, transparent 100%),
|
radial-gradient(1.61px 1.61px at 31.6px 74.4px, color-mix(in srgb, #db8a0c 74%, #ffffff), color-mix(in srgb, #db8a0c 74%, #ffffff) 34%, transparent 100%),
|
||||||
|
radial-gradient(1.97px 1.97px at 47.5px 78.3px, color-mix(in srgb, #61ff09 74%, #ffffff), color-mix(in srgb, #61ff09 74%, #ffffff) 34%, transparent 100%),
|
||||||
|
|
||||||
/* 6. EIN SCHRÄGER SCHLEIER quer über das Feld. Er bindet die
|
/* 6. EIN SCHRÄGER SCHLEIER quer über das Feld. Er bindet die
|
||||||
99 Punkte zu einer Fläche zusammen — ohne ihn
|
102 Punkte zu einer Fläche zusammen — ohne ihn
|
||||||
schweben sie einzeln. Sehr blass; er soll Richtung geben,
|
schweben sie einzeln. Sehr blass; er soll Richtung geben,
|
||||||
nicht auffallen. */
|
nicht auffallen. */
|
||||||
linear-gradient(104deg,
|
linear-gradient(104deg,
|
||||||
@@ -8682,11 +8686,11 @@ body.start .k-leiste .feldschild::after {
|
|||||||
linear-gradient(180deg, #0b1020 0%, #070b14 100%);
|
linear-gradient(180deg, #0b1020 0%, #070b14 100%);
|
||||||
background-size:
|
background-size:
|
||||||
100% 100%, 100% 100%,
|
100% 100%, 100% 100%,
|
||||||
197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px,
|
197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 197px 197px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 131px 131px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px, 83px 83px,
|
||||||
100% 100%, 100% 100%;
|
100% 100%, 100% 100%;
|
||||||
background-repeat:
|
background-repeat:
|
||||||
no-repeat, no-repeat,
|
no-repeat, no-repeat,
|
||||||
repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat,
|
repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat, repeat,
|
||||||
no-repeat, no-repeat;
|
no-repeat, no-repeat;
|
||||||
/* Die Kante trägt den Kachelton mit — sonst wäre ausgerechnet die
|
/* Die Kante trägt den Kachelton mit — sonst wäre ausgerechnet die
|
||||||
farbigste Kachel der Wand einfarbig umrandet. */
|
farbigste Kachel der Wand einfarbig umrandet. */
|
||||||
@@ -9078,3 +9082,77 @@ body[data-ton] .kopfleiste__rechts > *:hover {
|
|||||||
.kopf-mehr { border: 1px solid CanvasText; }
|
.kopf-mehr { border: 1px solid CanvasText; }
|
||||||
.kopf-mehr__fach { border: 1px solid CanvasText; background: Canvas; }
|
.kopf-mehr__fach { border: 1px solid CanvasText; background: Canvas; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
DIE NOTIZKACHEL IST EIN BLOCK (26.09.2026)
|
||||||
|
=====================================================================
|
||||||
|
|
||||||
|
Filipe: „ich will dass du das auch wie ein notizblock erstellst.
|
||||||
|
ich will dass es übelst geil und einzigartig ist."
|
||||||
|
|
||||||
|
Dieselbe Bauart wie `silber` bei den Aufgaben und `regenbogen` bei
|
||||||
|
Willkommen: ein MERKMAL am Element (`data-papier="ja"`), keine
|
||||||
|
Farbe. Die Farbwerkzeuge rechnen weiter mit `data-ton="46"`, und
|
||||||
|
wer diese Stilvorlage nicht bekommt (alter Zwischenspeicher,
|
||||||
|
Kontrastmodus), sieht die Kachel in ihrem Grün — erkennbar, nur
|
||||||
|
ohne Papier.
|
||||||
|
|
||||||
|
VIER EBENEN, von oben nach unten:
|
||||||
|
|
||||||
|
1. DIE PERFORATION. Eine Reihe dunkler Halbkreise dicht unter der
|
||||||
|
Oberkante — die Löcher, an denen man das Blatt abreißt. Sie
|
||||||
|
sitzen in einem hellen Streifen (dem Leimrand), sonst
|
||||||
|
schwebten sie im Nichts.
|
||||||
|
2. DIE LINIATUR. Waagerechte Linien im Ton der Kachel, sehr
|
||||||
|
schwach. Ihr Abstand ist 26 px und damit GRÖSSER als die
|
||||||
|
Zeilenhöhe des Kacheltextes: Läge eine Linie genau unter der
|
||||||
|
Schrift, sähe es aus wie eine Unterstreichung.
|
||||||
|
3. DER RANDSTRICH. Eine senkrechte Linie bei 14 % Breite, wie der
|
||||||
|
Rand auf Schulpapier. Er ist das Detail, an dem man „Papier"
|
||||||
|
erkennt, ohne es zu benennen.
|
||||||
|
4. DER GRUNDVERLAUF. Oben eine Spur heller — so liegt Licht auf
|
||||||
|
einem Block, der leicht gewölbt auf dem Tisch liegt.
|
||||||
|
|
||||||
|
WARUM DAS PAPIER DUNKEL IST. Ein cremeweißer Block auf einer
|
||||||
|
dunklen Wand wäre eine Lampe: Bei 95 % Leuchtdichte blendet die
|
||||||
|
Kachel, und die Hausregel heißt augenschonend. Das Papiergefühl
|
||||||
|
trägt hier die STRUKTUR (Löcher, Linien, Rand), nicht die
|
||||||
|
Helligkeit — genau wie eine schwarze Kladde immer noch als
|
||||||
|
Notizbuch zu erkennen ist.
|
||||||
|
===================================================================== */
|
||||||
|
.kachel[data-papier="ja"] {
|
||||||
|
background-image:
|
||||||
|
/* 1. Die Perforation: dunkle Löcher im hellen Leimrand. */
|
||||||
|
radial-gradient(circle at 50% 0,
|
||||||
|
color-mix(in srgb, #05070d 88%, transparent) 0 2.6px,
|
||||||
|
transparent 2.9px),
|
||||||
|
linear-gradient(color-mix(in srgb, var(--ton) 22%, transparent) 0 9px,
|
||||||
|
transparent 9px),
|
||||||
|
/* 2. Die Liniatur. */
|
||||||
|
repeating-linear-gradient(to bottom,
|
||||||
|
transparent 0 25px,
|
||||||
|
color-mix(in srgb, var(--ton) 13%, transparent) 25px 26px),
|
||||||
|
/* 3. Der Randstrich. */
|
||||||
|
linear-gradient(to right,
|
||||||
|
transparent 0 14%,
|
||||||
|
color-mix(in srgb, var(--ton) 26%, transparent) 14%,
|
||||||
|
transparent calc(14% + 1px)),
|
||||||
|
/* 4. Der Grundverlauf. */
|
||||||
|
linear-gradient(to bottom,
|
||||||
|
color-mix(in srgb, var(--ton) 13%, transparent),
|
||||||
|
color-mix(in srgb, var(--ton) 5%, transparent) 62%,
|
||||||
|
transparent);
|
||||||
|
background-size: 18px 9px, 100% 9px, 100% 100%, 100% 100%, 100% 100%;
|
||||||
|
background-position: 0 3px, 0 0, 0 0, 0 0, 0 0;
|
||||||
|
background-repeat: repeat-x, no-repeat, repeat, no-repeat, no-repeat;
|
||||||
|
}
|
||||||
|
/* Beim Anfassen wird die Liniatur eine Spur deutlicher — als würde man
|
||||||
|
das Blatt zum Licht drehen. Kein Blinken, keine Bewegung. */
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
.kachel[data-papier="ja"] { transition: filter .2s ease; }
|
||||||
|
}
|
||||||
|
/* Im Kontrastmodus trägt allein die Kante. Ein Verlauf würde dort zu
|
||||||
|
einer grauen Fläche — dieselbe Entscheidung wie bei Silber. */
|
||||||
|
@media (forced-colors: active) {
|
||||||
|
.kachel[data-papier="ja"] { background-image: none; }
|
||||||
|
}
|
||||||
|
|||||||
@@ -348,6 +348,23 @@
|
|||||||
hin, man schliesst es nicht ein. */
|
hin, man schliesst es nicht ein. */
|
||||||
linien: ['M4.9 14.4c0 2.9 3.18 4.9 7.1 4.9s7.1-2 7.1-4.9'],
|
linien: ['M4.9 14.4c0 2.9 3.18 4.9 7.1 4.9s7.1-2 7.1-4.9'],
|
||||||
},
|
},
|
||||||
|
/* NOTIZEN (26.09.2026) -- ein Block, kein Blatt.
|
||||||
|
|
||||||
|
Der Unterschied steckt im LEIMSTREIFEN oben: Ein Rechteck mit
|
||||||
|
Linien darin ist ein Dokument; erst der geschlossene Balken am
|
||||||
|
Kopf macht daraus einen Block, den man abreisst. Er ist
|
||||||
|
deshalb die Flaeche -- das Einzige, was bei zwanzig Bildpunkten
|
||||||
|
sicher ankommt.
|
||||||
|
|
||||||
|
DIE DRITTE LINIE IST KUERZER. Drei gleich lange Striche lesen
|
||||||
|
sich als Text; ein abbrechender Strich liest sich als
|
||||||
|
Handschrift, die mitten im Wort aufhoert. Das ist der
|
||||||
|
Unterschied zwischen „Dokument" und „Notiz", und er kostet
|
||||||
|
vier Bildpunkte. */
|
||||||
|
notizen: {
|
||||||
|
fuellung: ['M5 3.6h14v2.9H5z'],
|
||||||
|
linien: ['M5 3.6h14v16.8H5z', 'M8.2 10.4h7.6', 'M8.2 13.8h7.6', 'M8.2 17.2h4.4'],
|
||||||
|
},
|
||||||
hinaus: {
|
hinaus: {
|
||||||
/* Nur der Pfeilkopf ist Flaeche -- er ist die Aussage. */
|
/* Nur der Pfeilkopf ist Flaeche -- er ist die Aussage. */
|
||||||
fuellung: ['M13.8 4H20v6.2z'],
|
fuellung: ['M13.8 4H20v6.2z'],
|
||||||
|
|||||||
@@ -0,0 +1,904 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
DER NOTIZBLOCK (26.09.2026)
|
||||||
|
=====================================================================
|
||||||
|
|
||||||
|
Filipe: „ich will dass du das auch wie ein notizblock erstellst.
|
||||||
|
ich will dass es übelst geil und einzigartig ist."
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
DREI ENTSCHEIDUNGEN, AUS DENEN DER REST FOLGT
|
||||||
|
|
||||||
|
1. ES GIBT KEINEN SPEICHERN-KNOPF. Wer einen Block aufschlägt und
|
||||||
|
lostippt, drückt hinterher nicht auf „sichern"; er klappt ihn zu.
|
||||||
|
Gesichert wird 800 ms nach dem letzten Tastendruck. Geht das
|
||||||
|
nicht (Funkloch, Server weg), bleibt der Text im Browser liegen
|
||||||
|
und wird nachgereicht — und der Fuß sagt ehrlich, dass er noch
|
||||||
|
nicht drüben ist.
|
||||||
|
|
||||||
|
2. DAS BLATT IST EIN SCHREIBFELD, KEIN CONTENTEDITABLE. Die
|
||||||
|
Auszeichnung (Haken, Überschriften, Striche) zeichnet eine
|
||||||
|
Deckschicht darüber. Warum, steht im Kopf von notizen.css — kurz:
|
||||||
|
Ein `contenteditable` bricht beim Einfügen aus fremden
|
||||||
|
Programmen, beim Rückgängigmachen und an jeder Handytastatur,
|
||||||
|
die über eine Zwischenzeile schreibt.
|
||||||
|
|
||||||
|
3. DIE TASTATUR FÜHRT DIE LISTE FORT, NICHT EINE LEISTE. Eine
|
||||||
|
Werkzeugleiste über einem Notizblock ist eine Leiste zu viel:
|
||||||
|
Man schreibt `- ` und drückt Enter, und die nächste Zeile fängt
|
||||||
|
wieder mit `- ` an. Genauso bei `[ ] `. Das ist alles, was es an
|
||||||
|
„Formatierung" gibt, und es fühlt sich wie Papier an, weil man
|
||||||
|
nie den Stift wechselt.
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
WAS HIER NICHT PASSIERT
|
||||||
|
|
||||||
|
Es gibt keine Rolle, die mehr sieht. Kein „Leitung"-Zweig, kein
|
||||||
|
Umschalter, keine fremde Liste. Wer das hier liest und eine
|
||||||
|
Sichtbarkeitsregel sucht: Es gibt keine, und das ist der Sinn der
|
||||||
|
Sache (siehe notiz-tabellen.js).
|
||||||
|
===================================================================== */
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const $ = (id) => document.getElementById(id);
|
||||||
|
const el = (was, klasse, text) => {
|
||||||
|
const e = document.createElement(was);
|
||||||
|
if (klasse) e.className = klasse;
|
||||||
|
if (text !== undefined) e.textContent = text;
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---------- Zustand ---------------------------------------------- */
|
||||||
|
let notizen = [];
|
||||||
|
let farben = [];
|
||||||
|
let aktuell = null; // die offene Notiz (Objekt aus `notizen`)
|
||||||
|
let suchwort = '';
|
||||||
|
let papierkorbZahl = 0;
|
||||||
|
let textMax = 20000;
|
||||||
|
let korbTage = 30;
|
||||||
|
|
||||||
|
const feld = $('feld');
|
||||||
|
const deck = $('deck');
|
||||||
|
|
||||||
|
/* ---------- Fetch-Hülle ------------------------------------------- */
|
||||||
|
async function hole(pfad, einstellungen) {
|
||||||
|
const a = await fetch(pfad, { credentials: 'same-origin', ...einstellungen });
|
||||||
|
if (a.status === 401) { location.href = 'index.html'; throw new Error('weg'); }
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
async function fehlerText(a, ersatz) {
|
||||||
|
try {
|
||||||
|
const j = await a.json();
|
||||||
|
return typeof j?.fehler === 'string' && j.fehler ? j.fehler : ersatz;
|
||||||
|
} catch { return ersatz; }
|
||||||
|
}
|
||||||
|
function melde(text) {
|
||||||
|
const f = $('fehler');
|
||||||
|
f.textContent = text || '';
|
||||||
|
f.hidden = !text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* „vor 3 Minuten" statt eines Zeitstempels: Bei einer Notiz ist die
|
||||||
|
Frage „wann war ich zuletzt dran?", nicht „welcher Tag war das?".
|
||||||
|
Das genaue Datum bleibt als Titel am Element. */
|
||||||
|
function herZeit(iso) {
|
||||||
|
if (!iso) return '';
|
||||||
|
const ms = Date.now() - Date.parse(iso);
|
||||||
|
if (!Number.isFinite(ms)) return '';
|
||||||
|
const min = Math.floor(ms / 60000);
|
||||||
|
if (min < 1) return 'gerade eben';
|
||||||
|
if (min < 60) return `vor ${min} Min.`;
|
||||||
|
const std = Math.floor(min / 60);
|
||||||
|
if (std < 24) return `vor ${std} Std.`;
|
||||||
|
const tage = Math.floor(std / 24);
|
||||||
|
if (tage === 1) return 'gestern';
|
||||||
|
if (tage < 30) return `vor ${tage} Tagen`;
|
||||||
|
return new Date(iso).toLocaleDateString('de-DE',
|
||||||
|
{ day: '2-digit', month: '2-digit', year: 'numeric' });
|
||||||
|
}
|
||||||
|
const genau = (iso) => (iso
|
||||||
|
? new Date(iso).toLocaleString('de-DE', { dateStyle: 'full', timeStyle: 'short' })
|
||||||
|
: '');
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
DIE DECKSCHICHT
|
||||||
|
|
||||||
|
Sie zeichnet denselben Text noch einmal — mit Haken, Überschriften
|
||||||
|
und Strichen. Die eiserne Regel dabei steht im Kopf von
|
||||||
|
notizen.css: KEINE Auszeichnung darf die Breite eines Zeichens
|
||||||
|
ändern. Deshalb gibt es hier kein `<b>`, kein `<h1>` und keine
|
||||||
|
Zeichen, die hinzukommen oder wegfallen — jedes Zeichen des Textes
|
||||||
|
landet genau einmal in der Ausgabe.
|
||||||
|
|
||||||
|
GEBAUT WIRD MIT createElement UND textContent, nicht mit einer
|
||||||
|
Zeichenkette und innerHTML. Ein Notizblock ist der Ort, an dem
|
||||||
|
jemand irgendwann `<img onerror=...>` hineinschreibt — nicht aus
|
||||||
|
Bosheit, sondern weil er sich etwas notiert. Mit textContent ist
|
||||||
|
das ein Text und bleibt einer.
|
||||||
|
===================================================================== */
|
||||||
|
|
||||||
|
/** Wandelt eine Zeile in Knoten um und hängt sie an `ziel`. */
|
||||||
|
function zeileZeichnen(ziel, zeile, treffer) {
|
||||||
|
/* Die Zeile wird von links nach rechts abgearbeitet; `rest` ist,
|
||||||
|
was noch nicht gezeichnet wurde. */
|
||||||
|
let rest = zeile;
|
||||||
|
let erledigt = false;
|
||||||
|
const stueck = (text, klasse, aufbau) => {
|
||||||
|
if (!text) return null;
|
||||||
|
const s = el('span', klasse, text);
|
||||||
|
if (aufbau) aufbau(s);
|
||||||
|
ziel.append(s);
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 1. Der Zeilenanfang: Überschrift, Aufzählung, Zitat, Haken. */
|
||||||
|
let m;
|
||||||
|
if ((m = /^(\s*)(#{1,3}\s)/.exec(rest))) {
|
||||||
|
stueck(m[1], null);
|
||||||
|
stueck(m[2], 'n-marke');
|
||||||
|
const inhalt = rest.slice(m[0].length);
|
||||||
|
textMitLinks(ziel, inhalt, treffer, 'n-h');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((m = /^(\s*)(\[[ xX]\])(\s?)/.exec(rest))) {
|
||||||
|
stueck(m[1], null);
|
||||||
|
const an = m[2][1] !== ' ';
|
||||||
|
erledigt = an;
|
||||||
|
stueck(m[2], 'n-hk', (s) => {
|
||||||
|
s.dataset.an = an ? 'ja' : 'nein';
|
||||||
|
s.setAttribute('role', 'checkbox');
|
||||||
|
s.setAttribute('aria-checked', an ? 'true' : 'false');
|
||||||
|
s.title = an ? 'Erledigt – klicken zum Aufheben' : 'Als erledigt abhaken';
|
||||||
|
});
|
||||||
|
stueck(m[3], null);
|
||||||
|
textMitLinks(ziel, rest.slice(m[0].length), treffer, erledigt ? 'n-erledigt' : null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((m = /^(\s*)([-*]\s)/.exec(rest))) {
|
||||||
|
stueck(m[1], null);
|
||||||
|
stueck(m[2], 'n-punkt');
|
||||||
|
textMitLinks(ziel, rest.slice(m[0].length), treffer, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((m = /^(\s*)(>\s?)/.exec(rest))) {
|
||||||
|
stueck(m[1], null);
|
||||||
|
stueck(m[2], 'n-zitat');
|
||||||
|
textMitLinks(ziel, rest.slice(m[0].length), treffer, 'n-zitat');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
textMitLinks(ziel, rest, treffer, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adressen werden anklickbar. Das Muster ist absichtlich eng: Es
|
||||||
|
trifft nur, was mit http(s):// anfängt. Ein „www." ohne Schema
|
||||||
|
wäre eine Vermutung, und eine Notiz ist kein Ort für Vermutungen. */
|
||||||
|
const ADRESSE = /https?:\/\/[^\s<>"')\]]+/g;
|
||||||
|
|
||||||
|
function textMitLinks(ziel, text, treffer, klasse) {
|
||||||
|
const stuecke = [];
|
||||||
|
let zuletzt = 0;
|
||||||
|
ADRESSE.lastIndex = 0;
|
||||||
|
let m;
|
||||||
|
while ((m = ADRESSE.exec(text))) {
|
||||||
|
if (m.index > zuletzt) stuecke.push({ t: text.slice(zuletzt, m.index), link: false });
|
||||||
|
stuecke.push({ t: m[0], link: true });
|
||||||
|
zuletzt = m.index + m[0].length;
|
||||||
|
}
|
||||||
|
if (zuletzt < text.length) stuecke.push({ t: text.slice(zuletzt), link: false });
|
||||||
|
if (!stuecke.length) stuecke.push({ t: '', link: false });
|
||||||
|
|
||||||
|
for (const s of stuecke) {
|
||||||
|
const klassen = [klasse, s.link ? 'n-link' : null].filter(Boolean).join(' ');
|
||||||
|
if (s.link) {
|
||||||
|
const a = el('a', klassen, s.t);
|
||||||
|
a.href = s.t;
|
||||||
|
a.target = '_blank';
|
||||||
|
/* `noopener` ist Pflicht: Ohne ihn bekommt die geöffnete Seite
|
||||||
|
über `window.opener` Zugriff auf diese hier. */
|
||||||
|
a.rel = 'noopener noreferrer';
|
||||||
|
ziel.append(a);
|
||||||
|
} else if (treffer) {
|
||||||
|
trefferZeichnen(ziel, s.t, treffer, klassen);
|
||||||
|
} else if (klassen) {
|
||||||
|
ziel.append(el('span', klassen, s.t));
|
||||||
|
} else {
|
||||||
|
ziel.append(document.createTextNode(s.t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Hebt die Fundstellen der Suche hervor — mit Fläche, nicht mit Breite. */
|
||||||
|
function trefferZeichnen(ziel, text, wort, klassen) {
|
||||||
|
const klein = text.toLowerCase();
|
||||||
|
let von = 0, ab = 0;
|
||||||
|
while ((von = klein.indexOf(wort, ab)) !== -1) {
|
||||||
|
if (von > ab) ziel.append(el('span', klassen, text.slice(ab, von)));
|
||||||
|
const t = el('span', (klassen ? klassen + ' ' : '') + 'n-treffer',
|
||||||
|
text.slice(von, von + wort.length));
|
||||||
|
ziel.append(t);
|
||||||
|
ab = von + wort.length;
|
||||||
|
}
|
||||||
|
if (ab < text.length) ziel.append(el('span', klassen, text.slice(ab)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function deckZeichnen() {
|
||||||
|
const text = feld.value;
|
||||||
|
deck.textContent = '';
|
||||||
|
const treffer = suchwort.length >= 2 ? suchwort : '';
|
||||||
|
const zeilen = text.split('\n');
|
||||||
|
zeilen.forEach((z, i) => {
|
||||||
|
zeileZeichnen(deck, z, treffer);
|
||||||
|
/* Der Zeilenumbruch gehört zum Text und muss mitgezeichnet
|
||||||
|
werden -- sonst stünde alles in einer Zeile. Beim LETZTEN
|
||||||
|
Element darf er nicht dazu: Ein Schreibfeld hat nach der
|
||||||
|
letzten Zeile keinen Umbruch, und die Deckschicht wäre eine
|
||||||
|
Zeile zu hoch. */
|
||||||
|
if (i < zeilen.length - 1) deck.append(document.createTextNode('\n'));
|
||||||
|
});
|
||||||
|
/* Ein Schreibfeld, das auf einen Umbruch endet, zeigt darunter
|
||||||
|
noch eine leere Zeile. Ein <div> tut das nicht -- deshalb eine
|
||||||
|
Leerzeile anhängen, damit beide gleich hoch bleiben. */
|
||||||
|
if (text.endsWith('\n')) deck.append(document.createTextNode(' '));
|
||||||
|
|
||||||
|
hoeheAnpassen();
|
||||||
|
titelSetzen();
|
||||||
|
hakenStandSetzen();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Das Schreibfeld wächst mit dem Text — gescrollt wird der Rahmen.
|
||||||
|
*
|
||||||
|
* So bleibt die Deckschicht ohne eigenes Scrollen deckungsgleich:
|
||||||
|
* Beide stehen im selben scrollenden Kasten. Ein eigener
|
||||||
|
* Bildlauf im Schreibfeld würde außerdem eine Bildlaufleiste
|
||||||
|
* einblenden, die Zeilen schmaler macht — und ab da bräche der
|
||||||
|
* Text in beiden Schichten an verschiedenen Stellen um. */
|
||||||
|
function hoeheAnpassen() {
|
||||||
|
feld.style.height = 'auto';
|
||||||
|
const hoch = Math.max(feld.scrollHeight, 320);
|
||||||
|
feld.style.height = hoch + 'px';
|
||||||
|
deck.style.minHeight = hoch + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
function titelSetzen() {
|
||||||
|
const t = (feld.value.split('\n').find((z) => z.trim().length) || '')
|
||||||
|
.replace(/^\s*#{1,3}\s*/, '')
|
||||||
|
.replace(/^\s*[-*]\s+/, '')
|
||||||
|
.replace(/^\s*\[[ xX]\]\s*/, '')
|
||||||
|
.trim();
|
||||||
|
const k = $('blatt-titel');
|
||||||
|
k.textContent = t || 'Ohne Titel';
|
||||||
|
k.dataset.leer = t ? 'nein' : 'ja';
|
||||||
|
k.title = t || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Wie viele Haken gesetzt sind. Erscheint nur, wenn es welche gibt. */
|
||||||
|
function hakenZaehlen(text) {
|
||||||
|
const alle = String(text || '').match(/^\s*\[[ xX]\]/gm) || [];
|
||||||
|
const an = alle.filter((s) => !/\[ \]/.test(s)).length;
|
||||||
|
return { alle: alle.length, an };
|
||||||
|
}
|
||||||
|
function hakenStandSetzen() {
|
||||||
|
const s = $('haken-stand');
|
||||||
|
const z = hakenZaehlen(feld.value);
|
||||||
|
if (!z.alle) { s.hidden = true; return; }
|
||||||
|
s.hidden = false;
|
||||||
|
s.textContent = `${z.an} von ${z.alle} erledigt`;
|
||||||
|
s.dataset.fertig = z.an === z.alle ? 'ja' : 'nein';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
SICHERN
|
||||||
|
|
||||||
|
Gespeichert wird 800 ms nach dem letzten Tastendruck. Klappt das
|
||||||
|
nicht, bleibt der Text hier liegen und wird beim nächsten Versuch
|
||||||
|
nachgereicht — und der Fuß sagt es. Ein „konnte nicht speichern"
|
||||||
|
ohne Rettung wäre die schlechteste aller Meldungen: Sie erzählt
|
||||||
|
von einem Verlust, den man gerade nicht verhindern kann.
|
||||||
|
|
||||||
|
DER ZWISCHENSPEICHER IM BROWSER ist die zweite Sicherung, für den
|
||||||
|
Fall, dass jemand den Reiter schließt, bevor es durchgeht. Beim
|
||||||
|
nächsten Öffnen wird er angeboten — nicht heimlich eingespielt:
|
||||||
|
Wer zwischendurch auf einem anderen Gerät geschrieben hat, würde
|
||||||
|
sonst seine neuere Fassung verlieren.
|
||||||
|
===================================================================== */
|
||||||
|
const LAGER = 'dogi-notizen-offen';
|
||||||
|
let sicherUhr = null;
|
||||||
|
let laeuft = false;
|
||||||
|
let nochmal = false;
|
||||||
|
|
||||||
|
function standSetzen(stand, wort) {
|
||||||
|
const s = $('stand');
|
||||||
|
s.dataset.stand = stand;
|
||||||
|
s.textContent = wort;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lagerSchreiben(id, text) {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(LAGER, JSON.stringify({ id, text, am: Date.now() }));
|
||||||
|
} catch { /* Voller Speicher ist kein Grund, das Tippen zu stoppen. */ }
|
||||||
|
}
|
||||||
|
function lagerLeeren() {
|
||||||
|
try { localStorage.removeItem(LAGER); } catch { /* egal */ }
|
||||||
|
}
|
||||||
|
function lagerLesen() {
|
||||||
|
try { return JSON.parse(localStorage.getItem(LAGER) || 'null'); } catch { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function aendernGeplant() {
|
||||||
|
if (!aktuell) return;
|
||||||
|
lagerSchreiben(aktuell.id, feld.value);
|
||||||
|
standSetzen('sichert', 'sichert …');
|
||||||
|
clearTimeout(sicherUhr);
|
||||||
|
sicherUhr = setTimeout(sichern, 800);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sichern() {
|
||||||
|
if (!aktuell) return;
|
||||||
|
if (laeuft) { nochmal = true; return; }
|
||||||
|
laeuft = true;
|
||||||
|
const id = aktuell.id;
|
||||||
|
const text = feld.value;
|
||||||
|
try {
|
||||||
|
const a = await hole(`/workspace/api/notizblock/${id}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ text }),
|
||||||
|
});
|
||||||
|
if (!a.ok) {
|
||||||
|
standSetzen('offen', await fehlerText(a, 'noch nicht gesichert'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const j = await a.json();
|
||||||
|
const n = notizen.find((x) => x.id === id);
|
||||||
|
if (n) { n.text = text; n.geaendert = j.geaendert; n.titel = titelAus(text); }
|
||||||
|
lagerLeeren();
|
||||||
|
standSetzen('gesichert', 'gesichert');
|
||||||
|
wannSetzen();
|
||||||
|
stapelZeichnen();
|
||||||
|
} catch {
|
||||||
|
/* Kein Netz. Der Text liegt im Browser; beim nächsten Tippen
|
||||||
|
oder beim nächsten Öffnen geht es weiter. */
|
||||||
|
standSetzen('offen', 'noch nicht gesichert – bleibt hier, bis es geht');
|
||||||
|
} finally {
|
||||||
|
laeuft = false;
|
||||||
|
if (nochmal) { nochmal = false; setTimeout(sichern, 200); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const titelAus = (text) => (String(text || '').split('\n').find((z) => z.trim().length) || '')
|
||||||
|
.replace(/^\s*#{1,3}\s*/, '').replace(/^\s*[-*]\s+/, '')
|
||||||
|
.replace(/^\s*\[[ xX]\]\s*/, '').trim().slice(0, 120);
|
||||||
|
|
||||||
|
function wannSetzen() {
|
||||||
|
const w = $('wann');
|
||||||
|
if (!aktuell) { w.textContent = ''; return; }
|
||||||
|
w.textContent = herZeit(aktuell.geaendert);
|
||||||
|
w.title = genau(aktuell.geaendert);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
DER STAPEL
|
||||||
|
===================================================================== */
|
||||||
|
function passt(n) {
|
||||||
|
if (suchwort.length < 2) return true;
|
||||||
|
return String(n.text || '').toLowerCase().includes(suchwort);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stapelZeichnen() {
|
||||||
|
const liste = $('liste');
|
||||||
|
liste.textContent = '';
|
||||||
|
const sichtbar = notizen.filter(passt);
|
||||||
|
$('anzahl').textContent = suchwort.length >= 2
|
||||||
|
? `${sichtbar.length}/${notizen.length}` : String(notizen.length);
|
||||||
|
|
||||||
|
for (const n of sichtbar) {
|
||||||
|
const li = el('li');
|
||||||
|
const b = el('button', 'n-zettel');
|
||||||
|
b.type = 'button';
|
||||||
|
b.style.setProperty('--n-tinte', farbHex(n.farbe));
|
||||||
|
if (aktuell && n.id === aktuell.id) b.setAttribute('aria-current', 'true');
|
||||||
|
|
||||||
|
b.append(el('span', 'n-zettel__titel', n.titel || 'Ohne Titel'));
|
||||||
|
const vor = zweiteZeile(n.text);
|
||||||
|
if (vor) b.append(el('span', 'n-zettel__vor', vor));
|
||||||
|
|
||||||
|
const fuss = el('span', 'n-zettel__fuss');
|
||||||
|
if (n.angeheftet) {
|
||||||
|
const nadel = el('span', 'n-zettel__nadel', '📌');
|
||||||
|
nadel.title = 'Angeheftet';
|
||||||
|
fuss.append(nadel);
|
||||||
|
}
|
||||||
|
fuss.append(el('span', null, herZeit(n.geaendert)));
|
||||||
|
const h = hakenZaehlen(n.text);
|
||||||
|
if (h.alle) {
|
||||||
|
const hz = el('span', 'n-zettel__haken', `${h.an}/${h.alle}`);
|
||||||
|
hz.dataset.fertig = h.an === h.alle ? 'ja' : 'nein';
|
||||||
|
hz.title = `${h.an} von ${h.alle} erledigt`;
|
||||||
|
fuss.append(hz);
|
||||||
|
}
|
||||||
|
b.append(fuss);
|
||||||
|
|
||||||
|
b.addEventListener('click', () => oeffne(n.id));
|
||||||
|
li.append(b);
|
||||||
|
liste.append(li);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('leer').hidden = notizen.length > 0;
|
||||||
|
$('korb-auf').hidden = papierkorbZahl === 0;
|
||||||
|
$('korb-zahl').textContent = String(papierkorbZahl);
|
||||||
|
}
|
||||||
|
|
||||||
|
function zweiteZeile(text) {
|
||||||
|
const zeilen = String(text || '').split('\n').filter((z) => z.trim().length);
|
||||||
|
return (zeilen[1] || '').replace(/^\s*\[[ xX]\]\s*/, '').replace(/^\s*[-*#>]+\s*/, '')
|
||||||
|
.trim().slice(0, 90);
|
||||||
|
}
|
||||||
|
|
||||||
|
const farbHex = (name) => (farben.find((f) => f.name === name) || farben[0] || {}).hex || '#9cde90';
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
EIN BLATT AUFSCHLAGEN
|
||||||
|
===================================================================== */
|
||||||
|
function oeffne(id, { fokus = true } = {}) {
|
||||||
|
const n = notizen.find((x) => x.id === id);
|
||||||
|
if (!n) return;
|
||||||
|
/* Was im alten Blatt noch offen ist, geht zuerst raus. Sonst
|
||||||
|
landet der letzte Satz beim nächsten Blatt -- oder nirgends. */
|
||||||
|
if (aktuell && aktuell.id !== id) { clearTimeout(sicherUhr); sichern(); }
|
||||||
|
aktuell = n;
|
||||||
|
$('korb').hidden = true;
|
||||||
|
$('blatt').hidden = false;
|
||||||
|
$('blatt').style.setProperty('--n-tinte', farbHex(n.farbe));
|
||||||
|
feld.value = n.text || '';
|
||||||
|
feld.maxLength = textMax;
|
||||||
|
deckZeichnen();
|
||||||
|
wannSetzen();
|
||||||
|
standSetzen('gesichert', 'gesichert');
|
||||||
|
$('anheften').setAttribute('aria-pressed', n.angeheftet ? 'true' : 'false');
|
||||||
|
farbenZeichnen();
|
||||||
|
stapelZeichnen();
|
||||||
|
if (fokus) {
|
||||||
|
feld.focus();
|
||||||
|
/* Ans Ende springen, nicht an den Anfang: Wer ein Blatt
|
||||||
|
aufschlägt, schreibt weiter -- er liest es nicht von vorn. */
|
||||||
|
feld.setSelectionRange(feld.value.length, feld.value.length);
|
||||||
|
}
|
||||||
|
zurueckKnopf(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function schliessen() {
|
||||||
|
clearTimeout(sicherUhr);
|
||||||
|
if (aktuell) sichern();
|
||||||
|
aktuell = null;
|
||||||
|
$('blatt').hidden = true;
|
||||||
|
$('korb').hidden = true;
|
||||||
|
stapelZeichnen();
|
||||||
|
zurueckKnopf(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Der Zurück-Knopf der Kopfleiste schließt auf dem Handy das Blatt.
|
||||||
|
Auf dem Rechner wird er nicht gebraucht -- dort stehen Stapel und
|
||||||
|
Blatt nebeneinander, es gibt nichts zu schließen. */
|
||||||
|
function zurueckKnopf(an) {
|
||||||
|
const k = $('zurueck');
|
||||||
|
if (!k) return;
|
||||||
|
const schmal = window.matchMedia('(max-width: 860px)').matches;
|
||||||
|
k.hidden = !(an && schmal);
|
||||||
|
}
|
||||||
|
|
||||||
|
function farbenZeichnen() {
|
||||||
|
const kasten = $('farben');
|
||||||
|
kasten.textContent = '';
|
||||||
|
for (const f of farben) {
|
||||||
|
const b = el('button', 'n-farbe');
|
||||||
|
b.type = 'button';
|
||||||
|
b.style.setProperty('--f', f.hex);
|
||||||
|
b.title = f.wort;
|
||||||
|
b.setAttribute('aria-label', `Papierfarbe ${f.wort}`);
|
||||||
|
b.setAttribute('aria-pressed', aktuell && aktuell.farbe === f.name ? 'true' : 'false');
|
||||||
|
b.addEventListener('click', () => farbeSetzen(f.name));
|
||||||
|
kasten.append(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function farbeSetzen(name) {
|
||||||
|
if (!aktuell) return;
|
||||||
|
const alt = aktuell.farbe;
|
||||||
|
aktuell.farbe = name;
|
||||||
|
$('blatt').style.setProperty('--n-tinte', farbHex(name));
|
||||||
|
farbenZeichnen();
|
||||||
|
stapelZeichnen();
|
||||||
|
try {
|
||||||
|
const a = await hole(`/workspace/api/notizblock/${aktuell.id}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ farbe: name }),
|
||||||
|
});
|
||||||
|
if (!a.ok) throw new Error('nein');
|
||||||
|
} catch {
|
||||||
|
/* Zurückdrehen. Eine Farbe, die nach dem Neuladen wieder
|
||||||
|
umspringt, ist schlimmer als eine, die gar nicht erst
|
||||||
|
umspringt. */
|
||||||
|
aktuell.farbe = alt;
|
||||||
|
$('blatt').style.setProperty('--n-tinte', farbHex(alt));
|
||||||
|
farbenZeichnen(); stapelZeichnen();
|
||||||
|
melde('Die Farbe ließ sich nicht sichern. Versuch es gleich noch einmal.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
DIE TASTATUR — sie führt die Liste fort
|
||||||
|
===================================================================== */
|
||||||
|
|
||||||
|
/** Setzt Text und Cursor in einem Zug, so dass der Browser es
|
||||||
|
* rückgängig machen kann.
|
||||||
|
*
|
||||||
|
* `document.execCommand('insertText')` ist offiziell veraltet und
|
||||||
|
* trotzdem das Einzige, was den Rückgängig-Stapel des Schreibfelds
|
||||||
|
* füllt. Wer stattdessen `value` setzt, nimmt dem Benutzer Strg+Z
|
||||||
|
* weg -- in einem Notizblock die unfreundlichste aller Ersparnisse.
|
||||||
|
* Geht es nicht, wird `value` gesetzt; dann fehlt nur das
|
||||||
|
* Rückgängigmachen für diesen einen Schritt. */
|
||||||
|
function einfuegen(text) {
|
||||||
|
feld.focus();
|
||||||
|
let ging = false;
|
||||||
|
try { ging = document.execCommand('insertText', false, text); } catch { ging = false; }
|
||||||
|
if (!ging) {
|
||||||
|
const a = feld.selectionStart, b = feld.selectionEnd;
|
||||||
|
feld.value = feld.value.slice(0, a) + text + feld.value.slice(b);
|
||||||
|
feld.setSelectionRange(a + text.length, a + text.length);
|
||||||
|
}
|
||||||
|
deckZeichnen();
|
||||||
|
aendernGeplant();
|
||||||
|
}
|
||||||
|
|
||||||
|
function zeileUm(pos) {
|
||||||
|
const v = feld.value;
|
||||||
|
const von = v.lastIndexOf('\n', pos - 1) + 1;
|
||||||
|
let bis = v.indexOf('\n', pos);
|
||||||
|
if (bis === -1) bis = v.length;
|
||||||
|
return { von, bis, text: v.slice(von, bis) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Haken auf der Zeile des Cursors umschalten — oder einen setzen. */
|
||||||
|
function hakenUmschalten() {
|
||||||
|
const z = zeileUm(feld.selectionStart);
|
||||||
|
const m = /^(\s*)\[([ xX])\]/.exec(z.text);
|
||||||
|
if (m) {
|
||||||
|
const neu = m[2] === ' ' ? 'x' : ' ';
|
||||||
|
feld.setSelectionRange(z.von + m[1].length + 1, z.von + m[1].length + 2);
|
||||||
|
einfuegen(neu);
|
||||||
|
feld.setSelectionRange(z.bis, z.bis);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Keiner da: einen setzen, und zwar hinter der Einrückung. */
|
||||||
|
const e = /^(\s*)/.exec(z.text)[1];
|
||||||
|
feld.setSelectionRange(z.von + e.length, z.von + e.length);
|
||||||
|
einfuegen('[ ] ');
|
||||||
|
}
|
||||||
|
|
||||||
|
feld.addEventListener('keydown', (e) => {
|
||||||
|
/* Strg/Cmd + Enter: neues Blatt. */
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
||||||
|
e.preventDefault(); neuesBlatt(); return;
|
||||||
|
}
|
||||||
|
/* Strg/Cmd + Leertaste: Haken auf dieser Zeile. */
|
||||||
|
if ((e.ctrlKey || e.metaKey) && (e.key === ' ' || e.code === 'Space')) {
|
||||||
|
e.preventDefault(); hakenUmschalten(); return;
|
||||||
|
}
|
||||||
|
/* Tabulator rückt ein, statt aus dem Feld zu springen.
|
||||||
|
MIT UMSCHALT springt er weiter -- sonst käme man mit der
|
||||||
|
Tastatur nicht mehr aus dem Blatt heraus, und das wäre eine
|
||||||
|
Falle für alle, die nicht mit der Maus arbeiten. */
|
||||||
|
if (e.key === 'Tab' && !e.shiftKey) {
|
||||||
|
e.preventDefault(); einfuegen(' '); return;
|
||||||
|
}
|
||||||
|
if (e.key !== 'Enter' || e.shiftKey || e.ctrlKey || e.metaKey || e.altKey) return;
|
||||||
|
|
||||||
|
/* Enter setzt die Liste fort. */
|
||||||
|
const z = zeileUm(feld.selectionStart);
|
||||||
|
const vorn = /^(\s*)(\[[ xX]\]\s|[-*]\s|>\s?)/.exec(z.text);
|
||||||
|
if (!vorn) return;
|
||||||
|
const nurZeichen = z.text.trim() === vorn[2].trim();
|
||||||
|
e.preventDefault();
|
||||||
|
if (nurZeichen) {
|
||||||
|
/* Zweimal Enter beendet die Liste -- wie in jedem Programm, das
|
||||||
|
Listen kann. Die angefangene Zeile wird dabei geleert. */
|
||||||
|
feld.setSelectionRange(z.von, z.bis);
|
||||||
|
einfuegen('\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Ein gesetzter Haken wird NICHT mitgenommen: Die nächste Aufgabe
|
||||||
|
ist noch nicht erledigt. */
|
||||||
|
const fortsetzung = vorn[2].replace(/\[[xX]\]/, '[ ]');
|
||||||
|
einfuegen('\n' + vorn[1] + fortsetzung);
|
||||||
|
});
|
||||||
|
|
||||||
|
feld.addEventListener('input', () => { deckZeichnen(); aendernGeplant(); });
|
||||||
|
feld.addEventListener('scroll', () => { $('papier').scrollTop = feld.scrollTop; });
|
||||||
|
/* Beim Verlassen sofort sichern statt die 800 ms abzuwarten. */
|
||||||
|
feld.addEventListener('blur', () => { clearTimeout(sicherUhr); sichern(); });
|
||||||
|
|
||||||
|
/* Ein Klick auf ein Kästchen schaltet es um. Die Deckschicht lässt
|
||||||
|
sonst alles durch; nur die Haken fangen den Klick ab. */
|
||||||
|
deck.addEventListener('click', (e) => {
|
||||||
|
const hk = e.target.closest('.n-hk');
|
||||||
|
if (!hk) return;
|
||||||
|
e.preventDefault();
|
||||||
|
/* Wo im Text steht dieses Kästchen? Gezählt wird über die Knoten
|
||||||
|
davor -- das ist genauer als eine Suche nach dem n-ten „[ ]",
|
||||||
|
weil derselbe Text mehrfach vorkommen kann. */
|
||||||
|
let pos = 0;
|
||||||
|
const gehen = (knoten) => {
|
||||||
|
for (const k of knoten.childNodes) {
|
||||||
|
if (k === hk) return true;
|
||||||
|
if (k.nodeType === 3) pos += k.nodeValue.length;
|
||||||
|
else if (gehen(k)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (!gehen(deck)) return;
|
||||||
|
const an = hk.dataset.an === 'ja';
|
||||||
|
feld.focus();
|
||||||
|
feld.setSelectionRange(pos + 1, pos + 2);
|
||||||
|
einfuegen(an ? ' ' : 'x');
|
||||||
|
feld.setSelectionRange(pos + 3, pos + 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
DIE GRIFFE
|
||||||
|
===================================================================== */
|
||||||
|
async function neuesBlatt() {
|
||||||
|
melde('');
|
||||||
|
try {
|
||||||
|
const a = await hole('/workspace/api/notizblock', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ text: '' }),
|
||||||
|
});
|
||||||
|
if (!a.ok) { melde(await fehlerText(a, 'Das Blatt ließ sich nicht anlegen.')); return; }
|
||||||
|
const j = await a.json();
|
||||||
|
notizen.unshift(j.notiz);
|
||||||
|
oeffne(j.notiz.id);
|
||||||
|
} catch {
|
||||||
|
melde('Keine Verbindung. Sobald sie wieder da ist, geht es weiter.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('neu').addEventListener('click', neuesBlatt);
|
||||||
|
$('leer-neu').addEventListener('click', neuesBlatt);
|
||||||
|
|
||||||
|
$('anheften').addEventListener('click', async () => {
|
||||||
|
if (!aktuell) return;
|
||||||
|
const neu = !aktuell.angeheftet;
|
||||||
|
aktuell.angeheftet = neu;
|
||||||
|
$('anheften').setAttribute('aria-pressed', neu ? 'true' : 'false');
|
||||||
|
/* Umsortieren, damit man sieht, was der Knopf bewirkt hat. */
|
||||||
|
notizen.sort((a, b) => (Number(b.angeheftet) - Number(a.angeheftet))
|
||||||
|
|| (Date.parse(b.geaendert) - Date.parse(a.geaendert)));
|
||||||
|
stapelZeichnen();
|
||||||
|
try {
|
||||||
|
const a = await hole(`/workspace/api/notizblock/${aktuell.id}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ angeheftet: neu }),
|
||||||
|
});
|
||||||
|
if (!a.ok) throw new Error('nein');
|
||||||
|
} catch {
|
||||||
|
aktuell.angeheftet = !neu;
|
||||||
|
$('anheften').setAttribute('aria-pressed', !neu ? 'true' : 'false');
|
||||||
|
stapelZeichnen();
|
||||||
|
melde('Das Anheften ließ sich nicht sichern.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('wegtun').addEventListener('click', async () => {
|
||||||
|
if (!aktuell) return;
|
||||||
|
const id = aktuell.id;
|
||||||
|
clearTimeout(sicherUhr);
|
||||||
|
await sichern();
|
||||||
|
try {
|
||||||
|
const a = await hole(`/workspace/api/notizblock/${id}`, { method: 'DELETE' });
|
||||||
|
if (!a.ok) { melde(await fehlerText(a, 'Ging nicht.')); return; }
|
||||||
|
notizen = notizen.filter((x) => x.id !== id);
|
||||||
|
papierkorbZahl += 1;
|
||||||
|
aktuell = null;
|
||||||
|
$('blatt').hidden = true;
|
||||||
|
stapelZeichnen();
|
||||||
|
zurueckKnopf(false);
|
||||||
|
/* KEINE RÜCKFRAGE VORHER, ABER EIN WEG ZURÜCK NACHHER. Eine
|
||||||
|
Rückfrage bei jedem Wegtun ist eine Warnung, die immer kommt
|
||||||
|
-- die klickt man weg. Der Papierkorb ist die bessere
|
||||||
|
Sicherung: Er kostet nichts und hält dreißig Tage. */
|
||||||
|
melde(`Weggelegt. Liegt ${korbTage} Tage im Papierkorb.`);
|
||||||
|
setTimeout(() => melde(''), 6000);
|
||||||
|
} catch {
|
||||||
|
melde('Keine Verbindung.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
DER PAPIERKORB
|
||||||
|
===================================================================== */
|
||||||
|
$('korb-auf').addEventListener('click', korbZeigen);
|
||||||
|
$('korb-zu').addEventListener('click', () => {
|
||||||
|
$('korb').hidden = true;
|
||||||
|
if (aktuell) $('blatt').hidden = false; else zurueckKnopf(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function korbZeigen() {
|
||||||
|
melde('');
|
||||||
|
try {
|
||||||
|
const a = await hole('/workspace/api/notizblock/papierkorb');
|
||||||
|
if (!a.ok) { melde(await fehlerText(a, 'Der Papierkorb ließ sich nicht öffnen.')); return; }
|
||||||
|
const j = await a.json();
|
||||||
|
korbTage = j.papierkorb_tage || korbTage;
|
||||||
|
$('blatt').hidden = true;
|
||||||
|
$('korb').hidden = false;
|
||||||
|
zurueckKnopf(true);
|
||||||
|
$('korb-satz').textContent = j.notizen.length
|
||||||
|
? `Hier bleibt alles ${korbTage} Tage liegen und geht dann von selbst.`
|
||||||
|
: 'Nichts drin.';
|
||||||
|
const liste = $('korb-liste');
|
||||||
|
liste.textContent = '';
|
||||||
|
for (const n of j.notizen) {
|
||||||
|
const li = el('li');
|
||||||
|
const zeile = el('div', 'n-korb-zeile');
|
||||||
|
zeile.style.setProperty('--n-tinte', farbHex(n.farbe));
|
||||||
|
const t = el('div', 'n-korb-zeile__text');
|
||||||
|
t.append(el('span', 'n-korb-zeile__titel', n.titel || 'Ohne Titel'));
|
||||||
|
const w = el('span', 'n-korb-zeile__wann',
|
||||||
|
`weggelegt ${herZeit(n.weg_am)} · verschwindet ${herTag(n.endgueltig_am)}`);
|
||||||
|
w.title = genau(n.endgueltig_am);
|
||||||
|
t.append(w);
|
||||||
|
zeile.append(t);
|
||||||
|
|
||||||
|
const zurueck = el('button', 'knopf-still', 'Zurückholen');
|
||||||
|
zurueck.type = 'button';
|
||||||
|
zurueck.addEventListener('click', () => zurueckholen(n.id));
|
||||||
|
zeile.append(zurueck);
|
||||||
|
|
||||||
|
const weg = el('button', 'n-griff n-griff--weg');
|
||||||
|
weg.type = 'button';
|
||||||
|
weg.append(el('span', 'n-griff__text', 'Endgültig'));
|
||||||
|
weg.addEventListener('click', () => endgueltig(n.id, n.titel));
|
||||||
|
zeile.append(weg);
|
||||||
|
|
||||||
|
li.append(zeile);
|
||||||
|
liste.append(li);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
melde('Keine Verbindung.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** „in 12 Tagen" — dieselbe Idee wie herZeit, nur nach vorn. */
|
||||||
|
function herTag(iso) {
|
||||||
|
const ms = Date.parse(iso) - Date.now();
|
||||||
|
if (!Number.isFinite(ms)) return '';
|
||||||
|
const tage = Math.ceil(ms / 86400000);
|
||||||
|
if (tage <= 0) return 'demnächst';
|
||||||
|
if (tage === 1) return 'morgen';
|
||||||
|
return `in ${tage} Tagen`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function zurueckholen(id) {
|
||||||
|
try {
|
||||||
|
const a = await hole(`/workspace/api/notizblock/${id}/zurueck`, { method: 'POST' });
|
||||||
|
if (!a.ok) { melde(await fehlerText(a, 'Ging nicht.')); return; }
|
||||||
|
const j = await a.json();
|
||||||
|
notizen.unshift(j.notiz);
|
||||||
|
notizen.sort((x, y) => (Number(y.angeheftet) - Number(x.angeheftet))
|
||||||
|
|| (Date.parse(y.geaendert) - Date.parse(x.geaendert)));
|
||||||
|
papierkorbZahl = Math.max(0, papierkorbZahl - 1);
|
||||||
|
$('korb').hidden = true;
|
||||||
|
oeffne(j.notiz.id);
|
||||||
|
} catch { melde('Keine Verbindung.'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function endgueltig(id, titel) {
|
||||||
|
/* HIER WIRD GEFRAGT, und nur hier. Der Unterschied zum Wegtun ist
|
||||||
|
genau der, der eine Rückfrage rechtfertigt: Danach gibt es
|
||||||
|
keinen Weg zurück mehr. */
|
||||||
|
const name = titel ? `„${titel}"` : 'dieses Blatt';
|
||||||
|
if (!window.confirm(`${name} endgültig löschen?\n\nDanach ist es weg – `
|
||||||
|
+ 'auch für dich.')) return;
|
||||||
|
try {
|
||||||
|
const a = await hole(`/workspace/api/notizblock/${id}/endgueltig`, { method: 'DELETE' });
|
||||||
|
if (!a.ok) { melde(await fehlerText(a, 'Ging nicht.')); return; }
|
||||||
|
papierkorbZahl = Math.max(0, papierkorbZahl - 1);
|
||||||
|
stapelZeichnen();
|
||||||
|
korbZeigen();
|
||||||
|
} catch { melde('Keine Verbindung.'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
SUCHE
|
||||||
|
===================================================================== */
|
||||||
|
$('suche').addEventListener('input', (e) => {
|
||||||
|
suchwort = String(e.target.value || '').trim().toLowerCase();
|
||||||
|
stapelZeichnen();
|
||||||
|
if (aktuell) deckZeichnen();
|
||||||
|
});
|
||||||
|
$('suche').addEventListener('keydown', (e) => {
|
||||||
|
if (e.key !== 'Escape') return;
|
||||||
|
e.target.value = ''; suchwort = '';
|
||||||
|
stapelZeichnen(); if (aktuell) deckZeichnen();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if ((e.ctrlKey || e.metaKey) && (e.key === 'k' || e.key === 'K')) {
|
||||||
|
e.preventDefault(); $('suche').focus(); $('suche').select();
|
||||||
|
}
|
||||||
|
if (e.key === 'Escape' && document.activeElement === feld) {
|
||||||
|
feld.blur();
|
||||||
|
if (window.matchMedia('(max-width: 860px)').matches) schliessen();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('zurueck')?.addEventListener('click', (e) => {
|
||||||
|
if ($('blatt').hidden && $('korb').hidden) return; // nicht unseres
|
||||||
|
e.preventDefault(); e.stopPropagation();
|
||||||
|
schliessen();
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
window.addEventListener('resize', () => { zurueckKnopf(!!aktuell); hoeheAnpassen(); });
|
||||||
|
|
||||||
|
/* Beim Verlassen der Seite noch einmal losschicken. `keepalive`
|
||||||
|
sorgt dafür, dass die Anfrage den Seitenwechsel überlebt --
|
||||||
|
ohne das wird sie vom Browser abgebrochen, und der letzte Satz
|
||||||
|
wäre weg. */
|
||||||
|
window.addEventListener('pagehide', () => {
|
||||||
|
if (!aktuell) return;
|
||||||
|
const n = notizen.find((x) => x.id === aktuell.id);
|
||||||
|
if (n && n.text === feld.value) return;
|
||||||
|
lagerSchreiben(aktuell.id, feld.value);
|
||||||
|
try {
|
||||||
|
fetch(`/workspace/api/notizblock/${aktuell.id}`, {
|
||||||
|
method: 'PATCH', credentials: 'same-origin', keepalive: true,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ text: feld.value }),
|
||||||
|
});
|
||||||
|
} catch { /* Dann hilft der Zwischenspeicher beim nächsten Mal. */ }
|
||||||
|
});
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
LOS
|
||||||
|
===================================================================== */
|
||||||
|
async function laden() {
|
||||||
|
try {
|
||||||
|
const a = await hole('/workspace/api/notizblock');
|
||||||
|
if (!a.ok) { melde(await fehlerText(a, 'Der Block ließ sich nicht laden.')); return; }
|
||||||
|
const j = await a.json();
|
||||||
|
notizen = j.notizen || [];
|
||||||
|
farben = j.farben || [];
|
||||||
|
papierkorbZahl = j.papierkorb || 0;
|
||||||
|
textMax = j.text_max || textMax;
|
||||||
|
korbTage = j.papierkorb_tage || korbTage;
|
||||||
|
stapelZeichnen();
|
||||||
|
|
||||||
|
/* Lag noch etwas im Browser? Anbieten, nicht einspielen. */
|
||||||
|
const rest = lagerLesen();
|
||||||
|
const dazu = rest && notizen.find((x) => x.id === rest.id);
|
||||||
|
if (dazu && rest.text !== dazu.text) {
|
||||||
|
melde('Auf diesem Gerät liegt noch eine ungesicherte Fassung von '
|
||||||
|
+ `„${dazu.titel || 'Ohne Titel'}". Öffne das Blatt – du kannst sie dann übernehmen.`);
|
||||||
|
dazu.rest = rest.text;
|
||||||
|
} else if (rest) {
|
||||||
|
lagerLeeren();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Auf dem Rechner wird das oberste Blatt gleich aufgeschlagen --
|
||||||
|
ein leerer Arbeitsplatz neben einem vollen Stapel ist eine
|
||||||
|
Aufforderung zum Suchen. Auf dem Handy nicht: Dort legt sich
|
||||||
|
das Blatt über alles, und man käme nie an die Liste. */
|
||||||
|
if (notizen.length && !window.matchMedia('(max-width: 860px)').matches) {
|
||||||
|
oeffne(notizen[0].id, { fokus: false });
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
melde('Keine Verbindung zum Server.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
laden();
|
||||||
|
})();
|
||||||
@@ -445,6 +445,10 @@
|
|||||||
nicht am Ton, damit die Farbwerkzeuge weiter mit Zahlen
|
nicht am Ton, damit die Farbwerkzeuge weiter mit Zahlen
|
||||||
rechnen koennen. */
|
rechnen koennen. */
|
||||||
if (b.silber) li.dataset.silber = 'ja';
|
if (b.silber) li.dataset.silber = 'ja';
|
||||||
|
/* PAPIER -- die Oberflaeche des Notizblocks. Dieselbe Bauart
|
||||||
|
wie Silber und Regenbogen: ein Merkmal am Element, damit
|
||||||
|
die Farbwerkzeuge weiter mit Zahlen rechnen koennen. */
|
||||||
|
if (b.papier) li.dataset.papier = 'ja';
|
||||||
if (b.gross) li.dataset.gross = 'ja';
|
if (b.gross) li.dataset.gross = 'ja';
|
||||||
/* Der Einlauf wird gestaffelt -- aber über eine Eigenschaft, die
|
/* Der Einlauf wird gestaffelt -- aber über eine Eigenschaft, die
|
||||||
bei "weniger Bewegung" nichts tut (siehe start.css). */
|
bei "weniger Bewegung" nichts tut (siehe start.css). */
|
||||||
|
|||||||
+19
-19
@@ -12,19 +12,19 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -391,22 +391,22 @@
|
|||||||
<!-- TEXTFORM ZUERST (21.09.2026): Die Seite fragt beim Zeichnen nach
|
<!-- TEXTFORM ZUERST (21.09.2026): Die Seite fragt beim Zeichnen nach
|
||||||
`window.Textform`. Kaeme sie spaeter, waere der erste Eintrag ohne
|
`window.Textform`. Kaeme sie spaeter, waere der erste Eintrag ohne
|
||||||
Gestaltung. Kein `defer`, damit sie wirklich vorher dasteht. -->
|
Gestaltung. Kein `defer`, damit sie wirklich vorher dasteht. -->
|
||||||
<script src="assets/js/textform.js?v=202609252349"></script>
|
<script src="assets/js/textform.js?v=202609261240"></script>
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/hochladen.js?v=202609252349" defer></script>
|
<script src="assets/js/hochladen.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/zuteilung.js?v=202609252349" defer></script>
|
<script src="assets/js/zuteilung.js?v=202609261240" defer></script>
|
||||||
<!-- Der Vorlagenkatalog stand bis zum 23.09.2026 mitten in
|
<!-- Der Vorlagenkatalog stand bis zum 23.09.2026 mitten in
|
||||||
aufgaben.js. Er bedient jetzt zwei Seiten und hat deshalb eine
|
aufgaben.js. Er bedient jetzt zwei Seiten und hat deshalb eine
|
||||||
eigene Datei; hier zeigt er den Creator-Katalog.
|
eigene Datei; hier zeigt er den Creator-Katalog.
|
||||||
Vor aufgaben.js, weil die Seite ihn beim Start einrichtet. -->
|
Vor aufgaben.js, weil die Seite ihn beim Start einrichtet. -->
|
||||||
<script src="assets/js/vorlagenbrett.js?v=202609252349" defer></script>
|
<script src="assets/js/vorlagenbrett.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/aufgaben.js?v=202609252349" defer></script>
|
<script src="assets/js/aufgaben.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+16
-16
@@ -12,20 +12,20 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/automation.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -168,15 +168,15 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/automation.js?v=202609252349" defer></script>
|
<script src="assets/js/automation.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+17
-17
@@ -7,14 +7,14 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -197,16 +197,16 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/holen.js?v=202609252349" defer></script>
|
<script src="assets/js/holen.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/befinden.js?v=202609252349" defer></script>
|
<script src="assets/js/befinden.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+22
-22
@@ -12,24 +12,24 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/bereich.css?v=202609261240" />
|
||||||
<!-- Wegen des Blocks „Willst du dazugehoeren?" auf dem Brett
|
<!-- Wegen des Blocks „Willst du dazugehoeren?" auf dem Brett
|
||||||
„Mitmachen". Eine eigene Kopie der Regel hier waere die zweite
|
„Mitmachen". Eine eigene Kopie der Regel hier waere die zweite
|
||||||
Stelle, an der dieselbe Sache anders aussieht. -->
|
Stelle, an der dieselbe Sache anders aussieht. -->
|
||||||
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -304,19 +304,19 @@
|
|||||||
<!-- TEXTFORM ZUERST (21.09.2026): Die Seite fragt beim Zeichnen nach
|
<!-- TEXTFORM ZUERST (21.09.2026): Die Seite fragt beim Zeichnen nach
|
||||||
`window.Textform`. Kaeme sie spaeter, waere der erste Eintrag ohne
|
`window.Textform`. Kaeme sie spaeter, waere der erste Eintrag ohne
|
||||||
Gestaltung. Kein `defer`, damit sie wirklich vorher dasteht. -->
|
Gestaltung. Kein `defer`, damit sie wirklich vorher dasteht. -->
|
||||||
<script src="assets/js/textform.js?v=202609252349"></script>
|
<script src="assets/js/textform.js?v=202609261240"></script>
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/checkliste.js?v=202609252349" defer></script>
|
<script src="assets/js/checkliste.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/ampel.js?v=202609252349" defer></script>
|
<script src="assets/js/ampel.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/schulung.js?v=202609252349" defer></script>
|
<script src="assets/js/schulung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/holen.js?v=202609252349" defer></script>
|
<script src="assets/js/holen.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereich.js?v=202609252349" defer></script>
|
<script src="assets/js/bereich.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+19
-19
@@ -7,17 +7,17 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/bereich.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/treff.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/treff.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein - siehe treff-regeln.html. -->
|
<!-- haus.css MUSS die letzte Stilvorlage sein - siehe treff-regeln.html. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -108,15 +108,15 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- Der Fuß gehört zu jeder Seite - siehe pruef-css-klassen. -->
|
<!-- Der Fuß gehört zu jeder Seite - siehe pruef-css-klassen. -->
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/auskunft.js?v=202609252349" defer></script>
|
<script src="assets/js/auskunft.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bewerben.js?v=202609252349" defer></script>
|
<script src="assets/js/bewerben.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+18
-18
@@ -7,16 +7,16 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein - siehe treff-regeln.html. -->
|
<!-- haus.css MUSS die letzte Stilvorlage sein - siehe treff-regeln.html. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -80,16 +80,16 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/auskunft.js?v=202609252349" defer></script>
|
<script src="assets/js/auskunft.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bewerbungen.js?v=202609252349" defer></script>
|
<script src="assets/js/bewerbungen.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+17
-17
@@ -12,20 +12,20 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/calls.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -89,15 +89,15 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/ki.js?v=202609252349" defer></script>
|
<script src="assets/js/ki.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/calls.js?v=202609252349" defer></script>
|
<script src="assets/js/calls.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+20
-20
@@ -12,26 +12,26 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<!-- aufgaben.css bringt die Dialog- und Formularstile mit (.dialog,
|
<!-- aufgaben.css bringt die Dialog- und Formularstile mit (.dialog,
|
||||||
.neu__zeile, .dialog__knoepfe). Sie stehen dort seit dem
|
.neu__zeile, .dialog__knoepfe). Sie stehen dort seit dem
|
||||||
Aufgabenbrett; sie hierher zu kopieren hiesse, sie an zwei Stellen
|
Aufgabenbrett; sie hierher zu kopieren hiesse, sie an zwei Stellen
|
||||||
zu pflegen. Dieselbe Lösung nutzen kalender.html und content.html.
|
zu pflegen. Dieselbe Lösung nutzen kalender.html und content.html.
|
||||||
Gemeldet von pruef-css-klassen.mjs, bevor jemand einen ungestylten
|
Gemeldet von pruef-css-klassen.mjs, bevor jemand einen ungestylten
|
||||||
Dialog zu sehen bekam. -->
|
Dialog zu sehen bekam. -->
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/chat.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -739,24 +739,24 @@
|
|||||||
</form>
|
</form>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<script src="assets/js/bereiche.js?v=202609252349"></script>
|
<script src="assets/js/bereiche.js?v=202609261240"></script>
|
||||||
<!-- TEXTFORM VOR ALLEM ANDEREN (21.09.2026): chat.js und bereich.js
|
<!-- TEXTFORM VOR ALLEM ANDEREN (21.09.2026): chat.js und bereich.js
|
||||||
fragen beim Zeichnen nach `window.Textform`. Kaeme sie spaeter,
|
fragen beim Zeichnen nach `window.Textform`. Kaeme sie spaeter,
|
||||||
waere die erste gezeichnete Nachricht ohne Gestaltung -- und das
|
waere die erste gezeichnete Nachricht ohne Gestaltung -- und das
|
||||||
faellt nur dem auf, der genau hinsieht. KEIN `defer`, damit sie
|
faellt nur dem auf, der genau hinsieht. KEIN `defer`, damit sie
|
||||||
wirklich vor den anderen dasteht. -->
|
wirklich vor den anderen dasteht. -->
|
||||||
<script src="assets/js/textform.js?v=202609252349"></script>
|
<script src="assets/js/textform.js?v=202609261240"></script>
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/hochladen.js?v=202609252349" defer></script>
|
<script src="assets/js/hochladen.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349"></script>
|
<script src="assets/js/kopf.js?v=202609261240"></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349"></script>
|
<script src="assets/js/glocke.js?v=202609261240"></script>
|
||||||
<!-- ANRUF VOR CHAT (18.09.2026): anruf.js stellt `window.anrufStarten`
|
<!-- ANRUF VOR CHAT (18.09.2026): anruf.js stellt `window.anrufStarten`
|
||||||
bereit, und chat.js baut die Knoepfe nur, wenn es das gibt.
|
bereit, und chat.js baut die Knoepfe nur, wenn es das gibt.
|
||||||
Andersherum waere die Reihenfolge eine stille Abhaengigkeit --
|
Andersherum waere die Reihenfolge eine stille Abhaengigkeit --
|
||||||
die Knoepfe fehlten, und niemand wuesste warum. -->
|
die Knoepfe fehlten, und niemand wuesste warum. -->
|
||||||
<script src="assets/js/anruf.js?v=202609252349"></script>
|
<script src="assets/js/anruf.js?v=202609261240"></script>
|
||||||
<!-- DIE BILDSCHAU (25.09.2026). Eigene Datei, weil sie nichts vom
|
<!-- DIE BILDSCHAU (25.09.2026). Eigene Datei, weil sie nichts vom
|
||||||
Chat weiss: Sie bekommt einen Weg und einen Namen und zeigt ein
|
Chat weiss: Sie bekommt einen Weg und einen Namen und zeigt ein
|
||||||
Bild. Jede andere Seite mit Fotos kann sie ebenso benutzen, ohne
|
Bild. Jede andere Seite mit Fotos kann sie ebenso benutzen, ohne
|
||||||
@@ -767,8 +767,8 @@
|
|||||||
sie beim ersten Foto noch nicht da -- und der erste Klick ginge
|
sie beim ersten Foto noch nicht da -- und der erste Klick ginge
|
||||||
wie frueher in den Tab. Ein Fehler, der nur beim ERSTEN Mal
|
wie frueher in den Tab. Ein Fehler, der nur beim ERSTEN Mal
|
||||||
auftritt, ist der, den man nie nachstellen kann. -->
|
auftritt, ist der, den man nie nachstellen kann. -->
|
||||||
<script src="assets/js/bildschau.js?v=202609252349"></script>
|
<script src="assets/js/bildschau.js?v=202609261240"></script>
|
||||||
<script src="assets/js/chat.js?v=202609252349"></script>
|
<script src="assets/js/chat.js?v=202609261240"></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+17
-17
@@ -12,20 +12,20 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/content.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/content.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -179,15 +179,15 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/checkliste.js?v=202609252349" defer></script>
|
<script src="assets/js/checkliste.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/content.js?v=202609252349" defer></script>
|
<script src="assets/js/content.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -36,15 +36,15 @@
|
|||||||
<link rel="preload" as="image" href="assets/img/buehne/crew-gate-hoch-580.webp"
|
<link rel="preload" as="image" href="assets/img/buehne/crew-gate-hoch-580.webp"
|
||||||
type="image/webp" media="(max-width: 899px)" />
|
type="image/webp" media="(max-width: 899px)" />
|
||||||
|
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/crew-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/crew-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/crew-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/crew-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<!-- Diese Wand steht NUR auf der Adresse von Team Dogi (ueberall sonst
|
<!-- Diese Wand steht NUR auf der Adresse von Team Dogi (ueberall sonst
|
||||||
404, siehe crewWeiche). Sie darf das Haus deshalb direkt nennen
|
404, siehe crewWeiche). Sie darf das Haus deshalb direkt nennen
|
||||||
statt ueber den Platzhalter haus.css zu gehen -- und sie MUSS es:
|
statt ueber den Platzhalter haus.css zu gehen -- und sie MUSS es:
|
||||||
Eine Zugangswand, die sich erst nach dem Umbiegen faerbt, waere
|
Eine Zugangswand, die sich erst nach dem Umbiegen faerbt, waere
|
||||||
die eine Seite, auf der man das Umfaerben sieht. -->
|
die eine Seite, auf der man das Umfaerben sieht. -->
|
||||||
<link rel="stylesheet" href="assets/css/crew-haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/crew-haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="gate gate--crew">
|
<body class="gate gate--crew">
|
||||||
@@ -459,7 +459,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script src="assets/js/gate.js?v=202609252349" defer></script>
|
<script src="assets/js/gate.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -23,25 +23,25 @@
|
|||||||
},
|
},
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/crew-192.png?v=202609252349",
|
"src": "/assets/img/app-symbole/crew-192.png?v=202609261240",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/crew-512.png?v=202609252349",
|
"src": "/assets/img/app-symbole/crew-512.png?v=202609261240",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/crew-192-maskable.png?v=202609252349",
|
"src": "/assets/img/app-symbole/crew-192-maskable.png?v=202609261240",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable"
|
"purpose": "maskable"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/img/app-symbole/crew-512-maskable.png?v=202609252349",
|
"src": "/assets/img/app-symbole/crew-512-maskable.png?v=202609261240",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "maskable"
|
"purpose": "maskable"
|
||||||
|
|||||||
+17
-17
@@ -12,20 +12,20 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/dateien.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -108,15 +108,15 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/hochladen.js?v=202609252349" defer></script>
|
<script src="assets/js/hochladen.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/dateien.js?v=202609252349" defer></script>
|
<script src="assets/js/dateien.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+18
-18
@@ -7,14 +7,14 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -297,20 +297,20 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/holen.js?v=202609252349" defer></script>
|
<script src="assets/js/holen.js?v=202609261240" defer></script>
|
||||||
<!-- VOR entwicklung.js: Die Seite richtet den Katalog beim Start ein,
|
<!-- VOR entwicklung.js: Die Seite richtet den Katalog beim Start ein,
|
||||||
also muss es ihn zu dem Zeitpunkt geben. Mit "defer" laufen beide
|
also muss es ihn zu dem Zeitpunkt geben. Mit "defer" laufen beide
|
||||||
in Dokumentreihenfolge. -->
|
in Dokumentreihenfolge. -->
|
||||||
<script src="assets/js/vorlagenbrett.js?v=202609252349" defer></script>
|
<script src="assets/js/vorlagenbrett.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/entwicklung.js?v=202609252349" defer></script>
|
<script src="assets/js/entwicklung.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+15
-15
@@ -7,13 +7,13 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/hilfe.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/hilfe.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -146,14 +146,14 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/hilfe.js?v=202609252349" defer></script>
|
<script src="assets/js/hilfe.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -30,9 +30,9 @@
|
|||||||
<link rel="preload" as="image" href="assets/img/buehne/gate-hoch-580.webp"
|
<link rel="preload" as="image" href="assets/img/buehne/gate-hoch-580.webp"
|
||||||
type="image/webp" media="(max-width: 899px)" />
|
type="image/webp" media="(max-width: 899px)" />
|
||||||
|
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="gate">
|
<body class="gate">
|
||||||
@@ -427,7 +427,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script src="assets/js/gate.js?v=202609252349" defer></script>
|
<script src="assets/js/gate.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+17
-17
@@ -12,20 +12,20 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/kalender.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -383,15 +383,15 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kalender.js?v=202609252349" defer></script>
|
<script src="assets/js/kalender.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kalender-abo.js?v=202609252349" defer></script>
|
<script src="assets/js/kalender-abo.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+16
-16
@@ -12,20 +12,20 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/leistung.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -444,13 +444,13 @@
|
|||||||
</form>
|
</form>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349"></script>
|
<script src="assets/js/meldung.js?v=202609261240"></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349"></script>
|
<script src="assets/js/nachfrage.js?v=202609261240"></script>
|
||||||
<script src="assets/js/wahl.js?v=202609252349"></script>
|
<script src="assets/js/wahl.js?v=202609261240"></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349"></script>
|
<script src="assets/js/bereiche.js?v=202609261240"></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349"></script>
|
<script src="assets/js/kopf.js?v=202609261240"></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349"></script>
|
<script src="assets/js/glocke.js?v=202609261240"></script>
|
||||||
<script src="assets/js/leistung.js?v=202609252349"></script>
|
<script src="assets/js/leistung.js?v=202609261240"></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+16
-16
@@ -12,13 +12,13 @@
|
|||||||
vergessen wird. -->
|
vergessen wird. -->
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/material.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/material.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
<body class="start" data-ton="42" data-buehne="studio">
|
<body class="start" data-ton="42" data-buehne="studio">
|
||||||
|
|
||||||
@@ -138,14 +138,14 @@
|
|||||||
<p class="m-laedt" id="laedt">Einen Moment …</p>
|
<p class="m-laedt" id="laedt">Einen Moment …</p>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/hochladen.js?v=202609252349" defer></script>
|
<script src="assets/js/hochladen.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/material.js?v=202609252349" defer></script>
|
<script src="assets/js/material.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -0,0 +1,192 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||||
|
<title>Notizen · Team Dogi</title>
|
||||||
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
|
<meta name="theme-color" content="#06090f" />
|
||||||
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
|
<link rel="stylesheet" href="assets/css/notizen.css?v=202609261240" />
|
||||||
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="start" data-ton="46">
|
||||||
|
|
||||||
|
<div class="schleier" aria-hidden="true"></div>
|
||||||
|
|
||||||
|
<header class="kopfleiste">
|
||||||
|
<button type="button" class="zurueck-knopf" id="zurueck" hidden>
|
||||||
|
<svg class="zurueck-knopf__pfeil" viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path d="M14.5 5.5 8 12l6.5 6.5" />
|
||||||
|
</svg>
|
||||||
|
<span class="zurueck-knopf__text">Zurück</span>
|
||||||
|
</button>
|
||||||
|
<p class="marke"><span class="marke__text"><a class="zurueck" href="start.html">Team Dogi</a> · Notizen</span></p>
|
||||||
|
<div class="kopfleiste__rechts">
|
||||||
|
<span class="wer" id="wer">…</span>
|
||||||
|
<button type="button" class="abmelden" id="abmelden">Abmelden</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- ======================================================================
|
||||||
|
DER BLOCK (26.09.2026)
|
||||||
|
|
||||||
|
Filipe: „ich will dass du das auch wie ein notizblock erstellst.
|
||||||
|
ich will dass es übelst geil und einzigartig ist."
|
||||||
|
|
||||||
|
ZWEI SPALTEN AUF DEM RECHNER, EINE AUF DEM HANDY. Links der
|
||||||
|
Stapel, rechts das aufgeschlagene Blatt. Auf dem Handy wäre
|
||||||
|
nebeneinander beides zu schmal; dort ist der Stapel die Seite,
|
||||||
|
und ein Blatt legt sich darüber. Der Zurück-Knopf der Kopfleiste
|
||||||
|
schließt es wieder — das ist die Geste, die jeder kennt.
|
||||||
|
|
||||||
|
KEIN SPEICHERN-KNOPF. Nirgends. Wer einen Block aufschlägt und
|
||||||
|
lostippt, drückt hinterher nicht auf „sichern"; er klappt ihn zu.
|
||||||
|
Die Anzeige unten rechts sagt, was gerade passiert, und ist der
|
||||||
|
einzige Ort, an dem das Thema überhaupt vorkommt.
|
||||||
|
====================================================================== -->
|
||||||
|
<main class="inhalt inhalt--breit notizblock" id="block">
|
||||||
|
|
||||||
|
<!-- ---------- Die Kopfzeile der Seite ---------------------------- -->
|
||||||
|
<section class="kopf-zeile n-kopf">
|
||||||
|
<div>
|
||||||
|
<p class="marke">Notizen</p>
|
||||||
|
<h1 class="titel">Dein Block</h1>
|
||||||
|
<p class="unterzeile">
|
||||||
|
Schreib hinein, was dir einfällt. Es sichert sich von selbst —
|
||||||
|
und niemand außer dir sieht es.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="n-kopf__griffe">
|
||||||
|
<label class="n-suche" for="suche">
|
||||||
|
<svg class="n-suche__zeichen" viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<circle cx="10.5" cy="10.5" r="6" />
|
||||||
|
<path d="M15.2 15.2 20 20" />
|
||||||
|
</svg>
|
||||||
|
<input type="search" id="suche" class="n-suche__feld" placeholder="Suchen"
|
||||||
|
autocomplete="off" spellcheck="false" />
|
||||||
|
</label>
|
||||||
|
<button type="button" class="knopf n-neu" id="neu">
|
||||||
|
<span aria-hidden="true">+</span> Neues Blatt
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<p class="fehler" id="fehler" role="alert" aria-live="polite" hidden></p>
|
||||||
|
|
||||||
|
<div class="n-raum">
|
||||||
|
|
||||||
|
<!-- ---------- Links: der Stapel ------------------------------- -->
|
||||||
|
<aside class="n-stapel" id="stapel-spalte" aria-label="Deine Blätter">
|
||||||
|
<div class="n-stapel__kopf">
|
||||||
|
<h2 class="n-stapel__titel">Blätter <span class="n-zahl" id="anzahl">–</span></h2>
|
||||||
|
<button type="button" class="n-korb-knopf" id="korb-auf" hidden>
|
||||||
|
Papierkorb <span class="n-zahl" id="korb-zahl">0</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ol class="n-liste" id="liste" aria-live="polite"></ol>
|
||||||
|
<p class="n-leer" id="leer" hidden>
|
||||||
|
Noch nichts drauf. <button type="button" class="n-leer__knopf" id="leer-neu">Fang an</button>
|
||||||
|
</p>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<!-- ---------- Rechts: das aufgeschlagene Blatt ---------------- -->
|
||||||
|
<section class="n-blatt" id="blatt" hidden aria-label="Das aufgeschlagene Blatt">
|
||||||
|
|
||||||
|
<!-- Die Perforation. Reine Zierde, deshalb aria-hidden — sie
|
||||||
|
sagt dem Auge „Block", einer Vorlesefunktion nichts. -->
|
||||||
|
<div class="n-blatt__perforation" aria-hidden="true"></div>
|
||||||
|
|
||||||
|
<header class="n-blatt__kopf">
|
||||||
|
<h2 class="n-blatt__titel" id="blatt-titel">Ohne Titel</h2>
|
||||||
|
<div class="n-blatt__griffe">
|
||||||
|
<!-- Die Haken-Bilanz. Sie erscheint nur, wenn es welche
|
||||||
|
gibt: Eine Null neben einer Notiz ohne Aufgaben wäre
|
||||||
|
eine Zahl, die etwas behauptet. -->
|
||||||
|
<span class="n-haken-stand" id="haken-stand" hidden></span>
|
||||||
|
<button type="button" class="n-griff" id="anheften" aria-pressed="false"
|
||||||
|
title="Oben anheften">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path d="M9 3.6h6l-.9 5.2 3.4 3.1H6.5l3.4-3.1z" />
|
||||||
|
<path d="M12 11.9v8.5" />
|
||||||
|
</svg>
|
||||||
|
<span class="n-griff__text">Anheften</span>
|
||||||
|
</button>
|
||||||
|
<div class="n-farben" id="farben" role="group" aria-label="Papierfarbe"></div>
|
||||||
|
<button type="button" class="n-griff n-griff--weg" id="wegtun" title="In den Papierkorb">
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path d="M5.5 7h13" />
|
||||||
|
<path d="M7.5 7V5.2h9V7" />
|
||||||
|
<path d="M6.8 7.6 7.7 20h8.6l.9-12.4" />
|
||||||
|
</svg>
|
||||||
|
<span class="n-griff__text">Wegtun</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- ==================================================================
|
||||||
|
DAS BLATT SELBST — ein Schreibfeld mit einer Deckschicht
|
||||||
|
|
||||||
|
Das Feld trägt den Text und den Cursor, sieht ihn aber
|
||||||
|
nicht: `color: transparent`. Sichtbar ist die Deckschicht
|
||||||
|
darüber, die denselben Text noch einmal zeichnet — nur mit
|
||||||
|
Haken, Überschriften, Strichen und Links.
|
||||||
|
|
||||||
|
WARUM DIESER UMWEG: Ein `contenteditable` kann all das
|
||||||
|
direkt, bricht aber beim Einfügen aus fremden Programmen,
|
||||||
|
beim Rückgängigmachen und bei jeder Tastatur, die über
|
||||||
|
eine Zwischenzeile schreibt. Ein Schreibfeld tut nichts
|
||||||
|
davon — es ist das Bauteil, das der Browser seit dreißig
|
||||||
|
Jahren richtig hinbekommt.
|
||||||
|
|
||||||
|
DAMIT DAS AUFGEHT, müssen beide Schichten auf den Pixel
|
||||||
|
gleich fließen: gleiche Schrift, gleiche Zeilenhöhe,
|
||||||
|
gleicher Innenabstand, gleicher Umbruch. Und die Auszeichnung
|
||||||
|
darf die BREITE eines Zeichens nie ändern — kein Fettdruck,
|
||||||
|
keine andere Schriftgröße, kein Sperren. Erlaubt sind nur
|
||||||
|
Farbe, Durchstreichen, Schatten und Rahmen. Genau danach
|
||||||
|
ist die Gestaltung gebaut, und pruef-notizen misst es nach.
|
||||||
|
================================================================== -->
|
||||||
|
<div class="n-papier" id="papier">
|
||||||
|
<div class="n-papier__deck" id="deck" aria-hidden="true"></div>
|
||||||
|
<textarea class="n-papier__feld" id="feld" spellcheck="true"
|
||||||
|
autocomplete="off" autocapitalize="sentences"
|
||||||
|
aria-label="Text dieses Blattes"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="n-blatt__fuss">
|
||||||
|
<span class="n-stand" id="stand" aria-live="polite"></span>
|
||||||
|
<span class="n-wann" id="wann"></span>
|
||||||
|
</footer>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ---------- Der Papierkorb ---------------------------------- -->
|
||||||
|
<section class="n-korb" id="korb" hidden aria-label="Papierkorb">
|
||||||
|
<header class="n-korb__kopf">
|
||||||
|
<h2 class="n-blatt__titel">Papierkorb</h2>
|
||||||
|
<button type="button" class="knopf-still" id="korb-zu">Zurück zum Block</button>
|
||||||
|
</header>
|
||||||
|
<p class="n-korb__satz" id="korb-satz"></p>
|
||||||
|
<ol class="n-liste n-liste--korb" id="korb-liste" aria-live="polite"></ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- bereiche.js VOR kopf.js: Dort steht `window.Bereiche`, und kopf.js
|
||||||
|
liest daraus die Leitungsrollen. -->
|
||||||
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
|
<script src="assets/js/notizen.js?v=202609261240" defer></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+16
-16
@@ -12,20 +12,20 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/personen.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -453,14 +453,14 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/personen.js?v=202609252349" defer></script>
|
<script src="assets/js/personen.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+18
-18
@@ -12,20 +12,20 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/profil.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -214,16 +214,16 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/hochladen.js?v=202609252349" defer></script>
|
<script src="assets/js/hochladen.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/steckbrief.js?v=202609252349" defer></script>
|
<script src="assets/js/steckbrief.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/profil.js?v=202609252349" defer></script>
|
<script src="assets/js/profil.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+17
-17
@@ -7,14 +7,14 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/treff.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/treff.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -134,14 +134,14 @@
|
|||||||
Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen
|
Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen
|
||||||
Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen,
|
Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen,
|
||||||
die seit dem 07.09. jede Seite danach absucht. -->
|
die seit dem 07.09. jede Seite danach absucht. -->
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/holen.js?v=202609252349" defer></script>
|
<script src="assets/js/holen.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/rechte.js?v=202609252349" defer></script>
|
<script src="assets/js/rechte.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+18
-18
@@ -12,20 +12,20 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/report.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/report.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -106,17 +106,17 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
|
|
||||||
<script src="assets/js/ki.js?v=202609252349" defer></script>
|
<script src="assets/js/ki.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/holen.js?v=202609252349" defer></script>
|
<script src="assets/js/holen.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/report.js?v=202609252349" defer></script>
|
<script src="assets/js/report.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+16
-16
@@ -12,20 +12,20 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/scouting.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -173,14 +173,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/scouting.js?v=202609252349" defer></script>
|
<script src="assets/js/scouting.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+16
-16
@@ -12,21 +12,21 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<!-- Nur die Startseite: Begrüßungskachel, Uhr, Glockenmulde. Siehe
|
<!-- Nur die Startseite: Begrüßungskachel, Uhr, Glockenmulde. Siehe
|
||||||
den Hinweis in start.css -- dort stand es bis zum 07.09.2026. -->
|
den Hinweis in start.css -- dort stand es bis zum 07.09.2026. -->
|
||||||
<link rel="stylesheet" href="assets/css/heim.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/heim.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -700,16 +700,16 @@
|
|||||||
</p>
|
</p>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/live.js?v=202609252349" defer></script>
|
<script src="assets/js/live.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/start.js?v=202609252349" defer></script>
|
<script src="assets/js/start.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+17
-17
@@ -12,20 +12,20 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -85,16 +85,16 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
|
|
||||||
<script src="assets/js/ki.js?v=202609252349" defer></script>
|
<script src="assets/js/ki.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/startcheck.js?v=202609252349" defer></script>
|
<script src="assets/js/startcheck.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+18
-18
@@ -12,20 +12,20 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/profil.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -150,16 +150,16 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/hochladen.js?v=202609252349" defer></script>
|
<script src="assets/js/hochladen.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/auskunft.js?v=202609252349" defer></script>
|
<script src="assets/js/auskunft.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/steckbrief.js?v=202609252349" defer></script>
|
<script src="assets/js/steckbrief.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+14
-14
@@ -7,13 +7,13 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/support.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/support.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -128,16 +128,16 @@
|
|||||||
(reading 'LEITUNG')" ab -- gemessen am 24.09.2026, als diese
|
(reading 'LEITUNG')" ab -- gemessen am 24.09.2026, als diese
|
||||||
Zeile beim Anlegen der Seite fehlte. Die Kopfleiste kam
|
Zeile beim Anlegen der Seite fehlte. Die Kopfleiste kam
|
||||||
trotzdem, deshalb sah man es nur in der Browserkonsole. -->
|
trotzdem, deshalb sah man es nur in der Browserkonsole. -->
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<!-- wahl.js gehoert zu kopf.js: Der Sicht-Umschalter in der
|
<!-- wahl.js gehoert zu kopf.js: Der Sicht-Umschalter in der
|
||||||
Kopfleiste baut daraus seine Liste. pruef-css-klassen setzt das
|
Kopfleiste baut daraus seine Liste. pruef-css-klassen setzt das
|
||||||
seit dem 07.09.2026 durch -- damals fehlte es auf chat.html und
|
seit dem 07.09.2026 durch -- damals fehlte es auf chat.html und
|
||||||
uebersicht.html, und heute hier. -->
|
uebersicht.html, und heute hier. -->
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/support.js?v=202609252349" defer></script>
|
<script src="assets/js/support.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+18
-18
@@ -7,18 +7,18 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609261240" />
|
||||||
<!-- Wegen des Kastens „Anfragen“ ueber der Liste. Eine eigene Kopie
|
<!-- Wegen des Kastens „Anfragen“ ueber der Liste. Eine eigene Kopie
|
||||||
der Regel hier waere die zweite Stelle, an der dieselbe Sache
|
der Regel hier waere die zweite Stelle, an der dieselbe Sache
|
||||||
anders aussieht. -->
|
anders aussieht. -->
|
||||||
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/bewerbung.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -111,15 +111,15 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/holen.js?v=202609252349" defer></script>
|
<script src="assets/js/holen.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/talente.js?v=202609252349" defer></script>
|
<script src="assets/js/talente.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+16
-16
@@ -7,20 +7,20 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/team.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/team.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -110,15 +110,15 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/holen.js?v=202609252349" defer></script>
|
<script src="assets/js/holen.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/team.js?v=202609252349" defer></script>
|
<script src="assets/js/team.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+17
-17
@@ -7,20 +7,20 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/team.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/team.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<!-- `data-buehne` steht hier fest und nicht im Skript: Die Kachel zu
|
<!-- `data-buehne` steht hier fest und nicht im Skript: Die Kachel zu
|
||||||
@@ -84,15 +84,15 @@
|
|||||||
Kaputt ist dabei nichts -- er funktioniert. Genau deshalb faellt
|
Kaputt ist dabei nichts -- er funktioniert. Genau deshalb faellt
|
||||||
es niemandem auf, und deshalb prueft pruef-css-klassen die
|
es niemandem auf, und deshalb prueft pruef-css-klassen die
|
||||||
ABHAENGIGKEIT statt der Optik. Hier hat sie angeschlagen. -->
|
ABHAENGIGKEIT statt der Optik. Hier hat sie angeschlagen. -->
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/holen.js?v=202609252349" defer></script>
|
<script src="assets/js/holen.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/teamlage.js?v=202609252349" defer></script>
|
<script src="assets/js/teamlage.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+15
-15
@@ -7,13 +7,13 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/bereich.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -112,15 +112,15 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/teilen.js?v=202609252349" defer></script>
|
<script src="assets/js/teilen.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,14 +7,14 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/treff.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/treff.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -126,14 +126,14 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/treff-moderation.js?v=202609252349" defer></script>
|
<script src="assets/js/treff-moderation.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+16
-16
@@ -7,18 +7,18 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/treff.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/treff.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein. Auf der Adresse des
|
<!-- haus.css MUSS die letzte Stilvorlage sein. Auf der Adresse des
|
||||||
Rudels biegt der Server genau diesen Dateinamen auf crew-haus.css
|
Rudels biegt der Server genau diesen Dateinamen auf crew-haus.css
|
||||||
um, und die Seite kommt schon beim ERSTEN Abruf in Lila und
|
um, und die Seite kommt schon beim ERSTEN Abruf in Lila und
|
||||||
Babyblau an -- ohne Umfaerben im Browser, ohne Flackern. -->
|
Babyblau an -- ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -248,14 +248,14 @@
|
|||||||
Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen
|
Beim ersten Anlauf fehlte die Zeile auf genau diesen beiden neuen
|
||||||
Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen,
|
Seiten. Gefunden hat das nicht das Auge, sondern pruef-css-klassen,
|
||||||
die seit dem 07.09. jede Seite danach absucht. -->
|
die seit dem 07.09. jede Seite danach absucht. -->
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/auskunft.js?v=202609252349" defer></script>
|
<script src="assets/js/auskunft.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/treff-regeln.js?v=202609252349" defer></script>
|
<script src="assets/js/treff-regeln.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+16
-16
@@ -12,22 +12,22 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<!-- Formular- und Dialogstile: seit dem 07.09.2026 steht hier der
|
<!-- Formular- und Dialogstile: seit dem 07.09.2026 steht hier der
|
||||||
Dialog "Creator anlegen". -->
|
Dialog "Creator anlegen". -->
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -149,16 +149,16 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
|
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/uebersicht.js?v=202609252349" defer></script>
|
<script src="assets/js/uebersicht.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,13 +7,13 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/unsere-seiten.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/unsere-seiten.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -335,14 +335,14 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/live.js?v=202609252349" defer></script>
|
<script src="assets/js/live.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/unsere-seiten.js?v=202609252349" defer></script>
|
<script src="assets/js/unsere-seiten.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,13 +7,13 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/unterstuetzen.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/unterstuetzen.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -106,17 +106,17 @@
|
|||||||
<!-- bereiche.js VOR kopf.js: Dort steht `window.Bereiche`, und kopf.js
|
<!-- bereiche.js VOR kopf.js: Dort steht `window.Bereiche`, und kopf.js
|
||||||
liest daraus die Leitungsrollen. Ohne sie stürzt jede Seite mit
|
liest daraus die Leitungsrollen. Ohne sie stürzt jede Seite mit
|
||||||
„Cannot read properties of undefined (reading 'LEITUNG')" ab. -->
|
„Cannot read properties of undefined (reading 'LEITUNG')" ab. -->
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/holen.js?v=202609252349" defer></script>
|
<script src="assets/js/holen.js?v=202609261240" defer></script>
|
||||||
<!-- Die Satz-Tafel. Ohne sie stand auf dieser Seite das Maschinenwort
|
<!-- Die Satz-Tafel. Ohne sie stand auf dieser Seite das Maschinenwort
|
||||||
des Servers (z.B. "nicht_gefunden") statt eines Satzes -- gemeldet
|
des Servers (z.B. "nicht_gefunden") statt eines Satzes -- gemeldet
|
||||||
von pruef-meldungen, nicht beim Ansehen. -->
|
von pruef-meldungen, nicht beim Ansehen. -->
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/unterstuetzen.js?v=202609252349" defer></script>
|
<script src="assets/js/unterstuetzen.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+16
-16
@@ -7,15 +7,15 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/entwicklung.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/werdegang.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/werdegang.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -144,14 +144,14 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/werdegang.js?v=202609252349" defer></script>
|
<script src="assets/js/werdegang.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+15
-15
@@ -12,13 +12,13 @@
|
|||||||
vergessen wird. -->
|
vergessen wird. -->
|
||||||
<link rel="manifest" href="/workspace/app.webmanifest" />
|
<link rel="manifest" href="/workspace/app.webmanifest" />
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/willkommen.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/willkommen.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
<!-- KOERPERKLASSE "start", NICHT "gate" (berichtigt 21.09.2026).
|
<!-- KOERPERKLASSE "start", NICHT "gate" (berichtigt 21.09.2026).
|
||||||
`body.gate` ist die Anmeldewand: `display:flex; align-items:center`,
|
`body.gate` ist die Anmeldewand: `display:flex; align-items:center`,
|
||||||
@@ -75,13 +75,13 @@
|
|||||||
<p class="fehler" id="fehler" hidden></p>
|
<p class="fehler" id="fehler" hidden></p>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/willkommen.js?v=202609252349" defer></script>
|
<script src="assets/js/willkommen.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+17
-17
@@ -12,20 +12,20 @@
|
|||||||
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
Ton wie die Kopfleiste darunter (#06090f), damit Leiste und Seite
|
||||||
eine Flaeche sind statt zweier. -->
|
eine Flaeche sind statt zweier. -->
|
||||||
<meta name="theme-color" content="#06090f" />
|
<meta name="theme-color" content="#06090f" />
|
||||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609252349" />
|
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png?v=202609261240" />
|
||||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609252349" />
|
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/gate.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/start.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/start.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/wissen.css?v=202609261240" />
|
||||||
<link rel="stylesheet" href="assets/css/module.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/module.css?v=202609261240" />
|
||||||
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
<!-- haus.css MUSS die letzte Stilvorlage sein (10.09.2026).
|
||||||
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
Auf der Agenturadresse ist sie leer -- das Haus hier IST der
|
||||||
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
Grundzustand. Auf der Adresse von Team Dogi biegt der Server
|
||||||
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
genau diesen Dateinamen auf crew-haus.css um, und die Seite
|
||||||
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
kommt schon beim ERSTEN Abruf in Lila und Babyblau an --
|
||||||
ohne Umfaerben im Browser, ohne Flackern. -->
|
ohne Umfaerben im Browser, ohne Flackern. -->
|
||||||
<link rel="stylesheet" href="assets/css/haus.css?v=202609252349" />
|
<link rel="stylesheet" href="assets/css/haus.css?v=202609261240" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="start">
|
<body class="start">
|
||||||
@@ -190,15 +190,15 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="assets/js/meldung.js?v=202609252349" defer></script>
|
<script src="assets/js/meldung.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/hochladen.js?v=202609252349" defer></script>
|
<script src="assets/js/hochladen.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/nachfrage.js?v=202609252349" defer></script>
|
<script src="assets/js/nachfrage.js?v=202609261240" defer></script>
|
||||||
|
|
||||||
<script src="assets/js/wahl.js?v=202609252349" defer></script>
|
<script src="assets/js/wahl.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/bereiche.js?v=202609252349" defer></script>
|
<script src="assets/js/bereiche.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/installieren.js?v=202609252349" defer></script>
|
<script src="assets/js/installieren.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/kopf.js?v=202609252349" defer></script>
|
<script src="assets/js/kopf.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/glocke.js?v=202609252349" defer></script>
|
<script src="assets/js/glocke.js?v=202609261240" defer></script>
|
||||||
<script src="assets/js/wissen.js?v=202609252349" defer></script>
|
<script src="assets/js/wissen.js?v=202609261240" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user