@blankdotpage/cake 0.1.8 → 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 +1 -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 -11793
  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 -11793
@@ -0,0 +1,202 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { defineExtension, } from "../../core/runtime";
3
+ import { CursorSourceBuilder } from "../../core/mapping/cursor-source-map";
4
+ import { CakeLinkPopover } from "./link-popover";
5
+ import { getDocLines } from "../../engine/selection/selection-layout";
6
+ import { cursorOffsetToVisibleOffset, getVisibleText, } from "../../engine/selection/visible-text";
7
+ import { ensureHttpsProtocol, isUrl } from "../../shared/url";
8
+ const LINK_KIND = "link";
9
+ export const linkExtension = defineExtension({
10
+ name: "link",
11
+ inlineWrapperAffinity: [{ kind: LINK_KIND, inclusive: false }],
12
+ keybindings: [
13
+ {
14
+ key: "u",
15
+ meta: true,
16
+ shift: true,
17
+ command: (state) => {
18
+ if (state.selection.start === state.selection.end) {
19
+ return null;
20
+ }
21
+ return { type: "wrap-link", openPopover: true };
22
+ },
23
+ },
24
+ {
25
+ key: "u",
26
+ ctrl: true,
27
+ shift: true,
28
+ command: (state) => {
29
+ if (state.selection.start === state.selection.end) {
30
+ return null;
31
+ }
32
+ return { type: "wrap-link", openPopover: true };
33
+ },
34
+ },
35
+ ],
36
+ onEdit(command, state) {
37
+ if (command.type === "unlink") {
38
+ // Find the link at the given cursor position and remove the link markup
39
+ const cursorPos = command.start;
40
+ const sourcePos = state.map.cursorToSource(cursorPos, "forward");
41
+ const source = state.source;
42
+ // Search backwards for the opening bracket
43
+ let linkStart = sourcePos;
44
+ while (linkStart > 0 && source[linkStart] !== "[") {
45
+ linkStart--;
46
+ }
47
+ if (source[linkStart] !== "[") {
48
+ return null;
49
+ }
50
+ // Find the ]( separator
51
+ const labelClose = source.indexOf("](", linkStart + 1);
52
+ if (labelClose === -1) {
53
+ return null;
54
+ }
55
+ // Find the closing )
56
+ const urlClose = source.indexOf(")", labelClose + 2);
57
+ if (urlClose === -1) {
58
+ return null;
59
+ }
60
+ // Extract the label (text between [ and ]( )
61
+ const label = source.slice(linkStart + 1, labelClose);
62
+ // Replace [label](url) with just label
63
+ const nextSource = source.slice(0, linkStart) + label + source.slice(urlClose + 1);
64
+ // Calculate new cursor position - place it at the end of the label
65
+ const newState = state.runtime.createState(nextSource);
66
+ const labelEndSource = linkStart + label.length;
67
+ const newCursor = newState.map.sourceToCursor(labelEndSource, "forward");
68
+ return {
69
+ source: nextSource,
70
+ selection: {
71
+ start: newCursor.cursorOffset,
72
+ end: newCursor.cursorOffset,
73
+ affinity: "forward",
74
+ },
75
+ };
76
+ }
77
+ if (command.type !== "wrap-link") {
78
+ return null;
79
+ }
80
+ const selection = state.selection;
81
+ const cursorStart = Math.min(selection.start, selection.end);
82
+ const cursorEnd = Math.max(selection.start, selection.end);
83
+ if (cursorStart === cursorEnd) {
84
+ return null;
85
+ }
86
+ const from = state.map.cursorToSource(cursorStart, "forward");
87
+ const to = state.map.cursorToSource(cursorEnd, "backward");
88
+ if (from === to) {
89
+ return null;
90
+ }
91
+ const label = state.source.slice(from, to);
92
+ const url = command.url ?? "";
93
+ const linkMarkdown = `[${label}](${url})`;
94
+ const nextSource = state.source.slice(0, from) + linkMarkdown + state.source.slice(to);
95
+ return {
96
+ source: nextSource,
97
+ selection: {
98
+ start: cursorEnd,
99
+ end: cursorEnd,
100
+ affinity: "backward",
101
+ },
102
+ };
103
+ },
104
+ onPasteText(text, state) {
105
+ if (!isUrl(text)) {
106
+ return null;
107
+ }
108
+ const url = ensureHttpsProtocol(text.trim());
109
+ const selection = state.selection;
110
+ const start = Math.min(selection.start, selection.end);
111
+ const end = Math.max(selection.start, selection.end);
112
+ if (start !== end) {
113
+ const lines = getDocLines(state.doc);
114
+ const visibleText = getVisibleText(lines);
115
+ const visibleStart = cursorOffsetToVisibleOffset(lines, start);
116
+ const visibleEnd = cursorOffsetToVisibleOffset(lines, end);
117
+ const selectedText = visibleText.slice(visibleStart, visibleEnd);
118
+ const linkMarkdown = `[${selectedText}](${url})`;
119
+ return { type: "insert", text: linkMarkdown };
120
+ }
121
+ const linkMarkdown = `[${url}](${url})`;
122
+ return { type: "insert", text: linkMarkdown };
123
+ },
124
+ parseInline(source, start, end, context) {
125
+ if (source[start] !== "[") {
126
+ return null;
127
+ }
128
+ // Don't match image syntax ![...](...)
129
+ if (start > 0 && source[start - 1] === "!") {
130
+ return null;
131
+ }
132
+ const labelClose = source.indexOf("](", start + 1);
133
+ if (labelClose === -1 || labelClose >= end) {
134
+ return null;
135
+ }
136
+ const urlClose = source.indexOf(")", labelClose + 2);
137
+ if (urlClose === -1 || urlClose >= end) {
138
+ return null;
139
+ }
140
+ const labelStart = start + 1;
141
+ const labelEnd = labelClose;
142
+ const urlStart = labelClose + 2;
143
+ const urlEnd = urlClose;
144
+ const children = context.parseInline(source, labelStart, labelEnd);
145
+ const url = source.slice(urlStart, urlEnd);
146
+ return {
147
+ inline: {
148
+ type: "inline-wrapper",
149
+ kind: LINK_KIND,
150
+ children,
151
+ data: { url },
152
+ },
153
+ nextPos: urlClose + 1,
154
+ };
155
+ },
156
+ serializeInline(inline, context) {
157
+ if (inline.type !== "inline-wrapper" || inline.kind !== LINK_KIND) {
158
+ return null;
159
+ }
160
+ const builder = new CursorSourceBuilder();
161
+ builder.appendSourceOnly("[");
162
+ for (const child of inline.children) {
163
+ const serialized = context.serializeInline(child);
164
+ builder.appendSerialized(serialized);
165
+ }
166
+ builder.appendSourceOnly("](");
167
+ const url = typeof inline.data?.url === "string" ? inline.data.url : "";
168
+ builder.appendSourceOnly(url);
169
+ builder.appendSourceOnly(")");
170
+ return builder.build();
171
+ },
172
+ normalizeInline(inline) {
173
+ if (inline.type !== "inline-wrapper" || inline.kind !== LINK_KIND) {
174
+ return inline;
175
+ }
176
+ if (inline.children.length === 0) {
177
+ return null;
178
+ }
179
+ return inline;
180
+ },
181
+ renderInline(inline, context) {
182
+ if (inline.type !== "inline-wrapper" || inline.kind !== LINK_KIND) {
183
+ return null;
184
+ }
185
+ const element = document.createElement("a");
186
+ element.className = "cake-link";
187
+ const url = typeof inline.data?.url === "string" ? inline.data.url : "";
188
+ element.setAttribute("href", url);
189
+ for (const child of inline.children) {
190
+ for (const node of context.renderInline(child)) {
191
+ element.append(node);
192
+ }
193
+ }
194
+ return element;
195
+ },
196
+ renderOverlay(context) {
197
+ if (!context.contentRoot || !context.toOverlayRect) {
198
+ return null;
199
+ }
200
+ return (_jsx(CakeLinkPopover, { container: context.container, contentRoot: context.contentRoot, toOverlayRect: context.toOverlayRect, getSelection: context.getSelection, executeCommand: context.executeCommand }));
201
+ },
202
+ });
@@ -0,0 +1,2 @@
1
+ export * from "./list";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/cake/extensions/list/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC"}
@@ -0,0 +1 @@
1
+ export * from "./list";
@@ -48,3 +48,4 @@ export declare function isListLine(line: string): boolean;
48
48
  export declare function parseListItem(line: string): ListItem | null;
49
49
  export declare function countNumberedItemsBefore(source: string, beforeLineIndex: number): number;
50
50
  export {};
51
+ //# sourceMappingURL=list-ast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-ast.d.ts","sourceRoot":"","sources":["../../../../src/cake/extensions/list/list-ast.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,KAAK,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;AAExC,UAAU,QAAQ;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,SAAS;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;CAChB;AAED,KAAK,QAAQ,GAAG,SAAS,GAAG,YAAY,CAAC;AAEzC,UAAU,SAAS;IACjB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AA0BD,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,SAAS,CAMX;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,EAChB,WAAW,GAAE,MAAU,GACtB,MAAM,CAqCR;AAID,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,EAChB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,QAAQ,GACb,SAAS,CAIX;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,CAIzE;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,EAChB,SAAS,EAAE,MAAM,GAChB,SAAS,CASX;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,CAYzE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,CAiB1E;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,SAAS,CAYX;AAED,wBAAgB,UAAU,CACxB,KAAK,EAAE,SAAS,EAChB,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,GACtB,SAAS,CAeX;AAGD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GACnB;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA4DtE;AAGD,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACtB,CA4BA;AAGD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM/D;AAGD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAGD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAM3D;AAGD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,MAAM,GACtB,MAAM,CAUR"}
@@ -0,0 +1,248 @@
1
+ /**
2
+ * List AST - a structured representation of list content that can be
3
+ * transformed and serialized back to text with correct numbering.
4
+ */
5
+ // Match list lines - capture exactly one space after marker, rest goes to content
6
+ const LIST_LINE_REGEX = /^(\s*)([-*+]|\d+\.)( )(.*)$/;
7
+ const INDENT_SIZE = 2;
8
+ function parseMarkerType(marker) {
9
+ return /^\d+\.$/.test(marker) ? "numbered" : "bullet";
10
+ }
11
+ function parseLine(line) {
12
+ const match = line.match(LIST_LINE_REGEX);
13
+ if (!match) {
14
+ return { type: "plain", content: line };
15
+ }
16
+ const indent = Math.floor(match[1].length / INDENT_SIZE);
17
+ const markerType = parseMarkerType(match[2]);
18
+ const content = match[4] ?? "";
19
+ return {
20
+ type: "list-item",
21
+ item: { indent, markerType, content },
22
+ };
23
+ }
24
+ export function parseListRange(source, startOffset, endOffset) {
25
+ const text = source.slice(startOffset, endOffset);
26
+ const rawLines = text.split("\n");
27
+ const lines = rawLines.map(parseLine);
28
+ return { lines, startOffset, endOffset };
29
+ }
30
+ export function serializeListRange(range, startNumber = 1) {
31
+ // Track current number at each indent level
32
+ const numbersByIndent = new Map();
33
+ numbersByIndent.set(0, startNumber);
34
+ return range.lines
35
+ .map((line) => {
36
+ if (line.type === "plain") {
37
+ // Only blank lines reset numbering
38
+ if (line.content.trim() === "") {
39
+ numbersByIndent.clear();
40
+ }
41
+ return line.content;
42
+ }
43
+ const { item } = line;
44
+ const indentStr = " ".repeat(item.indent * INDENT_SIZE);
45
+ if (item.markerType === "bullet") {
46
+ // Bullet doesn't affect numbering
47
+ return `${indentStr}- ${item.content}`;
48
+ }
49
+ // Numbered: get current number at this indent level
50
+ const currentNum = numbersByIndent.get(item.indent) ?? 1;
51
+ numbersByIndent.set(item.indent, currentNum + 1);
52
+ // Reset numbering for deeper indent levels
53
+ for (const [indent] of numbersByIndent) {
54
+ if (indent > item.indent) {
55
+ numbersByIndent.delete(indent);
56
+ }
57
+ }
58
+ return `${indentStr}${currentNum}. ${item.content}`;
59
+ })
60
+ .join("\n");
61
+ }
62
+ // AST Operations
63
+ export function insertItemAfter(range, lineIndex, item) {
64
+ const newLines = [...range.lines];
65
+ newLines.splice(lineIndex + 1, 0, { type: "list-item", item });
66
+ return { ...range, lines: newLines };
67
+ }
68
+ export function removeItem(range, lineIndex) {
69
+ const newLines = [...range.lines];
70
+ newLines.splice(lineIndex, 1);
71
+ return { ...range, lines: newLines };
72
+ }
73
+ export function convertToPlainText(range, lineIndex) {
74
+ const line = range.lines[lineIndex];
75
+ if (line.type !== "list-item") {
76
+ return range;
77
+ }
78
+ const newLines = [...range.lines];
79
+ newLines[lineIndex] = { type: "plain", content: line.item.content };
80
+ return { ...range, lines: newLines };
81
+ }
82
+ export function indentItem(range, lineIndex) {
83
+ const line = range.lines[lineIndex];
84
+ if (line.type !== "list-item") {
85
+ return range;
86
+ }
87
+ const newLines = [...range.lines];
88
+ newLines[lineIndex] = {
89
+ type: "list-item",
90
+ item: { ...line.item, indent: line.item.indent + 1 },
91
+ };
92
+ return { ...range, lines: newLines };
93
+ }
94
+ export function outdentItem(range, lineIndex) {
95
+ const line = range.lines[lineIndex];
96
+ if (line.type !== "list-item") {
97
+ return range;
98
+ }
99
+ if (line.item.indent === 0) {
100
+ // Top-level item: convert to plain text
101
+ return convertToPlainText(range, lineIndex);
102
+ }
103
+ const newLines = [...range.lines];
104
+ newLines[lineIndex] = {
105
+ type: "list-item",
106
+ item: { ...line.item, indent: line.item.indent - 1 },
107
+ };
108
+ return { ...range, lines: newLines };
109
+ }
110
+ export function updateItemContent(range, lineIndex, content) {
111
+ const line = range.lines[lineIndex];
112
+ if (line.type !== "list-item") {
113
+ return range;
114
+ }
115
+ const newLines = [...range.lines];
116
+ newLines[lineIndex] = {
117
+ type: "list-item",
118
+ item: { ...line.item, content },
119
+ };
120
+ return { ...range, lines: newLines };
121
+ }
122
+ export function mergeItems(range, targetLineIndex, sourceLineIndex) {
123
+ const targetLine = range.lines[targetLineIndex];
124
+ const sourceLine = range.lines[sourceLineIndex];
125
+ if (targetLine.type !== "list-item" || sourceLine.type !== "list-item") {
126
+ return range;
127
+ }
128
+ const mergedContent = targetLine.item.content +
129
+ (sourceLine.item.content ? " " + sourceLine.item.content : "");
130
+ let result = updateItemContent(range, targetLineIndex, mergedContent);
131
+ result = removeItem(result, sourceLineIndex);
132
+ return result;
133
+ }
134
+ // Helper to find list range boundaries from a cursor position
135
+ export function findListRangeBoundaries(source, cursorOffset) {
136
+ const lines = source.split("\n");
137
+ let offset = 0;
138
+ let cursorLineIndex = 0;
139
+ // Find which line the cursor is on
140
+ for (let i = 0; i < lines.length; i++) {
141
+ const lineEnd = offset + lines[i].length;
142
+ if (cursorOffset >= offset && cursorOffset <= lineEnd) {
143
+ cursorLineIndex = i;
144
+ break;
145
+ }
146
+ offset = lineEnd + 1;
147
+ }
148
+ // Check if current line is a list item
149
+ const currentLine = lines[cursorLineIndex];
150
+ if (!LIST_LINE_REGEX.test(currentLine)) {
151
+ return null;
152
+ }
153
+ // Expand backwards to find start of list
154
+ let startLineIndex = cursorLineIndex;
155
+ while (startLineIndex > 0) {
156
+ const prevLine = lines[startLineIndex - 1];
157
+ if (prevLine.trim() === "" || !LIST_LINE_REGEX.test(prevLine)) {
158
+ break;
159
+ }
160
+ startLineIndex--;
161
+ }
162
+ // Expand forwards to find end of list
163
+ let endLineIndex = cursorLineIndex;
164
+ while (endLineIndex < lines.length - 1) {
165
+ const nextLine = lines[endLineIndex + 1];
166
+ if (nextLine.trim() === "" || !LIST_LINE_REGEX.test(nextLine)) {
167
+ break;
168
+ }
169
+ endLineIndex++;
170
+ }
171
+ // Calculate offsets
172
+ let startOffset = 0;
173
+ for (let i = 0; i < startLineIndex; i++) {
174
+ startOffset += lines[i].length + 1;
175
+ }
176
+ let endOffset = startOffset;
177
+ for (let i = startLineIndex; i <= endLineIndex; i++) {
178
+ endOffset += lines[i].length;
179
+ if (i < endLineIndex) {
180
+ endOffset += 1;
181
+ }
182
+ }
183
+ return {
184
+ startOffset,
185
+ endOffset,
186
+ lineIndex: cursorLineIndex - startLineIndex,
187
+ };
188
+ }
189
+ // Get line info at a specific offset
190
+ export function getLineInfo(source, offset) {
191
+ const lines = source.split("\n");
192
+ let pos = 0;
193
+ for (let i = 0; i < lines.length; i++) {
194
+ const lineStart = pos;
195
+ const lineEnd = pos + lines[i].length;
196
+ if (offset >= lineStart && offset <= lineEnd) {
197
+ return {
198
+ lineIndex: i,
199
+ lineStart,
200
+ lineEnd,
201
+ line: lines[i],
202
+ offsetInLine: offset - lineStart,
203
+ };
204
+ }
205
+ pos = lineEnd + 1;
206
+ }
207
+ const lastLine = lines[lines.length - 1] ?? "";
208
+ const lastLineStart = source.length - lastLine.length;
209
+ return {
210
+ lineIndex: lines.length - 1,
211
+ lineStart: lastLineStart,
212
+ lineEnd: source.length,
213
+ line: lastLine,
214
+ offsetInLine: offset - lastLineStart,
215
+ };
216
+ }
217
+ // Get the prefix length for a list line (indent + marker + space)
218
+ export function getListPrefixLength(line) {
219
+ const match = line.match(LIST_LINE_REGEX);
220
+ if (!match) {
221
+ return null;
222
+ }
223
+ return match[1].length + match[2].length + match[3].length;
224
+ }
225
+ // Check if a line is a list item
226
+ export function isListLine(line) {
227
+ return LIST_LINE_REGEX.test(line);
228
+ }
229
+ // Parse a single line and return the list item if it is one
230
+ export function parseListItem(line) {
231
+ const parsed = parseLine(line);
232
+ if (parsed.type !== "list-item") {
233
+ return null;
234
+ }
235
+ return parsed.item;
236
+ }
237
+ // Count numbered items at top level (indent 0) before a given line index
238
+ export function countNumberedItemsBefore(source, beforeLineIndex) {
239
+ const lines = source.split("\n");
240
+ let count = 0;
241
+ for (let i = 0; i < beforeLineIndex && i < lines.length; i++) {
242
+ const item = parseListItem(lines[i]);
243
+ if (item && item.markerType === "numbered" && item.indent === 0) {
244
+ count++;
245
+ }
246
+ }
247
+ return count;
248
+ }
@@ -9,3 +9,4 @@ export type ToggleNumberedListCommand = {
9
9
  /** All list extension commands */
10
10
  export type ListCommand = ToggleBulletListCommand | ToggleNumberedListCommand;
11
11
  export declare const listExtension: import("../../..").CakeExtension;
12
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../src/cake/extensions/list/list.ts"],"names":[],"mappings":"AAygCA,+CAA+C;AAC/C,MAAM,MAAM,uBAAuB,GAAG;IAAE,IAAI,EAAE,oBAAoB,CAAA;CAAE,CAAC;AAErE,iDAAiD;AACjD,MAAM,MAAM,yBAAyB,GAAG;IAAE,IAAI,EAAE,sBAAsB,CAAA;CAAE,CAAC;AAEzE,kCAAkC;AAClC,MAAM,MAAM,WAAW,GAAG,uBAAuB,GAAG,yBAAyB,CAAC;AAE9E,eAAO,MAAM,aAAa,kCAmGxB,CAAC"}