import type { ReactNode } from "react";
import type { SlotProps } from "../slotProps.ts";
export type MarkdownViewProps = SlotProps & {
className?: string;
/**
* The accessible name of the region, as `aria-label`.
*
* Worth passing on anything that is not the only thing on the
* page. A read view is announced as an article, and "article" with
* no name tells a reader stepping through the page nothing about
* which article it is.
*/
label?: string;
/**
* Called when the reader ticks a `- [ ]` box, with the **whole**
* next markdown document — the same shape the editors' `onChange`
* hands back, so the same autosave wires to both.
*
* **Passing it is what makes the checkboxes operable at all.**
* Leave it out and they render disabled: the state is still on
* screen, and nothing pretends the reader can change it. That is
* deliberate rather than defensive. Two of the three surfaces this
* component was built for — a rendered comment, and a repo file
* fetched at a commit hash — have nowhere to put a tick, and a
* checkbox that visibly toggles and then loses the change on
* reload is worse than one that says up front it cannot.
*
* The view still applies the tick to its own document
* immediately, so the box responds under the pointer. If the
* consumer rejects the change, the next `value` it passes puts it
* back.
*/
onToggleTask?: (nextValue: string) => void;
/**
* Re-point a link before it becomes an `href`, for a document
* whose paths mean something only the app knows.
*
* The case it exists for is a **relative** path in a fetched or
* imported file. `[the runbook](../docs/runbook.md)` is resolved
* by the browser against the page the reader is on — the app —
* so it lands on nothing. Return the URL it should point at
* instead, or `undefined` to leave the link exactly as written.
*
* The document's own URL has already been through the scheme
* guard when it arrives, so `javascript:` never reaches this.
* What comes back is the app's own string and is used as given.
*
* **Links only.** An image's `src` is deliberately not offered:
* an app that maps a document path to a page URL would turn a
* working image into a broken one.
*
* Read once, at mount, through a ref — so a resolver defined
* inline in a render does not rebuild the view and lose the
* reader's scroll position.
*/
resolveUrl?: (url: string) => string | undefined;
/**
* The markdown to render, and **controlled** — unlike either
* editor.
*
* The editors take `defaultValue` because reassigning their
* document from outside would throw away the undo history that
* belonged to it, which is a real cost and is why they ask for a
* `key` instead. A view has no undo history and no caret to
* protect, so it has nothing to lose by simply rendering what it
* is given: fetch a file at a different commit hash, pass the new
* string, and the page changes. No remount, no `key`, no stale
* document above a new title.
*/
value: string;
};
/**
* Rendered markdown, with no toolbar and nothing to type into.
*
* ## Why this is a component and not `isReadOnly` on the editors
*
* Both editors already have `isReadOnly`, and it means *an editor
* you cannot type in*: the nine formatting buttons are still in the
* DOM, still queryable, still in the accessibility tree, drawn in
* their disabled colours above a document nobody is editing. That is
* the right shape for a form field that is temporarily locked. It is
* the wrong shape for reading a file, and a `hasToolbar={false}`
* prop would not fix it — the type would still carry
* `onUploadImage`, `icons`, `placeholder` and `rawModeLabel`, none
* of which mean anything to a reader, and "no toolbar" would be a
* branch that could be got wrong rather than a thing that does not
* exist.
*
* So this is its own component, the way `Listbox`, `Combobox` and
* `Picker` are their own components rather than three modes of one.
* The props are the four a reader needs, and there is no code path
* in this file that could render a toolbar.
*
* ## Why it is in the CodeMirror subpath
*
* Because the requirement is that it renders **the same** as the
* editor a consumer edits the same document in — and the only way
* to guarantee that is to be the same code, not to be a second
* implementation that agrees today.
*
* A `react-markdown` reader in the main barrel was the obvious
* alternative and is the one that goes wrong slowly: two parsers,
* two table implementations, two answers to what a bare URL does,
* and a bug report six months later that says "the table changed
* when I saved it". This shares `livePreviewRanges.ts` — the 1,200
* lines that decide what a document *looks* like — with
* `MarkdownEditorCodeMirror`, and the theme with it too. There is no
* second renderer to keep in step.
*
* The cost is that it lives behind the same optional peers, so a
* consumer who wants only the reader still installs CodeMirror. That
* is the honest trade and it is the right way round: an app that
* renders markdown in three places and edits it in one should pay
* once.
*
* ## What differs from the editor, and why each one differs
*
* | | editor | this |
* | --- | --- | --- |
* | Toolbar | nine buttons | **none in the DOM** |
* | Value | `defaultValue`, uncontrolled | `value`, **controlled** |
* | Content role | `textbox` | **`article`** |
* | Headings | scaled text | scaled **and `role="heading"`** |
* | Links | painted span, opened by a handler | **real ``** |
* | Markers | revealed by the caret | never revealed |
* | Task boxes | always operable | **inert unless `onToggleTask`** |
*
* Every one of those follows from there being no caret. The editor
* paints a span instead of an anchor because an `` inside a
* `contenteditable` competes with the caret for the line; a document
* has no caret, so the link is simply a link — tabbable, activated
* by Enter, and copyable from the context menu. The editor reveals
* markup when the selection touches it because you cannot edit a URL
* you cannot see; there is nothing here to edit.
*
* ## Hostile markdown
*
* This renders files fetched from a git host, so the document is
* untrusted by default, and the answer is mostly structural rather
* than a sanitiser:
*
* - **Raw HTML in the source is never parsed as HTML.** There is no
* `innerHTML` in this subpath, no `dangerouslySetInnerHTML`, and
* no sanitiser to have a bug in. CodeMirror's state holds the
* source as *text*, and every rendered node is `createElement`
* plus `textContent`. A `