[feature] Build the companion configuration page #5
- @github-anthodev opened this issue · Aug 29, 2026edited
🔢 Impact Version (SemVer)
MINOR
🌿 Suggested Branch
feature/phone-configuration/configuration/5
📝 Description
Build the small responsive page opened by Pebble's companion app so a user can enter/replace/remove a personal PRIM key, search valid places, choose only backend-offered line/direction services, preview, and atomically propose one complete configuration. Users never see transport identifiers or their previously stored key.
The page must support French/English copy, preserve unsaved edits through recoverable failures, keep the stored secret out of the page/server, and meet a measured narrow-phone performance budget.
✅ Expected Behavior
Inside the real companion webview, options show only whether a key is configured, offer a masked change/removal control, and edit the complete favorite list. The page returns
ApiKeyUpdateplus favorites; unchanged secrets never re-enter the webview. French locales show French copy, all others English, and the narrow page remains responsive.
📋 Acceptance Criteria
- Select a small configuration-page stack compatible with supported companion webviews; do not add a native app or stock Clay auto-handler that reopens/pushes every setting.
- Open with favorites plus
hasKeyin an encoded URL fragment never sent to the host; include no stored key. Never put configuration in path/query, HTML, generated JavaScript, or server-visible request data. - Add a masked key field (
type=password,autocomplete=off, autocorrect/capitalization/spellcheck disabled), “replace” and explicit “remove” actions, bilingual explanation of phone-local plaintext storage, and a link to PRIM token creation. - Return the phone-only
ApiKeyUpdate:KEEPby default,REPLACEwithvalueonly when the user typed a new key, orREMOVE. The key may transit only the documented one-time encoded configuration-close fragment, which is never sent to the HTTP server; do not log the close response. - Accept a non-empty replacement key within the foundation-frozen UTF-8 byte bound as opaque input; do not infer token syntax or call live stop-monitoring here. The first composed departure event in
[feature] Deliver Pebble watch consultation(https://github.com/Anthodev/lapin-fute/issues/7) owns authoritative miss-time 401/403. - Implement stop/station → valid line → valid direction → preview → save using only backend combinations. Debounce search by 300 ms, require 2 characters, cancel superseded requests, and render at most 20 results; catalog search never spends PRIM quota.
- Create three favorites without exposing any raw identifier; implement rename/reorder/delete and return the expected ordered list.
- Preserve unsaved key/favorite state through recoverable errors. On cancel, empty/oversized replacement key, backend failure, or failed favorite validation, return no replacement and preserve the prior phone configuration.
- Cover missing/configured/replacement/removal key presentation plus empty/loading/no-result/invalid-service/backend-unavailable favorite states.
- On a narrow mobile profile, require compressed first-load JS+CSS ≤200 KiB, LCP ≤2.5 s, INP ≤200 ms, and CLS ≤0.1 in a recorded synthetic run; add no telemetry/RUM.
- The complete flow works inside the real companion app in both languages without the key appearing in page/server/console/network logs.
- Record real-webview captures, key enter/replace/remove flows, three-favorite editing, cancel/failure preservation, bundle/Web-Vitals output, and redacted page/server/console proof in a resolution comment.
🔗 Affected Endpoints/Routes
- Consume bounded catalog routes without a key and recorded departure fixtures for preview; do not make live departure normalization a dependency.
- Own the HTTPS configuration path/static assets and exact
hasKeyinput plusApiKeyUpdateclose-response contract consumed by[feature] Persist and synchronize favorites(https://github.com/Anthodev/lapin-fute/issues/6).
🧪 Tests to Add
Type Coverage UI/component tests Masked key state/actions, bounded opaque input, debounced search, valid combinations/fixture preview, editing, complete-save payload Secret/state tests `KEEP Localization tests French, English/default, fallback, unchanged user/backend labels, key-state/error copy Performance/webview smoke ≤200 KiB compressed JS+CSS; LCP/INP/CLS budgets; narrow real-companion flow in both locales
⚙️ Constraints
- The page proposes one whole configuration; PebbleKit JS is the only persistence owner.
- A saved favorite is one backend-validated service; never construct unrelated stop/line/direction values.
- The key exists in page memory only while newly entered and in the encoded close fragment only on
REPLACE; fragments are the documented unavoidable handoff and are never HTTP requests. - The page never receives the existing key. No key in query parameters, server logs, analytics, error reports, DOM serialization, or screenshots.
- Use explicit HTML text assignment/escaping and a same-origin HTTPS/CSP policy; no telemetry, accounts, nearby stops, journey planning, or native app.
🔀 Dependencies
- Blocked by
[feature] Resolve valid IDFM services(https://github.com/Anthodev/lapin-fute/issues/3). Consume its frozen route and opaque service-option contracts. - Reuse the shared
Favoritecontract from[feature] Establish the application foundation and contracts(https://github.com/Anthodev/lapin-fute/issues/2).
⚠️ Impact on Existing Behavior
Introduces the user-facing configuration surface. It returns a candidate replacement only;
[feature] Persist and synchronize favorites(https://github.com/Anthodev/lapin-fute/issues/6) remains the sole owner of durable storage and watch synchronization.
🔍 Technical Context
PebbleKit JS opens the page with favorites plus
hasKey; the page returns only changed credential intent. A configuration close fragment is the documented companion channel and is accepted as one-time transient exposure, never a server request. Avoid Clay's automatic settings reopen/watch push for the secret.Required references
Sign in to comment.