xtablo-source/.planning/phases/17-chat-planning/17-CONTEXT.md
2026-05-17 00:15:15 +02:00

11 KiB
Raw Blame History

Phase 17: Chat & Planning - Context

Gathered: 2026-05-17 Status: Ready for planning

## Phase Boundary

Restyle the discussion view and planning page using design system components to make them visually consistent with the Phases 1516 restyled surfaces. Phase 17 delivers:

  • Discussion view: constrained chat bubbles with own-vs-others alignment (own right, others left), brand-tinted backgrounds, new CSS classes in app.css using design tokens, .ui-card surface for message container
  • Planning page: .overview-section / .overview-section-heading header layout, day-grouped events with date separators, DateLabel removed from individual event rows

Does NOT include: avatars or profile pictures, message reactions or threading, planning sorting/filtering controls, new routes or data models, schema changes.

<spec_lock>

Requirements (locked via SPEC.md)

6 requirements are locked. See 17-SPEC.md for full requirements, boundaries, and acceptance criteria.

Downstream agents MUST read 17-SPEC.md before planning or implementing. Requirements are not duplicated here.

In scope (from SPEC.md):

  • DiscussionMessageView — add IsOwn bool field
  • DiscussionMessagesFromRows — add currentUserID uuid.UUID parameter; set IsOwn by comparing to each message's author
  • DiscussionMessageFromRow — handle isOwn appropriately for SSE vs. HTMX paths
  • DiscussionTabFragment / DiscussionMessageRow — restyle as right/left-aligned chat bubbles; message container adopts .ui-card surface
  • PlanningPage / PlanningEventListItem — adopt .overview-section heading; add PlanningShowDaySeparator helper; add day-separator element; remove DateLabel from event row body
  • All call sites for changed function signatures updated (handlers, test files where necessary to compile)
  • Browser walkthrough confirming visual consistency on both pages

Out of scope (from SPEC.md):

  • Avatars or profile pictures in message rows
  • Message reactions or reply threading
  • Planning page sorting or filtering controls
  • New database schema, migrations, or sqlc queries
  • New routes or HTTP handlers

</spec_lock>

## Implementation Decisions

Chat Bubble Visual Design

  • D-M01: Own messages render as constrained bubbles — max-width ~70%, right-aligned (use margin-left: auto). Others render at full width, left-aligned. Each message row is a flex container; own uses .message-own class, others use .message-other.
  • D-M02: Bubble backgrounds use design tokens: own messages use var(--color-brand-primary) at low opacity (tint — e.g., rgba(128, 78, 236, 0.1) or a CSS custom property layered on --color-brand-primary). Others use var(--color-surface-default) (white). Border: subtle var(--color-border-subtle).
  • D-M03: Author email + timestamp appear ABOVE the bubble text, mirrored to match bubble alignment. Own: author + time right-aligned. Others: author + time left-aligned.
  • D-M04: New CSS classes in backend/internal/web/ui/app.css using var(--...) tokens only — no Tailwind inline, no hardcoded hex values. Classes: .message-row, .message-own, .message-other, .message-bubble, .message-meta.

Discussion Data Model

  • D-D01: DiscussionMessageView gains IsOwn bool. Set via DiscussionMessagesFromRows(rows, currentUserID) — the handler already has user.ID from loadOwnedTablo.
  • D-D02: loadDiscussionTabData receives the current user ID and passes it to DiscussionMessagesFromRows. The TabloDiscussionTabHandler already has user — thread user.ID through.
  • D-D03: For the HTMX POST response (posting user): message.IsOwn = true. For renderDiscussionMessageHTML (SSE broadcast to other viewers): IsOwn = false. Since DiscussionMessageView is a value type in Go, set IsOwn = false before SSE publish and IsOwn = true before HTMX render — two sequential mutations on the same local copy work cleanly.
  • D-D04: DiscussionMessageFromRow no longer needs an isOwn bool parameter — use the struct field mutation approach in the handler instead. The function returns a view with IsOwn: false by default; the handler sets .IsOwn = true for the HTMX path.

Discussion Container

  • D-C01: The #discussion-messages container div changes from class="rounded border border-slate-200 bg-white" to use the .ui-card CSS class. The composer form remains outside the card (below it), as currently — no structural change needed.

Planning Page

  • D-P01: PlanningPage heading section wraps in <section class="overview-section"> + <div class="overview-section-heading"> with <h1>Planning</h1> and the date range label below — matching the .overview-section pattern from tablos.templ.
  • D-P02: Add PlanningShowDaySeparator(events []PlanningEventRow, index int) bool helper in planning_forms.go — compares events[index].DateLabel != events[index-1].DateLabel (string comparison, returns true for index 0 or label change).
  • D-P03: Day separator element mirrors the discussion day separator style — a centered strip with the date label (e.g., <div class="bg-slate-50 px-4 py-2 text-center text-sm text-slate-500">).
  • D-P04: DateLabel field is removed from PlanningEventListItem row body — it's now rendered in the separator. Event row shows: TimeRange → Title → TabloTitle + Location (existing right column).

Claude's Discretion

  • Exact max-width value for own message bubbles (recommend 70%)
  • Border-radius on bubbles (recommend 0.75rem for all corners, or asymmetric: own 0.75rem 0.75rem 0.25rem 0.75rem)
  • Exact opacity/alpha for the brand-primary bubble tint (recommend 1015% — subtle, readable)
  • Divider between messages: keep divide-y divide-slate-100 inside the card or drop it now that bubbles have their own visual weight (recommend dropping the divider)
  • Empty state for planning when no events: adopt @ui.EmptyState(...) to match Phase 16 files section (consistent with established pattern)
  • Navigation controls in planning heading: keep using .ui-button inline utility classes (no change needed)

<canonical_refs>

Canonical References

Downstream agents MUST read these before planning or implementing.

Locked Requirements

  • .planning/phases/17-chat-planning/17-SPEC.md — Locked requirements (6), boundaries, acceptance criteria — MUST read before planning

Product Scope

  • .planning/ROADMAP.md — Phase 17 goal, success criteria (4 criteria), requirements CHAT-UI-01, PLAN-UI-01
  • .planning/REQUIREMENTS.md — CHAT-UI-01 (card/surface design, own-vs-others), PLAN-UI-01 (overview-section layout)
  • .planning/PROJECT.md — v3.0 scope: visual polish only; no new data models or routes

Current Backend (files being changed)

  • backend/templates/discussion.templDiscussionTabFragment, DiscussionMessageRow, DiscussionEmptyState, DiscussionDaySeparator, DiscussionComposer — all being restyled
  • backend/templates/discussion_forms.goDiscussionMessageView (add IsOwn bool), DiscussionMessagesFromRows (add currentUserID uuid.UUID), DiscussionMessageFromRow (returns IsOwn: false; handler sets IsOwn: true for HTMX path)
  • backend/templates/planning.templPlanningPage, PlanningEventListItem — adopt overview-section and day separators
  • backend/templates/planning_forms.go — add PlanningShowDaySeparator helper
  • backend/internal/web/handlers_discussion.goloadDiscussionTabData, DiscussionMessageCreateHandler — thread user.ID through; set IsOwn for HTMX vs. SSE paths

Design System (Phase 13 — already in backend)

  • backend/internal/web/ui/app.css — receives new .message-row, .message-own, .message-other, .message-bubble, .message-meta CSS classes using design tokens
  • backend/internal/web/ui/base.css — design tokens; all new CSS uses var(--...) tokens. Key tokens: --color-brand-primary, --color-surface-default, --color-border-subtle, --color-text-muted, --color-surface-muted-inverse
  • backend/internal/web/ui/card.templ + backend/internal/web/ui/card.css.ui-card class to apply to discussion message container
  • backend/internal/web/ui/empty_state.templ@ui.EmptyState(...) for planning empty state (Claude's discretion)

Established Patterns (Phase 1516)

  • .planning/phases/16-tablo-detail/16-CONTEXT.md — overview-section heading pattern, design token constraints, all-CSS-in-app.css rule
  • .planning/phases/15-dashboard-tablos/15-CONTEXT.md — AppLayout integration, sidebar tablos list pattern

</canonical_refs>

<code_context>

Existing Code Insights

Reusable Assets

  • backend/internal/web/ui/card.css .ui-card: Apply to #discussion-messages container — swap class="rounded border border-slate-200 bg-white" with class="ui-card"
  • backend/templates/discussion.templ DiscussionDaySeparator: Day separator pattern already exists for discussion — use the same visual approach for planning day separators
  • backend/internal/web/ui/empty_state.templ @ui.EmptyState(...): Ready for planning empty state to replace the current raw div

Established Patterns

  • All new CSS in app.css uses var(--...) tokens only — no hardcoded hex, no Tailwind inline in CSS files (Phases 1316 convention)
  • Value-type Go structs: DiscussionMessageView is a struct (not pointer) — handler can mutate .IsOwn between SSE publish and HTMX render without affecting the published copy
  • loadOwnedTablo returns tablo, user, okuser.ID is already available in all discussion handlers without additional queries

Integration Points

  • backend/internal/web/handlers_discussion.go loadDiscussionTabData: needs userID uuid.UUID as a new parameter; one caller (TabloDiscussionTabHandler) passes user.ID
  • backend/internal/web/handlers_discussion.go DiscussionMessageCreateHandler: sets message.IsOwn = false before SSE publish; message.IsOwn = true before HTMX render (line ~141)
  • backend/templates/tablos.templ TabloDetailPage: discussion tab passes discussion DiscussionTabData unchanged — no signature change needed at this level
  • All non-discussion handlers that pass empty templates.DiscussionTabData{} are unaffected — IsOwn defaults to false which is correct for empty data

</code_context>

## Specific Ideas
  • Message bubble CSS: .message-own .message-bubble { margin-left: auto; max-width: 70%; background: rgba(128, 78, 236, 0.10); border: 1px solid var(--color-border-subtle); border-radius: 0.75rem 0.75rem 0.25rem 0.75rem; } — asymmetric radius gives the classic chat feel for own messages; others get border-radius: 0.25rem 0.75rem 0.75rem 0.75rem
  • .message-meta for own: text-align: right; display: flex; justify-content: flex-end; gap: 0.5rem; margin-bottom: 0.25rem
  • Planning day separator: reuse the DiscussionDaySeparator visual pattern — same gray-tinted strip (bg-slate-50 px-4 py-2 text-center text-sm text-slate-500) for visual consistency between the two restyled surfaces
## Deferred Ideas
  • Hover-to-reveal timestamp (compact mode) — not in v3.0 requirements
  • Message edit or delete — not in v3.0 requirements
  • Sidebar collapse toggle (JS) — deferred since Phase 15; still deferred
  • Mobile-responsive kanban — still deferred (RESP-01 future)

Phase: 17-chat-planning Context gathered: 2026-05-17