mirror of
https://github.com/nmasur/dotfiles
synced 2025-07-05 19:40:14 +00:00
split nixos from darwin
required because they don't share all attributes
This commit is contained in:
@ -1,112 +0,0 @@
|
||||
--- === ControlEscape ===
|
||||
---
|
||||
--- Adapted very loosely from https://github.com/jasonrudolph/ControlEscape.spoon
|
||||
--- Removed timing/delay; always send Escape as well as Control
|
||||
---
|
||||
--- Make the `control` key more useful: If the `control` key is tapped, treat it
|
||||
--- as the `escape` key. If the `control` key is held down and used in
|
||||
--- combination with another key, then provide the normal `control` key
|
||||
--- behavior.
|
||||
|
||||
local obj = {}
|
||||
obj.__index = obj
|
||||
|
||||
-- Metadata
|
||||
obj.name = "ControlEscape"
|
||||
obj.version = "0.1"
|
||||
obj.author = "Jason Rudolph <jason@jasonrudolph.com>"
|
||||
obj.homepage = "https://github.com/jasonrudolph/ControlEscape.spoon"
|
||||
obj.license = "MIT - https://opensource.org/licenses/MIT"
|
||||
|
||||
function obj:init()
|
||||
self.movements = 0
|
||||
self.sendEscape = false
|
||||
self.lastModifiers = {}
|
||||
|
||||
-- Create an eventtap to run each time the modifier keys change (i.e., each
|
||||
-- time a key like control, shift, option, or command is pressed or released)
|
||||
self.controlTap = hs.eventtap.new({ hs.eventtap.event.types.flagsChanged }, function(event)
|
||||
local newModifiers = event:getFlags()
|
||||
|
||||
-- If this change to the modifier keys does not involve a *change* to the
|
||||
-- up/down state of the `control` key (i.e., it was up before and it's
|
||||
-- still up, or it was down before and it's still down), then don't take
|
||||
-- any action.
|
||||
if self.lastModifiers["ctrl"] == newModifiers["ctrl"] then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Control was not down but is now
|
||||
if not self.lastModifiers["ctrl"] then
|
||||
-- Only prepare to send escape if no other modifier keys are in use
|
||||
self.lastModifiers = newModifiers
|
||||
if not self.lastModifiers["cmd"] and not self.lastModifiers["alt"] then
|
||||
self.sendEscape = true
|
||||
self.movements = 0
|
||||
end
|
||||
|
||||
-- Control was down and is up, hasn't been blocked by another key, and
|
||||
-- isn't above the movement threshold
|
||||
elseif self.sendEscape == true and not newModifiers["ctrl"] and self.movements < 30 then
|
||||
self.lastModifiers = newModifiers
|
||||
|
||||
-- Allow for shift-escape
|
||||
if newModifiers["shift"] then
|
||||
hs.eventtap.keyStroke({ "shift" }, "escape", 0)
|
||||
else
|
||||
hs.eventtap.keyStroke(newModifiers, "escape", 0)
|
||||
end
|
||||
self.sendEscape = false
|
||||
self.movements = 0
|
||||
self.numberOfCharacters = 0
|
||||
|
||||
-- Control was down and is up, but isn't ready to send escape
|
||||
else
|
||||
self.lastModifiers = newModifiers
|
||||
end
|
||||
end)
|
||||
|
||||
-- If any other key is pressed, don't send escape
|
||||
self.asModifier = hs.eventtap.new({ hs.eventtap.event.types.keyDown }, function(_)
|
||||
self.sendEscape = false
|
||||
-- print("Don't sent escape")
|
||||
end)
|
||||
|
||||
-- If mouse is moving significantly, don't send escape
|
||||
self.scrolling = hs.eventtap.new({ hs.eventtap.event.types.gesture }, function(event)
|
||||
local touches = event:getTouches()
|
||||
local i, v = next(touches, nil)
|
||||
while i do
|
||||
if v["phase"] == "moved" then
|
||||
-- Increment the movement counter
|
||||
self.movements = self.movements + 1
|
||||
end
|
||||
i, v = next(touches, i) -- get next index
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--- ControlEscape:start()
|
||||
--- Method
|
||||
--- Start sending `escape` when `control` is pressed and released in isolation
|
||||
function obj:start()
|
||||
self.controlTap:start()
|
||||
self.asModifier:start()
|
||||
self.scrolling:start()
|
||||
end
|
||||
|
||||
--- ControlEscape:stop()
|
||||
--- Method
|
||||
--- Stop sending `escape` when `control` is pressed and released in isolation
|
||||
function obj:stop()
|
||||
-- Stop monitoring keystrokes
|
||||
self.controlTap:stop()
|
||||
self.asModifier:stop()
|
||||
self.scrolling:stop()
|
||||
|
||||
-- Reset state
|
||||
self.sendEscape = false
|
||||
self.lastModifiers = {}
|
||||
end
|
||||
|
||||
return obj
|
@ -1,298 +0,0 @@
|
||||
/* Credit: https://gist.github.com/lancethomps/a5ac103f334b171f70ce2ff983220b4f */
|
||||
|
||||
function run(input, parameters) {
|
||||
|
||||
const appNames = [];
|
||||
const skipAppNames = [];
|
||||
const verbose = true;
|
||||
|
||||
const scriptName = "close_notifications_applescript";
|
||||
|
||||
const CLEAR_ALL_ACTION = "Clear All";
|
||||
const CLEAR_ALL_ACTION_TOP = "Clear";
|
||||
const CLOSE_ACTION = "Close";
|
||||
|
||||
const notNull = (val) => {
|
||||
return val !== null && val !== undefined;
|
||||
};
|
||||
|
||||
const isNull = (val) => {
|
||||
return !notNull(val);
|
||||
};
|
||||
|
||||
const notNullOrEmpty = (val) => {
|
||||
return notNull(val) && val.length > 0;
|
||||
};
|
||||
|
||||
const isNullOrEmpty = (val) => {
|
||||
return !notNullOrEmpty(val);
|
||||
};
|
||||
|
||||
const isError = (maybeErr) => {
|
||||
return notNull(maybeErr) && (maybeErr instanceof Error || maybeErr.message);
|
||||
};
|
||||
|
||||
const systemVersion = () => {
|
||||
return Application("Finder").version().split(".").map(val => parseInt(val));
|
||||
};
|
||||
|
||||
const systemVersionGreaterThanOrEqualTo = (vers) => {
|
||||
return systemVersion()[0] >= vers;
|
||||
};
|
||||
|
||||
const isBigSurOrGreater = () => {
|
||||
return systemVersionGreaterThanOrEqualTo(11);
|
||||
};
|
||||
|
||||
const V11_OR_GREATER = isBigSurOrGreater();
|
||||
const APP_NAME_MATCHER_ROLE = V11_OR_GREATER ? "AXStaticText" : "AXImage";
|
||||
const hasAppNames = notNullOrEmpty(appNames);
|
||||
const hasSkipAppNames = notNullOrEmpty(skipAppNames);
|
||||
const hasAppNameFilters = hasAppNames || hasSkipAppNames;
|
||||
const appNameForLog = hasAppNames ? ` [${appNames.join(",")}]` : "";
|
||||
|
||||
const logs = [];
|
||||
const log = (message, ...optionalParams) => {
|
||||
let message_with_prefix = `${new Date().toISOString().replace("Z", "").replace("T", " ")} [${scriptName}]${appNameForLog} ${message}`;
|
||||
console.log(message_with_prefix, optionalParams);
|
||||
logs.push(message_with_prefix);
|
||||
};
|
||||
|
||||
const logError = (message, ...optionalParams) => {
|
||||
if (isError(message)) {
|
||||
let err = message;
|
||||
message = `${err}${err.stack ? (" " + err.stack) : ""}`;
|
||||
}
|
||||
log(`ERROR ${message}`, optionalParams);
|
||||
};
|
||||
|
||||
const logErrorVerbose = (message, ...optionalParams) => {
|
||||
if (verbose) {
|
||||
logError(message, optionalParams);
|
||||
}
|
||||
};
|
||||
|
||||
const logVerbose = (message) => {
|
||||
if (verbose) {
|
||||
log(message);
|
||||
}
|
||||
};
|
||||
|
||||
const getLogLines = () => {
|
||||
return logs.join("\n");
|
||||
};
|
||||
|
||||
const getSystemEvents = () => {
|
||||
let systemEvents = Application("System Events");
|
||||
systemEvents.includeStandardAdditions = true;
|
||||
return systemEvents;
|
||||
};
|
||||
|
||||
const getNotificationCenter = () => {
|
||||
try {
|
||||
return getSystemEvents().processes.byName("NotificationCenter");
|
||||
} catch (err) {
|
||||
logError("Could not get NotificationCenter");
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const getNotificationCenterGroups = (retryOnError = false) => {
|
||||
try {
|
||||
let notificationCenter = getNotificationCenter();
|
||||
if (notificationCenter.windows.length <= 0) {
|
||||
return [];
|
||||
}
|
||||
if (!V11_OR_GREATER) {
|
||||
return notificationCenter.windows();
|
||||
}
|
||||
return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements();
|
||||
} catch (err) {
|
||||
logError("Could not get NotificationCenter groups");
|
||||
if (retryOnError) {
|
||||
logError(err);
|
||||
log("Retrying getNotificationCenterGroups...");
|
||||
return getNotificationCenterGroups(false);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const isClearButton = (description, name) => {
|
||||
return description === "button" && name === CLEAR_ALL_ACTION_TOP;
|
||||
};
|
||||
|
||||
const matchesAnyAppNames = (value, checkValues) => {
|
||||
if (isNullOrEmpty(checkValues)) {
|
||||
return false;
|
||||
}
|
||||
let lowerAppName = value.toLowerCase();
|
||||
for (let checkValue of checkValues) {
|
||||
if (lowerAppName === checkValue.toLowerCase()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const matchesAppName = (role, value) => {
|
||||
if (role !== APP_NAME_MATCHER_ROLE) {
|
||||
return false;
|
||||
}
|
||||
if (hasAppNames) {
|
||||
return matchesAnyAppNames(value, appNames);
|
||||
}
|
||||
return !matchesAnyAppNames(value, skipAppNames);
|
||||
};
|
||||
|
||||
const notificationGroupMatches = (group) => {
|
||||
try {
|
||||
let description = group.description();
|
||||
if (V11_OR_GREATER && isClearButton(description, group.name())) {
|
||||
return true;
|
||||
}
|
||||
if (V11_OR_GREATER && description !== "group") {
|
||||
return false;
|
||||
}
|
||||
if (!V11_OR_GREATER) {
|
||||
let matchedAppName = !hasAppNameFilters;
|
||||
if (!matchedAppName) {
|
||||
for (let elem of group.uiElements()) {
|
||||
if (matchesAppName(elem.role(), elem.description())) {
|
||||
matchedAppName = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matchedAppName) {
|
||||
return notNull(findCloseActionV10(group, -1));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!hasAppNameFilters) {
|
||||
return true;
|
||||
}
|
||||
let firstElem = group.uiElements[0];
|
||||
return matchesAppName(firstElem.role(), firstElem.value());
|
||||
} catch (err) {
|
||||
logErrorVerbose(`Caught error while checking window, window is probably closed: ${err}`);
|
||||
logErrorVerbose(err);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const findCloseActionV10 = (group, closedCount) => {
|
||||
try {
|
||||
for (let elem of group.uiElements()) {
|
||||
if (elem.role() === "AXButton" && elem.title() === CLOSE_ACTION) {
|
||||
return elem.actions["AXPress"];
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logErrorVerbose(`(group_${closedCount}) Caught error while searching for close action, window is probably closed: ${err}`);
|
||||
logErrorVerbose(err);
|
||||
return null;
|
||||
}
|
||||
log("No close action found for notification");
|
||||
return null;
|
||||
};
|
||||
|
||||
const findCloseAction = (group, closedCount) => {
|
||||
try {
|
||||
if (!V11_OR_GREATER) {
|
||||
return findCloseActionV10(group, closedCount);
|
||||
}
|
||||
let checkForPress = isClearButton(group.description(), group.name());
|
||||
let clearAllAction;
|
||||
let closeAction;
|
||||
for (let action of group.actions()) {
|
||||
let description = action.description();
|
||||
if (description === CLEAR_ALL_ACTION) {
|
||||
clearAllAction = action;
|
||||
break;
|
||||
} else if (description === CLOSE_ACTION) {
|
||||
closeAction = action;
|
||||
} else if (checkForPress && description === "press") {
|
||||
clearAllAction = action;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (notNull(clearAllAction)) {
|
||||
return clearAllAction;
|
||||
} else if (notNull(closeAction)) {
|
||||
return closeAction;
|
||||
}
|
||||
} catch (err) {
|
||||
logErrorVerbose(`(group_${closedCount}) Caught error while searching for close action, window is probably closed: ${err}`);
|
||||
logErrorVerbose(err);
|
||||
return null;
|
||||
}
|
||||
log("No close action found for notification");
|
||||
return null;
|
||||
};
|
||||
|
||||
const closeNextGroup = (groups, closedCount) => {
|
||||
try {
|
||||
for (let group of groups) {
|
||||
if (notificationGroupMatches(group)) {
|
||||
let closeAction = findCloseAction(group, closedCount);
|
||||
|
||||
if (notNull(closeAction)) {
|
||||
try {
|
||||
closeAction.perform();
|
||||
return [true, 1];
|
||||
} catch (err) {
|
||||
logErrorVerbose(`(group_${closedCount}) Caught error while performing close action, window is probably closed: ${err}`);
|
||||
logErrorVerbose(err);
|
||||
}
|
||||
}
|
||||
return [true, 0];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (err) {
|
||||
logError("Could not run closeNextGroup");
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
let groupsCount = getNotificationCenterGroups(true).filter(group => notificationGroupMatches(group)).length;
|
||||
|
||||
if (groupsCount > 0) {
|
||||
logVerbose(`Closing ${groupsCount}${appNameForLog} notification group${(groupsCount > 1 ? "s" : "")}`);
|
||||
|
||||
let startTime = new Date().getTime();
|
||||
let closedCount = 0;
|
||||
let maybeMore = true;
|
||||
let maxAttempts = 2;
|
||||
let attempts = 1;
|
||||
while (maybeMore && ((new Date().getTime() - startTime) <= (1000 * 30))) {
|
||||
try {
|
||||
let closeResult = closeNextGroup(getNotificationCenterGroups(), closedCount);
|
||||
maybeMore = closeResult[0];
|
||||
if (maybeMore) {
|
||||
closedCount = closedCount + closeResult[1];
|
||||
}
|
||||
} catch (innerErr) {
|
||||
if (maybeMore && closedCount === 0 && attempts < maxAttempts) {
|
||||
log(`Caught an error before anything closed, trying ${maxAttempts - attempts} more time(s).`)
|
||||
attempts++;
|
||||
} else {
|
||||
throw innerErr;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw Error(`No${appNameForLog} notifications found...`);
|
||||
}
|
||||
} catch (err) {
|
||||
logError(err);
|
||||
logError(err.message);
|
||||
getLogLines();
|
||||
throw err;
|
||||
}
|
||||
|
||||
return getLogLines();
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
--- === Dismiss Alerts ===
|
||||
|
||||
local obj = {}
|
||||
obj.__index = obj
|
||||
|
||||
-- Metadata
|
||||
obj.name = "DismissAlerts"
|
||||
obj.version = "0.1"
|
||||
obj.license = "MIT - https://opensource.org/licenses/MIT"
|
||||
|
||||
function obj:init()
|
||||
hs.hotkey.bind({ "cmd", "alt", "ctrl" }, "k", function()
|
||||
hs.osascript.javascriptFromFile("Spoons/DismissAlerts.spoon/close_notifications_applescript.js")
|
||||
end)
|
||||
end
|
||||
|
||||
return obj
|
@ -1,107 +0,0 @@
|
||||
--- === Launcher ===
|
||||
|
||||
local obj = {}
|
||||
obj.__index = obj
|
||||
|
||||
-- Metadata
|
||||
obj.name = "Launcher"
|
||||
obj.version = "0.1"
|
||||
obj.license = "MIT - https://opensource.org/licenses/MIT"
|
||||
|
||||
function DrawSwitcher()
|
||||
-- Drawing
|
||||
local width = hs.screen.mainScreen():fullFrame().w
|
||||
local switcherWidth = 500
|
||||
local canv = hs.canvas.new({
|
||||
x = width / 2 - switcherWidth / 2,
|
||||
y = 1,
|
||||
h = 3,
|
||||
w = switcherWidth,
|
||||
})
|
||||
canv[#canv + 1] = {
|
||||
action = "build",
|
||||
type = "rectangle",
|
||||
}
|
||||
canv[#canv + 1] = {
|
||||
type = "rectangle",
|
||||
fillColor = { alpha = 1, red = 0.8, green = 0.6, blue = 0.3 },
|
||||
action = "fill",
|
||||
}
|
||||
return canv:show()
|
||||
end
|
||||
|
||||
function obj:init()
|
||||
-- Begin launcher mode
|
||||
if self.launcher == nil then
|
||||
self.launcher = hs.hotkey.modal.new("ctrl", "space")
|
||||
end
|
||||
|
||||
-- Behaviors on enter
|
||||
function self.launcher:entered()
|
||||
-- hs.alert("Entered mode")
|
||||
self.canv = DrawSwitcher()
|
||||
end
|
||||
|
||||
-- Behaviors on exit
|
||||
function self.launcher:exited()
|
||||
-- hs.alert("Exited mode")
|
||||
self.canv:hide()
|
||||
end
|
||||
|
||||
-- Use escape to exit launcher mode
|
||||
self.launcher:bind("", "escape", function()
|
||||
self.launcher:exit()
|
||||
end)
|
||||
|
||||
-- Launcher shortcuts
|
||||
self.launcher:bind("ctrl", "space", function() end)
|
||||
self.launcher:bind("", "return", function()
|
||||
self:switch("@kitty@")
|
||||
end)
|
||||
self.launcher:bind("", "C", function()
|
||||
self:switch("Calendar.app")
|
||||
end)
|
||||
self.launcher:bind("", "D", function()
|
||||
self:switch("@discord@")
|
||||
end)
|
||||
self.launcher:bind("", "E", function()
|
||||
self:switch("Mail.app")
|
||||
end)
|
||||
self.launcher:bind("", "U", function()
|
||||
self:switch("Music.app")
|
||||
end)
|
||||
self.launcher:bind("", "F", function()
|
||||
self:switch("@firefox@")
|
||||
end)
|
||||
self.launcher:bind("", "H", function()
|
||||
self:switch("Hammerspoon.app")
|
||||
end)
|
||||
self.launcher:bind("", "G", function()
|
||||
self:switch("Mimestream.app")
|
||||
end)
|
||||
self.launcher:bind("", "M", function()
|
||||
self:switch("Messages.app")
|
||||
end)
|
||||
self.launcher:bind("", "O", function()
|
||||
self:switch("Obsidian.app")
|
||||
end)
|
||||
self.launcher:bind("", "P", function()
|
||||
self:switch("System Preferences.app")
|
||||
end)
|
||||
self.launcher:bind("", "R", function()
|
||||
hs.reload()
|
||||
end)
|
||||
self.launcher:bind("", "S", function()
|
||||
self:switch("Slack.app")
|
||||
end)
|
||||
self.launcher:bind("", "Z", function()
|
||||
self:switch("zoom.us.app")
|
||||
end)
|
||||
end
|
||||
|
||||
function obj:switch(app)
|
||||
hs.application.launchOrFocus(app)
|
||||
self.launcher:exit()
|
||||
end
|
||||
|
||||
return obj
|
@ -1,52 +0,0 @@
|
||||
--- === Move Window ===
|
||||
|
||||
local obj = {}
|
||||
obj.__index = obj
|
||||
|
||||
-- Metadata
|
||||
obj.name = "MoveWindow"
|
||||
obj.version = "0.1"
|
||||
obj.license = "MIT - https://opensource.org/licenses/MIT"
|
||||
|
||||
function obj:init()
|
||||
hs.window.animationDuration = 0
|
||||
dofile(hs.spoons.resourcePath("worklayout.lua"))()
|
||||
-- bind hotkey
|
||||
hs.hotkey.bind({ "alt", "ctrl", "cmd" }, "n", function()
|
||||
-- get the focused window
|
||||
local win = hs.window.focusedWindow()
|
||||
-- get the screen where the focused window is displayed, a.k.a. current screen
|
||||
local screen = win:screen()
|
||||
-- local nextScreen = screen:next()
|
||||
-- compute the unitRect of the focused window relative to the current screen
|
||||
-- and move the window to the next screen setting the same unitRect
|
||||
win:moveToScreen(screen:next(), true, true, 0)
|
||||
end)
|
||||
|
||||
hs.hotkey.bind({ "alt", "ctrl", "cmd" }, "b", function()
|
||||
local win = hs.window.focusedWindow()
|
||||
local screen = win:screen()
|
||||
win:moveToScreen(screen:previous(), true, true, 0)
|
||||
end)
|
||||
|
||||
hs.hotkey.bind({ "alt", "ctrl", "cmd" }, "m", function()
|
||||
-- get the focused window
|
||||
local win = hs.window.focusedWindow()
|
||||
local frame = win:frame()
|
||||
-- maximize if possible
|
||||
local max = win:screen():fullFrame()
|
||||
frame.x = max.x
|
||||
frame.y = max.y
|
||||
frame.w = max.w
|
||||
frame.h = max.h
|
||||
win:setFrame(frame)
|
||||
-- -- first maximize to grid
|
||||
-- hs.grid.maximizeWindow(win)
|
||||
-- -- then spam maximize
|
||||
-- for i = 1, 8 do
|
||||
-- win:maximize()
|
||||
-- end
|
||||
end)
|
||||
end
|
||||
|
||||
return obj
|
@ -1,60 +0,0 @@
|
||||
--- === Work Layout ===
|
||||
-- Portions of this is adopted from:
|
||||
-- https://github.com/anishathalye/dotfiles-local/tree/ffdadd313e58514eb622736b09b91a7d7eb7c6c9/hammerspoon
|
||||
-- License is also available:
|
||||
-- https://github.com/anishathalye/dotfiles-local/blob/ffdadd313e58514eb622736b09b91a7d7eb7c6c9/LICENSE.md
|
||||
|
||||
WORK_LEFT_MONITOR = "DELL U2415 (2)"
|
||||
WORK_RIGHT_MONITOR = "DELL U2415 (1)"
|
||||
LAPTOP_MONITOR = "Built-in Retina Display"
|
||||
|
||||
-- Used to find out the name of the monitor in Hammerspoon
|
||||
-- local function dump(o)
|
||||
-- if type(o) == "table" then
|
||||
-- local s = "{ "
|
||||
-- for k, v in pairs(o) do
|
||||
-- if type(k) ~= "number" then
|
||||
-- k = '"' .. k .. '"'
|
||||
-- end
|
||||
-- s = s .. "[" .. k .. "] = " .. dump(v) .. ","
|
||||
-- end
|
||||
-- return s .. "} "
|
||||
-- else
|
||||
-- return tostring(o)
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- Turn on when looking for the monitor name
|
||||
-- print(dump(hs.screen.allScreens()))
|
||||
|
||||
local function concat(...)
|
||||
local res = {}
|
||||
for _, tab in ipairs({ ... }) do
|
||||
for _, elem in ipairs(tab) do
|
||||
table.insert(res, elem)
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local function worklayout()
|
||||
hs.hotkey.bind({ "alt", "ctrl", "cmd" }, "l", function()
|
||||
local u = hs.geometry.unitrect
|
||||
-- set the layout
|
||||
local left = {
|
||||
{ "kitty", nil, WORK_LEFT_MONITOR, u(0, 0, 1, 1), nil, nil, visible = true },
|
||||
}
|
||||
local right = {
|
||||
{ "Slack", nil, WORK_RIGHT_MONITOR, u(0, 0, 1, 1), nil, nil, visible = true },
|
||||
{ "Mail", nil, WORK_RIGHT_MONITOR, u(0, 0, 1, 1), nil, nil, visible = true },
|
||||
{ "zoom.us", nil, WORK_RIGHT_MONITOR, u(1 / 4, 1 / 4, 1 / 2, 1 / 2), nil, nil, visible = true },
|
||||
}
|
||||
local laptop = {
|
||||
{ "Firefox", nil, LAPTOP_MONITOR, u(0, 0, 1, 1), nil, nil, visible = true },
|
||||
}
|
||||
local layout = concat(left, right, laptop)
|
||||
hs.layout.apply(layout)
|
||||
end)
|
||||
end
|
||||
|
||||
return worklayout
|
Reference in New Issue
Block a user