Classes
AperturaErrorfrom @apertura/core
class AperturaError extends Error
Base class for every error raised by Apertura.
A common ancestor lets consumers distinguish "the file failed to open" from
a genuine bug in their own code with a single `instanceof` check.
code
string
Stable machine-readable code; unaffected by message wording changes.
ContentDocumentfrom @apertura/extract
class ContentDocument
An extracted document: the tree, and every way of writing it down.
The serialisers are methods rather than free functions for one reason: the
addresses. A caller who has the tree and reaches for a `render` from
somewhere else gets text with no way back into the document, which is the
thing this package exists to prevent. Everything that produces text from here
can also produce the map beside it.
walk
() => Generator<Block>
Every block in reading order, parents before children.
sections
readonly Block[]
The top-level sections: slides, sheets, or the parts of a document.
toPlainText
{ (options?: TextOptions & { withMap?: false; }): string; (options: TextOptions & { withMap: true; }): { text: string; map: OffsetMap; }; }
toPlainText
{ (options?: TextOptions & { withMap?: false; }): string; (options: TextOptions & { withMap: true; }): { text: string; map: OffsetMap; }; }
toMarkdown
{ (options?: MarkdownOptions & { withMap?: false; }): string; (options: MarkdownOptions & { withMap: true; }): { markdown: string; map: OffsetMap; }; }
toMarkdown
{ (options?: MarkdownOptions & { withMap?: false; }): string; (options: MarkdownOptions & { withMap: true; }): { markdown: string; map: OffsetMap; }; }
toHtml
{ (options?: HtmlOptions & { withMap?: false; }): string; (options: HtmlOptions & { withMap: true; }): { html: string; map: OffsetMap; }; }
toHtml
{ (options?: HtmlOptions & { withMap?: false; }): string; (options: HtmlOptions & { withMap: true; }): { html: string; map: OffsetMap; }; }
toJSON
() => ContentDocumentJson
chunks
(options?: ChunkOptions) => Chunk[]
Structure-aware chunks, each carrying the selectors to find it again.
selectorsFor
(text: string, map: OffsetMap, start: number, end: number, output?: OutputKind) => Selector[]
Selectors for a range of one of this document's text outputs.
The bridge for everybody who chunked the text themselves. Give it the
offsets a splitter reported, the map that produced them and which output
they came from, and it gives back the anchor triple: exact, portable and
robust.
CorruptFileErrorfrom @apertura/core
class CorruptFileError extends AperturaError
The file was identified, but its contents violate the format specification.
offset
number | undefined
Byte offset where the violation was found, when known.
DocumentSearch
class DocumentSearch
currentIndex
number
Index of the hit currently marked as active, or -1.
find
(query: string, options?: SearchOptions) => readonly SearchHit[]
Finds every occurrence and highlights all of them.
All of them, not the visible ones: a highlight is a registered intention,
so the four hundredth hit is already painted by the time the reader scrolls
to it. That is what makes "next" instant on a long document.
next
() => number
Moves to the next hit, wrapping. Returns its index, or -1.
show
(selectors: readonly Selector[], options?: HighlightOptions) => HighlightHandle
Highlights a passage identified by stored selectors, and scrolls to it.
The other half of the loop: a chunk extracted months ago, embedded, stored,
retrieved by a question, and now shown in the document it came from.
FormatRegistryfrom @apertura/core
class FormatRegistry
Registry of parsers.
An instance rather than a global singleton: a single page may host several
independently configured viewers, and tests must not see each other's
registrations. {@link defaultRegistry} is available for simple cases.
Renderers are registered in `ViewRegistry` from `@apertura/render`, which
extends this class. The split is what lets a server open a document without
the DOM appearing anywhere in the type graph.
registerParser
<TDocument extends AperturaDocument>(plugin: ParserPlugin<TDocument>) => this
getParser
(format: FormatId) => ParserPlugin | undefined
supportedFormats
() => FormatId[]
Formats that have a registered parser.
open
(source: ByteSource, options?: OpenOptions) => Promise<AperturaDocument>
Detects the format and opens the document with the matching plugin.
When detection returns `unknown` (the typical case for a ZIP container) the
registered plugins are polled through `canOpen`, so docx, xlsx and pptx sort
themselves out without the core knowing anything about their internals.
HighlightRegistryfrom @apertura/highlight
class HighlightRegistry
add
(selectors: readonly Selector[], options?: HighlightOptions) => HighlightHandle
Registers a highlight and paints whatever of it is currently on the page.
Resolution happens once, here, rather than on every repaint: a scroll
through a document with four hundred hits would otherwise re-resolve four
hundred anchors per frame, and quote resolution scans the whole text.
highlights
readonly HighlightHandle[]
Every registered highlight, painted or not.
get
(id: string) => HighlightHandle | undefined
refresh
() => void
Repaints from the DOM as it is now.
Cheap enough to call on every scroll: the work is one selector lookup and
one range construction per highlight whose block is mounted, and none at
all for the rest. Coalesced to an animation frame so a burst of mount
events costs one repaint.
scrollTo
(id: string, options?: ScrollIntoViewOptions) => boolean
Brings a highlight into view.
Returns false when the highlight is not on the page and the host has given
no way to get there. That is not a failure to report as an error — it is
the ordinary state of a highlight nine hundred pages away — but the caller
has to know, because "scroll to the answer" that silently does nothing is
worse than a message saying the document has moved on.
pageOf
((block: Locator) => number | undefined) | undefined
Set by the host: which page a block landed on after layout.
onScrollToPage
((page: number) => void) | undefined
Set by the host: scroll to a page so its content mounts.
NotImplementedErrorfrom @apertura/core
class NotImplementedError extends AperturaError
A format feature that has not been implemented yet.
A dedicated type lets the viewer show "this part of the document is not
supported yet" instead of a generic read failure.
UnsupportedFormatErrorfrom @apertura/core
class UnsupportedFormatError extends AperturaError
The file format was not recognised, or no plugin is registered for it.
Viewer
class Viewer
A framework-independent document viewer.
The single place that knows the full path from bytes to format to document to
DOM. The React, Vue and Angular wrappers are thin adapters over this class, so
their behaviour is identical by construction rather than by convention.
view
DocumentView | undefined
The live view, for reaching format-specific APIs such as page navigation.
subscribe
(listener: (state: ViewerState) => void) => () => void
Subscribes to state changes. Returns an unsubscribe function.
open
(input: ByteSourceInput, options?: ViewerOptions) => Promise<void>
Opens a file and mounts its view.
Calling it again before the previous call settles is correct: the older
result is discarded. That is the normal case when a user picks files in
quick succession.
refresh
() => void
Re-renders the current view.
close
() => void
Closes the document and clears the container.
destroy
() => void
Releases every resource. The instance must not be used afterwards.
ViewRegistryfrom @apertura/render
class ViewRegistry extends FormatRegistry
A format registry that also knows how to draw.
Parsers are registered in `@apertura/core`, which knows nothing about the
browser; renderers are registered here. Subclassing rather than composing
keeps the fluent chain a caller expects — `registerParser` returns `this`,
so a registry can be built in one expression — while leaving the parser half
usable on a server that has no DOM at all.
registerView
<TDocument extends AperturaDocument>(plugin: ViewPlugin<TDocument>) => this
getView
(format: FormatId) => ViewPlugin | undefined
viewableFormats
() => FormatId[]
Formats that have both a parser and a renderer.
Functions
createDefaultRegistry
function createDefaultRegistry(): ViewRegistry
A registry with every supported format registered.
The convenient entry point for an application that just needs to open a file.
When bundle size matters, build a {@link ViewRegistry} by hand so only the
parsers actually used are included.
createDocxRegistry
function createDocxRegistry(): ViewRegistry
A registry holding only the Word format, for applications that need just that.
createSearch
function createSearch(container: HTMLElement, source: ByteSourceInput, options?: ExtractOptions): Promise<DocumentSearch>
Builds the search index for a document already on screen.
The bytes are wanted but not required. With them the addresses carry a hash
and a stored anchor from a different file is refused rather than resolved to
whatever sits at that path; without them everything still works and simply
cannot warn.
createViewer
function createViewer(container: HTMLElement, options?: ViewerOptions): Viewer
Creates a viewer mounted into the given element.
describeFormatfrom @apertura/core
function describeFormat(id: FormatId): FormatDescriptor | undefined
detectFormatfrom @apertura/core
function detectFormat(source: ByteSource): Promise<DetectionResult>
Determines the file format from its contents, name and MIME type.
For ZIP containers the result is always `probable`: telling docx, xlsx, pptx
and odt apart requires looking inside the archive, which is the job of
`@apertura/ooxml`. The core deliberately avoids pulling in decompression
just to detect a format.
findTextfrom @apertura/extract
function findText(document: ContentDocument, query: string, options?: { caseSensitive?: boolean; wholeWord?: boolean; limit?: number; }): { range: { start: Locator; end: Locator; }; text: string; }[]
Plain search over a document, returning anchors rather than offsets.
What the viewer's find box is built on. Everything it returns is a selector
set, so a hit found here highlights through exactly the same path as a chunk
retrieved from a vector database — one mechanism, not two.
fromDocumentfrom @apertura/extract
function fromDocument(document: AperturaDocument, bytes: Uint8Array | undefined, options?: ExtractOptions): Promise<ContentDocument>
Read the content of a document that is already open.
The viewer's path. A hash is computed from the bytes when they are to hand
and omitted otherwise — an address with no hash still resolves, it simply
cannot warn that it came from a different file.
resolveSelectorsfrom @apertura/extract
function resolveSelectors(document: ContentDocument, selectors: readonly Selector[], options?: ResolveOptions): ResolvedRange | undefined
toByteSourcefrom @apertura/core
function toByteSource(input: ByteSourceInput): Promise<ByteSource>
Normalises any supported input into a {@link ByteSource}. Strings are URLs.
Interfaces
AperturaDocumentfrom @apertura/core
interface AperturaDocument
An opened document: the contract shared by every parser.
Deliberately narrow — it only carries what is meaningful for any format.
Everything else (docx sections, xlsx sheets, pptx slides) lives in subtypes
inside the format packages. The viewer works against this interface so that
it can still show a title and a page count for a format whose renderer is
not registered.
pageCount?
number | undefined
Number of pages/sheets/slides, when the format exposes it cheaply.
For docx this is `undefined` until the document has been laid out: splitting
a text flow into pages depends on fonts, hyphenation and the printable area.
extractText
() => Promise<string>
Extracts the whole document text for search, indexing and previews.
A dedicated method because this is the one operation every format needs in
the same way and which requires no rendering.
dispose
() => void
Releases retained resources. Calling it twice is safe.
ByteSourcefrom @apertura/core
interface ByteSource
A random-access source of bytes.
This is the central abstraction of the project: parsers never touch `File`,
`Blob` or the network directly. That makes it possible to read a ZIP central
directory at the end of a file, or a PDF xref table, without pulling the whole
document into memory, and to run the same parser in a browser, in Node, or on
top of HTTP range requests.
byteLength
number
Total size of the source in bytes.
name?
string | undefined
File name when known. Used as a hint for format detection.
mimeType?
string | undefined
MIME type when the source reports one.
slice
(start: number, end?: number) => Promise<Uint8Array>
Reads the `[start, end)` range.
Implementations must return exactly the requested number of bytes or throw
{@link OutOfBoundsError}. Short reads are not allowed, otherwise every
parser would have to re-check the length after each call.
dispose?
(() => void) | undefined
Releases retained resources such as caches or network connections.
Chunkfrom @apertura/extract
interface Chunk
text
string
The chunk as stored and embedded, contextualised if that was asked for.
contextLength
number
How much of `text` is the prepended heading path.
selectors
readonly Selector[]
Where to find it again: exact, portable and robust, in that order.
breadcrumbs
readonly string[]
The heading path above it.
section
string | undefined
`Slide 5`, `Sheet Budget`, or the section's label.
start
number
Offsets into the serialised output this came from.
ChunkOptionsfrom @apertura/extract
interface ChunkOptions
Chunking that knows what a document is.
The state of the art in JavaScript is to take the text, split it every 512
characters, and hope. That destroys exactly the things retrieval depends on:
a table loses its header three rows in, a heading is separated from the
section it names, a sentence is cut in half, and every chunk arrives at the
index with no idea where it came from.
This walks the tree instead. Headings become breadcrumbs and stay with their
content, a table row is never split, a table too big for one chunk is split
by rows with its header repeated in each, and every chunk carries the
selectors that find it again in the document it came from — which is the part
nobody else has, and the reason a retrieval hit can be highlighted rather
than merely quoted.
maxTokens?
number | undefined
Target size. Default 512.
overlap?
number | undefined
Overlap between neighbours, in tokens. Default 64.
tokenCounter?
((text: string) => number) | undefined
How to count. Default: characters over four.
Pluggable because the right answer depends on a model this package has
never heard of, and a default that shipped a tokenizer would be a
megabyte of tables for something the caller can do in one line.
contextualize?
boolean | undefined
Prepend the heading path to each chunk's text. Default true.
"Q3 Results › Risks › Currency exposure" in front of a paragraph that says
"the position was closed in October" is the difference between a chunk that
retrieves and one that does not. The context is marked in `contextLength`
so a caller that wants the bare text can strip it.
tables?
"whole" | "rows" | undefined
`whole` keeps a table together; `rows` splits large ones. Default `rows`.
source?
"text" | "markdown" | undefined
Which output the chunk text comes from. Default `markdown`.
DetectionResultfrom @apertura/core
interface DetectionResult
Result of format detection.
confidence
"certain" | "probable" | "guess"
How much the detector can be trusted.
`certain` — an unambiguous signature matched; `probable` — the container was
identified and the concrete format was inferred from the extension or MIME
type and still needs confirmation from the contents; `guess` — no signature
matched and the decision rests on the file name alone.
reason
string
What led to the decision — useful when debugging third-party files.
DocumentMetadatafrom @apertura/core
interface DocumentMetadata
Metadata common to every format.
keywords?
readonly string[] | undefined
producer?
string | undefined
The application that produced the file.
language?
string | undefined
Language of the main content, BCP 47.
custom?
Readonly<Record<string, string | number | boolean | Date>> | undefined
Format-specific fields that do not fit the common schema.
DocumentViewfrom @apertura/render
interface DocumentView
A live view of a document mounted into the DOM.
update
() => void
Re-render after the container was resized or settings changed.
destroy
() => void
Tear the view down and release resources. The container is left empty.
HighlightHandlefrom @apertura/highlight
interface HighlightHandle
resolvedBy
"locator" | "position" | "quote" | "native" | undefined
How the address was found: exact, portable, robust — or not at all.
fragments
readonly HighlightFragment[]
What is on the page right now. Empty while the pages are elsewhere.
HighlightOptionsfrom @apertura/highlight
interface HighlightOptions extends HighlightStyle
Highlights, as state rather than as an operation.
This is the design decision virtualisation forces, and it is not a detail.
Only the pages near the viewport exist in the DOM — that is what lets a
thousand-page document open as quickly as a five-page one — so "highlight
this passage" cannot mean "find it and paint it". The passage is usually not
there yet.
So a highlight is a registered intention. The registry keeps it, paints what
is currently on the page, and repaints when the page changes: a scroll, a
zoom, a mount. Add a highlight on page four hundred of an unopened document
and nothing visible happens until page four hundred arrives, at which point
it is already there.
The other thing that falls out of this: a highlight is a *list of fragments*,
never a rectangle. A paragraph split between page three and page four gives
two groups of rectangles on two pages, and an API that promised one would
have to be rewritten the first time somebody highlighted a long quotation.
id?
string | undefined
A caller-chosen id. Adding twice with the same id replaces the first.
data?
unknown
Carried through to the caller; the registry does not read it.
HtmlOptionsfrom @apertura/extract
interface HtmlOptions
Semantic HTML: the structure, not the appearance.
Not a renderer, and the distinction is the whole design. `@apertura/docx-view`
reproduces what a document looks like — fonts, page boxes, measured line
breaks. This produces what it *is*: headings that are headings, tables that
are tables, a `<figure>` around a picture and its caption. There is no CSS
and no colour, because the consumer is a language model, a search index, or a
page that has its own stylesheet and does not want this one.
Every element carries its address in `data-loc`, which is what makes an HTML
extraction round-trip: a click in the rendered output can be turned back into
a place in the document.
locators?
boolean | undefined
Write `data-loc` on every element that has an address. Default true.
figures?
boolean | undefined
`<figure>`/`<figcaption>` around images. Default true.
sections?
boolean | undefined
`<section>` per slide, sheet or document section. Default true.
wrap?
boolean | undefined
Wrap the output in `<article>`. Default false.
footnotes?
boolean | undefined
Footnotes as a `<section class="footnotes">` at the end. Default true.
MarkdownOptionsfrom @apertura/extract
interface MarkdownOptions
Markdown, written properly.
The bar is not "produces something a renderer accepts". Every library in this
space clears that. The bar is that a person reading the output can tell what
the document said, and a language model reading it does not have to guess —
which means the table alignment survives, the nested list stays nested, a
pipe inside a cell does not end the column, a paragraph starting with `1.`
does not silently become a list, and a footnote is a footnote rather than a
number floating in the middle of a sentence.
Every one of those is a bug this had at some point.
tables?
"html" | "list" | "gfm" | undefined
`gfm` for pipe tables, `html` for a `<table>`, `list` for one line a row.
`html` is not a cop-out: a pipe table cannot express a row span, and a
merged cell rendered as a pipe table is silently wrong in a way nobody
notices. `list` is for tables so wide that either of the others is
unreadable — a workbook of forty columns, most often.
tableColumnLimit?
number | undefined
Widest table, in columns, still worth a pipe table. Default 12.
images?
"text" | "link" | "omit" | "alt" | undefined
`alt` writes `![alt]()`; `link` writes a real path; `omit` drops them;
`text` writes just the alt text with no image syntax at all.
footnotes?
boolean | undefined
`[^1]` footnotes plus a section at the end. Default true.
comments?
boolean | undefined
Comments as footnotes too, marked with the author. Default false.
frontMatter?
boolean | undefined
A YAML block of the document metadata at the top. Default false.
sections?
"none" | "heading" | "rule" | undefined
`---` between sections, and a heading naming each. Default `heading`.
bullet?
"-" | "*" | "+" | undefined
`*` or `-`. Default `-`.
nativeMarkers?
boolean | undefined
Keep the document's own list markers instead of `1.` and `-`.
On by default, and it is the right default: Word's `%1.%2` patterns produce
`2.3.1`, which three levels of markdown nesting cannot express, and a
numbered list that restarts partway down is invisible to a counter. The
marker is `derived` text and anchors to the item.
math?
boolean | undefined
`$…$` for maths. Default true where a LaTeX form is available.
marks?
boolean | undefined
Emphasis, strikethrough and the rest. Default true.
OpenOptionsfrom @apertura/core
interface OpenOptions
Options shared by every parser.
signal?
AbortSignal | undefined
Aborts parsing of large files.
tolerant?
boolean | undefined
Keep parsing when the file locally violates the specification.
Defaults to `true`: real files produced by office suites break the standard
routinely, and failing the whole document where a single paragraph could be
dropped is a bad trade.
password?
string | undefined
Password for encrypted documents.
onProgress?
((fraction: number) => void) | undefined
Progress callback, 0..1.
ResolvedRangefrom @apertura/core
interface ResolvedRange
A resolution, and how much to trust it.
`resolvedBy` is not decoration. An application that watches it can see its
documents drifting — the day the exact selectors stop matching and everything
falls through to quotes is the day somebody started editing the corpus — and
it can see that before a user reports a highlight in the wrong place.
resolvedBy
"locator" | "position" | "quote" | "native"
confidence
number
1 for an exact match, lower for a fuzzy one.
SearchHit
interface SearchHit
SearchOptions
interface SearchOptions
Search and highlighting, over one mechanism rather than two.
The temptation is to write a find box that walks the DOM looking for a
string, which every viewer has, and which is wrong here for three reasons: it
cannot see the pages that are not mounted, it finds the list numbers and page
numbers the renderer computed, and what it produces is a DOM node rather than
an address — so a hit cannot be stored, sent anywhere, or found again after a
relayout.
Instead the search runs over the extracted text, which the extractor can
produce in the browser because it depends on nothing that is not there. A hit
is a range of that text, which is an address, which is a highlight. The same
path a chunk retrieved from a vector database takes.
So `find` and `showChunk` are the same operation with different inputs, and
neither had to be built twice.
limit?
number | undefined
Stop after this many hits. Default 1000.
TextOptionsfrom @apertura/extract
interface TextOptions
Plain text: the output with nothing added.
The one that has to be genuinely plain. It is what goes into a search index,
a diff, a `grep`, and every pipeline whose next stage is not a markdown
parser — and every character of markup in it is a false hit waiting to
happen. So the only characters here that are not from the document are the
separators between blocks, and a caller who wants none of those can say so.
separator?
string | undefined
Between blocks. Default `\n`.
sectionBreaks?
boolean | undefined
An extra newline between sections. Default true.
sectionLabels?
boolean | undefined
Name each section (`Slide 5`) before its content. Default true.
tables?
"tab" | "align" | "lines" | undefined
How a table row is written. Default `tab`.
`tab` keeps the columns machine-readable — a row is still a record — and
`align` pads them so a person can read the table in a terminal. `align`
costs a second pass over the table to measure it.
markers?
boolean | undefined
Include list markers. Default true.
annotations?
boolean | undefined
Append footnote and comment text at the end. Default true.
ViewerOptions
interface ViewerOptions extends ViewOptions, OpenOptions
registry?
ViewRegistry | undefined
The plugin set to use. Defaults to every supported format.
Pass a custom registry to keep unused parsers out of the bundle.
renderOptions?
Record<string, unknown> | undefined
Extra options forwarded to the format renderer.
ViewerState
interface ViewerState
document
AperturaDocument | undefined
detection
DetectionResult | undefined
progress
number
Parse progress, 0..1.
ViewOptionsfrom @apertura/render
interface ViewOptions
zoom?
number | undefined
Rendering scale, 1 means 100%.
fit?
"page" | "none" | "width" | undefined
How the page should be fitted into the container.
initialPage?
number | undefined
Initial page/sheet/slide, zero-based.
ViewPluginfrom @apertura/render
interface ViewPlugin<TDocument extends AperturaDocument = AperturaDocument>
A renderer plugin: turns a parsed document into DOM.
mount
(document: TDocument, container: HTMLElement, options?: ViewOptions) => Promise<DocumentView>
Type aliases
Blockfrom @apertura/extract
type Block = SectionBlock | HeadingBlock | ParagraphBlock | ListBlock | ListItemBlock | TableBlock | TableRowBlock | TableCellBlock | ImageBlock | ChartBlock | DiagramBlock | MathBlock | BlockquoteBlock | CodeBlock | BreakBlock
ByteSourceInputfrom @apertura/core
type ByteSourceInput = ByteSource | Blob | ArrayBuffer | Uint8Array | string
Everything Apertura can turn into a {@link ByteSource}.
DocumentKindfrom @apertura/core
type DocumentKind = 'text-document'
/** A grid of cells: xlsx, ods, csv. */
| 'spreadsheet'
/** A sequence of slides: pptx, odp. */
| 'presentation'
/** Fixed page layout: pdf. */
| 'paged-document'
/** A raster or vector image. */
| 'image'
Broad document category; determines which viewer applies.
FormatIdfrom @apertura/core
type FormatId = 'docx' | 'xlsx' | 'pptx' | 'doc' | 'xls' | 'ppt' | 'pdf' | 'odt' | 'ods' | 'odp' | 'txt' | 'md' | 'csv' | 'json' | 'xml' | 'html' | 'png' | 'jpeg' | 'gif' | 'webp' | 'bmp' | 'tiff' | 'svg' | 'unknown'
Identifier of a concrete file format.
A string literal union rather than an enum: the values are part of the public
API, get serialised to JSON, and are used as registry keys.
Locatorfrom @apertura/core
type Locator = string
The opaque wire form. Parse it with {@link parseLocator}.
Selectorfrom @apertura/core
type Selector = LocatorSelector | TextPositionSelector | TextQuoteSelector | SheetSelector | SlideSelector
ViewerStatus
type ViewerStatus = 'idle' | 'loading' | 'ready' | 'error'
Viewer state; UI wrappers render indicators from it.
Values
docxParserfrom @apertura/docx
docxParser: ParserPlugin<DocxDocument>
The docx parser plugin for {@link FormatRegistry }.
`canOpen` looks inside the ZIP: the extension cannot be trusted, and every
OOXML format shares one signature. Opening the package for the check is cheap
— only the central directory and `[Content_Types].xml` are read — and the
result is cached by the archive, so the subsequent `open` pays nothing twice.
docxViewfrom @apertura/docx-view
docxView: ViewPlugin<DocxDocument>
The docx renderer plugin for {@link FormatRegistry }.
HIGHLIGHT_CSSfrom @apertura/highlight
HIGHLIGHT_CSS: "\n.apertura-highlight {\n --apertura-highlight-fill: rgb(255 213 0 / 0.42);\n background: var(--apertura-highlight-fill);\n border-radius: 2px;\n}\n\n.apertura-highlight--active {\n --apertura-highlight-fill: rgb(255 145 0 / 0.55);\n outline: 1px solid rgb(255 145 0 / 0.9);\n}\n\n::highlight(apertura-find) {\n background-color: rgb(255 213 0 / 0.42);\n}\n\n::highlight(apertura-find-active) {\n background-color: rgb(255 145 0 / 0.6);\n}\n"
The default look, for a host that would rather not write any.
Injected by the caller rather than by the registry: a viewer embedded in an
application has a design system, and a package that put a stylesheet in the
head without being asked would be fighting it.
The colours are stated as custom properties so overriding one does not mean
copying the rule. `::highlight()` accepts a short list of properties —
colour, background, decoration and shadow — and nothing that affects layout,
which is the point of it: a highlight cannot move the text it marks.
pptxParserfrom @apertura/pptx
pptxParser: ParserPlugin<PptxDocument>
pptxViewfrom @apertura/pptx-view
pptxView: ViewPlugin<PptxDocument>
xlsxParserfrom @apertura/xlsx
xlsxParser: ParserPlugin<XlsxDocument>
xlsxViewfrom @apertura/xlsx-view
xlsxView: ViewPlugin<XlsxDocument>