Skip to content

delete the native terminal tab strip, so it cannot come back on a refresh - #159

Merged
pythonlearner1025 merged 5 commits into
mainfrom
delete-legacy-tab-system
Sep 1, 2026
Merged

delete the native terminal tab strip, so it cannot come back on a refresh#159
pythonlearner1025 merged 5 commits into
mainfrom
delete-legacy-tab-system

Conversation

@pythonlearner1025

Copy link
Copy Markdown
Member

The panels-a and panels-b QA lanes confirmed seven rows on a real box. This
branch corrects all seven. Five are approved fixes. Two are user rulings.

Seam patch 10 — the side panel file surfaces

Three of these four rows are defects in the upstream desktop branch. Every hunk
is additive. Each row is one upstream PR.

  • BUG-1 (major). Ctrl+P and Cmd+P did nothing on a desktop session. The page
    mounted {fileQuickOpenDialog} only in the mobile return. The key handler ran
    and prevented the default, but no dialog existed. The dialog now mounts at the
    desktop root, beside the other portalled dialogs.
  • BUG-2 (major). "Files unavailable" was terminal. requestKey holds the
    identity of the acquisition effect, and a reconnect moves no input in that
    key. The failure therefore outlived the outage. The hook now takes
    reloadNonce and returns reload. It re-arms on an offline-to-online edge of
    the owning machine. The panel also draws the same "Try again" that the
    local-error branch had.
  • SP28 (minor). The desktop file viewer had no way to copy the path. It now
    draws "Copy file path". MobileFileViewerDrawer carried this control from the
    start, under the same key.
  • SP26-LSP (minor, USER RULING). Go to Definition and Find References are
    hidden for v1. A box runs no language service, so both entries answered every
    identifier with "Host language service does not support this file". The gate
    sits on the two addAction calls, not on their callbacks. An action with no
    callback still sits in the context menu and does nothing.

SP26 uses the shape of seam patch 7. Each level takes one optional prop, and
every prop defaults to the behaviour of today. One new v1-scope.ts field
controls all of them: languageService.

vendor/lody/BLITZ-PATCHES.md declares every hunk. PR #157 took the number 9
while this branch was open, so this patch is 10. The merge drill of seam patch 1
now counts both: FIFTEEN diverged files after patch 9, and TWENTY after this one.

Three fixes outside the vendor tree

  • BUG-3 (minor). A collapsed side panel is 0 wide, so the two shrink-0
    controls of the tab strip overflowed past the right window edge. QA measured
    "Add panel" at cx=1911 and "Show sidebar" at cx=1943 on a 1920 screen. One
    declaration in blitz-skin.css sends that overflow to the left. The
    declaration can move nothing while the strip has room, because the scroll area
    beside those controls is flex-1. The reachable duplicate at cx=1844 is the
    header toolbar copy, which upstream draws only while collapsed.
  • SP23-I18N (minor). sessions.fileSave.conflictDetail interpolates
    {{conflict}}. No call site supplies that variable, and none can. The
    save-conflict banner therefore printed the raw placeholder.
  • SP21-KEY (minor, USER RULING). sessions.fileViewer.save.withShortcut
    advertised "Save (⌘S / Ctrl+S)". This surface mounts no dispatcher for that
    chord. The title now names the action alone. The command layer stays
    unmounted.

packages/webapp/src/lody/i18n.ts overrides both strings, so neither fix is a
vendor edit. Each test also asserts that the vendored string still holds the
defect. An upstream correction therefore fails a test, and the override is
deleted rather than left in place.

Tests

packages/webapp/test/lody-panel-fixes.test.tsx pins all seven rows. It mounts
and drives the real vendored file-tree panel and side-panel strip. It runs both
strings through the real i18next instance. It pins at the source what needs a
daemon. Both rulings have their own test: no Cmd+S advert, and the LSP entries
off behind the v1-scope flag. No test was deleted.

Gates

npm run typecheck passes. npm run lint:gate passes: 74 anti-slop findings,
all at baseline, and 0 blitz-house findings. BLITZDEV_MANAGED=1 npm test gives
792 passed and 0 assertion failures. One file, lody-post-signin-turn.test.ts,
cannot boot its harness on this box: the box runs its own lody start daemon,
which holds port 17789 permanently. That is the flake the harness names in its
own error text, and it is unrelated to this branch.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UhdxNAYjV97JTv2pddcJiN

pythonlearner1025 and others added 5 commits September 1, 2026 21:29
plans/LODY-TERMINAL-TABS.md §4.6 named this "PR 2 — the deletion". A
terminal is a tab of the session strip; the native strip was the second
tab system item 4 forbids, and it kept coming back.

TWO LEAKS, NOT ONE.

1. The BOOT WINDOW. `lodySurfaceMounts` needs a box that answered
   `present`, and the capability probe starts at `probing` on every cold
   load — up to 7.5 s of retries. The panes filled that window with the
   native strip and then swapped it for the session strip. That is the
   flash on refresh.

2. The BARE `/workspaces/:id`, which is the permanent half. That address
   is `chat === null`, where the surface is hidden and the panes owned
   the tabs. `useLodyRail` only sent a workspace to the landing when its
   document held ZERO tabs, so a returning member with tabs stayed on the
   native strip for good — every bookmark, every workspace switch and
   every `closeChat()` landed there.

Deleted: `WebAppHeader` (the strip, the `+`, rename and the row context
menu), its per-region mount in `WorkPanes`, the tab drag-and-drop
(`use-workspace-tab-drag.ts`, `workspace-drag.ts`, `moveTab`, `splitTab`,
`otherRegion`) whose only handle was a draggable cell in that strip, and
every stylesheet rule that drew them.

Replaced with:

- `shell/PaneChrome.tsx`. The boot window gets a state of its own — a
  tab-strip skeleton in the shape `WebAppLoadingShell` already uses — and
  it carries the mobile nav-drawer button, which was a child of the
  deleted header and is not a tab control.
- The workspace ROOT resolves into the chat plane wherever the session
  strip draws the tabs, so no address is left without one.
- A close on the rail's terminal rows. The strip's `×` was the only close
  there was, and mobile and a box with no session plane never draw the
  session strip — without this a member could open a terminal and never
  end its tmux session.

`terminalFirstWorkspaceTabs()` goes too: it seeded exactly the tabs only
the deleted strip could draw into a fresh workspace on a pre-Lody image.
That box now gets the rail's "Sessions need a newer machine" notice,
which is the honest answer. Nothing stored is rewritten.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhdxNAYjV97JTv2pddcJiN
Type-level fallout only: the rail hook lost `tabCount` and
`onLegacyDefaultTabs` and gained `surfaceHostsTabs`; `WorkPanes` lost its
strip props; `SessionRail` and `ShellNav` gained the row close.

`lody-old-box-fallback.test.tsx` changes MEANING, not just shape: it
asserted that a box with no session plane keeps the native pane strip,
which was §4.6's rule and is exactly what "PR 2" reverses. It now asserts
the opposite — no strip anywhere — and that a fresh workspace on such a
box seeds no legacy tabs.

`workspace-panes.test.ts` loses the cases that exercised `moveTab`,
`splitTab` and the drag geometry, which are deleted. The one behaviour
worth keeping from them — the split collapses when the main pane empties
— is rewritten against a hand-built document.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhdxNAYjV97JTv2pddcJiN
`legacy-tab-strip-deleted.test.ts` is the source pin, on the precedent
`lody-lazy-boundary.test.ts` set: no module imports the deleted modules,
names their DOM markers or their symbols, and no stylesheet still draws
them. A render test can only prove the strip did not appear in the cases
somebody thought to mount, and both leaks were cases nobody had mounted —
this one cannot be satisfied by a gate or a branch, only by the code being
gone. It also pins what must SURVIVE: the drawer's segment strip shares
the tab-cell chrome, and a class-name sweep is exactly how that gets
deleted by accident.

`lody-terminal-tab-wave3.test.tsx` gets the behavioural half. F2's answer
changed with the product — it asserted the panes draw a strip where the
surface is hidden, and now asserts the workspace root resolves into the
chat plane instead — and a new case holds the probe open to prove the boot
window draws a skeleton and never the old strip.

Two neighbouring cases followed the same rule rather than being weakened:
ADJ1 loses its "with the panes up" arm because there is no pane address
left to read, and F7's inertness loop states the root's normalisation
instead of hiding it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhdxNAYjV97JTv2pddcJiN
`WebAppHeader`'s `×` was the whole of `closeTtydSession`'s reach into the
UI. Mobile and a box with no session plane never draw the session strip,
so deleting the native one left them able to open a terminal and never
end its tmux session.

The row becomes a `div` wrapping `.shell-s__open`, because a button
cannot nest inside a button. The row's OWN geometry stays on `.shell-s`:
`lody-blitz-theme.test.ts` pins it equal to the vendored row's, and the
two have to read as one list rather than two components.

On a touch device there is no hover to reveal the close with, and that
rail IS the tab list there, so it is always drawn and finger-sized.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhdxNAYjV97JTv2pddcJiN
`shell-smoke.test.tsx` runs with the flag OFF, where the native strip was
the whole tab UI. Its subjects survive — persistence, pane retention, the
rail's own record, the mobile sheet — so each case is re-driven through
the control that survives rather than dropped: a rail row selects, the
row's `×` closes, and the rail's `+` spawns.

Rail rows gain `data-session-id`, so a test can address one the way it
addressed a tab cell.

Two cases are REMOVED, both because their subject is deleted and neither
has any other: the managed-session context menu (rename), and dragging a
tab into the other pane. Every other case changed its selector, not its
claim.

`lody-rail-defaults`, `lody-rail-interactions` and `lody-tab-selection-sync`
now state which layout they describe. `surfaceHostsTabs: false` is "the
panes own the view", which is exactly what those files are about and is
the only place `closeChat` still means anything.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhdxNAYjV97JTv2pddcJiN
@pythonlearner1025
pythonlearner1025 merged commit c19c649 into main Sep 1, 2026
10 checks passed
pythonlearner1025 added a commit that referenced this pull request Sep 1, 2026
One conflict, in `webapp-workspace.css`, where two deletions met:

- main's #159 removed the pane tab strips and their drag chrome
  (`.webapp-pane-strip`, `.webapp-tab-cell--dragging`, `.webapp-tab-insert`,
  `.webapp-pane-drop`), and `test/legacy-tab-strip-deleted.test.ts` now pins
  their absence.
- the redesign removed all seven `.webapp-statusline--degraded` rules; four
  copies merged away on their own, and these three conflicted only because
  they sit against the block #159 deleted. Nothing renders the class on
  either side.

The resolution takes both deletions and keeps main's note about where the
split chrome went. Braces balance; the media query still closes where it did.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UhdxNAYjV97JTv2pddcJiN
pythonlearner1025 added a commit that referenced this pull request Sep 4, 2026
Brings the quick-action strip up to date with main after #159, #164,
#171, #173, #181, #183, #197 and #207. Thirteen files conflicted; the
resolutions, so a reviewer can check them one by one:

- `WebAppHeader.tsx`: deleted with the native tab strip on main (#159);
  the PR's edits to it are moot. Its two exports moved where main put
  them (`NewTabMenu`, `SessionTypeIcon`).
- `CloudApp.tsx`: main's strip deletion wins over the PR's drag wiring
  (`useWorkspaceTabDrag`, `moveTab`, `splitTab`, the drag ghost);
  the PR's side-panel binding, Connections host tab and mobile panel
  sheet stay; main's `workspace-chat-memory` stays. `filesHostRegion`,
  `ShareToDriveDialog` and `FolderAttachmentView` went with the Files
  pane the PR retires — their only callers were inside it.
- `router.tsx`: the PR's side-panel hooks sit above main's mobile early
  return, because hooks must.
- `SessionRailSidebar.tsx`, `SessionRail.tsx`: main's rows with the
  close button, minus the `filePath` the PR removed with file tabs.
- `lody-surface-tabs.test.tsx`: main moved the seam pin into
  `lody-seam-pin.test.ts`; the PR's anchors for the three new files and
  the four session-detail lines are ported there.
- `shell-smoke.test.tsx`: main's "the split is a placement" assertions,
  driven by the Connections icon the PR's strip draws; the drag test
  goes with the drag.
- `workspace-panes.test.ts`: `moveTab` is gone, so the backgrounded
  panel case builds its side-pane document by hand.
- `SessionTypeIcon.tsx`: `WebAppTabModel.panel` narrows to
  `'connections'`, which is the one panel the PR leaves.

Main already used seam numbers 10 and 11 for other patches, so the PR's
two seams are 19 and 20 everywhere: the ledger, the seam-pin test, the
baselines README, the upstream PR drafts and every code comment. The
merge runbook's file table keeps the PR's rows with the new numbers and
now says plainly that the ledger is the authority for the count.

Gates on the merged tree: webapp typecheck, lint gate, and the full
webapp vitest run (113 files, 948 tests) pass.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant