Repository navigation
Widgets: let a floating card grow to fit content that arrives after its size was saved #988
Description
Split out of #913 (case 1 in my comment there), since #914 handles repositioning and this is about height.
What happens
A floating widget keeps the exact rectangle the user last saved, and its body scrolls (.os-widgets__card-body { overflow-y: auto }). When the widget's content grows later, the new rows land below the fold of the card. Controls at the bottom (a "Sweep now" button, a "Check for updates" button) and any line that only appears when something needs attention are hidden until the user notices and drags the card taller.
On a six-card desktop this happens with any status card:
- a status card that adds "1 watch due" or "Credential refused" only when it applies;
- a queue card that adds a gap warning;
- a provenance card that lists failing documents while a sweep runs.
These are exactly the lines a user most needs to see, and they are the ones the saved height hides.
Docked cards don't have this problem: without a persisted height they size to their content. Floating cards have no equivalent, and a widget has no way to ask for more room (WidgetContext is { id, pluginUrl, storage }, and wp.os.widgets exposes only redock).
Proposal
An opt-in on the widget definition. It is off by default, so no existing widget changes.
- Registration:
openstation_register_widget( $id, array( 'fit_content' => true ) )in PHP andfitContent: trueon the JSWidgetDef, carried through the existing server payload likemaxHeight. - Behavior, for a floating card that opts in:
- When its content needs more height than the user's saved height, the card grows to fit.
- It is capped at
maxHeightwhen set, otherwise at the bottom of the work area, where a resize already stops (above the dock pill). - When the content shrinks, it shrinks back, never below the height the user set.
- Width and position never change.
- Persistence: none. The saved geometry stays the user's, so the grown height is never written to
desktop-mode-widgets-geometry. A user resize sets a new floor, and a drag saves the user's height, not the grown one. - Docked cards: a docked card with a persisted height follows the same rule. One without a persisted height already sizes naturally.
Out of scope, and how it meets #914
Moving the cards below a grown card out of its way is a layout question, and it fits with the reflow in #914 rather than here.
Because the grown height is not persisted, #914's grouping, which reads stored geometry, keeps seeing the user's heights. A grown card can therefore overlap a card directly below it until the content shrinks or the user moves one of them. I'd rather leave that visible than write geometry outside a user gesture. Happy to discuss either direction.
Questions for you
- Is the name right?
fitContentreads as what it does.autoHeightis the other candidate. - Should the cap default to the work-area bottom or to
maxHeightonly, so a widget that sets neither never grows? - Should the growth be animated? I'd say no, matching the resize handle.
I have this working on a branch in my fork, with tests and docs: frame-local logic in frame.ts, staying out of the files #914 touches. I'll open the PR once you're happy with the shape.
Engineered with Claude as a pair programmer.
Activity
juanlentino commentedon Oct 12, 2026
A related bug on the same code path, now fixed in #989 (e8dd5d5): the card is
box-sizing: content-boxwith a 1 px border, andcurrentGeometry()insrc/widgets/frame.ts(:661-662at trunk 572ce21) savesoffsetWidth/offsetHeight, which are the border-box size. So every drag end and resize end stores the size plus 2 px, and the next apply draws it 2 px larger. The resize start (:564), the liberate snapshot (:420,:435) and the docked height (:618) read the border box the same way. Seen live: an inline width of 364 px against anoffsetWidthof 366, with every stored width even and well above the registered 312. The PR now saves the content box, with a test that round-trips save, apply, save.