# AI and Agent Guide

Quick-start for AI coding agents working with SLT.

## Core mental model

- SLT is immediate mode: the closure is the app.
- Application state usually lives in normal Rust variables or structs.
- Widget-local persistent state uses `use_state()` / `use_memo()`.
- Interactive widgets usually return `Response`.
- Layout is mostly `row()`, `col()`, `container()`, `scrollable()`.

## Reading order

1. `README.md`
2. `docs/QUICK_START.md`
3. `docs/WIDGETS.md`
4. `docs/PATTERNS.md`
5. `docs/EXAMPLES.md`
6. `docs/ARCHITECTURE.md` for internals

## Questions agents get wrong

### "What's the difference between text().bold() and styled()?"

`text().bold()` chains after the last text command. `styled()` applies a pre-built `Style` in one call. Use `styled()` when reusing styles across widgets.

### "I called ui.bg() but the container background didn't change"

`ui.bg()` modifies the LAST TEXT element, not the container. For container background, use `container().bg(color).col(...)`.

### "How do I animate something?"

Animation types (`Tween`, `Spring`, `Keyframes`, `Sequence`, `Stagger`) are standalone structs, not Context methods. Compute values with `tick()` and pass them to style/layout methods. See `docs/ANIMATION.md`.

### "Where are the color palettes?"

Use `palette::tailwind::{BLUE, RED, ...}.c500` for Tailwind-style colors. See `docs/THEMING.md`.

### "What's the difference between line() and row()?"

Both are horizontal. `row()` is a full layout container returning `Response`. `line()` is for inline rich text returning `&mut Self` with zero gap between children.

### "How do I use _colored variants?"

Pass `WidgetColors::new().fg(color).bg(color)` as the last argument. See `docs/THEMING.md`.

### "Why does my hook panic?"

`use_state()` and `use_memo()` must be called in the same order every frame. Never put them inside conditionals.

Exception (v0.19.0): `ui.use_state_named(id)` is the id-keyed variant and IS safe inside `if`/`match` branches because it keys by the supplied `&'static str` instead of call order. Reach for it when you genuinely need a hook in a conditional path.

```rust
// Wrong: order-based hook inside a conditional drifts the call order.
if expanded {
    let count = ui.use_state(|| 0); // BAD — frame N has it, frame N+1 may not.
}

// Right: id-keyed variant is safe inside conditionals.
if expanded {
    let count = ui.use_state_named::<i32>("sidebar.count"); // OK
}
```

The original `use_state` / `use_memo` order rule still holds — only the id-keyed `*_named` variants opt out of it.

### "How do I handle keyboard shortcuts?"

Use `key(c)`, `key_code(code)`, or `key_mod(c, mods)`. For modal-aware shortcuts use the regular versions. For global shortcuts that bypass modals, use `raw_key_code()` or `raw_key_mod()`. For key sequence detection use `key_seq("gg")`.

### "How do I make a custom widget?"

Implement the `Widget` trait with `type Response` and `fn ui(&mut self, ctx: &mut Context) -> Self::Response`. Use `register_focusable()` for keyboard support and `interaction()` for click/hover.

### "Can I use SLT without crossterm?"

Yes, for custom backends with `Backend`, `AppState`, and `frame()`.

### "Should I create an app struct?"

Only if it helps. SLT does not require one.

### "Should every widget return Response?"

No. Built-in interactive widgets usually do. Custom widgets can return `()`, `bool`, or `Response`.

### "Why is hover/focus data weird on the first frame?"

Because layout feedback often uses previous-frame data in immediate-mode UI.
For the frame timeline and prev-frame rect rules, see [Previous Frame Guide](PREVIOUS_FRAME_GUIDE.md).

## Implementation rules for agents

- Prefer `ui.container().p(1).col(...)` over inventing new layout patterns.
- Prefer existing `*State` types over custom ad-hoc input structs.
- Use `Response.changed` / `.clicked` instead of inventing parallel booleans.
- Use `register_focusable()` and `interaction()` in custom widgets.
- Respect hook ordering rules.
- Animation types are standalone structs, not Context methods — compute values separately.
- Use `palette::tailwind` colors instead of hardcoding RGB values.
- Check `docs/FEATURES.md` before using feature-gated APIs.

### Context injection: stop threading shared state through every render fn

When a value is *read* by many nested render functions (theme, current tick, current user, toast bus), do not thread `&theme`, `&tick`, `&mut toasts` parameters through every signature. Use `ui.provide(value, |ui| ...)` once near the root, then have nested code read it back with `ui.use_context::<T>()` (panics if missing) or `ui.try_use_context::<T>()` (returns `Option<&T>`). Added in v0.19.0.

```rust
// `provide` boxes the value as `dyn Any`, so the type must satisfy `T: 'static`.
// `Theme` is `Copy`, so deref-copy from `ui.theme()`. Use `&'static str` for
// string literals; switch to `String` if the value comes from runtime input.
struct AppCtx {
    theme: slt::Theme,
    tick: u64,
    user: &'static str,
}

slt::run(|ui: &mut slt::Context| {
    let ctx = AppCtx { theme: *ui.theme(), tick: ui.tick(), user: "subin" };
    ui.provide(ctx, |ui| {
        render_header(ui);
        render_card(ui);
    });
});

fn render_card(ui: &mut slt::Context) {
    let ctx = ui.use_context::<AppCtx>();
    ui.text(format!("hi {} (tick {})", ctx.user, ctx.tick));
}
```

Reserve explicit parameters for **writes** (e.g. `&mut MyDocState`) — those should still be passed in, not read out of the context bag. See [PATTERNS.md](PATTERNS.md) for the full pattern.

### Conditional styling without re-chaining

`with_if(cond, modifier)` and `with(modifier)` (v0.19.0) let you fold conditional styling into a single fluent chain on text and `ContainerBuilder`. Replace `if cond { t.bold(); t.fg(Color::Red); }` style branching with `ui.text(...).with_if(is_error, |t| t.bold().fg(Color::Red))`. Cleaner diffs, no broken chains.

## Internal widget rules for agents

When editing SLT built-in widgets inside `src/context/*`, prefer the internal interaction helpers instead of hand-rolling event scans again.
These are internal `pub(crate)` helpers, not stable public API for external widgets.

- Use `begin_widget_interaction(focused)` for marker + `Response` setup.
- Use `available_key_presses()` / `available_pastes()` to iterate unconsumed inputs.
- Use `left_clicks_for_interaction()` or `mouse_events_in_rect()` for hit-tested mouse handling.
- Finish by calling `consume_indices(...)` instead of mutating `self.consumed[...]` across the widget body.

Public custom widgets should still use the stable public surface (`register_focusable()`, `interaction()`, `Response`).

## Keep docs and implementation aligned

When an agent changes a public API, it should update:

- crate rustdoc in `src/lib.rs` or the defining file
- the most relevant guide in `docs/`
- example coverage if the new API needs a runnable reference

If the behavior is hard to explain cleanly, the API may still need refinement.
