feat(app): sidebar display preferences, a shared menu engine, and a colour pass

Sidebar polish round.

Menus. `dropdown-menu` and `context-menu` were two copies of one engine that
differed only in how the trigger fires — identical from `computePosition` down —
and neither could nest. Both are now thin wrappers over
`components/ui/menu/`, which owns anchoring, a pure page-stack navigation model,
and one presentation fork: flyout on pointer, pushed pages in a sheet on compact.
All 31 call sites are unchanged. Items got a rounded inset fill, a visible
separator, and `selected` (a check) split from `active` (a fill).

Sidebar display preferences. The flat wall of four sections becomes one row per
decision with its value, and per-feature toggles over what a workspace row shows:
host, pull request, checks, scripts. Each is independent — a ticked toggle that
draws nothing is a lie. `display-preferences/` hides that grouping lives in
zustand while title source and row items are app settings.

Timestamps. "Last activity" now ticks itself. The label rounds to the minute and
never shows seconds; past a week it becomes a date. The timer lives in the leaf
`<Text>`, at most one per resolution tier shared by every row, and a tick only
re-renders when the string actually changed — so a memoized row that legitimately
never re-renders still stays current.

Colour. Full status tier regenerated at 90% of gamut max (was 85), same rule as
the muted tier, lightness and hue untouched. File icons are pulled toward neutral
in OKLab behind `getFileIconSvg` — material-icon-theme picks its palette for a
tree where the icon is the only colour on the row, and ours is not. New/deleted
files use the forge square-plus/square-minus glyphs at muted weight instead of
tinted pills. Project icons round a little more; uploaded icons never do.

The project status badge is a knockout again: it fills with the colour of the row
behind it, so the ring reads as a gap in the icon rather than a white halo.

File rows. The always-visible kebab is gone from the file explorer and diff tree
in favour of right-click, which also reclaims the reserved action slot.
This commit is contained in:
Mohamed Boudra
2026-08-03 18:58:06 +02:00
parent 777fe968f5
commit ab8151d33f
81 changed files with 5457 additions and 2903 deletions
+4
View File
@@ -159,6 +159,10 @@ describe("translation resources", () => {
expect(zhCN.settings.providers.models.many).toBe("{{count}} 个 Model");
});
it("labels the immediate add-to-chat action without an ellipsis", () => {
expect(en.workspace.fileActions.addToChat).toBe("Add to chat");
});
it("keeps local connection fallback errors translated", () => {
expect(findUntranslatedConnectionErrors()).toEqual([]);
});
+36 -1
View File
@@ -384,7 +384,7 @@ export const ar: TranslationResources = {
openFile: "افتح الملف",
copyPath: "نسخ المسار",
download: "تحميل",
addToChat: "إضافة إلى الدردشة…",
addToChat: "إضافة إلى الدردشة",
moreActions: "المزيد من الإجراءات",
},
fileExplorer: {
@@ -835,6 +835,14 @@ export const ar: TranslationResources = {
viewPullRequest: "عرض",
openOn: "فتح على {{brand}}",
},
checksSummary: {
passedLabel: "نجح",
failedLabel: "فشل",
runningLabel: "قيد التشغيل",
passedAccessible: "نجحت الفحوصات",
failedAccessible: "فشلت الفحوصات",
runningAccessible: "الفحوصات قيد التشغيل",
},
sections: {
checks: "الشيكات",
pipeline: "خط المعالجة",
@@ -882,6 +890,33 @@ export const ar: TranslationResources = {
},
},
sidebar: {
display: {
trigger: "تفضيلات العرض",
heading: "العرض",
grouping: {
label: "التجميع",
project: "المشروع",
status: "الحالة",
},
titleSource: {
label: "العنوان",
title: "العنوان",
branch: "اسم الفرع",
},
show: {
label: "إظهار",
host: "المضيف",
changeRequest: "طلب السحب",
checks: "الفحوصات",
scripts: "البرامج النصية",
diff: "إحصائيات الفروق",
timestamp: "آخر نشاط",
},
hostFilter: {
label: "المضيف",
all: "كل المضيفين",
},
},
pinned: {
title: "المثبتة",
},
+36 -1
View File
@@ -383,7 +383,7 @@ export const en = {
openFile: "Open file",
copyPath: "Copy path",
download: "Download",
addToChat: "Add to chat…",
addToChat: "Add to chat",
moreActions: "More actions",
},
fileExplorer: {
@@ -845,6 +845,14 @@ export const en = {
viewPullRequest: "View",
openOn: "Open on {{brand}}",
},
checksSummary: {
passedLabel: "passed",
failedLabel: "failed",
runningLabel: "running",
passedAccessible: "Checks passed",
failedAccessible: "Checks failed",
runningAccessible: "Checks running",
},
sections: {
checks: "Checks",
pipeline: "Pipeline",
@@ -892,6 +900,33 @@ export const en = {
},
},
sidebar: {
display: {
trigger: "Display preferences",
heading: "Display",
grouping: {
label: "Grouping",
project: "Project",
status: "Status",
},
titleSource: {
label: "Title",
title: "Title",
branch: "Branch name",
},
show: {
label: "Show",
host: "Host",
changeRequest: "Pull request",
checks: "Checks",
scripts: "Scripts",
diff: "Diff stats",
timestamp: "Last activity",
},
hostFilter: {
label: "Host",
all: "All hosts",
},
},
pinned: {
title: "Pinned",
},
+36 -1
View File
@@ -388,7 +388,7 @@ export const es: TranslationResources = {
openFile: "Abrir archivo",
copyPath: "Copiar ruta",
download: "Descargar",
addToChat: "Añadir al chat…",
addToChat: "Añadir al chat",
moreActions: "Más acciones",
},
fileExplorer: {
@@ -866,6 +866,14 @@ export const es: TranslationResources = {
viewPullRequest: "Ver",
openOn: "Abrir en {{brand}}",
},
checksSummary: {
passedLabel: "pasó",
failedLabel: "falló",
runningLabel: "en curso",
passedAccessible: "Comprobaciones superadas",
failedAccessible: "Comprobaciones fallidas",
runningAccessible: "Comprobaciones en curso",
},
sections: {
checks: "cheques",
pipeline: "Pipeline",
@@ -913,6 +921,33 @@ export const es: TranslationResources = {
},
},
sidebar: {
display: {
trigger: "Preferencias de visualización",
heading: "Visualización",
grouping: {
label: "Agrupación",
project: "Proyecto",
status: "Estado",
},
titleSource: {
label: "Título",
title: "Título",
branch: "Nombre de rama",
},
show: {
label: "Mostrar",
host: "Host",
changeRequest: "Pull request",
checks: "Comprobaciones",
scripts: "Scripts",
diff: "Estadísticas de cambios",
timestamp: "Última actividad",
},
hostFilter: {
label: "Host",
all: "Todos los hosts",
},
},
pinned: {
title: "Anclados",
},
+36 -1
View File
@@ -388,7 +388,7 @@ export const fr: TranslationResources = {
openFile: "Ouvrir le fichier",
copyPath: "Copier le chemin",
download: "Télécharger",
addToChat: "Ajouter au chat…",
addToChat: "Ajouter au chat",
moreActions: "Plus de propositions",
},
fileExplorer: {
@@ -865,6 +865,14 @@ export const fr: TranslationResources = {
viewPullRequest: "Voir",
openOn: "Ouvrir sur {{brand}}",
},
checksSummary: {
passedLabel: "succès",
failedLabel: "échec",
runningLabel: "en cours",
passedAccessible: "Vérifications réussies",
failedAccessible: "Vérifications en échec",
runningAccessible: "Vérifications en cours",
},
sections: {
checks: "Chèques",
pipeline: "Pipeline",
@@ -912,6 +920,33 @@ export const fr: TranslationResources = {
},
},
sidebar: {
display: {
trigger: "Préférences d'affichage",
heading: "Affichage",
grouping: {
label: "Regroupement",
project: "Projet",
status: "Statut",
},
titleSource: {
label: "Titre",
title: "Titre",
branch: "Nom de branche",
},
show: {
label: "Afficher",
host: "Hôte",
changeRequest: "Pull request",
checks: "Vérifications",
scripts: "Scripts",
diff: "Statistiques de diff",
timestamp: "Dernière activité",
},
hostFilter: {
label: "Hôte",
all: "Tous les hôtes",
},
},
pinned: {
title: "Épinglés",
},
+36 -1
View File
@@ -388,7 +388,7 @@ export const ja: TranslationResources = {
openFile: "ファイルを開く",
copyPath: "パスをコピー",
download: "ダウンロード",
addToChat: "チャットに追加…",
addToChat: "チャットに追加",
moreActions: "その他のアクション",
},
fileExplorer: {
@@ -846,6 +846,14 @@ export const ja: TranslationResources = {
viewPullRequest: "表示",
openOn: "{{brand}}で開く",
},
checksSummary: {
passedLabel: "成功",
failedLabel: "失敗",
runningLabel: "実行中",
passedAccessible: "チェック成功",
failedAccessible: "チェック失敗",
runningAccessible: "チェック実行中",
},
sections: {
checks: "チェック",
pipeline: "パイプライン",
@@ -893,6 +901,33 @@ export const ja: TranslationResources = {
},
},
sidebar: {
display: {
trigger: "表示設定",
heading: "表示",
grouping: {
label: "グループ化",
project: "プロジェクト",
status: "ステータス",
},
titleSource: {
label: "タイトル",
title: "タイトル",
branch: "ブランチ名",
},
show: {
label: "表示項目",
host: "ホスト",
changeRequest: "プルリクエスト",
checks: "チェック",
scripts: "スクリプト",
diff: "差分統計",
timestamp: "最終アクティビティ",
},
hostFilter: {
label: "ホスト",
all: "すべてのホスト",
},
},
pinned: {
title: "固定済み",
},
+36 -1
View File
@@ -388,7 +388,7 @@ export const ptBR: TranslationResources = {
openFile: "Abrir arquivo",
copyPath: "Copiar caminho",
download: "Baixar",
addToChat: "Adicionar ao chat…",
addToChat: "Adicionar ao chat",
moreActions: "Mais ações",
},
fileExplorer: {
@@ -857,6 +857,14 @@ export const ptBR: TranslationResources = {
viewPullRequest: "Ver",
openOn: "Abrir no {{brand}}",
},
checksSummary: {
passedLabel: "passou",
failedLabel: "falhou",
runningLabel: "em andamento",
passedAccessible: "Verificações aprovadas",
failedAccessible: "Verificações com falha",
runningAccessible: "Verificações em andamento",
},
sections: {
checks: "Verificações",
pipeline: "Pipeline",
@@ -904,6 +912,33 @@ export const ptBR: TranslationResources = {
},
},
sidebar: {
display: {
trigger: "Preferências de exibição",
heading: "Exibição",
grouping: {
label: "Agrupamento",
project: "Projeto",
status: "Status",
},
titleSource: {
label: "Título",
title: "Título",
branch: "Nome da branch",
},
show: {
label: "Mostrar",
host: "Host",
changeRequest: "Pull request",
checks: "Verificações",
scripts: "Scripts",
diff: "Estatísticas de diff",
timestamp: "Última atividade",
},
hostFilter: {
label: "Host",
all: "Todos os hosts",
},
},
pinned: {
title: "Fixados",
},
+36 -1
View File
@@ -387,7 +387,7 @@ export const ru: TranslationResources = {
openFile: "Открыть файл",
copyPath: "Копировать путь",
download: "Скачать",
addToChat: "Добавить в чат…",
addToChat: "Добавить в чат",
moreActions: "Дополнительные действия",
},
fileExplorer: {
@@ -857,6 +857,14 @@ export const ru: TranslationResources = {
viewPullRequest: "Открыть",
openOn: "Открыть на {{brand}}",
},
checksSummary: {
passedLabel: "успешно",
failedLabel: "ошибка",
runningLabel: "выполняется",
passedAccessible: "Проверки пройдены",
failedAccessible: "Проверки не пройдены",
runningAccessible: "Проверки выполняются",
},
sections: {
checks: "Чеки",
pipeline: "Пайплайн",
@@ -904,6 +912,33 @@ export const ru: TranslationResources = {
},
},
sidebar: {
display: {
trigger: "Настройки отображения",
heading: "Отображение",
grouping: {
label: "Группировка",
project: "Проект",
status: "Статус",
},
titleSource: {
label: "Заголовок",
title: "Заголовок",
branch: "Имя ветки",
},
show: {
label: "Показывать",
host: "Хост",
changeRequest: "Pull request",
checks: "Проверки",
scripts: "Скрипты",
diff: "Статистика изменений",
timestamp: "Последняя активность",
},
hostFilter: {
label: "Хост",
all: "Все хосты",
},
},
pinned: {
title: "Закреплённые",
},
+36 -1
View File
@@ -384,7 +384,7 @@ export const zhCN: TranslationResources = {
openFile: "打开文件",
copyPath: "复制路径",
download: "下载",
addToChat: "添加到聊天…",
addToChat: "添加到聊天",
moreActions: "更多操作",
},
fileExplorer: {
@@ -827,6 +827,14 @@ export const zhCN: TranslationResources = {
viewPullRequest: "查看",
openOn: "在 {{brand}} 上打开",
},
checksSummary: {
passedLabel: "通过",
failedLabel: "失败",
runningLabel: "运行中",
passedAccessible: "检查通过",
failedAccessible: "检查失败",
runningAccessible: "检查运行中",
},
sections: {
checks: "Checks",
pipeline: "流水线",
@@ -874,6 +882,33 @@ export const zhCN: TranslationResources = {
},
},
sidebar: {
display: {
trigger: "显示偏好",
heading: "显示",
grouping: {
label: "分组",
project: "项目",
status: "状态",
},
titleSource: {
label: "标题",
title: "标题",
branch: "分支名称",
},
show: {
label: "显示",
host: "主机",
changeRequest: "拉取请求",
checks: "检查",
scripts: "脚本",
diff: "差异统计",
timestamp: "最近活动",
},
hostFilter: {
label: "主机",
all: "所有主机",
},
},
pinned: {
title: "已置顶",
},