UX critique + next-gen sketches (geospatial-ux-ui role) (Stac Manager Nextgen)

**Note** from Bead: Stac Manager Nextgen · [canonical source](https://redfish.acequia.io/guerin/.agents/b73189b7-e605-4473-9789-20bcff57ba35/2026-06-21/notes/00-ux-critique-and-sketches.md) · session 2026-06-21 · discussion: Talk: Stac Manager Nextgen

> Prepared by the UX/UI senior-designer role binding the [`f80f1929` geospatial-ux-ui](https://redfish.acequia.io/guerin/.agents/f80f1929-57ee-43d6-b6c1-4ef594f5fa8e/) grammar. > Method: **sketch-ux** (Buxton divergence) per feedback_ux-sketch-to-foreground-questions — these are deliberately rough, alternative-forward sketches, NOT a mockup to build verbatim.

## (A) Critique of the current `santafe.live/STAC-manager.html` A competent **engineering** scaffold and a weak **browsing experience**. It proves the data pipeline; it does not make catalogs feel good to explore. **Keep:** - **The loader is the real asset.** `stac-loader.js` is catalog-of-catalogs aware — walks `child`/`item` links recursively into a uniform `{roots:[node]}` union. Don't touch it. - **Clean component seams.** `<stac-layer-tree>` + `<stac-timeline>` talk by events (`item-select`, `item-visibility`, `item-opacity`, `seek`) — swappable. - **Two honest timeline modes** — `multichannel` (lane per channel) and `heatmap`. The lane-per-collection idea is the seed of catalog comparison. - **STAC-aware detail** — already renders `view:azimuth`, `msb:horizontal_fov_deg`, `forage:vo`, and inline video/image assets. **Weak:** 1. **Contradicts our own design system.** The picker should be `<stac-menu>` + `stac-registry.json` (both exist on disk next to the app) but the app uses a **raw comma-separated `<input>` of catalog.json URLs** (`STAC-manager.html:41,128-132`). Biggest miss: it makes the user *paste URLs* instead of *picking* catalogs — exactly the storage-locations gap. 2. **No `--acq-*` tokens.** Hardcoded hex; off-grammar. 3. **Layer-tree is a checklist, not a catalog.** No thumbnails, no asset-type glyphs, no catalog identity — 59 camera frames and an aircraft track render as identical text rows. 4. **Half-present interaction grammar.** `item-select` flies the map but there's no `h` fly-to / `t` time-span keys, no per-row ⌖/◴ affordances, no context menu, nothing for touch. 5. **Space and time divorced.** `seek` only writes a status string (`:122`) — it does NOT cross-filter the map/tree. For temporal catalogs that's the whole point. 6. **Undifferentiated markers.** Every item is the same `#4fc3f7` dot — camera frame, aircraft, perimeter all identical; no per-catalog color/glyph, no track lines, no clustering. 7. **No "catalog as a thing you own."** No notion of where a catalog lives, no save target, no recents, no add/remove. Stateless URL viewer. **Net:** a *viewer of one merged tree* when the brief wants a *browser of many distinct catalogs across storage locations*. Redesign = (a) restore catalog identity, (b) wire space↔time↔tree cross-filtering, (c) promote "storage location" to first-class.

## (B) Four rough sketches — different organizing ideas Each foregrounds one design question. Not mutually exclusive; the point is to choose the *primary* metaphor. ### Sketch 1 — "Catalog Shelf" (catalog-first, gallery of catalogs) ``` ┌──────────────────────────────────────────────────────────────────────┐ │ STAC [ + Add storage ▾ ] search across catalogs… 🔍 │ ├──────────────────────────────────────────────────────────────────────┤ │ YOUR CATALOGS (cards, not a tree) │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ ▦▦▦ thumb │ │ ✈ track │ │ ▦ frames │ │ ◇ perims │ │ │ │ Treehouse │ │ AIR7 ADS-B │ │ AlertWest │ │ EGP heat │ │ │ │ cam · 1.2k │ │ 1 track │ │ 8 cams·40k │ │ 12 polys │ │ │ │ santafe.live│ │ local:disk │ │ simtable │ │ egp mission │ │ │ │ ▸ Jun 3–21 │ │ ▸ Jun 14 │ │ ▸ live │ │ ▸ Jun 18 │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │ click a card → it "opens onto" the map+timeline below (drill-in) │ ├──────────────────────────────────────────────────────────────────────┤ │ [ map preview strip + mini-timeline of the hovered/open catalog ] │ └──────────────────────────────────────────────────────────────────────┘ ``` **Question: is the top-level unit a *catalog* or an *item*?** Home = gallery of catalogs; each card has identity (glyph by dominant asset type, count, time-extent, **storage badge**, thumbnail). Browse *which catalog*, then drill in. - *Alt A:* cards drill into full-screen map+timeline (catalog = a "world" you enter). - *Alt B:* cards become a persistent rail; clicking toggles the catalog *into* a shared map (compare mode). ### Sketch 2 — "Map-first, catalogs as toggleable layers" (incident-viewer lineage) ``` ┌───────────────┬──────────────────────────────────────────────────────┐ │ CATALOGS │ │ │ ☑ Treehouse ⌖◴│ ░░░ MAP (3D/2D) ░░░ │ │ └ frames │ ✈──────✈ (track, colored per-catalog) │ │ ☑ AIR7 ✈ ⌖◴│ ● ● ● (camera frames, glyph + color) │ │ ☐ AlertWest⌖◴│ ◇ (perimeter) │ │ ☑ EGP heat ⌖◴│ │ │ [+ storage ▾] │ right-click row/marker → Fly-to · Time-span · Props │ ├───────────────┴──────────────────────────────────────────────────────┤ │ ◀ ░░▓▓░ Treehouse ▓▓░░ ─── ✈ AIR7 ▓ ─── ░ EGP ░ ▶ [⏮ ▶ ⏭] T=14:32Z │ │ playhead scrubs → map shows only assets live at T (space↔time link) │ └───────────────────────────────────────────────────────────────────────┘ ``` **Question: is each catalog a *layer you blend on one map* (compare-by-overlay) or a *context you switch between*?** Closest to the shipped incident-viewer grammar; adds the mandated ⌖/◴ affordances + context menu + `h`/`t` keys, per-catalog color, and **playhead cross-filtering** (fixes weakness #5). Strong for comparison; weaker on catalog identity. - *Alt A:* auto-assigned catalog color; tree header shows swatch + storage badge. - *Alt B:* solo/mute per catalog (audio-mixing metaphor) for fast A/B. ### Sketch 3 — "Timeline-first / mission-tape" (temporal browsing as the spine) ``` ┌──────────────────────────────────────────────────────────────────────┐ │ catalogs ▾ storage ▾ window: Jun 14 12–18Z │ ├──────────────────────────────────────────────────────────────────────┤ │ Treehouse ▏▕▎▏▕ ▎▏ ▕▎▏ frames lane │ │ AIR7 ✈ ▏━━━━━━━━━━━━━━━━▏ continuous track lane │ │ AlertWest ▕▎▏▎ ▏▕ ▎ ▏▕ ▎ ▏ frames lane │ │ EGP FMV ▏███▏ ▏██▏ clips (hover→scrub thumb) lane │ │ └──────────▲ playhead 14:32Z──────────────────────────┘ │ ├───────────────────────────────────┬────────────────────────────────────┤ │ MAP at playhead (what's live now) │ PREVIEW of focused item (img/vid) │ └───────────────────────────────────┴────────────────────────────────────┘ ``` **Question: is the user mainly asking "what happened *when*?" not "what's *where*?"** Promote the multichannel timeline (exists) to the primary surface, one lane per catalog; map+preview are consequences of the playhead. Best for incident review / synchronized playback. Risk: spatial browsing feels secondary. - *Alt A:* draggable/reorderable lanes; pinch-zoom the window (extract the shared timeline component). - *Alt B:* tape transport (⏮▶⏭) stepping item-to-item vs free scrub. ### Sketch 4 — "Three-pane comparison browser" (catalogs side-by-side) ``` ┌──────────────────────────────────────────────────────────────────────┐ │ Compare catalogs: [Treehouse ▾] | [AIR7 ✈ ▾] | [+ add pane] │ ├──────────────────┬──────────────────┬──────────────────────────────────┤ │ ▦ Treehouse │ ✈ AIR7 ADS-B │ shared linked map + timeline │ │ thumb grid │ track + ticks │ (same viewport & playhead │ │ scrubbable │ alt/speed chart │ drive all panes) │ │ santafe.live │ local:disk │ storage badge per pane │ └──────────────────┴──────────────────┴────────────────────────────────────┘ ``` **Question: is the core job *comparing* two catalogs (does the camera see what the aircraft was over)?** Each pane gets a **type-specific renderer** (thumb grid / track+telemetry chart / polygon list) but they **share one map + one playhead**. Foregrounds heterogeneity. Risk: complex, screen-hungry. - *Alt A:* linked (shared time/space) vs independent panes. - *Alt B:* "diff" affordance — items in A with no spatial/temporal match in B.

**Recommendation to provoke the choice:** Sketch 1 (Shelf) as the *home/entry* + Sketch 2 (map-first layers) as the *drill-in workspace* compose naturally. 3 and 4 are specialist modes. **The fork to settle first: catalog-first (1/4) vs item-on-shared-surface (2/3).** The v0 reference build implements 1→2 to make the choice tangible.

## (C) Storage-locations UX Generalizes the EGP bookmarklet's Settings→Storage (one path abstraction, two backends: local folder via File System Access API, or WebDAV+Bearer) and the `<stac-menu>`/`stac-registry.json` picker. A **storage location** becomes a first-class noun — a place you *browse catalogs from* AND *save into*. Model: `StorageLocation = { name, kind:'webdav'|'http'|'local'|'opfs', root, auth?, caps:[read|write] }`. A catalog is *found within* a location; assets are *saved into* one. "Path = address, backend = swappable transport" (project_uri-bind-mount). ``` ┌──────────────────────────────────────────────────────────────────────┐ │ ⛁ Storage locations [+ Add ▾] │ ├──────────────────────────────────────────────────────────────────────┤ │ ● santafe.live webdav · 🔑 token read ✓ write ✓ default │ │ catalogs here: santafe-history (59) · dev-stac (4 sub-catalogs) │ │ ● This computer ▸ Incidents/ local folder read ✓ write ✓ │ │ ○ simtable.acequia.io webdav · 🔑 token read ✓ write ✓ │ │ ○ + ArcGIS World_Imagery http (basemap) read ✓ │ │ Save new products into: [ This computer ▸ Incidents/ ▾ ] │ └──────────────────────────────────────────────────────────────────────┘ [+ Add ▾] → ( ○ Local folder… FSA pick ○ WebDAV URL+token ○ Catalog URL ) ``` - **`[+ Add storage]`** replaces the raw URL `<input>`. Adding a location lists its root for `catalog.json`s and **populates the shelf** — storage locations are *how catalogs enter the app*. - **Every card/row carries a storage badge** — the user always knows where a catalog lives; per-origin auth is legible (feedback_browser-is-a-parciante-node). - **Persistent "Save into → [location ▾]"** is the write side, via the shared `storagePut(path,data)` module the EGP note wants factored out — the manager and the bookmarklet import the *same* module. - **Local vs remote is a backend detail, not a UX divide** (duck-typed resources, project_agent-as-file-ducktyping). **Storage fork: flat list of mounts, or composable namespace?** Flat ships now (matches `stac-registry.json`). Aspirational: locations as *mount points* in a composed tree so one logical catalog unions across backends (the loader already produces a union — extend persistence to remember the mount set). See note 01 for the architecture.

## Build pointers - Reuse `stac-loader.js` (catalog-of-catalogs union) and the `<stac-menu>`/`stac-registry.json` picker; adopt `--acq-*` tokens; wire `seek`→cross-filter; add ⌖/◴ + context menu + `h`/`t` to the layer-tree; per-catalog color/glyph; factor EGP `storagePut/...` into a shared `storage.js`.

## References (bead cross-links) - Bead: Geospatial Ux Ui · [canonical](https://redfish.acequia.io/guerin/.agents/f80f1929-57ee-43d6-b6c1-4ef594f5fa8e/)