wolfy-reader 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +277 -0
- package/dist/core/book.d.ts +75 -0
- package/dist/core/book.d.ts.map +1 -0
- package/dist/core/book.js +2 -0
- package/dist/core/book.js.map +1 -0
- package/dist/core/errors.d.ts +17 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +29 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/format.d.ts +13 -0
- package/dist/core/format.d.ts.map +1 -0
- package/dist/core/format.js +2 -0
- package/dist/core/format.js.map +1 -0
- package/dist/core/graphemes.d.ts +36 -0
- package/dist/core/graphemes.d.ts.map +1 -0
- package/dist/core/graphemes.js +62 -0
- package/dist/core/graphemes.js.map +1 -0
- package/dist/core/index.d.ts +11 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/lookup.d.ts +10 -0
- package/dist/core/lookup.d.ts.map +1 -0
- package/dist/core/lookup.js +16 -0
- package/dist/core/lookup.js.map +1 -0
- package/dist/core/open.d.ts +12 -0
- package/dist/core/open.d.ts.map +1 -0
- package/dist/core/open.js +23 -0
- package/dist/core/open.js.map +1 -0
- package/dist/core/position.d.ts +121 -0
- package/dist/core/position.d.ts.map +1 -0
- package/dist/core/position.js +327 -0
- package/dist/core/position.js.map +1 -0
- package/dist/core/reading-text.d.ts +77 -0
- package/dist/core/reading-text.d.ts.map +1 -0
- package/dist/core/reading-text.js +144 -0
- package/dist/core/reading-text.js.map +1 -0
- package/dist/core/source.d.ts +17 -0
- package/dist/core/source.d.ts.map +1 -0
- package/dist/core/source.js +39 -0
- package/dist/core/source.js.map +1 -0
- package/dist/core/storage.d.ts +7 -0
- package/dist/core/storage.d.ts.map +1 -0
- package/dist/core/storage.js +2 -0
- package/dist/core/storage.js.map +1 -0
- package/dist/core/text.d.ts +18 -0
- package/dist/core/text.d.ts.map +1 -0
- package/dist/core/text.js +32 -0
- package/dist/core/text.js.map +1 -0
- package/dist/formats/epub/href.d.ts +14 -0
- package/dist/formats/epub/href.d.ts.map +1 -0
- package/dist/formats/epub/href.js +52 -0
- package/dist/formats/epub/href.js.map +1 -0
- package/dist/formats/epub/index.d.ts +3 -0
- package/dist/formats/epub/index.d.ts.map +1 -0
- package/dist/formats/epub/index.js +223 -0
- package/dist/formats/epub/index.js.map +1 -0
- package/dist/formats/epub/opf.d.ts +27 -0
- package/dist/formats/epub/opf.d.ts.map +1 -0
- package/dist/formats/epub/opf.js +110 -0
- package/dist/formats/epub/opf.js.map +1 -0
- package/dist/formats/epub/toc.d.ts +6 -0
- package/dist/formats/epub/toc.d.ts.map +1 -0
- package/dist/formats/epub/toc.js +88 -0
- package/dist/formats/epub/toc.js.map +1 -0
- package/dist/formats/fb2/index.d.ts +13 -0
- package/dist/formats/fb2/index.d.ts.map +1 -0
- package/dist/formats/fb2/index.js +317 -0
- package/dist/formats/fb2/index.js.map +1 -0
- package/dist/formats/index.d.ts +4 -0
- package/dist/formats/index.d.ts.map +1 -0
- package/dist/formats/index.js +4 -0
- package/dist/formats/index.js.map +1 -0
- package/dist/formats/text/index.d.ts +11 -0
- package/dist/formats/text/index.d.ts.map +1 -0
- package/dist/formats/text/index.js +201 -0
- package/dist/formats/text/index.js.map +1 -0
- package/dist/formats/xml.d.ts +33 -0
- package/dist/formats/xml.d.ts.map +1 -0
- package/dist/formats/xml.js +321 -0
- package/dist/formats/xml.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/layout/chunk.d.ts +51 -0
- package/dist/layout/chunk.d.ts.map +1 -0
- package/dist/layout/chunk.js +131 -0
- package/dist/layout/chunk.js.map +1 -0
- package/dist/layout/index.d.ts +238 -0
- package/dist/layout/index.d.ts.map +1 -0
- package/dist/layout/index.js +476 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/reader/index.d.ts +231 -0
- package/dist/reader/index.d.ts.map +1 -0
- package/dist/reader/index.js +958 -0
- package/dist/reader/index.js.map +1 -0
- package/dist/reader/input.d.ts +69 -0
- package/dist/reader/input.d.ts.map +1 -0
- package/dist/reader/input.js +75 -0
- package/dist/reader/input.js.map +1 -0
- package/dist/search/extract.d.ts +51 -0
- package/dist/search/extract.d.ts.map +1 -0
- package/dist/search/extract.js +401 -0
- package/dist/search/extract.js.map +1 -0
- package/dist/search/index.d.ts +11 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +11 -0
- package/dist/search/index.js.map +1 -0
- package/dist/search/matcher.d.ts +56 -0
- package/dist/search/matcher.d.ts.map +1 -0
- package/dist/search/matcher.js +128 -0
- package/dist/search/matcher.js.map +1 -0
- package/dist/search/normalize.d.ts +48 -0
- package/dist/search/normalize.d.ts.map +1 -0
- package/dist/search/normalize.js +103 -0
- package/dist/search/normalize.js.map +1 -0
- package/dist/view/allowlist.d.ts +59 -0
- package/dist/view/allowlist.d.ts.map +1 -0
- package/dist/view/allowlist.js +198 -0
- package/dist/view/allowlist.js.map +1 -0
- package/dist/view/appearance.d.ts +137 -0
- package/dist/view/appearance.d.ts.map +1 -0
- package/dist/view/appearance.js +274 -0
- package/dist/view/appearance.js.map +1 -0
- package/dist/view/css.d.ts +24 -0
- package/dist/view/css.d.ts.map +1 -0
- package/dist/view/css.js +153 -0
- package/dist/view/css.js.map +1 -0
- package/dist/view/frame.d.ts +54 -0
- package/dist/view/frame.d.ts.map +1 -0
- package/dist/view/frame.js +980 -0
- package/dist/view/frame.js.map +1 -0
- package/dist/view/host.d.ts +160 -0
- package/dist/view/host.d.ts.map +1 -0
- package/dist/view/host.js +385 -0
- package/dist/view/host.js.map +1 -0
- package/dist/view/index.d.ts +12 -0
- package/dist/view/index.d.ts.map +1 -0
- package/dist/view/index.js +7 -0
- package/dist/view/index.js.map +1 -0
- package/dist/view/protocol.d.ts +220 -0
- package/dist/view/protocol.d.ts.map +1 -0
- package/dist/view/protocol.js +246 -0
- package/dist/view/protocol.js.map +1 -0
- package/dist/view/reference.d.ts +18 -0
- package/dist/view/reference.d.ts.map +1 -0
- package/dist/view/reference.js +25 -0
- package/dist/view/reference.js.map +1 -0
- package/dist/view/resources.d.ts +51 -0
- package/dist/view/resources.d.ts.map +1 -0
- package/dist/view/resources.js +242 -0
- package/dist/view/resources.js.map +1 -0
- package/dist/view/sanitize.d.ts +32 -0
- package/dist/view/sanitize.d.ts.map +1 -0
- package/dist/view/sanitize.js +226 -0
- package/dist/view/sanitize.js.map +1 -0
- package/dist/zip/central.d.ts +12 -0
- package/dist/zip/central.d.ts.map +1 -0
- package/dist/zip/central.js +72 -0
- package/dist/zip/central.js.map +1 -0
- package/dist/zip/crc32.d.ts +3 -0
- package/dist/zip/crc32.d.ts.map +1 -0
- package/dist/zip/crc32.js +28 -0
- package/dist/zip/crc32.js.map +1 -0
- package/dist/zip/eocd.d.ts +9 -0
- package/dist/zip/eocd.d.ts.map +1 -0
- package/dist/zip/eocd.js +84 -0
- package/dist/zip/eocd.js.map +1 -0
- package/dist/zip/errors.d.ts +19 -0
- package/dist/zip/errors.d.ts.map +1 -0
- package/dist/zip/errors.js +37 -0
- package/dist/zip/errors.js.map +1 -0
- package/dist/zip/index.d.ts +18 -0
- package/dist/zip/index.d.ts.map +1 -0
- package/dist/zip/index.js +78 -0
- package/dist/zip/index.js.map +1 -0
- package/dist/zip/inflate.d.ts +2 -0
- package/dist/zip/inflate.d.ts.map +1 -0
- package/dist/zip/inflate.js +43 -0
- package/dist/zip/inflate.js.map +1 -0
- package/dist/zip/source.d.ts +15 -0
- package/dist/zip/source.d.ts.map +1 -0
- package/dist/zip/source.js +34 -0
- package/dist/zip/source.js.map +1 -0
- package/package.json +75 -0
- package/src/core/book.ts +80 -0
- package/src/core/errors.ts +31 -0
- package/src/core/format.ts +14 -0
- package/src/core/graphemes.ts +73 -0
- package/src/core/index.ts +27 -0
- package/src/core/lookup.ts +16 -0
- package/src/core/open.ts +31 -0
- package/src/core/position.ts +485 -0
- package/src/core/reading-text.ts +200 -0
- package/src/core/source.ts +61 -0
- package/src/core/storage.ts +6 -0
- package/src/core/text.ts +35 -0
- package/src/formats/epub/href.ts +54 -0
- package/src/formats/epub/index.ts +235 -0
- package/src/formats/epub/opf.ts +143 -0
- package/src/formats/epub/toc.ts +120 -0
- package/src/formats/fb2/index.ts +348 -0
- package/src/formats/index.ts +3 -0
- package/src/formats/text/index.ts +208 -0
- package/src/formats/xml.ts +326 -0
- package/src/index.ts +26 -0
- package/src/layout/chunk.ts +169 -0
- package/src/layout/index.ts +573 -0
- package/src/reader/index.ts +1206 -0
- package/src/reader/input.ts +122 -0
- package/src/search/extract.ts +403 -0
- package/src/search/index.ts +10 -0
- package/src/search/matcher.ts +187 -0
- package/src/search/normalize.ts +122 -0
- package/src/view/allowlist.ts +273 -0
- package/src/view/appearance.ts +380 -0
- package/src/view/css.ts +172 -0
- package/src/view/frame.ts +1018 -0
- package/src/view/host.ts +527 -0
- package/src/view/index.ts +24 -0
- package/src/view/protocol.ts +412 -0
- package/src/view/reference.ts +30 -0
- package/src/view/resources.ts +265 -0
- package/src/view/sanitize.ts +266 -0
- package/src/zip/central.ts +85 -0
- package/src/zip/crc32.ts +30 -0
- package/src/zip/eocd.ts +97 -0
- package/src/zip/errors.ts +41 -0
- package/src/zip/index.ts +114 -0
- package/src/zip/inflate.ts +41 -0
- package/src/zip/source.ts +51 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookup.js","sourceRoot":"","sources":["../../src/core/lookup.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,QAA4B;IACxD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAmB,CAAC;IACxC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Book } from './book.ts';
|
|
2
|
+
import type { BookFormat } from './format.ts';
|
|
3
|
+
import type { BookInput } from './source.ts';
|
|
4
|
+
import type { StorageAdapter } from './storage.ts';
|
|
5
|
+
export interface OpenOptions {
|
|
6
|
+
/** Tried in order; the first format whose sniff claims the input decodes it. */
|
|
7
|
+
readonly formats: readonly BookFormat[];
|
|
8
|
+
readonly storage?: StorageAdapter;
|
|
9
|
+
}
|
|
10
|
+
/** Bytes in, Book out: runs the registered sniffers in order and decodes with the first claimant. */
|
|
11
|
+
export declare function open(input: BookInput, options: OpenOptions): Promise<Book>;
|
|
12
|
+
//# sourceMappingURL=open.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open.d.ts","sourceRoot":"","sources":["../../src/core/open.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAInD,MAAM,WAAW,WAAW;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;IACxC,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC;CACnC;AAED,qGAAqG;AACrG,wBAAsB,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { BookError, CorruptContainerError, UnrecognizedFormatError } from './errors.js';
|
|
2
|
+
import { toByteSource } from './source.js';
|
|
3
|
+
/** Bytes in, Book out: runs the registered sniffers in order and decodes with the first claimant. */
|
|
4
|
+
export async function open(input, options) {
|
|
5
|
+
const source = toByteSource(input);
|
|
6
|
+
const context = options.storage === undefined ? {} : { storage: options.storage };
|
|
7
|
+
for (const format of options.formats) {
|
|
8
|
+
if (!(await format.sniff(source)))
|
|
9
|
+
continue;
|
|
10
|
+
try {
|
|
11
|
+
return await format.decode(source, context);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (error instanceof BookError)
|
|
15
|
+
throw error;
|
|
16
|
+
throw new CorruptContainerError(`${format.name} failed to decode the input`, {
|
|
17
|
+
cause: error,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
throw new UnrecognizedFormatError('no registered format recognizes this input');
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=open.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open.js","sourceRoot":"","sources":["../../src/core/open.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAQ3C,qGAAqG;AACrG,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,KAAgB,EAAE,OAAoB;IAC/D,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,OAAO,GACX,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;IACpE,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAAE,SAAS;QAC5C,IAAI,CAAC;YACH,OAAO,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,SAAS;gBAAE,MAAM,KAAK,CAAC;YAC5C,MAAM,IAAI,qBAAqB,CAAC,GAAG,MAAM,CAAC,IAAI,6BAA6B,EAAE;gBAC3E,KAAK,EAAE,KAAK;aACb,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,MAAM,IAAI,uBAAuB,CAAC,4CAA4C,CAAC,CAAC;AAClF,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A content-addressed anchor into a section's text, Hypothesis-style. A match is
|
|
3
|
+
* found by content (the exact quote plus surrounding context), never by offset:
|
|
4
|
+
* `offset` exists only to break ties between identical quotes and is never
|
|
5
|
+
* load-bearing on its own.
|
|
6
|
+
*/
|
|
7
|
+
export interface TextAnchor {
|
|
8
|
+
/** The verbatim run of text the position points at. */
|
|
9
|
+
readonly exact: string;
|
|
10
|
+
/** Text immediately before `exact`, bounded length; boundary-snapped. */
|
|
11
|
+
readonly prefix: string;
|
|
12
|
+
/** Text immediately after `exact`, bounded length; boundary-snapped. */
|
|
13
|
+
readonly suffix: string;
|
|
14
|
+
/**
|
|
15
|
+
* Grapheme-cluster offset of `exact` within the section text. A tiebreak only —
|
|
16
|
+
* used to pick among duplicate quotes, never to locate a match alone.
|
|
17
|
+
*/
|
|
18
|
+
readonly offset: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A reading position within a book. Its `serialized` form is an opaque string a
|
|
22
|
+
* host persists verbatim and never parses; `progress` rides alongside for host
|
|
23
|
+
* UI (scrollbars, "42%") without decoding the string. Backed by a content anchor
|
|
24
|
+
* so it survives reflow, font-size, and layout changes.
|
|
25
|
+
*/
|
|
26
|
+
export interface Position {
|
|
27
|
+
/** The section this position falls in. */
|
|
28
|
+
readonly sectionId: string;
|
|
29
|
+
/** Fraction through the section, 0..1. For host progress UI only. */
|
|
30
|
+
readonly progress: number;
|
|
31
|
+
/** Opaque, versioned, round-trippable interchange string. Hosts store it as-is. */
|
|
32
|
+
readonly serialized: string;
|
|
33
|
+
/** The content anchor. Resolution matches on this, not on `progress`. */
|
|
34
|
+
readonly anchor: TextAnchor;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Where a `Position` resolved to in a piece of target text. Offsets are grapheme
|
|
38
|
+
* indices into that text, matching `TextAnchor.offset`. A resolution *miss* is not
|
|
39
|
+
* this type — it is `undefined`, returned by `resolvePosition`.
|
|
40
|
+
*/
|
|
41
|
+
export interface ResolvedPosition {
|
|
42
|
+
readonly sectionId: string;
|
|
43
|
+
/** Grapheme index where the matched quote starts in the target text. */
|
|
44
|
+
readonly offset: number;
|
|
45
|
+
/** Length of the matched quote in graphemes. */
|
|
46
|
+
readonly length: number;
|
|
47
|
+
}
|
|
48
|
+
export interface CapturePositionOptions {
|
|
49
|
+
/**
|
|
50
|
+
* Locale for `Intl.Segmenter` word snapping. Defaults to the runtime default
|
|
51
|
+
* locale. Grapheme snapping is locale-independent.
|
|
52
|
+
*/
|
|
53
|
+
readonly locale?: string;
|
|
54
|
+
/** Maximum length (in graphemes) of the captured quote. */
|
|
55
|
+
readonly quoteLength?: number;
|
|
56
|
+
/** Maximum length (in graphemes) of each context window. */
|
|
57
|
+
readonly contextLength?: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Captures a `Position` at `offset` in a section's plain text. Core is headless:
|
|
61
|
+
* the caller supplies already-extracted text and a UTF-16 code-unit offset into
|
|
62
|
+
* it; no DOM, HTML, or Range is touched here. The offset is snapped to grapheme
|
|
63
|
+
* and word boundaries with `Intl.Segmenter` so an anchor never splits a surrogate
|
|
64
|
+
* pair, combining sequence, or emoji cluster.
|
|
65
|
+
*/
|
|
66
|
+
export declare function capturePosition(text: string, offset: number, sectionId: string, options?: CapturePositionOptions): Position;
|
|
67
|
+
/** Serializes a position to its opaque, versioned interchange string. */
|
|
68
|
+
export declare function serializePosition(position: Position): string;
|
|
69
|
+
/** One sentence of a section, with a `Position` anchored over its whole span. */
|
|
70
|
+
export interface SentenceRange {
|
|
71
|
+
/** The sentence text, trailing whitespace trimmed. */
|
|
72
|
+
readonly text: string;
|
|
73
|
+
/**
|
|
74
|
+
* A `Position` whose anchored quote is the whole sentence, so `resolvePosition`
|
|
75
|
+
* returns its full span — a host can `goTo` it (jump) and `decorate` it
|
|
76
|
+
* (highlight the entire sentence, not just its start).
|
|
77
|
+
*/
|
|
78
|
+
readonly position: Position;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Segments a section's plain text into sentences with `Intl.Segmenter`, each
|
|
82
|
+
* carrying a `Position` anchored over the whole sentence. This is the TTS *enabler*
|
|
83
|
+
* (PLAN M3-6): the library exposes sentence ranges and reuses `decorate` as the
|
|
84
|
+
* highlight primitive so a host can build text-to-speech on top — it speaks nothing
|
|
85
|
+
* and stores nothing. Whitespace-only segments are dropped. Headless: the caller
|
|
86
|
+
* supplies already-extracted text, exactly like `capturePosition`.
|
|
87
|
+
*
|
|
88
|
+
* `Intl.Segmenter` follows UAX #29, which ends a "sentence" at every hard line
|
|
89
|
+
* break — but the reading text is section `textContent`, whose source newlines the
|
|
90
|
+
* rendered page collapses via `white-space:normal`. Segmenting it verbatim would
|
|
91
|
+
* split one visual sentence at every wrapped source line (a heading or short line
|
|
92
|
+
* becoming a two-word "sentence"). So adjacent Intl segments are merged until the
|
|
93
|
+
* run actually ends on sentence-terminal punctuation; the anchor still spans the
|
|
94
|
+
* raw text (newlines included) so it content-matches the frame's section text,
|
|
95
|
+
* while the returned `text` collapses whitespace runs to single spaces for a clean
|
|
96
|
+
* TTS string. Abbreviations (`J. R. R.`) still split — that break is Intl's, and a
|
|
97
|
+
* period genuinely can end a sentence, so we do not second-guess it.
|
|
98
|
+
*/
|
|
99
|
+
export declare function segmentSentences(text: string, sectionId: string, options?: CapturePositionOptions): SentenceRange[];
|
|
100
|
+
/**
|
|
101
|
+
* Parses a serialized position back into a `Position`. Round-trips losslessly
|
|
102
|
+
* with `serializePosition`. Throws `CorruptContainerError` when the string is
|
|
103
|
+
* malformed or carries an unknown version — a version this reader cannot honor is
|
|
104
|
+
* a structural failure the host catches by class, distinct from a resolution miss
|
|
105
|
+
* (which the resolver returns as a value, never an error).
|
|
106
|
+
*/
|
|
107
|
+
export declare function parsePosition(serialized: string): Position;
|
|
108
|
+
/**
|
|
109
|
+
* Resolves a `Position` against a piece of section text — which may have changed
|
|
110
|
+
* since capture — returning where its anchor now lands, or `undefined` when the
|
|
111
|
+
* quote is gone. A miss is a value, never a throw: matching live content is an
|
|
112
|
+
* expected outcome the caller degrades on (skip the restore, drop the decoration),
|
|
113
|
+
* mirroring `Section.resolve()` returning `undefined`.
|
|
114
|
+
*
|
|
115
|
+
* The match is by content. Every occurrence of the exact quote is found, then
|
|
116
|
+
* scored by how much of the stored prefix/suffix context still surrounds it; the
|
|
117
|
+
* best-scoring occurrence wins. The stored offset breaks a tie only when context
|
|
118
|
+
* cannot — it never selects a location the content contradicts.
|
|
119
|
+
*/
|
|
120
|
+
export declare function resolvePosition(position: Position, text: string): ResolvedPosition | undefined;
|
|
121
|
+
//# sourceMappingURL=position.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"position.d.ts","sourceRoot":"","sources":["../../src/core/position.ts"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,mFAAmF;IACnF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAmBD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,sBAA2B,GACnC,QAAQ,CAEV;AAwDD,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAE5D;AAED,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,sBAA2B,GACnC,aAAa,EAAE,CAwCjB;AAyBD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,CA8B1D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,GACX,gBAAgB,GAAG,SAAS,CA8B9B"}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { CorruptContainerError } from './errors.js';
|
|
2
|
+
import { codeUnitOffsetToGraphemeIndex, countGraphemes, segmentGraphemes, } from './graphemes.js';
|
|
3
|
+
const DEFAULT_QUOTE_LENGTH = 32;
|
|
4
|
+
const DEFAULT_CONTEXT_LENGTH = 32;
|
|
5
|
+
/** Version tag that leads every serialized position. Version-first, non-negotiable. */
|
|
6
|
+
const SERIAL_PREFIX = 'wr1:';
|
|
7
|
+
const SERIAL_VERSION = 1;
|
|
8
|
+
/**
|
|
9
|
+
* Captures a `Position` at `offset` in a section's plain text. Core is headless:
|
|
10
|
+
* the caller supplies already-extracted text and a UTF-16 code-unit offset into
|
|
11
|
+
* it; no DOM, HTML, or Range is touched here. The offset is snapped to grapheme
|
|
12
|
+
* and word boundaries with `Intl.Segmenter` so an anchor never splits a surrogate
|
|
13
|
+
* pair, combining sequence, or emoji cluster.
|
|
14
|
+
*/
|
|
15
|
+
export function capturePosition(text, offset, sectionId, options = {}) {
|
|
16
|
+
return captureSegmented(segmentText(text), offset, sectionId, options);
|
|
17
|
+
}
|
|
18
|
+
function segmentText(text) {
|
|
19
|
+
return { text, graphemes: segmentGraphemes(text), wordStarts: null };
|
|
20
|
+
}
|
|
21
|
+
/** `capturePosition` over an already-segmented text; behavior is identical. */
|
|
22
|
+
function captureSegmented(segmented, offset, sectionId, options) {
|
|
23
|
+
const { graphemes } = segmented;
|
|
24
|
+
const total = graphemes.length;
|
|
25
|
+
const rawIndex = codeUnitOffsetToGraphemeIndex(graphemes, offset);
|
|
26
|
+
const start = snapToWordStart(segmented, rawIndex, options.locale);
|
|
27
|
+
const quoteLength = options.quoteLength ?? DEFAULT_QUOTE_LENGTH;
|
|
28
|
+
const contextLength = options.contextLength ?? DEFAULT_CONTEXT_LENGTH;
|
|
29
|
+
const quoteEnd = Math.min(start + quoteLength, total);
|
|
30
|
+
const prefixStart = Math.max(start - contextLength, 0);
|
|
31
|
+
const suffixEnd = Math.min(quoteEnd + contextLength, total);
|
|
32
|
+
const anchor = {
|
|
33
|
+
exact: join(graphemes, start, quoteEnd),
|
|
34
|
+
prefix: join(graphemes, prefixStart, start),
|
|
35
|
+
suffix: join(graphemes, quoteEnd, suffixEnd),
|
|
36
|
+
offset: start,
|
|
37
|
+
};
|
|
38
|
+
const progress = total === 0 ? 0 : start / total;
|
|
39
|
+
return {
|
|
40
|
+
sectionId,
|
|
41
|
+
progress,
|
|
42
|
+
anchor,
|
|
43
|
+
serialized: serializeParts(sectionId, progress, anchor),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Serializes a position to its opaque, versioned interchange string. */
|
|
47
|
+
export function serializePosition(position) {
|
|
48
|
+
return serializeParts(position.sectionId, position.progress, position.anchor);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Segments a section's plain text into sentences with `Intl.Segmenter`, each
|
|
52
|
+
* carrying a `Position` anchored over the whole sentence. This is the TTS *enabler*
|
|
53
|
+
* (PLAN M3-6): the library exposes sentence ranges and reuses `decorate` as the
|
|
54
|
+
* highlight primitive so a host can build text-to-speech on top — it speaks nothing
|
|
55
|
+
* and stores nothing. Whitespace-only segments are dropped. Headless: the caller
|
|
56
|
+
* supplies already-extracted text, exactly like `capturePosition`.
|
|
57
|
+
*
|
|
58
|
+
* `Intl.Segmenter` follows UAX #29, which ends a "sentence" at every hard line
|
|
59
|
+
* break — but the reading text is section `textContent`, whose source newlines the
|
|
60
|
+
* rendered page collapses via `white-space:normal`. Segmenting it verbatim would
|
|
61
|
+
* split one visual sentence at every wrapped source line (a heading or short line
|
|
62
|
+
* becoming a two-word "sentence"). So adjacent Intl segments are merged until the
|
|
63
|
+
* run actually ends on sentence-terminal punctuation; the anchor still spans the
|
|
64
|
+
* raw text (newlines included) so it content-matches the frame's section text,
|
|
65
|
+
* while the returned `text` collapses whitespace runs to single spaces for a clean
|
|
66
|
+
* TTS string. Abbreviations (`J. R. R.`) still split — that break is Intl's, and a
|
|
67
|
+
* period genuinely can end a sentence, so we do not second-guess it.
|
|
68
|
+
*/
|
|
69
|
+
export function segmentSentences(text, sectionId, options = {}) {
|
|
70
|
+
const out = [];
|
|
71
|
+
// Segment graphemes (and, lazily, words) once for the whole text; every
|
|
72
|
+
// sentence capture reuses the same arrays instead of re-segmenting — this is
|
|
73
|
+
// what keeps a long chapter linear instead of quadratic.
|
|
74
|
+
const segmented = segmentText(text);
|
|
75
|
+
// Accumulate a run of Intl segments and its code-unit start until the run ends on
|
|
76
|
+
// sentence-terminal punctuation, then anchor over the raw span. A run start of -1
|
|
77
|
+
// means no run is open (all leading/inter-sentence whitespace is skipped).
|
|
78
|
+
let runStart = -1;
|
|
79
|
+
let runText = '';
|
|
80
|
+
const flush = () => {
|
|
81
|
+
const trimmed = runText.replace(/\s+$/u, '');
|
|
82
|
+
if (trimmed.trim().length !== 0) {
|
|
83
|
+
const quoteLength = countGraphemes(trimmed);
|
|
84
|
+
// Anchor over the whole (raw, newline-bearing) run so decorate highlights all
|
|
85
|
+
// of it against the frame's section text.
|
|
86
|
+
const position = captureSegmented(segmented, runStart, sectionId, {
|
|
87
|
+
...options,
|
|
88
|
+
quoteLength,
|
|
89
|
+
});
|
|
90
|
+
out.push({ text: collapseWhitespace(trimmed), position });
|
|
91
|
+
}
|
|
92
|
+
runStart = -1;
|
|
93
|
+
runText = '';
|
|
94
|
+
};
|
|
95
|
+
for (const segment of sentenceSegmenter(options.locale).segment(text)) {
|
|
96
|
+
if (segment.segment.trim().length === 0) {
|
|
97
|
+
// Whitespace-only Intl segment: part of an open run, else inter-sentence space.
|
|
98
|
+
if (runStart >= 0)
|
|
99
|
+
runText += segment.segment;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (runStart < 0)
|
|
103
|
+
runStart = segment.index;
|
|
104
|
+
runText += segment.segment;
|
|
105
|
+
if (endsOnSentenceTerminal(runText))
|
|
106
|
+
flush();
|
|
107
|
+
}
|
|
108
|
+
if (runStart >= 0)
|
|
109
|
+
flush();
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
/** Sentence-final punctuation, incl. CJK and other-script terminators. */
|
|
113
|
+
const SENTENCE_TERMINAL = /[.!?…。!?؟।]/u;
|
|
114
|
+
/** Trailing closing punctuation a terminator may hide behind (quotes, brackets). */
|
|
115
|
+
const TRAILING_CLOSERS = /[)\]}"'”’»›]+$/u;
|
|
116
|
+
/**
|
|
117
|
+
* Whether a run, ignoring trailing whitespace and closing punctuation, ends on a
|
|
118
|
+
* sentence terminator — the seam that decides a real sentence break from a mere
|
|
119
|
+
* line wrap. `"the lazy dog."` and `"he said 'go.'"` end a sentence; `"The quick
|
|
120
|
+
* brown\n"` (a wrapped line) does not.
|
|
121
|
+
*/
|
|
122
|
+
function endsOnSentenceTerminal(run) {
|
|
123
|
+
const core = run.replace(/\s+$/u, '').replace(TRAILING_CLOSERS, '');
|
|
124
|
+
const chars = [...core];
|
|
125
|
+
const last = chars[chars.length - 1];
|
|
126
|
+
return last !== undefined && SENTENCE_TERMINAL.test(last);
|
|
127
|
+
}
|
|
128
|
+
/** Collapses every run of whitespace to a single space and trims the ends. */
|
|
129
|
+
function collapseWhitespace(text) {
|
|
130
|
+
return text.replace(/\s+/gu, ' ').trim();
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Parses a serialized position back into a `Position`. Round-trips losslessly
|
|
134
|
+
* with `serializePosition`. Throws `CorruptContainerError` when the string is
|
|
135
|
+
* malformed or carries an unknown version — a version this reader cannot honor is
|
|
136
|
+
* a structural failure the host catches by class, distinct from a resolution miss
|
|
137
|
+
* (which the resolver returns as a value, never an error).
|
|
138
|
+
*/
|
|
139
|
+
export function parsePosition(serialized) {
|
|
140
|
+
if (!serialized.startsWith(SERIAL_PREFIX)) {
|
|
141
|
+
throw new CorruptContainerError(`not a wolfy-reader position: missing '${SERIAL_PREFIX}' version prefix`);
|
|
142
|
+
}
|
|
143
|
+
let payload;
|
|
144
|
+
try {
|
|
145
|
+
payload = JSON.parse(serialized.slice(SERIAL_PREFIX.length));
|
|
146
|
+
}
|
|
147
|
+
catch (cause) {
|
|
148
|
+
throw new CorruptContainerError('position payload is not valid JSON', { cause });
|
|
149
|
+
}
|
|
150
|
+
if (!isSerialPayload(payload)) {
|
|
151
|
+
throw new CorruptContainerError('position payload is missing required fields');
|
|
152
|
+
}
|
|
153
|
+
if (payload.v !== SERIAL_VERSION) {
|
|
154
|
+
throw new CorruptContainerError(`unsupported position version ${payload.v}; this reader understands version ${SERIAL_VERSION}`);
|
|
155
|
+
}
|
|
156
|
+
const anchor = {
|
|
157
|
+
exact: payload.e,
|
|
158
|
+
prefix: payload.pre,
|
|
159
|
+
suffix: payload.suf,
|
|
160
|
+
offset: payload.o,
|
|
161
|
+
};
|
|
162
|
+
return { sectionId: payload.s, progress: payload.p, anchor, serialized };
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Resolves a `Position` against a piece of section text — which may have changed
|
|
166
|
+
* since capture — returning where its anchor now lands, or `undefined` when the
|
|
167
|
+
* quote is gone. A miss is a value, never a throw: matching live content is an
|
|
168
|
+
* expected outcome the caller degrades on (skip the restore, drop the decoration),
|
|
169
|
+
* mirroring `Section.resolve()` returning `undefined`.
|
|
170
|
+
*
|
|
171
|
+
* The match is by content. Every occurrence of the exact quote is found, then
|
|
172
|
+
* scored by how much of the stored prefix/suffix context still surrounds it; the
|
|
173
|
+
* best-scoring occurrence wins. The stored offset breaks a tie only when context
|
|
174
|
+
* cannot — it never selects a location the content contradicts.
|
|
175
|
+
*/
|
|
176
|
+
export function resolvePosition(position, text) {
|
|
177
|
+
const { anchor, sectionId } = position;
|
|
178
|
+
const graphemes = segmentGraphemes(text);
|
|
179
|
+
if (anchor.exact === '') {
|
|
180
|
+
const offset = Math.min(anchor.offset, graphemes.length);
|
|
181
|
+
return { sectionId, offset, length: 0 };
|
|
182
|
+
}
|
|
183
|
+
const occurrences = findGraphemeOccurrences(graphemes, anchor.exact);
|
|
184
|
+
if (occurrences.length === 0)
|
|
185
|
+
return undefined;
|
|
186
|
+
const quoteLength = segmentGraphemes(anchor.exact).length;
|
|
187
|
+
// Segment the stored context once, outside the occurrence loop.
|
|
188
|
+
const prefix = segmentGraphemes(anchor.prefix).map((g) => g.segment);
|
|
189
|
+
const suffix = segmentGraphemes(anchor.suffix).map((g) => g.segment);
|
|
190
|
+
let best;
|
|
191
|
+
for (const offset of occurrences) {
|
|
192
|
+
const score = contextScore(graphemes, offset, quoteLength, prefix, suffix);
|
|
193
|
+
if (best === undefined ||
|
|
194
|
+
score > best.score ||
|
|
195
|
+
(score === best.score &&
|
|
196
|
+
Math.abs(offset - anchor.offset) < Math.abs(best.offset - anchor.offset))) {
|
|
197
|
+
best = { offset, score };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return { sectionId, offset: best.offset, length: quoteLength };
|
|
201
|
+
}
|
|
202
|
+
/** Grapheme start indices at which `quote` occurs in `graphemes`. */
|
|
203
|
+
function findGraphemeOccurrences(graphemes, quote) {
|
|
204
|
+
const quoteGraphemes = segmentGraphemes(quote).map((g) => g.segment);
|
|
205
|
+
const hits = [];
|
|
206
|
+
if (quoteGraphemes.length === 0)
|
|
207
|
+
return hits;
|
|
208
|
+
const last = graphemes.length - quoteGraphemes.length;
|
|
209
|
+
for (let start = 0; start <= last; start += 1) {
|
|
210
|
+
let matched = true;
|
|
211
|
+
for (let offset = 0; offset < quoteGraphemes.length; offset += 1) {
|
|
212
|
+
if (graphemes[start + offset].segment !== quoteGraphemes[offset]) {
|
|
213
|
+
matched = false;
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (matched)
|
|
218
|
+
hits.push(start);
|
|
219
|
+
}
|
|
220
|
+
return hits;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Count of trailing prefix graphemes + leading suffix graphemes that still match.
|
|
224
|
+
* `prefix`/`suffix` are the anchor's context pre-segmented into grapheme strings —
|
|
225
|
+
* segmented once by the caller, never per occurrence.
|
|
226
|
+
*/
|
|
227
|
+
function contextScore(graphemes, quoteStart, quoteLength, prefix, suffix) {
|
|
228
|
+
let score = 0;
|
|
229
|
+
for (let back = 1; back <= prefix.length; back += 1) {
|
|
230
|
+
const here = graphemes[quoteStart - back];
|
|
231
|
+
const want = prefix[prefix.length - back];
|
|
232
|
+
if (here === undefined || here.segment !== want)
|
|
233
|
+
break;
|
|
234
|
+
score += 1;
|
|
235
|
+
}
|
|
236
|
+
const afterStart = quoteStart + quoteLength;
|
|
237
|
+
for (let ahead = 0; ahead < suffix.length; ahead += 1) {
|
|
238
|
+
const here = graphemes[afterStart + ahead];
|
|
239
|
+
if (here === undefined || here.segment !== suffix[ahead])
|
|
240
|
+
break;
|
|
241
|
+
score += 1;
|
|
242
|
+
}
|
|
243
|
+
return score;
|
|
244
|
+
}
|
|
245
|
+
function serializeParts(sectionId, progress, anchor) {
|
|
246
|
+
const payload = {
|
|
247
|
+
v: SERIAL_VERSION,
|
|
248
|
+
s: sectionId,
|
|
249
|
+
p: progress,
|
|
250
|
+
e: anchor.exact,
|
|
251
|
+
pre: anchor.prefix,
|
|
252
|
+
suf: anchor.suffix,
|
|
253
|
+
o: anchor.offset,
|
|
254
|
+
};
|
|
255
|
+
return SERIAL_PREFIX + JSON.stringify(payload);
|
|
256
|
+
}
|
|
257
|
+
function isSerialPayload(value) {
|
|
258
|
+
if (typeof value !== 'object' || value === null)
|
|
259
|
+
return false;
|
|
260
|
+
const record = value;
|
|
261
|
+
return (typeof record.v === 'number' &&
|
|
262
|
+
typeof record.s === 'string' &&
|
|
263
|
+
typeof record.p === 'number' &&
|
|
264
|
+
typeof record.e === 'string' &&
|
|
265
|
+
typeof record.pre === 'string' &&
|
|
266
|
+
typeof record.suf === 'string' &&
|
|
267
|
+
typeof record.o === 'number');
|
|
268
|
+
}
|
|
269
|
+
const wordSegmenters = new Map();
|
|
270
|
+
const sentenceSegmenters = new Map();
|
|
271
|
+
function sentenceSegmenter(locale) {
|
|
272
|
+
const key = locale ?? '';
|
|
273
|
+
let segmenter = sentenceSegmenters.get(key);
|
|
274
|
+
if (segmenter === undefined) {
|
|
275
|
+
segmenter = new Intl.Segmenter(locale, { granularity: 'sentence' });
|
|
276
|
+
sentenceSegmenters.set(key, segmenter);
|
|
277
|
+
}
|
|
278
|
+
return segmenter;
|
|
279
|
+
}
|
|
280
|
+
function wordSegmenter(locale) {
|
|
281
|
+
const key = locale ?? '';
|
|
282
|
+
let segmenter = wordSegmenters.get(key);
|
|
283
|
+
if (segmenter === undefined) {
|
|
284
|
+
segmenter = new Intl.Segmenter(locale, { granularity: 'word' });
|
|
285
|
+
wordSegmenters.set(key, segmenter);
|
|
286
|
+
}
|
|
287
|
+
return segmenter;
|
|
288
|
+
}
|
|
289
|
+
function join(graphemes, start, end) {
|
|
290
|
+
let out = '';
|
|
291
|
+
for (let index = start; index < end; index += 1)
|
|
292
|
+
out += graphemes[index].segment;
|
|
293
|
+
return out;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Snaps a grapheme index to the start of the word segment it falls in, so a
|
|
297
|
+
* captured quote begins on a word boundary rather than mid-token. Word segments
|
|
298
|
+
* tile the text, so the containing segment is the one with the greatest start
|
|
299
|
+
* offset <= the grapheme's offset — a binary search over the word-start array,
|
|
300
|
+
* which is built once per `SegmentedText` and reused by every capture.
|
|
301
|
+
*/
|
|
302
|
+
function snapToWordStart(segmented, index, locale) {
|
|
303
|
+
const { graphemes } = segmented;
|
|
304
|
+
if (index <= 0 || index >= graphemes.length)
|
|
305
|
+
return index;
|
|
306
|
+
if (segmented.wordStarts === null) {
|
|
307
|
+
const starts = [];
|
|
308
|
+
for (const word of wordSegmenter(locale).segment(segmented.text))
|
|
309
|
+
starts.push(word.index);
|
|
310
|
+
segmented.wordStarts = starts;
|
|
311
|
+
}
|
|
312
|
+
const starts = segmented.wordStarts;
|
|
313
|
+
const unitOffset = graphemes[index].index;
|
|
314
|
+
// Greatest word start <= unitOffset. starts[0] is 0 and unitOffset > 0 here,
|
|
315
|
+
// so the search never underflows.
|
|
316
|
+
let low = 0;
|
|
317
|
+
let high = starts.length - 1;
|
|
318
|
+
while (low < high) {
|
|
319
|
+
const mid = (low + high + 1) >>> 1;
|
|
320
|
+
if (starts[mid] <= unitOffset)
|
|
321
|
+
low = mid;
|
|
322
|
+
else
|
|
323
|
+
high = mid - 1;
|
|
324
|
+
}
|
|
325
|
+
return codeUnitOffsetToGraphemeIndex(graphemes, starts[low]);
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=position.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"position.js","sourceRoot":"","sources":["../../src/core/position.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The canonical "reading text" policy — the single definition of which parts of a
|
|
3
|
+
* section's markup become the text a reader actually sees in the frame.
|
|
4
|
+
*
|
|
5
|
+
* Two independent consumers must agree on it, and this module is how they stay one
|
|
6
|
+
* model rather than two:
|
|
7
|
+
*
|
|
8
|
+
* - `src/view` sanitizes and applies resources before rendering: discarded elements
|
|
9
|
+
* vanish with their children (`sanitize.ts`), and an `<img>` that cannot be served
|
|
10
|
+
* is replaced by its `alt` text (`resources.ts`). The frame's measured section
|
|
11
|
+
* text — what a `Position` resolves against — is the `textContent` of that output.
|
|
12
|
+
* - `src/search` captures hit anchors headlessly, without a DOM, over the same text.
|
|
13
|
+
* Its extractor (`src/search/extract.ts`) mirrors the pipeline using the tables
|
|
14
|
+
* and predicates here, so a captured quote/context window exists verbatim in the
|
|
15
|
+
* text the frame resolves against.
|
|
16
|
+
*
|
|
17
|
+
* Internal to the library: nothing here is re-exported from `src/core/index.ts`
|
|
18
|
+
* (same status as `graphemes.ts`), so it is shared implementation, not public
|
|
19
|
+
* surface. It is headless by rule — string tables and string predicates only —
|
|
20
|
+
* because `src/search` may import only `src/core` and platform primitives.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* HTML elements the sanitizer removes with everything inside them, because their
|
|
24
|
+
* children are not prose: raw-text elements whose content is source rather than
|
|
25
|
+
* text, form controls, and void elements with no children to keep. Everything
|
|
26
|
+
* else absent from the allowlist is unwrapped instead — the element goes, its
|
|
27
|
+
* text stays. The unwrap-vs-discard rationale lives with the allowlist
|
|
28
|
+
* (`src/view/allowlist.ts`), which re-exports these under their frozen names.
|
|
29
|
+
*/
|
|
30
|
+
export declare const DISCARDED_HTML_ELEMENTS: ReadonlySet<string>;
|
|
31
|
+
/**
|
|
32
|
+
* SVG elements discarded whole. SMIL animation is on this list, not merely absent
|
|
33
|
+
* from the allowlist, because `<animate attributeName="href" to="javascript:...">`
|
|
34
|
+
* turns an inert element into a live one after sanitization has already run.
|
|
35
|
+
*/
|
|
36
|
+
export declare const DISCARDED_SVG_ELEMENTS: ReadonlySet<string>;
|
|
37
|
+
export type ReferenceKind = 'empty' | 'fragment' | 'relative' | 'scheme';
|
|
38
|
+
export interface ClassifiedReference {
|
|
39
|
+
readonly kind: ReferenceKind;
|
|
40
|
+
/** The reference with URL-insignificant whitespace removed. */
|
|
41
|
+
readonly value: string;
|
|
42
|
+
/** Lowercased scheme name, present only when kind is 'scheme'. */
|
|
43
|
+
readonly scheme?: string;
|
|
44
|
+
}
|
|
45
|
+
export declare function classifyReference(raw: string): ClassifiedReference;
|
|
46
|
+
/** Collapses `.` and `..` segments, keeping leading `..` that cannot be resolved. */
|
|
47
|
+
export declare function normalizeReference(reference: string): string;
|
|
48
|
+
/** The bare media type: parameters trimmed, lowercased. */
|
|
49
|
+
export declare function baseMediaType(mediaType: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Only these media types are minted a `data:` URL by the resource layer. A
|
|
52
|
+
* reference resolving is not evidence it should be served: the seam answers for
|
|
53
|
+
* the whole manifest, so a `text/javascript` entry resolves as readily as an
|
|
54
|
+
* image — and a URL is a capability, not a convenience.
|
|
55
|
+
*/
|
|
56
|
+
export declare function isServableResource(mediaType: string): boolean;
|
|
57
|
+
/** The synchronous face of `Section.resolve` — all the image policy needs. */
|
|
58
|
+
export type ReadingResolver = (reference: string) => {
|
|
59
|
+
readonly mediaType: string;
|
|
60
|
+
} | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* The text an `<img>` contributes to the reading text: `''` when the element
|
|
63
|
+
* survives as an image (a served resource, or a remote/`data:` reference left for
|
|
64
|
+
* the CSP to refuse), or its `alt` when the resource layer substitutes the alt
|
|
65
|
+
* text for an image it cannot serve (`applyResources` in `src/view/resources.ts`
|
|
66
|
+
* — Gutenberg drop caps are the motivating case). An empty/absent `alt` on an
|
|
67
|
+
* unservable image means decorative: the element is removed and contributes
|
|
68
|
+
* nothing, which `''` also expresses.
|
|
69
|
+
*
|
|
70
|
+
* This is the headless mirror of the frame-side decision; the one edge it cannot
|
|
71
|
+
* see is a resource whose bytes fail to *load* after resolving servable — the
|
|
72
|
+
* frame substitutes alt there too, and a search anchor over that rare case
|
|
73
|
+
* degrades to the ordinary soft miss. Change this and the `applyResources` image
|
|
74
|
+
* branch together.
|
|
75
|
+
*/
|
|
76
|
+
export declare function imageReadingText(src: string | null, alt: string | null, resolve: ReadingResolver | undefined): string;
|
|
77
|
+
//# sourceMappingURL=reading-text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reading-text.d.ts","sourceRoot":"","sources":["../../src/core/reading-text.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,qBA0BnC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,qBAclC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEzE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAyBD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAOlE;AAED,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAa5D;AAcD,2DAA2D;AAC3D,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAG7D;AAED,8EAA8E;AAC9E,MAAM,MAAM,eAAe,GAAG,CAAC,SAAS,EAAE,MAAM,KAAK;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAC;AAEhG;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,GAAG,IAAI,EAClB,GAAG,EAAE,MAAM,GAAG,IAAI,EAClB,OAAO,EAAE,eAAe,GAAG,SAAS,GACnC,MAAM,CAQR"}
|