* [35a27c3d] UX/UI: design task-detail overhaul (#404) * [39ea1900] docs(ux_ui): add content-readability spec for markdown, collapsible sections, timestamps (#388) Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech> * [71f9aec6] docs(ux_ui): add task navigation/structure design spec (#400) Adds docs/ux_ui/design/task-navigation-structure.md covering the breadcrumb trail, prev/next sibling navigation, and a distinct visual treatment for the read-only constraints section, grounded in the real task-detail components and existing amber/Lock read-only tokens. Co-authored-by: UX/UI Developer 2 <ux-dev-2@roboco.tech> --------- Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech> Co-authored-by: UX/UI Developer 2 <ux-dev-2@roboco.tech> * [9baa1c34] Frontend: implement task-detail overhaul (#408) * [13b6c723] Task detail: inline timestamps + breadcrumb + prev/next navigation (#390) * [13b6c723] feat(panel): add inline absolute timestamps, task breadcrumb, and prev/next list nav to task detail Adds a shared formatAbsoluteTimestamp helper used inline (with tooltip) next to relative time on progress updates and checkpoints in tab-progress.tsx, progress-timeline.tsx, and checkpoint-card.tsx. Adds TaskBreadcrumb (renders only when task.parent_task_id is set) and TaskListNav, which reads a new taskListNav context in the scroll-restoration zustand store — populated by the Tasks list page from TaskTable's live filtered/sorted order — to move to the adjacent task. When no list context exists for the session or the current task isn't part of the captured order, both nav buttons render disabled with an explanatory tooltip (the documented fallback). * [13b6c723] docs(guide): task detail navigation, timestamps, breadcrumb, and prev/next behavior --------- Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> * [40acdd31] Task detail: collapsible markdown sections + distinct Constraints styling (#407) * [40acdd31] feat(panel): collapsible task-detail sections + distinct Constraints styling Wrap the Description, per-field Notes, and Plan cards in a new CollapsibleSection (Radix Collapsible + tw-animate-css fade/slide, so collapse/expand only animates opacity/transform) so a long task no longer forces continuous scrolling. Restyle the read-only Constraints card with an amber accent border, background tint, and ShieldAlert icon so it reads as distinct from authored content. Existing edit/preview toggles are force-open while active and otherwise unchanged. Adds a global prefers-reduced-motion override in globals.css. * [40acdd31] docs(panel): CollapsibleSection component API and usage guide Documents the new CollapsibleSection wrapper component used for independent collapse/expand of task-detail sections (Description, Constraints, Notes, Plan). Covers component API, controlled vs. uncontrolled state patterns, animation behavior (fade+slide, transform/opacity only), prefers-reduced-motion handling, and usage examples across task-description.tsx / tab-notes.tsx / tab-plan.tsx. --------- Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> * [73f8311f] fix(task-table): remove exhaustive-deps suppression on visible-order effect (#409) Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> --------- Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> * [eb417ef1] Fix: apply auto-collapse thresholds to Progress and Acceptance Criteria surfaces (#429) * [4e855d24] Apply content-readability-spec collapse thresholds to Progress and Acceptance Criteria surfaces (#416) * [4e855d24] feat(task-detail): auto-collapse long progress/checkpoint/AC content per readability spec * [4e855d24] refactor(task-detail): remove inline JSX section-marker comments per no-inline-comments convention * [4e855d24] docs(task-detail): document content-readability-spec collapse thresholds for CollapsibleSection --------- Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> * [3c90ef34] Wire content-readability thresholds into CollapsibleSection, tab-progress, acceptance-criteria (#430) * [3c90ef34] test(task-detail): add AC4 combined readability test — 30+ progress entries + long acceptance-criteria list * [3c90ef34] docs: enhance content-readability thresholds documentation and code comments - Enhance panel/src/lib/content-readability.ts with usage examples and clarified intent - Enhance CollapsibleSection with auto-collapse logic explanation and precedence rules - Enhance TabProgress's RECENT_OPEN_COUNT logic with dual-threshold explanation - Add comprehensive architecture guide: panel/docs/CONTENT_READABILITY_THRESHOLDS.md covering thresholds, components, testing, and implementation notes The readability feature prevents long-history tasks (30+ updates, 20+ criteria) from rendering fully expanded, keeping pages navigable. Tests confirm 32 progress updates default to 2 open, and long criteria lists collapse while short ones stay expanded. --------- Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> --------- Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> * [fc04d84a] Round-3 revision: fix 4 named gaps on task-detail overhaul, one dev leaf per fix (#455) * [cac9b603] fix(panel): fall back to task.created_at for missing written_at stamp in tab-notes.tsx (#446) Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> * [31dd4f99] Remove ArrowLeft back button from task-header.tsx (#441) * [31dd4f99] Remove ArrowLeft back button and Link wrapper from task-header.tsx, drop now-unused imports * [31dd4f99] docs(task-navigation): mark spec as implemented, clarify ArrowLeft button removal Update task-navigation-structure.md to reflect v0.21.0+ implementation: - Status changed from "proposed" to "implemented" - Clarified that ArrowLeft back button was removed from task-header.tsx - Noted that breadcrumb and prev/next navigation now provide all navigation - Constraints section styling with amber tint and ShieldAlert icon is complete - Referenced related guide documentation for task-detail-navigation features --------- Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> * [75fd7444] Wire content prop into EditableNoteCard's CollapsibleSection (#449) * [75fd7444] feat(panel): wire content prop into EditableNoteCard's CollapsibleSection Pass the note field's current value into CollapsibleSection's content prop and derive EditableNoteCard's initial sectionOpen state from exceedsReadabilityThreshold, so long notes default collapsed with an expand affordance while short notes render fully expanded. * [75fd7444] docs(panel): document EditableNoteCard's content-driven collapse pattern in collapsible-section.md Updated docs/frontend/components/collapsible-section.md to reflect how EditableNoteCard in tab-notes.tsx uses both controlled mode (force-open while editing) and content-driven initialization (seed sectionOpen from content length). Added a new "Combined: controlled + content-driven initialization" example showing this pattern for future developers extending editable-content sections. Pattern: long notes default collapsed with expand affordance, short notes default expanded, edit forms always visible during editing. --------- Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> * [18ada610] docs(ux-ui): reconcile prev/next nav design spec with shipped list-order behavior (#453) Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> --------- Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> * [3dfc43a1] round-3 fixes: reconcile nav spec, Alt+Arrow shortcuts, CHANGELOG The breadcrumb section of task-navigation-structure.md now describes the shipped single-ancestor design (and drops the stale DropdownMenu claims); Alt+ArrowLeft/Right on TaskListNav mirror the visible prev/next buttons, suppressed while an editable element has focus, with tests; the user-facing CHANGELOG entry lands under Unreleased. Also reflows the round-1 content-readability-spec so the prose gate is green branch-wide. * [3dfc43a1] blank line between Unreleased and 0.22.0 sections --------- Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech> Co-authored-by: UX/UI Developer 2 <ux-dev-2@roboco.tech> Co-authored-by: Frontend Developer 2 <fe-dev-2@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> Co-authored-by: Renn F <rennf93@users.noreply.github.com>
9.3 KiB
Content-readability spec: markdown, collapsible sections, timestamps
Status: proposed Owner: ux-dev-1 Surface: task detail panel (panel/src/components/tasks/task-detail/) and any other view that renders task-authored markdown or an activity/notes feed (journals, A2A transcript).
Dial read
Per the team design bar, this is dense product UI (task detail / admin panel), not a marketing surface:
- DESIGN_VARIANCE: 2 — predictable, symmetric card layout. Collapsing content changes height, not position; no asymmetric grid.
- MOTION_INTENSITY: 2 — a single
transform/opacity/grid-template-rowsexpand-collapse transition on the section body, nothing else animates. - VISUAL_DENSITY: 8 — tight padding, no added chrome beyond what already exists in
Card, tabular timestamps in list contexts.
Problem
Long task bodies (descriptions, dev/QA/PR-reviewer notes, journal entries) render in full with no way to collapse them, so a task with a large plan or a long QA note pushes the rest of the tab below the fold — this is the "scrolling fatigue" the acceptance criteria name. Separately, the panel currently renders timestamps three different ways depending on which component you're looking at:
| Component | Format | Example |
|---|---|---|
progress-timeline.tsx formatTime() |
relative bucket, falls back to short absolute past 7 days | "3h ago" / "Jul 3, 02:14 PM" |
tab-notes.tsx writtenAt() |
always absolute, no time-of-day granularity beyond minutes | "Jul 10, 04:41 AM" |
a2a-transcript.tsx |
date-fns formatDistanceToNow, always relative, no absolute fallback |
"3 hours ago" |
Three renderers means three different date-math implementations to keep correct and three different reading experiences in the same activity feed. This spec fixes both problems without introducing a new dependency — date-fns is already installed and used by a2a-transcript.tsx.
1. Collapsible-section pattern
Use the existing Radix wrapper (panel/src/components/ui/collapsible.tsx, Collapsible/CollapsibleTrigger/CollapsibleContent) — do not add a new library or hand-roll a useState show/hide toggle for this; the primitive already handles the data-state/animation attributes lint components key off.
Trigger condition. A markdown body collapses when its rendered content would exceed ~10 lines or ~640 characters of source markdown, whichever comes first — measured on the raw markdown string before render, not the rendered DOM height, so the decision is synchronous and doesn't require a layout pass. Content under that threshold renders inline with no trigger/affordance at all (a collapse control on a 2-line note is chrome for its own sake).
Default state by field, not a single global rule — a reader's expectation of "do I need this right now" differs per field:
| Field / feed | Default state | Rationale |
|---|---|---|
Task description |
open | The task's own brief — the reason the reader opened the task. |
dev_notes / qa_notes / pr_reviewer_notes / auditor_notes / doc_notes |
collapsed if over threshold | Historical record; opened on demand while triaging or auditing, not on first load. |
quick_context (resumption) |
open | Explicitly the field a resuming agent/PM needs first. |
| Progress-timeline / journal entries (list items) | collapsed, most recent 2 entries open | Matches "activity feed" convention — recent items visible, older ones summarized. |
Task constraints |
collapsed | Read-only, project-wide, rarely the thing a reader is here for. |
Affordance. The CollapsibleTrigger wraps a text button reading Show more (N lines) / Show less, right-aligned in the existing CardHeader row those components already use (see tab-notes.tsx's header flex items-center justify-between) — no new header layout. Use a ChevronDown (lucide-react, already a dependency) that rotates 180° on open via data-[state=open]:rotate-180 transition-transform, matching the MOTION_INTENSITY: 2 budget (transform only, no scroll listener).
Persistence. Collapse state is component-local (useState), not persisted to the task record or localStorage — a reader re-opening the tab should see the field-default state again, not their last session's toggle. This keeps the change purely presentational with zero backend/schema touch.
Accessibility. CollapsibleTrigger already renders aria-expanded via the Radix primitive; keep the visible label text in sync with that state ("Show more" / "Show less", not just an icon) so screen readers get the same information sighted users do.
2. Markdown rendering treatment
The existing Markdown component (panel/src/components/ui/markdown.tsx) already defines the full token set — this spec does not introduce a second markdown renderer or a competing prose scale. It formalizes which of its two existing modes (compact vs. default) each surface should use, since that choice is currently made ad hoc per call site:
- Default mode (
prose-headings:font-bold,h12xl /h2xl with a bottom border /h3lg /h4base,prose-pre:bg-muted prose-pre:border prose-pre:rounded-lg) is for content the reader is reading top-to-bottom as a document: taskdescription,dev_notes/qa_notes/etc. bodies, journal entries, constraints. compactmode (uniformtext-xsheadings, hiddenpreblocks, tight list/paragraph spacing) is for anything rendered inside a list row or card summary where markdown is secondary to the surrounding metadata — e.g. a one-line progress-update message or an A2A transcript bubble. Do not disablecompact'sprose-pre:hiddenfor these contexts: a code block inside a chat bubble already breaks density, and this spec's threshold-based collapse (§1) is the mechanism for anyone who needs the full content, not an inline fenced-block render.- Code blocks (default mode only, since
compacthides them): rely on the existingprose-pre:bg-muted prose-pre:border prose-pre:rounded-lgplus inline-code'sprose-code:bg-muted prose-code:rounded prose-code:font-mono— this already matches the panel's card/muted tokens, so no new color is introduced. No syntax highlighting — out of scope; a highlighter is a new dependency this spec's content doesn't justify (ponytail: revisit only if a future task specifically needs highlighted diffs/code review inline). - Lists: GFM task-list checkboxes already render via the
Checkboxcomponent whenonCheckboxChangeis supplied (editable contexts: description, notes) and as a plain styledinput[type=checkbox]otherwise (read-only contexts: journals, PR bodies) — keep that split; do not force every consumer to wire uponCheckboxChange.
No changes to markdown.tsx's Tailwind token classes are proposed — they already satisfy this criterion. What was missing was a documented rule for which mode a new call site should pick, captured above.
3. Timestamp presentation format
One format, applied everywhere a note or progress/journal entry shows a timestamp: a relative bucket for anything within the last 7 days, an absolute short date+time beyond that — this is progress-timeline.tsx's existing formatTime() behavior, and it becomes the canonical implementation the other two call sites adopt instead of maintaining their own date math:
< 1 min "Just now"
< 60 min "{n}m ago"
< 24 h "{n}h ago"
< 7 d "{n}d ago"
>= 7 d "Jul 3, 2:14 PM" (Intl "MMM d, h:mm a", locale en-US)
Always-visible absolute timestamp on hover/focus. Every timestamp element carries a title attribute with the full ISO-derived absolute stamp ("Jul 3, 2026, 2:14:32 PM"), so a relative bucket never fully hides the precise time — this is a one-line addition to each call site, no new component required beyond the shared formatter.
Consolidation, not three rules. Extract progress-timeline.tsx's formatTime() into a shared helper (e.g. panel/src/lib/format-timestamp.ts, matching where other date/agent-name helpers already live like lib/agent-utils.ts) and have tab-notes.tsx's writtenAt() and a2a-transcript.tsx's formatDistanceToNow(...) call replaced with it. This removes two of the three divergent implementations rather than adding a fourth. date-fns stays a dependency (already used elsewhere in the panel) but this specific formatter does not need it — the bucket math is short enough to stay plain Date arithmetic, consistent with formatTime()'s current implementation, so format-timestamp.ts has no new dependency to justify.
Where this applies: progress updates, journal/note entries in every task-detail tab (quick_context/dev_notes/qa_notes/pr_reviewer_notes/ auditor_notes/doc_notes "written at" stamps), and the A2A transcript. Any future feed showing a note/progress entry should reuse the same helper rather than writing a fourth formatter.
Non-goals
- No new component library or animation dependency — everything above composes
Collapsible,Markdown, anddate-fns/plainDate, all already in the panel's dependency tree. - No change to how markdown is stored or how notes are written (backend,
notes_structured, gateway verbs) — this is purely a rendering-layer spec. - Syntax highlighting, persisted collapse-state, and a global "collapse all" control are explicitly out of scope for this pass.