12 KiB
spec, created, status
| spec | created | status |
|---|---|---|
| docs/specs/ui/requirements/sidebar-task-row-presentation.md | 2026-08-23 | complete |
Implementation Plan: Sidebar Task Row Presentation
Overview
Add one task-row presentation object to each saved sidebar view and its draft. Normalize the object at the frontend boundary, preserve it through the backend-owned user-settings path, and apply the effective value to every sidebar task row.
Add a compact, collapsed Task row editor to the existing view settings. Reuse the installed
@dnd-kit packages for pointer, touch, and keyboard ordering. Keep the desktop popover and use a
bottom drawer for the phone and tablet editor.
Refine the provider-neutral change-request status summary in parallel. Give its rows shared label, icon, and value columns without changing provider status logic.
Related specification: PR task status summary.
Confirmed Product Decisions
- Presentation is saved per sidebar view, not globally.
- The current row layout is the migration and new-view default.
- The Task row section starts collapsed on every editor open. Its open state is transient.
- The details master toggle hides all under-title metadata, including plugin metadata and queue or debug indicators.
- Detail order covers relative time, repository, and pull request number. Hidden fields keep their place in the order.
- The right side supports Git changes, relative time, pull request status, and nothing.
- A trailing relative time suppresses the duplicate details value without changing the saved field.
- A trailing pull request status moves the existing provider indicator. It does not duplicate it.
- Repository remains suppressed when the active view groups by repository.
- The mobile editor is an inset bottom drawer with one scroll body, safe-area padding, and 44 pixel controls.
- Mobile task rows keep their native primary tap behavior. The compact status indicator stays passive on coarse pointers.
Data Contract and Persistence
Backend
- Add a nullable task-row presentation object to
models.SidebarViewandmodels.SidebarViewDraft. - Use explicit nested JSON fields for
details_enabled,detail_order,visible_details, andtrailing. - Preserve the nested object through user-settings request and response DTOs, SQLite JSON storage, optimistic updates, and boot-state camel-case mapping.
- Set the canonical default object on backend-created default views. Keep a missing object valid for legacy saved data.
- Retain the existing service validation for view IDs, names, active-view references, and the maximum view count. The frontend owns enum normalization because older clients and future fields must render safely.
Frontend
- Define the domain and API types in the sidebar-view type and HTTP contracts.
- Add one pure normalizer for saved and draft presentation data. It removes unknown and duplicate fields, appends missing known fields, limits visibility to known fields, and applies current defaults when the object is absent or invalid.
- Extend API wire mapping, boot hydration, migration, built-in views, direct view creation, duplication, save-as, overwrite, discard, and draft equality.
- Extend
useEffectiveSidebarViewso a draft previews presentation changes immediately. - Send complete normalized presentation objects to the backend. Do not add local storage.
Task Row Rendering
- Pass the effective presentation through
TaskSwitcherand its row plumbing toTaskItem. - Extract pure helpers that resolve visible detail fields, time semantics, group suppression, and the trailing item. Test these helpers before changing markup.
- Render configurable detail fields in saved order. Append non-configurable queue and debug indicators after configurable fields when details are enabled.
- Keep plugin
TaskRowMetadatawithin the details area so the master toggle produces a true compact row. - Let title badges render all current non-change-request badges. Move only the change-request contribution indicator when the right-side choice requests it.
- Generalize the current trailing Git-change slot. Passive Git changes or time can yield to the row menu. Interactive provider status and the row menu remain separately focusable.
- Do not reserve trailing space when the selected value is absent.
View Settings UI
- Extract shared editor content from
SidebarFilterPopoverso desktop and mobile can use the same draft and save actions. - Keep desktop in the anchored, viewport-contained popover.
- Render the phone and tablet editor in an inset bottom drawer. The drawer has one scroll owner, bottom safe-area padding, escape and outside-close behavior, and focus restoration.
- Add a collapsed Task row section after Group by. Use local component state for expansion.
- Derive the compact summary from details-enabled state, visible detail count, and trailing choice.
- Use sortable rows with a visible handle, visibility switch, keyboard sensor, and accessible drag announcements. Touch handles and other mobile controls are at least 44 by 44 CSS pixels.
- Explain the relative-time meaning and the dynamic Shown on right state in localized copy.
- Add every new key to English, Portuguese, Simplified Chinese, Hong Kong Chinese, and Taiwan Chinese catalogs. Generate the Traditional Chinese pair with the repository command.
Change-Request Summary Alignment
- Refactor
ChangeRequestTaskStatusSummaryso all available rows in one entry share amax-content, fixed-icon, and flexible-value grid. - Align secondary detail to the value column.
- Preserve current test IDs, provider presentation data, semantic tones, icons, wrapping, multi-entry separators, tooltip state, and viewport containment.
- Exercise the shared component through GitHub, GitLab, and registered-provider unit coverage.
Tests
- What: defaulting, malformed-data normalization, wire round trips, draft equality, save,
save-as, discard, duplicate, and optimistic rollback.
Files:
apps/web/lib/state/slices/ui/sidebar-view-wire.test.ts,ui-slice-migration.test.ts, andsidebar-view-actions.test.ts. How: use old, valid, future-field, duplicate-field, and invalid enum fixtures. - What: backend JSON preservation and boot-state mapping.
Files:
apps/backend/internal/user/store/sqlite_test.go,service/service_test.go,handlers/handlers_test.go, andinternal/backendapp/boot_state_routes_test.go. How: persist an explicit disabled/details order and trailing choice, reload it, and compare both REST and camel-case boot shapes. Include a legacy missing object. - What: field order, group suppression, relative-time deduplication, details hiding, provider
icon movement, missing-data collapse, and menu coexistence.
Files:
apps/web/components/task/task-item.test.tsx,task-item-repository.test.tsx, and a focused presentation helper test. How: render table-driven presentation and task-data combinations. - What: collapsed editor state, draft patches, summaries, reorder input, and responsive surface
choice.
Files: focused tests beside
apps/web/components/task/sidebar-filter/. How: render with the shared state provider and mock breakpoint, drag-end, and keyboard reorder events. - What: shared status-value and detail alignment. Files: provider status-summary render tests. How: render label lengths and wrapped detail, then assert the shared grid contract and unchanged provider text.
E2E Tests
- Scenario: Configure, preview, save, discard, save as, and reload a desktop task-row layout.
File:
apps/web/e2e/tests/task/sidebar-filter.spec.ts. What to verify: the section starts collapsed, its open state does not mark the view dirty, field order and visibility preview immediately, and saved views retain independent layouts. - Scenario: Use the editor and reordered task row on a phone viewport.
File:
apps/web/e2e/tests/task/mobile-sidebar-views.spec.ts. What to verify: the bottom drawer, 44 pixel controls, touch reorder, internal scroll, safe-area clearance, primary row tap, reload persistence, and no horizontal overflow. - Scenario: Put pull request status on the right and open its summary beside the row menu.
File:
apps/web/e2e/tests/pr/pr-status-badge.spec.ts. What to verify: one status indicator, separate keyboard targets for indicator and menu, aligned status-value left edges, aligned merge-queue detail, and viewport containment. - Scenario: Keep the compact provider indicator passive on mobile.
File:
apps/web/e2e/tests/task/mobile-task-status-summary.spec.ts. What to verify: tapping the row still navigates and no compact status control steals the tap.
Documentation Impact
This design changes no CLI command, public API, install flow, or public configuration key. The internal specs and plan are sufficient at this checkpoint. During implementation, search public task and view documentation for screenshots or labels that show the old view editor. Update only the affected page if one exists.
Risks
- A non-null Go zero value can confuse a legacy missing object with an explicit disabled setting. Use a nullable nested object at the persistence boundary and normalize it in the frontend.
- Moving a provider indicator can remove the only hover or focus trigger. Keep one mounted instance and move its presentation location rather than rendering a second copy.
- The task menu currently overlays passive Git changes. Reusing that overlay for an interactive status would block its trigger. Give the status and menu separate hit targets.
- Nested drag contexts exist in the mobile sidebar. Keep field sorting inside the editor drawer and stop its events from reaching task or saved-view sorting contexts.
- Longer translations can widen labels and summaries. Use content-sized grid tracks with a flexible value column and test narrow viewports and pseudo-locale output.
- Hiding the whole details area also hides plugin metadata and queue indicators. The master-toggle test must cover those values so future components do not escape the contract.
Verification Results
- Backend targeted coverage passed:
go test -tags fts5 ./internal/user/... ./internal/backendappreported 808 passing tests across 7 packages. - Frontend persistence coverage passed: 4 files, 61 tests. Task-row rendering coverage passed: 5 files, 78 tests. Settings coverage passed: 4 files, 12 tests. Shared provider-summary coverage passed: 4 files, 31 tests.
- Frontend typecheck, full workspace lint, changed-file ESLint,
i18n:check,i18n:ratchet, andgit diff --checkpassed.make -C apps/backend testpassed the full Go suite. - Desktop task-row E2E passed: 1 test in 6.1 seconds. Mobile task-row E2E passed: 1 test in 10.2 seconds. PR summary E2E passed: 1 test in 3.2 seconds. The mobile status-summary regression passed: 1 test in 4.7 seconds. Final capture runs also passed the desktop and mobile task-row scenarios.
- The repository
i18n:zh-hantwrapper remains blocked by its pre-existingagentsnamespace residual warning. The scoped task conversion completed successfully for both Traditional Chinese locales, with zero task residual warnings.
Implementation Waves and Parallel Candidates
Wave 1:
Wave 2:
Wave 3:
Tasks 01 and 04 are independent. Tasks 02 and 03 both depend on Task 01 and can proceed in parallel. Task 05 integrates all four production tasks. These labels document dependency only and do not authorize delegated implementation.