feat(frontend): v0.7.0 PR #2 — theme toggle, workspace persistence, profile theme colors - #109
Conversation
…uge [profile.theme] (plan PR #2) Ready-to-Claim pile, three items in one Tier A commit: 1. Theme toggle completed: the pre-existing toggle only re-skinned a handful of panels; app.css now routes every theme-relevant surface through CSS variables with a full body[data-theme=dark] override block. The light default stays pixel-identical (:root gains the exact literals that were hardcoded before). Persistence moves from localStorage to workspace.json (one persistence system). 2. Workspace layout persistence: theme, app mode, active tab, connection panel choices, live/log profile selectors, traffic auto-refresh, and the per-profile log channel enabled map save debounced to ~/beeemuu-exports/workspace.json via the new async read_export_text command (writes reuse export_text). First boot migrates beeemuu_dark/beeemuu_settings/beeemuu_mode from localStorage; corrupt/missing files fall back to defaults. Also fixes the legacy connOptsOpen flag, which ANDed the two option panels and was effectively always false. 3. Gauge [profile.theme] blocks: ProfileToml/Profile carry an optional theme map, surfaced via list_profiles (omitted from JSON when empty) and round-tripped by export_profile/import_profiles. gauges.js resolves nine colour keys with per-key fallback and CSS.supports validation; b58.toml ships the reference block; syntax documented in DECODE_FUNCTIONS.md section 9. Tests: cargo 95 -> 97 lib (+2 community theme parse tests) + 1 integration guard (command count 44 -> 45); node --test 43 -> 57 (+14 workspace round-trip/migration tests). Plan deviations: theme toggle already existed (completed, not added); light is the default theme (plan said dark); [profile.theme] needed a minimal Rust pass-through (plan claimed the TOML format already supported it); workspace file lives in ~/beeemuu-exports/ (plan said ~/beeemuu/).
|
ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d75297e78f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| $("view-" + active.dataset.view).classList.add("active"); | ||
| } | ||
| try { localStorage.setItem("beeemuu_mode", mode); } catch (_) {} | ||
| saveSettings(); // persist the mode (v0.7.0 workspace; was beeemuu_mode) |
There was a problem hiding this comment.
Restore the saved tab before saving mode
When workspace.json contains a non-default activeTab, startup calls applyMode() before restoreActiveTab(). This new saveSettings() call copies the currently active default Vehicle tab into workspaceState.activeTab, so restoreActiveTab() reads the overwritten value and the saved tab is never restored; the following debounced write also persists the wrong tab.
Useful? React with 👍 / 👎.
| buildLogParams(); | ||
| saveSettings(); // persist the selected log profile (v0.7.0 workspace) |
There was a problem hiding this comment.
Wait for log rows before saving the new profile
When connected, buildLogParams() awaits read_live_data before replacing #log-params, but this handler calls saveSettings() immediately. Switching the log profile therefore saves the new logProfile together with the old profile's still-rendered checkboxes, storing the old channel map under the new profile key and corrupting the per-profile channel preferences.
Useful? React with 👍 / 👎.
| if (!logSeries.has(v.id)) { | ||
| logSeries.set(v.id, new LogSeries(v.label, v.unit, color, i < 3)); | ||
| const enabled = typeof savedEnabled[v.id] === "boolean" ? savedEnabled[v.id] : i < 3; | ||
| logSeries.set(v.id, new LogSeries(v.label, v.unit, color, enabled)); |
There was a problem hiding this comment.
Reapply saved channel state for reused log IDs
When switching between log profiles that share channel IDs such as rpm or coolant, the saved per-profile map is only consulted while creating a brand-new LogSeries. Existing series keep the previous profile's enabled value, so a restored preference for the newly selected profile is ignored for every overlapping channel unless the ID has never been seen in this session.
Useful? React with 👍 / 👎.
| function restoreActiveTab() { | ||
| const name = workspaceState.activeTab; | ||
| if (!name) return; | ||
| const tab = document.querySelector(`.tab[data-view="${name}"]`); |
There was a problem hiding this comment.
Escape the restored tab name before querying
Because parseWorkspace() accepts any non-empty string for activeTab, a hand-edited or forward-version workspace value containing selector syntax like " or ] makes this template string an invalid CSS selector. In that case querySelector() throws during startup instead of ignoring the bad saved tab, so a malformed workspace file can break the init path until the user fixes the file manually.
Useful? React with 👍 / 👎.
…moved to ✅ Done (#200) Six items in the v0.3.0 'Real Car' historical section were marked 🟢 Ready but have actually shipped: | Item | Shipped in | Reference | |-----------------------|-----------------|--------------------------| | KWP2000 slow timeout | v0.13.0 (PR #153) | commit fd9efc2 | | ISO-TP multi-frame | v0.14.x | src-tauri/src/transport/isotp.rs (enforced by CLAUDE.md, refreshed in PR #198) | | Dark/light theme | v0.7.0 (PR #109) | commit afefc32 | | Gauge theming | v0.7.0 (PR #109) | commit afefc32 | | Save/load workspace | v0.7.0 (PR #109) | commit afefc32 | | Export PNG/SVG charts | v0.11.0 (PR #136) | commit 7f92ccb | This is the same doc-rot pattern that PR #198 fixed in CLAUDE.md: items the inventory still claims as 'Ready to land' have actually shipped, but nobody re-tagged the historical section afterward. Same 'data over invention' fix as the CLAUDE.md refresh. What changed: - '⭐ Protocol & Transport' table — kept the four genuinely-🟡 items (ENET/DoIP, BLE, WiFi, CAN-bus listener), removed the two stale 🟢 Ready items. - 'UI / UX' table — kept the only genuinely-🟡 item (Mobile- responsive), removed the five stale 🟢 Ready items. - New '✅ Protocol, Transport, UI/UX — historical (shipped)' table — pins the six shipped items with their PR references and code locations, following the v0.3.0 'Decode Functions' historical pattern that already exists just above. What this PR does NOT change: - No CHANGELOG.md edits (the shipped-PR references are in CHANGELOG.md already) - No CLAUDE.md edits (the ISO-TP / keepalive / read_vin references there are already accurate post PR #198) - No code edits - No test edits (markdown only) Verified locally (docs-only, no test diff expected): - node --test src/js/**/*.test.js src/js/**/*.test.cjs: 226/226 - python -m pytest backend/tests/ -q: 166/166 - cd src-tauri && cargo test --lib --offline: 149/149 - npm run build: rc=0, both BeeEmUu_0.14.3 bundles built Tier A docs-only per CLAUDE.md. Self-merge on CI green. Cross-references: - PR #198: 'docs: refresh CLAUDE.md hardware/timing invariants' (the same doc-rot sweep on CLAUDE.md) - PR #183: 'docs(proposal): fix stale NOT YET IMPLEMENTED claims' (proposed similar fixes but never applied) Co-authored-by: ohgeeceee <ohgeeceee@users.noreply.github.com>
…moved to ✅ Done (#205) Six items in the v0.3.0 'Real Car' historical section were marked 🟢 Ready but have actually shipped: | Item | Shipped in | Reference | |-----------------------|-----------------|--------------------------| | KWP2000 slow timeout | v0.13.0 (PR #153) | commit fd9efc2 | | ISO-TP multi-frame | v0.14.x | src-tauri/src/transport/isotp.rs (enforced by CLAUDE.md, refreshed in PR #198) | | Dark/light theme | v0.7.0 (PR #109) | commit afefc32 | | Gauge theming | v0.7.0 (PR #109) | commit afefc32 | | Save/load workspace | v0.7.0 (PR #109) | commit afefc32 | | Export PNG/SVG charts | v0.11.0 (PR #136) | commit 7f92ccb | This is the same doc-rot pattern that PR #198 fixed in CLAUDE.md: items the inventory still claims as 'Ready to land' have actually shipped, but nobody re-tagged the historical section afterward. Same 'data over invention' fix as the CLAUDE.md refresh. What changed: - '⭐ Protocol & Transport' table — kept the four genuinely-🟡 items (ENET/DoIP, BLE, WiFi, CAN-bus listener), removed the two stale 🟢 Ready items. - 'UI / UX' table — kept the only genuinely-🟡 item (Mobile- responsive), removed the five stale 🟢 Ready items. - New '✅ Protocol, Transport, UI/UX — historical (shipped)' table — pins the six shipped items with their PR references and code locations, following the v0.3.0 'Decode Functions' historical pattern that already exists just above. What this PR does NOT change: - No CHANGELOG.md edits (the shipped-PR references are in CHANGELOG.md already) - No CLAUDE.md edits (the ISO-TP / keepalive / read_vin references there are already accurate post PR #198) - No code edits - No test edits (markdown only) Verified locally (docs-only, no test diff expected): - node --test src/js/**/*.test.js src/js/**/*.test.cjs: 226/226 - python -m pytest backend/tests/ -q: 166/166 - cd src-tauri && cargo test --lib --offline: 149/149 - npm run build: rc=0, both BeeEmUu_0.14.3 bundles built Tier A docs-only per CLAUDE.md. Self-merge on CI green. Cross-references: - PR #198: 'docs: refresh CLAUDE.md hardware/timing invariants' (the same doc-rot sweep on CLAUDE.md) - PR #183: 'docs(proposal): fix stale NOT YET IMPLEMENTED claims' (proposed similar fixes but never applied) Co-authored-by: ohgeeceee <ohgeeceee@users.noreply.github.com>
…e.rs (#201) * docs(roadmap): audit v0.3.0 historical section — stale 🟢 Ready items moved to ✅ Done Six items in the v0.3.0 'Real Car' historical section were marked 🟢 Ready but have actually shipped: | Item | Shipped in | Reference | |-----------------------|-----------------|--------------------------| | KWP2000 slow timeout | v0.13.0 (PR #153) | commit fd9efc2 | | ISO-TP multi-frame | v0.14.x | src-tauri/src/transport/isotp.rs (enforced by CLAUDE.md, refreshed in PR #198) | | Dark/light theme | v0.7.0 (PR #109) | commit afefc32 | | Gauge theming | v0.7.0 (PR #109) | commit afefc32 | | Save/load workspace | v0.7.0 (PR #109) | commit afefc32 | | Export PNG/SVG charts | v0.11.0 (PR #136) | commit 7f92ccb | This is the same doc-rot pattern that PR #198 fixed in CLAUDE.md: items the inventory still claims as 'Ready to land' have actually shipped, but nobody re-tagged the historical section afterward. Same 'data over invention' fix as the CLAUDE.md refresh. What changed: - '⭐ Protocol & Transport' table — kept the four genuinely-🟡 items (ENET/DoIP, BLE, WiFi, CAN-bus listener), removed the two stale 🟢 Ready items. - 'UI / UX' table — kept the only genuinely-🟡 item (Mobile- responsive), removed the five stale 🟢 Ready items. - New '✅ Protocol, Transport, UI/UX — historical (shipped)' table — pins the six shipped items with their PR references and code locations, following the v0.3.0 'Decode Functions' historical pattern that already exists just above. What this PR does NOT change: - No CHANGELOG.md edits (the shipped-PR references are in CHANGELOG.md already) - No CLAUDE.md edits (the ISO-TP / keepalive / read_vin references there are already accurate post PR #198) - No code edits - No test edits (markdown only) Verified locally (docs-only, no test diff expected): - node --test src/js/**/*.test.js src/js/**/*.test.cjs: 226/226 - python -m pytest backend/tests/ -q: 166/166 - cd src-tauri && cargo test --lib --offline: 149/149 - npm run build: rc=0, both BeeEmUu_0.14.3 bundles built Tier A docs-only per CLAUDE.md. Self-merge on CI green. Cross-references: - PR #198: 'docs: refresh CLAUDE.md hardware/timing invariants' (the same doc-rot sweep on CLAUDE.md) - PR #183: 'docs(proposal): fix stale NOT YET IMPLEMENTED claims' (proposed similar fixes but never applied) * feat(v0.14.4): story coverage — 52 unit tests for story.rs + anonymize.rs Two user-facing Rust modules have shipped with **zero unit tests** since the diagnostic-story + secure-snapshot-share features landed: - `src-tauri/src/story.rs` (350 LOC) — the Generate Story pipeline that powers the one-click mechanic narrative modal in `src/index.html:513` + `src/js/main.js:2961` (renderStory). - `src-tauri/src/anonymize.rs` (113 LOC) — the VIN-stripping layer that powers the Secure Snapshot Share feature (`src/js/main.js:1071`). Both are pure-Rust, deterministic, and snapshot-driven — the right shape for unit tests with fixture-built `SessionSnapshot` inputs. This PR adds 52 unit tests covering: **story.rs (32 tests):** - `Severity::from_str` bucketing (critical / warning / unknown → info). - `Severity` ordering (Critical > Warning > Info). - `priority_for` severity → priority number. - `parse_cost_range` parser: single value, tilde, hyphen, **en-dash** (the TOML files use en-dash — parser must normalise), whitespace, empty, garbage. - `format_vehicle` for empty / VIN-only / mileage-only / decoded. - `build_context` freeze-frame string assembly. - Full `generate` pipeline: - empty snapshot → Info story with no findings. - unknown DTC → generic Info finding. - n55-specific DTC (2A82) → uses engine template. - generic DTC fallback when engine-specific missing. - severity = max of all findings (sorted critical-first). - recommendations sorted ascending by priority. - cost range sums across findings. - cost-max invariant: max >= min + 50. - DTC code case-insensitive lookup (TOML keys are uppercased on load). - summary text counts critical + warning correctly. - title uses manufacturer + VIN prefix. **anonymize.rs (20 tests):** - `hash_vin` properties: 16 hex chars, stable for same input, distinct for distinct inputs, case-sensitive (current behaviour pinned). - `anonymize` pipeline: - VIN never leaks into the anonymized JSON. - VIN → fingerprint via hash_vin. - Missing VIN → "unknown" fingerprint. - engine_family preserved from suggested_profile. - engine_family defaults to "generic". - Modules / DTCs / freeze frames / ident all preserved. - Mileage (mileage_km) stripped (privacy). - Empty modules handled. - fault_count = None → 0 in the output. - recorded_at populated with "(UTC)" suffix. - live_data is always empty (current anonymizer strips it; pinning the decision). - `export_json`: - No VIN leak in pretty JSON. - No mileage leak. - Pretty-printed (multi-line + indented). - Round-trips through serde. ## Verification - `cd src-tauri && cargo test --lib --offline` — **201/201 pass** (149 existing + 52 new). - `cd src-tauri && cargo test --test async_commands --offline` — 1/1 (the CLAUDE.md invariant guard still green — no new sync commands). - `node --test src/js/**/*.test.js src/js/**/*.test.cjs` — 226/226 (no JS diff). - `pytest backend/tests/ -q` — 166/166 (no backend diff). - `npm run build` — rc=0, 2m32s; both BeeEmUu_0.14.3 bundles built. ## Tier **A** — pure additions to existing Rust modules, no `transport/**` / `protocol/**` / `commands.rs` / `.claude/**` touches. Self-merge on CI green per CLAUDE.md rule 2. ## Cross-references - The Story modal UI is `renderStory` in `src/js/main.js:2961`, the button at `src/index.html:513`. - The Secure Snapshot Share wiring is `doSecureShare` in `src/js/main.js:1071`, invoking `anonymize_snapshot` (sync, in the SYNC_ALLOWLIST). - The story knowledge base lives in `community/stories/{generic,n55}.toml`; tests load it via `story::load()`. * fix(ci): handle stacked PRs in autonomous auto-merge job (#204) * Initial plan * fix(v0.14.4): handle stacked PR auto-merge in CI --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --------- Co-authored-by: ohgeeceee <ohgeeceee@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
* docs(roadmap): audit v0.3.0 historical section — stale 🟢 Ready items moved to ✅ Done Six items in the v0.3.0 'Real Car' historical section were marked 🟢 Ready but have actually shipped: | Item | Shipped in | Reference | |-----------------------|-----------------|--------------------------| | KWP2000 slow timeout | v0.13.0 (PR #153) | commit fd9efc2 | | ISO-TP multi-frame | v0.14.x | src-tauri/src/transport/isotp.rs (enforced by CLAUDE.md, refreshed in PR #198) | | Dark/light theme | v0.7.0 (PR #109) | commit afefc32 | | Gauge theming | v0.7.0 (PR #109) | commit afefc32 | | Save/load workspace | v0.7.0 (PR #109) | commit afefc32 | | Export PNG/SVG charts | v0.11.0 (PR #136) | commit 7f92ccb | This is the same doc-rot pattern that PR #198 fixed in CLAUDE.md: items the inventory still claims as 'Ready to land' have actually shipped, but nobody re-tagged the historical section afterward. Same 'data over invention' fix as the CLAUDE.md refresh. What changed: - '⭐ Protocol & Transport' table — kept the four genuinely-🟡 items (ENET/DoIP, BLE, WiFi, CAN-bus listener), removed the two stale 🟢 Ready items. - 'UI / UX' table — kept the only genuinely-🟡 item (Mobile- responsive), removed the five stale 🟢 Ready items. - New '✅ Protocol, Transport, UI/UX — historical (shipped)' table — pins the six shipped items with their PR references and code locations, following the v0.3.0 'Decode Functions' historical pattern that already exists just above. What this PR does NOT change: - No CHANGELOG.md edits (the shipped-PR references are in CHANGELOG.md already) - No CLAUDE.md edits (the ISO-TP / keepalive / read_vin references there are already accurate post PR #198) - No code edits - No test edits (markdown only) Verified locally (docs-only, no test diff expected): - node --test src/js/**/*.test.js src/js/**/*.test.cjs: 226/226 - python -m pytest backend/tests/ -q: 166/166 - cd src-tauri && cargo test --lib --offline: 149/149 - npm run build: rc=0, both BeeEmUu_0.14.3 bundles built Tier A docs-only per CLAUDE.md. Self-merge on CI green. Cross-references: - PR #198: 'docs: refresh CLAUDE.md hardware/timing invariants' (the same doc-rot sweep on CLAUDE.md) - PR #183: 'docs(proposal): fix stale NOT YET IMPLEMENTED claims' (proposed similar fixes but never applied) * feat(v0.14.4): story coverage — 52 unit tests for story.rs + anonymize.rs Two user-facing Rust modules have shipped with **zero unit tests** since the diagnostic-story + secure-snapshot-share features landed: - `src-tauri/src/story.rs` (350 LOC) — the Generate Story pipeline that powers the one-click mechanic narrative modal in `src/index.html:513` + `src/js/main.js:2961` (renderStory). - `src-tauri/src/anonymize.rs` (113 LOC) — the VIN-stripping layer that powers the Secure Snapshot Share feature (`src/js/main.js:1071`). Both are pure-Rust, deterministic, and snapshot-driven — the right shape for unit tests with fixture-built `SessionSnapshot` inputs. This PR adds 52 unit tests covering: **story.rs (32 tests):** - `Severity::from_str` bucketing (critical / warning / unknown → info). - `Severity` ordering (Critical > Warning > Info). - `priority_for` severity → priority number. - `parse_cost_range` parser: single value, tilde, hyphen, **en-dash** (the TOML files use en-dash — parser must normalise), whitespace, empty, garbage. - `format_vehicle` for empty / VIN-only / mileage-only / decoded. - `build_context` freeze-frame string assembly. - Full `generate` pipeline: - empty snapshot → Info story with no findings. - unknown DTC → generic Info finding. - n55-specific DTC (2A82) → uses engine template. - generic DTC fallback when engine-specific missing. - severity = max of all findings (sorted critical-first). - recommendations sorted ascending by priority. - cost range sums across findings. - cost-max invariant: max >= min + 50. - DTC code case-insensitive lookup (TOML keys are uppercased on load). - summary text counts critical + warning correctly. - title uses manufacturer + VIN prefix. **anonymize.rs (20 tests):** - `hash_vin` properties: 16 hex chars, stable for same input, distinct for distinct inputs, case-sensitive (current behaviour pinned). - `anonymize` pipeline: - VIN never leaks into the anonymized JSON. - VIN → fingerprint via hash_vin. - Missing VIN → "unknown" fingerprint. - engine_family preserved from suggested_profile. - engine_family defaults to "generic". - Modules / DTCs / freeze frames / ident all preserved. - Mileage (mileage_km) stripped (privacy). - Empty modules handled. - fault_count = None → 0 in the output. - recorded_at populated with "(UTC)" suffix. - live_data is always empty (current anonymizer strips it; pinning the decision). - `export_json`: - No VIN leak in pretty JSON. - No mileage leak. - Pretty-printed (multi-line + indented). - Round-trips through serde. ## Verification - `cd src-tauri && cargo test --lib --offline` — **201/201 pass** (149 existing + 52 new). - `cd src-tauri && cargo test --test async_commands --offline` — 1/1 (the CLAUDE.md invariant guard still green — no new sync commands). - `node --test src/js/**/*.test.js src/js/**/*.test.cjs` — 226/226 (no JS diff). - `pytest backend/tests/ -q` — 166/166 (no backend diff). - `npm run build` — rc=0, 2m32s; both BeeEmUu_0.14.3 bundles built. ## Tier **A** — pure additions to existing Rust modules, no `transport/**` / `protocol/**` / `commands.rs` / `.claude/**` touches. Self-merge on CI green per CLAUDE.md rule 2. ## Cross-references - The Story modal UI is `renderStory` in `src/js/main.js:2961`, the button at `src/index.html:513`. - The Secure Snapshot Share wiring is `doSecureShare` in `src/js/main.js:1071`, invoking `anonymize_snapshot` (sync, in the SYNC_ALLOWLIST). - The story knowledge base lives in `community/stories/{generic,n55}.toml`; tests load it via `story::load()`. * fix(ci): handle stacked PRs in autonomous auto-merge job (#204) * Initial plan * fix(v0.14.4): handle stacked PR auto-merge in CI --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> * Initial plan (#206) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ohgeecee <ohjoncurrie@gmail.com> --------- Co-authored-by: ohgeeceee <ohgeeceee@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Summary
Second PR from
docs/v0.7.0_plan.md— the "Ready-to-Claim pile" (three small, independent features bundled per plan §PR #2):~/beeemuu-exports/workspace.jsonand restore on launch. One new async command (read_export_text; async-command guard now at 45).[profile.theme]gauge colors — profiles can override gauge palette (e.g. M-car red/blue). Required a small Rust pass-through — the plan's "already supported" claim was inaccurate.b58.tomlships a reference theme block.Test plan
cargo test— 97 lib + 1 integration greennode --test— 57 greenpytest— 151 greencargo buildwarning-cleantauri devsmoke: toggle theme, restart, confirm tabs + theme restore; load b58 profile, confirm gauge colors — flagged for mergerMerge policy
Tier A under CLAUDE.md (frontend-only + docs + one additive non-transport command) — agent may self-merge when CI is green.
Closes part of
docs/v0.7.0_plan.mdPR #2.