← back to Desktop Dotbar
dotbar: add top-strip Arrange button — one-click re-tile every screen via the dot-screen router
71dbcd1e5530ac98a5388f2e0f064fc4163e9614 · 2026-09-16 10:15:08 -0700 · Steve Abrams
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Szengr4hMUDtYdw3jSCfA
Files touched
M public/index.htmlM server.js
Diff
commit 71dbcd1e5530ac98a5388f2e0f064fc4163e9614
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Sep 16 10:15:08 2026 -0700
dotbar: add top-strip Arrange button — one-click re-tile every screen via the dot-screen router
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Szengr4hMUDtYdw3jSCfA
---
public/index.html | 22 ++++++++++++++++++++--
server.js | 17 +++++++++++++++++
2 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/public/index.html b/public/index.html
index 6d080e7..8951a88 100644
--- a/public/index.html
+++ b/public/index.html
@@ -44,6 +44,13 @@
#meta b { color:var(--fg); font-variant-numeric:tabular-nums; }
.icobtn { -webkit-app-region:no-drag; cursor:pointer; color:var(--dim); padding:4px 6px; border-radius:6px; }
.icobtn:hover { background:var(--bg2); color:var(--fg); }
+ /* One-click re-tile of every screen (Steve 2026-09-16): after dragging windows around, click
+ this to snap them all back into the grid (green left, colour bands right). */
+ .arrbtn { -webkit-app-region:no-drag; cursor:pointer; font-weight:700; font-size:12px;
+ padding:5px 11px; border-radius:8px; border:1px solid #35507a; background:#1d2836; color:#cfe2ff;
+ white-space:nowrap; }
+ .arrbtn:hover { background:#25344a; border-color:#4a6ea8; }
+ .arrbtn.busy { opacity:.6; cursor:progress; }
/* the dropdown region (only visible when a chip is open; window grows to fit) */
#panel { display:none; background:var(--bg); border-bottom:1px solid var(--line);
max-height: calc(100vh - var(--bar-h)); overflow:auto; }
@@ -68,7 +75,7 @@
<div id="panel"></div>
<script>
const BAR_H = 48, PANEL_H = 360;
-let openColor = null, data = null, _miss = 0, curBarH = BAR_H;
+let openColor = null, data = null, _miss = 0, curBarH = BAR_H, arranging = false;
// The four needs-Steve states pulse; green/pink stay solid (Steve 2026-09-15 dot-flash directive).
const WAIT = new Set(['lightblue','orange','purple','yellow']);
@@ -94,7 +101,8 @@ function renderBar(){
}
const sp = document.createElement('div'); sp.id='spacer'; bar.appendChild(sp);
const meta = document.createElement('div'); meta.id='meta';
- meta.innerHTML = `<span><b>${data.total}</b> live</span>`
+ meta.innerHTML = `<button class="arrbtn${arranging?' busy':''}" title="re-tile every screen into the grid now" onclick="arrange()">${arranging?'⧉ Arranging…':'⧉ Arrange'}</button>`
+ + `<span><b>${data.total}</b> live</span>`
+ `<span class="icobtn" title="refresh" onclick="tick()">⟳</span>`
+ `<span class="icobtn" title="quit bar" onclick="(window.dotbar&&window.dotbar.quit)&&window.dotbar.quit()">✕</span>`;
bar.appendChild(meta);
@@ -129,6 +137,16 @@ async function reveal(tty){
try { await fetch('/api/reveal', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({tty}) }); } catch(e){}
}
+// One-click "Arrange": ask the server to run the dot-screen router now, re-tiling every screen
+// into the grid. Shows a busy state on the button until the (multi-pass, converging) run returns.
+async function arrange(){
+ if (arranging) return;
+ arranging = true; renderBar();
+ try { await fetch('/api/arrange', { method:'POST' }); } catch(e){}
+ arranging = false; renderBar();
+ tick(); // refresh counts/labels once windows have settled
+}
+
async function tick(){
const d = await fetchDots();
if (!d) return;
diff --git a/server.js b/server.js
index 1018e47..e50b9e3 100755
--- a/server.js
+++ b/server.js
@@ -10,6 +10,7 @@ const fs = require('fs');
const path = require('path');
const ALLCOLORDOTS = `${process.env.HOME}/.claude/skills/allcolordots/allcolordots.sh`;
+const ROUTER = `${process.env.HOME}/.claude/skills/dot-screen-router/router.sh`;
// Urgency order (matches allcolordots): needs-Steve first, working/parked last.
const ORDER = ['lightblue', 'orange', 'purple', 'yellow', 'green', 'pink', 'none'];
@@ -98,6 +99,19 @@ return "notfound"`;
return { ok: /ok/.test(stdout) && !err, result: (stdout || '').trim(), error: err ? String(err) : null };
}
+// One-click re-tile: run the dot-screen router NOW (force a full grid re-pack of every screen —
+// green tiled on the left, colour bands row-major on the right). The launchd loop already does
+// this every ~30s; the Arrange button makes it instant after Steve manually drags windows around.
+// ONE pass only: a single router pass reads every window's current position and moves ALL the
+// misplaced ones to their slots in one osascript batch, so one click snaps everything back. (An
+// earlier 3-pass "converge" loop cost ~74s on a loaded box AND could never settle, because dot
+// colours change live between passes — it was chasing a moving target. The 30s loop covers drift.)
+async function arrange() {
+ const { stdout, err } = await run('bash', [ROUTER], 45000);
+ const m = /routed (\d+)/.exec(stdout || '');
+ return { ok: !err, moved: m ? parseInt(m[1], 10) : 0, err: err ? String(err).slice(0, 120) : null };
+}
+
function send(res, code, body, type = 'application/json') {
res.writeHead(code, { 'Content-Type': type, 'Cache-Control': 'no-store' });
res.end(typeof body === 'string' ? body : JSON.stringify(body));
@@ -124,6 +138,9 @@ const server = http.createServer(async (req, res) => {
const url = new URL(req.url, 'http://x');
if (url.pathname === '/health') return send(res, 200, { ok: true, port: server.address() && server.address().port });
if (url.pathname === '/api/dots') { if (!snapshot.updated) await refresh(); refresh(); return send(res, 200, snapshot); }
+ if (url.pathname === '/api/arrange' && req.method === 'POST') {
+ return send(res, 200, await arrange());
+ }
if (url.pathname === '/api/reveal' && req.method === 'POST') {
let raw = '';
req.on('data', c => (raw += c));
← 0d25ad6 dotbar: drop bare-Tab global resize hotkeys; keep drag grip
·
back to Desktop Dotbar
·
auto-data-snapshot: 2026-09-16T10:40:23 (1 data files) — ele 158144e →