mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 08:59:40 +02:00
docs: Pane B is a TerminalTile (reconnect, exactly-once input, focus)
CLAUDE.md, architecture-invariants#split-pane-sessions and the split-pane spec described Pane B as having no reconnect, seq-less input and xterm's own Ctrl+V. Updated for TerminalTile (terminal-tile.js, load order 7.4): reconnect and stop codes, the input-socket map and which input is kept out of the persisted queue, image paste, the geometry rules, and _focusedPane() with its Ctrl+W exception. The tile-grid spec now records PR 1 as built (no key handler factory; scheduleLoad and scrollback move to PR 2 with their first user). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -274,9 +274,9 @@ Codeman is a Claude Code session manager with web interface and autonomous Ralph
|
||||
|
||||
**Circuit breakers**: the Ralph breaker prevents respawn thrashing (`CLOSED` → `HALF_OPEN` → `OPEN`; reset via `/api/sessions/:id/ralph-circuit-breaker/reset`). **Distinct: the PTY-exit breaker** (`session-pty-exit-breaker.ts`) trips after repeated rapid PTY exits and blocks auto-restarts. ⚠️ It resets ONLY via an explicit `{clearBreaker:true}` body on `POST /api/sessions/:id/interactive`; the frontend's auto-reattach in `selectSession()` sends no body and must never clear it. → [architecture-invariants#circuit-breakers-ralph--pty-exit](docs/architecture-invariants.md#circuit-breakers-ralph-and-pty-exit)
|
||||
|
||||
**Full-scrollback replay**: `GET /api/sessions/:id/terminal?full=1` returns the whole tmux scrollback ALONE (`source='mux-full-history'`), superseding the byte buffer. First load of each non-shell TUI session requests it (`_fullHistoryLoaded`); Shell selection and drop recovery use a bounded 1 MiB `?tail=`, and a Shell scroll-to-top pulls a bounded `?full=1&tail=` window (a window no longer than the browser's buffer is skipped before the downgrade guard, so it never marks the session exhausted); the unbounded pull stays behind **Load full history**. A Shell split-pane Pane B has its own copy of the bounded pull against its own xterm (`SplitTerminalPane._pullHistory`, terminal-split.js); keep the two in step. → [architecture-invariants#split-pane-sessions](docs/architecture-invariants.md#split-pane-sessions) ⚠️ The capture ends with a RELATIVE cursor move back to the pane's caret (never `CUP`), so no line-deleting transform may run over it; those skips key on `isFullCapture`, never on `?full=1` alone. ⚠️ A re-pull must never shrink the buffer (`_replayWouldShrinkBuffer()`). ⚠️ `captureCols`/`captureRows` are absent when no frame was positioned: test `Number.isFinite`, never truthiness. ⚠️ A frame dropped at the 128 KiB render cap MUST be recovered, and the recovery verifies itself: `_scheduleDroppedOutputRecovery` re-arms (bounded by `DROP_RECOVERY_MAX_ATTEMPTS`) while `_onSessionNeedsRefresh` reports no repaint, but never after a capture-fetch `'deadline'`. → [architecture-invariants#full-scrollback-replay](docs/architecture-invariants.md#full-scrollback-replay)
|
||||
**Full-scrollback replay**: `GET /api/sessions/:id/terminal?full=1` returns the whole tmux scrollback ALONE (`source='mux-full-history'`), superseding the byte buffer. First load of each non-shell TUI session requests it (`_fullHistoryLoaded`); Shell selection and drop recovery use a bounded 1 MiB `?tail=`, and a Shell scroll-to-top pulls a bounded `?full=1&tail=` window (a window no longer than the browser's buffer is skipped before the downgrade guard, so it never marks the session exhausted); the unbounded pull stays behind **Load full history**. A Shell split-pane Pane B has its own copy of the bounded pull against its own xterm (`TerminalTile._pullHistory`, terminal-tile.js); keep the two in step. → [architecture-invariants#split-pane-sessions](docs/architecture-invariants.md#split-pane-sessions) ⚠️ The capture ends with a RELATIVE cursor move back to the pane's caret (never `CUP`), so no line-deleting transform may run over it; those skips key on `isFullCapture`, never on `?full=1` alone. ⚠️ A re-pull must never shrink the buffer (`_replayWouldShrinkBuffer()`). ⚠️ `captureCols`/`captureRows` are absent when no frame was positioned: test `Number.isFinite`, never truthiness. ⚠️ A frame dropped at the 128 KiB render cap MUST be recovered, and the recovery verifies itself: `_scheduleDroppedOutputRecovery` re-arms (bounded by `DROP_RECOVERY_MAX_ATTEMPTS`) while `_onSessionNeedsRefresh` reports no repaint, but never after a capture-fetch `'deadline'`. → [architecture-invariants#full-scrollback-replay](docs/architecture-invariants.md#full-scrollback-replay)
|
||||
|
||||
**Split-pane sessions** (`showSplitButton`, header button, default OFF, desktop-only, per-device): a second live session ("Pane B") beside the active one, in its own `SplitTerminalPane` (terminal-split.js) with its own xterm + WebSocket, resizable via a draggable divider. Deliberately plainer than the primary pane — no local-echo overlay, CJK IME, or touch handlers — and NOT persisted across reloads. → [architecture-invariants#split-pane-sessions](docs/architecture-invariants.md#split-pane-sessions)
|
||||
**Split-pane sessions** (`showSplitButton`, header button, default OFF, desktop-only, per-device): a second live session ("Pane B") beside the active one, in a `TerminalTile` (terminal-tile.js; the picker, divider and auto-collapse stay in terminal-split.js) with its own xterm + WebSocket, resizable via a draggable divider. Pane B reconnects after a drop, sends input through the exactly-once queue over its own socket (`_registerInputSocket`), has clickable paths and image paste, and owns its geometry (no 40x10 floor, `zc` columns adopted, font changes call `tile.fit()`). ⚠️ Only typed input enters that persisted queue: xterm's query replies are dropped and focus/mouse reports go out ephemeral. ⚠️ App-level terminal actions find their pane through `_focusedPane()` (the terminal focused last), never `this.terminal`; Ctrl+W deliberately still closes `activeSessionId`. Still plainer than the primary pane (no local-echo overlay, CJK IME or touch handlers) and NOT persisted across reloads. The planned tile grid reuses `TerminalTile` (`docs/tile-grid-plan.md`). → [architecture-invariants#split-pane-sessions](docs/architecture-invariants.md#split-pane-sessions)
|
||||
|
||||
**Terminal touch gestures: link taps and text selection**: on touch devices xterm's linkifier and SelectionService never see the gesture, so both are driven explicitly (terminal-ui.js). ⚠️ A tap activates the link under it through the SAME provider as the hover linkifier (`_terminalLinkAtPoint`), synchronously inside `touchend` (keeps the user gesture `window.open` needs) and BEFORE any mouse report; the caret's logical line (`_tapIsOnCaretLine`) and TUI-owned rows (`_isActionableMobileTerminalTap`) keep their meaning. ⚠️ Gate on the caret line, never on tap intent (a shell calls every tap `'input'`). ⚠️ Long-press selects via xterm's public `select()`; keep the three guards: suppress the compat mouse pair after `touchend`, the bounded focus guard + `contextmenu` suppression for the platform long-press, and no closing `terminal.focus()` on phones. Tests: `test/terminal-touch-tap.test.ts`. → [architecture-invariants#terminal-touch-gestures-link-taps-and-text-selection](docs/architecture-invariants.md#terminal-touch-gestures-link-taps-and-text-selection)
|
||||
|
||||
@@ -325,7 +325,7 @@ Codeman is a Claude Code session manager with web interface and autonomous Ralph
|
||||
|
||||
### Frontend
|
||||
|
||||
Frontend JS modules have `@fileoverview` with `@dependency`/`@loadorder` tags. Load order: `constants.js`(1) → `i18n.js`(1.5) → `mobile-handlers.js`(2) → `voice-input.js`(3) → `notification-manager.js`(4) → `keyboard-accessory.js`(5) → `input-cjk.js`(5.5) → `mobile-ime-preview.js`(5.52) → `terminal-keycode229-recovery.js`(5.55) → `sanitize-html.js`(5.6) → `tab-layout-browser.js`(5.9) → `app.js`(6) → `tab-rail-resize.js`(6.5) → `terminal-ui.js`(7) → `terminal-split.js`(7.5) → `respawn-ui.js`(8) → `ralph-panel.js`(9) → `orchestrator-panel.js`(9.5) → `cron-ui.js`(9.7) → `settings-ui.js`(10) → `panels-ui.js`(11) → `readmymind-ui.js`(11.3) → `ultracode-panel.js`(11.5) → `approvals-ui.js`(11.6) → `reboot-restore-ui.js`(11.65) → `admin-ui.js`(11.7) → `session-ui.js`(12) → `host-wake-ui.js`(12.2) → `webview-tabs.js`(12.5) → `mobile-overview.js`(12.55) → `home-sessions.js`(12.56) → `git-status-ui.js`(12.57) → `entrance-animations.js`(12.6) → `ralph-wizard.js`(13) → `api-client.js`(14) → `subagent-windows.js`(15) → `ultracode-windows.js`(15.5) → `session-lineage.js`(15.6) → `image-input.js`(16). `i18n.js` translates static + newly inserted application DOM while skipping terminal/response/file/user-name surfaces; `input-cjk.js` handles CJK IME composition via an always-visible textarea below the terminal (`window.cjkActive` blocks xterm's onData). `terminal-keycode229-recovery.js` forwards a committed `input` event that xterm's `_inputEvent` guard drops (Chrome-on-Android soft keyboards send `composed: true` after a keydown), and only when xterm emitted no canonical data for that keystroke. ⚠️ **That decision is settled at the NEXT keydown as well as on its own zero-delay timer** (#441): the drain runs from xterm's custom key handler, which fires BEFORE xterm processes that key, so a soft keyboard that commits the last character and sends Enter in one InputConnection transaction puts the character on the wire ahead of the `\r`. On the timer alone that character is not merely late, it is LOST: xterm emits the `\r` first and bumps the canonical counter past the candidate's snapshot, so the candidate stands down (measured, `hell\r` where the user typed `hello`). The trade is that a keydown decides with less evidence than the timer did, since xterm's own keyCode-229 rescue has not run yet; that is safe for Enter, which clears the textarea so the pending diff emits nothing. Ordering is pinned by `test/terminal-keycode229-recovery.browser.test.ts`, which the CI gate does NOT run. `mobile-ime-preview.js` (iOS WebKit only) paints the text an IME is composing: an iOS IME commit is routed into the local-echo overlay through the ordinary printable/paste branch and then `_transferMobileImeCommitToLocalEcho`, and without local echo the preview clears only on output parsed AFTER the commit (or its 2 s fallback). ⚠️ It watches keydown in the capture phase on `terminal.element`, never on the textarea, because xterm finalizes the composition and emits the commit in its own capture listener on the textarea.
|
||||
Frontend JS modules have `@fileoverview` with `@dependency`/`@loadorder` tags. Load order: `constants.js`(1) → `i18n.js`(1.5) → `mobile-handlers.js`(2) → `voice-input.js`(3) → `notification-manager.js`(4) → `keyboard-accessory.js`(5) → `input-cjk.js`(5.5) → `mobile-ime-preview.js`(5.52) → `terminal-keycode229-recovery.js`(5.55) → `sanitize-html.js`(5.6) → `tab-layout-browser.js`(5.9) → `app.js`(6) → `tab-rail-resize.js`(6.5) → `terminal-ui.js`(7) → `terminal-tile.js`(7.4) → `terminal-split.js`(7.5) → `respawn-ui.js`(8) → `ralph-panel.js`(9) → `orchestrator-panel.js`(9.5) → `cron-ui.js`(9.7) → `settings-ui.js`(10) → `panels-ui.js`(11) → `readmymind-ui.js`(11.3) → `ultracode-panel.js`(11.5) → `approvals-ui.js`(11.6) → `reboot-restore-ui.js`(11.65) → `admin-ui.js`(11.7) → `session-ui.js`(12) → `host-wake-ui.js`(12.2) → `webview-tabs.js`(12.5) → `mobile-overview.js`(12.55) → `home-sessions.js`(12.56) → `git-status-ui.js`(12.57) → `entrance-animations.js`(12.6) → `ralph-wizard.js`(13) → `api-client.js`(14) → `subagent-windows.js`(15) → `ultracode-windows.js`(15.5) → `session-lineage.js`(15.6) → `image-input.js`(16). `i18n.js` translates static + newly inserted application DOM while skipping terminal/response/file/user-name surfaces; `input-cjk.js` handles CJK IME composition via an always-visible textarea below the terminal (`window.cjkActive` blocks xterm's onData). `terminal-keycode229-recovery.js` forwards a committed `input` event that xterm's `_inputEvent` guard drops (Chrome-on-Android soft keyboards send `composed: true` after a keydown), and only when xterm emitted no canonical data for that keystroke. ⚠️ **That decision is settled at the NEXT keydown as well as on its own zero-delay timer** (#441): the drain runs from xterm's custom key handler, which fires BEFORE xterm processes that key, so a soft keyboard that commits the last character and sends Enter in one InputConnection transaction puts the character on the wire ahead of the `\r`. On the timer alone that character is not merely late, it is LOST: xterm emits the `\r` first and bumps the canonical counter past the candidate's snapshot, so the candidate stands down (measured, `hell\r` where the user typed `hello`). The trade is that a keydown decides with less evidence than the timer did, since xterm's own keyCode-229 rescue has not run yet; that is safe for Enter, which clears the textarea so the pending diff emits nothing. Ordering is pinned by `test/terminal-keycode229-recovery.browser.test.ts`, which the CI gate does NOT run. `mobile-ime-preview.js` (iOS WebKit only) paints the text an IME is composing: an iOS IME commit is routed into the local-echo overlay through the ordinary printable/paste branch and then `_transferMobileImeCommitToLocalEcho`, and without local echo the preview clears only on output parsed AFTER the commit (or its 2 s fallback). ⚠️ It watches keydown in the capture phase on `terminal.element`, never on the textarea, because xterm finalizes the composition and emits the commit in its own capture listener on the textarea.
|
||||
|
||||
**Entrance animations** (`entrance-animations.js`, all OFF by default): opt-in animations for tabs, terminal, windows and connection lines, chosen via `data-tab-anim` / `data-term-anim` / `data-win-anim` / `data-line-anim` on `<html>`; the default `legacy` theme short-circuits every hook. ⚠️ Tabs and lines are destroyed mid-animation on re-render, so re-apply to the fresh element by id with a negative `animation-delay` (resume, never restart). ⚠️ Terminal-pane styles may animate only transform / opacity / clip-path (anything else resizes the PTY via FitAddon); `blur` is the ONE sanctioned `filter` exception, do not generalise it. ⚠️ Line glow lives in `--line-glow` so blur keyframes interpolate. Persisted per-device in `codeman:*Anim` localStorage keys, never in `SettingsUpdateSchema`; lab at `?animlab=1`. Test: `test/entrance-animations.test.ts`. → [architecture-invariants#entrance-animations](docs/architecture-invariants.md#entrance-animations)
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -4,6 +4,13 @@
|
||||
**Author**: Claude (session with Tim), 2026-09-15
|
||||
**Scope**: v1 only. v2 items are named and explicitly deferred, not designed.
|
||||
|
||||
> **Update (tile grid, PR 1):** Pane B is now a `TerminalTile`
|
||||
> (`terminal-tile.js`) and is no longer as plain as this spec describes: it
|
||||
> reconnects after a drop, delivers input exactly once, has clickable paths
|
||||
> and image paste, sizes its PTY without a floor and adopts `zc` columns, and
|
||||
> the app-level terminal shortcuts follow the focused pane (Ctrl+W excepted).
|
||||
> See `docs/tile-grid-plan.md` and `architecture-invariants#split-pane-sessions`.
|
||||
|
||||
## Problem
|
||||
|
||||
Codeman's terminal area shows exactly one active session (pane) at a time —
|
||||
|
||||
+27
-21
@@ -1,6 +1,6 @@
|
||||
# Tile Grid: Design Spec
|
||||
|
||||
**Status**: Proposed, not implemented. Builds on `docs/split-pane-sessions-plan.md`; the split pane stays.
|
||||
**Status**: PR 1 (tile foundation) implemented on `feat/terminal-tile`, local only; PR 2 (the grid) proposed. Builds on `docs/split-pane-sessions-plan.md`; the split pane stays.
|
||||
**Author**: Claude (planning session with the maintainer), 2026-10-06
|
||||
**Branches**: PR 1 `feat/terminal-tile`, PR 2 `feat/tile-grid` stacked on it (worktree `claudeman-tiles`)
|
||||
**Scope**: v1 is fully designed here; follow-ups are named at the end and explicitly deferred.
|
||||
@@ -239,8 +239,9 @@ behavior listed under "Current architecture". The split orchestration stays in
|
||||
`terminal-split.js` and constructs a `TerminalTile` for Pane B; the grid (PR 2)
|
||||
constructs one per tile. New in the class:
|
||||
|
||||
**Reconnect.** Backoff 0.5 s, 1 s, 2 s, 4 s, 8 s, capped at 15 s, reset on a
|
||||
successful open. A reconnect is also kicked when SSE `handleInit` reports the
|
||||
**Reconnect.** The primary pane's backoff ladder (`CodemanWsReconnect`,
|
||||
constants.js: 0, 250 ms, 500 ms, ... capped at 10 s) plus up to 250 ms of
|
||||
jitter, the attempt count reset only by a successful open. A reconnect is also kicked when SSE `handleInit` reports the
|
||||
server is back. After every reopen the tile runs a bounded refresh (the same
|
||||
in-stream `\x1bc` clear plus replay as `_refreshBuffer`), because output
|
||||
frames carry no sequence number and a gap cannot be replayed otherwise. That
|
||||
@@ -252,7 +253,7 @@ prevent. Close codes that must NOT reconnect:
|
||||
| Code | Meaning | Tile does | Owner decides (via `onExit`) |
|
||||
|---|---|---|---|
|
||||
| 4009 | Session exited | Stops reconnecting, reports the code | PR 1 split: an "exited" marker in Pane B (the split's existing delete path collapses it if the session is removed). PR 2 grid: the Attach overlay |
|
||||
| 4003 / 4004 | Forbidden / session gone | Stops reconnecting, reports the code | PR 1 split: a "stopped" marker. PR 2 grid: removes the tile |
|
||||
| 4003 / 4004 | Forbidden / session gone | Stops reconnecting, reports the code (4003 is stopped by the tile itself: `CodemanWsReconnect` classes it as transient) | PR 1 split: a marker saying why. PR 2 grid: removes the tile |
|
||||
| 4010 | Superseded by a socket with the same cid | Stops, but only for the CURRENT socket (see below) | Same marker as 4003 |
|
||||
|
||||
The class never decides what happens to its container; it reports the close
|
||||
@@ -268,8 +269,11 @@ opening a replacement the tile detaches the old socket's handlers (as
|
||||
`close()`), and every handler checks `event.target === this.ws` and ignores
|
||||
events from any socket that is no longer current.
|
||||
|
||||
The marker text becomes `[disconnected, reconnecting…]` and keeps its
|
||||
"last thing on screen" rule.
|
||||
The marker text becomes `[disconnected, reconnecting…]` (a stop writes
|
||||
`[disconnected: <why>]`, `TerminalTile.STOP_MARKERS`) and keeps its "last
|
||||
thing on screen" rule. On reopen the closed state is cleared BEFORE the gap
|
||||
refresh, or the refresh re-owes the marker and stamps it under a healthy
|
||||
pane. `reconnectNow()` skips the backoff and never replaces an open socket.
|
||||
|
||||
**Client id on the upgrade URL.** `cid=${clientId}:${tabNonce}:tile`. The
|
||||
connection registry supersedes by cid PER SESSION, so a distinct suffix means a
|
||||
@@ -317,9 +321,9 @@ to a 1 MiB window, so a larger buffer only fills with live output over time.
|
||||
The shell history pull's "pane full" check reads `term.options.scrollback`, so
|
||||
it adapts to the lower cap unchanged.
|
||||
|
||||
**Key handler.** Pane B's `attachCustomKeyEventHandler` body is extracted into
|
||||
a shared factory, `createPaneKeyHandler({ terminal, getSessionId, getMode })`,
|
||||
used by every tile, plus:
|
||||
**Key handler.** Pane B's `attachCustomKeyEventHandler` stays in
|
||||
`TerminalTile` (every tile IS a `TerminalTile`, so no separate factory is
|
||||
needed), plus:
|
||||
|
||||
- Ctrl+V routes into the image-paste trap with this tile's terminal and session;
|
||||
- the new tile chords are swallowed (return false) so they never reach the PTY.
|
||||
@@ -508,7 +512,7 @@ run back to back on the event loop and stall every WS and SSE stream on the
|
||||
server for seconds.
|
||||
|
||||
So EVERY tile load goes through ONE grid-level client queue (PR 2, plugged in
|
||||
through the `scheduleLoad` option `TerminalTile` gets in PR 1): the initial load,
|
||||
through a `scheduleLoad` option PR 2 adds to `TerminalTile`): the initial load,
|
||||
the refresh after a reconnect, a server `{t:'r'}` refresh, and the shell
|
||||
history pull. No tile calls `fetch('/terminal…')` on its own. The tile's
|
||||
single-flight flag stays (it is what keeps one tile's replays from
|
||||
@@ -638,11 +642,9 @@ Commits:
|
||||
`TerminalTile` for Pane B. New in the class: reconnect with race-free socket
|
||||
replacement and the close-code table, the `:tile` cid suffix, reliable input
|
||||
through the socket map, `zc` handling with one geometry method, the
|
||||
file-path link provider, image paste, and the shared key handler factory.
|
||||
Constructor options make it reusable by PR 2 without changes:
|
||||
`scrollback` (the split passes `DEFAULT_SCROLLBACK`), `scheduleLoad`
|
||||
(default: run the load directly; PR 2 injects the grid queue), `onFocus`
|
||||
and `onExit` callbacks.
|
||||
file-path link provider and image paste, and an `onExit` callback. The
|
||||
`scrollback` and `scheduleLoad` options were deferred to PR 2, which
|
||||
introduces them together with the grid's load queue, their first user.
|
||||
3. **Split pane follows focus.** `_focusedPane()` returns Pane B while its
|
||||
xterm has focus, so Ctrl+L, Ctrl+Shift+R, voice and image paste act on the
|
||||
pane you are typing in. This removes most of the asymmetry the split-pane
|
||||
@@ -672,8 +674,8 @@ PR 1 tests (gate):
|
||||
(single-flight, marker-last including the async-parse fake, history pull),
|
||||
plus: reconnect backoff and each close code, a late `onclose` (4010) from a
|
||||
replaced socket is ignored and the tile keeps running, `zc` columns-only
|
||||
adoption, the cid suffix, input routed through `_sendInputAsync`, loads routed
|
||||
through an injected `scheduleLoad`.
|
||||
adoption, the cid suffix, input routed through `_sendInputAsync` (as built:
|
||||
`test/terminal-tile-input.test.ts`, which runs `connect()` for real).
|
||||
- `test/input-socket-map.test.ts`: acks routed to the right session's queue,
|
||||
redelivery per socket, POST fallback when no socket is registered.
|
||||
- `test/focused-pane-shortcuts.test.ts`: with Pane B focused, Ctrl+L clears
|
||||
@@ -689,9 +691,12 @@ PR 1 tests (gate):
|
||||
- Every existing `split-pane-*` test keeps passing (class name updated where it
|
||||
is referenced).
|
||||
|
||||
PR 1 browser tests: the existing `split-pane-*.browser.test.ts` files, plus
|
||||
Pane B reconnecting after a server restart and a click on a printed path in
|
||||
Pane B opening the file preview for Pane B's session.
|
||||
PR 1 browser tests: the existing `split-pane-*.browser.test.ts` files (they
|
||||
match master, one pre-existing environmental failure in both). Pane B
|
||||
reconnecting after a server restart and a click on a printed path opening
|
||||
Pane B's file preview are covered by unit tests and checked live on the beta
|
||||
instance rather than as browser tests (the harness cannot restart its own
|
||||
server).
|
||||
|
||||
PR 1 verification: a split with two real Claude sessions on the beta instance;
|
||||
restart the server mid-typing in Pane B (reconnect, refresh, no lost or doubled
|
||||
@@ -708,7 +713,8 @@ Commits:
|
||||
observer), the `selectSession` tile branch with the `auto` rule, the
|
||||
grid-aware `closeSession` fallback, focus rules, tile chords in the shortcut
|
||||
registry, the single grid-level load queue that every tile load goes
|
||||
through (injected as each tile's `scheduleLoad`), coexistence with the split.
|
||||
through (a new `scheduleLoad` option on `TerminalTile`, plus a `scrollback`
|
||||
option for `TILE_SCROLLBACK`), coexistence with the split.
|
||||
2. **Tile chrome and entry points.** Header (dot, name, menu, zoom, add,
|
||||
remove), the Attach overlay, picker, dividers, drag-a-tab, Ctrl/Cmd+click,
|
||||
"Open group as tiles".
|
||||
|
||||
Reference in New Issue
Block a user