feat: page padding, hiding, a Components section and a bookmarks list #7

Open
hutao wants to merge 30 commits from feat/components-padding into main
Owner

Summary

The page's layout and its settings, reworked so every part can be placed, hidden and found again:

  • Page padding: one setting, or horizontal and vertical when untied (Settings › General).
  • Pen boxes: the outline is drawn inside each part, so the menu's panel and the window's edge no longer clip a side, and the menu has a pen box of its own.
  • Hiding: every part and menu tab gets Hide, last in its form, added by CtPartEditor, so no component implements it; a component that exports neither widget nor tab is a building block and gets none. Hiding the toolbar warns that Ctrl+, still opens Settings.
  • Settings › Components: every part as a tree (the menu's tabs under Menu, in one box), each with a switch; a hidden menu greys its tabs without touching their own values.
  • Settings transitions: tabs slide sideways like the menu's; an editor slides up, back slides down; back returns to the screen a sub-screen came from.
  • Bookmarks: the grid in numbers (count, row height, gap) with a Rows/Columns switch, floating or docked, an optional coloured top line, a list to reorder them (drag or Alt+arrows) and switch each off. Older CSS-string settings are carried over (bookmarks.ts).
  • The menu fills the page when there are no bookmarks to show, with the clock at the page's dead centre.
  • Settings › Websites: user-defined sites above the catppuccin ones, each section switched all at once.
  • Defaults: the setup this was built on.
  • Also on the branch: the media tab's card around the cover and controls (78b0038).

Design and plan: docs/superpowers/specs/2026-09-30-components-padding-design.md, docs/superpowers/plans/2026-09-30-components-padding.md. The IDEAS.md entry notes the plugin system and an in-browser editor, left for later.

Testing

  • node --test tests/*.test.mjs: 6/6, including the bookmarks migration and the Tree Style Tab defaults.
  • Playwright (npm run --prefix extension test): 44/44, with tests for each interaction above; the one that flaked under load now passes 40 runs in a row.
  • cargo fmt --check, cargo clippy, svelte-check, the build, web-ext lint and the book build pass in the pre-commit and pre-push hooks, and tests/e2e-firefox.mjs in pre-push.
  • Loaded by hand in Floorp.

Checklist

  • cargo fmt --check and cargo clippy --all-targets -- -D warnings pass
  • web-ext lint --source-dir extension --self-hosted has no errors
  • The handbook (docs/src/), README and CHANGELOG match the change
  • Conventional-commit title (type(scope): ...)
## Summary The page's layout and its settings, reworked so every part can be placed, hidden and found again: - **Page padding**: one setting, or horizontal and vertical when untied (Settings › General). - **Pen boxes**: the outline is drawn inside each part, so the menu's panel and the window's edge no longer clip a side, and the menu has a pen box of its own. - **Hiding**: every part and menu tab gets *Hide*, last in its form, added by `CtPartEditor`, so no component implements it; a component that exports neither `widget` nor `tab` is a building block and gets none. Hiding the toolbar warns that Ctrl+, still opens Settings. - **Settings › Components**: every part as a tree (the menu's tabs under Menu, in one box), each with a switch; a hidden menu greys its tabs without touching their own values. - **Settings transitions**: tabs slide sideways like the menu's; an editor slides up, back slides down; back returns to the screen a sub-screen came from. - **Bookmarks**: the grid in numbers (count, row height, gap) with a Rows/Columns switch, floating or docked, an optional coloured top line, a list to reorder them (drag or Alt+arrows) and switch each off. Older CSS-string settings are carried over (`bookmarks.ts`). - **The menu fills the page** when there are no bookmarks to show, with the clock at the page's dead centre. - **Settings › Websites**: user-defined sites above the catppuccin ones, each section switched all at once. - **Defaults**: the setup this was built on. - Also on the branch: the media tab's card around the cover and controls (78b0038). Design and plan: `docs/superpowers/specs/2026-09-30-components-padding-design.md`, `docs/superpowers/plans/2026-09-30-components-padding.md`. The IDEAS.md entry notes the plugin system and an in-browser editor, left for later. ## Testing - `node --test tests/*.test.mjs`: 6/6, including the bookmarks migration and the Tree Style Tab defaults. - Playwright (`npm run --prefix extension test`): 44/44, with tests for each interaction above; the one that flaked under load now passes 40 runs in a row. - `cargo fmt --check`, `cargo clippy`, svelte-check, the build, `web-ext lint` and the book build pass in the pre-commit and pre-push hooks, and `tests/e2e-firefox.mjs` in pre-push. - Loaded by hand in Floorp. ## Checklist - [x] `cargo fmt --check` and `cargo clippy --all-targets -- -D warnings` pass - [x] `web-ext lint --source-dir extension --self-hosted` has no errors - [x] The handbook (`docs/src/`), README and CHANGELOG match the change - [x] Conventional-commit title (`type(scope): ...`)
feat: a card around the media tab's cover and controls
All checks were successful
CI / rust (push) Successful in 1m17s
CI / extension (push) Successful in 2m31s
Pages / pages (push) Successful in 52s
CI / nix (push) Successful in 5m26s
78b0038380
Side by side with the lyrics, the cover's column is wider than the cover,
so the space around it looked like stray padding. A card tinted with the
primary container, as wide as the cover plus its padding, makes that
space the card's: it reads well across schemes, light and dark.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The page's edges were fixed at 2.5rem horizontal, 1rem vertical
(px-10 pt-4). Padding is now a setting: tied (one slider, all four
sides) or split into Horizontal and Vertical, in Settings, General.
CtLayout reads it as --px/--pt/--pb, so a run-time value stays a CSS
variable rather than a Tailwind class per value.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The pen's outline used a positive outline-offset, so it sat outside the
part: the menu's panel clipped it and a bookmark on the window's edge cut
it off. Flip it inward (-outline-offset-2, marked with .ct-edit) so every
part keeps all four sides.

The menu had no pen box of its own; only its tabs and the toolbar did. It
now exports a WidgetInfo ("Menu", no fields, since there's nothing to edit
beyond opening it) and gets a box on the button while closed, and on the
whole panel while open (outside the tabs' clipping div, whose
overflow-hidden is now conditional on being open, since there's nothing to
clip while closed).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Every part of the page and every menu tab gets a Hide checkbox, last in
its own pen: WidgetInfo grows an optional hideHint (the toolbar warns
that Ctrl+, still opens Settings without it), and CtPartEditor appends
the checkbox to every form so no component has to opt in. Hiding the
menu takes its button and panel but leaves the clock in their place;
hiding the currently open tab moves the bar on to the next one shown.

widgets.ts also gains the placeables list Task 4 will read to build a
Settings > Components section.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The second Settings tab lists every placeable (ours, then yours), a
switch to show or hide it, and a row that opens its settings, which is
now the only way to a hidden part's or a covered tab's form. Notes an
idea for git-hosted plugins and an in-browser editor in IDEAS.md.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The grid's settings are numbers now (a flow switch, a count named for it,
row height and gap in rem) instead of raw CSS strings; bookmarkOptions()
migrates the old grid-template-columns/rows and "even rows" shape once,
on load. Placement is floating (the default, rounded like the menu with
the page's own bottom padding under it) or docked (flush with the
window's edge, dropping that padding). A top line, on by default, is
optional and its colour a setting, resolved the same way a tile's is.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Hidden bookmarks already left the menu alone; an empty list didn't.
CtLayout now derives a single `shown` guard (not hidden, and either
some items or edit mode for the pen box) and uses it for the grid
rows, the section's render and --pb, so the menu takes the whole
page either way while edit mode still gets a place to add the first
bookmark.

Also drops CHANGELOG's claim that the dense grid has "the same
effect as the Tiles preset" -- it states only what the code does.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Only the bookmarks' wrapper gets ct-edge-*; the clock isn't inside it and nothing reads its classes on the clock, and layout.ts's tree entry was missing bookmarks.ts.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Settings › Components was Ours/Yours; now it's one tree, the menu first
with its tabs indented under it (ours first, then a user's), then the
clock, the toolbar and the bookmarks. A user's component gets a small
"yours" label. A tab's switch greys and disables while its menu is
hidden, without touching the tab's own setting, and its row still opens
the tab's settings.

Same commit: the page's default padding drops from 2.5rem to 1.25rem on
every side (DEFAULTS.padding in store.svelte.ts), since 40px round the
page reads heavier than intended.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The six main tabs now slide sideways, mirroring the menu's own tabs;
opening a screen (the pen, a Components row, a bookmark) slides it up
over whatever was showing, and back slides it down again. Opening the
side panel itself, with nothing focused yet, still only plays its own
slide in from the edge: Svelte only intros a transition that's local to
a block created after its component has mounted, so a fresh mount with
a screen already focused plays none.

`edit()` gains an optional `back`, the focus a screen's back arrow
returns to; unset (nothing calls it with one yet) it goes to the main
settings, which is also where back goes when a pen opens one screen
while another is already open. Escape takes the same step. Both the
tab and the editor screen share the same crossing trick as CtMedia's
tabs, one grid cell, so the panel doesn't jump to the new one's height;
the leaving one gets aria-hidden, since Svelte's own `inert` on it
doesn't keep Playwright's role queries from also matching it while
both share the DOM for the fly.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Two Settings panel bugs surface once a screen opens over another and
steps back through more than one level, as the bookmarks' new list
does. First, since the tabs' own remount (an #if/:else branch swap
back to null focus, not a tab pick) still played its sideways fly,
back replayed a stale sideways motion on top of the intended vertical
one; a `noFly` flag now zeroes that one remount's duration. Second,
going back and forth quickly enough (list to a bookmark and back) can
reverse a block's outro into an intro on the same DOM node rather than
swap in a fresh one, and the aria-hidden an outro sets by hand never
got cleared on the way back in, leaving a screen's rows in the DOM but
invisible to role queries. `onintrostart` now clears it, on both the
focus block and the tabs' own.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Each bookmark gets its own on/off (hidden, default shown) and a new
Bookmarks screen in its settings, reached through a full-width `screen`
field CtField.svelte and fields.ts now support: one row per bookmark,
its glyph or letters, its name and a switch, reordered by dragging the
handle or Alt+Up/Down. Switching one off drops its tile from the page,
edit mode included, unless the bookmarks' own new "Show hidden
bookmarks while editing" is on, which dims it there instead with its
edit bar still working. With every bookmark off the menu takes the
whole page, same as having none. A row opens the same editor a tile's
own Edit does, back to the list; CtBookmarks' editItem is exported and
takes that back focus instead of being duplicated.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Settings: one `{#key focus}` now wraps the whole main-tabs/screen swap,
so opening a screen from Components (not just screen-to-screen) plays
its slide up, and back plays its slide down; the tabs' own sideways
key is freshly mounted on every such return, so the `noFly` latch it
used to need (and which stuck after the first back, killing every
later tab switch) is gone.

Bookmarks list: rows are now keyed by the bookmark's id instead of its
array position, so Alt+arrow and drag track the item, not the slot;
Firefox blurs a keyed-each row it physically reorders, so the row
refocuses itself once the reorder's reached the DOM. The drag handle
drops its false button semantics (Enter/Space did nothing) for
tabindex="-1", and a row now opens its bookmark from anywhere but the
handle and the switch, not only its name button.

Docs: plugins.md gets edit()'s `back` parameter and the `switch` field
type, both already shipped but undocumented.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Each root part (the menu, the clock, the toolbar, the bookmarks) now
draws its own rounded, bordered box in Settings > Components, the same
gap between every box; the menu's tabs share its box rather than
floating with no border of their own, and carry a primary line on
their left instead of the neutral one, so it reads as "belongs to
Menu" rather than "just indented".

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Settings > Websites now lists User-defined sites above
catppuccin/userstyles, each under its own heading; the filter still
narrows both. A heading's own checkbox switches every site in that
section on or off in one click, whether or not the filter is hiding
some of them (ticked when all are on, dashed when only some are); an
empty User-defined section hints that typing a name in the filter adds
one instead of showing nothing.

`indeterminate` is a DOM property, not an attribute, so it's kept in
sync through a small action rather than a template expression.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
CtMenu centred the clock in the space under the nav bar, so with the
bookmarks hidden or empty it sat half the bar's height above the
page's true centre. The clock's wrapper is now absolute inset-0 over
the whole (relative) section, padded top and bottom by the bar's own
measured height, so its centre is the section's centre regardless of
the bar. The layer is pointer-events-none and the clock itself
pointer-events-auto so the nav's buttons stay clickable underneath.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Ships the maintainer's own new-tab settings as the extension's
defaults, verbatim, in place of the placeholder ones: font Rubik,
padding, the Tiles-preset bookmarks grid down to its ten tiles and
their colours and glyphs (typos in a name and a custom site's URL
kept as they were), the menu opening on Media, GitHub's Private
activity off, the two custom site overrides (Forgejo, Navidrome),
and Tree Style Tab tinted from the wallpaper.

Two exceptions: menu.tabs.CtMedia.player keeps the tab's own default
("") rather than a session-specific MPRIS instance id, and clock's
legacy timeSize/dateSize (superseded by pt sizes) are dropped.

Most of the Playwright suite was written against the old defaults
(four shown bookmarks, the menu closed) as a convenient fixture for
testing unrelated behaviour; it now seeds that shape itself before
each test's first load, and a new test asserts the real defaults on
an untouched page.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
DEFAULTS.menu opened on Media and CtBookmarks' widget defaults hid
every bookmark: neither matches the maintainer's actual setup, just
what storage happened to hold at export time (menu open, on Media,
mid-browse). menu now starts closed on GitHub, and bookmarks default
to shown. complete() was merging DEFAULTS.bookmarks over a saved
object with no `hidden` key, so this also hid every upgrading user's
bookmarks; the widget-grid migration test's `hidden: false` seed was
masking exactly that, so it's reverted to prove the fix.

Also: the ten default bookmarks' glyphs as literal characters with an
`// nf-...` comment, matching the repo's other defaults instead of
`\uXXXX` escapes; and the CHANGELOG bullet now covers the clock,
background dim and Media cover defaults that shipped alongside these.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
onoutrostart only fires after the outro's delay and first frame, so
for a beat the leaving screen was still visible to a role query even
though it's already inert: Playwright's "Hide" checkbox lookup could
catch two of them and flake. A `leave` transition (fly plus setting
aria-hidden synchronously) replaces the out:fly + onoutrostart pair
at both sites, so the attribute lands the instant the outro starts.

Verified with `--repeat-each 40 --workers 6` on the affected test.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
fix: minor review nits, and two gaps in test coverage
All checks were successful
CI / rust (pull_request) Successful in 1m18s
CI / extension (pull_request) Successful in 3m20s
CI / nix (pull_request) Successful in 5m46s
f37916bae8
userstyles.ts's pasted SITES.overrides had quoted keys and one-level
indentation throughout; reformatted to the file's own style (unquoted
keys where they're valid identifiers, nested indentation matching the
rest of the object).

The Tree Style Tab migration test seeded colour/blur/x/y itself, so it
never proved tstOptions() fills them from TREE_STYLE_TAB; it now seeds
only source/strength and asserts against the real defaults. Added a
test for the Websites toggle-all's indeterminate state (one vendored
site off, not all): there wasn't one.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
style: Websites' sections in boxes, their switches on the right
All checks were successful
CI / rust (pull_request) Successful in 1m19s
CI / extension (pull_request) Successful in 3m19s
CI / nix (pull_request) Successful in 5m26s
ce54eac44f
Each section is a rounded box with a larger title, and every site's
checkbox sits at the row's end, as Components' switches do, so the two
sections read as separate.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Your own components get a section of their own, above ours, with the
note on where they go when there are none, like Websites' own sites.
Ours stay a tree of boxes under Predefined.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
style: tabs spread from edge to edge on every row
Some checks failed
CI / rust (pull_request) Successful in 1m18s
CI / extension (pull_request) Successful in 3m17s
CI / nix (pull_request) Has been cancelled
af9ab6b9c4
A wrapped row of tabs sat against the left, leaving a ragged gap on the
right; each row now starts and ends at the bar's edges.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
fix: Settings' tabs in columns, the middle ones centred on each other
Some checks failed
CI / rust (pull_request) Successful in 1m22s
CI / extension (pull_request) Failing after 3m16s
CI / nix (pull_request) Has been cancelled
8f95961a74
Spreading each wrapped row on its own left the middle tabs wherever
their widths put them, off-centre and out of line with the row above.
Equal grid columns put the first at the left edge, the last at the
right and the rest at the centre, on every row.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
style: Settings' tabs centred in their columns, evenly spaced
Some checks failed
CI / rust (pull_request) Successful in 1m20s
CI / extension (pull_request) Successful in 3m5s
CI / nix (pull_request) Has been cancelled
2a72e4f216
Every tab centres in its own equal column rather than the outer ones
hugging the edges, so the spacing is even and each column still lines
up from row to row. The test measures every tab in one frame, once the
panel has slid in.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
feat: Settings' tabs take as many columns as fit
All checks were successful
CI / rust (pull_request) Successful in 1m31s
CI / extension (pull_request) Successful in 3m15s
CI / nix (pull_request) Successful in 5m26s
72f77bf3ac
The columns were fixed at three. Now they're as many as the widest tab
fits, then as few as keep that many rows, so a narrow window gets two
columns of three rather than tabs spilling past the bar, and six never
split four and two.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
feat: section switches in Components, stars in Websites
Some checks failed
CI / rust (pull_request) Successful in 1m18s
CI / extension (pull_request) Failing after 3m34s
CI / nix (pull_request) Successful in 5m41s
6d7e3d29cd
Components' User-defined and Predefined titles get the switch Websites'
sections have, showing or hiding everything in them at once. Each site
gets a star: starred sites sort first in their section, then the rest,
each alphabetically.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
fix: a site's star left of its name, and the rows slide when it sorts
All checks were successful
CI / rust (pull_request) Successful in 1m16s
CI / extension (pull_request) Successful in 3m30s
CI / nix (pull_request) Successful in 5m42s
afc46c534e
The star sat between the name and the switch; it leads the row now. A
star re-sorts the section, and the rows slide to their new places rather
than jump there.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
All checks were successful
CI / rust (pull_request) Successful in 1m16s
CI / extension (pull_request) Successful in 3m30s
CI / nix (pull_request) Successful in 5m42s
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/components-padding:feat/components-padding
git switch feat/components-padding

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff feat/components-padding
git switch feat/components-padding
git rebase main
git switch main
git merge --ff-only feat/components-padding
git switch feat/components-padding
git rebase main
git switch main
git merge --no-ff feat/components-padding
git switch main
git merge --squash feat/components-padding
git switch main
git merge --ff-only feat/components-padding
git switch main
git merge feat/components-padding
git push origin main
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
hutao/caelestia-tab!7
No description provided.