CI resolves dependencies fresh (pubspec.lock is gitignored), so the
v1.7.0 build failed when dio 5.10 introduced transformTimeout. A
default clause absorbs future enum additions on either dio version;
unknown keeps identical behavior through it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Network-migration release: default Core API and Tatlock API URLs now
point at the public https schweitz.net domains, and the decommissioned
Netdata / code-server quick links are removed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Netdata was never deployed and code-server was decommissioned on
2026-07-19; their quick links pointed at dead domains. The
portainerUrl/netdataUrl constants had no consumers (quick links
hardcode their own URLs). The Portainer quick link stays — its
portainer.schweitz.net host arrives with the port-lockdown phase.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Browser clients run on machines other than the host, and the homelab is
retiring direct LAN IP:port access (ports move to loopback behind NPM),
so the 192.168.86.149 defaults would stop working. The public domains
work from anywhere; LAN clients bypass Authentik via source-IP rules.
LAN development can still override via --dart-define.
Portainer (9000) and Netdata (19999) defaults are left unchanged: no
*.schweitz.net proxy hosts exist for those services yet.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The .internal registry domain is being retired; git.schweitz.net now
serves the registry without SSO on /v2/.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add NewsTickerWidget with horizontal auto-scrolling at 40px/sec
- Add NewsData and NewsHeadline Freezed models
- Add news datasource fetching from /tools/news endpoint
- Add news provider with 30-minute auto-refresh
- Place ticker between Welcome card and System Stats on dashboard
- Show placeholder headlines when no data (italic, muted style)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Weather/Air Quality dividers align with Sun Position horizon
- Forecast card bottoms align with same horizon line
- Unified visual rhythm across all cards
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- All environment widgets align content from bottom for visual harmony
- Wind chip now shows direction (e.g., "SE 14 km/h")
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Consistent 170px minHeight across Weather, Air Quality, Forecast widgets
- Swap sunrise/sunset labels at night to match arc direction
- Weather header shows "Weather" instead of location
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Constrain arc height to fit within card boundaries
- Scale radius down when arc would overflow on wider displays
- Revert user display in section header, use debugPrint instead
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add user display in environment section header for debugging OIDC
user resolution issues.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Arc angle now proportional to day/night duration (day = daylight/24 × 360°)
- Horizon points represent sunrise/sunset times
- Day arc with sun icon and yellow/orange gradient
- Night arc with moon icon and blue/indigo gradient
- Position updates every 10 minutes aligned to clock (0/10/20/30/40/50)
- Default to 07:00-17:00 when API data unavailable (asymmetric for visual effect)
- All environment cards maintain consistent height in "no data" state
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Desktop: 4-in-a-row layout (30/20/20/30 distribution)
- Tablet: 2x2 grid layout
- Mobile: Stacked vertically
- All widgets show "No data available" state instead of being hidden
- Sun position calculates from clock, defaults to 6am/6pm
- Environment refresh changed from 5min to 1 hour
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
feat: add dynamic environment widgets
Add live weather, sun position, and forecast widgets to Front Hall dashboard,
powered by data from the Qdrant volatile collection via core-api.
New widgets:
- SunPositionWidget: Animated arc showing sun/moon position with gradient colors
- ForecastWidget: Multi-day weather outlook
- Updated WeatherWidget and AirQualityWidget to accept API data
Infrastructure:
- Environment datasource calling GET /tools/environment
- Environment provider with 5-minute auto-refresh
- Freezed models for environment data
Tests:
- 12 widget tests for environment section
- Updated existing tests with givenEnvironment() harness method
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add shared screen size constants and configureScreenSize() helper in
test/harness/screen_sizes.dart. Add responsive test groups to all page
tests covering desktop (1920x1080), tablet landscape (1024x768), tablet
portrait (768x1024), mobile (375x812), and small mobile (320x568).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Decentralized Room Registry pattern
- Each room registers itself with central registry
- Dynamic navigation tabs and settings dropdown
- New Media Room and Parlor feature folders
- Permission-based room filtering support
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Root `/` now redirects to user's preferred default room
- Front Hall moved to `/front-hall` route (was `/`)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add PageUrlState utility for URL ↔ state serialization
- Add column `id` field for unique column identification in URLs
- Update idSelector to return String for URL compatibility
- All DataGrid pages now support URL params: search, sort, order, id
- Browser URL updates via replaceState (no GoRouter rebuilds)
- Add FilterPanelSemantics for filter panel semantic IDs
- Add TESTING.md documentation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Created lib/core/semantics/ with semantic ID constants and helper widget
- Enabled SemanticsBinding on web builds for accessibility tree exposure
- Added semantic IDs to:
- ProfileDropdown (button, settings, theme options, logout)
- TopHeaderBar room tabs (frontHall, controlRoom, security, parlor)
- NavPanel items (nav_item_{id})
This enables browser automation tools like Puppeteer and WebDriver to
discover and interact with Flutter widgets via the accessibility tree.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The AuthInterceptor was calling signOut() on any 401 error, which caused
the theme toggle to trigger logout when the preferences API returned 401.
Now 401 errors propagate to calling code for graceful handling.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Applied @persistentRiverpod annotation to AuthNotifier so it persists
for app lifetime. Previously, theme changes could trigger AuthProvider
rebuild via auto-dispose, causing AsyncLoading state and auth issues.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add @persistentRiverpod annotation for providers that need keepAlive
- ThemeProvider now persists for app lifetime
- Refactored API clients to use @persistentRiverpod
- Documented in ARCHITECTURE.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Settings page with Appearance, Navigation, and Account sections
- Theme toggle (System/Light/Dark) in profile dropdown
- Theme syncs with API preferences on login
- Default room preference syncs with backend
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
API client providers (coreApiClientProvider, tatlockApiClientProvider) now
use keepAlive: true. This fixes "DioException [unknown]: null" errors on
pages like /security/users where ref.read() was used without subscription.
The AuthInterceptor stores a Ref that became invalid when the provider
auto-disposed after a one-time read.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Config for home.schweitz.net with Authentik forward auth:
- Static assets excluded via auth_request off
- Proper proxy pass to upstream
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove callback route - AuthController handles it before app starts:
- Removed /callback route from Flutter router
- Removed _OidcCallbackPage widget
- Auth is now invisible - no Flutter UI during auth flow
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Auth moved to standalone controller outside Riverpod:
- New AuthController runs in main() before runApp()
- Handles callback, token exchange, and /auth/sync before app starts
- If auth not ready (redirecting), app doesn't start at all
- AuthProvider now just loads stored tokens (no async OIDC logic)
- Fixes "Cannot use Ref after disposed" errors
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
AuthProvider.build() was initiating silent OIDC while the callback
page was processing the auth code, causing PKCE state to be cleared.
Now checks if on /callback route and skips silent OIDC initiation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When prompt=none fails with login_required (no Authentik session),
automatically redirect to regular OIDC flow to show login UI.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add prompt=none to silently obtain JWT when Authentik session exists
- Flutter sends Bearer token to core-api instead of forward auth cookies
- Fixes cross-subdomain cookie issues between home/api.schweitz.net
- Callback syncs with /auth/sync for user profile and roles
- API interceptor now adds Bearer token on web
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
On web, NPM forward auth handles authentication at the proxy level.
By the time the Flutter app loads, the user is already authenticated.
Skip the redundant Flutter OIDC flow that was causing Riverpod
"Ref disposed" errors from conflicting auth state updates.
Mobile still uses Flutter's OIDC flow as before.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix "Cannot use Ref after disposed" error in OIDC callback page
- Store notifier reference before async gap
- Add mounted check at start of processing
- Add proper SSO logout via Authentik end_session_endpoint
- Clears local tokens AND redirects to Authentik logout
- Returns to app after Authentik session ends
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>