Commit Graph

2198 Commits

Author SHA1 Message Date
Kpa-clawbot 76658dcc44 ci: update go-ingestor-coverage.json [skip ci] 2026-05-27 14:58:19 +00:00
Kpa-clawbot 5b4349a93b ci: update frontend-tests.json [skip ci] 2026-05-27 14:58:18 +00:00
Kpa-clawbot 08dcc864f0 ci: update frontend-coverage.json [skip ci] 2026-05-27 14:58:17 +00:00
Kpa-clawbot 3deb3188d4 ci: update e2e-tests.json [skip ci] 2026-05-27 14:58:13 +00:00
Kpa-clawbot 777f77a451 feat(#1420): dark-tile provider picker in customizer (4 variants) (#1430)
# feat(#1420): dark-tile provider picker in customizer (4 variants)

Closes #1420.

## What

Operator pick: don't force a single dark-tile choice on everyone. Wire 4
candidates into the customizer + server config so users can choose which
dark basemap they want, with per-browser persistence.

## Providers shipped

| ID | Source | Filter |
|---|---|---|
| `carto-dark` (default) |
`https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png` | none |
| `esri-darkgray-labels` | Esri Dark Gray Base + Reference (two stacked
layers) | none |
| `voyager-inverted` | Carto Voyager + CSS `invert(1) hue-rotate(180deg)
brightness(0.9) contrast(1.05)` on `.leaflet-tile-pane` | applied in
dark, cleared in light |
| `positron-inverted` | Carto Positron + same CSS invert | applied in
dark, cleared in light |

No new dependencies — all providers are URL-only.

## Architecture

- **`public/map-tile-providers.js`** — registry + 5 public helpers
(`MC_TILE_PROVIDERS`, `MC_setDarkTileProvider`,
`MC_getDarkTileProvider`, `MC_setServerDefaultTileProvider`,
`MC_applyTileFilter`). Persists to
`localStorage['mc-dark-tile-provider']`. Dispatches
`mc-tile-provider-changed` on user pick.
- **`public/map.js` / `public/live.js`** — resolve the active dark
provider via the registry, manage the Esri labels overlay lifecycle (add
when needed, remove cleanly so we don't leak layers on repeated theme
toggles), and apply/clear the CSS filter on `.leaflet-tile-pane`. Listen
for both `data-theme` mutations AND `mc-tile-provider-changed`.
- **`public/customize-v2.js`** — new "Dark Map Tiles" dropdown in the
Display tab. On change, calls `MC_setDarkTileProvider(id)`; the maps
re-render live without reload.
- **`public/roles.js`** — hydrates the server default via
`MC_setServerDefaultTileProvider` from `/api/config/client`.
- **Server (`cmd/server/`)** — new `mapDarkTileProvider` string on
`Config` + surfaced in `ClientConfigResponse`. Default empty → client
uses `carto-dark`.
- **`config.example.json`** — documents the new field with all allowed
values.

## Behavior guarantees (from the acceptance criteria)

-  Light mode is **completely unchanged** — `_resolveTileUrl(false)`
short-circuits to `TILE_LIGHT` with no filter and no overlay logic.
-  Switching dark→light always clears the CSS filter, even if an
inverted provider remains selected (`MC_applyTileFilter` is called on
every theme change and early-returns to `style.filter = ''` when not
dark).
-  Switching light→dark with an inverted provider re-applies the
filter.
-  Attribution is updated per provider (Esri credit for Esri, CartoDB
credit for the others); the Leaflet attribution control is refreshed.
-  Esri uses two stacked layers (base + reference labels). The
reference layer is added/removed cleanly so repeat toggles do not leak.
-  Customizer change → immediate re-render, no reload. Uses the same
"live setting + persist + dispatch event" pattern as cb-presets (#1361).

## TDD

- Red commit: `148b71c3` — `test(#1420): add failing tests for dark-tile
provider registry (red)` — 6/7 assertions fail (stub only returns
nulls).
- Green commit: `49ffb230` — `feat(#1420): dark-tile provider picker — 4
variants wired into customizer` — 7/7 pass.

## Tests

`test-issue-1420-tile-providers.js` (wired into `test-all.sh` and
`.github/workflows/deploy.yml` JS-unit step):

```
── #1420 Dark-tile provider registry ──
   MC_TILE_PROVIDERS has all 4 IDs with url + attribution
   Inverted providers have non-null invertFilter; non-inverted have null
   MC_setDarkTileProvider persists to localStorage and dispatches mc-tile-provider-changed
   MC_setDarkTileProvider rejects unknown IDs (no persistence, no dispatch)
   MC_getDarkTileProvider falls back to server default, then carto-dark
   Apply filter for inverted provider in dark mode; clear when switching to non-inverted
   Light mode always clears the CSS filter even if inverted provider is selected
  7 passed, 0 failed
```

`cd cmd/server && go build ./... && go vet ./...` — clean.

## CDP verification

Not run in this PR — the sandbox does not have a Chrome CDP endpoint
reachable, and staging cannot exercise this code path until this branch
is deployed. The issue body's "CDP-verified candidate set" table covers
prior provider-URL validation; the new code path (registry lookup +
filter swap + Esri overlay lifecycle) is covered by the unit tests
above. **Recommend operator run a quick manual verification on staging
post-deploy:** dark mode → open customizer → cycle through all 4
providers, confirm tiles render and the CSS filter is applied for
`voyager-inverted` / `positron-inverted` (verify via
`getComputedStyle(document.querySelector('.leaflet-tile-pane')).filter`).

## Files touched

- `public/map-tile-providers.js` (new)
- `public/map.js`, `public/live.js`, `public/customize-v2.js`,
`public/roles.js`, `public/index.html`
- `cmd/server/config.go`, `cmd/server/routes.go`, `cmd/server/types.go`
- `config.example.json`
- `test-issue-1420-tile-providers.js` (new), `test-all.sh`,
`.github/workflows/deploy.yml`
- `.eslintrc.json` (register new `MC_*` globals)

---------

Co-authored-by: openclaw <bot@openclaw.local>
2026-05-27 14:37:51 +00:00
Kpa-clawbot d01f41483b ci: update go-server-coverage.json [skip ci] 2026-05-27 08:48:53 +00:00
Kpa-clawbot 8cf2347131 ci: update go-ingestor-coverage.json [skip ci] 2026-05-27 08:48:52 +00:00
Kpa-clawbot 00d351f053 ci: update frontend-tests.json [skip ci] 2026-05-27 08:48:51 +00:00
Kpa-clawbot 32cb0e9664 ci: update frontend-coverage.json [skip ci] 2026-05-27 08:48:49 +00:00
Kpa-clawbot 9535f367a5 ci: update e2e-tests.json [skip ci] 2026-05-27 08:48:48 +00:00
efiten f0c69d5fe7 perf(server): fix repeaterEnrichTTL mismatch causing 18s /api/nodes latency (#1425)
## Root cause

`repeaterEnrichTTL` was **15 seconds**, but the background recomputer
(`StartRepeaterEnrichmentRecomputer`) runs every **5 minutes**.

After each recomputer tick, the relay/usefulness caches were valid for
15 seconds. For the remaining 4m45s, every `/api/nodes` request hit a
stale TTL gate in `GetRepeaterRelayInfoMap` /
`GetRepeaterUsefulnessScoreMap` and fell through to
`computeRepeaterRelayInfoMap` **on the request goroutine**. On
production (16k+ transmissions, 240k hop records) that rebuild takes ~18
seconds, making `/api/nodes?limit=5000` freeze on virtually every page
load.

The pattern was:
```
recomputer runs at T=0  → cache valid
T=15s                   → TTL expires
T=15s … T=5min          → every request rebuilds on-thread (18s each)
T=5min                  → recomputer runs again → 15s valid window
repeat
```

## Fix

One line in `repeater_enrich_bulk.go`:

```go
// Before
const repeaterEnrichTTL = 15 * time.Second

// After
const repeaterEnrichTTL = 10 * time.Minute
```

The TTL now exceeds the recomputer interval so the cache is always warm
between background ticks. The TTL remains as a safety net for cases
where the recomputer isn't running (tests, early startup edge cases) —
it just no longer expires between ticks.

## Production results (analyzer.on8ar.eu)

Tested with binary injection on the live server before opening this PR.

| Metric | Before | After |
|--------|--------|-------|
| TTFB (`/api/nodes?limit=5000`) | 18.6 s | 0.47–0.54 s |
| Total response time | 18.9 s | 1.55–1.73 s |
| Improvement | — | **34–39×** |

Confirmed still fast at t+60s (well past the old 15s window).

## Test results

```
TestHandleNodesPerfLargeFleet      elapsed=1.9ms   budget=2s  PASS
TestHandleNodesLimit2000ColdMiss   elapsed=5.3ms   budget=2s  PASS
```

Both existing perf regression tests pass unchanged — the TTL change
doesn't affect their behavior (they test the cold-prewarm path, not TTL
expiry).

## Why this wasn't caught by tests

`TestHandleNodesLimit2000ColdMiss` only tests the cold-startup path
(cache nil → on-thread build → cache hit). It doesn't test the
TTL-expiry path (cache exists but stale → on-thread rebuild). A test
covering the latter would need to fast-forward time past the TTL, which
the existing fixture doesn't do.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
v3.8.1
2026-05-27 01:28:46 -07:00
Kpa-clawbot 48717aaccb ci: update go-server-coverage.json [skip ci] 2026-05-27 08:21:00 +00:00
Kpa-clawbot 13ae0dd6aa ci: update go-ingestor-coverage.json [skip ci] 2026-05-27 08:20:59 +00:00
Kpa-clawbot ec7ff4c597 ci: update frontend-tests.json [skip ci] 2026-05-27 08:20:58 +00:00
Kpa-clawbot 5d8d857cfb ci: update frontend-coverage.json [skip ci] 2026-05-27 08:20:57 +00:00
Kpa-clawbot 8d702bdfd9 ci: update e2e-tests.json [skip ci] 2026-05-27 08:20:55 +00:00
Kpa-clawbot 77d1925f30 Route view v2 — Tufte redesign (packet context, multi-path picker, mobile bottom-sheet, CB-preset live colors) (#1423)
# Route view v2 redesign

Fixes #1418, Fixes #1419, Fixes #1422

This is the route-view redesign that came out of a long iterative QA
cycle. The first commit (`a3c39636`) landed the v1 sidebar timeline +
multi-path baseline; this PR's second commit (`0e2e913f`) is the v2
polish covering packet context, multi-path picker, mobile bottom-sheet,
CB-preset live colors, and dozens of operator-driven UX fixes.

## The journey, in one line

> "The data is a sequence. Geography is annotation. The packet is the
cargo, the route is the road — show both."

## New surfaces

### 1. Packet context block (sidebar header)
Above the multi-path chip, a per-type fact list explaining **what** is
traveling. Operator was tired of "the route view shows the road but not
the cargo."

| Type | Chip | Facts |

|-------------|-----------------|---------------------------------------------------------|
| ADVERT | 📡 ADVERT | name · role · sig ✓ · self-reported GPS · pubkey
prefix |
| TXT_MSG | ✉ DM | src → dst · 🔒 encrypted |
| REQ/RESPONSE| 🔒/🔓 REQUEST/…| src → dst · 🔒 encrypted |
| GRP_TXT | # CHANNEL MSG | #channel · 🔓 decrypted · "…content preview…"
· sender |
| TRACE | ⌖ TRACE | Official: N hops · Observed: M |
| PATH | 🔀 PATH | src → dst (with "from payload" chip on SRC/DST rows) |

Sources merge `pkt.decoded_json` + `obs.decoded_json` (channel data
often lives at packet level) and fall back to byte-level `raw_hex`
parsing for encrypted DMs and unkeyed channel msgs.

### 2. Multi-path picker
The header lists every unique observer-path with `<count>/<total>` chip
+ hex hop string. Click a path → full-clear and redraw that path only
(Tufte v6's "replace + retain subpath weights"). "All" →
edge-deduplicated UNION view (each unique edge drawn once, stroke =
observer count, single accent color, no seq numbers because there's no
single ordering).

### 3. Deep-link URLs
`#/map?packet=<hash>&obs=<id>` — bookmarkable, shareable, the single
source of truth. sessionStorage flow removed. "Back to packet" preserves
the obs id.

### 4. Hop resolution
Priority: server `resolved_path` → shared `window.HopResolver` (same
resolver as packets page, observer-IATA-aware) → raw prefix. Eliminates
a whole class of "route view named hops differently than packet detail"
bugs.

### 5. Markers (v5/v6/v7)
- All markers same 22 px filled circle, seq number rendered **inside**
- SRC + DST get a 2 px hollow endpoint ring
- SRC = DST loop → **double concentric ring** (ring grammar extended, no
new glyph)
- Spider-fan within 14 px collisions (16 px arc, dashed hairline),
re-runs on `zoomend` only, debounced

### 6. CB preset live colors
- Each preset gets a `routeRamp` (5 stops): default/trit = viridis,
deut/prot = plasma, achromat = pure luminance
- `cb-presets.js` writes `--mc-rt-ramp-0..4` CSS vars; route reads them
via `getComputedStyle`
- `cb-preset-changed` + `theme-changed` listeners hot-recolor without
re-render

### 7. Desktop chrome
- **Resize handle** on right edge of sidebar (drag, persisted to
`localStorage["mc-rt-sidebar-width"]`)
- **Collapse button** = round chevron **centered on the right edge**
(Material/Drive style — not in the top-right corner, doesn't collide
with the close X)
- Collapsed = 36 px strip with rotated "ROUTE" label, expand on click

### 8. Mobile (bottom sheet)
- Anchored above bottom-nav (`bottom: 56px + safe-area-inset`)
- Collapsed = thin summary line `TYPE · N hops · X km · M obs` + hex
preview, tap chevron to expand to ~75 vh
- Drag-grip removed (conflicted with browser pull-to-refresh +
CoreScope's own pull-to-reconnect)
- Desktop collapse / resize affordances hidden on mobile (sheet is the
mobile collapse affordance)
- Map controls toggle floats top-right, panel collapses on route entry,
reachable via toggle click
- All three mobile detail panels (`pktRight`, `.slide-over-panel`,
`#mobileDetailSheet`) explicitly closed when entering route view

### 9. Map fit / centering
- Manual layer-children walk because `L.LayerGroup.getBounds()` doesn't
aggregate (only `FeatureGroup` does)
- Mobile padding: `paddingTopLeft: [30, 70]`, `paddingBottomRight: [30,
190]` to clear top-nav + sheet+nav stack
- Re-fits on: initial render, isolate, All, `window.resize` (iOS URL-bar
collapse)
- Staggered timers 0/200/600/1400 ms (and 2800 ms on initial render) to
survive layout settles

### 10. Hop drill-in refinements
- SNR sparkline suppresses connecting polyline when n < 3 (two points
implies a trend across time it can't represent — dots only)
- "Node details" link properly chip-styled with aria-label including
node name + route count

## Edge weight scales

| View                            | Range          |
|---------------------------------|----------------|
| Single-path                     | 5 px flat      |
| Multi-path interior             | 3..9           |
| Origin→hop1 / last-hop→dest     | proxy via max adjacent edge count |
| Union overlay                   | 2..8           |

Boundary edges (SRC→first hop, last hop→DST) used to render thin because
`edgeCounts` only tracks `path_json` transitions. Now they take the
strongest adjacent edge count as proxy (every observer who saw the
packet implicitly transited that boundary edge).

## Files

- **NEW** `public/route-tufte.js` (~1700 lines) — the route renderer +
sidebar
- **NEW** `public/route-tufte.css` (~750 lines) — all styling
- **MOD** `public/map.js` — async draw functions, deep-link loader,
`__mc_nodes` exposure, raw_hex extraction
- **MOD** `public/packets.js` — View Route → deep-link URL only, closes
all mobile panels
- **MOD** `public/cb-presets.js` — `routeRamp` per preset + CSS var
write
- **MOD** `public/index.html` — script + stylesheet tags

## Testing

Manually CDP-validated across desktop and mobile-emulator viewports for
every major change. Fixtures cover:
- ADVERT (4 hops, single-obs)
- DM (TXT_MSG, raw_hex parse)
- GRP_TXT (#test channel, decrypted text)
- PATH (operator's bug case)
- TRACE (3-hop)
- 1-hop edge case
- Multi-path (75-observer 4-hop with 47 unique paths)
- 32-hop stress
- Loop (SRC = DST)
- Bay Area dense cluster (spider-fan)

Per AGENTS.md net-new-UI exemption, no failing-test-first; existing
tests stay green. **TODO**: Playwright E2E follow-up PR.

## What's deferred to v2.1 / follow-ups

- **Glyph overlay on SRC marker** for packet type (e.g. 📡 corner glyph
on ADVERT marker, ⌖ on TRACE)
- **Per-hop SNR sparkline for TRACE packets** (their payload contains
real per-hop SNR contributions, distinct from observer-derived SNR)
- **GRP_TXT full content preview** (currently truncated at 80 chars;
could expand inline)
- **Playwright E2E test** covering the deep-link → isolate → All flow

## Screenshots

(would be useful here — CDP screenshots captured during dev show:
desktop with sidebar + multi-path picker, mobile with bottom sheet +
overlay toggle, isolated-path view, union view, spider-fan on Bay Area
cluster, packet context for each of the 5 main types)

## Operator's frustration patterns (lessons for next time)

1. **Browser-validate every UI change, not just compute state** —
CDP-screenshot before claiming a UI fix is done. Verifying
`display:none` resolves correctly is necessary but not sufficient; the
visual layout matters.
2. **Edge-deduplicated drawing beats per-path overlays** for union views
(Tufte v6) — operator's instinct was correct from the start.
3. **Material/Drive UI conventions exist** because they work — center
collapse handles on borders, don't pile them in corners.
4. **Mobile = different problem than desktop** — bottom-sheet, no
drag-grip near pull-to-refresh zone, asymmetric fitBounds padding,
redundant refits to survive iOS URL-bar collapse.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: openclaw-bot <bot@openclaw.local>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: corescope-bot <bot@corescope.local>
v3.8.0
2026-05-27 08:01:15 +00:00
Kpa-clawbot 306ac37ea0 ci: update go-server-coverage.json [skip ci] 2026-05-27 01:08:57 +00:00
Kpa-clawbot 50a1b1c6e8 ci: update go-ingestor-coverage.json [skip ci] 2026-05-27 01:08:56 +00:00
Kpa-clawbot 0c52cf663a ci: update frontend-tests.json [skip ci] 2026-05-27 01:08:55 +00:00
Kpa-clawbot be1b014269 ci: update frontend-coverage.json [skip ci] 2026-05-27 01:08:54 +00:00
Kpa-clawbot c796d48442 ci: update e2e-tests.json [skip ci] 2026-05-27 01:08:52 +00:00
Kpa-clawbot 0986caaa44 fix(#1412): customizer nodeColors stops force-overriding ROLE_COLORS — CB presets now actually propagate (#1414)
WIP — red commit only. Reproduces #1412.

## TDD red phase
`test-issue-1412-customizer-no-override.js` asserts that after
`MeshCorePresets.applyPreset('deut')` and a server-config push of legacy
`nodeColors`, `window.ROLE_COLORS.repeater === '#FE6100'`. On master
this
fails because `customize-v2.js:553` pushes server-config into the
`_roleOverrides` map, which the live getter prefers over CSS vars.

Green commit (customize-v2.js + customize.js fix) follows.

Refs #1412

---------

Co-authored-by: corescope-bot <bot@corescope.local>
2026-05-26 17:48:07 -07:00
Kpa-clawbot 89410d58b4 fix(#1413): nav-left + nav-stats overlap at vw~1200 — flex sizing fix (#1417)
## What

Fix the horizontal overlap between `.nav-more-btn` (in `.nav-left`) and
`.nav-stats` (in `.nav-right`) at viewport widths roughly 1101..1599px.
At vw=1200 the count number in the stats badge rendered on top of the
"More ▾" text.

## Root cause

`.top-nav` uses `display: flex; justify-content: space-between;` but had
**no column gap** between its children, and `.nav-links` had **no
flex-grow**. So `.nav-left` only consumed its content's intrinsic width
and `.nav-right` (with `flex-shrink: 0`) was free to abut it. Worse, the
Priority+ measurement loop in `app.js` (`applyNavPriority` → `fits()`)
compared intrinsic widths against `window.innerWidth` while `.top-nav {
overflow: hidden }` masked the actual collision — so the loop happily
declared "fits" while pixels overlapped.

CDP measurement on master at vw=1200 (`/#/packets`):

- `.nav-more-btn` rect: x=499..557 (w=58)
- `.nav-stats` rect: x=496..962 (w=466)
- Gap: **−60.7px** (overlapping)

Fix candidates tested via Chrome DevTools Protocol (`Runtime.evaluate` +
`Emulation.setDeviceMetricsOverride`) across vw=1101, 1200, 1366, 1440,
1600, 1920 (plus 768, 900, 1024, 1080, 1100, 1300, 1500, 1700, 1800 as a
sanity sweep). Winner:

```css
.top-nav   { column-gap: 16px; }
.nav-links { flex: 1 1 auto; min-width: 0; }
```

Per-viewport gap (`stats.left - more.right`) baseline → fix:

| vw   | baseline | fix      |
|------|----------|----------|
| 1101 | −144.0   | **16.0** |
| 1200 |  −60.7   | **16.0** |
| 1300 |    8.4   | **16.0** |
| 1366 |   64.2   | 64.2     |
| 1440 |    0.0   | **44.5** |
| 1600 |   24.2   | 24.2     |
| 1920 | more hidden (no overflow) — n/a | n/a |

Single-candidate variants (`.nav-left { flex: 1 1 auto }` alone,
`.top-nav { justify-content: space-between }` alone — already on, no
effect, `.nav-links { flex: 1 1 auto }` alone, margin/padding hacks on
`.nav-right`/`.nav-stats`) all still produced ≤8px gap at vw=1200. Only
the combo (column-gap on parent + flex-grow on `.nav-links`) cleanly
resolves all six required widths.

## TDD

Red commit: `3d374b4c93319805e89e46d8fdc8a8ea8c6c1479` (CI:
https://github.com/Kpa-clawbot/CoreScope/actions/runs/26482870401)

- `test-issue-1413-nav-overlap-e2e.js` — Playwright at vw 1101, 1200,
1366, 1440, 1600, 1920 on `/#/packets`. Asserts `.nav-more-btn.right + 8
<= .nav-stats.left` (when both visible) and that `.top-nav` does not
horizontally scroll. Wired into `.github/workflows/deploy.yml` alongside
the other `test-nav-*-e2e.js` entries.
- Red commit ships ONLY the test (+workflow line); CI fails on the
assertion at vw=1101..1300 and vw=1440 (gap below 8px threshold).
- Green commit applies the two CSS rules above and turns CI green.

## Manual verification

1. Open `http://analyzer-stg.00id.net/#/packets` in a desktop browser.
2. Resize the viewport to ~1200px wide.
3. Confirm the "More ▾" button and the stats badge are visibly separated
(≥16px gap) and the badge count is not stacked on the button text.
4. Repeat at 1101, 1300, 1440, 1600, 1920px — gap ≥16px at all widths
where stats is visible.
5. At ≤1100px confirm `.nav-stats` is still hidden (display:none,
unchanged).

## Scope guards

- No changes to the Priority+ algorithm (`applyNavPriority` / `fits()`
in `app.js`). #1391, #1311, #1139, #1148, #1102, #1055 logic untouched.
- No changes to the More dropdown (`position: fixed`, #1406).
- No changes to `.nav-left { overflow }` (#1405 stayed dropped).
- Mobile (<768px) hamburger layout unchanged.

Fixes #1413

---------

Co-authored-by: openclaw-bot <bot@openclaw.local>
2026-05-26 17:38:47 -07:00
Kpa-clawbot f72b1bd2ca fix(#1409): channels — stop force-enabling 'show encrypted' on every init (#1410)
## What

Delete the unconditional
`localStorage.setItem('channels-show-encrypted', 'true')` call (+
misleading "#1034 PR1: sectioned sidebar" comment) at
`public/channels.js:783-786`. The sectioned-sidebar grouping the comment
referenced was never implemented; in practice the call was
force-flipping the encrypted-visibility gate on every init so an
operator could never turn it off.

## Root cause

`channels.js` init ran:

```js
var showEncrypted = true;
try { localStorage.setItem('channels-show-encrypted', 'true'); } catch (e) {}
```

unconditionally on every load. The `loadChannels()` reader at line ~1563
(`localStorage.getItem('channels-show-encrypted') === 'true'`) then sent
`includeEncrypted=true` on the `/api/channels` call, so the server
returned all 246 encrypted placeholder channels alongside the 19 real
ones — 265 rows flooding the sidebar with no UI control to suppress.

Verified via CDP on staging:
- `localStorage['channels-show-encrypted']` was always `"true"` after
page load.
- `GET /api/channels` → **19** entries (default — encrypted excluded).
- `GET /api/channels?includeEncrypted=true` → **265** entries (246
encrypted).
- Manually `removeItem('channels-show-encrypted')` + reload → list
dropped to 19.

Confirmed the force-set was the only gate driving the flood.

## TDD

- RED commit `a71cecbc` — `test-issue-1409-no-encrypted-flood.js`
source-greps `public/channels.js` for the forbidden literal
`setItem('channels-show-encrypted', 'true')`. Asserts no match. Fails on
master.
- GREEN commit `14281b63` — delete the 2 lines + rewrite comment. Test
passes.

Tests:

```
$ node test-issue-1409-no-encrypted-flood.js
Issue #1409 — no force-enable of channels-show-encrypted
   channels.js does NOT unconditionally setItem(channels-show-encrypted, true)
   channels.js still reads channels-show-encrypted (toggle gate preserved)
2 passed, 0 failed
```

## Manual verification

- After fix, default `localStorage.getItem('channels-show-encrypted')`
is `null` on first load.
- `loadChannels()` reader returns `false`, so `includeEncrypted` is
omitted from the API call → server returns the 19 real channels only.
- Existing reader is preserved, so a future user-facing toggle that
writes the flag will continue to work.

## Out of scope (follow-ups)

- "Show encrypted" header toggle UI — issue acceptance criteria mentions
it as optional; not added here.
- Sectioned-sidebar grouping of encrypted channels (#1034 PR1 design) —
separate issue.
- Cap/collapse behavior when toggle is ON — separate issue.

Fixes #1409

---------

Co-authored-by: openclaw-bot <bot@openclaw.local>
2026-05-26 17:23:02 -07:00
Kpa-clawbot 037a54d9c2 ci: update go-server-coverage.json [skip ci] 2026-05-27 00:03:00 +00:00
Kpa-clawbot b6395afbc6 ci: update go-ingestor-coverage.json [skip ci] 2026-05-27 00:02:59 +00:00
Kpa-clawbot f799bc106c ci: update frontend-tests.json [skip ci] 2026-05-27 00:02:58 +00:00
Kpa-clawbot 5a962f8d0b ci: update frontend-coverage.json [skip ci] 2026-05-27 00:02:57 +00:00
Kpa-clawbot 0aa67b2d61 ci: update e2e-tests.json [skip ci] 2026-05-27 00:02:56 +00:00
Kpa-clawbot 52b6dd82ac fix(#1407): cb-preset propagation via live ROLE_COLORS getter + per-role text color for WCAG AA (#1408)
WIP — RED commit only. Tests demonstrate two bugs from #1407:

1. `window.ROLE_COLORS` is a static literal (legacy April palette), not
synced to `--mc-role-*` CSS vars.
2. Achromat preset pairs `#1a1a1a` text with 3 dark grays → WCAG 1.4.3
fails (1.27 / 2.55 / 4.43).

Expect CI red on `test-issue-1407-cb-preset-propagation.js` assertion
failures (not compile errors). GREEN follows.

Refs #1407

---------

Co-authored-by: openclaw-bot <bot@openclaw.local>
2026-05-26 16:42:47 -07:00
Kpa-clawbot 060e0d5aa1 ci: update go-server-coverage.json [skip ci] 2026-05-26 23:23:30 +00:00
Kpa-clawbot 0aa70ca9c6 ci: update go-ingestor-coverage.json [skip ci] 2026-05-26 23:23:29 +00:00
Kpa-clawbot 217d23b7bd ci: update frontend-tests.json [skip ci] 2026-05-26 23:23:28 +00:00
Kpa-clawbot a544283661 ci: update frontend-coverage.json [skip ci] 2026-05-26 23:23:27 +00:00
Kpa-clawbot 45085b9a59 ci: update e2e-tests.json [skip ci] 2026-05-26 23:23:26 +00:00
Kpa-clawbot 9b0a4ee054 fix(nav): .nav-more-wrap contain:layout — open dropdown inflated parent flex line, clipped nav offscreen (#1406)
ACTUAL root cause of the recurring nav-vanishing bug, validated live via
Chrome CDP probe on staging at vw=1030.

## What happens

When the More dropdown opens:
- BEFORE: nav_links.y = 2.67, nav_left.scrollHeight = 47, nav visible 
- OPEN: nav_links.y = -46.67, nav_left.scrollHeight = 279, nav clipped
offscreen 

The .nav-more-menu is position:absolute but its content extents inflate
.nav-more-wrap.scrollHeight. .nav-left { display:flex;
align-items:center } then centers a 279px content line in a 52px
container, putting everything above the visible band.

## Fix

Add contain:layout to .nav-more-wrap — isolates its layout box from the
parent flex calculation. No more bubble-up.

CDP verification with the fix applied: dropdown opens, all 6 items
render at proper y (56, 93, 130, 166, 203, 240), nav_links_y stays at
2.67, nav_left.scrollHeight stays at 47.

## Why prior 22 fixes didn't catch it

Every prior fix treated symptoms — Priority+ algorithm tweaks, overflow
flag toggles, min-height drops, etc. None instrumented the CLOSED→OPEN
state transition that reveals the flex-line bug. Required Chrome
DevTools Protocol on a real broken viewport to see the inflate happen
live.

Fixes #1406 and likely supersedes #1391, #1396, #1400, #1404.

Co-authored-by: openclaw-bot <bot@openclaw.local>
2026-05-26 23:03:32 +00:00
Kpa-clawbot 080f2c6609 ci: update go-server-coverage.json [skip ci] 2026-05-26 19:56:56 +00:00
Kpa-clawbot 3095668347 ci: update go-ingestor-coverage.json [skip ci] 2026-05-26 19:56:55 +00:00
Kpa-clawbot 51c5ed9345 ci: update frontend-tests.json [skip ci] 2026-05-26 19:56:54 +00:00
Kpa-clawbot 1bfbbd6bb2 ci: update frontend-coverage.json [skip ci] 2026-05-26 19:56:53 +00:00
Kpa-clawbot b3b81a57ba ci: update e2e-tests.json [skip ci] 2026-05-26 19:56:52 +00:00
Kpa-clawbot ae77d58ec5 fix(#1403): drop .nav-left overflow:hidden — root cause of nav vanishing + truncated More dropdown (#1405)
Root cause of the recurring nav-vanishing family of bugs — confirmed
live via operator console probe at vw=1030 on /#/channels (also
reproduces on /#/home, /#/packets, all routes).

## Symptoms

1. All `.nav-links` (Home, Packets, Map, Live, Channels, Nodes) and
brand + More button render OFFSCREEN above the visible top-nav band.
`.nav-left` reports y=0..52 but every child reports y=-47.5.
2. More dropdown when opened shows only ONE item ("Tools") instead of
the 6 expected (Channels, Tools, Observers, Analytics, Perf, Audio Lab).

## Root cause

`.nav-left { overflow: hidden }` at `public/style.css:509`. With flex
children whose effective layout exceeds the container box, Firefox clips
children to negative y. The same `overflow: hidden` ALSO clips the
descendant `.nav-more-menu` dropdown contents.

## Fix

Drop `overflow: hidden` from `.nav-left`. The original
horizontal-overflow guard from #1066 is preserved at the `.top-nav`
level (which still has `overflow: hidden`).

## Verification

Operator console probe after applying the same `overflow: visible`
in-page:
- All 6 visible nav links render at y >= 0 inside the top-nav.
- More dropdown contains all 6 expected items (Channels, Tools,
Observers, Analytics, Perf, Lab).
- Both bugs collapse into ONE root cause.

## Why prior fixes didn't catch this

- #1400 fixed `.nav-link { min-height: 48px }` overflow — reduced
children from 56px to 47px tall. Helped slightly but didn't address the
`.nav-left { overflow: hidden }` interaction.
- #1391, #1394 fixed the active-pill-in-overflow algorithm. Different
layer.
- #1311, #1148, #1106, #1102, #1097, #1067, #1055 — every prior
Priority+ fix treated overflow as an algorithmic question, never as a
CSS clipping bug at the container level.

22nd nav fix in this saga. This one targets the actual cause.

Refs #1391, #1396, #1400. Operator probe transcript available on
request.

Fixes #1403

Co-authored-by: openclaw-bot <bot@openclaw.local>
2026-05-26 19:37:51 +00:00
Kpa-clawbot 46424909cf ci: update go-server-coverage.json [skip ci] 2026-05-26 18:29:09 +00:00
Kpa-clawbot 7b50be14fc ci: update go-ingestor-coverage.json [skip ci] 2026-05-26 18:29:08 +00:00
Kpa-clawbot a665e065bf ci: update frontend-tests.json [skip ci] 2026-05-26 18:29:07 +00:00
Kpa-clawbot c32cc06de4 ci: update frontend-coverage.json [skip ci] 2026-05-26 18:29:06 +00:00
Kpa-clawbot 3711cc6fed ci: update e2e-tests.json [skip ci] 2026-05-26 18:29:05 +00:00
Kpa-clawbot 7e492a71a0 fix(#1400): root cause of recurring nav-vanishing — min-height:48px overflowed 52px top-nav, clipped link strip above viewport (#1401)
**RED commit phase** — TDD failing test for #1400. Green fix incoming
next push.

See full PR body on ready-for-review.

Fixes #1400

---------

Co-authored-by: openclaw-bot <bot@openclaw.local>
2026-05-26 11:07:17 -07:00
Kpa-clawbot d88cf28a80 ci: update go-server-coverage.json [skip ci] 2026-05-26 16:40:01 +00:00