From b78358672019c49a6d1286e1b25a63551ef6ddf5 Mon Sep 17 00:00:00 2001 From: Thorsten Hindermann Date: Sun, 12 Jul 2026 10:02:38 +0200 Subject: [PATCH] feat: port Wave 4 visual components --- .github/agents/copilot-instructions.md | 9 + .github/copilot-instructions.md | 9 + .specify/feature.json | 2 +- AGENTS.md | 9 + CLAUDE.md | 9 + Directory.Build.props | 6 +- GEMINI.md | 9 + ...ting.022-wave4-visual-component-porting.md | 0 Pflichtenheft.md | 8 +- TuiVision.sln | 75 ++++ coverlet.runsettings | 4 +- docs/architecture/runtime-view.md | 29 ++ docs/guides/examples/cyrillic.md | 57 +++ docs/guides/examples/eterm.md | 54 +++ docs/guides/examples/fonts.md | 50 +++ docs/guides/examples/terminal.md | 65 +++ docs/guides/examples/xterm.md | 55 +++ docs/project-statistics.md | 71 ++-- docs/security/threat-model.md | 1 + docs/toc.yml | 10 + examples/Cyrillic/Cyrillic.csproj | 13 + examples/Cyrillic/CyrillicApp.cs | 121 ++++++ examples/Cyrillic/Program.cs | 14 + examples/ETerm/ETerm.csproj | 13 + examples/ETerm/ETermApp.cs | 138 +++++++ examples/ETerm/Program.cs | 14 + examples/Fonts/Fixtures/font-8x16.bin | Bin 0 -> 4096 bytes examples/Fonts/Fonts.csproj | 14 + examples/Fonts/FontsApp.cs | 182 ++++++++ examples/Fonts/Program.cs | 14 + examples/README.md | 26 ++ examples/Shared/Wave4Runtime.cs | 172 ++++++++ examples/Terminal/Program.cs | 14 + examples/Terminal/Terminal.csproj | 13 + examples/Terminal/TerminalApp.cs | 141 +++++++ examples/XTerm/Program.cs | 14 + examples/XTerm/XTerm.csproj | 13 + examples/XTerm/XTermApp.cs | 138 +++++++ .../checklists/domain-acceptance.md | 39 ++ .../checklists/plan-quality.md | 37 ++ .../checklists/plan-review.md | 23 ++ .../checklists/requirements.md | 35 ++ .../wave4-visual-component-acceptance.md | 73 ++++ .../data-model.md | 131 ++++++ .../plan.md | 257 ++++++++++++ .../pr-evidence.md | 239 +++++++++++ .../quickstart.md | 63 +++ .../research.md | 127 ++++++ .../spec.md | 391 ++++++++++++++++++ .../tasks.md | 225 ++++++++++ .../CyrillicSmokeTests.cs | 85 ++++ .../ETermSmokeTests.cs | 71 ++++ .../FontsSmokeTests.cs | 93 +++++ .../TerminalSmokeTests.cs | 91 ++++ .../TuiVision.Examples.SmokeTests.csproj | 9 + .../Wave4VisualSmokeMatrixTests.cs | 176 ++++++++ .../XTermSmokeTests.cs | 72 ++++ 57 files changed, 3785 insertions(+), 38 deletions(-) rename Lastenheft_Wave4-Visual-Component-Porting.md => Lastenheft_Wave4-Visual-Component-Porting.022-wave4-visual-component-porting.md (100%) create mode 100644 docs/guides/examples/cyrillic.md create mode 100644 docs/guides/examples/eterm.md create mode 100644 docs/guides/examples/fonts.md create mode 100644 docs/guides/examples/terminal.md create mode 100644 docs/guides/examples/xterm.md create mode 100644 examples/Cyrillic/Cyrillic.csproj create mode 100644 examples/Cyrillic/CyrillicApp.cs create mode 100644 examples/Cyrillic/Program.cs create mode 100644 examples/ETerm/ETerm.csproj create mode 100644 examples/ETerm/ETermApp.cs create mode 100644 examples/ETerm/Program.cs create mode 100644 examples/Fonts/Fixtures/font-8x16.bin create mode 100644 examples/Fonts/Fonts.csproj create mode 100644 examples/Fonts/FontsApp.cs create mode 100644 examples/Fonts/Program.cs create mode 100644 examples/Shared/Wave4Runtime.cs create mode 100644 examples/Terminal/Program.cs create mode 100644 examples/Terminal/Terminal.csproj create mode 100644 examples/Terminal/TerminalApp.cs create mode 100644 examples/XTerm/Program.cs create mode 100644 examples/XTerm/XTerm.csproj create mode 100644 examples/XTerm/XTermApp.cs create mode 100644 specs/022-wave4-visual-component-porting/checklists/domain-acceptance.md create mode 100644 specs/022-wave4-visual-component-porting/checklists/plan-quality.md create mode 100644 specs/022-wave4-visual-component-porting/checklists/plan-review.md create mode 100644 specs/022-wave4-visual-component-porting/checklists/requirements.md create mode 100644 specs/022-wave4-visual-component-porting/contracts/wave4-visual-component-acceptance.md create mode 100644 specs/022-wave4-visual-component-porting/data-model.md create mode 100644 specs/022-wave4-visual-component-porting/plan.md create mode 100644 specs/022-wave4-visual-component-porting/pr-evidence.md create mode 100644 specs/022-wave4-visual-component-porting/quickstart.md create mode 100644 specs/022-wave4-visual-component-porting/research.md create mode 100644 specs/022-wave4-visual-component-porting/spec.md create mode 100644 specs/022-wave4-visual-component-porting/tasks.md create mode 100644 tests/TuiVision.Examples.SmokeTests/CyrillicSmokeTests.cs create mode 100644 tests/TuiVision.Examples.SmokeTests/ETermSmokeTests.cs create mode 100644 tests/TuiVision.Examples.SmokeTests/FontsSmokeTests.cs create mode 100644 tests/TuiVision.Examples.SmokeTests/TerminalSmokeTests.cs create mode 100644 tests/TuiVision.Examples.SmokeTests/Wave4VisualSmokeMatrixTests.cs create mode 100644 tests/TuiVision.Examples.SmokeTests/XTermSmokeTests.cs diff --git a/.github/agents/copilot-instructions.md b/.github/agents/copilot-instructions.md index c1f3a933..afd58eaf 100644 --- a/.github/agents/copilot-instructions.md +++ b/.github/agents/copilot-instructions.md @@ -121,6 +121,15 @@ GitHub Pages is published from `.github/workflows/pages.yml`: build root `docfx. - Historical terminal, Cyrillic, font, Eterm, and XTerm sources remain read-only intent. Full ANSI/VT/XTerm parity, visible Wave-4 examples, host mutation, new dependencies, services, persistence, and runtime/product AI remain outside 021. - The next prioritized intake is `Lastenheft_Wave4-Visual-Component-Porting.md`. +### 022-wave4-visual-component-porting +- Current implementation status: Wave-4 visual component porting is implemented locally; final evidence is in `specs/022-wave4-visual-component-porting/pr-evidence.md`. +- `Terminal`, `Cyrillic`, `Fonts`, `ETerm`, and `XTerm` provide a visible main component, real `TStatusLine`, and keyboard-reachable `Help -> Description`. +- Primary proof runs through `app.Run()` and combines concrete state, exact view identity, rendered buffer/cell evidence, controlled operation, fallback, description, and deterministic host classification. Narrow viewports preserve example identity and status meaning. +- Terminal, Cyrillic, and Fonts are `UseExistingFramework`; ETerm and XTerm are bounded `IntentionalDeviation` immutable manifests that do not parse or execute historical configuration. +- The copied raw 8x16 fixture is byte-identical to historical `font.016`; historical sources remain read-only. No process, shell, PTY, host font/codepage/locale/keyboard mutation, X resource database, terminfo, external command, or new dependency entered scope. +- Physical host observation remains separate from deterministic in-process and remote-CI evidence. Unsupported capabilities use text-first fallbacks and never claim unavailable native behavior. +- The next prioritized intake is `Lastenheft_06_A11Y_Framework.md`. + ### Autonomous Red-Proof Completeness - Before the first red test batch, review imports, public XML docs, harness helpers, focus/ownership assertions, and linked-source assembly identity. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 642b978b..00b115bd 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -245,6 +245,15 @@ On numbered Spec-Kit branches, align those three version fields before pushing. - Historical terminal, Cyrillic, font, Eterm, and XTerm sources remain read-only intent. Full ANSI/VT/XTerm parity, visible Wave-4 examples, host mutation, new dependencies, services, persistence, and runtime/product AI remain outside 021. - The next prioritized intake is `Lastenheft_Wave4-Visual-Component-Porting.md`. +### 022-wave4-visual-component-porting +- Current implementation status: Wave-4 visual component porting is implemented locally; final evidence is in `specs/022-wave4-visual-component-porting/pr-evidence.md`. +- `Terminal`, `Cyrillic`, `Fonts`, `ETerm`, and `XTerm` provide a visible main component, real `TStatusLine`, and keyboard-reachable `Help -> Description`. +- Primary proof runs through `app.Run()` and combines concrete state, exact view identity, rendered buffer/cell evidence, controlled operation, fallback, description, and deterministic host classification. Narrow viewports preserve example identity and status meaning. +- Terminal, Cyrillic, and Fonts are `UseExistingFramework`; ETerm and XTerm are bounded `IntentionalDeviation` immutable manifests that do not parse or execute historical configuration. +- The copied raw 8x16 fixture is byte-identical to historical `font.016`; historical sources remain read-only. No process, shell, PTY, host font/codepage/locale/keyboard mutation, X resource database, terminfo, external command, or new dependency entered scope. +- Physical host observation remains separate from deterministic in-process and remote-CI evidence. Unsupported capabilities use text-first fallbacks and never claim unavailable native behavior. +- The next prioritized intake is `Lastenheft_06_A11Y_Framework.md`. + ### Autonomous Red-Proof Completeness - Before the first red test batch, review imports, public XML docs, harness helpers, focus/ownership assertions, and linked-source assembly identity. diff --git a/.specify/feature.json b/.specify/feature.json index 42e155e2..a783036f 100644 --- a/.specify/feature.json +++ b/.specify/feature.json @@ -1,3 +1,3 @@ { - "feature_directory": "specs/021-terminal-charset-hardening" + "feature_directory": "specs/022-wave4-visual-component-porting" } diff --git a/AGENTS.md b/AGENTS.md index 805e7a9e..77353b01 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -291,6 +291,15 @@ npm run test:docfx - Historical terminal, Cyrillic, font, Eterm, and XTerm sources remain read-only intent. Full ANSI/VT/XTerm parity, visible Wave-4 examples, host mutation, new dependencies, services, persistence, and runtime/product AI remain outside 021. - The next prioritized intake is `Lastenheft_Wave4-Visual-Component-Porting.md`. +### 022-wave4-visual-component-porting +- Current implementation status: Wave-4 visual component porting is implemented locally; final evidence is in `specs/022-wave4-visual-component-porting/pr-evidence.md`. +- `Terminal`, `Cyrillic`, `Fonts`, `ETerm`, and `XTerm` provide a visible main component, real `TStatusLine`, and keyboard-reachable `Help -> Description`. +- Primary proof runs through `app.Run()` and combines concrete state, exact view identity, rendered buffer/cell evidence, controlled operation, fallback, description, and deterministic host classification. Narrow viewports preserve example identity and status meaning. +- Terminal, Cyrillic, and Fonts are `UseExistingFramework`; ETerm and XTerm are bounded `IntentionalDeviation` immutable manifests that do not parse or execute historical configuration. +- The copied raw 8x16 fixture is byte-identical to historical `font.016`; historical sources remain read-only. No process, shell, PTY, host font/codepage/locale/keyboard mutation, X resource database, terminfo, external command, or new dependency entered scope. +- Physical host observation remains separate from deterministic in-process and remote-CI evidence. Unsupported capabilities use text-first fallbacks and never claim unavailable native behavior. +- The next prioritized intake is `Lastenheft_06_A11Y_Framework.md`. + ### Autonomous Red-Proof Completeness - Before the first red test batch, review imports, public XML docs, harness helpers, focus/ownership assertions, and linked-source assembly identity. diff --git a/CLAUDE.md b/CLAUDE.md index a5972078..01be9825 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -237,6 +237,15 @@ When a dedicated feature branch has implemented the requirements of a Lastenheft - Historical terminal, Cyrillic, font, Eterm, and XTerm sources remain read-only intent. Full ANSI/VT/XTerm parity, visible Wave-4 examples, host mutation, new dependencies, services, persistence, and runtime/product AI remain outside 021. - The next prioritized intake is `Lastenheft_Wave4-Visual-Component-Porting.md`. +### 022-wave4-visual-component-porting +- Current implementation status: Wave-4 visual component porting is implemented locally; final evidence is in `specs/022-wave4-visual-component-porting/pr-evidence.md`. +- `Terminal`, `Cyrillic`, `Fonts`, `ETerm`, and `XTerm` provide a visible main component, real `TStatusLine`, and keyboard-reachable `Help -> Description`. +- Primary proof runs through `app.Run()` and combines concrete state, exact view identity, rendered buffer/cell evidence, controlled operation, fallback, description, and deterministic host classification. Narrow viewports preserve example identity and status meaning. +- Terminal, Cyrillic, and Fonts are `UseExistingFramework`; ETerm and XTerm are bounded `IntentionalDeviation` immutable manifests that do not parse or execute historical configuration. +- The copied raw 8x16 fixture is byte-identical to historical `font.016`; historical sources remain read-only. No process, shell, PTY, host font/codepage/locale/keyboard mutation, X resource database, terminfo, external command, or new dependency entered scope. +- Physical host observation remains separate from deterministic in-process and remote-CI evidence. Unsupported capabilities use text-first fallbacks and never claim unavailable native behavior. +- The next prioritized intake is `Lastenheft_06_A11Y_Framework.md`. + ### Autonomous Red-Proof Completeness - Before the first red test batch, review imports, public XML docs, harness helpers, focus/ownership assertions, and linked-source assembly identity. diff --git a/Directory.Build.props b/Directory.Build.props index 4834e396..2cf0a6e8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -5,9 +5,9 @@ enable enable true - 1.21.1.160 - 1.21.1.160 - 1.21.1.160 + 1.22.1.173 + 1.22.1.173 + 1.22.1.173 CS1591 diff --git a/GEMINI.md b/GEMINI.md index 3167373e..7eca37cc 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -264,6 +264,15 @@ Das Projekt folgt einer modularen Struktur gemäß .NET Best Practices: - Historical terminal, Cyrillic, font, Eterm, and XTerm sources remain read-only intent. Full ANSI/VT/XTerm parity, visible Wave-4 examples, host mutation, new dependencies, services, persistence, and runtime/product AI remain outside 021. - The next prioritized intake is `Lastenheft_Wave4-Visual-Component-Porting.md`. +### 022-wave4-visual-component-porting +- Current implementation status: Wave-4 visual component porting is implemented locally; final evidence is in `specs/022-wave4-visual-component-porting/pr-evidence.md`. +- `Terminal`, `Cyrillic`, `Fonts`, `ETerm`, and `XTerm` provide a visible main component, real `TStatusLine`, and keyboard-reachable `Help -> Description`. +- Primary proof runs through `app.Run()` and combines concrete state, exact view identity, rendered buffer/cell evidence, controlled operation, fallback, description, and deterministic host classification. Narrow viewports preserve example identity and status meaning. +- Terminal, Cyrillic, and Fonts are `UseExistingFramework`; ETerm and XTerm are bounded `IntentionalDeviation` immutable manifests that do not parse or execute historical configuration. +- The copied raw 8x16 fixture is byte-identical to historical `font.016`; historical sources remain read-only. No process, shell, PTY, host font/codepage/locale/keyboard mutation, X resource database, terminfo, external command, or new dependency entered scope. +- Physical host observation remains separate from deterministic in-process and remote-CI evidence. Unsupported capabilities use text-first fallbacks and never claim unavailable native behavior. +- The next prioritized intake is `Lastenheft_06_A11Y_Framework.md`. + ### Autonomous Red-Proof Completeness - Before the first red test batch, review imports, public XML docs, harness helpers, focus/ownership assertions, and linked-source assembly identity. diff --git a/Lastenheft_Wave4-Visual-Component-Porting.md b/Lastenheft_Wave4-Visual-Component-Porting.022-wave4-visual-component-porting.md similarity index 100% rename from Lastenheft_Wave4-Visual-Component-Porting.md rename to Lastenheft_Wave4-Visual-Component-Porting.022-wave4-visual-component-porting.md diff --git a/Pflichtenheft.md b/Pflichtenheft.md index febac81b..000c5d33 100644 --- a/Pflichtenheft.md +++ b/Pflichtenheft.md @@ -262,8 +262,8 @@ Statuscheckliste Beispielwellen: Reihenfolgehinweis: abgeschlossen (Branch `020-mouse-support-interaction`); begrenzter SGR-1006-Ingress, Fokus/Aktivierung, Doppelklick, ein Titelzeilen-Drag und vollständige Tastaturfallbacks sind frameworkweit nachgewiesen. - [x] **Welle 4 - Terminal-/Charset-/Plattform-Vorhaertung** Reihenfolgehinweis: abgeschlossen (Branch `021-terminal-charset-hardening`); kontrollierte Terminal-Session, begrenztes Emulations-Subset, KOI8-R-/Unicode-Mapping, rohe 8x16-Fixture, geschlossene Profile und App-Loop-/Cell-Proof sind frameworkweit nachgewiesen. -- [ ] **Welle 4 - Visual Component Porting** - Reihenfolgehinweis: nach der Wave-4-Vorhaertung ueber `Lastenheft_Wave4-Visual-Component-Porting.md`; die terminalnahen Beispiele duerfen nicht auf Host-Screenshots oder unstrukturierte Konsolenzugriffe als Primaerbeweis bauen. +- [x] **Welle 4 - Visual Component Porting** + Reihenfolgehinweis: abgeschlossen (Branch `022-wave4-visual-component-porting`); `terminal`, `cyrillic`, `fonts`, `eterm` und `xterm` sind sichtbare Drei-Schichten-Demos mit App-Loop-, Zustands-, View- und Buffer-/Cell-Proof sowie ehrlichen Host-Fallbacks. - [ ] **Welle 5 - Turbo-Pascal-Demos aus TP7 (`TVDEMOS/`)** Reihenfolgehinweis: erst nach Abschluss der MUSS-Wellen 1 bis 4; bevorzugt nach stabilen Portierungen von `tvdemo`, `tvedit`, Hilfesystem und Dialogschicht. - [ ] **Welle 6 - Turbo-Pascal-Dateimanager `TVFM/`** @@ -659,9 +659,9 @@ Dieser Marker ist bei jeder wesentlichen Fortschreibung des Pflichtenhefts auf d - `3.3` = Welle 3: Editor, Dateien, Hilfe und Streams — ✓ ABGESCHLOSSEN (Branch `019-wave3-visual-component-porting`) - `3.3b` = Maussupport und Interaktions-Härtung — ✓ ABGESCHLOSSEN (Branch `020-mouse-support-interaction`) - `3.3c` = Terminal-/Charset-/Plattform-Vorhärtung — ✓ ABGESCHLOSSEN (Branch `021-terminal-charset-hardening`) - - `3.4` = Welle 4: Terminal-Emulation und erweiterte Zeichensaetze + - `3.4` = Welle 4: Terminal-Emulation und erweiterte Zeichensaetze — ✓ ABGESCHLOSSEN (Branch `022-wave4-visual-component-porting`) ->>> NAECHSTER SCHRITT <<< Welle 4 sichtbar portieren: `Lastenheft_Wave4-Visual-Component-Porting.md` umsetzen. Danach folgt `Lastenheft_06_A11Y_Framework.md`. +>>> NAECHSTER SCHRITT <<< A11Y-Framework über `Lastenheft_06_A11Y_Framework.md` umsetzen. Wave 5 bleibt bis zu diesem Abschluss zurückgestellt. 4. **MUSS-Testumfang und Beispiel-Smoke-Tests schliessen** - Fuer alle 25 portierten Originalbeispiele automatisierte Smoke-Tests in CI bereitstellen. diff --git a/TuiVision.sln b/TuiVision.sln index b14d4381..5877b7a8 100644 --- a/TuiVision.sln +++ b/TuiVision.sln @@ -61,6 +61,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TCombo", "examples\TCombo\T EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TProgB", "examples\TProgB\TProgB.csproj", "{2E5C3392-7812-4D51-96F0-378EE165E542}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cyrillic", "examples\Cyrillic\Cyrillic.csproj", "{29B9F2F0-F28C-4E42-B0EB-188E52722D0C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ETerm", "examples\ETerm\ETerm.csproj", "{18DE6AA0-9C27-4A8E-BECE-B5EC43DB49FA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fonts", "examples\Fonts\Fonts.csproj", "{6596A719-E309-4E16-83B3-687B9A1DD2A1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Terminal", "examples\Terminal\Terminal.csproj", "{2D56E4E0-CF55-409E-AA67-52A7CDD3824D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XTerm", "examples\XTerm\XTerm.csproj", "{D68F02F2-422E-491E-B638-F376F67E948C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -383,6 +393,66 @@ Global {2E5C3392-7812-4D51-96F0-378EE165E542}.Release|x64.Build.0 = Release|Any CPU {2E5C3392-7812-4D51-96F0-378EE165E542}.Release|x86.ActiveCfg = Release|Any CPU {2E5C3392-7812-4D51-96F0-378EE165E542}.Release|x86.Build.0 = Release|Any CPU + {29B9F2F0-F28C-4E42-B0EB-188E52722D0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29B9F2F0-F28C-4E42-B0EB-188E52722D0C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29B9F2F0-F28C-4E42-B0EB-188E52722D0C}.Debug|x64.ActiveCfg = Debug|Any CPU + {29B9F2F0-F28C-4E42-B0EB-188E52722D0C}.Debug|x64.Build.0 = Debug|Any CPU + {29B9F2F0-F28C-4E42-B0EB-188E52722D0C}.Debug|x86.ActiveCfg = Debug|Any CPU + {29B9F2F0-F28C-4E42-B0EB-188E52722D0C}.Debug|x86.Build.0 = Debug|Any CPU + {29B9F2F0-F28C-4E42-B0EB-188E52722D0C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29B9F2F0-F28C-4E42-B0EB-188E52722D0C}.Release|Any CPU.Build.0 = Release|Any CPU + {29B9F2F0-F28C-4E42-B0EB-188E52722D0C}.Release|x64.ActiveCfg = Release|Any CPU + {29B9F2F0-F28C-4E42-B0EB-188E52722D0C}.Release|x64.Build.0 = Release|Any CPU + {29B9F2F0-F28C-4E42-B0EB-188E52722D0C}.Release|x86.ActiveCfg = Release|Any CPU + {29B9F2F0-F28C-4E42-B0EB-188E52722D0C}.Release|x86.Build.0 = Release|Any CPU + {18DE6AA0-9C27-4A8E-BECE-B5EC43DB49FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18DE6AA0-9C27-4A8E-BECE-B5EC43DB49FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18DE6AA0-9C27-4A8E-BECE-B5EC43DB49FA}.Debug|x64.ActiveCfg = Debug|Any CPU + {18DE6AA0-9C27-4A8E-BECE-B5EC43DB49FA}.Debug|x64.Build.0 = Debug|Any CPU + {18DE6AA0-9C27-4A8E-BECE-B5EC43DB49FA}.Debug|x86.ActiveCfg = Debug|Any CPU + {18DE6AA0-9C27-4A8E-BECE-B5EC43DB49FA}.Debug|x86.Build.0 = Debug|Any CPU + {18DE6AA0-9C27-4A8E-BECE-B5EC43DB49FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18DE6AA0-9C27-4A8E-BECE-B5EC43DB49FA}.Release|Any CPU.Build.0 = Release|Any CPU + {18DE6AA0-9C27-4A8E-BECE-B5EC43DB49FA}.Release|x64.ActiveCfg = Release|Any CPU + {18DE6AA0-9C27-4A8E-BECE-B5EC43DB49FA}.Release|x64.Build.0 = Release|Any CPU + {18DE6AA0-9C27-4A8E-BECE-B5EC43DB49FA}.Release|x86.ActiveCfg = Release|Any CPU + {18DE6AA0-9C27-4A8E-BECE-B5EC43DB49FA}.Release|x86.Build.0 = Release|Any CPU + {6596A719-E309-4E16-83B3-687B9A1DD2A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6596A719-E309-4E16-83B3-687B9A1DD2A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6596A719-E309-4E16-83B3-687B9A1DD2A1}.Debug|x64.ActiveCfg = Debug|Any CPU + {6596A719-E309-4E16-83B3-687B9A1DD2A1}.Debug|x64.Build.0 = Debug|Any CPU + {6596A719-E309-4E16-83B3-687B9A1DD2A1}.Debug|x86.ActiveCfg = Debug|Any CPU + {6596A719-E309-4E16-83B3-687B9A1DD2A1}.Debug|x86.Build.0 = Debug|Any CPU + {6596A719-E309-4E16-83B3-687B9A1DD2A1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6596A719-E309-4E16-83B3-687B9A1DD2A1}.Release|Any CPU.Build.0 = Release|Any CPU + {6596A719-E309-4E16-83B3-687B9A1DD2A1}.Release|x64.ActiveCfg = Release|Any CPU + {6596A719-E309-4E16-83B3-687B9A1DD2A1}.Release|x64.Build.0 = Release|Any CPU + {6596A719-E309-4E16-83B3-687B9A1DD2A1}.Release|x86.ActiveCfg = Release|Any CPU + {6596A719-E309-4E16-83B3-687B9A1DD2A1}.Release|x86.Build.0 = Release|Any CPU + {2D56E4E0-CF55-409E-AA67-52A7CDD3824D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D56E4E0-CF55-409E-AA67-52A7CDD3824D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D56E4E0-CF55-409E-AA67-52A7CDD3824D}.Debug|x64.ActiveCfg = Debug|Any CPU + {2D56E4E0-CF55-409E-AA67-52A7CDD3824D}.Debug|x64.Build.0 = Debug|Any CPU + {2D56E4E0-CF55-409E-AA67-52A7CDD3824D}.Debug|x86.ActiveCfg = Debug|Any CPU + {2D56E4E0-CF55-409E-AA67-52A7CDD3824D}.Debug|x86.Build.0 = Debug|Any CPU + {2D56E4E0-CF55-409E-AA67-52A7CDD3824D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D56E4E0-CF55-409E-AA67-52A7CDD3824D}.Release|Any CPU.Build.0 = Release|Any CPU + {2D56E4E0-CF55-409E-AA67-52A7CDD3824D}.Release|x64.ActiveCfg = Release|Any CPU + {2D56E4E0-CF55-409E-AA67-52A7CDD3824D}.Release|x64.Build.0 = Release|Any CPU + {2D56E4E0-CF55-409E-AA67-52A7CDD3824D}.Release|x86.ActiveCfg = Release|Any CPU + {2D56E4E0-CF55-409E-AA67-52A7CDD3824D}.Release|x86.Build.0 = Release|Any CPU + {D68F02F2-422E-491E-B638-F376F67E948C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D68F02F2-422E-491E-B638-F376F67E948C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D68F02F2-422E-491E-B638-F376F67E948C}.Debug|x64.ActiveCfg = Debug|Any CPU + {D68F02F2-422E-491E-B638-F376F67E948C}.Debug|x64.Build.0 = Debug|Any CPU + {D68F02F2-422E-491E-B638-F376F67E948C}.Debug|x86.ActiveCfg = Debug|Any CPU + {D68F02F2-422E-491E-B638-F376F67E948C}.Debug|x86.Build.0 = Debug|Any CPU + {D68F02F2-422E-491E-B638-F376F67E948C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D68F02F2-422E-491E-B638-F376F67E948C}.Release|Any CPU.Build.0 = Release|Any CPU + {D68F02F2-422E-491E-B638-F376F67E948C}.Release|x64.ActiveCfg = Release|Any CPU + {D68F02F2-422E-491E-B638-F376F67E948C}.Release|x64.Build.0 = Release|Any CPU + {D68F02F2-422E-491E-B638-F376F67E948C}.Release|x86.ActiveCfg = Release|Any CPU + {D68F02F2-422E-491E-B638-F376F67E948C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -414,5 +484,10 @@ Global {C7AE862D-26A3-47F2-A4DC-FADD92CE2665} = {730CB5BB-6AC0-4155-86CB-1F7816658AF8} {F6CA6663-0DD1-4B9B-9F3E-BBC6CF73E010} = {730CB5BB-6AC0-4155-86CB-1F7816658AF8} {2E5C3392-7812-4D51-96F0-378EE165E542} = {730CB5BB-6AC0-4155-86CB-1F7816658AF8} + {29B9F2F0-F28C-4E42-B0EB-188E52722D0C} = {730CB5BB-6AC0-4155-86CB-1F7816658AF8} + {18DE6AA0-9C27-4A8E-BECE-B5EC43DB49FA} = {730CB5BB-6AC0-4155-86CB-1F7816658AF8} + {6596A719-E309-4E16-83B3-687B9A1DD2A1} = {730CB5BB-6AC0-4155-86CB-1F7816658AF8} + {2D56E4E0-CF55-409E-AA67-52A7CDD3824D} = {730CB5BB-6AC0-4155-86CB-1F7816658AF8} + {D68F02F2-422E-491E-B638-F376F67E948C} = {730CB5BB-6AC0-4155-86CB-1F7816658AF8} EndGlobalSection EndGlobal diff --git a/coverlet.runsettings b/coverlet.runsettings index a7d4376c..ce1115b6 100644 --- a/coverlet.runsettings +++ b/coverlet.runsettings @@ -7,7 +7,7 @@ the five framework modules listed under below: TuiVision.Core, TuiVision.Controls, TuiVision.Serialization, TuiVision.Compatibility, and TuiVision.Drivers.Console. - - Example application assemblies (4 wave-1 + 11 wave-2) and every *.Tests + - Example application assemblies (4 wave-1 + 11 wave-2 + 5 wave-3 + 5 wave-4) and every *.Tests project are intentionally excluded from the gate aggregation. Smoke tests remain required proof of behaviour but do not replace per-module coverage. - The >=80% target documented in plan.md is informational tracking only and @@ -24,7 +24,7 @@ cobertura [TuiVision.Core]*,[TuiVision.Controls]*,[TuiVision.Serialization]*,[TuiVision.Compatibility]*,[TuiVision.Drivers.Console]* - [Desklogo]*,[MsgCls]*,[Tutorial]*,[Videomode]*,[Clipboard]*,[Demo]*,[DlgDsn]*,[DynTxt]*,[InpLis]*,[ListVi]*,[ProgBa]*,[Sdlg]*,[Sdlg2]*,[TCombo]*,[TProgB]*,[TuiVision.Core.Tests]*,[TuiVision.Controls.Tests]*,[TuiVision.Serialization.Tests]*,[TuiVision.Compatibility.Tests]*,[TuiVision.Drivers.Tests]*,[TuiVision.Examples.SmokeTests]* + [Desklogo]*,[MsgCls]*,[Tutorial]*,[Videomode]*,[Clipboard]*,[Demo]*,[DlgDsn]*,[DynTxt]*,[InpLis]*,[ListVi]*,[ProgBa]*,[Sdlg]*,[Sdlg2]*,[TCombo]*,[TProgB]*,[BHelp]*,[HelpDemo]*,[I18n]*,[TvEdit]*,[TvHc]*,[Cyrillic]*,[ETerm]*,[Fonts]*,[Terminal]*,[XTerm]*,[TuiVision.Core.Tests]*,[TuiVision.Controls.Tests]*,[TuiVision.Serialization.Tests]*,[TuiVision.Compatibility.Tests]*,[TuiVision.Drivers.Tests]*,[TuiVision.Examples.SmokeTests]* Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute false true diff --git a/docs/architecture/runtime-view.md b/docs/architecture/runtime-view.md index c9dd5c2f..58d1dfb5 100644 --- a/docs/architecture/runtime-view.md +++ b/docs/architecture/runtime-view.md @@ -167,3 +167,32 @@ English: Drivers.Console owns session, parser, mapping, fixture, and profile. Core remains the cell/buffer contract. Controls only projects validated state and adds no second terminal or key parser. Physical host observation remains separate from deterministic in-process proof. + +## 022 Wave-4 Visual Component Addendum + +```text +Feature-021 contracts or immutable source-controlled manifest + | + | controlled command through app.Run() + v +Terminal | Cyrillic | Fonts | ETerm | XTerm + | + | visible main component + TStatusLine + Help -> Description + v +State + exact view identity + buffer/cell proof + text-first fallback + | + v +Deterministic host classification; physical host evidence remains separate +``` + +Deutsch: Terminal, Cyrillic und Fonts projizieren bestehende 021-Verträge. +ETerm und XTerm zeigen nur exakte unveränderliche Ressourcenmanifeste und führen +keinen historischen Parser aus. Die verlinkte Präsentationsquelle wird pro +Beispiel-Assembly kompiliert; Querschnittstests teilen deshalb neutrale DTOs und +Delegates statt eine gemeinsame CLR-Typidentität anzunehmen. + +English: Terminal, Cyrillic, and Fonts project existing 021 contracts. ETerm +and XTerm show only exact immutable resource manifests and execute no historical +parser. Linked presentation source is compiled into each example assembly, so +cross-project tests share neutral DTOs and delegates instead of assuming one CLR +type identity. diff --git a/docs/guides/examples/cyrillic.md b/docs/guides/examples/cyrillic.md new file mode 100644 index 00000000..7c44de34 --- /dev/null +++ b/docs/guides/examples/cyrillic.md @@ -0,0 +1,57 @@ +# Cyrillic Beispiel / Cyrillic Example + +## Deutsch + +`Cyrillic` zeigt kontrollierte KOI8-R-Bytes als Unicode-Zellen. Start: + +```bash +dotnet run --project examples/Cyrillic +``` + +Die Hauptfläche beschriftet Quelle, Zielzeichen, Ergebnis und Begründung. Der +Menübefehl `Nächstes Mapping` durchläuft direkte Abbildung, Ersatzzeichen, +ungültigen Quellwert und nicht unterstützten Zeichensatz. Die Statuszeile und +`Help -> Description` bleiben per Tastatur erreichbar. + +Die Abbildung verwendet die feste Framework-Tabelle. Sie liest oder verändert +weder Host-Locale noch Codepage, Font oder Tastaturbelegung. Damit bleibt das +Ergebnis auch in umgeleiteten und schmalen Terminals als Text prüfbar. + +Historisch übernimmt die Demo den sichtbaren KOI8-R-/Cyrillic-Zweck der +Linux/X11-Beispiele. Root-Rechte, `/dev/vcsa`, Setup-Skripte und Hoständerungen +sind bewusst nicht Teil der modernen Portierung. + +Host-Nachweis und Barrierefreiheit: Der automatisierte Nachweis ist +deterministisch und textorientiert; physische Hostbeobachtung bleibt getrennt. +Status, Mapping und Fallback sind per Tastatur erreichbar und nicht farbabhängig. + +## English + +`Cyrillic` shows controlled KOI8-R bytes as Unicode cells. Launch it with: + +```bash +dotnet run --project examples/Cyrillic +``` + +The main area labels source, target glyph, outcome, and reason. `Next mapping` +cycles through direct mapping, replacement, invalid source value, and an +unsupported charset. The status line and `Help -> Description` remain keyboard +reachable. + +Mapping uses the fixed framework table. It neither reads nor changes the host +locale, codepage, font, or keyboard map. The result therefore remains textually +reviewable in redirected and narrow terminals. + +The demo retains the visible KOI8-R/Cyrillic purpose of the historical Linux/X11 +examples. Root access, `/dev/vcsa`, setup scripts, and host mutation are +intentionally outside the modern port. + +Host evidence and accessibility: Automated proof is deterministic and +text-first; physical host observation remains separate. Status, mapping, and +fallback are keyboard reachable and do not depend on color. + +## Nachweis / Proof + +```bash +dotnet test tests/TuiVision.Examples.SmokeTests/ --filter "FullyQualifiedName~CyrillicSmokeTests" +``` diff --git a/docs/guides/examples/eterm.md b/docs/guides/examples/eterm.md new file mode 100644 index 00000000..254c72b1 --- /dev/null +++ b/docs/guides/examples/eterm.md @@ -0,0 +1,54 @@ +# ETerm Beispiel / ETerm Example + +## Deutsch + +`ETerm` zeigt ausgewählte historische Menü-, Theme- und +Präsentationswerte als unveränderliches, typisiertes Manifest. Start: + +```bash +dotnet run --project examples/ETerm +``` + +Die Hauptfläche nennt Schlüssel, Wert, Kategorie und historische Quelle. +`Nächster Eintrag` ändert die Auswahl; die Statuszeile nennt den aktiven +Schlüssel. `Help -> Description` erklärt die Grenze. + +Die Demo parst weder `menus.cfg` noch `theme.cfg`. Sie führt insbesondere keine +historischen `spawn`, `save`, `exit`- oder Theme-Aktionen aus. Fehlende oder +nicht belegte Einträge erscheinen als textorientierter `Unsupported`-Fallback. + +Damit bleibt der historische Zweck sichtbar, ohne eine native ETerm-Capability +vorzutäuschen. Alle Hauptpfade sind per Tastatur erreichbar und nicht nur durch +Farbe unterscheidbar. + +Host-Nachweis: Der automatische Pfad beweist nur das In-Process-Manifest. +Physische ETerm- oder Theme-Beobachtung wird nicht behauptet. + +## English + +`ETerm` shows selected historical menu, theme, and presentation values as an +immutable typed manifest. Launch it with: + +```bash +dotnet run --project examples/ETerm +``` + +The main area names each key, value, category, and historical source. `Next +entry` changes the selection, and the status line names the active key. `Help -> +Description` explains the boundary. + +The demo parses neither `menus.cfg` nor `theme.cfg`. In particular, it executes +none of the historical `spawn`, `save`, `exit`, or theme actions. Missing or +unproven entries appear as a text-first `Unsupported` fallback. + +This keeps the historical purpose visible without claiming a native ETerm +capability. All primary paths are keyboard reachable and do not rely on color. + +Host evidence: The automated path proves only the in-process manifest. It does +not claim physical ETerm or theme observation. + +## Nachweis / Proof + +```bash +dotnet test tests/TuiVision.Examples.SmokeTests/ --filter "FullyQualifiedName~ETermSmokeTests" +``` diff --git a/docs/guides/examples/fonts.md b/docs/guides/examples/fonts.md new file mode 100644 index 00000000..92be5096 --- /dev/null +++ b/docs/guides/examples/fonts.md @@ -0,0 +1,50 @@ +# Fonts Beispiel / Fonts Example + +## Deutsch + +`Fonts` zeigt eine kontrollierte rohe 8x16-Font-Fixture als Textmatrix. Start: + +```bash +dotnet run --project examples/Fonts +``` + +Die Hauptfläche nennt Breite, Höhe, Glyphenzahl, Datenlänge und ausgewählte +Glyphe. `Nächste Glyphe` wechselt die Auswahl. `Help -> Description` erklärt die +Grenze. Gesetzte Pixel erscheinen als `#`, leere Pixel als `.`, sodass Farbe +keine Bedeutung tragen muss. + +Die 4.096-Byte-Fixture ist eine unveränderte, projektkontrollierte Kopie der +historischen `font.016`. Die Demo installiert keinen Font, startet keinen +Generator und schreibt keine Datei. Falsche Länge, Geometrie, Stride, Quelle, +Format oder eine leere Glyphe führen zu einem sichtbaren `Fallback`. + +Host-Nachweis und Barrierefreiheit: Der Nachweis liest nur die kontrollierte +Fixture. Physische Fontdarstellung bleibt außerhalb des Claims. Auswahl, +Status, Beschreibung und Fallback sind per Tastatur und als Text erreichbar. + +## English + +`Fonts` shows a controlled raw 8x16 font fixture as a text matrix. Launch it with: + +```bash +dotnet run --project examples/Fonts +``` + +The main area names width, height, glyph count, data length, and selected glyph. +`Next glyph` changes the selection. `Help -> Description` explains the boundary. +Set pixels use `#` and empty pixels use `.`, so meaning does not depend on color. + +The 4,096-byte fixture is an unchanged project-controlled copy of the historical +`font.016`. The demo installs no font, runs no generator, and writes no file. +Wrong length, geometry, stride, source, format, or a blank glyph produces a +visible `Fallback`. + +Host evidence and accessibility: Proof reads only the controlled fixture. +Physical font rendering remains outside the claim. Selection, status, +description, and fallback are keyboard reachable and textual. + +## Nachweis / Proof + +```bash +dotnet test tests/TuiVision.Examples.SmokeTests/ --filter "FullyQualifiedName~FontsSmokeTests" +``` diff --git a/docs/guides/examples/terminal.md b/docs/guides/examples/terminal.md new file mode 100644 index 00000000..d772d1cc --- /dev/null +++ b/docs/guides/examples/terminal.md @@ -0,0 +1,65 @@ +# Terminal Beispiel / Terminal Example + +## Deutsch + +`Terminal` zeigt die kontrollierte In-Process-Sitzung aus Feature 021 in einer +echten `TTerminalView`. Start: + +```bash +dotnet run --project examples/Terminal +``` + +Die Hauptfläche zeigt Eingabe, Ausgabe und Cursor. Die Statuszeile nennt +Capability, Cursorposition und letztes Ergebnis. `Help -> Description` erklärt +den begrenzten Vertrag. Die Demo startet keinen Prozess, keine Shell und kein PTY. + +Unterstützte Aktionen bleiben im dokumentierten C0-/CSI-Subset. Ungültige oder +nicht unterstützte Folgen werden atomar abgelehnt. Danach bleibt eine neue, +unabhängige Eingabe nutzbar. Bei fehlender Terminal-Capability erscheint ein +stabiler textorientierter `Unsupported`-Fallback. + +Historisch bleibt der Zweck von `terminal.cc` und `terminal.h` erhalten: Eine +sichtbare Terminalansicht macht Puffer, Cursor und Ablauf prüfbar. Die moderne +Variante verwendet verwaltete Zellen statt Ringpuffer, Hostprozess oder Shell. + +Barrierefreiheit: Alle Hauptpfade sind per Tastatur erreichbar. Zustand, +Fallback und nächster Bedienpfad stehen als Text und hängen nicht nur von Farbe ab. + +Host-Nachweis: Automatische Tests klassifizieren den Prozess deterministisch. +Physische macOS-, Linux- und Windows-/WSL-Terminals bleiben getrennte manuelle +oder CI-Beobachtungen; die Demo behauptet dafür keinen lokalen Screenshot-Proof. + +## English + +`Terminal` shows the controlled in-process session from Feature 021 in a real +`TTerminalView`. Launch it with: + +```bash +dotnet run --project examples/Terminal +``` + +The main area shows input, output, and the cursor. The status line names the +capability, cursor position, and latest result. `Help -> Description` explains +the bounded contract. The demo starts no process, shell, or PTY. + +Supported actions stay within the documented C0/CSI subset. Invalid or +unsupported sequences are rejected atomically, and the next independent input +remains usable. An unavailable terminal capability produces a stable text-first +`Unsupported` fallback. + +The historical purpose of `terminal.cc` and `terminal.h` remains: a visible +terminal view makes buffer, cursor, and flow reviewable. The modern version uses +managed cells instead of a ring buffer, host process, or shell. + +Accessibility: All primary paths are keyboard reachable. State, fallback, and +the next operation are text and do not rely on color alone. + +Host evidence: Automated tests classify the process deterministically. Physical +macOS, Linux, and Windows/WSL terminals remain separate manual or CI +observations; the demo does not claim local screenshot proof for them. + +## Nachweis / Proof + +```bash +dotnet test tests/TuiVision.Examples.SmokeTests/ --filter "FullyQualifiedName~TerminalSmokeTests" +``` diff --git a/docs/guides/examples/xterm.md b/docs/guides/examples/xterm.md new file mode 100644 index 00000000..4ea9e083 --- /dev/null +++ b/docs/guides/examples/xterm.md @@ -0,0 +1,55 @@ +# XTerm Beispiel / XTerm Example + +## Deutsch + +`XTerm` zeigt ausgewählte historische Tastenfolgen, Farbwerte und Capabilities +als unveränderliches Manifest. Start: + +```bash +dotnet run --project examples/XTerm +``` + +Die Hauptfläche nennt Schlüssel, Wert, Kategorie und `Xterm.res` als Quelle. +`Nächster Eintrag`, Statuszeile und `Help -> Description` bilden den vollständigen +Tastaturpfad. + +Die Demo startet kein XTerm, liest keine X-Resource-Datenbank und wertet weder +terminfo noch externe Kommandos aus. Laufzeitparsing bleibt bei der bestehenden +Compatibility-Eingabe und dem Feature-021-Session-Parser. Native oder nicht +belegte Ressourcen erscheinen sichtbar als `Unsupported`. + +Der historische Zweck bleibt als prüfbare Ressourcen- und Sequenzübersicht +erhalten. Zustand und Fallback sind Text und benötigen keine Farbwahrnehmung. + +Host-Nachweis: Der automatische Pfad beweist die kontrollierte Ansicht, nicht +ein physisches XTerm. Auswahl, Status und Hilfe bleiben per Tastatur erreichbar. + +## English + +`XTerm` shows selected historical key sequences, colors, and capabilities as an +immutable manifest. Launch it with: + +```bash +dotnet run --project examples/XTerm +``` + +The main area names each key, value, category, and `Xterm.res` as its source. +`Next entry`, the status line, and `Help -> Description` form the complete +keyboard path. + +The demo starts no XTerm process, reads no X resource database, and evaluates +neither terminfo nor external commands. Runtime parsing remains with existing +Compatibility input and the Feature-021 session parser. Native or unproven +resources appear visibly as `Unsupported`. + +The historical purpose remains as a reviewable resource and sequence overview. +State and fallback are textual and require no color perception. + +Host evidence: The automated path proves the controlled view, not a physical +XTerm. Selection, status, and help remain keyboard reachable. + +## Nachweis / Proof + +```bash +dotnet test tests/TuiVision.Examples.SmokeTests/ --filter "FullyQualifiedName~XTermSmokeTests" +``` diff --git a/docs/project-statistics.md b/docs/project-statistics.md index a0e736e7..170154ca 100644 --- a/docs/project-statistics.md +++ b/docs/project-statistics.md @@ -1,6 +1,6 @@ # Projektstatistik TuiVision -Stand: 2026-07-12 (aktualisiert einschließlich Feature 021 mit kontrollierter Terminal-Session, KOI8-R-/Font-/Profil-Verträgen und App-Loop-/Cell-Proof) +Stand: 2026-07-12 (aktualisiert einschließlich Feature 022 mit fünf sichtbaren Wave-4-Beispielen, Host-Fallbacks und App-Loop-/Cell-Proof) Aktueller Zusatz: Feature 016 dokumentiert 157/157 Secure-Development- Kontrollen, sechs behobene Medium-Funde, 498/498 grüne Release-Tests und eine @@ -14,6 +14,11 @@ Feature 021 ergänzt eine rein in-process arbeitende Terminal-Session, ein begrenztes C0-/CSI-Subset, hostunabhängiges KOI8-R-/Unicode-Mapping, eine rohe 8x16-Fixture, geschlossene Profile und ein sichtbares `TTerminalView`. +Feature 022 ergänzt `Terminal`, `Cyrillic`, `Fonts`, `ETerm` und `XTerm` als +sichtbare Drei-Schichten-Demos. Deterministische In-Process-Evidence bleibt von +physischer Hostbeobachtung getrennt; native Ressourcen erhalten ehrliche +textorientierte Fallbacks. + ## Zweck und Pflege Diese Datei ist das fortlaufende Statistik-Register fuer TuiVision. Sie wird @@ -642,6 +647,7 @@ fortgeschrieben. | 2026-07-12 | Post-Merge-Ledger-Abschluss für `020-mouse-support-interaction` | PR #48 bestand Ubuntu-/macOS-CI, macOS-/Linux-/Windows-Homogeneity, DocFX, Supply Chain, Gitleaks, Agent-Secret-Scan und Claude Review auf dem finalen Head `6944719`. GraphQL meldete null Review-Threads und null Konversationskommentare; Copilot blieb wegen Nutzerquota ein fehlender Review. Nur Human Approval erforderte den autorisierten engen Admin-Bypass. Feature-Merge `b52d90f` und Evidence-Closeout #49 mit Merge `5aca8c1` löschten ihre Remote-Branches; lokales sauberes `main` entsprach danach jeweils `origin/main`. Feature 020 schließt mit 126/126 Tasks. | | 2026-07-12 | Lernende Nacharbeit nach autonomem Feature 020 | Der dritte Feldlauf bestätigt Compile-Surface-Check und projektlokale Red-Matrix. Neu korrigiert wird eine Evidence-Schleife: Ein Commit mit aktuellem Check-/Thread-Stand ändert den geprüften Head und entwertet seine eigene Aussage. Runbook, `$speckit-autonomous`, Tasks-/Evidence-Template und alle fünf Agentenflächen prüfen die Gates deshalb weiter vor dem Merge, legen selbstinvalidierende Reviewed-Head- und echte Post-Merge-Fakten aber in genau einen benannten kausalen Closeout-Pfad. Änderungsvolumen vor dieser Statistikzeile: `+65/-14` reine Dokumentations-, Skill-, Template-, Guidance- und Feature-Evidence-Zeilen, kein Produktions- oder Testcode. Konservative Manualreferenz: `0,8` Tage beziehungsweise `6,3` Stunden bei 80 Zeilen/Tag; Thorsten-Solo: `0,5` Tage beziehungsweise `4,1` Stunden bei 125 Zeilen/Tag. Validierung: Skill-Schema, `specify check`, sechs Presets, 5/5 Agentenparität, 31 eindeutige numerische Template-Task-IDs, Diff- und Secret-Prüfung, DocFX zweimal mit 0 Warnungen/0 Fehlern, Playwright/axe 2/2 und UTF-8-`lynx`-Review bestanden. Kein .NET-Build, Test oder Coverage-Lauf wurde ausgelöst, weil keine ausführbare Logik betroffen ist. Ein stackneutraler Detektor ist noch nicht belegt; deshalb wird kein neues Skript eingeführt. | | 2026-07-12 | `/speckit-autonomous` für `021-terminal-charset-hardening` | Feature 021 liefert `TerminalSession` mit begrenztem C0-/CSI-Subset, 4.096-Zellen-FIFO, atomarer Recovery und Lifecycle, eine feste KOI8-R-Tabelle mit U+FFFD, die exakte rohe 8x16/256/4.096-Byte-Fixture, geschlossene `System.Text.Json`-Profile und ein `TTerminalView` mit Profil-/Capability-Status, Cursor und App-Loop-/Cell-Proof. Der Vor-Commit-Snapshot ohne diese Statistikzeile umfasst `+1580/-6` Produktionscode, `+933/-6` Tests, `+2120/-4` Dokumentation/Evidence/Guidance und `+4/-4` Metadaten, zusammen `+4637/-20` beziehungsweise `4617` Nettozeilen. Konservative Manualreferenz für 4637 hinzugefügte oder aktualisierte Zeilen: `58,0` Tage beziehungsweise `452,1` Stunden bei 80 Zeilen/Tag; Thorsten-Solo: `37,1` Tage beziehungsweise `289,3` Stunden bei 125 Zeilen/Tag. Sichtbares Arbeitsfenster ist der autonome Lauf am 2026-07-12; `58,0x` und `37,1x` beschreiben Lieferdichte, keine Stoppuhrzeit. Lokale Abnahme: 51/51 Driver-, 314/314 Controls- und 18/18 Compatibility-Targeted-Tests, 640/640 Volltests, Coverage Core 89,78 %, Controls 83,42 %, Serialization 89,50 %, Compatibility 80,55 % und Drivers.Console 89,18 %, DocFX 0 Warnungen/0 Fehler, Playwright/axe 2/2 sowie UTF-8-`lynx`-Review. Die lokale Umgebung ist Darwin arm64 mit `TERM=dumb`; physische Host-Evidence bleibt deshalb ehrlich `NotRun`. Nächster Intake ist `Lastenheft_Wave4-Visual-Component-Porting.md`. | +| 2026-07-12 | `/speckit-autonomous` für `022-wave4-visual-component-porting` | Feature 022 liefert `Terminal`, `Cyrillic`, `Fonts`, `ETerm` und `XTerm` als sichtbare Hauptkomponente mit echter `TStatusLine`, `Help -> Description`, kontrollierter Operation, Fallback sowie Zustands-, View- und Buffer-/Cell-Proof. Der Vor-Statistik-Snapshot umfasst `+962/-0` Produktionscode, `+588/-0` Tests, `+2007/-4` Markdown/Evidence/Guidance und `+166/-6` Projekt-/Tooling-Metadaten, zusammen `+3723/-10` beziehungsweise `3713` Nettozeilen. Konservative Manualreferenz für 3723 hinzugefügte oder aktualisierte Zeilen: `46,5` Tage beziehungsweise `362,9` Stunden bei 80 Zeilen/Tag; Thorsten-Solo: `29,8` Tage beziehungsweise `232,3` Stunden bei 125 Zeilen/Tag. Sichtbares Arbeitsfenster ist der autonome Lauf am 2026-07-12; `46,5x` und `29,8x` beschreiben Lieferdichte, keine Stoppuhrzeit. Lokale Abnahme: 137/137 vollständige Beispiel-Smokes, 662/662 Volltests, Coverage Core 89,78 %, Controls 83,42 %, Serialization 89,50 %, Compatibility 80,55 % und Drivers.Console 89,18 %, DocFX 0 Warnungen/0 Fehler, Playwright/axe 2/2 sowie UTF-8-`lynx` für alle fünf Guides. Die lokale Umgebung ist Darwin arm64 mit `TERM=dumb`; physische macOS-/Linux-/Windows-/WSL-Evidence bleibt ehrlich `NotRun`. Nächster Intake ist `Lastenheft_06_A11Y_Framework.md`. | ## Gesamtstatistik @@ -651,21 +657,21 @@ nächsten Statistiklauf eine dokumentierte Momentaufnahme. | Kennzahl | Verdichteter Gesamtblick | |---|---:| -| Artefaktbasis gesamt | 231653 Zeilen | -| Produktions- und Testcode zusammen | 42936 Zeilen (18.5 %) | -| Dokumentationsanteil | 188717 Zeilen (81.5 %) | -| Spec-Kit-Anteil innerhalb der Doku | 47659 Zeilen (25.3 %) | -| Zentrale Governance-/Agent-Dateien | 2588 Zeilen (1.4 % der Doku) | +| Artefaktbasis gesamt | 234363 Zeilen | +| Produktions- und Testcode zusammen | 44486 Zeilen (19.0 %) | +| Dokumentationsanteil | 189877 Zeilen (81.0 %) | +| Spec-Kit-Anteil innerhalb der Doku | 49465 Zeilen (26.1 %) | +| Zentrale Governance-/Agent-Dateien | 2633 Zeilen (1.4 % der Doku) | | Projektgebundene Agent-Skills | 2415 Zeilen (1.3 % der Doku) | | Beobachtbarer Projektzeitraum | 2026-02-08 bis 2026-07-12 | -| Git-Commits / sichtbare Aktivtage | 432 / 69 | -| Gesamtzeilen pro sichtbarem Aktivtag | 3357.3 | -| Gesamtzeilen pro Commit | 536.2 | -| Konservative Einzelentwickler-Untergrenze | 2895.7 Arbeitstage / 22586.0 Stunden | -| Thorsten-Solo-Untergrenze | 1853.2 Arbeitstage / 14455.0 Stunden | -| Kleines 3er-Team mit Koordinationsaufschlag | 1158.3 Arbeitstage | -| Repo-Speedup gegen 80-Zeilen-Referenz | 42.0x | -| Repo-Speedup gegen Thorsten-Referenz | 26.9x | +| Git-Commits / sichtbare Aktivtage | 460 / 69 | +| Gesamtzeilen pro sichtbarem Aktivtag | 3396.6 | +| Gesamtzeilen pro Commit | 509.5 | +| Konservative Einzelentwickler-Untergrenze | 2929.5 Arbeitstage / 22850.4 Stunden | +| Thorsten-Solo-Untergrenze | 1874.9 Arbeitstage / 14624.3 Stunden | +| Kleines 3er-Team mit Koordinationsaufschlag | 1171.8 Arbeitstage | +| Repo-Speedup gegen 80-Zeilen-Referenz | 42.5x | +| Repo-Speedup gegen Thorsten-Referenz | 27.2x | Die hohe Dokumentationsquote enthält die umfangreiche Secure-Development-, Governance- und Spec-Kit-Basis. Das autonome Runbook und der neue @@ -676,17 +682,17 @@ Git-Aktivtagen und sind keine Messung persönlicher Arbeitszeit. The high documentation share includes the extensive secure-development, governance, and Spec-Kit baseline. The autonomous runbook and orchestration -skill made the workflow repeated in 018 and 019 reproducible, while Wave 3 -keeps presentation, controlled I/O, and visual proof bounded to feature scope. Speedups +skill made the workflow repeated in 018 through 022 reproducible, while Wave 4 +keeps terminal-adjacent presentation, host fallbacks, and visual proof bounded to feature scope. Speedups compare visible scope with Git active days and do not measure personal time. ### ASCII-Diagramme ```text Artefaktmix nach Snapshot (Zeilen) -Produktion | #### | 25701 | 11.1 % -Tests | ### | 17235 | 7.4 % -Dokumentation | ##############################| 188717 | 81.5 % +Produktion | #### | 26663 | 11.4 % +Tests | ### | 17823 | 7.6 % +Dokumentation | ##############################| 189877 | 81.0 % ``` Die Balken verwenden dieselbe Skala. Die Zahlen bleiben der genaue, @@ -722,6 +728,7 @@ Branch-/Phasenvolumen nach dokumentierter Netto-Basis (Zeilen) 22 019i | ##### | 3392 23 020i | ##### | 3584 24 021i | ####### | 4617 +25 022i | ###### | 3713 ``` Feature 017 kombiniert einen kleinen beispielinternen Runtime-Anteil mit einer @@ -748,6 +755,9 @@ window drag with deterministic Driver, Controls, and app-loop proof. Feature 021 combines bounded terminal state and parser recovery with KOI8-R, raw-font metadata, closed profiles, and deterministic view/cell proof. +Feature 022 combines five visible terminal-adjacent demos with deterministic +state/view/cell proof, immutable resource manifests, and honest host fallbacks. + ```text Konservative Handarbeits-Referenz je dokumentierter Phase 0 main | ################# | 127.6 d @@ -775,6 +785,7 @@ Konservative Handarbeits-Referenz je dokumentierter Phase 22 019i | ##### | 42.4 d 23 020i | ##### | 44.8 d 24 021i | ####### | 57.7 d +25 022i | ###### | 46.4 d ``` Die Referenz rechnet den Netto-Phasenumfang mit 80 Zeilen pro Arbeitstag. Für @@ -788,8 +799,8 @@ before statistics maintenance. The ledger also records added or updated lines. ```text Dokumentierte Beschleunigungsfaktoren durch agentische KI + Spec-Kit/SDD -Repo 80 | ############################## | 41.1x -Repo125 | ################### | 26.3x +Repo 80 | ############################## | 42.5x +Repo125 | ################### | 27.2x 014i | ##### | 7.0x 015i-80 | #### | 5.4x 015i125 | ### | 3.4x @@ -807,20 +818,22 @@ AUTO125 | ### | 5.0x 020i125 | ################### | 28.8x 021i-80 | ############################## | 58.0x 021i125 | ################### | 37.1x +022i-80 | ######################## | 46.5x +022i125 | ############### | 29.8x ``` -Die 017-, AUTO-, 018-, 019-, 020- und 021-Werte beziehen sich jeweils auf einen sichtbaren autonomen +Die 017-, AUTO-, 018-, 019-, 020-, 021- und 022-Werte beziehen sich jeweils auf einen sichtbaren autonomen Arbeitstag. Sie beschreiben Lieferdichte, nicht die Dauer einzelner Denk-, Review-, CI- oder Wartephasen. -The 017, AUTO, 018, 019, 020, and 021 values each use one visible autonomous workday. They describe +The 017, AUTO, 018, 019, 020, 021, and 022 values each use one visible autonomous workday. They describe delivery density, not the duration of individual thinking, review, CI, or waiting phases. ```text Vergleich Gesamtaufwand / sichtbares KI-Lieferfenster -Erfahren | ############################## | 2895.7 d -Thorsten | ################### | 1853.2 d +Erfahren | ############################## | 2929.5 d +Thorsten | ################### | 1874.9 d KI sichtbar | # | 69.0 d ``` @@ -854,7 +867,7 @@ X/Y: Phasenvolumen 0..15 (Y ungefähr in Zeilen) ```text X/Y: Phasenvolumen 16..31 (Y ungefähr in Zeilen) 5000 | * | -3500 | * * * * | +3500 | * * * * * | 3000 | * | 1500 | | 600 |* * | @@ -867,9 +880,11 @@ X/Y: Phasenvolumen 16..31 (Y ungefähr in Zeilen) Die beiden Blöcke zeigen den Verlauf ohne eine zu breite Zeile. G bis J stehen für die Phasen 16 bis 19, K für die autonome Standardisierung, L für Feature -018, M für Feature 019, N für Feature 020 und O für Feature 021; alle späteren Slots sind bewusst leer. +018, M für Feature 019, N für Feature 020, O für Feature 021 und P für Feature +022; alle späteren Slots sind bewusst leer. The two blocks show progression without an overly wide line. G through J represent phases 16 through 19, K represents autonomous standardization, L -represents Feature 018, M represents Feature 019, N represents Feature 020, and O represents Feature 021; all later slots are +represents Feature 018, M represents Feature 019, N represents Feature 020, O +represents Feature 021, and P represents Feature 022; all later slots are intentionally empty. diff --git a/docs/security/threat-model.md b/docs/security/threat-model.md index cb08dab1..ca4a11f3 100644 --- a/docs/security/threat-model.md +++ b/docs/security/threat-model.md @@ -40,6 +40,7 @@ Security-`N/A`. | Terminal-/Eventeingabe | Tampering, DoS; I/A | CAPEC-10 Buffer Overflow, CAPEC-20 Input Data Manipulation | Begrenzte managed Buffer, Eventvalidierung, Tests in Core/Controls/Drivers | Low | | SGR-1006-Mauseingang | Spoofing, Tampering, DoS; I/A | CAPEC-20 Input Data Manipulation, CAPEC-130 Excessive Allocation | Feste Sequenz-/Zahlengrenzen, atomare Syntax-/Koordinaten-/Phasenprüfung, genau null oder ein Event, Capability-Reset und Driver-/App-Loop-Tests | Low; physische Terminalvarianten bleiben Host-Evidence | | Terminal-, Charset-, Font- und Profileingabe | Spoofing, Tampering, DoS; I/A | CAPEC-20 Input Data Manipulation, CAPEC-130 Excessive Allocation, CAPEC-153 Input Data Manipulation | 64-Zeichen-/4-Parameter-/9.999-Wert-Grenzen, atomare Parser-Recovery, 4.096-Zellen-FIFO, feste KOI8-R-Tabelle, exakte 8x16-Fixture und geschlossenes JSON-Schema | Low; vollständige Emulation und physische Hostvarianten bleiben außerhalb des Vertrags | +| Wave-4-Fixture und Ressourcenmanifeste | Spoofing, Tampering, DoS; I/A | CAPEC-20 Input Data Manipulation, CAPEC-130 Excessive Allocation, CAPEC-153 Input Data Manipulation | Byte-identische 4.096-Byte-Fixture, exakte Geometrie/Quelle/Format, eindeutige case-sensitive Manifest-Schlüssel und atomare `Rejected`-/`Unsupported`-Fallbacks vor sichtbarer Veröffentlichung | Low; native ETerm-/XTerm-Ressourcen und physische Hostbeobachtung bleiben außerhalb des Claims | | Datei-/Ressourcenpfade | Tampering, Information Disclosure; C/I/A | CAPEC-126 Path Traversal, CAPEC-153 Input Data Manipulation | Pfadvalidierung, kontrollierte Fixtures, sichere Ablehnung, keine beliebigen Dateiinhalte als Proof | Low | | Serialisierte Daten | Tampering, DoS; I/A | CAPEC-130 Excessive Allocation, CAPEC-153 | Truncated/trailing/unknown/cyclic rejection tests, Typregistrierung | Low | | Fehlermeldungen/Output | Information Disclosure; C | CAPEC-215 Fuzzing for sensitive output | Keine Secrets/Stack-Traces in nutzerseitiger Ausgabe, Secret-Scans | Low | diff --git a/docs/toc.yml b/docs/toc.yml index 009ed54b..2818e39d 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -54,3 +54,13 @@ href: guides/examples/i18n.md - name: TvHc href: guides/examples/tvhc.md + - name: Terminal + href: guides/examples/terminal.md + - name: Cyrillic + href: guides/examples/cyrillic.md + - name: Fonts + href: guides/examples/fonts.md + - name: ETerm + href: guides/examples/eterm.md + - name: XTerm + href: guides/examples/xterm.md diff --git a/examples/Cyrillic/Cyrillic.csproj b/examples/Cyrillic/Cyrillic.csproj new file mode 100644 index 00000000..acdc1ee6 --- /dev/null +++ b/examples/Cyrillic/Cyrillic.csproj @@ -0,0 +1,13 @@ + + + Exe + TuiVision.Examples.Cyrillic + Cyrillic + + + + + + + + diff --git a/examples/Cyrillic/CyrillicApp.cs b/examples/Cyrillic/CyrillicApp.cs new file mode 100644 index 00000000..33c14c16 --- /dev/null +++ b/examples/Cyrillic/CyrillicApp.cs @@ -0,0 +1,121 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Controls; +using TuiVision.Core; +using TuiVision.Drivers.Console; +using TuiVision.Examples.Shared; + +namespace TuiVision.Examples.Cyrillic; + +/// +/// Zeigt kontrollierte KOI8-R-/Unicode-Abbildungen ohne Host-Codepage. +/// Shows controlled KOI8-R/Unicode mappings without a host codepage. +/// +public sealed class CyrillicApp : Wave4Application +{ + /// Wechselt zum nächsten Mappingzustand. / Advances to the next mapping state. + public const ushort CmNextMapping = 22101; + + /// Zeigt die Beschreibung. / Shows the description. + public const ushort CmDescription = 22102; + + private readonly List _observedOutcomes = []; + private int _mappingIndex; + private TWindow? _mappingWindow; + + /// Initialisiert die Zeichensatzdemo. / Initializes the charset demo. + public CyrillicApp(TRect bounds, bool headless = false) : base(bounds, headless) + { + CurrentMapping = ResolveMapping(0); + _observedOutcomes.Add(CurrentMapping.Outcome); + RenderMapping(); + } + + /// Aktuelles Mappingergebnis. / Current mapping result. + public CharsetMappingResult CurrentMapping { get; private set; } + + /// Im App-Loop beobachtete Zustände. / States observed in the app loop. + public IReadOnlyList ObservedOutcomes => _observedOutcomes; + + /// Text des Beschreibungspfads. / Description-path text. + public string DescriptionText => + "Cyrillic description: Die feste KOI8-R-Tabelle macht die Abbildung ohne Host-Locale oder Host-Codepage prüfbar. / " + + "The fixed KOI8-R table keeps mapping verifiable without a host locale or host codepage."; + + /// + protected override TMenuBar InitMenuBar(TRect bounds) => new(bounds) + { + Menu = new TMenuItem( + "~C~yrillic", + 0, + Wave4Runtime.HelpMenu(CmDescription, new TMenuItem("~E~nde / E~x~it", ShellCommandIds.cmQuit)), + new TMenuItem("~N~ächstes Mapping / ~N~ext mapping", CmNextMapping)) + }; + + /// + public override void HandleEvent(TEvent @event) + { + if (@event.What == TEventKind.Command && @event.Message.Command == CmNextMapping) + { + _mappingIndex = (_mappingIndex + 1) % 4; + CurrentMapping = ResolveMapping(_mappingIndex); + _observedOutcomes.Add(CurrentMapping.Outcome); + RenderMapping(); + @event.Clear(); + return; + } + + if (@event.What == TEventKind.Command && @event.Message.Command == CmDescription) + { + ShowWave4Description("Cyrillic", DescriptionText); + @event.Clear(); + return; + } + + base.HandleEvent(@event); + } + + private static CharsetMappingResult ResolveMapping(int index) => index switch + { + 0 => TerminalCharsetMapper.MapByte(0xE1, TerminalCharset.Koi8R), + 1 => TerminalCharsetMapper.MapByte(0xE1, TerminalCharset.Unicode), + // Der ungültige Bereich wird vor dem Byte-Mapper abgelehnt, damit kein stilles Kürzen entsteht. + // The invalid range is rejected before the byte mapper so no silent truncation occurs. + 2 => new CharsetMappingResult( + TerminalCharset.Koi8R, + -1, + '\uFFFD', + CharsetMappingOutcome.Rejected, + "Source value is outside one byte. / Quellwert liegt außerhalb eines Bytes."), + _ => TerminalCharsetMapper.MapByte(0xE1, TerminalCharset.Unknown) + }; + + private void RenderMapping() + { + TGroup desktop = Desktop!; + if (_mappingWindow is TWindow previous && previous.Owner == desktop) + { + desktop.Remove(previous); + } + + TRect region = Wave4Runtime.MainRegion(desktop, 64, 14); + TWindow window = new("Cyrillic", region.A.X, region.A.Y, region.Width, region.Height); + string source = CurrentMapping.SourceValue < 0 ? "invalid" : $"0x{CurrentMapping.SourceValue:X2}"; + string text = + "Cyrillic mapping grid\n" + + $"KOI8-R source: {source}\n" + + $"Unicode glyph: {CurrentMapping.Glyph}\n" + + $"Outcome: {CurrentMapping.Outcome}\n" + + $"Reason: {CurrentMapping.Reason}\n" + + "Proof: fixed-table; no Host-Locale/codepage"; + window.Insert(new TStaticText( + new TRect(2, 2, Math.Max(3, region.Width - 2), Math.Max(3, region.Height - 1)), + text)); + desktop.Insert(window); + desktop.SetFocus(window); + _mappingWindow = window; + SetWave4Visible(window, "TWindow"); + SetWave4Status("Cyrillic", $"fixed-table {CurrentMapping.Outcome} source={source}"); + } +} diff --git a/examples/Cyrillic/Program.cs b/examples/Cyrillic/Program.cs new file mode 100644 index 00000000..21181d93 --- /dev/null +++ b/examples/Cyrillic/Program.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Core; +using TuiVision.Examples.Cyrillic; + +int width; +int height; +try { width = Console.WindowWidth; height = Console.WindowHeight; } +catch { width = 80; height = 25; } +if (width <= 0) width = 80; +if (height <= 0) height = 25; + +new CyrillicApp(new TRect(0, 0, width, height)).Run(); diff --git a/examples/ETerm/ETerm.csproj b/examples/ETerm/ETerm.csproj new file mode 100644 index 00000000..7f56dca4 --- /dev/null +++ b/examples/ETerm/ETerm.csproj @@ -0,0 +1,13 @@ + + + Exe + TuiVision.Examples.ETerm + ETerm + + + + + + + + diff --git a/examples/ETerm/ETermApp.cs b/examples/ETerm/ETermApp.cs new file mode 100644 index 00000000..70eedaae --- /dev/null +++ b/examples/ETerm/ETermApp.cs @@ -0,0 +1,138 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Controls; +using TuiVision.Core; +using TuiVision.Examples.Shared; + +namespace TuiVision.Examples.ETerm; + +/// Unveränderlicher historischer ETerm-Eintrag. / Immutable historical ETerm entry. +public sealed record ETermManifestEntry(string Key, string Value, string Category, string SourceId); + +/// +/// Zeigt ein begrenztes ETerm-Ressourcenmanifest ohne Legacy-Parser oder Hostmutation. +/// Shows a bounded ETerm resource manifest without a legacy parser or host mutation. +/// +public sealed class ETermApp : Wave4Application +{ + /// Wählt den nächsten Eintrag. / Selects the next entry. + public const ushort CmNextEntry = 22301; + + /// Zeigt die Beschreibung. / Shows the description. + public const ushort CmDescription = 22302; + + private readonly bool _requestUnsupported; + private TWindow? _manifestWindow; + + /// Initialisiert die ETerm-Ansicht. / Initializes the ETerm view. + public ETermApp(TRect bounds, bool headless = false, bool requestUnsupported = false) : base(bounds, headless) + { + Entries = + [ + new ETermManifestEntry("Font3", "8x16", "Font", "menus.cfg"), + new ETermManifestEntry("Foreground", "#aaaaaa", "Theme", "theme.cfg"), + new ETermManifestEntry("Version", "ESC[8n", "Presentation", "menus.cfg") + ]; + EnsureUniqueEntries(Entries); + _requestUnsupported = requestUnsupported; + RenderManifest(); + } + + /// Kontrollierte Manifest-Einträge. / Controlled manifest entries. + public IReadOnlyList Entries { get; } + + /// Ausgewählter Index. / Selected index. + public int SelectedIndex { get; private set; } + + /// Ausgewählter Eintrag. / Selected entry. + public ETermManifestEntry SelectedEntry => Entries[SelectedIndex]; + + /// Ob der sichtbare Fallback aktiv ist. / Whether the visible fallback is active. + public bool FallbackVisible { get; private set; } + + /// Der historische Parser wird nie ausgeführt. / The historical parser is never executed. + public bool NativeParserUsed => false; + + /// Host-Theme-Mutation wird nie ausgeführt. / Host theme mutation is never executed. + public bool HostThemeMutationUsed => false; + + /// Explizite Ressourcen- und Ausführungsgrenze. / Explicit resource and execution boundary. + public string BoundaryText => "ETerm uses an immutable manifest; no native parser, script action, spawn, save, exit, or host theme mutation."; + + /// + protected override TMenuBar InitMenuBar(TRect bounds) => new(bounds) + { + Menu = new TMenuItem( + "~E~Term", + 0, + Wave4Runtime.HelpMenu(CmDescription, new TMenuItem("~E~nde / E~x~it", ShellCommandIds.cmQuit)), + new TMenuItem("~N~ächster Eintrag / ~N~ext entry", CmNextEntry)) + }; + + /// + public override void HandleEvent(TEvent @event) + { + if (@event.What == TEventKind.Command && @event.Message.Command == CmNextEntry) + { + SelectedIndex = (SelectedIndex + 1) % Entries.Count; + RenderManifest(); + @event.Clear(); + return; + } + + if (@event.What == TEventKind.Command && @event.Message.Command == CmDescription) + { + ShowWave4Description( + "ETerm", + "ETerm description: Historische Werte werden als unveränderliches Manifest gezeigt; kein Parser oder Host-Theme wird ausgeführt. / " + + "Historical values are shown as an immutable manifest; no parser or host theme is executed."); + @event.Clear(); + return; + } + + base.HandleEvent(@event); + } + + private static void EnsureUniqueEntries(IEnumerable entries) + { + // Exakte Schlüssel verhindern, dass ein Alias oder Duplikat eine nicht belegte Capability vortäuscht. + // Exact keys prevent an alias or duplicate from implying an unproven capability. + if (entries.GroupBy(entry => entry.Key, StringComparer.Ordinal).Any(group => group.Count() != 1)) + { + throw new InvalidOperationException("ETerm manifest keys must be unique and case-sensitive."); + } + } + + private void RenderManifest() + { + TGroup desktop = Desktop!; + if (_manifestWindow is TWindow previous && previous.Owner == desktop) + { + desktop.Remove(previous); + } + + TRect region = Wave4Runtime.MainRegion(desktop, 66, 15); + TWindow window = new("ETerm", region.A.X, region.A.Y, region.Width, region.Height); + string text; + if (_requestUnsupported) + { + FallbackVisible = true; + text = "ETerm Resource Fallback\nUnsupported: native config entry is outside the immutable manifest\nNo parser or host theme action"; + } + else + { + FallbackVisible = false; + text = "ETerm immutable manifest\n" + string.Join( + "\n", + Entries.Select((entry, index) => $"{(index == SelectedIndex ? '>' : ' ')} {entry.Key} = {entry.Value} [{entry.Category}; {entry.SourceId}]")); + } + + window.Insert(new TStaticText(new TRect(2, 2, Math.Max(3, region.Width - 2), Math.Max(3, region.Height - 1)), text)); + desktop.Insert(window); + desktop.SetFocus(window); + _manifestWindow = window; + SetWave4Visible(window, "TWindow"); + SetWave4Status("ETerm", _requestUnsupported ? "Unsupported immutable-manifest boundary" : $"selected={SelectedEntry.Key}"); + } +} diff --git a/examples/ETerm/Program.cs b/examples/ETerm/Program.cs new file mode 100644 index 00000000..7b2e92da --- /dev/null +++ b/examples/ETerm/Program.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Core; +using TuiVision.Examples.ETerm; + +int width; +int height; +try { width = Console.WindowWidth; height = Console.WindowHeight; } +catch { width = 80; height = 25; } +if (width <= 0) width = 80; +if (height <= 0) height = 25; + +new ETermApp(new TRect(0, 0, width, height)).Run(); diff --git a/examples/Fonts/Fixtures/font-8x16.bin b/examples/Fonts/Fixtures/font-8x16.bin new file mode 100644 index 0000000000000000000000000000000000000000..e2bbe998ea59578809866615106cdf48fe44ff38 GIT binary patch literal 4096 zcmZWsL2KMb6duQB*9YOa5QrcmUP8z*hz1eniYGl`rT`Hs{ zgm!N|^pfP5Q}HpsU`b$`!{TFEShr15urECr3eFzFZofA(Pa19BdbRVt@6CH}-aJWT zA+lFbpZ<4pGCeuTgqDX#0)JXI$HU>#a5x-qvPNuM7erIXs96)+orFCOIX7Ql z$ye(0b6U{~lQd70u%+>$v-Sg(OVvvVIsWpx!&nGwX?o6LcldQ%)5K+r>f4ZF1E9Ye zO{1%t|2TiqnZ#1q!_2QkA_RS}>E!R;y-PKAoaJRbls}g3k=VRK+gc*AbVCVYk1{_S6_8SMa+#y)#&OCS zBF{~V#)kdeyiBmAO@b{YYQKf>VJfBM&pC~Dn_UL&cKvd_-DdmC*YDi9lOtnopSN3C zyWd0ATq4P+ zp#sAH++3E3Bw_sh?(SZaaQ|X`?VJ);w8)T~{Avuo)oPXNk&AAscDH-$E$f3rlO{M>Qy*12dVP5r2;sxL3}n z^0b>}{Cvsrl3eptPZwVukEXIn3m^B&Jl`+LFSXJwjUo|2e7rG={+SvU^eQ1SR5V;+ ztXSJF6pCQ=N54F8Wrks_`7ztES)tGb=g2%`o;PMf|1xHNI>R$&9w!DlRBy&OPD=u# zJj^v-m2%T&uWv3|EbPS2<7H|ftA^$c2@C! zR$#?X#rq2XzQC0*5@7F9E8ph>9h6q{3rJJGpuKn_JPjeoobt^3{}q}m!$#I&}o$i3XR2rA`(X;2zpFew~cfgP1pg3x2vm4PMx?xNaIi5i#7DoBf zM=uUu92~GeWoGbDIM=Q@#&p|8cW^@_Pu%de(T!=%uhn#JGzUi=W2ze7f&{J`DwK~# zqaQCp3_D#5LxU1o`@pt`Lir`(#jz)hc*J1!(LkH@dN2C}+l>yNHEZ-l&^SE&wPvjV zzj5!a{;PTUC~#eY)O!8CbQ-$Ci~IdaA3`2+rl{bwzg)rizo3GJSYO8*T^<-{zK3kt zfH0o5@nA3+Kn@l5qnAA-8Tl5K(Y3`Sy7yrJ!M#ZC2@Z}>glQNm?8Q|c{Zg^Ve0r9V z$f6;7Q) zp?`eT>tT$ugV?uH?9W5Z!-etw$M#owtgvc-|GsHY4N&EU*t~ry8y*$!H$TGeV*BNW zQT((&>Gf27kpsmq&kq#8!lO@3O!Gb3{^F*oS?2k{Mh8^*Xa-wWX8GvRV*S(tvZAsc zDnF{Z+<)lc-#9<0{0zS60Jg^l8TRB9cmI?wEa~B2XUEz<#X~YiK8PpNQT^lY4`z67 e0L9buh_dPTvzkRM{U%fmfDVu?fch2uzW5)e#~U&L literal 0 HcmV?d00001 diff --git a/examples/Fonts/Fonts.csproj b/examples/Fonts/Fonts.csproj new file mode 100644 index 00000000..c95f6e91 --- /dev/null +++ b/examples/Fonts/Fonts.csproj @@ -0,0 +1,14 @@ + + + Exe + TuiVision.Examples.Fonts + Fonts + + + + + + + + + diff --git a/examples/Fonts/FontsApp.cs b/examples/Fonts/FontsApp.cs new file mode 100644 index 00000000..87645bc5 --- /dev/null +++ b/examples/Fonts/FontsApp.cs @@ -0,0 +1,182 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using System.Text; +using TuiVision.Controls; +using TuiVision.Core; +using TuiVision.Drivers.Console; +using TuiVision.Examples.Shared; + +namespace TuiVision.Examples.Fonts; + +/// Kontrollierte Font-Fixture-Szenarien. / Controlled font-fixture scenarios. +public enum FontFixtureScenario +{ + /// Gültige Projekt-Fixture. / Valid project fixture. + Valid, + /// Falsche Datenlänge. / Wrong data length. + WrongLength, + /// Falsche Geometrie. / Wrong geometry. + WrongGeometry, + /// Falscher Stride. / Wrong stride. + WrongStride, + /// Nicht unterstütztes Format. / Unsupported format. + UnsupportedFormat, + /// Unzulässige Quellenkennung. / Invalid source identifier. + InvalidSource, + /// Formal gültige, aber leere ausgewählte Glyphe. / Formally valid but blank selected glyph. + BlankSelectedGlyph +} + +/// +/// Zeigt eine kontrollierte rohe 8x16-Font-Fixture ohne Host-Fontzugriff. +/// Shows a controlled raw 8x16 font fixture without host-font access. +/// +public sealed class FontsApp : Wave4Application +{ + /// Wählt die nächste Glyphe. / Selects the next glyph. + public const ushort CmNextGlyph = 22201; + + /// Zeigt die Beschreibung. / Shows the description. + public const ushort CmDescription = 22202; + + private readonly FontFixtureScenario _scenario; + private TWindow? _fontWindow; + + /// Initialisiert die Fontdemo. / Initializes the font demo. + public FontsApp(TRect bounds, bool headless = false, FontFixtureScenario scenario = FontFixtureScenario.Valid) : base(bounds, headless) + { + _scenario = scenario; + SelectedGlyph = 65; + FixtureResult = CreateFixture(scenario); + RenderFixture(); + } + + /// Validierungsergebnis der Fixture. / Fixture validation result. + public BitmapFontFixtureResult FixtureResult { get; } + + /// Ausgewählte Glyphe. / Selected glyph. + public int SelectedGlyph { get; private set; } + + /// Ob die ausgewählte Glyphe sichtbare Pixel enthält. / Whether the selected glyph contains visible pixels. + public bool SelectedGlyphHasInk { get; private set; } + + /// Ob ein sichtbarer Fallback nötig ist. / Whether a visible fallback is required. + public bool FallbackVisible { get; private set; } + + /// Text des Beschreibungspfads. / Description-path text. + public string DescriptionText => + "Fonts description: Die kopierte 8x16-Rohfixture wird nur gelesen und als Textmatrix gezeigt; kein Font wird installiert. / " + + "The copied raw 8x16 fixture is read only and shown as a text matrix; no font is installed."; + + /// + protected override TMenuBar InitMenuBar(TRect bounds) => new(bounds) + { + Menu = new TMenuItem( + "~F~onts", + 0, + Wave4Runtime.HelpMenu(CmDescription, new TMenuItem("~E~nde / E~x~it", ShellCommandIds.cmQuit)), + new TMenuItem("~N~ächste Glyphe / ~N~ext glyph", CmNextGlyph)) + }; + + /// + public override void HandleEvent(TEvent @event) + { + if (@event.What == TEventKind.Command && @event.Message.Command == CmNextGlyph) + { + SelectedGlyph = (SelectedGlyph + 1) % BitmapFontFixture.RequiredGlyphCount; + RenderFixture(); + @event.Clear(); + return; + } + + if (@event.What == TEventKind.Command && @event.Message.Command == CmDescription) + { + ShowWave4Description("Fonts", DescriptionText); + @event.Clear(); + return; + } + + base.HandleEvent(@event); + } + + private static BitmapFontFixtureResult CreateFixture(FontFixtureScenario scenario) + { + byte[] data = scenario == FontFixtureScenario.BlankSelectedGlyph + ? new byte[BitmapFontFixture.RequiredDataLength] + : File.ReadAllBytes(Path.Combine(AppContext.BaseDirectory, "Fixtures", "font-8x16.bin")); + int width = scenario == FontFixtureScenario.WrongGeometry ? 9 : BitmapFontFixture.RequiredWidth; + int stride = scenario == FontFixtureScenario.WrongStride ? 15 : BitmapFontFixture.RequiredBytesPerGlyph; + string format = scenario == FontFixtureScenario.UnsupportedFormat ? "psf" : "raw"; + string source = scenario == FontFixtureScenario.InvalidSource ? "../font-8x16.bin" : "Fixtures/font-8x16.bin"; + ReadOnlySpan candidate = scenario == FontFixtureScenario.WrongLength ? data.AsSpan(0, data.Length - 1) : data; + + // Alle Metadaten gehen gemeinsam durch den Framework-Vertrag; die App veröffentlicht nie Teilzustände. + // All metadata passes through the framework contract together; the app never publishes partial state. + return BitmapFontFixture.TryCreate( + candidate, + width, + BitmapFontFixture.RequiredHeight, + BitmapFontFixture.RequiredGlyphCount, + stride, + format, + source); + } + + private void RenderFixture() + { + TGroup desktop = Desktop!; + if (_fontWindow is TWindow previous && previous.Owner == desktop) + { + desktop.Remove(previous); + } + + TRect region = Wave4Runtime.MainRegion(desktop, 58, 22); + TWindow window = new("Fonts", region.A.X, region.A.Y, region.Width, region.Height); + string text = BuildFixtureText(); + window.Insert(new TStaticText( + new TRect(2, 2, Math.Max(3, region.Width - 2), Math.Max(3, region.Height - 1)), + text)); + desktop.Insert(window); + desktop.SetFocus(window); + _fontWindow = window; + SetWave4Visible(window, "TWindow"); + SetWave4Status("Fonts", $"{FixtureResult.Outcome} scenario={_scenario} glyph={SelectedGlyph}"); + } + + private string BuildFixtureText() + { + if (FixtureResult.Fixture is null) + { + FallbackVisible = true; + SelectedGlyphHasInk = false; + return $"Fonts Fixture Fallback\nScenario: {_scenario}\nOutcome: {FixtureResult.Outcome}\n{FixtureResult.Reason}"; + } + + ReadOnlySpan rows = FixtureResult.Fixture.GetGlyphRows(SelectedGlyph).Span; + SelectedGlyphHasInk = rows.ContainsAnyExcept((byte)0); + if (!SelectedGlyphHasInk) + { + FallbackVisible = true; + return $"Fonts Fixture Fallback\nScenario: {_scenario}\nGlyph {SelectedGlyph} is blank\nOutcome: {FixtureResult.Outcome}"; + } + + FallbackVisible = false; + StringBuilder text = new(); + text.AppendLine("Fonts raw 8x16 fixture"); + text.AppendLine($"Glyph {SelectedGlyph} | 8x16 | 256 glyphs | 4096 bytes"); + // MSB links zu zeichnen macht Bitreihenfolge und Zell-Proof ohne Farbe sichtbar. + // Drawing the MSB on the left makes bit order and cell proof visible without color. + foreach (byte row in rows) + { + for (int bit = 7; bit >= 0; bit--) + { + text.Append((row & (1 << bit)) == 0 ? '.' : '#'); + } + + text.AppendLine(); + } + + return text.ToString(); + } +} diff --git a/examples/Fonts/Program.cs b/examples/Fonts/Program.cs new file mode 100644 index 00000000..5591640b --- /dev/null +++ b/examples/Fonts/Program.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Core; +using TuiVision.Examples.Fonts; + +int width; +int height; +try { width = Console.WindowWidth; height = Console.WindowHeight; } +catch { width = 80; height = 25; } +if (width <= 0) width = 80; +if (height <= 0) height = 25; + +new FontsApp(new TRect(0, 0, width, height)).Run(); diff --git a/examples/README.md b/examples/README.md index 61f3f9d7..a072ca7b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -115,6 +115,27 @@ smoke with state, view, and buffer proof. --- +## Wave-4-Beispiele / Wave 4 Examples + +Feature `022-wave4-visual-component-porting` macht Terminal-, Charset-, Font- +und Resource-Zustände sichtbar. Die Beispiele verwenden die kontrollierten +Verträge aus Feature 021 und verändern keine Host-Terminal-, Font-, Codepage- +oder Keyboard-Einstellung. + +Feature `022-wave4-visual-component-porting` makes terminal, charset, font, and +resource states visible. The examples use the controlled Feature-021 contracts +and do not change host terminal, font, codepage, or keyboard settings. + +| Verwalteter Name / Managed Name | Startbefehl / Launch Command | Sichtbarer Pfad und Grenze / Visible Path and Boundary | +|---|---|---| +| `Terminal` | `dotnet run --project examples/Terminal` | Echte `TTerminalView`, kontrollierte Eingabe, Cursor, Ablehnung/Recovery und `Unsupported`-Fallback; kein Prozess, keine Shell und kein PTY. | +| `Cyrillic` | `dotnet run --project examples/Cyrillic` | Beschriftete KOI8-R-/Unicode-Zellen und vier Mappingzustände über feste Framework-Tabelle; keine Host-Locale- oder Codepage-Änderung. | +| `Fonts` | `dotnet run --project examples/Fonts` | Projektkontrollierte rohe 8x16-Fixture, Metadaten, Glyphenraster und sichtbare Fallbackklassen; keine Fontinstallation oder Generatorausführung. | +| `ETerm` | `dotnet run --project examples/ETerm` | Unveränderliches Manifest aus Menü-, Theme- und Präsentationswerten mit `Unsupported`-Fallback; kein Legacy-Parser, Spawn, Save oder Host-Theme. | +| `XTerm` | `dotnet run --project examples/XTerm` | Unveränderliches Ressourcen-/Sequenzmanifest mit nativer Resource-Fallbackgrenze; keine X-Datenbank, terminfo-Auswertung oder externe Kommandos. | + +--- + ## Didaktische Begleitdokumentation / Didactic companion documentation Die Anleitungen für diese Beispiele befinden sich unter `docs/guides/examples/`: @@ -141,6 +162,11 @@ Guides for these examples are located under `docs/guides/examples/`: - `docs/guides/examples/helpdemo.md` - `docs/guides/examples/i18n.md` - `docs/guides/examples/tvhc.md` +- `docs/guides/examples/terminal.md` +- `docs/guides/examples/cyrillic.md` +- `docs/guides/examples/fonts.md` +- `docs/guides/examples/eterm.md` +- `docs/guides/examples/xterm.md` --- diff --git a/examples/Shared/Wave4Runtime.cs b/examples/Shared/Wave4Runtime.cs new file mode 100644 index 00000000..0743e492 --- /dev/null +++ b/examples/Shared/Wave4Runtime.cs @@ -0,0 +1,172 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Controls; +using TuiVision.Core; + +namespace TuiVision.Examples.Shared; + +/// +/// Zeichnet den kurzen Laufzeitzustand der Wave-4-Beispiele. +/// Draws the short runtime state of the Wave-4 examples. +/// +internal sealed class Wave4StatusLine : TStatusLine +{ + public Wave4StatusLine(TRect bounds, string message) : base(bounds) => Message = message; + + public string Message { get; private set; } + + public void SetMessage(string message) + { + Message = message ?? string.Empty; + DrawView(); + } + + public override void Draw() + { + TConsoleBuffer? buffer = GetDrawBuffer(); + if (buffer is null || Size.X <= 0) + { + return; + } + + // Explizite Zellen halten Status und Fallback auch ohne Host-Screenshot beweisbar. + // Explicit cells keep status and fallback provable without a host screenshot. + for (int x = 0; x < Size.X; x++) + { + buffer.TrySetCell(Origin.X + x, Origin.Y, new TConsoleCell(' ', ConsoleColor.Black, ConsoleColor.Cyan)); + } + + string text = Message.Length <= Size.X ? Message : Message[..Size.X]; + buffer.WriteText(Origin.X, Origin.Y, text.AsSpan(), ConsoleColor.Yellow, ConsoleColor.Cyan); + } +} + +/// +/// Gemeinsame Präsentations- und Smoke-Orchestrierung der Wave-4-Beispiele. +/// Terminal-, Mapping-, Font-, Profil- und Hostlogik bleibt in den +/// Feature-021-Framework-Komponenten. +/// +/// Shared presentation and smoke orchestration for Wave-4 examples. Terminal, +/// mapping, font, profile, and host logic remains in Feature-021 framework components. +/// +public abstract class Wave4Application : TApplication +{ + private readonly bool _headless; + private readonly Queue _scriptedEvents = []; + private TView? _descriptionView; + + /// Initialisiert die gemeinsame Anwendungsshell. / Initializes the shared application shell. + protected Wave4Application(TRect bounds, bool headless) : base(bounds) => _headless = headless; + + /// Letzter sichtbarer Haupttyp. / Last visible main view kind. + public string LastVisibleComponentKind { get; protected set; } = string.Empty; + + /// Letzte stabile Proof-Region. / Last stable proof region. + public TRect LastVisibleRegion { get; protected set; } + + /// Letzter sichtbarer Status. / Last visible status. + public string LastStatusMessage { get; protected set; } = string.Empty; + + /// Ob der Headless-App-Loop den kontrollierten Quit erzeugt hat. / Whether the headless app loop issued controlled quit. + public bool QuitIssued { get; private set; } + + /// Fügt deterministische App-Loop-Ereignisse hinzu. / Adds deterministic app-loop events. + public void QueueEvents(IEnumerable events) + { + foreach (TEvent @event in events) + { + _scriptedEvents.Enqueue(@event); + } + } + + /// + public override void GetEvent(out TEvent @event) + { + if (_headless && _scriptedEvents.Count > 0) + { + @event = _scriptedEvents.Dequeue(); + return; + } + + if (_headless && !QuitIssued) + { + QuitIssued = true; + @event = TEvent.CreateCommand(ShellCommandIds.cmQuit); + return; + } + + base.GetEvent(out @event); + } + + /// + protected override TStatusLine InitStatusLine(TRect bounds) => + new Wave4StatusLine(bounds, "Wave 4: ready | Help -> Description | ^Q Quit"); + + /// Aktualisiert den sichtbaren kurzen Zustand. / Updates the visible short state. + protected void SetWave4Status(string example, string state) + { + LastStatusMessage = $"{example}: {state} | Help -> Description | ^Q Quit"; + if (StatusLine is Wave4StatusLine status) + { + status.SetMessage(LastStatusMessage); + } + } + + /// Merkt eine konkrete Hauptansicht als Proof-Ziel. / Records a concrete main view as proof target. + protected void SetWave4Visible(TView view, string kind) + { + LastVisibleComponentKind = kind; + LastVisibleRegion = Desktop is null ? default : Wave4Runtime.ScreenRegion(Desktop, view); + } + + /// Zeigt eine zweisprachige Beschreibung. / Shows a bilingual description. + protected void ShowWave4Description(string example, string body) + { + if (Desktop is null) + { + return; + } + + if (_descriptionView?.Owner == Desktop) + { + Desktop.Remove(_descriptionView); + } + + TRect region = Wave4Runtime.MainRegion(Desktop, 66, 13); + TWindow window = new($"{example} Description", region.A.X, region.A.Y, region.Width, region.Height); + window.Insert(new TStaticText(new TRect(2, 2, Math.Max(3, region.Width - 2), Math.Max(3, region.Height - 1)), body)); + Desktop.Insert(window); + Desktop.SetFocus(window); + _descriptionView = window; + SetWave4Visible(window, "TWindow"); + SetWave4Status(example, "Description visible"); + } +} + +/// Hilfen für wiederholte Wave-4-Präsentation. / Helpers for repeated Wave-4 presentation. +public static class Wave4Runtime +{ + /// Erstellt das gemeinsame Hilfemenü. / Creates the shared Help menu. + public static TMenuItem HelpMenu(ushort descriptionCommand, TMenuItem? next = null) => + new("~H~ilfe / ~H~elp", 0, next, new TMenuItem("~B~eschreibung / ~D~escription", descriptionCommand)); + + /// Ermittelt eine begrenzte Hauptregion. / Resolves a bounded main region. + public static TRect MainRegion(TGroup desktop, int width, int height) + { + int actualWidth = Math.Clamp(width, 12, Math.Max(12, desktop.Size.X - 4)); + int actualHeight = Math.Clamp(height, 6, Math.Max(6, desktop.Size.Y - 3)); + return new TRect(2, 1, 2 + actualWidth, 1 + actualHeight); + } + + /// Konvertiert lokale View-Bounds in Bildschirmkoordinaten. / Converts local view bounds to screen coordinates. + public static TRect ScreenRegion(TGroup owner, TView view) + { + TRect bounds = view.GetBounds(); + return new TRect( + owner.Origin.X + bounds.A.X, + owner.Origin.Y + bounds.A.Y, + owner.Origin.X + bounds.B.X, + owner.Origin.Y + bounds.B.Y); + } +} diff --git a/examples/Terminal/Program.cs b/examples/Terminal/Program.cs new file mode 100644 index 00000000..baeb4255 --- /dev/null +++ b/examples/Terminal/Program.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Core; +using TuiVision.Examples.Terminal; + +int width; +int height; +try { width = Console.WindowWidth; height = Console.WindowHeight; } +catch { width = 80; height = 25; } +if (width <= 0) width = 80; +if (height <= 0) height = 25; + +new TerminalApp(new TRect(0, 0, width, height)).Run(); diff --git a/examples/Terminal/Terminal.csproj b/examples/Terminal/Terminal.csproj new file mode 100644 index 00000000..e6ab6fd8 --- /dev/null +++ b/examples/Terminal/Terminal.csproj @@ -0,0 +1,13 @@ + + + Exe + TuiVision.Examples.Terminal + Terminal + + + + + + + + diff --git a/examples/Terminal/TerminalApp.cs b/examples/Terminal/TerminalApp.cs new file mode 100644 index 00000000..f2fea84c --- /dev/null +++ b/examples/Terminal/TerminalApp.cs @@ -0,0 +1,141 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Controls; +using TuiVision.Core; +using TuiVision.Drivers.Console; +using TuiVision.Examples.Shared; + +namespace TuiVision.Examples.Terminal; + +/// +/// Sichtbare Wave-4-Terminaldemo auf Basis der kontrollierten Feature-021-Sitzung. +/// Visible Wave-4 terminal demo based on the controlled Feature-021 session. +/// +public sealed class TerminalApp : Wave4Application +{ + /// Schreibt die sichtbare Probe. / Writes the visible sample. + public const ushort CmWriteSample = 22001; + /// Führt eine unterstützte Cursoraktion aus. / Performs a supported cursor action. + public const ushort CmCursorAction = 22002; + /// Führt eine atomar abgelehnte Aktion aus. / Performs an atomically rejected action. + public const ushort CmRejectedAction = 22003; + /// Schreibt die unabhängige Recovery-Eingabe. / Writes independent recovery input. + public const ushort CmWriteRecovery = 22004; + /// Setzt die Sitzung kontrolliert zurück. / Resets the session in a controlled manner. + public const ushort CmReset = 22005; + /// Zeigt die Beschreibung. / Shows the description. + public const ushort CmDescription = 22006; + + private readonly bool _capabilityAvailable; + private readonly TerminalProfile _profile; + + /// Initialisiert die kontrollierte Terminaldemo. / Initializes the controlled terminal demo. + public TerminalApp(TRect bounds, bool headless = false, bool capabilityAvailable = true) : base(bounds, headless) + { + _capabilityAvailable = capabilityAvailable; + TerminalProfileParseResult parsed = TerminalProfile.Parse( + "{\"ProfileId\":\"wave4-terminal\",\"Charset\":\"Unicode\",\"FontId\":\"built-in-8x16\",\"Foreground\":\"Gray\",\"Background\":\"Black\"}", + [TerminalProfile.BuiltInFontId], + capabilityAvailable); + _profile = parsed.Profile ?? throw new InvalidOperationException(parsed.Reason); + + TRect region = Wave4Runtime.MainRegion(Desktop!, 70, 18); + Session = new TerminalSession(Math.Max(1, region.Width), Math.Max(1, region.Height - 1)); + Session.ApplyProfile(_profile); + LastOutcome = Session.Write("Wave4 terminal ready").Outcome; + TerminalView = new TTerminalView(region, Session); + Desktop!.Insert(TerminalView); + Desktop.SetFocus(TerminalView); + SetWave4Visible(TerminalView, "TTerminalView"); + UpdateStatus(capabilityAvailable ? "ready" : "fallback Unsupported"); + } + + /// Kontrollierte Driver-Sitzung. / Controlled Driver session. + public TerminalSession Session { get; } + + /// Sichtbare Terminalprojektion. / Visible terminal projection. + public TTerminalView TerminalView { get; } + + /// Letztes Emulationsergebnis. / Last emulation outcome. + public TerminalEmulationOutcome LastOutcome { get; private set; } + + /// Ob eine Ablehnung vor der Recovery beobachtet wurde. / Whether a rejection was observed before recovery. + public bool RejectionObserved { get; private set; } + + /// Text des Beschreibungspfads. / Description-path text. + public string DescriptionText => + "Terminal description: Die Demo nutzt eine kontrollierte In-Process-Sitzung; kein Prozess, keine Shell und kein PTY werden gestartet. / " + + "The demo uses a controlled in-process session; no process, shell, or PTY is started."; + + /// + protected override TMenuBar InitMenuBar(TRect bounds) + { + TMenuBar bar = new(bounds) + { + Menu = new TMenuItem( + "~T~erminal", + 0, + Wave4Runtime.HelpMenu(CmDescription, new TMenuItem("~E~nde / E~x~it", ShellCommandIds.cmQuit)), + new TMenuItem("~P~robe / ~S~ample", CmWriteSample)) + }; + return bar; + } + + /// + public override void HandleEvent(TEvent @event) + { + if (@event.What == TEventKind.Command) + { + switch (@event.Message.Command) + { + case CmWriteSample: + Apply(Session.Write(" Wave4 sample"), "sample accepted"); + break; + case CmCursorAction: + Apply(Session.Write("\u001b[2;4H"), "cursor action accepted"); + break; + case CmRejectedAction: + TerminalEmulationResult rejected = Session.Write("\u001b[10000A"); + LastOutcome = rejected.Outcome; + RejectionObserved = rejected.Outcome == TerminalEmulationOutcome.Rejected; + UpdateStatus("rejected atomically"); + break; + case CmWriteRecovery: + Apply(Session.Write(" recovered"), "recovery accepted"); + break; + case CmReset: + Session.Reset(); + Session.ApplyProfile(_profile); + Apply(Session.Write(_capabilityAvailable ? "Wave4 reset" : "Unsupported fallback"), + _capabilityAvailable ? "reset" : "fallback Unsupported"); + break; + case CmDescription: + ShowWave4Description("Terminal", DescriptionText); + @event.Clear(); + return; + default: + base.HandleEvent(@event); + return; + } + + TerminalView.DrawView(); + SetWave4Visible(TerminalView, "TTerminalView"); + @event.Clear(); + return; + } + + base.HandleEvent(@event); + } + + private void Apply(TerminalEmulationResult result, string state) + { + LastOutcome = result.Outcome; + UpdateStatus(state); + } + + private void UpdateStatus(string state) => + SetWave4Status( + "Terminal", + $"{state} capability={Session.PresentationCapability} cursor={Session.Cursor.X},{Session.Cursor.Y} outcome={LastOutcome}"); +} diff --git a/examples/XTerm/Program.cs b/examples/XTerm/Program.cs new file mode 100644 index 00000000..6ae131ce --- /dev/null +++ b/examples/XTerm/Program.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Core; +using TuiVision.Examples.XTerm; + +int width; +int height; +try { width = Console.WindowWidth; height = Console.WindowHeight; } +catch { width = 80; height = 25; } +if (width <= 0) width = 80; +if (height <= 0) height = 25; + +new XTermApp(new TRect(0, 0, width, height)).Run(); diff --git a/examples/XTerm/XTerm.csproj b/examples/XTerm/XTerm.csproj new file mode 100644 index 00000000..982bc62c --- /dev/null +++ b/examples/XTerm/XTerm.csproj @@ -0,0 +1,13 @@ + + + Exe + TuiVision.Examples.XTerm + XTerm + + + + + + + + diff --git a/examples/XTerm/XTermApp.cs b/examples/XTerm/XTermApp.cs new file mode 100644 index 00000000..178dca45 --- /dev/null +++ b/examples/XTerm/XTermApp.cs @@ -0,0 +1,138 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Controls; +using TuiVision.Core; +using TuiVision.Examples.Shared; + +namespace TuiVision.Examples.XTerm; + +/// Unveränderlicher historischer XTerm-Eintrag. / Immutable historical XTerm entry. +public sealed record XTermManifestEntry(string Key, string Value, string Category, string SourceId); + +/// +/// Zeigt ein begrenztes XTerm-Ressourcenmanifest ohne X-Resource-Ausführung. +/// Shows a bounded XTerm resource manifest without X resource execution. +/// +public sealed class XTermApp : Wave4Application +{ + /// Wählt den nächsten Eintrag. / Selects the next entry. + public const ushort CmNextEntry = 22401; + + /// Zeigt die Beschreibung. / Shows the description. + public const ushort CmDescription = 22402; + + private readonly bool _requestUnsupported; + private TWindow? _manifestWindow; + + /// Initialisiert die XTerm-Ansicht. / Initializes the XTerm view. + public XTermApp(TRect bounds, bool headless = false, bool requestUnsupported = false) : base(bounds, headless) + { + Entries = + [ + new XTermManifestEntry("Insert", "ESC[2~", "Sequence", "Xterm.res"), + new XTermManifestEntry("Color1", "#a80000", "Resource", "Xterm.res"), + new XTermManifestEntry("metaSendsEscape", "true", "Capability", "Xterm.res") + ]; + EnsureUniqueEntries(Entries); + _requestUnsupported = requestUnsupported; + RenderManifest(); + } + + /// Kontrollierte Manifest-Einträge. / Controlled manifest entries. + public IReadOnlyList Entries { get; } + + /// Ausgewählter Index. / Selected index. + public int SelectedIndex { get; private set; } + + /// Ausgewählter Eintrag. / Selected entry. + public XTermManifestEntry SelectedEntry => Entries[SelectedIndex]; + + /// Ob der sichtbare Fallback aktiv ist. / Whether the visible fallback is active. + public bool FallbackVisible { get; private set; } + + /// Die X-Resource-Datenbank wird nie verwendet. / The X resource database is never used. + public bool XResourceDatabaseUsed => false; + + /// Externe Kommandos werden nie ausgeführt. / External commands are never executed. + public bool ExternalCommandUsed => false; + + /// Explizite Wiederverwendungsgrenze. / Explicit reuse boundary. + public string BoundaryText => "Compatibility input and the Feature-021 session parser remain the only runtime parsing boundaries."; + + /// + protected override TMenuBar InitMenuBar(TRect bounds) => new(bounds) + { + Menu = new TMenuItem( + "~X~Term", + 0, + Wave4Runtime.HelpMenu(CmDescription, new TMenuItem("~E~nde / E~x~it", ShellCommandIds.cmQuit)), + new TMenuItem("~N~ächster Eintrag / ~N~ext entry", CmNextEntry)) + }; + + /// + public override void HandleEvent(TEvent @event) + { + if (@event.What == TEventKind.Command && @event.Message.Command == CmNextEntry) + { + SelectedIndex = (SelectedIndex + 1) % Entries.Count; + RenderManifest(); + @event.Clear(); + return; + } + + if (@event.What == TEventKind.Command && @event.Message.Command == CmDescription) + { + ShowWave4Description( + "XTerm", + "XTerm description: Ressourcen und Sequenzen sind ein unveränderliches Manifest; keine X-Datenbank oder Hostmutation wird ausgeführt. / " + + "Resources and sequences are an immutable manifest; no X database or host mutation is executed."); + @event.Clear(); + return; + } + + base.HandleEvent(@event); + } + + private static void EnsureUniqueEntries(IEnumerable entries) + { + // Nur exakte belegte Schlüssel werden veröffentlicht; native X-Ressourcen bleiben außerhalb des Subsets. + // Only exact evidenced keys are published; native X resources remain outside the subset. + if (entries.GroupBy(entry => entry.Key, StringComparer.Ordinal).Any(group => group.Count() != 1)) + { + throw new InvalidOperationException("XTerm manifest keys must be unique and case-sensitive."); + } + } + + private void RenderManifest() + { + TGroup desktop = Desktop!; + if (_manifestWindow is TWindow previous && previous.Owner == desktop) + { + desktop.Remove(previous); + } + + TRect region = Wave4Runtime.MainRegion(desktop, 66, 15); + TWindow window = new("XTerm", region.A.X, region.A.Y, region.Width, region.Height); + string text; + if (_requestUnsupported) + { + FallbackVisible = true; + text = "XTerm Resource Fallback\nUnsupported: native X resource is outside the immutable manifest\nNo X database, terminfo, or external command"; + } + else + { + FallbackVisible = false; + text = "XTerm immutable manifest\n" + string.Join( + "\n", + Entries.Select((entry, index) => $"{(index == SelectedIndex ? '>' : ' ')} {entry.Key} = {entry.Value} [{entry.Category}; {entry.SourceId}]")); + } + + window.Insert(new TStaticText(new TRect(2, 2, Math.Max(3, region.Width - 2), Math.Max(3, region.Height - 1)), text)); + desktop.Insert(window); + desktop.SetFocus(window); + _manifestWindow = window; + SetWave4Visible(window, "TWindow"); + SetWave4Status("XTerm", _requestUnsupported ? "Unsupported native-resource boundary" : $"selected={SelectedEntry.Key}"); + } +} diff --git a/specs/022-wave4-visual-component-porting/checklists/domain-acceptance.md b/specs/022-wave4-visual-component-porting/checklists/domain-acceptance.md new file mode 100644 index 00000000..47d2b522 --- /dev/null +++ b/specs/022-wave4-visual-component-porting/checklists/domain-acceptance.md @@ -0,0 +1,39 @@ +# Domain Acceptance Checklist: Wave-4 Visual Component Porting + +**Purpose**: Verify that the accepted Wave-4 visual, host, historical, safety, +and proof contract is complete before planning. +**Created**: 2026-07-12 +**Feature**: [spec.md](../spec.md) + +## Example and Visual Contract + +- [X] The scope names exactly `Cyrillic`, `ETerm`, `Fonts`, `Terminal`, and `XTerm`. +- [X] Feature 021 is the binding reusable session/mapping/font/profile/view baseline. +- [X] Every example requires main composition, dynamic status, and keyboard-reachable description. +- [X] Every primary smoke requires app-loop, concrete state, exact view, and rendered-cell proof. + +## Domain Boundaries + +- [X] `Terminal` forbids process, shell, PTY, and host mutation while proving accepted/rejected recovery. +- [X] `Cyrillic` covers direct, replacement, invalid, and unsupported host-independent mapping. +- [X] `Fonts` requires exact fixture validation, recognizable 8x16 raster, and invalid fallback. +- [X] `ETerm` and `XTerm` use immutable representative manifests and explicitly reject general legacy-parser claims. + +## Evidence and Safety + +- [X] Deterministic, remote, and physical host evidence classes remain separate; unavailable physical proof is `NotRun`. +- [X] Relevant implementations, headers, configs, resources, scripts, and fixtures under `tv203s/` are read-only evidence. +- [X] Every example receives exactly one accepted framework decision with follow-up boundaries. +- [X] Assets are controlled/read-only and arbitrary user data plus persistent writes are excluded. + +## A11Y, Governance, and Delivery + +- [X] Keyboard access, text-first status/fallback, non-color-only state, and bilingual CEFR-B2 guidance are acceptance criteria. +- [X] All six preset applicability paths and complete governance-row fields are explicit. +- [X] Statistics, Pflichtenheft, five agent surfaces, guides, indexes, archive, and generated-output hygiene are included. +- [X] Remote tasks require exact evidence paths and one pre-named causal closeout path. + +## Notes + +- Review pass 1: 16/16 complete. +- No domain ambiguity remains that would materially change planning or validation. diff --git a/specs/022-wave4-visual-component-porting/checklists/plan-quality.md b/specs/022-wave4-visual-component-porting/checklists/plan-quality.md new file mode 100644 index 00000000..5191cd1d --- /dev/null +++ b/specs/022-wave4-visual-component-porting/checklists/plan-quality.md @@ -0,0 +1,37 @@ +# Plan Quality Checklist: Wave-4 Visual Component Porting + +**Purpose**: Validate plan completeness before task generation +**Created**: 2026-07-12 +**Plan**: [plan.md](../plan.md) + +## Architecture and Scope + +- [X] Existing Feature-021 Driver/Core/Controls ownership is reused without a competing parser or view. +- [X] Five projects and the presentation-only linked helper have explicit ownership. +- [X] Linked-source CLR identity is addressed before cross-project proof. +- [X] Full emulation, host mutation, arbitrary data, dependencies, and Feature 023 remain excluded. + +## Test-First and Evidence + +- [X] `Terminal` is a complete app-loop/state/view/cell vertical slice before pattern spread. +- [X] Project-local negative matrices preserve each failure boundary and repair owner. +- [X] Evidence, framework, host, governance, validation, and causal-closeout tables precede implementation. +- [X] Deterministic, remote, and physical host evidence classes remain separate. + +## Assets, Safety, and A11Y + +- [X] The font fixture is exact, controlled, read-only, and rendered visibly without generator/host install. +- [X] ETerm/XTerm manifests are immutable and cannot imply general legacy parsing. +- [X] Keyboard, text status/fallback, non-color-only state, narrow viewport, and description proof are planned. +- [X] Historical implementations, headers, configs, resources, scripts, and fixture metadata stay read-only. + +## Governance and Delivery + +- [X] All six preset applicability paths and trigger-based N/A decisions are planned. +- [X] Build-counter/version, targeted/full/coverage, DocFX/A11Y/lynx, secret, and hygiene gates are explicit. +- [X] Pflichtenheft, five agent surfaces, guides, statistics, archive, and next-intake routing are included. +- [X] Every remote task will name `pr-evidence.md` or the single pre-named `closeout-evidence.md` path. + +## Notes + +- Review pass 1: 16/16 complete. diff --git a/specs/022-wave4-visual-component-porting/checklists/plan-review.md b/specs/022-wave4-visual-component-porting/checklists/plan-review.md new file mode 100644 index 00000000..3317dc13 --- /dev/null +++ b/specs/022-wave4-visual-component-porting/checklists/plan-review.md @@ -0,0 +1,23 @@ +# Plan Execution Review: Wave-4 Visual Component Porting + +**Purpose**: Execute concrete review instructions before tasks are generated +**Created**: 2026-07-12 +**Plan**: [plan.md](../plan.md) + +- [X] **Baseline**: Compare plan ownership with Feature-021 public session/mapping/font/profile/view contracts; reject duplicate example-domain implementations. +- [X] **Historical scope**: Enumerate each Wave-4 implementation/header/config/resource/script/fixture source and retained intent before implementation. +- [X] **Vertical slice**: Verify `Terminal` tests fail before implementation and cover first frame, action, rejection, recovery, fallback, description, cells, and quit. +- [X] **Compile surface**: Require imports, XML docs, references, fixture copying, harness helpers, ownership assertions, and linked-source identity before red commands. +- [X] **Fixture**: Verify one exact 4,096-byte source-controlled nonblank font fixture and at least four negative classes; no generator or host action. +- [X] **Manifests**: Verify ETerm/XTerm entries are typed immutable data with source identity and explicit native-parser deviation. +- [X] **Visual proof**: Require app-loop plus concrete state, exact view, dynamic status, description, and stable cell region for all five examples. +- [X] **Host evidence**: Verify deterministic, CI, and physical evidence are separate and unavailable physical conditions remain `NotRun`. +- [X] **A11Y**: Verify keyboard-only operation, non-color-only state, narrow viewport, text-first fallback, bilingual guides, DocFX/axe, and lynx paths. +- [X] **Governance**: Verify all six preset rows contain complete fields, N/A rationale, residual risk, and re-evaluation trigger. +- [X] **Shared writers**: Serialize evidence, shared runtime, smoke project, solution, version, agents, navigation, statistics, and archive edits. +- [X] **Delivery**: Require exact evidence paths on remote tasks, unchanged-head review convergence, one closeout path, authorized merge, cleanup, and main sync. + +## Outcome + +All 12 execution instructions are represented in the plan and must remain +dependency-ordered in `tasks.md`. No plan correction is outstanding. diff --git a/specs/022-wave4-visual-component-porting/checklists/requirements.md b/specs/022-wave4-visual-component-porting/checklists/requirements.md new file mode 100644 index 00000000..ab006a45 --- /dev/null +++ b/specs/022-wave4-visual-component-porting/checklists/requirements.md @@ -0,0 +1,35 @@ +# Specification Quality Checklist: Wave-4 Visual Component Porting + +**Purpose**: Validate specification completeness and quality before planning +**Created**: 2026-07-12 +**Feature**: [spec.md](../spec.md) + +## Content Quality + +- [X] No implementation details beyond binding existing feature contracts and governance applicability +- [X] Focused on learner, reviewer, maintainer, and platform-aware user value +- [X] Written for technical and non-technical project stakeholders +- [X] All mandatory sections completed + +## Requirement Completeness + +- [X] No `[NEEDS CLARIFICATION]` markers remain +- [X] Requirements are testable and unambiguous +- [X] Success criteria are measurable +- [X] Success criteria describe observable outcomes rather than code structure +- [X] All acceptance scenarios are defined +- [X] Edge cases are identified +- [X] Scope is clearly bounded +- [X] Dependencies and assumptions are identified + +## Feature Readiness + +- [X] All functional requirements have clear acceptance evidence +- [X] User scenarios cover terminal, charset/font, resource-only, and platform-aware flows +- [X] Feature meets measurable outcomes defined in Success Criteria +- [X] Governance applicability and historical proof boundaries are explicit + +## Notes + +- Initial validation pass: 16/16 complete. +- Clarification Pass 1 records seven material decisions; Pass 2 converged with no remaining planning-impact ambiguity. diff --git a/specs/022-wave4-visual-component-porting/contracts/wave4-visual-component-acceptance.md b/specs/022-wave4-visual-component-porting/contracts/wave4-visual-component-acceptance.md new file mode 100644 index 00000000..a1dc213d --- /dev/null +++ b/specs/022-wave4-visual-component-porting/contracts/wave4-visual-component-acceptance.md @@ -0,0 +1,73 @@ +# Acceptance Contract: Wave-4 Visual Component Porting + +## Scope + +The contract covers exactly `Cyrillic`, `ETerm`, `Fonts`, `Terminal`, and +`XTerm`. Feature 021 is the reusable baseline. Full emulation, host processes, +host mutation, legacy parser compatibility, Feature 023, and TP7/Wave-5 work are excluded. + +## Common Visual Contract + +Every example must expose: + +1. a visible domain main composition or stable visible fallback, +2. dynamic text-first status, +3. keyboard-reachable bilingual description, +4. at least one real operation through application dispatch, +5. one primary app-loop proof with concrete state, exact view identity, and cells. + +Startup-only, helper-only, status-only, explanation-only, screenshot-only, or +host-rendering-only evidence is not primary proof. + +## Example Contracts + +| Example | Required visible contract | Required negative/fallback contract | +|---|---|---| +| Terminal | Session view, text, cursor/attribute action, profile/capability status, description, quit | Rejected/unsupported action is atomic, next input works, unavailable capability remains visible | +| Cyrillic | Labeled KOI8-R/Unicode grid, source values, glyphs, outcomes | Replaced, invalid, and unsupported outcomes agree in state/status/cells | +| Fonts | Exact fixture metadata and recognizable 8x16 glyph raster | At least four shape/source/format/blank or unsupported fallback classes | +| ETerm | At least three immutable menu/theme/presentation entries with source identity | Out-of-subset entry is visibly unsupported; no general parser claim | +| XTerm | At least three immutable resource/sequence/capability entries with source identity | Native resource/host integration remains visibly unsupported | + +## Asset and Host Contract + +- Assets are embedded or project-owned source-controlled read-only files. +- No arbitrary user-path read/write, process, shell, PTY, command execution, + audio, font/codepage/keyboard/terminal mutation, or generator execution is allowed. +- Host rows separate deterministic, remote CI, and physical observation. +- An unavailable physical condition is `NotRun` with risk and trigger. + +## Historical Contract + +Relevant implementation, header, config, resource, README, setup script, and +fixture metadata under `tv203s/` are reviewed read-only. Each example records +retained intent and material deviation. ETerm/XTerm native syntax omission is +an explicit `IntentionalDeviation` unless a different accepted framework +decision is proven during planning review. + +## Framework Gate + +Each example receives exactly one primary decision: + +| Decision | Meaning | +|---|---| +| `UseExistingFramework` | Existing contracts and composition satisfy acceptance | +| `SmallFrameworkFix` | One narrow reusable gap is fixed with focused tests | +| `IntentionalDeviation` | Historical behavior is deliberately represented differently | +| `FollowUpHardening` | A real issue lies outside 022 and is not implemented here | + +Reusable terminal, mapping, font, profile, host, or proof behavior may not be +duplicated in example-local code. + +## Evidence Contract + +`pr-evidence.md` contains one row per example plus host, governance, +validation, generated-output, archive, and remote-delivery tables. Every remote +task names that path or the pre-named `closeout-evidence.md` path. + +## Validation Contract + +Triggered gates are static diff/scope/secret checks, targeted Wave-4 smokes, +full Release tests, canonical coverage, format, DocFX, Playwright/axe, UTF-8 +text-browser review, generated-output cleanup, remote checks, thread-aware +review convergence, authorized merge, and local-main synchronization. diff --git a/specs/022-wave4-visual-component-porting/data-model.md b/specs/022-wave4-visual-component-porting/data-model.md new file mode 100644 index 00000000..a35f672e --- /dev/null +++ b/specs/022-wave4-visual-component-porting/data-model.md @@ -0,0 +1,131 @@ +# Data Model: Wave-4 Visual Component Porting + +The feature adds no database. These in-process and evidence entities define +visible state, controlled assets, platform claims, proof, and governance. + +## Wave4Example + +| Field | Type | Rules | +|---|---|---| +| `ExampleId` | enum | Exactly `Cyrillic`, `ETerm`, `Fonts`, `Terminal`, or `XTerm` | +| `ProjectPath` | path | Unique executable project under `examples/` | +| `HistoricalSources` | list | Read-only implementation/header/config/resource/script/fixture paths | +| `MainSurface` | descriptor | Real visible view or stable visible fallback | +| `StatusSurface` | descriptor | Dynamic status line/equivalent | +| `DescriptionPath` | descriptor | Keyboard reachable | +| `PrimaryOperation` | descriptor | Real event/command/key route | +| `FrameworkDecision` | enum | Exactly one accepted value | +| `Assets` | list | Embedded or source-controlled/read-only only | +| `ProofRecords` | list | At least one passing primary proof | + +## VisibleTerminalState + +| Field | Type | Rules | +|---|---|---| +| `SessionId` | string | Stable controlled identity | +| `Lifecycle` | enum | Active/Closed/Disposed/CapabilityLost | +| `Cursor` | point | Inside visible buffer | +| `Attributes` | value | Current 16-color attributes | +| `VisibleCells` | snapshot | Independent deterministic cells | +| `ProfileId` / `CharsetId` / `FontId` | string | Requested/effective identity remains distinguishable | +| `LastOutcome` | enum | Accepted/Rejected/Unsupported/Fallback | +| `Status` | string | Text-first current state and next route | + +## CharacterGridState + +| Field | Type | Rules | +|---|---|---| +| `SourceCharset` | enum | Unicode/KOI8-R/Unsupported | +| `SourceValue` | integer | Exact displayed source value | +| `Glyph` | character | Mapped scalar or U+FFFD | +| `Outcome` | enum | Mapped/Replaced/Invalid/Unsupported | +| `GridPosition` | point | Stable proof location | +| `Reason` | string | Required for non-exact result | + +## FontGridState + +| Field | Type | Rules | +|---|---|---| +| `FixtureId` | string | Source-controlled identity | +| `Width` / `Height` | integer | Exactly 8/16 | +| `GlyphCount` | integer | Exactly 256 | +| `BytesPerGlyph` | integer | Exactly 16 | +| `TotalBytes` | integer | Exactly 4,096 | +| `SelectedGlyph` | integer | 0..255 and nonblank for primary proof | +| `RowBytes` | immutable bytes | Exactly 16 copied bytes | +| `VisiblePixels` | cell region | Recognizable and non-color-only | +| `ValidationOutcome` | enum | Valid/Invalid/Unsupported | + +## ResourceManifestEntry + +| Field | Type | Rules | +|---|---|---| +| `ManifestId` | enum | ETerm or XTerm | +| `Key` | string | Unique, exact, stable | +| `Value` | string | Controlled representative value | +| `Category` | enum | Menu/Theme/Presentation/Resource/Sequence/Capability | +| `HistoricalSource` | path | Read-only source identity | +| `SupportState` | enum | Demonstrated/Informational/Unsupported | +| `Deviation` | string | Required for native parser/resource omission | + +## HostEvidenceRecord + +| Field | Type | Rules | +|---|---|---| +| `HostFamily` | enum | macOS/Linux/Windows/WSL/Headless/Unknown | +| `Condition` | string | Interactive/redirected/capability detail | +| `EvidenceClass` | enum | DeterministicInProcess/RemoteCI/PhysicalObservation | +| `Capability` | enum | Enabled/Disabled/Unsupported/NotRun | +| `Result` | enum | Pass/Fail/NotRun | +| `ResidualRisk` | string | Required for NotRun/Unsupported | +| `ReevaluationTrigger` | string | Required unless fully closed | + +## FrameworkUsageDecision + +| Field | Type | Rules | +|---|---|---| +| `ExampleId` | enum | Exactly one row per example | +| `Decision` | enum | UseExistingFramework/SmallFrameworkFix/IntentionalDeviation/FollowUpHardening | +| `ExistingComponents` | list | Exact reused contracts | +| `LocalLogic` | string | Presentation/composition only unless justified | +| `Rationale` | string | Required | +| `EvidencePath` | path | Required | +| `ResidualRisk` | string | Explicit, including None | +| `FollowUp` | string | Required for FollowUpHardening | + +## VisualProofRecord + +| Field | Type | Rules | +|---|---|---| +| `ExampleId` / `ProofId` | string | Unique | +| `AppLoopRoute` | string | Real run/dispatch path | +| `Operation` | string | Injected event/command/key | +| `ConcreteState` | string | Domain assertion | +| `ViewIdentity` | string | Exact visible view type/role | +| `RenderedRegion` | rectangle | Stable in current buffer | +| `ExpectedCells` | string/list | At least one concrete visible assertion | +| `Status` / `Description` | string | Both required | +| `HelperRole` | enum | PrimaryProof/SupplementalProof/SetupOnly | +| `Result` | enum | Pass/Fail/Open | + +## GovernanceCheckpoint + +| Field | Type | Rules | +|---|---|---| +| `RunId` / `PresetName` / `PresetVersion` | string | Required | +| `Checkpoint` | string | Unique within preset/run | +| `Applicability` | enum | Applicable/N/A/Open | +| `Rationale` / `EvidencePath` | string/path | Required | +| `Owner` / `Reviewer` / `ReviewDate` | identity/date | Required | +| `Result` / `ResidualRisk` | string | Required | +| `FollowUp` / `ReevaluationTrigger` | string | Trigger required for N/A/Open | + +## Relationships + +```text +Wave4Example 1 --- * VisibleTerminalState | CharacterGridState | FontGridState | ResourceManifestEntry +Wave4Example 1 --- 1 FrameworkUsageDecision +Wave4Example 1 --- 1..* VisualProofRecord +Wave4Example * --- * HostEvidenceRecord +GovernanceCheckpoint * --- 1 FeatureEvidence +``` diff --git a/specs/022-wave4-visual-component-porting/plan.md b/specs/022-wave4-visual-component-porting/plan.md new file mode 100644 index 00000000..707c5e9a --- /dev/null +++ b/specs/022-wave4-visual-component-porting/plan.md @@ -0,0 +1,257 @@ +# Implementation Plan: Wave-4 Visual Component Porting + +**Branch**: `022-wave4-visual-component-porting` | **Date**: 2026-07-12 | **Spec**: [spec.md](spec.md) +**Input**: `Lastenheft_Wave4-Visual-Component-Porting.md` plus accepted Features 019-021 + +## Summary + +Create five normal .NET terminal applications: `Cyrillic`, `ETerm`, `Fonts`, +`Terminal`, and `XTerm`. Each application uses existing Feature-021 contracts +and current TuiVision controls to show a visible main composition, dynamic +status line, and keyboard-reachable bilingual description. `Terminal` is the +test-first vertical slice. Primary proof drives the real app loop and combines +concrete domain state, exact view identity, and rendered buffer/cell regions. +Historical sources remain read-only; immutable manifests and one controlled +font fixture preserve intent without host mutation or general legacy parsers. + +## Technical Context + +**Language/Version**: C# 14 on .NET 10 +**Primary Dependencies**: Existing `TuiVision.Core`, `TuiVision.Controls`, and `TuiVision.Drivers.Console`; no new packages +**Storage**: Embedded/source-controlled read-only manifests and one exact raw font fixture; no user persistence +**Testing**: MSTest 4, in-process app-loop smokes, Coverlet, DocFX, Playwright with axe +**Target Platform**: macOS/Linux terminals plus Windows/WSL compatibility through current CI +**Project Type**: Multi-project terminal UI framework with executable learning examples +**Performance Goals**: Deterministic first frame and smoke completion within existing repository timeouts +**Constraints**: Keyboard-first, no process/shell/PTY, no host mutation, no new dependency, no `tv203s/` edit, no full emulator, no Feature-023 scope +**Scale/Scope**: Five projects, one shared presentation helper, five primary smoke suites, five guides, one README/index, feature/governance evidence + +## Constitution Check + +*GATE: Passed before research and re-checked after design.* + +- **Level-2 environment**: .NET 10, MSTest, coverage, DocFX/A11Y, + project statistics, versioning, and five agent surfaces are binding. +- **Memory-safe language**: C#/.NET is approved; historical C/C++ and scripts + are read-only evidence. +- **Secure coding**: Terminal observations, profile/resource identities, + fixture shape, sequence bounds, and fallback states are validated fail-safe. +- **Architecture**: Driver/Core/Controls ownership from 021 remains unchanged. + `examples/Shared/Wave4Runtime.cs` contains presentation and proof helpers only. +- **NIST SSDF / CWE Top 25**: Applicable to changed code, tests, bounded + resources, fixture handling, status publication, and evidence. +- **ASVS**: `N/A`; no web/API/auth surface. +- **Supply chain**: Existing SBOM/VEX/SLSA/OpenSSF evidence remains authoritative; + no package, lockfile, distribution, or provenance scope is planned. +- **AI-SBOM / regulation**: `N/A`; AI is development tooling and no runtime AI, + regulated service, market product, or regulated supply-chain role enters. +- **STRIDE/CIA/CAPEC**: Applicable proportionally to terminal input, resource + identity, state integrity, bounded memory, and capability claims. +- **S-ADR / arc42 security / Zero Trust / SAMM / BSI C3A / BSI C5**: New + artifacts are `N/A` unless architecture/cloud/provider/deployment boundaries change. +- **iSAQB**: Existing runtime/component views and quality goals are reused; a + material ownership change requires a separate architecture decision. +- **A11Y**: Keyboard operation, text status/fallback, non-color-only proof, + bilingual guides, WCAG 2.2 AA review, DocFX/axe, and text-browser proof apply. +- **Didactic comments**: Non-trivial app-loop, raster, manifest, and proof + helpers receive selective why/trade-off/proof-boundary review. +- **Cross-platform**: Host and asset/path proof applies. Script parity is `N/A` + because no script is planned or changed. +- **Agent parity**: Active feature context changes; all five maintained surfaces + are synchronized together. +- **Autonomous evidence**: Every remote task names + `specs/022-wave4-visual-component-porting/pr-evidence.md` or the pre-named + `specs/022-wave4-visual-component-porting/closeout-evidence.md`. +- **Versioning**: Increment only the manual build component before each + `dotnet build` or `dotnet test`; align commits/pushes to + `1.22..`. + +### Governance Checkpoint Matrix + +| Domain | Planned applicability | Evidence boundary | +|---|---|---| +| NIST SSDF / CWE Top 25 | Applicable | Changed C#, tests, resources, fixtures, and feature evidence | +| OWASP ASVS | N/A unless web/API/auth appears | Existing ASVS ledger plus feature trigger row | +| SBOM / VEX / SLSA / OpenSSF | Existing baseline; no new feature artifact | Supply-chain ledger and feature evidence | +| AI-SBOM / NIS2 / CRA / EU AI Act / DORA | N/A for local non-AI training demos | Feature row with re-evaluation trigger | +| STRIDE / CIA / CAPEC | Terminal/resource/fixture/fallback review | Feature risk/evidence rows and threat-model update if triggered | +| S-ADR / arc42 security / Zero Trust / SAMM | N/A absent architecture change | Existing architecture/security evidence | +| BSI C3A / BSI C5 | N/A; no cloud/provider/deployment boundary | Existing cloud applicability documents | +| iSAQB architecture | Applicable as reuse/quality-boundary review | Plan, research, framework decisions, runtime view | +| A11Y | Applicable | App-loop keyboard/text/cell proof, guides, DocFX, axe, lynx | +| Cross-platform | Host proof applicable; script governance N/A | deterministic/remote/physical host rows | +| Agent parity | Applicable | five synchronized agent surfaces | + +## Project Structure + +### Documentation + +```text +specs/022-wave4-visual-component-porting/ +├── spec.md +├── plan.md +├── research.md +├── data-model.md +├── quickstart.md +├── contracts/ +│ └── wave4-visual-component-acceptance.md +├── checklists/ +├── tasks.md +├── pr-evidence.md +└── closeout-evidence.md +``` + +### Runtime and Tests + +```text +examples/ +├── Shared/Wave4Runtime.cs +├── Cyrillic/ +├── ETerm/ +├── Fonts/ +│ └── Fixtures/font-8x16.bin +├── Terminal/ +└── XTerm/ + +tests/TuiVision.Examples.SmokeTests/ +├── Wave4VisualSmokeMatrixTests.cs +├── CyrillicSmokeTests.cs +├── ETermSmokeTests.cs +├── FontsSmokeTests.cs +├── TerminalSmokeTests.cs +└── XTermSmokeTests.cs + +docs/guides/examples/ +├── cyrillic.md +├── eterm.md +├── fonts.md +├── terminal.md +└── xterm.md +``` + +**Structure Decision**: Five independent executable projects own their domain +composition. `Wave4Runtime.cs` contains only repeated status, description, +region, manifest-display, and smoke-event presentation. Session parsing, +charset mapping, fixture validation, profiles, host classification, and terminal +rendering remain in existing 021 framework types. Because the helper is linked +into five assemblies, cross-project tests use public state/contract delegates, +not shared CLR type identity. + +## Phase 0: Research Decisions + +1. Treat Features 019-021 as complete; do not reopen their accepted behavior. +2. Use `Terminal` as the vertical slice because it exercises the complete + Driver-to-Controls-to-app-loop chain and visible recovery/fallback proof. +3. Reuse `TerminalSession`, `TTerminalView`, `TerminalCharsetMapper`, + `BitmapFontFixture`, `TerminalProfile`, and deterministic host classification. +4. Preserve Cyrillic KOI8-R learning intent without locale, codepage, setup + script, root access, or host keyboard-map mutation. +5. Preserve Fonts raster intent through an exact project-owned fixture and + visible 8x16 glyph, never generator execution or host installation. +6. Preserve ETerm/XTerm resource intent through immutable representative + manifests with explicit `IntentionalDeviation`, not general parser claims. +7. Reuse the Wave-1 through Wave-3 app-loop/state/view/cell proof shape. +8. Keep deterministic, remote, and physical host evidence separate; physical + gaps remain `NotRun` with a trigger. + +## Phase 1: Design + +### Runtime Design + +- `Wave4Runtime` supplies a drawable status line, Help/Description menu, + stable bounded regions, text-first fallback, and read-only manifest display. +- `TerminalApp` owns one 021 session and `TTerminalView`, queues controlled + observations, exposes reset/rejection/description commands, and reports + cursor/profile/capability state. +- `CyrillicApp` maps a fixed sample through `TerminalCharsetMapper`, renders a + labeled character grid, and exposes direct/replaced/invalid/unsupported states. +- `FontsApp` loads only its copied source-controlled fixture, validates exact + metadata through `BitmapFontFixture`, renders one nonblank glyph as text-first + pixels, and shows invalid-fixture fallback in tests. +- `ETermApp` and `XTermApp` expose immutable typed manifest entries with source + identity, representative values, accepted subset, and visible unsupported state. + +### Vertical Slice + +1. Add five project references and a complete failing `Terminal` matrix. +2. Prove first frame, controlled text, cursor/attribute action, rejection, + recovery, fallback, description, view identity, cell region, and quit. +3. Implement shared presentation plus `Terminal` only. +4. Run one focused project-local green batch and record its evidence row. +5. Spread the established composition/proof pattern to Cyrillic/Fonts and manifests. + +### Proof Design + +- One acceptance row per example records historical sources, framework decision, + operation, state, exact view type, cell region, status, description, host class, + fallback, helper role, and result. +- Primary smokes drive queued events through `app.Run()` and capture the real + owner buffer. Direct methods only create deterministic inputs or add supplemental proof. +- Project-local red matrices group related negative cases while preserving every + expected boundary and repair owner. +- The solution-level smoke matrix accesses linked example assemblies through + public contracts or state delegates and never assumes linked helper type identity. +- Standard and narrow viewport cases prove that identity/status/fallback remain + visible even when a list or raster is clipped. + +### Assets and Safety Design + +- The font fixture is copied once into `examples/Fonts/Fixtures/` with source + origin documented and `CopyToOutputDirectory` for controlled project access. +- ETerm/XTerm values are typed immutable data in their projects; historical + files are not executed, parsed at arbitrary paths, or mutated. +- Examples perform no writes and no user-path discovery. +- Static review rejects process, shell, PTY, external-command, locale mutation, + font install, codepage, keyboard-map, terminal-setting, and audio APIs. + +### Documentation and Governance Design + +- Add five bilingual guides and update `docs/toc.yml` plus `examples/README.md`. +- Update Pflichtenheft and all five agent contexts to route next to Feature 023. +- Populate complete governance/host/framework/proof rows in `pr-evidence.md`. +- Archive the Lastenheft after runtime, proof, docs, and all local gates pass. +- Generic autonomous-workflow changes remain a separate post-feature + retrospective PR; 022 implementation does not silently change templates. + +## Phase 2: Implementation Order + +1. Create evidence schema, historical inventory, framework matrix, host matrix, + governance rows, and pre-name the causal closeout path. +2. Review full compile surface, project references, linked-source identity, + fixture copying, harness helpers, and public XML documentation. +3. Add project skeletons and complete failing `Terminal` vertical-slice tests. +4. Implement shared presentation and `Terminal`; validate and record proof. +5. Add grouped failing Cyrillic/Fonts matrices, then their implementations. +6. Add grouped failing ETerm/XTerm matrices, then immutable manifest demos. +7. Complete cross-example matrix, narrow viewport, fallback, comments, and decisions. +8. Update guides, README, navigation, architecture/security evidence if triggered, + Pflichtenheft, agents, statistics, and archive. +9. Run static, targeted, full Release, coverage, DocFX/A11Y/lynx, secret, scope, + and generated-output gates. +10. Align version, commit, push, open PR, converge checks/reviews, merge, clean + branches, sync main, and use exactly one causal closeout PR when required. + +## Validation Strategy + +| Change | Required proof | +|---|---| +| Every change | `git diff --check`, placeholder/scope/generated/secret scans | +| Five examples/shared helper | Targeted Release example-smoke project | +| Shared executable/proof behavior | Full Release suite and canonical coverage gate | +| XML/guides/navigation | DocFX then Playwright/axe and UTF-8 lynx review | +| Visible TUI | App-loop, state, exact view, status, description, and cell-region proof | +| Historical behavior | Relevant implementations, headers, resources, configs, scripts, and fixture metadata reviewed read-only | +| Host claims | Separate deterministic, remote, and physical evidence rows | + +## Post-Design Constitution Re-check + +Passed. The design adds no package, network, cloud, authentication, script, +runtime AI, host process, host mutation, legacy parser, arbitrary user path, or +new architecture boundary. Controlled assets, A11Y, platform evidence, +historical intent, agent parity, versioning, and causal remote evidence are explicit. + +## Complexity Tracking + +No Constitution violation or exceptional complexity is planned. The linked +presentation helper is a deliberate example-only reuse boundary; reusable +terminal, mapping, font, profile, and host behavior remains in framework code. diff --git a/specs/022-wave4-visual-component-porting/pr-evidence.md b/specs/022-wave4-visual-component-porting/pr-evidence.md new file mode 100644 index 00000000..44c26d63 --- /dev/null +++ b/specs/022-wave4-visual-component-porting/pr-evidence.md @@ -0,0 +1,239 @@ +# Autonomous Run Evidence: Wave-4 Visual Component Porting + +**Branch**: `022-wave4-visual-component-porting` +**Feature directory**: `specs/022-wave4-visual-component-porting` +**Binding intake**: `Lastenheft_Wave4-Visual-Component-Porting.md`, archived after acceptance as `Lastenheft_Wave4-Visual-Component-Porting.022-wave4-visual-component-porting.md` +**Delivery mode**: `MergeAndSync` +**Authority source**: User instruction for autonomous Features 018-023, non-empty PRs, merge/main sync, and narrowly bounded admin bypass + +## Scope + +### Included + +- Five visible Wave-4 applications, controlled assets/manifests, app-loop/state/ + view/cell smokes, host evidence, guides, governance, routing, statistics, and archive. + +### Excluded + +- Full emulator, process/shell/PTY, external commands, host terminal/font/ + codepage/locale/keyboard/audio mutation, general legacy parsers, arbitrary + user data, persistence, dependencies, Feature 023, Wave 5/TP7, and `tv203s/` edits. + +## Run Gates + +| Phase | Attempt | Result | Evidence | Remaining action | +|---|---:|---|---|---| +| Preflight | 1 | Pass | clean synchronized baseline `0ed394c4b0e6a96b3eaa5b582d88b9e96401c040`; branch, tools, presets, intake | None | +| Specify | 1 | Pass | `spec.md`; 36 FR, 16 CR, 13 SC | None | +| Clarify | 2 | Pass | Seven accepted decisions; second pass found no material ambiguity | None | +| Checklists | 4 | Pass | `checklists/`; 60/60 complete | None | +| Plan | 1 | Pass | `plan.md`, research, model, contract, quickstart | None | +| Plan Review | 1 | Pass | 12/12 execution instructions complete | None | +| Tasks | 1 | Pass | `tasks.md`; T001-T149 unique and sequential | None | +| Analyze | 3 | Pass | Planning remediation plus final post-implementation read-only pass: 65/65 mapped, 149 tasks, constitution issues 0, unmapped tasks 0, Critical/High/Medium 0 | None | +| Implement | 1 | Pass | Five test-first slices plus cross-project delegate matrix; T001-T120 evidence below | None | +| Validate | 1 | Pass | 137/137 example smokes, 662/662 full tests, five-assembly coverage, DocFX/axe/lynx | None locally; remote CI remains delivery evidence | +| Deliver | 1 | Open | Authorized T138-T149 | Complete local gates first | + +## Artifact Convergence + +| Artifact or pass | Result | Evidence | +|---|---|---| +| Requirements quality | Pass | `checklists/requirements.md` 16/16 | +| Domain acceptance | Pass | `checklists/domain-acceptance.md` 16/16 | +| Plan quality | Pass | `checklists/plan-quality.md` 16/16 | +| Plan execution review | Pass | `checklists/plan-review.md` 12/12 | +| Task IDs | Pass | T001-T149, unique, no gaps | +| Requirement coverage | Pass | FR-001..FR-036, CR-001..CR-016, SC-001..SC-013 mapped | + +## Preflight Results + +| Check | Result | Evidence | +|---|---|---| +| Branch and ancestry | Pass | Branch `022-wave4-visual-component-porting`; HEAD/merge-base/origin-main `0ed394c4b0e6a96b3eaa5b582d88b9e96401c040` before edits | +| Feature metadata | Pass | `.specify/feature.json` references `specs/022-wave4-visual-component-porting` | +| `specify check` | Pass | CLI ready; local integrations detected | +| PowerShell/Bash prerequisites | Pass | Feature and all planning/task artifacts resolved | +| Checklists | Pass | requirements 16, domain 16, plan quality 16, plan review 12 | +| Presets | Pass | security 0.6.0/10; architecture 0.5.0/20; isaqb 0.2.0/30; a11y 0.4.0/40; cross-platform 0.2.0/50; agent-parity 0.3.0/60 | +| Governance conflict | None | Accepted 022 artifacts align with current Constitution and 021 baseline | +| Initial historical diff | Pass | `git diff -- tv203s/` empty before implementation | + +## Optional Command Disposition + +| Command | Result | Rationale | +|---|---|---| +| `speckit-constitution` | N/A | Constitution and six-preset matrix require no amendment | +| `speckit-taskstoissues` | N/A | One dependency-ordered single-writer feature PR is the delivery unit | + +## Compile-Surface Review + +| Surface | Result | Decision | +|---|---|---| +| Project graph | Pass with planned alignment | New examples reference Core/Controls/Drivers only; no cycle. `TuiVision.sln`, smoke references, and Coverlet example exclusions require explicit T031 alignment | +| Feature-021 contracts | Pass | `TerminalSession`, mapper, fixture, profile/host detector, and `TTerminalView` are public and sufficient; no duplicate parser/view allowed | +| Imports/XML docs | Pass | New public app types follow existing bilingual XML surface; no framework API is planned | +| Harness and ownership | Pass | `ExampleTestBase`, `InteractiveSmokeEventScript`, Driver back buffer, status/description, exact view, and region assertions are reusable | +| Linked-source identity | Pass | `Wave4Runtime.cs` will compile into five assemblies; matrix tests use public state/delegates, never cross-assembly casts | +| Font fixture | Pass | Historical `font.016` is 4,096 bytes; SHA-256 `a40cb00a1075f342426f6ea6571a2665426b7362f7e33bed2e2f5e28daaca169`; glyph 65 contains a nonblank A pattern | +| Historical diff | Pass | `git diff -- tv203s/` remains empty after review | + +## Example Acceptance Matrix + +| Example | Main surface | Primary operation | Framework decision | Historical evidence | App-loop/state/view/cell proof | Fallback | Result | +|---|---|---|---|---|---|---|---| +| Terminal | `TTerminalView` session | Controlled input/action/reset | UseExistingFramework | Reviewed | App-loop, state, exact view, rendered cells, description, and narrow viewport Pass | Reset produces controlled `Unsupported`; atomic rejection/recovery Pass | Pass | +| Cyrillic | Labeled character grid | Cycle mapping states | UseExistingFramework | Reviewed | App-loop, direct/replaced/rejected/unsupported state, exact view/cells, status, description, quit, and narrow viewport Pass | Fixed-table text fallback; no host locale/codepage Pass | Pass | +| Fonts | 8x16 glyph grid | Select/validate glyph | UseExistingFramework | Reviewed | App-loop, exact 8x16/256/4,096 metadata, known A raster, selection, exact view/cells, status, description, quit, and narrow viewport Pass | Wrong length/geometry/stride/source, unsupported format, and blank-glyph fallbacks Pass | Pass | +| ETerm | Immutable manifest view | Select entry | IntentionalDeviation | Reviewed | Three typed entries, exact source/category/value, app-loop selection, exact view/cells, status, description, quit, and narrow viewport Pass | Missing/native entry is visible `Unsupported`; no parser or theme mutation Pass | Pass | +| XTerm | Immutable manifest view | Select entry | IntentionalDeviation | Reviewed | Three typed resource/sequence/capability entries, app-loop selection, exact view/cells, status, description, quit, and narrow viewport Pass | Native X resource is visible `Unsupported`; no X database/terminfo/command Pass | Pass | + +### Terminal Red/Green Evidence + +| Attempt | Version | Result | Boundary | +|---:|---|---|---| +| Red 1 | `1.22.0.161` | Expected compile failure | Five skeletons, shared link, solution, Coverlet, and smoke references compiled; only missing `TerminalApp` and its named command/state contract failed | +| Green 1 | `1.22.0.162` | 5/5 Pass | First frame, controlled input/cursor, exact view/cells, rejection/recovery, reset/`Unsupported` fallback, description, narrow viewport, and quit passed | + +### Cyrillic and Fonts Red/Green Evidence + +| Attempt | Version | Result | Boundary | +|---:|---|---|---| +| Red 1 | `1.22.0.163` | Expected compile failure | Both projects and the copied fixture compiled; only the missing `CyrillicApp`, `FontsApp`, and named state contracts failed | +| Green probe | `1.22.0.164` | 7/8 Pass | Implementation compiled without errors; one test expected the wrong textual bit layout for historical bytes `0x3E` and `0x47` | +| Green 1 | `1.22.0.165` | 8/8 Pass | Mapping states, host independence, exact fixture metadata/hash, known A raster, all fallback classes, descriptions, narrow viewports, and quit passed without warnings | + +### ETerm and XTerm Red/Green Evidence + +| Attempt | Version | Result | Boundary | +|---:|---|---|---| +| Red 1 | `1.22.0.166` | Expected compile failure | Both skeleton projects compiled; only missing `ETermApp`, `XTermApp`, and their immutable manifest/state contracts failed | +| Green 1 | `1.22.0.167` | 8/8 Pass | Representative entries, exact source identity, app-loop selection, status, description, native/out-of-subset fallback, narrow viewport, and no-parser/no-host-action assertions passed | + +## Host Evidence + +| Host | Condition | DeterministicInProcess | RemoteCI | PhysicalObservation | Residual risk | Re-evaluation trigger | +|---|---|---|---|---|---|---| +| macOS | Darwin 25.5.0 arm64; `TERM=dumb`; stdin/stdout redirected | Pass as `Headless`/`Unsupported`; no physical claim | Open until PR checks | NotRun | Physical terminal variation | Safe interactive observation | +| Linux | No local physical host | Synthetic 021 detector contract Pass in full suite | Open until PR checks | NotRun | Backend variation | Linux CI/manual host | +| Windows/WSL | No local physical host | Synthetic 021 detector contract Pass in full suite | Open until PR checks | NotRun | Native/WSL variation | Windows/WSL CI/manual host | +| Headless/redirected | Controlled non-interactive condition | Five-app matrix Pass with `DeterministicInProcess` | N/A | N/A | None within controlled fallback contract | I/O model changes | + +## Asset and Manifest Matrix + +| Asset | Ownership | Validation | Host action | Result | +|---|---|---|---|---| +| Fonts raw fixture | SourceControlled/ReadOnly | Exact 8x16/256/16/4,096; SHA-256 `a40cb00a1075f342426f6ea6571a2665426b7362f7e33bed2e2f5e28daaca169`; glyph 65 nonblank | None | Historical source and byte-identical modern copy Pass | +| ETerm entries | Embedded immutable manifest | Exact case-sensitive unique `Font3=8x16`, `Foreground=#aaaaaa`, `Version=ESC[8n` with source/category | None | Pass | +| XTerm entries | Embedded immutable manifest | Exact case-sensitive unique `Insert=ESC[2~`, `Color1=#a80000`, `metaSendsEscape=true` with source/category | None | Pass | + +## Didactic Comment Decisions + +| Area | Decision | Rationale | Boundary | Result | +|---|---|---|---|---| +| Shared app-loop/proof | CommentNeeded | Linked ownership and event publication are non-trivial | Explain why/proof, not obvious what | Pass | +| Terminal recovery/fallback | CommentNeeded | Atomic recovery and host boundary matter | Short bilingual reason block | Pass | +| Cyrillic mapping | CommentNeeded | Historical fixed mapping versus host codec | Short bilingual reason block | Pass | +| Font raster | CommentNeeded | Bit order and proof region are non-obvious | Short bilingual reason block | Pass | +| Resource manifests | CommentNeeded | Intentional parser omission prevents false capability | Short bilingual trade-off block | Pass | +| Obvious declarations/accessors | NoCommentNeeded | Names/XML docs are sufficient | No inline narration | Accepted | + +## Historical Intent + +| Modern area | Historical source | Intent retained | Intentional deviation | Proof | +|---|---|---|---|---| +| Terminal | `examples/terminal/terminal.cc`, `include/tv/terminal.h` | Visible bounded terminal, buffer/history/cursor and app-loop dialog | Managed 021 session/view; no ring-buffer mechanical port, process, shell, or PTY | Read-only review and managed session/view/cell proof Pass | +| Cyrillic | Linux/X11 `test.cc`, Linux README, `trivial.acm`, setup scripts and compressed assets | KOI8-R/Cyrillic display, font/codepage awareness, and host limits | Unicode cells; no `/dev/vcsa`, root, locale/codepage/font/keyboard setup | Read-only review and four-state fixed-table implementation proof Pass | +| Fonts | `test.cc`, `genraw.cc`, `font.016`, `ocr.sft`, `fontcoll.cc`, `fontcoll.h` | 8x16 raster/font presentation and unavailable-host awareness | Exact metadata/raster only; no shell, generator, native format, install, or restore | Read-only review, byte-identical fixture, exact metadata, and known-raster proof Pass | +| ETerm | `menus.cfg`, `theme.cfg` | Font menu, 16-color/theme, and terminal presentation values | Immutable typed manifest; no config parser, spawn/save/exit script, image path, or theme mutation | Read-only review and three-entry typed-manifest proof Pass | +| XTerm | `Xterm.res`, `xtermkey.cc`, `xtermdis.cc`, `xtermscr.cc` | Resource/key/sequence/capability variation and explicit setup | Immutable manifest plus existing Compatibility/021 subset; no xterm process, X resource database, or native screen mutation | Read-only review and three-entry immutable-manifest proof Pass | + +## Governance Applicability + +| Preset | Version | Checkpoint | Applicability | Rationale | Evidence path | Owner | Reviewer | Result | Residual risk | Follow-up | Re-evaluation trigger | +|---|---|---|---|---|---|---|---|---|---|---|---| +| security-governance | 0.6.0 | NIST SSDF, CWE Top 25, input/resource bounds, fail-safe, least privilege | Applicable | Terminal input, fixture, manifests, state, and fallback change | This file; `docs/security/threat-model.md`; 22 Wave-4 smokes | Feature owner | Codex | Pass reviewed 2026-07-12 | Physical host variation only | Remote checks | Contract or trust-boundary changes | +| security-governance | 0.6.0 | OWASP ASVS | N/A | No web endpoint, authentication, session, browser application, or authorization change | `docs/security/asvs-verification.md`; this file | Feature owner | Codex | N/A reviewed 2026-07-12 | Trigger drift | None | Web/auth scope enters | +| security-governance | 0.6.0 | New SBOM, VEX, SLSA, OpenSSF Scorecard | N/A | No package, dependency, release, distribution, or supply-chain workflow change | `docs/security/supply-chain-evidence.md`; package diff | Feature owner | Codex | N/A reviewed 2026-07-12 | Trigger drift | None | Dependency/release workflow changes | +| security-governance | 0.6.0 | AI-SBOM, NIS2, CRA, EU AI Act, DORA | N/A | No product AI, regulated operation, service, provider, market release, or financial-service boundary | `docs/security/regulatory-applicability.md`; this file | Feature owner | Codex | N/A reviewed 2026-07-12 | Trigger drift | None | Named regulatory/product trigger enters | +| architecture-governance | 0.5.0 | STRIDE/CIA/CAPEC | Applicable | Input, state, resource identity, exhaustion, and false-capability claims change | `docs/security/threat-model.md`; this file; fallback smokes | Feature owner | Codex | Pass reviewed 2026-07-12 | Native host variants remain outside claim | Remote checks | Trust boundary changes | +| architecture-governance | 0.5.0 | S-ADR and arc42 change | N/A | Existing 021 ownership remains; 022 adds example projections and a runtime-view addendum, not a new architectural decision | `docs/architecture/runtime-view.md`; diff review | Feature owner | Codex | N/A reviewed 2026-07-12 | Architecture drift | None | New framework boundary or irreversible decision | +| architecture-governance | 0.5.0 | Zero Trust, SAMM, BSI C3A, BSI C5 | N/A | No cloud, distributed service, identity plane, provider dependency, deployment topology, or cloud compliance boundary | Existing security applicability ledgers | Feature owner | Codex | N/A reviewed 2026-07-12 | Trigger drift | None | Cloud/service/provider boundary enters | +| isaqb-architecture-governance | 0.2.0 | Reuse, quality, runtime view | Applicable | Driver/Core/Controls/example ownership and linked-source identity are central | `plan.md`; `research.md`; `docs/architecture/runtime-view.md`; matrix smoke | Feature owner | Codex | Pass reviewed 2026-07-12 | Example-only manifests remain bounded | Remote checks | Ownership changes | +| a11y-governance | 0.4.0 | Keyboard, text-first status/fallback, narrow viewport, WCAG, didactic comments | Applicable | Five visible demos, learner guides, navigation, and non-trivial proof code change | 22 Wave-4 smokes; five guides; DocFX 0/0; axe 2/2; lynx five/five | Feature owner | Codex | Pass reviewed 2026-07-12 | Physical assistive-host variation | Remote checks | UI/docs/interaction changes | +| cross-platform-governance | 0.2.0 | Host/assets | Applicable | Host capability and fixture/resource behavior vary | Host/asset rows; fixture hash; full suite | Feature owner | Codex | Local Pass; remote Open 2026-07-12 | Physical Linux/Windows/WSL not locally observed | CI/manual observation | Host contract changes | +| cross-platform-governance | 0.2.0 | Script parity | N/A | No script planned or changed | Diff review | Feature owner | Codex | N/A reviewed 2026-07-12 | Scope drift | None | Script enters diff | +| agent-parity-governance | 0.3.0 | Five agent surfaces | Applicable | Active feature/next intake changes | Five files; normalized block SHA-256 `eda988f733879893dbe81f12824a1d4d241dd7044a18fbad8d0f1ef514337c21` | Feature owner | Codex | Pass reviewed 2026-07-12 | None within synchronized block | Remote diff review | Shared context changes | +| agent-parity-governance | 0.3.0 | `.specify/templates/` | N/A | Generic workflow changes use separate retrospective PR | Diff review | Feature owner | Codex | N/A reviewed 2026-07-12 | Retrospective finding | Separate PR | Generic correction proven | + +## Validation + +| Command or review | Trigger | Result | Evidence or failure boundary | +|---|---|---|---| +| `git diff --check` | Always | Pass before and after implementation | No whitespace errors | +| Placeholder/task/scope scans | Always | Pass through local completion | 149 unique tasks, no clarification/TODO/TBD marker, no historical or dependency diff | +| `dotnet format --verify-no-changes --no-restore` | C# changes | Pass | Exit 0 after all five apps, shared runtime, and tests | +| Targeted Wave-4 smokes | Five examples/helper | All slice red/green cycles Pass | Terminal 5/5 at `1.22.0.162`; Cyrillic/Fonts 8/8 at `1.22.0.165`; ETerm/XTerm 8/8 at `1.22.0.167` | +| Cross-project Wave-4 matrix | Linked source/public state | Pass 1/1 | `1.22.0.169`; first `1.22.0.168` probe exposed only missing harness marker, then exact views/DTO delegates/host/framework/fallback passed | +| Complete example smokes | Example matrix | Pass 137/137 | `1.22.0.170`; Wave-1/2/3 regressions plus all five Wave-4 projects | +| Full Release tests | Shared executable/proof change | Pass 662/662 | `1.22.0.171`: Core 44, Serialization 44, Drivers 105, Compatibility 18, Controls 314, examples 137 | +| Canonical coverage | Shared executable/proof change | Pass | `xmllint` Pass; `1.22.0.172` stopped before tests because the collector name was split; corrected single-argument command at `1.22.0.173` passed: Core 89.78%, Controls 83.42%, Serialization 89.50%, Compatibility 80.55%, Drivers 89.18% | +| DocFX plus Playwright/axe | XML/guides/navigation | Pass | DocFX 280 models, 0 warnings/0 errors; Playwright/axe 2/2 | +| UTF-8 lynx | Learner guides | Pass 5/5 | Terminal, Cyrillic, Fonts, ETerm, XTerm each retain Deutsch, English, umlauts, semantic text order | +| Secrets/generated output | Always | Pass | gitleaks diff clean; local `.claude/settings.local.json` remains ignored medium; `_site`, generated API YAML, TestResults removed | + +## Requirement and Success Coverage + +| Range | Primary evidence | Current result | +|---|---|---| +| FR-001..FR-010 | Common/Terminal matrices and tests | Pass; T016-T020, T031-T060, T103-T106 | +| FR-011..FR-019 | Cyrillic/Fonts/manifest matrices and tests | Pass; T061-T102, T103-T105 | +| FR-020..FR-028 | Historical/framework/host/asset evidence | Local Pass; remote host rows remain delivery evidence; T080-T120 | +| FR-029..FR-036 | A11Y/docs/routing/archive/scope validation | Pass; T107-T135 | +| CR-001..CR-016 | Governance/compile/validation/delivery evidence | Local Pass; T108-T137; remote T138-T149 Open | +| SC-001..SC-013 | Exact proof/validation/archive/remote closeout | SC-001..SC-012 local Pass; SC-013 remote closeout Open | + +## Generated and Sensitive Output Hygiene + +| Surface | Required result | Current result | +|---|---|---| +| `_site/`, generated `api/` | Absent from commit | Generated for proof, then removed; absent from status | +| TestResults/coverage/reports/logs | Absent from commit | Generated for coverage, then removed; absent from status | +| Credentials/secrets | No diff/tracked secret | gitleaks current-diff Pass; ignored local agent setting not staged | +| `tv203s/` | No diff | Pass before and after implementation; historical files read-only | + +## Local Completion + +| Item | Result | +|---|---| +| Local tasks | T001-T137 complete; T138-T149 remain remote delivery only | +| Changed files | 57 intentional paths including five projects, shared presentation, six smoke files, five guides, Spec-Kit evidence, governance, navigation, statistics, and archived intake | +| Diff volume before statistics row | `+3723/-10`, 3713 net; production 962, tests 588, Markdown/evidence/guidance 2007, project/tooling metadata 166 | +| Comment decisions | `CommentNeeded` 5; `NoCommentNeeded` 1; no trivial what-comment added | +| Governance decisions | 6 `Applicable` locally passed, including one remote-host continuation; 7 justified `N/A`; 0 unowned `Open` local checkpoint | +| Local validation | Format Pass; example smokes 137/137; full Release 662/662; all five coverage gates Pass; DocFX 0/0; axe 2/2; lynx 5/5; secret/generated/historical scans Pass | +| Archive | `Lastenheft_Wave4-Visual-Component-Porting.022-wave4-visual-component-porting.md` | +| Conditional evidence | Physical macOS/Linux/Windows/WSL observation `NotRun`; remote CI/review/merge evidence belongs to `closeout-evidence.md` | +| Final Analyze | 65/65 requirement/success coverage; 149/149 mapped tasks; constitution issues 0; ambiguity/duplication/Critical/High/Medium findings 0 | +| Planned feature commit | Prospective branch commit count 1; aligned version `1.22.1.173`; 57 intentional paths; observed commit hash deferred to `closeout-evidence.md` | + +## Remote Delivery + +| Item | Result | Evidence | +|---|---|---| +| Push | Open | T141; exact closeout path pre-named | +| Pull request | Open | T142 | +| Required checks | Open | Verify unchanged reviewed head | +| Review threads | Open | Thread-aware zero-actionable requirement | +| Unavailable reviews | None observed | Record missing, never successful | +| Reviewed head | Open | Closeout-only fact | +| Merge | Open | Authorized after convergence | +| Local `main` sync | Open | Require clean equality | +| Causal closeout | Pre-named | `specs/022-wave4-visual-component-porting/closeout-evidence.md`; no recursive self-URL requirement | + +## Retrospective + +- **Effective**: The Terminal vertical slice, bounded grouped red matrices, and neutral DTO/delegate matrix kept the five linked assemblies independently reviewable. +- **Waste**: One historical-bit expectation and one missing harness marker required an extra green probe; both failures were explicit and local. +- **Recurring blocker**: A shell command can split the multi-word Coverlet collector name unless automation treats it as one argument. Record as `ObserveAgain`, because repository documentation already shows correct quoting. +- **Recommended refinement**: Continue the single-commit closeout experiment without requiring its own URL in repository evidence; also observe whether app-loop helper APIs should expose a less error-prone primary-proof marker. diff --git a/specs/022-wave4-visual-component-porting/quickstart.md b/specs/022-wave4-visual-component-porting/quickstart.md new file mode 100644 index 00000000..90769a36 --- /dev/null +++ b/specs/022-wave4-visual-component-porting/quickstart.md @@ -0,0 +1,63 @@ +# Quickstart: Wave-4 Visual Component Porting + +## Preconditions + +```bash +git switch 022-wave4-visual-component-porting +specify check +.specify/scripts/bash/check-prerequisites.sh --json --require-tasks --include-tasks +``` + +Confirm `.specify/feature.json` points to +`specs/022-wave4-visual-component-porting` and every checklist is complete. + +## Start the Examples + +```bash +dotnet run --project examples/Cyrillic +dotnet run --project examples/ETerm +dotnet run --project examples/Fonts +dotnet run --project examples/Terminal +dotnet run --project examples/XTerm +``` + +Each first frame must show a domain main surface, dynamic status, and +keyboard-reachable description. No demo may start a host process or mutate +terminal, locale, font, codepage, or keyboard state. + +## Proof Order + +1. Create `pr-evidence.md` and pre-name `closeout-evidence.md` before runtime edits. +2. Review historical sources and the full compile/linked-source surface. +3. Add the complete failing `Terminal` vertical-slice matrix. +4. Implement shared presentation plus `Terminal`; record the passing slice. +5. Add grouped failing Cyrillic/Fonts proof, then implementations. +6. Add grouped failing ETerm/XTerm proof, then immutable manifest demos. +7. Complete five-example, host, narrow-viewport, guide, governance, and validation matrices. + +## Versioned Validation + +Before every `dotnet build` or `dotnet test`, increment only the manual build +counter. Use `1.22..`. + +```bash +git diff --check +dotnet format --verify-no-changes --no-restore +dotnet test tests/TuiVision.Examples.SmokeTests/ --configuration Release +dotnet test --configuration Release +xmllint --noout coverlet.runsettings +dotnet test --configuration Release --collect:"XPlat Code Coverage" --settings coverlet.runsettings +docfx docfx.json +cd tests/web-a11y && npm run test:docfx +``` + +Record exact command, version, result, counts, coverage, skipped trigger, and +failure boundary. Never commit `_site/`, generated `api/`, test results, caches, +logs, credentials, or validation output. + +## Completion + +- Five framework decisions, primary proof rows, host rows, and governance rows are complete. +- Lastenheft is archived with `.022-wave4-visual-component-porting.md` suffix. +- Required checks pass, actionable threads are zero, and authorized merge/sync is recorded. +- Self-invalidating reviewed-head and post-merge facts use exactly one causal closeout path. diff --git a/specs/022-wave4-visual-component-porting/research.md b/specs/022-wave4-visual-component-porting/research.md new file mode 100644 index 00000000..8f49d91d --- /dev/null +++ b/specs/022-wave4-visual-component-porting/research.md @@ -0,0 +1,127 @@ +# Research: Wave-4 Visual Component Porting + +## R1 - Feature 021 Baseline + +**Decision**: Reuse the controlled terminal session, bounded emulation subset, +KOI8-R/Unicode mapper, raw font-fixture validator, closed profile/fallback, +host-classification, and `TTerminalView` contracts from Feature 021. + +**Rationale**: Feature 022 demonstrates these contracts visibly. Reimplementation +inside examples would create local special logic and competing parsers. + +**Rejected**: Reopening 021, local escape parsing, host codec use, or a second terminal view. + +## R2 - Vertical Slice + +**Decision**: Implement `Terminal` first with one complete project-local red +matrix for first frame, input/output, cursor/attribute action, rejection, +recovery, fallback, description, cell proof, and quit. + +**Rationale**: It exercises the entire Driver/Core/Controls/app-loop chain and +detects ownership or harness gaps before four compositions repeat the pattern. + +**Rejected**: Starting with a static manifest demo or implementing all five before proof. + +## R3 - Shared Presentation Boundary + +**Decision**: Link `examples/Shared/Wave4Runtime.cs` into all five projects for +status drawing, description, bounded regions, manifest display, and scripted events. + +**Rationale**: These are repeated presentation/proof concerns. Domain contracts +stay in framework types and per-example compositions. + +**Rejected**: Five copies or a public framework API without a reusable framework gap. + +## R4 - Linked-source Identity + +**Decision**: Cross-example matrix tests use public state or delegates and do +not cast linked `Wave4Runtime` types across example assemblies. + +**Rationale**: One source file linked into five assemblies creates five CLR type identities. + +## R5 - Cyrillic Intent + +**Decision**: Show a fixed labeled KOI8-R sample through the 021 mapper, plus +direct/replaced/invalid/unsupported outcomes and visible status. + +**Rationale**: The historical Linux/X11 examples teach Cyrillic display and +host setup constraints; deterministic Unicode cells preserve that intent safely. + +**Rejected**: `LANG` mutation, host codepages, `/dev/vcsa`, `consolechars`, +`loadkeys`, root setup scripts, or claims of physical console parity. + +## R6 - Font Fixture + +**Decision**: Copy one exact 4,096-byte raw 8x16 fixture into the modern Fonts +project, validate before publication, and render a known nonblank glyph as a +text-first pixel matrix. + +**Rationale**: A project-owned fixture makes normal startup and tests independent +of repository root and host font installation while retaining historical origin. + +**Rejected**: Executing `genraw`, parsing SFT/compressed formats, installing a +font, or using a blank-only fixture as visible proof. + +## R7 - ETerm Resource Boundary + +**Decision**: Represent selected menu/theme/presentation values as immutable +typed entries with exact historical source identity. + +**Rationale**: `menus.cfg` and `theme.cfg` are configuration-only teaching +sources. A visible manifest is useful without claiming native parser compatibility. + +**Rejected**: General ETerm parser, arbitrary path loading, or terminal theme mutation. + +## R8 - XTerm Resource Boundary + +**Decision**: Represent selected `Xterm.res` values and the accepted 021 +sequence/capability subset as immutable typed entries with unsupported boundaries. + +**Rationale**: Resource/protocol intent becomes visible while input translation +and session emulation remain owned by existing framework contracts. + +**Rejected**: X resource database parser, terminfo/native XTerm integration, or full emulator claims. + +## R9 - Primary Visual Proof + +**Decision**: Require real app-loop dispatch plus concrete state, exact view, +dynamic status, description route, and rendered cell proof for every example. + +**Rationale**: This is the proven Wave-1 through Wave-3 acceptance pattern and +excludes helper-only, screenshot-only, or text-description-only proof. + +## R10 - Host Evidence + +**Decision**: Maintain distinct `DeterministicInProcess`, `RemoteCI`, and +`PhysicalObservation` rows. Missing physical conditions are `NotRun` with risk +and re-evaluation trigger. + +**Rationale**: A runner OS label does not prove an interactive terminal, font, +locale, or emulator condition. Features 020 and 021 established this evidence rule. + +## R11 - Security and Resource Bounds + +**Decision**: Keep assets source-controlled/read-only, validate fixture shape, +use fixed typed manifests, enforce the 021 parser bounds, and publish safe fallback. + +**Rationale**: The relevant STRIDE/CIA/CAPEC risks are malformed input, state +corruption, resource exhaustion, spoofed capability/source claims, and host mutation. + +## R12 - Governance Applicability + +**Decision**: Apply NIST SSDF/CWE, proportional STRIDE/CAPEC, iSAQB reuse, +A11Y, host cross-platform proof, and agent parity. Keep ASVS, new supply-chain +evidence, AI/regulatory, S-ADR/Zero Trust/SAMM/C3A/C5, and script parity +trigger-based `N/A` unless actual scope changes. + +## R13 - Documentation and Delivery + +**Decision**: Add five bilingual guides, update navigation/index, run DocFX, +axe, and lynx, and use `MergeAndSync` with exact evidence paths and one pre-named closeout. + +## R14 - Retrospective Boundary + +**Decision**: Record 022 field observations in feature evidence; modify generic +autonomous workflow only through a separate non-empty post-feature PR and Home-Baseline handoff. + +**Rationale**: Product implementation and preset productization remain separate review units. diff --git a/specs/022-wave4-visual-component-porting/spec.md b/specs/022-wave4-visual-component-porting/spec.md new file mode 100644 index 00000000..f55861c4 --- /dev/null +++ b/specs/022-wave4-visual-component-porting/spec.md @@ -0,0 +1,391 @@ +# Feature Specification: Wave-4 Visual Component Porting + +**Feature Branch**: `022-wave4-visual-component-porting` +**Created**: 2026-07-12 +**Status**: Draft +**Binding Input**: `Lastenheft_Wave4-Visual-Component-Porting.md` + +## Clarifications + +### Session 2026-07-12 (Pass 1) + +- Q: Welche Projektordner sind bindend? → A: `examples/Cyrillic`, + `examples/ETerm`, `examples/Fonts`, `examples/Terminal` und `examples/XTerm`. +- Q: Darf `Terminal` einen Prozess, eine Shell oder ein PTY starten? → A: Nein. + Die Demo verwendet ausschließlich die kontrollierte In-Process-Sitzung aus + Feature 021 und zeigt Eingabe, Ausgabe, Cursor, Attribute und Status sichtbar. +- Q: Müssen ETerm- und XTerm-Legacy-Konfigurationssyntax vollständig geparst + werden? → A: Nein. Repräsentative historische Werte werden als + source-controlled, unveränderliche Demo-Manifeste mit Herkunft und + `IntentionalDeviation` gezeigt; kein allgemeiner Legacy-Parser entsteht. +- Q: Welcher Font-Nachweis ist bindend? → A: `Fonts` besitzt eine kontrollierte + exakte 8x16-/256-Glyphen-/4.096-Byte-Fixture, validiert sie über den 021-Vertrag + und rendert mindestens eine erkennbare Glyphenmatrix. Generatoren und + Host-Fontinstallation bleiben ausgeschlossen. +- Q: Wie wird Plattform-Evidence klassifiziert? → A: `DeterministicInProcess`, + `RemoteCI` und `PhysicalObservation` bleiben getrennt. Nicht verfügbare + physische Bedingungen sind `NotRun`, nie ein implizites Pass. +- Q: Welche Bedien- und Datengrenze gilt? → A: Tastaturbedienung ist + verpflichtend; Maus ist ergänzend. Alle Assets sind eingebettet oder + source-controlled und read-only. Kein Beispiel liest beliebige Nutzerpfade, + schreibt Nutzerdateien oder verändert Host-Terminal, Font, Codepage oder + Keyboardmap. +- Q: Welche kleine Bildschirmgrenze gilt? → A: Der normale Proof nutzt eine + stabile Standardfläche; bei engem Viewport bleiben Identität, Status, + Beschreibungspfad und ehrlicher Fallback textorientiert erkennbar, auch wenn + Raster oder Listen gekürzt werden. + +### Session 2026-07-12 (Pass 2) + +Keine weitere Frage würde Scope, Plan, Task-Zuschnitt, Validierung oder +Abnahme materiell ändern. Die sieben Entscheidungen oben, die 021-Verträge und +das verbindliche Drei-Schichten-Modell reichen für die Planung aus. + +No further question would materially change scope, planning, task shaping, +validation, or acceptance. The seven decisions above, the Feature-021 +contracts, and the binding three-layer model are sufficient for planning. + +## User Scenarios & Testing *(mandatory)* + +### User Story 1 - Sichtbare Terminal-Sitzung / Visible Terminal Session (Priority: P1) + +Als lernende Person möchte ich `Terminal` normal starten und eine echte +TuiVision-Terminalansicht mit kontrollierter Eingabe, sichtbarer Ausgabe, +Cursor, Status und Beschreibung bedienen, ohne dass ein Hostprozess gestartet +wird. / As a learner, I want to start `Terminal` normally and use a real +TuiVision terminal view with controlled input, visible output, cursor, status, +and description without starting a host process. + +**Why this priority**: Dieser Slice beweist früh, dass die in Feature 021 +gehärteten Verträge in einer normalen sichtbaren Anwendung funktionieren. + +**Independent Test**: Ein App-Loop-Smoke injiziert Text und einen +Cursorbefehl, prüft Session-Zustand, konkrete View-Identität, Statuszeile und +gerenderte Zellen und beendet die Anwendung über einen echten Quit-Pfad. + +**Acceptance Scenarios**: + +1. **Given** `Terminal` starts normally, **When** its first frame is rendered, + **Then** the terminal view, welcome output, cursor, capability/profile status, + and keyboard-reachable description are visible. +2. **Given** the application loop is running, **When** controlled text and a + supported cursor action are injected, **Then** session state, cursor status, + view tree, and rendered cells agree. +3. **Given** terminal capability is unavailable, **When** the demo starts, + **Then** a stable text-first fallback remains visible and keyboard quit works. + +--- + +### User Story 2 - Kyrillisch und Font-Raster / Cyrillic and Font Grids (Priority: P1) + +Als Nutzerin möchte ich `Cyrillic` und `Fonts` starten und erkennbare Zeichen- +beziehungsweise Pixelraster sehen, damit direkte Abbildung, Ersatzzeichen, +Fontquelle und Grenzen verständlich werden. / As a user, I want to start +`Cyrillic` and `Fonts` and see recognizable character or pixel grids so direct +mapping, replacement, font source, and boundaries are understandable. + +**Why this priority**: Diese beiden Beispiele liefern den sichtbaren +Lernnachweis für Charset- und Fontverträge statt nur Metadaten zu behaupten. + +**Independent Test**: Getrennte App-Loop-Smokes prüfen bekannte kyrillische +Zellen, Mapping-/Fallbackstatus sowie eine validierte Font-Glyphe mit +konkreten Rasterzellen, Status und Beschreibung. + +**Acceptance Scenarios**: + +1. **Given** `Cyrillic` starts, **When** the KOI8-R sample is mapped, **Then** + known Cyrillic characters, charset identity, mapping result, and fallback + explanation are visible without host-locale dependence. +2. **Given** an invalid or unsupported source value, **When** mapping runs, + **Then** U+FFFD or `Unsupported` is shown consistently in state, status, and cells. +3. **Given** `Fonts` starts with its controlled fixture, **When** the selected + glyph is rendered, **Then** fixture identity, exact metadata, and a recognizable + 8x16 pixel matrix are visible. + +--- + +### User Story 3 - ETerm- und XTerm-Resource-Demos / ETerm and XTerm Resource Demos (Priority: P1) + +Als Maintainer möchte ich `ETerm` und `XTerm` sinnvoll starten können, obwohl +die historischen Quellen überwiegend Konfiguration oder Resources sind, damit +die Portierung weder leer noch fälschlich als vollständiger Emulator erscheint. +/ As a maintainer, I want to start `ETerm` and `XTerm` meaningfully even though +their historical sources are mainly configuration or resources, so the port is +neither empty nor presented as a complete emulator. + +**Why this priority**: Sichtbare, begrenzte Resource-Manifeste erhalten die +historische Lehrabsicht ohne neue unsichere Parser- oder Hostgrenzen. + +**Independent Test**: Jede Demo zeigt mehrere repräsentative benannte Werte, +Herkunft, unterstützte Teilmenge, bewusste Abweichung und Fallback; der Smoke +prüft Zustand, View, Status und Zellen durch den App-Loop. + +**Acceptance Scenarios**: + +1. **Given** `ETerm` starts, **When** its manifest is displayed, **Then** menu, + theme, source identity, and resource-only deviation are visible. +2. **Given** `XTerm` starts, **When** its manifest is displayed, **Then** named + resource values, supported sequence subset, and unsupported host boundaries + are visible. +3. **Given** a requested manifest item is outside the accepted subset, **When** + it is selected, **Then** the demo shows a stable unsupported/fallback result + rather than guessing or parsing arbitrary data. + +--- + +### User Story 4 - Plattformbewusster Lernpfad / Platform-Aware Learning Path (Priority: P2) + +Als textorientiert arbeitende Person möchte ich jede Wave-4-Demo über +Tastatur, Statuszeile, Beschreibung, Guide und Evidence verstehen können und +erkennen, welche Aussagen deterministisch, remote oder physisch geprüft wurden. +/ As a text-oriented user, I want to understand every Wave-4 demo through +keyboard, status line, description, guide, and evidence and see which claims +were proven deterministically, remotely, or physically. + +**Independent Test**: Jeder Guide und jede Evidence-Zeile nennt Start, +Hauptfläche, Bedienpfad, Status, Beschreibung, historische Quelle, +Framework-Entscheidung, Host-Evidence-Klasse, Fallback und Proof in Deutsch +zuerst und Englisch danach auf ungefähr CEFR-B2. + +**Acceptance Scenarios**: + +1. **Given** any Wave-4 demo or guide, **When** it is used without color or + pointer input, **Then** purpose, current state, next action, and fallback + remain understandable. +2. **Given** a physical host condition was unavailable, **When** evidence is + reviewed, **Then** it is `NotRun` with residual risk and re-evaluation trigger. +3. **Given** implementation completion, **When** the acceptance matrix is + inspected, **Then** all five examples have historical, framework, app-loop, + view, cell, A11Y, host, and validation evidence. + +### Edge Cases + +- A viewport is too small for the full terminal, character grid, glyph raster, + or resource list. +- A terminal sequence is malformed, truncated, unsupported, overlong, or + outside the Feature-021 subset. +- A KOI8-R value maps directly, is replaced, is invalid, or names an unsupported charset. +- A font fixture has the wrong length, geometry, format, source identity, or + contains a blank selected glyph. +- An ETerm/XTerm manifest item is absent, duplicated, outside the accepted + representative set, or mistakenly presented as native configuration support. +- Host capability is disabled, unsupported, redirected, or physically unobserved. +- A visible view exists in the tree but is clipped or absent from rendered cells. +- A helper bypasses `app.Run()` or duplicates reusable 021 framework behavior. +- A discovered full-emulation, native-resource, process, host-mutation, A11Y, + or architecture gap is too broad for 022 and must become `FollowUpHardening`. + +## Requirements *(mandatory)* + +### Functional Requirements + +- **FR-001**: The feature MUST cover exactly `Cyrillic`, `ETerm`, `Fonts`, + `Terminal`, and `XTerm` as startable example projects under `examples/`. +- **FR-002**: Feature 021 session, emulation, mapping, font-fixture, profile, + host-evidence, and Controls contracts MUST be the accepted technical baseline; + examples MUST NOT reimplement those reusable contracts locally. +- **FR-003**: Every example MUST show a visible terminal, charset, font, or + resource composition during normal CLI startup, or a stable visible fallback. +- **FR-004**: Every example MUST implement the three-layer model: visible main + area, real status line or equivalent framework status surface, and + keyboard-reachable Help, Description, or About content. +- **FR-005**: Startup success, static status, direct helper output, explanatory + text alone, or host screenshot alone MUST NOT count as primary visual proof. +- **FR-006**: Primary smokes MUST run `app.Run()` or an equivalent real + application loop and inject events, commands, or keys through real dispatch. +- **FR-007**: Every primary smoke MUST verify concrete state, exact view-tree + identity, and rendered buffer/cell visibility at stable positions or regions. +- **FR-008**: Direct helpers MAY support deterministic setup or supplemental + assertions but MUST NOT be the primary acceptance proof. +- **FR-009**: `Terminal` MUST expose a visible `TTerminalView`-equivalent + composition with controlled input/output, cursor, attributes, session/profile/ + capability status, quit, and description without process, shell, or PTY access. +- **FR-010**: `Terminal` MUST visibly prove at least plain text, one accepted + cursor or attribute action, one rejected or unsupported action, reset or + fallback state, and a usable next independent input. +- **FR-011**: `Cyrillic` MUST show a deterministic KOI8-R/Unicode character grid + with direct, replacement, invalid, and unsupported outcomes independent of host locale. +- **FR-012**: `Cyrillic` MUST expose source charset, source value, visible glyph, + mapping outcome, fallback reason, status, and description. +- **FR-013**: `Fonts` MUST validate one exact source-controlled raw + 8x16/256-glyph/16-byte-stride/4,096-byte fixture before displaying it. +- **FR-014**: `Fonts` MUST show fixture identity, metadata, selected glyph, + recognizable pixel rows, status, description, and invalid-fixture fallback. +- **FR-015**: Historical font generators, setup scripts, compressed/native font + formats, host font installation, and automatic host font restoration MUST NOT run. +- **FR-016**: `ETerm` MUST show a source-controlled immutable manifest with + representative historical menu, theme, and terminal-presentation values. +- **FR-017**: `XTerm` MUST show a source-controlled immutable manifest with + representative resource values, the accepted 021 sequence/capability subset, + and explicit unsupported host/native-resource boundaries. +- **FR-018**: ETerm/XTerm legacy syntax parsing MUST be an + `IntentionalDeviation`; examples MUST NOT claim general native config or resource support. +- **FR-019**: Missing or out-of-subset manifest values MUST produce stable + visible unsupported/fallback state without arbitrary file parsing. +- **FR-020**: All example assets MUST be embedded or source-controlled and + read-only; no example may discover arbitrary user paths, persist user data, + or mutate terminal, font, codepage, locale, keyboard map, or audio state. +- **FR-021**: macOS, Linux, Windows/WSL, redirected/headless, and unavailable + capability conditions MUST have reviewable host rows with explicit evidence class. +- **FR-022**: Deterministic in-process, remote CI, and physical observations + MUST remain separate; an unavailable physical condition MUST be `NotRun`. +- **FR-023**: Every example MUST be checked against relevant `.c`, `.cc`, `.h`, + config, resource, README, script, and fixture files under `tv203s/` read-only. +- **FR-024**: Every intentional historical deviation MUST record source, + retained intent, modern behavior, rationale, and learner-visible effect. +- **FR-025**: Feature evidence MUST identify the existing framework components + used for main area, status, description, operation, host fallback, and proof. +- **FR-026**: Every example MUST receive exactly one framework decision: + `UseExistingFramework`, `SmallFrameworkFix`, `IntentionalDeviation`, or + `FollowUpHardening`. +- **FR-027**: Reusable logic MUST NOT remain duplicated in example-local code; + it MUST use existing framework, a narrow tested fix, or a named follow-up. +- **FR-028**: `FollowUpHardening` MUST name the issue, out-of-scope reason, + owner or tracked boundary, residual risk, and re-evaluation trigger. +- **FR-029**: Every example MUST remain keyboard operable. Mouse support MAY + supplement interaction but MUST NOT be required to reach primary behavior, + description, fallback, or quit. +- **FR-030**: Descriptions, guides, and `examples/README.md` MUST be German + first and English second at approximately CEFR-B2 and remain text-first usable. +- **FR-031**: `pr-evidence.md` MUST trace every example through historical + intent, framework decision, visible state, app-loop/view/cell proof, host + evidence, safety/A11Y review, validation, residual risk, and follow-up. +- **FR-032**: New or changed non-trivial logic MUST receive selective didactic + inline-comment review under the accepted moderate reason-focused policy. +- **FR-033**: Project statistics, Pflichtenheft completion/next-intake markers, + five maintained agent contexts, and affected guides/indexes MUST update together. +- **FR-034**: The completed Lastenheft MUST be archived with the exact + `022-wave4-visual-component-porting` suffix through the repository workflow. +- **FR-035**: The feature MUST NOT add full terminal emulation, process/shell/ + PTY integration, host mutation, Wave-5/TP7 examples, broad framework redesign, + arbitrary user data, new runtime dependencies, or edits under `tv203s/`. +- **FR-036**: Generated DocFX output, API YAML, caches, logs, credentials, test + output, and validation output MUST remain untracked. + +### Constitution Requirements *(mandatory)* + +- **CR-001**: The TuiVision Level-2 registry entry, current Constitution, C# as + approved memory-safe language, and .NET 10/MSTest environment are binding. +- **CR-002**: NIST SSDF, CWE Top 25, secure input/resource validation, size + bounds, fail-safe fallback, and least host privilege are applicable. +- **CR-003**: STRIDE/CIA/CAPEC review is applicable to terminal input, resource + identity, state integrity, resource exhaustion, and false capability claims. +- **CR-004**: OWASP ASVS is `N/A` unless web/API/HTTP/auth scope enters. +- **CR-005**: Repository SBOM, VEX, SLSA, OpenSSF, and supply-chain controls + remain applicable at repository level; new feature evidence is `N/A` unless + dependency, packaging, provenance, or distributable scope changes. +- **CR-006**: AI-SBOM, NIS2, CRA, EU AI Act, and DORA are `N/A` while AI is + development tooling and no regulated, operated, or released AI component enters. +- **CR-007**: S-ADR, arc42 security-concept changes, Zero Trust, SAMM, BSI C3A, + and BSI C5 are `N/A` unless architecture, cloud, provider, deployment, or + distributed-service boundaries change. +- **CR-008**: iSAQB/arc42 goals, runtime/component views, quality scenarios, + risks, and debt MUST record reuse of the 021 Driver/Core/Controls ownership. +- **CR-009**: WCAG 2.2 AA applicability, keyboard completeness, text status, + visible non-color-only state, bilingual CEFR-B2 docs, and didactic comments apply. +- **CR-010**: Cross-platform governance applies to host evidence and asset/path + behavior; Bash/PowerShell script parity is `N/A` unless a script changes. +- **CR-011**: Agent parity applies when active context or shared guidance changes; + all five maintained surfaces MUST be reviewed together. +- **CR-012**: `.specify/templates/` are `N/A` unless a generic autonomous-run + correction is accepted through the separate retrospective workflow. +- **CR-013**: All six installed preset rows MUST record `Applicable`, `N/A`, or + `Open` with rationale, evidence, owner, reviewer, date/result, residual risk, + follow-up, and re-evaluation trigger. +- **CR-014**: Historical source review MUST include relevant implementations, + headers, configs, resources, scripts, and binary fixture metadata read-only. +- **CR-015**: Remote gates MUST be verified on an unchanged reviewed head; + self-invalidating and post-merge facts MUST use one pre-named closeout path. +- **CR-016**: Every remote/delivery task MUST name the exact repository evidence + path that records its acceptance result. + +### Key Entities + +- **Wave4Example**: Project identity, historical sources, main component, + status, description, operation, framework decision, and primary proof. +- **VisibleTerminalState**: Session/profile/capability, cursor, attributes, + visible cells, last outcome, and fallback. +- **CharacterGridState**: Source charset/value, mapped glyph, outcome, grid + position, replacement, and reason. +- **FontGridState**: Fixture identity, exact metadata, selected glyph, 16 row + bytes, visible pixels, and validation state. +- **ResourceManifestState**: Source identity, representative immutable entries, + accepted subset, selected entry, and intentional-deviation boundary. +- **HostEvidenceRecord**: Host family, condition, evidence class, capability, + result, residual risk, and re-evaluation trigger. +- **FrameworkUsageDecision**: One allowed decision plus rationale, evidence, + residual risk, owner, and follow-up boundary. +- **ProofRecord**: App-loop route, concrete state, view identity, cell region, + operation, validation command, and result. + +## Success Criteria *(mandatory)* + +### Measurable Outcomes + +- **SC-001**: All five documented CLI starts launch the intended project and + show a recognizable main composition, dynamic status, and description route. +- **SC-002**: Five of five examples have exactly one framework decision and + complete historical-source, operation, status, description, host, and proof evidence. +- **SC-003**: Primary smokes cover all five examples and each uses the app loop + plus concrete state, exact view identity, and rendered cell-region proof. +- **SC-004**: `Terminal` proves accepted input, one supported cursor/attribute + action, one rejected/unsupported action, recovery, fallback, and quit without host process. +- **SC-005**: `Cyrillic` proves direct, replacement, invalid, and unsupported + mapping states with host-independent visible glyph/status agreement. +- **SC-006**: `Fonts` proves one exact 4,096-byte fixture, a recognizable 8x16 + glyph, and at least four distinct invalid-fixture/fallback classes. +- **SC-007**: `ETerm` and `XTerm` each prove at least three representative + immutable manifest entries plus one out-of-subset fallback without legacy parser claims. +- **SC-008**: macOS, Linux, Windows/WSL, and redirected/headless rows each have + explicit deterministic, remote, physical, unsupported, or `NotRun` evidence. +- **SC-009**: Five guides plus `examples/README.md` pass DE-first/EN-second + CEFR-B2, keyboard/text-first, semantic, historical-deviation, and fallback review. +- **SC-010**: Targeted Wave-4 smokes, full Release tests, canonical coverage, + format, DocFX, web-A11Y, text-browser, secret, generated-output, and remote + gates pass with exact results recorded. +- **SC-011**: The final diff contains no full emulator, process/shell/PTY, + host mutation, new dependency, Wave-5/TP7, arbitrary user-data, generated, + or `tv203s/` change. +- **SC-012**: All six governance presets and all remote tasks have complete + evidence fields/paths; Analyze converges with no Critical, High, or unresolved Medium. +- **SC-013**: Authorized delivery ends with green required checks, zero + actionable threads, merge/branch cleanup evidence, and clean local `main` + equal to `origin/main`. + +## Assumptions + +- Features 019, 020, and 021 are accepted prerequisites and are not reopened. +- Existing TuiVision controls and 021 contracts can compose the five demos; + only narrow, test-backed framework fixes may address a proven reusable gap. +- Historical ETerm/XTerm config/resource sources provide intent and selected + values, not a requirement for general native parser compatibility. +- A source-controlled exact raw font fixture may be copied into the modern + example as a read-only asset with origin recorded. +- Keyboard operation is mandatory; mouse remains supplemental. +- Runtime/product AI, cloud services, databases, network services, and user + persistence are absent. + +## Scope Boundaries + +### In Scope + +- Five visible Wave-4 example projects, app-loop/view/cell smokes, controlled + assets/manifests, guides, evidence, governance, routing, statistics, archive, + and only proven narrow reusable fixes. + +### Out of Scope + +- Full ANSI/VT/XTerm/Eterm emulation or native config/resource compatibility +- process, shell, PTY, external command, audio, terminal/font/codepage/keyboard mutation +- Wave-5, TP7, editor/help/resource Wave-3 work, or A11Y-framework Feature 023 +- arbitrary user file discovery, writes, persistence, database, network, or cloud +- new runtime dependencies, generated documentation output, and `tv203s/` edits + +### Decision and Follow-up Model + +- Each example uses exactly one of `UseExistingFramework`, + `SmallFrameworkFix`, `IntentionalDeviation`, or `FollowUpHardening`. +- `FollowUpHardening` records issue, scope reason, owner/boundary, residual risk, + and re-evaluation trigger; it does not silently expand Feature 022. +- Host evidence uses `DeterministicInProcess`, `RemoteCI`, or + `PhysicalObservation`; unavailable physical proof is `NotRun`. diff --git a/specs/022-wave4-visual-component-porting/tasks.md b/specs/022-wave4-visual-component-porting/tasks.md new file mode 100644 index 00000000..b26308d3 --- /dev/null +++ b/specs/022-wave4-visual-component-porting/tasks.md @@ -0,0 +1,225 @@ +# Tasks: Wave-4 Visual Component Porting + +**Input**: Accepted artifacts under `specs/022-wave4-visual-component-porting/` +**Delivery mode**: `MergeAndSync` +**Acceptance ledger**: `specs/022-wave4-visual-component-porting/pr-evidence.md` + +Tasks are sequential because most slices share the smoke project, linked +runtime, solution, evidence, version, documentation, statistics, agent, or +delivery files. No task-level `[P]` marker is used where ownership could overlap. + +## Phase 1: Preflight and Evidence Foundation + +- [X] T001 Create `specs/022-wave4-visual-component-porting/pr-evidence.md` from the autonomous evidence template before runtime/test edits +- [X] T002 Verify branch, ancestry from synchronized `main`, `.specify/feature.json`, and intake order; record exact state in `pr-evidence.md` +- [X] T003 Run `specify check` and PowerShell prerequisite checks with tasks included; record results in `pr-evidence.md` +- [X] T004 Verify every 022 checklist has zero incomplete items and record counts in `pr-evidence.md` +- [X] T005 Read AGENTS, Constitution, binding Lastenheft, and all 022 artifacts; record material conflicts or `None` +- [X] T006 Verify the six installed preset names, versions, and priorities; record them in `pr-evidence.md` +- [X] T007 Add five-example, visible-state, historical, framework, host, asset, proof, and comment-decision tables to `pr-evidence.md` +- [X] T008 Add governance rows with owner, reviewer, date/result, residual risk, follow-up, and re-evaluation trigger +- [X] T009 Add requirement/SC coverage, validation, generated-output hygiene, remote delivery, causal closeout, and retrospective tables +- [X] T010 Pre-name `specs/022-wave4-visual-component-porting/closeout-evidence.md` for self-invalidating reviewed-head and post-merge facts +- [X] T011 Record `speckit-constitution` unchanged and `speckit-taskstoissues` N/A because one dependency-ordered feature PR is the delivery unit +- [X] T012 Record Specify, two Clarify passes, checklist convergence, Plan, Plan Review, Tasks, and pending Analyze gates +- [X] T013 Scan 022 artifacts for unresolved markers, temporary requirement IDs, duplicate tasks, and task gaps; record result +- [X] T014 Record hard scope boundaries, `MergeAndSync` authority, missing-review semantics, and narrow admin-bypass policy +- [X] T015 Prove `git diff -- tv203s/` contains no implementation change before historical review + +## Phase 2: Compile Surface, Historical Intent, and Architecture Gate + +- [X] T016 Review project graph, example references, solution entries, and absence of dependency cycles +- [X] T017 Review public types/imports/XML docs/nullability/lifecycle/naming before the first red command +- [X] T018 Review smoke harness, event queues, app-loop ownership, focus, status, description, view-tree, and buffer/cell helpers +- [X] T019 Review linked-source assembly identity and define public-state/delegate cross-example proof +- [X] T020 Review Feature-021 `TerminalSession`, mapper, fixture, profile, host, `TTerminalView`, and tests as binding reusable contracts +- [X] T021 Review historical `terminal.cc` and `terminal.h` read-only; record retained session/view intent and process deviations +- [X] T022 Review both Cyrillic `test.cc` files, README, ACM data, and setup scripts read-only; record mapping intent and prohibited host actions +- [X] T023 Review Fonts `test.cc`, `genraw.cc`, `font.016`, `ocr.sft`, `fontcoll.cc`, and `fontcoll.h` read-only; record fixture/generator boundaries +- [X] T024 Review ETerm `menus.cfg`, `theme.cfg`, docs and XTerm `Xterm.res` plus relevant xterm key/display/screen sources read-only +- [X] T025 Verify historical `font.016` shape and select a nonblank glyph without executing a generator +- [X] T026 Classify each example with one preliminary framework decision and exact reused components +- [X] T027 Record shared presentation-only ownership and reject reusable terminal/mapping/font/profile/host logic in `examples/` +- [X] T028 Record exact historical source inventory, retained purpose, intentional deviations, and non-executed scripts/generators +- [X] T029 Record compile-surface findings and any required pre-red correction in `pr-evidence.md` +- [X] T030 Re-prove `tv203s/` remains unchanged after read-only review + +## Phase 3: User Story 1 - Failing Terminal Vertical Slice + +- [X] T031 Add five Wave-4 project skeletons and references without domain implementation; align `TuiVision.sln` and `coverlet.runsettings` with the new example assemblies +- [X] T032 Link `examples/Shared/Wave4Runtime.cs` into all five projects and add smoke-project references without implementing behavior +- [X] T033 Add failing `TerminalSmokeTests.cs` construction and first-frame assertions +- [X] T034 Add failing controlled text input/output, cursor, attribute, session/profile/capability assertions +- [X] T035 Add failing app-loop exact `TTerminalView` identity and rendered-cell region proof +- [X] T036 Add failing status-line and keyboard-reachable description command proof +- [X] T037 Add failing accepted cursor/attribute action with exact before/after state +- [X] T038 Add failing rejected/unsupported action atomicity and next-input recovery proof +- [X] T039 Add failing reset and unavailable-capability visible fallback proof +- [X] T040 Add failing deterministic quit and post-run state proof +- [X] T041 Add failing narrow-viewport identity/status/fallback/clipping proof +- [X] T042 Review the grouped Terminal red matrix so every expected failure and ownership boundary is explicit +- [X] T043 Increment manual build counter and run focused Terminal matrix expecting the documented red compile/contract boundary +- [X] T044 Record red test names, state/view/cell expectations, version, and proof limits in `pr-evidence.md` +- [X] T045 Recheck imports, XML docs, harness helpers, ownership, and linked-source identity exposed by the red surface + +## Phase 4: User Story 1 - Terminal and Shared Presentation Implementation + +- [X] T046 Add bilingual XML-documented `Wave4StatusLine`, `Wave4Application`, and presentation helpers in `examples/Shared/Wave4Runtime.cs` +- [X] T047 Implement deterministic scripted event queue, quit fallback, dynamic status, stable main region, and screen-region conversion +- [X] T048 Implement keyboard-reachable bilingual description and text-first fallback presentation +- [X] T049 Add `TerminalApp` using one existing `TerminalSession` and `TTerminalView` without duplicate parser or host access +- [X] T050 Implement controlled first-frame welcome text, profile/charset/font/capability status, and exact visible view identity +- [X] T051 Implement accepted text/cursor/attribute command routes through real event dispatch +- [X] T052 Implement visible rejected/unsupported outcome, atomic recovery, reset, and next independent input +- [X] T053 Implement unavailable-capability fallback preserving description and quit +- [X] T054 Implement standard and narrow viewport composition without status/identity loss +- [X] T055 Review shared/Terminal code for concise DE-first/EN-second why/trade-off/A11Y/proof-boundary comments +- [X] T056 Increment manual build counter and run focused Terminal tests to green; record exact count/version/result +- [X] T057 Record Terminal framework decision, reused 021 contracts, app-loop/state/view/cell proof, and process boundary +- [X] T058 Confirm no process, shell, PTY, audio, font, codepage, locale, keyboard-map, or terminal-setting API enters Terminal/shared code +- [X] T059 Add Terminal normal-start guide draft and README matrix row only after the slice is green +- [X] T060 Re-prove the shared helper contains presentation/proof only and no duplicate 021 domain behavior + +## Phase 5: User Story 2 - Cyrillic and Fonts Test-First Delivery + +- [X] T061 Add failing `CyrillicSmokeTests.cs` first-frame labeled KOI8-R/Unicode grid proof +- [X] T062 Add failing direct, replacement, invalid, and unsupported mapping state/status/cell cases +- [X] T063 Add failing host-locale-independent and narrow-viewport fallback cases +- [X] T064 Add failing Cyrillic description, exact view identity, app-loop command, and quit proof +- [X] T065 Add exact source-controlled `examples/Fonts/Fixtures/font-8x16.bin` with documented historical origin +- [X] T066 Add failing `FontsSmokeTests.cs` exact metadata and nonblank selected-glyph proof +- [X] T067 Add failing recognizable 8x16 pixel-region state/view/cell proof +- [X] T068 Add failing wrong length, geometry, stride, format/source, and blank-selected-glyph fallback classes +- [X] T069 Add failing Fonts description, status, narrow-viewport, app-loop command, and quit proof +- [X] T070 Review grouped Cyrillic/Fonts red matrices for explicit expected failures and separate ownership +- [X] T071 Increment manual build counter and run Cyrillic/Fonts matrices expecting documented red boundaries +- [X] T072 Implement `CyrillicApp` using `TerminalCharsetMapper` and fixed controlled sample data +- [X] T073 Render labeled source bytes, glyphs, outcomes, reasons, status, and fallback without host locale/codepage +- [X] T074 Implement Cyrillic mapping-cycle command, description, narrow viewport, and quit route +- [X] T075 Implement `FontsApp` using `BitmapFontFixture` and only the project-owned copied fixture +- [X] T076 Render fixture metadata and one known nonblank glyph as a stable non-color-only 8x16 pixel matrix +- [X] T077 Implement invalid/unsupported fixture fallback, selected-glyph command, description, narrow viewport, and quit +- [X] T078 Review mapping/font/raster code for historical, replacement, host-independence, and proof-boundary comments +- [X] T079 Increment manual build counter and run Cyrillic/Fonts tests to green; record exact counts/version/results +- [X] T080 Record Cyrillic and Fonts framework decisions, historical deviations, asset hashes/shape, proof regions, and host boundaries +- [X] T081 Confirm no historical setup script/generator, host codec/font API, arbitrary path, or write operation was executed +- [X] T082 Add Cyrillic and Fonts guide drafts plus README matrix rows only after both slices are green + +## Phase 6: User Story 3 - ETerm and XTerm Manifest Delivery + +- [X] T083 Add failing `ETermSmokeTests.cs` first-frame proof for at least three menu/theme/presentation entries +- [X] T084 Add failing ETerm source identity, selection command, dynamic status, description, view, and cell proof +- [X] T085 Add failing ETerm absent/out-of-subset entry visible unsupported fallback and narrow-viewport cases +- [X] T086 Add failing assertion that ETerm never claims or invokes native legacy parser/host theme support +- [X] T087 Add failing `XTermSmokeTests.cs` first-frame proof for at least three resource/sequence/capability entries +- [X] T088 Add failing XTerm source identity, selection command, dynamic status, description, view, and cell proof +- [X] T089 Add failing XTerm out-of-subset/native-resource visible unsupported fallback and narrow-viewport cases +- [X] T090 Add failing assertion that existing Compatibility input and 021 session parser remain the only relevant reusable boundaries +- [X] T091 Review grouped ETerm/XTerm red matrices for explicit failure classes and immutable ownership +- [X] T092 Increment manual build counter and run ETerm/XTerm matrices expecting documented red boundaries +- [X] T093 Implement immutable typed ETerm manifest entries with exact historical source metadata +- [X] T094 Implement ETerm selection, main list, status, description, unsupported fallback, narrow viewport, and quit +- [X] T095 Implement immutable typed XTerm manifest entries plus accepted 021 subset metadata +- [X] T096 Implement XTerm selection, main list, status, description, unsupported native-resource fallback, narrow viewport, and quit +- [X] T097 Keep manifest lookup exact and bounded; reject missing/duplicate/out-of-subset entries before visible state publication +- [X] T098 Review manifest code for resource-only deviation, false-capability, and proof-boundary comments +- [X] T099 Increment manual build counter and run ETerm/XTerm tests to green; record exact counts/version/results +- [X] T100 Record ETerm/XTerm `IntentionalDeviation` decisions, representative entries, proof regions, and native-parser boundaries +- [X] T101 Confirm no arbitrary file parser, X resource database, terminfo, external command, host theme, or terminal mutation exists +- [X] T102 Add ETerm and XTerm guide drafts plus README matrix rows only after both slices are green + +## Phase 7: Cross-Example Proof, Governance, and Documentation + +- [X] T103 Add `Wave4VisualSmokeMatrixTests.cs` covering all five unique projects through public state/delegates +- [X] T104 Prove each project has main/status/description, app-loop operation, exact view, cells, host classification, framework decision, and fallback +- [X] T105 Prove linked `Wave4Runtime` source is not treated as one cross-assembly CLR type identity +- [X] T106 Increment the manual build counter, run existing Wave-1/2/3 smoke regressions with the five new projects, and record version plus no behavioral regression +- [X] T107 Complete all five comment-decision rows and confirm no trivial what-comments were added +- [X] T108 Populate NIST SSDF, CWE, input/resource/fixture bounds, fail-safe, least privilege, and STRIDE/CIA/CAPEC rows +- [X] T109 Populate trigger-based N/A rows for ASVS, new SBOM/VEX/SLSA/OpenSSF, AI-SBOM, NIS2, CRA, EU AI Act, and DORA +- [X] T110 Populate iSAQB/reuse rows and trigger-based N/A for S-ADR, arc42 change, Zero Trust, SAMM, BSI C3A, and BSI C5 +- [X] T111 Populate A11Y, host cross-platform, script-governance N/A, agent parity, and `.specify/templates/` N/A rows +- [X] T112 Update architecture/security evidence only for actually triggered Wave-4 resource/host boundaries; record unchanged rationale elsewhere +- [X] T113 Complete all five guides with DE-first/EN-second startup, operation, main area, status, description, host evidence, fallback, historical source, and A11Y sections +- [X] T114 Update `examples/README.md`, root README if relevant, and `docs/toc.yml` for five examples +- [X] T115 Review changed Markdown for CEFR-B2, umlauts/ß, semantic headings/tables/lists, fenced language tags, and text-first accessibility +- [X] T116 Update active Feature-022 and next Feature-023 context in all five maintained agent surfaces +- [X] T117 Verify five agent blocks are synchronized and record hashes/results +- [X] T118 Update `Pflichtenheft.md` completion and next-intake marker to `Lastenheft_06_A11Y_Framework.md` +- [X] T119 Update `docs/project-statistics.md` with 022 scope, lines, work window, 80/125-line baselines, validation, and next intake +- [X] T120 Complete exact FR/CR/SC links, example/framework/host/governance rows, and residual-risk/follow-up fields in `pr-evidence.md` + +## Phase 8: Validation, Archive, and Local Completion + +- [X] T121 Run `git diff --check`, placeholder, TODO, scope, duplicate-domain, generated-output, `tv203s/`, dependency, and host-mutation scans; record results +- [X] T122 Run `dotnet format --verify-no-changes --no-restore` and record result +- [X] T123 Increment manual build counter and run all targeted Wave-4/example Release smokes; record per-suite counts/version +- [X] T124 Increment manual build counter and run the complete example-smoke suite; record count/version +- [X] T125 Increment manual build counter and run the full Release suite; record per-project and total counts +- [X] T126 Validate `coverlet.runsettings`, increment manual build counter, run canonical coverage, and record all five required assembly percentages +- [X] T127 Run `docfx docfx.json`, then Playwright/axe DocFX smoke; record models/warnings/errors/tests +- [X] T128 Review all five generated guides through UTF-8 `lynx` when available and record text-first result +- [X] T129 Run repository diff/tracked-secret scans and record results +- [X] T130 Remove generated DocFX/API/test artifacts and prove they are absent from Git +- [X] T131 Verify five CLI project paths build/start contractually through project/smoke proof without requiring interactive manual input +- [X] T132 Verify SC-003 through SC-008 exact matrices, five framework decisions, host rows, and no local blocker +- [X] T133 Verify final diff contains no process/shell/PTY, host mutation, legacy parser, dependency, Wave-5/023, user-data, generated, or `tv203s/` change +- [X] T134 Archive `Lastenheft_Wave4-Visual-Component-Porting.md` through PowerShell rename with suffix `022-wave4-visual-component-porting` +- [X] T135 Record final local task count, changed files, validation, conditional gates, follow-ups, and retrospective observations +- [X] T136 Re-run `specify check`, prerequisite/task checks, checklist counts, and final Analyze consistency after implementation evidence +- [X] T137 Mark all local tasks through T137 complete only after their acceptance results are present in `pr-evidence.md` + +## Phase 9: Authorized GitHub Delivery + +- [X] T138 Align `Directory.Build.props` to `1.22..` without incrementing Build, stage intentional files, and record scope in `specs/022-wave4-visual-component-porting/pr-evidence.md` +- [ ] T139 Commit the complete 022 implementation after recording planned version/scope in `specs/022-wave4-visual-component-porting/pr-evidence.md`; defer observed hash to `specs/022-wave4-visual-component-porting/closeout-evidence.md` +- [ ] T140 Recalculate branch commit count, align version and `specs/022-wave4-visual-component-porting/pr-evidence.md`, and create a bounded follow-up commit only when required +- [ ] T141 Push `022-wave4-visual-component-porting` and record observed branch/head in `specs/022-wave4-visual-component-porting/closeout-evidence.md` +- [ ] T142 Create a ready feature PR from `specs/022-wave4-visual-component-porting/pr-evidence.md` and record its URL in `specs/022-wave4-visual-component-porting/closeout-evidence.md` without invalidating reviewed-head claims +- [ ] T143 Monitor required CI, Claude/Copilot availability, comments, and GraphQL threads to convergence; record current-head state in `specs/022-wave4-visual-component-porting/closeout-evidence.md` +- [ ] T144 Remediate every actionable remote finding with focused validation and record response/thread resolution in `specs/022-wave4-visual-component-porting/closeout-evidence.md` +- [ ] T145 Use authorized narrow admin bypass only after green checks, zero actionable threads, and sole human-approval block; record boundary in `specs/022-wave4-visual-component-porting/closeout-evidence.md` +- [ ] T146 Merge with merge commit, delete remote feature branch, switch to local `main`, fetch/prune/pull, and prove clean `HEAD == origin/main` in `specs/022-wave4-visual-component-porting/closeout-evidence.md` +- [ ] T147 Record post-merge facts through one non-empty evidence-only closeout PR using `specs/022-wave4-visual-component-porting/closeout-evidence.md` only when causally necessary +- [ ] T148 Converge closeout-PR checks/reviews under the same policy, record the result in `specs/022-wave4-visual-component-porting/closeout-evidence.md`, and merge/sync without recursive closeout evidence +- [ ] T149 Finish with synchronized clean `main` and complete Feature-022 evidence in `specs/022-wave4-visual-component-porting/closeout-evidence.md`, ready for separate retrospective and Home-Baseline handoff + +## Dependencies and Execution Order + +- T001-T030 gate all implementation. +- T031-T045 establish the Terminal red slice; T046-T060 make it green. +- T061-T082 deliver Cyrillic/Fonts; T083-T102 deliver ETerm/XTerm. +- T103-T120 complete shared proof, governance, docs, routing, and statistics. +- T121-T137 gate local completion; T138-T149 gate remote delivery. +- `pr-evidence.md`, `closeout-evidence.md`, `Wave4Runtime.cs`, smoke csproj, + solution, version, docs navigation, statistics, and agent files remain single-writer. + +## Requirement Coverage + +| Requirement | Task coverage | +|---|---| +| FR-001 to FR-008 | T016-T020, T031-T060, T103-T106 | +| FR-009 to FR-010 | T033-T060 | +| FR-011 to FR-015 | T022-T025, T061-T082 | +| FR-016 to FR-019 | T024, T083-T102 | +| FR-020 to FR-024 | T021-T030, T058, T081, T101, T120-T133 | +| FR-025 to FR-028 | T026-T029, T057, T080, T100, T120 | +| FR-029 to FR-036 | T103-T137 | +| CR-001 to CR-003 | T005-T006, T016-T030, T108, T121-T126 | +| CR-004 to CR-007 | T108-T112 | +| CR-008 to CR-014 | T016-T030, T103-T120 | +| CR-015 to CR-016 | T009-T010, T138-T149 | +| SC-001 to SC-003 | T031-T120, T123-T125 | +| SC-004 to SC-008 | T033-T102, T120-T133 | +| SC-009 to SC-013 | T103-T149 | + +## Independent Story Acceptance + +- **US1**: Terminal proves controlled session, action, rejection, recovery, + fallback, description, exact view/cells, and quit without host process. +- **US2**: Cyrillic and Fonts prove host-independent mappings plus one exact + visible nonblank glyph with honest invalid/unsupported fallbacks. +- **US3**: ETerm and XTerm prove useful immutable resource manifests without + claiming native parser or emulator compatibility. +- **US4**: All demos and guides expose keyboard/text-first operation, distinct + host evidence classes, historical deviations, and complete validation. diff --git a/tests/TuiVision.Examples.SmokeTests/CyrillicSmokeTests.cs b/tests/TuiVision.Examples.SmokeTests/CyrillicSmokeTests.cs new file mode 100644 index 00000000..036583a9 --- /dev/null +++ b/tests/TuiVision.Examples.SmokeTests/CyrillicSmokeTests.cs @@ -0,0 +1,85 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Drivers.Console; +using TuiVision.Examples.Cyrillic; + +namespace TuiVision.Examples.SmokeTests; + +/// +/// Prüft die hostunabhängige Wave-4-Zeichensatzdemo über den echten App-Loop. +/// Verifies the host-independent Wave-4 charset demo through the real app loop. +/// +[TestClass] +public sealed class CyrillicSmokeTests : ExampleTestBase +{ + /// Prüft erste KOI8-R-Zellen und ihre Beschriftung. / Verifies first KOI8-R cells and their labels. + [TestMethod] + public void Cyrillic_AppLoop_Shows_Labeled_Koi8R_Grid() + { + CyrillicApp app = new(DefaultBounds(), headless: true); + + AssertSmokeRunCompletes(() => app.Run()); + + Assert.AreEqual(CharsetMappingOutcome.Mapped, app.CurrentMapping.Outcome); + AssertViewTreeProofFromAppLoop(app.LastVisibleComponentKind, "TWindow", "Cyrillic view identity"); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "KOI8-R", "Cyrillic source label"); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "Unicode", "Cyrillic target label"); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, "Mapped", "Cyrillic status"); + AssertPrimaryAssertionUsedAppLoop(); + } + + /// Prüft alle kontrollierten Mappingzustände. / Verifies all controlled mapping states. + [TestMethod] + public void Cyrillic_AppLoop_Cycles_Direct_Replacement_Rejected_And_Unsupported_States() + { + CyrillicApp app = new(DefaultBounds(), headless: true); + app.QueueEvents(InteractiveSmokeEventScript.Commands( + CyrillicApp.CmNextMapping, + CyrillicApp.CmNextMapping, + CyrillicApp.CmNextMapping).Events); + + AssertSmokeRunCompletes(() => app.Run()); + + CollectionAssert.AreEqual( + new[] + { + CharsetMappingOutcome.Mapped, + CharsetMappingOutcome.Replaced, + CharsetMappingOutcome.Rejected, + CharsetMappingOutcome.Unsupported + }, + app.ObservedOutcomes.ToArray()); + AssertRenderedContainsFromAppLoop(app.Driver.BackBuffer, "Unsupported", "Cyrillic unsupported cells"); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, "Unsupported", "Cyrillic unsupported status"); + } + + /// Prüft enge Ansicht ohne Host-Locale-Fallback. / Verifies constrained view without host-locale fallback. + [TestMethod] + public void Cyrillic_AppLoop_Is_Host_Independent_In_Narrow_Viewport() + { + CyrillicApp app = new(DefaultBounds(40, 12), headless: true); + + AssertSmokeRunCompletes(() => app.Run()); + + AssertRenderedContainsFromAppLoop(app.Driver.BackBuffer, "Cyrillic", "Narrow Cyrillic identity"); + AssertRenderedContainsFromAppLoop(app.Driver.BackBuffer, "KOI8-R", "Narrow Cyrillic mapping"); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, "fixed-table", "Host-independent status"); + StringAssert.Contains(app.DescriptionText, "Host-Locale"); + Assert.IsTrue(app.QuitIssued); + } + + /// Prüft den tastaturerreichbaren Beschreibungspfad. / Verifies the keyboard-reachable description path. + [TestMethod] + public void Cyrillic_AppLoop_Shows_Description() + { + CyrillicApp app = new(DefaultBounds(), headless: true); + app.QueueEvents(InteractiveSmokeEventScript.Commands(CyrillicApp.CmDescription).Events); + + AssertSmokeRunCompletes(() => app.Run()); + + AssertViewTreeProofFromAppLoop(app.LastVisibleComponentKind, "TWindow", "Cyrillic description view"); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "Cyrillic description", "Cyrillic description cells"); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, "Description", "Cyrillic description status"); + } +} diff --git a/tests/TuiVision.Examples.SmokeTests/ETermSmokeTests.cs b/tests/TuiVision.Examples.SmokeTests/ETermSmokeTests.cs new file mode 100644 index 00000000..6a0c8a13 --- /dev/null +++ b/tests/TuiVision.Examples.SmokeTests/ETermSmokeTests.cs @@ -0,0 +1,71 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Examples.ETerm; + +namespace TuiVision.Examples.SmokeTests; + +/// Prüft die unveränderliche ETerm-Ressourcenansicht. / Verifies the immutable ETerm resource view. +[TestClass] +public sealed class ETermSmokeTests : ExampleTestBase +{ + /// Prüft repräsentative historische Einträge. / Verifies representative historical entries. + [TestMethod] + public void ETerm_AppLoop_Shows_Three_Immutable_Manifest_Entries() + { + ETermApp app = new(DefaultBounds(), headless: true); + + AssertSmokeRunCompletes(() => app.Run()); + + Assert.IsGreaterThanOrEqualTo(3, app.Entries.Count); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "Font3", "ETerm font entry"); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "#aaaaaa", "ETerm foreground entry"); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "ESC[8n", "ETerm sequence entry"); + AssertViewTreeProofFromAppLoop(app.LastVisibleComponentKind, "TWindow", "ETerm manifest view"); + AssertPrimaryAssertionUsedAppLoop(); + } + + /// Prüft Auswahl, Status und Quellenkennung. / Verifies selection, status, and source identity. + [TestMethod] + public void ETerm_AppLoop_Selects_Entry_And_Updates_Status() + { + ETermApp app = new(DefaultBounds(), headless: true); + app.QueueEvents(InteractiveSmokeEventScript.Commands(ETermApp.CmNextEntry).Events); + + AssertSmokeRunCompletes(() => app.Run()); + + Assert.AreEqual(1, app.SelectedIndex); + Assert.AreEqual("theme.cfg", app.SelectedEntry.SourceId); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, app.SelectedEntry.Key, "ETerm selection status"); + Assert.IsTrue(app.QuitIssued); + } + + /// Prüft sichtbaren Unsupported-Fallback in enger Ansicht. / Verifies visible unsupported fallback in a narrow view. + [TestMethod] + public void ETerm_AppLoop_Shows_Unsupported_Fallback_Without_Native_Parser() + { + ETermApp app = new(DefaultBounds(40, 12), headless: true, requestUnsupported: true); + + AssertSmokeRunCompletes(() => app.Run()); + + Assert.IsTrue(app.FallbackVisible); + AssertRenderedContainsFromAppLoop(app.Driver.BackBuffer, "Unsupported", "ETerm fallback cells"); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, "Unsupported", "ETerm fallback status"); + Assert.IsFalse(app.NativeParserUsed); + Assert.IsFalse(app.HostThemeMutationUsed); + StringAssert.Contains(app.BoundaryText, "immutable manifest"); + } + + /// Prüft den Beschreibungspfad. / Verifies the description path. + [TestMethod] + public void ETerm_AppLoop_Shows_Description() + { + ETermApp app = new(DefaultBounds(), headless: true); + app.QueueEvents(InteractiveSmokeEventScript.Commands(ETermApp.CmDescription).Events); + + AssertSmokeRunCompletes(() => app.Run()); + + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "ETerm description", "ETerm description cells"); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, "Description", "ETerm description status"); + } +} diff --git a/tests/TuiVision.Examples.SmokeTests/FontsSmokeTests.cs b/tests/TuiVision.Examples.SmokeTests/FontsSmokeTests.cs new file mode 100644 index 00000000..e8376e0a --- /dev/null +++ b/tests/TuiVision.Examples.SmokeTests/FontsSmokeTests.cs @@ -0,0 +1,93 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Drivers.Console; +using TuiVision.Examples.Fonts; + +namespace TuiVision.Examples.SmokeTests; + +/// +/// Prüft die kontrollierte Wave-4-8x16-Font-Fixture über den echten App-Loop. +/// Verifies the controlled Wave-4 8x16 font fixture through the real app loop. +/// +[TestClass] +public sealed class FontsSmokeTests : ExampleTestBase +{ + /// Prüft exakte Metadaten und eine nichtleere Glyphe. / Verifies exact metadata and a nonblank glyph. + [TestMethod] + public void Fonts_AppLoop_Shows_Exact_Metadata_And_Nonblank_Glyph() + { + FontsApp app = new(DefaultBounds(), headless: true); + + AssertSmokeRunCompletes(() => app.Run()); + + Assert.AreEqual(BitmapFontFixtureOutcome.Valid, app.FixtureResult.Outcome); + Assert.AreEqual(8, app.FixtureResult.Fixture!.Width); + Assert.AreEqual(16, app.FixtureResult.Fixture.Height); + Assert.AreEqual(256, app.FixtureResult.Fixture.GlyphCount); + Assert.AreEqual(4096, app.FixtureResult.Fixture.DataLength); + Assert.IsTrue(app.SelectedGlyphHasInk); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "Glyph 65", "Font glyph label"); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, "Valid", "Font status"); + AssertPrimaryAssertionUsedAppLoop(); + } + + /// Prüft das erkennbare 8x16-Raster. / Verifies the recognizable 8x16 raster. + [TestMethod] + public void Fonts_AppLoop_Renders_Known_A_Pixel_Region() + { + FontsApp app = new(DefaultBounds(), headless: true); + + AssertSmokeRunCompletes(() => app.Run()); + + AssertViewTreeProofFromAppLoop(app.LastVisibleComponentKind, "TWindow", "Fonts view identity"); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "..#####.", "Font A raster row"); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, ".#...###", "Font A stem row"); + } + + /// Prüft getrennte Ablehnungs- und Unsupported-Klassen. / Verifies separate rejection and unsupported classes. + [TestMethod] + public void Fonts_AppLoop_Shows_All_Invalid_And_Unsupported_Fallback_Classes() + { + FontFixtureScenario[] scenarios = + [ + FontFixtureScenario.WrongLength, + FontFixtureScenario.WrongGeometry, + FontFixtureScenario.WrongStride, + FontFixtureScenario.UnsupportedFormat, + FontFixtureScenario.InvalidSource, + FontFixtureScenario.BlankSelectedGlyph + ]; + + foreach (FontFixtureScenario scenario in scenarios) + { + FontsApp app = new(DefaultBounds(), headless: true, scenario); + AssertSmokeRunCompletes(() => app.Run()); + Assert.IsTrue(app.FallbackVisible, $"Expected visible fallback for {scenario}."); + AssertRenderedContainsFromAppLoop(app.Driver.BackBuffer, "Fallback", $"Font fallback cells for {scenario}"); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, scenario.ToString(), $"Font status for {scenario}"); + } + } + + /// Prüft Auswahl, Beschreibung, Status und enge Ansicht. / Verifies selection, description, status, and constrained view. + [TestMethod] + public void Fonts_AppLoop_Handles_Selection_Description_And_Narrow_Viewport() + { + FontsApp selected = new(DefaultBounds(), headless: true); + selected.QueueEvents(InteractiveSmokeEventScript.Commands(FontsApp.CmNextGlyph).Events); + AssertSmokeRunCompletes(() => selected.Run()); + Assert.AreEqual(66, selected.SelectedGlyph); + AssertVisibleContainsFromAppLoop(selected.LastStatusMessage, "glyph=66", "Font selection status"); + + FontsApp described = new(DefaultBounds(), headless: true); + described.QueueEvents(InteractiveSmokeEventScript.Commands(FontsApp.CmDescription).Events); + AssertSmokeRunCompletes(() => described.Run()); + AssertRenderedRegionContainsFromAppLoop(described.Driver.BackBuffer, described.LastVisibleRegion, "Fonts description", "Font description cells"); + + FontsApp narrow = new(DefaultBounds(40, 12), headless: true); + AssertSmokeRunCompletes(() => narrow.Run()); + AssertRenderedContainsFromAppLoop(narrow.Driver.BackBuffer, "Fonts", "Narrow Fonts identity"); + AssertRenderedContainsFromAppLoop(narrow.Driver.BackBuffer, "Glyph", "Narrow Fonts glyph label"); + Assert.IsTrue(narrow.QuitIssued); + } +} diff --git a/tests/TuiVision.Examples.SmokeTests/TerminalSmokeTests.cs b/tests/TuiVision.Examples.SmokeTests/TerminalSmokeTests.cs new file mode 100644 index 00000000..6f201081 --- /dev/null +++ b/tests/TuiVision.Examples.SmokeTests/TerminalSmokeTests.cs @@ -0,0 +1,91 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Core; +using TuiVision.Drivers.Console; +using TuiVision.Examples.Terminal; + +namespace TuiVision.Examples.SmokeTests; + +/// +/// Prüft die sichtbare Wave-4-Terminal-Sitzung durch den echten App-Loop. +/// Verifies the visible Wave-4 terminal session through the real app loop. +/// +[TestClass] +public sealed class TerminalSmokeTests : ExampleTestBase +{ + /// Prüft ersten Frame, Eingabe, Cursor und Zellen. / Verifies first frame, input, cursor, and cells. + [TestMethod] + public void Terminal_AppLoop_Shows_Session_Input_Cursor_And_Status() + { + TerminalApp app = new(DefaultBounds(), headless: true); + app.QueueEvents(InteractiveSmokeEventScript.Commands(TerminalApp.CmWriteSample, TerminalApp.CmCursorAction).Events); + + AssertSmokeRunCompletes(() => app.Run()); + + Assert.AreEqual(TerminalSessionLifecycle.Active, app.Session.Lifecycle); + AssertViewTreeProofFromAppLoop(app.LastVisibleComponentKind, "TTerminalView", "Terminal view identity"); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "Wave4", "Terminal output cells"); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, "cursor=", "Terminal cursor status"); + Assert.AreEqual(TerminalEmulationOutcome.Accepted, app.LastOutcome); + AssertPrimaryAssertionUsedAppLoop(); + } + + /// Prüft atomare Ablehnung und Folgeeingabe. / Verifies atomic rejection and following input. + [TestMethod] + public void Terminal_AppLoop_Rejects_Atomically_And_Recovers() + { + TerminalApp app = new(DefaultBounds(), headless: true); + app.QueueEvents(InteractiveSmokeEventScript.Commands(TerminalApp.CmRejectedAction, TerminalApp.CmWriteRecovery).Events); + + AssertSmokeRunCompletes(() => app.Run()); + + Assert.IsTrue(app.RejectionObserved); + Assert.AreEqual(TerminalEmulationOutcome.Accepted, app.LastOutcome); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "recovered", "Terminal recovery cells"); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, "recovery", "Terminal recovery status"); + } + + /// Prüft Reset und sichtbaren Fallback. / Verifies reset and visible fallback. + [TestMethod] + public void Terminal_AppLoop_Shows_Reset_And_Unsupported_Fallback() + { + TerminalApp app = new(DefaultBounds(), headless: true, capabilityAvailable: false); + app.QueueEvents(InteractiveSmokeEventScript.Commands(TerminalApp.CmReset).Events); + + AssertSmokeRunCompletes(() => app.Run()); + + Assert.AreEqual(TerminalProfileCapabilityState.Unsupported, app.Session.PresentationCapability); + AssertRenderedContainsFromAppLoop(app.Driver.BackBuffer, "Unsupported", "Terminal fallback cells"); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, "fallback", "Terminal fallback status"); + Assert.IsTrue(app.QuitIssued); + } + + /// Prüft den Beschreibungspfad. / Verifies the description path. + [TestMethod] + public void Terminal_AppLoop_Shows_Description() + { + TerminalApp app = new(DefaultBounds(), headless: true); + app.QueueEvents(InteractiveSmokeEventScript.Commands(TerminalApp.CmDescription).Events); + + AssertSmokeRunCompletes(() => app.Run()); + + AssertViewTreeProofFromAppLoop(app.LastVisibleComponentKind, "TWindow", "Terminal description view"); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "Terminal description", "Terminal description cells"); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, "Description", "Terminal description status"); + } + + /// Prüft enge Ansicht und sichtbare Identität. / Verifies constrained viewport and visible identity. + [TestMethod] + public void Terminal_AppLoop_Preserves_Identity_Status_And_Fallback_In_Narrow_Viewport() + { + TerminalApp app = new(DefaultBounds(40, 12), headless: true, capabilityAvailable: false); + + AssertSmokeRunCompletes(() => app.Run()); + + AssertViewTreeProofFromAppLoop(app.LastVisibleComponentKind, "TTerminalView", "Narrow Terminal view"); + AssertRenderedContainsFromAppLoop(app.Driver.BackBuffer, "Terminal", "Narrow Terminal identity"); + AssertRenderedContainsFromAppLoop(app.Driver.BackBuffer, "Unsupported", "Narrow Terminal fallback"); + Assert.IsTrue(app.LastVisibleRegion.Width > 0 && app.LastVisibleRegion.Height > 0); + } +} diff --git a/tests/TuiVision.Examples.SmokeTests/TuiVision.Examples.SmokeTests.csproj b/tests/TuiVision.Examples.SmokeTests/TuiVision.Examples.SmokeTests.csproj index 0581c2e3..63805553 100644 --- a/tests/TuiVision.Examples.SmokeTests/TuiVision.Examples.SmokeTests.csproj +++ b/tests/TuiVision.Examples.SmokeTests/TuiVision.Examples.SmokeTests.csproj @@ -56,4 +56,13 @@ + + + + + + + + + diff --git a/tests/TuiVision.Examples.SmokeTests/Wave4VisualSmokeMatrixTests.cs b/tests/TuiVision.Examples.SmokeTests/Wave4VisualSmokeMatrixTests.cs new file mode 100644 index 00000000..b68480c0 --- /dev/null +++ b/tests/TuiVision.Examples.SmokeTests/Wave4VisualSmokeMatrixTests.cs @@ -0,0 +1,176 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Drivers.Console; +using CyrillicApp = TuiVision.Examples.Cyrillic.CyrillicApp; +using ETermApp = TuiVision.Examples.ETerm.ETermApp; +using FontFixtureScenario = TuiVision.Examples.Fonts.FontFixtureScenario; +using FontsApp = TuiVision.Examples.Fonts.FontsApp; +using TerminalApp = TuiVision.Examples.Terminal.TerminalApp; +using XTermApp = TuiVision.Examples.XTerm.XTermApp; + +namespace TuiVision.Examples.SmokeTests; + +/// +/// Prüft die fünf Wave-4-Beispiele über neutrale Zustands-Delegates. +/// Verifies the five Wave-4 examples through neutral state delegates. +/// +[TestClass] +public sealed class Wave4VisualSmokeMatrixTests : ExampleTestBase +{ + /// Prüft eindeutige Haupt-, Status-, Hilfe- und Fallbackpfade. / Verifies unique main, status, help, and fallback paths. + [TestMethod] + public void Wave4_Matrix_Proves_All_Five_Projects_Without_Linked_Type_Identity() + { + Func[] factories = [ProveTerminal, ProveCyrillic, ProveFonts, ProveETerm, ProveXTerm]; + Wave4Proof[] proofs = factories.Select(factory => factory()).ToArray(); + + CollectionAssert.AreEquivalent( + new[] { "Terminal", "Cyrillic", "Fonts", "ETerm", "XTerm" }, + proofs.Select(proof => proof.Name).ToArray()); + foreach (Wave4Proof proof in proofs) + { + Assert.IsFalse(string.IsNullOrWhiteSpace(proof.RuntimeType), $"{proof.Name} runtime type"); + Assert.IsFalse(string.IsNullOrWhiteSpace(proof.MainKind), $"{proof.Name} main view"); + Assert.IsFalse(string.IsNullOrWhiteSpace(proof.Status), $"{proof.Name} status"); + Assert.IsTrue(proof.MainCellsVisible, $"{proof.Name} rendered cells"); + Assert.IsTrue(proof.DescriptionVisible, $"{proof.Name} description"); + Assert.IsTrue(proof.FallbackVisible, $"{proof.Name} fallback"); + Assert.AreEqual(TerminalHostEvidenceClass.DeterministicInProcess, proof.HostEvidence); + Assert.IsTrue(proof.FrameworkDecision is "UseExistingFramework" or "IntentionalDeviation"); + } + + // Verlinkte Quellen erzeugen fünf Assembly-Typen; die Matrix teilt nur DTOs und Delegates. + // Linked sources create five assembly types; the matrix shares only DTOs and delegates. + Assert.AreNotEqual(typeof(TerminalApp).BaseType, typeof(CyrillicApp).BaseType); + Assert.AreNotEqual(typeof(ETermApp).BaseType, typeof(XTermApp).BaseType); + AssertPrimaryAssertionUsedAppLoop(); + } + + private Wave4Proof ProveTerminal() + { + TerminalApp main = new(DefaultBounds(), headless: true); + main.QueueEvents(InteractiveSmokeEventScript.Commands(TerminalApp.CmWriteSample).Events); + AssertSmokeRunCompletes(() => main.Run()); + AssertViewTreeProofFromAppLoop(main.LastVisibleComponentKind, "TTerminalView", "Terminal matrix main view"); + TerminalApp description = new(DefaultBounds(), headless: true); + description.QueueEvents(InteractiveSmokeEventScript.Commands(TerminalApp.CmDescription).Events); + AssertSmokeRunCompletes(() => description.Run()); + TerminalApp fallback = new(DefaultBounds(), headless: true, capabilityAvailable: false); + AssertSmokeRunCompletes(() => fallback.Run()); + return Proof( + "Terminal", main.GetType(), main.LastVisibleComponentKind, main.LastStatusMessage, + BufferToText(main.Driver.BackBuffer).Contains("Wave4", StringComparison.Ordinal), + BufferToText(description.Driver.BackBuffer).Contains("Terminal description", StringComparison.Ordinal), + BufferToText(fallback.Driver.BackBuffer).Contains("Unsupported", StringComparison.Ordinal), + "UseExistingFramework"); + } + + private Wave4Proof ProveCyrillic() + { + CyrillicApp main = new(DefaultBounds(), headless: true); + AssertSmokeRunCompletes(() => main.Run()); + AssertViewTreeProofFromAppLoop(main.LastVisibleComponentKind, "TWindow", "Cyrillic matrix main view"); + CyrillicApp description = new(DefaultBounds(), headless: true); + description.QueueEvents(InteractiveSmokeEventScript.Commands(CyrillicApp.CmDescription).Events); + AssertSmokeRunCompletes(() => description.Run()); + CyrillicApp fallback = new(DefaultBounds(), headless: true); + fallback.QueueEvents(InteractiveSmokeEventScript.Commands( + CyrillicApp.CmNextMapping, CyrillicApp.CmNextMapping, CyrillicApp.CmNextMapping).Events); + AssertSmokeRunCompletes(() => fallback.Run()); + return Proof( + "Cyrillic", main.GetType(), main.LastVisibleComponentKind, main.LastStatusMessage, + BufferToText(main.Driver.BackBuffer).Contains("KOI8-R", StringComparison.Ordinal), + BufferToText(description.Driver.BackBuffer).Contains("Cyrillic description", StringComparison.Ordinal), + BufferToText(fallback.Driver.BackBuffer).Contains("Unsupported", StringComparison.Ordinal), + "UseExistingFramework"); + } + + private Wave4Proof ProveFonts() + { + FontsApp main = new(DefaultBounds(), headless: true); + main.QueueEvents(InteractiveSmokeEventScript.Commands(FontsApp.CmNextGlyph).Events); + AssertSmokeRunCompletes(() => main.Run()); + AssertViewTreeProofFromAppLoop(main.LastVisibleComponentKind, "TWindow", "Fonts matrix main view"); + FontsApp description = new(DefaultBounds(), headless: true); + description.QueueEvents(InteractiveSmokeEventScript.Commands(FontsApp.CmDescription).Events); + AssertSmokeRunCompletes(() => description.Run()); + FontsApp fallback = new(DefaultBounds(), headless: true, FontFixtureScenario.WrongLength); + AssertSmokeRunCompletes(() => fallback.Run()); + return Proof( + "Fonts", main.GetType(), main.LastVisibleComponentKind, main.LastStatusMessage, + BufferToText(main.Driver.BackBuffer).Contains("Glyph 66", StringComparison.Ordinal), + BufferToText(description.Driver.BackBuffer).Contains("Fonts description", StringComparison.Ordinal), + fallback.FallbackVisible, + "UseExistingFramework"); + } + + private Wave4Proof ProveETerm() + { + ETermApp main = new(DefaultBounds(), headless: true); + main.QueueEvents(InteractiveSmokeEventScript.Commands(ETermApp.CmNextEntry).Events); + AssertSmokeRunCompletes(() => main.Run()); + AssertViewTreeProofFromAppLoop(main.LastVisibleComponentKind, "TWindow", "ETerm matrix main view"); + ETermApp description = new(DefaultBounds(), headless: true); + description.QueueEvents(InteractiveSmokeEventScript.Commands(ETermApp.CmDescription).Events); + AssertSmokeRunCompletes(() => description.Run()); + ETermApp fallback = new(DefaultBounds(), headless: true, requestUnsupported: true); + AssertSmokeRunCompletes(() => fallback.Run()); + return Proof( + "ETerm", main.GetType(), main.LastVisibleComponentKind, main.LastStatusMessage, + BufferToText(main.Driver.BackBuffer).Contains("Foreground", StringComparison.Ordinal), + BufferToText(description.Driver.BackBuffer).Contains("ETerm description", StringComparison.Ordinal), + fallback.FallbackVisible, + "IntentionalDeviation"); + } + + private Wave4Proof ProveXTerm() + { + XTermApp main = new(DefaultBounds(), headless: true); + main.QueueEvents(InteractiveSmokeEventScript.Commands(XTermApp.CmNextEntry).Events); + AssertSmokeRunCompletes(() => main.Run()); + AssertViewTreeProofFromAppLoop(main.LastVisibleComponentKind, "TWindow", "XTerm matrix main view"); + XTermApp description = new(DefaultBounds(), headless: true); + description.QueueEvents(InteractiveSmokeEventScript.Commands(XTermApp.CmDescription).Events); + AssertSmokeRunCompletes(() => description.Run()); + XTermApp fallback = new(DefaultBounds(), headless: true, requestUnsupported: true); + AssertSmokeRunCompletes(() => fallback.Run()); + return Proof( + "XTerm", main.GetType(), main.LastVisibleComponentKind, main.LastStatusMessage, + BufferToText(main.Driver.BackBuffer).Contains("Color1", StringComparison.Ordinal), + BufferToText(description.Driver.BackBuffer).Contains("XTerm description", StringComparison.Ordinal), + fallback.FallbackVisible, + "IntentionalDeviation"); + } + + private static Wave4Proof Proof( + string name, + Type runtimeType, + string mainKind, + string status, + bool mainCellsVisible, + bool descriptionVisible, + bool fallbackVisible, + string frameworkDecision) => + new( + name, + runtimeType.AssemblyQualifiedName ?? runtimeType.FullName ?? runtimeType.Name, + mainKind, + status, + mainCellsVisible, + descriptionVisible, + fallbackVisible, + TerminalHostCapabilityDetector.DetectCurrent().EvidenceClass, + frameworkDecision); + + private sealed record Wave4Proof( + string Name, + string RuntimeType, + string MainKind, + string Status, + bool MainCellsVisible, + bool DescriptionVisible, + bool FallbackVisible, + TerminalHostEvidenceClass HostEvidence, + string FrameworkDecision); +} diff --git a/tests/TuiVision.Examples.SmokeTests/XTermSmokeTests.cs b/tests/TuiVision.Examples.SmokeTests/XTermSmokeTests.cs new file mode 100644 index 00000000..e8dc3819 --- /dev/null +++ b/tests/TuiVision.Examples.SmokeTests/XTermSmokeTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) 2026 Thorsten Hindermann / TuiVision Contributors. +// Licensed under the MIT Licence. See LICENSE file in the project root for full licence information. + +using TuiVision.Examples.XTerm; + +namespace TuiVision.Examples.SmokeTests; + +/// Prüft die unveränderliche XTerm-Ressourcenansicht. / Verifies the immutable XTerm resource view. +[TestClass] +public sealed class XTermSmokeTests : ExampleTestBase +{ + /// Prüft repräsentative Ressourcen und Sequenzen. / Verifies representative resources and sequences. + [TestMethod] + public void XTerm_AppLoop_Shows_Three_Immutable_Manifest_Entries() + { + XTermApp app = new(DefaultBounds(), headless: true); + + AssertSmokeRunCompletes(() => app.Run()); + + Assert.IsGreaterThanOrEqualTo(3, app.Entries.Count); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "Insert", "XTerm insert sequence"); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "#a80000", "XTerm color entry"); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "metaSendsEscape", "XTerm capability entry"); + AssertViewTreeProofFromAppLoop(app.LastVisibleComponentKind, "TWindow", "XTerm manifest view"); + AssertPrimaryAssertionUsedAppLoop(); + } + + /// Prüft Auswahl, Status und Quellenkennung. / Verifies selection, status, and source identity. + [TestMethod] + public void XTerm_AppLoop_Selects_Entry_And_Updates_Status() + { + XTermApp app = new(DefaultBounds(), headless: true); + app.QueueEvents(InteractiveSmokeEventScript.Commands(XTermApp.CmNextEntry).Events); + + AssertSmokeRunCompletes(() => app.Run()); + + Assert.AreEqual(1, app.SelectedIndex); + Assert.AreEqual("Xterm.res", app.SelectedEntry.SourceId); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, app.SelectedEntry.Key, "XTerm selection status"); + Assert.IsTrue(app.QuitIssued); + } + + /// Prüft sichtbaren Native-Resource-Fallback. / Verifies visible native-resource fallback. + [TestMethod] + public void XTerm_AppLoop_Shows_Unsupported_Native_Resource_Fallback() + { + XTermApp app = new(DefaultBounds(40, 12), headless: true, requestUnsupported: true); + + AssertSmokeRunCompletes(() => app.Run()); + + Assert.IsTrue(app.FallbackVisible); + AssertRenderedContainsFromAppLoop(app.Driver.BackBuffer, "Unsupported", "XTerm fallback cells"); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, "Unsupported", "XTerm fallback status"); + Assert.IsFalse(app.XResourceDatabaseUsed); + Assert.IsFalse(app.ExternalCommandUsed); + } + + /// Prüft Beschreibung und bestehende Wiederverwendungsgrenzen. / Verifies description and existing reuse boundaries. + [TestMethod] + public void XTerm_AppLoop_Uses_Compatibility_And_021_Boundaries_Only() + { + XTermApp app = new(DefaultBounds(), headless: true); + app.QueueEvents(InteractiveSmokeEventScript.Commands(XTermApp.CmDescription).Events); + + AssertSmokeRunCompletes(() => app.Run()); + + StringAssert.Contains(app.BoundaryText, "Compatibility input"); + StringAssert.Contains(app.BoundaryText, "Feature-021 session parser"); + AssertRenderedRegionContainsFromAppLoop(app.Driver.BackBuffer, app.LastVisibleRegion, "XTerm description", "XTerm description cells"); + AssertVisibleContainsFromAppLoop(app.LastStatusMessage, "Description", "XTerm description status"); + } +}