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

# `lr-archive-viewer`

- **Import** `import '@aceshooting/lyra-ui/components/lr-archive-viewer.js';` (stable tag alias; registers the tag)
- **Class** `LyraArchiveViewer`, also available unregistered from `@aceshooting/lyra-ui/components/viewers/archive-viewer/archive-viewer.class.js`
- **Family** `components/viewers/` — 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)
- **Deprecations** none
- **Optional peers** none
- **Themeable via** 11 parts, 8 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-archive-viewer`

Lists entry names and human-readable declared uncompressed sizes inside a `.zip` archive. It is
listing-only: entry content is never inflated, rendered, or previewed, and the component has no
runtime archive-parser dependency. One owned central-directory parser validates local-header
bounds, supported compression methods, entry names, and the 10,000-entry/100 MB declared-expansion
ceilings, then returns the immutable metadata used directly by the listing. The list composes
`<lr-virtual-list>` for large archives.

**Properties:** `src: string = ''`, `name: string = ''`, and `maxHeight: string = ''` (attribute
`max-height`) — a host-level `aria-label` takes precedence over `name` by attribute presence,
including an explicitly empty value, when naming the `role="region"` listing. `maxHeight` caps the
scrollable archive body; invalid CSS `max-height` values, declaration breaks, and `url()` are
ignored. The viewer also exposes the shared text-viewer
contract: `highlights`, `activeHighlightId`, `anchor`, and `anchorKinds` (`['text-quote', 'fragment']`).

**Methods:** `search(query)`, `searchNext()`, `searchPrevious()`, and `clearSearch()` provide
case-insensitive text search over every loaded entry path; next/previous wrap and scroll the active
virtualized row into view. Queries are capped at 4,096 code units and each pass at 4,000,000 path
code units; `matchCountExact: false` reports a ceiling-truncated lower bound. `scrollToAnchor()` resolves text-quote and fragment anchors and emits
`lr-anchor-result`. A fragment id is the exact ZIP entry path. A text quote resolves within one
complete entry path; both forms first mount the absolute virtualized row and only then perform the
shared DOM-level anchor resolution. Rendered rows intentionally do not expose entry paths as DOM
ids; the fragment mapping is resolved against archive metadata. A jump whose archive is replaced
by a concurrent `src` reassignment mid-flight, or whose row cannot be located after the wait,
reports `found: false` rather than a phantom success.

**Events:** `lr-render-error` with `detail.error` when fetching or parsing fails;
`lr-search-change` (`detail: { query, matchCount, matchCountExact, activeIndex }`) from search,
navigation, clear, canonical source reset, and effective-locale re-evaluation; `lr-text-select`
(`detail: { text, anchor, rects }`) for a selection contained within one
entry path; and `lr-anchor-result` (`detail: { found }`) after anchor resolution.
`lr-highlight-activate` is not part of this viewer's event contract: archive entry-path highlights
are passive and cannot be activated.

**CSS parts:** `base`, `body`, `entry`, `entry-icon`, `entry-name`, `entry-name-dir`, `entry-size`,
`highlight` (the `<mark>` fallback for a painted entry-path quote), `spinner`, `error`, and
`anchor-live-region` (an aria-hidden, non-live shadow mirror of the latest anchor-jump message; the
spoken copy is appended to the shared document-level polite sink only while the viewer and its
composed ancestors are exposed to the accessibility tree). A directory row's name element carries
both `entry-name` and
`entry-name-dir` (a part list), so `::part(entry-name-dir)` selects only directory names while
`::part(entry-name)` still selects every name. Entry rows are rendered into the embedded
`<lr-virtual-list>`'s own shadow root and forwarded with `exportparts`, so
`lr-archive-viewer::part(entry)` (and every other row part above) reaches them from a consuming
stylesheet.

**Themeable custom properties:** `--lr-archive-viewer-max-height` (default `none`) caps the
scrollable `[part="body"]` and is also settable through `maxHeight`/`max-height`.
`--lr-archive-viewer-highlight-accent-background`,
`--lr-archive-viewer-highlight-success-background`,
`--lr-archive-viewer-highlight-warning-background`,
`--lr-archive-viewer-highlight-danger-background`, and
`--lr-archive-viewer-highlight-neutral-background` control tone backgrounds. The neutral default is
`var(--lr-color-surface-raised)`, deliberately not `--lr-color-surface`: entry rows paint no
background of their own and therefore show the viewer's `--lr-color-surface`, so a neutral highlight
falling back to that same token would render as unhighlighted.
`--lr-archive-viewer-highlight-active-background` and
`--lr-archive-viewer-highlight-active-outline` control the active quote.

**Exports:** `ArchiveEntry` — `{ name: string; dir: boolean; size: number }`.

The lazy registry registers `application/zip` and `application/x-zip-compressed`, with a `.zip`
filename fallback, and imports the viewer only when a matching archive is opened. Both registrations declare
`capabilities: { anchors: ['text-quote', 'fragment'], search: true, textSelect: true }` — sibling to
`load`, not inside it, so feature detection can read the capabilities without paying for the lazy
import. Opening a `.zip` through `<lr-document-viewer>` forwards `anchor`/`highlights` to the mounted
viewer, so a deep link into an entry name survives the registry hop. `.tar`, `.rar`, and other
archive formats fall through to `<lr-document-preview>`'s generic download fallback.

A granular consumer (not importing the `all.js` compatibility bundle) must import
`@aceshooting/lyra-ui/components/viewers/archive-viewer/archive-viewer-register.js` once to install
this registration; without it `<lr-document-viewer>` never recognizes `.zip` files and falls
through to the generic download fallback described above. That entry also exports
`ARCHIVE_VIEWER_TAG` (`'lr-archive-viewer'`) as a stable reference to the tag it eventually
registers.

Remote resources are capped at 25 MB; exceeding it surfaces the localized
`documentPreviewResourceTooLarge` message instead of the entry listing.
