Compare commits
2
Commits
71ede73265
...
55408a57fc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55408a57fc | ||
|
|
907f743cef |
No files matched your search
@@ -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,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:
|
||||
|
||||
@@ -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>
|
||||