diff --git a/apps/staged/src/App.svelte b/apps/staged/src/App.svelte index 0c11b85a..ef7d682e 100644 --- a/apps/staged/src/App.svelte +++ b/apps/staged/src/App.svelte @@ -47,6 +47,7 @@ import { runSearchShortcut } from './lib/features/keyboard/searchTargets'; import { projectStateStore } from './lib/stores/projectState.svelte'; import { projectsDataStore } from './lib/stores/projectsData.svelte'; + import { projectRunActionsStore } from './lib/stores/projectRunActions.svelte'; import { initBloxEnv } from './lib/stores/bloxEnv.svelte'; import { listenForSessionStatus } from './lib/listeners/sessionStatusListener'; import { listenForCacheInvalidation } from './lib/listeners/cacheInvalidationListener'; @@ -309,6 +310,12 @@ // Keep the shared project-list cache fresh for the app's lifetime — the // store dedupes, so starting before any view consumes it is safe. projectsDataStore.startListeners(); + // Run-action state feeds the shared project filters (Running chip, + // filtered lists), which every route renders via the sidebar or the + // landing grid — so its listeners are app-lifetime too. A view-scoped + // lifecycle wiped the state on the repos route, where neither view that + // used to own it is mounted. + projectRunActionsStore.startListening(); try { await initPreferences(); @@ -499,6 +506,7 @@ unlistenPageLifecycle?.(); unlistenAcpToolsReconciled?.(); projectsDataStore.stopListeners(); + projectRunActionsStore.stopListening(); stopUpdaterLoop?.(); }); diff --git a/apps/staged/src/lib/features/projects/ProjectFilterChips.svelte b/apps/staged/src/lib/features/projects/ProjectFilterChips.svelte new file mode 100644 index 00000000..ed261f55 --- /dev/null +++ b/apps/staged/src/lib/features/projects/ProjectFilterChips.svelte @@ -0,0 +1,226 @@ + + + +
+ + diff --git a/apps/staged/src/lib/features/projects/ProjectHome.svelte b/apps/staged/src/lib/features/projects/ProjectHome.svelte index 8763fd92..3eaa8965 100644 --- a/apps/staged/src/lib/features/projects/ProjectHome.svelte +++ b/apps/staged/src/lib/features/projects/ProjectHome.svelte @@ -128,14 +128,14 @@ onMount(() => { // Backend/window listeners for the shared data (pr-status-changed, // session-status-changed, project-setup-progress, cache-stale) live in - // the projectsData store, started once from App.svelte. + // the projectsData and projectRunActions stores, started once from + // App.svelte. workspaceLifecycle.start({ getBranchesByProject: () => projectsDataStore.branchesByProject, setBranchesByProject: (next) => projectsDataStore.setBranchesByProject(next), isProjectDeleting: (projectId) => projectsDataStore.isProjectDeleting(projectId), }); checkStoreAndLoad(); - void projectRunActionsStore.startListening(); const onNewProject = (event: Event) => handleNewProject(repoSeedFromNewProjectEvent(event)); window.addEventListener('staged:new-project', onNewProject); @@ -165,7 +165,6 @@ unlistenDetection(); cancelQueuedSessionDrain(); workspaceLifecycle.stop(); - projectRunActionsStore.stopListening(); }; }); diff --git a/apps/staged/src/lib/features/projects/ProjectsList.svelte b/apps/staged/src/lib/features/projects/ProjectsList.svelte index 880a85c5..fa5cd849 100644 --- a/apps/staged/src/lib/features/projects/ProjectsList.svelte +++ b/apps/staged/src/lib/features/projects/ProjectsList.svelte @@ -44,17 +44,18 @@ } from './projectsListViewState.svelte'; import { darkMode } from '../../stores/isDark.svelte'; import { repoBadgeStore } from '../../stores/repoBadges.svelte'; - import { badgeBg, badgeFg, badgeBgHover } from '../../shared/badgeColors'; + import { badgeBg, badgeFg } from '../../shared/badgeColors'; + import { projectFiltersStore } from './projectFilters.svelte'; + import ProjectFilterChips from './ProjectFilterChips.svelte'; import { repoSeedFromNewProjectEvent } from './newProjectEvent'; import type { RepoSelection } from '../../shared/githubUrl'; import { viewport } from '../../shared/viewport.svelte'; import TopBarPortal from '../layout/TopBarPortal.svelte'; - type FilterKind = 'unread' | 'running' | { repo: string; subpath: string }; - // Data comes from the shared projectsData store — returning to the landing // page paints instantly from memory while the store revalidates in the - // background. Filters, scroll restore, and modal state stay view-local. + // background. Filters live in the shared projectFiltersStore so the sidebar + // sees the same selection; scroll restore and modal state stay view-local. let projects = $derived(projectsDataStore.projects); let projectBranches = $derived(projectsDataStore.branchesByProject); let reposByProject = $derived(projectsDataStore.reposByProject); @@ -71,127 +72,40 @@ let newProjectInitialRepo = $state