diff --git a/.superpowers/sdd/2026-08-18-first-visible-vertical-slice/task-8-report.md b/.superpowers/sdd/2026-08-18-first-visible-vertical-slice/task-8-report.md index 8092f55..134ed2e 100644 --- a/.superpowers/sdd/2026-08-18-first-visible-vertical-slice/task-8-report.md +++ b/.superpowers/sdd/2026-08-18-first-visible-vertical-slice/task-8-report.md @@ -45,3 +45,30 @@ The Browser plugin is not available in this session. The repository has no insta - The world route placeholder contains no scene, nodes, API loading, or travel panel; Task 9 owns those surfaces. - The context panel is only a reusable framed shell until Task 9 supplies world data. + +## Review fix round 1 + +### Accessibility and responsive behavior + +- The active Karte button now has the explicit accessible name `Karte`, so its label remains available when the compact viewport hides visible navigation text. +- At widths below 900px, the context panel now reflows into a full-width row below main content instead of being hidden. At widths below 620px it remains in DOM order after main content. + +### Runtime HUD derivatives + +The five original 1254 x 1254 user assets remain unmodified. The shell now loads deterministic 128 x 128 PNG derivatives from `images/hud/runtime/`; each was resized locally with high-quality bicubic interpolation and no creative image change. + +| Asset | Original bytes | Runtime derivative bytes | +| --- | ---: | ---: | +| CharacterIcon | 2,283,892 | 26,670 | +| MapsIcon | 2,144,772 | 27,774 | +| HuntIcon | 2,313,949 | 29,486 | +| QuestsIcon | 2,235,955 | 29,554 | +| InventoryIcon | 2,060,169 | 26,142 | + +The combined persistent-icon payload is reduced from 11,038,737 bytes to 139,626 bytes (about 98.7%). The resized Maps icon was visually inspected after generation. + +### Regression evidence + +RED: the added explicit accessible-name assertion failed before the fix because the active Karte control had no `aria-label`. + +GREEN: `npm test --workspace=@ashen-realms/web -- --watch=false` passed with 3 test files and 14 tests. The added coverage verifies the explicit Karte name, non-null WorldStore character rendering, and root/wildcard `/world` route redirects. diff --git a/apps/web/public/images/hud/runtime/CharacterIcon-128.png b/apps/web/public/images/hud/runtime/CharacterIcon-128.png new file mode 100644 index 0000000..61cbfdc Binary files /dev/null and b/apps/web/public/images/hud/runtime/CharacterIcon-128.png differ diff --git a/apps/web/public/images/hud/runtime/HuntIcon-128.png b/apps/web/public/images/hud/runtime/HuntIcon-128.png new file mode 100644 index 0000000..15b3890 Binary files /dev/null and b/apps/web/public/images/hud/runtime/HuntIcon-128.png differ diff --git a/apps/web/public/images/hud/runtime/InventoryIcon-128.png b/apps/web/public/images/hud/runtime/InventoryIcon-128.png new file mode 100644 index 0000000..29face0 Binary files /dev/null and b/apps/web/public/images/hud/runtime/InventoryIcon-128.png differ diff --git a/apps/web/public/images/hud/runtime/MapsIcon-128.png b/apps/web/public/images/hud/runtime/MapsIcon-128.png new file mode 100644 index 0000000..3f75aa4 Binary files /dev/null and b/apps/web/public/images/hud/runtime/MapsIcon-128.png differ diff --git a/apps/web/public/images/hud/runtime/QuestsIcon-128.png b/apps/web/public/images/hud/runtime/QuestsIcon-128.png new file mode 100644 index 0000000..c11f378 Binary files /dev/null and b/apps/web/public/images/hud/runtime/QuestsIcon-128.png differ diff --git a/apps/web/src/app/app.spec.ts b/apps/web/src/app/app.spec.ts index f31b0d5..04c4311 100644 --- a/apps/web/src/app/app.spec.ts +++ b/apps/web/src/app/app.spec.ts @@ -1,18 +1,24 @@ -import { signal } from '@angular/core'; +import { signal, WritableSignal } from '@angular/core'; import { TestBed } from '@angular/core/testing'; import { provideRouter } from '@angular/router'; +import { CharacterResponse } from './core/api/game-api.models'; import { WorldStore } from './features/world/world.store'; import { AppShellComponent } from './layout/app-shell/app-shell.component'; +import { routes } from './app.routes'; describe('App', () => { + let character: WritableSignal; + beforeEach(async () => { + character = signal(null); + await TestBed.configureTestingModule({ imports: [AppShellComponent], providers: [ provideRouter([]), { provide: WorldStore, - useValue: { character: signal(null) }, + useValue: { character }, }, ], }).compileComponents(); @@ -32,6 +38,7 @@ describe('App', () => { expect(mapButton).not.toBeNull(); expect(mapButton?.disabled).toBe(false); expect(mapButton?.getAttribute('aria-current')).toBe('page'); + expect(mapButton?.getAttribute('aria-label')).toBe('Karte'); for (const destination of ['hunt', 'quests', 'inventory', 'character']) { expect( @@ -41,4 +48,33 @@ describe('App', () => { expect(element.textContent).not.toContain('Shop'); }); + + it('renders loaded character values supplied by the WorldStore', () => { + character.set({ + id: 'character-id', + name: 'Mara Ashfall', + level: 7, + experience: 320, + currentHp: 52, + maxHp: 80, + attack: 12, + currentLocation: { id: 'location-id', key: 'south-gate', name: 'Südtor von Graufurt' }, + }); + const fixture = TestBed.createComponent(AppShellComponent); + fixture.detectChanges(); + + expect(fixture.nativeElement.querySelector('app-top-bar')?.textContent).toContain( + 'Mara Ashfall', + ); + expect(fixture.nativeElement.querySelector('app-top-bar')?.textContent).toContain('Stufe 7'); + expect(fixture.nativeElement.querySelector('app-top-bar')?.textContent).toContain('52 / 80'); + }); + + it('redirects root and unknown routes to the world shell', () => { + expect(routes.find((route) => route.path === '')).toMatchObject({ + pathMatch: 'full', + redirectTo: 'world', + }); + expect(routes.find((route) => route.path === '**')).toMatchObject({ redirectTo: 'world' }); + }); }); diff --git a/apps/web/src/app/layout/app-shell/app-shell.component.scss b/apps/web/src/app/layout/app-shell/app-shell.component.scss index 3fdc3b4..9f90797 100644 --- a/apps/web/src/app/layout/app-shell/app-shell.component.scss +++ b/apps/web/src/app/layout/app-shell/app-shell.component.scss @@ -27,11 +27,22 @@ @media (width < 900px) { .app-shell__content { - grid-template-columns: minmax(8.5rem, 10rem) minmax(0, 1fr); + grid-template: + 'navigation main' minmax(0, 1fr) + 'context context' auto / minmax(8.5rem, 10rem) minmax(0, 1fr); + } + + app-side-navigation { + grid-area: navigation; + } + + .app-shell__main { + grid-area: main; } app-context-panel { - display: none; + display: block; + grid-area: context; } } diff --git a/apps/web/src/app/layout/side-navigation/side-navigation.component.html b/apps/web/src/app/layout/side-navigation/side-navigation.component.html index 719e58d..2a34f08 100644 --- a/apps/web/src/app/layout/side-navigation/side-navigation.component.html +++ b/apps/web/src/app/layout/side-navigation/side-navigation.component.html @@ -5,8 +5,9 @@ routerLink="/world" data-navigation="world" aria-current="page" + aria-label="Karte" > - + Karte @@ -17,7 +18,7 @@ disabled aria-label="Jagd ist noch nicht verfügbar" > - + Jagd diff --git a/apps/web/src/app/layout/top-bar/top-bar.component.html b/apps/web/src/app/layout/top-bar/top-bar.component.html index 783ac05..dde5327 100644 --- a/apps/web/src/app/layout/top-bar/top-bar.component.html +++ b/apps/web/src/app/layout/top-bar/top-bar.component.html @@ -1,6 +1,6 @@
- + @if (character(); as character) {
{{ character.name }}