<!-- GENERATED by scripts/build-llms.mjs from llms/conversation.md — do not edit this file. -->

# `lr-model-settings-panel`

- **Import** `import '@aceshooting/lyra-ui/components/lr-model-settings-panel.js';` (stable tag alias; registers the tag)
- **Class** `LyraModelSettingsPanel`, also available unregistered from `@aceshooting/lyra-ui/components/conversation/model-settings-panel/model-settings-panel.class.js`
- **Family** `components/conversation/` — see `llms/index.md` for its siblings
- **Status** `stable` since `4.0.0` — see the maturity and deprecation policy in `llms/shared.md`
- **Release history** [CHANGELOG.md](../../CHANGELOG.md); family-wide breaking-change summaries: [llms-full.txt](../../llms-full.txt)
- **Deprecations** none
- **Optional peers** none
- **Themeable via** 7 parts, 1 custom property — see this component's own `@csspart`/`@cssprop` list below
- **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`

---

## `lr-model-settings-panel`

A fixed composition of `<lr-model-select>` and `<lr-slider>` into one agent-configuration card:
pick a provider's model, then tune its sampling temperature. First-party invention (no Web Awesome
equivalent). Not a generic layout shell — it exists so a consumer doesn't have to re-wire the same
two child `lr-change` events into one combined settings object by hand every time this pairing
comes up.

Every prop here is a plain pass-through to (or mirror of) the matching child control's own prop of
the same/similar name — see `lr-model-select` and `lr-slider` themselves for the exact semantics
of `catalog`/`allowCustom` and `temperatureMin`/`temperatureMax`/`temperatureStep`.

**Properties:**

- `provider: string = ''` — informational provider badge, passed straight through to the internal
  `lr-model-select`.
- `catalog?: LyraCatalog<LyraModelCatalogEntry>` (attribute: false, JS-only) — a readonly string
  catalog or readonly object-row catalog (every entry must be one shape or the other, never mixed);
  passed straight through to the internal `lr-model-select`, with the shared unique, nonempty,
  first-wins catalog projection also used for this panel's `inCatalog` event field. The array is
  clone-owned, bounded, and frozen; reassign a new catalog array after changing its rows.
- `model: string = ''` — the current model id.
- `allowCustom: boolean = false` (attribute `allow-custom`) — lets the model control accept a value
  outside `catalog`; passed straight through.
- `temperature: number = 1` — the current sampling temperature. `1` is the midpoint of the default
  `[0, 2]` range and matches both OpenAI's and Anthropic's own provider default; reassign it yourself
  if your provider differs.
- `temperatureMin: number = 0` (attribute `temperature-min`)
- `temperatureMax: number = 2` (attribute `temperature-max`)
- `temperatureStep: number = 0.1` (attribute `temperature-step`)
- `layout: 'vertical' | 'compact' = 'vertical'` (reflected) — `vertical` stacks full-width rows with
  visible labels; `compact` runs the same two rows side by side with a smaller, uppercase temperature
  caption, for toolbars/sidebars where the vertical layout's height doesn't fit.
- `disabled: boolean = false` (reflected) — disables the panel as a unit by forwarding to _both_
  internal `lr-model-select` and `lr-slider`; a wrapping `<fieldset disabled>` alone would not
  reach either, since a form-associated control's own `disabled` IDL property/attribute is never
  mutated by fieldset cascading.

**Events:** `lr-change` — `detail: { model: string; inCatalog: boolean; temperature: number }`.
Fires whenever _either_ child control's own `lr-change` fires, and always carries the full current
settings snapshot, not just whichever field actually changed. `inCatalog` is recomputed fresh from
`catalog`/`model` on every emission (mirroring `lr-model-select`'s own `effectiveEntries` logic)
rather than cached from the last child event, so it's still correct even when `model` was just
assigned directly instead of via the child's own event. The nested selector's native `focus` and
`blur` events remain contained at that child boundary; the panel does not declare or relay them.

**Slots:** none — this is a fixed two-control composition, not a generic layout shell.

**CSS parts:** `base`, `model-row`, `model-select`, `model-label` (forwarded visible internal
selector label), `temperature-row`, `temperature-label`, `temperature-value`

**Themeable custom properties:** `--lr-model-settings-panel-max-inline-size` — the card's own width
ceiling (default `var(--lr-size-28rem)`). Set a length to retune it, or `none` for a full-width
card. `layout="compact"` uncaps the card by default and reads the same name, so a length narrows a
compact card too. The panel also sets `--lr-model-select-max-inline-size: none` on its own
`[part="model-row"]`, so the nested selector fills the row rather than stopping at its standalone
24rem ceiling — set that name on the row to re-cap it. Otherwise it consumes shared tokens
`--lr-space-l/-m/-s/-xs`, `--lr-color-border`, `--lr-radius`, `--lr-color-surface`,
`--lr-color-text`, `--lr-color-text-quiet`.

**Optional peer deps:** none — it composes the library's own `<lr-model-select>` and `<lr-slider>`
internally (both imported unconditionally as side effects, not optional).

```html
<lr-model-settings-panel provider="OpenAI" model="gpt-4o" temperature="0.7"></lr-model-settings-panel>

<lr-model-settings-panel layout="compact"></lr-model-settings-panel>
<script type="module">
  const [panel, compactPanel] = document.querySelectorAll("lr-model-settings-panel");
  panel.catalog = ["gpt-4o", "gpt-4o-mini", "gpt-4.1"];
  panel.addEventListener("lr-change", (e) => console.log(e.detail));
  compactPanel.catalog = catalog;
</script>
```

The internal `lr-slider` renders with its own value readout suppressed (`.showValue=${false}`);
the current temperature is instead shown via this component's own `[part="temperature-value"]` span,
which formats `temperature` through the cached `Intl.NumberFormat` for the effective locale with up
to 20 fractional digits, matching `lr-slider`'s own numeric readout. For example, `temperature="0.7"`
under `locale="de-DE"` displays `0,7`. When the full decimal expansion would exceed 24 characters —
reachable only through an extreme `temperatureMin`/`temperatureMax`/`temperatureStep` combination —
the readout switches to bounded scientific notation at up to 6 significant digits instead, so
`1e308` renders as `7E+307` rather than a 300-digit string that would overflow the label.

The panel's own `temperature` property mirrors the nested slider's _live_ value on every one of its
`lr-input` events (drag/key-repeat), not just its committed `lr-change` — so `temperature` (and
the visible readout) tick continuously during a drag, but the panel's own `lr-change` event only
fires once the slider's own `lr-change` commits (pointerup/keyup) or the model changes; reading
`.temperature` mid-drag will already reflect the live position even though no `lr-change` has fired
yet for it.

**Known gotchas:**

- `catalog` is JS-only (`attribute: false`) — set it via a property binding (`.catalog=`), never as
  an HTML attribute, same requirement as the underlying `lr-model-select`.
- `layout="compact"` removes the host's own `max-inline-size` cap (`28rem` in `vertical` layout)
  entirely, so a compact panel can grow as wide as its container/flex context allows.
- The nested `lr-model-select`'s own `max-inline-size` (sized for a standalone dropdown) is
  overridden to `100%`/`none` inside `[part="model-row"]` so it fills the card's full width — a
  detail only worth knowing if you're targeting `lr-model-select` internals with your own CSS
  through this component.

---
