Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 57 additions & 9 deletions CLI/cmux.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1141,8 +1141,17 @@ struct CMUXCLI {
let (wsArg, rem0) = parseOption(commandArgs, name: "--workspace")
let (titleOpt, rem1) = parseOption(rem0, name: "--title")
let workspaceArg = wsArg ?? (windowId == nil ? ProcessInfo.processInfo.environment["CMUX_WORKSPACE_ID"] : nil)
let trailingTitle = rem1.dropFirst(rem1.first == "--" ? 1 : 0).joined(separator: " ").trimmingCharacters(in: .whitespacesAndNewlines)
let title = titleOpt ?? (trailingTitle.isEmpty ? nil : trailingTitle)
let trailing = try positionalArguments(rem1, command: "new-page")
let title: String?
if let titleOpt {
guard trailing.isEmpty else {
throw CLIError(message: "new-page: unexpected arguments: \(trailing.joined(separator: " "))")
}
title = titleOpt
} else {
let trailingTitle = trailing.joined(separator: " ").trimmingCharacters(in: .whitespacesAndNewlines)
title = trailingTitle.isEmpty ? nil : trailingTitle
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
var params: [String: Any] = [:]
let wsId = try normalizeWorkspaceHandle(workspaceArg, client: client, allowCurrent: true)
if let wsId { params["workspace_id"] = wsId }
Expand All @@ -1155,8 +1164,17 @@ struct CMUXCLI {
let (pageOpt, rem1) = parseOption(rem0, name: "--page")
let (titleOpt, rem2) = parseOption(rem1, name: "--title")
let workspaceArg = wsArg ?? (windowId == nil ? ProcessInfo.processInfo.environment["CMUX_WORKSPACE_ID"] : nil)
let trailingTitle = rem2.dropFirst(rem2.first == "--" ? 1 : 0).joined(separator: " ").trimmingCharacters(in: .whitespacesAndNewlines)
let title = titleOpt ?? (trailingTitle.isEmpty ? nil : trailingTitle)
let trailing = try positionalArguments(rem2, command: "duplicate-page")
let title: String?
if let titleOpt {
guard trailing.isEmpty else {
throw CLIError(message: "duplicate-page: unexpected arguments: \(trailing.joined(separator: " "))")
}
title = titleOpt
} else {
let trailingTitle = trailing.joined(separator: " ").trimmingCharacters(in: .whitespacesAndNewlines)
title = trailingTitle.isEmpty ? nil : trailingTitle
}
var params: [String: Any] = [:]
let wsId = try normalizeWorkspaceHandle(workspaceArg, client: client, allowCurrent: true)
if let wsId { params["workspace_id"] = wsId }
Expand Down Expand Up @@ -1397,8 +1415,22 @@ struct CMUXCLI {
printV2Payload(payload, jsonOutput: jsonOutput, idFormat: idFormat, fallbackText: v2OKSummary(payload, idFormat: idFormat, kinds: ["workspace"]))

case "close-page":
let workspaceArg = workspaceFromArgsOrEnv(commandArgs, windowOverride: windowId)
let pageRaw = optionValue(commandArgs, name: "--page") ?? commandArgs.first
let (wsArg, rem0) = parseOption(commandArgs, name: "--workspace")
let (pageOpt, rem1) = parseOption(rem0, name: "--page")
let workspaceArg = wsArg ?? (windowId == nil ? ProcessInfo.processInfo.environment["CMUX_WORKSPACE_ID"] : nil)
let trailing = try positionalArguments(rem1, command: "close-page")
let pageRaw: String?
if let pageOpt {
guard trailing.isEmpty else {
throw CLIError(message: "close-page: unexpected arguments: \(trailing.joined(separator: " "))")
}
pageRaw = pageOpt
} else {
guard trailing.count <= 1 else {
throw CLIError(message: "close-page: unexpected arguments: \(trailing.dropFirst().joined(separator: " "))")
}
pageRaw = trailing.first
}
var params: [String: Any] = [:]
let wsId = try normalizeWorkspaceHandle(workspaceArg, client: client, allowCurrent: true)
if let wsId { params["workspace_id"] = wsId }
Expand Down Expand Up @@ -1460,14 +1492,15 @@ struct CMUXCLI {
let wsId = try normalizeWorkspaceHandle(workspaceArg, client: client, allowCurrent: true)
if let wsId { params["workspace_id"] = wsId }
let payload = try client.sendV2(method: "page.current", params: params)
printV2Payload(payload, jsonOutput: jsonOutput, idFormat: idFormat, fallbackText: v2OKSummary(payload, idFormat: idFormat, kinds: ["page", "workspace"]))
let fallbackText = formatHandle(payload, kind: "page", idFormat: idFormat)
?? v2OKSummary(payload, idFormat: idFormat, kinds: ["page", "workspace"])
printV2Payload(payload, jsonOutput: jsonOutput, idFormat: idFormat, fallbackText: fallbackText)

case "rename-page":
let (wsArg, rem0) = parseOption(commandArgs, name: "--workspace")
let (pageOpt, rem1) = parseOption(rem0, name: "--page")
let workspaceArg = wsArg ?? (windowId == nil ? ProcessInfo.processInfo.environment["CMUX_WORKSPACE_ID"] : nil)
let titleArgs = rem1.dropFirst(rem1.first == "--" ? 1 : 0)
let title = titleArgs.joined(separator: " ").trimmingCharacters(in: .whitespacesAndNewlines)
let title = try trailingTextArgument(rem1, command: "rename-page") ?? ""
guard !title.isEmpty else {
throw CLIError(message: "rename-page requires a title")
}
Expand Down Expand Up @@ -5493,6 +5526,21 @@ struct CMUXCLI {
args.contains(name)
}

private func positionalArguments(_ args: [String], command: String) throws -> [String] {
let positional = args.first == "--" ? Array(args.dropFirst()) : args
if args.first != "--",
let unknown = positional.first(where: { $0.hasPrefix("--") }) {
throw CLIError(message: "\(command): unknown flag '\(unknown)'")
}
return positional
}

private func trailingTextArgument(_ args: [String], command: String) throws -> String? {
let positional = try positionalArguments(args, command: command)
let text = positional.joined(separator: " ").trimmingCharacters(in: .whitespacesAndNewlines)
return text.isEmpty ? nil : text
}

private func replaceToken(_ args: [String], from: String, to: String) -> [String] {
args.map { $0 == from ? to : $0 }
}
Expand Down
10 changes: 10 additions & 0 deletions Resources/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@
<string>public.data</string>
</array>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>com.cmux.titlebar-page-reorder</string>
<key>UTTypeDescription</key>
<string>cmux Titlebar Page Reorder</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
</dict>
</array>
<key>NSAppTransportSecurity</key>
<dict>
Expand Down
54 changes: 43 additions & 11 deletions Sources/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -625,10 +625,6 @@ enum WorkspaceShortcutMapper {
return nil
}

static func pageIndex(forOptionDigit digit: Int, pageCount: Int) -> Int? {
workspaceIndex(forCommandDigit: digit, workspaceCount: pageCount)
}

static func optionDigitForPage(at index: Int, pageCount: Int) -> Int? {
commandDigitForWorkspace(at: index, workspaceCount: pageCount)
}
Expand Down Expand Up @@ -6852,35 +6848,64 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
}

if matchShortcut(event: event, shortcut: KeyboardShortcutSettings.shortcut(for: .newPage)) {
_ = tabManager?.selectedWorkspace?.newPage(select: true)
let createdPage = tabManager?.selectedWorkspace?.newPage(select: true)
#if DEBUG
dlog(
"shortcut.action name=newPage handled=\(createdPage == nil ? 0 : 1) " +
"\(debugShortcutRouteSnapshot(event: event)) " +
"page=\(createdPage?.id.uuidString.prefix(5) ?? "nil")"
)
#endif
return true
}

if matchShortcut(event: event, shortcut: KeyboardShortcutSettings.shortcut(for: .renamePage)) {
guard let workspace = tabManager?.selectedWorkspace,
let pageId = workspace.activePage?.id else {
guard let pageId = tabManager?.selectedWorkspace?.activePage?.id else {
#if DEBUG
dlog("shortcut.action name=renamePage handled=0 \(debugShortcutRouteSnapshot(event: event))")
#endif
return false
}
workspace.promptRenamePage(pageId: pageId)
tabManager?.selectedWorkspace?.promptRenamePage(pageId: pageId)
#if DEBUG
dlog(
"shortcut.action name=renamePage handled=1 " +
"\(debugShortcutRouteSnapshot(event: event)) page=\(pageId.uuidString.prefix(5))"
)
#endif
return true
}

if matchShortcut(event: event, shortcut: KeyboardShortcutSettings.shortcut(for: .closePage)) {
guard let workspace = tabManager?.selectedWorkspace,
let pageId = workspace.activePage?.id else {
guard let pageId = tabManager?.selectedWorkspace?.activePage?.id else {
#if DEBUG
dlog("shortcut.action name=closePage handled=0 \(debugShortcutRouteSnapshot(event: event))")
#endif
return false
}
workspace.closePage(pageId)
tabManager?.selectedWorkspace?.closePage(pageId)
#if DEBUG
dlog(
"shortcut.action name=closePage handled=1 " +
"\(debugShortcutRouteSnapshot(event: event)) page=\(pageId.uuidString.prefix(5))"
)
#endif
return true
}

if matchShortcut(event: event, shortcut: KeyboardShortcutSettings.shortcut(for: .nextPage)) {
tabManager?.selectedWorkspace?.selectNextPage()
#if DEBUG
dlog("shortcut.action name=nextPage handled=1 \(debugShortcutRouteSnapshot(event: event))")
#endif
return true
}

if matchShortcut(event: event, shortcut: KeyboardShortcutSettings.shortcut(for: .previousPage)) {
tabManager?.selectedWorkspace?.selectPreviousPage()
#if DEBUG
dlog("shortcut.action name=previousPage handled=1 \(debugShortcutRouteSnapshot(event: event))")
#endif
return true
}

Expand All @@ -6902,6 +6927,13 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
} else {
tabManager?.selectedWorkspace?.selectLastPage()
}
#if DEBUG
let targetText = pageIndex.map(String.init) ?? "last"
dlog(
"shortcut.action name=\(action.rawValue) handled=1 " +
"\(debugShortcutRouteSnapshot(event: event)) target=\(targetText)"
)
#endif
return true
}
}
Expand Down
Loading