Repository navigation
feat(appearance): configurable display dimming - #334
Conversation
Move the fork's kiosk dimming (CSS brightness filter, idle fade, day/night targets) from URL params + localStorage into the appearance cascade: a dimming object field with household default and per-display override, normalized and clamped like autoDark. The old localStorage keys seed a display override once via the legacy-migration pattern (with their own marker, so a theme migrated long ago does not block the dim seed). Kiosk URL params still override per-field for the session, and saving in Admin applies live without a reload. Generated with Muse AI 🤖
…tages live Night start/end show their formatted time under the hour field, using the app's standard time format (12-hour where that is the locale convention, 24-hour elsewhere). Brightness headings now carry the live percentage, so the value is visible while the slider moves, not only in the drag bubble.
|
Nice idea for panels whose backlight floor is too bright. One behavior issue and a few small things, from reading the code (not run):
|
jherforth
left a comment
There was a problem hiding this comment.
Thanks, dimming below the backlight floor is a feature wall panels need, and keeping it in the appearance cascade (household default plus per-display override) is the right home for the data. The settings UI needs rework before this merges, and mrramam's review is still open.
Required: Display dimming gets its own section in Look
I rendered Admin → Look from this branch. "Display dimming" is a subtitle2 heading inside the Appearance card, directly under "Personalize". It's the same style as "Background" and "Card opacity", so it reads as a third Personalize item. It isn't aligned with the sections it should sit beside ("Appearance", "Themes"), and it shares the Appearance card's Save buttons.
1. Make it its own card. Move the dimming controls into a new DisplayDimmingSettings component, rendered as its own AdminFormSection in the Look tab, between Appearance and Themes:
{location.tab === 'look' && (
<Card>
<CardContent>
<AppearanceSettings />
<DisplayDimmingSettings />
<ThemeLibrary />
</CardContent>
</Card>
)}with
<AdminFormSection title={t('admin:appearance.dimming.heading')} subtitle={t('admin:appearance.dimming.help')}>
…
</AdminFormSection>That gives it the same h6 title and bordered box as "Appearance" and "Themes", aligned with them.
2. Give the card its own scope and Save.
- Scope: its own "This display / Household" toggle, the "Use household" switch, and its own Save button.
- Data: no change.
dimmingstays a field of the appearance setting, so the cascade andmakeHouseholdDefaultkeep working.
3. Each card writes only its own fields. Today save() writes the whole household appearance object from the Appearance card's draft. With two cards, that would let one card overwrite the other's saved changes. For example, Appearance loaded at mount and saved later would put back the old dimming. So:
- The dimming card writes only
dimming, over what's stored now:- household: read the current appearance setting, replace
dimming, write it back; - display: the current device overrides, plus
dimmingor without it when "Use household" is on.
- household: read the current appearance setting, replace
- The Appearance card stops writing
dimming:- household: keep the stored
dimming; - display: leave
dimmingout of the overrides it sends.
- household: keep the stored
- A shared helper that both use avoids getting this wrong twice, e.g.
saveAppearanceFields(scope, partial), which reads the stored object and mergespartialinto it.
4. Match the label styles to the rest of Look.
- The slider headings ("Daytime brightness — 70%", "Nighttime brightness — 3%") are bold
body2. Usesubtitle2, like "Card opacity", with the percentage after it. - Group the three number fields under a
subtitle2"Night hours" heading, with the night-hours help text under them, as now.
5. Night start and end are hours, so offer hours. Two number fields with the time as helper text ("0" with "12:00 AM" under it) make people translate in their head, and they accept values the normalizer then clamps. A Select of the 24 hours, labelled with formatHourOfDay(h), shows the time directly and can't be out of range. "Idle seconds before dimming" can stay a number field.
6. Don't dim while the Admin Panel is open. With the default 20 seconds, someone reading the dimming settings at night gets a 3%-bright Admin Panel mid-read, and their next tap is swallowed by the shield. Pause the idle timer while Admin is open.
Still open from mrramam's review
There are no commits since it, so all five points stand:
- activity inside plugin frames (the SDK pinging the parent);
- the fork-only legacy keys;
- the screenshot in the repo;
- the night window following the device's clock instead of the household zone;
- the frame-rate check with an animated theme.
The legacy-keys point also applies to two more pieces that exist only for your fork, so please drop them too:
- The kiosk URL parameters (
?dim,?dimNight,?dimAfter,?nightStart,?nightEnd). The comment says they keep "existing kiosk launch URLs" working, but no upstream install has them. They also turn dimming on even when the setting is off, which the settings UI can't show. window.hgSetDim: a global devtools hook.
Without them, resolveDimmingConfig reduces to normalizeDimming(settings).
Checks before re-review
- Layout: Admin → Look shows three sections, Appearance, Display dimming and Themes, each with the same
h6title and bordered box, aligned on desktop and at phone width (390 px). - Scope: the dimming card's Save writes only
dimming, at either scope. Save dimming, then save Appearance without reloading the panel, and the dimming setting survives. The same goes the other way round. - Use household: on a display with "Use household" on, the card shows the household's values, disabled.
- Night hours: they're chosen from a list of times.
- Admin open: the display doesn't dim while the Admin Panel is open, and does after it closes.
- mrramam's points are addressed.
- Client tests,
check:i18nand the build pass.
…usehold timezone Address jherforth's + mrramam's review on the dimming PR: - Own card: dimming moves out of AppearanceSettings into DisplayDimmingSettings, rendered in the Look tab between Appearance and Themes as its own AdminFormSection with its own scope toggle, Use-household switch and Save button. - Scope isolation: both cards save through the new appearanceSave helper, which re-reads the stored appearance and merges only its own fields (device saves delete fields switched to Use household instead of omitting them). Saving dimming, then Appearance without reloading, leaves dimming intact, and vice versa; Make-household- default preserves every display's dimming override. - Labels: slider headings are subtitle2 with the live percentage after the label; the three number fields sit under a subtitle2 Night hours heading (new en+es string). - Night start/end are Selects of the 24 hours, labelled with formatHourOfDay; idle seconds stays a number field. - Dimming pauses while the Admin Panel is open and resumes after a full idle delay on close. - Dropped fork-only pieces: kiosk URL parameters (?dim, ?dimNight, ?dimAfter, ?nightStart, ?nightEnd), the window.hgSetDim hook, and the hg-dim/hg-dim-night/hg-dim-after legacy seeding with its dimmingMigrated marker (theme migration untouched). - Plugin frames now report pointer/key/wheel use as homeglow:activity (always on, 1/sec throttle) and displayDim counts it as activity, so using a plugin holds the dim off. - The night window is read in the household timezone (getServerTimezoneSync) via Intl, with a local-hours fallback for unknown zones. - The PR-body screenshot is deleted from the repo. No low-end-panel frame-rate measurement: the dimming filter sits on <html> and theme animations are transform/opacity-only, so it is addressed by mechanism, not measured on hardware. Generated with Muse AI 🤖
|
Done in 83d14b7 — all of it, including mrramam's five points. The PR body is updated too (screenshot section removed, testing section refreshed). Your six items
Dropped fork-only pieces
mrramam's five
Checks
|
# Conflicts: # client/src/app.jsx
- "Idle seconds before dimming" moves up beside "Dim the display when idle": it's when dimming starts, not part of the night hours. Under "Night hours" are just its start and end. - The "Night hours" heading gets space below it; the fields' floating labels overlapped it by 9 px. - Idle seconds is saved as a number (editWholeNumber), not the field's text. - homeglow:activity counts only from a frame on this page, like every other plugin message since jherforth#320. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
Thanks, this addressed everything from both reviews. In Edge, the card sits aligned between Appearance and Themes, and the two cards' saves leave each other alone in both directions. Night hours are a list of times, and dimming waits while Admin is open. I added two commits before merging:
Client tests, the Edge checks and CI pass. |
… revision counter jherforth#339, dimming jherforth#334, Save layout jherforth#336, chore perf jherforth#324, admin jherforth#330/jherforth#331/jherforth#332, PIN hardening jherforth#329 Conflicts resolved taking upstream's rewrite, preserving fork(prod) features: - displayDim.js: upstream jherforth#334 base plus fork-only kiosk URL dim overrides and LCD pixel shift (both absent upstream) - ChoreWidget/app.jsx/ChoreSchedulesTab: upstream jherforth#324/jherforth#336 rework
Summary
Wall kiosk displays often cannot go dark enough: many panels' backlight floor is still too bright at night, and letting the device sleep blanks a dashboard the household still wants glanceable. This adds dimming as a first-class appearance setting — a CSS brightness filter over the rendered pixels, so the screen can go darker than the hardware allows while the image stays live (a dim-but-live image does not trip the panel's no-signal power save).
It is opt-in and off by default, and it lives on the existing appearance cascade: a household default plus a per-display override, like theme and auto-dark. So a wall display can dim while phones and other displays stay untouched.
Settings
New
dimmingobject on the appearance settings, edited in Admin → Look, in its own Display dimming section between Appearance and Themes, with its own scope toggle, Use household switch, and Save button:enabled(default false),day(default 0.7),night(default 0.03),afteridle seconds (default 20),nightStart/nightEndhours (default 0–6)Behavior: full brightness while the panel is in use, fading to the target after the idle delay, back to full brightness on the next interaction. While dimmed the page background is forced black and scrollbars are hidden (Chrome composites them outside the filtered layer, full-brightness), and a transparent shield catches the first tap over plugin iframes, whose pointer events never reach the document.
Testing
hgSetDimdevtools hook were removed, as were the fork-only legacy keys and the repo screenshot.Generated with Muse AI 🤖