Skip to content

layout: a document tab dragged out floats in a workspace of its own - #584

Merged
foxnne merged 1 commit into
mainfrom
layout/document-tab-float
Oct 19, 2026
Merged

foxnne merged 1 commit into
mainfrom
layout/document-tab-float

Conversation

@foxnne

@foxnne foxnne commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

Part of #260 (step 5 of plans/WORKSPACES_PLAN.md: floats and tear-off).

What changes

Behind FIZZY_WORKSPACES=1, a document tab can float. It floats inside a workspace of its own, so the new window is a tabbed area of documents holding just that one.

  • Torn off over the desktop (where floats are OS windows, macOS today): a tab let go over no window of the app's opens a float there, holding a new workspace with that document. Before, nothing happened: a document could not float.
  • Dropped on its own pane's middle: the same, the float opening over the pane. The drop's middle shows the float icon there, as it does for a view over its own place.
  • A workspace's only tab carries the workspace (State.Documents.alone, from layout: a document lands in any place as a view would #462): the workspace itself floats, rather than a new one being made round the document and the old one closing behind it. Already alone in a float nobody split, it doesn't float again; the pane takes the drop.
  • Its float closes when the workspace empties (its last tab closed or carried out), as any float its last view leaves.
  • Closing the float gives its documents to the main window's workspace: the one the person was in, when that is in the main window, else the first there. They go in after its own tabs, in order, and the one the float showed is shown. The emptied workspace then goes. With no workspace in the main window, it goes home as a view, as before.

How: State.Documents gets one more member, fold(id) -> bool, which ViewDrag.sendHome asks before sending a closing float's view home. ViewDrag.floatDocument floats the view a document carries alone (carriedView), or a new one (houseView). A new workspace floats out of the pane its photograph was taken in, at that pane's size.

With the flag off, documents is null, and nothing changes.

SDK impact

  • None. State.Documents is the app framework's (app/layout), not the plugin boundary.

Verified

  • zig build, zig build test, zig build test-integration, zig build check-web, zig build test-sdk-version, zig build check-tests-reached, scripts/check-fmt.sh.
  • New integration test, "float: a document tab dropped on its own pane's middle floats in a view holding it". It covers one tab of two floating in a new view, a lone tab floating its workspace, no second float for one already alone, and closing the float folding instead of going home.
  • macOS, a sandbox profile run with FIZZY_WORKSPACES=1, driven through the agent plugin (fizzy_act), each checked by fizzy_screen and a capture:
    • tear-c.txt, one of two tabs, dropped on its own pane's middle: Float 1 opens over the pane in an OS window. It holds a new workspace (Pane 3) with only tear-c.txt, and the pane keeps tt-b.pixi.
    • Closing tear-c.txt in the float: the float closes, and the main window is unchanged.
    • tt-a.pixi, its workspace's only tab, dropped on its own pane's middle: that workspace (Pane 4) floats, and the emptied half of Main closes into the other.
  • macOS, with the tear-out fixes since merged into a local build (backend: tear-off works again, and a window keeps its size out and back #591, backend: floats are OS windows again on macOS #592, backend: dvui's Debug window tears off too #594): a tab carried past the window's edge opens its own window, holding a new workspace with just that document (Float 1, Pane 6, tear-d.txt).
  • Review: fold copied the workspace's tab list but not the id strings in it, then emptied the workspace, and State.setAssignment frees those strings. It now copies the ids too. The gates pass on the branch merged with current main.
  • macOS, still to see with a real mouse: a tab torn off over the desktop (my run was interrupted by a person's input), and a float closed from its OS close button folding into the main workspace (the integration test covers the layout side).
  • Windows:
  • Linux:
  • Web: builds; the flag is never on there.

Follow-ups

  • A window from ⌘N (step 8) stays open when it empties. The float needs to remember which kind it is then.

🤖 Generated with Claude Code

@foxnne foxnne mentioned this pull request Oct 18, 2026
2 of 3 tasks
foxnne added a commit that referenced this pull request Oct 18, 2026
Part of the multi-window plan (fizzyedit/plans#26): the tear-off (#577,
#591).

**The bug** (the user, on `run-dvui`): dvui's Debug window, opened from
the demo window, never tore off; the demo window itself did. Diagnosed
by the session working on #584.

**Why:**
- dvui draws its Debug window, dialogs and toasts late, in
`Window.drawRetained`.
- The backend called that in `os_windows.afterFrame` only after the
tear-off had looked for the floating window being dragged (`tearFrame`),
and only while OS windows were open.
- So when the tear-off looked, the Debug window hadn't been drawn this
frame (its subwindow not `used`), and hadn't been noted as able to close
(`closable`, which `floatingWindowOs` notes as dvui begins it). It was
never the dragged window, and never tore off. Anything else dvui draws
in `drawRetained` was the same.

**The fix:** with tear-off on (`viewports.tears`), `afterFrame` draws
the retained layer first. `drawRetained` keeps to once a frame, and
`Window.end` would have drawn it at this point anyway, so dialogs and
toasts are drawn no differently.

## SDK impact

- [x] None

## Verified

- [x] macOS (arm64, Zig 0.17.0): `scripts/check-fmt.sh`, `zig build`,
`zig build test`, `zig build check-tests-reached`, `zig build
check-web`, `zig build test-sdk-version` pass. `zig build
test-integration` passes. Its first run aborted in `automation service:
tape bytes in, a ticket back, the outcome by ticket` ("replaying to 4860
ms did not reach what playing did"); a rerun with no change passed. That
test runs on dvui's testing backend, which never reaches this code
(`viewports.tears` is false there): flaky under load, not this change.
- [ ] By hand on `run-dvui`: open the Debug window from the demo and
drag it out.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Behind FIZZY_WORKSPACES=1, a document tab torn off over the desktop, or
dropped on its own pane's middle, floats in a new workspace holding just
it; a workspace's only tab floats the workspace itself. The float closes
when the workspace empties, and closed from its header gives its documents
to the main window's workspace (State.Documents.fold) instead of going
home as a view.

Part of #260 (step 5 of plans/WORKSPACES_PLAN.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne
foxnne force-pushed the layout/document-tab-float branch from be36cd2 to 81070ae Compare October 19, 2026 01:14
@foxnne
foxnne marked this pull request as ready for review October 19, 2026 01:14
@foxnne
foxnne enabled auto-merge (squash) October 19, 2026 01:14
@foxnne
foxnne merged commit ba4388a into main Oct 19, 2026
11 checks passed
@foxnne
foxnne deleted the layout/document-tab-float branch October 19, 2026 01:33
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