Skip to content

Instantly share code, notes, and snippets.

@sandro
Last active June 3, 2026 20:28
Show Gist options
  • Select an option

  • Save sandro/1224b973b9445814b368fbc01bf93a18 to your computer and use it in GitHub Desktop.

Select an option

Save sandro/1224b973b9445814b368fbc01bf93a18 to your computer and use it in GitHub Desktop.
phoenix config
Phoenix.set({ openAtLogin: true });
const RIGHT_PROPORTION = 0.55;
const LEFT_PROPORTION = 1.0 - RIGHT_PROPORTION;
const TOP_PROPORTION = 0.55;
const BOTTOM_PROPORTION = 1.0 - TOP_PROPORTION;
const MODIFIERS = ["ctrl", "alt", "cmd"];
const HINT_CHARS = "0123456789qertyuiop".split("");
const MOVE_STEP = 200;
let animationTimer;
function animateFrame(w, target, steps = 6, duration = 0.06) {
if (animationTimer) clearInterval(animationTimer);
const start = w.frame();
let step = 0;
const interval = (duration / steps) * 1000;
animationTimer = setInterval(() => {
step++;
const t = step / steps;
const ease = 1 - Math.pow(1 - t, 3);
w.setFrame({
x: Math.round(start.x + (target.x - start.x) * ease),
y: Math.round(start.y + (target.y - start.y) * ease),
width: Math.round(start.width + (target.width - start.width) * ease),
height: Math.round(start.height + (target.height - start.height) * ease),
});
if (step >= steps) clearInterval(animationTimer);
}, interval);
}
class SimpleMover {
constructor() {
this.enabled = false;
this.autoCloseId = undefined;
this.keys = [];
this.hintKeys = [];
this.hintModals = [];
this.savedFrames = new Map();
this.helpModal = Modal.build({
icon: App.get("Phoenix").icon(),
text: [
"← ↑ → ↓ Move window",
"⌘+← ↑ → ↓ Resize window",
"w Raise app windows",
"g Focus any window",
"t Tile app windows",
"T Restore app windows",
"esc Exit",
].join("\n"),
appearance: "light",
font: "Menlo",
weight: 16,
animationDuration: 0.15,
origin: () => ({ x: 0, y: 200 }),
});
this.exitKey = new Key("escape", [], () => this.disable());
this.exitKey.disable();
this.setupKeys();
}
resetAutoClose() {
clearTimeout(this.autoCloseId);
this.autoCloseId = setTimeout(() => this.disable(), 10000);
}
withFocusedWindow(fn) {
return () => {
if (!this.enabled) return;
this.resetAutoClose();
const w = Window.focused();
if (w) fn(w, w.frame());
};
}
moverAction(fn) {
return () => {
if (!this.enabled) return;
this.resetAutoClose();
fn();
};
}
killHints() {
for (const key of this.hintKeys) key.disable();
this.hintKeys = [];
for (const modal of this.hintModals) modal.close();
this.hintModals = [];
}
showWindowHints(windows, onSelect) {
const screenFrame = Screen.main().flippedFrame();
for (const [index, win] of windows.entries()) {
if (index >= HINT_CHARS.length) break;
const winFrame = win.frame();
const hint = HINT_CHARS[index];
const modal = Modal.build({
icon: win.app().icon(),
text: hint,
appearance: "dark",
animationDuration: 0.1,
duration: 10,
origin: () => ({
x: winFrame.x,
y: screenFrame.height - winFrame.y - index * 100,
}),
});
this.hintModals.push(modal);
modal.show();
const hintKey = new Key(hint, [], () => {
onSelect(win);
this.disable();
});
this.hintKeys.push(hintKey);
}
}
raiseAppWindows(w) {
this.killHints();
const windows = w.app().windows({ visible: true });
const mainIndex = windows.findIndex((win) => win.isMain());
if (mainIndex >= 0) windows.splice(mainIndex, 1);
windows.sort((a, b) => a.frame().y - b.frame().y);
windows.unshift(w);
this.showWindowHints(windows, (selected) => {
selected.raise();
selected.focus();
});
}
refocusAnyWindow() {
this.killHints();
this.showWindowHints(Window.recent(), (selected) => {
selected.raise();
selected.focus();
});
}
tileAppWindows() {
clearTimeout(this.autoCloseId);
const w = Window.focused();
if (!w) return;
const windows = w.app().windows({ visible: true });
if (windows.length === 0) return;
if (this.savedFrames.size === 0) {
for (const win of windows) {
this.savedFrames.set(win.hash(), win.frame());
}
}
const screen = w.screen();
const frame = screen.flippedVisibleFrame();
const n = windows.length;
const cols = Math.ceil(Math.sqrt(n));
const rows = Math.ceil(n / cols);
let windowIndex = 0;
let y = frame.y;
for (let row = 0; row < rows; row++) {
const colsInRow = (row === rows - 1 && n % cols !== 0) ? n % cols : cols;
const cellWidth = Math.round(frame.width / colsInRow);
const cellHeight = Math.round(frame.height / rows);
let x = frame.x;
for (let col = 0; col < colsInRow; col++) {
if (windowIndex >= n) break;
const width = (col === colsInRow - 1) ? frame.x + frame.width - x : cellWidth;
const height = (row === rows - 1) ? frame.y + frame.height - y : cellHeight;
windows[windowIndex].raise();
windows[windowIndex].setFrame({ x, y, width, height });
x += cellWidth;
windowIndex++;
}
y += cellHeight;
}
}
restoreWindows() {
if (this.savedFrames.size === 0) return;
for (const [hash, savedFrame] of this.savedFrames) {
const win = Window.all().find((w) => w.hash() === hash);
if (win) win.setFrame(savedFrame);
}
this.disable();
}
enable() {
if (this.enabled) return;
this.enabled = true;
this.exitKey.enable();
for (const key of this.keys) key.enable();
this.helpModal.show();
this.resetAutoClose();
}
disable() {
if (!this.enabled) return;
this.enabled = false;
this.exitKey.disable();
for (const key of this.keys) key.disable();
this.helpModal.close();
this.killHints();
this.savedFrames = new Map();
}
addKey(name, mods, handler) {
const key = new Key(name, mods, handler);
key.disable();
this.keys.push(key);
}
setupKeys() {
this.addKey("left", [], this.withFocusedWindow((w, f) => w.setTopLeft({ x: f.x - MOVE_STEP, y: f.y })));
this.addKey("right", [], this.withFocusedWindow((w, f) => w.setTopLeft({ x: f.x + MOVE_STEP, y: f.y })));
this.addKey("up", [], this.withFocusedWindow((w, f) => w.setTopLeft({ x: f.x, y: f.y - MOVE_STEP })));
this.addKey("down", [], this.withFocusedWindow((w, f) => w.setTopLeft({ x: f.x, y: f.y + MOVE_STEP })));
this.addKey("left", ["cmd"], this.withFocusedWindow((w, f) => w.setSize({ width: f.width - MOVE_STEP, height: f.height })));
this.addKey("right", ["cmd"], this.withFocusedWindow((w, f) => w.setSize({ width: f.width + MOVE_STEP, height: f.height })));
this.addKey("up", ["cmd"], this.withFocusedWindow((w, f) => w.setSize({ width: f.width, height: f.height - MOVE_STEP })));
this.addKey("down", ["cmd"], this.withFocusedWindow((w, f) => w.setSize({ width: f.width, height: f.height + MOVE_STEP })));
this.addKey("w", [], this.withFocusedWindow((w) => this.raiseAppWindows(w)));
this.addKey("g", [], this.moverAction(() => this.refocusAnyWindow()));
this.addKey("t", [], this.withFocusedWindow(() => this.tileAppWindows()));
this.addKey("t", ["shift"], this.moverAction(() => this.restoreWindows()));
}
}
class CyclingKeyHandler {
constructor() {
this.pressCounts = {};
this.resetTimeoutId = undefined;
}
pressed(key) {
clearTimeout(this.resetTimeoutId);
this.resetTimeoutId = setTimeout(() => { this.pressCounts = {}; }, 2000);
let count = this.pressCounts[key] || 0;
if (count >= 3) count = 0;
this.pressCounts[key] = count + 1;
return this.pressCounts[key];
}
on(key, modifiers, fn) {
Key.on(key, modifiers, () => {
simpleMover.disable();
const num = this.pressed(key);
const w = Window.focused();
if (!w) return;
const screen = w.screen().flippedFrame();
fn(screen, w, num);
});
}
}
const simpleMover = new SimpleMover();
const cyclingKeyHandler = new CyclingKeyHandler();
function center(screen, w) {
const x = screen.x + screen.width * 0.15;
animateFrame(w, { x, y: screen.y, width: screen.width * 0.7, height: screen.height });
}
function leftSide(screen, w, num) {
const sizes = [
{ x: screen.x, width: screen.width * LEFT_PROPORTION },
{ x: screen.x, width: screen.width * LEFT_PROPORTION - 200 },
{ x: screen.x, width: screen.width * LEFT_PROPORTION - 500 },
];
const size = sizes[num - 1];
animateFrame(w, { x: size.x, y: screen.y, width: size.width, height: screen.height });
}
function rightSide(screen, w, num) {
const base = {
x: screen.x + screen.width * LEFT_PROPORTION,
width: screen.width * RIGHT_PROPORTION,
};
const sizes = [
base,
{ x: base.x - 200, width: base.width + 200 },
{ x: base.x + 100, width: base.width - 100 },
];
const size = sizes[num - 1];
animateFrame(w, { x: size.x, y: screen.y, width: size.width, height: screen.height });
}
function topSide(screen, w) {
animateFrame(w, {
x: screen.x, y: screen.y,
width: screen.width, height: screen.height * TOP_PROPORTION,
});
}
function bottomSide(screen, w) {
animateFrame(w, {
x: screen.x,
y: screen.y + screen.height * TOP_PROPORTION,
width: screen.width,
height: screen.height * BOTTOM_PROPORTION,
});
}
cyclingKeyHandler.on("c", MODIFIERS, center);
cyclingKeyHandler.on("m", MODIFIERS, (screen, w) => w.maximize());
cyclingKeyHandler.on("left", MODIFIERS, leftSide);
cyclingKeyHandler.on("right", MODIFIERS, rightSide);
cyclingKeyHandler.on("up", MODIFIERS, topSide);
cyclingKeyHandler.on("down", MODIFIERS, bottomSide);
cyclingKeyHandler.on("f", MODIFIERS, () => simpleMover.enable());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment