Retile Mode#213
Conversation
…y with graceful fallbacks Changed BleManager to lazy-load native modules only on Linux platform and throw descriptive error on unsupported platforms. Updated PeripheralManager error handling to downgrade BLE initialization errors from error to warn level and automatically disable BLE/HRM modes when unavailable. Made GPIO timer service conditional on Linux platform in server.js with warning message for other platforms.
Introduces the `simulateWithoutHardware` flag to the default configuration and config validation. This enables developers to bypass GPIO and BLE hardware checks to simulate the environment on headless servers or non-Linux systems without causing error logs. AI-assisted-by: Gemini 3.1 Pro
Removes the static `process.platform === 'linux'` check in favor of dynamically importing `hci-socket` and `ble-host`. Uses the new `simulateWithoutHardware` config flag to bypass BLE initialization when requested. Refactors `open()` to use guard clauses, preventing deep nesting and improving readability. Catches and logs missing dependencies cleanly. AI-assisted-by: Gemini 3.1 Pro
Removes the static `process.platform === 'linux'` check from `server.js` and moves hardware checking into `GpioTimerService.js`. Uses dynamic imports for `pigpio` wrapped in a try/catch block. If `simulateWithoutHardware` is true or if `pigpio` fails to load (e.g., on non-Raspberry Pi devices or without root), the service logs a warning and exits cleanly instead of crashing the app. AI-assisted-by: Gemini 3.1 Pro
Updates `GpioTimerService` to explicitly read `/proc/device-tree/model` to confirm it is running on a supported Raspberry Pi (3, 4, or Zero 2W) before attempting to load `pigpio`. Updates `BleManager` to explicitly check for Linux before attempting to load `hci-socket`. Both services now log explicit messages detailing their initialization decisions as requested by maintainers, ensuring clear visibility into why hardware modules are bypassed or loaded. AI-assisted-by: Gemini 3.1 Pro
…on failure gracefully The `pigpio` JS wrapper exports functions even if the underlying C library fails to initialize (e.g. on macOS or non-Pi Linux without root), causing a hard crash later when those undefined C-bindings are called. This commit explicitly wraps the first call to `pigpio.hardwareRevision()` in a try/catch block to catch the `TypeError: is not a function`. This prevents the application from crashing and instead cleanly bypasses the GPIO service as requested by maintainers. AI-assisted-by: Gemini 3.1 Pro
… JSDoc imports Replaces the temporary `any` types with `typeof import(...)` for the BLE modules (`hci-socket` and `ble-host`). This restores strong type checking for the class properties while keeping the actual module loading deferred to runtime, satisfying the requirement to lazy-load these modules only on supported platforms (Linux) without losing static analysis benefits.
This reverts commit 5a352e5.
…yIcon. The ::slotted(*) styles were applying absolute positioning to all slotted content. Moving these to BatteryIcon's :host allows the slot to be unstyled and capable of filling the entire tile when other elements are collapsed. AI-assisted-by: Claude (Anthropic)
New dialog for selecting metrics to add/replace on dashboard. Properties:
- currentMetrics: Array of metric keys already displayed
- metricIndex: Target index for add/replace operation
Emits 'metric-selected' event with { metricKey, metricIndex, mode }.
Dialog is not yet wired to any UI trigger.
AI-assisted-by: Claude (Anthropic)
Prepares for Phase 2 integration where retile mode will open this dialog. AI-assisted-by: Claude (Anthropic)
Button shows 'Retile' normally, 'Retiling' when active with highlighted style. Dispatches 'retile-mode-changed' event for PerformanceDashboard to handle. AI-assisted-by: Claude (Anthropic)
- Local _localMetrics state holds working copy during retile mode - On retile mode enter: copy appState metrics to local - On retile mode exit: save local metrics via changeGuiSetting event - Handles metric-selected events for add/replace operations AI-assisted-by: Claude (Anthropic)
In retile mode: - Each metric tile gets a Remove button overlay (no wrapper div) - retile-mode class on grid ensures tiles can have absolute children - An Add tile appears at the end to open MetricSelectorDialog - Dialog allows selecting from metrics not currently displayed AI-assisted-by: Claude (Anthropic)
Each tile now has both Replace and Remove buttons in retile mode. Replace opens MetricSelectorDialog with the tile's index, allowing the selected metric to take its place. AI-assisted-by: Claude (Anthropic)
Button only visible when retile mode is active. Dispatches 'reset-layout-to-default' event for PerformanceDashboard to handle. AI-assisted-by: Claude (Anthropic)
- Export DEFAULT_DASHBOARD_METRICS from appState.js - PerformanceDashboard resets _localMetrics to defaults when event received - Changes only applied to local state; user must exit retile mode to save AI-assisted-by: Claude (Anthropic)
Renamed to 'Additional Settings'. Removed: - Metric selector checkboxes and logic - '12 cell grid' toggle (grid config moved to Phase 4) - Metric table visualization - Complex validation logic Only showIcons and trueBlackTheme toggles remain. AI-assisted-by: Claude (Anthropic)
New gridConfig in guiConfigs contains:
- landscape: { columns: 4, rows: 2 }
- portrait: { columns: 2, rows: 4 }
AI-assisted-by: Claude (Anthropic)
Replaced hardcoded grid columns/rows with CSS custom properties. Grid layout now reads from config.gridConfig with separate landscape/portrait settings. Uses window.matchMedia to detect orientation. Removed rows-3 class in favor of dynamic grid sizing. AI-assisted-by: Claude (Anthropic)
Native number inputs for landscape and portrait grid dimensions: - Landscape: columns and rows (default 4x2) - Portrait: columns and rows (default 2x4) - Min 1, max 8 for all dimensions - Saves to gridConfig in guiConfigs AI-assisted-by: Claude (Anthropic)
Add tile now shows scrollable list of radio buttons for available metrics. Clicking a radio button adds that metric immediately (no dialog). Shows 'All metrics in use' message when no metrics are available. AI-assisted-by: Claude (Anthropic)
Each tile now has a select dropdown instead of Replace button. Selecting from dropdown replaces the metric immediately (no dialog). Shows available metrics that aren't currently in use. AI-assisted-by: Claude (Anthropic)
Removed MetricSelectorDialog import, _dialog state, and metric-selected event handler since Quick Edit mode uses inline controls instead of dialogs. AI-assisted-by: Claude (Anthropic)
Dialog is no longer needed since Quick Edit mode uses inline controls (radio buttons for add, select dropdowns for replace) instead of dialogs. AI-assisted-by: Claude (Anthropic)
Clearing out reference to MetricSelectorDialog. Instead of exporting a separate DEFAULT_DASHBOARD_METRICS constant, we can just use the initial state defined in APP_STATE.config.guiConfigs. AI-assisted-by: Claude (Anthropic)
Consolidated dialog state to PerformanceDashboard so only one instance of AppDialog handles both Settings and other potential future dialogs. DashboardToolbar now dispatches 'open-settings' instead of managing its own dialog. Moved retile controls into the slots of DashboardMetric and DashboardForceCurve. This avoids 'wrapper' divs breaking the CSS Grid, and keeps the controls inside the actual component encapsulation. AI-assisted-by: Claude (Anthropic)
Grid rows are now flexible and don't need to be explicitly configured. Removed from appState defaults, SettingsDialog UI, and CSS Grid templates. AI-assisted-by: Claude (Anthropic)
Moved slot inside title div so controls appear next to "Force Curve" text when chart has no data. Maintains proper layout when Quick Edit mode is active.
Added to so that dynamically added implicit rows maintain an equal fractional height, matching the previous behavior where explicit rows were defined with . AI-assisted-by: Claude (Anthropic)
Relocated slot from after content div to inside label div so Quick Edit controls appear next to the icon when present, maintaining proper layout alignment with the metric's visual hierarchy.
This reverts commit e3fdec8.
Updated retile controls to correctly display as a flex row side-by-side. Added logic to calculate current grid slot usage based on tile sizes, and hide the 'Add Tile' button once the total max allowed slots (columns * rows) is reached. Also filtered the list of available metrics in both 'Replace' and 'Add Tile' menus to only show options that would fit in the remaining grid slots. AI-assisted-by: Claude (Anthropic)
Updated SettingsDialog to track the current window orientation using a media query listener. The dialog now only displays the Columns and Rows configuration inputs for the currently active orientation (landscape or portrait). AI-assisted-by: Claude (Anthropic)
The retile-mode class was being conditionally applied but never actually used in the CSS. Removed the class variable and conditional application to simplify the template. AI-assisted-by: Claude (Anthropic)
Added defensive optional chaining and fallback values in both SettingsDialog and PerformanceDashboard. This prevents crashes if the saved user config is missing the gridConfig object or its orientation properties before the component fully initializes. AI-assisted-by: Claude (Anthropic)
Added defensive optional chaining and fallback values in both SettingsDialog and PerformanceDashboard. This prevents crashes if the saved user config is missing the gridConfig object or its orientation properties before the component fully initializes. Also dynamically limits metrics on reset if grid is smaller than defaults. AI-assisted-by: Claude (Anthropic)
Updated the SettingsDialog to use HTML range inputs instead of number inputs for configuring the dashboard grid layout. AI-assisted-by: Claude (Anthropic)
Simplified the retile controls in the performance dashboard to use a single select element instead of multiple buttons/selects. Refactored the add-tile placeholder to render as a proper dashboard metric using slots for better styling consistency. AI-assisted-by: Claude (Anthropic)
When retile mode is active, the button text now displays 'Submit' instead of 'Retiling' to better indicate to the user how to save their layout changes. AI-assisted-by: Claude (Anthropic)
…hod. Updated the PerformanceDashboard to use the Lit lifecycle callback instead of manual trimming or a custom setter to enforce maximum grid size constraints on local metrics. Also passed the persistent to SettingsDialog so it can compute space constraints upon grid configuration changes. AI-assisted-by: Claude (Anthropic)
Updated SettingsDialog so that when the user submits a new grid layout, it will automatically calculate the new max slots and pop items off the end of the app's persistent `dashboardMetrics` array if the new grid layout is smaller than the current metrics list. AI-assisted-by: Claude (Anthropic)
Reverted the add-tile template wrapper in PerformanceDashboard to use standard HTML instead of the dashboard-metric slotting because it was causing visibility issues. Refactored grid layout settings in SettingsDialog to properly display the current value reactively inside the label text and use minimal styling with full-width sliders. AI-assisted-by: Claude (Anthropic)
|
Currently also contains #194 ; will remove once that's merged. |
|
You will probably hate me but why not a drag and drop :D ? The good part is that this allows full resizability. And now with the beauty of container queries in CSS we can set the font size based on the tile size (most confess unfortunately the base idea is from claude opus... :) )!
I uploaded the code to a Test repo (I like clean commit history so I rebase and amend a lot during development so this is likely to be changed but the drag and drop is rather stable. Also when I designed the DnD architecture (while it relies on some Angular concept though) I tried it to be as standalone as possible as I have/had the intention to port it to ORM at some point. Code is in the https://github.com/Abasz/Test/tree/main/src/app/settings-dialog/tile-layout-editor folder It has the wrapper, tile-layout-editor.component. The DnD library (but I would be carefully to call it that) for the dragging part uses native browser events but not the dragging as that is basically imitated via transform (this is necessary due to the difference for touch screens). https://github.com/Abasz/Test I like the idea of grid "density" setting freely so I might "steal" that :) Currently I used a portrait and landscape mode that generates a 3x4 or a 4x3 grid. |
I had thought of that early on 😊. But over the past month week I have been losing my mind with buggy drag and drop with some tooling I use for work, and then again for personal tools. Then I recall all the bug reports from drag and drop at my old Frontend position. Leaning maybe later! Plus the logic in place here makes incremental steps easier.
I do like that — nice going Claude. |
Refactored inline styles across the application into properly scoped CSS classes following Lit best practices. Kept only necessary dynamic CSS variables (like --grid-columns) as inline styles. Consolidated duplicate grid configuration logic in PerformanceDashboard: - Added computed state properties (_columns, _rows, _maxGridSlots) - Moved grid config calculation to willUpdate() lifecycle method - Removed redundant _getMaxGridSlots() method - Simplified render() to use pre-computed values This follows Lit's recommendation to use willUpdate() for computing derived state from reactive properties. AI-assisted-by: Claude (Anthropic)
|
This was actually highly inspired by some of the asks you've made across the repo — it's slated for the next release, which aligns with my availability (or lack thereof… 2026 has been no joke so far), but I'd like to know your thoughts :) |
|
i see. this all seems very nice and a big upgrade to the current UI features. cant wait to test it out! |


Alright; this is something I've been noodling on while using ORM.
Seeing the discussion in #165, I was emboldened to use my Sunday morning to try and make it happen.
Then it became my sunday afternoon.
And part of the night.
It was a bigger change than I anticipated 😅, as the diff will show
But I have a prototype going! Basically: Making a distinction between settings and "retiling"; The styles aren't done yet, but the basic functionality is mostly there.
If some of the settings/dialog changes seem confusing, it's because I initially envisioned all the "available metric" options to live in a single modal/dialog, activated when hitting a "replace" button or an "add" "tile. But after starting down that path, I realized it's actually more work than the current settings-dialog implementation. So I thought about a "quick edit" mode that abandoned the dialog altogether. I still like the idea of keeping all dialogs at the "dashboard" level, though.
Forgive the long commit history; it started with a well laid out plan to execute and quickly devolved into fast patches done by hand and by AI. AI used heavily for commit messaging and the like. Testing has been a pain, dealing with edge cases, etc.
But here are some demo shots:









