maruzzella 0.1.1

GTK4 desktop shell prototype in Rust with persisted layouts and plugin-backed views.
Documentation
# Appearance API

Maruzzella now treats shell styling as a semantic API instead of a downstream CSS exercise.

The intended model is:

- downstream apps define or override named appearances in `ThemeSpec`
- shell specs reference those appearances by stable ids
- the shell maps those ids to GTK/CSS internally
- raw stylesheet overrides remain available as an escape hatch

## Two Layers

There are two cooperating layers.

### 1. Appearance Registry In `ThemeSpec`

`ThemeSpec` owns named appearance recipes for:

- surfaces
- buttons
- text
- inputs
- tab strips

The main builder methods are:

- `with_surface_appearance(id, SurfaceAppearance)`
- `with_button_appearance(id, ButtonAppearance)`
- `with_text_appearance(id, TextAppearance)`
- `with_input_appearance(id, InputAppearance)`
- `with_tab_strip_appearance(id, TabStripAppearance)`

This is where a downstream app defines what `primary`, `secondary`, `danger`, or `title` means for that product.

### 2. Appearance References In Shell Specs

The shell spec types reference those appearances by id:

- `ShellSpec`
- `TabGroupSpec`
- `TabSpec`
- `ToolbarItemSpec`

This is where a downstream app says which role a widget should use.

Example:

```rust
use maruzzella::{
    default_product_spec, ButtonAppearance, ButtonStyle, MaruzzellaConfig, SurfaceAppearance,
    SurfaceLevel, TextRole, ThemeSpec, Tone,
};

let theme = ThemeSpec::default()
    .with_surface_appearance(
        "secondary",
        SurfaceAppearance::new(Tone::Secondary, SurfaceLevel::Raised, TextRole::Body),
    )
    .with_button_appearance(
        "primary",
        ButtonAppearance::new(Tone::Accent, ButtonStyle::Solid, TextRole::BodyStrong),
    );

let mut product = default_product_spec();
product.layout.left_panel = product
    .layout
    .left_panel
    .clone()
    .with_panel_appearance("secondary")
    .with_panel_header_appearance("secondary")
    .with_tab_strip_appearance("utility");

let config = MaruzzellaConfig::new("com.example.my-app")
    .with_theme(theme)
    .with_product(product);
```

## Built-In Appearance Ids

These ids are available by default and may be overridden by downstream apps by reusing the same id.

### Surface ids

- `app-shell`
- `topbar`
- `menu`
- `toolbar`
- `status`
- `primary`
- `secondary`
- `tertiary`
- `workbench`
- `console`
- `inspector`

### Button ids

- `primary`
- `secondary`
- `ghost`
- `toolbar`
- `icon`
- `danger`

### Text ids

- `title`
- `subtitle`
- `body`
- `body-strong`
- `meta`
- `section-label`
- `tab-label`
- `code`

### Input ids

- `search`
- `command`
- `field`

### Tab strip ids

- `editor`
- `utility`
- `console`

## Which Specs Control What

### `ShellSpec`

Top-level shell appearance ids:

- `app_appearance_id`
- `topbar_appearance_id`
- `menu_appearance_id`
- `toolbar_appearance_id`
- `search_input_appearance_id`
- `status_appearance_id`
- `button_appearance_id`
- `text_appearance_id`

### `TabGroupSpec`

Per-group appearance ids:

- `panel_appearance_id`
- `panel_header_appearance_id`
- `tab_strip_appearance_id`
- `text_appearance_id`

Convenience builders:

- `with_panel_appearance(...)`
- `with_panel_header_appearance(...)`
- `with_tab_strip_appearance(...)`
- `with_text_appearance(...)`

### `TabSpec`

Per-tab content appearance:

- `text_appearance_id`
- `with_text_appearance(...)`

### `ToolbarItemSpec`

Per-item button appearance:

- `appearance_id`

## Typography

Typography is now standardized through text roles rather than scattered widget classes.

Common roles:

- `title`: main emphasis
- `subtitle`: secondary heading
- `body`: standard UI/body copy
- `body-strong`: emphasized UI/body copy
- `meta`: low-emphasis supporting text
- `section-label`: uppercase shell labels
- `tab-label`: tab text
- `code`: monospace content

Downstreams should prefer assigning a text role or overriding a built-in text id instead of changing label CSS directly.

## Buttons

Buttons are standardized through semantic button ids and shell-owned interaction states.

Common roles:

- `primary`: strongest action
- `secondary`: normal structured action
- `ghost`: low-chrome action
- `toolbar`: toolbar action
- `icon`: icon-only action
- `danger`: destructive action

Downstreams should set `ToolbarItemSpec.appearance_id` or override the corresponding button appearance in `ThemeSpec`.

## Escape Hatch

`ThemeSpec::with_stylesheet_path(...)` and `ThemeSpec::with_override(...)` still work.

Use them when:

- a product needs a shell treatment not yet expressible with the semantic API
- a legacy downstream app is still migrating
- the product wants a near-total visual rewrite

They are no longer the preferred downstream customization path.