-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathfrontend.mdc
More file actions
169 lines (154 loc) · 9.97 KB
/
frontend.mdc
File metadata and controls
169 lines (154 loc) · 9.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
---
globs: frontend/**/*.ts,frontend/**/*.html,frontend/**/*.scss
---
# Frontend-Regeln
## Framework
- Angular ~20.3.4 mit **Standalone Components** (keine NgModules)
- Alle Components müssen `standalone: true` haben
- Dependency Injection über `inject()` statt Constructor-Injection bevorzugt
## Paket-Scope
- Paketname: `@webmud3/frontend`
- Pfad-Alias: `@webmud3/frontend/*` → `src/app/*`
- Shared-Typen: `import { ... } from '@webmud3/shared'`
## Terminal-Subsystem
- **MudInputController**: Verwaltet den lokalen Editing-Buffer (LINEMODE). Nicht direkt auf xterm schreiben!
- **MudPromptManager**: State-Machine für Prompt-Visibility bei Server-Output
- **MudSocketAdapter**: Adapter – Observable → WebSocket-Interface für xterm AttachAddon
- **MudScreenReaderAnnouncer**: Custom ARIA-Announcer (kein xterm screenReaderMode!)
- Alle ANSI/Terminal-Escape-Konstanten in `features/terminal/models/escapes.ts`
## Barrierefreiheit (A11y)
- Screenreader-Support ist ein Kernfeature – ARIA-Regionen nicht entfernen!
- Drei Announcement-Ebenen: Zeichen, Wort (bei Space), vollständige Zeile (bei Enter)
- `aria-live="assertive"` für Output, `role="log"` für History
- Bei Änderungen am Terminal: Screenreader-Tests durchführen (siehe SCREENREADER_TESTS.md)
## GMCP-Subsystem
- **GmcpService** (`features/gmcp/gmcp.service.ts`): Zentraler Service für GMCP-Modul-Verwaltung
- Registry: Module registrieren/deregistrieren über `registerModule()`/`unregisterModule()`
- Routing: Eingehende Nachrichten werden an den passenden `GmcpModuleHandler` delegiert
- Observable: `gmcpEvent$` streamt alle GMCP-Events, `gmcpStart$` signalisiert GMCP-Aktivierung
- Senden: `sendOutgoing()` leitet über die von SocketsService gesetzte Sendefunktion
- **GmcpModuleHandler** (`features/gmcp/gmcp-module-handler.ts`): Strategy-Interface für Module
- Jedes GMCP-Modul (Sound, Char, Files, etc.) implementiert dieses Interface
- `handleMessage(message, data)` verarbeitet eingehende Nachrichten
- Optional: `getMenuItems()` für dynamische Menüeinträge, `dispose()` für Cleanup
- **GmcpEvent** (`features/gmcp/gmcp-event.ts`): Event-Typ für den Observable-Stream
- Verdrahtung: SocketsService → GmcpService.handleIncoming() / handleGmcpStart()
- Reset: GmcpService.reset() wird bei mudDisconnect aufgerufen
## Fenstersystem (Modeless Windows)
- **WindowConfig** (`features/windows/window-config.ts`): Interface für Fenster-Konfiguration
- UUID-basierte `windowId`, Parent-Child über `parentWindowId`
- `componentType` String bestimmt, welcher Content im Fenster gerendert wird
- Position, Größe, Z-Index, Sichtbarkeit, Titel
- **WindowAction** Enum: Focus, Hide, Save, Cancel, SaveAndClose, WinError, Resize, CloseParent
- **WindowEvent** Interface: `{ action, windowId, data? }` für Event-Kommunikation
- **WindowService** (`features/windows/window.service.ts`): Zentraler Injectable Service
- `open()` erstellt neues Fenster mit UUID und auto-Z-Index
- `close()` schließt Fenster + rekursiv alle Children
- `focus()` bringt Fenster nach vorne (höchster Z-Index)
- `windows$` BehaviorSubject liefert reaktive Liste aller sichtbaren Fenster
- `incomingEvents$` / `outgoingEvents$` Subject-basierter Event-Bus
- `updatePosition()` / `updateSize()` / `updateData()` für State-Updates
- **WindowComponent** (`features/windows/window.component.ts`): Standalone Component
- Draggable Titelleiste via native Pointer Events
- Resize-Handle (unten-rechts), Min-Constraints 200×120px
- ARIA: `role="dialog"`, `aria-label`, Focus-Buttons
- Emittet WindowEvents über Output an den Container
- **WindowContainerComponent** (`features/windows/window-container.component.ts`):
- Fixed-Position Overlay, `pointer-events: none` (Fenster haben `auto`)
- Rendert `@for`-Loop über `windows$ | async`
- Routet Events an WindowService
- Design-Prinzip: Catppuccin Mocha Farbschema (dark theme, passt zum Terminal)
- Content-Routing: `WindowContainerComponent` nutzt `@switch (config.componentType)` um Content-Komponenten einzubetten
- Neue Fenster-Typen: Component importieren + `@case` hinzufügen
## GMCP Files-Modul (Datei-Browser & Code-Editor)
- **FilesGmcpHandler** (`features/gmcp-files/files-gmcp-handler.ts`): GmcpModuleHandler für `Files`
- `Files.URL` → Datei laden + Editor-Fenster öffnen
- `Files.DirectoryList` → DirList-Fenster öffnen/aktualisieren
- `openFile()` / `changeDirectory()` → GMCP-Requests an MUD
- `saveFile()` → Save-Workflow: GMCP Files.OpenFile (flag=1) → HTTP PUT → GMCP Files.fileSaved
- **FilesService** (`features/gmcp-files/files.service.ts`): HTTP Load/Save + FileInfo-Registry
- `processFileUrl()` erstellt/aktualisiert FileInfo aus GMCP-Payload
- `loadFileContent()` / `saveFileContent()` → async HTTP-Operationen
- `isDirty()` für ungespeicherte Änderungen, `reset()` bei Disconnect
- **DirListComponent** (`features/gmcp-files/dirlist.component.ts`): Verzeichnis-Browser
- Standalone, zeigt Dateien/Ordner als Tabelle an
- Klick auf Datei → `FilesGmcpHandler.openFile()`
- Klick auf Ordner → `FilesGmcpHandler.changeDirectory()`
- **EditorComponent** (`features/gmcp-files/editor.component.ts`): Code-Editor
- Ace Editor (`ace-builds`), lazy-loaded via `import()`
- Syntax-Highlighting: C/C++ (`.c`, `.h`, `.inc`), Text (alles andere)
- Theme-Auswahl mit localStorage-Persistenz
- Toolbar: Theme-Select, Read-Only-Toggle, Zwischenspeichern, Speichern&Schließen, Abbrechen
- Dirty-Check bei Schließen (window.confirm)
- **FileInfo** / **FileEntry** (`features/gmcp-files/file-types.ts`): Typen für Dateisystem-Daten
- Parent-Child: DirList-Fenster ist Parent aller geöffneten Editor-Fenster
## GMCP Sound-Modul
- **SoundGmcpHandler** (`features/gmcp-sound/sound-gmcp-handler.ts`): GmcpModuleHandler für `Sound`
- `Sound.Url` → speichert Base-URL für Sound-Dateien
- `Sound.Event` → spielt Sound via `new Audio(baseUrl + "/" + file)`
- HTML5 Audio API (fire-and-forget, autoplay-Policy behandelt)
- Toggle: `Core.Supports.Add ["Sound 1"]` / `Core.Supports.Remove ["Sound 1"]`
- `getMenuItems()` liefert "Vertonung" Toggle-Eintrag für das Menü
- Backend: GMCP Sound-Messages werden transparent durchgereicht (kein Backend-Code nötig)
## GMCP Char-Modul (Statusleiste + Inventar)
- **CharacterData** (`features/gmcp-char/character-data.ts`): Typen + Parsing
- `CharacterData` Interface: Name, Fullname, Gender, Wizard-Flag, Vitals, Stats, Status
- `CharacterVitals`: HP/SP + MaxHP/MaxSP
- `CharacterStat`: key/label/value für STR/INT/CON/DEX
- `parseVitals("hp=100|sp=80|maxhp=120|maxsp=100")` → strukturierte Vitals
- `parseStats("con=34,2|dex=59,7|int=130|str=59,8")` → sortiertes Stats-Array
- `formatStatusSummary()` → kompakte Status-Anzeige
- **InventoryData** (`features/gmcp-char/inventory-data.ts`): Inventar-Datenmodell
- `InventoryEntry` Interface: `{ name, category }` — einzelner Gegenstand
- `InventoryList` Klasse: Kategorisierte Verwaltung mit `addItem()`, `removeItem()`, `initList()`
- Kategorien werden automatisch erstellt/gelöscht
- `getCategories()`, `getItems(category)`, `totalItems`, `isEmpty`
- **CharGmcpHandler** (`features/gmcp-char/char-gmcp-handler.ts`): GmcpModuleHandler für `Char`
- `Char.Name` → Charakter-Init, Browser-Titel setzen, Wizard-Module aktivieren
- `Char.StatusVars` → Status-Variablen-Definitionen speichern
- `Char.Status` → Aktuelle Statuswerte (Gilde, Rasse, Rang)
- `Char.Vitals` → HP/SP Werte (string + object Format)
- `Char.Stats` → STR/INT/CON/DEX Attribute
- `Char.Items.List` → Komplettes Inventar ersetzen + Fenster öffnen
- `Char.Items.Add` → Einzelnen Gegenstand hinzufügen
- `Char.Items.Remove` → Einzelnen Gegenstand entfernen
- `characterData$` BehaviorSubject (null = nicht eingeloggt)
- `inventory$` BehaviorSubject für reaktive Inventar-Updates
- Wizard-Erkennung: bei `wizard > 0` werden Files/Input/Numpad GMCP-Module aktiviert
- Inventar-Fenster wird automatisch bei `Items.List` geöffnet, bei `dispose()` geschlossen
- **CharStatusBarComponent** (`features/gmcp-char/char-statusbar.component.ts`): Kompakte Statusleiste
- Standalone, direkt in `app.component.html` zwischen Terminal und Window-Container
- Nur sichtbar nach `Char.Name` Empfang (characterData$ nicht null + name gesetzt)
- Layout: `[Name] HP: x/y | SP: x/y | STR: x INT: y CON: z DEX: w | Status`
- Catppuccin Mocha Farben, monospace, responsive (Stats auf schmalen Viewports ausgeblendet)
- ARIA: `role="status"`, `aria-live="polite"`
- `flex: 0 0 auto` — verdrängt Terminal von unten, überlappt nicht
- **InventoryComponent** (`features/gmcp-char/inventory.component.ts`): Inventar-Fenster
- Standalone, wird als Modeless-Window im WindowContainer gerendert (`componentType: 'InventoryComponent'`)
- Kategorisierte Liste mit Kategorie-Überschriften und Items
- Abonniert `CharGmcpHandler.inventory$` für reaktive Updates
- Catppuccin Mocha Farben, Gegenstandszähler im Footer
- ARIA: `role="list"`, `aria-label="Inventar"`
## GMCP Handler-Registrierung
- **GmcpBootstrapService** (`features/gmcp/gmcp-bootstrap.service.ts`): Zentrale Registrierung
- Registriert alle GMCP-Handler (Char, Sound, Files) beim GmcpService
- Wird als `provideAppInitializer()` in `main.ts` aufgerufen
- Idempotent (mehrfacher Aufruf sicher)
## Styling
- SCSS als Preprocessor
- Komponenten-spezifische Styles in `.component.scss`
- Globale Styles in `src/styles.scss`
## Environment
- Dev: `environment.ts` → backendUrl = `http://localhost:5000`
- Prod: `environment.prod.ts` → backendUrl = `window.location.href`
- Compile-Time File-Replacement über `angular.json` `fileReplacements`
## Testing
- Jest über @angular-builders/jest (nicht Karma!)
- Test-Dateien: `*.spec.ts` neben den Source-Dateien
- Setup: `setup-jest.ts` (nutzt `setupZoneTestEnv()` aus `jest-preset-angular`)
- Angular-DI in Tests: `TestBed.configureTestingModule()` + `provideHttpClient()` bei Services mit HTTP
## Conventions
- TypeScript strict mode
- Single quotes für Strings (EditorConfig)
- 2 Spaces Einrückung
- SCSS für Styles