The win7 skin is frameless so 7.css can draw the Aero caption, and on GTK that
means undecorated, which takes the window manager's resize borders and corner
grips with it. All three win7 windows were completely unresizable.
electrobun 1.18.1 offers nothing to re-enable, verified against the shipped
binary rather than assumed: libNativeWrapper exports startWindowMove/
stopWindowMove with no resize counterpart, imports gtk_window_begin_move_drag
but not gtk_window_begin_resize_drag, dragRegions.ts implements moving only,
and BrowserWindow has neither setResizable nor min-size options. But the
window is still programmatically resizable, so only the input affordance is
missing: eight handles overlay the frame and drag it through a new
windowGetFrame/windowSetFrame pair on the shared windowControlHandlers factory,
which gives all three window kinds resize with no per-window wiring.
Three things that would have made this quietly wrong:
- dragRegions.ts starts a window MOVE from a mousedown listener on document,
never checks defaultPrevented, and is not suppressed by a pointerdown
handler either (the compatibility mousedown still fires). Every handle
carries app-region-no-drag, which short-circuits its check first.
- deltas come from screenX, not clientX: a north/west resize moves the window
origin under a stationary pointer, so client coords feed that back into the
next delta and the window runs away.
- the min-size clamp re-anchors the opposite edge; clamping width alone lets x
keep tracking the pointer, walking the window off-screen while it looks
frozen.
This is a stopgap and the point is that it is removable on sight. The frame
math is a dependency-free module so scripts/resize-math-probe.ts asserts all
eight edges without a window (verified it catches the unanchored-clamp bug:
x jumps to 5100 instead of parking at 460). scripts/check-electrobun-resize.ts
scans the installed electrobun for the primitives this is waiting on and prints
the removal checklist when they appear; it runs from postinstall so a version
bump surfaces it, never fails an install, and is silent otherwise. Both trigger
paths were simulated. The component header carries the full checklist; docs
cover the platform consequence and the dragRegions traps.
Harness diffs exactly 0 in both themes; hover and menu probes still clean.
Not yet exercised with a real pointer — that needs a live window.
Co-Authored-By: Claude <noreply@anthropic.com>
All 14 components (including title-bar/, which has no modern counterpart)
move under app/skins/7like/. git records every one as a rename — no content
changed except main.ts.
The @core alias from stage 1 paid off exactly as intended: not one of the 67
service imports needed touching despite every file changing depth. The only
relative imports left inside the tree are skin-internal (app -> its dialogs,
connection-manager -> title-bar), and those survived because the move
preserved their relative structure.
Added skins/7like/index.ts exporting SEVEN_ROOTS { main, manager, editor }.
Every skin will export that same shape, so main.ts can pick a tree by skin
and then a root by APP_MODE without knowing either skin's internals. main.ts
selects from that barrel now, with the second axis still hardcoded until the
shadlike tree lands.
Verified: tsc clean, build:ui clean, opaque interior pixel-identical to the
pre-refactor baseline. That last check also proves Tailwind still scans the
templates from their new skins/** paths — a silent utility purge would have
collapsed the layout rather than reproducing it exactly.
The three structural 7.css contracts that a careless move breaks silently are
intact by construction, since no markup changed: app-title-bar is still a
direct child of .window.w7-frame, nothing new nests inside menu[role=tablist],
and the context menu still emits ul[role=menu] > li[role=menuitem] > button.
Co-Authored-By: Claude <noreply@anthropic.com>