Filipe zu dem Feld in der Kopfleiste: "das soll viel besser aussehen
viel geiler aber nicht so eine scheisse."
Er hatte recht. Da stand ein Etikett mit dem Wort SICHT und daneben ein
Auswahlfeld -- ein Formular, das in die Kopfleiste gerutscht war. Drei
Aenderungen:
* DAS AUGE ersetzt das Wort. Es sagt dasselbe in einem Zeichen und
laesst dem NAMEN den Platz, um den es eigentlich geht. Fuer
Vorleseprogramme bleibt die Beschriftung erhalten, nur unsichtbar --
sie ersatzlos zu loeschen haette das Feld unbeschriftet gelassen.
* "Meine Sicht" statt "Alles (meine Sicht)". Der Zusatz erklaerte
nichts und machte den Knopf so breit, dass am Handy nichts anderes
mehr danebenpasste.
* EIN KREUZ statt "zurueck zu mir". Der Satz brauchte mehr Platz als
der Name daneben, und was ein Kreuz an einer aktiven Auswahl tut,
weiss jeder. Die Worte bleiben im aria-label und im Titel.
DIE EIGENTLICHE VERBESSERUNG ist aber die Hierarchie: Im Normalfall
ist der Umschalter still (graues Auge, ruhiger Rand). Sobald eine
FREMDE Sicht laeuft, traegt er ganz Farbe -- nicht nur ein
Zusatzknopf am Rand.
Das ist der gefaehrlichste Zustand dieser Funktion: Wer sie vergisst,
sieht am naechsten Tag drei Aufgaben statt dreissig und haelt das fuer
den Bestand. Ein Zustand, der Schaden anrichtet, muss lauter sein als
einer, der nichts tut.
Co-Authored-By: Claude Opus 5 <[email protected]>
89 lines
3.9 KiB
JavaScript
89 lines
3.9 KiB
JavaScript
/* Bild des Sicht-Umschalters in BEIDEN Zustaenden -- zum Ansehen.
|
|
Der Normalfall (eigene Sicht) und der Ausnahmefall (fremde Sicht)
|
|
sollen sich deutlich unterscheiden; nur nebeneinander sieht man, ob
|
|
das gelungen ist. */
|
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
|
|
const ordner = mkdtempSync(join(tmpdir(), "ws-sicht-"));
|
|
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
|
const { portMussFreiSein } = await import("./helfer-port.mjs");
|
|
await portMussFreiSein(4329, "das Bildwerkzeug");
|
|
process.env.PORT = "4329";
|
|
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");
|
|
await new Promise((r) => setTimeout(r, 1000));
|
|
const BASIS = "http://127.0.0.1:4329";
|
|
|
|
const { DatabaseSync } = await import("node:sqlite");
|
|
const { scryptSync, randomBytes } = await import("node:crypto");
|
|
await fetch(BASIS + "/workspace/api/anmelden", {
|
|
method: "POST", headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ rolle: "admin", code: "x" }) }).catch(() => {});
|
|
for (let i = 0; i < 60; i++) {
|
|
try {
|
|
const p = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
try { p.prepare("SELECT 1 FROM personen LIMIT 1").get(); break; } finally { p.close(); }
|
|
} catch { await new Promise((r) => setTimeout(r, 250)); }
|
|
}
|
|
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
const j = new Date().toISOString();
|
|
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,aktiv,erstellt) VALUES (?,?,?,?,?,1,?)")
|
|
.run(name, rolle, hash, salt, 32768, j);
|
|
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
|
|
}
|
|
anlegen("Filipe", "admin", "CODE-DOGI-0001");
|
|
anlegen("Patrick von Hohenstein", "scout", "CODE-SCOU-0001");
|
|
const idLuna = anlegen("Luna", "creator", "CODE-CREA-0001");
|
|
d.close();
|
|
|
|
const pw = await import("file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
|
const browser = await pw.chromium.launch();
|
|
const ctx = await browser.newContext({ viewport: { width: 1400, height: 900 }, deviceScaleFactor: 3 });
|
|
const seite = await ctx.newPage();
|
|
await seite.goto(BASIS + "/workspace/", { waitUntil: "networkidle" });
|
|
await seite.click('.rolle[data-rolle="admin"]');
|
|
await seite.fill("#code", "CODE-DOGI-0001");
|
|
await seite.click("button[type=submit]");
|
|
await seite.waitForURL("**/start.html", { timeout: 25000 });
|
|
await seite.waitForTimeout(1500);
|
|
|
|
const feld = await seite.$("#sicht-wahl");
|
|
if (feld) {
|
|
await feld.screenshot({ path: "sicht-eigene.png" });
|
|
console.log("sicht-eigene.png geschrieben");
|
|
}
|
|
|
|
/* Jetzt auf Lunas Sicht umschalten. */
|
|
await seite.selectOption("#sicht-feld", String(idLuna)).catch(async () => {
|
|
/* wahl.js ersetzt das <select> -- dann ueber den Knopf gehen. */
|
|
await seite.click(".sicht .wahl2__knopf");
|
|
await seite.waitForTimeout(300);
|
|
await seite.click(`.wahl2__liste [data-wert="${idLuna}"], .wahl2__eintrag:has-text("Luna")`);
|
|
});
|
|
await seite.waitForTimeout(2500);
|
|
|
|
const feld2 = await seite.$("#sicht-wahl");
|
|
if (feld2) {
|
|
await feld2.screenshot({ path: "sicht-fremde.png" });
|
|
console.log("sicht-fremde.png geschrieben");
|
|
}
|
|
const zustand = await seite.evaluate(() => {
|
|
const f = document.querySelector("#sicht-wahl");
|
|
return f ? { fremd: f.dataset.fremd, text: f.textContent.trim().slice(0, 40) } : null;
|
|
});
|
|
console.log("Zustand:", JSON.stringify(zustand));
|
|
|
|
await browser.close();
|
|
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ }
|
|
process.exit(0);
|