mirror of
https://github.com/stablyai/orca.git
synced 2026-11-14 01:47:24 +00:00
* Hide projects from the sidebar without stopping them
Adds Repo.hiddenAt, a per-host, view-only flag. A hidden project's
workspaces, empty header and project-filter entry leave the desktop
sidebar, the paired phone's list, the new-workspace picker and the
palette's project filter. Its terminals, agents and automations keep
running, and repo.list / worktree.ps still list it.
- Hide Project in the project menu (toast with Undo), a collapsed
"N hidden projects" list under the workspace list with Show buttons,
and a Hide from sidebar switch in Settings > Projects.
- repo.setHidden RPC ({repo, repoHostId?, hidden} -> {repo, hostId,
hidden}) and `orca repo hide|unhide [--host]`; `repo list` marks
hidden rows.
- Opening a hidden project's workspace (notification, CLI, deep link)
focuses it without unhiding it, lifting sidebar filters, or leaving a
pending sidebar reveal.
- repo.update echo check: a host that drops hiddenAt reports failure
instead of reading as hidden.
* Address hidden-projects review: multi-host hide, picker scope, mute, stale filters, shared predicate, restore list styling
* Hidden projects re-review: keep saved filters, Agents filter, composer seed, per-toast Undo, shared default host
* Hidden projects: address review threads (pre-feed, palette search, notices, dialog default, seed remap, row keys, a11y)
* Hidden projects: no implicit dialog default when every project is hidden
87 lines
3.5 KiB
TypeScript
87 lines
3.5 KiB
TypeScript
import type { ProjectGroup } from './project-group-types'
|
|
import type { ProjectHostSetupProjection } from './project-host-setup-projection'
|
|
import type { ProjectSectionProject, ProjectSectionSetup } from './project-section-grouping'
|
|
import type { RepoIcon } from './repo-icon'
|
|
import type { Repo } from './repo-types'
|
|
import type { ProjectOrderBy } from './ui-chrome-types'
|
|
|
|
// Desktop owns the sidebar grouping inputs the phone must match.
|
|
export const MOBILE_PROJECT_SECTIONS_METHOD = 'mobileRelay.projectSections'
|
|
|
|
/** A sidebar repo as Project sections read it; never its hooks, credentials or AI settings. */
|
|
export type MobileProjectSectionRepo = Pick<Repo, 'id' | 'displayName' | 'path' | 'badgeColor'> &
|
|
Partial<
|
|
Pick<
|
|
Repo,
|
|
| 'addedAt'
|
|
| 'connectionId'
|
|
| 'executionHostId'
|
|
| 'projectGroupId'
|
|
| 'projectGroupOrder'
|
|
| 'hiddenAt'
|
|
>
|
|
> & { repoIcon?: RepoIcon }
|
|
|
|
export type MobileProjectSectionCatalog = {
|
|
/** Every repo the sidebar holds, across hosts, in its header order. */
|
|
repos: MobileProjectSectionRepo[]
|
|
projects: ProjectSectionProject[]
|
|
setups: ProjectSectionSetup[]
|
|
projectOrderBy: ProjectOrderBy
|
|
/** Every host's project groups, which a feed reader lists folder workspaces under; older desktops omit it. */
|
|
projectGroups?: ProjectGroup[]
|
|
}
|
|
|
|
/** Null until a desktop window has published its sidebar catalog. */
|
|
export type MobileProjectSectionsResult = { catalog: MobileProjectSectionCatalog | null }
|
|
|
|
function sectionRepo(repo: Repo): MobileProjectSectionRepo {
|
|
return {
|
|
id: repo.id,
|
|
displayName: repo.displayName,
|
|
path: repo.path,
|
|
badgeColor: repo.badgeColor,
|
|
addedAt: repo.addedAt,
|
|
...(repo.connectionId !== undefined ? { connectionId: repo.connectionId } : {}),
|
|
...(repo.executionHostId ? { executionHostId: repo.executionHostId } : {}),
|
|
// Why: the phone's list files a project under its group, in the group's order.
|
|
...(repo.projectGroupId ? { projectGroupId: repo.projectGroupId } : {}),
|
|
...(typeof repo.projectGroupOrder === 'number'
|
|
? { projectGroupOrder: repo.projectGroupOrder }
|
|
: {}),
|
|
// Why: a reader must drop a hidden project's header and rows, as the sidebar does.
|
|
...(typeof repo.hiddenAt === 'number' ? { hiddenAt: repo.hiddenAt } : {}),
|
|
// Exclude uploaded data URLs: each repo icon can add hundreds of KB to every read.
|
|
...(repo.repoIcon && !(repo.repoIcon.type === 'image' && repo.repoIcon.src.startsWith('data:'))
|
|
? { repoIcon: repo.repoIcon }
|
|
: {})
|
|
}
|
|
}
|
|
|
|
/** The sidebar's repos, in its order, and the projection its project-header selector produced. */
|
|
export function buildMobileProjectSectionCatalog(
|
|
repos: readonly Repo[],
|
|
projection: ProjectHostSetupProjection,
|
|
projectOrderBy: ProjectOrderBy,
|
|
projectGroups?: readonly ProjectGroup[]
|
|
): MobileProjectSectionCatalog {
|
|
return {
|
|
repos: repos.map(sectionRepo),
|
|
projects: projection.projects.map(({ id, displayName }) => ({ id, displayName })),
|
|
setups: projection.setups.map((setup) => ({
|
|
id: setup.id,
|
|
projectId: setup.projectId,
|
|
hostId: setup.hostId,
|
|
repoId: setup.repoId,
|
|
path: setup.path,
|
|
displayName: setup.displayName,
|
|
setupMethod: setup.setupMethod,
|
|
...(setup.kind ? { kind: setup.kind } : {}),
|
|
...(setup.connectionId !== undefined ? { connectionId: setup.connectionId } : {}),
|
|
...(setup.executionHostId ? { executionHostId: setup.executionHostId } : {})
|
|
})),
|
|
projectOrderBy,
|
|
...(projectGroups ? { projectGroups: [...projectGroups] } : {})
|
|
}
|
|
}
|