Skip to content

Retile Mode#213

Draft
DXCanas wants to merge 46 commits intoJaapvanEkris:0.9.7-(under-construction)from
DXCanas:retile-mode
Draft

Retile Mode#213
DXCanas wants to merge 46 commits intoJaapvanEkris:0.9.7-(under-construction)from
DXCanas:retile-mode

Conversation

@DXCanas
Copy link

@DXCanas DXCanas commented Mar 2, 2026

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:
retile_base
retile_mode_activated
retile_options
retile_add_tile
retile_with_force_curve
retile_new_settings
retile_settings_portrait
retile_portrait_basic
retile_portrait_extra_long
retile_landscape_extralong

DXCanas added 30 commits March 1, 2026 17:07
…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.
…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)
DXCanas added 15 commits March 1, 2026 17:07
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.
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)
@DXCanas
Copy link
Author

DXCanas commented Mar 2, 2026

Currently also contains #194 ; will remove once that's merged.

@Abasz
Copy link
Collaborator

Abasz commented Mar 2, 2026

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... :) )!

image image

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
https://abasz.github.io/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.

@DXCanas
Copy link
Author

DXCanas commented Mar 2, 2026

You will probably hate me but why not a drag and drop :D ?

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.

set the font size based on the tile size

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)
@JaapvanEkris JaapvanEkris added this to the 0.9.8 milestone Mar 8, 2026
@DXCanas
Copy link
Author

DXCanas commented Mar 12, 2026

@aschmid

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 :)

@aschmid
Copy link

aschmid commented Mar 14, 2026

i see. this all seems very nice and a big upgrade to the current UI features. cant wait to test it out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants