Compare commits

...
2 Commits
Author SHA1 Message Date
Thomas Boksberger 55408a57fc added harness todo befor start implementing
CI / build (push) Successful in 1m54s
2026-10-10 13:56:48 +02:00
Thomas Boksberger 907f743cef add wireframe index page to harness/templates
index.html switches between the five wireframes via banner tabs, keys 1-5 or arrow
keys, keeps the choice in the URL hash and offers "Open file" for a wireframe on its
own. Self-contained like the wireframes themselves; the file list is hard-coded and
documented in README.md.
2026-10-10 13:41:59 +02:00
3 changed files with 186 additions and 1 deletions

No files matched your search

+39
View File
@@ -0,0 +1,39 @@
# Harness: offene Anpassungen
Stand 2026-10-10. Der Harness wurde aus einem C#/React-Projekt (PersonManager) importiert; diese Dateien tragen noch dessen Inhalt. Reihenfolge nach Wirkung: oben wirkt in jeder Session, unten beschreibt Artefakte, die es noch nicht gibt.
## 1. `AGENTS.md` neu schreiben
- [ ] Indexzeilen für `_uitests.md` und `_code.md` ohne Playwright, `tests/ui`, `docker-compose.tests.yml`, `code/backend`, ReSharper.
- [ ] Abschnitte „Dev Workflow" und „Push Workflow" auf Terrata umstellen: `./gradlew build` (Compile, Tests, Konsist, detekt, spotlessCheck), `./gradlew spotlessApply` vor dem Commit, `harness/markdown-fmt.py --check` und `harness/glossary-lint.sh` nach Doku-Änderungen. `analyse_all.py`, UI-Tests und `docker compose up` entfernen.
- [ ] Tippfehler („ide neuen Tests", „grundenen") verschwinden damit von selbst.
- [ ] Begründung: Die Datei wird in jeder Session geladen; heute folgt jeder Agent bei jedem Commit einer Anleitung, deren Werkzeuge nicht existieren.
## 2. `harness/_code.md` für Kotlin neu schreiben
- [ ] `harness/PersonManager.sln.DotSettings` löschen (einzige Referenz ist `_code.md`).
- [ ] Nicht 1:1 übersetzen. Was spotless/ktlint und detekt im Build erzwingen, nicht wiederholen. Das Prinzip „die Regeldatei gewinnt" bleibt und zeigt auf die detekt-Konfiguration und `.editorconfig` in `build-logic` (`terrata.quality`).
- [ ] Nur aufnehmen, was kein Werkzeug prüft: `explicitApi` (heute Warning, später strict), KDoc und Kommentare auf Englisch, Enums ohne Display-Labels (Lokalisierung ist Sache der UI), `kotlinx-datetime` statt eigener Datumslogik, Logging von Fehlern immer mit vollem Stacktrace (`logger.error(msg, e)`), Benennung von Tests, Package-Root `terrata.<module>`.
- [ ] Compose-Konventionen (Previews, Tokens statt Literale) nicht duplizieren, sondern auf `CLAUDE.md` § Component previews und `_uiux.md` § 12 verweisen.
- [ ] Abschnitt „Compliance Example" durch ein kurzes Kotlin-Beispiel ersetzen oder streichen.
## 3. `harness/_devdocs.md`: Beispiele austauschen
- [ ] Gerüst behalten (fünf Grundregeln, Mermaid-only, Struktur von `docs/dev/`).
- [ ] Bausteintabelle (`code/backend/PersonenApi/…`) durch die KMP-Module aus `_design.md` ersetzen (`:core:model`, `:core:app`, `:adapter:*`, `:ui`, `:map:*`, `:interop:dav`, `:app:*`).
- [ ] Kontextdiagramm (React, ASP.NET Core, EF Core, `personen.db`) durch Terrata ersetzen: Desktop-App, Store auf der Platte, SQLite-Index, DAV-Server, Sync-Ziel.
- [ ] Deployment-Abschnitt (`docker-compose.yml`, Docker-Host) streichen oder auf die Desktop-Distribution umstellen.
- [ ] Prozessbeispiel „Sachbearbeiter wählt Anlegen" durch einen Terrata-Prozess ersetzen, etwa „Record anlegen" oder „Store scannen".
- [ ] Zuständigkeits-Kasten (um Zeile 190): den Verweis auf den C#-Codestil auf die neue `_code.md` umbiegen und die Beschreibung von `_design.md` („Backend-Architektur: Entitäten, DTOs, EF-Konfigurationen, Controller, Statuscodes, Migrationen") durch die Ports-&-Adapter-Begriffe ersetzen.
- [ ] Zeitpunkt: vor dem ersten Dokument unter `docs/dev/` (laut `CLAUDE.md` die Index-Tabellen, sobald gebaut).
## 4. `harness/_userdocs.md`: zwei Zeilen
- [ ] Screenshot-Erzeugung (um Zeile 101 und 119): Playwright und den Entwicklungs-Stack durch ein Compose-Pendant ersetzen oder als „offen, bis `docs/user/` entsteht" markieren.
- [ ] Rest ist sauber.
## 5. `harness/_uitests.md`: auf Stub kürzen
- [ ] Playwright, Vitest, xUnit, Testdatenbank, Docker-Stack entfernen.
- [ ] Generische Regeln behalten: kein Test ohne Akzeptanzkriterium, Zuordnung Story ↔ Test, keine Tests gegen Produktivdaten.
- [ ] Stack benennen: Compose-UI-Tests in `:ui` (laufen offscreen, siehe `ci/docker/Dockerfile`), Rest explizit „offen, bis es eine bedienbare App gibt".
- [ ] Keine Compose-Testkonvention erfinden, bevor es etwas zu testen gibt.
## 6. Nacharbeiten
- [ ] `CLAUDE.md` § Specs & harness: den Pitfall-Satz „the last four still carry the C#/Angular content" entfernen, sobald 2 bis 5 erledigt sind.
- [ ] `harness/_requirements.md`, `_design.md`, `_uiux.md`, `_glossary.md`, `_ideas.md`, `_markdown.md` sind bereits Terrata, nichts zu tun.
- [ ] Diese Datei löschen, wenn alles abgehakt ist.
+1 -1
View File
@@ -1,5 +1,5 @@
# UI/UX wireframes
Interactive, self-contained HTML wireframes for the desktop UI. Design exploration only — not production code, no build step. Open a file directly in a browser.
Interactive, self-contained HTML wireframes for the desktop UI. Design exploration only — not production code, no build step. Open a file directly in a browser, or open [`index.html`](index.html) to switch between them: tabs in the banner, keys `1`–`5` or `←` / `→`, the file name in the URL hash (`index.html#record-view`), and "Open file ↗" for the wireframe on its own. Its file list is hard-coded; add new wireframes there as well.
## Files
- [`main-view.html`](main-view.html) — the main app shell. Demonstrates the agreed structure:
+146
View File
@@ -0,0 +1,146 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Terrata — Wireframes</title>
<style>
/* Tokens mirror harness/_uiux.md §3 (neutral chrome, Pine as default accent). */
:root{
--terra:#C85C3C;
--bg:#E8E7E3; --surface:#FFFFFF; --surface-2:#F4F3F0;
--text:#26251F; --muted:#78766F; --line:#E0DED8; --line-strong:#CDCAC2;
--chrome:#2B2A27; --chrome-text:#ECEAE4; --chrome-muted:#A6A39B;
--accent:#2F7A64;
--font-display:"Space Grotesk","Avenir Next","Segoe UI",system-ui,sans-serif;
--font-body:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
--r-sm:8px;
}
@media (prefers-color-scheme: dark){
:root:not([data-theme="light"]){
--bg:#1A1A18; --surface:#242320; --surface-2:#1F1E1C; --text:#ECEAE4; --muted:#95928A;
--line:#322F2B; --line-strong:#413D38; --chrome:#141312; --chrome-text:#ECEAE4; --chrome-muted:#8C8981;
--accent:#4E9E86;
}
}
:root[data-theme="dark"]{
--bg:#1A1A18; --surface:#242320; --surface-2:#1F1E1C; --text:#ECEAE4; --muted:#95928A;
--line:#322F2B; --line-strong:#413D38; --chrome:#141312; --chrome-text:#ECEAE4; --chrome-muted:#8C8981;
--accent:#4E9E86;
}
*{box-sizing:border-box}
html,body{height:100%}
body{margin:0;display:flex;flex-direction:column;background:var(--bg);color:var(--text);font-family:var(--font-body);font-size:14px;line-height:1.5;-webkit-font-smoothing:antialiased}
/* Top banner: wordmark + file tabs + actions */
.banner{display:flex;align-items:center;gap:16px;height:48px;padding:0 16px;background:var(--chrome);color:var(--chrome-text);border-bottom:2px solid var(--accent);flex:0 0 auto}
.wordmark{font-family:var(--font-display);font-weight:600;font-size:19px;letter-spacing:.02em;white-space:nowrap}
.wordmark b{color:var(--terra);font-weight:600}
.eyebrow{font-family:var(--font-display);font-size:10.5px;text-transform:uppercase;letter-spacing:.09em;color:var(--chrome-muted);font-weight:600;white-space:nowrap}
.tabs{display:flex;gap:4px;flex:1 1 auto;min-width:0;overflow-x:auto;scrollbar-width:none}
.tabs::-webkit-scrollbar{display:none}
.tab{appearance:none;border:1px solid transparent;background:transparent;color:var(--chrome-muted);font-family:var(--font-display);font-weight:600;font-size:12.5px;letter-spacing:.01em;padding:6px 12px;border-radius:999px;cursor:pointer;white-space:nowrap;transition:background .12s,color .12s}
.tab:hover{color:var(--chrome-text);background:rgba(255,255,255,.06)}
.tab[aria-selected="true"]{color:var(--chrome-text);background:var(--accent);border-color:var(--accent)}
.tab kbd{font-family:inherit;font-size:10px;opacity:.6;margin-left:6px}
.actions{display:flex;gap:8px;flex:0 0 auto}
.ghost{appearance:none;background:transparent;color:var(--chrome-text);border:1px solid var(--chrome-muted);border-radius:999px;font:inherit;font-size:12px;padding:5px 11px;cursor:pointer;text-decoration:none;white-space:nowrap}
.ghost:hover{border-color:var(--chrome-text)}
/* Description strip below the banner */
.strip{display:flex;align-items:baseline;gap:12px;padding:6px 16px;background:var(--surface-2);border-bottom:1px solid var(--line);color:var(--muted);font-size:12.5px;flex:0 0 auto}
.strip code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;background:var(--surface);border:1px solid var(--line);border-radius:5px;padding:0 6px;color:var(--text)}
/* The wireframe itself */
.frame{flex:1 1 auto;min-height:0;position:relative;background:var(--surface)}
.frame iframe{position:absolute;inset:0;width:100%;height:100%;border:0;background:var(--surface)}
@media (max-width:720px){
.eyebrow,.tab kbd,.strip{display:none}
.banner{gap:10px;padding:0 12px}
}
</style>
</head>
<body>
<header class="banner">
<div class="wordmark">terrata<b>.</b></div>
<div class="eyebrow">Wireframes</div>
<nav class="tabs" id="tabs" role="tablist" aria-label="Wireframe files"></nav>
<div class="actions">
<a class="ghost" id="open" href="#" target="_blank" rel="noopener" title="Open the current file in its own tab">Open file ↗</a>
<button class="ghost" id="theme" type="button" title="Toggle light / dark for this page (the wireframes keep their own switch)">Theme</button>
</div>
</header>
<div class="strip">
<code id="file"></code>
<span id="desc"></span>
</div>
<main class="frame">
<iframe id="view" title="Wireframe"></iframe>
</main>
<script>
// Keep this list in sync with README.md in this folder.
const FILES = [
{ id: 'main-view', file: 'main-view.html', label: 'Main view', desc: 'App shell: collection as query, Box / Liste / Kanban / Map / Graph renderers, tabs, links panel.' },
{ id: 'record-view', file: 'record-view.html', label: 'Record view', desc: 'The record drill-in with type switcher, WYSIWYG block editor, core blocks, Info / Links panel.' },
{ id: 'record-edit', file: 'record-edit.html', label: 'Record edit', desc: 'Field-edit state of the core blocks across all six types; the pencil opens it, "Fertig" returns.' },
{ id: 'field-types', file: 'field-types.html', label: 'Field types', desc: 'Every field type: frontmatter form, display and editing side by side; Palette vs. base-schema only.' },
{ id: 'design-system', file: 'design-system.html', label: 'Design system', desc: 'Living styleguide: tokens and the full component inventory, with theme and accent switchers.' },
];
const tabs = document.getElementById('tabs');
const view = document.getElementById('view');
const open = document.getElementById('open');
const fileEl = document.getElementById('file');
const descEl = document.getElementById('desc');
FILES.forEach((f, i) => {
const b = document.createElement('button');
b.className = 'tab';
b.type = 'button';
b.role = 'tab';
b.dataset.id = f.id;
b.innerHTML = `${f.label}<kbd>${i + 1}</kbd>`;
b.addEventListener('click', () => select(f.id, true));
tabs.appendChild(b);
});
function select(id, pushHash) {
const f = FILES.find(x => x.id === id) || FILES[0];
for (const b of tabs.children) b.setAttribute('aria-selected', String(b.dataset.id === f.id));
if (view.getAttribute('src') !== f.file) view.setAttribute('src', f.file);
open.href = f.file;
fileEl.textContent = f.file;
descEl.textContent = f.desc;
document.title = `Terrata — ${f.label}`;
if (pushHash && location.hash !== '#' + f.id) history.replaceState(null, '', '#' + f.id);
}
function fromHash() {
select(location.hash.replace(/^#/, '') || FILES[0].id, false);
}
window.addEventListener('hashchange', fromHash);
fromHash();
// Keyboard: 1..n picks a file, ← / → cycles. Ignored while typing in the iframe (it has its own focus).
document.addEventListener('keydown', e => {
if (e.metaKey || e.ctrlKey || e.altKey) return;
const cur = FILES.findIndex(f => f.id === (location.hash.replace(/^#/, '') || FILES[0].id));
const n = parseInt(e.key, 10);
if (n >= 1 && n <= FILES.length) select(FILES[n - 1].id, true);
else if (e.key === 'ArrowRight') select(FILES[(cur + 1) % FILES.length].id, true);
else if (e.key === 'ArrowLeft') select(FILES[(cur - 1 + FILES.length) % FILES.length].id, true);
});
// Theme toggle for the index page only; remembered per browser.
const root = document.documentElement;
try { const t = localStorage.getItem('terrata-wireframes-theme'); if (t) root.dataset.theme = t; } catch (_) {}
document.getElementById('theme').addEventListener('click', () => {
const dark = root.dataset.theme === 'dark' || (!root.dataset.theme && matchMedia('(prefers-color-scheme: dark)').matches);
root.dataset.theme = dark ? 'light' : 'dark';
try { localStorage.setItem('terrata-wireframes-theme', root.dataset.theme); } catch (_) {}
});
</script>
</body>
</html>
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.