diff --git a/src/components/TabBar.tsx b/src/components/TabBar.tsx index b897d37..54d0ab8 100644 --- a/src/components/TabBar.tsx +++ b/src/components/TabBar.tsx @@ -1,50 +1,85 @@ -import { Plus } from "lucide-react"; +import { Plus, SlidersHorizontal } from "lucide-react"; import { Button } from "./ui/button"; import { useGrimoire } from "@/core/state"; import { cn } from "@/lib/utils"; +import { WorkspaceSettings } from "./WorkspaceSettings"; +import { useState } from "react"; export function TabBar() { const { state, setActiveWorkspace, createWorkspace } = useGrimoire(); const { workspaces, activeWorkspaceId } = state; + const [settingsWorkspaceId, setSettingsWorkspaceId] = useState( + null, + ); const handleNewTab = () => { createWorkspace(); }; + const handleSettingsClick = (e: React.MouseEvent, workspaceId: string) => { + e.stopPropagation(); // Prevent workspace switch + setSettingsWorkspaceId(workspaceId); + }; + // Sort workspaces by number const sortedWorkspaces = Object.values(workspaces).sort( (a, b) => a.number - b.number, ); return ( -
-
- {sortedWorkspaces.map((ws) => ( - + +
+ ))} + - ))} - + + +
- + + {settingsWorkspaceId && ( + { + if (!open) setSettingsWorkspaceId(null); + }} + /> + )} + ); } diff --git a/src/components/WorkspaceSettings.tsx b/src/components/WorkspaceSettings.tsx new file mode 100644 index 0000000..2eb9b31 --- /dev/null +++ b/src/components/WorkspaceSettings.tsx @@ -0,0 +1,231 @@ +import { useState } from "react"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "./ui/dialog"; +import { Label } from "./ui/Label"; +import { Button } from "./ui/button"; +import { + Sparkles, + SplitSquareHorizontal, + SplitSquareVertical, +} from "lucide-react"; +import { useGrimoire } from "@/core/state"; +import type { LayoutConfig } from "@/types/app"; +import { cn } from "@/lib/utils"; + +interface WorkspaceSettingsProps { + workspaceId: string; + open: boolean; + onOpenChange: (open: boolean) => void; +} + +export function WorkspaceSettings({ + workspaceId, + open, + onOpenChange, +}: WorkspaceSettingsProps) { + const { state, updateWorkspaceLayoutConfig } = useGrimoire(); + const workspace = state.workspaces[workspaceId]; + + // Local state for settings + const [insertionMode, setInsertionMode] = useState( + workspace?.layoutConfig?.insertionMode || "smart" + ); + const [splitPercentage, setSplitPercentage] = useState( + workspace?.layoutConfig?.splitPercentage || 50 + ); + const [insertionPosition, setInsertionPosition] = useState( + workspace?.layoutConfig?.insertionPosition || "second" + ); + + if (!workspace) return null; + + const handleSave = () => { + updateWorkspaceLayoutConfig(workspaceId, { + insertionMode, + splitPercentage, + insertionPosition, + }); + onOpenChange(false); + }; + + const handleReset = () => { + setInsertionMode("smart"); + setSplitPercentage(50); + setInsertionPosition("second"); + }; + + return ( + + + + + Workspace {workspace.number} + {workspace.label && ` - ${workspace.label}`} Settings + + + Configure how new windows are inserted into this workspace layout. + + + +
+ {/* Insertion Mode */} +
+ +
+ + + + + +
+
+ + {/* Split Percentage */} +
+
+ + + {splitPercentage}% / {100 - splitPercentage}% + +
+ setSplitPercentage(Number(e.target.value))} + className="w-full h-2 bg-muted rounded-lg appearance-none cursor-pointer accent-primary" + /> +
+ Existing content gets {splitPercentage}% + New window gets {100 - splitPercentage}% +
+
+ + {/* Insertion Position */} +
+ +
+ + + +
+
+ + {/* Preview */} +
+
Preview:
+
+ + {insertionMode === "smart" && "Smart mode"} + {insertionMode === "row" && "Horizontal splits"} + {insertionMode === "column" && "Vertical splits"} + + {" · "} + + {splitPercentage}%/{100 - splitPercentage}% split + + {" · "} + + New window on{" "} + {insertionPosition === "first" ? "left/top" : "right/bottom"} + +
+
+
+ + {/* Footer */} +
+ +
+ + +
+
+
+
+ ); +} diff --git a/src/core/logic.ts b/src/core/logic.ts index 0178a19..656e7f7 100644 --- a/src/core/logic.ts +++ b/src/core/logic.ts @@ -344,3 +344,32 @@ export const updateWindow = ( }, }; }; + +/** + * Updates the layout configuration for a workspace. + * Controls how new windows are inserted into the workspace layout. + */ +export const updateWorkspaceLayoutConfig = ( + state: GrimoireState, + workspaceId: string, + layoutConfig: Partial, +): GrimoireState => { + const workspace = state.workspaces[workspaceId]; + if (!workspace) { + return state; // Workspace doesn't exist, return unchanged + } + + return { + ...state, + workspaces: { + ...state.workspaces, + [workspaceId]: { + ...workspace, + layoutConfig: { + ...workspace.layoutConfig, + ...layoutConfig, + }, + }, + }, + }; +}; diff --git a/src/core/state.ts b/src/core/state.ts index 9155daa..4773892 100644 --- a/src/core/state.ts +++ b/src/core/state.ts @@ -229,6 +229,17 @@ export const useGrimoire = () => { [setState], ); + const updateWorkspaceLayoutConfig = useCallback( + ( + workspaceId: string, + layoutConfig: Partial, + ) => + setState((prev) => + Logic.updateWorkspaceLayoutConfig(prev, workspaceId, layoutConfig), + ), + [setState], + ); + return { state, locale: state.locale || browserLocale, @@ -242,5 +253,6 @@ export const useGrimoire = () => { setActiveWorkspace, setActiveAccount, setActiveAccountRelays, + updateWorkspaceLayoutConfig, }; };