# `<llumi-review>`

Review wrapper for commenting on light-DOM content. Wrap any content in
`<llumi-review>`; mark commentable elements with `data-review-id` and selectable
text regions with `data-review-selectable`. Commenters click an element to leave
an element-level comment, or select text to leave a selection-level comment
anchored to the chosen words.

## What it is

A `<llumi-review>` element wraps arbitrary light-DOM content and drives an
XState-driven annotation flow: a floating review bar (toggle/comment-count/
submit), floating comment dialogs anchored to the edited element or selection,
and CSS Custom Highlight API painting for commented/edited/hovered ranges.

- **Element comments** — click any `[data-review-id]` element to comment on it.
- **Selection comments** — select text inside any `[data-review-selectable]`
  element; the comment dialog anchors beside the selected range.
- **Overall comment** — a free-form note covering the whole document, opened from
  the review bar.
- **Persistence** — set the `persist` attribute to a localStorage key to keep
  the draft across reloads.

## Usage

> **Requires the base theme.** Load `@llumi/design-system/base.css` (or
> `base.css`) on the page for the document-level styles the component adopts.

```html
<!-- 1. Register the element (auto side-effect) -->
<script type="module">
  import "@llumi/design-system/review";
</script>

<!-- 2. Wrap content and mark the commentable parts -->
<llumi-review default-mode="annotating" bar-position="bottom-center">
  <h1 data-review-id="title">Project Proposal</h1>
  <p data-review-selectable="intro">
    Select a sentence in this paragraph to comment on it, or click the heading.
  </p>
  <div data-review-id="metric-1" data-review-selectable="metric-1">
    <strong>42</strong> open issues
  </div>
</llumi-review>
```

Or with a named import:

```js
import { LlumiReview } from "@llumi/design-system/review";
```

## Marking up commentable content

| Attribute | Applies to | Meaning |
|-----------|-----------|---------|
| `data-review-id` | any element | The element is commentable as a whole; clicks open an element-level comment. The value is the `targetId`. |
| `data-review-selectable` | any element containing text | Text inside the element can be selected to open a selection-level comment anchored to the chosen range. The value is the `targetId`. |

Both attributes may be set on the same element (e.g. a paragraph you want both
click- and selection-commentable). Elements without either attribute are not
commentable. `targetId` values are arbitrary strings; the component does not
generate them for you.

## Attributes and properties

| Name | Attribute | Type | Default | Notes |
|------|-----------|------|---------|-------|
| `defaultMode` | `default-mode` | `"idle" \| "annotating"` | `"idle"` | Whether the component starts in annotation mode. Set `default-mode="annotating"` to enter review mode immediately on connect. |
| `barPosition` | `bar-position` | `ReviewBarPosition` | `"bottom-center"` | Position of the floating review bar. One of: `top-left`, `top-center`, `top-right`, `bottom-left`, `bottom-center`, `bottom-right`. |
| `shortcut` | `shortcut` | `string` | `"$mod+Shift+M"` | Keyboard shortcut (tinykeys syntax) to toggle annotation mode. `$mod` is `Cmd` on macOS, `Ctrl` elsewhere. |
| `persist` | `persist` | `string` | — (off) | When set to a non-empty value, the review draft is persisted to `localStorage` under that key and restored on load. Read once on connect. |
| `initialData` | — (property) | `ReviewResult` | `undefined` | Pre-loaded review state (existing comments). No HTML attribute. Set before connect. |
| `customTargets` | — (property) | `CustomTarget[]` | `undefined` | Additional non-DOM comment targets. No HTML attribute. |

## Events

Both events bubble and are composed (they cross shadow boundaries).

### `llumi-review-change`

Fires on every edit — a comment added, edited, or deleted, or the overall
comment changed. `detail` is a `ReviewResult`:

```ts
element.addEventListener("llumi-review-change", (e: CustomEvent<ReviewResult>) => {
  console.log("current review:", e.detail);
});
```

### `llumi-review-submit`

Fires when the user clicks Submit / Approve on the review bar. `detail` is
`{ result, clearPersisted }`:

```ts
element.addEventListener(
  "llumi-review-submit",
  (e: CustomEvent<ReviewSubmitDetail>) => {
    const { result, clearPersisted } = e.detail;
    // Persist `result` to your server, then:
    clearPersisted(); // discard the localStorage draft for this element
  },
);
```

Call `clearPersisted()` only after a confirmed server save; it is a no-op when
persistence is off. You can also call `element.clearPersisted()` directly.

## Data shapes

```ts
interface SelectionRange {
  startOffset: number;
  endOffset: number;
  selectedText: string;
  matchIndex: number;
}

interface ReviewComment {
  commentId: string;
  targetId: string;        // matches a data-review-id / data-review-selectable value
  content: string;
  createdAt: string;       // ISO timestamp
  selection?: SelectionRange; // present for selection-level comments
}

type ReviewResult =
  | { status: "approved" }
  | { status: "commented"; comments: ReviewComment[]; overallComment?: string };

interface ReviewSubmitDetail {
  result: ReviewResult;
  clearPersisted: () => void;
}
```

## Dependencies

`xstate`, `@floating-ui/dom`, `tinykeys`. This package ships ESM only; consume
this component via `import "@llumi/design-system/review"`.
