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

# `lr-chunk-inspector`

- **Import** `import '@aceshooting/lyra-ui/components/lr-chunk-inspector.js';` (stable tag alias; registers the tag)
- **Class** `LyraChunkInspector`, also available unregistered from `@aceshooting/lyra-ui/components/retrieval/chunk-inspector/chunk-inspector.class.js`
- **Family** `components/retrieval/` — 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** 16 parts, 2 custom properties — see this component's own `@csspart`/`@cssprop` list below
- **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`

---

## `lr-chunk-inspector`

A ranked retrieved-chunks list: relevance score bars with tier tones, expandable chunk text, and the
deep-link event that lands a chunk in `lr-document-viewer`. Never fetches or ranks and never opens
documents itself. Blank chunk ids and later duplicates are omitted first-wins before sorting,
rendering, state, or events.

**Properties:**

- `chunks: LyraChunk[] = []` (attribute: false) — `LyraChunk { id: string; text: string; score:
number; sourceId: string; title?: string; page?: string | number; anchor?: LyraChunkAnchor }`;
  blank ids and later duplicate ids are omitted first-wins before any derived path;
  `score` is 0–1, `title` falls back to a localized "untitled source", `anchor` (the same
  discriminated union `lr-document-viewer.anchor` accepts — page/text-quote/fragment/line-range/
  cell-range/cfi/time-range/region/node-path) is carried through `lr-chunk-open` verbatim; finite
  numeric `page` values are formatted with the effective locale in the visible title and open-button
  name, while string locators remain verbatim
- `thresholds: { high: number; medium: number } = { high: 0.75, medium: 0.5 }` (attribute: false) —
  score-bar tier cutoffs
- `sort: ChunkInspectorSort = 'score'` — `ChunkInspectorSort = 'score' | 'none'`, exported by this
  module. The sorted view is memoized on the `chunks`/`sort` pair, so an unrelated update (a new
  `activeChunkId`, toggling `compact`) hands the internal `lr-virtual-list` the same array reference it
  already holds instead of forcing a full offset/identity rebuild
- `activeChunkId: string = ''` (attribute `active-chunk-id`)
- `virtualizeAt: number = 50` (attribute `virtualize-at`)
- `compact: boolean = false` (reflected) — hides the text preview/toggle, title/score row only
- `label: string = ''` — fallback name for the populated result group. A non-empty host
  `aria-label` makes the host the sole overall owner; an explicitly empty host label stays empty

**Events:** `lr-chunk-open` (`detail: { chunkId, sourceId, anchor? }`, a chunk's title/open button was
activated — the event a host routes into `lr-document-viewer`, setting `src` from `sourceId` and
`anchor` from the chunk's own), `lr-expand` (`detail: { chunkId, expanded }`, a chunk's text toggle was
activated).

**Slots:** none.

**CSS parts:** `base` (`role="group"`), `chunk` (one chunk row; carries `role="listitem"` only
below `virtualize-at` — while virtualized the surrounding `lr-virtual-list` row supplies that role
instead), `chunk-current` (additional part on the row matching `activeChunkId`), `score` (visible
percent text), `score-current` (additional part on the current row's score line), `score-bar`
(`aria-hidden` track), `score-fill` (tone-mapped fill), `score-fill-success` /
`score-fill-warning` / `score-fill-danger` (additional part on the fill, one per scoring tier),
`open-button`, `title` (the `<span>` inside `open-button` carrying the visible title text), `text`
(omitted when `compact`), `text-clamped` (additional part on a `text` preview that is still
collapsed; dropped once expanded), `toggle` ("Show more"/"Show less", omitted when `compact`),
`empty` (shown when `chunks` is empty).

Every row-level part is reachable through `::part()` in both rendering paths: above
`virtualize-at` the row lives in the internal `lr-virtual-list`'s shadow root and its parts are
re-exported from there under the same names. Row _state_ is exposed as an additional part name
rather than as an attribute on the part, because Shadow Parts forbids an attribute selector after
`::part()` — `::part(chunk)[aria-current='true']` is invalid CSS. The equivalent attributes
(`aria-current`, `data-tone`, `data-clamped`) are still present on the elements. A state part is a
second token in the same `part` attribute, so a `[part~="…"]` (not `[part="…"]`) selector is the
one that matches inside a tree.

**Themeable custom properties:** `--lr-chunk-inspector-current-bg` (default
`var(--lr-color-brand-quiet)`) — the background of the chunk matching `activeChunkId`.
`--lr-chunk-inspector-current-color` (default `var(--lr-color-text)`) — the text color of that
chunk's `score` line (`::part(score-current)`). Both are inline `var()` fallbacks at the point of
use rather than `:host` declarations, so either can be set on the element _or on any ancestor_:
`::part(chunk)[data-active]` is invalid CSS — Shadow Parts forbids an attribute selector after
`::part()` — which is why the current-chunk state is also published as its own part name
(`chunk-current`, `score-current`); either the custom properties or `::part(chunk-current)` will
retint it.

**They are a contrast-sensitive pair — override them together, never one alone.** The `-current-color`
hook exists precisely because the quiet token it replaces only reaches about 4.24:1 against the
current background; keep any override at 4.5:1 or better against `--lr-chunk-inspector-current-bg`.

Plus shared tokens otherwise.

**Optional peer deps:** none.

```html
<lr-chunk-inspector></lr-chunk-inspector>
<lr-document-viewer id="document-viewer"></lr-document-viewer>
<script>
  const inspector = document.querySelector("lr-chunk-inspector");
  const documentViewer = document.getElementById("document-viewer");
  const sourcesById = new Map([
    [
      "doc-1",
      {
        name: "Q3 report.pdf",
        mimeType: "application/pdf",
        src: "/reports/q3-report.pdf",
      },
    ],
  ]);
  inspector.chunks = [
    {
      id: "c1",
      text: "Revenue grew 12% year over year…",
      score: 0.91,
      sourceId: "doc-1",
      title: "Q3 report",
      page: 4,
      anchor: { kind: "page", page: 4 },
    },
  ];
  inspector.addEventListener("lr-chunk-open", (e) => {
    const source = sourcesById.get(e.detail.sourceId);
    if (!source) return;
    documentViewer.name = source.name;
    documentViewer.mimeType = source.mimeType;
    documentViewer.src = source.src;
    documentViewer.anchor = e.detail.anchor ?? null;
    documentViewer.open = true;
  });
</script>
```

**Known gotchas:**

- `title` and `open-button` are split into two separate parts (rather than one dual-part-name
  element) because an exact-match `[part="..."]` CSS attribute selector cannot match a multi-token
  `part` attribute value.

---
