plugin_catalog.rs was 516 lines after the marketplace redesign. Move
the editor's pick card, the featured plugin selector, and the shared
action_button helper into plugin_editors_pick.rs so the catalog file
stays under the 500-line per-module budget.
splits.rs had grown to 551 lines after adding the design's pane header.
Move the header renderer, the close glyph, the URL helpers, and the
compact-canvas placeholder into chrome::split_pane so splits.rs is back
to a 422-line action + render orchestrator and the new helpers stay
under their own 150-line file.
Behaviour preserved: the pane click still selects the tab, the close
glyph still selects+closes, and pane_host/path/secure still derive from
the same UrlText fields.
The marketplace landing now matches the design's two-row hero (serif
"Quiet tools. Everyday magic." headline + native-to-ELY tagline on the
left, an Editor's Pick card on the right that promotes the first
enabled plugin or, when none are installed, an empty-state CTA), a row
of category chips with sandbox audit count, and a 4-column card grid.
Each plugin card uses real manifest data (name, author, description,
permission counts, sandbox flag) and routes to ely://plugin/{id} on
click. Empty state surfaces an honest "drop a signed .rplug" message
plus the install action. Nothing on the page is fabricated catalog data.
Sync now opens with the design's two-column structure: a serif headline
and Cloudflare Sync status pill on the left with a local-queue card
showing pending/failed counts and a reset action, and a "What syncs"
card on the right that lists every SyncObjectKind with a state dot,
local count, and a working policy toggle.
The connection label is honest: until Better Auth integration ships, it
reads "Local-only · sign-in coming soon" rather than displaying a fake
sign-in form. All toggles dispatch real shell.set_sync_object_policy
calls so the state changes are persisted through the existing core API.
Settings now opens with the design's two-column layout: a 232 wide nav
column on the left with grouped sections (General, Account, Power,
About) and a content column on the right that leads with a serif
headline, snapshot metric cards, and a "Jump in" list of every settings
route. Each nav entry and jump row deep-links to the existing internal
page so the destinations remain real.
The previous settings.rs is now a thin shim that calls into the chrome
module — keeps the module under 500 lines and lets future
sub-page redesigns share the same shell layout.
Whenever the command query starts with the existing '>' prefix the shell
now overlays a 640-wide floating panel — backdrop dim, glass card, three
ranked sections (Open tabs from snapshot.tabs, History from
snapshot.history_entries, and a fixed action set for workspace + key
internal routes), and a footer with the design's keyboard hints. Each
result is a real navigation hop; selecting a row dispatches the
appropriate shell action and clears the command query so the overlay
dismisses without leaving stale chrome state.
The overlay reads from the live BrowserSnapshot rather than fabricating
results, and matches case-insensitively against title and URL. Empty
queries surface the most recent items so the panel stays useful even
before the user types.
Each split pane now opens with the design's titlebar layout: an accent
status dot, a lock/globe glyph keyed off the URL scheme, the host in
ink-1 + the path in ink-4, and a close action on the right that selects
and closes the pane's tab. The body sits below a 1px hairline on the
panel cream so the panes feel like miniature browser windows instead of
borderless cards.
The pane click target stays on the outer container so clicking anywhere
in a pane focuses it without intercepting the close glyph.
render.rs was 525 lines because it carried both the shell orchestrator
and every sidebar row helper. The expanded sidebar (favorites, pinned,
spaces, tabs, archive sections plus the profile footer and the row
helpers) now lives in chrome::sidebar, where panel/soft shadow tokens
also moved. render.rs is back to a 150-line orchestrator that only
composes wallpaper, sidebar, topbar, and content area.
Behaviour preserved: render_tab_row stays available for tab_groups via
pub(crate) on impl ElyShell, and all existing on_action listeners and
keyboard captures remain wired in render_browser.
Home now ends with the design's two-card recap row: a "Continue where
you left off" card on the left (history list + reading-list cover that
opens the latest item, falling back to the reading list page when the
list is empty) and a "Recent activity" card on the right that shows
visited entries with relative time labels and a "View all history" link.
The previous open-tabs list is dropped from home — the sidebar already
exposes tabs and the design home doesn't repeat them. Source data is
real history_entries / reading_list off the snapshot; nothing is
fabricated. The home page is split into chrome/home/{hero, favorites,
recap, section, style, time}.rs so each file stays under 500 lines, and
internal_pages/new_tab.rs is now a thin shim that calls into it.
The home page now opens with the design's three-stage hero — a sun-icon
greeting row that picks morning/afternoon/evening from the system clock
and addresses the active profile by name, the serif headline, then a
54-tall search pill with an arrow chip and three contextual pills below
(Search Tabs, Switch Workspace, Open History).
Greeting derives "morning/afternoon/evening" from a UTC hour bucket,
which matches the design's 1500x940 dawn artboard well enough without
pulling in a tz-aware crate. Pure helper covered by unit tests so the
phase boundaries don't drift unintentionally.
The favourites grid is widened to 7 columns to match the design and
gains a plus-tile that routes to ely://bookmarks. Tabs section keeps the
existing data flow with a chevron section label to match the design's
collapsible affordance.
Move topbar rendering into chrome::topbar so it does not bloat render.rs.
The omnibar now ends in two glass chips (filters + favorite star) inline
with the address input, matching the design's pill layout. The toolbar
right cluster is rebuilt as copy-url, downloads, theme, and menu icons:
- Copy chip writes the active tab's URL to the system clipboard.
- Downloads opens ely://downloads (existing internal route).
- Theme chip routes to ely://settings/appearance for now (a Light/Dark
toggle still has to live in appearance settings before it can be a
one-shot).
- Menu opens ely://settings.
The pin/new-tab buttons are removed; they were not part of the design's
topbar. Pinning still works through the favorite chip + sidebar
selection state, and Cmd+T continues to create tabs.
The design's sidebar opens with an ELY title row plus a three-part
workspace picker: a layout-grid tile (opens spaces settings), a glass
pill showing the active space's emoji + name (cycles to next space on
click), and a plus button (opens spaces settings). The previous header
was a static title + space-name label that did not match the design.
Cycle behaviour reuses the existing select_next_space core API to avoid
introducing a new state flow; the existing SelectNextSpace shortcut
delegates through the same helper.
Designs call for a radial-gradient dawn (cream base + pink upper-right
glow + blue lower-left glow). GPUI 0.2.2 only supports linear gradients,
so the wallpaper is composed of a cream base layer with two diagonal
linear gradients that fade to transparent at ~60% — the perceived effect
matches the design's softness without needing radial-gradient support.
Wallpaper is isolated to chrome::wallpaper so future appearance themes
(violet, mint, slate) extend the enum without touching the shell.
The flat white background is the root visual difference from the
design reference. Replace it with a warm-to-cool diagonal linear
gradient (pink-beige to lavender-blue). Add BoxShadow to both
sidebar and main pane panels for floating depth. Use warmer panel
background (88% white) instead of 62% transparent. Add soft shadows
to active nav items and workspace tiles. Avatar uses gradient
matching the design reference.
Hero heading at 64px, search bar at 54px height with 14px radius
and card-like background (85% white), favorites grid at 7 columns
with 16px card radius matching the design spec.
Without continuous MouseMove events, Servo never updates hover state:
no cursor changes over links, no :hover CSS effects, no mouseenter
JavaScript events. The web page appears completely non-interactive.
Track mouse position from GPUI on_mouse_move through the full IPC
pipeline to Servo. Hover position is included with each ensure
request so Servo updates hover state at frame rate (~60fps).
Split panes use rounded corners, glass backgrounds, and accent dot
indicators matching the design reference. Web surface error page
uses centered layout with proper typography. Remove opaque white
backgrounds in favor of transparent glass surfaces.
Enter, Backspace, Tab, Escape, Delete, arrow keys, Home, End,
PageUp, PageDown now forward to Servo. Previously only printable
characters reached the web engine, making forms unusable.