Commit Graph

34 Commits

Author SHA1 Message Date
Carlos Florêncio e1f0632325 fix(web): anchor PR detail panel to session group on auto-open (#646) 2026-04-20 18:52:45 +01:00
José Almeida f6f45b5777 feat: add filter/group/sort and saved views to task sidebar (#644)
* feat(web): add sidebar view types and apply-view engine

Introduces the typed representation of a sidebar view (filter clauses,
sort spec, group key) and the pure engine that applies a view to a list
of tasks to produce a filtered, sorted, grouped result.

* feat(web): add built-in sidebar view presets

Provides the four shipped presets (All tasks, No PR reviews, Active,
Archived). The 'No PR reviews' preset filters out PR-watcher tasks that
would otherwise crowd the sidebar.

* feat(web): persist sidebar views in ui store

Adds sidebar view state to the ui slice with draft semantics for saved
views, localStorage persistence of active view + user-created views, and
root store wiring.

* feat(web): add filter dimension registry and clause editor

Registry of filterable task dimensions (status, workspace, workflow, task
type, etc.) plus the primitive UI pieces — clause editor, sort picker,
group picker, and a hook that supplies operator and value options for a
selected dimension.

* feat(web): add sidebar filter bar, popover, and view manager

Composes the filter primitives into the user-facing surfaces: the top
filter bar, the popover that hosts clause editing and sort/group
selection, the chips that switch between views, and the manager for
saving, renaming, and deleting user views.

* refactor(web): extract NewTaskButton component

Pulls the Create Task button and its command-palette registration into a
standalone memoized component in preparation for reuse across sidebar
surfaces.

* feat(web): integrate filter/view system into task sidebar

Wires the filter bar, view chips, and apply-view engine into the task
sidebar (desktop and mobile sheet). Updates task-switcher to consume the
grouped list from the engine and adds mock data for tests and previews.

* test(web): add e2e coverage for sidebar filter/view system

Page object for the filter popover plus Playwright specs covering built-in
presets, clause editing, sort/group, saved-view draft semantics, and
persistence across reloads.

* refactor(web): trim sidebar built-in views to a single seeded default

Drop the "No PR reviews", "Active", and "Archived" built-in view presets
and the isBuiltIn protection flag. The sidebar now seeds a single "All
tasks" view on first load; users can rename, overwrite, or delete it like
any other view. Deletion is guarded against removing the last remaining
view so the user can never lock themselves out.

* fix(web): humanize executor type labels in sidebar filter and grouping

The executor-type filter dropdown and group-by label were rendering raw
backend identifiers like "worktree" and "local_docker". Route both
through the existing getExecutorLabel() helper so the sidebar shows
"Worktree" and "Local Docker" like the rest of the app.

* feat(web): replace "Has PR" filter with "Is PR review"

The old dimension matched any task with an associated PR, including
tasks a user manually linked. The new dimension isolates tasks created
by the GitHub PR-review watcher specifically, which is what the filter
is useful for — hiding a wall of auto-generated review tasks.

The flag is derived from the review_watch_id metadata key the backend
already stamps onto watcher-created tasks, threaded through the kanban
snapshot and into the sidebar TaskSwitcherItem. A small migration drops
unknown dimensions (including the old "hasPR") from stored views so the
popover does not crash when reading legacy data.

* fix(web): tighten sidebar filter popover layout

Reduce the popover width (w-96 → w-[22rem]), trim section padding
(p-3 → p-2.5) and label margins (mb-2 → mb-1.5), and drop the
"No filters applied." placeholder. Extract a shared SectionLabel to
keep the three section headers visually aligned.

* feat(web): show workflow step color dot in sidebar filter dropdown

Surface the step color on the workflow-step filter's value dropdown so
each option renders with the same coloured circle used elsewhere (task
switcher, kanban column, command panel). Adds an optional color field
on filter value options; only workflow-step options populate it today.

* feat(web): replace cramped in/not-in filter editor with a multiselect dropdown

The previous "in/not in" value input rendered selected values as tiny
10px pills inside a fixed-height row that overflowed and truncated as
the list of options grew. Replace it with a combobox-style multiselect
(Popover + Command) that shows a compact summary ("N selected" or the
single label with its color dot) and opens a searchable, scrollable
list with check indicators on click.

* fix(web): show workflow and step names (not UUIDs) in sidebar grouping

Group-by Workflow and Workflow Step rendered raw UUID strings as group
headers because `TaskSwitcherItem` did not carry the resolved names.
Pass `workflowName` and `workflowStepTitle` through `toSidebarItem` and
prefer them in the group extractors.

* fix(web): drop redundant true/false selector on boolean sidebar filters

Boolean dimensions (PR review, Archived, Has diff) previously exposed
both an operator (is / is not) and a value selector (true / false),
which gives four user inputs for two real states. Hide the value input
for boolean clauses and always store `true`; negation is expressed via
the operator. Rename "Is PR review" to "PR review" since the dimension
label no longer needs to read as a question.

* refactor(web): drop Sort by Repository and relabel State to Status

Sorting by repository duplicates what Group by Repository already
offers, and "State" leaks a backend term into the picker. Remove the
repository sort key (comparator, type, picker option) and rename the
state option to "Status". Stored views referencing the removed key are
migrated back to `state` at load time so the popover does not crash.

* fix(web): tighten sidebar filter popover section spacing

The FILTERS, SORT, and GROUP BY section labels each sat above their
controls with both padded containers (p-2.5) and a 6px bottom margin,
leaving visible dead space above each header. Reduce container padding
to p-2 and label margin to mb-1 so sections read as a compact stack.

* chore(web): apply prettier formatting drift in sidebar-filter area

* revert(web): undo store.ts prettier reformat to stay under max-lines cap

The packed single-line UIA declaration in store.ts is load-bearing for
the 600-line ESLint cap: the previous chore commit unpacked it to 9
lines and pushed the file to 609 lines, turning the max-lines warning
into a lint failure. Restore the packed layout (the /* prettier-ignore */
comment is there for exactly this reason).

* refactor(web): label boolean filter ops as Show/Hide instead of is/is not

* fix(web): pull section labels flush to sidebar filter popover borders

* refactor(web): inline rename and save-as into sidebar view header row

* refactor(web): split ViewHeaderRow into mode, display, and action subcomponents

* feat(backend): persist sidebar views in user settings

Adds a SidebarViews field to UserSettings (stored in the existing
users.settings JSON blob) plus validation in the service layer:
views are capped at 50, ids and names must be non-empty and unique.

* feat(web): hydrate and sync sidebar views to backend

Moves the source of truth for saved sidebar views from localStorage
to the user settings endpoint. Views hydrate from SSR on first load;
create/rename/overwrite/delete/duplicate/toggle-collapsed all PATCH
the backend with optimistic updates and revert-on-failure. The
active view id and draft remain per-device in localStorage. A
sync bridge surfaces PATCH errors as toasts and runs a one-time
migration to upload any pre-existing local views.

As a side effect, localStorage writes and PATCH side effects are
moved out of immer producers to avoid proxy-serialization risk.

* chore(web): apply prettier to store.ts sidebar actions block

* fix(web): run migrateView on SSR-hydrated sidebar views

Defensively drop clauses with unknown dimensions and reset stale sort
keys when views come from the backend, matching the localStorage load
path. Also notes the default-state module and sidebar views in the
store structure docs.

* docs: note default-state module and sidebar views in store structure

* fix(web): type sidebar view storage wrappers properly

Drop the as-unknown-as-SidebarView and as-never casts at call sites by
making the local-storage getters/setters accept the domain type and do
the JsonValue cast once inside the wrapper.

* refactor(web): stop PATCHing on sidebar group collapse toggles

collapsedGroups is per-device visual state; routing it through
mutateViews made every expand/collapse click fire a user-settings
PATCH. Keep it local (in-memory + localStorage cache); the next real
view mutation picks up whatever state is current.

* fix(web): skip sidebar view migration when settings hydrated from server

After SSR hydration the in-memory views are already the backend's
truth, so migrating re-POSTs the same data every page load. Gate the
one-shot migration on userSettings.loaded — only run if the server
didn't answer and the slice fell back to localStorage.
2026-04-20 13:44:24 +01:00
Francisco Magalhães cd46516cef fix: ux consistency on archiving and deleting tasks (#627) 2026-04-17 23:20:10 +01:00
João Salavisa f440007efc feat: add confirmation dialog before archiving tasks (#621) 2026-04-17 18:27:10 +01:00
Francisco Magalhães 78d783237b feat: introduce card multi-selection (#573) 2026-04-17 18:16:39 +01:00
Carlos Florêncio 99a8407dd8 fix: close clarification overlay when agent moves on (#608) 2026-04-16 15:58:01 +01:00
Carlos Florêncio a222854422 feat: associate agent profiles with workflows and steps (#597) 2026-04-16 10:14:45 +01:00
Luan Martins 59d7270056 fix(web): disable resume and show agent selector when profile is deleted (#578) 2026-04-15 22:31:02 +01:00
José Almeida 2b8513f902 feat: add multi-select and drag-to-move for file tree and changes panel (#490)
* feat: add multi-select and drag-to-move for file tree and changes panel

Add Shift-click range and Ctrl/Cmd-click toggle selection to the file
tree and staged/unstaged changes sections. Includes bulk actions
(delete, stage, unstage, discard), drag-and-drop file moving into
directories, keyboard shortcuts (Escape, Ctrl+A), and E2E tests.

* fix: move bulk action components to file-row to fix max-lines lint

* style: apply prettier formatting to all changed files

* fix: bulk delete all selected items and update anchor on stale shift-click

* fix: address CodeRabbit review comments

- Keep original file-row-* data-testid for backwards compat
- Remove eager onClearSelection from bulk actions (auto-pruned by hook)
- Variant-scope bulk-discard/bulk-unstage test ids
- Remove index.lock deletion from E2E GitHelper retry loop

* fix: fix E2E tests — create files after session open, remove DnD test

- Create files AFTER opening session so WS detects git status changes
- Add proper git env vars (GIT_AUTHOR_NAME, etc.) matching existing tests
- Use element.press("Escape") instead of page keyboard for proper focus
- Remove drag-to-directory test (Playwright dragTo doesn't support HTML5
  dataTransfer events needed by our DnD implementation)

* fix: rename E2E task titles to avoid ambiguous 'Changes' tab selector

* fix: make file list focusable for Escape key handler in E2E tests

* fix: improve selection styling and ignore right-clicks in selection handler

- Use text-accent-foreground on selected rows for dark mode readability
- Override child span colors with [&_span]:text-accent-foreground
- Use hover:bg-accent/80 instead of hover:bg-muted on selected items
- Skip selection handling on right-click (button === 2) so context menu
  works without clearing multi-selection

* fix: plain click should not select — only Ctrl/Cmd+click and Shift+click

Plain click now clears any selection and performs the original action
(open file / toggle folder / open diff). Selection only happens with
modifier keys. handleClick returns boolean to indicate whether the
click was consumed by selection logic.

* fix: fix multi-select bugs and expand E2E test coverage

Bug fixes:
- Change isSelected from boolean to function prop so children compute
  their own selection state (fixes nested file selection)
- Inline TreeNodeRow JSX into TreeNodeItem so ContextMenuTrigger asChild
  can attach refs directly (fixes right-click context menu)
- Set lastClickedRef on plain click so shift-click has a valid anchor
- Add click-outside handler to clear selection

E2E tests expanded to 14 file-tree tests + 3 changes-panel tests:
- Ctrl-click select, toggle, multi-select
- Plain click clears selection
- Shift-click range after plain click and ctrl-click anchors
- Directory selection and nested file selection
- Right-click context menu (single + bulk delete)
- Click outside clears selection
- Escape clears selection

* fix: move AlertDialog outside ContextMenu to fix delete action

The AlertDialog wrapping the ContextMenu caused Radix focus management
to intercept context menu item selection, making Delete trigger Rename.
Moving AlertDialog to a sibling of ContextMenu fixes the conflict.

* fix: reorder context menu — Delete first, Rename second

Radix ContextMenu auto-focuses the first item on open, which caused
accidental Rename triggers. Putting Delete first (the more common
action for selection) avoids this.

* fix: don't clear selection when clicking context menu or dialog

The click-outside handler was clearing selection when clicking on
context menu items or confirmation dialog buttons (rendered in portals
outside the tree container). Now skips clearing for elements with
role=menu, role=alertdialog, or role=dialog.

Also extended the bulk delete E2E test to verify both files are
actually removed after confirming the dialog.

* fix: hide file count in delete dialog when folder children not loaded

When a folder hasn't been expanded, its children aren't loaded yet so
countFilesInTree returns 0. Instead of showing "0 files", hide the
count entirely and just show "delete <folder name>".

* fix: show confirmation dialog for bulk discard in changes panel

Bulk discard was calling git.discard() directly, bypassing the
confirmation dialog. Now routes through handleBulkDiscardClick which
opens the DiscardDialog with a bulk message ("discard changes to N
files") before executing.

* fix: address remaining CodeRabbit review comments

- Deduplicate filenames on move: when moving files with identical names
  to the same directory, append "(1)", "(2)" etc. to avoid collisions.
  Both tree update and rename API calls use the same deduplication.
- Replace void operator with .catch(() => {}) for bulk stage/unstage
  to prevent unhandled promise rejections.
- Add document-level dragend listener as safety net to clear stuck drag
  state if the element's onDragEnd doesn't fire (Escape, out-of-window).

* test: add unit tests for file tree move deduplication and visible paths

* fix: address all remaining PR review comments

- Move onSelectionChange out of React state updater (side effect fix)
- Stabilize keyboard listener deps (pass callbacks not object)
- Remove unnecessary useMemo in BulkActionBar
- Fix ghost-restore on partial bulk delete (remove all at once)
- Fix blank filename on single-file bulk discard
- Add aria-selected and role=treeitem for accessibility
- Extract computeNextSelection params into object (max-params lint)

* fix: add missing bulk action props to WorkingTreeProps type

WorkingTreeSections referenced onBulkStage, onBulkUnstage, and
onBulkDiscard but WorkingTreeProps Pick didn't include them, causing
a TypeScript build failure.

* fix: address final review comments

- Add unit tests for computeNextSelection (8 tests covering ctrl-click,
  shift-click range, stale anchor, reverse range, shift+ctrl extend)
- Extract shared E2E helpers (GitHelper, makeGitEnv, openTaskSession,
  createStandardProfile) to e2e/helpers/git-helper.ts
- Fix misleading comment in bulk delete (says "re-insert only failed"
  but implementation restores full snapshot)

* fix: use Promise.all for bulk delete to prevent partial rollback

Individual .catch() handlers could restore already-deleted files
if a later delete failed. Now uses Promise.all so rollback only
happens if any delete rejects.

* test: add renderHook tests for useMultiSelect hook behaviors

- Stale-path pruning: items shrink → selected paths auto-pruned
- Plain click: returns false, clears existing selection
- selectAll/clearSelection: work correctly, fire onSelectionChange
- Install @testing-library/react for renderHook support

* fix: handle false returns from onDeleteFile in bulk delete Promise.all

* chore: remove unused @testing-library/react-hooks dependency
2026-04-11 17:43:21 +01:00
Carlos Florêncio 61e5d5cce9 feat(web): redesign task sidebar with repo-grouped layout and diff stats (#550) 2026-04-07 09:12:17 +01:00
Carlos Florêncio 82eb59d91e fix: disable plan mode when moving to next workflow step (#525) 2026-04-03 15:07:46 +01:00
Carlos Florêncio 12310e12ba fix: show Push button when task has open PR and unpushed commits (#537) 2026-04-03 15:03:05 +01:00
Carlos Florêncio dbe778e4e8 feat: auto-show PR details panel when task has associated PR (#517) 2026-04-02 00:08:35 +01:00
Carlos Florêncio 1c6506911e feat: add workflow sorting with drag-and-drop reordering (#520) 2026-04-01 23:58:43 +01:00
Carlos Florêncio 1cd3a6b800 feat(web): rename sidebar sections to "Turn Finished" and "Running" (#506) 2026-03-31 21:01:51 +01:00
Carlos Florêncio 59ee830d71 feat(web): unified commit list and dockview panel fix (#500) 2026-03-30 22:44:10 +01:00
Carlos Florêncio db816d4222 fix(web): deduplicate agent and session tabs in task view (#496) 2026-03-30 12:19:55 +01:00
José Almeida d1a028fdb6 feat: introduce subtasks, allow sessions to task reuse executor (#419) 2026-03-29 15:33:40 +01:00
Carlos Florêncio 7da6550093 fix(orchestrator): fail session with guidance when PR branch is missing (#466) 2026-03-26 19:35:17 +00:00
Carlos Florêncio ec01a55354 fix(web): prevent dockview layout from not filling viewport after session switch (#471) 2026-03-26 10:04:04 +00:00
Carlos Florêncio df70eee749 fix(web): merge chat status bar into single row and switch task on archive (#460) 2026-03-24 23:22:20 +00:00
José Almeida c0daa1901b feat(ui): improve mobile kanban view (#400)
* feat(ui): add responsive layouts to swimlane kanban content

Mobile gets swipeable columns with tab navigation and touch-friendly
drag sensors. Tablet gets 2-column horizontal scroll with snap.
Desktop keeps the existing CSS grid layout.

* feat(ui): add mobile task sheet and search bar components

Bottom sheet (vaul Drawer) for task preview with Edit/Delete/Open Session
actions. Compact search bar component for filtering tasks on mobile.

* feat(ui): wire mobile components into kanban board

Integrate MobileFab, MobileSearchBar, and MobileTaskSheet into the
kanban board. Skip desktop preview panel on mobile — card taps open
a bottom sheet instead of navigating directly.

* refactor(ui): optimize swimlane container for mobile

Hide swimlane headers for single workflow on mobile, remove horizontal
padding on small screens. Extract filterTasks and WorkflowItem to keep
the main component under the line limit.

* test: add mobile kanban E2E tests

8 Playwright tests covering mobile layout, FAB, search, column tabs,
bottom sheet, preview panel suppression, and swimlane header hiding.
Adds mobile-chrome project (Pixel 5) to playwright config.

* fix(ui): fix mobile E2E test failures in CI

Exclude mobile tests from chromium project (wrong viewport), add
startIndex to Embla carousel to fix initial scroll race condition,
fix strict locator for menu heading, use first step for swimlane test.

* fix(ui): fix create task dialog button layout on mobile

Make StartTaskSplitButton full-width on mobile, hide dropdown chevron
on small screens, and show a visible "Create only" button instead.

* fix(ui): match header button sizes and add icon to create-only action

Align hamburger menu button with changelog/health buttons by removing
explicit h-10 w-10 override. Add IconPlus to "Create without starting
agent" in both the desktop dropdown and mobile button.

* fix(ui): address code review feedback for mobile kanban

- Base hideHeaders on workflowFilter/total count, not filtered result
- Clamp mobile column index via useMemo when steps change
- Fix wrong assertion in preview panel test (was checking toBeVisible)
- Separate altDisabled from splitDisabled (no agent needed for create-only)
- Add visible plan mode button on mobile (was hidden behind dropdown)
- Wait for mobile-kanban-layout in page object goto()
- Extract computeBaseDisabled to reduce complexity

* fix(test): fix flaky mobile E2E tests in headless Chrome

- Use instant jump (scrollTo with jump=true) for tab-triggered carousel
  navigation instead of animated scroll that doesn't complete in headless
- Add data-testid to swimlane header to avoid ambiguous Badge locator
  matching column tab buttons
- Add timeout to viewport assertion for resilience

* fix(ui): address review feedback and fix carousel reinitialization

- Stabilize Embla options to prevent carousel reinit on tab change
  (startIndex was causing re-initialization, resetting scroll position)
- Guard programmatic scrolls from triggering select callback revert
- Remove PointerSensor from mobile DnD sensors (conflicts with touch)
- Fix plan mode button using wrong disabled prop on mobile
- Use literal name matching in columnTab locator (avoid regex injection)
- Use toHaveCount(0) for stricter preview panel absence assertion

* fix(ui): use pointerDown gating for carousel swipe sync

Embla's scrollTo doesn't work in headless Chrome, causing the select
event to fire with index 0 and reset the active tab. Fix by only
syncing Embla→tabs on user-initiated pointer interactions (swipes),
not on programmatic scrollTo calls.

Update E2E test to verify tab active state instead of carousel
viewport positions, which are unreliable in headless environments.

* fix(test): add data-testid and data-active to column tabs

Use explicit data attributes for more reliable test targeting
instead of getByRole and CSS class assertions.

* refactor(ui): use touch sensors for tablet and deduplicate getTasksForStep

* fix(ui): improve spacing for stats and settings buttons in mobile menu
2026-03-10 15:16:14 +00:00
Carlos Florêncio ddcd101762 fix: persist template step edits on workflow save and add step delete confirmation (#394) 2026-03-08 20:41:11 +00:00
Carlos Florêncio 0da44478d1 feat: add port proxy, symlink file save fix, and remote executor improvements (#358) 2026-03-07 14:53:21 +00:00
Carlos Florêncio 7fb2f0bcfa feat: add git failed operations as failed chat messages (#371) 2026-03-05 20:26:55 +00:00
Carlos Florêncio a34693e993 feat: improve session recovery and context reset (#369) 2026-03-05 00:30:50 +00:00
Carlos Florêncio 3a292098ca fix: auto-start code-server when opening file via VS Code (#368)
fix: correctly open file with vscode if vscode is not started yet
2026-03-05 00:25:34 +00:00
Carlos Florêncio c73fa7471b fix: resolve clarification MCP timeout with cancel-and-resume flow (#362) 2026-03-05 00:18:57 +00:00
Carlos Florêncio c7978a4ef3 fix: resolve stale PR data on task switch and deduplicate lifecycle code (#361) 2026-03-03 23:40:44 +00:00
Carlos Florêncio ee59ccdfbb feat: tui agents with workflows and code quality improvements (#360) 2026-03-03 22:59:22 +00:00
Carlos Florêncio d9d4beea98 fix: sidebar task delete/archive redirects to next task or home (#351) 2026-03-02 21:51:28 +00:00
Carlos Florêncio 2aab682cd8 fix: sidebar task switcher shows outdated session state (#349) 2026-03-02 20:15:48 +00:00
Carlos Florêncio 76c1dcce89 fix: sidebar task switcher shows outdated session state and custom maximize layout (#345) 2026-03-02 14:12:11 +00:00
Carlos Florêncio 44811da702 feat: web e2e tests (#304) 2026-03-02 08:54:58 +00:00