feat: add "+ new tab" option to move window menu (#229)

* feat: add "+ new tab" option to move window menu

Adds a "+ New tab" option at the top of the "Move to tab" submenu in the
window toolbar. This allows users to create a new tab and move the current
window to it in a single action, streamlining the workflow.

Changes:
- Add moveWindowToNewWorkspace function in logic.ts
- Expose moveWindowToNewWorkspace through useGrimoire hook
- Always show "Move to tab" menu (not just when multiple tabs exist)
- Add "+ New tab" option with Plus icon at top of submenu

https://claude.ai/code/session_01Hy2vYBooPyrF7ZJCodcCav

* refactor: simplify new tab menu item copy

- Change menu text from "+ New tab" to "New" (icon already shows +)
- Simplify toast to just "Moved to new tab"

https://claude.ai/code/session_01Hy2vYBooPyrF7ZJCodcCav

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Alejandro
2026-01-29 21:08:34 +01:00
committed by GitHub
parent 5d35832f85
commit 121fbb7654
3 changed files with 93 additions and 27 deletions

View File

@@ -7,6 +7,7 @@ import {
CopyCheck,
ArrowRightFromLine,
ExternalLink,
Plus,
} from "lucide-react";
import { useSetAtom } from "jotai";
import { useState } from "react";
@@ -44,24 +45,29 @@ export function WindowToolbar({
}: WindowToolbarProps) {
const setEditMode = useSetAtom(commandLauncherEditModeAtom);
const [showSpellDialog, setShowSpellDialog] = useState(false);
const { state, moveWindowToWorkspace, setActiveWorkspace } = useGrimoire();
const { state, moveWindowToWorkspace, moveWindowToNewWorkspace } =
useGrimoire();
// Get workspaces for move action
const otherWorkspaces = Object.values(state.workspaces)
.filter((ws) => ws.id !== state.activeWorkspaceId)
.sort((a, b) => a.number - b.number);
const hasMultipleWorkspaces = Object.keys(state.workspaces).length > 1;
const handleMoveToWorkspace = (targetWorkspaceId: string) => {
if (!window) return;
const targetWorkspace = state.workspaces[targetWorkspaceId];
moveWindowToWorkspace(window.id, targetWorkspaceId);
setActiveWorkspace(targetWorkspaceId);
toast.success(
`Moved to tab ${targetWorkspace.number}${targetWorkspace.label ? ` (${targetWorkspace.label})` : ""}`,
);
};
const handleMoveToNewTab = () => {
if (!window) return;
moveWindowToNewWorkspace(window.id);
toast.success("Moved to new tab");
};
const handleEdit = () => {
if (!window) return;
@@ -195,31 +201,30 @@ export function WindowToolbar({
Pop out window
</DropdownMenuItem>
{/* Move to tab submenu - only show if multiple workspaces */}
{hasMultipleWorkspaces && (
<>
<DropdownMenuSeparator />
<DropdownMenuSub>
<DropdownMenuSubTrigger
disabled={otherWorkspaces.length === 0}
{/* Move to tab submenu */}
<DropdownMenuSeparator />
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<ArrowRightFromLine className="size-4 mr-2" />
Move to tab
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem onClick={handleMoveToNewTab}>
<Plus className="size-4 mr-2" />
New
</DropdownMenuItem>
{otherWorkspaces.length > 0 && <DropdownMenuSeparator />}
{otherWorkspaces.map((ws) => (
<DropdownMenuItem
key={ws.id}
onClick={() => handleMoveToWorkspace(ws.id)}
>
<ArrowRightFromLine className="size-4 mr-2" />
Move to tab
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
{otherWorkspaces.map((ws) => (
<DropdownMenuItem
key={ws.id}
onClick={() => handleMoveToWorkspace(ws.id)}
>
{ws.number}
{ws.label ? ` ${ws.label}` : ""}
</DropdownMenuItem>
))}
</DropdownMenuSubContent>
</DropdownMenuSub>
</>
)}
{ws.number}
{ws.label ? ` ${ws.label}` : ""}
</DropdownMenuItem>
))}
</DropdownMenuSubContent>
</DropdownMenuSub>
{/* REQ/COUNT-specific actions */}
{isSpellableWindow && (