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

# `lr-diff-view`

- **Import** `import '@aceshooting/lyra-ui/components/lr-diff-view.js';` (stable tag alias; registers the tag)
- **Class** `LyraDiffView`, also available unregistered from `@aceshooting/lyra-ui/components/utility/diff-view/diff-view.class.js`
- **Family** `components/utility/` — 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** `shiki` — see `llms/peers.md`
- **Themeable via** 5 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-diff-view`

A real two-string line diff (a classic longest-common-subsequence dynamic program, not a Myers
implementation), rendered as interleaved unified-diff output — not diff-flavored syntax highlighting
over an already-formatted string (`<lr-code-block>`'s `language="diff"` only lexically colors a
string the consumer already unified-diffed; it has no two-string-compare entry point of its own).
First-party invention (no Web Awesome equivalent).

**Properties:**

- `oldText: string = ''` (attribute: false) — the "before" text. Default `''` renders an
  all-additions diff of `newText`.
- `newText: string = ''` (attribute: false) — the "after" text. Default `''` renders an
  all-removals diff of `oldText`.
- `copyable: boolean = false` — shows a copy-to-clipboard button for the full unified-diff text.
  `false` (the default) renders no button.
- `maxHeight: string = ''` (attribute `max-height`) — a CSS length (e.g. `"20rem"`); once set, the
  view scrolls internally past this height instead of growing the page. Invalid values are ignored.
- `layout: 'unified' | 'split' = 'unified'` (reflected) — `'unified'` (the default) renders today's
  single interleaved `<pre>`; `'split'` renders two side-by-side `[part="side"]` columns derived from
  the same diff alignment. Unsupported attributes and untyped property writes normalize to
  reflected `unified`.
- `language: string = ''` — a shiki-recognized language id. Highlighting activates only when this
  has a matching entry in `languages` — there is deliberately no default full-table
  `lr-code-block`-style fallback, so this component never reaches shiki's ~200-language
  dynamic-import table.
- `languages?: Record<string, ShikiLanguageInput>` (attribute: false) — grammar definitions this
  instance can highlight, same shape as `lr-code-block-core`'s own `languages`.
- `contextLines: number | undefined` (attribute: `context-lines`) — keeps this many unchanged
  lines around each change and collapses longer unchanged runs into a localized fold marker. The
  default `undefined` shows every line; negative and non-finite values also disable folding.
- `maxLines: number = 5000` (attribute `max-lines`) — maximum logical lines accepted on either
  side. Larger input renders the localized `diffViewTooLarge` fallback without computing or
  highlighting the diff. `Infinity` relaxes this line-count ceiling, but the fixed aggregate
  character and comparison-work ceilings remain in force.

Grammar entries are read from own enumerable data fields into a bounded frozen map. A getter,
unreadable field, non-enumerable field, or malformed proxy branch is skipped without preventing a
later valid grammar from being used; grammar values themselves remain opaque to the component.
Non-enumerable names do not consume language slots, but every inspected name counts toward the
20,000 structural ceiling.

**Events:**

- `lr-copy` (`detail: LyraClipboardWriteSuccess`, `{ ok: true; text: string }`) — the frozen full
  unified-diff outcome, fired only after the owning browsing context's clipboard write fulfills.
- `lr-error` (no detail) — the Clipboard API was unavailable or the write failed.
- `lr-copy-error` (`detail: LyraClipboardWriteFailure`) — the frozen failure outcome contains
  `ok: false`, the text, a `LyraCopyErrorReason`, and the original error. The reason is
  `'unsupported' | 'denied' | 'failed'`.

The copy button stays in its resting state until `writeText()` resolves. Success renders and
announces localized `copied`; failure renders and announces localized `copyFailed`. A newer
activation, source-text change, `copyable`/`maxLines` transition, disconnect, or document adoption
retires an older pending outcome, so stale writes cannot confirm or fail a hidden or replaced diff.

**Slots:** none.

**CSS parts:** `base` (the root wrapper), `line` (a single line; carries
`data-type="equal"|"add"|"remove"|"empty"|"fold"` — `"empty"` is an unbalanced-replace placeholder cell in
`layout="split"` and never carries a `+`/`-` prefix; `"fold"` is the localized unchanged-lines
marker), `copy-button` (the copy affordance, only
rendered while `copyable`), `limit` (the localized over-`maxLines` fallback), `side` (one column in
`layout="split"`, `data-side="old"|"new"`).

**Themeable custom properties:** `--lr-diff-view-max-height` (default `none` — an independently
settable scroll cap on `[part="base"]`; the `maxHeight` property writes the same custom property
inline on `[part="base"]`), `--lr-diff-view-font` (default `var(--lr-font-mono)`), plus
shared tokens `--lr-color-border`/`-surface`/`-success`/`-success-quiet`/`-danger`/
`-danger-quiet`/`-text`, `--lr-radius`, `--lr-space-xs`/`-s`, `--lr-font-size-sm`,
`--lr-line-height-snug`, `--lr-focus-ring-*`.

**Optional peer deps:** `shiki` (the same fine-grained `shiki/core`/`shiki/engine/oniguruma`/binary
`shiki/onig.wasm` subset `lr-code-block-core` uses by default, never `shiki`'s full ~200-language
main entry point) — only loaded once both `language` and a matching `languages` entry are set;
unset/unmatched leaves the plain unhighlighted diff text untouched.

```html
<lr-diff-view copyable></lr-diff-view>
<script type="module">
  const diff = document.querySelector("lr-diff-view");
  diff.oldText = "line one\nline two\nline three";
  diff.newText = "line one\nline TWO\nline three\nline four";
  diff.addEventListener("lr-copy", (e) => console.log(e.detail.text));
  diff.addEventListener("lr-copy-error", (e) => {
    console.error(`Copy ${e.detail.reason}`, e.detail.error);
  });
</script>
```

The package root also exports the pure `computeLineDiff(oldLines: string[], newLines: string[]):
LyraDiffOp[]` helper (plus the `LyraDiffOp` type, `{ type: 'equal' | 'add' | 'remove'; text: string }`) — the
same line-diff function this component's own `render()`/copy handler call, exposed standalone so a
consumer can compute or unit-test the same alignment without instantiating the element at all.

**Known gotchas:**

- line splitting normalizes LF, CRLF, and lone CR endings before alignment and syntax-token indexing,
  so files that differ only by line-ending convention do not appear wholly changed.
- An empty document contains zero logical lines. A genuine trailing newline is still represented,
  so empty/one-sided diffs and copied unified text do not gain a phantom blank operation.
- alignment uses Hirschberg longest-common-subsequence matching: O(n·m) time with linear working
  memory. The 5,000-line per-side default, aggregate character ceiling, and comparison-work ceiling
  bound pathological inputs; `Infinity` opts out of only the first of those limits.
- the computed `diffOps` state is cached and recomputed only when `oldText`, `newText`, or
  `maxLines` changes. Copy-confirmation and other unrelated renders reuse the cached alignment.
- Changing either `oldText` or `newText` clears any in-progress "Copied" feedback immediately.

**Additional API surface:**

- `--lr-diff-view-add-background` — Added-line background.
- `--lr-diff-view-add-color` — Added-line text color.
- `--lr-diff-view-remove-background` — Removed-line background.
- `--lr-diff-view-remove-color` — Removed-line text color.
- `--lr-diff-view-fold-color` — Fold-marker text color.
- `--lr-diff-view-fold-background` — Fold-marker background.

---
