From 83d0caa20989baf4835ccd214c363eb5b631ff92 Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Tue, 6 Oct 2026 17:45:28 +0200 Subject: [PATCH] feat(tiles): tile header (status dot, name, menu, remove) and the in-tiles tab marker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every tile gets a fixed-height header above its body: `● name ......... ⋯ ×`. - The dot is the six-state classifier the tab rows and both home screens share (_sidebarRichRow), with the existing .home-sessions-dot--* classes; hovering the header says the state and for how long ("working 3m"). A tile whose session waits on a permission prompt or question gets a pulsing red border (box-shadow only, never layout; still under reduced motion). - The name is text with data-i18n-skip; a double-click renames it through the tab rename's own write queue (Enter or leaving the field commits, Escape cancels, an IME composition owns Enter), and an in-flight name shows as already applied, as on the tab. - ⋯ is the tab rail's session menu (options, new window, close session with its confirm); × removes the tile ONLY, the session keeps running. Neither button focuses a tile that is not focused. - The header is fixed at 24px so nothing in it can resize the body, and with it the xterm and its PTY (#464). Every tab render refreshes the headers, so they follow status and name changes. Tabs of tiled sessions carry .in-tiles (both render paths), and the tab strip re-renders when tiles come and go. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/web/public/app.js | 5 +- src/web/public/styles.css | 96 +++++++++++++++ src/web/public/tile-grid.js | 152 +++++++++++++++++++++++- test/mocks/tile-grid-vm.ts | 18 +++ test/tile-grid-chrome.test.ts | 212 ++++++++++++++++++++++++++++++++++ 5 files changed, 480 insertions(+), 3 deletions(-) create mode 100644 test/tile-grid-chrome.test.ts diff --git a/src/web/public/app.js b/src/web/public/app.js index b5075b32..32774db0 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -5647,6 +5647,9 @@ class CodemanApp { else if (wantIdle && !hasIdle) { tab.classList.add('tab-alert-idle'); tab.classList.remove('tab-alert-action'); } else if (!alertType && (hasAction || hasIdle)) { tab.classList.remove('tab-alert-action', 'tab-alert-idle'); } + // Tile grid membership (tile-grid.js): a tiled session's tab says so. + tab.classList.toggle('in-tiles', !!this._tileGrid?.has(id)); + // Inject tab-number badge if missing (added after initial render) if (!tab.querySelector('.tab-number')) { const idx = this.sessionOrder.indexOf(id); @@ -6000,7 +6003,7 @@ class CodemanApp { const inlineSessionActions = this.shouldInlineSessionActions(); const tabActionsHtml = `⚙⧉×`; - rowHtml.set(id, `