From a4aaea33b8ce6024259c899bf39c76956f1e33e9 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Tue, 30 Dec 2025 18:21:52 +0100 Subject: [PATCH] refactor: rename features to room-based directory structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename features/dashboard โ†’ features/front_hall - Update routes: /, /control-room, /parlor, /settings - Update navigation to 4 rooms (removed chat tab - will be omnipresent dock) - Add docs/UI_LAYOUT.md with wireframes and responsive specs - Update ARCHITECTURE.md and PLAN.md to reflect room structure Directory structure now mirrors "Rooms of the Estate" UI navigation: - front_hall/ (dashboard) - control_room/ (infrastructure - containers, stacks, etc.) - parlor/ (home automation) - library/ (future) - study/ (future, hidden) ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- AGENTS.md | 5 +- CHANGELOG.md | 6 + PLAN.md | 160 +++++---- docs/ARCHITECTURE.md | 31 +- docs/UI_LAYOUT.md | 322 ++++++++++++++++++ .../presentation/pages/front_hall_page.dart} | 8 +- lib/routing/app_router.dart | 57 ++-- lib/shared/layouts/app_scaffold.dart | 42 ++- 8 files changed, 505 insertions(+), 126 deletions(-) create mode 100644 docs/UI_LAYOUT.md rename lib/features/{dashboard/presentation/pages/dashboard_page.dart => front_hall/presentation/pages/front_hall_page.dart} (95%) diff --git a/AGENTS.md b/AGENTS.md index 56cd1ab..2aff633 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,12 +32,15 @@ This document contains instructions and documentation references for AI assistan * **Health check**: `curl http://192.168.86.149:8000/health` ### ๐Ÿ›ก๏ธ Git Discipline -* **NEVER commit to `main` or `master` directly.** Always create a feature branch: `feature/your-feature-name` or `fix/issue-description`. * **Commit Messages:** Use the [Conventional Commits](https://www.conventionalcommits.org/) format. * `feat: add user login endpoint` * `fix: resolve database connection timeout` * `refactor: split monolith dependency file` * **Atomic Commits:** Keep commits small. One logical change = one commit. +* **Version Tagging:** Every version increment (major.minor.patch, not build count) must have a corresponding git tag. + * Format: `v{major}.{minor}.{patch}` (e.g., `v0.3.0`) + * Tag after updating `pubspec.yaml` version and CHANGELOG + * Push tags with `git push --tags` ### ๐Ÿงช Testing Requirements * **Always add tests for new code before committing.** No exceptions. diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a88534..1b9e557 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- docs/UI_LAYOUT.md - Responsive layout specification with "Rooms of the Estate" navigation + ### Changed +- Directory structure now mirrors room navigation (front_hall/, control_room/, parlor/, library/, study/) +- Chat moved to top-level `lib/chat/` (omnipresent, not a room) +- Updated ARCHITECTURE.md and PLAN.md to reflect room-based structure - Version generation now automated via build_runner (replaces manual script) - `lib/version.g.dart` auto-generated from `pubspec.yaml` during build diff --git a/PLAN.md b/PLAN.md index fcaf9b8..6c9c442 100644 --- a/PLAN.md +++ b/PLAN.md @@ -91,7 +91,8 @@ tatlock-ui/ โ”‚ โ”œโ”€โ”€ API_INTEGRATION.md # Core API and Tatlock API endpoints โ”‚ โ”œโ”€โ”€ DEPLOYMENT.md # Docker, NPM, Portainer setup โ”‚ โ”œโ”€โ”€ DATAGRID.md # DataGrid component API specification -โ”‚ โ””โ”€โ”€ THEMING.md # Theme system documentation +โ”‚ โ”œโ”€โ”€ THEMING.md # Theme system documentation +โ”‚ โ””โ”€โ”€ UI_LAYOUT.md # Wireframes, responsive breakpoints, room navigation ``` **Content for ARCHITECTURE.md** - Document: @@ -165,32 +166,31 @@ lib/shared/components/data_grid/ โ””โ”€โ”€ core_api_source.dart # Core API data source adapter ``` -### Phase 3: Dashboard (Organizr Replacement) +### Phase 3: Front Hall (Organizr Replacement) **Goal**: Replace Organizr homepage with Tatlock UI dashboard. **Features to match** (from portainer-ui.png): -- Sidebar navigation with grouped items -- System metrics gauges (CPU, Memory, Disk) -- Weather widget (optional - requires API) -- Air quality widget (optional) +- System metrics gauges (CPU, Memory, Disk, Containers) - Service quick links grid +- Recent activity timeline +- Chat dock expanded by default **Files to create**: ``` -lib/features/dashboard/ +lib/features/front_hall/ โ”œโ”€โ”€ presentation/ -โ”‚ โ”œโ”€โ”€ pages/dashboard_page.dart +โ”‚ โ”œโ”€โ”€ pages/front_hall_page.dart โ”‚ โ””โ”€โ”€ widgets/ -โ”‚ โ”œโ”€โ”€ metrics_gauge.dart +โ”‚ โ”œโ”€โ”€ stat_card.dart โ”‚ โ”œโ”€โ”€ service_card.dart โ”‚ โ”œโ”€โ”€ service_grid.dart -โ”‚ โ””โ”€โ”€ quick_actions.dart +โ”‚ โ””โ”€โ”€ activity_timeline.dart โ”œโ”€โ”€ domain/ โ”‚ โ”œโ”€โ”€ entities/system_metrics.dart -โ”‚ โ””โ”€โ”€ repositories/dashboard_repository.dart +โ”‚ โ””โ”€โ”€ repositories/front_hall_repository.dart โ””โ”€โ”€ data/ - โ”œโ”€โ”€ datasources/core_api_dashboard_source.dart - โ””โ”€โ”€ repositories/dashboard_repository_impl.dart + โ”œโ”€โ”€ datasources/core_api_front_hall_source.dart + โ””โ”€โ”€ repositories/front_hall_repository_impl.dart ``` **Core API endpoints used**: @@ -198,35 +198,41 @@ lib/features/dashboard/ - `GET /infrastructure/widget-data` - Services + groups - `GET /health` - Overall health status -### Phase 4: Container Management +### Phase 4: Control Room - Container Management **Goal**: Full container CRUD and monitoring. **Files to create**: ``` -lib/features/containers/ -โ”œโ”€โ”€ presentation/ -โ”‚ โ”œโ”€โ”€ pages/ -โ”‚ โ”‚ โ”œโ”€โ”€ containers_list_page.dart -โ”‚ โ”‚ โ””โ”€โ”€ container_detail_page.dart -โ”‚ โ”œโ”€โ”€ widgets/ -โ”‚ โ”‚ โ”œโ”€โ”€ container_status_badge.dart -โ”‚ โ”‚ โ”œโ”€โ”€ container_logs_viewer.dart -โ”‚ โ”‚ โ””โ”€โ”€ container_resource_chart.dart -โ”‚ โ””โ”€โ”€ providers/ -โ”‚ โ””โ”€โ”€ containers_provider.dart -โ”œโ”€โ”€ domain/ -โ”‚ โ”œโ”€โ”€ entities/container.dart -โ”‚ โ”œโ”€โ”€ repositories/container_repository.dart -โ”‚ โ””โ”€โ”€ usecases/ -โ”‚ โ”œโ”€โ”€ get_containers.dart -โ”‚ โ”œโ”€โ”€ start_container.dart -โ”‚ โ”œโ”€โ”€ stop_container.dart -โ”‚ โ”œโ”€โ”€ restart_container.dart -โ”‚ โ””โ”€โ”€ get_container_logs.dart -โ””โ”€โ”€ data/ - โ”œโ”€โ”€ models/container_model.dart - โ”œโ”€โ”€ datasources/containers_datasource.dart - โ””โ”€โ”€ repositories/container_repository_impl.dart +lib/features/control_room/ +โ”œโ”€โ”€ shared/ # Shared within Control Room +โ”‚ โ””โ”€โ”€ widgets/ +โ”‚ โ””โ”€โ”€ resource_chart.dart +โ”œโ”€โ”€ containers/ +โ”‚ โ”œโ”€โ”€ presentation/ +โ”‚ โ”‚ โ”œโ”€โ”€ pages/ +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ containers_list_page.dart +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ container_detail_page.dart +โ”‚ โ”‚ โ”œโ”€โ”€ widgets/ +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ container_status_badge.dart +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ container_logs_viewer.dart +โ”‚ โ”‚ โ””โ”€โ”€ providers/ +โ”‚ โ”‚ โ””โ”€โ”€ containers_provider.dart +โ”‚ โ”œโ”€โ”€ domain/ +โ”‚ โ”‚ โ”œโ”€โ”€ entities/container.dart +โ”‚ โ”‚ โ”œโ”€โ”€ repositories/container_repository.dart +โ”‚ โ”‚ โ””โ”€โ”€ usecases/ +โ”‚ โ”‚ โ”œโ”€โ”€ get_containers.dart +โ”‚ โ”‚ โ”œโ”€โ”€ start_container.dart +โ”‚ โ”‚ โ”œโ”€โ”€ stop_container.dart +โ”‚ โ”‚ โ”œโ”€โ”€ restart_container.dart +โ”‚ โ”‚ โ””โ”€โ”€ get_container_logs.dart +โ”‚ โ””โ”€โ”€ data/ +โ”‚ โ”œโ”€โ”€ models/container_model.dart +โ”‚ โ”œโ”€โ”€ datasources/containers_datasource.dart +โ”‚ โ””โ”€โ”€ repositories/container_repository_impl.dart +โ”œโ”€โ”€ stacks/ # Future +โ”œโ”€โ”€ networks/ # Future +โ””โ”€โ”€ volumes/ # Future ``` **Core API endpoints used**: @@ -236,17 +242,20 @@ lib/features/containers/ - `POST /infrastructure/containers/{id}/{action}` - `GET /infrastructure/resources/containers` -### Phase 5: LLM Chat Interface -**Goal**: Custom chat UI for Tatlock agents with SSE streaming. +### Phase 5: Chat Dock (Omnipresent) +**Goal**: Custom chat UI for Tatlock agents with SSE streaming. Injected at layout level, not a room. + +**Future requirement**: Expand-to-fullscreen option as secondary UI overlay. **Files to create**: ``` lib/core/api/ โ””โ”€โ”€ sse_client.dart # Platform-aware SSE streaming -lib/features/chat/ +lib/chat/ # Top-level, not under features/ โ”œโ”€โ”€ presentation/ -โ”‚ โ”œโ”€โ”€ pages/chat_page.dart +โ”‚ โ”œโ”€โ”€ chat_dock.dart # Collapsible dock widget +โ”‚ โ”œโ”€โ”€ chat_fullscreen.dart # Fullscreen overlay (future) โ”‚ โ””โ”€โ”€ widgets/ โ”‚ โ”œโ”€โ”€ message_list.dart โ”‚ โ”œโ”€โ”€ message_bubble.dart @@ -280,15 +289,15 @@ lib/features/chat/ - Mobile/Desktop: eventsource or http package with stream parsing - Handle `reasoning_content` field for think blocks (DeepSeek R1 format) -### Phase 6: Home Automation +### Phase 6: Parlor (Home Automation) **Goal**: Control Home Assistant via Core API. **Files to create**: ``` -lib/features/housekeeping/ +lib/features/parlor/ โ”œโ”€โ”€ presentation/ โ”‚ โ”œโ”€โ”€ pages/ -โ”‚ โ”‚ โ”œโ”€โ”€ housekeeping_page.dart +โ”‚ โ”‚ โ”œโ”€โ”€ parlor_page.dart โ”‚ โ”‚ โ””โ”€โ”€ area_detail_page.dart โ”‚ โ””โ”€โ”€ widgets/ โ”‚ โ”œโ”€โ”€ device_tile.dart @@ -300,13 +309,13 @@ lib/features/housekeeping/ โ”‚ โ”‚ โ”œโ”€โ”€ device.dart โ”‚ โ”‚ โ”œโ”€โ”€ area.dart โ”‚ โ”‚ โ””โ”€โ”€ scene.dart -โ”‚ โ””โ”€โ”€ repositories/housekeeping_repository.dart +โ”‚ โ””โ”€โ”€ repositories/parlor_repository.dart โ””โ”€โ”€ data/ โ”œโ”€โ”€ models/ โ”‚ โ”œโ”€โ”€ device_model.dart โ”‚ โ””โ”€โ”€ device_control_request.dart - โ”œโ”€โ”€ datasources/housekeeping_datasource.dart - โ””โ”€โ”€ repositories/housekeeping_repository_impl.dart + โ”œโ”€โ”€ datasources/parlor_datasource.dart + โ””โ”€โ”€ repositories/parlor_repository_impl.dart ``` **Core API endpoints used**: @@ -608,6 +617,7 @@ dev_dependencies: ## Reference Documents +- **UI Layout Spec**: `docs/UI_LAYOUT.md` - Wireframes, breakpoints, responsive behavior - **Infrastructure**: `/mnt/media/Projects/portainer-core/CONTAINERS.md` - **Tatlock Philosophy**: `/mnt/media/Projects/tatlock/PHILOSOPHY.md` - **Core API**: `http://192.168.86.149:8083/docs` @@ -618,26 +628,44 @@ dev_dependencies: --- -## Navigation Structure (matching Organizr) +## Navigation Structure ("Rooms of the Estate") + +> **See [docs/UI_LAYOUT.md](docs/UI_LAYOUT.md) for detailed wireframes and responsive specifications.** + +The UI uses **tabbed room navigation** in the header rather than a traditional sidebar. Each room is a focused context with its own layout. The Tatlock chat assistant is omnipresent as a collapsible right dock. ``` -โ”œโ”€โ”€ Homepage (Dashboard) -โ”œโ”€โ”€ Tatlock (Chat) -โ”œโ”€โ”€ Jellyfin (external link) -โ”œโ”€โ”€ SearXNG (external link) -โ”œโ”€โ”€ AMP -โ”‚ โ””โ”€โ”€ AMP Home -โ”œโ”€โ”€ Coding -โ”‚ โ”œโ”€โ”€ Cloud IDE (external) -โ”‚ โ””โ”€โ”€ Gitea (external) -โ”œโ”€โ”€ Infrastructure -โ”‚ โ”œโ”€โ”€ Containers (main feature) -โ”‚ โ”œโ”€โ”€ Netdata (external link) -โ”‚ โ”œโ”€โ”€ Portainer (external link) -โ”‚ โ””โ”€โ”€ Proxy Manager (external link) -โ”œโ”€โ”€ Housekeeping (Home Automation) -โ”œโ”€โ”€ API Docs (external link to Core API) -โ””โ”€โ”€ Settings +Header Tabs: +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Front Hall โ”‚ Control Room โ”‚ Parlor โ”‚ Library โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + +Room Contents: +โ”œโ”€โ”€ Front Hall (Dashboard) +โ”‚ โ”œโ”€โ”€ Stat cards (CPU, Memory, Disk, Containers) +โ”‚ โ”œโ”€โ”€ Quick access service grid (external links) +โ”‚ โ”œโ”€โ”€ Recent activity timeline +โ”‚ โ””โ”€โ”€ Chat dock EXPANDED by default +โ”‚ +โ”œโ”€โ”€ Control Room (Infrastructure) +โ”‚ โ”œโ”€โ”€ Context sidebar: Containers, Stacks, Networks, Volumes, Images +โ”‚ โ”œโ”€โ”€ External links: Netdata, Portainer, NPM +โ”‚ โ”œโ”€โ”€ DataGrid views for each section +โ”‚ โ””โ”€โ”€ Chat dock collapsed +โ”‚ +โ”œโ”€โ”€ Parlor (Housekeeping) +โ”‚ โ”œโ”€โ”€ Context sidebar: Areas, Scenes +โ”‚ โ”œโ”€โ”€ Device control tiles per area +โ”‚ โ”œโ”€โ”€ Scene activation buttons +โ”‚ โ””โ”€โ”€ Chat dock collapsed +โ”‚ +โ”œโ”€โ”€ Library (Future) +โ”‚ โ”œโ”€โ”€ Knowledge management +โ”‚ โ”œโ”€โ”€ Bookmarks, notes, docs +โ”‚ โ””โ”€โ”€ Chat dock collapsed +โ”‚ +โ””โ”€โ”€ Study (Hidden - Future) + โ””โ”€โ”€ Secretarial: email, calendar ``` --- diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index e5dcb5d..73bb0a2 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -8,6 +8,8 @@ Tatlock UI follows **Clean Architecture** with feature-based organization. This ## Project Structure +The directory structure mirrors the "Rooms of the Estate" UI navigation (see [UI_LAYOUT.md](./UI_LAYOUT.md)). + ``` lib/ โ”œโ”€โ”€ main.dart # Entry point @@ -23,13 +25,32 @@ lib/ โ”œโ”€โ”€ shared/ # Reusable components โ”‚ โ”œโ”€โ”€ components/ # DataGrid, common widgets โ”‚ โ””โ”€โ”€ layouts/ # App scaffold, navigation -โ””โ”€โ”€ features/ # Feature modules - โ”œโ”€โ”€ dashboard/ - โ”œโ”€โ”€ chat/ - โ”œโ”€โ”€ containers/ - โ””โ”€โ”€ housekeeping/ +โ”œโ”€โ”€ features/ # Feature modules (rooms) +โ”‚ โ”œโ”€โ”€ front_hall/ # Dashboard - estate overview +โ”‚ โ”œโ”€โ”€ control_room/ # Infrastructure +โ”‚ โ”‚ โ”œโ”€โ”€ containers/ # Container management +โ”‚ โ”‚ โ”œโ”€โ”€ stacks/ # Stack management +โ”‚ โ”‚ โ”œโ”€โ”€ networks/ # Network management +โ”‚ โ”‚ โ””โ”€โ”€ volumes/ # Volume management +โ”‚ โ”œโ”€โ”€ parlor/ # Housekeeping - home automation +โ”‚ โ”œโ”€โ”€ library/ # Knowledge management (future) +โ”‚ โ””โ”€โ”€ study/ # Secretarial tasks (future) +โ””โ”€โ”€ chat/ # Tatlock chat - omnipresent, NOT a room ``` +### Room-to-Directory Mapping + +| UI Room | Directory | Purpose | +|---------|-----------|---------| +| Front Hall | `features/front_hall/` | Dashboard, overview, quick access | +| Control Room | `features/control_room/` | Infrastructure management | +| Parlor | `features/parlor/` | Home automation | +| Library | `features/library/` | Knowledge, docs, bookmarks | +| Study | `features/study/` | Email, calendar (hidden for now) | +| *(omnipresent)* | `chat/` | Tatlock assistant dock | + +Note: `chat/` lives at the top level of `lib/` (not under `features/`) because it's not a navigable room - it's an omnipresent dock injected at the layout level. + ## Feature Structure Each feature follows a three-layer architecture: diff --git a/docs/UI_LAYOUT.md b/docs/UI_LAYOUT.md new file mode 100644 index 0000000..a9a7887 --- /dev/null +++ b/docs/UI_LAYOUT.md @@ -0,0 +1,322 @@ +# UI Layout Specification + +This document defines the layout structure, responsive behavior, and component constraints for Tatlock UI. It implements the "Rooms of the Estate" metaphor from [PHILOSOPHY.md](../PHILOSOPHY.md). + +## Navigation Structure + +The UI uses a **tabbed room navigation** in the header rather than a traditional sidebar. Each "room" represents a distinct usage context, reducing cognitive clutter. + +| Room | Purpose | Chat Dock Default | +|------|---------|-------------------| +| **Front Hall** | Dashboard - estate overview, quick access, activity | Expanded | +| **Control Room** | Infrastructure - containers, stacks, monitoring | Collapsed | +| **Parlor** | Housekeeping - home automation, devices, scenes | Collapsed | +| **Library** | Knowledge - docs, notes, bookmarks (future) | Collapsed | + +**Hidden for now:** +- **Study** - Secretarial tasks (email, calendaring) - future implementation + +--- + +## Layout Structure + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ HEADER (intrinsic height) โ”‚ +โ”‚ [Logo] [โ•โ•โ•โ•โ•โ•โ• Room Tabs (scrollable) โ•โ•โ•โ•โ•โ•โ•] [Notifications] [Profile] โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ MAIN CONTENT โ”‚ โ”‚ CHAT DOCK โ”‚ โ”‚ +โ”‚ โ”‚ flex: 1 โ”‚ โ”‚ flex: 0 โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ Tatlock chat โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ CONTEXT โ”‚ โ”‚ PRIMARY โ”‚ โ”‚ โ”‚ assistant, โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ SIDEBAR โ”‚ โ”‚ CONTENT โ”‚ โ”‚ โ”‚ persistent โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ across rooms โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ flex: 0 โ”‚ โ”‚ flex: 1 โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ intrinsic โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ +โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +### Component Roles + +| Component | Flex | Description | +|-----------|------|-------------| +| **Header** | intrinsic | Logo, room tabs, action icons | +| **Main Content** | `flex: 1` | Fills remaining horizontal space | +| **Context Sidebar** | `flex: 0`, intrinsic | Room-specific navigation (Control Room, Parlor) | +| **Primary Content** | `flex: 1` | Room's main working area | +| **Chat Dock** | `flex: 0`, intrinsic | Tatlock assistant, collapsible | + +--- + +## Responsive Breakpoints + +### Wide (>= 1200px) + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ [Sidebar] [โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• Content โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•] โ”‚ Chat (expanded) โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +- Chat dock expanded by default on Front Hall +- Context sidebar visible with labels +- Full data grid columns + +### Medium (>= 800px, < 1200px) + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ” +โ”‚ [Sidebar] [โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• Content โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•] โ”‚ ๐Ÿ’ฌ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”˜ +``` + +- Chat dock collapsed to icon rail +- Click to expand as overlay +- Context sidebar still visible + +### Compact (>= 600px, < 800px) + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ” +โ”‚ [โ‰ก] [โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• Content โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•] โ”‚ ๐Ÿ’ฌ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”˜ +``` + +- Context sidebar becomes drawer (hamburger menu) +- Chat dock collapsed +- Reduced data grid columns + +### Mobile (< 600px) + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ [โ‰ก] [Tabs scroll horizontally] [๐Ÿ’ฌ] โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ โ”‚ +โ”‚ [โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• Content โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•] โ”‚ +โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +- Single column layout +- Room tabs scroll horizontally +- Chat opens as bottom sheet +- Context sidebar as full-screen drawer + +--- + +## Component Constraints + +| Component | Min Width | Max Width | Collapse Behavior | +|-----------|-----------|-----------|-------------------| +| Chat Dock (expanded) | 280px | 33vw | Collapses to icon rail | +| Chat Dock (collapsed) | 48px | 48px | - | +| Context Sidebar | 200px | - | Collapses to 56px icon rail, then drawer | +| Stat Card | 180px | 1fr | Grid reflows | +| Service Card | 120px | 1fr | Grid reflows | +| Data Grid | 400px | - | Horizontal scroll if needed | + +--- + +## Room Wireframes + +### Front Hall (Dashboard) + +Primary landing page. Chat dock expanded by default. + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ โ”‚ โ”‚ +โ”‚ STAT CARDS (auto-fit grid, min 180px) โ”‚ TATLOCK CHAT โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ +โ”‚ โ”‚ CPU โ”‚ โ”‚ Memory โ”‚ โ”‚ Disk โ”‚ โ”‚Containeโ”‚ โ”‚ [conversation] โ”‚ +โ”‚ โ”‚ 45% โ”‚ โ”‚ 62% โ”‚ โ”‚ 78% โ”‚ โ”‚ 34/40 โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘ โ”‚ โ”‚ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘ โ”‚ โ”‚ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ โ”‚ โ”‚ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ โ”‚ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ QUICK ACCESS (auto-fill grid, min 120px) โ”‚ โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ +โ”‚ โ”‚ ๐ŸŽฌ โ”‚ โ”‚ ๐Ÿ” โ”‚ โ”‚ ๐Ÿ“š โ”‚ โ”‚ ๐ŸŽฎ โ”‚ โ”‚ ๐ŸŒ โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚Jellyfinโ”‚ โ”‚SearXNG โ”‚ โ”‚ Wiki โ”‚ โ”‚ AMP โ”‚ โ”‚Requestsโ”‚ โ”‚ โ”‚ +โ”‚ โ”‚โ— Onlineโ”‚ โ”‚โ— Onlineโ”‚ โ”‚โ— Onlineโ”‚ โ”‚โ— Onlineโ”‚ โ”‚โ— Onlineโ”‚ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ RECENT ACTIVITY โ”‚ [input field] โ”‚ +โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ +โ”‚ โ”‚ ๐Ÿ“ฆ Container 'ollama' restarted 15 min ago โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ ๐Ÿ  Living room set to evening mode 1 hr ago โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ ๐Ÿ”„ System backup completed 2 hrs ago โ”‚ โ”‚ โ”‚ +โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +**Components:** +- Stat Cards: CPU, Memory, Disk, Containers +- Quick Access: Service cards grid (external links) +- Recent Activity: Timeline of system events + +--- + +### Control Room (Infrastructure) + +Context sidebar with section navigation. Chat collapsed. + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ SECTIONS โ”‚ CONTAINERS [Search] [+ New] โ”‚ ๐Ÿ’ฌ โ”‚ +โ”‚ โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚ โ”‚ +โ”‚ โ–ธ Containers โ”‚ โ˜‘ NAME STATUS CPU MEM IMAGE โ‹ฎ โ”‚ โ”‚ +โ”‚ Stacks โ”‚ โ˜ jellyfin โ— Run 2.3% 1.2GB latest โ‹ฎ โ”‚ โ”‚ +โ”‚ Networks โ”‚ โ˜ ollama โ— Run 45% 8.0GB 0.1.32 โ‹ฎ โ”‚ โ”‚ +โ”‚ Volumes โ”‚ โ˜ postgres โ— Run 1.1% 512MB 16-alp โ‹ฎ โ”‚ โ”‚ +โ”‚ Images โ”‚ โ˜ redis โ— Run 0.2% 128MB 7-alp โ‹ฎ โ”‚ โ”‚ +โ”‚ โ”‚ โ˜ authentik โ—‹ Stop - - 2024.2 โ‹ฎ โ”‚ โ”‚ +โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚ โ”‚ +โ”‚ Netdata โ†— โ”‚ Showing 5 of 40 < 1 2 3 4 5 > โ”‚ โ”‚ +โ”‚ Portainerโ†— โ”‚ โ”‚ โ”‚ +โ”‚ NPM โ†— โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +**Components:** +- Context Sidebar: Section nav + external links +- DataGrid: Container list with bulk actions +- Detail Panel: Opens on row selection (replaces grid or slides in) + +--- + +### Parlor (Housekeeping) + +Context sidebar with areas and scenes. Chat collapsed. + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ โ”‚ โ”‚ โ”‚ +โ”‚ AREAS โ”‚ LIVING ROOM โ”‚ ๐Ÿ’ฌ โ”‚ +โ”‚ โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚ โ”‚ +โ”‚ โ–ธ Living Rm โ”‚ โ”‚ โ”‚ +โ”‚ Bedroom โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ +โ”‚ Kitchen โ”‚ โ”‚ ๐Ÿ’ก โ”‚ โ”‚ ๐Ÿ“บ โ”‚ โ”‚ ๐ŸŒก๏ธ โ”‚ โ”‚ ๐Ÿ”Œ โ”‚ โ”‚ โ”‚ +โ”‚ Office โ”‚ โ”‚ Ceiling โ”‚ โ”‚ Samsung โ”‚ โ”‚ Thermo โ”‚ โ”‚ Outlet 1 โ”‚ โ”‚ โ”‚ +โ”‚ Garage โ”‚ โ”‚ 75% โ”‚ โ”‚ OFF โ”‚ โ”‚ 72ยฐF โ”‚ โ”‚ ON โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ”‚ โ—‹โ”€โ”€โ”€โ”€โ”€โ— โ”‚ โ”‚ [ ] โ”‚ โ”‚ [-] [+] โ”‚ โ”‚ [ ] โ”‚ โ”‚ โ”‚ +โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ +โ”‚ SCENES โ”‚ โ”‚ โ”‚ +โ”‚ ๐ŸŒ… Morning โ”‚ SCENES โ”‚ โ”‚ +โ”‚ โ˜€๏ธ Day โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ +โ”‚ ๐ŸŒ† Evening โ”‚ โ”‚ ๐ŸŒ… โ”‚ โ”‚ โ˜€๏ธ โ”‚ โ”‚ ๐ŸŒ† โ”‚ โ”‚ ๐ŸŒ™ โ”‚ โ”‚ ๐ŸŽฌ โ”‚ โ”‚ โ”‚ +โ”‚ ๐ŸŒ™ Night โ”‚ โ”‚ Morn โ”‚ โ”‚ Day โ”‚ โ”‚ Eve โ”‚ โ”‚Night โ”‚ โ”‚Movie โ”‚ โ”‚ โ”‚ +โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +**Components:** +- Context Sidebar: Area list + scene shortcuts +- Device Grid: Control tiles for current area +- Scene Bar: Quick activation buttons + +--- + +## Grid Specifications + +### Stat Cards + +```css +grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); +gap: 1rem; +``` + +| Viewport Width | Cards per Row | +|----------------|---------------| +| >= 900px | 4 | +| >= 720px | 3 | +| >= 540px | 2 | +| < 540px | 1 | + +### Service Cards + +```css +grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); +gap: 0.75rem; +``` + +Reflows naturally based on available width. + +--- + +## Chat Dock Behavior + +The Tatlock chat assistant is **omnipresent** - accessible from any room. + +| State | Width | Trigger | +|-------|-------|---------| +| Expanded | intrinsic (min 280px, max 33vw) | Default on Front Hall, click icon elsewhere | +| Collapsed | 48px (icon rail) | Default on other rooms, viewport < 1200px | +| Overlay | 90vw or 400px max | Expanded on medium viewports | +| Bottom Sheet | 100vw, 60vh max | Mobile viewports | +| **Fullscreen** | 100vw, 100vh | User-triggered overlay (future) | + +**Persistence:** Conversation state persists across room navigation. + +**Future:** Fullscreen mode provides an immersive chat experience as a modal overlay, independent of the current room. + +--- + +## Flutter Implementation Notes + +### Recommended Widgets + +| Concept | Flutter Widget | +|---------|----------------| +| Breakpoint detection | `LayoutBuilder`, `MediaQuery` | +| Main layout | `Row` with `Expanded` and `SizedBox` | +| Collapsible dock | `AnimatedContainer` or `AnimatedSize` | +| Grid layouts | `GridView.builder` with `SliverGridDelegateWithMaxCrossAxisExtent` | +| Drawer fallback | `Scaffold.drawer` or custom `Drawer` | +| Bottom sheet chat | `showModalBottomSheet` or `DraggableScrollableSheet` | + +### Breakpoint Constants + +```dart +abstract class Breakpoints { + static const double mobile = 600; + static const double compact = 800; + static const double medium = 1200; +} +``` + +### Layout Builder Pattern + +```dart +LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxWidth >= Breakpoints.medium) { + return WideLayout(...); + } else if (constraints.maxWidth >= Breakpoints.compact) { + return MediumLayout(...); + } else { + return CompactLayout(...); + } + }, +) +``` + +--- + +## Related Documents + +- [PHILOSOPHY.md](../PHILOSOPHY.md) - "Rooms of the Estate" metaphor +- [ARCHITECTURE.md](./ARCHITECTURE.md) - Clean Architecture patterns +- [THEMING.md](./THEMING.md) - Material3 color scheme +- [DATAGRID.md](./DATAGRID.md) - DataGrid component API diff --git a/lib/features/dashboard/presentation/pages/dashboard_page.dart b/lib/features/front_hall/presentation/pages/front_hall_page.dart similarity index 95% rename from lib/features/dashboard/presentation/pages/dashboard_page.dart rename to lib/features/front_hall/presentation/pages/front_hall_page.dart index 562fe0e..a6a1f2a 100644 --- a/lib/features/dashboard/presentation/pages/dashboard_page.dart +++ b/lib/features/front_hall/presentation/pages/front_hall_page.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import '../../../../version.g.dart'; -/// Dashboard home page - overview of the homelab. -class DashboardPage extends StatelessWidget { - const DashboardPage({super.key}); +/// Front Hall - estate overview and quick access. +class FrontHallPage extends StatelessWidget { + const FrontHallPage({super.key}); @override Widget build(BuildContext context) { @@ -12,7 +12,7 @@ class DashboardPage extends StatelessWidget { return Scaffold( appBar: AppBar( - title: const Text('Dashboard'), + title: const Text('Front Hall'), actions: [ IconButton( icon: const Icon(Icons.refresh), diff --git a/lib/routing/app_router.dart b/lib/routing/app_router.dart index 0ae71b6..d72803f 100644 --- a/lib/routing/app_router.dart +++ b/lib/routing/app_router.dart @@ -3,17 +3,17 @@ import 'package:go_router/go_router.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../shared/layouts/app_scaffold.dart'; -import '../features/dashboard/presentation/pages/dashboard_page.dart'; +import '../features/front_hall/presentation/pages/front_hall_page.dart'; part 'app_router.g.dart'; /// Route paths as constants. abstract class AppRoutes { - static const dashboard = '/'; - static const chat = '/chat'; - static const containers = '/containers'; - static const containerDetail = '/containers/:id'; - static const housekeeping = '/housekeeping'; + static const frontHall = '/'; + static const controlRoom = '/control-room'; + static const containers = '/control-room/containers'; + static const containerDetail = '/control-room/containers/:id'; + static const parlor = '/parlor'; static const settings = '/settings'; } @@ -21,43 +21,46 @@ abstract class AppRoutes { @riverpod GoRouter appRouter(AppRouterRef ref) { return GoRouter( - initialLocation: AppRoutes.dashboard, + initialLocation: AppRoutes.frontHall, debugLogDiagnostics: true, routes: [ ShellRoute( builder: (context, state, child) => AppScaffold(child: child), routes: [ GoRoute( - path: AppRoutes.dashboard, - name: 'dashboard', - builder: (context, state) => const DashboardPage(), + path: AppRoutes.frontHall, + name: 'frontHall', + builder: (context, state) => const FrontHallPage(), ), GoRoute( - path: AppRoutes.chat, - name: 'chat', - builder: (context, state) => const _PlaceholderPage(title: 'Chat'), - ), - GoRoute( - path: AppRoutes.containers, - name: 'containers', + path: AppRoutes.controlRoom, + name: 'controlRoom', builder: (context, state) => - const _PlaceholderPage(title: 'Containers'), + const _PlaceholderPage(title: 'Control Room'), routes: [ GoRoute( - path: ':id', - name: 'containerDetail', - builder: (context, state) { - final id = state.pathParameters['id']!; - return _PlaceholderPage(title: 'Container: $id'); - }, + path: 'containers', + name: 'containers', + builder: (context, state) => + const _PlaceholderPage(title: 'Containers'), + routes: [ + GoRoute( + path: ':id', + name: 'containerDetail', + builder: (context, state) { + final id = state.pathParameters['id']!; + return _PlaceholderPage(title: 'Container: $id'); + }, + ), + ], ), ], ), GoRoute( - path: AppRoutes.housekeeping, - name: 'housekeeping', + path: AppRoutes.parlor, + name: 'parlor', builder: (context, state) => - const _PlaceholderPage(title: 'Housekeeping'), + const _PlaceholderPage(title: 'Parlor'), ), GoRoute( path: AppRoutes.settings, diff --git a/lib/shared/layouts/app_scaffold.dart b/lib/shared/layouts/app_scaffold.dart index 8323ff9..6ae92b2 100644 --- a/lib/shared/layouts/app_scaffold.dart +++ b/lib/shared/layouts/app_scaffold.dart @@ -12,29 +12,27 @@ class AppScaffold extends StatelessWidget { @override Widget build(BuildContext context) { + // TODO: Replace AdaptiveScaffold with custom layout per UI_LAYOUT.md + // - Header with room tabs (not bottom/rail nav) + // - Chat dock on right side return AdaptiveScaffold( selectedIndex: _selectedIndex(context), onSelectedIndexChange: (index) => _onNavSelected(context, index), destinations: const [ NavigationDestination( - icon: Icon(Icons.dashboard_outlined), - selectedIcon: Icon(Icons.dashboard), - label: 'Dashboard', - ), - NavigationDestination( - icon: Icon(Icons.chat_outlined), - selectedIcon: Icon(Icons.chat), - label: 'Tatlock', + icon: Icon(Icons.door_front_door_outlined), + selectedIcon: Icon(Icons.door_front_door), + label: 'Front Hall', ), NavigationDestination( icon: Icon(Icons.dns_outlined), selectedIcon: Icon(Icons.dns), - label: 'Containers', + label: 'Control Room', ), NavigationDestination( - icon: Icon(Icons.home_outlined), - selectedIcon: Icon(Icons.home), - label: 'Housekeeping', + icon: Icon(Icons.weekend_outlined), + selectedIcon: Icon(Icons.weekend), + label: 'Parlor', ), NavigationDestination( icon: Icon(Icons.settings_outlined), @@ -51,21 +49,19 @@ class AppScaffold extends StatelessWidget { int _selectedIndex(BuildContext context) { final location = GoRouterState.of(context).matchedLocation; - if (location.startsWith(AppRoutes.containers)) return 2; - if (location.startsWith(AppRoutes.chat)) return 1; - if (location.startsWith(AppRoutes.housekeeping)) return 3; - if (location.startsWith(AppRoutes.settings)) return 4; - return 0; // Dashboard + if (location.startsWith(AppRoutes.controlRoom)) return 1; + if (location.startsWith(AppRoutes.parlor)) return 2; + if (location.startsWith(AppRoutes.settings)) return 3; + return 0; // Front Hall } void _onNavSelected(BuildContext context, int index) { final route = switch (index) { - 0 => AppRoutes.dashboard, - 1 => AppRoutes.chat, - 2 => AppRoutes.containers, - 3 => AppRoutes.housekeeping, - 4 => AppRoutes.settings, - _ => AppRoutes.dashboard, + 0 => AppRoutes.frontHall, + 1 => AppRoutes.controlRoom, + 2 => AppRoutes.parlor, + 3 => AppRoutes.settings, + _ => AppRoutes.frontHall, }; context.go(route); }