mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-10 17:29:41 +02:00
Merge the final checkup's fixes into the 1.40.0 release
From the read-only final review of the release, adversarially verified, then reviewed again: - tiles: a file dropped on the grid uploads to that tile's session instead of navigating away; app-driven tile changes no longer move the keyboard into another session; popping out the last tile no longer leaves a frozen view; "Open group as tiles" no longer merges an open split; the Tiles button defaults off on touch tablets (opt-in) - voice: dictation with the grid open reaches the focused tile - css: By case stays one scrolling strip on 600-767px tablets, the needs-you pulse animates opacity only, phone welcome chips are 40px - i18n: zh-CN for the case picker rows, the git status settings, new toasts, tile and spreadsheet texts - cli registry: codex launch defaults are registry data, not an id branch; the codex footer reads an ultra effort - build and docs: a dependency preflight runs before the build deletes dist; docs no longer name 1.36.0 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -805,7 +805,7 @@ Further detail: with many sessions the horizontal strip stops being scannable, w
|
||||
|
||||
### Tile grid
|
||||
|
||||
**Tile grid** (`showTileGridButton`, header Tiles button + `Ctrl+Shift+G`, default ON on desktop and OFF on handhelds, per-device: in `displayKeys`, stripped from the settings PUT, not in `SettingsUpdateSchema`; desktop-only at 1180px by a JS width check with a live media listener plus a `@media (max-width: 1179px)` backstop, never in a solo window). 1 to 6 live sessions side by side in one window, each a `TerminalTile` (terminal-tile.js), orchestrated by tile-grid.js (load order 7.6, `CodemanApp.prototype` methods like the split's). Pure helpers live in constants.js as `window.CodemanTileGrid`: `computeTileLayout` (1x1, 2x1, 3x1 on a grid area at least 1800px wide else 2x2, 2x2, 3x2, and 3x3 up to `TILE_LAYOUT_MAX` (9), unreachable today; `fits` against a 480x240 minimum tile), `tileGridCapacity` (never more than `TILE_GRID_MAX`), `sanitizeTileGridState` (truncates a stored grid to the cap, keeps focus only if it survives), `buildTilePickerSessions`, `dragTrackFractions`, `tileNeighbor`, `tileInDirection`, `cycleTile`, `TILE_SCROLLBACK` (10,000), and `TILE_GRID_MAX` (6), the ONE cap (owner decision 7: six tested smooth on a real desktop, nine missed the headless frame bar). ⚠️ Every limit reads the cap through `_tileGridLimit()` (tile-grid.js: the window's capacity, at most the cap), never a literal, and its texts say which binds ("at most 6 tiles" vs "what this window fits"). The grid is a `<section class="tile-grid">` SIBLING of `.terminal-wrap`, swapped in by `.main.tiles-active` (no reparenting); `.main.webview-active .tile-grid` hides it like the single view.
|
||||
**Tile grid** (`showTileGridButton`, header Tiles button + `Ctrl+Shift+G`, default ON on desktop and OFF on handhelds and touch-primary tablets (primary pointer coarse, resolved in `getDefaultSettings()`, never `isTouchDevice()`, so a touchscreen laptop keeps ON), per-device: in `displayKeys`, stripped from the settings PUT, not in `SettingsUpdateSchema`; desktop-only at 1180px by a JS width check with a live media listener plus a `@media (max-width: 1179px)` backstop, never in a solo window). 1 to 6 live sessions side by side in one window, each a `TerminalTile` (terminal-tile.js), orchestrated by tile-grid.js (load order 7.6, `CodemanApp.prototype` methods like the split's). Pure helpers live in constants.js as `window.CodemanTileGrid`: `computeTileLayout` (1x1, 2x1, 3x1 on a grid area at least 1800px wide else 2x2, 2x2, 3x2, and 3x3 up to `TILE_LAYOUT_MAX` (9), unreachable today; `fits` against a 480x240 minimum tile), `tileGridCapacity` (never more than `TILE_GRID_MAX`), `sanitizeTileGridState` (truncates a stored grid to the cap, keeps focus only if it survives), `buildTilePickerSessions`, `dragTrackFractions`, `tileNeighbor`, `tileInDirection`, `cycleTile`, `TILE_SCROLLBACK` (10,000), and `TILE_GRID_MAX` (6), the ONE cap (owner decision 7: six tested smooth on a real desktop, nine missed the headless frame bar). ⚠️ Every limit reads the cap through `_tileGridLimit()` (tile-grid.js: the window's capacity, at most the cap), never a literal, and its texts say which binds ("at most 6 tiles" vs "what this window fits"). The grid is a `<section class="tile-grid">` SIBLING of `.terminal-wrap`, swapped in by `.main.tiles-active` (no reparenting); `.main.webview-active .tile-grid` hides it like the single view.
|
||||
|
||||
⚠️ **The main terminal is parked while the grid is open.** Opening runs `_cleanupPreviousSession()` ONCE (its snapshot is right at that moment, and it closes the main socket), and `activeSessionId` is always the FOCUSED tile's session, so everything keyed on it (files panel, git status, respawn and Ralph panels, subagent windows, voice, image paste, the tab highlight) follows focus. With the main socket closed `_wsReady` is false, so every SSE terminal handler would write the focused tile's output into the hidden xterm: `_tilesOwnTerminal()` turns `_onSessionTerminal`, `_onSessionClearTerminal`, `_onSessionNeedsRefresh` (returns `false`, which the drop recovery reads), `_scheduleDroppedOutputRecovery`, the `writeln` in `_onSessionCompletion`/`_onSessionError`, `sendResize`, `throttledResize`'s fit and `_maybeRefetchFullHistory` into no-ops; `retryConnection` and `handleInit` re-arm the TILES' sockets instead of the main one (`handleInit` keeps live tiles, drops dead ones through `_reconcileTileGrid`, and re-selects only when the focused tile is gone, so an SSE blip never hides an open web tab). ⚠️ The page's SSE filter names `TILE_GRID_SSE_FILTER` (constants.js, an id no session takes) instead of the focused session while tiles own the terminal: the server's filter gates only `session:terminal` batches, which the parked terminal could only parse and drop. Both places that set the filter ask `_sseFilterSessionId()`: the live re-subscribe every tile focus runs (`_updateSseSubscription`) and the connect URL an SSE reconnect rebuilds (`connectSSE`); leaving the grid re-subscribes the shown session through `selectSession`. `test/sse-tile-grid-filter.test.ts` pins the server's side in multi-user mode (the id is accepted on connect and on re-subscribe, and session/hook events still reach their owner through it). ⚠️ The WebGL long-task observer watches the WHOLE page: it counts nothing while tiles own the terminal, or tile renders would write the sticky 7-day WebGL disable. The header connection dot reads the tile sockets (`_tileGridSocketState`; a tile stopped for good does not count). `_focusedPane()` answers with the focused tile even when DOM focus left every terminal, and `_forEachTile` reaches every grid tile (`{ grid: false }` skips them where tiles keep their own font size). Leaving the grid destroys every tile, resets `_lastResizeDims`, invalidates the main terminal's cached content for EVERY tiled id (`_xtermSnapshots`, `codeman-xs-<id>`, `terminalBufferCache`: written before the grid opened, and selectSession paints a snapshot as its first frame) and replays the focused session through `selectSession(id, { forceReload: true, auto: true })`.
|
||||
|
||||
|
||||
@@ -71,17 +71,21 @@ We tested three browser automation frameworks against the Codeman web UI:
|
||||
|
||||
## Test File Structure
|
||||
|
||||
### Port Allocation
|
||||
### Ports
|
||||
|
||||
| Port Range | Test File |
|
||||
|------------|-----------|
|
||||
| 3150-3153 | browser-e2e.test.ts (existing) |
|
||||
| 3154 | file-link-click.test.ts |
|
||||
| 3155 | browser-playwright.test.ts |
|
||||
| 3156 | browser-puppeteer.test.ts |
|
||||
| 3157 | browser-agent.test.ts |
|
||||
| 3158-3160 | browser-comparison.test.ts |
|
||||
| 3180-3182 | scripts/browser-comparison.mjs |
|
||||
A test that starts a server binds an ephemeral port, never a fixed one:
|
||||
|
||||
- `WebServer`: `new WebServer(0, false, true)`, then read the port the OS handed out from
|
||||
`server.boundPort` after `await server.start()`. `test/test-ports-guard.test.ts` fails
|
||||
any `WebServer` built under `test/` on a non-zero port (a shrink-only legacy list
|
||||
excepted).
|
||||
- A raw Fastify or `ws` server: `listen({ port: 0 })`, then `address().port`.
|
||||
- The mobile suite (`test/mobile/**`, via `createTestServer(PORT)`) keeps the fixed-port
|
||||
convention in `test/mobile/README.md` for now.
|
||||
- Never port 3000: that is the live instance.
|
||||
|
||||
`scripts/browser-comparison.mjs` is a standalone script outside the guard and still uses
|
||||
fixed ports 3180-3182.
|
||||
|
||||
### File Purposes
|
||||
|
||||
@@ -106,7 +110,7 @@ const browser = await chromium.launch({
|
||||
});
|
||||
|
||||
const page = await browser.newPage();
|
||||
await page.goto('http://localhost:3000');
|
||||
await page.goto(BASE_URL);
|
||||
|
||||
// Auto-waiting selectors
|
||||
await page.click('.btn-claude');
|
||||
@@ -140,7 +144,7 @@ const browser = await puppeteer.launch({
|
||||
});
|
||||
|
||||
const page = await browser.newPage();
|
||||
await page.goto('http://localhost:3000');
|
||||
await page.goto(BASE_URL);
|
||||
|
||||
// Manual waiting often needed
|
||||
await page.click('.btn-claude');
|
||||
@@ -186,7 +190,7 @@ function agentBrowserJson<T>(cmd: string): T {
|
||||
}
|
||||
|
||||
// Usage
|
||||
agentBrowser('open http://localhost:3000');
|
||||
agentBrowser(`open ${BASE_URL}`);
|
||||
agentBrowser('click ".btn-claude"');
|
||||
const title = agentBrowserJson<{title: string}>('get title');
|
||||
|
||||
@@ -246,11 +250,14 @@ npx playwright install chromium
|
||||
### 4. Wait for Server Startup
|
||||
|
||||
```typescript
|
||||
const server = new WebServer(PORT);
|
||||
const server = new WebServer(0, false, true); // port 0 (the OS picks one), no TLS, testMode
|
||||
await server.start();
|
||||
await new Promise(r => setTimeout(r, 1000)); // Allow server to stabilize
|
||||
const BASE_URL = `http://localhost:${server.boundPort}`;
|
||||
```
|
||||
|
||||
`boundPort` holds the real port only once `start()` has resolved. The `BASE_URL` used by the
|
||||
other snippets on this page is this one.
|
||||
|
||||
### 5. Clean Up Sessions
|
||||
|
||||
Track created sessions for cleanup:
|
||||
|
||||
@@ -51,6 +51,9 @@ interface CliEntry {
|
||||
// that ended waiting for workers it will resume from
|
||||
// .modelDetect?: { screenLine, screenLines? }
|
||||
// (where this CLI's own chrome names the model it runs: SessionState.displayModel)
|
||||
// .launchDefaults?: { [launchParam]: settingsKey }
|
||||
// (synced App Settings that seed a LOCAL launch's params the caller left unset;
|
||||
// codex's model and reasoning effort, via src/web/launch-defaults.ts)
|
||||
overlays: CliOverlays; // remote-SSH / Docker pane commands, credential store
|
||||
}
|
||||
```
|
||||
|
||||
@@ -124,7 +124,7 @@ environment is injected through socket-scoped `tmux setenv` rather than the comm
|
||||
Working and idle come from the screen: while a turn runs, OpenCode draws a small spinner at
|
||||
the start of its footer (`⬝■■■■■■⬝ esc interrupt`), and Codeman reads that to tell a working
|
||||
session from an idle one. A pending permission prompt shows as idle, since it is waiting on
|
||||
you. Before 1.36.0 an OpenCode session that had run a tool showed as working for good.
|
||||
you. Before 1.40.0 an OpenCode session that had run a tool showed as working for good.
|
||||
|
||||
Integration detail: [`docs/opencode-integration.md`](https://github.com/Ark0N/Codeman/blob/master/docs/opencode-integration.md).
|
||||
|
||||
@@ -164,7 +164,7 @@ only the CLI you spawned yourself.
|
||||
|
||||
Working and idle come from the screen: while a turn runs, Gemini CLI draws a spinner line
|
||||
(`⠦ Thinking... (esc to cancel, 6s)`) above its composer, and Codeman reads that. A tool
|
||||
confirmation that waits for you shows as idle. Before 1.36.0 a Gemini session showed as
|
||||
confirmation that waits for you shows as idle. Before 1.40.0 a Gemini session showed as
|
||||
working for good after its first turn.
|
||||
|
||||
### Antigravity
|
||||
@@ -190,7 +190,7 @@ Pi needs the opposite instincts from every other CLI here.
|
||||
once. They stay out.
|
||||
- **Work detection reads Pi's composer rule.** Pi has no prompt glyph; while a turn runs it
|
||||
puts a spinner into the rule above the composer (`── ⠏ Working ───`), and Codeman reads
|
||||
that to tell working from idle. Before 1.36.0 a Pi session that had started a turn showed
|
||||
that to tell working from idle. Before 1.40.0 a Pi session that had started a turn showed
|
||||
as working for good.
|
||||
|
||||
Guide: [`docs/pi-integration.md`](https://github.com/Ark0N/Codeman/blob/master/docs/pi-integration.md).
|
||||
@@ -246,7 +246,7 @@ flag from Codeman; change that in OMP's own config, not here.
|
||||
|
||||
OMP conversations appear in Past Sessions and can be resumed, and a respawn continues the
|
||||
same conversation with `--continue`. Codeman tells working from idle by reading OMP's status
|
||||
bar, where a spinner and the elapsed time replace the `π` while a turn runs. Before 1.36.0
|
||||
bar, where a spinner and the elapsed time replace the `π` while a turn runs. Before 1.40.0
|
||||
an OMP session that had started a turn showed as working for good.
|
||||
|
||||
Guide: [`docs/omp-integration.md`](https://github.com/Ark0N/Codeman/blob/master/docs/omp-integration.md).
|
||||
|
||||
@@ -62,7 +62,9 @@ codeman web # then open http://localhost:3000
|
||||
| Page | What it answers |
|
||||
| ------------------------------------------ | ---------------------------------------------------------- |
|
||||
| [The Dashboard](The-Dashboard) | What is the UI telling me? |
|
||||
| [Tile Grid](Tile-Grid) | How do I watch and drive several sessions side by side? |
|
||||
| [Agent CLIs](Agent-CLIs) | Which agent should this session run, and how do I set it up? |
|
||||
| [Custom Model Endpoints](Custom-Model-Endpoints) | How do I point a session at my own OpenAI-compatible endpoint? |
|
||||
| [Working With Files](Working-With-Files) | How do I read, edit, and attach files? |
|
||||
| [Input And Voice](Input-And-Voice) | How do I talk to an agent, including by voice? |
|
||||
| [Mobile Guide](Mobile-Guide) | How well does this work on a phone? |
|
||||
|
||||
@@ -10,7 +10,8 @@ never offered in a popped-out session window.
|
||||
## Turning it on
|
||||
|
||||
**App Settings → Header & Panels → Tiles.** This is a per-device setting, on by default
|
||||
everywhere except phones, and the button only appears in a window at least 1180px wide.
|
||||
on desktops and laptops and off on phones and tablets, and the button only appears in a
|
||||
window at least 1180px wide.
|
||||
It shows a **Tiles** button in the header, beside Split, and enables `Ctrl+Shift+G`.
|
||||
|
||||
## Opening a grid
|
||||
|
||||
Reference in New Issue
Block a user