mirror of
https://github.com/different-ai/openwork
synced 2026-05-14 11:06:25 +02:00
fix(app): keep workspace shell navigation reachable (#874)
This commit is contained in:
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 |
26
packages/app/pr/workspace-session-sidebar-ux.md
Normal file
26
packages/app/pr/workspace-session-sidebar-ux.md
Normal 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`)
|
||||
@@ -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(),
|
||||
|
||||
@@ -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>
|
||||
|
||||
138
packages/app/src/app/lib/workspace-shell-layout.ts
Normal file
138
packages/app/src/app/lib/workspace-shell-layout.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
@@ -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} />)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user