FN-6786: narrow experimental sidebar labels

Refine the experimental left sidebar so it uses shorter labels in a narrower layout.

- Reduce the default and tokenized sidebar width while preserving resize bounds.
- Switch sidebar entries to noun labels and remove plugin "view" suffixes.
- Shorten the Compound Engineering plugin label to Compound and cover the behavior in sidebar tests.
- Add localized nav label keys for supported app locales.

Files changed:
 .../dashboard/app/components/LeftSidebarNav.css    |  2 +-
 .../dashboard/app/components/LeftSidebarNav.tsx    | 34 ++++++++++++------
 .../components/__tests__/LeftSidebarNav.test.tsx   | 40 ++++++++++++++++++++++
 packages/i18n/locales/en/app.json                  |  2 ++
 packages/i18n/locales/es/app.json                  |  2 ++
 packages/i18n/locales/fr/app.json                  |  2 ++
 packages/i18n/locales/ko/app.json                  |  2 ++
 packages/i18n/locales/zh-CN/app.json               |  2 ++
 packages/i18n/locales/zh-TW/app.json               |  2 ++
 9 files changed, 76 insertions(+), 12 deletions(-)

Fusion-Task-Id: FN-6786

Fusion-Task-Lineage: e8a35018-3b9c-44f6-a4ae-5b6a59281df3
This commit is contained in:
gsxdsm
2026-06-20 01:11:42 -07:00
parent 2e3b965909
commit aaa1aa2fed
9 changed files with 76 additions and 12 deletions

View File

@@ -3,7 +3,7 @@ FNXC:Navigation 2026-06-19-00:00:
The experimental sidebar is a persistent desktop/tablet navigation replacement for the Header view-toggle row. It uses the same tokenized visual rhythm as Header navigation so the flag can be toggled without changing dashboard information architecture.
*/
.left-sidebar-nav {
--left-sidebar-nav-width: calc(var(--space-2xl) * 8);
--left-sidebar-nav-width: calc(var(--space-2xl) * 7);
--left-sidebar-nav-rail-width: calc(var(--space-2xl) * 2);
position: relative;
display: flex;

View File

@@ -56,7 +56,11 @@ interface SidebarNavEntry {
onSelect: () => void;
}
const LEFT_SIDEBAR_DEFAULT_WIDTH = 256;
/*
FNXC:Navigation 2026-06-20-00:00:
The experimental sidebar default is intentionally narrower than the original 256px layout so desktop/tablet navigation preserves more board content while keeping the existing resize clamps.
*/
const LEFT_SIDEBAR_DEFAULT_WIDTH = 224;
const LEFT_SIDEBAR_MIN_WIDTH = 192;
const LEFT_SIDEBAR_MAX_WIDTH = 384;
const LEFT_SIDEBAR_WIDTH_STORAGE_KEY = "fusion:left-sidebar-width";
@@ -144,6 +148,14 @@ function sortPluginViews(entries: PluginDashboardViewEntry[]): PluginDashboardVi
return [...entries].sort((a, b) => (a.view.order ?? Number.MAX_SAFE_INTEGER) - (b.view.order ?? Number.MAX_SAFE_INTEGER));
}
/*
FNXC:Navigation 2026-06-20-00:00:
Experimental sidebar plugin labels must read as plain navigation nouns without an appended "view" suffix. The Compound Engineering plugin is intentionally shortened to "Compound" so its label fits the narrower sidebar.
*/
function getSidebarPluginLabel(entry: PluginDashboardViewEntry): string {
return entry.pluginId === "fusion-plugin-compound-engineering" ? "Compound" : entry.view.label;
}
export function LeftSidebarNav({
view,
onChangeView,
@@ -234,7 +246,7 @@ export function LeftSidebarNav({
const primaryEntries: SidebarNavEntry[] = [
{
id: "board",
label: t("header.boardView", "Board view"),
label: t("nav.board", "Board"),
view: "board",
isActive: view === "board",
icon: LayoutGrid,
@@ -243,7 +255,7 @@ export function LeftSidebarNav({
},
{
id: "list",
label: t("header.listView", "List view"),
label: t("nav.list", "List"),
view: "list",
isActive: view === "list",
icon: List,
@@ -254,7 +266,7 @@ export function LeftSidebarNav({
? [
{
id: "agents",
label: t("header.agentsView", "Agents view"),
label: t("nav.agents", "Agents"),
view: "agents" as TaskView,
isActive: view === "agents",
icon: Bot,
@@ -265,7 +277,7 @@ export function LeftSidebarNav({
: []),
{
id: "command-center",
label: t("header.commandCenterView", "Command Center"),
label: t("nav.commandCenter", "Command Center"),
view: "command-center",
isActive: view === "command-center",
icon: Gauge,
@@ -274,7 +286,7 @@ export function LeftSidebarNav({
},
{
id: "missions",
label: t("header.missionsView", "Missions view"),
label: t("nav.missions", "Missions"),
view: "missions",
isActive: view === "missions",
icon: Target,
@@ -283,7 +295,7 @@ export function LeftSidebarNav({
},
{
id: "chat",
label: t("header.chatView", "Chat view"),
label: t("nav.chat", "Chat"),
view: "chat",
isActive: view === "chat",
icon: MessageSquare,
@@ -293,7 +305,7 @@ export function LeftSidebarNav({
},
{
id: "documents",
label: t("header.documentsView", "Documents view"),
label: t("nav.documents", "Documents"),
view: "documents",
isActive: view === "documents",
icon: FileText,
@@ -302,7 +314,7 @@ export function LeftSidebarNav({
},
{
id: "mailbox",
label: t("header.mailboxView", "Mailbox view"),
label: t("nav.mailbox", "Mailbox"),
view: "mailbox",
isActive: view === "mailbox",
icon: Mail,
@@ -316,7 +328,7 @@ export function LeftSidebarNav({
const targetView = getPluginEntryView(entry);
return {
id: `plugin-${entry.pluginId}-${entry.view.viewId}`,
label: `${entry.view.label} view`,
label: getSidebarPluginLabel(entry),
view: targetView,
isActive: isPluginEntryActive(view, entry),
icon: PluginIcon,
@@ -358,7 +370,7 @@ export function LeftSidebarNav({
const targetView = getPluginEntryView(entry);
return {
id: `plugin-${entry.pluginId}-${entry.view.viewId}`,
label: entry.view.label,
label: getSidebarPluginLabel(entry),
view: targetView,
isActive: isPluginEntryActive(view, entry),
icon: PluginIcon,

View File

@@ -111,6 +111,17 @@ describe("LeftSidebarNav", () => {
expect(screen.queryByTestId("sidebar-nav-plugin-fusion-plugin-primary-primary-view")).toBeNull();
});
it("renders shortened primary labels and default width", () => {
renderSidebar();
expect(screen.getByTestId("left-sidebar-nav")).toHaveStyle({ width: "224px", minWidth: "224px" });
expect(screen.getByTestId("sidebar-nav-board")).toHaveAccessibleName("Board");
expect(screen.getByTestId("sidebar-nav-list")).toHaveAccessibleName("List");
expect(screen.getByTestId("sidebar-nav-agents")).toHaveAccessibleName("Agents");
expect(screen.getByTestId("sidebar-nav-missions")).toHaveAccessibleName("Missions");
expect(screen.queryByRole("button", { name: /view$/i })).toBeNull();
});
it("renders mailbox and stash badges", () => {
renderSidebar();
@@ -131,6 +142,35 @@ describe("LeftSidebarNav", () => {
expect(screen.getByTestId("sidebar-nav-plugin-fusion-plugin-overflow-overflow-view")).toBeDefined();
});
it("renders plugin labels without view suffix and shortens Compound Engineering", () => {
renderSidebar({
pluginDashboardViews: [
...pluginViews,
{
pluginId: "fusion-plugin-compound-engineering",
view: {
viewId: "compound",
label: "Compound Engineering",
componentPath: "./CompoundEngineering",
placement: "primary",
order: 0,
},
},
],
});
const primaryPlugin = screen.getByTestId("sidebar-nav-plugin-fusion-plugin-primary-primary-view");
const compoundPlugin = screen.getByTestId("sidebar-nav-plugin-fusion-plugin-compound-engineering-compound");
expect(primaryPlugin).toHaveAccessibleName("Primary Plugin");
expect(primaryPlugin).toHaveAttribute("title", "Primary Plugin");
expect(primaryPlugin).toHaveTextContent("Primary Plugin");
expect(primaryPlugin).not.toHaveTextContent("view");
expect(compoundPlugin).toHaveAccessibleName("Compound");
expect(compoundPlugin).toHaveAttribute("title", "Compound");
expect(compoundPlugin).toHaveTextContent("Compound");
expect(compoundPlugin).not.toHaveTextContent("Compound Engineering");
});
it.each<[TaskView, string]>([
["board", "sidebar-nav-board"],
["research", "sidebar-nav-research"],

View File

@@ -3823,6 +3823,7 @@
"activityLog": "Activity Log",
"agents": "Agents",
"automation": "Automation",
"board": "Board",
"chat": "Chat",
"chatUnreadAriaLabel": "Unread chat response",
"commandCenter": "Command Center",
@@ -3834,6 +3835,7 @@
"goals": "Goals",
"importFromGitHub": "Import from GitHub",
"insights": "Insights",
"list": "List",
"loadingScripts": "Loading scripts…",
"mailbox": "Mailbox",
"mailboxPendingAriaLabel": "Pending approvals",

View File

@@ -3823,6 +3823,7 @@
"activityLog": "Registro de actividad",
"agents": "Agentes",
"automation": "Automatización",
"board": "Tablero",
"chat": "Chat",
"chatUnreadAriaLabel": "Respuesta de chat no leída",
"commandCenter": "",
@@ -3834,6 +3835,7 @@
"goals": "Objetivos",
"importFromGitHub": "Importar desde GitHub",
"insights": "Perspectivas",
"list": "Lista",
"loadingScripts": "Cargando scripts…",
"mailbox": "Bandeja de entrada",
"mailboxPendingAriaLabel": "Aprobaciones pendientes",

View File

@@ -3823,6 +3823,7 @@
"activityLog": "Journal d'activité",
"agents": "Agents",
"automation": "Automatisation",
"board": "Tableau",
"chat": "Chat",
"chatUnreadAriaLabel": "Réponse non lue",
"commandCenter": "",
@@ -3834,6 +3835,7 @@
"goals": "Objectifs",
"importFromGitHub": "Importer depuis GitHub",
"insights": "Insights",
"list": "Liste",
"loadingScripts": "Chargement des scripts…",
"mailbox": "Boîte de réception",
"mailboxPendingAriaLabel": "Approbations en attente",

View File

@@ -3823,6 +3823,7 @@
"activityLog": "활동 로그",
"agents": "에이전트",
"automation": "자동화",
"board": "보드",
"chat": "채팅",
"chatUnreadAriaLabel": "읽지 않은 채팅 응답",
"commandCenter": "",
@@ -3834,6 +3835,7 @@
"goals": "목표",
"importFromGitHub": "GitHub에서 가져오기",
"insights": "인사이트",
"list": "목록",
"loadingScripts": "스크립트 불러오는 중…",
"mailbox": "메일함",
"mailboxPendingAriaLabel": "승인 대기 중",

View File

@@ -3823,6 +3823,7 @@
"activityLog": "活动日志",
"agents": "智能体",
"automation": "自动化",
"board": "看板",
"chat": "聊天",
"chatUnreadAriaLabel": "未读聊天回复",
"commandCenter": "",
@@ -3834,6 +3835,7 @@
"goals": "目标",
"importFromGitHub": "从 GitHub 导入",
"insights": "洞察",
"list": "列表",
"loadingScripts": "正在加载脚本…",
"mailbox": "邮箱",
"mailboxPendingAriaLabel": "待审批",

View File

@@ -3823,6 +3823,7 @@
"activityLog": "活動紀錄",
"agents": "代理",
"automation": "自動化",
"board": "看板",
"chat": "聊天",
"chatUnreadAriaLabel": "未讀聊天回覆",
"commandCenter": "",
@@ -3834,6 +3835,7 @@
"goals": "目標",
"importFromGitHub": "從 GitHub 匯入",
"insights": "洞察",
"list": "清單",
"loadingScripts": "正在載入腳本…",
"mailbox": "收件匣",
"mailboxPendingAriaLabel": "待審核",