Skip to content

Latest commit

 

History

History
134 lines (90 loc) · 11.5 KB

File metadata and controls

134 lines (90 loc) · 11.5 KB

Interface

Rill uses GPUI Kit for native input, selection, menus, dialogs, and scrolling. The app-owned design system in crates/rill-desktop/src/ui supplies reusable components, semantic colors, and layout. User workflows and shortcuts are documented in Using Rill; this guide defines the interface conventions for contributors.

Component ownership

Screens compose shared UI components and supply text, selected and disabled states, native state entities, and event handlers. Visual controls, colors, spacing, sizes, and type styles belong in ui.

Source under src/ui Owns
theme.rs and tokens.rs Light and dark colors, native theme synchronization, shared typography and geometry
controls.rs Buttons, navigation and save variants, checkboxes, labeled inputs and hints
typography.rs Copyable text, headings, labels, captions and feedback
layout.rs Forms, sections, action groups, records, cards, utility pages and settings layout
shell.rs Window chrome, sidebar, navigation and error banners
library.rs Scope selector, virtual article list and article rows
reader.rs Reader layout, toolbar, article typography and loading states
search.rs Search dialog, result rows, group headings and popup sizing

For example, a screen binds an operation to a shared action:

ui::fields()
    .child(self.field("folder", "Folder", "Optional."))
    .child(
        ui::action("subscribe", ActionStyle::Primary, cx)
            .label("Add feed")
            .disabled(self.busy)
            .on_click(cx.listener(|this, _, _, cx| this.submit_feed(cx))),
    )

Add a recipe to ui when a new visual treatment is needed, then use it from the screen. Keep numeric measurements in that recipe or tokens.rs; shared geometry such as article row height also serves session restoration. The design system accepts presentation data and callbacks, and has no dependency on Reader, the core engine, storage, or network operations.

GPUI Kit's styled controls retain their native input, focus, and menu behavior. Its base layer supplies text selection and modal behavior. Rill owns the recipes around those controls. Dependency bug fixes remain scoped to the existing vendor patches; ordinary customization belongs in ui.

Layout

The main window has a 340 px library sidebar beside the reader or a utility page. Hiding the sidebar removes its controls and article list while preserving selection. The minimum window is 980 × 640; the default is 1380 × 860.

Region Layout
Window controls A 48 px row owned by the sidebar while visible. Windowed macOS reserves space for native controls; fullscreen removes that inset.
Sidebar search Fills a 12 px horizontal inset and shows the platform shortcut.
View picker A native searchable Select below Search, beside Add feed. Views, folders, and feeds share the picker.
Article list The sidebar's only persistent scroll region, rendered by a native uniform list.
Article row An 8 px horizontal inset and 96 px allocation including the bottom gap. Source and age share a line; titles reserve two lines.
Sidebar footer Settings, Refresh, and Library actions while reading; a full-width Back to reading action on utility pages.
Reader A maximum content width of 680 px, with its own native scroll handle.
Utility page A maximum content width of 840 px and a separate native scroll handle.

With the sidebar hidden, window controls join the reader's action row. An empty reader retains the window controls alone. Utility pages keep a reachable back action beside those controls and show their title within the page.

The picker shows the committed scope while the popup is filtered. Read-count updates must not rebuild its choices or erase a query. Confirming the current scope preserves the reading position. Search navigation and session restoration synchronize the committed picker value.

Rows fill the available width and use stable element IDs derived from record IDs. Unread titles have a dot and semibold weight; selected rows use the shared selected surface. Stable IDs also keep identical labels in different rows from sharing selection or accessibility state.

Selecting another article resets reader scrolling. Opening a utility page or changing settings sections resets page scrolling. Opening search preserves both positions. Attach scroll ownership to the native handle; overflow_y_scrollbar() replaces the inner element ID.

Fields and record lists fill their container. Standalone actions align to the start and size to their labels. Show bulk feed actions when selection or feed health makes them useful. Use spacing to group sections before adding borders or cards.

Reading intent and feedback

Hover intent prepares article content without changing reading state. Opening an article follows the saved read-on-open preference. The reader keeps the title, metadata, and body's starting position anchored while content loads. A late response must preserve visible prose, selection, and newer reading-state choices. See Architecture for preparation and persistence behavior.

Navigation, search, and reading-state controls respond immediately. Use native pressed, hover, focus, and selected states. Article rows show press feedback and open on release through the native click handler, including cancellation when the pointer moves away.

A starred article uses a filled star in both the list and toolbar. Tooltips and accessible names describe the next action. Copying a URL shows a check in the same button for two seconds without changing its bounds. Repeated copying replaces the pending acknowledgment task. These interactions do not need a recurring repaint or animation loop.

Routine success belongs in the affected control. Recoverable errors appear near the relevant page and can be dismissed. Provider setup results stay in their settings section because the details help configure the connection.

Settings and drafts

Preferences use a fixed-width save control with Save changes, Saving, and Saved states. Editing enables saving; only a committed write produces Saved. Failed validation or persistence keeps the draft available. An older receipt cannot acknowledge newer edits or overwrite a reopened form.

Native input subscriptions track edits while the form is open. Rendering does not poll, compare, or serialize every field. Invoking Settings while it is already open preserves the draft and focus.

Briefing schedule and reader-client fields appear when their features are enabled. Hiding fields retains the in-memory draft. Disabling a feature preserves its last saved configuration, and hidden inactive fields do not block unrelated preference saves.

Rules and their editor live inside Settings. A new rule infers the active feed or folder, defaults to starring incoming articles, and is ready to save. Customize rule reveals optional scope, filter, and action controls. No filters means all new articles in scope; an explicitly empty text filter is invalid.

Preference and rule fields have distinct keys. Switching sections preserves both drafts. Saving captures the editor entity and revision, so its completion can close only the editor that submitted it. Cancel returns to the rules list. Back and Escape return to the existing reading context.

Search and focus

Search and commands share a native modal above the current page. It owns its query, selection, and focus. Library queries run on a worker and reject obsolete responses. If the palette opens before library loading finishes, it refreshes its results when the engine is ready.

The search field stays anchored as the panel grows to fit results within the window. Empty results use a compact panel. Result rows are 38 px high and full width; list measurement must use an existing row when the first nonempty group changes.

Nonempty queries put matching commands before articles. Results from the preceding query clear immediately. An empty query shows recent articles first; loading them may replace the initial selection but must preserve navigation already made by the user.

Arrow keys choose results, Enter opens them, and Tab stays inside the modal. Escape and the backdrop dismiss it and restore the previous focus. Background reading shortcuts remain inactive. Opening search leaves the current article and form drafts in place.

Reading commands use the Reader key context; global commands use Rill. Inputs, the palette, selects, menus, and popovers exclude reading shortcuts. Page navigation keys operate the visible page's scroll handle outside these controls. When a settings section removes its focused input, focus returns to the reader root.

Typography and color

New windows inherit the active theme while their library loads. The first process window uses native appearance until saved preferences arrive. Loading and empty-library states are distinct.

macOS uses the system sans-serif family; Linux uses the bundled Noto Sans. ui/tokens.rs owns shared type sizes. ui/theme.rs synchronizes application colors with native inputs, menus, selection, and base components.

Role Size Treatment
Page heading 28 px Semibold
Article heading 30 px Semibold, 1.2 line height
Article body 18 px default User-adjustable, 1.6 line height
Article row title 15 px Medium; semibold when unread
Navigation and controls 14 px Regular
Summaries and reader source 13 px Secondary color
List source, timestamp, count 12 px Secondary color
Token Light Dark
Background #fcfcfd #151619
Sidebar #f4f4f5 #1d1e22
Floating surface #ffffff #242529
Text #242428 #ededef
Secondary text #62646c #a1a3ab
Selected row #e5e5e9 #35363c
Hover #ececef #2b2c31
Control outline #7b7d86 #81838c
Text selection #00000022 #ffffff30

Use ui::button with ActionStyle::Quiet for unobtrusive controls, or ui::action for a standalone action that sizes to its label. Their hover and selected states follow the shared theme. Control outlines have a separate token from decorative dividers.

Color changes must preserve at least 4.5:1 contrast for normal text and 3:1 for controls and focus indicators. Check default, hovered, selected, and floating surfaces in both themes. Rich-text selection paints over glyphs, so its token must remain translucent. Calculate selected-text contrast after compositing the selection color over both foreground and background.

Native text and accessibility

Informational plain text uses ui::text and the design system's text roles, backed by GPUI's SelectableText and TextSelectionHandle. Keyed element state retains the handle's selection-change repaint subscription so highlights update during a drag. GPUI owns hit testing, selection geometry, scrolling, and clipboard actions.

Article bodies use native rich-text selection. Selection must remain readable and copyable across title, author, and body text. Give icon buttons explicit names and list options explicit state. Keep copyable article text available as text.

The native root coordinates palette focus, input, selection, and clipboard handling. Dependency changes must preserve the released-pointer auto-scroll guard and parsing-completion behavior described in Dependency patches. Use the native checks for keyboard, pointer, screenshot, and assistive-technology coverage.