Skip to content
Merged
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
33 changes: 23 additions & 10 deletions CLI/cmux.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3300,14 +3300,18 @@ struct CMUXCLI {
let workspaceArg = workspaceOpt ?? (windowOverride == nil ? ProcessInfo.processInfo.environment["CMUX_WORKSPACE_ID"] : nil)
let workspaceId = try normalizeWorkspaceHandle(workspaceArg, client: client, allowCurrent: true)

let inferredTitle = positional.joined(separator: " ").trimmingCharacters(in: .whitespacesAndNewlines)
let title = (titleOpt ?? (inferredTitle.isEmpty ? nil : inferredTitle))?.trimmingCharacters(in: .whitespacesAndNewlines)
let inferredPositional = positional.joined(separator: " ").trimmingCharacters(in: .whitespacesAndNewlines)
let title = (titleOpt ?? (action == "rename" && !inferredPositional.isEmpty ? inferredPositional : nil))?.trimmingCharacters(in: .whitespacesAndNewlines)

if action == "rename", (title?.isEmpty ?? true) {
throw CLIError(message: "workspace-action rename requires --title <text> (or a trailing title)")
}
if action == "set_color", (colorOpt?.isEmpty ?? true) {
throw CLIError(message: "workspace-action set-color requires --color <#hex|name>")

let color = (
colorOpt ?? (action == "set_color" ? (inferredPositional.isEmpty ? nil : inferredPositional) : nil)
)?.trimmingCharacters(in: .whitespacesAndNewlines)
if action == "set_color", (color?.isEmpty ?? true) {
throw CLIError(message: "workspace-action set-color requires --color <name|#hex> (or a trailing color)")
}

var params: [String: Any] = ["action": action]
Expand All @@ -3317,7 +3321,7 @@ struct CMUXCLI {
if let title, !title.isEmpty {
params["title"] = title
}
if let color = colorOpt, !color.isEmpty {
if let color, !color.isEmpty {
params["color"] = color
}

Expand All @@ -3335,6 +3339,9 @@ struct CMUXCLI {
if let index = payload["index"] {
summaryParts.append("index=\(index)")
}
if let color = payload["color"] as? String {
summaryParts.append("color=\(color)")
}
Comment thread
ariel-wiz marked this conversation as resolved.
printV2Payload(payload, jsonOutput: jsonOutput, idFormat: idFormat, fallbackText: summaryParts.joined(separator: " "))
}

Expand Down Expand Up @@ -6190,15 +6197,21 @@ struct CMUXCLI {
Flags:
--action <name> Action name (required if not positional)
--workspace <id|ref|index> Target workspace (default: current/$CMUX_WORKSPACE_ID)
--title <text> Title for rename (or pass trailing title text)
--color <#hex|name> Color for set-color (e.g. '#C0392B' or 'Red')
--title <text> Title for rename
--color <name|#hex> Color for set-color (name or #RRGGBB hex)

Named colors:
Red, Crimson, Orange, Amber, Olive, Green, Teal, Aqua,
Blue, Navy, Indigo, Purple, Magenta, Rose, Brown, Charcoal

Example:
cmux workspace-action --workspace workspace:2 --action pin
cmux workspace-action --action rename --title "infra"
cmux workspace-action close-others
cmux workspace-action --action set-color --workspace workspace:1 --color '#C0392B'
cmux workspace-action --action clear-color --workspace workspace:1
cmux workspace-action --action set-color --color blue
cmux workspace-action --action set-color --color "#C0392B"
cmux workspace-action set-color Amber
cmux workspace-action clear-color
"""
case "tab-action":
return """
Expand Down Expand Up @@ -11141,7 +11154,7 @@ struct CMUXCLI {
close-window --window <id>
move-workspace-to-window --workspace <id|ref> --window <id|ref>
reorder-workspace --workspace <id|ref|index> (--index <n> | --before <id|ref|index> | --after <id|ref|index>) [--window <id|ref|index>]
workspace-action --action <name> [--workspace <id|ref|index>] [--title <text>] [--color <#hex|name>]
workspace-action --action <name> [--workspace <id|ref|index>] [--title <text>] [--color <name|#hex>]
list-workspaces
new-workspace [--cwd <path>] [--command <text>]
ssh <destination> [--name <title>] [--port <n>] [--identity <path>] [--ssh-option <opt>] [-- <remote-command-args>]
Expand Down
35 changes: 18 additions & 17 deletions Sources/TerminalController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4061,29 +4061,30 @@ class TerminalController {
finish()

case "set_color":
guard let colorRaw = v2String(params, "color"), !colorRaw.isEmpty else {
result = .err(code: "invalid_params", message: "set-color requires --color", data: nil)
guard let colorRaw = v2String(params, "color"),
!colorRaw.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
result = .err(code: "invalid_params", message: "Missing or invalid color", data: nil)
return
}
// Resolve named color to hex via palette lookup
let resolved: String
if colorRaw.hasPrefix("#") {
guard let normalized = WorkspaceTabColorSettings.normalizedHex(colorRaw) else {
result = .err(code: "invalid_params", message: "Invalid hex color '\(colorRaw)'. Expected #RRGGBB", data: nil)
return
}
resolved = normalized
} else if let entry = WorkspaceTabColorSettings.defaultPalette.first(where: {
$0.name.lowercased() == colorRaw.lowercased()
let colorInput = colorRaw.trimmingCharacters(in: .whitespacesAndNewlines)
// Resolve named colors from effective palette (includes user overrides, excludes custom entries)
let effectivePalette = WorkspaceTabColorSettings.defaultPaletteWithOverrides()
let hex: String
if let entry = effectivePalette.first(where: {
$0.name.caseInsensitiveCompare(colorInput) == .orderedSame
}) {
resolved = entry.hex
hex = entry.hex
} else if let normalized = WorkspaceTabColorSettings.normalizedHex(colorInput) {
hex = normalized
} else {
let names = WorkspaceTabColorSettings.defaultPalette.map(\.name).joined(separator: ", ")
result = .err(code: "invalid_params", message: "Unknown color '\(colorRaw)'. Use #RRGGBB or: \(names)", data: nil)
let colorNames = effectivePalette.map(\.name)
result = .err(code: "invalid_params", message: "Invalid color. Use a hex value (#RRGGBB) or a named color.", data: [
"named_colors": colorNames
])
return
}
tabManager.setTabColor(tabId: workspace.id, color: resolved)
finish(["color": resolved])
tabManager.setTabColor(tabId: workspace.id, color: hex)
finish(["color": hex])
Comment thread
ariel-wiz marked this conversation as resolved.

case "clear_color":
tabManager.setTabColor(tabId: workspace.id, color: nil)
Expand Down