mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 16:59:43 +02:00
feat(tiles): sessions Run from this tab join the open grid; + offers a new session in the tile's case
Every Run path makes each session it created visible through _ensureCreatedSessionVisible and then selects the first one, a human selection that used to leave the grid for the single view. That helper now hands the new session to _joinTileGridFromRun: with the grid open it joins the next free slot, so Run's selection focuses its tile. No Attach overlay flashes on it while Run starts its pane. Sessions created elsewhere (agents, other devices, cron) arrive only by session:created and never join; a grid already holding what the window fits does not take it, and a hint says the new session opens on its own. A tile's + adds "New session in this case": the normal Run (current run mode) for the case the tile's session belongs to, with the toolbar's case put back afterwards; the session it creates joins the grid like any Run from this tab. Disabled for a session outside every case. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -465,7 +465,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
* adds it to the grid and focuses it (a human selection). Disabled once the
|
||||
* grid holds what the window can fit.
|
||||
*/
|
||||
openTileAddMenu(event) {
|
||||
openTileAddMenu(event, fromSessionId = null) {
|
||||
event?.preventDefault?.();
|
||||
event?.stopPropagation?.();
|
||||
const grid = this._tileGrid;
|
||||
@@ -504,6 +504,23 @@ Object.assign(CodemanApp.prototype, {
|
||||
});
|
||||
menu.appendChild(item);
|
||||
}
|
||||
// A new session in the case this tile's session belongs to: the normal Run
|
||||
// for that case, which then joins the grid like any Run from this tab.
|
||||
const fromSession = fromSessionId ? this.sessions.get(fromSessionId) : null;
|
||||
const theCase = fromSession ? this._mobileOverviewCaseFor?.(fromSession.workingDir, this.cases || []) : null;
|
||||
const create = document.createElement('button');
|
||||
create.type = 'button';
|
||||
create.className = 'tile-add-new';
|
||||
create.setAttribute('role', 'menuitem');
|
||||
create.textContent = 'New session in this case';
|
||||
create.disabled = full || !theCase;
|
||||
if (!theCase) create.title = 'This session is not in a case';
|
||||
else if (full) create.title = `The grid already holds what this window fits (${capacity})`;
|
||||
create.addEventListener('click', () => {
|
||||
this.closeTileAddMenu();
|
||||
if (theCase) void this.runInCaseForTiles(theCase.name);
|
||||
});
|
||||
menu.appendChild(create);
|
||||
document.body.appendChild(menu);
|
||||
if (trigger?.getBoundingClientRect) {
|
||||
const rect = trigger.getBoundingClientRect();
|
||||
@@ -524,6 +541,47 @@ Object.assign(CodemanApp.prototype, {
|
||||
menu.querySelector?.('button:not([disabled])')?.focus?.();
|
||||
},
|
||||
|
||||
/**
|
||||
* Runs the normal Run (current run mode) in `caseName`, then puts the
|
||||
* toolbar's case back as it was. The session it creates joins the grid
|
||||
* through _joinTileGridFromRun.
|
||||
*/
|
||||
async runInCaseForTiles(caseName) {
|
||||
const select = document.getElementById('quickStartCase');
|
||||
const previous = select?.value;
|
||||
const swap = !!select && !!caseName && previous !== caseName;
|
||||
if (swap) this.selectQuickStartCase?.(caseName, { save: false });
|
||||
try {
|
||||
await this.run?.();
|
||||
} finally {
|
||||
if (swap && previous) this.selectQuickStartCase?.(previous, { save: false });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* A session THIS tab's Run just created (session-ui.js
|
||||
* _ensureCreatedSessionVisible, reached only from the Run paths): with the
|
||||
* grid open it joins the next free slot, and Run's own selectSession then
|
||||
* focuses it through the tile branch. Sessions created elsewhere (agents,
|
||||
* other devices, cron) arrive only by session:created and never join. A grid
|
||||
* already holding what the window fits does not take it; Run's selection
|
||||
* then shows it in the single view (decision 1), and a hint says why.
|
||||
*/
|
||||
_joinTileGridFromRun(sessionId) {
|
||||
const grid = this._tileGrid;
|
||||
if (!grid?.open || grid.tiles.has(sessionId) || !this.sessions.has(sessionId)) return false;
|
||||
const T = window.CodemanTileGrid;
|
||||
const capacity = Math.max(1, Math.min(this._tileGridCapacityNow(), T.TILE_GRID_MAX));
|
||||
if (grid.ids.length >= capacity) {
|
||||
this.showToast?.(`The grid holds what this window fits (${capacity}): the new session opens on its own`, 'info');
|
||||
return false;
|
||||
}
|
||||
// Run starts the session right after creating it: no Attach overlay
|
||||
// meanwhile for a pane that is about to exist.
|
||||
(this._tileAttachPending ||= new Map()).set(sessionId, Date.now());
|
||||
return this.addTile(sessionId);
|
||||
},
|
||||
|
||||
/** Idempotent, like closeTilePicker. */
|
||||
closeTileAddMenu() {
|
||||
const m = this._tileAddMenu;
|
||||
@@ -1087,7 +1145,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
actions.append(
|
||||
button('tile-menu', 'Session actions', '\u22EF', (e) => this.openTabRailActionMenu?.(e, sessionId)),
|
||||
zoomBtn,
|
||||
button('tile-add', 'Add a session to the grid', '+', (e) => this.openTileAddMenu(e)),
|
||||
button('tile-add', 'Add a session to the grid', '+', (e) => this.openTileAddMenu(e, sessionId)),
|
||||
// Removes the tile ONLY: the session keeps running. Killing it stays
|
||||
// behind the menu's Close session and its confirm.
|
||||
button('tile-remove', 'Remove tile (the session keeps running)', '\u00D7', () =>
|
||||
|
||||
Reference in New Issue
Block a user