@blankdotpage/cake 0.1.7 → 0.1.10

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.
Files changed (147) hide show
  1. package/dist/cake/clipboard.d.ts +1 -0
  2. package/dist/cake/clipboard.d.ts.map +1 -0
  3. package/dist/cake/clipboard.js +391 -0
  4. package/dist/cake/core/mapping/cursor-source-map.d.ts +1 -0
  5. package/dist/cake/core/mapping/cursor-source-map.d.ts.map +1 -0
  6. package/dist/cake/core/mapping/cursor-source-map.js +146 -0
  7. package/dist/cake/core/runtime.d.ts +3 -0
  8. package/dist/cake/core/runtime.d.ts.map +1 -0
  9. package/dist/cake/core/runtime.js +1758 -0
  10. package/dist/cake/core/types.d.ts +1 -0
  11. package/dist/cake/core/types.d.ts.map +1 -0
  12. package/dist/cake/core/types.js +1 -0
  13. package/dist/cake/dom/dom-map.d.ts +1 -0
  14. package/dist/cake/dom/dom-map.d.ts.map +1 -0
  15. package/dist/cake/dom/dom-map.js +151 -0
  16. package/dist/cake/dom/dom-selection.d.ts +1 -0
  17. package/dist/cake/dom/dom-selection.d.ts.map +1 -0
  18. package/dist/cake/dom/dom-selection.js +216 -0
  19. package/dist/cake/dom/render.d.ts +1 -0
  20. package/dist/cake/dom/render.d.ts.map +1 -0
  21. package/dist/cake/dom/render.js +470 -0
  22. package/dist/cake/dom/types.d.ts +1 -0
  23. package/dist/cake/dom/types.d.ts.map +1 -0
  24. package/dist/cake/dom/types.js +1 -0
  25. package/dist/cake/engine/cake-engine.d.ts +1 -0
  26. package/dist/cake/engine/cake-engine.d.ts.map +1 -0
  27. package/dist/cake/engine/cake-engine.js +3589 -0
  28. package/dist/cake/engine/selection/selection-geometry-dom.d.ts +1 -0
  29. package/dist/cake/engine/selection/selection-geometry-dom.d.ts.map +1 -0
  30. package/dist/cake/engine/selection/selection-geometry-dom.js +302 -0
  31. package/dist/cake/engine/selection/selection-geometry.d.ts +1 -0
  32. package/dist/cake/engine/selection/selection-geometry.d.ts.map +1 -0
  33. package/dist/cake/engine/selection/selection-geometry.js +158 -0
  34. package/dist/cake/engine/selection/selection-layout-dom.d.ts +1 -0
  35. package/dist/cake/engine/selection/selection-layout-dom.d.ts.map +1 -0
  36. package/dist/cake/engine/selection/selection-layout-dom.js +781 -0
  37. package/dist/cake/engine/selection/selection-layout.d.ts +1 -0
  38. package/dist/cake/engine/selection/selection-layout.d.ts.map +1 -0
  39. package/dist/cake/engine/selection/selection-layout.js +128 -0
  40. package/dist/cake/engine/selection/selection-navigation.d.ts +1 -0
  41. package/dist/cake/engine/selection/selection-navigation.d.ts.map +1 -0
  42. package/dist/cake/engine/selection/selection-navigation.js +229 -0
  43. package/dist/cake/engine/selection/visible-text.d.ts +1 -0
  44. package/dist/cake/engine/selection/visible-text.d.ts.map +1 -0
  45. package/dist/cake/engine/selection/visible-text.js +66 -0
  46. package/dist/cake/extensions/blockquote/blockquote.d.ts +1 -0
  47. package/dist/cake/extensions/blockquote/blockquote.d.ts.map +1 -0
  48. package/dist/cake/extensions/blockquote/blockquote.js +177 -0
  49. package/dist/cake/extensions/blockquote/index.d.ts +2 -0
  50. package/dist/cake/extensions/blockquote/index.d.ts.map +1 -0
  51. package/dist/cake/extensions/blockquote/index.js +1 -0
  52. package/dist/cake/extensions/bold/bold.d.ts +1 -0
  53. package/dist/cake/extensions/bold/bold.d.ts.map +1 -0
  54. package/dist/cake/extensions/bold/bold.js +113 -0
  55. package/dist/cake/extensions/bold/index.d.ts +2 -0
  56. package/dist/cake/extensions/bold/index.d.ts.map +1 -0
  57. package/dist/cake/extensions/bold/index.js +1 -0
  58. package/dist/cake/extensions/bundles.d.ts +1 -0
  59. package/dist/cake/extensions/bundles.d.ts.map +1 -0
  60. package/dist/cake/extensions/bundles.js +12 -0
  61. package/dist/cake/extensions/combined-emphasis/combined-emphasis.d.ts +1 -0
  62. package/dist/cake/extensions/combined-emphasis/combined-emphasis.d.ts.map +1 -0
  63. package/dist/cake/extensions/combined-emphasis/combined-emphasis.js +42 -0
  64. package/dist/cake/extensions/combined-emphasis/index.d.ts +2 -0
  65. package/dist/cake/extensions/combined-emphasis/index.d.ts.map +1 -0
  66. package/dist/cake/extensions/combined-emphasis/index.js +1 -0
  67. package/dist/cake/extensions/heading/heading.d.ts +1 -0
  68. package/dist/cake/extensions/heading/heading.d.ts.map +1 -0
  69. package/dist/cake/extensions/heading/heading.js +337 -0
  70. package/dist/cake/extensions/heading/index.d.ts +2 -0
  71. package/dist/cake/extensions/heading/index.d.ts.map +1 -0
  72. package/dist/cake/extensions/heading/index.js +1 -0
  73. package/dist/cake/extensions/image/image.d.ts +1 -0
  74. package/dist/cake/extensions/image/image.d.ts.map +1 -0
  75. package/dist/cake/extensions/image/image.js +120 -0
  76. package/dist/cake/extensions/image/index.d.ts +2 -0
  77. package/dist/cake/extensions/image/index.d.ts.map +1 -0
  78. package/dist/cake/extensions/image/index.js +1 -0
  79. package/dist/cake/extensions/index.d.ts +2 -2
  80. package/dist/cake/extensions/index.d.ts.map +1 -0
  81. package/dist/cake/extensions/index.js +25 -0
  82. package/dist/cake/extensions/italic/index.d.ts +2 -0
  83. package/dist/cake/extensions/italic/index.d.ts.map +1 -0
  84. package/dist/cake/extensions/italic/index.js +1 -0
  85. package/dist/cake/extensions/italic/italic.d.ts +1 -0
  86. package/dist/cake/extensions/italic/italic.d.ts.map +1 -0
  87. package/dist/cake/extensions/italic/italic.js +91 -0
  88. package/dist/cake/extensions/link/index.d.ts +2 -0
  89. package/dist/cake/extensions/link/index.d.ts.map +1 -0
  90. package/dist/cake/extensions/link/index.js +1 -0
  91. package/dist/cake/extensions/link/link-popover.d.ts +1 -0
  92. package/dist/cake/extensions/link/link-popover.d.ts.map +1 -0
  93. package/dist/cake/extensions/link/link-popover.js +205 -0
  94. package/dist/cake/extensions/link/link.d.ts +1 -0
  95. package/dist/cake/extensions/link/link.d.ts.map +1 -0
  96. package/dist/cake/extensions/link/link.js +202 -0
  97. package/dist/cake/extensions/list/index.d.ts +2 -0
  98. package/dist/cake/extensions/list/index.d.ts.map +1 -0
  99. package/dist/cake/extensions/list/index.js +1 -0
  100. package/dist/cake/extensions/list/list-ast.d.ts +1 -0
  101. package/dist/cake/extensions/list/list-ast.d.ts.map +1 -0
  102. package/dist/cake/extensions/list/list-ast.js +248 -0
  103. package/dist/cake/extensions/list/list.d.ts +1 -0
  104. package/dist/cake/extensions/list/list.d.ts.map +1 -0
  105. package/dist/cake/extensions/list/list.js +859 -0
  106. package/dist/cake/extensions/scrollbar/index.d.ts +1 -0
  107. package/dist/cake/extensions/scrollbar/index.d.ts.map +1 -0
  108. package/dist/cake/extensions/scrollbar/index.js +216 -0
  109. package/dist/cake/extensions/strikethrough/index.d.ts +2 -0
  110. package/dist/cake/extensions/strikethrough/index.d.ts.map +1 -0
  111. package/dist/cake/extensions/strikethrough/index.js +1 -0
  112. package/dist/cake/extensions/strikethrough/strikethrough.d.ts +1 -0
  113. package/dist/cake/extensions/strikethrough/strikethrough.d.ts.map +1 -0
  114. package/dist/cake/extensions/strikethrough/strikethrough.js +84 -0
  115. package/dist/cake/extensions/types.d.ts +1 -0
  116. package/dist/cake/extensions/types.d.ts.map +1 -0
  117. package/dist/cake/extensions/types.js +1 -0
  118. package/dist/cake/index.d.ts +1 -0
  119. package/dist/cake/index.d.ts.map +1 -0
  120. package/dist/cake/index.js +1 -0
  121. package/dist/cake/react/CakeEditor.d.ts +1 -0
  122. package/dist/cake/react/CakeEditor.d.ts.map +1 -0
  123. package/dist/cake/react/CakeEditor.js +233 -0
  124. package/dist/cake/shared/platform.d.ts +1 -0
  125. package/dist/cake/shared/platform.d.ts.map +1 -0
  126. package/dist/cake/shared/platform.js +19 -0
  127. package/dist/cake/shared/segmenter.d.ts +1 -0
  128. package/dist/cake/shared/segmenter.d.ts.map +1 -0
  129. package/dist/cake/shared/segmenter.js +46 -0
  130. package/dist/cake/shared/url.d.ts +1 -0
  131. package/dist/cake/shared/url.d.ts.map +1 -0
  132. package/dist/cake/shared/url.js +37 -0
  133. package/dist/cake/shared/word-break.d.ts +1 -0
  134. package/dist/cake/shared/word-break.d.ts.map +1 -0
  135. package/dist/cake/shared/word-break.js +178 -0
  136. package/dist/cake/test/harness.d.ts +1 -0
  137. package/dist/cake/test/harness.d.ts.map +1 -0
  138. package/dist/cake/test/harness.js +504 -0
  139. package/dist/codemirror/markdown-commands.d.ts +1 -0
  140. package/dist/codemirror/markdown-commands.d.ts.map +1 -0
  141. package/dist/codemirror/markdown-commands.js +532 -0
  142. package/dist/index.d.ts +1 -0
  143. package/dist/index.d.ts.map +1 -0
  144. package/dist/index.js +3 -11740
  145. package/package.json +8 -6
  146. package/dist/cake/extensions/pipe-link/pipe-link.d.ts +0 -1
  147. package/dist/index.cjs +0 -11740
@@ -52,3 +52,4 @@ export declare function resolveOffsetToLine(lines: LineInfo[], offset: number):
52
52
  lineIndex: number;
53
53
  offsetInLine: number;
54
54
  };
55
+ //# sourceMappingURL=selection-layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection-layout.d.ts","sourceRoot":"","sources":["../../../../src/cake/engine/selection/selection-layout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,GAAG,EAAU,MAAM,kBAAkB,CAAC;AAG3D,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,UAAU,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,OAAO,EAAE,UAAU,CAAC;IACpB,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,SAAS,EAAE,UAAU,CAAC;IACtB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,UAAU,CAAC;IACpB,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,UAAU,CAAC;IACtB,WAAW,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,eAAe,CAAC;CAC/D,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,QAAQ,EAAE,EACjB,QAAQ,EAAE,cAAc,GACvB,WAAW,CAqCb;AAOD,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,QAAQ,EAAE,CAchD;AA6BD,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,CAW1D;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,QAAQ,EAAE,EACjB,MAAM,EAAE,MAAM,GACb;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAsB7C"}
@@ -0,0 +1,128 @@
1
+ import { graphemeSegments } from "../../shared/segmenter";
2
+ export function buildLayoutModel(lines, measurer) {
3
+ const layouts = [];
4
+ let top = measurer.container.top;
5
+ let lineStartOffset = 0;
6
+ lines.forEach((line) => {
7
+ const measurement = measurer.measureLine({
8
+ lineIndex: line.lineIndex,
9
+ lineText: line.text,
10
+ lineLength: line.cursorLength,
11
+ lineHasNewline: line.hasNewline,
12
+ top,
13
+ });
14
+ layouts.push({
15
+ lineIndex: line.lineIndex,
16
+ lineStartOffset,
17
+ lineLength: line.cursorLength,
18
+ lineHasNewline: line.hasNewline,
19
+ lineBox: measurement.lineBox,
20
+ rows: measurement.rows,
21
+ });
22
+ top = measurement.lineBox.top + measurement.lineBox.height;
23
+ lineStartOffset += line.cursorLength + (line.hasNewline ? 1 : 0);
24
+ });
25
+ const height = Math.max(measurer.container.height, top - measurer.container.top);
26
+ return {
27
+ container: {
28
+ ...measurer.container,
29
+ height,
30
+ },
31
+ lines: layouts,
32
+ };
33
+ }
34
+ export function getDocLines(doc) {
35
+ const flattenedLines = flattenBlocksWithAtomicInfo(doc.blocks);
36
+ return flattenedLines.map((line, index) => {
37
+ const cursorToCodeUnit = buildCursorToCodeUnit(line.text);
38
+ const cursorLength = Math.max(0, cursorToCodeUnit.length - 1);
39
+ return {
40
+ lineIndex: index,
41
+ text: line.text,
42
+ cursorLength,
43
+ hasNewline: index < flattenedLines.length - 1,
44
+ cursorToCodeUnit,
45
+ isAtomic: line.isAtomic,
46
+ };
47
+ });
48
+ }
49
+ function flattenBlocksWithAtomicInfo(blocks) {
50
+ const lines = [];
51
+ blocks.forEach((block) => {
52
+ lines.push(...flattenBlockWithAtomicInfo(block));
53
+ });
54
+ if (lines.length === 0) {
55
+ lines.push({ text: "", isAtomic: false });
56
+ }
57
+ return lines;
58
+ }
59
+ function flattenBlockWithAtomicInfo(block) {
60
+ if (block.type === "paragraph") {
61
+ return [
62
+ { text: block.content.map(flattenInline).join(""), isAtomic: false },
63
+ ];
64
+ }
65
+ if (block.type === "block-wrapper") {
66
+ return flattenBlocksWithAtomicInfo(block.blocks);
67
+ }
68
+ if (block.type === "block-atom") {
69
+ // Atomic blocks are represented as empty lines for layout purposes
70
+ return [{ text: "", isAtomic: true }];
71
+ }
72
+ return [];
73
+ }
74
+ export function getLineOffsets(lines) {
75
+ const offsets = [];
76
+ let current = 0;
77
+ lines.forEach((line) => {
78
+ offsets.push(current);
79
+ current += line.cursorLength;
80
+ if (line.hasNewline) {
81
+ current += 1;
82
+ }
83
+ });
84
+ return offsets;
85
+ }
86
+ export function resolveOffsetToLine(lines, offset) {
87
+ if (lines.length === 0) {
88
+ return { lineIndex: 0, offsetInLine: 0 };
89
+ }
90
+ const lineOffsets = getLineOffsets(lines);
91
+ const totalLength = lineOffsets[lineOffsets.length - 1] + lines[lines.length - 1].cursorLength;
92
+ const clamped = Math.max(0, Math.min(offset, totalLength));
93
+ for (let index = 0; index < lines.length; index += 1) {
94
+ const lineStart = lineOffsets[index];
95
+ const lineEnd = lineStart + lines[index].cursorLength;
96
+ if (clamped <= lineEnd || index === lines.length - 1) {
97
+ return { lineIndex: index, offsetInLine: clamped - lineStart };
98
+ }
99
+ }
100
+ const lastIndex = lines.length - 1;
101
+ return {
102
+ lineIndex: lastIndex,
103
+ offsetInLine: lines[lastIndex].cursorLength,
104
+ };
105
+ }
106
+ function flattenInline(inline) {
107
+ if (inline.type === "text") {
108
+ return inline.text;
109
+ }
110
+ if (inline.type === "inline-wrapper") {
111
+ return inline.children.map(flattenInline).join("");
112
+ }
113
+ if (inline.type === "inline-atom") {
114
+ return " ";
115
+ }
116
+ return "";
117
+ }
118
+ function buildCursorToCodeUnit(text) {
119
+ const segments = graphemeSegments(text);
120
+ const mapping = [0];
121
+ for (const segment of segments) {
122
+ mapping.push(segment.index + segment.segment.length);
123
+ }
124
+ if (mapping.length === 0) {
125
+ mapping.push(0);
126
+ }
127
+ return mapping;
128
+ }
@@ -19,3 +19,4 @@ export declare function moveSelectionVertically(params: {
19
19
  } | null;
20
20
  }): VerticalNavigationResult | null;
21
21
  export {};
22
+ //# sourceMappingURL=selection-navigation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection-navigation.d.ts","sourceRoot":"","sources":["../../../../src/cake/engine/selection/selection-navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAa,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAG3E,KAAK,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAEvC,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,SAAS,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAsIF,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,IAAI,GAAG,MAAM,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,CAChB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,KACN;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,QAAQ,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAC7E,GAAG,wBAAwB,GAAG,IAAI,CA+JlC"}
@@ -0,0 +1,229 @@
1
+ import { resolveOffsetToLine } from "./selection-layout";
2
+ function rectRight(rect) {
3
+ return rect.left + rect.width;
4
+ }
5
+ function clampNumber(value, min, max) {
6
+ return Math.max(min, Math.min(value, max));
7
+ }
8
+ function resolveSelectionAffinity(selection) {
9
+ if (selection.start === selection.end) {
10
+ return selection.affinity ?? "backward";
11
+ }
12
+ return selection.affinity ?? "forward";
13
+ }
14
+ function resolveSelectionAnchorAndFocus(selection) {
15
+ if (selection.start === selection.end) {
16
+ return { anchor: selection.start, focus: selection.start };
17
+ }
18
+ const affinity = resolveSelectionAffinity(selection);
19
+ if (affinity === "backward") {
20
+ return { anchor: selection.end, focus: selection.start };
21
+ }
22
+ return { anchor: selection.start, focus: selection.end };
23
+ }
24
+ function findRowIndexForOffset(rows, offsetInLine, affinity) {
25
+ if (rows.length === 0) {
26
+ return 0;
27
+ }
28
+ for (let index = 0; index < rows.length; index += 1) {
29
+ const row = rows[index];
30
+ if (offsetInLine === row.startOffset) {
31
+ if (affinity === "backward" && index > 0) {
32
+ return index - 1;
33
+ }
34
+ return index;
35
+ }
36
+ if (offsetInLine === row.endOffset) {
37
+ if (affinity === "forward" && index + 1 < rows.length) {
38
+ return index + 1;
39
+ }
40
+ return index;
41
+ }
42
+ if (offsetInLine > row.startOffset && offsetInLine < row.endOffset) {
43
+ return index;
44
+ }
45
+ }
46
+ return rows.length - 1;
47
+ }
48
+ function asSingleRow(line) {
49
+ return {
50
+ startOffset: 0,
51
+ endOffset: line.lineLength,
52
+ rect: line.lineBox,
53
+ };
54
+ }
55
+ function rowOffsetToX(params) {
56
+ const { row, offsetInLine } = params;
57
+ const rowRight = rectRight(row.rect);
58
+ const clampedOffset = clampNumber(offsetInLine, row.startOffset, row.endOffset);
59
+ if (row.endOffset <= row.startOffset || row.rect.width <= 0) {
60
+ return row.rect.left;
61
+ }
62
+ if (clampedOffset === row.endOffset) {
63
+ return rowRight;
64
+ }
65
+ const rowLength = row.endOffset - row.startOffset;
66
+ const fraction = (clampedOffset - row.startOffset) / rowLength;
67
+ return row.rect.left + row.rect.width * fraction;
68
+ }
69
+ function rowXToOffset(params) {
70
+ const { row, x } = params;
71
+ const rowRight = rectRight(row.rect);
72
+ if (x <= row.rect.left) {
73
+ return { offsetInLine: row.startOffset, affinity: "forward" };
74
+ }
75
+ if (x >= rowRight) {
76
+ return { offsetInLine: row.endOffset, affinity: "backward" };
77
+ }
78
+ const rowLength = row.endOffset - row.startOffset;
79
+ if (rowLength <= 0 || row.rect.width <= 0) {
80
+ return { offsetInLine: row.startOffset, affinity: "forward" };
81
+ }
82
+ const fraction = (x - row.rect.left) / row.rect.width;
83
+ const raw = row.startOffset + fraction * rowLength;
84
+ const rounded = Math.round(raw);
85
+ const offsetInLine = clampNumber(rounded, row.startOffset, row.endOffset);
86
+ return { offsetInLine, affinity: "forward" };
87
+ }
88
+ function resolveTargetLineIndex(params) {
89
+ const delta = params.direction === "down" ? 1 : -1;
90
+ let index = params.fromLineIndex + delta;
91
+ while (index >= 0 && index < params.lines.length) {
92
+ if (!params.lines[index]?.isAtomic) {
93
+ return index;
94
+ }
95
+ index += delta;
96
+ }
97
+ return null;
98
+ }
99
+ export function moveSelectionVertically(params) {
100
+ const { lines, layout, direction } = params;
101
+ if (layout.lines.length === 0 || lines.length === 0) {
102
+ return null;
103
+ }
104
+ const affinity = resolveSelectionAffinity(params.selection);
105
+ const { anchor, focus } = resolveSelectionAnchorAndFocus(params.selection);
106
+ const focusResolved = resolveOffsetToLine(lines, focus);
107
+ const focusLineLayout = layout.lines[focusResolved.lineIndex];
108
+ if (!focusLineLayout) {
109
+ return null;
110
+ }
111
+ const focusRows = focusLineLayout.rows.length > 0
112
+ ? focusLineLayout.rows
113
+ : [asSingleRow(focusLineLayout)];
114
+ let focusRowIndex = params.focusRowIndex ??
115
+ findRowIndexForOffset(focusRows, focusResolved.offsetInLine, affinity);
116
+ focusRowIndex = clampNumber(focusRowIndex, 0, Math.max(0, focusRows.length - 1));
117
+ const focusRow = focusRows[focusRowIndex] ?? focusRows[focusRows.length - 1];
118
+ if (!focusRow) {
119
+ return null;
120
+ }
121
+ const goalX = params.goalX ??
122
+ rowOffsetToX({ row: focusRow, offsetInLine: focusResolved.offsetInLine });
123
+ let targetLineIndex = focusResolved.lineIndex;
124
+ let targetRowIndex = focusRowIndex;
125
+ if (direction === "up") {
126
+ if (focusRowIndex > 0) {
127
+ targetRowIndex = focusRowIndex - 1;
128
+ }
129
+ else {
130
+ const nextLineIndex = resolveTargetLineIndex({
131
+ lines,
132
+ fromLineIndex: focusResolved.lineIndex,
133
+ direction: "up",
134
+ });
135
+ if (nextLineIndex === null) {
136
+ return null;
137
+ }
138
+ targetLineIndex = nextLineIndex;
139
+ targetRowIndex = Number.POSITIVE_INFINITY;
140
+ }
141
+ }
142
+ else {
143
+ if (focusRowIndex + 1 < focusRows.length) {
144
+ targetRowIndex = focusRowIndex + 1;
145
+ }
146
+ else {
147
+ const nextLineIndex = resolveTargetLineIndex({
148
+ lines,
149
+ fromLineIndex: focusResolved.lineIndex,
150
+ direction: "down",
151
+ });
152
+ if (nextLineIndex === null) {
153
+ return null;
154
+ }
155
+ targetLineIndex = nextLineIndex;
156
+ targetRowIndex = 0;
157
+ }
158
+ }
159
+ const targetLineLayout = layout.lines[targetLineIndex];
160
+ if (!targetLineLayout) {
161
+ return null;
162
+ }
163
+ const targetRows = targetLineLayout.rows.length > 0
164
+ ? targetLineLayout.rows
165
+ : [asSingleRow(targetLineLayout)];
166
+ const resolvedTargetRowIndex = targetRowIndex === Number.POSITIVE_INFINITY
167
+ ? Math.max(0, targetRows.length - 1)
168
+ : clampNumber(targetRowIndex, 0, Math.max(0, targetRows.length - 1));
169
+ const targetRow = targetRows[resolvedTargetRowIndex] ?? targetRows[targetRows.length - 1];
170
+ if (!targetRow) {
171
+ return null;
172
+ }
173
+ const targetLineStart = targetLineLayout.lineStartOffset;
174
+ const targetLineEnd = targetLineStart + targetLineLayout.lineLength;
175
+ if (params.hitTestCursorAt) {
176
+ const midY = targetRow.rect.top + targetRow.rect.height / 2;
177
+ const rowLeft = targetRow.rect.left;
178
+ const rowRight = rectRight(targetRow.rect);
179
+ const hitX = rowRight - rowLeft > 1
180
+ ? clampNumber(goalX, rowLeft + 0.5, rowRight - 0.5)
181
+ : goalX;
182
+ let hit = params.hitTestCursorAt(hitX, midY);
183
+ // If we're in trailing whitespace, some caret APIs will return the end of
184
+ // the whole line (last visual row). Nudge left until we get a hit that
185
+ // actually lands on the requested visual row.
186
+ if (hit && hit.cursorOffset === focus) {
187
+ for (let attempt = 0; attempt < 80; attempt += 1) {
188
+ const nudgedX = hitX - (attempt + 1) * 1;
189
+ if (nudgedX <= rowLeft + 0.5) {
190
+ break;
191
+ }
192
+ const next = params.hitTestCursorAt(nudgedX, midY);
193
+ const matchesRow = next?.caretTop === undefined ||
194
+ Math.abs(next.caretTop - targetRow.rect.top) <= 2;
195
+ if (next && next.cursorOffset !== focus && matchesRow) {
196
+ hit = next;
197
+ break;
198
+ }
199
+ }
200
+ }
201
+ const matchesRow = hit?.caretTop === undefined ||
202
+ Math.abs(hit.caretTop - targetRow.rect.top) <= 2;
203
+ if (hit &&
204
+ matchesRow &&
205
+ hit.cursorOffset >= targetLineStart &&
206
+ hit.cursorOffset <= targetLineEnd) {
207
+ const normalizedAffinity = targetLineLayout.lineLength === 0 ? "forward" : hit.affinity;
208
+ const nextSelection = anchor === focus
209
+ ? {
210
+ start: hit.cursorOffset,
211
+ end: hit.cursorOffset,
212
+ affinity: normalizedAffinity,
213
+ }
214
+ : { start: anchor, end: hit.cursorOffset, affinity: normalizedAffinity };
215
+ return { selection: nextSelection, goalX };
216
+ }
217
+ }
218
+ const rowHit = rowXToOffset({ row: targetRow, x: goalX });
219
+ const nextPos = targetLineStart + rowHit.offsetInLine;
220
+ const normalizedAffinity = targetLineLayout.lineLength === 0 ? "forward" : rowHit.affinity;
221
+ const nextSelection = anchor === focus
222
+ ? { start: nextPos, end: nextPos, affinity: normalizedAffinity }
223
+ : {
224
+ start: anchor,
225
+ end: nextPos,
226
+ affinity: normalizedAffinity,
227
+ };
228
+ return { selection: nextSelection, goalX };
229
+ }
@@ -2,3 +2,4 @@ import { type LineInfo } from "./selection-layout";
2
2
  export declare function getVisibleText(lines: LineInfo[]): string;
3
3
  export declare function visibleOffsetToCursorOffset(lines: LineInfo[], visibleOffset: number): number | null;
4
4
  export declare function cursorOffsetToVisibleOffset(lines: LineInfo[], cursorOffset: number): number;
5
+ //# sourceMappingURL=visible-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visible-text.d.ts","sourceRoot":"","sources":["../../../../src/cake/engine/selection/visible-text.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,QAAQ,EACd,MAAM,oBAAoB,CAAC;AAuB5B,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CAKxD;AAED,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,QAAQ,EAAE,EACjB,aAAa,EAAE,MAAM,GACpB,MAAM,GAAG,IAAI,CA2Bf;AAED,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,QAAQ,EAAE,EACjB,YAAY,EAAE,MAAM,GACnB,MAAM,CAsBR"}
@@ -0,0 +1,66 @@
1
+ import { getLineOffsets, resolveOffsetToLine, } from "./selection-layout";
2
+ function cursorOffsetFromCodeUnit(line, codeUnitOffset) {
3
+ const maxCodeUnit = line.cursorToCodeUnit[line.cursorToCodeUnit.length - 1] ?? 0;
4
+ const clamped = Math.max(0, Math.min(codeUnitOffset, maxCodeUnit));
5
+ for (let i = 0; i < line.cursorToCodeUnit.length; i += 1) {
6
+ if (line.cursorToCodeUnit[i] === clamped) {
7
+ return i;
8
+ }
9
+ if (line.cursorToCodeUnit[i] > clamped) {
10
+ return Math.max(0, i - 1);
11
+ }
12
+ }
13
+ return line.cursorLength;
14
+ }
15
+ export function getVisibleText(lines) {
16
+ if (lines.length === 0) {
17
+ return "";
18
+ }
19
+ return lines.map((line) => line.text).join("\n");
20
+ }
21
+ export function visibleOffsetToCursorOffset(lines, visibleOffset) {
22
+ if (lines.length === 0) {
23
+ return 0;
24
+ }
25
+ const clampedOffset = Math.max(0, visibleOffset);
26
+ const lineOffsets = getLineOffsets(lines);
27
+ let codeUnitIndex = 0;
28
+ for (let index = 0; index < lines.length; index += 1) {
29
+ const line = lines[index];
30
+ const lineStart = codeUnitIndex;
31
+ const lineEnd = lineStart + line.text.length;
32
+ if (clampedOffset <= lineEnd || index === lines.length - 1) {
33
+ const offsetInLine = Math.max(0, Math.min(clampedOffset - lineStart, line.text.length));
34
+ const cursorOffsetInLine = cursorOffsetFromCodeUnit(line, offsetInLine);
35
+ const lineStartOffset = lineOffsets[index] ?? 0;
36
+ return lineStartOffset + cursorOffsetInLine;
37
+ }
38
+ codeUnitIndex = lineEnd + (line.hasNewline ? 1 : 0);
39
+ if (line.hasNewline && clampedOffset === codeUnitIndex) {
40
+ const lineStartOffset = lineOffsets[index] ?? 0;
41
+ return lineStartOffset + line.cursorLength + 1;
42
+ }
43
+ }
44
+ return null;
45
+ }
46
+ export function cursorOffsetToVisibleOffset(lines, cursorOffset) {
47
+ if (lines.length === 0) {
48
+ return 0;
49
+ }
50
+ const lineOffsets = getLineOffsets(lines);
51
+ const { lineIndex, offsetInLine } = resolveOffsetToLine(lines, cursorOffset);
52
+ let codeUnitIndex = 0;
53
+ for (let index = 0; index < lines.length; index += 1) {
54
+ const line = lines[index];
55
+ if (index === lineIndex) {
56
+ const offset = Math.max(0, Math.min(offsetInLine, line.cursorLength));
57
+ return codeUnitIndex + (line.cursorToCodeUnit[offset] ?? 0);
58
+ }
59
+ codeUnitIndex += line.text.length + (line.hasNewline ? 1 : 0);
60
+ }
61
+ const lastLine = lines[lines.length - 1];
62
+ const lastStart = lineOffsets[lineOffsets.length - 1] ?? 0;
63
+ const lastOffset = Math.max(0, cursorOffset - lastStart);
64
+ return (codeUnitIndex +
65
+ (lastLine.cursorToCodeUnit[lastOffset] ?? lastLine.text.length));
66
+ }
@@ -1 +1,2 @@
1
1
  export declare const blockquoteExtension: import("../../..").CakeExtension;
2
+ //# sourceMappingURL=blockquote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blockquote.d.ts","sourceRoot":"","sources":["../../../../src/cake/extensions/blockquote/blockquote.ts"],"names":[],"mappings":"AAsIA,eAAO,MAAM,mBAAmB,kCA8F9B,CAAC"}
@@ -0,0 +1,177 @@
1
+ import { defineExtension, } from "../../core/runtime";
2
+ import { CursorSourceBuilder } from "../../core/mapping/cursor-source-map";
3
+ const BLOCKQUOTE_KIND = "blockquote";
4
+ const PREFIX = "> ";
5
+ const BLOCKQUOTE_PATTERN = /^> /;
6
+ function findLineStartInSource(source, sourceOffset) {
7
+ let lineStart = sourceOffset;
8
+ while (lineStart > 0 && source[lineStart - 1] !== "\n") {
9
+ lineStart--;
10
+ }
11
+ return lineStart;
12
+ }
13
+ function isInsideBlockquote(source, sourcePos) {
14
+ const lineStart = findLineStartInSource(source, sourcePos);
15
+ const lineContent = source.slice(lineStart, source.indexOf("\n", lineStart) === -1
16
+ ? source.length
17
+ : source.indexOf("\n", lineStart));
18
+ return BLOCKQUOTE_PATTERN.test(lineContent);
19
+ }
20
+ function handleExitBlockquote(state) {
21
+ const { source, selection, map, runtime } = state;
22
+ const cursorPos = Math.min(selection.start, selection.end);
23
+ const sourcePos = map.cursorToSource(cursorPos, selection.affinity ?? "forward");
24
+ // Only handle if we're inside a blockquote
25
+ if (!isInsideBlockquote(source, sourcePos)) {
26
+ return null;
27
+ }
28
+ // Find the end of the current line
29
+ let lineEnd = source.indexOf("\n", sourcePos);
30
+ if (lineEnd === -1) {
31
+ lineEnd = source.length;
32
+ }
33
+ // Insert a newline after the current line (exits the blockquote)
34
+ const newSource = source.slice(0, lineEnd) + "\n" + source.slice(lineEnd);
35
+ // Position cursor at the start of the new line (after the blockquote)
36
+ const next = runtime.createState(newSource);
37
+ const newCursorOffset = lineEnd + 1;
38
+ const caretCursor = next.map.sourceToCursor(newCursorOffset, "forward");
39
+ return {
40
+ source: newSource,
41
+ selection: {
42
+ start: caretCursor.cursorOffset,
43
+ end: caretCursor.cursorOffset,
44
+ affinity: "forward",
45
+ },
46
+ };
47
+ }
48
+ function handleToggleBlockquote(state) {
49
+ const { source, selection, map, runtime } = state;
50
+ // Get the cursor's source position
51
+ const cursorPos = Math.min(selection.start, selection.end);
52
+ const sourcePos = map.cursorToSource(cursorPos, selection.affinity ?? "forward");
53
+ // Find line boundaries in source
54
+ const lineStart = findLineStartInSource(source, sourcePos);
55
+ let lineEnd = source.indexOf("\n", lineStart);
56
+ if (lineEnd === -1) {
57
+ lineEnd = source.length;
58
+ }
59
+ const lineContent = source.slice(lineStart, lineEnd);
60
+ const blockquoteMatch = lineContent.match(BLOCKQUOTE_PATTERN);
61
+ let newSource;
62
+ let newCursorOffset;
63
+ if (blockquoteMatch) {
64
+ // Line is already a blockquote - remove the prefix
65
+ newSource =
66
+ source.slice(0, lineStart) +
67
+ lineContent.slice(PREFIX.length) +
68
+ source.slice(lineEnd);
69
+ // Adjust cursor position - move back by prefix length
70
+ const cursorLineOffset = sourcePos - lineStart;
71
+ if (cursorLineOffset >= PREFIX.length) {
72
+ newCursorOffset = sourcePos - PREFIX.length;
73
+ }
74
+ else {
75
+ newCursorOffset = lineStart;
76
+ }
77
+ }
78
+ else {
79
+ // Line is not a blockquote - add the prefix
80
+ newSource =
81
+ source.slice(0, lineStart) + PREFIX + lineContent + source.slice(lineEnd);
82
+ // Cursor moves forward by prefix length
83
+ newCursorOffset = sourcePos + PREFIX.length;
84
+ }
85
+ // Create new state and map cursor through it
86
+ const next = runtime.createState(newSource);
87
+ const caretCursor = next.map.sourceToCursor(newCursorOffset, "forward");
88
+ return {
89
+ source: newSource,
90
+ selection: {
91
+ start: caretCursor.cursorOffset,
92
+ end: caretCursor.cursorOffset,
93
+ affinity: "forward",
94
+ },
95
+ };
96
+ }
97
+ export const blockquoteExtension = defineExtension({
98
+ name: "blockquote",
99
+ onEdit(command, state) {
100
+ if (command.type === "toggle-blockquote") {
101
+ return handleToggleBlockquote(state);
102
+ }
103
+ // Handle insert-hard-line-break (Cmd+Enter) to exit blockquote
104
+ if (command.type === "insert-hard-line-break") {
105
+ return handleExitBlockquote(state);
106
+ }
107
+ return null;
108
+ },
109
+ parseBlock(source, start, context) {
110
+ if (source.slice(start, start + PREFIX.length) !== PREFIX) {
111
+ return null;
112
+ }
113
+ const blocks = [];
114
+ let pos = start;
115
+ while (pos < source.length) {
116
+ if (source.slice(pos, pos + PREFIX.length) !== PREFIX) {
117
+ break;
118
+ }
119
+ let lineEnd = source.indexOf("\n", pos);
120
+ if (lineEnd === -1) {
121
+ lineEnd = source.length;
122
+ }
123
+ const contentStart = pos + PREFIX.length;
124
+ const content = context.parseInline(source, contentStart, lineEnd);
125
+ const paragraph = {
126
+ type: "paragraph",
127
+ content,
128
+ };
129
+ blocks.push(paragraph);
130
+ if (lineEnd >= source.length) {
131
+ pos = lineEnd;
132
+ break;
133
+ }
134
+ const nextLineStart = lineEnd + 1;
135
+ if (source.slice(nextLineStart, nextLineStart + PREFIX.length) === PREFIX) {
136
+ pos = nextLineStart;
137
+ continue;
138
+ }
139
+ pos = lineEnd;
140
+ break;
141
+ }
142
+ return {
143
+ block: {
144
+ type: "block-wrapper",
145
+ kind: BLOCKQUOTE_KIND,
146
+ blocks,
147
+ },
148
+ nextPos: pos,
149
+ };
150
+ },
151
+ serializeBlock(block, context) {
152
+ if (block.type !== "block-wrapper" || block.kind !== BLOCKQUOTE_KIND) {
153
+ return null;
154
+ }
155
+ const builder = new CursorSourceBuilder();
156
+ block.blocks.forEach((child, index) => {
157
+ builder.appendSourceOnly(PREFIX);
158
+ const serialized = context.serializeBlock(child);
159
+ builder.appendSerialized(serialized);
160
+ if (index < block.blocks.length - 1) {
161
+ builder.appendText("\n");
162
+ }
163
+ });
164
+ return builder.build();
165
+ },
166
+ renderBlock(block, context) {
167
+ if (block.type !== "block-wrapper" || block.kind !== BLOCKQUOTE_KIND) {
168
+ return null;
169
+ }
170
+ const element = document.createElement("blockquote");
171
+ element.setAttribute("data-block-wrapper", BLOCKQUOTE_KIND);
172
+ for (const node of context.renderBlocks(block.blocks)) {
173
+ element.append(node);
174
+ }
175
+ return element;
176
+ },
177
+ });
@@ -0,0 +1,2 @@
1
+ export * from "./blockquote";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/cake/extensions/blockquote/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC"}
@@ -0,0 +1 @@
1
+ export * from "./blockquote";
@@ -1 +1,2 @@
1
1
  export declare const boldExtension: import("../../..").CakeExtension;
2
+ //# sourceMappingURL=bold.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bold.d.ts","sourceRoot":"","sources":["../../../../src/cake/extensions/bold/bold.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,aAAa,kCAuGxB,CAAC"}