← back to Desktop Dotbar
desktop-dotbar: collapsed PARKED chip/panel from the durable registry
b102cca8d022089a0668f41c5e0abbd9ca255f86 · 2026-09-20 11:29:57 -0700 · Steve
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZehHfR2cgtmFvznNavrn5
Files touched
M public/index.htmlM server.js
Diff
commit b102cca8d022089a0668f41c5e0abbd9ca255f86
Author: Steve <steve@designerwallcoverings.com>
Date: Sun Sep 20 11:29:57 2026 -0700
desktop-dotbar: collapsed PARKED chip/panel from the durable registry
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZehHfR2cgtmFvznNavrn5
---
public/index.html | 34 +++++++++++++++++++++++++++++++++-
server.js | 17 +++++++++++++++--
2 files changed, 48 insertions(+), 3 deletions(-)
diff --git a/public/index.html b/public/index.html
index 0ef6b05..5d8f511 100644
--- a/public/index.html
+++ b/public/index.html
@@ -135,6 +135,18 @@ function renderBar(){
chip.onclick = () => toggle(g.color);
bar.appendChild(chip);
}
+ // Durable PARKED chip (TK-11946) — collapsed badge, distinct from live pink; opens a
+ // panel listing the registry (parked tabs + parked tickets). Hidden when the registry is empty.
+ if (data.parked && data.parked.count){
+ const pchip = document.createElement('div');
+ pchip.className = 'chip' + (openColor==='parked' ? ' active':'');
+ const showName = (openColor==='parked') || cfg.orientation !== 'top';
+ pchip.innerHTML = `<span class="dot" style="background:#ff8fc8"></span>`
+ + `<span class="cnt">${data.parked.count}</span>`
+ + (showName ? `<span class="nm">parked</span>` : '');
+ pchip.onclick = () => toggle('parked');
+ bar.appendChild(pchip);
+ }
const sp = document.createElement('div'); sp.id='spacer'; bar.appendChild(sp);
const meta = document.createElement('div'); meta.id='meta';
const nextO = cfg.orientation==='top'?'left':cfg.orientation==='left'?'right':'top';
@@ -150,6 +162,23 @@ function renderBar(){
function renderPanel(){
const panel = document.getElementById('panel');
if (!openColor){ panel.className=''; panel.innerHTML=''; return; }
+ // Durable PARKED panel (TK-11946): registry entries, not a live colour group.
+ if (openColor==='parked'){
+ panel.className = 'open';
+ const items = (data.parked && data.parked.items) || [];
+ if (!items.length){ panel.innerHTML = `<div class="empty">No parked items.</div>`; return; }
+ let html = `<div class="phead">🩷 parked (durably held) — ${items.length}</div>`;
+ for (const s of items){
+ html += `<div class="row">`
+ + `<span class="rdot" style="background:#ff8fc8"></span>`
+ + `<span class="tk">${s.ticket || s.id || '—'}</span>`
+ + `<span class="doing" title="${(s.doing||'').replace(/"/g,'"')}">${s.doing || ('('+s.kind+')')}</span>`
+ + `<span class="tty">${s.tty || s.kind}</span>`
+ + (s.tty ? `<button class="open" onclick='reveal(${JSON.stringify(s.tty)})'>open terminal ↗</button>` : '')
+ + `</div>`;
+ }
+ panel.innerHTML = html; return;
+ }
const g = data.groups.find(x=>x.color===openColor);
panel.className = 'open';
if (!g || !g.sessions.length){ panel.innerHTML = `<div class="empty">No ${openColor} sessions.</div>`; return; }
@@ -199,7 +228,10 @@ async function tick(){
const d = await fetchDots();
if (!d) return;
data = d;
- if (openColor && !data.groups.some(g=>g.color===openColor && g.count>0)) {
+ const openStillHasItems = openColor==='parked'
+ ? !!(data.parked && data.parked.count)
+ : data.groups.some(g=>g.color===openColor && g.count>0);
+ if (openColor && !openStillHasItems) {
if (++_miss >= 2) { openColor=null; resize(false); _miss=0; }
} else { _miss = 0; }
renderBar(); renderPanel();
diff --git a/server.js b/server.js
index f4dac89..51345fb 100755
--- a/server.js
+++ b/server.js
@@ -11,6 +11,7 @@ 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`;
+const PARKED_MJS = `${process.env.HOME}/.claude/skills/parked/parked.mjs`; // durable PARKED registry (TK-11946)
// Urgency order (matches allcolordots): needs-Steve first, working/parked last.
const ORDER = ['lightblue', 'orange', 'purple', 'yellow', 'green', 'pink', 'none'];
@@ -49,7 +50,14 @@ async function getDots() {
const { stdout } = await run('bash', [ALLCOLORDOTS, '--json'], 120000);
let rows = [];
try { rows = JSON.parse(stdout || '[]'); } catch { rows = []; }
- rows = rows.filter(r => r && r.live); // only live sessions count as "running"
+ // Durable PARKED group straight from the registry (both tab + ticket kinds),
+ // collapsed and distinct from the transient live-pink dots.
+ const { stdout: pj } = await run(process.execPath, [PARKED_MJS, 'list-parked', '--json'], 8000);
+ let parked = [];
+ try { parked = JSON.parse(pj || '[]'); } catch { parked = []; }
+ // A durably-parked live tab peels OUT of the active colour groups into the PARKED
+ // section, so the active dots stay uncluttered (allcolordots --json carries `parked`).
+ rows = rows.filter(r => r && r.live && !r.parked);
const groups = {};
for (const key of ORDER) groups[key] = [];
for (const r of rows) {
@@ -64,7 +72,12 @@ async function getDots() {
const out = ORDER
.map(color => ({ color, ...META[color], count: groups[color].length, sessions: groups[color] }))
.filter(g => g.count > 0 || g.color === 'green'); // always show green; hide empty others
- return { updated: Date.now(), total: rows.length, groups: out };
+ const parkedItems = parked.map(e => ({
+ kind: e.kind, id: e.id, ticket: e.kind === 'ticket' ? e.id : ticketOf(e.label),
+ tty: e.tty || (e.kind === 'tab' ? e.id : ''), doing: cleanLabel(e.label), parked_at: e.parked_at,
+ }));
+ return { updated: Date.now(), total: rows.length, groups: out,
+ parked: { count: parkedItems.length, items: parkedItems } };
}
// Bring the iTerm2 session whose tty matches to the front.
← d88f4e1 dotbar: horizontal contract/expand for the top bar (right-en
·
back to Desktop Dotbar
·
Add RAM Sniper to the nav bar: live RAM%/CPU% chip, ON/OFF d 34abb18 →