Phase 3: drag pane past window edge to detach
Extends the existing header-drag gesture (which swaps panes inside the window) with an "outside the window" case: release the drag more than 60px past any viewport edge and the pane detaches into a new window via the same moveToNewWindow path the right-click menu uses. The 60px slop avoids triggering on accidental release over the OS titlebar / window chrome — without it any drag that ended above clientY=0 would fire as a detach, which is wrong because that area is still inside the user's window. No backend changes — Phase 2's transfer mechanism already handles everything; this just wires a second entry point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8ad51787fc
commit
6faf7e5e19
3 changed files with 32 additions and 6 deletions
|
|
@ -52,6 +52,7 @@ A Windows desktop app for running and arranging many WSL terminals at once. Buil
|
|||
| Key | Action |
|
||||
|---|---|
|
||||
| `Right-click pane toolbar → Move to new window` | Pop the active pane into a fresh tiletopia window (PTY survives the move; scrollback ring replays) |
|
||||
| `Drag pane toolbar past the window edge` | Same as the right-click action — release the drag well outside the window to detach into a new window |
|
||||
|
||||
**Navigation**
|
||||
|
||||
|
|
@ -92,7 +93,7 @@ A Windows desktop app for running and arranging many WSL terminals at once. Buil
|
|||
- **SSH host manager** — Titlebar 🔑 SSH hosts opens the manager. Add hostname / user / port / identity file / jump host / extra ssh args. Saved hosts appear in every pane's dropdown.
|
||||
- **Saved passwords** — Optionally save a host's password — stored in Windows Credential Manager (DPAPI-encrypted), never written to hosts.json. When ssh prompts on connect it's typed automatically. Hosts with a saved password show 🔒 in the list.
|
||||
- **Clickable links** — http and https URLs in terminal output get underlined and open in your default browser on click.
|
||||
- **Drag pane headers to swap** — Grab a pane's title bar and drag it onto another pane to swap their tree positions. Useful for reorganizing without keyboard.
|
||||
- **Drag pane headers to swap or detach** — Grab a pane's title bar and drag onto another pane to swap their tree positions. Drag well outside the window edge (more than ~60px past) and release to detach the pane into a new window — same mechanism as the right-click 'Move to new window' action, PTY stays alive.
|
||||
- **Workspace persistence** — Layout, labels, distro choices, and SSH hosts auto-save to %APPDATA%/com.megaproxy.tiletopia (debounced 500ms). Closed panes don't come back — only the structure is restored, shells spawn fresh on next launch.
|
||||
- **Tabs (workspaces)** — Each tab is an independent tile layout — useful for keeping one tab per project. PTYs in non-active tabs keep running (a Claude session in tab A keeps going while you work in tab B). New tab starts with one default-shell pane; close confirms when the tab has live panes. Tabs auto-save to the same workspace.json.
|
||||
- **MCP server (let Claude drive the workspace)** — Titlebar 🤖 opens the MCP control panel. Start the server, then for Claude Desktop click 'Download .mcpb' and drag the file into Settings → Extensions — zero-config because the bundle reads your bearer token from %APPDATA% at launch (no copy-paste, survives token rotation). For Claude Code (terminal CLI) use the fallback snippet in the panel: it wires npx mcp-remote as a stdio shim because Claude Code's HTTP-MCP client ignores static bearer auth and tries OAuth instead. URL + token persist across restarts; Regenerate the token in the panel if it leaks. Default-deny per pane: toggle 🤖 on each pane's toolbar to expose it to MCP.
|
||||
|
|
|
|||
|
|
@ -268,6 +268,12 @@ export default function LeafPane({ leaf }: { leaf: LeafNode }) {
|
|||
[orch.beginHeaderDrag, orch.setHeaderDragOver, leaf.id],
|
||||
);
|
||||
|
||||
/** How far past a viewport edge the cursor must travel before a release
|
||||
* is treated as "drag pane out of window" instead of "drop on empty
|
||||
* space inside this window". Picked so an accidental release on the OS
|
||||
* titlebar (~30px tall) stays inside the threshold. */
|
||||
const PANE_DRAG_OUT_MARGIN = 60;
|
||||
|
||||
const onToolbarPointerUp = useCallback(
|
||||
(e: ReactPointerEvent<HTMLDivElement>) => {
|
||||
const st = dragStartRef.current;
|
||||
|
|
@ -275,12 +281,26 @@ export default function LeafPane({ leaf }: { leaf: LeafNode }) {
|
|||
(e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId);
|
||||
const wasDragging = st.dragging;
|
||||
dragStartRef.current = null;
|
||||
if (wasDragging) {
|
||||
document.body.style.cursor = "";
|
||||
if (!wasDragging) return;
|
||||
document.body.style.cursor = "";
|
||||
|
||||
const releasedFarOutside =
|
||||
e.clientX < -PANE_DRAG_OUT_MARGIN ||
|
||||
e.clientX > window.innerWidth + PANE_DRAG_OUT_MARGIN ||
|
||||
e.clientY < -PANE_DRAG_OUT_MARGIN ||
|
||||
e.clientY > window.innerHeight + PANE_DRAG_OUT_MARGIN;
|
||||
|
||||
if (releasedFarOutside) {
|
||||
// Cancel any in-flight swap state without committing, then pop
|
||||
// this pane into a fresh window. moveToNewWindow handles the
|
||||
// PTY-handoff + closeLeaf in the source.
|
||||
orch.endHeaderDrag(false);
|
||||
orch.moveToNewWindow(leaf.id);
|
||||
} else {
|
||||
orch.endHeaderDrag(true);
|
||||
}
|
||||
},
|
||||
[orch.endHeaderDrag],
|
||||
[orch.endHeaderDrag, orch.moveToNewWindow, leaf.id],
|
||||
);
|
||||
|
||||
const onToolbarPointerCancel = useCallback(
|
||||
|
|
|
|||
|
|
@ -53,6 +53,11 @@ export const SHORTCUT_SECTIONS: ShortcutSection[] = [
|
|||
description:
|
||||
"Pop the active pane into a fresh tiletopia window (PTY survives the move; scrollback ring replays)",
|
||||
},
|
||||
{
|
||||
keys: "Drag pane toolbar past the window edge",
|
||||
description:
|
||||
"Same as the right-click action — release the drag well outside the window to detach into a new window",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -126,8 +131,8 @@ export const TIPS: TipSpec[] = [
|
|||
body: "http and https URLs in terminal output get underlined and open in your default browser on click.",
|
||||
},
|
||||
{
|
||||
title: "Drag pane headers to swap",
|
||||
body: "Grab a pane's title bar and drag it onto another pane to swap their tree positions. Useful for reorganizing without keyboard.",
|
||||
title: "Drag pane headers to swap or detach",
|
||||
body: "Grab a pane's title bar and drag onto another pane to swap their tree positions. Drag well outside the window edge (more than ~60px past) and release to detach the pane into a new window — same mechanism as the right-click 'Move to new window' action, PTY stays alive.",
|
||||
},
|
||||
{
|
||||
title: "Workspace persistence",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue