Skip to content

feat(ui): floating widget windows (shell capability), enable for Image Map - #39

Open
lstein wants to merge 1 commit into
feat/image-map-05-widgetfrom
feat/image-map-06-floating
Open

feat(ui): floating widget windows (shell capability), enable for Image Map#39
lstein wants to merge 1 commit into
feat/image-map-05-widgetfrom
feat/image-map-06-floating

Conversation

@lstein

@lstein lstein commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

PR 6 of the Image Map stack. Adds a generic floating-window layer to the workbench shell — any widget can opt in via manifest.allowFloating; the Image Map is the first user.

  • State: Project.floatingWidgets maps instance ids to geometry, mode (windowed/maximized/shaded), z-order, and the region to dock back into. Additive + optional, so persisted projects hydrate unchanged; the field round-trips through project persistence and is captured/restored with widgetRegions in undo snapshots (they are one placement fact).
  • Reducer actions: floatWidget (detaches from its region, repairs the active instance), dockFloatingWidget, setFloatingWidgetGeometry (min-size clamped), setFloatingWidgetMode, focusFloatingWidget. Invariant: an instance never renders docked and floating at once — placing a floating instance into a region implicitly docks it, and applying a layout preset docks all floating widgets.
  • Window chrome: fixed-position Chakra window with title-bar drag and corner resize (raw-pointer commit-on-release, main button only), shade/maximize/dock buttons, double-click-to-shade (button-safe), and the standard widget renderer (bare) as its body. CSS clamp() keeps a grabbable sliver on-screen even for geometry persisted on a larger display. z-index derives from stacking rank, not the persisted monotonic counter, so long-lived projects can never climb past the UI library's layer tokens.
  • "Float Window" lives in the shared widget actions menu, gated on the manifest flag.

An adversarial review of this diff was run; all confirmed findings are fixed with regression tests: preset-apply/undo double-render and widget-loss holes, off-screen stranding, z-token decay, double-click-on-button shading, non-main-button drags, plus the lint failures the review caught (my earlier lint invocation masked a failing format step — now verified by exit code).

Known accepted gaps (documented for reviewers): floating bodies don't participate in focus-region/hotkey attribution (irrelevant for Image Map, which registers no hotkeys — flagged for future floatable widgets); mid-drag unmount leaves window listeners until the next global pointerup (self-healing).

Testing

16 reducer/helper tests: float/dock round-trips, active-instance repair, geometry clamps, mode/no-op semantics, z-rank raising, preset-docking, undo consistency (widget is exactly one of docked/floating), viewport clamp math. Full suite: 885+ tests, pnpm lint + pnpm build pass.

🤖 Generated with Claude Code

@lstein
lstein requested a review from blessedcoolant as a code owner August 2, 2026 21:29
@github-actions github-actions Bot added the python label Aug 2, 2026
@lstein
lstein marked this pull request as draft August 2, 2026 22:23
@lstein
lstein force-pushed the feat/image-map-05-widget branch from 0da5cc5 to 5fb4304 Compare August 2, 2026 22:38
@lstein
lstein force-pushed the feat/image-map-06-floating branch 2 times, most recently from 035de15 to d36b46d Compare August 3, 2026 01:38
@lstein
lstein force-pushed the feat/image-map-05-widget branch 2 times, most recently from 599acac to 72b3041 Compare August 3, 2026 02:26
@lstein
lstein force-pushed the feat/image-map-06-floating branch from d36b46d to 0a64ade Compare August 3, 2026 02:26
@lstein
lstein marked this pull request as ready for review August 3, 2026 18:28
@lstein
lstein force-pushed the feat/image-map-06-floating branch from 0a64ade to a58e9c7 Compare August 4, 2026 01:43
@lstein
lstein force-pushed the feat/image-map-05-widget branch from 72b3041 to 8030e09 Compare August 4, 2026 01:43
@lstein
lstein force-pushed the feat/image-map-06-floating branch from a58e9c7 to d7e56a7 Compare August 4, 2026 05:37
@lstein
lstein force-pushed the feat/image-map-05-widget branch from 8030e09 to 0be308f Compare August 4, 2026 05:37
@lstein
lstein force-pushed the feat/image-map-06-floating branch from d7e56a7 to 925649a Compare August 4, 2026 14:58
@lstein
lstein force-pushed the feat/image-map-05-widget branch 2 times, most recently from 8f0e63b to e4f4132 Compare August 4, 2026 22:32
@lstein
lstein force-pushed the feat/image-map-06-floating branch 2 times, most recently from 08aa934 to ef19bf0 Compare August 5, 2026 00:31
@lstein
lstein force-pushed the feat/image-map-05-widget branch from e4f4132 to 408ef0c Compare August 5, 2026 00:31
@lstein
lstein force-pushed the feat/image-map-06-floating branch from ef19bf0 to 8666f99 Compare August 5, 2026 00:38
@lstein
lstein force-pushed the feat/image-map-05-widget branch from 408ef0c to 7259190 Compare August 5, 2026 00:38
@lstein
lstein force-pushed the feat/image-map-06-floating branch from 8666f99 to dc89f69 Compare August 5, 2026 01:18
@lstein
lstein force-pushed the feat/image-map-05-widget branch from 7259190 to 3726790 Compare August 5, 2026 01:18
…e Map

Adds a generic floating-window layer to the workbench shell. Any widget can
opt in via `manifest.allowFloating`; the Image Map is the first user.

- New per-project state: `floatingWidgets` maps instance ids to geometry,
  mode (windowed/maximized/shaded), z-order, and the region to dock back
  into. Additive and optional, so persisted projects hydrate unchanged.
- Reducer actions: floatWidget (detaches from its region and repairs the
  active instance), dockFloatingWidget (returns it as the region's active
  widget), setFloatingWidgetGeometry (min-size clamped),
  setFloatingWidgetMode, focusFloatingWidget (raise). Placing a floating
  instance back into a region via openRegionWidget implicitly docks it so
  an instance can never render twice.
- FloatingWidgetWindow: fixed-position chrome with title-bar drag and
  corner resize (same raw-pointer commit-on-release pattern as the panel
  resize handles), shade/maximize/dock buttons, double-click-to-shade,
  viewport clamping so a window can never be stranded off-screen, and the
  standard widget renderer (bare, chrome-less) as its body.
- "Float Window" lives in the shared widget actions menu, gated on the
  manifest flag.

Amended during the rebase onto the re-architected webv2:

- Types moved from the deleted `workbench/types.ts` into the split contract
  files: `FloatingWidgetMode`/`FloatingWidgetState` live in
  `layoutContracts.ts`, `allowFloating` on `WidgetManifest` and the
  'floating' member of `WorkbenchRegion` in `widgetContracts.ts`, and
  `Project.floatingWidgets` + `ProjectUndoSnapshot.floatingWidgets` in
  `projectContracts.ts`. `WidgetRegion` stays untouched — floating
  instances are tracked solely in `Project.floatingWidgets`.
- The reducer action union is private on main, so the five floating actions
  join `WorkbenchReducerAction` in `workbenchState.ts` and are surfaced as
  mechanical commands in the `widgets` namespace of `createCommands()`:
  `widgets.float`, `widgets.dockFloating`, `widgets.focusFloating`,
  `widgets.setFloatingGeometry`, `widgets.setFloatingMode`. All components
  now call `useWorkbenchCommands()` instead of the removed
  `useWorkbenchDispatch`.
- The three feature-local structural mirrors of `WorkbenchRegion`
  (gallery's `GalleryWidgetProps`, queue's view props, workflow's
  `WorkflowRegion`) gained 'floating', as their drift-check design demands.
- `FloatingWidgetWindow` resolves the widget via
  `useWorkbenchWidgetRegistry()`, renders through main's existing
  `WidgetRendererById`, titles via `resolveWidgetInstanceLabel`, uses
  `IconButton`/`Tooltip` from `@platform/ui`, adopts the AbortController
  pointer-session idiom from `WidgetFrames.tsx`, and takes all chrome
  strings from new i18n keys under `widgets.floating` (en.json).
- `FloatingWidgetLayer` lazy-loads the window chrome (`React.lazy`) so it
  stays out of the shell's eager path; the layer itself mounts eagerly in
  the rewritten `WorkbenchShell` after `StatusBar`, inside the DndContext.
- Reducer tests rewritten against the `workbenchState.testing` facade
  (mirroring `workbenchState.test.ts`) with the renamed 'compose' preset;
  the widget-command mocks in `createWidgetRuntime.test.ts` and
  `widgetPlacementCommands.test.ts` implement the five new commands.
- `floatingWindows.ts` registered in `workbenchOwnershipManifest.json`
  rootFiles; architecture and browser performance baselines recaptured for
  the intended new eager bytes (commands + layer mount; window chrome stays
  lazy).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lstein
lstein force-pushed the feat/image-map-06-floating branch from dc89f69 to b3136de Compare August 5, 2026 01:21
@lstein
lstein force-pushed the feat/image-map-05-widget branch from 3726790 to 43542e6 Compare August 5, 2026 01:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant