@lapismd/mira 0.0.3 → 0.0.5

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 (203) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/extensions/index.d.ts +7 -0
  3. package/dist/extensions/index.d.ts.map +1 -1
  4. package/dist/preview/editable-markdown-preview/editable-markdown-surface.svelte +20 -9
  5. package/dist/preview/editable-markdown-preview/editable-markdown-surface.svelte.d.ts.map +1 -1
  6. package/dist/preview/editable-markdown-preview/editing-session.d.ts +13 -0
  7. package/dist/preview/editable-markdown-preview/editing-session.d.ts.map +1 -0
  8. package/dist/preview/editable-markdown-preview/editing-session.js +50 -0
  9. package/dist/preview/editable-markdown-preview/editor-surface.svelte +13 -4
  10. package/dist/preview/editable-markdown-preview/editor-surface.svelte.d.ts +1 -0
  11. package/dist/preview/editable-markdown-preview/editor-surface.svelte.d.ts.map +1 -1
  12. package/dist/preview/editable-markdown-preview/serialized-writer.d.ts +5 -1
  13. package/dist/preview/editable-markdown-preview/serialized-writer.d.ts.map +1 -1
  14. package/dist/preview/editable-markdown-preview/serialized-writer.js +29 -0
  15. package/dist/preview/editable-markdown-preview.svelte +8 -6
  16. package/dist/preview/editable-markdown-preview.svelte.d.ts.map +1 -1
  17. package/package.json +6 -3
  18. package/dist/core/code-mirror.test.d.ts +0 -2
  19. package/dist/core/code-mirror.test.d.ts.map +0 -1
  20. package/dist/core/code-mirror.test.js +0 -68
  21. package/dist/core/images.test.d.ts +0 -2
  22. package/dist/core/images.test.d.ts.map +0 -1
  23. package/dist/core/images.test.js +0 -109
  24. package/dist/core/index.test.d.ts +0 -2
  25. package/dist/core/index.test.d.ts.map +0 -1
  26. package/dist/core/index.test.js +0 -11
  27. package/dist/core/markdown-actions.test.d.ts +0 -2
  28. package/dist/core/markdown-actions.test.d.ts.map +0 -1
  29. package/dist/core/markdown-actions.test.js +0 -182
  30. package/dist/extensions/doodle-dividers.test.d.ts +0 -2
  31. package/dist/extensions/doodle-dividers.test.d.ts.map +0 -1
  32. package/dist/extensions/doodle-dividers.test.js +0 -296
  33. package/dist/extensions/index.test.d.ts +0 -2
  34. package/dist/extensions/index.test.d.ts.map +0 -1
  35. package/dist/extensions/index.test.js +0 -283
  36. package/dist/index.test.d.ts +0 -2
  37. package/dist/index.test.d.ts.map +0 -1
  38. package/dist/index.test.js +0 -64
  39. package/dist/internal/appearance-context.test.d.ts +0 -2
  40. package/dist/internal/appearance-context.test.d.ts.map +0 -1
  41. package/dist/internal/appearance-context.test.js +0 -31
  42. package/dist/internal/codemirror/base/index.test.d.ts +0 -2
  43. package/dist/internal/codemirror/base/index.test.d.ts.map +0 -1
  44. package/dist/internal/codemirror/base/index.test.js +0 -303
  45. package/dist/internal/codemirror/base/search.test.d.ts +0 -2
  46. package/dist/internal/codemirror/base/search.test.d.ts.map +0 -1
  47. package/dist/internal/codemirror/base/search.test.js +0 -155
  48. package/dist/internal/codemirror/base/selection-toolbar.test.d.ts +0 -2
  49. package/dist/internal/codemirror/base/selection-toolbar.test.d.ts.map +0 -1
  50. package/dist/internal/codemirror/base/selection-toolbar.test.js +0 -149
  51. package/dist/internal/codemirror/markdown/authoring.test.d.ts +0 -2
  52. package/dist/internal/codemirror/markdown/authoring.test.d.ts.map +0 -1
  53. package/dist/internal/codemirror/markdown/authoring.test.js +0 -222
  54. package/dist/internal/codemirror/markdown/index.test.d.ts +0 -2
  55. package/dist/internal/codemirror/markdown/index.test.d.ts.map +0 -1
  56. package/dist/internal/codemirror/markdown/index.test.js +0 -158
  57. package/dist/internal/codemirror/markdown/showcase-fixture.test.d.ts +0 -2
  58. package/dist/internal/codemirror/markdown/showcase-fixture.test.d.ts.map +0 -1
  59. package/dist/internal/codemirror/markdown/showcase-fixture.test.js +0 -50
  60. package/dist/internal/codemirror/rich/block-controls.test.d.ts +0 -2
  61. package/dist/internal/codemirror/rich/block-controls.test.d.ts.map +0 -1
  62. package/dist/internal/codemirror/rich/block-controls.test.js +0 -401
  63. package/dist/internal/codemirror/rich/block-ranges.test.d.ts +0 -2
  64. package/dist/internal/codemirror/rich/block-ranges.test.d.ts.map +0 -1
  65. package/dist/internal/codemirror/rich/block-ranges.test.js +0 -310
  66. package/dist/internal/codemirror/rich/block-toolbar.test.d.ts +0 -2
  67. package/dist/internal/codemirror/rich/block-toolbar.test.d.ts.map +0 -1
  68. package/dist/internal/codemirror/rich/block-toolbar.test.js +0 -178
  69. package/dist/internal/codemirror/rich/decorations/fold-indicators.test.d.ts +0 -2
  70. package/dist/internal/codemirror/rich/decorations/fold-indicators.test.d.ts.map +0 -1
  71. package/dist/internal/codemirror/rich/decorations/fold-indicators.test.js +0 -8
  72. package/dist/internal/codemirror/rich/index.test.d.ts +0 -2
  73. package/dist/internal/codemirror/rich/index.test.d.ts.map +0 -1
  74. package/dist/internal/codemirror/rich/index.test.js +0 -607
  75. package/dist/internal/codemirror/rich/utils/code-block-lines.test.d.ts +0 -2
  76. package/dist/internal/codemirror/rich/utils/code-block-lines.test.d.ts.map +0 -1
  77. package/dist/internal/codemirror/rich/utils/code-block-lines.test.js +0 -82
  78. package/dist/internal/codemirror/rich/utils/fenced-code.test.d.ts +0 -2
  79. package/dist/internal/codemirror/rich/utils/fenced-code.test.d.ts.map +0 -1
  80. package/dist/internal/codemirror/rich/utils/fenced-code.test.js +0 -12
  81. package/dist/internal/codemirror/rich/utils/headings.test.d.ts +0 -2
  82. package/dist/internal/codemirror/rich/utils/headings.test.d.ts.map +0 -1
  83. package/dist/internal/codemirror/rich/utils/headings.test.js +0 -27
  84. package/dist/internal/codemirror/rich/utils/height-estimates.test.d.ts +0 -2
  85. package/dist/internal/codemirror/rich/utils/height-estimates.test.d.ts.map +0 -1
  86. package/dist/internal/codemirror/rich/utils/height-estimates.test.js +0 -10
  87. package/dist/internal/codemirror/rich/utils/images.test.d.ts +0 -2
  88. package/dist/internal/codemirror/rich/utils/images.test.d.ts.map +0 -1
  89. package/dist/internal/codemirror/rich/utils/images.test.js +0 -37
  90. package/dist/internal/codemirror/rich/utils/indent.test.d.ts +0 -2
  91. package/dist/internal/codemirror/rich/utils/indent.test.d.ts.map +0 -1
  92. package/dist/internal/codemirror/rich/utils/indent.test.js +0 -169
  93. package/dist/internal/codemirror/rich/utils/inline-math.test.d.ts +0 -2
  94. package/dist/internal/codemirror/rich/utils/inline-math.test.d.ts.map +0 -1
  95. package/dist/internal/codemirror/rich/utils/inline-math.test.js +0 -15
  96. package/dist/internal/codemirror/rich/utils/links.test.d.ts +0 -2
  97. package/dist/internal/codemirror/rich/utils/links.test.d.ts.map +0 -1
  98. package/dist/internal/codemirror/rich/utils/links.test.js +0 -28
  99. package/dist/internal/codemirror/rich/utils/list-callouts.test.d.ts +0 -2
  100. package/dist/internal/codemirror/rich/utils/list-callouts.test.d.ts.map +0 -1
  101. package/dist/internal/codemirror/rich/utils/list-callouts.test.js +0 -47
  102. package/dist/internal/codemirror/rich/utils/measured-indent.test.d.ts +0 -2
  103. package/dist/internal/codemirror/rich/utils/measured-indent.test.d.ts.map +0 -1
  104. package/dist/internal/codemirror/rich/utils/measured-indent.test.js +0 -102
  105. package/dist/internal/codemirror/rich/utils/ranges.test.d.ts +0 -2
  106. package/dist/internal/codemirror/rich/utils/ranges.test.d.ts.map +0 -1
  107. package/dist/internal/codemirror/rich/utils/ranges.test.js +0 -22
  108. package/dist/internal/codemirror/rich/utils/tasks.test.d.ts +0 -2
  109. package/dist/internal/codemirror/rich/utils/tasks.test.d.ts.map +0 -1
  110. package/dist/internal/codemirror/rich/utils/tasks.test.js +0 -57
  111. package/dist/internal/task-states.test.d.ts +0 -2
  112. package/dist/internal/task-states.test.d.ts.map +0 -1
  113. package/dist/internal/task-states.test.js +0 -36
  114. package/dist/mira-code-editor.test.d.ts +0 -2
  115. package/dist/mira-code-editor.test.d.ts.map +0 -1
  116. package/dist/mira-code-editor.test.js +0 -93
  117. package/dist/mode-position.test.d.ts +0 -2
  118. package/dist/mode-position.test.d.ts.map +0 -1
  119. package/dist/mode-position.test.js +0 -60
  120. package/dist/preview/code-language.test.d.ts +0 -2
  121. package/dist/preview/code-language.test.d.ts.map +0 -1
  122. package/dist/preview/code-language.test.js +0 -12
  123. package/dist/preview/components/icon.test.d.ts +0 -2
  124. package/dist/preview/components/icon.test.d.ts.map +0 -1
  125. package/dist/preview/components/icon.test.js +0 -20
  126. package/dist/preview/editable-markdown-preview/behavior.test.d.ts +0 -2
  127. package/dist/preview/editable-markdown-preview/behavior.test.d.ts.map +0 -1
  128. package/dist/preview/editable-markdown-preview/behavior.test.js +0 -36
  129. package/dist/preview/editable-markdown-preview/serialized-writer.test.d.ts +0 -2
  130. package/dist/preview/editable-markdown-preview/serialized-writer.test.d.ts.map +0 -1
  131. package/dist/preview/editable-markdown-preview/serialized-writer.test.js +0 -59
  132. package/dist/preview/embed-target.test.d.ts +0 -2
  133. package/dist/preview/embed-target.test.d.ts.map +0 -1
  134. package/dist/preview/embed-target.test.js +0 -78
  135. package/dist/preview/frontmatter/frontmatter.test.d.ts +0 -2
  136. package/dist/preview/frontmatter/frontmatter.test.d.ts.map +0 -1
  137. package/dist/preview/frontmatter/frontmatter.test.js +0 -190
  138. package/dist/preview/frontmatter/suggestions.test.d.ts +0 -2
  139. package/dist/preview/frontmatter/suggestions.test.d.ts.map +0 -1
  140. package/dist/preview/frontmatter/suggestions.test.js +0 -45
  141. package/dist/preview/outline.test.d.ts +0 -2
  142. package/dist/preview/outline.test.d.ts.map +0 -1
  143. package/dist/preview/outline.test.js +0 -98
  144. package/dist/preview/portable-surfaces.test.d.ts +0 -2
  145. package/dist/preview/portable-surfaces.test.d.ts.map +0 -1
  146. package/dist/preview/portable-surfaces.test.js +0 -513
  147. package/dist/preview/preview.test.d.ts +0 -2
  148. package/dist/preview/preview.test.d.ts.map +0 -1
  149. package/dist/preview/preview.test.js +0 -154
  150. package/dist/preview/remark/callouts.test.d.ts +0 -2
  151. package/dist/preview/remark/callouts.test.d.ts.map +0 -1
  152. package/dist/preview/remark/callouts.test.js +0 -23
  153. package/dist/preview/remark/custom-checklists.test.d.ts +0 -2
  154. package/dist/preview/remark/custom-checklists.test.d.ts.map +0 -1
  155. package/dist/preview/remark/custom-checklists.test.js +0 -51
  156. package/dist/preview/remark/links.test.d.ts +0 -2
  157. package/dist/preview/remark/links.test.d.ts.map +0 -1
  158. package/dist/preview/remark/links.test.js +0 -62
  159. package/dist/preview/remark/parity.test.d.ts +0 -2
  160. package/dist/preview/remark/parity.test.d.ts.map +0 -1
  161. package/dist/preview/remark/parity.test.js +0 -105
  162. package/dist/preview/styles/frontmatter-properties.test.d.ts +0 -2
  163. package/dist/preview/styles/frontmatter-properties.test.d.ts.map +0 -1
  164. package/dist/preview/styles/frontmatter-properties.test.js +0 -57
  165. package/dist/preview/styles/source-mode.test.d.ts +0 -2
  166. package/dist/preview/styles/source-mode.test.d.ts.map +0 -1
  167. package/dist/preview/styles/source-mode.test.js +0 -28
  168. package/dist/tables/extension.test.d.ts +0 -2
  169. package/dist/tables/extension.test.d.ts.map +0 -1
  170. package/dist/tables/extension.test.js +0 -100
  171. package/dist/tables/grid-editor-column.test.d.ts +0 -2
  172. package/dist/tables/grid-editor-column.test.d.ts.map +0 -1
  173. package/dist/tables/grid-editor-column.test.js +0 -27
  174. package/dist/tables/grid-table.test.d.ts +0 -2
  175. package/dist/tables/grid-table.test.d.ts.map +0 -1
  176. package/dist/tables/grid-table.test.js +0 -140
  177. package/dist/tables/index.test.d.ts +0 -2
  178. package/dist/tables/index.test.d.ts.map +0 -1
  179. package/dist/tables/index.test.js +0 -7
  180. package/dist/tables/inline-editor-extensions.test.d.ts +0 -2
  181. package/dist/tables/inline-editor-extensions.test.d.ts.map +0 -1
  182. package/dist/tables/inline-editor-extensions.test.js +0 -23
  183. package/dist/tables/pipe-table.test.d.ts +0 -2
  184. package/dist/tables/pipe-table.test.d.ts.map +0 -1
  185. package/dist/tables/pipe-table.test.js +0 -13
  186. package/dist/tables/table-node.test.d.ts +0 -2
  187. package/dist/tables/table-node.test.d.ts.map +0 -1
  188. package/dist/tables/table-node.test.js +0 -167
  189. package/dist/tables/widgets/source-button.test.d.ts +0 -2
  190. package/dist/tables/widgets/source-button.test.d.ts.map +0 -1
  191. package/dist/tables/widgets/source-button.test.js +0 -11
  192. package/dist/themes/theme-contract.test.d.ts +0 -2
  193. package/dist/themes/theme-contract.test.d.ts.map +0 -1
  194. package/dist/themes/theme-contract.test.js +0 -88
  195. package/dist/ui/index.test.d.ts +0 -2
  196. package/dist/ui/index.test.d.ts.map +0 -1
  197. package/dist/ui/index.test.js +0 -7
  198. package/dist/ui/styles.test.d.ts +0 -2
  199. package/dist/ui/styles.test.d.ts.map +0 -1
  200. package/dist/ui/styles.test.js +0 -26
  201. package/dist/ui/table-dnd/table-dnd-utils.test.d.ts +0 -2
  202. package/dist/ui/table-dnd/table-dnd-utils.test.d.ts.map +0 -1
  203. package/dist/ui/table-dnd/table-dnd-utils.test.js +0 -46
@@ -1,607 +0,0 @@
1
- import { history, undo } from "@codemirror/commands";
2
- import { describe, expect, it, vi } from "vitest";
3
- import { EditorView } from "@codemirror/view";
4
- import { defineMiraExtension, doodleDividersExtension, } from "@lapismd/mira/extensions";
5
- import { createMarkdownCodeMirrorExtensions } from "../markdown";
6
- import { createRichEditorExtensions } from ".";
7
- function nextFrame() {
8
- return new Promise((resolve) => requestAnimationFrame(() => resolve()));
9
- }
10
- describe("createRichEditorExtensions", () => {
11
- it("can be disabled", () => {
12
- expect(createRichEditorExtensions({ enabled: false })).toEqual([]);
13
- });
14
- it("preserves a local frontmatter disclosure across preview widget remounts", async () => {
15
- const parent = document.createElement("div");
16
- document.body.append(parent);
17
- const source = "---\ntitle: Portable\n---\n\n# Note";
18
- const view = new EditorView({
19
- doc: source,
20
- selection: { anchor: source.length },
21
- extensions: [
22
- createMarkdownCodeMirrorExtensions(),
23
- createRichEditorExtensions({
24
- livePreview: true,
25
- frontmatterOpen: false,
26
- }),
27
- ],
28
- parent,
29
- });
30
- await nextFrame();
31
- await nextFrame();
32
- const expand = parent.querySelector('.mira-rich-widget--frontmatter button[aria-label="Expand properties"]');
33
- expect(expand).not.toBeNull();
34
- expand?.click();
35
- await nextFrame();
36
- expect(parent.querySelector(".md-frontmatter__content")).not.toBeNull();
37
- view.dispatch({ selection: { anchor: 2 } });
38
- await nextFrame();
39
- expect(parent.querySelector(".mira-rich-widget--frontmatter")).toBeNull();
40
- view.dispatch({ selection: { anchor: source.length } });
41
- await nextFrame();
42
- await nextFrame();
43
- expect(parent.querySelector('.mira-rich-widget--frontmatter button[aria-label="Collapse properties"]')).not.toBeNull();
44
- expect(parent.querySelector(".md-frontmatter__content")).not.toBeNull();
45
- view.destroy();
46
- parent.remove();
47
- });
48
- it("keeps an interactive frontmatter menu mounted while its trigger receives focus", async () => {
49
- const parent = document.createElement("div");
50
- document.body.append(parent);
51
- const source = "---\ntitle: Portable\n---\n\n# Note";
52
- const view = new EditorView({
53
- doc: source,
54
- selection: { anchor: source.length },
55
- extensions: [
56
- createMarkdownCodeMirrorExtensions(),
57
- createRichEditorExtensions({
58
- livePreview: true,
59
- frontmatterOpen: true,
60
- }),
61
- ],
62
- parent,
63
- });
64
- await nextFrame();
65
- await nextFrame();
66
- const trigger = parent.querySelector('button[aria-label="Property options for title"]');
67
- expect(trigger).not.toBeNull();
68
- trigger?.focus();
69
- trigger?.click();
70
- await nextFrame();
71
- expect(trigger?.isConnected).toBe(true);
72
- expect(trigger?.getAttribute("aria-expanded")).toBe("true");
73
- expect(document.body.querySelector('[role="menu"][aria-label="Property options for title"]')).not.toBeNull();
74
- view.destroy();
75
- parent.remove();
76
- });
77
- it("renders and reveals a seeded divider comment and rule as one widget", async () => {
78
- const parent = document.createElement("div");
79
- document.body.append(parent);
80
- const source = `<!-- mira-divider:v1:4f32a91c -->
81
- ---
82
-
83
- After`;
84
- const doodles = doodleDividersExtension();
85
- const view = new EditorView({
86
- doc: source,
87
- selection: { anchor: source.length },
88
- extensions: [
89
- createMarkdownCodeMirrorExtensions(),
90
- createRichEditorExtensions({
91
- livePreview: true,
92
- extensions: [doodles],
93
- }),
94
- ],
95
- parent,
96
- });
97
- await nextFrame();
98
- await nextFrame();
99
- const widget = parent.querySelector(".mira-rich-widget--horizontalrule");
100
- const divider = widget?.querySelector(".mira-doodle-divider");
101
- expect(widget).not.toBeNull();
102
- expect(divider?.dataset.seed).toBe("4f32a91c");
103
- expect(parent.textContent).not.toContain("mira-divider:v1");
104
- divider?.dispatchEvent(new MouseEvent("click", { bubbles: true, button: 0 }));
105
- await nextFrame();
106
- expect(view.state.selection.main.from).toBe(0);
107
- expect(view.state.selection.main.to).toBe(source.indexOf("\n\nAfter"));
108
- expect(parent.textContent).toContain("mira-divider:v1:4f32a91c");
109
- view.destroy();
110
- parent.remove();
111
- });
112
- it("rerolls and selects a seeded divider family from its Live Preview controls", async () => {
113
- const parent = document.createElement("div");
114
- document.body.append(parent);
115
- const source = `<!-- mira-divider:v1:00000008 -->
116
- ---
117
-
118
- After`;
119
- const createSeed = vi
120
- .fn()
121
- .mockReturnValueOnce(0x00000008)
122
- .mockReturnValue(0);
123
- const doodles = doodleDividersExtension({ createSeed });
124
- const view = new EditorView({
125
- doc: source,
126
- selection: { anchor: source.length },
127
- extensions: [
128
- history(),
129
- createMarkdownCodeMirrorExtensions(),
130
- createRichEditorExtensions({
131
- livePreview: true,
132
- sourcePath: "notes/dividers.md",
133
- extensions: [doodles],
134
- }),
135
- ],
136
- parent,
137
- });
138
- await nextFrame();
139
- await nextFrame();
140
- const initialVariant = parent.querySelector(".mira-doodle-divider")?.dataset.variant;
141
- const refresh = parent.querySelector('button[aria-label="Refresh divider style"]');
142
- expect(initialVariant).toBe("scribble");
143
- expect(refresh).not.toBeNull();
144
- refresh?.click();
145
- await nextFrame();
146
- await nextFrame();
147
- expect(view.state.doc.toString()).not.toContain("<!-- mira-divider:v1:00000008 -->");
148
- expect(parent.querySelector(".mira-doodle-divider")?.dataset.variant).not.toBe("scribble");
149
- expect(view.state.selection.main.anchor).toBe(source.length);
150
- expect(createSeed).toHaveBeenNthCalledWith(1, expect.objectContaining({
151
- line: 2,
152
- reason: "reroll",
153
- sourcePath: "notes/dividers.md",
154
- }));
155
- expect(undo(view)).toBe(true);
156
- await nextFrame();
157
- await nextFrame();
158
- expect(view.state.doc.toString()).toBe(source);
159
- const picker = parent.querySelector('button[aria-label="Choose divider style"]');
160
- picker?.click();
161
- const menu = parent.querySelector('[role="menu"][aria-label="Divider style"]');
162
- expect(picker?.getAttribute("aria-expanded")).toBe("true");
163
- expect(menu?.hidden).toBe(false);
164
- expect(menu?.querySelectorAll('[role="menuitemradio"]')).toHaveLength(8);
165
- expect(menu?.querySelector('[role="menuitemradio"][aria-checked="true"]')
166
- ?.textContent).toContain("Scribble");
167
- await nextFrame();
168
- const plain = [
169
- ...(menu?.querySelectorAll("button") ?? []),
170
- ].find((button) => button.textContent?.includes("Plain"));
171
- menu?.dispatchEvent(new KeyboardEvent("keydown", { bubbles: true, key: "End" }));
172
- expect(document.activeElement).toBe(plain);
173
- menu?.dispatchEvent(new KeyboardEvent("keydown", { bubbles: true, key: "Escape" }));
174
- expect(menu?.hidden).toBe(true);
175
- expect(document.activeElement).toBe(picker);
176
- picker?.click();
177
- await nextFrame();
178
- document.body.dispatchEvent(new Event("pointerdown", { bubbles: true }));
179
- expect(menu?.hidden).toBe(true);
180
- picker?.click();
181
- plain?.click();
182
- await nextFrame();
183
- await nextFrame();
184
- expect(parent.querySelector(".mira-doodle-divider")?.dataset.variant).toBe("plain");
185
- expect(view.state.doc.toString()).not.toContain("00000008");
186
- expect(createSeed).toHaveBeenNthCalledWith(2, expect.objectContaining({
187
- line: 2,
188
- reason: "variant",
189
- sourcePath: "notes/dividers.md",
190
- }));
191
- expect(undo(view)).toBe(true);
192
- expect(view.state.doc.toString()).toBe(source);
193
- view.destroy();
194
- parent.remove();
195
- });
196
- it("allows consumers to disable Live Preview divider controls", async () => {
197
- const parent = document.createElement("div");
198
- document.body.append(parent);
199
- const doodles = doodleDividersExtension({ controls: false });
200
- const view = new EditorView({
201
- doc: "<!-- mira-divider:v1:00000008 -->\n---",
202
- extensions: [
203
- createMarkdownCodeMirrorExtensions(),
204
- createRichEditorExtensions({
205
- livePreview: true,
206
- extensions: [doodles],
207
- }),
208
- ],
209
- parent,
210
- });
211
- await nextFrame();
212
- await nextFrame();
213
- expect(parent.querySelector(".mira-doodle-divider")).not.toBeNull();
214
- expect(parent.querySelector(".mira-doodle-divider__controls")).toBeNull();
215
- view.destroy();
216
- parent.remove();
217
- });
218
- it("keeps readonly Live Preview dividers decorative", async () => {
219
- const parent = document.createElement("div");
220
- document.body.append(parent);
221
- const doodles = doodleDividersExtension();
222
- const view = new EditorView({
223
- doc: "<!-- mira-divider:v1:00000008 -->\n---",
224
- extensions: [
225
- createMarkdownCodeMirrorExtensions(),
226
- createRichEditorExtensions({
227
- livePreview: true,
228
- readonly: true,
229
- extensions: [doodles],
230
- }),
231
- ],
232
- parent,
233
- });
234
- await nextFrame();
235
- await nextFrame();
236
- expect(parent.querySelector(".mira-doodle-divider")).not.toBeNull();
237
- expect(parent.querySelector(".mira-doodle-divider__controls")).toBeNull();
238
- view.destroy();
239
- parent.remove();
240
- });
241
- it("renders an indented seeded divider inside a list item", async () => {
242
- const parent = document.createElement("div");
243
- document.body.append(parent);
244
- const source = `- Parent item
245
-
246
- <!-- mira-divider:v1:00000004 -->
247
- ---
248
-
249
- Content after the divider`;
250
- const doodles = doodleDividersExtension();
251
- const view = new EditorView({
252
- doc: source,
253
- selection: { anchor: 0 },
254
- extensions: [
255
- createMarkdownCodeMirrorExtensions(),
256
- createRichEditorExtensions({
257
- livePreview: true,
258
- extensions: [doodles],
259
- }),
260
- ],
261
- parent,
262
- });
263
- await nextFrame();
264
- await nextFrame();
265
- expect(parent.querySelector(".mira-doodle-divider")?.dataset.seed).toBe("00000004");
266
- expect(parent.textContent).not.toContain("mira-divider:v1");
267
- view.destroy();
268
- parent.remove();
269
- });
270
- it("keeps source mode free of live-preview hide/replace decorations", async () => {
271
- const nonempty = (extensions) => extensions.filter((extension) => {
272
- if (Array.isArray(extension)) {
273
- return extension.length > 0;
274
- }
275
- return extension != null;
276
- });
277
- const livePreview = nonempty(createRichEditorExtensions({ livePreview: true }));
278
- const sourceMode = nonempty(createRichEditorExtensions({ livePreview: false }));
279
- expect(sourceMode.length).toBeGreaterThan(0);
280
- expect(sourceMode.length).toBeLessThan(livePreview.length);
281
- const parent = document.createElement("div");
282
- document.body.append(parent);
283
- const view = new EditorView({
284
- doc: [
285
- "> quoted line",
286
- "",
287
- "Has `inline code` here",
288
- "",
289
- "```js",
290
- "const value = 1;",
291
- "```",
292
- ].join("\n"),
293
- extensions: createRichEditorExtensions({ livePreview: false }),
294
- parent,
295
- });
296
- await nextFrame();
297
- expect(parent.querySelector(".cm-formatting-hidden")).toBeNull();
298
- expect(parent.querySelector(".cm-inline-code")).toBeNull();
299
- expect(parent.querySelector(".mira-rich-widget")).toBeNull();
300
- expect(parent.querySelector(".cm-formatting-quote")).toBeNull();
301
- // Source keeps fence chrome (bg + start/end radius) with visible markers.
302
- expect(parent.querySelector(".cm-formatting-code-start")).not.toBeNull();
303
- expect(parent.querySelector(".cm-formatting-code-end")).not.toBeNull();
304
- expect(parent.textContent).toContain("> quoted line");
305
- expect(parent.textContent).toContain("`inline code`");
306
- expect(parent.textContent).toContain("```js");
307
- view.destroy();
308
- parent.remove();
309
- });
310
- it("keeps ordinary parentheses visible while hiding real link closers", async () => {
311
- const parent = document.createElement("div");
312
- document.body.append(parent);
313
- const source = [
314
- "You might mean tools (what I can run in Cursor) next.",
315
- "",
316
- "## Agent tools (what I can use in this chat)",
317
- "",
318
- "See [docs](https://example.com) after.",
319
- ].join("\n");
320
- const view = new EditorView({
321
- doc: source,
322
- selection: { anchor: source.length },
323
- extensions: [
324
- createMarkdownCodeMirrorExtensions(),
325
- createRichEditorExtensions({ livePreview: true }),
326
- ],
327
- parent,
328
- });
329
- await nextFrame();
330
- await nextFrame();
331
- const proseLine = [...parent.querySelectorAll(".cm-line")].find((line) => line.textContent?.includes("what I can run in Cursor"));
332
- const headingLine = [...parent.querySelectorAll(".cm-line")].find((line) => line.textContent?.includes("what I can use in this chat"));
333
- const linkLine = [...parent.querySelectorAll(".cm-line")].find((line) => line.textContent?.includes("https://example.com"));
334
- expect(proseLine).toBeDefined();
335
- expect(headingLine).toBeDefined();
336
- expect(linkLine).toBeDefined();
337
- const hiddenProseParens = [
338
- ...(proseLine?.querySelectorAll(".cm-formatting-hidden") ?? []),
339
- ].filter((node) => (node.textContent || "").includes(")"));
340
- const hiddenHeadingParens = [
341
- ...(headingLine?.querySelectorAll(".cm-formatting-hidden") ?? []),
342
- ].filter((node) => (node.textContent || "").includes(")"));
343
- const hiddenLinkClosers = [
344
- ...(linkLine?.querySelectorAll(".cm-formatting-hidden") ?? []),
345
- ].filter((node) => {
346
- const text = node.textContent || "";
347
- return text.includes(")") || text.includes("](");
348
- });
349
- expect(hiddenProseParens).toHaveLength(0);
350
- expect(hiddenHeadingParens).toHaveLength(0);
351
- expect(proseLine?.textContent).toContain("Cursor)");
352
- expect(headingLine?.textContent).toContain("chat)");
353
- expect(hiddenLinkClosers.length).toBeGreaterThan(0);
354
- expect(hiddenLinkClosers.some((node) => (node.textContent || "").includes(")"))).toBe(true);
355
- view.destroy();
356
- parent.remove();
357
- });
358
- it("keeps hidden heading syntax out of the live-preview line box", async () => {
359
- const parent = document.createElement("div");
360
- document.body.append(parent);
361
- const source = "# Visible heading\n\nParagraph";
362
- const view = new EditorView({
363
- doc: source,
364
- selection: { anchor: source.length },
365
- extensions: [
366
- createMarkdownCodeMirrorExtensions(),
367
- createRichEditorExtensions({ livePreview: true }),
368
- ],
369
- parent,
370
- });
371
- await nextFrame();
372
- await nextFrame();
373
- const hiddenFormatting = parent.querySelector(".cm-header .cm-formatting-hidden");
374
- expect(hiddenFormatting).not.toBeNull();
375
- const style = getComputedStyle(hiddenFormatting);
376
- expect(style.display).toBe("inline-block");
377
- expect(Number.parseFloat(style.height)).toBe(0);
378
- expect(Number.parseFloat(style.lineHeight)).toBe(0);
379
- view.destroy();
380
- parent.remove();
381
- });
382
- it("keeps blockquote markers from collapsing when the line is focused", async () => {
383
- const parent = document.createElement("div");
384
- document.body.append(parent);
385
- const view = new EditorView({
386
- doc: "> quoted line\n\nAfter",
387
- extensions: createRichEditorExtensions({ livePreview: true }),
388
- parent,
389
- });
390
- await nextFrame();
391
- // Caret in quote content (after "> "), not on the marker itself.
392
- view.dispatch({ selection: { anchor: 4, head: 4 } });
393
- await nextFrame();
394
- const quoteLine = [...parent.querySelectorAll(".cm-line")].find((line) => (line.textContent || "").includes("quoted line"));
395
- expect(quoteLine).toBeTruthy();
396
- const hiddenQuoteMarks = [
397
- ...quoteLine.querySelectorAll(".cm-formatting-hidden"),
398
- ].filter((node) => (node.textContent || "").includes(">"));
399
- expect(hiddenQuoteMarks).toHaveLength(0);
400
- expect(quoteLine.textContent).toContain(">");
401
- view.destroy();
402
- parent.remove();
403
- });
404
- it("keeps a blockquote-nested task marker complete while it is edited", async () => {
405
- const parent = document.createElement("div");
406
- document.body.append(parent);
407
- const source = " > - [ ] Quoted checklist child";
408
- const view = new EditorView({
409
- doc: source,
410
- selection: { anchor: source.indexOf("[") + 1 },
411
- extensions: [
412
- createMarkdownCodeMirrorExtensions(),
413
- createRichEditorExtensions({ livePreview: true }),
414
- ],
415
- parent,
416
- });
417
- await nextFrame();
418
- await nextFrame();
419
- const taskLine = [...parent.querySelectorAll(".cm-line")].find((line) => line.textContent?.includes("Quoted checklist child"));
420
- expect(taskLine?.textContent).toContain("- [ ] Quoted checklist child");
421
- expect([...(taskLine?.querySelectorAll(".cm-formatting-hidden") ?? [])].filter((node) => node.textContent?.includes("["))).toHaveLength(0);
422
- expect(taskLine?.querySelector(".cm-task-checkbox")).toBeNull();
423
- view.dispatch({
424
- selection: { anchor: source.indexOf("Quoted checklist child") + 3 },
425
- });
426
- await nextFrame();
427
- await nextFrame();
428
- expect(taskLine?.querySelector(".cm-task-checkbox")).not.toBeNull();
429
- view.destroy();
430
- parent.remove();
431
- });
432
- it("replaces only the task marker when a custom task type is selected", async () => {
433
- const parent = document.createElement("div");
434
- document.body.append(parent);
435
- const source = "- [?] Needs decision";
436
- const view = new EditorView({
437
- doc: source,
438
- selection: { anchor: source.indexOf("Needs") + 2 },
439
- extensions: [
440
- createMarkdownCodeMirrorExtensions(),
441
- createRichEditorExtensions({ livePreview: true }),
442
- ],
443
- parent,
444
- });
445
- await nextFrame();
446
- await nextFrame();
447
- parent
448
- .querySelector('button[aria-label="Change task type"]')
449
- ?.click();
450
- await nextFrame();
451
- document.body
452
- .querySelector('.mira-task-state-option__action[aria-label="Starred"]')
453
- ?.click();
454
- await nextFrame();
455
- await nextFrame();
456
- expect(view.state.doc.toString()).toBe("- [*] Needs decision");
457
- view.destroy();
458
- parent.remove();
459
- });
460
- it("reserves nested border chrome for nested quote prefixes", async () => {
461
- const parent = document.createElement("div");
462
- document.body.append(parent);
463
- const view = new EditorView({
464
- doc: "> outer quote\n> > nested quote",
465
- extensions: [
466
- createMarkdownCodeMirrorExtensions(),
467
- createRichEditorExtensions({ livePreview: true }),
468
- ],
469
- parent,
470
- });
471
- view.dispatch({ selection: { anchor: 5 } });
472
- await nextFrame();
473
- await nextFrame();
474
- const lines = [...parent.querySelectorAll(".cm-line")];
475
- const outer = lines.find((line) => line.textContent?.includes("outer quote"));
476
- const nested = lines.find((line) => line.textContent?.includes("nested quote"));
477
- expect(outer?.querySelector(".cm-formatting-quote-1")).not.toBeNull();
478
- expect(outer?.querySelector(".cm-blockquote-border")).toBeNull();
479
- expect(nested?.querySelector(".cm-formatting-quote-2")).not.toBeNull();
480
- expect(nested?.querySelector(".cm-blockquote-border")).not.toBeNull();
481
- view.destroy();
482
- parent.remove();
483
- });
484
- it("resets source whitespace on rendered block widgets", () => {
485
- const parent = document.createElement("div");
486
- document.body.append(parent);
487
- const view = new EditorView({
488
- doc: "After",
489
- extensions: createRichEditorExtensions({ livePreview: true }),
490
- parent,
491
- });
492
- const widget = document.createElement("div");
493
- widget.className =
494
- "mira-rich-widget mira-rich-widget--block mira-rich-widget--blockquote";
495
- view.contentDOM.append(widget);
496
- expect(getComputedStyle(view.contentDOM).whiteSpace).not.toBe("normal");
497
- expect(getComputedStyle(widget).whiteSpace).toBe("normal");
498
- view.destroy();
499
- parent.remove();
500
- });
501
- it("renders a four-space list continuation as a blockquote widget", async () => {
502
- const parent = document.createElement("div");
503
- document.body.append(parent);
504
- const view = new EditorView({
505
- doc: [
506
- "> > Nested quote keeps a second quote guide.",
507
- "3. Blockquote inside a list item:",
508
- "",
509
- " > Skip a line and indent the quote markers four spaces.",
510
- " > The rendered blockquote stays attached to its first source line.",
511
- ].join("\n"),
512
- extensions: [
513
- createMarkdownCodeMirrorExtensions(),
514
- createRichEditorExtensions({ livePreview: true }),
515
- ],
516
- parent,
517
- });
518
- await nextFrame();
519
- await nextFrame();
520
- const widget = [...parent.querySelectorAll(".mira-rich-widget")].find((element) => element.textContent?.includes("Skip a line and indent"));
521
- expect(widget?.getAttribute("data-node")).toBe("Blockquote");
522
- expect(widget?.querySelector("blockquote")).not.toBeNull();
523
- expect(widget?.querySelector(".mira-code-block")).toBeNull();
524
- expect(widget?.textContent).not.toContain("> The rendered blockquote stays attached");
525
- expect(parent.querySelector(".cm-indented-codeblock")).toBeNull();
526
- view.destroy();
527
- parent.remove();
528
- });
529
- it("maps normalized blockquote widget edits back to indented source", async () => {
530
- const parent = document.createElement("div");
531
- document.body.append(parent);
532
- const source = [
533
- "3. Blockquote inside a list item:",
534
- "",
535
- " > First quoted line",
536
- " > - [ ] Nested task",
537
- ].join("\n");
538
- let edit;
539
- const view = new EditorView({
540
- doc: source,
541
- extensions: [
542
- createMarkdownCodeMirrorExtensions(),
543
- createRichEditorExtensions({
544
- livePreview: true,
545
- onChange(replacement, from, to, nextValue) {
546
- edit = { replacement, from, to, nextValue };
547
- },
548
- }),
549
- ],
550
- parent,
551
- });
552
- await nextFrame();
553
- await nextFrame();
554
- const checkbox = parent.querySelector('input[aria-label="Toggle task"]');
555
- expect(checkbox).not.toBeNull();
556
- checkbox.checked = true;
557
- checkbox.dispatchEvent(new Event("change", { bubbles: true }));
558
- await nextFrame();
559
- expect(edit).toBeDefined();
560
- expect(source.slice(edit.from, edit.to)).toBe(" ");
561
- expect(edit.replacement).toBe("x");
562
- expect(edit.nextValue).toContain(" > - [x] Nested task");
563
- view.destroy();
564
- parent.remove();
565
- });
566
- it("marks editors that render full-height indentation guides", () => {
567
- const parent = document.createElement("div");
568
- document.body.append(parent);
569
- const view = new EditorView({
570
- doc: " Wrapped continuation",
571
- extensions: createRichEditorExtensions({ indentGuides: true }),
572
- parent,
573
- });
574
- expect(view.dom.classList.contains("cm-show-indentation-guides")).toBe(true);
575
- view.destroy();
576
- parent.remove();
577
- });
578
- it("uses list-callout contributions from Mira extensions", async () => {
579
- const parent = document.createElement("div");
580
- document.body.append(parent);
581
- const view = new EditorView({
582
- doc: "- ^ Decision\n- % Plain",
583
- extensions: [
584
- createRichEditorExtensions({
585
- extensions: [
586
- defineMiraExtension({
587
- name: "list-callouts",
588
- listCallouts: [
589
- { char: "^", color: "99, 102, 241" },
590
- { char: "%", enabled: false },
591
- ],
592
- }),
593
- ],
594
- }),
595
- ],
596
- parent,
597
- });
598
- await nextFrame();
599
- expect(parent.querySelector(".cm-line[data-callout='^']")).not.toBeNull();
600
- expect(parent.querySelector(".cm-line[data-callout='%']")).toBeNull();
601
- const trigger = parent.querySelector("[data-callout-char='^']");
602
- expect(trigger?.tagName).toBe("BUTTON");
603
- expect(trigger?.getAttribute("aria-label")).toBe("Change list highlight (^)");
604
- view.destroy();
605
- parent.remove();
606
- });
607
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=code-block-lines.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"code-block-lines.test.d.ts","sourceRoot":"","sources":["../../../../../src/internal/codemirror/rich/utils/code-block-lines.test.ts"],"names":[],"mappings":""}