Skip to content

feat(studio): station journal review surface in the diagram view#643

Open
themightychris wants to merge 12 commits into
mainfrom
feat/station-journal-desktop
Open

feat(studio): station journal review surface in the diagram view#643
themightychris wants to merge 12 commits into
mainfrom
feat/station-journal-desktop

Conversation

@themightychris

@themightychris themightychris commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Station journal — desktop review surface (Pathways Studio)

Surfaces companion-captured journal entries in the Studio station diagram view so they can be reviewed at a desk and closed. Implements the desktop side of specs/api/station-journal.md (spec lives in the companion repo, pathways-field-companion).

Rebased onto main now that the journal backend has merged (#655, which superseded #641). The branch carries the desktop surface plus one adaptation commit switching it to the merged backend's API (StationJournal scope-based listing with preloaded photos, PhotoStorage.public_path URLs, and desk-side close_journal_entry/reopen_journal_entry context functions — closure was response-only in the #655 API slice). Full suite on the branch: 2012 tests, 0 failures.

What it adds

  • A "Journal" toolbar button (open-entry count badge) opening a drawer that lists entries newest-first with a target chip (station / node / pathway / pin), author, time, note, and photo thumbnails (full image opens in a new tab).
  • Close / Reopen as a desk action — Gtfs.close_journal_entry/2 + reopen_journal_entry/1 set/clear closed_at + closed_by. Closed entries stay in the list, de-emphasized with a "Closed by X · when" badge — never hidden or filtered out.
  • Pin markers on the level diagram overlay, drawn from diagram_x/diagram_y in the same 0–100 space as node markers, filtered to the active level by stop_level_id; clicking one opens the drawer focused on that entry.

Photo URLs reuse the field-capture path; author/closer ids resolve to emails.

Verification

  • 5 LiveView tests (list, close-stays-visible, reopen, pin marker, count badge); 402 green across the diagram + journal suites.
  • Verified live on the devbox against the real 15th St seed: the drawer lists entries with photo thumbnails, and Close → Reopen round-trips closed_at/closed_by (confirmed via the bundle API).

Not in scope

  • In-page photo lightbox (thumbnails open full image in a new tab) — follow-up.
  • The OCR/text-extraction engine — only the attach-point seam is left.
  • Node/pathway/pin-targeted entries are exercised by LiveView fixtures; live verification awaits such entries being captured from the companion.

🤖 Generated with Claude Code

@themightychris
themightychris changed the base branch from main to feat/station-journal-entries June 24, 2026 13:56
@themightychris

Copy link
Copy Markdown
Collaborator Author

I made the journal sidebar be a full-page dock to enable it to be open side-by-side with work. Looks a bit weird to have it stacked with the edit modal, but every other configuration would have required constantly shuffling things around to work on things based on journal content:

Screenshot 2026-06-24 at 1 32 36 PM Screenshot 2026-06-24 at 1 32 43 PM Screenshot 2026-06-24 at 1 34 04 PM

themightychris and others added 12 commits July 14, 2026 17:21
Surface companion-captured journal entries in Pathways Studio so they can be
reviewed at a desk and closed. Adds to the station diagram LiveView:

- A "Journal" toolbar button (open-entry count badge) opening a drawer that lists
  entries newest-first with target chip (station/node/pathway/pin), author, time,
  note, and photo thumbnails (full image opens in a new tab).
- Close / reopen as a desk action: close_journal_entry/2 + reopen_journal_entry/1
  context fns set/clear closed_at + closed_by. Closed entries stay in the list,
  de-emphasized with a "Closed by X · when" badge — never hidden or filtered out.
- Pin markers on the level diagram overlay, drawn from diagram_x/diagram_y in the
  same 0–100 space as node markers, filtered to the active level by stop_level_id;
  clicking one opens the drawer focused on that entry.

Photo URLs reuse the field-capture path; author/closer names resolve to emails.
LiveView tests cover listing, close (stays visible), reopen, the pin marker, and
the count badge. Stacks on the journal backend (#641) until it merges.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UyezV5kznW5rbRFVsoXWvh
The journal was an overlay drawer that blocked the diagram while open, forcing
the reviewer to flip back and forth. But the entries are the *input* to the
editing work — they should stay visible while you edit. Convert it to a docked
side rail that takes layout space: the diagram canvas flexes to share width, the
journal sits beside it (sticky, independently scrollable), and the toolbar button
toggles it. Both stay fully interactive. Renamed the assign/handlers to
journal_panel_* to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UyezV5kznW5rbRFVsoXWvh
…e it

The docked-in-the-sub_header panel still got covered when a node/pathway edit
drawer (a fixed right-0 overlay) opened — so you couldn't read the journal while
editing. Make the journal a fixed full-height right rail (its own scroll), reserve
its width on #diagram-page (pr-96), and shift the shared slide-in drawers + their
overlay left by the rail width via a `.journal-open .app-drawer` CSS hook. Now the
node/pathway edit drawer opens to the LEFT of the journal — both visible at once.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UyezV5kznW5rbRFVsoXWvh
A sticky All | Open | Closed button group at the top of the journal rail filters
the list (default All — closed entries still show by default, consistent with the
never-hidden treatment). Open = no closed_at; Closed = closed_at set. Empty-state
message reflects the active filter. LiveView test covers all three scopes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UyezV5kznW5rbRFVsoXWvh
The target chip overflowed for long node names and gave no way to correlate an
entry to the map. Now:

- Target chip is a truncating pill (full name in title) and a click affordance:
  node/pathway open their editor (edit_child_stop/edit_pathway) beside the
  journal; a pin highlights its marker (pulsing ring).
- Diagram → journal: nodes/pathways with an OPEN entry show a subtle amber "!"
  indicator while the journal is open; clicking it opens/focuses that entry in
  the rail (rings it + scrolls into view via a phx:journal_focus event).

Threads journal_targets (target_id→entry_id, open-only) + journal_focus_id
through diagram_canvas → overlay → stops/pathways/pin layers. Tests cover the
clickable chip and focus_journal_entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UyezV5kznW5rbRFVsoXWvh
The node/pathway "has an open entry" indicators were rendered inside the
phx-update="stream" stops/pathways layers, which don't re-render on assign
changes — so toggling the journal never made them appear. Move them to a
dedicated non-stream journal_indicators_layer fed by server-resolved positions
(node = its diagram point, pathway = from/to midpoint), so they react to journal
open/close and entry changes. Test asserts none when closed, indicator when open.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UyezV5kznW5rbRFVsoXWvh
The journal pin markers and node/pathway "!" indicators were drawn in diagram
space, so they scaled with zoom — unlike node markers, which the DiagramCanvas
hook counter-scales. Tag both with data-journal-scale + data-center-x/y and add
a counter-scale pass in scaleOverlayElements (transform scale 1/iconScale about
each center), so they hold a fixed on-screen size like node markers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UyezV5kznW5rbRFVsoXWvh
The map→journal indicators + pins were already scoped to the active level, but a
chip click (journal→map) called edit_child_stop/edit_pathway/focus_journal_pin
directly, which don't switch level — so a target on another level acted off
the visible diagram. Route chips through a new journal_locate (keyed by entry id):
resolve the target's level (pin → its stop_level; node/pathway → the stop's GTFS
level_id), switch the diagram there if needed (reusing search_stop's pipeline,
extracted as switch_to_level/2), then open the editor / focus the pin. Tests
cover same-level open + the cross-level switch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UyezV5kznW5rbRFVsoXWvh
- Node + pathway editors are mutually exclusive now: opening the node editor
  closes the pathway drawer and vice-versa. They were both staying open (same
  z-50, pathway rendered later + wider, so it only *looked* closed in one order).
- Note bodies no longer render with a leading indent: the whitespace-pre-wrap
  <p> had the interpolation on its own line, so the markup's indentation was
  preserved as text. Inlined {entry.body}.
- Each rail entry shows its target's level as a small tag (resolved in
  load_journal via entry_level_name), so you see an entry's level before clicking.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UyezV5kznW5rbRFVsoXWvh
…format

The previous inline fix was undone by the HEEx formatter, which reflowed
>{entry.body}</p> onto its own indented line; whitespace-pre-wrap then rendered
that markup indentation as a leading indent. phx-no-format keeps it flush.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UyezV5kznW5rbRFVsoXWvh
Complaint: the journal rail looked like the edit drawers (white surface, emerald
header, right-anchored), so an open editor beside it read as double-stacked
panels. Re-style the rail as a distinct reference surface, not another modal:
muted recessed background (bg-base-200, no shadow) with an amber identity (amber
left-edge accent, amber header + clipboard icon, amber active filter + focus
ring). Open entries become white lifted cards floating on the muted rail; closed
ones blend in. The white, shadowed, emerald editors now clearly float in front.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UyezV5kznW5rbRFVsoXWvh
The station-journal backend merged to main via #655 (replacing #641, which
this branch was stacked on). Same tables and wire contract, different domain
shape — journal operations now go through GtfsPlanner.Gtfs.StationJournal
with a trusted Scope struct:

- load_journal builds a Scope from the LiveView's already-authorized
  org/version/station + current user and calls Gtfs.list_station_journal/1;
  photos arrive preloaded on each entry (the separate photo listing and
  group_by are gone).
- Photo thumbnails use PhotoStorage.public_path/2 instead of a hand-rolled
  field-captures URL helper.
- switch_to_level drops assign_selectable_reference_stop_levels/
  sync_reference_overlay_state — main replaced the reference-overlay
  internals with the other-levels work (#646); the journal pipeline mirrors
  main's search_stop level switch.
- Test fixtures insert JournalEntry rows directly (the 641-era
  upsert_journal_entry sync path no longer exists; the companion's write
  path is the scoped sync API, which these desktop read/close tests don't
  exercise).

Full suite on this branch: 2012 tests, 0 failures, 6 skipped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HoGkr6FT6SKZCYDBkNqKBj
@themightychris
themightychris force-pushed the feat/station-journal-desktop branch from d21b591 to a1d67fd Compare July 14, 2026 21:34
@themightychris
themightychris changed the base branch from feat/station-journal-entries to main July 14, 2026 21:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant