@vectojs/markdown 0.1.1 → 0.1.2
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/dist/Markdown.d.ts +14 -0
- package/dist/index.js +53 -10
- package/dist/index.mjs +53 -10
- package/package.json +1 -1
package/dist/Markdown.d.ts
CHANGED
|
@@ -37,6 +37,8 @@ export interface MarkdownTheme {
|
|
|
37
37
|
export declare class CodeBlock extends UIComponent {
|
|
38
38
|
private lines;
|
|
39
39
|
private grid;
|
|
40
|
+
/** Raw (unhighlighted) lines of the last build, for prefix reuse in buildLines. */
|
|
41
|
+
private rawLines;
|
|
40
42
|
private cellWidth;
|
|
41
43
|
private source;
|
|
42
44
|
private lang;
|
|
@@ -51,6 +53,18 @@ export declare class CodeBlock extends UIComponent {
|
|
|
51
53
|
/** Enable or disable browser-native selection for this code block. */
|
|
52
54
|
setSelectable(selectable: boolean): this;
|
|
53
55
|
getContentProjection(): ContentProjection | null;
|
|
56
|
+
/**
|
|
57
|
+
* Re-highlight the code, reusing the highlight of any unchanged line prefix.
|
|
58
|
+
*
|
|
59
|
+
* Streaming appends to the END of a block, so all but the last line or two are
|
|
60
|
+
* byte-identical to the previous call — yet this used to re-highlight every
|
|
61
|
+
* line on every chunk, making a streamed block O(N) per append and O(N^2)
|
|
62
|
+
* overall. Reusing the stable prefix makes an append proportional to what
|
|
63
|
+
* actually changed.
|
|
64
|
+
*
|
|
65
|
+
* The last previously-seen line is deliberately NOT reused: a chunk usually
|
|
66
|
+
* lands mid-line, so that line's text (and therefore its tokenization) changes.
|
|
67
|
+
*/
|
|
54
68
|
private buildLines;
|
|
55
69
|
private ensureGrid;
|
|
56
70
|
/** Code blocks are decorative — not interactive. */
|
package/dist/index.js
CHANGED
|
@@ -438,6 +438,8 @@ function highlightLine(line, lang, theme) {
|
|
|
438
438
|
var CodeBlock = class extends import_ui.UIComponent {
|
|
439
439
|
lines;
|
|
440
440
|
grid = null;
|
|
441
|
+
/** Raw (unhighlighted) lines of the last build, for prefix reuse in buildLines. */
|
|
442
|
+
rawLines = null;
|
|
441
443
|
cellWidth = 0;
|
|
442
444
|
source;
|
|
443
445
|
lang;
|
|
@@ -497,9 +499,36 @@ var CodeBlock = class extends import_ui.UIComponent {
|
|
|
497
499
|
grid
|
|
498
500
|
};
|
|
499
501
|
}
|
|
502
|
+
/**
|
|
503
|
+
* Re-highlight the code, reusing the highlight of any unchanged line prefix.
|
|
504
|
+
*
|
|
505
|
+
* Streaming appends to the END of a block, so all but the last line or two are
|
|
506
|
+
* byte-identical to the previous call — yet this used to re-highlight every
|
|
507
|
+
* line on every chunk, making a streamed block O(N) per append and O(N^2)
|
|
508
|
+
* overall. Reusing the stable prefix makes an append proportional to what
|
|
509
|
+
* actually changed.
|
|
510
|
+
*
|
|
511
|
+
* The last previously-seen line is deliberately NOT reused: a chunk usually
|
|
512
|
+
* lands mid-line, so that line's text (and therefore its tokenization) changes.
|
|
513
|
+
*/
|
|
500
514
|
buildLines(code) {
|
|
501
515
|
const rawLines = code.split(/\r\n|\r|\n/);
|
|
502
|
-
|
|
516
|
+
const previous = this.rawLines;
|
|
517
|
+
let reusable = 0;
|
|
518
|
+
if (previous && this.lines.length === previous.length) {
|
|
519
|
+
const limit = Math.min(previous.length - 1, rawLines.length);
|
|
520
|
+
while (reusable < limit && previous[reusable] === rawLines[reusable]) reusable++;
|
|
521
|
+
}
|
|
522
|
+
if (reusable > 0) {
|
|
523
|
+
const next = this.lines.slice(0, reusable);
|
|
524
|
+
for (let i = reusable; i < rawLines.length; i++) {
|
|
525
|
+
next.push(highlightLine(rawLines[i], this.lang, this.theme));
|
|
526
|
+
}
|
|
527
|
+
this.lines = next;
|
|
528
|
+
} else {
|
|
529
|
+
this.lines = rawLines.map((l) => highlightLine(l, this.lang, this.theme));
|
|
530
|
+
}
|
|
531
|
+
this.rawLines = rawLines;
|
|
503
532
|
this.grid = null;
|
|
504
533
|
this.height = this.pad * 2 + rawLines.length * this.lineH;
|
|
505
534
|
}
|
|
@@ -831,7 +860,7 @@ var Markdown = class extends import_ui.UIComponent {
|
|
|
831
860
|
this.pendingWorkerIds.delete(id);
|
|
832
861
|
this.appendInFlight = false;
|
|
833
862
|
const newTokens = [...oldTokensSnapshot.slice(0, matchLen), ...tail];
|
|
834
|
-
this.updateTokens(newTokens);
|
|
863
|
+
this.updateTokens(newTokens, matchLen);
|
|
835
864
|
if (this.appendPending) {
|
|
836
865
|
this.appendPending = false;
|
|
837
866
|
this.dispatchAppend();
|
|
@@ -855,21 +884,35 @@ var Markdown = class extends import_ui.UIComponent {
|
|
|
855
884
|
...sendRaws ? { oldRaws: oldTokensSnapshot.map((t) => t.raw) } : {}
|
|
856
885
|
});
|
|
857
886
|
}
|
|
858
|
-
updateTokens(newTokens) {
|
|
887
|
+
updateTokens(newTokens, knownMatchLen) {
|
|
859
888
|
const oldTokens = this.tokens;
|
|
860
889
|
const oldChildren = [...this.content.children];
|
|
861
|
-
let matchLen
|
|
890
|
+
let matchLen;
|
|
862
891
|
const minLen = Math.min(oldTokens.length, newTokens.length);
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
892
|
+
if (knownMatchLen !== void 0 && knownMatchLen >= 0 && knownMatchLen <= minLen) {
|
|
893
|
+
matchLen = knownMatchLen;
|
|
894
|
+
} else {
|
|
895
|
+
matchLen = 0;
|
|
896
|
+
for (let i = 0; i < minLen; i++) {
|
|
897
|
+
if (oldTokens[i].raw === newTokens[i].raw) {
|
|
898
|
+
matchLen++;
|
|
899
|
+
} else {
|
|
900
|
+
break;
|
|
901
|
+
}
|
|
868
902
|
}
|
|
869
903
|
}
|
|
870
904
|
const oldTokenToChild = this.tokenChildPrefix;
|
|
871
905
|
const rawMatchLen = matchLen;
|
|
872
|
-
|
|
906
|
+
const lastTokenSameType = matchLen === oldTokens.length - 1 && matchLen < newTokens.length && oldTokens[matchLen]?.type === newTokens[matchLen]?.type;
|
|
907
|
+
if (lastTokenSameType && newTokens[matchLen]?.type === "code") {
|
|
908
|
+
const existingEntity = oldChildren[oldTokenToChild[matchLen]];
|
|
909
|
+
const codeToken = newTokens[matchLen];
|
|
910
|
+
if (existingEntity instanceof CodeBlock) {
|
|
911
|
+
existingEntity.setCode(codeToken.text, codeToken.lang ?? void 0);
|
|
912
|
+
matchLen++;
|
|
913
|
+
this.content.resizeLastChild(existingEntity);
|
|
914
|
+
}
|
|
915
|
+
} else if (lastTokenSameType && newTokens[matchLen]?.type === "paragraph") {
|
|
873
916
|
const entityIdx = oldTokenToChild[matchLen];
|
|
874
917
|
const existingEntity = oldChildren[entityIdx];
|
|
875
918
|
if (existingEntity && "setSpans" in existingEntity) {
|
package/dist/index.mjs
CHANGED
|
@@ -416,6 +416,8 @@ function highlightLine(line, lang, theme) {
|
|
|
416
416
|
var CodeBlock = class extends UIComponent {
|
|
417
417
|
lines;
|
|
418
418
|
grid = null;
|
|
419
|
+
/** Raw (unhighlighted) lines of the last build, for prefix reuse in buildLines. */
|
|
420
|
+
rawLines = null;
|
|
419
421
|
cellWidth = 0;
|
|
420
422
|
source;
|
|
421
423
|
lang;
|
|
@@ -475,9 +477,36 @@ var CodeBlock = class extends UIComponent {
|
|
|
475
477
|
grid
|
|
476
478
|
};
|
|
477
479
|
}
|
|
480
|
+
/**
|
|
481
|
+
* Re-highlight the code, reusing the highlight of any unchanged line prefix.
|
|
482
|
+
*
|
|
483
|
+
* Streaming appends to the END of a block, so all but the last line or two are
|
|
484
|
+
* byte-identical to the previous call — yet this used to re-highlight every
|
|
485
|
+
* line on every chunk, making a streamed block O(N) per append and O(N^2)
|
|
486
|
+
* overall. Reusing the stable prefix makes an append proportional to what
|
|
487
|
+
* actually changed.
|
|
488
|
+
*
|
|
489
|
+
* The last previously-seen line is deliberately NOT reused: a chunk usually
|
|
490
|
+
* lands mid-line, so that line's text (and therefore its tokenization) changes.
|
|
491
|
+
*/
|
|
478
492
|
buildLines(code) {
|
|
479
493
|
const rawLines = code.split(/\r\n|\r|\n/);
|
|
480
|
-
|
|
494
|
+
const previous = this.rawLines;
|
|
495
|
+
let reusable = 0;
|
|
496
|
+
if (previous && this.lines.length === previous.length) {
|
|
497
|
+
const limit = Math.min(previous.length - 1, rawLines.length);
|
|
498
|
+
while (reusable < limit && previous[reusable] === rawLines[reusable]) reusable++;
|
|
499
|
+
}
|
|
500
|
+
if (reusable > 0) {
|
|
501
|
+
const next = this.lines.slice(0, reusable);
|
|
502
|
+
for (let i = reusable; i < rawLines.length; i++) {
|
|
503
|
+
next.push(highlightLine(rawLines[i], this.lang, this.theme));
|
|
504
|
+
}
|
|
505
|
+
this.lines = next;
|
|
506
|
+
} else {
|
|
507
|
+
this.lines = rawLines.map((l) => highlightLine(l, this.lang, this.theme));
|
|
508
|
+
}
|
|
509
|
+
this.rawLines = rawLines;
|
|
481
510
|
this.grid = null;
|
|
482
511
|
this.height = this.pad * 2 + rawLines.length * this.lineH;
|
|
483
512
|
}
|
|
@@ -809,7 +838,7 @@ var Markdown = class extends UIComponent {
|
|
|
809
838
|
this.pendingWorkerIds.delete(id);
|
|
810
839
|
this.appendInFlight = false;
|
|
811
840
|
const newTokens = [...oldTokensSnapshot.slice(0, matchLen), ...tail];
|
|
812
|
-
this.updateTokens(newTokens);
|
|
841
|
+
this.updateTokens(newTokens, matchLen);
|
|
813
842
|
if (this.appendPending) {
|
|
814
843
|
this.appendPending = false;
|
|
815
844
|
this.dispatchAppend();
|
|
@@ -833,21 +862,35 @@ var Markdown = class extends UIComponent {
|
|
|
833
862
|
...sendRaws ? { oldRaws: oldTokensSnapshot.map((t) => t.raw) } : {}
|
|
834
863
|
});
|
|
835
864
|
}
|
|
836
|
-
updateTokens(newTokens) {
|
|
865
|
+
updateTokens(newTokens, knownMatchLen) {
|
|
837
866
|
const oldTokens = this.tokens;
|
|
838
867
|
const oldChildren = [...this.content.children];
|
|
839
|
-
let matchLen
|
|
868
|
+
let matchLen;
|
|
840
869
|
const minLen = Math.min(oldTokens.length, newTokens.length);
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
870
|
+
if (knownMatchLen !== void 0 && knownMatchLen >= 0 && knownMatchLen <= minLen) {
|
|
871
|
+
matchLen = knownMatchLen;
|
|
872
|
+
} else {
|
|
873
|
+
matchLen = 0;
|
|
874
|
+
for (let i = 0; i < minLen; i++) {
|
|
875
|
+
if (oldTokens[i].raw === newTokens[i].raw) {
|
|
876
|
+
matchLen++;
|
|
877
|
+
} else {
|
|
878
|
+
break;
|
|
879
|
+
}
|
|
846
880
|
}
|
|
847
881
|
}
|
|
848
882
|
const oldTokenToChild = this.tokenChildPrefix;
|
|
849
883
|
const rawMatchLen = matchLen;
|
|
850
|
-
|
|
884
|
+
const lastTokenSameType = matchLen === oldTokens.length - 1 && matchLen < newTokens.length && oldTokens[matchLen]?.type === newTokens[matchLen]?.type;
|
|
885
|
+
if (lastTokenSameType && newTokens[matchLen]?.type === "code") {
|
|
886
|
+
const existingEntity = oldChildren[oldTokenToChild[matchLen]];
|
|
887
|
+
const codeToken = newTokens[matchLen];
|
|
888
|
+
if (existingEntity instanceof CodeBlock) {
|
|
889
|
+
existingEntity.setCode(codeToken.text, codeToken.lang ?? void 0);
|
|
890
|
+
matchLen++;
|
|
891
|
+
this.content.resizeLastChild(existingEntity);
|
|
892
|
+
}
|
|
893
|
+
} else if (lastTokenSameType && newTokens[matchLen]?.type === "paragraph") {
|
|
851
894
|
const entityIdx = oldTokenToChild[matchLen];
|
|
852
895
|
const existingEntity = oldChildren[entityIdx];
|
|
853
896
|
if (existingEntity && "setSpans" in existingEntity) {
|