@blankdotpage/cake 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/index.cjs CHANGED
@@ -1,44 +1,53 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require$$0 = require("react");
4
- const reactDom = require("react-dom");
5
4
  const lucideReact = require("lucide-react");
6
5
  const TurndownService = require("turndown");
7
6
  const state = require("@codemirror/state");
8
- var jsxDevRuntime = { exports: {} };
9
- var reactJsxDevRuntime_production_min = {};
7
+ var jsxRuntime = { exports: {} };
8
+ var reactJsxRuntime_production_min = {};
10
9
  /**
11
10
  * @license React
12
- * react-jsx-dev-runtime.production.min.js
11
+ * react-jsx-runtime.production.min.js
13
12
  *
14
13
  * Copyright (c) Facebook, Inc. and its affiliates.
15
14
  *
16
15
  * This source code is licensed under the MIT license found in the
17
16
  * LICENSE file in the root directory of this source tree.
18
17
  */
19
- var hasRequiredReactJsxDevRuntime_production_min;
20
- function requireReactJsxDevRuntime_production_min() {
21
- if (hasRequiredReactJsxDevRuntime_production_min) return reactJsxDevRuntime_production_min;
22
- hasRequiredReactJsxDevRuntime_production_min = 1;
23
- var a = Symbol.for("react.fragment");
24
- reactJsxDevRuntime_production_min.Fragment = a;
25
- reactJsxDevRuntime_production_min.jsxDEV = void 0;
26
- return reactJsxDevRuntime_production_min;
27
- }
28
- var reactJsxDevRuntime_development = {};
18
+ var hasRequiredReactJsxRuntime_production_min;
19
+ function requireReactJsxRuntime_production_min() {
20
+ if (hasRequiredReactJsxRuntime_production_min) return reactJsxRuntime_production_min;
21
+ hasRequiredReactJsxRuntime_production_min = 1;
22
+ var f = require$$0, k = Symbol.for("react.element"), l = Symbol.for("react.fragment"), m = Object.prototype.hasOwnProperty, n = f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner, p = { key: true, ref: true, __self: true, __source: true };
23
+ function q(c, a, g) {
24
+ var b, d = {}, e = null, h = null;
25
+ void 0 !== g && (e = "" + g);
26
+ void 0 !== a.key && (e = "" + a.key);
27
+ void 0 !== a.ref && (h = a.ref);
28
+ for (b in a) m.call(a, b) && !p.hasOwnProperty(b) && (d[b] = a[b]);
29
+ if (c && c.defaultProps) for (b in a = c.defaultProps, a) void 0 === d[b] && (d[b] = a[b]);
30
+ return { $$typeof: k, type: c, key: e, ref: h, props: d, _owner: n.current };
31
+ }
32
+ reactJsxRuntime_production_min.Fragment = l;
33
+ reactJsxRuntime_production_min.jsx = q;
34
+ reactJsxRuntime_production_min.jsxs = q;
35
+ return reactJsxRuntime_production_min;
36
+ }
37
+ var reactJsxRuntime_development = {};
29
38
  /**
30
39
  * @license React
31
- * react-jsx-dev-runtime.development.js
40
+ * react-jsx-runtime.development.js
32
41
  *
33
42
  * Copyright (c) Facebook, Inc. and its affiliates.
34
43
  *
35
44
  * This source code is licensed under the MIT license found in the
36
45
  * LICENSE file in the root directory of this source tree.
37
46
  */
38
- var hasRequiredReactJsxDevRuntime_development;
39
- function requireReactJsxDevRuntime_development() {
40
- if (hasRequiredReactJsxDevRuntime_development) return reactJsxDevRuntime_development;
41
- hasRequiredReactJsxDevRuntime_development = 1;
47
+ var hasRequiredReactJsxRuntime_development;
48
+ function requireReactJsxRuntime_development() {
49
+ if (hasRequiredReactJsxRuntime_development) return reactJsxRuntime_development;
50
+ hasRequiredReactJsxRuntime_development = 1;
42
51
  if (process.env.NODE_ENV !== "production") {
43
52
  (function() {
44
53
  var React = require$$0;
@@ -525,10 +534,6 @@ function requireReactJsxDevRuntime_development() {
525
534
  };
526
535
  var specialPropKeyWarningShown;
527
536
  var specialPropRefWarningShown;
528
- var didWarnAboutStringRefs;
529
- {
530
- didWarnAboutStringRefs = {};
531
- }
532
537
  function hasValidRef(config) {
533
538
  {
534
539
  if (hasOwnProperty.call(config, "ref")) {
@@ -553,13 +558,7 @@ function requireReactJsxDevRuntime_development() {
553
558
  }
554
559
  function warnIfStringRefCannotBeAutoConverted(config, self) {
555
560
  {
556
- if (typeof config.ref === "string" && ReactCurrentOwner.current && self && ReactCurrentOwner.current.stateNode !== self) {
557
- var componentName = getComponentNameFromType(ReactCurrentOwner.current.type);
558
- if (!didWarnAboutStringRefs[componentName]) {
559
- error('Component "%s" contains the string ref "%s". Support for string refs will be removed in a future major release. This case cannot be automatically converted to an arrow function. We ask you to manually fix this case by using useRef() or createRef() instead. Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref', getComponentNameFromType(ReactCurrentOwner.current.type), config.ref);
560
- didWarnAboutStringRefs[componentName] = true;
561
- }
562
- }
561
+ if (typeof config.ref === "string" && ReactCurrentOwner.current && self) ;
563
562
  }
564
563
  }
565
564
  function defineKeyPropWarningGetter(props, displayName) {
@@ -713,11 +712,6 @@ function requireReactJsxDevRuntime_development() {
713
712
  }
714
713
  function getSourceInfoErrorAddendum(source) {
715
714
  {
716
- if (source !== void 0) {
717
- var fileName = source.fileName.replace(/^.*[\\\/]/, "");
718
- var lineNumber = source.lineNumber;
719
- return "\n\nCheck your code at " + fileName + ":" + lineNumber + ".";
720
- }
721
715
  return "";
722
716
  }
723
717
  }
@@ -843,7 +837,7 @@ function requireReactJsxDevRuntime_development() {
843
837
  if (type === void 0 || typeof type === "object" && type !== null && Object.keys(type).length === 0) {
844
838
  info += " You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.";
845
839
  }
846
- var sourceInfo = getSourceInfoErrorAddendum(source);
840
+ var sourceInfo = getSourceInfoErrorAddendum();
847
841
  if (sourceInfo) {
848
842
  info += sourceInfo;
849
843
  } else {
@@ -907,19 +901,31 @@ function requireReactJsxDevRuntime_development() {
907
901
  return element;
908
902
  }
909
903
  }
910
- var jsxDEV$1 = jsxWithValidation;
911
- reactJsxDevRuntime_development.Fragment = REACT_FRAGMENT_TYPE;
912
- reactJsxDevRuntime_development.jsxDEV = jsxDEV$1;
904
+ function jsxWithValidationStatic(type, props, key) {
905
+ {
906
+ return jsxWithValidation(type, props, key, true);
907
+ }
908
+ }
909
+ function jsxWithValidationDynamic(type, props, key) {
910
+ {
911
+ return jsxWithValidation(type, props, key, false);
912
+ }
913
+ }
914
+ var jsx = jsxWithValidationDynamic;
915
+ var jsxs = jsxWithValidationStatic;
916
+ reactJsxRuntime_development.Fragment = REACT_FRAGMENT_TYPE;
917
+ reactJsxRuntime_development.jsx = jsx;
918
+ reactJsxRuntime_development.jsxs = jsxs;
913
919
  })();
914
920
  }
915
- return reactJsxDevRuntime_development;
921
+ return reactJsxRuntime_development;
916
922
  }
917
923
  if (process.env.NODE_ENV === "production") {
918
- jsxDevRuntime.exports = requireReactJsxDevRuntime_production_min();
924
+ jsxRuntime.exports = requireReactJsxRuntime_production_min();
919
925
  } else {
920
- jsxDevRuntime.exports = requireReactJsxDevRuntime_development();
926
+ jsxRuntime.exports = requireReactJsxRuntime_development();
921
927
  }
922
- var jsxDevRuntimeExports = jsxDevRuntime.exports;
928
+ var jsxRuntimeExports = jsxRuntime.exports;
923
929
  const graphemeSegmenter = new Intl.Segmenter(void 0, {
924
930
  granularity: "grapheme"
925
931
  });
@@ -2411,13 +2417,150 @@ function createRuntime(extensions) {
2411
2417
  const endInLine = lineIndex === endLoc.lineIndex ? endLoc.offsetInLine : line.cursorLength;
2412
2418
  const selectedRuns = sliceRuns(runs, startInLine, endInLine).selected;
2413
2419
  const content = runsToInlines(normalizeRuns(selectedRuns));
2414
- blocks.push({ type: "paragraph", content });
2420
+ const paragraph = { type: "paragraph", content };
2421
+ if (line.path.length > 1) {
2422
+ const wrapperPath = line.path.slice(0, -1);
2423
+ const wrapper = getBlockAtPath(state2.doc.blocks, wrapperPath);
2424
+ if (wrapper && wrapper.type === "block-wrapper") {
2425
+ blocks.push({
2426
+ type: "block-wrapper",
2427
+ kind: wrapper.kind,
2428
+ data: wrapper.data,
2429
+ blocks: [paragraph]
2430
+ });
2431
+ continue;
2432
+ }
2433
+ }
2434
+ blocks.push(paragraph);
2415
2435
  }
2416
2436
  const sliceDoc = {
2417
2437
  blocks: blocks.length > 0 ? blocks : [{ type: "paragraph", content: [] }]
2418
2438
  };
2419
2439
  return serialize(normalize(sliceDoc)).source;
2420
2440
  }
2441
+ function escapeHtml(text) {
2442
+ return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2443
+ }
2444
+ function runsToHtml(runs) {
2445
+ var _a;
2446
+ let html = "";
2447
+ for (const run of runs) {
2448
+ let content = escapeHtml(run.text);
2449
+ const sortedMarks = [...run.marks].reverse();
2450
+ for (const mark of sortedMarks) {
2451
+ if (mark.kind === "bold") {
2452
+ content = `<strong>${content}</strong>`;
2453
+ } else if (mark.kind === "italic") {
2454
+ content = `<em>${content}</em>`;
2455
+ } else if (mark.kind === "strikethrough") {
2456
+ content = `<s>${content}</s>`;
2457
+ } else if (mark.kind === "link") {
2458
+ const url = ((_a = mark.data) == null ? void 0 : _a.url) ?? "";
2459
+ content = `<a href="${escapeHtml(url)}">${content}</a>`;
2460
+ }
2461
+ }
2462
+ html += content;
2463
+ }
2464
+ return html;
2465
+ }
2466
+ function serializeSelectionToHtml(state2, selection) {
2467
+ const normalized = normalizeSelection$1(selection);
2468
+ const lines = flattenDocToLines(state2.doc);
2469
+ const docCursorLength = cursorLengthForLines(lines);
2470
+ const cursorStart = Math.max(
2471
+ 0,
2472
+ Math.min(docCursorLength, Math.min(normalized.start, normalized.end))
2473
+ );
2474
+ const cursorEnd = Math.max(
2475
+ 0,
2476
+ Math.min(docCursorLength, Math.max(normalized.start, normalized.end))
2477
+ );
2478
+ if (cursorStart === cursorEnd) {
2479
+ return "";
2480
+ }
2481
+ const startLoc = resolveCursorToLine(lines, cursorStart);
2482
+ const endLoc = resolveCursorToLine(lines, cursorEnd);
2483
+ let html = "";
2484
+ let activeList = null;
2485
+ const closeList = () => {
2486
+ if (activeList) {
2487
+ html += `</${activeList.type}>`;
2488
+ activeList = null;
2489
+ }
2490
+ };
2491
+ const openList = (type, indent) => {
2492
+ if (activeList && activeList.type === type && activeList.indent === indent) {
2493
+ return;
2494
+ }
2495
+ closeList();
2496
+ html += `<${type}>`;
2497
+ activeList = { type, indent };
2498
+ };
2499
+ for (let lineIndex = startLoc.lineIndex; lineIndex <= endLoc.lineIndex; lineIndex += 1) {
2500
+ const line = lines[lineIndex];
2501
+ if (!line) {
2502
+ continue;
2503
+ }
2504
+ const block = getBlockAtPath(state2.doc.blocks, line.path);
2505
+ if (!block || block.type !== "paragraph") {
2506
+ continue;
2507
+ }
2508
+ const runs = paragraphToRuns(block);
2509
+ const startInLine = lineIndex === startLoc.lineIndex ? startLoc.offsetInLine : 0;
2510
+ const endInLine = lineIndex === endLoc.lineIndex ? endLoc.offsetInLine : line.cursorLength;
2511
+ const selectedRuns = sliceRuns(runs, startInLine, endInLine).selected;
2512
+ let wrapperKind = null;
2513
+ let wrapperData;
2514
+ if (line.path.length > 1) {
2515
+ const wrapperPath = line.path.slice(0, -1);
2516
+ const wrapper = getBlockAtPath(state2.doc.blocks, wrapperPath);
2517
+ if (wrapper && wrapper.type === "block-wrapper") {
2518
+ wrapperKind = wrapper.kind;
2519
+ wrapperData = wrapper.data;
2520
+ }
2521
+ }
2522
+ const plainText = runs.map((r) => r.text).join("");
2523
+ const listMatch = plainText.match(/^(\s*)([-*+]|\d+\.)( )(.*)$/);
2524
+ let lineHtml;
2525
+ if (listMatch && !wrapperKind) {
2526
+ const prefixLength = listMatch[1].length + listMatch[2].length + listMatch[3].length;
2527
+ const contentRuns = sliceRuns(runs, prefixLength, runs.reduce((sum, r) => sum + r.text.length, 0)).selected;
2528
+ lineHtml = runsToHtml(normalizeRuns(contentRuns));
2529
+ } else {
2530
+ lineHtml = runsToHtml(normalizeRuns(selectedRuns));
2531
+ }
2532
+ if (wrapperKind === "heading") {
2533
+ closeList();
2534
+ const level = Math.min(
2535
+ (wrapperData == null ? void 0 : wrapperData.level) ?? 1,
2536
+ 6
2537
+ );
2538
+ html += `<h${level} style="margin:0">${lineHtml}</h${level}>`;
2539
+ } else if (wrapperKind === "bullet-list") {
2540
+ openList("ul", 0);
2541
+ html += `<li>${lineHtml}</li>`;
2542
+ } else if (wrapperKind === "numbered-list") {
2543
+ openList("ol", 0);
2544
+ html += `<li>${lineHtml}</li>`;
2545
+ } else if (wrapperKind === "blockquote") {
2546
+ closeList();
2547
+ html += `<blockquote>${lineHtml}</blockquote>`;
2548
+ } else if (listMatch) {
2549
+ const isNumbered = /^\d+\.$/.test(listMatch[2]);
2550
+ const indent = Math.floor(listMatch[1].length / 2);
2551
+ openList(isNumbered ? "ol" : "ul", indent);
2552
+ html += `<li>${lineHtml}</li>`;
2553
+ } else {
2554
+ closeList();
2555
+ html += `<div>${lineHtml}</div>`;
2556
+ }
2557
+ }
2558
+ closeList();
2559
+ if (!html) {
2560
+ return "";
2561
+ }
2562
+ return `<div>${html}</div>`;
2563
+ }
2421
2564
  const runtime = {
2422
2565
  extensions,
2423
2566
  parse,
@@ -2425,6 +2568,7 @@ function createRuntime(extensions) {
2425
2568
  createState,
2426
2569
  updateSelection,
2427
2570
  serializeSelection,
2571
+ serializeSelectionToHtml,
2428
2572
  applyEdit
2429
2573
  };
2430
2574
  return runtime;
@@ -2579,7 +2723,7 @@ function normalizeNodes(result) {
2579
2723
  }
2580
2724
  return Array.isArray(result) ? result : [result];
2581
2725
  }
2582
- function renderDocContent(doc, extensions, _root) {
2726
+ function renderDocContent(doc, extensions, root) {
2583
2727
  const runs = [];
2584
2728
  let cursorOffset = 0;
2585
2729
  let lineIndex = 0;
@@ -2599,7 +2743,60 @@ function renderDocContent(doc, extensions, _root) {
2599
2743
  lineIndex += 1;
2600
2744
  }
2601
2745
  };
2602
- function renderInline(inline) {
2746
+ function getBlockKey(block) {
2747
+ if (block.type === "paragraph") {
2748
+ return "paragraph";
2749
+ }
2750
+ if (block.type === "block-wrapper") {
2751
+ return `block-wrapper:${block.kind}`;
2752
+ }
2753
+ if (block.type === "block-atom") {
2754
+ return `block-atom:${block.kind}`;
2755
+ }
2756
+ return "unknown";
2757
+ }
2758
+ function getElementKey(element) {
2759
+ if (element.hasAttribute("data-block")) {
2760
+ const blockType = element.getAttribute("data-block") ?? "unknown";
2761
+ const lineKind = element instanceof HTMLElement ? element.dataset.lineKind : null;
2762
+ if (lineKind && lineKind !== blockType) {
2763
+ return lineKind;
2764
+ }
2765
+ return blockType;
2766
+ }
2767
+ if (element.hasAttribute("data-block-wrapper")) {
2768
+ return `block-wrapper:${element.getAttribute("data-block-wrapper")}`;
2769
+ }
2770
+ if (element.hasAttribute("data-block-atom")) {
2771
+ return `block-atom:${element.getAttribute("data-block-atom")}`;
2772
+ }
2773
+ return "unknown";
2774
+ }
2775
+ function getInlineKey(inline) {
2776
+ if (inline.type === "text") {
2777
+ return "text";
2778
+ }
2779
+ if (inline.type === "inline-wrapper") {
2780
+ return `inline-wrapper:${inline.kind}`;
2781
+ }
2782
+ if (inline.type === "inline-atom") {
2783
+ return `inline-atom:${inline.kind}`;
2784
+ }
2785
+ return "unknown";
2786
+ }
2787
+ function getInlineElementKey(element) {
2788
+ if (element.classList.contains("cake-text")) {
2789
+ return "text";
2790
+ }
2791
+ if (element.hasAttribute("data-inline")) {
2792
+ return `inline-wrapper:${element.getAttribute("data-inline")}`;
2793
+ }
2794
+ if (element.hasAttribute("data-inline-atom")) {
2795
+ return `inline-atom:${element.getAttribute("data-inline-atom")}`;
2796
+ }
2797
+ return "unknown";
2798
+ }
2799
+ function reconcileInline(inline, existing) {
2603
2800
  for (const extension of extensions) {
2604
2801
  const render = extension.renderInline;
2605
2802
  if (!render) {
@@ -2611,6 +2808,17 @@ function renderDocContent(doc, extensions, _root) {
2611
2808
  }
2612
2809
  }
2613
2810
  if (inline.type === "text") {
2811
+ const canReuse = existing && existing instanceof HTMLSpanElement && getInlineElementKey(existing) === "text";
2812
+ if (canReuse) {
2813
+ const textNode = existing.firstChild;
2814
+ if (textNode instanceof Text) {
2815
+ if (textNode.textContent !== inline.text) {
2816
+ textNode.textContent = inline.text;
2817
+ }
2818
+ createTextRun$1(textNode);
2819
+ return [existing];
2820
+ }
2821
+ }
2614
2822
  const element = document.createElement("span");
2615
2823
  element.className = "cake-text";
2616
2824
  const node = document.createTextNode(inline.text);
@@ -2619,16 +2827,29 @@ function renderDocContent(doc, extensions, _root) {
2619
2827
  return [element];
2620
2828
  }
2621
2829
  if (inline.type === "inline-wrapper") {
2830
+ const canReuse = existing && existing instanceof HTMLSpanElement && getInlineElementKey(existing) === getInlineKey(inline);
2831
+ if (canReuse) {
2832
+ reconcileInlineChildren(existing, inline.children);
2833
+ return [existing];
2834
+ }
2622
2835
  const element = document.createElement("span");
2623
2836
  element.setAttribute("data-inline", inline.kind);
2624
2837
  for (const child of inline.children) {
2625
- for (const node of renderInline(child)) {
2838
+ for (const node of reconcileInline(child, null)) {
2626
2839
  element.append(node);
2627
2840
  }
2628
2841
  }
2629
2842
  return [element];
2630
2843
  }
2631
2844
  if (inline.type === "inline-atom") {
2845
+ const canReuse = existing && existing instanceof HTMLSpanElement && getInlineElementKey(existing) === getInlineKey(inline);
2846
+ if (canReuse) {
2847
+ const textNode = existing.firstChild;
2848
+ if (textNode instanceof Text) {
2849
+ createTextRun$1(textNode);
2850
+ return [existing];
2851
+ }
2852
+ }
2632
2853
  const element = document.createElement("span");
2633
2854
  element.setAttribute("data-inline-atom", inline.kind);
2634
2855
  const node = document.createTextNode(" ");
@@ -2638,7 +2859,25 @@ function renderDocContent(doc, extensions, _root) {
2638
2859
  }
2639
2860
  return [];
2640
2861
  }
2641
- function renderBlock(block) {
2862
+ function reconcileInlineChildren(parent, inlines) {
2863
+ const mergedInlines = mergeInlineForRender(inlines);
2864
+ const existingChildren2 = Array.from(parent.children);
2865
+ const newChildren = [];
2866
+ mergedInlines.forEach((inline, i) => {
2867
+ const existingChild = existingChildren2[i] ?? null;
2868
+ const canReuse = existingChild && getInlineElementKey(existingChild) === getInlineKey(inline);
2869
+ const nodes = reconcileInline(inline, canReuse ? existingChild : null);
2870
+ newChildren.push(...nodes);
2871
+ });
2872
+ if (newChildren.length === existingChildren2.length && newChildren.every((node, i) => node === existingChildren2[i])) {
2873
+ return;
2874
+ }
2875
+ parent.replaceChildren(...newChildren);
2876
+ }
2877
+ function renderInline(inline) {
2878
+ return reconcileInline(inline, null);
2879
+ }
2880
+ function reconcileBlock(block, existing) {
2642
2881
  for (const extension of extensions) {
2643
2882
  const render = extension.renderBlock;
2644
2883
  if (!render) {
@@ -2650,12 +2889,35 @@ function renderDocContent(doc, extensions, _root) {
2650
2889
  }
2651
2890
  }
2652
2891
  if (block.type === "paragraph") {
2892
+ const canReuse = existing && existing instanceof HTMLDivElement && getElementKey(existing) === "paragraph";
2893
+ const currentLineIndex = context.getLineIndex();
2894
+ context.incrementLineIndex();
2895
+ if (canReuse) {
2896
+ existing.setAttribute("data-line-index", String(currentLineIndex));
2897
+ if (block.content.length === 0) {
2898
+ const firstChild = existing.firstChild;
2899
+ if (firstChild instanceof Text && existing.querySelector("br")) {
2900
+ if (firstChild.textContent !== "") {
2901
+ firstChild.textContent = "";
2902
+ }
2903
+ createTextRun$1(firstChild);
2904
+ return [existing];
2905
+ }
2906
+ existing.replaceChildren();
2907
+ const textNode = document.createTextNode("");
2908
+ createTextRun$1(textNode);
2909
+ existing.append(textNode);
2910
+ existing.append(document.createElement("br"));
2911
+ return [existing];
2912
+ }
2913
+ reconcileInlineChildren(existing, block.content);
2914
+ return [existing];
2915
+ }
2653
2916
  const element = document.createElement("div");
2654
2917
  element.setAttribute("data-block", "paragraph");
2655
- element.setAttribute("data-line-index", String(context.getLineIndex()));
2918
+ element.setAttribute("data-line-index", String(currentLineIndex));
2656
2919
  element.classList.add("cake-line");
2657
2920
  element.dataset.lineKind = "paragraph";
2658
- context.incrementLineIndex();
2659
2921
  if (block.content.length === 0) {
2660
2922
  const textNode = document.createTextNode("");
2661
2923
  createTextRun$1(textNode);
@@ -2664,7 +2926,7 @@ function renderDocContent(doc, extensions, _root) {
2664
2926
  } else {
2665
2927
  const mergedContent = mergeInlineForRender(block.content);
2666
2928
  for (const inline of mergedContent) {
2667
- for (const node of renderInline(inline)) {
2929
+ for (const node of reconcileInline(inline, null)) {
2668
2930
  element.append(node);
2669
2931
  }
2670
2932
  }
@@ -2672,6 +2934,11 @@ function renderDocContent(doc, extensions, _root) {
2672
2934
  return [element];
2673
2935
  }
2674
2936
  if (block.type === "block-wrapper") {
2937
+ const canReuse = existing && existing instanceof HTMLDivElement && getElementKey(existing) === getBlockKey(block);
2938
+ if (canReuse) {
2939
+ reconcileBlockChildren(existing, block.blocks);
2940
+ return [existing];
2941
+ }
2675
2942
  const element = document.createElement("div");
2676
2943
  element.setAttribute("data-block-wrapper", block.kind);
2677
2944
  for (const node of renderBlocks(block.blocks)) {
@@ -2680,15 +2947,41 @@ function renderDocContent(doc, extensions, _root) {
2680
2947
  return [element];
2681
2948
  }
2682
2949
  if (block.type === "block-atom") {
2950
+ const canReuse = existing && existing instanceof HTMLDivElement && getElementKey(existing) === getBlockKey(block);
2951
+ const currentLineIndex = context.getLineIndex();
2952
+ context.incrementLineIndex();
2953
+ if (canReuse) {
2954
+ existing.setAttribute("data-line-index", String(currentLineIndex));
2955
+ return [existing];
2956
+ }
2683
2957
  const element = document.createElement("div");
2684
2958
  element.setAttribute("data-block-atom", block.kind);
2685
- element.setAttribute("data-line-index", String(context.getLineIndex()));
2959
+ element.setAttribute("data-line-index", String(currentLineIndex));
2686
2960
  element.classList.add("cake-line");
2687
- context.incrementLineIndex();
2688
2961
  return [element];
2689
2962
  }
2690
2963
  return [];
2691
2964
  }
2965
+ function reconcileBlockChildren(parent, blocks) {
2966
+ const existingChildren2 = Array.from(parent.children);
2967
+ const newChildren = [];
2968
+ blocks.forEach((block, index) => {
2969
+ const existingChild = existingChildren2[index] ?? null;
2970
+ const canReuse = existingChild && getElementKey(existingChild) === getBlockKey(block);
2971
+ const nodes = reconcileBlock(block, canReuse ? existingChild : null);
2972
+ newChildren.push(...nodes);
2973
+ if (index < blocks.length - 1) {
2974
+ cursorOffset += 1;
2975
+ }
2976
+ });
2977
+ if (newChildren.length === existingChildren2.length && newChildren.every((node, i) => node === existingChildren2[i])) {
2978
+ return;
2979
+ }
2980
+ parent.replaceChildren(...newChildren);
2981
+ }
2982
+ function renderBlock(block) {
2983
+ return reconcileBlock(block, null);
2984
+ }
2692
2985
  function renderBlocks(blocks) {
2693
2986
  const nodes = [];
2694
2987
  blocks.forEach((block, index) => {
@@ -2699,10 +2992,17 @@ function renderDocContent(doc, extensions, _root) {
2699
2992
  });
2700
2993
  return nodes;
2701
2994
  }
2995
+ const existingChildren = root ? Array.from(root.children) : [];
2702
2996
  const contentNodes = [];
2703
- for (const node of renderBlocks(doc.blocks)) {
2704
- contentNodes.push(node);
2705
- }
2997
+ doc.blocks.forEach((block, index) => {
2998
+ const existingChild = existingChildren[index] ?? null;
2999
+ const canReuse = existingChild && getElementKey(existingChild) === getBlockKey(block);
3000
+ const nodes = reconcileBlock(block, canReuse ? existingChild : null);
3001
+ contentNodes.push(...nodes);
3002
+ if (index < doc.blocks.length - 1) {
3003
+ cursorOffset += 1;
3004
+ }
3005
+ });
2706
3006
  return { content: contentNodes, map: createDomMap(runs) };
2707
3007
  }
2708
3008
  function mergeInlineForRender(inlines) {
@@ -3224,13 +3524,13 @@ function CakeLinkPopover(params) {
3224
3524
  if (state2.status !== "open") {
3225
3525
  return;
3226
3526
  }
3227
- container.addEventListener("scroll", reposition, { passive: true });
3527
+ container.addEventListener("scroll", close, { passive: true });
3228
3528
  window.addEventListener("resize", reposition);
3229
3529
  return () => {
3230
- container.removeEventListener("scroll", reposition);
3530
+ container.removeEventListener("scroll", close);
3231
3531
  window.removeEventListener("resize", reposition);
3232
3532
  };
3233
- }, [container, reposition, state2.status]);
3533
+ }, [close, container, reposition, state2.status]);
3234
3534
  const handleMouseDown = require$$0.useCallback(
3235
3535
  (event) => {
3236
3536
  event.stopPropagation();
@@ -3299,7 +3599,7 @@ function CakeLinkPopover(params) {
3299
3599
  handleCancel();
3300
3600
  }
3301
3601
  };
3302
- return /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(
3602
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
3303
3603
  "div",
3304
3604
  {
3305
3605
  className: "cake-link-popover",
@@ -3312,7 +3612,7 @@ function CakeLinkPopover(params) {
3312
3612
  },
3313
3613
  onMouseDown: handleMouseDown,
3314
3614
  onClick: (event) => event.stopPropagation(),
3315
- children: state2.isEditing ? /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(
3615
+ children: state2.isEditing ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
3316
3616
  "form",
3317
3617
  {
3318
3618
  className: "cake-link-editor",
@@ -3321,7 +3621,7 @@ function CakeLinkPopover(params) {
3321
3621
  handleSave();
3322
3622
  },
3323
3623
  children: [
3324
- /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(
3624
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3325
3625
  "input",
3326
3626
  {
3327
3627
  className: "cake-link-input",
@@ -3338,56 +3638,24 @@ function CakeLinkPopover(params) {
3338
3638
  },
3339
3639
  onKeyDown: handleInputKeyDown,
3340
3640
  placeholder: "https://"
3341
- },
3342
- void 0,
3343
- false,
3344
- {
3345
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
3346
- lineNumber: 281,
3347
- columnNumber: 11
3348
- },
3349
- this
3641
+ }
3350
3642
  ),
3351
- /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV("button", { type: "submit", className: "cake-link-save", children: "Save" }, void 0, false, {
3352
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
3353
- lineNumber: 297,
3354
- columnNumber: 11
3355
- }, this),
3356
- /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(
3643
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "submit", className: "cake-link-save", children: "Save" }),
3644
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3357
3645
  "button",
3358
3646
  {
3359
3647
  type: "button",
3360
3648
  className: "cake-link-cancel",
3361
3649
  onClick: handleCancel,
3362
3650
  children: "Cancel"
3363
- },
3364
- void 0,
3365
- false,
3366
- {
3367
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
3368
- lineNumber: 300,
3369
- columnNumber: 11
3370
- },
3371
- this
3651
+ }
3372
3652
  )
3373
3653
  ]
3374
- },
3375
- void 0,
3376
- true,
3377
- {
3378
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
3379
- lineNumber: 274,
3380
- columnNumber: 9
3381
- },
3382
- this
3383
- ) : /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(jsxDevRuntimeExports.Fragment, { children: [
3384
- /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV("div", { className: "cake-link-url", title: displayUrl, children: displayUrl }, void 0, false, {
3385
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
3386
- lineNumber: 310,
3387
- columnNumber: 11
3388
- }, this),
3389
- /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV("div", { className: "cake-link-actions", children: [
3390
- /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(
3654
+ }
3655
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3656
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "cake-link-url", title: displayUrl, children: displayUrl }),
3657
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "cake-link-actions", children: [
3658
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3391
3659
  "button",
3392
3660
  {
3393
3661
  type: "button",
@@ -3395,22 +3663,10 @@ function CakeLinkPopover(params) {
3395
3663
  onClick: handleEdit,
3396
3664
  title: "Edit link",
3397
3665
  "aria-label": "Edit link",
3398
- children: /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(lucideReact.Pencil, { className: "cake-link-icon" }, void 0, false, {
3399
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
3400
- lineNumber: 321,
3401
- columnNumber: 15
3402
- }, this)
3403
- },
3404
- void 0,
3405
- false,
3406
- {
3407
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
3408
- lineNumber: 314,
3409
- columnNumber: 13
3410
- },
3411
- this
3666
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(lucideReact.Pencil, { className: "cake-link-icon" })
3667
+ }
3412
3668
  ),
3413
- /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(
3669
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3414
3670
  "button",
3415
3671
  {
3416
3672
  type: "button",
@@ -3418,22 +3674,10 @@ function CakeLinkPopover(params) {
3418
3674
  onClick: handleOpen,
3419
3675
  title: "Open link",
3420
3676
  "aria-label": "Open link",
3421
- children: /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(lucideReact.ExternalLink, { className: "cake-link-icon" }, void 0, false, {
3422
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
3423
- lineNumber: 330,
3424
- columnNumber: 15
3425
- }, this)
3426
- },
3427
- void 0,
3428
- false,
3429
- {
3430
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
3431
- lineNumber: 323,
3432
- columnNumber: 13
3433
- },
3434
- this
3677
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(lucideReact.ExternalLink, { className: "cake-link-icon" })
3678
+ }
3435
3679
  ),
3436
- /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(
3680
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3437
3681
  "button",
3438
3682
  {
3439
3683
  type: "button",
@@ -3441,40 +3685,12 @@ function CakeLinkPopover(params) {
3441
3685
  onClick: handleUnlink,
3442
3686
  title: "Remove link",
3443
3687
  "aria-label": "Remove link",
3444
- children: /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(lucideReact.Unlink, { className: "cake-link-icon" }, void 0, false, {
3445
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
3446
- lineNumber: 339,
3447
- columnNumber: 15
3448
- }, this)
3449
- },
3450
- void 0,
3451
- false,
3452
- {
3453
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
3454
- lineNumber: 332,
3455
- columnNumber: 13
3456
- },
3457
- this
3688
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(lucideReact.Unlink, { className: "cake-link-icon" })
3689
+ }
3458
3690
  )
3459
- ] }, void 0, true, {
3460
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
3461
- lineNumber: 313,
3462
- columnNumber: 11
3463
- }, this)
3464
- ] }, void 0, true, {
3465
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
3466
- lineNumber: 309,
3467
- columnNumber: 9
3468
- }, this)
3469
- },
3470
- void 0,
3471
- false,
3472
- {
3473
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link-popover.tsx",
3474
- lineNumber: 261,
3475
- columnNumber: 5
3476
- },
3477
- this
3691
+ ] })
3692
+ ] })
3693
+ }
3478
3694
  );
3479
3695
  }
3480
3696
  function buildLayoutModel(lines, measurer) {
@@ -3823,21 +4039,13 @@ const linkExtension = {
3823
4039
  if (!context.contentRoot || !context.toOverlayRect) {
3824
4040
  return null;
3825
4041
  }
3826
- return /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(
4042
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
3827
4043
  CakeLinkPopover,
3828
4044
  {
3829
4045
  container: context.container,
3830
4046
  contentRoot: context.contentRoot,
3831
4047
  toOverlayRect: context.toOverlayRect
3832
- },
3833
- void 0,
3834
- false,
3835
- {
3836
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/link/link.tsx",
3837
- lineNumber: 179,
3838
- columnNumber: 7
3839
- },
3840
- this
4048
+ }
3841
4049
  );
3842
4050
  }
3843
4051
  };
@@ -5337,6 +5545,7 @@ function ScrollbarOverlay({ container }) {
5337
5545
  const [isDragging, setIsDragging] = require$$0.useState(false);
5338
5546
  const [isHovered, setIsHovered] = require$$0.useState(false);
5339
5547
  const [isScrolling, setIsScrolling] = require$$0.useState(false);
5548
+ const [isDarkMode, setIsDarkMode] = require$$0.useState(false);
5340
5549
  const dragStartRef = require$$0.useRef(
5341
5550
  null
5342
5551
  );
@@ -5351,6 +5560,24 @@ function ScrollbarOverlay({ container }) {
5351
5560
  );
5352
5561
  const maxScrollTop = scrollHeight - clientHeight;
5353
5562
  const thumbTop = maxScrollTop > 0 ? TRACK_PADDING + scrollTop / maxScrollTop * (trackHeight - thumbHeight) : TRACK_PADDING;
5563
+ require$$0.useEffect(() => {
5564
+ function checkDarkMode() {
5565
+ const html = document.documentElement;
5566
+ setIsDarkMode(html.classList.contains("dark"));
5567
+ }
5568
+ checkDarkMode();
5569
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
5570
+ mediaQuery.addEventListener("change", checkDarkMode);
5571
+ const observer = new MutationObserver(checkDarkMode);
5572
+ observer.observe(document.documentElement, {
5573
+ attributes: true,
5574
+ attributeFilter: ["class"]
5575
+ });
5576
+ return () => {
5577
+ mediaQuery.removeEventListener("change", checkDarkMode);
5578
+ observer.disconnect();
5579
+ };
5580
+ }, []);
5354
5581
  require$$0.useEffect(() => {
5355
5582
  function update() {
5356
5583
  setState({
@@ -5488,6 +5715,13 @@ function ScrollbarOverlay({ container }) {
5488
5715
  return null;
5489
5716
  }
5490
5717
  const isVisible = isDragging || isHovered || isScrolling;
5718
+ const wrapperStyle = {
5719
+ position: "absolute",
5720
+ inset: 0,
5721
+ pointerEvents: "none",
5722
+ overflow: "hidden",
5723
+ zIndex: 50
5724
+ };
5491
5725
  const trackStyle = {
5492
5726
  position: "absolute",
5493
5727
  top: 0,
@@ -5497,6 +5731,12 @@ function ScrollbarOverlay({ container }) {
5497
5731
  height: clientHeight,
5498
5732
  pointerEvents: "auto"
5499
5733
  };
5734
+ const getThumbColor = () => {
5735
+ if (isDarkMode) {
5736
+ return isDragging ? "rgba(255, 255, 255, 0.5)" : "rgba(255, 255, 255, 0.3)";
5737
+ }
5738
+ return isDragging ? "rgba(0, 0, 0, 0.5)" : "rgba(0, 0, 0, 0.3)";
5739
+ };
5500
5740
  const thumbStyle = {
5501
5741
  position: "absolute",
5502
5742
  right: "2px",
@@ -5504,54 +5744,35 @@ function ScrollbarOverlay({ container }) {
5504
5744
  borderRadius: "9999px",
5505
5745
  height: thumbHeight,
5506
5746
  top: thumbTop,
5507
- opacity: isVisible ? 1 : 0
5747
+ opacity: isVisible ? 1 : 0,
5748
+ backgroundColor: getThumbColor(),
5749
+ transition: "opacity 150ms",
5750
+ cursor: "pointer"
5508
5751
  };
5509
- return /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(
5752
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: wrapperStyle, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
5510
5753
  "div",
5511
5754
  {
5512
5755
  "data-testid": "custom-scrollbar",
5513
5756
  "aria-hidden": "true",
5514
- className: "pointer-events-auto absolute top-0 right-0 bottom-0 z-50 w-3",
5515
5757
  style: trackStyle,
5516
5758
  onClick: handleTrackClick,
5517
5759
  onMouseEnter: () => setIsHovered(true),
5518
5760
  onMouseLeave: () => setIsHovered(false),
5519
- children: /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(
5761
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
5520
5762
  "div",
5521
5763
  {
5522
5764
  "data-testid": "scrollbar-thumb",
5523
- className: `absolute right-0.5 w-1.5 cursor-pointer rounded-full transition-opacity duration-150 ${isDragging ? "bg-black/50 dark:bg-white/50" : "bg-black/30 dark:bg-white/30"}`,
5524
5765
  style: thumbStyle,
5525
5766
  onMouseDown: handleThumbMouseDown
5526
- },
5527
- void 0,
5528
- false,
5529
- {
5530
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/scrollbar/index.tsx",
5531
- lineNumber: 230,
5532
- columnNumber: 7
5533
- },
5534
- this
5767
+ }
5535
5768
  )
5536
- },
5537
- void 0,
5538
- false,
5539
- {
5540
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/scrollbar/index.tsx",
5541
- lineNumber: 221,
5542
- columnNumber: 5
5543
- },
5544
- this
5545
- );
5769
+ }
5770
+ ) });
5546
5771
  }
5547
5772
  const scrollbarExtension = {
5548
5773
  name: "scrollbar",
5549
5774
  renderOverlay(context) {
5550
- return /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(ScrollbarOverlay, { container: context.container }, void 0, false, {
5551
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/extensions/scrollbar/index.tsx",
5552
- lineNumber: 247,
5553
- columnNumber: 12
5554
- }, this);
5775
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollbarOverlay, { container: context.container });
5555
5776
  }
5556
5777
  };
5557
5778
  const STRIKE_KIND = "strikethrough";
@@ -6069,7 +6290,7 @@ function computeSelectionRects(layout, selection, measurer) {
6069
6290
  return rects;
6070
6291
  }
6071
6292
  function computeCaretRect(caret) {
6072
- const height = caret.lineRect.height;
6293
+ const height = caret.fontSize * 1.2;
6073
6294
  const contentHeight = caret.lineRect.height > 0 ? Math.max(
6074
6295
  0,
6075
6296
  caret.lineRect.height - caret.padding.top - caret.padding.bottom
@@ -6123,6 +6344,7 @@ function getSelectionGeometry(params) {
6123
6344
  scroll
6124
6345
  }),
6125
6346
  lineLength: lineInfo.cursorLength,
6347
+ fontSize: getComputedFontSize(lineElement),
6126
6348
  padding: getComputedVerticalPadding(lineElement)
6127
6349
  };
6128
6350
  const caretRect = computeCaretRect(caretMeasurement);
@@ -6166,6 +6388,7 @@ function getSelectionGeometry(params) {
6166
6388
  scroll
6167
6389
  }),
6168
6390
  lineLength: lineInfo.cursorLength,
6391
+ fontSize: getComputedFontSize(focusLineElement),
6169
6392
  padding: getComputedVerticalPadding(focusLineElement)
6170
6393
  };
6171
6394
  focusRect = computeCaretRect(caretMeasurement);
@@ -6292,6 +6515,11 @@ function getComputedVerticalPadding(lineElement) {
6292
6515
  bottom: Number.isFinite(bottom) ? bottom : 0
6293
6516
  };
6294
6517
  }
6518
+ function getComputedFontSize(lineElement) {
6519
+ const fontSize = window.getComputedStyle(lineElement).fontSize;
6520
+ const parsed = Number.parseFloat(fontSize);
6521
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 16;
6522
+ }
6295
6523
  function rectRight(rect) {
6296
6524
  return rect.left + rect.width;
6297
6525
  }
@@ -6689,6 +6917,36 @@ function nextWordBreak(text, offset) {
6689
6917
  }
6690
6918
  return text.length;
6691
6919
  }
6920
+ const MAX_HTML_INPUT_LENGTH = 5e5;
6921
+ const MAX_MARKDOWN_OUTPUT_LENGTH = 1e5;
6922
+ function isHTMLElement(node) {
6923
+ return node !== null && node.nodeType === Node.ELEMENT_NODE;
6924
+ }
6925
+ function isElement(node) {
6926
+ return node !== null && node.nodeType === Node.ELEMENT_NODE;
6927
+ }
6928
+ const CLEANUP_PATTERNS = {
6929
+ unescapeHeaders: /^\\(#{1,6})\s+/gm,
6930
+ unescapeBlockquote: /^\\>/gm,
6931
+ unescapeMarkdown: /\\([*_`~[\]])/g,
6932
+ unescapeListBullets: /^(\s*)\\([-*+])(\s+)/gm,
6933
+ unescapeListNumbers: /^(\s*)(\d+)\\\.(\s+)/gm,
6934
+ normalizeBullets: /^(\s*)[-*+](\s{2,})/gm,
6935
+ normalizeNumbers: /^[\s]*\d+\.[\s]+/gm,
6936
+ normalizeHeaders: /^(#{1,6})[\s]{2,}/gm,
6937
+ removeTrailingSpaces: /[ \t]+$/gm,
6938
+ headersInBlockquotes: /^>\s*(#{1,6}\s+.*)/gm,
6939
+ excessiveNewlines: /\n{3,}/g,
6940
+ interlacedTableGaps: /(\|[^\n]*\|)\s*\n\s*\n+\s*(\|[^\n]*\|)/g,
6941
+ complexTableGaps: /(\|[^\n]*\|)(\s*\n){2,}(\|[^\n]*\|)/g
6942
+ };
6943
+ const HTML_PREPROCESSING_PATTERNS = {
6944
+ removeStyleAndDataAttrs: /\s(?:style|data-[^=]*|id)="[^"]*"/gi,
6945
+ removeNonCodeClasses: /\sclass="(?![^"]*(?:language-|hljs))[^"]*"/gi,
6946
+ removeEmptyElements: /<(\w+)[^>]*>\s*<\/\1>/gi,
6947
+ normalizeSpaces: /[ \t]{2,}/g,
6948
+ reduceBlankLines: /\n\s*\n/g
6949
+ };
6692
6950
  const turndownService = new TurndownService({
6693
6951
  headingStyle: "atx",
6694
6952
  bulletListMarker: "-",
@@ -6706,6 +6964,13 @@ turndownService.addRule("strikethrough", {
6706
6964
  turndownService.addRule("codeBlock", {
6707
6965
  filter: "pre",
6708
6966
  replacement: (content, node) => {
6967
+ if (!isHTMLElement(node)) {
6968
+ return `
6969
+ \`\`\`
6970
+ ${content}
6971
+ \`\`\`
6972
+ `;
6973
+ }
6709
6974
  const codeElement = node.querySelector("code");
6710
6975
  if (codeElement) {
6711
6976
  const className = codeElement.className || "";
@@ -6724,12 +6989,308 @@ ${content}
6724
6989
  `;
6725
6990
  }
6726
6991
  });
6727
- function htmlToMarkdownForPaste(html) {
6728
- const trimmed = html.trim();
6729
- if (!trimmed) {
6992
+ turndownService.addRule("tableRow", {
6993
+ filter: "tr",
6994
+ replacement: (_content, node) => {
6995
+ var _a;
6996
+ if (!isHTMLElement(node)) {
6997
+ return "";
6998
+ }
6999
+ const isHeaderRow = ((_a = node.parentNode) == null ? void 0 : _a.nodeName) === "THEAD";
7000
+ const cells = Array.from(node.querySelectorAll("td, th"));
7001
+ const cellContents = cells.map((cell) => {
7002
+ const text = cell.textContent || "";
7003
+ return text.replace(/\|/g, "\\|").trim();
7004
+ });
7005
+ let result = "| " + cellContents.join(" | ") + " |\n";
7006
+ if (isHeaderRow) {
7007
+ const separators = cells.map((cell) => {
7008
+ const align = cell.getAttribute("align");
7009
+ if (align === "center") {
7010
+ return ":-------------:";
7011
+ }
7012
+ if (align === "right") {
7013
+ return "-------------:";
7014
+ }
7015
+ return "-------------";
7016
+ });
7017
+ result += "| " + separators.join(" | ") + " |\n";
7018
+ }
7019
+ return result;
7020
+ }
7021
+ });
7022
+ turndownService.addRule("taskList", {
7023
+ filter: (node) => {
7024
+ if (!isHTMLElement(node)) {
7025
+ return false;
7026
+ }
7027
+ if (node.nodeName !== "LI") {
7028
+ return false;
7029
+ }
7030
+ if (node.querySelector('input[type="checkbox"]') !== null) {
7031
+ return true;
7032
+ }
7033
+ const textContent = node.textContent || "";
7034
+ const hasCheckboxSymbols = /^[\s]*[☐☑✓✗[\]]/m.test(textContent) || /^[\s]*\[[ x]\]/m.test(textContent);
7035
+ const hasCheckboxClass = Boolean(
7036
+ node.className && (node.className.includes("task") || node.className.includes("checkbox") || node.className.includes("todo"))
7037
+ );
7038
+ return hasCheckboxSymbols || hasCheckboxClass;
7039
+ },
7040
+ replacement: (content, node) => {
7041
+ if (!isHTMLElement(node)) {
7042
+ return content;
7043
+ }
7044
+ const checkbox = node.querySelector('input[type="checkbox"]');
7045
+ const isCheckbox = checkbox instanceof HTMLInputElement && checkbox.type === "checkbox";
7046
+ let isChecked = false;
7047
+ if (isCheckbox) {
7048
+ isChecked = checkbox.checked;
7049
+ } else {
7050
+ const textContent2 = node.textContent || "";
7051
+ isChecked = /^[\s]*[☑✓✗]/.test(textContent2) || /^[\s]*\[x\]/i.test(textContent2);
7052
+ }
7053
+ const prefix = isChecked ? "- [x] " : "- [ ] ";
7054
+ let textContent = content;
7055
+ textContent = textContent.replace(/^\s*\[[ x]\]\s*/gi, "");
7056
+ textContent = textContent.replace(/^\s*[☐☑✓✗]\s*/g, "");
7057
+ textContent = textContent.replace(/^\s*\[[x ]\]\s*/gi, "");
7058
+ textContent = textContent.replace(/^\s*\\?\[[ x]\\?\]\s*/gi, "");
7059
+ textContent = textContent.replace(/\\?\[\\?\s*\\?\]\\?\s*/g, "");
7060
+ return prefix + textContent.trim() + "\n";
7061
+ }
7062
+ });
7063
+ turndownService.addRule("list", {
7064
+ filter: ["ul", "ol"],
7065
+ replacement: (content, node) => {
7066
+ const parent = node.parentNode;
7067
+ const isNested = isElement(parent) && parent.tagName === "LI";
7068
+ if (isNested) {
7069
+ return "\n" + content;
7070
+ }
7071
+ return "\n" + content + "\n";
7072
+ }
7073
+ });
7074
+ turndownService.addRule("blockquote", {
7075
+ filter: "blockquote",
7076
+ replacement: (content) => {
7077
+ const lines = content.trim().split("\n");
7078
+ const processedLines = lines.map((line) => {
7079
+ const trimmed = line.trim();
7080
+ if (!trimmed) {
7081
+ return ">";
7082
+ }
7083
+ const existingQuotes = trimmed.match(/^(>\s*)+/);
7084
+ if (existingQuotes) {
7085
+ return existingQuotes[0] + " " + trimmed;
7086
+ }
7087
+ return "> " + trimmed;
7088
+ });
7089
+ return "\n" + processedLines.join("\n") + "\n";
7090
+ }
7091
+ });
7092
+ turndownService.addRule("horizontalRule", {
7093
+ filter: "hr",
7094
+ replacement: () => "\n---\n"
7095
+ });
7096
+ turndownService.addRule("inlineCode", {
7097
+ filter: (node) => {
7098
+ const parent = node.parentNode;
7099
+ return node.nodeName === "CODE" && !(isElement(parent) && parent.tagName === "PRE");
7100
+ },
7101
+ replacement: (content) => {
7102
+ const backtickCount = Math.max(
7103
+ 1,
7104
+ (content.match(/`+/g) || []).reduce(
7105
+ (max, match) => Math.max(max, match.length),
7106
+ 0
7107
+ ) + 1
7108
+ );
7109
+ const delimiter = "`".repeat(backtickCount);
7110
+ return delimiter + content + delimiter;
7111
+ }
7112
+ });
7113
+ turndownService.addRule("image", {
7114
+ filter: "img",
7115
+ replacement: (_content, node) => {
7116
+ if (!isHTMLElement(node)) {
7117
+ return "";
7118
+ }
7119
+ const src = node.getAttribute("src") || "";
7120
+ const alt = node.getAttribute("alt") || "";
7121
+ const title = node.getAttribute("title");
7122
+ if (title) {
7123
+ return `![${alt}](${src} "${title}")`;
7124
+ }
7125
+ return `![${alt}](${src})`;
7126
+ }
7127
+ });
7128
+ turndownService.addRule("highlight", {
7129
+ filter: (node) => {
7130
+ var _a, _b;
7131
+ if (!isHTMLElement(node)) {
7132
+ return false;
7133
+ }
7134
+ return node.nodeName === "MARK" || node.nodeName === "SPAN" && (((_a = node.style) == null ? void 0 : _a.backgroundColor) === "yellow" || ((_b = node.className) == null ? void 0 : _b.includes("highlight")));
7135
+ },
7136
+ replacement: (content) => `==${content}==`
7137
+ });
7138
+ turndownService.addRule("headerWithId", {
7139
+ filter: ["h1", "h2", "h3", "h4", "h5", "h6"],
7140
+ replacement: (content, node) => {
7141
+ if (!isHTMLElement(node)) {
7142
+ return content;
7143
+ }
7144
+ const rawLevel = parseInt(node.nodeName.charAt(1));
7145
+ const level = Math.min(rawLevel, 3);
7146
+ const hashes = "#".repeat(level);
7147
+ const id = node.getAttribute("id");
7148
+ if (id) {
7149
+ return `
7150
+ ${hashes} ${content} {#${id}}
7151
+ `;
7152
+ }
7153
+ return `
7154
+ ${hashes} ${content}
7155
+ `;
7156
+ }
7157
+ });
7158
+ function processTextNodes(element) {
7159
+ const spans = Array.from(element.querySelectorAll("span"));
7160
+ spans.forEach((span) => {
7161
+ var _a;
7162
+ const textContent = span.textContent || "";
7163
+ const textNode = document.createTextNode(textContent);
7164
+ (_a = span.parentNode) == null ? void 0 : _a.replaceChild(textNode, span);
7165
+ });
7166
+ }
7167
+ function detectSourceApp(html) {
7168
+ const detectionPatterns = {
7169
+ notion: ["notion-", "notranslate"],
7170
+ github: ["github.com", "js-file-line-container"],
7171
+ slack: ["slack-", "c-message"],
7172
+ "google-docs": ["docs.google.com", "kix-"]
7173
+ };
7174
+ for (const [app, patterns] of Object.entries(detectionPatterns)) {
7175
+ if (patterns.some((pattern) => html.includes(pattern))) {
7176
+ return app;
7177
+ }
7178
+ }
7179
+ return "unknown";
7180
+ }
7181
+ function preprocessForApp(html, app) {
7182
+ switch (app) {
7183
+ case "notion":
7184
+ return html.replace(/<div[^>]*class="[^"]*notion-[^"]*"[^>]*>/gi, "<div>").replace(/<span[^>]*class="[^"]*notion-[^"]*"[^>]*>/gi, "<span>").replace(/<details[^>]*>/gi, "<div>").replace(/<\/details>/gi, "</div>").replace(/<summary[^>]*>/gi, "<strong>").replace(/<\/summary>/gi, "</strong>");
7185
+ case "github":
7186
+ return html.replace(/<td[^>]*class="[^"]*blob-num[^"]*"[^>]*>.*?<\/td>/gi, "").replace(/<span[^>]*class="[^"]*pl-[^"]*"[^>]*>/gi, "<span>").replace(/<span[^>]*class="[^"]*highlight[^"]*"[^>]*>/gi, "<span>");
7187
+ case "slack":
7188
+ return html.replace(/<span[^>]*class="[^"]*c-member[^"]*"[^>]*>/gi, "<span>").replace(/<span[^>]*data-stringify-type="mention"[^>]*>/gi, "<span>").replace(
7189
+ /<span[^>]*class="[^"]*c-emoji[^"]*"[^>]*>([^<]*)<\/span>/gi,
7190
+ "$1"
7191
+ );
7192
+ case "google-docs":
7193
+ return html.replace(
7194
+ /<span[^>]*style="[^"]*font-weight:[^;"]*bold[^"]*"[^>]*>/gi,
7195
+ "<strong>"
7196
+ ).replace(
7197
+ /<span[^>]*style="[^"]*font-style:[^;"]*italic[^"]*"[^>]*>/gi,
7198
+ "<em>"
7199
+ ).replace(/<\/span>/gi, "").replace(/<p[^>]*style="[^"]*"[^>]*>/gi, "<p>");
7200
+ default:
7201
+ return html;
7202
+ }
7203
+ }
7204
+ function cleanupMarkdown(markdown) {
7205
+ return markdown.replace(CLEANUP_PATTERNS.unescapeHeaders, "$1 ").replace(CLEANUP_PATTERNS.unescapeBlockquote, ">").replace(CLEANUP_PATTERNS.unescapeMarkdown, "$1").replace(CLEANUP_PATTERNS.unescapeListBullets, "$1$2$3").replace(CLEANUP_PATTERNS.unescapeListNumbers, "$1$2.$3").replace(CLEANUP_PATTERNS.normalizeBullets, "$1- ").replace(CLEANUP_PATTERNS.normalizeNumbers, (match) => {
7206
+ var _a;
7207
+ const num = ((_a = match.match(/\d+/)) == null ? void 0 : _a[0]) || "1";
7208
+ return `${num}. `;
7209
+ }).replace(CLEANUP_PATTERNS.normalizeHeaders, "$1 ").replace(CLEANUP_PATTERNS.removeTrailingSpaces, "").replace(CLEANUP_PATTERNS.headersInBlockquotes, "\n$1").replace(CLEANUP_PATTERNS.excessiveNewlines, "\n\n").replace(CLEANUP_PATTERNS.interlacedTableGaps, "$1\n$2").replace(CLEANUP_PATTERNS.complexTableGaps, "$1\n$3").replace(/\*\*\[([^\]]+?)\]\(([^)]+?)\)\*\*/g, "[**$1**]($2)").replace(/__\[([^\]]+?)\]\(([^)]+?)\)__/g, "[**$1**]($2)").replace(/\*\[([^\]]+?)\]\(([^)]+?)\)\*/g, "[*$1*]($2)").replace(/_\[([^\]]+?)\]\(([^)]+?)\)_/g, "[*$1*]($2)").trim();
7210
+ }
7211
+ function preprocessHtml(html) {
7212
+ const sourceApp = detectSourceApp(html);
7213
+ let processedHtml = preprocessForApp(html, sourceApp);
7214
+ const parser = new DOMParser();
7215
+ const doc = parser.parseFromString(processedHtml, "text/html");
7216
+ if (doc.body) {
7217
+ processTextNodes(doc.body);
7218
+ processedHtml = doc.body.innerHTML;
7219
+ }
7220
+ return processedHtml.replace(HTML_PREPROCESSING_PATTERNS.removeStyleAndDataAttrs, "").replace(HTML_PREPROCESSING_PATTERNS.removeNonCodeClasses, "").replace(HTML_PREPROCESSING_PATTERNS.removeEmptyElements, "").replace(HTML_PREPROCESSING_PATTERNS.normalizeSpaces, " ").replace(HTML_PREPROCESSING_PATTERNS.reduceBlankLines, "\n\n").trim();
7221
+ }
7222
+ function sanitizeContent(content) {
7223
+ return content.replace(/<script[^>]*>.*?<\/script>/gi, "").replace(
7224
+ /<(?:iframe|object|embed)[^>]*>.*?<\/(?:iframe|object|embed)>/gi,
7225
+ ""
7226
+ );
7227
+ }
7228
+ function limitContentLength(content, maxLength = MAX_MARKDOWN_OUTPUT_LENGTH) {
7229
+ return content.substring(0, maxLength);
7230
+ }
7231
+ function sanitizeMarkdown(markdown) {
7232
+ return limitContentLength(sanitizeContent(markdown));
7233
+ }
7234
+ function shouldProcessPaste(htmlContent) {
7235
+ if (htmlContent.length > MAX_HTML_INPUT_LENGTH) {
7236
+ console.warn("HTML content too large for paste processing");
7237
+ return false;
7238
+ }
7239
+ const hasFormatting = /<(?:strong|b|em|i|u|s|del|strike|code|pre|h[1-6]|blockquote|ul|ol|li|table|tr|td|th|a|img|mark|span|div)[\s>]/i.test(
7240
+ htmlContent
7241
+ );
7242
+ if (!hasFormatting) {
7243
+ return false;
7244
+ }
7245
+ if (/<img\s/i.test(htmlContent)) {
7246
+ return true;
7247
+ }
7248
+ const strippedContent = htmlContent.replace(/<[^>]*>/g, "").trim();
7249
+ if (!strippedContent || strippedContent.length < 3) {
7250
+ return false;
7251
+ }
7252
+ return true;
7253
+ }
7254
+ function normalizeListPrefixes(content) {
7255
+ const lines = content.split("\n");
7256
+ let currentListType = null;
7257
+ let currentNumber = 1;
7258
+ return lines.map((line) => {
7259
+ const match = line.match(/^(\s*)([-*+]|\d+\.)( +)(.*)$/);
7260
+ if (match) {
7261
+ const [, indent, marker, , listContent] = match;
7262
+ if (currentListType === null) {
7263
+ currentListType = /\d+\./.test(marker) ? "numbered" : "bullet";
7264
+ }
7265
+ const newMarker = currentListType === "bullet" ? "-" : `${currentNumber}.`;
7266
+ if (currentListType === "numbered") {
7267
+ currentNumber += 1;
7268
+ }
7269
+ return `${indent}${newMarker} ${listContent}`;
7270
+ }
7271
+ return line;
7272
+ }).join("\n");
7273
+ }
7274
+ function convertHtmlToMarkdown(html) {
7275
+ try {
7276
+ const processedHtml = preprocessHtml(html);
7277
+ const markdown = turndownService.turndown(processedHtml);
7278
+ return cleanupMarkdown(markdown);
7279
+ } catch (error) {
7280
+ console.error("Error converting HTML to markdown:", error);
6730
7281
  return "";
6731
7282
  }
6732
- return turndownService.turndown(trimmed);
7283
+ }
7284
+ function htmlToMarkdownForPaste(htmlContent) {
7285
+ if (!shouldProcessPaste(htmlContent)) {
7286
+ return null;
7287
+ }
7288
+ const markdown = convertHtmlToMarkdown(htmlContent);
7289
+ if (!markdown.trim()) {
7290
+ return null;
7291
+ }
7292
+ const normalizedMarkdown = normalizeListPrefixes(markdown);
7293
+ return sanitizeMarkdown(normalizedMarkdown).replace(/\r\n?/g, "\n");
6733
7294
  }
6734
7295
  const defaultSelection = { start: 0, end: 0, affinity: "forward" };
6735
7296
  const COMPOSITION_COMMIT_CLEAR_DELAY_MS = 50;
@@ -6908,10 +7469,15 @@ class CakeEngine {
6908
7469
  }
6909
7470
  }
6910
7471
  setValue({ value, selection }) {
6911
- const nextSelection = selection ?? this.state.selection;
6912
- if (value === this.state.source && selection === void 0) {
7472
+ const valueChanged = value !== this.state.source;
7473
+ if (!valueChanged && selection === void 0) {
6913
7474
  return;
6914
7475
  }
7476
+ if (!valueChanged && selection !== void 0) {
7477
+ this.setSelection(selection);
7478
+ return;
7479
+ }
7480
+ const nextSelection = selection ?? this.state.selection;
6915
7481
  this.state = this.runtime.createState(value, nextSelection);
6916
7482
  this.render();
6917
7483
  }
@@ -7171,7 +7737,11 @@ class CakeEngine {
7171
7737
  this.extensions,
7172
7738
  this.contentRoot
7173
7739
  );
7174
- this.contentRoot.replaceChildren(...content);
7740
+ const existingChildren = Array.from(this.contentRoot.childNodes);
7741
+ const needsUpdate = content.length !== existingChildren.length || content.some((node, i) => node !== existingChildren[i]);
7742
+ if (needsUpdate) {
7743
+ this.contentRoot.replaceChildren(...content);
7744
+ }
7175
7745
  this.domMap = map;
7176
7746
  this.updateExtensionsOverlayPosition();
7177
7747
  if (!this.isComposing) {
@@ -7767,6 +8337,13 @@ class CakeEngine {
7767
8337
  }
7768
8338
  event.preventDefault();
7769
8339
  clipboardData.setData("text/plain", text);
8340
+ const html = this.runtime.serializeSelectionToHtml(
8341
+ this.state,
8342
+ this.state.selection
8343
+ );
8344
+ if (html) {
8345
+ clipboardData.setData("text/html", html);
8346
+ }
7770
8347
  }
7771
8348
  handleCut(event) {
7772
8349
  if (this.readOnly) {
@@ -9076,8 +9653,8 @@ class CakeEngine {
9076
9653
  if (!this.extensionsRoot) {
9077
9654
  return;
9078
9655
  }
9079
- const scrollTop = this.container.scrollTop;
9080
- const scrollLeft = this.container.scrollLeft;
9656
+ const scrollTop = Math.max(0, this.container.scrollTop);
9657
+ const scrollLeft = Math.max(0, this.container.scrollLeft);
9081
9658
  if (scrollTop === 0 && scrollLeft === 0) {
9082
9659
  this.extensionsRoot.style.transform = "";
9083
9660
  return;
@@ -10740,10 +11317,10 @@ const CakeEditor = require$$0.forwardRef(
10740
11317
  containerStyle.position = "relative";
10741
11318
  }
10742
11319
  const containerClassName = props.className ? `cake ${props.className}` : "cake";
10743
- const overlayContext = overlayRoot && containerRef.current && contentRoot ? {
11320
+ const overlayContext = containerRef.current && contentRoot ? {
10744
11321
  container: containerRef.current,
10745
11322
  contentRoot,
10746
- overlayRoot,
11323
+ overlayRoot: overlayRoot ?? void 0,
10747
11324
  toOverlayRect: (rect) => {
10748
11325
  var _a;
10749
11326
  const containerRect = (_a = containerRef.current) == null ? void 0 : _a.getBoundingClientRect();
@@ -10780,8 +11357,11 @@ const CakeEditor = require$$0.forwardRef(
10780
11357
  return { start: focus, end: focus };
10781
11358
  }
10782
11359
  } : null;
10783
- return /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(jsxDevRuntimeExports.Fragment, { children: [
10784
- /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(
11360
+ const hasOverlayExtensions = allExtensionsRef.current.some(
11361
+ (ext) => ext.renderOverlay
11362
+ );
11363
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { position: "relative", height: "100%" }, children: [
11364
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
10785
11365
  "div",
10786
11366
  {
10787
11367
  ref: containerRef,
@@ -10792,35 +11372,12 @@ const CakeEditor = require$$0.forwardRef(
10792
11372
  var _a;
10793
11373
  (_a = props.onBlur) == null ? void 0 : _a.call(props, event.nativeEvent);
10794
11374
  }
10795
- },
10796
- void 0,
10797
- false,
10798
- {
10799
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/react/CakeEditor.tsx",
10800
- lineNumber: 351,
10801
- columnNumber: 9
10802
- },
10803
- this
11375
+ }
10804
11376
  ),
10805
- overlayRoot && overlayContext ? reactDom.createPortal(
10806
- /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(jsxDevRuntimeExports.Fragment, { children: allExtensionsRef.current.map(
10807
- (extension) => extension.renderOverlay ? /* @__PURE__ */ jsxDevRuntimeExports.jsxDEV(require$$0.Fragment, { children: extension.renderOverlay(overlayContext) }, extension.name, false, {
10808
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/react/CakeEditor.tsx",
10809
- lineNumber: 365,
10810
- columnNumber: 21
10811
- }, this) : null
10812
- ) }, void 0, false, {
10813
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/react/CakeEditor.tsx",
10814
- lineNumber: 362,
10815
- columnNumber: 15
10816
- }, this),
10817
- overlayRoot
11377
+ overlayContext && hasOverlayExtensions ? allExtensionsRef.current.map(
11378
+ (extension) => extension.renderOverlay ? /* @__PURE__ */ jsxRuntimeExports.jsx(require$$0.Fragment, { children: extension.renderOverlay(overlayContext) }, extension.name) : null
10818
11379
  ) : null
10819
- ] }, void 0, true, {
10820
- fileName: "/Users/moboudra/dev/blankpage/cake/src/cake/react/CakeEditor.tsx",
10821
- lineNumber: 350,
10822
- columnNumber: 7
10823
- }, this);
11380
+ ] });
10824
11381
  }
10825
11382
  );
10826
11383
  CakeEditor.displayName = "CakeEditor";