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

# `lr-result-field`

- **Import** `import '@aceshooting/lyra-ui/components/lr-result-field.js';` (stable tag alias; registers the tag)
- **Class** `LyraResultField`, also available unregistered from `@aceshooting/lyra-ui/components/agent-tools/result-card/result-field.class.js`
- **Family** `components/agent-tools/` — 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** 3 parts, 0 custom properties — see this component's own `@csspart`/`@cssprop` list below
- **Documented with** `lr-result-card` (same section below)
- **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`

---

## `lr-result-card` / `lr-result-field`

A small, tightly-coupled pair giving any custom `lr-tool-result-view` renderer (registered via
`registerToolRenderer()` in `../tool-result-view/registry.js`) a consistent "small bordered card +
label/value row" visual language, without each one hand-rolling its own box. Neither component has
any code dependency on the tool-result-view registry itself — they're generically usable anywhere a
small card/field shell is useful.

Removing `heading` or a result field `label` hides that text while preserving the card actions and
field value.

### `lr-result-card`

A small bordered card shell. Purely visual, with no state of its own beyond slot-presence tracking.

**Properties:**

- `heading: string = ''` — small heading for the card. Leave unset for an untitled card (e.g. a bare
  block of `lr-result-field` rows with no natural heading).
- `compact: boolean = false` (reflected) — tighter header/body padding for dense contexts (a card
  rendered as a row in a transcript or result list), same convention as `<lr-agent-run>`'s own
  `compact`. Purely a density knob: the border and background stay, so use `frame="plain"`
  instead to drop the chrome entirely. When both are set, plain leaves compact padding and gaps
  intact.
- `frame: LyraFrame = 'card'` (reflected) — container treatment, in the library-wide `frame`
  vocabulary (`'card' | 'plain'`), the same property `<lr-agent-run>`/`<lr-card>` carry. `'card'`
  (the default) keeps the bordered, filled box. `'plain'` removes the border, background, and corner
  radius, so a card nested inside a host frame that already draws a border (e.g.
  `<lr-tool-result-view>`'s own chrome) doesn't double it. Plain controls only the chrome; compact
  padding and gaps still apply when both are set. The exported alias `ResultCardAppearance` is
  retained as a name for the same union.
- `withActions: boolean = false` (attribute `with-actions`, reflected) — explicit first-render
  presence hint for the `actions` slot. Client-only markup normally does not need it because the
  component detects assigned actions during upgrade; set it before both server and browser first
  render when an actions-only header must be present in the no-JavaScript response and reused by
  hydration.

**Events:** none.

**Slots:** default (the card body — typically one or more `lr-result-field` rows, though any
content is accepted), `actions` (small header controls, e.g. a copy button, rendered alongside the
heading).

**CSS parts:** `base` (outer bordered container), `header` (present in the DOM at all times so a
later `slotchange` on `actions` is still observed, but `hidden` whenever there's no `heading` and no
`actions` content), `heading` (truncates with an ellipsis when it overflows; carries its own native
`title` attribute — the full string — so hovering the truncated text reveals it via the browser's
default tooltip, scoped to just this element rather than the whole card), `actions` (`hidden`
whenever the slot has no assigned content), `body`.

**Themeable custom properties:** `--lr-result-card-compact-header-padding` (default
`var(--lr-space-xs)`) — `[part="header"]` block/inline padding while `compact`;
`--lr-result-card-compact-header-gap` (default `var(--lr-space-xs)`) — gap between
`[part="header"]`'s heading and actions while `compact`, one step tighter than the uncompacted
`--lr-space-s`; `--lr-result-card-compact-body-padding` (default `var(--lr-space-xs)`) —
`[part="body"]` padding while `compact`; `--lr-result-card-compact-body-gap` (default
`var(--lr-space-2xs)`) — gap between `[part="body"]`'s children while `compact`, one step tighter
than the uncompacted `--lr-space-xs`. The two gap knobs mean `compact` now tightens interior spacing,
not only the padding box — a compact card no longer keeps full-size gaps inside a shrunken frame.
`--lr-result-card-background` (default `var(--lr-color-surface)`), `--lr-result-card-border-color`
(default `var(--lr-color-border)`) and `--lr-result-card-radius` (default `var(--lr-radius)`) retune
the card chrome without a `::part(base)` override. The border-color hook also colors
`[part="header"]`'s divider, so a retuned card doesn't strand a mismatched interior rule;
`frame="plain"` still drops the outer chrome and that divider.
Plus shared tokens — `--lr-space-2xs`/`-xs`/`-s`, `--lr-color-border`/`-surface`/`-text`,
`--lr-radius`.

### `lr-result-field`

A single label/value row — e.g. "Status: 200 OK" or "Duration: 340ms" — rendered as a dense
"label: value" line by default, matching the compact, small-card presentation this pair exists for.

**Properties:**

- `label: string = ''` — the field name, e.g. "Status". Leave unset to render a value with no label.
- `value: string = ''` — plain-text value, e.g. "200 OK". Ignored once the default slot carries real
  content.

**Events:** none.

**Slots:** default — rich value content (e.g. a `lr-chip` status badge, or a plain text override),
taking precedence over `value` whenever it has any assigned content. "Real content" means any
assigned _element_ (even one with no text of its own, like an attribute-driven status badge) or any
non-whitespace text node — both a rich slotted badge and a plain-text override are caught.

**CSS parts:** `base` (row container), `label` (including its trailing colon), `value` (wrapper
around either the slotted content or the plain `value` text).

**Themeable custom properties:** shared tokens only — `--lr-space-xs`, `--lr-color-text`/
`-text-quiet`, `--lr-font`.

**Optional peer deps:** none (either component).

```html
<lr-result-card heading="Weather">
  <lr-result-field label="Status" value="200 OK"></lr-result-field>
  <lr-result-field label="Duration" value="340ms"></lr-result-field>
  <lr-result-field label="Provider">
    <lr-chip variant="success">OpenWeather</lr-chip>
  </lr-result-field>
</lr-result-card>
```

**Known gotchas:**

- `HTMLElement.textContent` read on a shadow-DOM wrapper containing a `<slot>` does NOT include the
  slot's assigned/projected light-DOM content — only literal fallback children of the `<slot>` tag
  itself (there are none here). Reading `[part="value"]`'s own `.textContent` yields an empty value
  even when slotted content is rendering correctly; use the slot's
  `assignedNodes()`/`assignedElements()` instead.

---
