mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-05 22:29:05 +00:00
Stage 5 of #780. - Import summary: «Strips left unbound after import: {n}» from the backend `unbound_led_strips` count; «Optimize plans» reports strips passing through walls per space and edits none (AC16). - Linear field for long strips (ТЗ §13.2): pieces of at most the radius along the polyline, emitters thinned to r/4, each piece clipped to the visibility fans of its own emitters as separate clipPath children (no boolean pass per piece), one floor clip for the whole layer, no fan at all where nothing blocks within the radius; a grid index of body faces and boxed inside tests; unchanged fields skip re-diffing. 50×50 on the large house: first stable frame ~1.4 s, warm space ~1.1 s locally. - led-strips-v1 profile: demo/benchmark_led_strips.mjs with the derived large-house fixture (10×5, 50×50, none), absolute limits of the ТЗ table in demo/performance/budgets-led-strips.json, exact counters (zero recomputes on HA ticks/camera/colour, ≤50 cache entries, no growth over 20 cycles); added to the full performance workflow. - Bundle: LAZY_LED_GZIP_CEILING 10 KiB, LAZY_LED_EDITOR_GZIP_CEILING 11 KiB (measured + 10 %, rounded up); overlaps with the initial and editor graphs refused; the lazy editor graph stays inside its ceiling. - Smokes smoke_led_strip_draw/bind/glow, linked in smoke-links; 13 mutants in the registry (7 browser guards in the inventory); config field registry entry `spaces[].led_strips`. - Golden: five new scenes on the `golden-led` space of the visual fixture (`ledStrips` option, the designer's four strips on #868D94), matrix v71. - Docs: LIGHT, DEVICE-PRESENTATION, USER-GUIDE (en/ru), UX-MODES, ARCHITECTURE, ISOMETRIC, CONFIG-COMPATIBILITY, TOUCH-SUPPORT, demo/stand README, performance README; docs/design/led-strips with the unchanged designer archive, two paired frames and ACCEPTANCE.md; both changelogs. Issue: #780 User-Visible: yes
254 lines
13 KiB
Markdown
254 lines
13 KiB
Markdown
# Touch support policy
|
||
|
||
Approved by the owner on **2026-08-08**. This document is the source of truth
|
||
for product scope, implementation trade-offs, documentation and release
|
||
acceptance on touch devices.
|
||
|
||
## Product contract
|
||
|
||
House Plan is a **touch-first product in View and kiosk**, but its editors are
|
||
**desktop-first administration tools**.
|
||
|
||
This contract applies equally to the primary `/houseplan` sidebar panel and to
|
||
optional dashboard cards. The panel menu button remains available on every
|
||
width with a 44 × 44 px target; Home Assistant kiosk state does not silently
|
||
enable House Plan's separate card kiosk mode.
|
||
|
||
| Surface | Desktop browser with mouse/keyboard | Touch/coarse-pointer device |
|
||
|---|---|---|
|
||
| View | Fully supported | **Fully supported; must be convenient and reliable** |
|
||
| Sidebar panel shell and drawer access | Fully supported | **Fully supported** |
|
||
| Kiosk | Supported | **Primary supported environment** |
|
||
| View dialogs and safe device actions | Fully supported | **Fully supported** |
|
||
| Plan editor | **Reference editing environment** | Best effort; partial, awkward or missing operations are allowed |
|
||
| Device editor | **Reference editing environment** | Best effort |
|
||
| Background editor | **Reference editing environment** | Best effort |
|
||
|
||
Users must be told to create and maintain plans in a desktop browser. A tablet
|
||
or phone may expose the editor tabs, and individual operations may work, but
|
||
editor parity with desktop is not promised.
|
||
|
||
## What “fully supported View” means
|
||
|
||
On phones, tablets, wall panels and HA Companion apps, the ordinary View must:
|
||
|
||
- render the plan and current states correctly;
|
||
- on a phone (≤ 480 px) keep the header to one row of at most 56 px — space
|
||
tabs, zoom and one gear whose menu holds every other header action, each
|
||
item a 44 px target (#616);
|
||
- support convenient pan, pinch zoom and space switching;
|
||
- treat a linked stair as a floor-navigation target only after a clean tap;
|
||
pan, pinch, long press, swipe and cancellation tails must stay inert;
|
||
- reserve floor switching for an inward horizontal gesture that starts in the
|
||
48 CSS px strip of an edge with a neighbouring space; a drag elsewhere and
|
||
an unavailable edge must pan the plan;
|
||
- fit the whole plan after two clean taps on background, room fill or a passive
|
||
room label. A single room tap fits only after the 350 ms second-tap window,
|
||
with no intermediate camera motion; keyboard room activation stays immediate;
|
||
- provide a touch path for essential information that desktop exposes through
|
||
hover;
|
||
- open and close View dialogs without clipping their essential content or
|
||
actions;
|
||
- perform supported device actions safely and predictably;
|
||
- remain usable after backgrounding, resize, orientation changes and warm
|
||
remount;
|
||
- preserve kiosk gestures and prevent accidental editor interactions.
|
||
|
||
During every intermediate pan or pinch frame, walls, floors, room fills,
|
||
hatching, lighting and markers must remain continuously painted. The scene SVGs
|
||
stay on one promoted compositor path from the first movement until the terminal
|
||
frame; a budgeted redraw or an unrelated Home Assistant state update must never
|
||
flash white, become transparent or momentarily expose the stage background.
|
||
This contract applies equally to browsers and HA Companion WebViews.
|
||
`smoke_daycycle_layer_budget` additionally proves that the first camera move on
|
||
a 1 cm/grid-point day-cycle plan switches its filtered paper silhouette to the
|
||
screen-bounded fallback, leaves no implicit overlap-promoted plan layer, and
|
||
samples the compositor-presented pinch frames. A fresh idle frame still matches
|
||
the accepted `day-cycle-*` goldens; the real-device beta pass remains the field
|
||
acceptance for WebView-specific tile loss (#582).
|
||
|
||
Once a second touch joins an input sequence, that whole sequence is navigation
|
||
only, whichever contact began on a device marker. Marker tap, long press,
|
||
confirmation, service calls and touch-generated context menu stay blocked
|
||
through every release, cancellation or lost capture and through any delayed
|
||
browser compatibility activation. A new pointer down after the old contacts
|
||
have ended starts a deliberate input sequence immediately; users never have to
|
||
wait before the next single tap or long press. A later real mouse pointerdown or
|
||
keyboard context-menu key is likewise fresh intent and preserves the desktop
|
||
context-menu contract.
|
||
|
||
The summary panel, both halves of its control, and its simple settings form are
|
||
supported View surfaces: each control has a 44 × 44 px or larger target, panel
|
||
scrolling owns its touch gesture instead of zooming the plan, and the settings
|
||
dialog retains reachable content and actions on a narrow viewport. The panel
|
||
may hide because its measured House Plan stage is too small; this is responsive
|
||
safety, not loss of the user's per-card show preference.
|
||
|
||
A touch-only failure in View is a product defect, not an accepted limitation of
|
||
the editor policy.
|
||
|
||
## Pointer modality and hover ownership
|
||
|
||
Hover is instance-local and follows the latest real pointer input. It is
|
||
enabled only after a mouse event when the browser also reports fine,
|
||
hover-capable hardware. Touch and pen input immediately clear transient room
|
||
and device hover, including tooltips; browser-generated compatibility mouse
|
||
events must not restore it. A later real mouse move on a hybrid device restores
|
||
desktop hover without a reload. Space/mode changes, page hiding and remounts
|
||
also discard transient hover. Keyboard focus and explicit click/tap surfaces
|
||
remain independent of this visual hover gate.
|
||
|
||
In View, a device reached with visible keyboard focus opens the same anchored
|
||
device tooltip as mouse hover. It follows focus from marker to marker and closes
|
||
on blur, space/mode changes, page hiding or remount. Touch and pen still never
|
||
open it and immediately clear it on hybrid hardware.
|
||
|
||
When device markers are packed more tightly than their 44 × 44 px minimum
|
||
targets, the marker visibly painted under the pointer owns that point. Only an
|
||
otherwise empty overlap of invisible target floors is resolved by the nearest
|
||
marker core. This same screen-space owner is held for the complete pointer
|
||
sequence, so hover, tap, long press, context menu and Devices-editor drag cannot
|
||
switch to a neighbour between press and release. Saved marker coordinates and
|
||
the visible layout are not moved to manufacture separation.
|
||
|
||
The default-on **Show the room information window on hover** preference applies
|
||
only after this pointer-modality gate has enabled real mouse hover. Turning it
|
||
off leaves the room highlight and device tooltips unchanged; it does not add a
|
||
touch or pen replacement for the room window.
|
||
|
||
## What “best-effort editors” means
|
||
|
||
On a coarse-pointer or no-hover device, an editor operation may:
|
||
|
||
- have less convenient hit targets or gestures;
|
||
- require a workflow that is practical only with a mouse and keyboard;
|
||
- omit a desktop-only shortcut or precision interaction;
|
||
- be hidden or disabled when no safe touch interaction exists;
|
||
- have a documented layout or usability limitation;
|
||
- be intentionally deferred when touch parity is disproportionately expensive.
|
||
|
||
This is an explicit scope decision. New editor features are designed and
|
||
accepted against the desktop reference environment first. Touch editor support
|
||
is added when it is cheap, robust and does not complicate the desktop model.
|
||
|
||
## Safety floor that still applies to touch editors
|
||
|
||
“Best effort” never permits:
|
||
|
||
- data corruption or silent loss of saved plan data;
|
||
- an unsafe Home Assistant service call;
|
||
- bypassing permissions or destructive confirmation;
|
||
- leaving the card permanently stuck outside View;
|
||
- an editor exception that breaks ordinary View or kiosk;
|
||
- saving unintended geometry merely because a pinch, pointer cancellation or
|
||
second touch was misread as a click.
|
||
|
||
If a desktop interaction cannot be translated safely to touch, prefer a clear
|
||
disabled/absent action and a desktop recommendation over a deceptively working
|
||
control.
|
||
|
||
The shared editor context tray follows this safety floor: its visible surface
|
||
owns its pointer events, narrow action rows scroll internally, and a press used
|
||
only to dismiss an explicit group/palette is consumed instead of falling
|
||
through to the plan. Pinch/pan outside the tray remains scene-owned. This is a
|
||
safety guarantee, not a promise of full touch parity for editor precision work.
|
||
|
||
The Plan editor's Walls chain follows the same floor. One clean tap may append
|
||
one segment; pan, pinch, a second pointer, `pointercancel` and a suppressed
|
||
synthetic click never append, finish or convert the chain. An open chain
|
||
finishes as ordinary walls only through an explicit finish action: a
|
||
tool/editor/floor change, `Esc`, the tray's Reset, route/hash departure or
|
||
rejecting every closed face (docs/WALL-THICKNESS.md §11, Finishing a chain).
|
||
Junction hover is best effort on coarse pointers; click resolution remains
|
||
authoritative.
|
||
With no active chain, a clean tap inside an exact bounded wall face may offer a
|
||
room without drawing another segment; the tap is resolved again at commit time.
|
||
Desktop `Shift` bypasses that offer and constrains drawing to an exact 45° ray;
|
||
touch has no separate modifier gesture. A pan, pinch, cancellation or second
|
||
pointer never accepts a face, applies a small-gap repair or creates a room.
|
||
|
||
Stair placement and transforms remain best-effort editor interactions on
|
||
touch, but the safety floor is strict: cancellation and navigation gestures do
|
||
not create, move, save or follow a stair. A long press in View does not activate
|
||
its target floor, and the next deliberate tap is re-armed immediately.
|
||
|
||
During a View/editor visual transition the moving stage is inert while the
|
||
header tabs remain available. A pinch, cancelled pointer or synthetic click
|
||
cannot operate stale geometry; leaving the editor is always a single safe
|
||
action. The decorative smoothness of the editor chrome remains best effort on
|
||
coarse-pointer devices, while the correct final View frame is release-blocking.
|
||
|
||
### LED strip tool (#780)
|
||
|
||
The LED tool applies the same safety floor as the wall chain: only a clean
|
||
tap adds a point; pan, pinch, a second finger, `pointercancel`, a lost
|
||
capture and the synthetic click after navigation add nothing, finish nothing
|
||
and open no picker. The touch hint does not require keyboard modifiers:
|
||
switching the tool off finishes the chain. In the View the whole stripe is
|
||
one target with `max(22 CSS px, t/2)` hit radius; a pan or pinch over it calls
|
||
no action, long press or more-info, and the next clean tap works at once.
|
||
|
||
## Deliberate degradation rule
|
||
|
||
When an editor change would be expensive to implement correctly for touch, the
|
||
change may ship as desktop-only or with reduced touch behaviour if all of the
|
||
following are true:
|
||
|
||
1. View and kiosk are unaffected.
|
||
2. Plan data and device actions remain safe.
|
||
3. Desktop editing is complete and tested.
|
||
4. The touch limitation is deliberate, described in the user-facing
|
||
limitations and, where useful, next to the affected workflow.
|
||
5. Existing automated touch-editor coverage is explicitly updated or
|
||
reclassified in the same change; it is not silently ignored.
|
||
6. The release notes mention the limitation when it is material to users.
|
||
|
||
An accidental regression is not made acceptable merely by calling it
|
||
best-effort after discovery.
|
||
|
||
## Input classification
|
||
|
||
Do not classify support from screen width alone.
|
||
|
||
- `pointer: coarse`, no hover, touch pointer events and mobile/Companion
|
||
environments are touch scenarios.
|
||
- A narrow desktop window with a fine pointer remains a desktop editing
|
||
scenario.
|
||
- Hybrid laptops must keep both View paths usable. Their editors may use the
|
||
desktop path when a fine pointer and keyboard are actually available.
|
||
- Stylus editing is best effort unless a specific feature explicitly promises
|
||
it.
|
||
|
||
## Testing and release gates
|
||
|
||
Release-blocking guarantees:
|
||
|
||
1. View on desktop.
|
||
2. View and kiosk on representative touch/mobile environments.
|
||
3. All editors on desktop with mouse/keyboard.
|
||
4. The touch-editor safety floor above.
|
||
|
||
Touch-editor feature parity is not a general release gate. Targeted tests may
|
||
still protect individual touch workflows that the product intentionally keeps;
|
||
removing such a guarantee requires an explicit scope/documentation change, not
|
||
just deletion of the failing test.
|
||
|
||
Stable-release manual coverage must include at least one phone/Companion View
|
||
and one wall-tablet/kiosk View. Editor smoke coverage on touch is scoped to
|
||
safe entry/exit, no accidental mutation during multi-touch, and any separately
|
||
promised workflow.
|
||
|
||
## Documentation rule
|
||
|
||
Every user-facing description of the editors must recommend desktop for plan
|
||
creation and maintenance. Documentation must not imply full touch-editor
|
||
support merely because the tabs are visible on a tablet. New editor feature
|
||
specifications and code reviews must state one of:
|
||
|
||
- `Touch editor: supported`;
|
||
- `Touch editor: best effort / intentionally degraded`;
|
||
- `Touch editor: not exposed`.
|
||
|
||
If touch work is omitted because of cost, that is an accepted product decision
|
||
only after it is written down.
|