Repository navigation
feat(desktop): separate tasks and pull requests in sidebar - #5975
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis change separates Tasks and Pull requests into distinct dashboard destinations. It adds independent filters and routes, shared work-item controls, host-aware loading, detail pages, and compatibility redirects. ChangesTasks and Pull Requests separation
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)Tasks and issuessequenceDiagram
participant User
participant TasksTopBar
participant TasksView
participant GitHubIssuesContent
participant HostService
User->>TasksTopBar: Select Issues or Open/All
TasksTopBar->>TasksView: Update source and closed-state filters
TasksView->>GitHubIssuesContent: Pass filters and host context
GitHubIssuesContent->>HostService: Search GitHub issues
HostService-->>GitHubIssuesContent: Return issue results
Pull requestssequenceDiagram
participant User
participant PullRequestsView
participant PullRequestsContent
participant HostService
participant PullRequestDetailPage
User->>PullRequestsView: Change search, project, or state
PullRequestsView->>PullRequestsContent: Pass synchronized filters
PullRequestsContent->>HostService: Search pull requests
HostService-->>PullRequestsContent: Return pull-request results
User->>PullRequestsContent: Open a pull request
PullRequestsContent->>PullRequestDetailPage: Navigate with PR number and filters
PullRequestDetailPage->>HostService: Fetch pull-request details
HostService-->>PullRequestDetailPage: Return detail data
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Ready to review this PR? Stage has broken it down into 9 individual chapters for you: Chapters generated by Stage for commit a953e13 on Jul 27, 2026 6:53pm UTC. |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/issue/$issueNumber/page.tsx (1)
118-185: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider extracting the repeated header+state wrapper.
The
<div className="flex min-h-0 flex-1 flex-col">{header}<WorkItemDetailState .../></div>wrapper is duplicated across five early-return branches. A small local helper would reduce repetition and keep future markup changes in one place.♻️ Example extraction
const renderState = (props: React.ComponentProps<typeof WorkItemDetailState>) => ( <div className="flex min-h-0 flex-1 flex-col"> {header} <WorkItemDetailState {...props} /> </div> );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/issue/`$issueNumber/page.tsx around lines 118 - 185, Extract a local renderState helper near the early-return logic, using WorkItemDetailState props to render the shared flex wrapper and header. Replace the five duplicated header-plus-WorkItemDetailState branches for invalid issue, missing project, unavailable project, unavailable host, loading, and error states with calls to this helper, preserving each branch’s existing props and messages.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/`$prNumber/page.tsx:
- Around line 168-181: Update the conditional in the pull request detail
rendering flow to prioritize existing data: render the data-first path whenever
data is present, even when error is an Error, and only show the
WorkItemDetailState fallback when data is absent. Preserve the existing error
message and refetch retry behavior for the no-data case.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/PullRequestsView.tsx`:
- Around line 31-49: Update the initialSearch synchronization effect in
PullRequestsView so it calls setSearchQuery only when initialSearch is supplied,
preserving the storedSearch value on mount when initialSearch is undefined. Keep
subsequent updates to an explicitly provided initialSearch unchanged.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/stores/pullRequestsFilterStore/pullRequestsFilterStore.ts`:
- Around line 40-48: Update pullRequestsSearchFromFilters to return
Partial<PullRequestsSearch> (or the equivalent route search schema type) instead
of Record<string, string>, preserving the existing filter mappings while keeping
state constrained to the route’s "open" | "all" values and enabling compile-time
validation at navigate callers.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/`$taskId/components/EditableTitle/EditableTitle.tsx:
- Around line 17-22: Update the useLayoutEffect in EditableTitle to recalculate
the textarea height whenever the title value changes, rather than only on mount.
Preserve the existing reset-to-auto and scrollHeight measurement sequence so
wrapped text expands correctly.
---
Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/issue/`$issueNumber/page.tsx:
- Around line 118-185: Extract a local renderState helper near the early-return
logic, using WorkItemDetailState props to render the shared flex wrapper and
header. Replace the five duplicated header-plus-WorkItemDetailState branches for
invalid issue, missing project, unavailable project, unavailable host, loading,
and error states with calls to this helper, preserving each branch’s existing
props and messages.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 6e357af2-ef72-4853-acf1-f9e463d74845
📒 Files selected for processing (46)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHeader/DashboardSidebarHeader.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/OpenClosedFilter/OpenClosedFilter.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/OpenClosedFilter/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/ProjectFilter/ProjectFilter.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/ProjectFilter/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/WorkItemDetailHeader/WorkItemDetailHeader.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/WorkItemDetailHeader/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/WorkItemDetailState/WorkItemDetailState.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/WorkItemDetailState/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/WorkItemsSearch/WorkItemsSearch.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/WorkItemsSearch/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/hooks/useProjectHost/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/hooks/useProjectHost/useProjectHost.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/hooks/useProjectHost/useProjectHost.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/$prNumber/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/PullRequestsView.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/components/PullRequestsContent/PullRequestsContent.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/components/PullRequestsContent/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/components/PullRequestsTopBar/PullRequestsTopBar.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/components/PullRequestsTopBar/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/layout.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/stores/pullRequestsFilterStore/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/stores/pullRequestsFilterStore/pullRequestsFilterStore.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/stores/pullRequestsFilterStore/pullRequestsFilterStore.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/EditableTitle/EditableTitle.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/TasksView.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/TasksView.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/TasksTopBar/TasksTopBar.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/TasksTopBar/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/issue/$issueNumber/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/layout.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/pr/$prNumber/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/stores/tasks-filter-state.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/stores/tasks-filter-state.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/parsePositiveIntegerParam/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/parsePositiveIntegerParam/parsePositiveIntegerParam.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/parsePositiveIntegerParam/parsePositiveIntegerParam.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/shouldKeepWorkItemsPlaceholder/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/shouldKeepWorkItemsPlaceholder/shouldKeepWorkItemsPlaceholder.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/shouldKeepWorkItemsPlaceholder/shouldKeepWorkItemsPlaceholder.tsapps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/WorkspaceSidebarHeader.tsx
| if (error instanceof Error || !data) { | ||
| return ( | ||
| <div className="flex min-h-0 flex-1 flex-col"> | ||
| {header} | ||
| <WorkItemDetailState | ||
| message={ | ||
| error instanceof Error ? error.message : "Pull request not found." | ||
| } | ||
| isError | ||
| onRetry={() => void refetch()} | ||
| /> | ||
| </div> | ||
| ); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
In TanStack Query v5, does datafrom a previously successfuluseQueryfetch remain populated after a subsequent background refetch fails withretry: false?
💡 Result:
Yes, in TanStack Query v5, data from a previously successful fetch remains populated in the cache after a subsequent background refetch fails, even if retry: false is set [1][2]. When a background refetch fails, the query transitions into an error state [1][3]. However, the previously successfully fetched data is not discarded from the cache [1][2]. This behavior allows you to continue displaying the existing data to the user while the query is in an error state [2]. Key points regarding this behavior: 1. Data Persistence: The data property on the useQuery result object will still contain the last successful data [1][2]. 2. State Changes: The query status will update to error (isError becomes true), and the error property will be populated with the error object [1][3]. 3. Rendering: Because the data remains available, components that rely on the data can continue to render the stale information [2]. This is also why the default throwOnError behavior in TanStack Query typically does not propagate errors to an Error Boundary if existing data is available to show the user [2]. If you need to strictly clear the data upon a refetch error, you would need to implement custom logic, such as using the queryCache or specific state management patterns, as the default behavior preserves the existing cache entry [1].
Citations:
- 1: Retaining the persisted data after failed retries TanStack/query#6510
- 2: https://tanstack.com/query/v5/docs/framework/react/guides/suspense
- 3: https://tanstack.com/query/v5/docs/framework/react/guides/queries
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/$prNumber/page.tsx"
echo "Exists: $(test -f "$file" && echo yes || echo no)"
wc -l "$file"
echo "--- relevant slice ---"
sed -n '130,195p' "$file" | nl -ba -v130
echo "--- query options search ---"
rg -n "useQuery|refetch|retry|placeholderData|keepPreviousData|onRetry|data =|error is|isLoading|isSuccess|status" "$file"Repository: superset-sh/superset
Length of output: 199
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file='apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/$prNumber/page.tsx'
echo "Exists: $(test -f "$file" && echo yes || echo no)"
wc -l "$file"
echo "--- relevant slice ---"
sed -n '130,195p' "$file" | nl -ba -v130
echo "--- query options/consumer search ---"
rg -n "useQuery|refetch|retry|placeholderData|keepPreviousData|onRetry|data =|error is|isLoading|isSuccess|status" "$file"Repository: superset-sh/superset
Length of output: 329
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file='apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/$prNumber/page.tsx'
echo "--- relevant slice (lines 130-195) ---"
awk 'NR>=130 && NR<=195 { printf "%5d\t%s\n", NR, $0 }' "$file"
echo "--- query options/consumer search ---"
grep -En "useQuery|refetch|retry|placeholderData|keepPreviousData|onRetry|data =|error is|isLoading|isSuccess|status" "$file" || trueRepository: superset-sh/superset
Length of output: 2654
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file='apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/$prNumber/page.tsx'
echo "--- query declaration slice ---"
awk 'NR>=45 && NR<=75 { printf "%5d\t%s\n", NR, $0 }' "$file"
echo "--- full relevant hook/render flow ---"
awk 'NR>=1 && NR<=200 { printf "%5d\t%s\n", NR, $0 }' "$file"Repository: superset-sh/superset
Length of output: 8455
Prefer stale PR data over a transient refetch error.
TanStack Query keeps the last successful data even when a later refetch enters the error state, so error instanceof Error || !data can replace a fully rendered PR detail view with “Pull request not found” while valid content still exists. Render the data-first path whenever data is present, and only fall back to the error/not-found state when data is absent.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/`$prNumber/page.tsx
around lines 168 - 181, Update the conditional in the pull request detail
rendering flow to prioritize existing data: render the data-first path whenever
data is present, even when error is an Error, and only show the
WorkItemDetailState fallback when data is absent. Preserve the existing error
message and refetch retry behavior for the no-data case.
| const [searchQuery, setSearchQuery] = useState(initialSearch ?? storedSearch); | ||
| const projectFilter = initialProject ?? storedProjectFilter; | ||
| const includeClosed = | ||
| initialState === undefined ? storedIncludeClosed : initialState === "all"; | ||
| const debounceRef = useRef<ReturnType<typeof setTimeout>>(null); | ||
| const { | ||
| hostId: projectHostId, | ||
| isReady: areProjectsReady, | ||
| project: selectedProject, | ||
| projects: hostProjects, | ||
| } = useProjectHost(projectFilter); | ||
|
|
||
| useEffect(() => { | ||
| setSearchQuery(initialSearch ?? ""); | ||
| }, [initialSearch]); | ||
|
|
||
| useEffect(() => { | ||
| storeSetSearch(searchQuery); | ||
| }, [searchQuery, storeSetSearch]); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
fd -a 'layout.tsx|page.tsx' apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests
cat -n apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/layout.tsx apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/page.tsx 2>/dev/nullRepository: superset-sh/superset
Length of output: 2022
🏁 Script executed:
#!/bin/bash
set -e
fd -a 'PullRequestsView.tsx' apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components | while read -r f; do
echo "===== $f ====="
wc -l "$f"
sed -n '1,180p' "$f"
done
sed -n '1,240p' apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/page.tsx
sed -n '1,220p' apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/layout.tsxRepository: superset-sh/superset
Length of output: 6349
Persisted search filter is wiped on mount.
PullRequestsView seeds searchQuery from storedSearch, but the first effect runs setSearchQuery(initialSearch ?? "") on mount. Since /pull-requests/?search is validated to undefined when absent, opening the existing persisted search is immediately reset to "", and the following storeSetSearch(searchQuery) persists the empty value back to the filter store. Only override searchQuery when initialSearch is actually supplied.
🐛 Proposed fix
useEffect(() => {
- setSearchQuery(initialSearch ?? "");
+ if (initialSearch !== undefined) {
+ setSearchQuery(initialSearch);
+ }
}, [initialSearch]);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const [searchQuery, setSearchQuery] = useState(initialSearch ?? storedSearch); | |
| const projectFilter = initialProject ?? storedProjectFilter; | |
| const includeClosed = | |
| initialState === undefined ? storedIncludeClosed : initialState === "all"; | |
| const debounceRef = useRef<ReturnType<typeof setTimeout>>(null); | |
| const { | |
| hostId: projectHostId, | |
| isReady: areProjectsReady, | |
| project: selectedProject, | |
| projects: hostProjects, | |
| } = useProjectHost(projectFilter); | |
| useEffect(() => { | |
| setSearchQuery(initialSearch ?? ""); | |
| }, [initialSearch]); | |
| useEffect(() => { | |
| storeSetSearch(searchQuery); | |
| }, [searchQuery, storeSetSearch]); | |
| const [searchQuery, setSearchQuery] = useState(initialSearch ?? storedSearch); | |
| const projectFilter = initialProject ?? storedProjectFilter; | |
| const includeClosed = | |
| initialState === undefined ? storedIncludeClosed : initialState === "all"; | |
| const debounceRef = useRef<ReturnType<typeof setTimeout>>(null); | |
| const { | |
| hostId: projectHostId, | |
| isReady: areProjectsReady, | |
| project: selectedProject, | |
| projects: hostProjects, | |
| } = useProjectHost(projectFilter); | |
| useEffect(() => { | |
| if (initialSearch !== undefined) { | |
| setSearchQuery(initialSearch); | |
| } | |
| }, [initialSearch]); | |
| useEffect(() => { | |
| storeSetSearch(searchQuery); | |
| }, [searchQuery, storeSetSearch]); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/PullRequestsView.tsx`
around lines 31 - 49, Update the initialSearch synchronization effect in
PullRequestsView so it calls setSearchQuery only when initialSearch is supplied,
preserving the storedSearch value on mount when initialSearch is undefined. Keep
subsequent updates to an explicitly provided initialSearch unchanged.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/layout.tsx (1)
211-214: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winInclude pull requests in the explanatory comment.
onDashboardViewRoutenow includes/pull-requests, but the comment lists onlyautomations/tasks/workspaces. Update the comment so it documents all routes covered byhideTopBar.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/layout.tsx` around lines 211 - 214, Update the explanatory comment near the hideTopBar logic to include pull requests alongside automations, tasks, and workspaces, documenting every route covered by onDashboardViewRoute.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/layout.tsx`:
- Around line 211-214: Update the explanatory comment near the hideTopBar logic
to include pull requests alongside automations, tasks, and workspaces,
documenting every route covered by onDashboardViewRoute.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 6756a257-e857-4802-888b-5b36a4b6cc74
📒 Files selected for processing (7)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHeader/DashboardSidebarHeader.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/ProjectFilter/ProjectFilter.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/WorkItemDetailHeader/WorkItemDetailHeader.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/layout.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/components/PullRequestsTopBar/PullRequestsTopBar.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/TasksView.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/TasksTopBar/TasksTopBar.tsx
🚧 Files skipped from review as they are similar to previous changes (6)
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/WorkItemDetailHeader/WorkItemDetailHeader.tsx
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/ProjectFilter/ProjectFilter.tsx
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHeader/DashboardSidebarHeader.tsx
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/TasksTopBar/TasksTopBar.tsx
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/components/PullRequestsTopBar/PullRequestsTopBar.tsx
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/TasksView.tsx
There was a problem hiding this comment.
13 issues found and verified against the latest diff
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/$prNumber/page.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/$prNumber/page.tsx:30">
P3: PR and GitHub issue detail flows now require parallel fixes whenever their shared loading, navigation, or workspace-linking behavior changes. A shared work-item detail shell or hook would keep these flows consistent and reduce maintenance drift.
(Based on your team's feedback about avoiding duplicated business logic.)</violation>
</file>
<file name="apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/WorkspaceSidebarHeader.tsx">
<violation number="1" location="apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/WorkspaceSidebarHeader.tsx:56">
P2: Opening a pull-request detail URL directly (or after a reload) and clicking the Pull requests rail item drops the detail's search/project/state context because this navigation uses the store instead of the current detail route search. Preserving the route search or synchronizing it from the detail page would keep the workflow context intact.</violation>
</file>
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/EditableTitle/EditableTitle.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/EditableTitle/EditableTitle.tsx:18">
P2: Resizing the task detail pane can clip wrapped title lines or leave excess vertical space because the autosize effect does not rerun when the textarea width changes. Observing the element with `ResizeObserver` (or another width-change signal) would keep the height synchronized.</violation>
<violation number="2" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/EditableTitle/EditableTitle.tsx:47">
P2: Pasting a multiline title can now persist embedded newlines even though this editor still treats Enter as a save/blur action. Normalizing line breaks before updating would preserve the previous single-line title behavior.</violation>
</file>
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/WorkItemDetailState/WorkItemDetailState.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/WorkItemDetailState/WorkItemDetailState.tsx:19">
P3: When `isError` is true, the `role="alert"` (implicit `aria-live="assertive"`) conflicts with the static `aria-live="polite"`. Explicit `aria-live` takes precedence over implicit values from the role, so error announcements may be treated as polite instead of assertive, reducing responsiveness for time-sensitive errors. Other `role="alert"` usages in the codebase omit `aria-live` entirely. Consider removing `aria-live` from the container or making it conditional so it's omitted when `role="alert"` is active.</violation>
</file>
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/WorkItemsSearch/WorkItemsSearch.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/WorkItemsSearch/WorkItemsSearch.tsx:22">
P3: The Pull Requests search inherits a shortcut whose user-facing label and description explicitly say it only focuses the task search, so keyboard-shortcut settings and discoverability are incorrect on `/pull-requests`. A destination-neutral work-items shortcut (or a dedicated PR shortcut with matching metadata) would keep the new destination’s context accurate.</violation>
</file>
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsx:102">
P3: The GitHub issues and pull-request destinations now maintain nearly identical query, placeholder, pagination, and rendering-state workflows separately, so fixes to scoping or retry behavior can diverge between destinations. A shared work-item query/render helper would keep these flows consistent.
(Based on your team's feedback about avoiding duplicated business logic.)</violation>
<violation number="2" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsx:106">
P2: Changing the serving host can leave selected GitHub issues from the previous host in the toolbar while the list has reloaded from the new host, allowing actions to use stale issue URLs and metadata. The selection reset should follow the same host-scoped lifecycle as the query, not only project changes.</violation>
<violation number="3" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsx:224">
P2: Offline remote projects do not reach this unavailable state: cached project host IDs can remain selected while `useHostUrl` still returns a relay URL, so the request runs and exposes a generic fetch error instead of the dedicated unavailable-device message. Propagating host reachability into this component, or selecting only reachable serving hosts, would make the state accurate.</violation>
</file>
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHeader/DashboardSidebarHeader.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHeader/DashboardSidebarHeader.tsx:155">
P2: Both `DashboardSidebarHeader` and `WorkspaceSidebarHeader` now contain near-identical `handlePullRequestsClick` functions, identical `usePullRequestsFilterStore` subscriptions, and identical `includeClosedIssues` additions to `handleTasksClick`. This duplicated business logic means any future change to PR navigation behavior (adding a filter param, changing the gate feature, or altering the navigate call) must be applied in two separate files. The pattern matches prior team feedback about extracting shared navigation logic into a hook or helper to keep behavior consistent and reduce cognitive load when changes are made.</violation>
</file>
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/PullRequestsView.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/PullRequestsView.tsx:65">
P3: Filter synchronization and project fallback behavior are duplicated between `PullRequestsView` and `TasksView`, which makes navigation fixes easy to apply to one destination but miss the other. A shared workflow hook/helper for the common debounce, URL serialization, store synchronization, and first-project selection would keep the two destinations consistent while retaining separate filter stores.</violation>
<violation number="2" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/PullRequestsView.tsx:128">
P2: Changing the repository or state filter can be reverted by a pending search URL update: the 300 ms callback retains the previous `buildSearch` closure, while these immediate filter handlers do not cancel it. Cancel the pending debounce before non-search navigation (or have the callback read the latest route/filter state) so the final URL reflects the user's last action.</violation>
</file>
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/TasksView.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/TasksView.tsx:267">
P2: A pending search debounce can overwrite the URL state selected by the GitHub issue Open/All filter: typing and then toggling the filter within 300 ms can remove `state=all` from the URL. Clearing the existing search debounce before this immediate navigation would keep the URL and issue-state selection consistent.</violation>
</file>
Tip: cubic used a learning from your PR history. Let your coding agent read cubic learnings directly with the cubic MCP.
Re-trigger cubic
| includeClosedIssues: lastIncludeClosedIssues, | ||
| } = useTasksFilterStore(); | ||
| const { | ||
| search: lastPullRequestsSearch, |
There was a problem hiding this comment.
P2: Opening a pull-request detail URL directly (or after a reload) and clicking the Pull requests rail item drops the detail's search/project/state context because this navigation uses the store instead of the current detail route search. Preserving the route search or synchronizing it from the detail page would keep the workflow context intact.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/WorkspaceSidebarHeader.tsx, line 56:
<comment>Opening a pull-request detail URL directly (or after a reload) and clicking the Pull requests rail item drops the detail's search/project/state context because this navigation uses the store instead of the current detail route search. Preserving the route search or synchronizing it from the detail page would keep the workflow context intact.</comment>
<file context>
@@ -41,7 +50,13 @@ export function WorkspaceSidebarHeader({
+ includeClosedIssues: lastIncludeClosedIssues,
} = useTasksFilterStore();
+ const {
+ search: lastPullRequestsSearch,
+ projectFilter: lastPullRequestsProjectFilter,
+ includeClosed: lastPullRequestsIncludeClosed,
</file context>
|
|
||
| return ( | ||
| <input | ||
| <textarea |
There was a problem hiding this comment.
P2: Pasting a multiline title can now persist embedded newlines even though this editor still treats Enter as a save/blur action. Normalizing line breaks before updating would preserve the previous single-line title behavior.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/EditableTitle/EditableTitle.tsx, line 47:
<comment>Pasting a multiline title can now persist embedded newlines even though this editor still treats Enter as a save/blur action. Normalizing line breaks before updating would preserve the previous single-line title behavior.</comment>
<file context>
@@ -35,15 +44,16 @@ export function EditableTitle({ value, onSave }: EditableTitleProps) {
return (
- <input
+ <textarea
ref={inputRef}
- type="text"
</file context>
| useLayoutEffect(() => { | ||
| const input = inputRef.current; | ||
| if (!input) return; | ||
| input.style.height = "auto"; | ||
| input.style.height = `${input.scrollHeight}px`; | ||
| }, [localValue]); |
There was a problem hiding this comment.
P2: Resizing the task detail pane can clip wrapped title lines or leave excess vertical space because the autosize effect does not rerun when the textarea width changes. Observing the element with ResizeObserver (or another width-change signal) would keep the height synchronized.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/components/EditableTitle/EditableTitle.tsx, line 18:
<comment>Resizing the task detail pane can clip wrapped title lines or leave excess vertical space because the autosize effect does not rerun when the textarea width changes. Observing the element with `ResizeObserver` (or another width-change signal) would keep the height synchronized.</comment>
<file context>
@@ -7,13 +7,21 @@ interface EditableTitleProps {
}, [value]);
+ // biome-ignore lint/correctness/useExhaustiveDependencies: re-measure height whenever the rendered text changes
+ useLayoutEffect(() => {
+ const input = inputRef.current;
+ if (!input) return;
</file context>
| useLayoutEffect(() => { | |
| const input = inputRef.current; | |
| if (!input) return; | |
| input.style.height = "auto"; | |
| input.style.height = `${input.scrollHeight}px`; | |
| }, [localValue]); | |
| useLayoutEffect(() => { | |
| const input = inputRef.current; | |
| if (!input) return; | |
| const resize = () => { | |
| input.style.height = "auto"; | |
| input.style.height = `${input.scrollHeight}px`; | |
| }; | |
| resize(); | |
| const observer = new ResizeObserver(resize); | |
| observer.observe(input); | |
| return () => observer.disconnect(); | |
| }, [localValue]); |
| ); | ||
| } | ||
|
|
||
| if (!hostId || !hostUrl) { |
There was a problem hiding this comment.
P2: Offline remote projects do not reach this unavailable state: cached project host IDs can remain selected while useHostUrl still returns a relay URL, so the request runs and exposes a generic fetch error instead of the dedicated unavailable-device message. Propagating host reachability into this component, or selecting only reachable serving hosts, would make the state accurate.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsx, line 224:
<comment>Offline remote projects do not reach this unavailable state: cached project host IDs can remain selected while `useHostUrl` still returns a relay URL, so the request runs and exposes a generic fetch error instead of the dedicated unavailable-device message. Propagating host reachability into this component, or selecting only reachable serving hosts, would make the state accurate.</comment>
<file context>
@@ -195,7 +209,26 @@ export function GitHubIssuesContent({
+ );
+ }
+
+ if (!hostId || !hostUrl) {
+ return (
+ <div className="flex h-full items-center justify-center p-8">
</file context>
| }); | ||
| }; | ||
|
|
||
| const handlePullRequestsClick = () => { |
There was a problem hiding this comment.
P2: Both DashboardSidebarHeader and WorkspaceSidebarHeader now contain near-identical handlePullRequestsClick functions, identical usePullRequestsFilterStore subscriptions, and identical includeClosedIssues additions to handleTasksClick. This duplicated business logic means any future change to PR navigation behavior (adding a filter param, changing the gate feature, or altering the navigate call) must be applied in two separate files. The pattern matches prior team feedback about extracting shared navigation logic into a hook or helper to keep behavior consistent and reduce cognitive load when changes are made.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHeader/DashboardSidebarHeader.tsx, line 156:
<comment>Both `DashboardSidebarHeader` and `WorkspaceSidebarHeader` now contain near-identical `handlePullRequestsClick` functions, identical `usePullRequestsFilterStore` subscriptions, and identical `includeClosedIssues` additions to `handleTasksClick`. This duplicated business logic means any future change to PR navigation behavior (adding a filter param, changing the gate feature, or altering the navigate call) must be applied in two separate files. The pattern matches prior team feedback about extracting shared navigation logic into a hook or helper to keep behavior consistent and reduce cognitive load when changes are made.</comment>
<file context>
@@ -132,6 +147,20 @@ export function DashboardSidebarHeader({
+ });
+ };
+
+ const handlePullRequestsClick = () => {
+ gateFeature(GATED_FEATURES.TASKS, () => {
+ navigate({
</file context>
| component: PullRequestDetailPage, | ||
| }); | ||
|
|
||
| function PullRequestDetailPage() { |
There was a problem hiding this comment.
P3: PR and GitHub issue detail flows now require parallel fixes whenever their shared loading, navigation, or workspace-linking behavior changes. A shared work-item detail shell or hook would keep these flows consistent and reduce maintenance drift.
(Based on your team's feedback about avoiding duplicated business logic.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/$prNumber/page.tsx, line 30:
<comment>PR and GitHub issue detail flows now require parallel fixes whenever their shared loading, navigation, or workspace-linking behavior changes. A shared work-item detail shell or hook would keep these flows consistent and reduce maintenance drift.
(Based on your team's feedback about avoiding duplicated business logic.) </comment>
<file context>
@@ -0,0 +1,229 @@
+ component: PullRequestDetailPage,
+});
+
+function PullRequestDetailPage() {
+ const { prNumber: prNumberRaw } = Route.useParams();
+ const prNumber = parsePositiveIntegerParam(prNumberRaw);
</file context>
| onRetry, | ||
| }: WorkItemDetailStateProps) { | ||
| return ( | ||
| <div |
There was a problem hiding this comment.
P3: When isError is true, the role="alert" (implicit aria-live="assertive") conflicts with the static aria-live="polite". Explicit aria-live takes precedence over implicit values from the role, so error announcements may be treated as polite instead of assertive, reducing responsiveness for time-sensitive errors. Other role="alert" usages in the codebase omit aria-live entirely. Consider removing aria-live from the container or making it conditional so it's omitted when role="alert" is active.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/WorkItemDetailState/WorkItemDetailState.tsx, line 19:
<comment>When `isError` is true, the `role="alert"` (implicit `aria-live="assertive"`) conflicts with the static `aria-live="polite"`. Explicit `aria-live` takes precedence over implicit values from the role, so error announcements may be treated as polite instead of assertive, reducing responsiveness for time-sensitive errors. Other `role="alert"` usages in the codebase omit `aria-live` entirely. Consider removing `aria-live` from the container or making it conditional so it's omitted when `role="alert"` is active.</comment>
<file context>
@@ -0,0 +1,39 @@
+ onRetry,
+}: WorkItemDetailStateProps) {
+ return (
+ <div
+ className={cn(
+ "flex flex-1 flex-col items-center justify-center gap-3 px-6 py-10 text-center text-sm",
</file context>
| const inputRef = useRef<HTMLInputElement>(null); | ||
|
|
||
| useHotkey( | ||
| "FOCUS_TASK_SEARCH", |
There was a problem hiding this comment.
P3: The Pull Requests search inherits a shortcut whose user-facing label and description explicitly say it only focuses the task search, so keyboard-shortcut settings and discoverability are incorrect on /pull-requests. A destination-neutral work-items shortcut (or a dedicated PR shortcut with matching metadata) would keep the new destination’s context accurate.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/WorkItemsSearch/WorkItemsSearch.tsx, line 22:
<comment>The Pull Requests search inherits a shortcut whose user-facing label and description explicitly say it only focuses the task search, so keyboard-shortcut settings and discoverability are incorrect on `/pull-requests`. A destination-neutral work-items shortcut (or a dedicated PR shortcut with matching metadata) would keep the new destination’s context accurate.</comment>
<file context>
@@ -0,0 +1,52 @@
+ const inputRef = useRef<HTMLInputElement>(null);
+
+ useHotkey(
+ "FOCUS_TASK_SEARCH",
+ () => {
+ inputRef.current?.focus();
</file context>
| staleTime: 30_000, | ||
| gcTime: 10 * 60_000, | ||
| placeholderData: keepPreviousData, | ||
| placeholderData: (previousData, previousQuery) => { |
There was a problem hiding this comment.
P3: The GitHub issues and pull-request destinations now maintain nearly identical query, placeholder, pagination, and rendering-state workflows separately, so fixes to scoping or retry behavior can diverge between destinations. A shared work-item query/render helper would keep these flows consistent.
(Based on your team's feedback about avoiding duplicated business logic.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsx, line 102:
<comment>The GitHub issues and pull-request destinations now maintain nearly identical query, placeholder, pagination, and rendering-state workflows separately, so fixes to scoping or retry behavior can diverge between destinations. A shared work-item query/render helper would keep these flows consistent.
(Based on your team's feedback about avoiding duplicated business logic.) </comment>
<file context>
@@ -99,7 +99,15 @@ export function GitHubIssuesContent({
staleTime: 30_000,
gcTime: 10 * 60_000,
- placeholderData: keepPreviousData,
+ placeholderData: (previousData, previousQuery) => {
+ return shouldKeepWorkItemsPlaceholder(
+ previousQuery?.queryKey,
</file context>
| [includeClosed, projectFilter, searchQuery], | ||
| ); | ||
|
|
||
| const syncSearchToUrl = useCallback( |
There was a problem hiding this comment.
P3: Filter synchronization and project fallback behavior are duplicated between PullRequestsView and TasksView, which makes navigation fixes easy to apply to one destination but miss the other. A shared workflow hook/helper for the common debounce, URL serialization, store synchronization, and first-project selection would keep the two destinations consistent while retaining separate filter stores.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/PullRequestsView.tsx, line 65:
<comment>Filter synchronization and project fallback behavior are duplicated between `PullRequestsView` and `TasksView`, which makes navigation fixes easy to apply to one destination but miss the other. A shared workflow hook/helper for the common debounce, URL serialization, store synchronization, and first-project selection would keep the two destinations consistent while retaining separate filter stores.</comment>
<file context>
@@ -0,0 +1,171 @@
+ [includeClosed, projectFilter, searchQuery],
+ );
+
+ const syncSearchToUrl = useCallback(
+ (query: string) => {
+ if (debounceRef.current) clearTimeout(debounceRef.current);
</file context>
…stored-search seed Claude-Session: https://claude.ai/code/session_01WPvjzEZjYWFoedQUiCKBPb
…ests views Extends the hidden-TopBar treatment from ParadevAI#6023 to the new /pull-requests route: the layout hides the strip there, and the pull-requests toolbar and shared work-item detail header carry the window-drag filler leaves. Claude-Session: https://claude.ai/code/session_01WPvjzEZjYWFoedQUiCKBPb
555b2d6 to
c352f78
Compare
* docs: weekly update for PRs merged 2026-07-29 to 2026-08-05 - tasks.mdx: Tasks and Pull requests are now separate sidebar destinations with independent persisted filters; GitHub Issues as a Tasks source (#5975) - workspaces.mdx: prompt history popover (#6083), typed-name-seeds-branch behavior (#6129), right-click import of untracked worktrees (#6097), bulk workspace actions section (#6021) - automations.mdx: inline Retry and Retry all for failed runs (#6139) - keyboard-shortcuts.mdx: Open Command Palette ⌘⇧K and Check Resources ⌘⇧U (#6037) - setup-teardown-scripts.mdx: experimental wait-for-setup-before-agents setting (#5925) * docs(recipes): add agent-driven Superset recipe for the shipped superset:* skills Covers the workflow from #6181: standup, automate, doctor, setup, feedback, and orchestrate driven from the agent you're already in. * docs(recipes): focus agent-driven recipe on orchestrate and automate, defer to skills page * docs(recipes): defer orchestrate teaching to its page, center recipe on the automate arc * docs: address review — branch name is slugified, retry scoping, Linear source wording
…hes (#8440) * feat(desktop): add a refresh button to the workspace PR pane The button refetches the PR detail and marks the linked PR, review threads, and Code tab diff and threads stale, so the pane reloads what is on screen. It spins while the detail fetch runs. * perf(desktop): stop the PR view re-rendering and refetching unchanged data - Code tab: each file's version is a hash of its patch, its thread annotations, and its collapsed state, so a 30 s thread poll with no change redraws no files (155 of 155 before on a 155-file PR). This replaces the composer and collapse version counters. - Code tab: give each file a content-addressed cacheKey. Without one, @pierre/diffs falls back to the file name and keeps rendering a file's old content after its patch changes. - Code tab: no diff refetch on window focus. The pane's refresh button covers a manual reload. - Memoize MarkdownRenderer and PullRequestMarkdown and stop spreading the detail query result, so a refetch with identical data re-renders no comment bodies (40 before on a 10-body summary). - Persist page 1 of the default PR list again. #5975 moved the list off the persisted "tasks" key, so every cold start waited 4-5 s on the spinner. - Move hashString to _dashboard/utils now that two areas use it. * fix(desktop): scope PR pane refresh to its PR and harden Code tab keys - The refresh spinner watches only this PR's detail query, and refresh invalidates only this PR's diff and threads, so two PR panes no longer spin or reload each other. - Code tab file keys use the Changes pane's content key (two independent FNV-1a hashes plus length) instead of one 32-bit hash. buildContentKey moves to _dashboard/utils for both. - Keep window-focus refetch for the diff: the standalone PR page has no refresh button, and an identical refetch now redraws no files. The workspace client already turns focus refetch off for the pane. * fix(desktop): refresh only the pane's own diff and thread queries The refresh button matched diff and thread queries by PR number, so a PR with the same number in another project was refetched too. The Code tab query keys now come from shared builders, and refresh invalidates the exact keys for this pane's project, host, repository and PR. * feat(desktop): put the PR refresh button next to copy link The refresh button moves from the workspace pane's header into the PR detail's action bar, before copy link, so it shows on the Pull requests page and in the workspace pane. useRefreshPullRequest holds the refresh logic for both: it refetches the detail and invalidates this PR's exact diff and thread queries, and the workspace's linked-PR queries. * fix(desktop): match refresh's diff key to the Code tab and narrow list persistence - Refresh derives the diff key's repository from the PR's own URL, the same way the Code tab does, so a PR whose URL names another repository is still refreshed. - The PR list is persisted only without an author or review filter, as with search: those are typed in and unbounded. Page 1 of each of the three state tabs is still saved. * fix(desktop): key the persisted PR list by host id, not host URL The host-service port can change between launches, so a list key that included the host URL never matched the persisted page after a restart and the list fell back to the spinner. The key now relies on target.key, which already names the host by id and its projects, and the previous rows placeholder compares that key alone. Measured on a restart where the port changed: rows 0.66 s after the shell is ready, no spinner.
What & why
Closes #5974.
Tasks and pull requests are distinct workflows with different filters, empty states, and detail journeys, but the desktop dashboard placed them behind one “Tasks & PRs” destination and repeated the same choice as page-level tabs. This made the surface dense and gave navigation two competing sources of truth.
This change:
/pull-requestsroute with independent repository, state, and search context/tasks?type=prsand/tasks/pr/:prNumberlinks while preserving relevant filtersHow I tested it
bun run lint:fixbun run lint— 0 warnings/errorsbun run typecheck— 35/35 tasks passbun run --cwd apps/desktop compile:app— production main, preload, and renderer builds pass3085, API3081, Electric proxy3092, and CDP9307:The full local
bun testmonorepo run was also attempted: 4,284 passed, while 129 failed with 20 harness errors in unrelated environment-dependent PTY, Electron preload, localStorage, relay, and git-process suites. The changed-area tests, lint, root typecheck, production compile, and exact-app flows are green; CI remains the repository-wide acceptance gate.Before: combined destination and page tabs
Populated Tasks workflow
Task detail pane
Populated GitHub issues inside Tasks
Populated Pull requests workflow
Compact 960×720 verification with populated data
Tasks
GitHub issues
Pull requests
Checklist
type(scope): subject)bun run lintandbun run typecheckpassSummary by cubic
Split Tasks and Pull requests into separate sidebar destinations with a new
/pull-requestsroute and PR detail page. Each workflow has its own filters and search, picks the right serving host, task titles auto-resize while editing, and pull-requests views hide the empty TopBar while keeping window drag regions.New Features
/pull-requestsroute with project filter, Open/All toggle, and search; filters persist independently from Tasks and are stored underpull-requests-filter-state./pull-requests/$prNumberdetail page with shared header/state UI, back navigation, external link, and “Add to workspace”.ProjectFilter,OpenClosedFilter, andWorkItemsSearch; each destination restores its own filters and drops stale rows when project/host changes.Migration
/tasks?type=prsand/tasks/pr/:prNumberredirect to/pull-requestsand/pull-requests/$prNumber(preserves search/state).Written for commit 62e9192. Summary will update on new commits.
Summary by CodeRabbit
New Features
Bug Fixes
Tests