I am aware you can do it from Mission Control by clicking and there is no shortcut available in 'Keyboard'.
Are there any ways to do this by using a 3rd party program or Applescript?
I am aware you can do it from Mission Control by clicking and there is no shortcut available in 'Keyboard'.
Are there any ways to do this by using a 3rd party program or Applescript?
Update: This method works in OS X 10.11 and earlier at least to OS X 10.8 (didn't test it on anything older then OS X 10.8) but no longer works as coded in macOS 10.12 due to significant changes to Mission Control made by Apple.
To programmatically add a Desktop in Mission Control the code below can be used as an AppleScript or a BASH Script in conjunction with an Automator Service using Run AppleScript or Run Shell Script, respectively, and a key sequence assigned to the Service in System Preferences in Keyboard Shortcuts.
AppleScript Code:
do shell script "open -a 'Mission Control'"
delay 0.5
tell application "System Events" to click (every button whose value of attribute "AXDescription" is "add desktop") of group 1 of process "Dock"
delay 0.5
tell application "System Events" to key code 53
BASH Script Code:
#!/bin/bash
open -a 'Mission Control'
osascript -e 'delay 0.5' \
-e 'tell application "System Events" to click (every button whose value of attribute "AXDescription" is "add desktop") of group 1 of process "Dock"' \
-e 'delay 0.5' \
-e 'tell application "System Events" to key code 53'
Open Automator and select Service.
Set: Service receives no input in any application
Add either a Run AppleScript or Run Shell Script Action.
Add the appropriate code for the target Action.
Save the Service as, e.g.: Add New Desktop
Assign a keyboard shortcut for the Service in System Preferences.
Note: You'll have to assign a key sequence that is not used elsewhere on the System or in Finder if you choose is over any application in the Service.
I tested it with: ⌃⌥⌘D (Control-Option-Command-D)
OK, using the Automator recording, I made it work with a revised version of the AppleScript in the other answer.
Replace "of group 1" with "of group 2 of group 1 of group 1 of":
on run {input, parameters}
do shell script "open -a 'Mission Control'"
delay 0.5
tell application "System Events" to click (every button whose value of attribute "AXDescription" is "add desktop") of group 2 of group 1 of group 1 of process "Dock"
delay 0.5
tell application "System Events" to key code 53
return input
end run
Yep, that works, sort of. Problem is that user3439894's solution ends up throwing an error if the active application (Finder, textedit, etc.) hasn't been added to the Security & Privacy Accessibility pane.
To get around this, we need two workflows, user3439894's service with the updated group list as an Automator application, and a second Automator service to launch it.
The Automator Application (Saved into /Applications as "New Desktop".)
on run {input, parameters}
do shell script "open -a 'Mission Control'"
delay 0.5
tell application "System Events" to click (every button whose value of attribute "AXDescription" is "add desktop") of group 2 of group 1 of group 1 of process "Dock"
delay 0.5
tell application "System Events" to key code 53
return input
end run
Then we just need to create the second workflow as a service, using the "Launch Application" action and save it as "New_Desktop"
Last steps are to add “New Desktop.App” to System Preferences > Security & Privacy > Accessibility
Then add your keyboard shortcut in System Preferences > Keyboard > Shortcuts > Services > General for the New_Desktop.workflow.
In 2024 (currently in Sonoma 14.6.1), it's possible to create/destroy spaces using Hammerspoon utility. Below is a simple example (this ensures each monitor has ten spaces, removing or adding spaces as needed).
local spaces = require("hs.spaces")
local hotkey = require("hs.hotkey")
local screen = require("hs.screen")
local menubar = require("hs.menubar")
-- Target number of spaces per screen
local TARGET_SPACES = 10
-- Function to get indexed spaces for a screen
local function getIndexedSpaces(scr)
local allSpaces = spaces.allSpaces()
local screenSpaces = allSpaces[scr:getUUID()] or {}
local indexedSpaces = {}
for i, spaceID in ipairs(screenSpaces) do
indexedSpaces[i] = spaceID
end
return indexedSpaces
end
-- Function to ensure each screen has exactly TARGET_SPACES spaces
local function ensureSpacesPerScreen()
for _, scr in ipairs(screen.allScreens()) do
local indexedSpaces = getIndexedSpaces(scr)
local spaceCount = #indexedSpaces
if spaceCount < TARGET_SPACES then
for i = 1, TARGET_SPACES - spaceCount do
spaces.addSpaceToScreen(scr)
end
print("Added " .. (TARGET_SPACES - spaceCount) .. " spaces to screen " .. scr:name())
elseif spaceCount > TARGET_SPACES then
for i = spaceCount, TARGET_SPACES + 1, -1 do
spaces.removeSpace(indexedSpaces[i])
end
print("Removed " .. (spaceCount - TARGET_SPACES) .. " spaces from screen " .. scr:name())
else
print("Screen " .. scr:name() .. " already has " .. TARGET_SPACES .. " spaces")
end
end
hs.timer.doAfter(1, function()
updateMenuBar()
end)
end
-- Hotkey to trigger space adjustment
hotkey.bind({"cmd", "alt"}, "S", ensureSpacesPerScreen)
-- Menu bar item to show current space count for each screen
local spaceMenu = menubar.new()
function updateMenuBar()
local menuText = {}
for i, scr in ipairs(screen.allScreens()) do
local indexedSpaces = getIndexedSpaces(scr)
table.insert(menuText, "Screen " .. i .. ": " .. #indexedSpaces)
end
spaceMenu:setTitle(table.concat(menuText, " "))
end
-- Initialize menu bar
updateMenuBar()
-- Watch for space or screen changes
spaces.watcher.new(updateMenuBar):start()
screen.watcher.new(updateMenuBar):start()
print("Space Manager loaded!")