feat(app): filter the sidebar by project (#3563)

The sidebar can be pinned to a set of hosts or to a set of labels, but
not to a set of projects. If you keep many projects on one host, the
project is the filter you want.

Add a Project page to the sidebar display menu, next to Host. It
multi-selects projects, and an empty selection means every project.

Apply the filter at two points. Project grouping reads the projects
array, but Status grouping builds its rows from the workspace entries.
A filter applied to only one of the two leaves the other mode
unfiltered.

Apply nothing when a stored selection names no visible project. The
project list is narrowed by the host filter, and it is empty before any
host connects, so dropping keys that do not match would destroy the
selection on every cold start.

Count the project filter in the sidebar header gate. Pinning every chat
in a project drops that project from the unpinned list. Filtering to
that project then removed the header, and the header carries the display
menu, which is the only way back to the Project page.

Generalise the label filter empty state to all sidebar filters, and move
its strings out of the labels namespace.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Christoph Leiter
2026-08-20 23:51:35 +02:00
committed by GitHub
co-authored by Claude Opus 5
parent 33224ace47
commit c3d37eba4b
23 changed files with 747 additions and 78 deletions
+9 -2
View File
@@ -985,8 +985,6 @@ export const ar: TranslationResources = {
},
filter: {
clear: "مسح عامل التصفية",
noMatchesTitle: "لا توجد مساحات عمل مطابقة",
noMatchesDescription: "غيّر عامل تصفية التسميات أو امسحه لعرض مساحات العمل.",
},
manage: {
open: "إدارة التسميات…",
@@ -1042,6 +1040,15 @@ export const ar: TranslationResources = {
label: "المضيف",
all: "كل المضيفين",
},
projectFilter: {
label: "المشروع",
all: "كل المشاريع",
},
},
filterEmpty: {
title: "لا توجد مساحات عمل مطابقة",
description: "غيّر عوامل تصفية الشريط الجانبي أو امسحها لعرض مساحات العمل.",
clear: "مسح عوامل التصفية",
},
pinned: {
title: "المثبتة",
+9 -2
View File
@@ -994,8 +994,6 @@ export const en = {
},
filter: {
clear: "Clear filter",
noMatchesTitle: "No workspaces match",
noMatchesDescription: "Change or clear the label filter to see workspaces.",
},
manage: {
open: "Manage labels…",
@@ -1051,6 +1049,15 @@ export const en = {
label: "Host",
all: "All hosts",
},
projectFilter: {
label: "Project",
all: "All projects",
},
},
filterEmpty: {
title: "No workspaces match",
description: "Change or clear the sidebar filters to see workspaces.",
clear: "Clear filters",
},
pinned: {
title: "Pinned",
+9 -2
View File
@@ -1019,8 +1019,6 @@ export const es: TranslationResources = {
},
filter: {
clear: "Borrar filtro",
noMatchesTitle: "Ningún espacio de trabajo coincide",
noMatchesDescription: "Cambia o borra el filtro de etiquetas para ver espacios de trabajo.",
},
manage: {
open: "Gestionar etiquetas…",
@@ -1076,6 +1074,15 @@ export const es: TranslationResources = {
label: "Host",
all: "Todos los hosts",
},
projectFilter: {
label: "Proyecto",
all: "Todos los proyectos",
},
},
filterEmpty: {
title: "Ningún espacio de trabajo coincide",
description: "Cambia o borra los filtros de la barra lateral para ver espacios de trabajo.",
clear: "Borrar filtros",
},
pinned: {
title: "Anclados",
+10 -3
View File
@@ -1018,9 +1018,6 @@ export const fr: TranslationResources = {
},
filter: {
clear: "Effacer le filtre",
noMatchesTitle: "Aucun espace de travail ne correspond",
noMatchesDescription:
"Modifiez ou effacez le filtre d’étiquettes pour afficher les espaces de travail.",
},
manage: {
open: "Gérer les étiquettes…",
@@ -1076,6 +1073,16 @@ export const fr: TranslationResources = {
label: "Hôte",
all: "Tous les hôtes",
},
projectFilter: {
label: "Projet",
all: "Tous les projets",
},
},
filterEmpty: {
title: "Aucun espace de travail ne correspond",
description:
"Modifiez ou effacez les filtres de la barre latérale pour afficher les espaces de travail.",
clear: "Effacer les filtres",
},
pinned: {
title: "Épinglés",
+10 -3
View File
@@ -996,9 +996,6 @@ export const ja: TranslationResources = {
},
filter: {
clear: "フィルターをクリア",
noMatchesTitle: "一致するワークスペースがありません",
noMatchesDescription:
"ワークスペースを表示するにはラベルフィルターを変更またはクリアしてください。",
},
manage: {
open: "ラベルを管理…",
@@ -1054,6 +1051,16 @@ export const ja: TranslationResources = {
label: "ホスト",
all: "すべてのホスト",
},
projectFilter: {
label: "プロジェクト",
all: "すべてのプロジェクト",
},
},
filterEmpty: {
title: "一致するワークスペースがありません",
description:
"ワークスペースを表示するにはサイドバーのフィルターを変更またはクリアしてください。",
clear: "フィルターをクリア",
},
pinned: {
title: "固定済み",
+9 -2
View File
@@ -992,8 +992,6 @@ export const ko: TranslationResources = {
},
filter: {
clear: "필터 지우기",
noMatchesTitle: "일치하는 워크스페이스가 없습니다",
noMatchesDescription: "워크스페이스를 보려면 레이블 필터를 변경하거나 지우세요.",
},
manage: {
open: "레이블 관리…",
@@ -1049,6 +1047,15 @@ export const ko: TranslationResources = {
label: "호스트",
all: "모든 호스트",
},
projectFilter: {
label: "프로젝트",
all: "모든 프로젝트",
},
},
filterEmpty: {
title: "일치하는 워크스페이스가 없습니다",
description: "워크스페이스를 보려면 사이드바 필터를 변경하거나 지우세요.",
clear: "필터 지우기",
},
pinned: {
title: "고정됨",
+9 -2
View File
@@ -1010,8 +1010,6 @@ export const ptBR: TranslationResources = {
},
filter: {
clear: "Limpar filtro",
noMatchesTitle: "Nenhum espaço de trabalho corresponde",
noMatchesDescription: "Altere ou limpe o filtro de etiquetas para ver espaços de trabalho.",
},
manage: {
open: "Gerenciar etiquetas…",
@@ -1067,6 +1065,15 @@ export const ptBR: TranslationResources = {
label: "Host",
all: "Todos os hosts",
},
projectFilter: {
label: "Projeto",
all: "Todos os projetos",
},
},
filterEmpty: {
title: "Nenhum espaço de trabalho corresponde",
description: "Altere ou limpe os filtros da barra lateral para ver espaços de trabalho.",
clear: "Limpar filtros",
},
pinned: {
title: "Fixados",
+10 -3
View File
@@ -1000,9 +1000,6 @@ export const ru: TranslationResources = {
},
filter: {
clear: "Очистить фильтр",
noMatchesTitle: "Нет подходящих рабочих пространств",
noMatchesDescription:
"Измените или очистите фильтр меток, чтобы увидеть рабочие пространства.",
},
manage: {
open: "Управление метками…",
@@ -1058,6 +1055,16 @@ export const ru: TranslationResources = {
label: "Хост",
all: "Все хосты",
},
projectFilter: {
label: "Проект",
all: "Все проекты",
},
},
filterEmpty: {
title: "Нет подходящих рабочих пространств",
description:
"Измените или очистите фильтры боковой панели, чтобы увидеть рабочие пространства.",
clear: "Очистить фильтры",
},
pinned: {
title: "Закреплённые",
+9 -2
View File
@@ -977,8 +977,6 @@ export const zhCN: TranslationResources = {
},
filter: {
clear: "清除筛选",
noMatchesTitle: "没有匹配的工作区",
noMatchesDescription: "更改或清除标签筛选以查看工作区。",
},
manage: {
open: "管理标签…",
@@ -1034,6 +1032,15 @@ export const zhCN: TranslationResources = {
label: "主机",
all: "所有主机",
},
projectFilter: {
label: "项目",
all: "所有项目",
},
},
filterEmpty: {
title: "没有匹配的工作区",
description: "更改或清除侧边栏筛选以查看工作区。",
clear: "清除筛选",
},
pinned: {
title: "已置顶",