Punkt 15: Der Chat bekommt Suche, Antworten mit Zitat und die Ungelesen-Linie
Wunsch Filipe: "ich will dass du diese seite viel krasser und detaillierter machst, ich will dass du dich informierst und alles reinsetzt was wir noch gebrauchen koennten." NICHT ALLES, SONDERN WAS TAEGLICH FEHLT. Der Chat konnte schon Raeume, Verlauf, Gelesen-Stand, Live-Zustellung, Gruppen und Zuruecknehmen. Drei Dinge fehlten, und jedes davon kostet ohne es echte Zeit: 1. SUCHE IN DEN NACHRICHTEN. Ein Chat ohne sie ist ab dem zweiten Monat ein Archiv, in dem man nichts findet. Ein Suchfeld gab es -- es durchsuchte aber nur die NAMEN der Gespraeche, also die kleinere Haelfte. Jetzt durchsucht dasselbe Feld beides und zeigt die Fundstellen UNTER der Gespraechsliste: Wer "Patrick" eingibt, will vielleicht das Gespraech und vielleicht die Nachricht -- ein Umschalter haette ihn zwingen wollen, das vorher zu wissen. 2. ANTWORTEN MIT ZITAT. Zu zweit weiss man meistens, worauf sich etwas bezieht. In einer Gruppe laufen drei Faeden parallel, und "ja, mach das" kann alles heissen. Das Zitat steht IN der Blase (es gehoert zur Antwort, nicht darueber) und fuehrt per Klick zur Stelle. 3. DIE LINIE "AB HIER NEU". Wer nach zwei Tagen zurueckkommt, sucht sonst die Stelle, an der er aufgehoert hat, indem er Uhrzeiten liest. BEWUSST NICHT GEBAUT: Anhaenge (dafuer gibt es den Dateien-Bereich mit Rechten und Ablauf), Reaktionen (eine vierte Sache, bevor die drei sich bewaehrt haben) und Tipp-Anzeigen (dauernder Verkehr fuer eine Auskunft, die man in zwei Sekunden ohnehin sieht). DIE SICHERHEIT DER SUCHE STEHT IM JOIN, nicht in einer nachtraeglichen Pruefung: `chat_teilnehmer` wird mit der eigenen Personenkennung verbunden, und was dort nicht drinsteht, kommt gar nicht erst aus der Datenbank. Ein Filter, der erst hinterher aussortiert, ist eine Zeile davon entfernt, vergessen zu werden. Ebenso beim Zitat: Worauf geantwortet wird, muss im SELBEN Raum liegen -- sonst koennte jemand die Kennung aus einem fremden Gespraech mitschicken, und beim Empfaenger stuende ein Zitat aus einem Raum, den er nie gesehen hat. DREI FEHLER, DIE DER DURCHLAUF GEFUNDEN HAT: 1. `ESCAPE '\'` IN EINEM TEMPLATE-LITERAL. Dort ist `\'` eine Fluchtsequenz fuer das Anfuehrungszeichen -- SQLite bekam ein LEERES Fluchtzeichen und antwortete "ESCAPE expression must be a single character". Die Suche war damit komplett tot. Kein Syntaxfehler, kein Warnhinweis: Erst der Aufruf mit echten Daten hat es gezeigt. 2. DIE MASKIERUNG KANNTE ZWEI VON DREI ZEICHEN. `%` und `_` waren dabei, der Backslash nicht -- ausgerechnet das Fluchtzeichen selbst. Geprueft wird das jetzt an der ZEILE AUS DER DATEI, nicht an einem Nachbau: Mein erster Test hat die Maskierung nachgebaut und dabei die Shell-Maskierung mitgeschleppt -- er meldete einen Fehler, den nur er hatte. 3. DIE UNGELESEN-LINIE SCHIEN NICHT ZU FUNKTIONIEREN. Sie tat es -- mein Testaufbau war falsch: Filipes Seite war noch offen, die neuen Nachrichten kamen ueber den Live-Strom an und wurden sofort als gelesen gemeldet. Es gab schlicht nichts Ungelesenes. Erst als er die Seite verlaesst, bevor Patrick schreibt, steht die Linie da -- und zwar genau vor "Neu von Patrick, eins", und beim zweiten Oeffnen ist sie weg. Der Gelesen-Stand wird deshalb beim OEFFNEN mitgeschickt, bevor er gesetzt wird -- eine Zeile spaeter waere er immer die letzte Nachricht, und die Linie staende nie irgendwo. Ohne Volltextindex, mit Absicht: `LIKE` liest die Tabelle, und bei einem Team dieser Groesse sind das einige tausend Zeilen. Ein FTS5-Index waere eine zweite Tabelle, die synchron gehalten werden muss -- genau daran gehen solche Sachen kaputt. Wenn der Verlauf sechsstellig wird, ist das der Zeitpunkt dafuer, nicht heute. Geprueft: pruef-chat, pruef-chat-optik, pruef-css-klassen, pruef-workspace-seiten, pruef-handy -- alle in Ordnung. Dazu im Browser durchgespielt: Zitat gesetzt und gelesen, Suche nach "Bitrate" (1 Treffer), nach "100 %" (1 Treffer -- die Maskierung haelt), nach Unsinn (0), einbuchstabige Suche (zu kurz), Antwortleiste mit dem richtigen Namen, Ungelesen-Linie an der richtigen Stelle und beim zweiten Oeffnen weg. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+145
-3
@@ -300,14 +300,36 @@ chatRouter.get("/workspace/api/chat/raeume/:id/nachrichten", (req, res) => {
|
||||
Browser nicht mit zehntausend Zeilen bewerfen; ältere holt man
|
||||
über `vor`. */
|
||||
const vor = Number(req.query.vor) || 0;
|
||||
/* MIT DEM ZITAT IN EINER ABFRAGE (09.09.2026, Punkt 15).
|
||||
|
||||
Der zweite Verbund holt die Nachricht, auf die geantwortet
|
||||
wurde. Sie einzeln nachzuladen waere je Antwort eine weitere
|
||||
Abfrage -- bei 200 Zeilen im schlimmsten Fall 200 Stueck.
|
||||
|
||||
`LEFT` und nicht `INNER`: Die meisten Nachrichten sind keine
|
||||
Antworten, und die duerfen nicht verschwinden. */
|
||||
const reihen = db().prepare(`
|
||||
SELECT n.id, n.person_id, n.text, n.erstellt, n.weg_am, p.name AS von, p.rolle,
|
||||
p.bild AS von_bild
|
||||
p.bild AS von_bild,
|
||||
n.antwort_auf,
|
||||
a.text AS zitat_text, a.weg_am AS zitat_weg, ap.name AS zitat_von
|
||||
FROM chat_nachrichten n
|
||||
LEFT JOIN personen p ON p.id = n.person_id
|
||||
LEFT JOIN chat_nachrichten a ON a.id = n.antwort_auf AND a.raum_id = n.raum_id
|
||||
LEFT JOIN personen ap ON ap.id = a.person_id
|
||||
WHERE n.raum_id = ? ${vor ? "AND n.id < ?" : ""}
|
||||
ORDER BY n.id DESC LIMIT 200`).all(...(vor ? [raumId, vor] : [raumId]));
|
||||
|
||||
/* DER GELESEN-STAND, BEVOR ER GESETZT WIRD.
|
||||
|
||||
Die Oberflaeche zieht daraus die Linie "ab hier ist es neu". Sie
|
||||
muss ihn deshalb beim OEFFNEN bekommen -- eine Zeile spaeter
|
||||
(nach dem PUT auf /gelesen) waere er immer die letzte Nachricht,
|
||||
und die Linie staende nie irgendwo. */
|
||||
const gelesenBis = db().prepare(
|
||||
"SELECT gelesen_bis FROM chat_teilnehmer WHERE raum_id = ? AND person_id = ?")
|
||||
.get(raumId, req.person.id)?.gelesen_bis ?? 0;
|
||||
|
||||
const raum = db().prepare("SELECT id, art, name FROM chat_raeume WHERE id = ?").get(raumId);
|
||||
const leute = teilnehmerVon(raumId);
|
||||
|
||||
@@ -318,6 +340,7 @@ chatRouter.get("/workspace/api/chat/raeume/:id/nachrichten", (req, res) => {
|
||||
teilnehmer: leute.map((t) => ({ id: t.id, name: t.name, rolle: t.rolle,
|
||||
bild: t.bild, leitung: !!t.leitung })),
|
||||
},
|
||||
gelesen_bis: gelesenBis,
|
||||
/* Aufsteigend zurück -- gelesen wird von oben nach unten. */
|
||||
nachrichten: reihen.reverse().map((n) => ({
|
||||
id: n.id, von_id: n.person_id, von: n.von || "Gelöscht", rolle: n.rolle,
|
||||
@@ -326,6 +349,17 @@ chatRouter.get("/workspace/api/chat/raeume/:id/nachrichten", (req, res) => {
|
||||
zurueckgenommen: !!n.weg_am,
|
||||
erstellt: n.erstellt,
|
||||
selbst: n.person_id === req.person.id,
|
||||
/* Das Zitat wird GEKUERZT geschickt, nicht ganz: Es ist ein
|
||||
Hinweis auf die Stelle, nicht ihre Wiederholung. Und eine
|
||||
zurueckgenommene Nachricht bleibt auch im Zitat
|
||||
zurueckgenommen -- sonst waere das Zuruecknehmen ueber ein
|
||||
Zitat zu umgehen. */
|
||||
antwort: n.antwort_auf ? {
|
||||
id: n.antwort_auf,
|
||||
von: n.zitat_von || "Gelöscht",
|
||||
text: n.zitat_weg ? null : String(n.zitat_text || "").slice(0, 140),
|
||||
zurueckgenommen: !!n.zitat_weg,
|
||||
} : null,
|
||||
})),
|
||||
});
|
||||
} catch (fehler) {
|
||||
@@ -348,19 +382,43 @@ chatRouter.post("/workspace/api/chat/raeume/:id/nachrichten", gleicheHerkunft,
|
||||
return res.status(400).json({ fehler: `Länger als ${TEXT_MAX} Zeichen geht nicht.` });
|
||||
}
|
||||
|
||||
/* WORAUF GEANTWORTET WIRD, MUSS IN DEMSELBEN RAUM LIEGEN.
|
||||
|
||||
Ohne diese Pruefung koennte jemand die Kennung einer Nachricht
|
||||
aus einem fremden Gespraech mitschicken -- und beim Empfaenger
|
||||
stuende ein Zitat aus einem Raum, den er nie gesehen hat. Die
|
||||
Abfrage bindet deshalb `raum_id` mit ein; passt es nicht, wird
|
||||
das Zitat still weggelassen statt die Nachricht abzulehnen.
|
||||
Der Text ist das Wichtige, das Zitat die Zugabe. */
|
||||
let antwortAuf = Number(req.body?.antwort_auf) || null;
|
||||
if (antwortAuf) {
|
||||
const da = db().prepare(
|
||||
"SELECT 1 FROM chat_nachrichten WHERE id = ? AND raum_id = ?").get(antwortAuf, raumId);
|
||||
if (!da) antwortAuf = null;
|
||||
}
|
||||
|
||||
const d = db();
|
||||
const n = jetzt();
|
||||
d.prepare("INSERT INTO chat_nachrichten (raum_id, person_id, text, erstellt) VALUES (?,?,?,?)")
|
||||
.run(raumId, req.person.id, text, n);
|
||||
d.prepare("INSERT INTO chat_nachrichten (raum_id, person_id, text, erstellt, antwort_auf) VALUES (?,?,?,?,?)")
|
||||
.run(raumId, req.person.id, text, n, antwortAuf);
|
||||
const id = d.prepare("SELECT last_insert_rowid() AS id").get().id;
|
||||
d.prepare("UPDATE chat_raeume SET letzte_am = ? WHERE id = ?").run(n, raumId);
|
||||
/* Wer schreibt, hat das Eigene gelesen. */
|
||||
d.prepare("UPDATE chat_teilnehmer SET gelesen_bis = ? WHERE raum_id = ? AND person_id = ?")
|
||||
.run(id, raumId, req.person.id);
|
||||
|
||||
const zitat = antwortAuf ? d.prepare(`
|
||||
SELECT a.text, a.weg_am, p.name AS von FROM chat_nachrichten a
|
||||
LEFT JOIN personen p ON p.id = a.person_id WHERE a.id = ?`).get(antwortAuf) : null;
|
||||
|
||||
const nachricht = {
|
||||
id, raum_id: raumId, von_id: req.person.id, von: req.person.name,
|
||||
rolle: req.person.rolle, text, erstellt: n, zurueckgenommen: false,
|
||||
antwort: zitat ? {
|
||||
id: antwortAuf, von: zitat.von || "Gelöscht",
|
||||
text: zitat.weg_am ? null : String(zitat.text || "").slice(0, 140),
|
||||
zurueckgenommen: !!zitat.weg_am,
|
||||
} : null,
|
||||
};
|
||||
|
||||
/* Sofort an alle, die gerade zusehen -- und per Benachrichtigung
|
||||
@@ -376,6 +434,90 @@ chatRouter.post("/workspace/api/chat/raeume/:id/nachrichten", gleicheHerkunft,
|
||||
}
|
||||
});
|
||||
|
||||
/* =====================================================================
|
||||
SUCHE (09.09.2026, Punkt 15)
|
||||
|
||||
Filipe: "ich will dass du diese seite viel krasser und detaillierter
|
||||
machst ... alles reinsetzt was wir noch gebrauchen koennten."
|
||||
|
||||
Ein Chat ohne Suche ist ab dem zweiten Monat ein Archiv, in dem man
|
||||
nichts findet. Das ist die Funktion, die am haeufigsten fehlt und am
|
||||
meisten Zeit spart -- vor Reaktionen, vor Anhaengen, vor allem
|
||||
anderen.
|
||||
|
||||
DIE EINGRENZUNG AUF DIE EIGENEN RAEUME IST DIE GANZE SICHERHEIT
|
||||
DIESER FUNKTION. Sie steht deshalb im JOIN und nicht in einer
|
||||
nachtraeglichen Pruefung: `chat_teilnehmer` wird mit der eigenen
|
||||
Personenkennung verbunden, und was dort nicht drinsteht, kommt gar
|
||||
nicht erst aus der Datenbank. Ein Filter, der erst hinterher
|
||||
aussortiert, ist eine Zeile davon entfernt, vergessen zu werden.
|
||||
|
||||
OHNE VOLLTEXTINDEX, MIT ABSICHT. `LIKE '%...%'` kann keinen Index
|
||||
benutzen und liest die Tabelle. Bei einem Team dieser Groesse sind
|
||||
das einige tausend Zeilen -- gemessen unter einer Millisekunde. Ein
|
||||
FTS5-Index waere eine zweite Tabelle, die synchron gehalten werden
|
||||
muss, und genau daran gehen solche Sachen kaputt. Wenn der Verlauf
|
||||
einmal sechsstellig wird, ist das der richtige Zeitpunkt dafuer,
|
||||
nicht heute.
|
||||
|
||||
`escape` FUER DIE PLATZHALTER: In `LIKE` sind `%` und `_` Zeichen
|
||||
mit Bedeutung. Wer nach "100 %" sucht, wuerde sonst alles finden.
|
||||
===================================================================== */
|
||||
chatRouter.get("/workspace/api/chat/suche", (req, res) => {
|
||||
try {
|
||||
const roh = String(req.query.q ?? "").trim();
|
||||
if (roh.length < 2) return res.json({ frage: roh, treffer: [], zuKurz: true });
|
||||
if (roh.length > 120) return res.status(400).json({ fehler: "zu_lang" });
|
||||
|
||||
/* AUCH DER BACKSLASH SELBST. Er ist hier das Fluchtzeichen
|
||||
(`ESCAPE '\'`); wer ihn eingibt, wuerde sonst das naechste
|
||||
Zeichen entwerten und im Zweifel ein ungueltiges Muster erzeugen.
|
||||
Drei Zeichen haben in `LIKE` eine Bedeutung, nicht zwei. */
|
||||
const muster = "%" + roh.replace(/[\\%_]/g, (z) => "\\" + z) + "%";
|
||||
const reihen = db().prepare(`
|
||||
SELECT n.id, n.raum_id, n.text, n.erstellt,
|
||||
p.name AS von, p.rolle,
|
||||
r.art, r.name AS raum_name
|
||||
FROM chat_nachrichten n
|
||||
JOIN chat_teilnehmer t ON t.raum_id = n.raum_id AND t.person_id = ?
|
||||
JOIN chat_raeume r ON r.id = n.raum_id
|
||||
LEFT JOIN personen p ON p.id = n.person_id
|
||||
WHERE n.weg_am IS NULL
|
||||
AND n.text LIKE ? ESCAPE '\\'
|
||||
ORDER BY n.id DESC LIMIT 60`).all(req.person.id, muster);
|
||||
|
||||
res.json({
|
||||
frage: roh,
|
||||
treffer: reihen.map((n) => {
|
||||
const leute = teilnehmerVon(n.raum_id);
|
||||
return {
|
||||
id: n.id, raum_id: n.raum_id,
|
||||
raum: raumName({ id: n.raum_id, art: n.art, name: n.raum_name }, leute, req.person.id),
|
||||
von: n.von || "Gelöscht", rolle: n.rolle,
|
||||
erstellt: n.erstellt,
|
||||
/* Der Ausschnitt wird um den Treffer herum geschnitten, nicht
|
||||
vom Anfang: Bei einer langen Nachricht stuende sonst der
|
||||
Anfang da und das gesuchte Wort waere nicht dabei. */
|
||||
text: ausschnitt(n.text, roh),
|
||||
};
|
||||
}),
|
||||
});
|
||||
} catch (fehler) {
|
||||
console.error("[chat] Suche:", fehler?.message);
|
||||
res.status(503).json({ fehler: "nicht_verfuegbar" });
|
||||
}
|
||||
});
|
||||
|
||||
/** Ein Stueck Text um die Fundstelle herum. */
|
||||
function ausschnitt(text, frage) {
|
||||
const t = String(text || "");
|
||||
const i = t.toLowerCase().indexOf(frage.toLowerCase());
|
||||
if (i < 0 || t.length <= 160) return t.slice(0, 160);
|
||||
const von = Math.max(0, i - 50);
|
||||
const bis = Math.min(t.length, i + frage.length + 90);
|
||||
return (von > 0 ? "… " : "") + t.slice(von, bis) + (bis < t.length ? " …" : "");
|
||||
}
|
||||
|
||||
/** Bis hierher gelesen. */
|
||||
chatRouter.put("/workspace/api/chat/raeume/:id/gelesen", gleicheHerkunft,
|
||||
express.json({ limit: "4kb" }), (req, res) => {
|
||||
|
||||
@@ -376,6 +376,25 @@ function umstellungen(d) {
|
||||
Aenderung vergessen. Genau diese Sorte Doppelung hat mich heute
|
||||
schon dreimal aufgehalten. */
|
||||
["push_einstellungen", "wert", "TEXT"],
|
||||
|
||||
/* ANTWORTEN MIT ZITAT (09.09.2026, Punkt 15).
|
||||
|
||||
Filipe: "ich will dass du diese seite viel krasser und
|
||||
detaillierter machst ... alles reinsetzt was wir noch gebrauchen
|
||||
koennten."
|
||||
|
||||
In einem Gespraech zu zweit weiss man meistens, worauf sich
|
||||
etwas bezieht. In einer Gruppe nicht -- dort laufen drei Faeden
|
||||
parallel, und "ja, mach das" kann alles heissen. Die Spalte
|
||||
zeigt auf die Nachricht, auf die geantwortet wird.
|
||||
|
||||
KEIN FREMDSCHLUESSEL AUF chat_nachrichten: Wird die zitierte
|
||||
Nachricht zurueckgenommen, bleibt ihre Zeile ohnehin stehen
|
||||
(nur der Text wird geleert) -- und wird der ganze Raum
|
||||
geloescht, geht die Antwort per CASCADE ueber `raum_id`
|
||||
ohnehin mit. Ein zweiter Schluessel brauchte nur eine zweite
|
||||
Gelegenheit fuer eine Sperre. */
|
||||
["chat_nachrichten", "antwort_auf", "INTEGER"],
|
||||
]) {
|
||||
try {
|
||||
const vorhanden = d.prepare(`PRAGMA table_info(${tabelle})`).all().map((s) => s.name);
|
||||
|
||||
@@ -636,3 +636,161 @@
|
||||
.chat-raum__knopf[data-an="ja"] { box-shadow: none; border: 1px solid CanvasText; }
|
||||
.chat-raum__zeichen, .chat-nachricht__zeichen { background: Canvas; box-shadow: none; }
|
||||
}
|
||||
|
||||
/* =====================================================================
|
||||
DREI DINGE, DIE DEM CHAT GEFEHLT HABEN (Punkt 15, 09.09.2026)
|
||||
|
||||
Filipe: "ich will dass du diese seite viel krasser und detaillierter
|
||||
machst, ich will dass du dich informierst und alles reinsetzt was
|
||||
wir noch gebrauchen könnten."
|
||||
|
||||
Nicht alles, sondern das, was hier täglich fehlt. Der Chat konnte
|
||||
schon Räume, Verlauf, Gelesen-Stand, Live-Zustellung, Gruppen und
|
||||
Zurücknehmen. Was fehlte:
|
||||
|
||||
1. SUCHE IN DEN NACHRICHTEN. Ein Chat ohne sie ist ab dem zweiten
|
||||
Monat ein Archiv, in dem man nichts findet. Das Suchfeld gab es
|
||||
— es durchsuchte aber nur die NAMEN der Gespräche.
|
||||
2. ANTWORTEN MIT ZITAT. Zu zweit weiß man meist, worauf sich etwas
|
||||
bezieht. In einer Gruppe laufen drei Fäden parallel, und "ja,
|
||||
mach das" kann alles heißen.
|
||||
3. DIE LINIE "AB HIER NEU". Wer nach zwei Tagen zurückkommt, sucht
|
||||
sonst die Stelle, an der er aufgehört hat, indem er Uhrzeiten
|
||||
liest.
|
||||
|
||||
Bewusst NICHT gebaut: Anhänge (dafür gibt es den Dateien-Bereich mit
|
||||
Rechten und Ablauf), Reaktionen (eine vierte Sache, bevor die drei
|
||||
sich bewährt haben) und Tipp-Anzeigen (sie kosten dauernd Verkehr
|
||||
und sagen nichts, was man nicht in zwei Sekunden sieht).
|
||||
===================================================================== */
|
||||
|
||||
/* ---- Das Zitat in der Blase ---------------------------------------- */
|
||||
.chat-zitat {
|
||||
display: block; width: 100%; text-align: left;
|
||||
margin: 0 0 7px; padding: 6px 10px 6px 11px;
|
||||
border: 0; border-radius: 0;
|
||||
/* Der senkrechte Strich links ist das Zeichen für ein Zitat -- in
|
||||
jedem Mailprogramm seit dreißig Jahren dasselbe. */
|
||||
box-shadow: inset 3px 0 0 color-mix(in srgb, var(--akzent) 60%, transparent);
|
||||
background: rgba(255, 255, 255, .05);
|
||||
color: var(--text-leise);
|
||||
font: inherit; cursor: pointer;
|
||||
transition: background var(--tempo);
|
||||
}
|
||||
.chat-zitat:hover { background: rgba(255, 255, 255, .09); }
|
||||
.chat-zitat__von {
|
||||
display: block;
|
||||
font-size: .74rem; font-weight: 700;
|
||||
color: color-mix(in srgb, var(--akzent) 70%, #ffffff);
|
||||
}
|
||||
.chat-zitat__text {
|
||||
display: block;
|
||||
font-size: .8rem; line-height: 1.35;
|
||||
/* Zwei Zeilen reichen: Es ist ein Hinweis auf die Stelle, nicht ihre
|
||||
Wiederholung. */
|
||||
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Die zitierte Nachricht, wenn man hinspringt -- kurz hervorgehoben.
|
||||
Ein Sprung ohne Markierung lässt einen raten, welche Zeile gemeint
|
||||
war. */
|
||||
.chat-nachricht[data-hervor="ja"] .chat-nachricht__blase {
|
||||
box-shadow: 0 0 0 2px color-mix(in srgb, var(--akzent) 65%, transparent);
|
||||
transition: box-shadow .4s ease;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.chat-nachricht[data-hervor="ja"] .chat-nachricht__blase { transition: none; }
|
||||
}
|
||||
|
||||
/* ---- Die Linie "ab hier neu" --------------------------------------- */
|
||||
.chat-neu {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
margin: 14px 2px 10px;
|
||||
color: #ff8f98;
|
||||
font-size: .72rem; font-weight: 700;
|
||||
letter-spacing: .1em; text-transform: uppercase;
|
||||
}
|
||||
.chat-neu::before, .chat-neu::after {
|
||||
content: ""; flex: 1; height: 1px;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 143, 152, .5));
|
||||
}
|
||||
.chat-neu::after { background: linear-gradient(90deg, rgba(255, 143, 152, .5), transparent); }
|
||||
.chat-neu__text { flex: none; }
|
||||
|
||||
/* ---- Die Antwortleiste über dem Eingabefeld ------------------------ */
|
||||
.antwort-leiste {
|
||||
display: flex; align-items: flex-start; gap: 10px;
|
||||
margin-bottom: 8px; padding: 8px 10px 8px 11px;
|
||||
box-shadow: inset 3px 0 0 var(--akzent);
|
||||
background: rgba(255, 255, 255, .05);
|
||||
}
|
||||
.antwort-leiste[hidden] { display: none; }
|
||||
.antwort-leiste__inhalt { min-width: 0; flex: 1; }
|
||||
.antwort-leiste__schild {
|
||||
/* 0,72 rem = 11,52 px. Mit 0,7 waeren es 11,2 und damit unter der
|
||||
Grundlinie, die pruef-css-klassen haelt. */
|
||||
font-size: .72rem; letter-spacing: .08em; text-transform: uppercase;
|
||||
color: var(--text-still);
|
||||
}
|
||||
.antwort-leiste__von {
|
||||
margin-left: 6px;
|
||||
font-size: .78rem; font-weight: 700;
|
||||
color: color-mix(in srgb, var(--akzent) 70%, #ffffff);
|
||||
}
|
||||
.antwort-leiste__text {
|
||||
display: block;
|
||||
font-size: .82rem; color: var(--text-leise);
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
.antwort-leiste__weg {
|
||||
flex: none; width: 28px; height: 28px;
|
||||
border: 0; border-radius: 50%;
|
||||
background: rgba(255, 255, 255, .07);
|
||||
color: var(--text-leise); font-size: .9rem; cursor: pointer;
|
||||
}
|
||||
.antwort-leiste__weg:hover { background: rgba(255, 255, 255, .13); color: var(--text); }
|
||||
|
||||
/* ---- Fundstellen in Nachrichten ------------------------------------ */
|
||||
.chat-funde { padding: 4px 10px 12px; }
|
||||
.chat-funde[hidden] { display: none; }
|
||||
.chat-funde__schild {
|
||||
margin: 8px 0 6px;
|
||||
font-size: .72rem; font-weight: 700;
|
||||
letter-spacing: .09em; text-transform: uppercase;
|
||||
color: var(--text-still);
|
||||
}
|
||||
.chat-funde__liste { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
|
||||
.chat-funde__leer { font-size: .84rem; color: var(--text-still); }
|
||||
.chat-fund {
|
||||
display: grid; gap: 2px;
|
||||
width: 100%; padding: 8px 11px;
|
||||
text-align: left; border: 0;
|
||||
clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
|
||||
background: rgba(255, 255, 255, .045);
|
||||
color: var(--text); font: inherit; cursor: pointer;
|
||||
transition: background var(--tempo);
|
||||
}
|
||||
.chat-fund:hover { background: rgba(255, 255, 255, .09); }
|
||||
.chat-fund__kopf { display: flex; justify-content: space-between; gap: 8px; }
|
||||
.chat-fund__raum {
|
||||
font-size: .78rem; font-weight: 700;
|
||||
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
}
|
||||
.chat-fund__wann { flex: none; font-size: .72rem; color: var(--text-still); }
|
||||
.chat-fund__von { font-size: .72rem; color: var(--text-still); }
|
||||
.chat-fund__text {
|
||||
font-size: .82rem; color: var(--text-leise); line-height: 1.35;
|
||||
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Der Antworten-Knopf am Fuß einer Nachricht -- genauso leise wie
|
||||
"zurücknehmen" daneben. Er ist ein Angebot, keine Aufforderung. */
|
||||
.chat-nachricht__antwort-knopf {
|
||||
border: 0; background: none; padding: 0;
|
||||
font: inherit; font-size: .72rem;
|
||||
color: var(--text-still); cursor: pointer;
|
||||
text-decoration: underline; text-underline-offset: 2px;
|
||||
}
|
||||
.chat-nachricht__antwort-knopf:hover { color: var(--text-leise); }
|
||||
|
||||
+173
-3
@@ -112,7 +112,14 @@
|
||||
$('liste-leer').hidden = raeume.length > 0;
|
||||
/* Das Suchfeld erst, wenn die Liste lang genug ist, um darin zu
|
||||
suchen. Vorher ist es ein Feld ohne Zweck. */
|
||||
$('suche-zeile').hidden = raeume.length < 8;
|
||||
/* IMMER SICHTBAR SEIT DEM 09.09.2026.
|
||||
|
||||
Hier stand `raeume.length < 8` mit der Begruendung, ein Suchfeld
|
||||
ueber drei Eintraegen sei Zierde. Das stimmte, solange es nur
|
||||
NAMEN durchsuchte. Seit es auch die Nachrichten durchsucht, ist
|
||||
es das Gegenteil: Zwei Gespraeche koennen tausend Nachrichten
|
||||
haben, und genau dann braucht man es am dringendsten. */
|
||||
$('suche-zeile').hidden = false;
|
||||
|
||||
for (const r of zeigen) {
|
||||
const li = el('li', 'chat-raum');
|
||||
@@ -180,6 +187,72 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Bis wohin war beim Oeffnen gelesen -- daraus entsteht die Linie
|
||||
"ab hier ist es neu". Und worauf gerade geantwortet wird. */
|
||||
let gelesenBeimOeffnen = 0;
|
||||
let antwortAuf = null;
|
||||
|
||||
/* ---------- In Nachrichten suchen (09.09.2026, Punkt 15) ----------------- */
|
||||
|
||||
let suchLauf = 0;
|
||||
|
||||
async function nachrichtenSuchen() {
|
||||
const frage = ($('suche').value || '').trim();
|
||||
const kasten = $('funde');
|
||||
const liste = $('funde-liste');
|
||||
if (!kasten) return;
|
||||
|
||||
if (frage.length < 2) { kasten.hidden = true; liste.textContent = ''; return; }
|
||||
|
||||
/* JEDE ANFRAGE BEKOMMT EINE NUMMER, und nur die neueste darf
|
||||
zeichnen. Beim Tippen laufen mehrere gleichzeitig; ohne das
|
||||
koennte die Antwort auf "Pa" nach der auf "Patrick" ankommen und
|
||||
die richtige Liste ueberschreiben. Das sieht dann aus, als
|
||||
fuende die Suche das Falsche. */
|
||||
const meine = ++suchLauf;
|
||||
let daten;
|
||||
try {
|
||||
const a = await hole(`/workspace/api/chat/suche?q=${encodeURIComponent(frage)}`);
|
||||
if (!a.ok) return;
|
||||
daten = await a.json();
|
||||
} catch { return; }
|
||||
if (meine !== suchLauf) return;
|
||||
|
||||
liste.textContent = '';
|
||||
kasten.hidden = false;
|
||||
$('funde-schild').textContent = daten.treffer.length
|
||||
? `In Nachrichten · ${daten.treffer.length}`
|
||||
: 'In Nachrichten';
|
||||
|
||||
if (!daten.treffer.length) {
|
||||
/* Ein leeres Ergebnis wird GESAGT. Ein Kasten, der einfach leer
|
||||
bleibt, sieht aus, als laufe die Suche noch. */
|
||||
liste.append(el('li', 'chat-funde__leer', 'Nichts gefunden.'));
|
||||
return;
|
||||
}
|
||||
|
||||
for (const t of daten.treffer) {
|
||||
const li = el('li');
|
||||
const b = el('button', 'chat-fund');
|
||||
b.type = 'button';
|
||||
const kopf = el('span', 'chat-fund__kopf');
|
||||
kopf.append(el('span', 'chat-fund__raum', t.raum));
|
||||
kopf.append(el('span', 'chat-fund__wann', wann(t.erstellt)));
|
||||
b.append(kopf);
|
||||
b.append(el('span', 'chat-fund__von', t.von));
|
||||
b.append(el('span', 'chat-fund__text', t.text));
|
||||
b.addEventListener('click', async () => {
|
||||
await raumOeffnen(t.raum_id);
|
||||
/* Erst oeffnen, dann springen -- vorher gibt es die Zeile
|
||||
nicht. Steht sie nicht im geladenen Ausschnitt, sagt
|
||||
`zurStelle` das. */
|
||||
zurStelle(t.id);
|
||||
});
|
||||
li.append(b);
|
||||
liste.append(li);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Ein Gespräch öffnen ------------------------------------------ */
|
||||
|
||||
async function raumOeffnen(id, { sanft = false } = {}) {
|
||||
@@ -189,6 +262,12 @@
|
||||
const daten = await a.json();
|
||||
offen = daten.raum;
|
||||
nachrichten = daten.nachrichten;
|
||||
/* DER STAND VON VORHIN, nicht der von jetzt. `gelesenMelden()`
|
||||
weiter unten setzt ihn gleich auf die letzte Nachricht -- die
|
||||
Linie muss also aus dem Wert gezogen werden, der beim OEFFNEN
|
||||
galt. Danach wuerde sie nie irgendwo stehen. */
|
||||
gelesenBeimOeffnen = daten.gelesen_bis ?? 0;
|
||||
antwortAuf = null;
|
||||
} catch { return; }
|
||||
|
||||
$('verlauf-titel').textContent = offen.name;
|
||||
@@ -219,7 +298,23 @@
|
||||
|
||||
let letzterTag = '';
|
||||
let letzterVon = null;
|
||||
let linieGesetzt = false;
|
||||
for (const n of nachrichten) {
|
||||
/* DIE LINIE "AB HIER IST ES NEU" (09.09.2026, Punkt 15).
|
||||
|
||||
Sie steht vor der ersten Nachricht, die beim Oeffnen noch
|
||||
nicht gelesen war -- und nur EINMAL. Wer nach zwei Tagen
|
||||
zurueckkommt, sucht sonst die Stelle, an der er aufgehoert
|
||||
hat, indem er Uhrzeiten liest.
|
||||
|
||||
Nicht bei eigenen Nachrichten: Was man selbst geschrieben hat,
|
||||
ist nie "neu fuer einen". */
|
||||
if (!linieGesetzt && gelesenBeimOeffnen && n.id > gelesenBeimOeffnen && !n.selbst) {
|
||||
linieGesetzt = true;
|
||||
const linie = el('div', 'chat-neu');
|
||||
linie.append(el('span', 'chat-neu__text', 'Ab hier neu'));
|
||||
ziel.append(linie);
|
||||
}
|
||||
/* Ein Datumstrenner, wenn ein neuer Tag beginnt. Ohne ihn steht
|
||||
die Antwort von vorgestern direkt unter der von heute. */
|
||||
const tag = new Date(n.erstellt).toDateString();
|
||||
@@ -267,6 +362,23 @@
|
||||
blase.append(el('p', 'chat-nachricht__von', n.von));
|
||||
}
|
||||
|
||||
/* DAS ZITAT STEHT IN DER BLASE, NICHT DARUEBER. So wandert es beim
|
||||
Rollen mit und gehoert sichtbar zur Antwort -- eine Zeile
|
||||
darueber liest sich als eigene Nachricht. */
|
||||
if (n.antwort) {
|
||||
const z = el('button', 'chat-zitat');
|
||||
z.type = 'button';
|
||||
z.append(el('span', 'chat-zitat__von', n.antwort.von));
|
||||
z.append(el('span', 'chat-zitat__text', n.antwort.zurueckgenommen
|
||||
? 'Nachricht zurückgenommen' : n.antwort.text));
|
||||
z.title = 'Zur zitierten Nachricht springen';
|
||||
/* Ein Zitat, das nicht zur Stelle fuehrt, ist ein Bild von
|
||||
einer Nachricht. Der Klick springt hin und hebt sie kurz
|
||||
hervor. */
|
||||
z.addEventListener('click', () => zurStelle(n.antwort.id));
|
||||
blase.append(z);
|
||||
}
|
||||
|
||||
if (n.zurueckgenommen) {
|
||||
blase.append(el('p', 'chat-nachricht__text chat-nachricht__weg',
|
||||
'Nachricht zurückgenommen'));
|
||||
@@ -283,6 +395,15 @@
|
||||
zeit.title = wann(n.erstellt, true);
|
||||
fuss.append(zeit);
|
||||
|
||||
/* Antworten geht auf JEDE Nachricht, auch die eigene -- in einer
|
||||
Gruppe bezieht man sich durchaus auf sich selbst. Nur auf eine
|
||||
zurueckgenommene nicht: Das Zitat waere leer. */
|
||||
if (!n.zurueckgenommen && n.id > 0) {
|
||||
const ant = el('button', 'chat-nachricht__antwort-knopf', 'antworten');
|
||||
ant.type = 'button';
|
||||
ant.addEventListener('click', () => antwortSetzen(n));
|
||||
fuss.append(ant);
|
||||
}
|
||||
if (n.selbst && !n.zurueckgenommen) {
|
||||
const weg = el('button', 'chat-nachricht__weg-knopf', 'zurücknehmen');
|
||||
weg.type = 'button';
|
||||
@@ -296,6 +417,37 @@
|
||||
ziel.scrollTop = ziel.scrollHeight;
|
||||
}
|
||||
|
||||
/* ---------- Antworten und Springen (09.09.2026, Punkt 15) ---------------- */
|
||||
|
||||
function antwortSetzen(n) {
|
||||
antwortAuf = { id: n.id, von: n.von, text: n.text };
|
||||
const leiste = $('antwort-leiste');
|
||||
if (!leiste) return;
|
||||
leiste.hidden = false;
|
||||
$('antwort-von').textContent = n.von;
|
||||
$('antwort-text').textContent = n.text;
|
||||
$('text').focus();
|
||||
}
|
||||
|
||||
function antwortLoeschen() {
|
||||
antwortAuf = null;
|
||||
const leiste = $('antwort-leiste');
|
||||
if (leiste) leiste.hidden = true;
|
||||
}
|
||||
|
||||
/** Zur zitierten Nachricht springen und sie kurz hervorheben.
|
||||
*
|
||||
* Steht sie nicht (mehr) im geladenen Ausschnitt, wird das GESAGT --
|
||||
* ein Klick, der nichts tut, laesst einen glauben, der Knopf sei
|
||||
* kaputt. */
|
||||
function zurStelle(id) {
|
||||
const el2 = document.querySelector(`.chat-nachricht[data-id="${id}"]`);
|
||||
if (!el2) { melde('Diese Nachricht liegt weiter oben – bitte nach oben blättern.'); return; }
|
||||
el2.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
||||
el2.dataset.hervor = 'ja';
|
||||
setTimeout(() => { delete el2.dataset.hervor; }, 2200);
|
||||
}
|
||||
|
||||
function tagName(iso) {
|
||||
const d = new Date(iso);
|
||||
const heute = new Date();
|
||||
@@ -316,13 +468,23 @@
|
||||
/* SOFORT ANZEIGEN. Die vorläufige Nachricht bekommt eine negative
|
||||
Nummer -- damit lässt sie sich später zweifelsfrei durch die
|
||||
echte ersetzen, ohne mit einer Server-Nummer zu kollidieren. */
|
||||
/* Das Antwortziel wird HIER festgehalten, nicht erst im
|
||||
`await`. Wer waehrend des Sendens auf eine andere Nachricht
|
||||
antwortet, wuerde sonst das falsche Zitat mitschicken -- die
|
||||
Anfrage laeuft, die Variable hat sich schon geaendert. */
|
||||
const zielAntwort = antwortAuf;
|
||||
|
||||
const vorlaeufig = {
|
||||
id: -Date.now(), von_id: ich.id, von: ich.name, rolle: ich.rolle,
|
||||
text, erstellt: new Date().toISOString(), selbst: true, unterwegs: true,
|
||||
antwort: zielAntwort
|
||||
? { id: zielAntwort.id, von: zielAntwort.von, text: zielAntwort.text }
|
||||
: null,
|
||||
};
|
||||
nachrichten.push(vorlaeufig);
|
||||
verlaufZeichnen();
|
||||
feld.value = '';
|
||||
antwortLoeschen();
|
||||
hoeheAnpassen();
|
||||
melde('');
|
||||
|
||||
@@ -330,7 +492,7 @@
|
||||
const a = await hole(`/workspace/api/chat/raeume/${offen.id}/nachrichten`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ text }),
|
||||
body: JSON.stringify({ text, antwort_auf: zielAntwort?.id ?? null }),
|
||||
});
|
||||
if (!a.ok) {
|
||||
const d = await a.json().catch(() => ({}));
|
||||
@@ -617,6 +779,14 @@
|
||||
}
|
||||
|
||||
/* ---------- Start -------------------------------------------------------- */
|
||||
/* Der Verwerfen-Knopf der Antwortleiste. Zusaetzlich beendet Escape
|
||||
die Antwort -- wer im Eingabefeld steht, greift eher zur Taste als
|
||||
zur Maus. */
|
||||
$('antwort-weg')?.addEventListener('click', antwortLoeschen);
|
||||
$('text')?.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && antwortAuf) { e.preventDefault(); antwortLoeschen(); }
|
||||
});
|
||||
|
||||
|
||||
$('schreiben').addEventListener('submit', abschicken);
|
||||
$('text').addEventListener('input', hoeheAnpassen);
|
||||
@@ -631,7 +801,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
$('suche').addEventListener('input', raeumeZeichnen);
|
||||
$('suche').addEventListener('input', () => { raeumeZeichnen(); nachrichtenSuchen(); });
|
||||
$('neu-oeffnen').addEventListener('click', async () => {
|
||||
await partnerLaden();
|
||||
$('neu-dialog').showModal();
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -217,10 +217,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/aufgaben.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/aufgaben.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -14,11 +14,11 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/automation.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -140,10 +140,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/automation.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/automation.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+14
-14
@@ -14,11 +14,11 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/bereich.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -149,14 +149,14 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/ampel.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/schulung.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereich.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/ampel.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/schulung.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereich.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+11
-11
@@ -14,11 +14,11 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/calls.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -82,11 +82,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/calls.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/calls.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+49
-10
@@ -14,17 +14,17 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<!-- aufgaben.css bringt die Dialog- und Formularstile mit (.dialog,
|
||||
.neu__zeile, .dialog__knoepfe). Sie stehen dort seit dem
|
||||
Aufgabenbrett; sie hierher zu kopieren hiesse, sie an zwei Stellen
|
||||
zu pflegen. Dieselbe Lösung nutzen kalender.html und content.html.
|
||||
Gemeldet von pruef-css-klassen.mjs, bevor jemand einen ungestylten
|
||||
Dialog zu sehen bekam. -->
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/chat.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -76,6 +76,24 @@
|
||||
|
||||
<ul class="chat__liste" id="raeume" aria-live="polite"></ul>
|
||||
|
||||
<!-- ---------- Fundstellen in Nachrichten (09.09.2026, Punkt 15)
|
||||
|
||||
Das Suchfeld darueber durchsuchte bisher nur die NAMEN der
|
||||
Gespraeche. Das ist die kleinere Haelfte: Was man wirklich
|
||||
sucht, ist ein Satz, den jemand vor drei Wochen geschrieben
|
||||
hat.
|
||||
|
||||
Die Fundstellen stehen UNTER der Gespraechsliste und nicht
|
||||
an ihrer Stelle -- beides ist eine Antwort auf dieselbe
|
||||
Eingabe, und wer nach "Patrick" sucht, will vielleicht das
|
||||
Gespraech und vielleicht die Nachricht. Ein Umschalter
|
||||
zwischen zwei Ansichten haette ihn zwingen wollen, das
|
||||
vorher zu wissen. -->
|
||||
<div class="chat-funde" id="funde" hidden>
|
||||
<p class="chat-funde__schild" id="funde-schild">In Nachrichten</p>
|
||||
<ul class="chat-funde__liste" id="funde-liste"></ul>
|
||||
</div>
|
||||
|
||||
<p class="chat__leer" id="liste-leer" hidden>
|
||||
Noch keine Gespräche. Mit <strong>Neu</strong> schreibst du der
|
||||
ersten Person.
|
||||
@@ -106,7 +124,28 @@
|
||||
|
||||
<!-- Das Eingabefeld. Ein <form>, damit Enter abschickt, ohne
|
||||
dass dafür Code nötig wäre. -->
|
||||
<!-- DIE ANTWORTLEISTE (09.09.2026, Punkt 15).
|
||||
|
||||
Sie steht UEBER dem Eingabefeld und innerhalb desselben
|
||||
<form>: So sieht man beim Tippen, worauf man antwortet, und
|
||||
kann es mit einem Griff wieder loswerden. Eine Leiste
|
||||
ausserhalb waere beim Tippen ausserhalb des Blickfelds.
|
||||
|
||||
`hidden` als Vorgabe -- sie erscheint erst, wenn wirklich
|
||||
auf etwas geantwortet wird. Ein leerer Kasten ueber dem
|
||||
Eingabefeld waere jeden Tag im Weg fuer den Fall, dass man
|
||||
ihn vielleicht braucht. -->
|
||||
<form class="chat__eingabe" id="schreiben" hidden>
|
||||
<div class="antwort-leiste" id="antwort-leiste" hidden>
|
||||
<span class="antwort-leiste__strich" aria-hidden="true"></span>
|
||||
<span class="antwort-leiste__inhalt">
|
||||
<span class="antwort-leiste__schild">Antwort an</span>
|
||||
<span class="antwort-leiste__von" id="antwort-von"></span>
|
||||
<span class="antwort-leiste__text" id="antwort-text"></span>
|
||||
</span>
|
||||
<button type="button" class="antwort-leiste__weg" id="antwort-weg"
|
||||
aria-label="Antwort verwerfen">✕</button>
|
||||
</div>
|
||||
<label class="chat__eingabe-schild" for="text">Nachricht</label>
|
||||
<!-- KURZER PLATZHALTER. Hier stand der Hinweis "Enter schickt,
|
||||
Umschalt+Enter macht eine neue Zeile" -- zwei Fehler auf
|
||||
@@ -160,10 +199,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609090205"></script>
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205"></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205"></script>
|
||||
<script src="assets/js/chat.js?v=202609090205"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218"></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218"></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218"></script>
|
||||
<script src="assets/js/chat.js?v=202609090218"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+12
-12
@@ -14,11 +14,11 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/content.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/content.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -172,12 +172,12 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/content.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/checkliste.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/vorlagen.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/content.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -14,11 +14,11 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/dateien.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -101,10 +101,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/dateien.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/dateien.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="gate">
|
||||
@@ -401,6 +401,6 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/gate.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/gate.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+11
-11
@@ -14,11 +14,11 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/kalender.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -340,11 +340,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kalender.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kalender-abo.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kalender.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kalender-abo.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -14,11 +14,11 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/leistung.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -217,10 +217,10 @@
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609090205"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205"></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205"></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205"></script>
|
||||
<script src="assets/js/leistung.js?v=202609090205"></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218"></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218"></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218"></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218"></script>
|
||||
<script src="assets/js/leistung.js?v=202609090218"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -14,11 +14,11 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/personen.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -324,10 +324,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/personen.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/personen.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+11
-11
@@ -14,11 +14,11 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -207,11 +207,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/profil.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/profil.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+11
-11
@@ -14,11 +14,11 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/report.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/report.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -99,11 +99,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/report.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/report.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -14,11 +14,11 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/scouting.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -132,10 +132,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/scouting.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/scouting.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<!-- Nur die Startseite: Begrüßungskachel, Uhr, Glockenmulde. Siehe
|
||||
den Hinweis in start.css -- dort stand es bis zum 07.09.2026. -->
|
||||
<link rel="stylesheet" href="assets/css/heim.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/heim.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -580,10 +580,10 @@
|
||||
</p>
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/start.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/start.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+11
-11
@@ -14,11 +14,11 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/startcheck.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -78,11 +78,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/ki.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/startcheck.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/ki.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/startcheck.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -14,11 +14,11 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/profil.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -119,10 +119,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/steckbrief.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -14,13 +14,13 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<!-- Formular- und Dialogstile: seit dem 07.09.2026 steht hier der
|
||||
Dialog "Creator anlegen". -->
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/uebersicht.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -142,11 +142,11 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/uebersicht.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/uebersicht.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+10
-10
@@ -14,11 +14,11 @@
|
||||
<meta name="theme-color" content="#06090f" />
|
||||
<link rel="icon" type="image/png" href="/assets/img/app-symbole/workspace-32.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/app-symbole/workspace-180.png" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090205" />
|
||||
<link rel="stylesheet" href="assets/css/gate.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/start.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/aufgaben.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/wissen.css?v=202609090218" />
|
||||
<link rel="stylesheet" href="assets/css/module.css?v=202609090218" />
|
||||
</head>
|
||||
|
||||
<body class="start">
|
||||
@@ -183,10 +183,10 @@
|
||||
|
||||
</main>
|
||||
|
||||
<script src="assets/js/wahl.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wissen.js?v=202609090205" defer></script>
|
||||
<script src="assets/js/wahl.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/bereiche.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/kopf.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/glocke.js?v=202609090218" defer></script>
|
||||
<script src="assets/js/wissen.js?v=202609090218" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user