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

@@ -227,6 +227,52 @@ export const moveWindowToWorkspace = (
};
};
/**
* Creates a new workspace and moves a window to it.
* Returns an object with the new state and the new workspace ID.
*/
export const moveWindowToNewWorkspace = (
state: GrimoireState,
windowId: string,
): { state: GrimoireState; newWorkspaceId: string } => {
const currentId = state.activeWorkspaceId;
const currentWs = state.workspaces[currentId];
// Find next available workspace number
const nextNumber = findLowestAvailableWorkspaceNumber(state.workspaces);
// Create new workspace ID
const newWorkspaceId = uuidv4();
// Remove window from current workspace
const newCurrentLayout = removeFromLayout(currentWs.layout, windowId);
const newCurrentWindowIds = currentWs.windowIds.filter(
(id) => id !== windowId,
);
// Create new state with new workspace and moved window
const newState: GrimoireState = {
...state,
activeWorkspaceId: newWorkspaceId,
workspaces: {
...state.workspaces,
[currentId]: {
...currentWs,
layout: newCurrentLayout,
windowIds: newCurrentWindowIds,
},
[newWorkspaceId]: {
id: newWorkspaceId,
number: nextNumber,
layout: windowId,
windowIds: [windowId],
},
},
};
return { state: newState, newWorkspaceId };
};
export const updateLayout = (
state: GrimoireState,
layout: MosaicNode<string> | null,

View File

@@ -236,6 +236,20 @@ export const useGrimoire = () => {
[setState],
);
const moveWindowToNewWorkspace = useCallback(
(windowId: string): number => {
let newWorkspaceNumber = 0;
setState((prev) => {
const result = Logic.moveWindowToNewWorkspace(prev, windowId);
newWorkspaceNumber =
result.state.workspaces[result.newWorkspaceId].number;
return result.state;
});
return newWorkspaceNumber;
},
[setState],
);
const updateLayout = useCallback(
(layout: any) => {
setState((prev) => Logic.updateLayout(prev, layout));
@@ -385,6 +399,7 @@ export const useGrimoire = () => {
updateWindow,
removeWindow,
moveWindowToWorkspace,
moveWindowToNewWorkspace,
updateLayout,
setActiveWorkspace,
setActiveAccount,