Map list preview & details¶
Goal¶
In the project detail view, move "Add map" from the page header into the map list section, and show a small preview and details for each map — mirroring how the home page's project list already shows a thumbnail and description.
Decisions¶
| Decision | Choice | Why |
|---|---|---|
| Plan file location | docs/v0.0.2/map-list-preview.md |
Matches the existing versioned precedent in this folder (project-preview-image.md, map-upload.md) |
| Thumbnail source | Reuse the map's own stored path directly for kind: "upload" maps; a placeholder icon for kind: "tileSet" maps |
Confirmed with user — every map creatable via the UI today is kind: "upload" and already has a stored image path; generating real tile-set thumbnails is a bigger, separate effort |
| Map details | Add an optional description field to MapRef, captured on the existing add-map form; fall back to a source-derived label (e.g. "Uploaded image" or the tile set's label) when unset |
Confirmed with user — MapRef has no description today, unlike MapProject |
| Editing description later | Not in this task | No edit flow exists for any map field yet; deferred to the "Map View: Mode Switching" backlog item, same as project settings |
| "Add map" placement | Right-aligned next to the "Maps" heading, always visible (including the empty state) | Confirmed with user |
| Parent map select in add-map.tsx | Left untouched | Already inert today (not read by the action) — wiring it up is the separate "submaps" backlog idea, out of scope here |
Scope¶
In:
MapRefgains an optionaldescription- add-map.tsx gains an optional description field, saved on create
- project.tsx: "Add map" button moves next to the "Maps" heading; each map row gets a thumbnail (
ItemMedia) and details (ItemTitle+ItemDescription)
Out:
- Generating real thumbnails for
tileSet-kind maps from their tile pyramid - Any edit flow for an existing map's name/description
- Wiring up the "Parent Map" select
- Any change to the project-level settings/preview-image flow
Data model¶
// app/lib/projects.server.ts
export type MapRef = {
id: string
name: string
description?: string
source: MapSource
createdAt: string
}
data/projects.json, data/demo.json, data/demo2.json need no change —
the field is optional and existing maps just render the fallback label,
unchanged from today.
Why this shape¶
- A bare optional
description?: stringonMapRefmatches the shapeMapProject.descriptionalready uses — no new convention. - Deriving a fallback label from
source(rather than requiring a description) means every existing map — none of which have one — still renders something meaningful instead of blank text. - Keeping the thumbnail logic to "upload path or placeholder" avoids
building tile-set thumbnail generation, which no seed data or UI flow
needs yet (
kind: "tileSet"isn't reachable from any route today).
Tasks¶
Ordered so each task type-checks and is shippable on its own; later tasks depend on earlier ones.
T1 — Data model: description on MapRef¶
Goal: Represent an optional description on a map.
Acceptance criteria:
app/lib/projects.server.ts'sMapRefgainsdescription?: stringas shown above.addMap(projectId, { name, description, source })accepts an optionaldescriptionand stores it on the created map when present.data/projects.json's existing maps remain valid with nodescriptionpresent.pnpm typecheckpasses.
T2 — Add-map flow: capture a description¶
Goal: A user can optionally describe a map while creating it.
Acceptance criteria:
- add-map.tsx's form gains an optional
Textareafield (name="description"), using the existingField/FieldLabelpattern already in that form. - The field is optional — submitting without one still creates the map exactly as today.
- The
actionreadsdescriptionfromrequest.formData(), trims it, and passes it toaddMap()only when non-empty (otherwiseundefined). pnpm typecheckpasses.
T3 — Fallback label for maps without a description¶
Goal: A map with no description still shows a meaningful detail line.
Acceptance criteria:
- A small pure function (e.g.
mapSourceLabel(source: MapSource): string, defined in project.tsx alongside the route since it's display-only) returns"Uploaded image"forkind: "upload"and the resolved tile set'slabel(viagetTileSetfrom tile-sets.ts) forkind: "tileSet", falling back to"Tile set"if the id doesn't resolve. pnpm typecheckpasses.
T4 — Project page: move "Add map", render preview + details¶
Goal: The Maps section owns its own "Add map" action, and each row shows a thumbnail and details, matching the home list's pattern.
Acceptance criteria:
- The
CardAction/"Add map"Buttonis removed fromCardHeaderin project.tsx. - The "Maps" heading and an "Add map" button (
render={<Link to=".../map/new" />}) sit in one right-aligned row, visible whether or notproject.mapsis empty (replacing the current empty-state-only text branch). - Each map
Itemgains anItemMedia variant="image"containing an<img>withsrc={map.source.path}whenmap.source.kind === "upload", or anItemMediawith a placeholderImageIconotherwise. - Each map
Item'sItemContentgains anItemTitle(map.name, as today) and anItemDescriptionshowingmap.description ?? mapSourceLabel(map.source). - Manual check:
pnpm dev, visit a project with an uploaded map — its row shows the actual uploaded image and either its description or "Uploaded image". Temporarily adding akind: "tileSet"entry todata/demo.jsonconfirms the placeholder-icon and tile-set-label branch, then revert it. pnpm typecheckpasses.