Add last-visited tab quick switch (Ctrl+Tab) and surface.last API#1106
Add last-visited tab quick switch (Ctrl+Tab) and surface.last API#1106maxbeizer wants to merge 2 commits intomanaflow-ai:mainfrom
Conversation
Implement per-pane MRU tab tracking so Ctrl+Tab toggles to the previously visited surface instead of cycling through tabs. - Add currentSelectedPanelByPaneId/lastVisitedPanelByPaneId tracking to Workspace with pruning for closed surfaces - Add selectLastVisitedSurface() to Workspace and TabManager - Record surface selections in BonsplitDelegate didSelectTab - Map Ctrl+Tab and Ctrl+Shift+Tab to last-visited surface switching - Add surface.last v2 socket method and expose in capabilities - Add last-tab CLI command (tmux compat) - Add last_surface() test helper and compat matrix coverage Fixes manaflow-ai#474 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@maxbeizer is attempting to deploy a commit to the Manaflow Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthroughAdds last-visited surface (tab) navigation: new "last-tab" CLI command, Ctrl+Tab mapped to select last-visited surface, per-pane MRU tracking in Workspace, and V1/V2 API handlers for Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant AppDelegate
participant TabManager
participant Workspace
participant BonsplitDelegate
User->>AppDelegate: Press Ctrl+Tab / run last-tab
AppDelegate->>TabManager: selectLastVisitedSurface()
TabManager->>Workspace: selectLastVisitedSurface(inPane?)
Workspace->>Workspace: Lookup current & last panels for pane
Workspace->>BonsplitDelegate: instruct selection change
BonsplitDelegate->>Workspace: recordSurfaceSelection(panelId)
Workspace-->>User: Focus changes to last-visited surface
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~28 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
CLI/cmux.swift (1)
5985-5992: Prefer tab-formatted output forlast-tab.Text mode currently falls back to
v2OKSummary(...), so the new tab command printssurface:handles. This is functional, but it breaks the tab-oriented terminology already used bytab-actionandrename-tabin this file. ReusingformatTabHandle(...)here would keep the tmux-compat surface consistent.🔧 Suggested refactor
case "last-tab": let workspaceArg = workspaceFromArgsOrEnv(commandArgs, windowOverride: windowOverride) var params: [String: Any] = [:] let wsId = try normalizeWorkspaceHandle(workspaceArg, client: client) if let wsId { params["workspace_id"] = wsId } let payload = try client.sendV2(method: "surface.last", params: params) - printV2Payload(payload, jsonOutput: jsonOutput, idFormat: idFormat, fallbackText: v2OKSummary(payload, idFormat: idFormat, kinds: ["surface", "pane", "workspace"])) + var summaryParts = ["OK"] + if let tabHandle = formatTabHandle(payload, idFormat: idFormat) { + summaryParts.append("tab=\(tabHandle)") + } + if let paneHandle = formatHandle(payload, kind: "pane", idFormat: idFormat) { + summaryParts.append("pane=\(paneHandle)") + } + if let workspaceHandle = formatHandle(payload, kind: "workspace", idFormat: idFormat) { + summaryParts.append("workspace=\(workspaceHandle)") + } + printV2Payload( + payload, + jsonOutput: jsonOutput, + idFormat: idFormat, + fallbackText: summaryParts.joined(separator: " ") + )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@CLI/cmux.swift` around lines 5985 - 5992, The "last-tab" branch currently uses v2OKSummary as the text fallback which yields "surface:" handles and breaks tab-oriented terminology; update the fallback text passed to printV2Payload (in the case "last-tab" block that calls workspaceFromArgsOrEnv, normalizeWorkspaceHandle, client.sendV2 and printV2Payload) to use the tab formatter instead—call formatTabHandle (or derive a tab-friendly string from the payload) as the fallbackText so the printed output matches the tab terminology used by tab-action and rename-tab rather than the generic v2OKSummary.Sources/Workspace.swift (1)
3895-3919: Consider cleaning up MRU history when panes are closed.When a pane is closed via
splitTabBar(_:didClosePane:), the entries incurrentSelectedPanelByPaneIdandlastVisitedPanelByPaneIdfor that pane's UUID persist indefinitely. While this won't cause functional issues (pane UUIDs are unique and won't be reused), it creates a minor memory leak.♻️ Proposed cleanup in didClosePane
Add cleanup in
splitTabBar(_:didClosePane:)around line 4271:func splitTabBar(_ controller: BonsplitController, didClosePane paneId: PaneID) { let closedPanelIds = pendingPaneClosePanelIds.removeValue(forKey: paneId.id) ?? [] let shouldScheduleFocusReconcile = !isDetachingCloseTransaction + + // Clean up MRU history for the closed pane + currentSelectedPanelByPaneId.removeValue(forKey: paneId.id) + lastVisitedPanelByPaneId.removeValue(forKey: paneId.id)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/Workspace.swift` around lines 3895 - 3919, The MRU maps currentSelectedPanelByPaneId and lastVisitedPanelByPaneId are not cleaned when a pane is closed; update the SplitTabBar delegate method splitTabBar(_:didClosePane:) to remove the pane's UUID key from both dictionaries (use pane.id to identify the key) so entries for a closed pane are removed, e.g. call currentSelectedPanelByPaneId.removeValue(forKey: pane.id) and lastVisitedPanelByPaneId.removeValue(forKey: pane.id) (or invoke a shared cleanup helper) inside splitTabBar(_:didClosePane:).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@CLI/cmux.swift`:
- Around line 4631-4639: Update the help text for the "last-tab" command to
document that the --workspace flag also accepts numeric workspace indexes (since
the implementation uses normalizeWorkspaceHandle(...)); locate the "case
\"last-tab\"" help string in CLI/cmux.swift and add an "index" (or show a
numeric selector) to the --workspace description (e.g., change "<id|ref>" to
"<id|ref|index>" or similar) so the CLI docs match the
parser/normalizeWorkspaceHandle behavior.
---
Nitpick comments:
In `@CLI/cmux.swift`:
- Around line 5985-5992: The "last-tab" branch currently uses v2OKSummary as the
text fallback which yields "surface:" handles and breaks tab-oriented
terminology; update the fallback text passed to printV2Payload (in the case
"last-tab" block that calls workspaceFromArgsOrEnv, normalizeWorkspaceHandle,
client.sendV2 and printV2Payload) to use the tab formatter instead—call
formatTabHandle (or derive a tab-friendly string from the payload) as the
fallbackText so the printed output matches the tab terminology used by
tab-action and rename-tab rather than the generic v2OKSummary.
In `@Sources/Workspace.swift`:
- Around line 3895-3919: The MRU maps currentSelectedPanelByPaneId and
lastVisitedPanelByPaneId are not cleaned when a pane is closed; update the
SplitTabBar delegate method splitTabBar(_:didClosePane:) to remove the pane's
UUID key from both dictionaries (use pane.id to identify the key) so entries for
a closed pane are removed, e.g. call
currentSelectedPanelByPaneId.removeValue(forKey: pane.id) and
lastVisitedPanelByPaneId.removeValue(forKey: pane.id) (or invoke a shared
cleanup helper) inside splitTabBar(_:didClosePane:).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 96cb62eb-2cc0-4f32-b2bf-55700fc52f02
📒 Files selected for processing (7)
CLI/cmux.swiftSources/AppDelegate.swiftSources/TabManager.swiftSources/TerminalController.swiftSources/Workspace.swifttests_v2/cmux.pytests_v2/test_tmux_compat_matrix.py
There was a problem hiding this comment.
1 issue found across 7 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="Sources/Workspace.swift">
<violation number="1" location="Sources/Workspace.swift:4194">
P2: MRU tab history is only recorded in `didSelectTab`, but this callback is known to be skipped in some selection flows, so Ctrl+Tab can use stale targets.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
|
|
||
| func splitTabBar(_ controller: BonsplitController, didSelectTab tab: Bonsplit.Tab, inPane pane: PaneID) { | ||
| if let panelId = panelIdFromSurfaceId(tab.id) { | ||
| recordSurfaceSelection(panelId: panelId, in: pane) |
There was a problem hiding this comment.
P2: MRU tab history is only recorded in didSelectTab, but this callback is known to be skipped in some selection flows, so Ctrl+Tab can use stale targets.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Sources/Workspace.swift, line 4194:
<comment>MRU tab history is only recorded in `didSelectTab`, but this callback is known to be skipped in some selection flows, so Ctrl+Tab can use stale targets.</comment>
<file context>
@@ -4139,6 +4190,9 @@ extension Workspace: BonsplitDelegate {
func splitTabBar(_ controller: BonsplitController, didSelectTab tab: Bonsplit.Tab, inPane pane: PaneID) {
+ if let panelId = panelIdFromSurfaceId(tab.id) {
+ recordSurfaceSelection(panelId: panelId, in: pane)
+ }
applyTabSelection(tabId: tab.id, inPane: pane)
</file context>
Greptile SummaryThis PR implements a per-pane MRU ("most-recently-used") tab toggle so that The overall design is well-structured and consistent with existing patterns in the codebase. Key concerns:
Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant AppDelegate
participant TabManager
participant Workspace
participant BonsplitController
participant TerminalController
Note over User,TerminalController: Keyboard shortcut path (Ctrl+Tab)
User->>AppDelegate: Ctrl+Tab / Ctrl+Shift+Tab
AppDelegate->>TabManager: selectLastVisitedSurface()
TabManager->>Workspace: selectLastVisitedSurface()
Workspace->>Workspace: pruneSurfaceSelectionHistory(pane)
Workspace->>Workspace: recordSurfaceSelection(currentPanel, pane)
Note right of Workspace: Updates currentSelectedPanelByPaneId\nand lastVisitedPanelByPaneId
Workspace->>BonsplitController: selectTab(targetTabId)
Workspace->>Workspace: applyTabSelection(tabId, pane)
Note over User,TerminalController: Socket API path (surface.last)
User->>TerminalController: surface.last {workspace_id?}
TerminalController->>Workspace: selectLastVisitedSurface()
Workspace->>BonsplitController: selectTab(targetTabId)
Workspace->>Workspace: applyTabSelection(tabId, pane)
TerminalController-->>User: {surface_id, pane_id, workspace_id, window_id}
Note over User,BonsplitController: MRU recording path (tab bar click)
User->>BonsplitController: click tab
BonsplitController->>Workspace: splitTabBar(_:didSelectTab:inPane:)
Workspace->>Workspace: recordSurfaceSelection(panelId, pane)
Workspace->>Workspace: applyTabSelection(tabId, pane)
|
| // Surface quick switch (legacy Ctrl+Tab support) | ||
| if matchTabShortcut(event: event, shortcut: StoredShortcut(key: "\t", command: false, shift: false, option: false, control: true)) { | ||
| tabManager?.selectNextSurface() | ||
| tabManager?.selectLastVisitedSurface() | ||
| return true | ||
| } | ||
| if matchTabShortcut(event: event, shortcut: StoredShortcut(key: "\t", command: false, shift: true, option: false, control: true)) { | ||
| tabManager?.selectPreviousSurface() | ||
| tabManager?.selectLastVisitedSurface() | ||
| return true | ||
| } |
There was a problem hiding this comment.
Ctrl+Shift+Tab now has identical behavior to Ctrl+Tab
Previously these two shortcuts provided directional tab cycling — forward with Ctrl+Tab and backward with Ctrl+Shift+Tab. After this change both shortcuts call selectLastVisitedSurface(), making Ctrl+Shift+Tab redundant.
The PR description explicitly calls these "legacy" shortcuts, but conventionally Ctrl+Shift+Tab is expected to reverse the direction of Ctrl+Tab. Users who relied on Ctrl+Shift+Tab to cycle backward through surfaces will silently get MRU-toggle behavior instead, with no alternative key binding to cycle backward via the keyboard (the selectPreviousSurface() path is still accessible via Cmd+Shift+[ and the palette, but not via a ctrl-tab equivalent).
Worth confirming this is the intended end-state, or consider mapping Ctrl+Shift+Tab to selectPreviousSurface() to preserve backward compat.
Sources/Workspace.swift
Outdated
| /// Select the previously visited surface in the currently focused pane (MRU toggle). | ||
| func selectLastVisitedSurface() { | ||
| guard let focusedPaneId = bonsplitController.focusedPaneId else { return } | ||
| pruneSurfaceSelectionHistory(for: focusedPaneId) | ||
|
|
||
| guard let currentTabId = bonsplitController.selectedTab(inPane: focusedPaneId)?.id, | ||
| let currentPanelId = panelIdFromSurfaceId(currentTabId) else { return } | ||
| recordSurfaceSelection(panelId: currentPanelId, in: focusedPaneId) |
There was a problem hiding this comment.
Redundant pruneSurfaceSelectionHistory call
selectLastVisitedSurface() calls pruneSurfaceSelectionHistory(for: focusedPaneId) at line 3211, but then immediately calls recordSurfaceSelection(panelId:in:) at line 3215, which opens with another call to pruneSurfaceSelectionHistory. The initial explicit prune is therefore a no-op — the history will be pruned again inside recordSurfaceSelection before any mutation happens.
| /// Select the previously visited surface in the currently focused pane (MRU toggle). | |
| func selectLastVisitedSurface() { | |
| guard let focusedPaneId = bonsplitController.focusedPaneId else { return } | |
| pruneSurfaceSelectionHistory(for: focusedPaneId) | |
| guard let currentTabId = bonsplitController.selectedTab(inPane: focusedPaneId)?.id, | |
| let currentPanelId = panelIdFromSurfaceId(currentTabId) else { return } | |
| recordSurfaceSelection(panelId: currentPanelId, in: focusedPaneId) | |
| func selectLastVisitedSurface() { | |
| guard let focusedPaneId = bonsplitController.focusedPaneId else { return } | |
| guard let currentTabId = bonsplitController.selectedTab(inPane: focusedPaneId)?.id, | |
| let currentPanelId = panelIdFromSurfaceId(currentTabId) else { return } | |
| recordSurfaceSelection(panelId: currentPanelId, in: focusedPaneId) |
| let before = ws.focusedPanelId | ||
| ws.selectLastVisitedSurface() | ||
| guard let after = ws.focusedPanelId, after != before else { return } |
There was a problem hiding this comment.
Success detection via focusedPanelId diff may silently return an error on the first-ever invocation
let before = ws.focusedPanelId
ws.selectLastVisitedSurface()
guard let after = ws.focusedPanelId, after != before else { return }selectLastVisitedSurface() early-returns (without switching) if lastVisitedPanelByPaneId[pane] is absent or equals the current panel. The guard here catches that case correctly.
However, there is a subtle scenario: if selectLastVisitedSurface() does switch tabs but applyTabSelection posts an async UI update that sets focusedPanelId on the next run-loop cycle rather than synchronously, ws.focusedPanelId will still equal before immediately after the call. The function would then return the default "not_found" error even though the tab switch was successfully triggered.
Other v2Surface* methods (e.g. v2SurfaceFocus) avoid this by setting result = .ok(...) unconditionally after the action, not by diffing state. Aligning with that pattern would be safer:
ws.selectLastVisitedSurface()
guard let after = ws.focusedPanelId else { return }
let paneId = ws.paneId(forPanelId: after)?.id
let windowId = v2ResolveWindowId(tabManager: tabManager)
result = .ok([...])(This still returns "not_found" if focusedPanelId is nil, and returns ok with the now-focused surface otherwise — consistent with how v2SurfaceFocus behaves.)
- Honor pane_id param in surface.last to target a specific pane instead of always using the focused pane - Add selectLastVisitedSurface(inPane:) optional parameter - Clean up MRU history in didClosePane to prevent memory leaks - Use formatTabHandle for tab-oriented CLI output in last-tab - Document workspace index support in last-tab help text Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@CLI/cmux.swift`:
- Around line 5985-6002: The "last-tab" case must reject unexpected positional
tokens or unknown flags instead of silently proceeding; before calling
workspaceFromArgsOrEnv/normalizeWorkspaceHandle/sendV2, validate commandArgs
contains only the allowed options (e.g., the workspace flag) and no stray
positional tokens—if any unexpected args are present, print a clear error and
exit non-zero (or return a failure) so the command fails fast; implement this
check at the top of the "last-tab" case using the existing commandArgs variable
and short-circuit before invoking workspaceFromArgsOrEnv,
normalizeWorkspaceHandle, client.sendV2, or printV2Payload.
In `@Sources/TerminalController.swift`:
- Around line 3771-3773: The code incorrectly checks ws.focusedPanelId to
determine if selectLastVisitedSurface(inPane:) switched surfaces; instead, read
the selected surface for the target pane before and after calling
ws.selectLastVisitedSurface(inPane: targetPaneId) and compare those values (e.g.
obtain ws.selectedSurface(forPane: targetPaneId) or equivalent) so you only
report success when the pane's selected surface actually changed; do not rely on
ws.focusedPanelId because selectLastVisitedSurface(inPane:) may change focus
even when no alternate surface exists.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8f4fb054-b7d7-4568-b212-40b06592aedb
📒 Files selected for processing (3)
CLI/cmux.swiftSources/TerminalController.swiftSources/Workspace.swift
| case "last-tab": | ||
| let workspaceArg = workspaceFromArgsOrEnv(commandArgs, windowOverride: windowOverride) | ||
| var params: [String: Any] = [:] | ||
| let wsId = try normalizeWorkspaceHandle(workspaceArg, client: client) | ||
| if let wsId { params["workspace_id"] = wsId } | ||
| let payload = try client.sendV2(method: "surface.last", params: params) | ||
| var summaryParts = ["OK"] | ||
| if let tabHandle = formatTabHandle(payload, idFormat: idFormat) { | ||
| summaryParts.append("tab=\(tabHandle)") | ||
| } | ||
| if let paneHandle = formatHandle(payload, kind: "pane", idFormat: idFormat) { | ||
| summaryParts.append("pane=\(paneHandle)") | ||
| } | ||
| if let workspaceHandle = formatHandle(payload, kind: "workspace", idFormat: idFormat) { | ||
| summaryParts.append("workspace=\(workspaceHandle)") | ||
| } | ||
| printV2Payload(payload, jsonOutput: jsonOutput, idFormat: idFormat, fallbackText: summaryParts.joined(separator: " ")) | ||
|
|
There was a problem hiding this comment.
Reject unexpected args for last-tab.
Right now stray positional tokens and misspelled flags are ignored, and the command still switches the current workspace's tab. That makes typos silently do the wrong thing instead of failing fast.
🛠️ Suggested fix
case "last-tab":
- let workspaceArg = workspaceFromArgsOrEnv(commandArgs, windowOverride: windowOverride)
+ let (workspaceOpt, remaining) = parseOption(commandArgs, name: "--workspace")
+ if let unknown = remaining.first(where: { $0.hasPrefix("--") }) {
+ throw CLIError(message: "last-tab: unknown flag '\(unknown)'. Known flags: --workspace <id|ref|index>")
+ }
+ if let extra = remaining.first {
+ throw CLIError(message: "last-tab: unexpected argument '\(extra)'")
+ }
+ let workspaceArg = workspaceOpt ?? (windowOverride == nil ? ProcessInfo.processInfo.environment["CMUX_WORKSPACE_ID"] : nil)
var params: [String: Any] = [:]
let wsId = try normalizeWorkspaceHandle(workspaceArg, client: client)
if let wsId { params["workspace_id"] = wsId }
let payload = try client.sendV2(method: "surface.last", params: params)
var summaryParts = ["OK"]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@CLI/cmux.swift` around lines 5985 - 6002, The "last-tab" case must reject
unexpected positional tokens or unknown flags instead of silently proceeding;
before calling workspaceFromArgsOrEnv/normalizeWorkspaceHandle/sendV2, validate
commandArgs contains only the allowed options (e.g., the workspace flag) and no
stray positional tokens—if any unexpected args are present, print a clear error
and exit non-zero (or return a failure) so the command fails fast; implement
this check at the top of the "last-tab" case using the existing commandArgs
variable and short-circuit before invoking workspaceFromArgsOrEnv,
normalizeWorkspaceHandle, client.sendV2, or printV2Payload.
| let before = ws.focusedPanelId | ||
| ws.selectLastVisitedSurface(inPane: targetPaneId) | ||
| guard let after = ws.focusedPanelId, after != before else { return } |
There was a problem hiding this comment.
Compare the target pane’s selected surface, not ws.focusedPanelId.
When pane_id points at a different pane with no alternate surface, Workspace.selectLastVisitedSurface(inPane:) still focuses that pane before it returns. This code then sees ws.focusedPanelId change and reports success even though no last-visited switch happened. The result is a false OK plus an unintended pane-focus mutation.
🛠️ Suggested fix
- let before = ws.focusedPanelId
+ let before = (targetPaneId ?? ws.bonsplitController.focusedPaneId)
+ .flatMap { ws.bonsplitController.selectedTab(inPane: $0) }
+ .flatMap { ws.panelIdFromSurfaceId($0.id) }
ws.selectLastVisitedSurface(inPane: targetPaneId)
- guard let after = ws.focusedPanelId, after != before else { return }
+ guard let resolvedPaneId = targetPaneId ?? ws.bonsplitController.focusedPaneId,
+ let after = ws.bonsplitController.selectedTab(inPane: resolvedPaneId)
+ .flatMap({ ws.panelIdFromSurfaceId($0.id) }),
+ after != before else { return }Cross-file note: Sources/Workspace.swift focuses the target pane before it checks whether that pane actually has an MRU alternate surface.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Sources/TerminalController.swift` around lines 3771 - 3773, The code
incorrectly checks ws.focusedPanelId to determine if
selectLastVisitedSurface(inPane:) switched surfaces; instead, read the selected
surface for the target pane before and after calling
ws.selectLastVisitedSurface(inPane: targetPaneId) and compare those values (e.g.
obtain ws.selectedSurface(forPane: targetPaneId) or equivalent) so you only
report success when the pane's selected surface actually changed; do not rely on
ws.focusedPanelId because selectLastVisitedSurface(inPane:) may change focus
even when no alternate surface exists.
Summary
Ctrl+Tab/Ctrl+Shift+Tabto last-visited surface switchingsurface.lastand expose it in capabilitieslast-tabthat callssurface.lasttests_v2helpers and tmux compatibility matrix coverage forsurface.lastandlast-tabValidation
./scripts/setup.sh./scripts/reload.sh --tag fix-issue-474xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination 'platform=macOS' buildFixes #474
Summary by cubic
Add a quick switch to the previously visited tab per pane. Ctrl+Tab/Ctrl+Shift+Tab now toggle MRU in-pane, with a new
surface.lastAPI andcmux last-tabcommand.surface.lastsocket method (in capabilities) with optionalpane_idto target a specific pane.cmuxcommandlast-tab [--workspace <id|ref|index>]that callssurface.lastand returns formatted tab/pane/workspace handles.Written for commit e3f7fc3. Summary will update on new commits.
Summary by CodeRabbit
New Features
Tests