Commit Graph

27 Commits

Author SHA1 Message Date
football2801 70d48f9b11 fix: rotate portrait renders to EPD-native byte layout
CI / test (push) Has been cancelled
The .bin file for portrait orientation was packed as 480-pixel rows ×
800 rows, but firmware streams blindly at 800×480 (the EPD's native
scan order). Both layouts hit the same 192000-byte total, so the size
guard in epd_draw_image_with_border passed and the row-stride mismatch
showed up on the panel as the photo tiling/repeating.

Renderer now rotates the cropped photo 90° CW before dithering when
orientation is portrait, so the packed bytes always match the EPD's
800-pixel scan order. Firmware stays orientation-unaware (per the
"ESP32 never transforms images" decision in webApp/CLAUDE.md). Preview
decoder rotates -90° on the way out so the in-browser frame preview
stays upright.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 14:06:41 -04:00
football2801 c2b208f103 fix: decode .bin to PNG via PPM blob, not importImagePixels
CI / test (push) Has been cancelled
The Imagick extension on the server (3.7+) requires importImagePixels
to receive an array of ints, not a string blob, so the previous code
500'd. Wrap the raw RGB bytes in a PPM (P6) header and let Imagick
decode it via readImageBlob — same result, no 70 MB-array detour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 13:16:58 -04:00
football2801 b0c27a9332 fix: gate dama doctrine test bundle config under when@test
CI / test (push) Has been cancelled
Bundle was registered for test env only but its YAML config loaded
unconditionally, which broke `bin/console cache:clear` in dev/prod
when the bundle is enabled but the env doesn't load it. The file now
only contributes to the test container.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 12:46:02 -04:00
football2801 fc0111a18e feat: show currently selected image on home screen frame card
CI / test (push) Has been cancelled
Decode the device's rendered 4bpp Spectra-6 .bin into a PNG (cached
next to the .bin) so the home-screen preview matches the dithered
6-color output the e-ink actually displays.

- New endpoint: GET /api/devices/{id}/preview
- Expose currentImageId on device JSON
- HomeView passes preview URL to FrameCard for both single and compact layouts
- Drive-by: fix vite.config.ts to import defineConfig from vitest/config
  so the build no longer fails on the unknown `test` property; remove
  unused useUploadStore import in HomeView test

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 12:45:06 -04:00
football2801 199a75fd72 chore: verify repo split — webApp remote works
CI / test (push) Has been cancelled
2026-05-06 12:23:59 -04:00
football2801 12245759ac chore: stage all in-progress work before repo split
CI / test (push) Has been cancelled
Web app: new entities (Image, RenderedAsset, SharedImage, Token,
DeviceImageHistory), enums, repositories, controllers, message handlers,
migrations, tests, frontend upload/library/sticker UI, Vue components.

Firmware: EPD background screen binaries + gen scripts, setup_bg header.

Infra: ddev config, test bundle, gitignore coverage dir.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 12:11:31 -04:00
football2801 062c52eec7 feat: visual orientation picker on configure page + fix 404→setup QR in firmware
Replace orientation <select> dropdown on setup/configure with the same
visual two-button picker used in the SPA (SVG frame diagrams, ribbon
indicator, active highlight). Hidden input carries the value on submit.

Firmware: normal_operation() now calls show_setup_qr(mac) on 404 instead
of epd_fill(COLOR_RED) — device shows scannable QR with its own MAC when
not yet registered.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 21:59:37 -04:00
football2801 7408dc9d1a fix: render plainPassword first/second fields separately in setup form
RepeatedType renders as a wrapper div when called as a single widget,
producing unstyled inputs with no mismatch error handling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 21:30:02 -04:00
football2801 ff087ee461 fix: set vite base to /build/ so asset paths match the actual serve location
Assets built to public/build/assets/ but index.html referenced /assets/
(no /build/ prefix). Nginx couldn't find them, fell through to Symfony's
catch-all SPA route, which served HTML in place of JS — Vue never loaded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 21:21:51 -04:00
football2801 604d37d102 fix: trust Traefik reverse proxy so Symfony generates https:// redirects
Traefik terminates TLS and forwards X-Forwarded-Proto: https to Nginx,
which forwards it to PHP-FPM. Without trusted_proxies, Symfony ignored
this header and generated http:// redirect URLs after login/register,
causing session cookie loss on mobile (Secure cookie not sent over HTTP).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 19:10:44 -04:00
football2801 f7b0202711 chore: fix .gitignore — exclude .pio build artifacts and __pycache__, track public/build
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 16:59:28 -04:00
football2801 6c891d6fad feat: orientation model, password confirm, frontend build
- Collapse orientation to landscape/portrait (ribbon left = portrait standard)
- Add OrientationPicker component and wire settings sheet in HomeView
- Add password confirmation field to registration form (RepeatedType)
- Build frontend SPA to public/build/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 16:59:03 -04:00
football2801 6c7c7a1a6f feat(story-2.4): home screen device list with FrameCard component
- FrameCard: large (single device, 5:3 preview + Add Photo CTA) and
  compact (52px thumb + name + count + icon pill) variants; WCAG-
  compliant offline/sync-fail status (color + text, never color alone)
- devices Pinia store: fetchDevices() → GET /api/devices
- HomeView: 0 devices → dashed empty-state card; 1 device → large
  FrameCard; 2+ → compact stack; add-photo wired (Epic 3 stub)
- Fix Device type: rotationInterval → rotationIntervalHours to match API

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 00:50:46 -04:00
football2801 fb380c45bd feat(story-2.2+2.3): device setup page, account linking, naming & configuration
Story 2.2 — /setup/{mac} Twig page (no Vue, works JS-disabled):
- Register tab: creates account + logs in + links device → /setup/{mac}/configure
- Login tab: manual credential check via UserPasswordHasherInterface + Security::login()
  + links device → /setup/{mac}/configure
- Re-provisioning: DeviceService.linkToUser() atomically transfers ownership + stubs
  purgeDeviceHistory() (completed in Epic 3 when Image/Approval entities exist)

Story 2.3 — /setup/{mac}/configure (requires auth):
- GET: device name, orientation (landscape/portrait), rotation interval (6/12/24/48/168h),
  uniqueness window (5/10/20/50 cycles)
- POST: validates name, saves to Device entity, redirects to Vue SPA
- Device entity: mac, name, orientation (Orientation enum), rotationIntervalHours,
  uniquenessWindow, user (ManyToOne), linkedAt
- PATCH /api/devices/{id}: Vue SPA can edit any device field (Story 2.3 "edit from app")
- GET /api/devices: list authenticated user's devices
- Migration: create device table with Orientation enum column

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 00:47:14 -04:00
football2801 15bab87998 feat(story-1.5): theme selection and persistence
- SpaController: injects data-theme on <html> and window.__PF_USER__ before JS
  hydrates — theme applied without FOUC; no initial API call needed for user data
- UserApiController: PATCH /api/user/theme validates against 6 allowed theme IDs,
  persists to user.theme column, returns {theme}
- useTheme composable: applyTheme() sets html[data-theme], saveTheme() calls API
  and falls back with toast on error
- SettingsView: 3-col theme grid with swatch previews, aria-checked radio semantics,
  active indicator; Sign out link; signed-in email display
- App.vue: onMounted syncs Pinia theme state with SpaController-stamped html[data-theme]

Verified: data-theme injected on / load; PATCH saves to DB; reload shows persisted theme

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 00:37:59 -04:00
football2801 3c1d5f0eae feat(story-1.4): user login with remember_me, inline error, logout
- Login Twig template: styled to match register page; inline "Incorrect email or
  password" on both fields (no email-existence disclosure); aria-invalid on error
- security.yaml: always_remember_me: true — REMEMBERME cookie set on every login
- Logout: /logout → session invalidated → 302 /login (Symfony firewall handles it)

Verified: correct creds → 302 / + REMEMBERME cookie; wrong creds → 302 /login +
          inline error on re-render; logout → 302 /login; GET / after logout → 302 /login

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 23:36:39 -04:00
football2801 694843bdf0 feat(story-1.3): user registration with auto-login and inline validation
- RegistrationFormType: email + plainPassword, NotBlank/Email/Length(min=8) constraints
- SecurityController: register action hashes password, persists user, auto-logs in via Security::login()
- User entity: UniqueEntity constraint — "An account with this email already exists"
- Register Twig template: inline errors per field (role=alert), blur-validation JS
  (client fires on blur not keystroke; server-error flag prevents blur clobbering server messages)
- csrf.yaml: switched from stateless UX-dependent tokens to standard session CSRF
  (stateless token IDs require Stimulus JS to inject the real value — we removed Stimulus)

Verified: happy path → 302 + auto-login; duplicate email → 422 + inline error;
          short password → 422 + inline error

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 23:25:42 -04:00
football2801 a55b3bd187 feat(story-1.2): Vue 3 SPA scaffold, base component library, User entity, SpaController
Frontend:
- Vue 3 + Vite + TypeScript strict in frontend/; builds to public/build/
- Vue Router (hash-history, requiresAuth guard → /login) and Pinia
- Global SCSS design tokens with 6 full themes (Warm Craft, Playful Pop, Sage & Cream, Dusty Mauve, Ocean Dusk, Honey & Slate)
- Base components: BaseButton (5 variants), BaseInput (floating label, error state),
  BaseBottomSheet (slide-up, focus trap, tap-outside dismiss), BaseCard, BaseChip,
  BaseToast (2.5s auto-dismiss, aria-live polite), BottomNav (4 tabs, hides at 960px)
- Type stubs for all API shapes: User, Device, Image, StickerLayer, RenderedAsset, Token

Backend:
- SpaController catch-all serves public/build/index.html; excludes api/setup/token/login/register
- User entity (email+password+roles+theme); UserRepository with PasswordUpgrader
- SecurityController with /login, /logout, /register stubs; Twig login form
- security.yaml: form_login firewall, remember_me, role_hierarchy, access_control
- Migration: create user table

Verified: npm run build succeeds, GET / → 302 /login (unauthenticated)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 23:21:29 -04:00
football2801 378b0b858b feat(story-1.1): scaffold Symfony 7.4 LTS app with DDEV, Messenger, Scheduler
- DDEV config: PHP 8.4, PostgreSQL 16, nginx-fpm, Imagick via webimage_extra_packages
- Symfony 7.4 LTS skeleton + webapp pack scaffolded via Composer
- Removed AssetMapper, Stimulus, UX-Turbo (replaced by Vue 3 SPA per architecture)
- Added symfony/messenger + symfony/scheduler (Doctrine transport)
- Gitea CI workflow: PHP 8.4 container + PostgreSQL 16 service, runs phpunit

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 22:57:09 -04:00
football2801 543f02b72a chore: restructure firmware into subdirectory, add DDEV config
Move firmware files from repo root src/ into firmware/ to avoid
collision with Symfony's src/ PHP class directory. Add DDEV
config targeting PHP 8.4 / PostgreSQL 16 / nginx-fpm with
Imagick extension via docker-compose override.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 22:51:19 -04:00
football2801 21e9173508 docs: add complete epics and stories breakdown
6 epics, 34 stories, 44/44 FRs covered. Includes party mode review
fixes: sticker canvas split into interaction + state persistence stories,
zero-device upload edge case AC, FrameCard offline/sync-fail states,
and scheduler setup story. All stories have Given/When/Then AC and
no forward dependencies.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 22:20:44 -04:00
football2801 94dae685e2 docs: update architecture to reflect Vue 3 SPA frontend decision
Replace AssetMapper + Stimulus + Turbo with Vue 3 SPA (Vite,
TypeScript strict, SCSS modules, Konva.js). Authenticated app is
now a full SPA served by Symfony catch-all; public flows (provisioning,
email approve/decline) remain Symfony + Twig. Add JSON API controllers
for SPA, SpaController catch-all, updated directory structure, and
revised implementation sequence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 21:57:06 -04:00
football2801 2a2c8ae343 docs: add UX design specification and visual explorers
Complete 14-step UX design spec covering core experience, emotional
response, design system (Vue 3 + TypeScript + SCSS + Konva.js), 6
color themes, Direction 5 (Minimal Card), user journeys, component
strategy, UX patterns, and responsive/accessibility requirements.
Includes interactive theme explorer and design direction mockups.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 21:54:01 -04:00
football2801 663e39b2af chore: add credentials reference memory
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 15:46:07 -04:00
football2801 44783d0f46 docs: add planning artifacts — PRD, architecture, epics skeleton
One-time commit of AI-generated planning documents:
- prd.md — validated PRD, 44 FRs across 8 domains
- prd-validation-report.md — validation results (4/5 holistic quality)
- architecture.md — complete architecture document (all 8 steps)
- epics.md — epics skeleton with extracted requirements inventory

_bmad-output/ remains gitignored; these are committed explicitly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 15:39:24 -04:00
football2801 a536baabd6 feat: initial commit — BMAD tooling, Claude memories, firmware scaffold
Adds the complete project foundation:
- BMAD BMM workflow tooling (_bmad/)
- Claude slash commands, skills, and project memories (.claude/)
- ESP32 firmware scaffold (PlatformIO + Waveshare e-ink driver)
- .gitignore excluding _bmad-output/ and .pio/ build artifacts

Planning artifacts (PRD, architecture, epics) are intentionally not
tracked — they live in _bmad-output/ per project convention.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 15:38:46 -04:00
football2801 f40d902b7c chore: initial project scaffold with BMAD tooling 2026-04-27 12:50:18 -04:00