mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-06-05 13:41:19 +00:00
1bfbbd6bb2f3fdb10cfee461dbf16bce7d34da1f
13 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
101c11b4b3 |
fix(#1361): theme customizer — colorblind presets [WIP] (#1378)
WIP — draft PR for CI to exercise the RED test commit. Will be promoted
out of draft once the GREEN commit lands.
Red commit:
|
||
|
|
51f823bf7e |
feat: one-click prune nodes outside geofilter (#669 M4) (#738)
## Summary - Adds `POST /api/admin/prune-geo-filter` endpoint — dry-run by default, `?confirm=true` to permanently delete nodes outside the current geofilter polygon + buffer. Requires `X-API-Key` header. - Adds **Prune nodes** section inside the GeoFilter customizer tab (write-access only, same `writeEnabled` gate as PUT). **Preview** lists affected nodes; **Confirm delete** removes them. - Adds `GetNodesForGeoPrune` and `DeleteNodesByPubkeys` DB helpers. - Updates `docs/user-guide/geofilter.md` — documents the UI button as primary workflow, CLI script as alternative. > **Depends on M3** (`feat/geofilter-m3-customizer`, PR #736). Merge M3 first. ## Test plan - [x] `cd cmd/server && go test ./...` — all pass - [x] Customizer GeoFilter tab without `apiKey` — Prune section not visible - [x] With `apiKey` + polygon active — Prune section visible - [x] **Preview** returns list of nodes outside polygon (no deletions) - [x] **Confirm delete** removes nodes, list clears - [x] `POST /api/admin/prune-geo-filter` without `X-API-Key` → 401 - [x] `POST /api/admin/prune-geo-filter` with no polygon configured → 400 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
03b5d3fe28 |
fix(#1065): first-visit gesture discoverability hints (#1186)
Red commit:
|
||
|
|
cfd1903c6b |
fix(logo): default sage/teal brand colors, customizer mirrors accent (#1162)
## Summary Restores sage/teal as default logo colors while preserving customizer theming. Closes the gap from #1157 (closed) and the user's complaint about lost two-tone. Out-of-the-box, the navbar + hero CORE/SCOPE wordmarks now render the brand-identity duotone — `#cfd9c9` (sage / fog) and `#2c8c8c` (teal / water). When an operator picks a theme via the customizer (or sets a custom accent color), the wordmark recolors to follow. ## Approach (Option C — decoupled defaults + customizer mirror) - **`public/style.css` `:root`** — set `--logo-accent: #cfd9c9` and `--logo-accent-hi: #2c8c8c` as literal defaults. Removes the previous `var(--accent)` cascade so blue-by-default no longer leaks into the brand mark. - **`public/customize-v2.js`** — `applyTheme()`, the early-apply path, and the live color-picker `input` handler now mirror `themeSection.accent` → `--logo-accent` and `themeSection.accentHover` → `--logo-accent-hi`. - **`public/customize.js`** (legacy) — same mirroring in `applyThemePreview()` and the early localStorage replay. - **`.github/workflows/deploy.yml`** — adds the new e2e to the Chromium batch. This preserves `--accent` as the canonical app-wide accent token (no other UI changes) while giving the logo its own brand-defaulted tokens that the customizer still drives. ## Tests Red → green commit pair on the branch. - **NEW: `test-logo-default-sage-teal-e2e.js`** — gates both halves of the contract: 1. Clean localStorage → navbar + hero CORE = `rgb(207, 217, 201)`, SCOPE = `rgb(44, 140, 140)`. 2. Seeded `cs-theme-overrides` with red accent → navbar + hero recolor to red. - **UPDATED: `test-logo-theme-e2e.js`** — replaces the old "must NOT be sage" sentinel (sage was a regression marker; it's now the brand default) with a theme-reactivity probe that overrides `--logo-accent` / `--logo-accent-hi` directly and asserts the wordmark fill changes. Duotone, mobile-fit, and clip checks are unchanged. ## Verification - Default load: sage CORE + teal SCOPE in navbar AND hero ✔ (asserted by step 1 of the new e2e). - Customizer override: wordmark follows `accent` / `accentHover` ✔ (asserted by step 2 of the new e2e + the theme-reactivity probe in `test-logo-theme-e2e.js`). - Preflight: all hard gates green (PII, branch scope, red commit, CSS-var defined, CSS self-fallback, LIKE-on-JSON, sync migration); all warnings green. ## Browser verified E2E assertion added: `test-logo-default-sage-teal-e2e.js:73` (default sage), `test-logo-default-sage-teal-e2e.js:124` (customizer override). CI runs both via `deploy.yml:243`. Browser verified: covered by Chromium e2e against `http://localhost:13581` in CI; staging URL TBD on merge. --------- Co-authored-by: openclaw-bot <bot@openclaw.local> |
||
|
|
dfacfd0f6e |
fix(logo): widen navbar SVG viewBox so CORE/SCOPE wordmark fits (#1141 followup) (#1156)
Fixes #1141 follow-up — the visible-on-staging SCOPE→SCOP clip that the prior PRs (#1137, #1141) intended to address but didn't. ## What was actually broken (ground truth from staging) Staging at `http://20.109.157.39:80/` renders the inline navbar SVG correctly — duotone CORE/SCOPE fills inherit page CSS vars, mobile mark-only swap fires at ≤400px, customizer logo override path works. Those parts of #1137 + #1141 landed cleanly. What did **NOT** land: the SVG `viewBox` was never widened to fit the rendered Aldrich wordmark. At every desktop viewport the SCOPE `<text text-anchor="start" x="773.8">` produces a bbox extending to user-space x≈1112, but the navbar `viewBox="170 10 860 280"` ends at x=1030. Result: SCOPE renders as **SCOP** on every desktop load. CORE also slightly overflows the left edge (bbox.x=153.7 < viewBox.x=170). The original brief premise (mushroom emoji still in `index.html` + `<img>`-loaded SVG monotone fallback on staging) does not match current state — `public/index.html:45` already has the inline SVG, staging renders it, and computed fills are duotone (`rgb(74,158,255)` vs `rgb(109,179,255)`). The visible bug is geometric clipping, not CSS-var inheritance or a mushroom revert. ## Fix (one-liner SVG geometry change) - `public/index.html` — navbar `svg.brand-logo`: `viewBox="170 10 860 280"` → `viewBox="150 10 970 280"`; intrinsic `width="111"` → `width="125"` (preserves ~36px nav row height). - `public/style.css` — `.brand-logo { width }` 111px → 125px (desktop), tablet `@media (max-width:900px)` pin 99px → 112px to keep the new aspect ratio so wordmark still doesn't clip on tablets. - `public/customize-v2.js` — `_setBrandLogoUrl` `<img>` swap dimensions updated to match (when an operator overrides `branding.logoUrl`). The `≤400px` mobile mark-only swap is unchanged — at narrow widths the wordmark still hides entirely and the dedicated `.brand-mark-only` SVG (no `<text>`) renders. ## TDD (red → green) | commit | role | |---|---| | `16b7a60` | **RED** — `test-logo-theme-e2e.js` assertion #7: every `CORE`/`SCOPE` `<text>` bbox must fit inside the SVG `viewBox`. Master fails: `[{text:CORE, bboxX:153.7, bboxRight:426.2, vbX:170}, {text:SCOPE, bboxX:773.8, bboxRight:1111.5, vbRight:1030}]` | | `0db473b` | **GREEN** — widen viewBox + width to fit | Test exercises real `getBBox()` measurement on a headless Chromium DOM with the Aldrich webfont loaded — not a unit-test fill string check. The earlier #1141 tests asserted computed `fill` colors (which were correct) but never measured rendered geometry; that's the gap. ## Visual proof **Before** (master HEAD against staging, viewport 1280): `/tmp/staging-logo-before-1280.png` — SCOPE clearly clipped to "SCOP". **After** (this branch against local server, viewport 1280): `/tmp/local-after-1280-screen.png` — full CORE / SCOPE rendered. **Mobile (after, 375px)**: `/tmp/local-after-mobile.png` — mark-only SVG (no wordmark, no clip). ## Preflight `bash ~/.openclaw/skills/pr-preflight/scripts/run-all.sh origin/master` — all hard gates clean (PII, branch-scope, red-commit-genuine, css-vars-defined, css-self-fallback, like-on-json, sync-migration), all warnings clean (img-svg-ratio, themed-img-svg, fixture-coverage). E2E assertion added: `test-logo-theme-e2e.js:286-310` Browser verified: `/tmp/local-after-1280-screen.png` (local server) + `/tmp/staging-logo-before-1280.png` (staging baseline). --------- Co-authored-by: corescope-bot <bot@corescope.local> |
||
|
|
364c5766fc |
feat(logo): wire new CoreScope SVG logo into navbar + home hero (#1137)
## Adds new logo and home hero Replaces the navbar mushroom emoji + "CoreScope" text spans with the new CoreScope SVG mark, and adds a hero SVG (with the MESH ANALYZER tagline) above the home page H1. ### What changed - `public/img/corescope-logo.svg` — navbar mark, no tagline (locked "aggressive low-amp chirp" variant: facing-arcs + low-amp chirp connector between the two nodes). - `public/img/corescope-hero.svg` — home hero version, includes the MESH ANALYZER tagline. - `public/index.html` — replaces `<span class="brand-icon">🍄</span><span class="brand-text">CoreScope</span>` with `<img class="brand-logo" src="img/corescope-logo.svg?__BUST__" …>`. `.nav-brand` link still routes to `#/`. `.live-dot` retained. - `public/style.css` — adds `.brand-logo { height: 36px }` (32px on tablet ≤900px). Existing 52px nav height unchanged. - `public/home.js` / `public/home.css` — adds `<img class="home-hero-logo">` above the hero `<h1>`, sized `max-width: min(720px, 90vw)` and centered. ### TDD Red→green is visible in the branch: - `3159b82` — `test(logo): add failing E2E …` (red commit). Adds `test-logo-rebrand-e2e.js` and wires it into the `e2e-test` job in `deploy.yml` with `CHROMIUM_REQUIRE=1`. On this commit `index.html` still has the emoji + text spans, `home.js` has no hero img, and the SVG asset files do not exist — the test asserts on each so CI fails on assertion. - `19434e1` — `feat(logo): wire new CoreScope SVG logo …` (green commit). Implements the fix. ### E2E asserts 1. `.nav-brand img` exists with `src` ending `corescope-logo.svg` 2. legacy `.brand-icon` / `.brand-text` are gone 3. `.live-dot` is present, visible, and to the right of the logo (no overlap) 4. `.home-hero img.home-hero-logo` exists with `src` ending `corescope-hero.svg`, positioned BEFORE the `<h1>` 5. both `/img/corescope-{logo,hero}.svg` return 200 with svg content-type ### Customizer compatibility - `customize.js` still does `querySelector('.brand-text')` / `.brand-icon` for live branding updates. Both now return `null`; existing `if (el)` guards make those branches silent no-ops. **No JS errors, but the customizer's `branding.siteName` and `branding.logoUrl` fields no longer rewrite the navbar brand** — the brand is now a fixed SVG asset. - **Theme accent does NOT recolor the SVG.** SVGs loaded via `<img src>` are isolated documents and cannot inherit document CSS variables; the SVG falls back to its embedded brand colors. This is appropriate for a brand mark; if recoloring per theme is desired later, swap to inline SVG (separate PR). ### Browser validation Local Chromium not available in this env; the E2E test soft-skips locally and hard-fails in CI (`CHROMIUM_REQUIRE=1`). Server-side checks done locally: - `curl http://localhost:13581/` → confirmed `<img class="brand-logo" src="img/corescope-logo.svg?<bust>" …>` rendered, no `.brand-icon`/`.brand-text` spans. - `curl -I /img/corescope-logo.svg` and `/img/corescope-hero.svg` → both 200. ### Performance No hot-path changes. Two new static SVG assets (~7.6KB each), served directly by the Go static handler. Cache-busted via `?__BUST__` (auto-replaced server-side). --------- Co-authored-by: OpenClaw Bot <bot@openclaw.local> Co-authored-by: Kpa-clawbot <bot@kpa-clawbot.local> |
||
|
|
8dfcec2ff3 |
feat: include favorites and claimed nodes in export/import JSON (#1003)
## Summary
Extends the customizer v2 export/import to include favorite nodes and
claimed ("My Mesh") nodes, so users can transfer their full setup
between browsers/devices.
## Changes
### `public/customize-v2.js`
- `readOverrides()` now merges `favorites` (from `meshcore-favorites`)
and `myNodes` (from `meshcore-my-nodes`) into the exported JSON
- `writeOverrides()` extracts `favorites`/`myNodes` arrays and writes
them to their respective localStorage keys, keeping theme overrides
separate
- `validateShape()` validates both new keys as arrays, rejecting
non-array values
- `VALID_SECTIONS` updated to include `favorites` and `myNodes`
### `test-customizer-v2.js`
- 8 new tests covering read/write/validate for both favorites and
myNodes
## TDD
- Red commit: `0405fb7` (failing tests)
- Green commit: `bb9dc34` (implementation)
Fixes #895
---------
Co-authored-by: you <you@example.com>
|
||
|
|
9293ff408d |
fix(customize): skip panel re-render while a text field has focus (#927)
## Summary - `_debouncedWrite()` was calling `_refreshPanel()` 300ms after every keystroke - `_refreshPanel()` sets `container.innerHTML`, destroying the focused input element - On mobile, losing the focused input collapses the virtual keyboard after each keypress Guard the `_refreshPanel()` call so it is skipped when `document.activeElement` is inside the panel. The pipeline (`_runPipeline`) still runs immediately — CSS updates apply. Override dots update on the next natural re-render (tab switch, dark-mode toggle, panel reopen). ## Root cause `customize-v2.js` → `_debouncedWrite()` → `_refreshPanel()` → `_renderPanel()` → `container.innerHTML = ...` ## Test plan - [ ] New Playwright E2E test: open Customize, focus a text field, type, wait 500ms past debounce — asserts input element is still connected to DOM and focus remains inside panel - [ ] Manual: open Customize on mobile (or DevTools mobile emulation), type in Site Name — keyboard must not collapse after each character Fixes #896 |
||
|
|
1de80a9eaf |
feat: serve geofilter builder from app, link from customizer (#735)
## Summary Part of #669 — M2: Link the builder from the app. - **`public/geofilter-builder.html`** — the existing `tools/geofilter-builder.html` is now served by the static file server at `/geofilter-builder.html`. Additions vs the original: a `← CoreScope` back-link in the header, inline code comments explaining the output format, and a help bar below the output panel with paste instructions and a link to the documentation. - **`public/customize-v2.js`** — adds a "Tools" section at the bottom of the Export tab with a `🗺️ GeoFilter Builder →` link and a one-line description. - **`docs/user-guide/customization.md`** — documents the new GeoFilter Builder entry in the Export tab. > **Note:** `tools/geofilter-builder.html` is kept as-is for local/offline use. The `public/` copy is what the server serves. > **Depends on:** #734 (M1 docs) for `docs/user-guide/geofilter.md` — the link in the help bar references that file. Can be merged independently; the link still works once M1 lands. ## Test plan - [x] Open the app, go to Customizer → Export tab — "Tools" section appears with GeoFilter Builder link - [x] Click the link — opens `/geofilter-builder.html` in a new tab - [x] Builder loads the Leaflet map, draw 3+ points — JSON output appears - [x] Copy button works, output is valid `{ "geo_filter": { ... } }` JSON - [x] `← CoreScope` back-link navigates to `/` - [x] Help bar shows paste instructions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
bd54707987 |
feat: distance unit preference — km, mi, or auto (#621) (#646)
## Summary - **`app.js`**: `getDistanceUnit()`, `formatDistance(km)`, `formatDistanceRound(km)` helpers. Auto mode uses `navigator.language` — miles for `en-US`, `en-GB`, `my`, `lr`; km everywhere else. - **`customize-v2.js`**: Distance Unit preference (km / mi / auto) in Display Settings panel. Stored in `localStorage['meshcore-distance-unit']` via the existing apply pipeline. Override dot and reset work. Display tab badge counts it. - **`nodes.js`**: Neighbor table distance cell uses `formatDistance()`. - **`analytics.js`**: All rendered km values use `formatDistance()` or `formatDistanceRound()`. Column headers (`km`/`mi`) respond to the active unit. Collision classification thresholds (Local < 50 km / Regional 50–200 km / Distant > 200 km) also adapt. Default is `auto` — no change for existing users unless their locale maps to miles. ## Test plan - [x] `node test-frontend-helpers.js` — 456 passed, 0 failed (10 new formatDistance tests) - [ ] Set unit to **mi** in customize → Neighbors table shows `7.6 mi` instead of `12.3 km` - [ ] Analytics → Distance tab → stat cards, leaderboard, and column headers all show miles - [ ] Collision tool → Local/Regional/Distant thresholds show `31 mi` / `124 mi` - [ ] Route patterns popup shows miles per hop and total - [ ] Reset override dot → unit returns to auto Closes #621 🤖 Generated with [Claude Code](https://claude.ai/claude-code) --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: you <you@example.com> |
||
|
|
54fab0551e |
fix: add home defaults to server theme config (#525) (#526)
## Summary Fixes #525 — Customizer v2 home section shows empty fields and adding FAQ kills steps. ## Root Cause Server returned `home: null` from `/api/config/theme` when no home config existed in config.json or theme.json. The customizer had no built-in defaults, so all home fields appeared empty. When a user added a single override (e.g. FAQ), `computeEffective` started from `home: null`, created `home: {}`, and only applied the user's override — wiping steps and everything else. ## Fix ### Server-side (primary) In `handleConfigTheme()`, replaced the conditional `home` assignment with `mergeMap` using built-in defaults matching what `home.js` hardcodes: - `heroTitle`: "CoreScope" - `heroSubtitle`: "Real-time MeshCore LoRa mesh network analyzer" - `steps`: 4 default getting-started steps - `footerLinks`: Packets + Network Map links Config/theme overrides merge on top, so customization still works. ### Client-side (defense-in-depth) Added `DEFAULT_HOME` constant in `customize-v2.js`. `computeEffective()` now falls back to these defaults when server returns `home: null`, ensuring the customizer works even without server defaults. ## Tests - **Go**: `TestConfigThemeHomeDefaults` — verifies `/api/config/theme` returns non-null home with heroTitle, steps, footerLinks when no config is set - **JS**: Two new tests in `test-frontend-helpers.js` — verifies `computeEffective` provides defaults when home is null, and that user overrides merge correctly with defaults Co-authored-by: you <you@example.com> |
||
|
|
34489e0446 |
fix: customizer v2 — phantom overrides, missing defaults, stale dark mode (#518) (#520)
Fixes #518, Fixes #514, Fixes #515, Fixes #516 ## Summary Fixes all customizer v2 bugs from the consolidated tracker (#518). Both server and client changes. ## Server Changes (`routes.go`) - **typeColors defaults** — added all 10 type color defaults matching `roles.js` `TYPE_COLORS`. Previously returned `{}`, causing all type colors to render as black. - **themeDark defaults** — added 22 dark mode color defaults matching the Default preset. Previously returned `{}`, causing dark mode to have no server-side defaults. ## Client Changes (`customize-v2.js`) - [x] **P0: Phantom override cleanup on init** — new `_cleanPhantomOverrides()` runs on startup, scanning `cs-theme-overrides` and removing any values that match server defaults (arrays via `JSON.stringify`, scalars via `===`). - [x] **P1: `setOverride` auto-prunes matching defaults** — after debounced write, iterates the delta and removes any key whose value matches the server default. Prevents phantom overrides from accumulating. - [x] **P1: `_countOverrides` counts only real diffs** — now iterates keys and calls `_isOverridden()` instead of blindly counting `Object.keys().length`. Badge count reflects actual overrides only. - [x] **P1: `_isOverridden` handles arrays/objects** — uses `JSON.stringify` comparison for non-scalar values (home.steps, home.checklist, etc.). - [x] **P1: Type color fallback** — `_renderNodes()` falls back to `window.TYPE_COLORS` when effective typeColors are empty, preventing black color swatches. - [x] **P1: Dark/light toggle re-renders panel** — MutationObserver on `data-theme` now calls `_refreshPanel()` when panel is open, so switching modes updates the Theme tab immediately. ## Tests 6 new unit tests added to `test-customizer-v2.js`: - Phantom scalar overrides cleaned on init - Phantom array overrides cleaned on init - Real overrides preserved after cleanup - `isOverridden` handles matching arrays (returns false) - `isOverridden` handles differing arrays (returns true) - `setOverride` prunes value matching server default All 48 tests pass. Go tests pass. --------- Co-authored-by: you <you@example.com> |
||
|
|
64745f89b1 |
feat: customizer v2 — event-driven state management (#502) (#503)
## Summary Implements the customizer v2 per the [approved spec](docs/specs/customizer-rework.md), replacing the v1 customizer's scattered state management with a clean event-driven architecture. Resolves #502. ## What Changed ### New: `public/customize-v2.js` Complete rewrite of the customizer as a self-contained IIFE with: - **Single localStorage key** (`cs-theme-overrides`) replacing 7 scattered keys - **Three state layers:** server defaults (immutable) → user overrides (delta) → effective config (computed) - **Full data flow pipeline:** `write → read-back → merge → atomic SITE_CONFIG assign → apply CSS → dispatch theme-changed` - **Color picker optimistic CSS** (Decision #12): `input` events update CSS directly for responsiveness; `change` events trigger the full pipeline - **Override indicator dots** (●) on each field — click to reset individual values - **Section-level override count badges** on tabs - **Browser-local banner** in panel header: "These settings are saved in your browser only" - **Auto-save status indicator** in footer: "All changes saved" / "Saving..." / "⚠️ Storage full" - **Export/Import** with full shape validation (`validateShape()`) - **Presets** flow through the standard pipeline (`writeOverrides(presetData) → pipeline`) - **One-time migration** from 7 legacy localStorage keys (exact field mapping per spec) - **Validation** on all writes: color format, opacity range, timestamp enum values - **QuotaExceededError handling** with visible user warning ### Modified: `public/app.js` Replaced ~80 lines of inline theme application code with a 15-line `_customizerV2.init(cfg)` call. The customizer v2 handles all merging, CSS application, and global state updates. ### Modified: `public/index.html` Swapped `customize.js` → `customize-v2.js` script tag. ### Added: `docs/specs/customizer-rework.md` The full approved spec, included in the repo for reference. ## Migration On first page load: 1. Checks if `cs-theme-overrides` already exists → skip if yes 2. Reads all 7 legacy keys (`meshcore-user-theme`, `meshcore-timestamp-*`, `meshcore-heatmap-opacity`, `meshcore-live-heatmap-opacity`) 3. Maps them to the new delta format per the spec's field-by-field mapping 4. Writes to `cs-theme-overrides`, removes all legacy keys 5. Continues with normal init Users with existing customizations will see them preserved automatically. ## Dark/Light Mode - `theme` section stores light mode overrides, `themeDark` stores dark mode overrides - `meshcore-theme` localStorage key remains **separate** (view preference, not customization) - Switching modes re-runs the full pipeline with the correct section ## Testing - All existing tests pass (`test-packet-filter.js`, `test-aging.js`, `test-frontend-helpers.js`) - Old `customize.js` is NOT modified — left in place for reference but no longer loaded ## Not in Scope (per spec) - Undo/redo stack - Cross-tab synchronization - Server-side admin import endpoint - Map config / geo-filter overrides --------- Co-authored-by: you <you@example.com> |