@bendyline/squisq-editor-react 2.13.16 → 2.13.17

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.
@@ -482,6 +482,96 @@ function useBlockNavigator(source, setSource, opts) {
482
482
  };
483
483
  }
484
484
 
485
+ // src/markdownParseProtocol.ts
486
+ import { parseMarkdown as parseMarkdown3 } from "@bendyline/squisq/markdown";
487
+ function isMarkdownParseResult(value) {
488
+ if (!value || typeof value !== "object") return false;
489
+ const result = value;
490
+ if (Object.keys(result).length !== 3 || !Number.isSafeInteger(result.id) || typeof result.id !== "number" || result.id < 1)
491
+ return false;
492
+ if (result.markdown === null) return typeof result.error === "string";
493
+ if (result.error !== null || !result.markdown || typeof result.markdown !== "object")
494
+ return false;
495
+ const markdown = result.markdown;
496
+ return markdown.type === "document" && Array.isArray(markdown.children);
497
+ }
498
+
499
+ // src/backgroundMarkdownParser.ts
500
+ var BackgroundMarkdownParser = class {
501
+ constructor(createWorker = () => new Worker(new URL("./document-parse-worker/index.js", import.meta.url), {
502
+ type: "module",
503
+ name: "squisq-markdown-parser"
504
+ })) {
505
+ this.createWorker = createWorker;
506
+ this.worker = null;
507
+ this.active = null;
508
+ this.pending = null;
509
+ this.serial = 0;
510
+ this.failed = false;
511
+ this.disposed = false;
512
+ }
513
+ parse(source, done) {
514
+ if (this.disposed) return;
515
+ this.pending = { id: ++this.serial, source, done };
516
+ this.drain();
517
+ }
518
+ cancel() {
519
+ this.serial++;
520
+ this.pending = null;
521
+ }
522
+ dispose() {
523
+ this.disposed = true;
524
+ this.cancel();
525
+ this.stopWorker();
526
+ }
527
+ stopWorker() {
528
+ clearTimeout(this.timeout);
529
+ this.worker?.terminate();
530
+ this.worker = null;
531
+ this.active = null;
532
+ }
533
+ fail() {
534
+ const latest = this.pending ?? this.active;
535
+ this.failed = true;
536
+ this.pending = null;
537
+ this.stopWorker();
538
+ if (!this.disposed && latest?.id === this.serial) latest.done(null);
539
+ }
540
+ drain() {
541
+ if (this.disposed || this.active || !this.pending) return;
542
+ const job = this.pending;
543
+ this.pending = null;
544
+ if (this.failed) {
545
+ job.done(null);
546
+ return;
547
+ }
548
+ this.active = job;
549
+ try {
550
+ if (!this.worker) {
551
+ this.worker = this.createWorker();
552
+ this.worker.onmessage = ({ data }) => {
553
+ if (this.disposed || !this.active) return;
554
+ if (!isMarkdownParseResult(data) || data.id !== this.active.id) {
555
+ this.fail();
556
+ return;
557
+ }
558
+ const completed = this.active;
559
+ clearTimeout(this.timeout);
560
+ this.active = null;
561
+ if (completed.id === this.serial) completed.done(data);
562
+ this.drain();
563
+ };
564
+ this.worker.onerror = () => this.fail();
565
+ this.worker.onmessageerror = () => this.fail();
566
+ }
567
+ this.timeout = setTimeout(() => this.fail(), 3e4);
568
+ this.worker.postMessage({ id: job.id, source: job.source });
569
+ } catch {
570
+ this.fail();
571
+ }
572
+ }
573
+ };
574
+
485
575
  // src/EditorContext.tsx
486
576
  import {
487
577
  createContext as createContext3,
@@ -493,7 +583,7 @@ import {
493
583
  useEffect as useEffect3
494
584
  } from "react";
495
585
  import {
496
- parseMarkdown as parseMarkdown3,
586
+ parseMarkdown as parseMarkdown4,
497
587
  stringifyMarkdown,
498
588
  resolveWikiLinks
499
589
  } from "@bendyline/squisq/markdown";
@@ -1871,10 +1961,17 @@ function EditorProvider({
1871
1961
  }, [initialColorScheme]);
1872
1962
  const parseTimeoutRef = useRef3(null);
1873
1963
  const skipInitialDebouncedParseRef = useRef3(Boolean(initialMarkdown));
1874
- const doParse = useCallback3((source) => {
1964
+ const backgroundParserRef = useRef3(null);
1965
+ useEffect3(
1966
+ () => () => {
1967
+ backgroundParserRef.current?.dispose();
1968
+ backgroundParserRef.current = null;
1969
+ },
1970
+ []
1971
+ );
1972
+ const doParse = useCallback3((source, background = false) => {
1875
1973
  setIsParsing(true);
1876
- try {
1877
- const parsed = parseMarkdown3(source);
1974
+ const applyParsed = (parsed) => {
1878
1975
  setMarkdownDocState(parsed);
1879
1976
  setParseError(null);
1880
1977
  try {
@@ -1887,12 +1984,30 @@ function EditorProvider({
1887
1984
  setDoc(null);
1888
1985
  console.warn("Doc generation failed:", docErr instanceof Error ? docErr.message : docErr);
1889
1986
  }
1890
- } catch (err) {
1891
- setParseError(err instanceof Error ? err.message : "Parse error");
1987
+ setIsParsing(false);
1988
+ };
1989
+ const applyError = (error) => {
1990
+ setParseError(error);
1892
1991
  setMarkdownDocState(null);
1893
1992
  setDoc(null);
1894
- } finally {
1895
1993
  setIsParsing(false);
1994
+ };
1995
+ const synchronous = () => {
1996
+ try {
1997
+ applyParsed(parseMarkdown4(source));
1998
+ } catch (error) {
1999
+ applyError(error instanceof Error ? error.message : "Parse error");
2000
+ }
2001
+ };
2002
+ if (background && source.length >= 32768 && typeof Worker !== "undefined") {
2003
+ backgroundParserRef.current ?? (backgroundParserRef.current = new BackgroundMarkdownParser());
2004
+ backgroundParserRef.current.parse(source, (result) => {
2005
+ if (result === null) synchronous();
2006
+ else if (result.markdown) applyParsed(result.markdown);
2007
+ else applyError(result.error);
2008
+ });
2009
+ } else {
2010
+ synchronous();
1896
2011
  }
1897
2012
  }, []);
1898
2013
  const [wikiProjection, setWikiProjection] = useState3(null);
@@ -1933,14 +2048,15 @@ function EditorProvider({
1933
2048
  }
1934
2049
  setIsParsing(true);
1935
2050
  parseTimeoutRef.current = setTimeout(() => {
1936
- doParse(markdownSource);
2051
+ doParse(markdownSource, true);
1937
2052
  }, 150);
1938
2053
  return () => {
1939
2054
  if (parseTimeoutRef.current) {
1940
2055
  clearTimeout(parseTimeoutRef.current);
1941
2056
  }
2057
+ backgroundParserRef.current?.cancel();
1942
2058
  };
1943
- }, [markdownSource, doParse, editorMode]);
2059
+ }, [markdownSource, doParse, editorMode, articleId, fileName]);
1944
2060
  useEffect3(() => {
1945
2061
  if (editorMode !== "markdown") return;
1946
2062
  if (initialMarkdown) {