Skip to content

Commit 92f47ed

Browse files
authored
feat: slimmed down focus ui and move default terminal position (#780)
# Add Terminal Tab to Default Panel Layout and Improve Focus Workspace UX ### TL;DR Adds a Terminal tab to the default panel layout and improves the Focus Workspace UX by moving it from the bottom panel to the Changes panel. ### What changed? - Added a Terminal tab to the default panel layout in the bottom panel - Removed the `BottomPanel` component from the right sidebar - Simplified the `RightSidebarContent` component by removing the bottom panel section - Enhanced the `ChangesPanel` component with Focus Workspace functionality: - Added a Focus CTA button at the top of the Changes panel - Integrated focus status indicators (synced/not synced) - Added toggle functionality for focusing and unfocusing ### How to test? 1. Open the application and verify that the Terminal tab appears in the default panel layout 2. Check that the right sidebar no longer has a separate bottom panel 3. Navigate to a worktree workspace and verify that the Focus Workspace controls appear at the top of the Changes panel 4. Test focusing and unfocusing the workspace using the new controls 5. Verify that the focus status is correctly displayed ### Why make this change? This change improves the user experience by: 1. Making the Terminal more accessible as a default tab 2. Simplifying the UI by removing the redundant bottom panel 3. Placing the Focus Workspace controls in a more logical location within the Changes panel 4. Providing clearer visual feedback about the focus state The new design creates a more intuitive workflow for users working with worktree workspaces and reduces UI clutter. [focus state.mov <span class="graphite__hidden">(uploaded via Graphite)</span> <img class="graphite__hidden" src="https://app.graphite.com/user-attachments/thumbnails/d132e8c8-7d40-4883-ad58-202c3d5c2a76.mov" />](https://app.graphite.com/user-attachments/video/d132e8c8-7d40-4883-ad58-202c3d5c2a76.mov)
1 parent 68b4ad9 commit 92f47ed

4 files changed

Lines changed: 96 additions & 143 deletions

File tree

apps/twig/src/renderer/features/panels/store/panelLayoutStore.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,18 @@ function createDefaultPanelTree(): PanelNode {
125125
closeable: false,
126126
draggable: true,
127127
},
128+
{
129+
id: DEFAULT_TAB_IDS.SHELL,
130+
label: "Terminal",
131+
data: {
132+
type: "terminal",
133+
terminalId: DEFAULT_TAB_IDS.SHELL,
134+
cwd: "",
135+
},
136+
component: null,
137+
closeable: true,
138+
draggable: true,
139+
},
128140
],
129141
activeTabId: DEFAULT_TAB_IDS.LOGS,
130142
showTabs: true,

apps/twig/src/renderer/features/right-sidebar/components/BottomPanel.tsx

Lines changed: 0 additions & 136 deletions
This file was deleted.

apps/twig/src/renderer/features/right-sidebar/components/RightSidebarContent.tsx

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { Panel } from "@features/panels/components/Panel";
22
import { PanelGroup } from "@features/panels/components/PanelGroup";
3-
import { PanelResizeHandle } from "@features/panels/components/PanelResizeHandle";
43
import type { Task } from "@shared/types";
5-
import { BottomPanel } from "./BottomPanel";
64
import { TopPanel } from "./TopPanel";
75

86
interface RightSidebarContentProps {
@@ -23,10 +21,6 @@ export function RightSidebarContent({
2321
<Panel defaultSize={60} minSize={20}>
2422
<TopPanel taskId={taskId} task={task} />
2523
</Panel>
26-
<PanelResizeHandle style={{ height: "1px" }} />
27-
<Panel defaultSize={40} minSize={20}>
28-
<BottomPanel taskId={taskId} />
29-
</Panel>
3024
</PanelGroup>
3125
);
3226
}

apps/twig/src/renderer/features/task-detail/components/ChangesPanel.tsx

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import { Tooltip } from "@components/ui/Tooltip";
44
import { isDiffTabActiveInTree, usePanelLayoutStore } from "@features/panels";
55
import { usePendingPermissionsForTask } from "@features/sessions/stores/sessionStore";
66
import { useCwd } from "@features/sidebar/hooks/useCwd";
7+
import { useFocusWorkspace } from "@features/workspace/hooks/useFocusWorkspace";
78
import {
89
ArrowCounterClockwiseIcon,
10+
ArrowsClockwise,
911
CaretDownIcon,
1012
CaretUpIcon,
1113
CodeIcon,
@@ -15,9 +17,11 @@ import {
1517
import {
1618
Badge,
1719
Box,
20+
Button,
1821
DropdownMenu,
1922
Flex,
2023
IconButton,
24+
Spinner,
2125
Text,
2226
} from "@radix-ui/themes";
2327
import { trpcVanilla } from "@renderer/trpc/client";
@@ -376,6 +380,8 @@ function ChangedFileItem({
376380

377381
export function ChangesPanel({ taskId, task: _task }: ChangesPanelProps) {
378382
const workspace = useWorkspaceStore((s) => s.workspaces[taskId]);
383+
const { isFocused, isFocusLoading, handleToggleFocus, handleUnfocus } =
384+
useFocusWorkspace(taskId);
379385
const repoPath = useCwd(taskId);
380386
const layout = usePanelLayoutStore((state) => state.getLayout(taskId));
381387
const openDiff = usePanelLayoutStore((state) => state.openDiff);
@@ -443,6 +449,73 @@ export function ChangesPanel({ taskId, task: _task }: ChangesPanelProps) {
443449
return isDiffTabActiveInTree(layout.panelTree, file.path, file.status);
444450
};
445451

452+
const showFocusCta = workspace?.mode === "worktree";
453+
const focusCta = showFocusCta ? (
454+
<Box px="2" pb="2">
455+
<Flex
456+
align="center"
457+
justify="between"
458+
gap="2"
459+
px="3"
460+
py="2"
461+
style={{
462+
borderRadius: "999px",
463+
border: "1px solid var(--gray-4)",
464+
backgroundColor: "var(--gray-2)",
465+
}}
466+
>
467+
<Flex align="center" gap="2">
468+
{isFocused ? (
469+
<Box
470+
style={{
471+
width: "8px",
472+
height: "8px",
473+
borderRadius: "999px",
474+
backgroundColor: "var(--green-9)",
475+
}}
476+
/>
477+
) : (
478+
<ArrowsClockwise size={14} weight="bold" />
479+
)}
480+
<Text size="1" style={{ color: "var(--gray-11)" }}>
481+
{isFocused ? "Workspace synced" : "Focus workspace"}
482+
</Text>
483+
</Flex>
484+
{isFocused ? (
485+
<Button
486+
size="1"
487+
variant="ghost"
488+
color="gray"
489+
onClick={handleUnfocus}
490+
disabled={isFocusLoading}
491+
style={{
492+
textDecoration: "underline",
493+
textUnderlineOffset: "2px",
494+
color: "var(--gray-11)",
495+
}}
496+
>
497+
{isFocusLoading ? <Spinner size="1" /> : "Cancel"}
498+
</Button>
499+
) : (
500+
<Button
501+
size="1"
502+
variant="ghost"
503+
color="gray"
504+
onClick={handleToggleFocus}
505+
disabled={isFocusLoading}
506+
style={{
507+
textDecoration: "underline",
508+
textUnderlineOffset: "2px",
509+
color: "var(--gray-11)",
510+
}}
511+
>
512+
{isFocusLoading ? <Spinner size="1" /> : "Focus"}
513+
</Button>
514+
)}
515+
</Flex>
516+
</Box>
517+
) : null;
518+
446519
if (!repoPath) {
447520
return <PanelMessage>No repository path available</PanelMessage>;
448521
}
@@ -454,12 +527,22 @@ export function ChangesPanel({ taskId, task: _task }: ChangesPanelProps) {
454527
const hasChanges = changedFiles.length > 0;
455528

456529
if (!hasChanges) {
457-
return <PanelMessage>No file changes yet</PanelMessage>;
530+
return (
531+
<Box height="100%" overflowY="auto" py="2">
532+
<Flex direction="column" height="100%">
533+
{focusCta}
534+
<Box flexGrow="1">
535+
<PanelMessage>No file changes yet</PanelMessage>
536+
</Box>
537+
</Flex>
538+
</Box>
539+
);
458540
}
459541

460542
return (
461543
<Box height="100%" overflowY="auto" py="2">
462544
<Flex direction="column">
545+
{focusCta}
463546
{changedFiles.map((file) => (
464547
<ChangedFileItem
465548
key={file.path}

0 commit comments

Comments
 (0)