diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md
index a3ad1d28..13d73138 100644
--- a/docs/USER_GUIDE.md
+++ b/docs/USER_GUIDE.md
@@ -367,13 +367,13 @@ The Tracked tab lets you pin issues and PRs into a personal TODO list that you c
## Repo Pinning
-Each repo group header has a pin (lock) control, visible on hover on desktop and always visible on mobile. Pinning a repo keeps it at the top of the list on all tabs regardless of sort order or how recently it was updated. Pinned repos remain visible even when filters exclude all their items — they appear as compact, de-emphasized rows with the repo name and pin controls still accessible.
+Each repo group header has a pin (lock) control, visible on hover on desktop and always visible on mobile. Pinning a repo keeps it at the top of the list regardless of sort order or how recently it was updated. Pinned repos remain visible even when filters exclude all their items — they appear as compact, de-emphasized rows with the repo name and pin controls still accessible.
- Click the pin icon to pin a repo to the top.
- Click it again to unpin.
- Use the up/down arrows (visible when pinned) to reorder pinned repos relative to each other.
-Pin state is shared across all tabs — pinning a repo on the Issues tab also pins it on Pull Requests and Actions.
+Pin state is per-tab — pinning a repo on the Issues tab does not pin it on Pull Requests or Actions. Each tab maintains its own independent pin list and ordering.
---
@@ -455,7 +455,7 @@ These are UI preferences that persist across sessions but are not included in th
| Show PR runs (Actions) | Off | Whether to show workflow runs triggered by pull request events. |
| Hide Dependency Dashboard | On | Whether to hide the Renovate Dependency Dashboard issue. |
| Sort preferences | Updated (desc) | Sort field and direction per tab, remembered across sessions. |
-| Pinned repos | (none) | Repos pinned to the top of the list across all tabs. |
+| Pinned repos | (none) | Repos pinned to the top of the list, stored per tab independently. |
| Tracked items | (none) | Issues and PRs pinned to the Tracked tab (max 200). |
---
diff --git a/src/app/components/dashboard/ActionsTab.tsx b/src/app/components/dashboard/ActionsTab.tsx
index 062c306d..260c3ee4 100644
--- a/src/app/components/dashboard/ActionsTab.tsx
+++ b/src/app/components/dashboard/ActionsTab.tsx
@@ -189,23 +189,24 @@ export default function ActionsTab(props: ActionsTabProps) {
const repoGroups = createMemo(() => {
const groups = groupRuns(filteredRuns());
+ const lockedForTab = viewState.lockedRepos[tabKey()] ?? [];
const withLocked = ensureLockedRepoGroups(
groups,
- viewState.lockedRepos,
+ lockedForTab,
(name) => ({ repoFullName: name, workflows: [] }),
);
- return orderRepoGroups(withLocked, viewState.lockedRepos);
+ return orderRepoGroups(withLocked, lockedForTab);
});
createEffect(() => {
const names = activeRepoNames();
if (names.length === 0) return;
- pruneLockedRepos(names);
+ pruneLockedRepos(tabKey(), names);
});
const highlightedReposActions = createReorderHighlight(
() => repoGroups().map(g => g.repoFullName),
- () => viewState.lockedRepos,
+ () => viewState.lockedRepos[tabKey()] ?? [],
() => ignoredWorkflowRuns().length,
() => JSON.stringify(props.customTabId
? (viewState.customTabFilters[props.customTabId] ?? {})
@@ -291,7 +292,7 @@ export default function ActionsTab(props: ActionsTabProps) {
+
}
>
@@ -303,7 +304,7 @@ export default function ActionsTab(props: ActionsTabProps) {
trailing={
<>
-
+
>
}
collapsedSummary={
diff --git a/src/app/components/dashboard/DashboardPage.tsx b/src/app/components/dashboard/DashboardPage.tsx
index 035a2309..fed30b6e 100644
--- a/src/app/components/dashboard/DashboardPage.tsx
+++ b/src/app/components/dashboard/DashboardPage.tsx
@@ -703,6 +703,7 @@ export default function DashboardPage() {
const keys = new Set([
...Object.keys(viewState.customTabFilters),
...Object.keys(viewState.expandedRepos).filter((k) => !isBuiltinTab(k)),
+ ...Object.keys(viewState.lockedRepos).filter((k) => !isBuiltinTab(k)),
]);
return [...keys].filter((id) => !activeIds.has(id));
});
diff --git a/src/app/components/dashboard/IssuesTab.tsx b/src/app/components/dashboard/IssuesTab.tsx
index 3ae69934..07917b4e 100644
--- a/src/app/components/dashboard/IssuesTab.tsx
+++ b/src/app/components/dashboard/IssuesTab.tsx
@@ -187,12 +187,13 @@ export default function IssuesTab(props: IssuesTabProps) {
const repoGroups = createMemo(() => {
const groups = groupByRepo(filteredSorted());
+ const lockedForTab = viewState.lockedRepos[tabKey()] ?? [];
const withLocked = ensureLockedRepoGroups(
groups,
- viewState.lockedRepos,
+ lockedForTab,
(name) => ({ repoFullName: name, items: [] as typeof groups[0]["items"] }),
);
- return orderRepoGroups(withLocked, viewState.lockedRepos);
+ return orderRepoGroups(withLocked, lockedForTab);
});
const pageLayout = createMemo(() => computePageLayout(repoGroups(), config.itemsPerPage));
const pageCount = createMemo(() => pageLayout().pageCount);
@@ -218,7 +219,7 @@ export default function IssuesTab(props: IssuesTabProps) {
createEffect(() => {
const names = activeRepoNames();
if (names.length === 0) return;
- pruneLockedRepos(names);
+ pruneLockedRepos(tabKey(), names);
});
const trackedIssueIds = createMemo(() =>
@@ -229,7 +230,7 @@ export default function IssuesTab(props: IssuesTabProps) {
const highlightedReposIssues = createReorderHighlight(
() => repoGroups().map(g => g.repoFullName),
- () => viewState.lockedRepos,
+ () => viewState.lockedRepos[tabKey()] ?? [],
() => ignoredIssues().length,
() => JSON.stringify(props.customTabId
? (viewState.customTabFilters[props.customTabId] ?? {})
@@ -355,7 +356,7 @@ export default function IssuesTab(props: IssuesTabProps) {
+
}
>
@@ -375,7 +376,7 @@ export default function IssuesTab(props: IssuesTabProps) {
trailing={
<>
-
+
>
}
collapsedSummary={
diff --git a/src/app/components/dashboard/PullRequestsTab.tsx b/src/app/components/dashboard/PullRequestsTab.tsx
index defdf9c8..9bda6dc1 100644
--- a/src/app/components/dashboard/PullRequestsTab.tsx
+++ b/src/app/components/dashboard/PullRequestsTab.tsx
@@ -251,12 +251,13 @@ export default function PullRequestsTab(props: PullRequestsTabProps) {
const repoGroups = createMemo(() => {
const groups = groupByRepo(filteredSorted());
+ const lockedForTab = viewState.lockedRepos[tabKey()] ?? [];
const withLocked = ensureLockedRepoGroups(
groups,
- viewState.lockedRepos,
+ lockedForTab,
(name) => ({ repoFullName: name, items: [] as typeof groups[0]["items"] }),
);
- return orderRepoGroups(withLocked, viewState.lockedRepos);
+ return orderRepoGroups(withLocked, lockedForTab);
});
const pageLayout = createMemo(() => computePageLayout(repoGroups(), config.itemsPerPage));
const pageCount = createMemo(() => pageLayout().pageCount);
@@ -282,7 +283,7 @@ export default function PullRequestsTab(props: PullRequestsTabProps) {
createEffect(() => {
const names = activeRepoNames();
if (names.length === 0) return;
- pruneLockedRepos(names);
+ pruneLockedRepos(tabKey(), names);
});
const { flashingIds: flashingPRIds, peekUpdates } = createFlashDetection({
@@ -302,7 +303,7 @@ export default function PullRequestsTab(props: PullRequestsTabProps) {
const highlightedReposPRs = createReorderHighlight(
() => repoGroups().map(g => g.repoFullName),
- () => viewState.lockedRepos,
+ () => viewState.lockedRepos[tabKey()] ?? [],
() => ignoredPullRequests().length,
() => JSON.stringify(props.customTabId
? (viewState.customTabFilters[props.customTabId] ?? {})
@@ -429,7 +430,7 @@ export default function PullRequestsTab(props: PullRequestsTabProps) {
+
}
>
@@ -449,7 +450,7 @@ export default function PullRequestsTab(props: PullRequestsTabProps) {
trailing={
<>
-
+
>
}
collapsedSummary={
diff --git a/src/app/components/shared/EmptyLockedRepoRow.tsx b/src/app/components/shared/EmptyLockedRepoRow.tsx
index 74b1974e..934c075b 100644
--- a/src/app/components/shared/EmptyLockedRepoRow.tsx
+++ b/src/app/components/shared/EmptyLockedRepoRow.tsx
@@ -4,6 +4,7 @@ import RepoLockControls from "./RepoLockControls";
export default function EmptyLockedRepoRow(props: {
repoFullName: string;
section: "issues" | "pulls" | "actions";
+ tabKey: string;
}) {
return (
{props.repoFullName}
-
+
);
}
diff --git a/src/app/components/shared/RepoLockControls.tsx b/src/app/components/shared/RepoLockControls.tsx
index be6cbf25..1e4dbf8f 100644
--- a/src/app/components/shared/RepoLockControls.tsx
+++ b/src/app/components/shared/RepoLockControls.tsx
@@ -5,11 +5,12 @@ import { withFlipAnimation } from "../../lib/scroll";
interface RepoLockControlsProps {
repoFullName: string;
+ tabKey: string;
}
export default function RepoLockControls(props: RepoLockControlsProps) {
const lockInfo = createMemo(() => {
- const list = viewState.lockedRepos;
+ const list = viewState.lockedRepos[props.tabKey] ?? [];
const idx = list.indexOf(props.repoFullName);
return {
isLocked: idx !== -1,
@@ -26,7 +27,7 @@ export default function RepoLockControls(props: RepoLockControlsProps) {