Tune Bonsplit tab bar action lane

This commit is contained in:
Lawrence Chen
2026-05-01 05:34:05 -07:00
parent 04aa56cd16
commit 7a317875d5
6 changed files with 593 additions and 4 deletions
+7
View File
@@ -5350,6 +5350,13 @@ class TabManager: ObservableObject {
}
}
/// Refresh Bonsplit right-side action button backdrop tuning for all workspaces.
func refreshSplitButtonBackdropEffect() {
for workspace in tabs {
workspace.refreshSplitButtonBackdropEffect()
}
}
func applySurfaceTabBarButtons(
_ buttons: [CmuxSurfaceTabBarButton],
sourcePath: String?,
+119 -2
View File
@@ -74,6 +74,115 @@ private final class WorkspacePendingTerminalInputObserver: @unchecked Sendable {
var observer: NSObjectProtocol?
}
struct BonsplitTabBarDebugNumberSetting {
let key: String
let defaultValue: Double
let range: ClosedRange<Double>
let step: Double
func resolved(_ value: Double) -> Double {
guard value.isFinite else { return defaultValue }
return min(max(value, range.lowerBound), range.upperBound)
}
func currentValue(defaults: UserDefaults = .standard) -> Double {
guard defaults.object(forKey: key) != nil else {
return defaultValue
}
return resolved(defaults.double(forKey: key))
}
func format(_ value: Double) -> String {
String(format: "%.3f", resolved(value))
}
}
enum BonsplitTabBarDebugSettings {
static let backdropFadeWidth = 99.75
static let separatorFadeWidthSetting = BonsplitTabBarDebugNumberSetting(
key: "debugBonsplitTabBarSeparatorFadeWidthV2",
defaultValue: backdropFadeWidth,
range: 0.0...140.0,
step: 0.25
)
static let contentFadeWidthSetting = BonsplitTabBarDebugNumberSetting(
key: "debugBonsplitTabBarContentFadeWidth",
defaultValue: 28.875,
range: 0.0...80.0,
step: 0.5
)
static let solidSurfaceWidthAdjustmentSetting = BonsplitTabBarDebugNumberSetting(
key: "debugBonsplitTabBarSolidSurfaceWidthAdjustmentV2",
defaultValue: -80.0,
range: -80.0...120.0,
step: 0.5
)
static let separatorFadeWidthKey = separatorFadeWidthSetting.key
static let contentFadeWidthKey = contentFadeWidthSetting.key
static let solidSurfaceWidthAdjustmentKey = solidSurfaceWidthAdjustmentSetting.key
static let defaultSeparatorFadeWidth = separatorFadeWidthSetting.defaultValue
static let defaultContentFadeWidth = contentFadeWidthSetting.defaultValue
static let defaultSolidSurfaceWidthAdjustment = solidSurfaceWidthAdjustmentSetting.defaultValue
static let separatorFadeWidthRange = separatorFadeWidthSetting.range
static let contentFadeWidthRange = contentFadeWidthSetting.range
static let solidSurfaceWidthAdjustmentRange = solidSurfaceWidthAdjustmentSetting.range
static let separatorFadeWidthStep = separatorFadeWidthSetting.step
static let contentFadeWidthStep = contentFadeWidthSetting.step
static let solidSurfaceWidthAdjustmentStep = solidSurfaceWidthAdjustmentSetting.step
static func resolvedSeparatorFadeWidth(_ width: Double) -> Double {
separatorFadeWidthSetting.resolved(width)
}
static func resolvedContentFadeWidth(_ width: Double) -> Double {
contentFadeWidthSetting.resolved(width)
}
static func resolvedSolidSurfaceWidthAdjustment(_ width: Double) -> Double {
solidSurfaceWidthAdjustmentSetting.resolved(width)
}
static func separatorFadeWidth(defaults: UserDefaults = .standard) -> Double {
separatorFadeWidthSetting.currentValue(defaults: defaults)
}
static func contentFadeWidth(defaults: UserDefaults = .standard) -> Double {
contentFadeWidthSetting.currentValue(defaults: defaults)
}
static func solidSurfaceWidthAdjustment(defaults: UserDefaults = .standard) -> Double {
solidSurfaceWidthAdjustmentSetting.currentValue(defaults: defaults)
}
static func formatPixels(_ value: Double) -> String {
String(format: "%.3f", value)
}
static func currentTuningDescription(defaults: UserDefaults = .standard) -> String {
let effect = Workspace.bonsplitSplitButtonBackdropEffect()
return [
"bonsplit-tabbar-tuning",
"separatorFadeWidth=\(formatPixels(separatorFadeWidth(defaults: defaults)))",
"contentFadeWidth=\(formatPixels(contentFadeWidth(defaults: defaults)))",
"solidSurfaceWidthAdjustment=\(formatPixels(solidSurfaceWidthAdjustment(defaults: defaults)))",
"fadeWidth=\(String(format: "%.3f", Double(effect.fadeWidth)))",
"solidWidth=\(String(format: "%.3f", Double(effect.solidWidth)))",
"fadeRampStartFraction=\(String(format: "%.3f", Double(effect.fadeRampStartFraction)))",
"trailingOpacity=\(String(format: "%.3f", Double(effect.trailingOpacity)))",
"contentOcclusionFraction=\(String(format: "%.3f", Double(effect.contentOcclusionFraction)))",
"masksTabContent=\(effect.masksTabContent ? "true" : "false")"
].joined(separator: " ")
}
static func copyCurrentTuningToPasteboard(defaults: UserDefaults = .standard) {
let pasteboard = NSPasteboard.general
pasteboard.clearContents()
pasteboard.setString(currentTuningDescription(defaults: defaults), forType: .string)
}
}
func cmuxSurfaceContextName(_ context: ghostty_surface_context_e) -> String {
switch context {
case GHOSTTY_SURFACE_CONTEXT_WINDOW:
@@ -7588,9 +7697,11 @@ final class Workspace: Identifiable, ObservableObject {
nonisolated static func bonsplitSplitButtonBackdropEffect() -> BonsplitConfiguration.Appearance.SplitButtonBackdropEffect {
.init(
style: .translucentChrome,
fadeWidth: 99.75,
contentFadeWidth: 28.875,
fadeWidth: CGFloat(BonsplitTabBarDebugSettings.backdropFadeWidth),
contentFadeWidth: CGFloat(BonsplitTabBarDebugSettings.contentFadeWidth()),
solidWidth: 23.875,
solidSurfaceWidthAdjustment: CGFloat(BonsplitTabBarDebugSettings.solidSurfaceWidthAdjustment()),
separatorFadeWidth: CGFloat(BonsplitTabBarDebugSettings.separatorFadeWidth()),
fadeRampStartFraction: bonsplitSplitButtonBackdropSoftness,
leadingOpacity: 0,
trailingOpacity: 0.8625,
@@ -7952,6 +8063,12 @@ final class Workspace: Identifiable, ObservableObject {
bonsplitController.configuration = configuration
}
func refreshSplitButtonBackdropEffect() {
var configuration = bonsplitController.configuration
configuration.appearance.splitButtonBackdropEffect = Self.bonsplitSplitButtonBackdropEffect()
bonsplitController.configuration = configuration
}
func applySurfaceTabBarButtons(
_ buttons: [CmuxSurfaceTabBarButton],
sourcePath: String?,
+208 -1
View File
@@ -383,6 +383,9 @@ struct cmuxApp: App {
Button("Split Button Layout Debug…") {
SplitButtonLayoutDebugWindowController.shared.show()
}
Button("Bonsplit Tab Bar Debug…") {
BonsplitTabBarDebugWindowController.shared.show()
}
Button(
String(
localized: "debug.menu.tabBarBackdropLab",
@@ -1104,6 +1107,7 @@ struct cmuxApp: App {
FeedPreviewWindowController.shared.show()
FeedTextEditorDebugWindowController.shared.show()
FeedButtonStyleDebugWindowController.shared.show()
BonsplitTabBarDebugWindowController.shared.show()
}
#endif
}
@@ -1136,6 +1140,7 @@ private let cmuxAuxiliaryWindowIdentifiers: Set<String> = [
"cmux.menubarDebug",
"cmux.backgroundDebug",
"cmux.startupAppearanceDebug",
"cmux.bonsplitTabBarDebug",
]
/// Returns whether the given window should handle the standard close shortcut
@@ -1750,6 +1755,9 @@ private struct DebugWindowControlsView: View {
) {
PDFPreviewChromeDebugWindowController.shared.show()
}
Button("Bonsplit Tab Bar Debug…") {
BonsplitTabBarDebugWindowController.shared.show()
}
Button(
String(
localized: "debug.menu.tabBarBackdropLab",
@@ -1775,6 +1783,7 @@ private struct DebugWindowControlsView: View {
StartupAppearanceDebugWindowController.shared.show()
MenuBarExtraDebugWindowController.shared.show()
PDFPreviewChromeDebugWindowController.shared.show()
BonsplitTabBarDebugWindowController.shared.show()
TabBarBackdropLabWindowController.shared.show()
FeedTextEditorDebugWindowController.shared.show()
}
@@ -3331,6 +3340,203 @@ private struct SplitButtonLayoutDebugView: View {
}
}
// MARK: - Bonsplit Tab Bar Debug Window
private final class BonsplitTabBarDebugWindowController: NSWindowController, NSWindowDelegate {
static let shared = BonsplitTabBarDebugWindowController()
private init() {
let window = NSPanel(
contentRect: NSRect(x: 0, y: 0, width: 520, height: 420),
styleMask: [.titled, .closable, .utilityWindow],
backing: .buffered,
defer: false
)
window.title = "Bonsplit Tab Bar Debug"
window.titleVisibility = .visible
window.titlebarAppearsTransparent = false
window.isMovableByWindowBackground = true
window.isReleasedWhenClosed = false
window.identifier = NSUserInterfaceItemIdentifier("cmux.bonsplitTabBarDebug")
window.center()
window.contentView = NSHostingView(rootView: BonsplitTabBarDebugView())
AppDelegate.shared?.applyWindowDecorations(to: window)
super.init(window: window)
window.delegate = self
}
@available(*, unavailable)
required init?(coder: NSCoder) { fatalError() }
func show() {
window?.center()
window?.makeKeyAndOrderFront(nil)
}
}
private struct BonsplitTabBarDebugView: View {
@AppStorage(BonsplitTabBarDebugSettings.separatorFadeWidthKey)
private var separatorFadeWidth = BonsplitTabBarDebugSettings.defaultSeparatorFadeWidth
@AppStorage(BonsplitTabBarDebugSettings.contentFadeWidthKey)
private var contentFadeWidth = BonsplitTabBarDebugSettings.defaultContentFadeWidth
@AppStorage(BonsplitTabBarDebugSettings.solidSurfaceWidthAdjustmentKey)
private var solidSurfaceWidthAdjustment = BonsplitTabBarDebugSettings.defaultSolidSurfaceWidthAdjustment
private var resolvedSeparatorFadeWidth: Double {
BonsplitTabBarDebugSettings.resolvedSeparatorFadeWidth(separatorFadeWidth)
}
private var resolvedContentFadeWidth: Double {
BonsplitTabBarDebugSettings.resolvedContentFadeWidth(contentFadeWidth)
}
private var resolvedSolidSurfaceWidthAdjustment: Double {
BonsplitTabBarDebugSettings.resolvedSolidSurfaceWidthAdjustment(solidSurfaceWidthAdjustment)
}
private var separatorFadeBinding: Binding<Double> {
Binding(
get: { resolvedSeparatorFadeWidth },
set: { setSeparatorFadeWidth($0) }
)
}
private var contentFadeBinding: Binding<Double> {
Binding(
get: { resolvedContentFadeWidth },
set: { setContentFadeWidth($0) }
)
}
private var solidSurfaceWidthAdjustmentBinding: Binding<Double> {
Binding(
get: { resolvedSolidSurfaceWidthAdjustment },
set: { setSolidSurfaceWidthAdjustment($0) }
)
}
var body: some View {
VStack(alignment: .leading, spacing: 14) {
Text("Bonsplit Tab Bar")
.font(.headline)
GroupBox("Action Lane Geometry") {
VStack(alignment: .leading, spacing: 10) {
BonsplitTabBarDebugSliderRow(
title: "Content fade",
value: contentFadeBinding,
setting: BonsplitTabBarDebugSettings.contentFadeWidthSetting
)
BonsplitTabBarDebugSliderRow(
title: "Solid bg extra",
value: solidSurfaceWidthAdjustmentBinding,
setting: BonsplitTabBarDebugSettings.solidSurfaceWidthAdjustmentSetting
)
}
.padding(.top, 2)
}
GroupBox("Action Lane Border") {
VStack(alignment: .leading, spacing: 10) {
BonsplitTabBarDebugSliderRow(
title: "Separator fade frame",
value: separatorFadeBinding,
setting: BonsplitTabBarDebugSettings.separatorFadeWidthSetting
)
}
.padding(.top, 2)
}
HStack(spacing: 10) {
Button("Reset") {
setSeparatorFadeWidth(BonsplitTabBarDebugSettings.defaultSeparatorFadeWidth)
setContentFadeWidth(BonsplitTabBarDebugSettings.defaultContentFadeWidth)
setSolidSurfaceWidthAdjustment(BonsplitTabBarDebugSettings.defaultSolidSurfaceWidthAdjustment)
}
Button("Copy Config") {
BonsplitTabBarDebugSettings.copyCurrentTuningToPasteboard()
}
}
Text(verbatim: BonsplitTabBarDebugSettings.currentTuningDescription())
.font(.system(.caption, design: .monospaced))
.textSelection(.enabled)
.lineLimit(3)
.frame(maxWidth: .infinity, alignment: .leading)
}
.padding(16)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
}
private func setSeparatorFadeWidth(_ value: Double) {
separatorFadeWidth = BonsplitTabBarDebugSettings.resolvedSeparatorFadeWidth(value)
refreshLiveWorkspaces()
}
private func setContentFadeWidth(_ value: Double) {
contentFadeWidth = BonsplitTabBarDebugSettings.resolvedContentFadeWidth(value)
refreshLiveWorkspaces()
}
private func setSolidSurfaceWidthAdjustment(_ value: Double) {
solidSurfaceWidthAdjustment = BonsplitTabBarDebugSettings.resolvedSolidSurfaceWidthAdjustment(value)
refreshLiveWorkspaces()
}
private func refreshLiveWorkspaces() {
let managers = [
AppDelegate.shared?.activeTabManagerForCommands(),
AppDelegate.shared?.tabManager
].compactMap { $0 }
var seen = Set<ObjectIdentifier>()
for manager in managers {
guard seen.insert(ObjectIdentifier(manager)).inserted else { continue }
manager.refreshSplitButtonBackdropEffect()
}
}
}
private struct BonsplitTabBarDebugSliderRow: View {
let title: String
@Binding var value: Double
let setting: BonsplitTabBarDebugNumberSetting
private var resolvedValue: Double {
setting.resolved(value)
}
var body: some View {
VStack(alignment: .leading, spacing: 6) {
HStack(spacing: 8) {
Text(title)
.frame(width: 112, alignment: .leading)
Slider(
value: Binding(
get: { resolvedValue },
set: { value = setting.resolved($0) }
),
in: setting.range,
step: setting.step
)
Text("\(setting.format(resolvedValue)) px")
.font(.caption)
.monospacedDigit()
.frame(width: 76, alignment: .trailing)
}
Stepper(
"Fine tune",
value: Binding(
get: { resolvedValue },
set: { value = setting.resolved($0) }
),
in: setting.range,
step: setting.step
)
}
}
}
// MARK: - Tab Bar Backdrop Lab Window
private final class TabBarBackdropLabWindowController: NSWindowController, NSWindowDelegate {
@@ -3390,7 +3596,7 @@ private struct TabBarBackdropLabVariant: Identifiable {
let opacity: CGFloat
var renderIdentity: String {
"\(id)-\(chromeHex)-\(tabBarHex)-\(splitButtonBackdropHex)-\(paneHex)-\(borderHex)-\(String(format: "%.3f", opacity))-\(String(format: "%.1f", effect.fadeWidth))-\(String(format: "%.1f", effect.contentFadeWidth))-\(String(format: "%.1f", effect.solidWidth))-\(String(format: "%.2f", effect.fadeRampStartFraction))-\(String(format: "%.2f", effect.leadingOpacity))-\(String(format: "%.2f", effect.trailingOpacity))-\(String(format: "%.2f", effect.contentOcclusionFraction))-\(effect.masksTabContent ? 1 : 0)"
"\(id)-\(chromeHex)-\(tabBarHex)-\(splitButtonBackdropHex)-\(paneHex)-\(borderHex)-\(String(format: "%.3f", opacity))-\(String(format: "%.1f", effect.fadeWidth))-\(String(format: "%.1f", effect.contentFadeWidth))-\(String(format: "%.1f", effect.solidWidth))-\(String(format: "%.1f", effect.solidSurfaceWidthAdjustment))-\(String(format: "%.2f", effect.fadeRampStartFraction))-\(String(format: "%.2f", effect.leadingOpacity))-\(String(format: "%.2f", effect.trailingOpacity))-\(String(format: "%.2f", effect.contentOcclusionFraction))-\(effect.masksTabContent ? 1 : 0)"
}
}
@@ -3435,6 +3641,7 @@ private struct TabBarBackdropLabView: View {
fadeWidth: interpolate(strong: 20, production: production.fadeWidth, soft: 240),
contentFadeWidth: interpolate(strong: 0, production: production.contentFadeWidth, soft: 80),
solidWidth: interpolate(strong: 72, production: production.solidWidth, soft: 0),
solidSurfaceWidthAdjustment: production.solidSurfaceWidthAdjustment,
fadeRampStartFraction: interpolate(strong: 0, production: production.fadeRampStartFraction, soft: 0.98),
leadingOpacity: production.leadingOpacity,
trailingOpacity: interpolate(strong: 1.0, production: production.trailingOpacity, soft: 0.25),
+4
View File
@@ -8,6 +8,7 @@ Use a terminal pane in cmux and `cd` into these directories:
- `dogfood/directory-actions/alpha/nested`
- `dogfood/directory-actions/legacy`
- `dogfood/directory-actions/legacy/prefer-dot-cmux`
- `dogfood/directory-actions/many-tab-actions`
What each one demonstrates:
@@ -25,6 +26,9 @@ What each one demonstrates:
- `legacy/prefer-dot-cmux`
- Contains both `./cmux.json` and `./.cmux/cmux.json`
- The `./.cmux/cmux.json` file should win
- `many-tab-actions`
- Defines 24 custom actions plus the 4 built-ins
- Replaces the surface tab bar button list to stress wide action rows
General expectations:
@@ -0,0 +1,254 @@
{
"actions": {
"many.action01": {
"type": "command",
"title": "Action 01",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 01\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "terminal" }
},
"many.action02": {
"type": "command",
"title": "Action 02",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 02\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "globe" }
},
"many.action03": {
"type": "command",
"title": "Action 03",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 03\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "square.split.2x1" }
},
"many.action04": {
"type": "command",
"title": "Action 04",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 04\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "square.split.1x2" }
},
"many.action05": {
"type": "command",
"title": "Action 05",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 05\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "doc" }
},
"many.action06": {
"type": "command",
"title": "Action 06",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 06\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "folder" }
},
"many.action07": {
"type": "command",
"title": "Action 07",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 07\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "magnifyingglass" }
},
"many.action08": {
"type": "command",
"title": "Action 08",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 08\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "gearshape" }
},
"many.action09": {
"type": "command",
"title": "Action 09",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 09\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "hammer" }
},
"many.action10": {
"type": "command",
"title": "Action 10",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 10\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "wrench.and.screwdriver" }
},
"many.action11": {
"type": "command",
"title": "Action 11",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 11\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "bolt" }
},
"many.action12": {
"type": "command",
"title": "Action 12",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 12\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "flag" }
},
"many.action13": {
"type": "command",
"title": "Action 13",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 13\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "star" }
},
"many.action14": {
"type": "command",
"title": "Action 14",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 14\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "bookmark" }
},
"many.action15": {
"type": "command",
"title": "Action 15",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 15\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "bell" }
},
"many.action16": {
"type": "command",
"title": "Action 16",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 16\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "tray" }
},
"many.action17": {
"type": "command",
"title": "Action 17",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 17\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "arrow.clockwise" }
},
"many.action18": {
"type": "command",
"title": "Action 18",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 18\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "play" }
},
"many.action19": {
"type": "command",
"title": "Action 19",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 19\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "stop" }
},
"many.action20": {
"type": "command",
"title": "Action 20",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 20\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "pause" }
},
"many.action21": {
"type": "command",
"title": "Action 21",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 21\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "record.circle" }
},
"many.action22": {
"type": "command",
"title": "Action 22",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 22\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "shippingbox" }
},
"many.action23": {
"type": "command",
"title": "Action 23",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 23\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "network" }
},
"many.action24": {
"type": "command",
"title": "Action 24",
"subtitle": "Many tab actions dogfood",
"command": "printf 'MANY TAB ACTION 24\\n'; pwd",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "symbol", "name": "externaldrive" }
}
},
"ui": {
"surfaceTabBar": {
"buttons": [
"cmux.newTerminal",
"cmux.newBrowser",
"cmux.splitRight",
"cmux.splitDown",
"many.action01",
"many.action02",
"many.action03",
"many.action04",
"many.action05",
"many.action06",
"many.action07",
"many.action08",
"many.action09",
"many.action10",
"many.action11",
"many.action12",
"many.action13",
"many.action14",
"many.action15",
"many.action16",
"many.action17",
"many.action18",
"many.action19",
"many.action20",
"many.action21",
"many.action22",
"many.action23",
"many.action24"
]
}
}
}