go-htmx-gsd #1
2 changed files with 236 additions and 0 deletions
156
.planning/phases/17-chat-planning/17-CONTEXT.md
Normal file
156
.planning/phases/17-chat-planning/17-CONTEXT.md
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
# Phase 17: Chat & Planning - Context
|
||||
|
||||
**Gathered:** 2026-05-17
|
||||
**Status:** Ready for planning
|
||||
|
||||
<domain>
|
||||
## Phase Boundary
|
||||
|
||||
Restyle the discussion view and planning page using design system components to make them visually consistent with the Phases 15–16 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.
|
||||
|
||||
</domain>
|
||||
|
||||
<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>
|
||||
|
||||
<decisions>
|
||||
## 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 10–15% — 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)
|
||||
|
||||
</decisions>
|
||||
|
||||
<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.templ` — `DiscussionTabFragment`, `DiscussionMessageRow`, `DiscussionEmptyState`, `DiscussionDaySeparator`, `DiscussionComposer` — all being restyled
|
||||
- `backend/templates/discussion_forms.go` — `DiscussionMessageView` (add `IsOwn bool`), `DiscussionMessagesFromRows` (add `currentUserID uuid.UUID`), `DiscussionMessageFromRow` (returns `IsOwn: false`; handler sets `IsOwn: true` for HTMX path)
|
||||
- `backend/templates/planning.templ` — `PlanningPage`, `PlanningEventListItem` — adopt overview-section and day separators
|
||||
- `backend/templates/planning_forms.go` — add `PlanningShowDaySeparator` helper
|
||||
- `backend/internal/web/handlers_discussion.go` — `loadDiscussionTabData`, `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 15–16)
|
||||
- `.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 13–16 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, ok` — `user.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>
|
||||
|
||||
<specifics>
|
||||
## 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
|
||||
|
||||
</specifics>
|
||||
|
||||
<deferred>
|
||||
## 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)
|
||||
|
||||
</deferred>
|
||||
|
||||
---
|
||||
|
||||
*Phase: 17-chat-planning*
|
||||
*Context gathered: 2026-05-17*
|
||||
80
.planning/phases/17-chat-planning/17-DISCUSSION-LOG.md
Normal file
80
.planning/phases/17-chat-planning/17-DISCUSSION-LOG.md
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# Phase 17: Chat & Planning - Discussion Log
|
||||
|
||||
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
|
||||
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
|
||||
|
||||
**Date:** 2026-05-17
|
||||
**Phase:** 17-chat-planning
|
||||
**Areas discussed:** Chat bubble layout
|
||||
|
||||
---
|
||||
|
||||
## Chat Bubble Layout
|
||||
|
||||
### Bubble Width and Alignment
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Full-width, background only | Own message fills full container width but has a tinted background. Others white/neutral. Simpler layout, no max-width clipping. Matches Slack highlight style. | |
|
||||
| Constrained bubble, right-aligned | Own message max-width ~70-75%, floated right. Others at full-width or left-constrained. Classic WhatsApp/iMessage style. Requires more CSS. | ✓ |
|
||||
|
||||
**User's choice:** Constrained bubble, right-aligned
|
||||
|
||||
---
|
||||
|
||||
### Bubble Background Colors
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Brand primary tint for own, surface-default for others | Own: var(--color-brand-primary) at low opacity. Others: var(--color-surface-default) (white). Consistent with token palette. | ✓ |
|
||||
| Brand accent (blue) for own, neutral surface for others | Own: var(--color-brand-accent) (#3b82f6) tint. Others: var(--color-surface-muted). Blue accent, less "brand" than purple. | |
|
||||
|
||||
**User's choice:** Brand primary tint (purple) for own, surface-default for others
|
||||
|
||||
---
|
||||
|
||||
### Author Label and Timestamp Position
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Above bubble, mirrored | Author + timestamp sit above bubble text, aligned to match bubble side. Own: right-aligned meta. Others: left-aligned meta. | ✓ |
|
||||
| Below bubble, timestamp only | No inline author label. Timestamp shown below bubble. Very compact. | |
|
||||
|
||||
**User's choice:** Above bubble, mirrored — author + timestamp above, aligned to bubble side
|
||||
|
||||
---
|
||||
|
||||
### CSS Approach
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| New CSS classes in app.css using design tokens | Add .message-row, .message-own, .message-other, .message-bubble, .message-meta — all using var(--...) tokens. Consistent with Phase 13–16 pattern. | ✓ |
|
||||
| Inline Tailwind classes in templ | Conditional Tailwind classes directly in DiscussionMessageRow. Simpler to write, but mixes presentation into template and uses hardcoded colors. | |
|
||||
|
||||
**User's choice:** New CSS classes in app.css with design tokens
|
||||
|
||||
---
|
||||
|
||||
## Areas NOT Selected
|
||||
|
||||
The user was presented with three gray areas and selected only "Chat bubble layout":
|
||||
- **Composer + card integration** — not discussed; Claude's discretion (keep composer outside card, as currently)
|
||||
- **Planning event row layout after date removal** — not discussed; Claude's discretion (row shows TimeRange → Title → TabloTitle + Location)
|
||||
|
||||
## Claude's Discretion
|
||||
|
||||
- Exact max-width value for own bubbles (70%)
|
||||
- Border-radius on bubbles (asymmetric: own `0.75rem 0.75rem 0.25rem 0.75rem`, others `0.25rem 0.75rem 0.75rem 0.75rem`)
|
||||
- Exact opacity for brand-primary tint (10-15%)
|
||||
- Whether to keep `divide-y` inside the card or drop it now that bubbles have visual weight (recommend drop)
|
||||
- Composer placement (keep outside card, unchanged)
|
||||
- Planning day separator visual style (mirror discussion's `DiscussionDaySeparator` — gray strip, centered text)
|
||||
- Empty state for planning (adopt `@ui.EmptyState(...)` consistent with Phase 16)
|
||||
- Planning event row restructuring after removing DateLabel
|
||||
|
||||
## Deferred Ideas
|
||||
|
||||
- Hover-to-reveal timestamp (compact mode)
|
||||
- Message edit or delete functionality
|
||||
- Sidebar collapse toggle (JS interaction)
|
||||
- Mobile-responsive kanban layout
|
||||
Loading…
Reference in a new issue