rapier-html 1.2.51 → 1.2.55
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/BUILD.json +4 -4
- package/README.md +10 -15
- package/dist/ledger/math/delimiters.mjs +56 -17
- package/dist/ledger/math/markdown-rules.mjs +22 -6
- package/dist/ledger/md-assets.mjs +39 -80
- package/dist/ledger/md-engine.mjs +650 -26
- package/dist/ledger/md-marks.mjs +15 -12
- package/dist/ledger/md-parser-config.mjs +175 -25
- package/dist/rapier.html +72 -61
- package/package.json +1 -1
package/BUILD.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "rapier-html",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.55",
|
|
4
4
|
"path": "dist/rapier.html",
|
|
5
|
-
"bytes":
|
|
6
|
-
"sha256": "
|
|
7
|
-
"builtAt": "2026-
|
|
5
|
+
"bytes": 6613729,
|
|
6
|
+
"sha256": "66dd1e87abc3cd368fdd5481ce4ad8816ea5235fb08f1788fbfaa67ed111581f",
|
|
7
|
+
"builtAt": "2026-11-03T23:25:11.656Z",
|
|
8
8
|
"node": "v22.23.4",
|
|
9
9
|
"canonical": true,
|
|
10
10
|
"packing": "zopfli",
|
package/README.md
CHANGED
|
@@ -2,13 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Turn Markdown into one offline HTML file that contains the full Rapier editor.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
<img src="https://raw.githubusercontent.com/jackskip22/rapier-plugins/main/assets/write-390-light.png" alt="The Rapier editor on a phone: a launch plan with an agent's change marked" width="320">
|
|
8
|
-
</picture>
|
|
9
|
-
|
|
10
|
-
Try it: `npx rapier-html@1.2.51 notes.md`, then open `notes.rapier.html` in any browser.
|
|
11
|
-
Install: `npm install rapier-html@1.2.51`.
|
|
5
|
+
Try it: `npx rapier-html@1.2.52 notes.md`, then open `notes.rapier.html` in any browser.
|
|
6
|
+
Install: `npm install rapier-html@1.2.52`.
|
|
12
7
|
|
|
13
8
|
It opens on a phone or computer, with no account. People read, edit, draw and paint; agents hand over documents, show
|
|
14
9
|
what changed and get the edited copy back.
|
|
@@ -27,14 +22,14 @@ The file needs no hosted workspace.
|
|
|
27
22
|
Needs Node 22 or newer.
|
|
28
23
|
|
|
29
24
|
```sh
|
|
30
|
-
npx -- rapier-html@1.2.
|
|
31
|
-
npx -- rapier-html@1.2.
|
|
32
|
-
npx -- rapier-html@1.2.
|
|
33
|
-
npx -- rapier-html@1.2.
|
|
34
|
-
npx -- rapier-html@1.2.
|
|
35
|
-
npx -- rapier-html@1.2.
|
|
36
|
-
npx -- rapier-html@1.2.
|
|
37
|
-
npm install rapier-html@1.2.
|
|
25
|
+
npx -- rapier-html@1.2.52 notes.md # the editor, notes.md inside it, written beside the file
|
|
26
|
+
npx -- rapier-html@1.2.52 notes.md --view draw # opens on Draw: sketch and paint, the document behind it
|
|
27
|
+
npx -- rapier-html@1.2.52 notes.md --include draw,math # carries Draw and Math (default: what the document needs)
|
|
28
|
+
npx -- rapier-html@1.2.52 notes.md --drawing sketch.svg # carries a drawing, opened on Draw over the document
|
|
29
|
+
npx -- rapier-html@1.2.52 revised.md --compare original.md # opens showing what changed
|
|
30
|
+
npx -- rapier-html@1.2.52 notes.md --return <return_url> --return-expires-at <return_expires_at> # Send back
|
|
31
|
+
npx -- rapier-html@1.2.52 notes.md out.html # a named output
|
|
32
|
+
npm install rapier-html@1.2.52 # as a library: writePage, unwrap
|
|
38
33
|
```
|
|
39
34
|
|
|
40
35
|
The first `--` is for npm, so `--help` reaches the command. It never overwrites a file.
|
|
@@ -1,32 +1,71 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
+
function countLines(source, from, to) {
|
|
3
|
+
let lines = 0;
|
|
4
|
+
for (let i = from; i < to; i++) {
|
|
5
|
+
const code = source.charCodeAt(i);
|
|
6
|
+
if (code === 0x0a || code === 0x0d) lines++;
|
|
7
|
+
}
|
|
8
|
+
return lines;
|
|
9
|
+
}
|
|
10
|
+
|
|
2
11
|
export function createMathMatcher(source, {singleDollar = false, brackets = true} = {}) {
|
|
3
12
|
if (typeof source !== 'string') throw new TypeError('Math boundaries require source text.');
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
const len = source.length;
|
|
14
|
+
if (len === 0 || (source.indexOf('$') < 0 && (!brackets || source.indexOf('\\') < 0))) {
|
|
15
|
+
return () => null;
|
|
16
|
+
}
|
|
17
|
+
const markers = new Map(), list = [], next = Object.create(null);
|
|
18
|
+
if (!brackets) {
|
|
19
|
+
let line = 0, lastAt = 0;
|
|
20
|
+
for (let at = source.indexOf('$'); at >= 0; at = source.indexOf('$', at)) {
|
|
21
|
+
line += countLines(source, lastAt, at);
|
|
22
|
+
let slashes = 0;
|
|
23
|
+
for (let s = at - 1; s >= 0 && source.charCodeAt(s) === 0x5c; s--) slashes++;
|
|
11
24
|
let end = at + 1;
|
|
12
|
-
while (source
|
|
25
|
+
while (end < len && source.charCodeAt(end) === 0x24) end++;
|
|
13
26
|
const count = end - at;
|
|
14
|
-
if (!(slashes & 1) && (count === 2 || count === 1 && singleDollar))
|
|
15
|
-
|
|
27
|
+
if (!(slashes & 1) && (count === 2 || count === 1 && singleDollar)) {
|
|
28
|
+
const entry = {at, marker: count === 2 ? '$$' : '$', line, close: undefined};
|
|
29
|
+
markers.set(at, entry);
|
|
30
|
+
list.push(entry);
|
|
31
|
+
}
|
|
32
|
+
lastAt = end;
|
|
16
33
|
at = end;
|
|
17
|
-
} else {
|
|
18
|
-
if (brackets && slashes & 1 && '()[]'.includes(char))
|
|
19
|
-
markers.set(at - 1, {at: at - 1, marker: '\\' + char, line});
|
|
20
|
-
at++;
|
|
21
34
|
}
|
|
22
|
-
|
|
35
|
+
} else {
|
|
36
|
+
let slashes = 0, line = 0;
|
|
37
|
+
for (let at = 0; at < len;) {
|
|
38
|
+
const code = source.charCodeAt(at);
|
|
39
|
+
if (code === 0x5c) { slashes++; at++; continue; }
|
|
40
|
+
if (code === 0x0a || code === 0x0d) line++;
|
|
41
|
+
if (code === 0x24) {
|
|
42
|
+
let end = at + 1;
|
|
43
|
+
while (end < len && source.charCodeAt(end) === 0x24) end++;
|
|
44
|
+
const count = end - at;
|
|
45
|
+
if (!(slashes & 1) && (count === 2 || count === 1 && singleDollar)) {
|
|
46
|
+
const entry = {at, marker: count === 2 ? '$$' : '$', line, close: undefined};
|
|
47
|
+
markers.set(at, entry);
|
|
48
|
+
list.push(entry);
|
|
49
|
+
}
|
|
50
|
+
at = end;
|
|
51
|
+
} else {
|
|
52
|
+
if (slashes & 1 && (code === 0x28 || code === 0x29 || code === 0x5b || code === 0x5d)) {
|
|
53
|
+
const entry = {at: at - 1, marker: '\\' + source[at], line, close: undefined};
|
|
54
|
+
markers.set(at - 1, entry);
|
|
55
|
+
list.push(entry);
|
|
56
|
+
}
|
|
57
|
+
at++;
|
|
58
|
+
}
|
|
59
|
+
slashes = 0;
|
|
60
|
+
}
|
|
23
61
|
}
|
|
24
|
-
for (
|
|
62
|
+
for (let i = list.length - 1; i >= 0; i--) {
|
|
63
|
+
const entry = list[i];
|
|
25
64
|
const closing = entry.marker === '\\(' ? '\\)' : entry.marker === '\\[' ? '\\]' : entry.marker;
|
|
26
65
|
entry.close = next[closing];
|
|
27
66
|
next[entry.marker] = entry;
|
|
28
67
|
}
|
|
29
|
-
return function match(start, limit =
|
|
68
|
+
return function match(start, limit = len) {
|
|
30
69
|
const open = markers.get(start), close = open?.close;
|
|
31
70
|
if (!close || open.marker === '\\)' || open.marker === '\\]' || close.at + close.marker.length > limit) return null;
|
|
32
71
|
const bodyStart = start + open.marker.length, bodyEnd = close.at;
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
import {createMathMatcher} from "./delimiters.mjs";
|
|
3
3
|
|
|
4
|
+
function isMathFenceInfo(info) {
|
|
5
|
+
if (!info || info.length < 4) return false;
|
|
6
|
+
let start = 0;
|
|
7
|
+
const len = info.length;
|
|
8
|
+
while (start < len && info.charCodeAt(start) <= 0x20) start++;
|
|
9
|
+
if (start + 4 > len) return false;
|
|
10
|
+
if ((info.charCodeAt(start) | 32) !== 0x6d ||
|
|
11
|
+
(info.charCodeAt(start + 1) | 32) !== 0x61 ||
|
|
12
|
+
(info.charCodeAt(start + 2) | 32) !== 0x74 ||
|
|
13
|
+
(info.charCodeAt(start + 3) | 32) !== 0x68) return false;
|
|
14
|
+
return start + 4 === len || info.charCodeAt(start + 4) <= 0x20;
|
|
15
|
+
}
|
|
16
|
+
|
|
4
17
|
export function installMathRules(md, {singleDollar = false, brackets = true, ruleName = 'rapier_math', inLink = false} = {}) {
|
|
5
18
|
const authored = new WeakMap();
|
|
6
19
|
md.core.ruler.before('normalize', ruleName + '_source', state => {
|
|
@@ -9,13 +22,13 @@ export function installMathRules(md, {singleDollar = false, brackets = true, rul
|
|
|
9
22
|
md.core.ruler.after('block', ruleName + '_source_release', state => authored.delete(state.tokens));
|
|
10
23
|
const tokenize = md.block.tokenize;
|
|
11
24
|
md.block.tokenize = function(state, startLine, endLine) {
|
|
12
|
-
const first = state.tokens.length;
|
|
25
|
+
const first = Math.max(state.tokens.length, state._mathScanned || 0);
|
|
13
26
|
try { return tokenize.call(this, state, startLine, endLine); }
|
|
14
27
|
finally {
|
|
15
|
-
|
|
28
|
+
const len = state.tokens.length;
|
|
29
|
+
for (let index = first; index < len; index++) {
|
|
16
30
|
const token = state.tokens[index];
|
|
17
|
-
if (token.type !== 'fence' || !token.map || token.meta?.mathSource !== undefined ||
|
|
18
|
-
String(token.info || '').trim().split(/\s+/)[0].toLowerCase() !== 'math') continue;
|
|
31
|
+
if (token.type !== 'fence' || !token.map || token.meta?.mathSource !== undefined || !isMathFenceInfo(token.info)) continue;
|
|
19
32
|
const held = authored.get(state.tokens), source = held?.source ?? state.src;
|
|
20
33
|
let value;
|
|
21
34
|
if (!state.rapierSourceSlice && source === state.src) value = state.getLines(token.map[0], token.map[1], state.blkIndent, false);
|
|
@@ -42,6 +55,7 @@ export function installMathRules(md, {singleDollar = false, brackets = true, rul
|
|
|
42
55
|
}
|
|
43
56
|
token.meta = {...token.meta, mathSource: value};
|
|
44
57
|
}
|
|
58
|
+
state._mathScanned = len;
|
|
45
59
|
}
|
|
46
60
|
};
|
|
47
61
|
const renderText = md.renderer.renderInlineAsText;
|
|
@@ -62,7 +76,8 @@ export function installMathRules(md, {singleDollar = false, brackets = true, rul
|
|
|
62
76
|
}
|
|
63
77
|
md.inline.ruler.before('escape', ruleName, (state, silent) => {
|
|
64
78
|
if (!inLink && state.linkLevel > 0) return false;
|
|
65
|
-
|
|
79
|
+
const ch = state.src.charCodeAt(state.pos);
|
|
80
|
+
if (ch !== 0x24 && (!brackets || ch !== 0x5c)) return false;
|
|
66
81
|
const span = match(state, state.pos, state.posMax);
|
|
67
82
|
if (!span) return false;
|
|
68
83
|
if (!silent) {
|
|
@@ -77,7 +92,8 @@ export function installMathRules(md, {singleDollar = false, brackets = true, rul
|
|
|
77
92
|
md.block.ruler.before('fence', ruleName, (state, startLine, endLine, silent) => {
|
|
78
93
|
if (state.sCount[startLine] - state.blkIndent >= 4) return false;
|
|
79
94
|
const start = state.bMarks[startLine] + state.tShift[startLine];
|
|
80
|
-
|
|
95
|
+
const c0 = state.src.charCodeAt(start), c1 = state.src.charCodeAt(start + 1);
|
|
96
|
+
if (!(c0 === 0x24 && c1 === 0x24) && (!brackets || !(c0 === 0x5c && c1 === 0x5b))) return false;
|
|
81
97
|
const span = match(state, start, state.eMarks[endLine - 1]);
|
|
82
98
|
if (!span?.displayMode) return false;
|
|
83
99
|
let last = startLine;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
// Pure text and markdown-it tokens: no DOM, no byte decoding, no globalThis.Rapier* (MIT kit; decoders stay in images/assets.mjs).
|
|
3
3
|
import {isWordSidecarBlock} from "./word-sidecar.mjs";
|
|
4
|
+
import {tuneMarkdownEngine} from "./md-engine.mjs";
|
|
4
5
|
import {sourceFacts, sourceView, sourceEdit} from "./source.mjs";
|
|
5
6
|
|
|
6
7
|
export const IMAGE_LIMITS = Object.freeze({bytes: 16 * 1024 * 1024, sourceChars: 64 * 1024 * 1024, dimension: 16384, pixels: 24000000, assets: 1024});
|
|
@@ -66,27 +67,22 @@ function parserFor(factory = configured || globalThis.markdownit) {
|
|
|
66
67
|
if (configured && factory === globalThis.markdownit) return configured;
|
|
67
68
|
if (typeof factory !== 'function') return fail('markdown_parser_unavailable');
|
|
68
69
|
let parser = parsers.get(factory);
|
|
69
|
-
if (!parser) { parser = installMarkdownImages(factory({html: true, maxNesting: 20})); parsers.set(factory, parser); }
|
|
70
|
+
if (!parser) { parser = installMarkdownImages(tuneMarkdownEngine(factory({html: true, maxNesting: 20}))); parsers.set(factory, parser); }
|
|
70
71
|
return parser;
|
|
71
72
|
}
|
|
72
73
|
export {parserFor as markdownParser};
|
|
73
74
|
export const markdownBodyOffset = source => splitSource ? splitSource(source).bodyOffset : 0;
|
|
74
75
|
|
|
75
|
-
const BLOCK_WINDOW = 1024,
|
|
76
|
-
const LINE_TABLES = ['bMarks', 'eMarks', 'tShift', 'sCount', 'bsCount'];
|
|
77
|
-
const lineIndex = key => typeof key === 'string' && /^(?:0|[1-9]\d*)$/.test(key);
|
|
78
|
-
let unheldLine = null;
|
|
79
|
-
const unheldLines = new Proxy(Array.prototype, {
|
|
80
|
-
get: (target, key, table) => unheldLine && lineIndex(key) ? unheldLine(table, Number(key)) : Reflect.get(target, key, table),
|
|
81
|
-
set: (target, key, value, table) => unheldLine && lineIndex(key) ? unheldLine(table, Number(key), true, value) : Reflect.set(target, key, value, table),
|
|
82
|
-
});
|
|
76
|
+
const BLOCK_WINDOW = 1024, WINDOW_END = Symbol('block window end');
|
|
83
77
|
|
|
84
78
|
export function eachBlockWindow(source, bodyOffset, parser, env, visit, keepNul = false) {
|
|
85
79
|
for (const window of blockWindows(source, bodyOffset, parser, env, keepNul)) visit(window);
|
|
86
80
|
}
|
|
87
81
|
|
|
88
82
|
export function* blockWindows(source, bodyOffset, parser, env, keepNul = false) {
|
|
89
|
-
const
|
|
83
|
+
const rawSlice = bodyOffset ? source.slice(bodyOffset) : source;
|
|
84
|
+
const lineEnds = rawSlice.indexOf('\r') >= 0 ? rawSlice.replace(/\r\n?/g, '\n') : rawSlice;
|
|
85
|
+
const body = keepNul || lineEnds.indexOf('\0') < 0 ? lineEnds : lineEnds.replace(/\0/g, '\ufffd');
|
|
90
86
|
let pair = source.indexOf('\r\n', bodyOffset), shift = bodyOffset;
|
|
91
87
|
const sourceOffset = at => {
|
|
92
88
|
while (pair >= 0 && at + shift > pair) { shift++; pair = source.indexOf('\r\n', pair + 2); }
|
|
@@ -94,89 +90,52 @@ export function* blockWindows(source, bodyOffset, parser, env, keepNul = false)
|
|
|
94
90
|
return at + shift;
|
|
95
91
|
};
|
|
96
92
|
const tokens = [];
|
|
97
|
-
|
|
93
|
+
const state = new parser.block.State(body, parser, env, tokens);
|
|
94
|
+
state.rapierSourceSlice = (from, to) => source.slice(sourceOffset(from), sourceOffset(to));
|
|
95
|
+
const totalLines = state.lineMax;
|
|
96
|
+
let origin = 0, first = 0, windowStartLine = 0;
|
|
98
97
|
const report = () => ({tokens, firstLine: first, lineStart: line => {
|
|
99
98
|
if (!line) return sourceOffset(origin);
|
|
100
|
-
const
|
|
99
|
+
const absLine = first + line - 1;
|
|
100
|
+
const end = absLine >= 0 && absLine < totalLines ? state.eMarks[absLine] : body.length;
|
|
101
101
|
return end < body.length ? sourceOffset(end + 1) : undefined;
|
|
102
102
|
}});
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (lines <= BLOCK_WINDOW) {
|
|
106
|
-
state = new parser.block.State(body, parser, env, tokens);
|
|
107
|
-
state.rapierSourceSlice = (from, to) => source.slice(sourceOffset(from), sourceOffset(to));
|
|
108
|
-
parser.block.tokenize(state, state.line, state.lineMax);
|
|
103
|
+
if (totalLines <= BLOCK_WINDOW) {
|
|
104
|
+
parser.block.tokenize(state, state.line, totalLines);
|
|
109
105
|
yield report(); return;
|
|
110
106
|
}
|
|
111
|
-
state = new parser.block.State(body.slice(body.lastIndexOf('\n') + 1), parser, env, tokens);
|
|
112
|
-
state.rapierSourceSlice = (from, to) => source.slice(sourceOffset(from), sourceOffset(to));
|
|
113
|
-
lines += state.lineMax;
|
|
114
|
-
state.src = body;
|
|
115
|
-
const tables = new WeakMap();
|
|
116
|
-
for (const name of LINE_TABLES) { state[name].length = 0; tables.set(Object.setPrototypeOf(state[name], unheldLines), name); }
|
|
117
|
-
let frontier = 0, ended = false, betweenBlocks = false;
|
|
118
|
-
const fill = line => {
|
|
119
|
-
while (!ended && line >= state.bMarks.length) {
|
|
120
|
-
const held = state.bMarks.length;
|
|
121
|
-
let until = frontier;
|
|
122
|
-
for (let more = held ? Math.max(TABLE_STEP, held - BLOCK_WINDOW) : BLOCK_WINDOW; more && until < body.length; more--) {
|
|
123
|
-
const end = body.indexOf('\n', until);
|
|
124
|
-
until = end < 0 ? body.length : end + 1;
|
|
125
|
-
}
|
|
126
|
-
const scanned = new parser.block.State(body.slice(frontier, until), parser, env, []);
|
|
127
|
-
ended = until === body.length;
|
|
128
|
-
const added = ended ? scanned.lineMax + 1 : scanned.lineMax;
|
|
129
|
-
for (const name of LINE_TABLES) {
|
|
130
|
-
const old = state[name], next = scanned[name], offset = name === 'bMarks' || name === 'eMarks' ? frontier : 0, table = [];
|
|
131
|
-
for (let at = 0; at < held; at++) table.push(old[at]);
|
|
132
|
-
for (let at = 0; at < added; at++) table.push(next[at] + offset);
|
|
133
|
-
old.length = 0;
|
|
134
|
-
tables.set(table, name);
|
|
135
|
-
state[name] = Object.setPrototypeOf(table, unheldLines);
|
|
136
|
-
}
|
|
137
|
-
frontier = until;
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
const answer = (table, line, write, value) => {
|
|
141
|
-
const name = tables.get(table);
|
|
142
|
-
if (line >= state[name].length) {
|
|
143
|
-
if (betweenBlocks) throw WINDOW_END;
|
|
144
|
-
fill(line);
|
|
145
|
-
}
|
|
146
|
-
const current = state[name], held = Object.hasOwn(current, line);
|
|
147
|
-
if (!write) return held ? current[line] : undefined;
|
|
148
|
-
if (held) current[line] = value;
|
|
149
|
-
else Object.defineProperty(current, line, {value, writable: true, enumerable: true, configurable: true});
|
|
150
|
-
return true;
|
|
151
|
-
};
|
|
152
107
|
const skipEmptyLines = state.skipEmptyLines;
|
|
153
108
|
state.skipEmptyLines = function (line) {
|
|
154
|
-
if (this.level || line !== this.line ||
|
|
155
|
-
if (line >= BLOCK_WINDOW) throw WINDOW_END;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
};
|
|
161
|
-
for (;;) {
|
|
162
|
-
for (let at = origin; at < body.length; at++) {
|
|
163
|
-
const code = body.charCodeAt(at);
|
|
164
|
-
if (code === 10) { origin = at + 1; first++; }
|
|
165
|
-
else if (!parser.utils.isSpace(code)) break;
|
|
109
|
+
if (this.level || line !== this.line || line === windowStartLine) return skipEmptyLines.call(this, line);
|
|
110
|
+
if (line - windowStartLine >= BLOCK_WINDOW) throw WINDOW_END;
|
|
111
|
+
const next = skipEmptyLines.call(this, line);
|
|
112
|
+
if (next - windowStartLine >= BLOCK_WINDOW) {
|
|
113
|
+
this.line = windowStartLine + BLOCK_WINDOW;
|
|
114
|
+
throw WINDOW_END;
|
|
166
115
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
116
|
+
return next;
|
|
117
|
+
};
|
|
118
|
+
for (let startLine = 0; startLine < totalLines;) {
|
|
119
|
+
startLine = skipEmptyLines.call(state, startLine);
|
|
120
|
+
if (startLine >= totalLines) break;
|
|
121
|
+
windowStartLine = startLine;
|
|
122
|
+
first = startLine;
|
|
123
|
+
origin = startLine === 0 ? 0 : state.eMarks[startLine - 1] + 1;
|
|
124
|
+
state.line = startLine;
|
|
125
|
+
state.rapierFirstLine = first;
|
|
171
126
|
let finished = true;
|
|
172
|
-
try { parser.block.tokenize(state,
|
|
127
|
+
try { parser.block.tokenize(state, startLine, totalLines); }
|
|
173
128
|
catch (error) { if (error !== WINDOW_END) throw error; finished = false; }
|
|
174
|
-
|
|
129
|
+
if (first !== 0) {
|
|
130
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
131
|
+
const m = tokens[i].map;
|
|
132
|
+
if (m) { m[0] -= first; m[1] -= first; }
|
|
133
|
+
}
|
|
134
|
+
}
|
|
175
135
|
yield report();
|
|
176
136
|
if (finished) return;
|
|
177
137
|
tokens.length = 0;
|
|
178
|
-
|
|
179
|
-
for (const name of LINE_TABLES) state[name].length = 0;
|
|
138
|
+
startLine = state.line;
|
|
180
139
|
}
|
|
181
140
|
}
|
|
182
141
|
|
|
@@ -184,7 +143,7 @@ const INVALID_PERCENT_PAYLOAD = /[^A-Za-z0-9._~:\/?@+$,;=%-]/;
|
|
|
184
143
|
const isHexUnit = code => code >= 48 && code <= 57 || code >= 65 && code <= 70 || code >= 97 && code <= 102;
|
|
185
144
|
let lastScan = null;
|
|
186
145
|
export function dataImage(url) {
|
|
187
|
-
if (typeof url !== 'string' || url.length > IMAGE_LIMITS.sourceChars) return null;
|
|
146
|
+
if (typeof url !== 'string' || url.length < 15 || (url.charCodeAt(0) | 32) !== 0x64 || url.charCodeAt(4) !== 0x3a || url.length > IMAGE_LIMITS.sourceChars) return null;
|
|
188
147
|
if (url.length > 4096) {
|
|
189
148
|
if (lastScan?.url !== url) lastScan = {url, image: scanDataImage(url)};
|
|
190
149
|
return lastScan.image && {...lastScan.image};
|