fix(app): keep workspace shell navigation reachable (#874)

This commit is contained in:
ben
2026-03-12 15:26:09 -07:00
committed by GitHub
parent 61df5400d8
commit 85d3b32cfd
12 changed files with 406 additions and 107 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -0,0 +1,26 @@
# Workspace shell sidebar UX
## Summary
- keeps the workspace right rail visible on both session and dashboard surfaces, defaulting to a compact icon-only state
- adds a draggable resize handle for the left workspace column on desktop breakpoints
- makes the status bar settings cog act like a toggle so a second click returns to the previous screen
## Evidence
- `packages/app/pr/screenshots/workspace-session-sidebar-ux/session-right-sidebar-collapsed.png`
- `packages/app/pr/screenshots/workspace-session-sidebar-ux/dashboard-right-sidebar-collapsed.png`
- `packages/app/pr/screenshots/workspace-session-sidebar-ux/session-right-sidebar-expanded.png`
- `packages/app/pr/screenshots/workspace-session-sidebar-ux/session-left-sidebar-resized.png`
- `packages/app/pr/screenshots/workspace-session-sidebar-ux/settings-open-from-session.png`
- `packages/app/pr/screenshots/workspace-session-sidebar-ux/settings-toggle-returned-session.png`
## Verification
- `pnpm typecheck`
- `pnpm build:ui`
- local Playwright smoke against `http://localhost:4173`
## Docker blocker
- `packaging/docker/dev-up.sh` failed before the stack came up because Docker could not read the `node:22-bookworm-slim` image blob from containerd (`input/output error`)

View File

@@ -240,6 +240,12 @@ type SharedBundleImportTarget = {
directoryHint?: string | null;
};
type SettingsReturnTarget = {
view: View;
tab: DashboardTab;
sessionId: string | null;
};
function normalizeSharedBundleImportIntent(value: string | null | undefined): SharedBundleImportIntent {
const normalized = (value ?? "").trim().toLowerCase();
if (normalized === "new_worker" || normalized === "new-worker" || normalized === "newworker") {
@@ -1098,6 +1104,11 @@ export default function App() {
const [selectedSessionId, setSelectedSessionId] = createSignal<string | null>(
null
);
const [settingsReturnTarget, setSettingsReturnTarget] = createSignal<SettingsReturnTarget>({
view: "dashboard",
tab: "scheduled",
sessionId: null,
});
const SESSION_BY_WORKSPACE_KEY = "openwork.workspace-last-session.v1";
const readSessionByWorkspace = () => {
if (typeof window === "undefined") return {} as Record<string, string>;
@@ -1136,6 +1147,48 @@ export default function App() {
const [providerAuthError, setProviderAuthError] = createSignal<string | null>(null);
const [providerAuthMethods, setProviderAuthMethods] = createSignal<Record<string, ProviderAuthMethod[]>>({});
createEffect(() => {
const view = currentView();
const currentTab = tab();
if (view === "dashboard" && currentTab === "settings") return;
setSettingsReturnTarget({
view,
tab: currentTab,
sessionId: selectedSessionId(),
});
});
const restoreSettingsReturnTarget = () => {
const target = settingsReturnTarget();
if (target.view === "session") {
if (target.sessionId) {
goToSession(target.sessionId);
return;
}
navigate("/session");
return;
}
if (target.view === "onboarding") {
navigate("/onboarding");
return;
}
if (target.view === "proto") {
navigate("/proto/workspaces");
return;
}
goToDashboard(target.tab);
};
const toggleSettingsView = (nextTab: SettingsTab = "general") => {
const settingsOpen = currentView() === "dashboard" && tab() === "settings";
if (settingsOpen) {
restoreSettingsReturnTarget();
return;
}
setSettingsTab(nextTab);
goToDashboard("settings");
};
const sessionStore = createSessionStore({
client,
activeWorkspaceRoot: () => workspaceStore.activeWorkspaceRoot().trim(),
@@ -5862,6 +5915,7 @@ export default function App() {
submitProviderApiKey,
view: currentView(),
setView,
toggleSettings: () => toggleSettingsView("general"),
startupPreference: startupPreference(),
baseUrl: baseUrl(),
clientConnected: Boolean(client()),
@@ -6095,6 +6149,7 @@ export default function App() {
tab: tab(),
setTab,
setSettingsTab,
toggleSettings: () => toggleSettingsView("general"),
activeWorkspaceDisplay: activeWorkspaceDisplay(),
activeWorkspaceRoot: workspaceStore.activeWorkspaceRoot().trim(),
workspaces: workspaceStore.workspaces(),

View File

@@ -12,6 +12,7 @@ type StatusBarProps = {
clientConnected: boolean;
openworkServerStatus: OpenworkServerStatus;
developerMode: boolean;
settingsOpen: boolean;
onOpenSettings: () => void;
onOpenMessaging: () => void;
onOpenProviders: () => Promise<void> | void;
@@ -228,13 +229,17 @@ export default function StatusBar(props: StatusBarProps) {
</Show>
<Button
variant="ghost"
class="h-7 px-2.5 py-0 text-xs"
class={`h-7 px-2.5 py-0 text-xs ${
props.settingsOpen ? "bg-gray-3 text-gray-12 hover:bg-gray-4" : ""
}`}
onClick={props.onOpenSettings}
title="Settings"
title={props.settingsOpen ? "Back to previous screen" : "Settings"}
>
<Settings class="w-4 h-4" />
<Show when={props.developerMode}>
<span class="text-gray-11 font-medium">Settings</span>
<span class="text-gray-11 font-medium">
{props.settingsOpen ? "Back" : "Settings"}
</span>
</Show>
</Button>
</div>

View File

@@ -0,0 +1,138 @@
import { createEffect, createMemo, createSignal, onCleanup } from "solid-js";
const LEFT_SIDEBAR_WIDTH_KEY = "openwork.workspace-shell.left-width.v1";
const RIGHT_SIDEBAR_EXPANDED_KEY = "openwork.workspace-shell.right-expanded.v1";
export const DEFAULT_WORKSPACE_LEFT_SIDEBAR_WIDTH = 260;
export const MIN_WORKSPACE_LEFT_SIDEBAR_WIDTH = 220;
export const MAX_WORKSPACE_LEFT_SIDEBAR_WIDTH = 420;
export const DEFAULT_WORKSPACE_RIGHT_SIDEBAR_COLLAPSED_WIDTH = 72;
type WorkspaceShellLayoutOptions = {
defaultLeftWidth?: number;
minLeftWidth?: number;
maxLeftWidth?: number;
collapsedRightWidth?: number;
expandedRightWidth: number;
};
function readStorage(key: string): string | null {
if (typeof window === "undefined") return null;
try {
return window.localStorage.getItem(key);
} catch {
return null;
}
}
function writeStorage(key: string, value: string) {
if (typeof window === "undefined") return;
try {
window.localStorage.setItem(key, value);
} catch {
// ignore persistence failures
}
}
function clampNumber(value: number, min: number, max: number) {
return Math.min(max, Math.max(min, value));
}
export function createWorkspaceShellLayout(options: WorkspaceShellLayoutOptions) {
const minLeftWidth = Math.max(180, options.minLeftWidth ?? MIN_WORKSPACE_LEFT_SIDEBAR_WIDTH);
const maxLeftWidth = Math.max(minLeftWidth, options.maxLeftWidth ?? MAX_WORKSPACE_LEFT_SIDEBAR_WIDTH);
const defaultLeftWidth = clampNumber(
options.defaultLeftWidth ?? DEFAULT_WORKSPACE_LEFT_SIDEBAR_WIDTH,
minLeftWidth,
maxLeftWidth,
);
const collapsedRightWidth = Math.max(
56,
options.collapsedRightWidth ?? DEFAULT_WORKSPACE_RIGHT_SIDEBAR_COLLAPSED_WIDTH,
);
const expandedRightWidth = Math.max(collapsedRightWidth, options.expandedRightWidth);
const readLeftSidebarWidth = () => {
const raw = readStorage(LEFT_SIDEBAR_WIDTH_KEY);
const parsed = Number(raw);
if (!Number.isFinite(parsed)) return defaultLeftWidth;
return clampNumber(parsed, minLeftWidth, maxLeftWidth);
};
const readRightSidebarExpanded = () => readStorage(RIGHT_SIDEBAR_EXPANDED_KEY) === "1";
const [leftSidebarWidth, setLeftSidebarWidth] = createSignal(readLeftSidebarWidth());
const [rightSidebarExpanded, setRightSidebarExpanded] = createSignal(readRightSidebarExpanded());
createEffect(() => {
writeStorage(LEFT_SIDEBAR_WIDTH_KEY, String(clampNumber(leftSidebarWidth(), minLeftWidth, maxLeftWidth)));
});
createEffect(() => {
writeStorage(RIGHT_SIDEBAR_EXPANDED_KEY, rightSidebarExpanded() ? "1" : "0");
});
const rightSidebarWidth = createMemo(() =>
rightSidebarExpanded() ? expandedRightWidth : collapsedRightWidth,
);
let dragCleanup: (() => void) | null = null;
const stopLeftSidebarResize = () => {
dragCleanup?.();
dragCleanup = null;
if (typeof document === "undefined") return;
document.body.style.removeProperty("cursor");
document.body.style.removeProperty("user-select");
};
const startLeftSidebarResize = (event: PointerEvent) => {
if (event.button !== 0 || typeof window === "undefined") return;
stopLeftSidebarResize();
const initialX = event.clientX;
const initialWidth = leftSidebarWidth();
const handleMove = (moveEvent: PointerEvent) => {
const delta = moveEvent.clientX - initialX;
setLeftSidebarWidth(clampNumber(initialWidth + delta, minLeftWidth, maxLeftWidth));
};
const handleStop = () => {
stopLeftSidebarResize();
};
window.addEventListener("pointermove", handleMove);
window.addEventListener("pointerup", handleStop);
window.addEventListener("pointercancel", handleStop);
dragCleanup = () => {
window.removeEventListener("pointermove", handleMove);
window.removeEventListener("pointerup", handleStop);
window.removeEventListener("pointercancel", handleStop);
};
if (typeof document !== "undefined") {
document.body.style.cursor = "col-resize";
document.body.style.userSelect = "none";
}
event.preventDefault();
};
const toggleRightSidebar = () => {
setRightSidebarExpanded((current) => !current);
};
onCleanup(() => {
stopLeftSidebarResize();
});
return {
leftSidebarWidth,
rightSidebarExpanded,
rightSidebarWidth,
setRightSidebarExpanded,
startLeftSidebarResize,
toggleRightSidebar,
};
}

View File

@@ -23,6 +23,7 @@ import {
isWindowsPlatform,
normalizeDirectoryPath,
} from "../utils";
import { createWorkspaceShellLayout } from "../lib/workspace-shell-layout";
import {
buildOpenworkConnectInviteUrl,
buildOpenworkWorkspaceBaseUrl,
@@ -54,7 +55,7 @@ import ShareWorkspaceModal from "../components/share-workspace-modal";
import WorkspaceSessionList from "../components/session/workspace-session-list";
import {
Box,
ChevronDown,
ChevronLeft,
ChevronRight,
Circle,
History,
@@ -62,7 +63,6 @@ import {
MessageCircle,
MoreHorizontal,
Plus,
Settings,
SlidersHorizontal,
Zap,
} from "lucide-solid";
@@ -92,6 +92,7 @@ export type DashboardViewProps = {
refreshProviders: () => Promise<unknown>;
view: View;
setView: (view: View, sessionId?: string) => void;
toggleSettings: () => void;
startupPreference: StartupPreference | null;
baseUrl: string;
clientConnected: boolean;
@@ -399,6 +400,13 @@ export default function DashboardView(props: DashboardViewProps) {
const [refreshInProgress, setRefreshInProgress] = createSignal(false);
const [providerAuthActionBusy, setProviderAuthActionBusy] = createSignal(false);
const [shareWorkspaceId, setShareWorkspaceId] = createSignal<string | null>(null);
const {
leftSidebarWidth,
rightSidebarExpanded,
rightSidebarWidth,
startLeftSidebarResize,
toggleRightSidebar,
} = createWorkspaceShellLayout({ expandedRightWidth: 224 });
const handleProviderAuthSelect = async (providerId: string): Promise<ProviderOAuthStartResult> => {
if (providerAuthActionBusy()) {
@@ -494,15 +502,17 @@ export default function DashboardView(props: DashboardViewProps) {
return (
<button
type="button"
class={`w-full h-10 flex items-center gap-3 px-3 rounded-lg text-sm font-medium transition-colors ${
class={`w-full h-10 flex items-center rounded-lg text-sm font-medium transition-colors ${
active()
? "bg-dls-active text-dls-text"
: "text-dls-secondary hover:text-dls-text hover:bg-dls-hover"
}`}
} ${rightSidebarExpanded() ? "justify-start gap-3 px-3" : "justify-center px-0"}`}
onClick={() => props.setTab(t)}
title={label}
aria-label={label}
>
{icon}
{label}
<Show when={rightSidebarExpanded()}>{label}</Show>
</button>
);
};
@@ -1016,7 +1026,13 @@ export default function DashboardView(props: DashboardViewProps) {
return (
<div class="flex h-screen w-full bg-dls-surface text-dls-text font-sans overflow-hidden">
<aside class="w-64 hidden md:flex flex-col bg-dls-sidebar border-r border-dls-border p-4">
<aside
class="relative hidden md:flex shrink-0 flex-col bg-dls-sidebar border-r border-dls-border p-4"
style={{
width: `${leftSidebarWidth()}px`,
"min-width": `${leftSidebarWidth()}px`,
}}
>
<div class="flex-1 overflow-y-auto">
<Show when={showUpdatePill()}>
<button
@@ -1068,6 +1084,12 @@ export default function DashboardView(props: DashboardViewProps) {
onImportWorkspaceConfig={props.importWorkspaceConfig}
/>
</div>
<div
class="absolute right-0 top-0 hidden h-full w-2 translate-x-1/2 cursor-col-resize bg-transparent transition-colors hover:bg-gray-6/40 md:block"
onPointerDown={startLeftSidebarResize}
title="Resize workspace column"
aria-label="Resize workspace column"
/>
</aside>
@@ -1433,14 +1455,15 @@ export default function DashboardView(props: DashboardViewProps) {
clientConnected={props.clientConnected}
openworkServerStatus={props.openworkServerStatus}
developerMode={props.developerMode}
onOpenSettings={() => openSettings("general")}
settingsOpen={props.tab === "settings"}
onOpenSettings={props.toggleSettings}
onOpenMessaging={openConfig}
onOpenProviders={() => props.openProviderAuthModal()}
onOpenMcp={() => props.setTab("mcp")}
providerConnectedIds={props.providerConnectedIds}
mcpStatuses={props.mcpStatuses}
/>
<nav class="md:hidden border-t border-dls-border bg-dls-surface">
<nav class="hidden border-t border-dls-border bg-dls-surface">
<div class={`mx-auto max-w-5xl px-4 py-3 grid gap-2 ${props.developerMode ? "grid-cols-5" : "grid-cols-4"}`}>
<button
class={`flex flex-col items-center gap-1 text-xs ${
@@ -1493,8 +1516,27 @@ export default function DashboardView(props: DashboardViewProps) {
</nav>
</main>
<aside class="w-56 hidden md:flex flex-col bg-dls-sidebar border-l border-dls-border p-4">
<div class="space-y-1 pt-2">
<aside
class="flex shrink-0 flex-col overflow-hidden bg-dls-sidebar border-l border-dls-border p-3 transition-[width] duration-200"
style={{
width: `${rightSidebarWidth()}px`,
"min-width": `${rightSidebarWidth()}px`,
}}
>
<div class={`flex items-center pb-3 ${rightSidebarExpanded() ? "justify-end" : "justify-center"}`}>
<button
type="button"
class="flex h-9 w-9 items-center justify-center rounded-lg text-dls-secondary transition-colors hover:bg-dls-hover hover:text-dls-text"
onClick={toggleRightSidebar}
title={rightSidebarExpanded() ? "Collapse sidebar" : "Expand sidebar"}
aria-label={rightSidebarExpanded() ? "Collapse sidebar" : "Expand sidebar"}
>
<Show when={rightSidebarExpanded()} fallback={<ChevronLeft size={18} />}>
<ChevronRight size={18} />
</Show>
</button>
</div>
<div class="space-y-1 pt-1">
{navItem("scheduled", "Automations", <History size={18} />)}
{navItem("skills", "Skills", <Zap size={18} />)}
{navItem("mcp", "Extensions", <Box size={18} />)}

View File

@@ -29,12 +29,14 @@ import {
type OpenworkServerInfo,
type WorkspaceInfo,
} from "../lib/tauri";
import { createWorkspaceShellLayout } from "../lib/workspace-shell-layout";
import {
Box,
ChevronLeft,
ChevronRight,
Check,
Circle,
Cpu,
HardDrive,
History,
ListTodo,
@@ -45,7 +47,6 @@ import {
MoreHorizontal,
Redo2,
Search,
Settings,
Shield,
SlidersHorizontal,
Undo2,
@@ -102,6 +103,7 @@ export type SessionViewProps = {
tab: DashboardTab;
setTab: (tab: DashboardTab) => void;
setSettingsTab: (tab: SettingsTab) => void;
toggleSettings: () => void;
activeWorkspaceDisplay: WorkspaceDisplay;
activeWorkspaceRoot: string;
workspaces: WorkspaceInfo[];
@@ -335,6 +337,13 @@ export default function SessionView(props: SessionViewProps) {
const showRightSidebarSelection = createMemo(() => false);
let commandPaletteInputEl: HTMLInputElement | undefined;
const commandPaletteOptionRefs: HTMLButtonElement[] = [];
const {
leftSidebarWidth,
rightSidebarExpanded,
rightSidebarWidth,
startLeftSidebarResize,
toggleRightSidebar,
} = createWorkspaceShellLayout({ expandedRightWidth: 280 });
createEffect(() => {
if (!isTauriRuntime()) {
@@ -3293,9 +3302,35 @@ export default function SessionView(props: SessionViewProps) {
});
};
const rightSidebarNavButton = (
label: string,
icon: any,
active: boolean,
onClick: () => void,
) => (
<button
type="button"
class={`h-9 w-full rounded-lg text-[13px] font-medium transition-colors ${
active ? "bg-gray-4 text-gray-12" : "text-gray-11 hover:text-gray-12 hover:bg-gray-3"
} ${rightSidebarExpanded() ? "flex items-center gap-2.5 px-3 justify-start" : "flex items-center justify-center px-0"}`}
onClick={onClick}
title={label}
aria-label={label}
>
{icon}
<Show when={rightSidebarExpanded()}>{label}</Show>
</button>
);
return (
<div class="flex h-screen w-full bg-dls-sidebar text-gray-12 font-sans overflow-hidden">
<aside class="w-[260px] hidden lg:flex flex-col bg-dls-sidebar border-r border-gray-6/70 p-3 pt-5">
<aside
class="relative hidden lg:flex shrink-0 flex-col bg-dls-sidebar border-r border-gray-6/70 p-3 pt-5"
style={{
width: `${leftSidebarWidth()}px`,
"min-width": `${leftSidebarWidth()}px`,
}}
>
<div class="flex-1 overflow-y-auto">
<Show when={showUpdatePill()}>
<button
@@ -3348,6 +3383,12 @@ export default function SessionView(props: SessionViewProps) {
onImportWorkspaceConfig={props.importWorkspaceConfig}
/>
</div>
<div
class="absolute right-0 top-0 hidden h-full w-2 translate-x-1/2 cursor-col-resize bg-transparent transition-colors hover:bg-gray-6/40 lg:block"
onPointerDown={startLeftSidebarResize}
title="Resize workspace column"
aria-label="Resize workspace column"
/>
</aside>
@@ -3847,7 +3888,8 @@ export default function SessionView(props: SessionViewProps) {
clientConnected={props.clientConnected}
openworkServerStatus={props.openworkServerStatus}
developerMode={props.developerMode}
onOpenSettings={() => openSettings("general")}
settingsOpen={false}
onOpenSettings={props.toggleSettings}
onOpenMessaging={openConfig}
onOpenProviders={openProviderAuth}
onOpenMcp={openMcp}
@@ -3856,100 +3898,91 @@ export default function SessionView(props: SessionViewProps) {
/>
</main>
<aside class="w-[280px] hidden xl:flex flex-col bg-dls-sidebar border-l border-gray-6/70 p-3">
<div class="flex-1 overflow-y-auto space-y-5 pt-2">
<aside
class="flex shrink-0 flex-col overflow-hidden bg-dls-sidebar border-l border-gray-6/70 p-3 transition-[width] duration-200"
style={{
width: `${rightSidebarWidth()}px`,
"min-width": `${rightSidebarWidth()}px`,
}}
>
<div class={`flex items-center pb-3 ${rightSidebarExpanded() ? "justify-end" : "justify-center"}`}>
<button
type="button"
class="flex h-9 w-9 items-center justify-center rounded-lg text-gray-10 transition-colors hover:bg-gray-3 hover:text-gray-12"
onClick={toggleRightSidebar}
title={rightSidebarExpanded() ? "Collapse sidebar" : "Expand sidebar"}
aria-label={rightSidebarExpanded() ? "Collapse sidebar" : "Expand sidebar"}
>
<Show when={rightSidebarExpanded()} fallback={<ChevronLeft size={18} />}>
<ChevronRight size={18} />
</Show>
</button>
</div>
<div class={`flex-1 overflow-y-auto ${rightSidebarExpanded() ? "space-y-5 pt-1" : "space-y-3 pt-1"}`}>
<div class="space-y-1 mb-2">
<button
type="button"
class={`w-full h-9 flex items-center gap-2.5 px-3 rounded-lg text-[13px] font-medium transition-colors ${
showRightSidebarSelection() && props.tab === "scheduled"
? "bg-gray-4 text-gray-12"
: "text-gray-11 hover:text-gray-12 hover:bg-gray-3"
}`}
onClick={() => {
props.setTab("scheduled");
props.setView("dashboard");
}}
>
<History size={18} />
Automations
</button>
<button
type="button"
class={`w-full h-9 flex items-center gap-2.5 px-3 rounded-lg text-[13px] font-medium transition-colors ${
showRightSidebarSelection() && props.tab === "skills"
? "bg-gray-4 text-gray-12"
: "text-gray-11 hover:text-gray-12 hover:bg-gray-3"
}`}
onClick={() => {
props.setTab("skills");
props.setView("dashboard");
}}
>
<Zap size={18} />
Skills
</button>
<button
type="button"
class={`w-full h-9 flex items-center gap-2.5 px-3 rounded-lg text-[13px] font-medium transition-colors ${
showRightSidebarSelection() && (props.tab === "mcp" || props.tab === "plugins")
? "bg-gray-4 text-gray-12"
: "text-gray-11 hover:text-gray-12 hover:bg-gray-3"
}`}
onClick={() => {
props.setTab("mcp");
props.setView("dashboard");
}}
>
<Box size={18} />
Extensions
</button>
<button
type="button"
class={`w-full h-9 flex items-center gap-2.5 px-3 rounded-lg text-[13px] font-medium transition-colors ${
showRightSidebarSelection() && props.tab === "identities"
? "bg-gray-4 text-gray-12"
: "text-gray-11 hover:text-gray-12 hover:bg-gray-3"
}`}
onClick={() => {
props.setTab("identities");
props.setView("dashboard");
}}
>
<MessageCircle size={18} />
Messaging
</button>
<Show when={props.developerMode}>
<button
type="button"
class={`w-full h-9 flex items-center gap-2.5 px-3 rounded-lg text-[13px] font-medium transition-colors ${
showRightSidebarSelection() && props.tab === "config"
? "bg-gray-4 text-gray-12"
: "text-gray-11 hover:text-gray-12 hover:bg-gray-3"
}`}
onClick={openConfig}
>
<SlidersHorizontal size={18} />
Advanced
</button>
</Show>
{rightSidebarNavButton(
"Automations",
<History size={18} />,
showRightSidebarSelection() && props.tab === "scheduled",
() => {
props.setTab("scheduled");
props.setView("dashboard");
},
)}
{rightSidebarNavButton(
"Skills",
<Zap size={18} />,
showRightSidebarSelection() && props.tab === "skills",
() => {
props.setTab("skills");
props.setView("dashboard");
},
)}
{rightSidebarNavButton(
"Extensions",
<Box size={18} />,
showRightSidebarSelection() && (props.tab === "mcp" || props.tab === "plugins"),
() => {
props.setTab("mcp");
props.setView("dashboard");
},
)}
{rightSidebarNavButton(
"Messaging",
<MessageCircle size={18} />,
showRightSidebarSelection() && props.tab === "identities",
() => {
props.setTab("identities");
props.setView("dashboard");
},
)}
<Show when={props.developerMode}>
{rightSidebarNavButton(
"Advanced",
<SlidersHorizontal size={18} />,
showRightSidebarSelection() && props.tab === "config",
openConfig,
)}
</Show>
</div>
<InboxPanel
id="sidebar-inbox"
client={props.openworkServerClient}
workspaceId={props.openworkServerWorkspaceId}
onToast={(message) => setToastMessage(message)}
/>
<Show when={rightSidebarExpanded()}>
<InboxPanel
id="sidebar-inbox"
client={props.openworkServerClient}
workspaceId={props.openworkServerWorkspaceId}
onToast={(message) => setToastMessage(message)}
/>
<ArtifactsPanel
id="sidebar-artifacts"
files={touchedFiles()}
workspaceRoot={props.activeWorkspaceRoot}
onRevealArtifact={revealArtifact}
onOpenInObsidian={openArtifactInObsidian}
obsidianAvailable={obsidianAvailable()}
/>
<ArtifactsPanel
id="sidebar-artifacts"
files={touchedFiles()}
workspaceRoot={props.activeWorkspaceRoot}
onRevealArtifact={revealArtifact}
onOpenInObsidian={openArtifactInObsidian}
obsidianAvailable={obsidianAvailable()}
/>
</Show>
</div>
</aside>