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

# `lr-docx-viewer`

- **Import** `import '@aceshooting/lyra-ui/components/lr-docx-viewer.js';` (stable tag alias; registers the tag)
- **Class** `LyraDocxViewer`, also available unregistered from `@aceshooting/lyra-ui/components/viewers/docx-viewer/docx-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** `dompurify`, `mammoth` — see `llms/peers.md`
- **Themeable via** 11 parts, 12 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-docx-viewer`

Fetches a `.docx` Word document as an `ArrayBuffer`, converts it to semantic HTML with the optional
`mammoth` peer, sanitizes that HTML through the optional `dompurify` peer, and renders the result.
Mammoth preserves document structure such as headings, paragraphs, lists, tables, and validated
inline raster images; it is not intended to reproduce pixel-exact Word page layout. There is no
unsanitized rendering escape
hatch: if `dompurify` is unavailable, rendering is blocked even when Mammoth converted successfully.
Converted markup then passes through the passive-document profile: anchors, form controls, and
custom elements are unwrapped to ordinary text/children where safe, remote navigation/resource
attributes are removed, and an `<a>` itself never remains. Images render only inline base64 GIF,
JPEG, PNG, or WebP data; same-document SVG fragment references may remain.

Every rendered heading's slug (the same GitHub-slugger-style algorithm `<lr-markdown>` uses) is
stamped as its `id` and cached into `getHeadingTree()`'s document-ordered outline. Duplicate
headings receive monotonic `-1`, `-2`, … suffixes; used suffixes are never re-probed for the same
base, so one bounded heading pass performs linear aggregate dedupe work. Adopts
`DocumentAnchorTarget`: `fragment` anchors resolve against that outline, `text-quote` anchors via
the shared quote-scoping helpers; `highlights` re-resolve by quote after every render. Native
keyboard actions are exposed only for highlights whose quote resolves in the currently loaded
document; unresolved highlights and idle/loading/error states never expose an enabled no-op.

**Properties:** `src: string = ''`, `name: string = ''`, and `maxHeight: string = ''` (attribute
`max-height`). A nonempty host `aria-label` makes the host the sole named semantic owner. With an
explicitly empty host label, the shadow document keeps `role="document"` and an empty accessible
name; when the attribute is absent, `name` and the localized label are fallbacks. `maxHeight` caps the
scrollable document body; invalid CSS `max-height` values, declaration breaks, and `url()` are
ignored. The inherited anchor-target properties are `highlights: readonly LyraHighlight[] = []` (property
only; reassign after mutation), `activeHighlightId: string | null = null` (attribute
`active-highlight-id`), `anchor: LyraAnchor | string | null = null` (property only), and
`anchorKinds: readonly LyraAnchorKind[] = ['fragment', 'text-quote']` (this viewer's supported
`LyraAnchor.kind` values).

**Methods:** `getHeadingTree()` returns the document-ordered outline as `DocxHeadingItem[]` (`{ id,
label, level }`), cached on every successful load. `search(query)` resolves the match count via a
case-insensitive substring search over the rendered content's text (empty/whitespace query behaves
like `clearSearch()`); `searchNext()`/`searchPrevious()` advance/step back through matches
(wrapping, resolving `false` when there are none); `clearSearch()` clears the query, matches, and
painted marks. It indexes at most 1,000,000 code units/20,000 text nodes per content generation,
accepts at most 4,096 query code units, scans at most 4,000,000 code units, retains 1,000 matches,
and paints a 200-range search window.

**Events:** `lr-render-error` with `detail.error` only when fetching, conversion, or sanitization
fails terminally. Non-fatal Mammoth conversion messages emit `lr-viewer-diagnostic` instead;
`detail.diagnostic` is readonly `{ code: 'docx-conversion-message', severity: 'warning', fatal:
false, source: 'mammoth', cause }`. `lr-search-change` (`detail: { query, matchCount,
matchCountExact, activeIndex }`) — from search/navigation/clear, canonical source reset, and
effective-locale re-evaluation.
`lr-highlight-activate` (`detail: { highlightId }`) — a painted `text-quote` highlight was clicked or its
resolved keyboard action was activated.
`lr-text-select` (`detail: { text, anchor, rects }`) — fired on selection end inside the rendered
content. `lr-anchor-result` (`detail: { found }`) — fired after an `anchor` assignment or a
`scrollToAnchor()` call.

Mammoth's conversion result is usable only when it has own-data `value: string` and an own-data
`messages` array; an unusable primary result is terminal. From a usable result, the first 100
message positions are considered, malformed entries are omitted, and valid opaque causes are
retained in their diagnostics. Sanitized document content remains visible while those non-fatal
diagnostics are emitted.

**CSS parts:** `base`, `body`, `content`, `spinner`, `error`, `highlight` (a painted `text-quote`
highlight), `highlight-actions` (keyboard-accessible actions for resolved highlights),
`highlight-action` (one native highlight activation button), `search-match` (a painted in-document
search match), and `search-match-active` (the currently active search match, also carries
`search-match`), plus `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).

**Themeable custom properties:** `--lr-docx-viewer-max-height` (default `none`) — maximum block size
of `[part="body"]`; also settable via the `max-height` property, which writes this token inline.
`--lr-docx-viewer-table-header-background` (default `var(--lr-color-brand-quiet)`) retints a
rendered document table's header row (`th`), independent of the highlight backgrounds below.
Highlight backgrounds are independently themeable with
`--lr-docx-viewer-highlight-accent-background`,
`--lr-docx-viewer-highlight-success-background`,
`--lr-docx-viewer-highlight-warning-background`,
`--lr-docx-viewer-highlight-danger-background`, and
`--lr-docx-viewer-highlight-neutral-background`, defaulting to the matching quiet color tokens --
except neutral, which defaults to `var(--lr-color-surface-raised)`: `[part='content']` paints no
background of its own and therefore shows `[part='base']`'s `--lr-color-surface`, so a neutral
highlight falling back to that same token would render as unhighlighted.
`--lr-docx-viewer-highlight-active-background` and
`--lr-docx-viewer-highlight-active-outline` style the active host highlight.
`--lr-docx-viewer-search-match-background`,
`--lr-docx-viewer-search-match-active-background`, and
`--lr-docx-viewer-search-match-active-foreground` style resting and active search matches.

**Optional peer dependencies:** install `mammoth` and `dompurify` with `pnpm add mammoth dompurify`.
The component registers an eager `application/vnd.openxmlformats-officedocument.wordprocessingml.document`
renderer with `<lr-document-viewer>` (a plain `render`, no `load()` hook — importing this module
defines `<lr-docx-viewer>` immediately; only `mammoth`/`dompurify` themselves are loaded on demand)
and matches `.docx` filenames when the MIME type is generic.

A granular consumer (not importing the `all.js` compatibility bundle) who wants `<lr-docx-viewer>`'s
own class module deferred too can instead import
`@aceshooting/lyra-ui/components/viewers/docx-viewer/docx-viewer-register.js`, which installs the
same registration lazily and exports `DOCX_VIEWER_TAG` (`'lr-docx-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 document.

```html
<lr-docx-viewer
  src="/files/report.docx"
  name="report.docx"
  max-height="32rem"
></lr-docx-viewer>
```
