diff --git a/.planning/quick/260729-fw7-improve-mesh-message-hop-graphic-animati/260729-fw7-PLAN.md b/.planning/quick/260729-fw7-improve-mesh-message-hop-graphic-animati/260729-fw7-PLAN.md new file mode 100644 index 00000000..e8958ce7 --- /dev/null +++ b/.planning/quick/260729-fw7-improve-mesh-message-hop-graphic-animati/260729-fw7-PLAN.md @@ -0,0 +1,151 @@ +--- +phase: quick-260729-fw7 +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - neode-ui/src/views/mesh/HopVizModal.vue + - neode-ui/src/views/Mesh.vue + - neode-ui/src/views/mesh/mesh-styles.css +autonomous: true +requirements: [QUICK-FW7] + +must_haves: + truths: + - "Clicking a mesh message's transport pill (or the ⋯ button) opens a hop-route modal that is visually balanced on desktop — the hop chain fills the modal width, endpoint nodes are prominent, and the modal is no longer a cramped 420px card with tiny emoji + dot row" + - "On narrow viewports (phone width) the hop chain renders VERTICALLY — sender at top, relays stacked, recipient at bottom — with the packet animation traveling top-to-bottom, nothing overflowing or wrapping awkwardly" + - "The visualization uses Archipelago's design language: glass panel, dark-only palette, per-transport accent colors (meshcore orange #fb923c, meshtastic mint #3eb489, reticulum blue #60a5fa, lora amber #f59e0b, fips violet #a78bfa, tor indigo #818cf8), EQ-segment-style bars echoing ScreensaverRing, node glow, staggered reveal, animated packet traveling the path" + - "prefers-reduced-motion disables all looping animations (existing behavior preserved)" + - "Tor / FIPS / unknown-transport cases still render their distinct shapes (3 anonymous relays / direct P2P / not recorded), and SNR/RSSI + E2E/delivery metadata still display" + artifacts: + - "neode-ui/src/views/mesh/HopVizModal.vue — new self-contained modal component (Teleport to body) with scoped styles" + - "neode-ui/src/views/Mesh.vue — inline hop-viz modal markup replaced by " + - "web/dist/neode-ui/ — rebuilt bundle containing the new component's class strings" + key_links: + - "Mesh.vue transport pill / ⋯ button click → hopVizMsg → renders" + - "HopVizModal derives accent color from msg.transport, matching the pill colors in mesh-styles.css lines 168-173" +--- + + +Redesign the mesh-message hop visualization modal in neode-ui: properly sized and balanced on desktop, vertical stacked layout on mobile, and fully on-brand (glass, per-transport accents, EQ-segment motif from ScreensaverRing, animated packet travel, node glow, staggered reveal). + +User feedback (verbatim): "please make the hop graphic and animation on mesh messages much better balanced, the desktop one is very small and it doesn't work on mobile where it should be vertical, and make it much more archipelago style and branded, make it beautiful." + +Purpose: The current hop viz (inline in Mesh.vue lines ~2627-2678, styles in mesh-styles.css lines ~597-637) is a cramped horizontal flex row inside a 420px modal — tiny emoji endpoints, a dashed border with blinking `•` dots, no mobile handling. It reads as an afterthought, not an Archipelago feature. + +Output: New `HopVizModal.vue` component wired into Mesh.vue, old inline markup and `.mesh-hopviz-*` CSS removed, rebuilt bundle in web/dist/neode-ui/ verified to contain the new strings. + + + + +**Current implementation (read all of these first):** +- `neode-ui/src/views/Mesh.vue` lines ~1291-1310 (hopVizMsg / hopVizPeer / hopVizHops state), lines ~2340-2350 (transport pill + ⋯ button that set `hopVizMsg`), lines ~2627-2678 (the inline Teleported modal to replace). Also `transportLabel()` and `signalQualityLabel()` helpers used by the modal — grep for them in Mesh.vue. +- `neode-ui/src/views/mesh/mesh-styles.css` lines ~160-175 (transport pill accent colors + `.mesh-chat-e2e`), ~572-584 (`.mesh-transport-modal-backdrop`, `.mesh-transport-modal`, title/sub/cancel — SHARED with the send-transport and image-quality modals, do not break them), ~597-637 (`.mesh-hopviz-*` rules to delete/migrate). +- `neode-ui/src/components/ScreensaverRing.vue` — the brand EQ-segment motif: thin 4px rounded bars, white gradient fill, staggered `scaleY` pulse keyframes. Echo this visual language for relay-node markers. +- `neode-ui/tailwind.config.js` — glass tokens (glass-dark/glass-border/shadow-glass), fonts: `Montserrat` = `font-archipelago` header font; app is dark-only. +- Global focus glow + accent used app-wide: orange `rgba(251,146,60,…)` (#fb923c). + +**Transport accent map (must match pill colors):** meshtastic `#3eb489`, meshcore `#fb923c`, reticulum `#60a5fa`, lora `#f59e0b`, fips `#a78bfa`, tor `#818cf8`. + +**Data realities:** For LoRa transports only a hop COUNT is known (`peer.hops`, 0 or 0xff/null = direct), not per-relay identities — render count relays as anonymous branded markers. Tor = fixed "3 anonymous relays" shape. FIPS = direct P2P. `null` transport = not recorded. SNR/RSSI are current link readings (keep the existing disclaimer note). + +**Project rules that apply:** +- Modals MUST `` with full-screen backdrop (already true — preserve it). +- Frontend build can silently no-op: after `npm run build`, grep the built bundle in `web/dist/neode-ui/` for new strings before claiming done. +- Commit the code changes when they work; push via `git push gitea-ai main`. Stage explicitly by path (`git add `), never `git add -A` — other agents may share the tree. +- Do not deploy to any node; this rides the normal dev-pair → OTA pipeline later. + + + + + + + Task 1: Build HopVizModal.vue — branded, balanced desktop hop visualization + neode-ui/src/views/mesh/HopVizModal.vue, neode-ui/src/views/Mesh.vue, neode-ui/src/views/mesh/mesh-styles.css + +Create `neode-ui/src/views/mesh/HopVizModal.vue` (script setup, TypeScript) and move the hop-viz modal out of Mesh.vue into it. + +Component contract: +- Props: `msg: MeshMessage` (import type from `../../types/api` or wherever Mesh.vue imports it), `peer: MeshPeer | null`, plus the two label strings Mesh.vue already computes (`transportLabel` result and `signalQualityLabel` result) OR import/reuse those helpers if they are importable; if they are local functions in Mesh.vue, pass computed strings as props — do NOT duplicate logic. +- Emits: `close`. +- Template: `` wrapping a full-screen backdrop (reuse `.mesh-transport-modal-backdrop` class so backdrop behavior stays consistent) with `@click.self="emit('close')"`, containing a `glass-card` panel. + +Visual redesign (all styles SCOPED in the component; delete the old `.mesh-hopviz-*` rules from mesh-styles.css lines ~597-637 — but leave `.mesh-transport-modal-backdrop`, `.mesh-transport-modal`, `.mesh-transport-title/sub/cancel` untouched since the send-transport and image-quality modals still use them; the new panel should use its own width class, wider than 420px — target `min(560px, 94vw)` on desktop): + +1. **Header:** transport-colored title using the Montserrat brand font (`font-family: 'Montserrat', sans-serif` or the `font-archipelago` utility), e.g. "MeshCore route", with the existing You → peer subtitle. Derive an `--hop-accent` CSS custom property on the panel root from `msg.transport` using the exact pill color map (meshtastic #3eb489, meshcore #fb923c, reticulum #60a5fa, lora #f59e0b, fips #a78bfa, tor #818cf8, fallback rgba(251,146,60) orange). All accents below use `var(--hop-accent)`. + +2. **Endpoint nodes (You / peer):** substantial circular medallions (~64-72px) instead of bare emoji — island glyph 🏝️ centered inside a ring of 12-16 EQ-style segments (thin rounded bars radiating like a compact ScreensaverRing — reuse its technique: absolutely-positioned bars, `transform: rotate(deg) translateY(-radius)`, staggered `scaleY` pulse animation, white-to-transparent gradient tinted with the accent). Soft accent glow behind each medallion (`box-shadow: 0 0 24px color-mix(...)` or an rgba shadow). Node name below in white 600-weight, ellipsized. + +3. **Path between endpoints:** replace the dashed-border + blinking `•` row with a proper track: a horizontal line/gradient in the accent color connecting the medallions, with: + - Relay markers for each hop (LoRa transports: `Math.min(hops, 6)` markers; Tor: exactly 3 with a 🧅/anonymous treatment; FIPS: no relays, a single direct link) rendered as small EQ-segment clusters or glowing accent dots (~10-14px) sitting ON the track, each with its own subtle pulse, staggered. + - An animated packet: a small bright dot/comet (accent color, blurred glow trail) traveling from sender to recipient along the track on an infinite ~2s loop. Use a CSS keyframe translating along the track container (like the existing `mesh-hopviz-travel` sweep but as a discrete glowing packet, not a background sheen). + - Label under/over the track: "direct radio link" / "N hops" / "3 anonymous relays" / "direct peer-to-peer" / "transport wasn't recorded" — preserve the existing per-transport template branches and copy. + - Staggered entrance: sender medallion, then track+relays, then recipient fade/slide in (keep the existing appear pattern, ~0.05/0.35/0.65s delays). + +4. **Metadata footer:** keep the SNR/RSSI signal row (LoRa only, with the existing "current link readings" disclaimer note) and the E2E / delivered ✓✓ / timestamp row, restyled as small glass chips consistent with `.mesh-transport-meta` sizing. Keep the Close button (`.mesh-transport-cancel` class is fine). + +5. **Reduced motion:** wrap ALL looping animations (packet, segment pulses, relay pulses) and entrance animations in the component's own `@media (prefers-reduced-motion: reduce)` block that disables them — the old CSS did this; the new component must too. + +Wire-up in Mesh.vue: import HopVizModal, replace the inline `` block at lines ~2627-2678 with ``. Keep `hopVizMsg`/`hopVizPeer`/`hopVizHops` state in Mesh.vue (or move `hopVizHops` into the component — it only needs `peer.hops`; prefer moving it in to shrink Mesh.vue). Do not touch the transport pill / ⋯ button triggers. + + + cd /home/archipelago/Projects/archy/neode-ui && npx vue-tsc --noEmit 2>/dev/null || npm run build + + HopVizModal.vue exists with the medallion + track + packet design, Mesh.vue renders it in place of the inline modal, old `.mesh-hopviz-*` rules removed from mesh-styles.css, other transport modals' shared classes untouched, type-check/build passes. + + + + Task 2: Mobile vertical layout + neode-ui/src/views/mesh/HopVizModal.vue + +Add responsive behavior inside HopVizModal.vue's scoped styles. At narrow widths (`@media (max-width: 560px)` — pick the breakpoint so a typical phone portrait always gets it): +- The chain flips to a COLUMN: sender medallion at top, vertical track with relay markers stacked below it, recipient medallion at bottom. Implement so the same DOM works in both orientations (flex-direction column + a track that switches from horizontal line to vertical line), rather than duplicating markup. +- The packet animation travels TOP-TO-BOTTOM along the vertical track (a second keyframe or a transform-based animation that follows the flex axis). +- Medallions may shrink slightly (~56px) but stay prominent; names and hop label must not truncate mid-word or overflow the panel; panel uses near-full width (`width: 94vw`) with comfortable vertical padding, and the whole modal scrolls (`max-height: 90vh; overflow-y: auto`) if metadata pushes it tall. +- Entrance stagger and reduced-motion handling apply identically in vertical mode. +Sanity-check both orientations in the browser via the dev preview (`npm run dev`, viewport toggling in devtools) if a display is available; otherwise rely on the CSS being purely breakpoint-driven and symmetric. + + + cd /home/archipelago/Projects/archy/neode-ui && grep -q "max-width: 560px" src/views/mesh/HopVizModal.vue && grep -qi "column" src/views/mesh/HopVizModal.vue + + Below the breakpoint the hop chain renders vertically (sender top → recipient bottom) with the packet traveling downward; no overflow; desktop layout unchanged above the breakpoint. + + + + Task 3: Build, verify bundle, commit + web/dist/neode-ui/ (build output), neode-ui/src/views/mesh/HopVizModal.vue + +1. `cd neode-ui && npm run build` (outputs to `web/dist/neode-ui/`). +2. Per CLAUDE.md, prove the build actually picked up the change: grep the built JS/CSS bundle for a new unique string from the component (e.g. a distinctive class name like `hopviz-medallion` or `hopviz-packet` — whatever class names Task 1 used; pick one that did not exist before): `grep -rl "hopviz-packet" web/dist/neode-ui/assets/` (adjust the token to the actual class name). It MUST match; if it doesn't, the build silently no-opped — investigate before proceeding. +3. Also confirm the OLD inline markup is gone from the bundle source of truth: `grep -c "mesh-hopviz-chain" neode-ui/src/views/Mesh.vue` returns 0. +4. Commit the code changes only (docs/planning files are committed by the orchestrator): `git add neode-ui/src/views/mesh/HopVizModal.vue neode-ui/src/views/Mesh.vue neode-ui/src/views/mesh/mesh-styles.css web/dist/neode-ui` — stage exactly these paths, never `git add -A`. Check `git status` first for other agents' unrelated changes and leave them alone. Commit message: `feat(mesh): redesign hop-route visualization — branded, animated, vertical on mobile` ending with the `Co-Authored-By: Claude Fable 5 ` trailer. Push: `git push gitea-ai main`. + + + grep -rl "hopviz" /home/archipelago/Projects/archy/web/dist/neode-ui/assets/ | head -1 && cd /home/archipelago/Projects/archy && git log --oneline -1 | grep -qi "hop" + + Fresh build in web/dist/neode-ui/ contains the new component's class strings, Mesh.vue no longer contains the old inline hopviz markup, and the change is committed and pushed via gitea-ai. + + + + + +- `npm run build` succeeds in neode-ui/; bundle in web/dist/neode-ui/ contains a new hopviz class string (silent-no-op guard). +- Old `.mesh-hopviz-*` rules removed; `.mesh-transport-modal-backdrop` / `.mesh-transport-option` / image-quality modal styles untouched (grep mesh-styles.css and open the send-transport modal path in code to confirm shared classes intact). +- Modal still Teleports to body with full-screen backdrop. +- All five transport branches render: meshcore/meshtastic/reticulum (hop count), tor (3 relays), fips (direct), null (not recorded). +- prefers-reduced-motion block present in the new component. +On the dev preview (:8100 or `npm run dev`), open Mesh chat, click a message's transport pill: desktop shows the wide balanced medallion+packet layout; shrinking the window below the breakpoint flips it vertical. Confirm it "feels Archipelago" — glass, accent glow, EQ-segment motif. + + + +- Desktop hop modal is visually balanced: ~560px panel, prominent glowing endpoint medallions with EQ-segment rings, accent-colored track with animated traveling packet and staggered relay markers. +- Mobile (< 560px) renders the chain vertically top-to-bottom with the packet traveling downward; nothing overflows. +- Per-transport accent colors match the existing transport pill colors exactly. +- Reduced-motion users get a static layout. +- Built bundle verified to contain the new strings; code committed and pushed via gitea-ai. + + + +Executor commits code only. On completion, note results for the orchestrator; no SUMMARY.md required for quick mode unless the orchestrator asks. +