@dxos/react-ui-editor 0.8.2-staging.7ac8446 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/dist/lib/browser/index.mjs +4152 -2852
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/testing/index.mjs +6 -0
  5. package/dist/lib/browser/testing/index.mjs.map +7 -0
  6. package/dist/lib/node/index.cjs +3318 -2009
  7. package/dist/lib/node/index.cjs.map +4 -4
  8. package/dist/lib/node/meta.json +1 -1
  9. package/dist/lib/node/testing/index.cjs +29 -0
  10. package/dist/lib/node/testing/index.cjs.map +7 -0
  11. package/dist/lib/node-esm/index.mjs +4152 -2852
  12. package/dist/lib/node-esm/index.mjs.map +4 -4
  13. package/dist/lib/node-esm/meta.json +1 -1
  14. package/dist/lib/node-esm/testing/index.mjs +8 -0
  15. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  16. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +1 -1
  17. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  18. package/dist/types/src/components/EditorToolbar/blocks.d.ts +4 -3
  19. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  20. package/dist/types/src/components/EditorToolbar/formatting.d.ts +4 -3
  21. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  22. package/dist/types/src/components/EditorToolbar/headings.d.ts +4 -3
  23. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  24. package/dist/types/src/components/EditorToolbar/{comment.d.ts → image.d.ts} +4 -5
  25. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -0
  26. package/dist/types/src/components/EditorToolbar/index.d.ts +1 -1
  27. package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
  28. package/dist/types/src/components/EditorToolbar/lists.d.ts +4 -3
  29. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
  30. package/dist/types/src/components/EditorToolbar/search.d.ts +17 -0
  31. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -0
  32. package/dist/types/src/components/EditorToolbar/util.d.ts +17 -25
  33. package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
  34. package/dist/types/src/components/EditorToolbar/{viewMode.d.ts → view-mode.d.ts} +5 -4
  35. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -0
  36. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts +21 -0
  37. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +1 -0
  38. package/dist/types/src/components/Popover/RefPopover.d.ts +21 -0
  39. package/dist/types/src/components/Popover/RefPopover.d.ts.map +1 -0
  40. package/dist/types/src/components/Popover/index.d.ts +3 -0
  41. package/dist/types/src/components/Popover/index.d.ts.map +1 -0
  42. package/dist/types/src/components/index.d.ts +1 -0
  43. package/dist/types/src/components/index.d.ts.map +1 -1
  44. package/dist/types/src/defaults.d.ts +3 -5
  45. package/dist/types/src/defaults.d.ts.map +1 -1
  46. package/dist/types/src/extensions/annotations.d.ts +4 -1
  47. package/dist/types/src/extensions/annotations.d.ts.map +1 -1
  48. package/dist/types/src/extensions/autocomplete.d.ts +1 -2
  49. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  50. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  51. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  52. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  53. package/dist/types/src/extensions/automerge/defs.d.ts +1 -1
  54. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  55. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  56. package/dist/types/src/extensions/automerge/update-automerge.d.ts +1 -1
  57. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  58. package/dist/types/src/extensions/automerge/update-codemirror.d.ts +1 -1
  59. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -1
  60. package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +1 -1
  61. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
  62. package/dist/types/src/extensions/blast.d.ts.map +1 -1
  63. package/dist/types/src/extensions/command/action.d.ts +17 -0
  64. package/dist/types/src/extensions/command/action.d.ts.map +1 -0
  65. package/dist/types/src/extensions/command/command.d.ts +4 -10
  66. package/dist/types/src/extensions/command/command.d.ts.map +1 -1
  67. package/dist/types/src/extensions/command/hint.d.ts +18 -4
  68. package/dist/types/src/extensions/command/hint.d.ts.map +1 -1
  69. package/dist/types/src/extensions/command/index.d.ts +3 -0
  70. package/dist/types/src/extensions/command/index.d.ts.map +1 -1
  71. package/dist/types/src/extensions/command/menu.d.ts +6 -11
  72. package/dist/types/src/extensions/command/menu.d.ts.map +1 -1
  73. package/dist/types/src/extensions/command/state.d.ts +9 -11
  74. package/dist/types/src/extensions/command/state.d.ts.map +1 -1
  75. package/dist/types/src/extensions/command/typeahead.d.ts +17 -0
  76. package/dist/types/src/extensions/command/typeahead.d.ts.map +1 -0
  77. package/dist/types/src/extensions/comments.d.ts +9 -17
  78. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  79. package/dist/types/src/extensions/debug.d.ts.map +1 -1
  80. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  81. package/dist/types/src/extensions/factories.d.ts +4 -0
  82. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  83. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  84. package/dist/types/src/extensions/index.d.ts +3 -0
  85. package/dist/types/src/extensions/index.d.ts.map +1 -1
  86. package/dist/types/src/extensions/json.d.ts +7 -0
  87. package/dist/types/src/extensions/json.d.ts.map +1 -0
  88. package/dist/types/src/extensions/listener.d.ts.map +1 -1
  89. package/dist/types/src/extensions/markdown/{editorAction.d.ts → action.d.ts} +1 -1
  90. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -0
  91. package/dist/types/src/extensions/markdown/bundle.d.ts +2 -1
  92. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  93. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  94. package/dist/types/src/extensions/markdown/debug.d.ts.map +1 -1
  95. package/dist/types/src/extensions/markdown/decorate.d.ts +5 -1
  96. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  97. package/dist/types/src/extensions/markdown/formatting.d.ts +3 -3
  98. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  99. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  100. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  101. package/dist/types/src/extensions/markdown/index.d.ts +1 -1
  102. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  103. package/dist/types/src/extensions/markdown/link.d.ts +4 -1
  104. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  105. package/dist/types/src/extensions/markdown/styles.d.ts.map +1 -1
  106. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
  107. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  108. package/dist/types/src/extensions/modes.d.ts +5 -5
  109. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  110. package/dist/types/src/extensions/outliner/commands.d.ts +10 -0
  111. package/dist/types/src/extensions/outliner/commands.d.ts.map +1 -0
  112. package/dist/types/src/extensions/outliner/editor.d.ts +5 -0
  113. package/dist/types/src/extensions/outliner/editor.d.ts.map +1 -0
  114. package/dist/types/src/extensions/outliner/editor.test.d.ts +2 -0
  115. package/dist/types/src/extensions/outliner/editor.test.d.ts.map +1 -0
  116. package/dist/types/src/extensions/outliner/index.d.ts +4 -0
  117. package/dist/types/src/extensions/outliner/index.d.ts.map +1 -0
  118. package/dist/types/src/extensions/outliner/outliner.d.ts +13 -0
  119. package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -0
  120. package/dist/types/src/extensions/outliner/outliner.test.d.ts +2 -0
  121. package/dist/types/src/extensions/outliner/outliner.test.d.ts.map +1 -0
  122. package/dist/types/src/extensions/outliner/selection.d.ts +12 -0
  123. package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -0
  124. package/dist/types/src/extensions/outliner/tree.d.ts +79 -0
  125. package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -0
  126. package/dist/types/src/extensions/outliner/tree.test.d.ts +2 -0
  127. package/dist/types/src/extensions/outliner/tree.test.d.ts.map +1 -0
  128. package/dist/types/src/extensions/preview/index.d.ts +2 -0
  129. package/dist/types/src/extensions/preview/index.d.ts.map +1 -0
  130. package/dist/types/src/extensions/preview/preview.d.ts +39 -0
  131. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -0
  132. package/dist/types/src/extensions/selection.d.ts.map +1 -1
  133. package/dist/types/src/extensions/typewriter.d.ts.map +1 -1
  134. package/dist/types/src/hooks/index.d.ts +0 -1
  135. package/dist/types/src/hooks/index.d.ts.map +1 -1
  136. package/dist/types/src/hooks/useTextEditor.d.ts +2 -1
  137. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  138. package/dist/types/src/stories/Command.stories.d.ts +7 -0
  139. package/dist/types/src/stories/Command.stories.d.ts.map +1 -0
  140. package/dist/types/src/stories/Comments.stories.d.ts +13 -0
  141. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -0
  142. package/dist/types/src/stories/EditorToolbar.stories.d.ts +12 -0
  143. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -0
  144. package/dist/types/src/stories/Experimental.stories.d.ts +16 -0
  145. package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -0
  146. package/dist/types/src/stories/Markdown.stories.d.ts +46 -0
  147. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -0
  148. package/dist/types/src/stories/Outliner.stories.d.ts +26 -0
  149. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -0
  150. package/dist/types/src/stories/Preview.stories.d.ts +10 -0
  151. package/dist/types/src/stories/Preview.stories.d.ts.map +1 -0
  152. package/dist/types/src/stories/TextEditor.stories.d.ts +55 -0
  153. package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -0
  154. package/dist/types/src/stories/util.d.ts +53 -0
  155. package/dist/types/src/stories/util.d.ts.map +1 -0
  156. package/dist/types/src/styles/theme.d.ts.map +1 -1
  157. package/dist/types/src/styles/tokens.d.ts.map +1 -1
  158. package/dist/types/src/testing/index.d.ts +2 -0
  159. package/dist/types/src/testing/index.d.ts.map +1 -0
  160. package/dist/types/src/testing/util.d.ts +2 -0
  161. package/dist/types/src/testing/util.d.ts.map +1 -0
  162. package/dist/types/src/types.d.ts +5 -0
  163. package/dist/types/src/types.d.ts.map +1 -1
  164. package/dist/types/src/util/cursor.d.ts.map +1 -1
  165. package/dist/types/src/util/debug.d.ts.map +1 -1
  166. package/dist/types/src/util/dom.d.ts.map +1 -1
  167. package/dist/types/src/util/facet.d.ts.map +1 -1
  168. package/dist/types/src/util/react.d.ts +6 -1
  169. package/dist/types/src/util/react.d.ts.map +1 -1
  170. package/dist/types/tsconfig.tsbuildinfo +1 -1
  171. package/package.json +46 -30
  172. package/src/components/EditorToolbar/EditorToolbar.tsx +95 -72
  173. package/src/components/EditorToolbar/blocks.ts +27 -6
  174. package/src/components/EditorToolbar/formatting.ts +34 -7
  175. package/src/components/EditorToolbar/headings.ts +9 -8
  176. package/src/components/EditorToolbar/image.ts +16 -0
  177. package/src/components/EditorToolbar/index.ts +7 -1
  178. package/src/components/EditorToolbar/lists.ts +26 -7
  179. package/src/components/EditorToolbar/search.ts +19 -0
  180. package/src/components/EditorToolbar/util.ts +19 -20
  181. package/src/components/EditorToolbar/{viewMode.ts → view-mode.ts} +9 -8
  182. package/src/components/Popover/RefDropdownMenu.tsx +77 -0
  183. package/src/components/Popover/RefPopover.tsx +75 -0
  184. package/src/components/Popover/index.ts +6 -0
  185. package/src/components/index.ts +1 -0
  186. package/src/defaults.ts +12 -13
  187. package/src/extensions/annotations.ts +41 -64
  188. package/src/extensions/autocomplete.ts +5 -6
  189. package/src/extensions/automerge/automerge.stories.tsx +13 -24
  190. package/src/extensions/automerge/automerge.test.tsx +6 -5
  191. package/src/extensions/automerge/automerge.ts +2 -2
  192. package/src/extensions/automerge/defs.ts +1 -2
  193. package/src/extensions/automerge/sync.ts +7 -7
  194. package/src/extensions/automerge/update-automerge.ts +1 -1
  195. package/src/extensions/automerge/update-codemirror.ts +3 -4
  196. package/src/extensions/awareness/awareness-provider.ts +4 -4
  197. package/src/extensions/awareness/awareness.ts +7 -7
  198. package/src/extensions/blast.ts +9 -9
  199. package/src/extensions/command/action.ts +49 -0
  200. package/src/extensions/command/command.ts +7 -27
  201. package/src/extensions/command/hint.ts +36 -33
  202. package/src/extensions/command/index.ts +3 -0
  203. package/src/extensions/command/menu.ts +79 -51
  204. package/src/extensions/command/state.ts +41 -61
  205. package/src/extensions/command/typeahead.ts +116 -0
  206. package/src/extensions/comments.ts +11 -76
  207. package/src/extensions/factories.ts +13 -0
  208. package/src/extensions/folding.tsx +1 -1
  209. package/src/extensions/index.ts +3 -0
  210. package/src/extensions/json.ts +56 -0
  211. package/src/extensions/markdown/bundle.ts +13 -9
  212. package/src/extensions/markdown/changes.ts +3 -2
  213. package/src/extensions/markdown/decorate.ts +19 -17
  214. package/src/extensions/markdown/formatting.ts +6 -6
  215. package/src/extensions/markdown/image.ts +14 -13
  216. package/src/extensions/markdown/index.ts +1 -1
  217. package/src/extensions/markdown/link.ts +33 -24
  218. package/src/extensions/markdown/styles.ts +4 -3
  219. package/src/extensions/markdown/table.ts +3 -3
  220. package/src/extensions/modes.ts +5 -6
  221. package/src/extensions/outliner/commands.ts +270 -0
  222. package/src/extensions/outliner/editor.test.ts +33 -0
  223. package/src/extensions/outliner/editor.ts +184 -0
  224. package/src/extensions/outliner/index.ts +7 -0
  225. package/src/extensions/outliner/outliner.test.ts +99 -0
  226. package/src/extensions/outliner/outliner.ts +168 -0
  227. package/src/extensions/outliner/selection.ts +50 -0
  228. package/src/extensions/outliner/tree.test.ts +164 -0
  229. package/src/extensions/outliner/tree.ts +315 -0
  230. package/src/extensions/preview/index.ts +5 -0
  231. package/src/extensions/preview/preview.ts +271 -0
  232. package/src/hooks/index.ts +0 -1
  233. package/src/hooks/useTextEditor.ts +4 -3
  234. package/src/stories/Command.stories.tsx +97 -0
  235. package/src/stories/Comments.stories.tsx +98 -0
  236. package/src/stories/EditorToolbar.stories.tsx +96 -0
  237. package/src/stories/Experimental.stories.tsx +86 -0
  238. package/src/stories/Markdown.stories.tsx +121 -0
  239. package/src/stories/Outliner.stories.tsx +108 -0
  240. package/src/stories/Preview.stories.tsx +149 -0
  241. package/src/stories/TextEditor.stories.tsx +256 -0
  242. package/src/stories/util.tsx +326 -0
  243. package/src/styles/theme.ts +15 -5
  244. package/src/styles/tokens.ts +1 -2
  245. package/src/testing/index.ts +5 -0
  246. package/src/testing/util.ts +5 -0
  247. package/src/types.ts +7 -0
  248. package/src/util/react.tsx +20 -2
  249. package/dist/types/src/InputMode.stories.d.ts +0 -57
  250. package/dist/types/src/InputMode.stories.d.ts.map +0 -1
  251. package/dist/types/src/TextEditor.stories.d.ts +0 -115
  252. package/dist/types/src/TextEditor.stories.d.ts.map +0 -1
  253. package/dist/types/src/components/EditorToolbar/comment.d.ts.map +0 -1
  254. package/dist/types/src/components/EditorToolbar/viewMode.d.ts.map +0 -1
  255. package/dist/types/src/extensions/command/preview.d.ts +0 -12
  256. package/dist/types/src/extensions/command/preview.d.ts.map +0 -1
  257. package/dist/types/src/extensions/markdown/editorAction.d.ts.map +0 -1
  258. package/dist/types/src/fragments.d.ts +0 -3
  259. package/dist/types/src/fragments.d.ts.map +0 -1
  260. package/dist/types/src/hooks/useActionHandler.d.ts +0 -4
  261. package/dist/types/src/hooks/useActionHandler.d.ts.map +0 -1
  262. package/src/InputMode.stories.tsx +0 -124
  263. package/src/TextEditor.stories.tsx +0 -856
  264. package/src/components/EditorToolbar/comment.ts +0 -23
  265. package/src/extensions/command/preview.ts +0 -79
  266. package/src/fragments.ts +0 -19
  267. package/src/hooks/useActionHandler.ts +0 -12
  268. /package/src/extensions/markdown/{editorAction.ts → action.ts} +0 -0
@@ -95,17 +95,17 @@ export class RemoteSelectionsDecorator implements PluginValue {
95
95
  });
96
96
  }
97
97
 
98
- destroy() {
98
+ destroy(): void {
99
99
  void this._ctx.dispose();
100
100
  this._provider.close();
101
101
  }
102
102
 
103
- update(update: ViewUpdate) {
103
+ update(update: ViewUpdate): void {
104
104
  this._updateLocalSelection(update.view);
105
105
  this._updateRemoteSelections(update.view);
106
106
  }
107
107
 
108
- private _updateLocalSelection(view: EditorView) {
108
+ private _updateLocalSelection(view: EditorView): void {
109
109
  const hasFocus = view.hasFocus && view.dom.ownerDocument.hasFocus();
110
110
  const { anchor = undefined, head = undefined } = hasFocus ? view.state.selection.main : {};
111
111
  if (this._lastAnchor === anchor && this._lastHead === head) {
@@ -125,7 +125,7 @@ export class RemoteSelectionsDecorator implements PluginValue {
125
125
  );
126
126
  }
127
127
 
128
- private _updateRemoteSelections(view: EditorView) {
128
+ private _updateRemoteSelections(view: EditorView): void {
129
129
  const decorations: Range<Decoration>[] = [
130
130
  // TODO(burdon): Factor out for testing.
131
131
  // {
@@ -239,11 +239,11 @@ class RemoteCaretWidget extends WidgetType {
239
239
  return span;
240
240
  }
241
241
 
242
- override updateDOM() {
242
+ override updateDOM(): boolean {
243
243
  return false;
244
244
  }
245
245
 
246
- override eq(widget: this) {
246
+ override eq(widget: this): boolean {
247
247
  return widget._color === this._color;
248
248
  }
249
249
 
@@ -251,7 +251,7 @@ class RemoteCaretWidget extends WidgetType {
251
251
  return -1;
252
252
  }
253
253
 
254
- override ignoreEvent() {
254
+ override ignoreEvent(): boolean {
255
255
  return true;
256
256
  }
257
257
  }
@@ -136,7 +136,7 @@ class Blaster {
136
136
  return this._node;
137
137
  }
138
138
 
139
- initialize() {
139
+ initialize(): void {
140
140
  // console.log('initialize');
141
141
  invariant(!this._canvas && !this._ctx);
142
142
 
@@ -155,7 +155,7 @@ class Blaster {
155
155
  this.resize();
156
156
  }
157
157
 
158
- destroy() {
158
+ destroy(): void {
159
159
  this.stop();
160
160
  // console.log('destroy');
161
161
  if (this._canvas) {
@@ -165,7 +165,7 @@ class Blaster {
165
165
  }
166
166
  }
167
167
 
168
- resize() {
168
+ resize(): void {
169
169
  if (this._node.parentElement && this._canvas) {
170
170
  const { offsetLeft: x, offsetTop: y, offsetWidth: width, offsetHeight: height } = this._node.parentElement;
171
171
  this._canvas.style.top = `${y}px`;
@@ -175,20 +175,20 @@ class Blaster {
175
175
  }
176
176
  }
177
177
 
178
- start() {
178
+ start(): void {
179
179
  // console.log('start');
180
180
  invariant(this._canvas && this._ctx);
181
181
  this._running = true;
182
182
  this.loop();
183
183
  }
184
184
 
185
- stop() {
185
+ stop(): void {
186
186
  // console.log('stop');
187
187
  this._running = false;
188
188
  this._node.style.transform = 'translate(0px, 0px)';
189
189
  }
190
190
 
191
- loop() {
191
+ loop(): void {
192
192
  if (!this._running || !this._canvas || !this._ctx) {
193
193
  return;
194
194
  }
@@ -230,7 +230,7 @@ class Blaster {
230
230
  }
231
231
  }, 100);
232
232
 
233
- drawParticles() {
233
+ drawParticles(): void {
234
234
  for (let i = this._particles.length; i--; i > 0) {
235
235
  const particle = this._particles[i];
236
236
  if (!particle) {
@@ -282,7 +282,7 @@ class Effect1 extends Effect {
282
282
  };
283
283
  }
284
284
 
285
- update(ctx: CanvasRenderingContext2D, particle: Particle) {
285
+ update(ctx: CanvasRenderingContext2D, particle: Particle): void {
286
286
  particle.vy += this._options.particleGravity;
287
287
  particle.x += particle.vx;
288
288
  particle.y += particle.vy;
@@ -313,7 +313,7 @@ class Effect2 extends Effect {
313
313
  };
314
314
  }
315
315
 
316
- update(ctx: CanvasRenderingContext2D, particle: Particle) {
316
+ update(ctx: CanvasRenderingContext2D, particle: Particle): void {
317
317
  particle.vy += this._options.particleGravity;
318
318
  particle.x += particle.vx;
319
319
  particle.y += particle.vy;
@@ -0,0 +1,49 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { StateEffect } from '@codemirror/state';
6
+ import { type KeyBinding, type Command, type EditorView } from '@codemirror/view';
7
+
8
+ import { commandState } from './state';
9
+
10
+ export type Action =
11
+ | {
12
+ type: 'insert';
13
+ text: string;
14
+ }
15
+ | {
16
+ type: 'cancel';
17
+ };
18
+
19
+ export type ActionHandler = (action: Action) => void;
20
+
21
+ export const openEffect = StateEffect.define<{ pos: number; fullWidth?: boolean }>();
22
+ export const closeEffect = StateEffect.define<null>();
23
+
24
+ export const openCommand: Command = (view: EditorView) => {
25
+ if (view.state.field(commandState, false)) {
26
+ const selection = view.state.selection.main;
27
+ const line = view.state.doc.lineAt(selection.from);
28
+ if (line.from === selection.from && line.from === line.to) {
29
+ view.dispatch({ effects: openEffect.of({ pos: selection.anchor, fullWidth: true }) });
30
+ return true;
31
+ }
32
+ }
33
+
34
+ return false;
35
+ };
36
+
37
+ export const closeCommand: Command = (view: EditorView) => {
38
+ if (view.state.field(commandState, false)) {
39
+ view.dispatch({ effects: closeEffect.of(null) });
40
+ return true;
41
+ }
42
+
43
+ return false;
44
+ };
45
+
46
+ export const commandKeyBindings: readonly KeyBinding[] = [
47
+ { key: '/', run: openCommand },
48
+ { key: 'Escape', run: closeCommand },
49
+ ];
@@ -5,35 +5,23 @@
5
5
  import { type Extension } from '@codemirror/state';
6
6
  import { EditorView, keymap } from '@codemirror/view';
7
7
 
8
- import { hintViewPlugin } from './hint';
9
- import { floatingMenu } from './menu';
10
- import { preview, type PreviewOptions } from './preview';
11
- import { closeEffect, commandConfig, commandKeyBindings, commandState } from './state';
8
+ import { closeEffect, commandKeyBindings } from './action';
9
+ import { hintViewPlugin, type HintOptions } from './hint';
10
+ import { commandConfig, commandState, type PopupOptions } from './state';
12
11
 
13
12
  // TODO(burdon): Create knowledge base for CM notes and ideas.
14
13
  // https://discuss.codemirror.net/t/inline-code-hints-like-vscode/5533/4
15
14
  // https://github.com/saminzadeh/codemirror-extension-inline-suggestion
16
15
  // https://github.com/ChromeDevTools/devtools-frontend/blob/main/front_end/ui/components/text_editor/config.ts#L370
17
16
 
18
- // TODO(burdon): Discriminated union.
19
- export type CommandAction = {
20
- insert?: string;
21
- };
22
-
23
- export type CommandOptions = {
24
- onHint: () => string | undefined;
25
- onRenderDialog: (el: HTMLElement, cb: (action?: CommandAction) => void) => void;
26
- onRenderMenu: (el: HTMLElement, cb: () => void) => void;
27
- } & Pick<PreviewOptions, 'onRenderPreview'>;
17
+ export type CommandOptions = Partial<PopupOptions & HintOptions>;
28
18
 
29
- export const command = (options: CommandOptions): Extension => {
19
+ export const command = (options: CommandOptions = {}): Extension => {
30
20
  return [
21
+ keymap.of(commandKeyBindings),
31
22
  commandConfig.of(options),
32
23
  commandState,
33
- keymap.of(commandKeyBindings),
34
- preview(options),
35
- floatingMenu(options),
36
- hintViewPlugin(options),
24
+ options.onHint ? hintViewPlugin({ onHint: options.onHint }) : [],
37
25
  EditorView.focusChangeEffect.of((_, focusing) => {
38
26
  return focusing ? closeEffect.of(null) : null;
39
27
  }),
@@ -41,14 +29,6 @@ export const command = (options: CommandOptions): Extension => {
41
29
  '.cm-tooltip': {
42
30
  background: 'transparent',
43
31
  },
44
- '.cm-preview': {
45
- marginLeft: '-1rem',
46
- marginRight: '-1rem',
47
- padding: '1rem',
48
- borderRadius: '1rem',
49
- background: 'var(--dx-modalSurface)',
50
- border: '1px solid var(--dx-separator)',
51
- },
52
32
  }),
53
33
  ];
54
34
  };
@@ -5,16 +5,48 @@
5
5
  import { RangeSetBuilder } from '@codemirror/state';
6
6
  import { Decoration, EditorView, ViewPlugin, type ViewUpdate, WidgetType } from '@codemirror/view';
7
7
 
8
- import { type CommandOptions } from './command';
9
8
  import { commandState } from './state';
10
9
  import { clientRectsFor, flattenRect } from '../../util';
11
10
 
12
- class CommandHint extends WidgetType {
11
+ export type HintOptions = {
12
+ onHint: () => string | undefined;
13
+ };
14
+
15
+ export const hintViewPlugin = ({ onHint }: HintOptions) =>
16
+ ViewPlugin.fromClass(
17
+ class {
18
+ decorations = Decoration.none;
19
+ update(update: ViewUpdate) {
20
+ const builder = new RangeSetBuilder<Decoration>();
21
+ const cState = update.view.state.field(commandState, false);
22
+ if (!cState?.tooltip) {
23
+ const selection = update.view.state.selection.main;
24
+ const line = update.view.state.doc.lineAt(selection.from);
25
+ // Only show if blank line.
26
+ // TODO(burdon): Clashes with placeholder if pos === 0.
27
+ // TODO(burdon): Show after delay or if blank line above?
28
+ if (selection.from === selection.to && line.from === line.to) {
29
+ const hint = onHint();
30
+ if (hint) {
31
+ builder.add(selection.from, selection.to, Decoration.widget({ widget: new Hint(hint) }));
32
+ }
33
+ }
34
+ }
35
+
36
+ this.decorations = builder.finish();
37
+ }
38
+ },
39
+ {
40
+ provide: (plugin) => [EditorView.decorations.of((view) => view.plugin(plugin)?.decorations ?? Decoration.none)],
41
+ },
42
+ );
43
+
44
+ export class Hint extends WidgetType {
13
45
  constructor(readonly content: string | HTMLElement) {
14
46
  super();
15
47
  }
16
48
 
17
- toDOM() {
49
+ toDOM(): HTMLSpanElement {
18
50
  const wrap = document.createElement('span');
19
51
  wrap.className = 'cm-placeholder';
20
52
  wrap.style.pointerEvents = 'none';
@@ -44,36 +76,7 @@ class CommandHint extends WidgetType {
44
76
  return rect;
45
77
  }
46
78
 
47
- override ignoreEvent() {
79
+ override ignoreEvent(): boolean {
48
80
  return false;
49
81
  }
50
82
  }
51
-
52
- export const hintViewPlugin = ({ onHint }: CommandOptions) =>
53
- ViewPlugin.fromClass(
54
- class {
55
- deco = Decoration.none;
56
- update(update: ViewUpdate) {
57
- const builder = new RangeSetBuilder<Decoration>();
58
- const cState = update.view.state.field(commandState, false);
59
- if (!cState?.tooltip) {
60
- const selection = update.view.state.selection.main;
61
- const line = update.view.state.doc.lineAt(selection.from);
62
- // Only show if blank line.
63
- // TODO(burdon): Clashes with placeholder if pos === 0.
64
- // TODO(burdon): Show after delay or if blank line above?
65
- if (selection.from === selection.to && line.from === line.to) {
66
- const hint = onHint();
67
- if (hint) {
68
- builder.add(selection.from, selection.to, Decoration.widget({ widget: new CommandHint(hint) }));
69
- }
70
- }
71
- }
72
-
73
- this.deco = builder.finish();
74
- }
75
- },
76
- {
77
- provide: (plugin) => [EditorView.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration.none)],
78
- },
79
- );
@@ -2,4 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ export * from './action';
5
6
  export * from './command';
7
+ export * from './menu';
8
+ export * from './typeahead';
@@ -2,99 +2,127 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { type BlockInfo, type EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';
5
+ import { EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';
6
6
 
7
- import { type CommandOptions } from './command';
8
- import { closeEffect, openCommand, openEffect } from './state';
7
+ import { closeEffect, openEffect } from './action';
9
8
 
10
- // TODO(burdon): Trigger completion on click.
11
- // TODO(burdon): Hide when dialog is open.
12
- export const floatingMenu = (options: CommandOptions) =>
9
+ export type FloatingMenuOptions = {
10
+ icon?: string;
11
+ height?: number;
12
+ padding?: number;
13
+ };
14
+
15
+ export const floatingMenu = (options: FloatingMenuOptions = {}) => [
13
16
  ViewPlugin.fromClass(
14
17
  class {
15
- button: HTMLElement;
16
18
  view: EditorView;
19
+ tag: HTMLElement;
17
20
  rafId: number | null = null;
18
21
 
19
22
  constructor(view: EditorView) {
20
23
  this.view = view;
21
24
 
22
- // Position context: scrollDOM
25
+ // Position context.
23
26
  const container = view.scrollDOM;
24
27
  if (getComputedStyle(container).position === 'static') {
25
28
  container.style.position = 'relative';
26
29
  }
27
30
 
28
- // Render menu externally.
29
- this.button = document.createElement('div');
30
- this.button.style.position = 'absolute';
31
- this.button.style.zIndex = '10';
32
- this.button.style.display = 'none';
31
+ const icon = document.createElement('dx-icon');
32
+ icon.setAttribute('icon', options.icon ?? 'ph--dots-three-outline--regular');
33
+
34
+ const button = document.createElement('button');
35
+ button.appendChild(icon);
33
36
 
34
- options.onRenderMenu(this.button, () => {
35
- openCommand(view);
36
- });
37
- container.appendChild(this.button);
37
+ // TODO(burdon): Custom tag/styles?
38
+ this.tag = document.createElement('dx-ref-tag');
39
+ this.tag.classList.add('cm-ref-tag');
40
+ this.tag.appendChild(button);
41
+ container.appendChild(this.tag);
38
42
 
39
43
  // Listen for scroll events.
40
- container.addEventListener('scroll', this.scheduleUpdate);
44
+ container.addEventListener('scroll', this.scheduleUpdate.bind(this));
41
45
  this.scheduleUpdate();
42
46
  }
43
47
 
44
48
  update(update: ViewUpdate) {
49
+ this.tag.dataset.focused = update.view.hasFocus ? 'true' : 'false';
50
+ if (!update.view.hasFocus) {
51
+ return;
52
+ }
53
+
45
54
  // TODO(burdon): Timer to fade in/out.
46
55
  if (update.transactions.some((tr) => tr.effects.some((effect) => effect.is(openEffect)))) {
47
- this.button.style.display = 'none';
56
+ this.tag.style.display = 'none';
57
+ this.tag.classList.add('opacity-10');
48
58
  } else if (update.transactions.some((tr) => tr.effects.some((effect) => effect.is(closeEffect)))) {
49
- this.button.style.display = 'block';
50
- } else if (update.selectionSet || update.viewportChanged || update.docChanged || update.geometryChanged) {
59
+ this.tag.style.display = 'block';
60
+ } else if (
61
+ update.docChanged ||
62
+ update.focusChanged ||
63
+ update.geometryChanged ||
64
+ update.selectionSet ||
65
+ update.viewportChanged
66
+ ) {
51
67
  this.scheduleUpdate();
52
68
  }
53
69
  }
54
70
 
55
- scheduleUpdate() {
56
- if (this.rafId != null) {
57
- cancelAnimationFrame(this.rafId);
58
- }
59
- this.rafId = requestAnimationFrame(() => this.updateButtonPosition());
60
- }
61
-
62
71
  updateButtonPosition() {
63
- const pos = this.view.state.selection.main.head;
64
- const lineBlock: BlockInfo = this.view.lineBlockAt(pos);
65
- const domInfo = this.view.domAtPos(lineBlock.from);
72
+ const { x, width } = this.view.contentDOM.getBoundingClientRect();
66
73
 
67
- // Find nearest HTMLElement for the line block
68
- let node: Node | null = domInfo.node;
69
- while (node && !(node instanceof HTMLElement)) {
70
- node = node.parentNode;
71
- }
72
-
73
- if (!node) {
74
- this.button.style.display = 'none';
74
+ const pos = this.view.state.selection.main.head;
75
+ const line = this.view.lineBlockAt(pos);
76
+ const coords = this.view.coordsAtPos(line.from);
77
+ if (!coords) {
75
78
  return;
76
79
  }
77
80
 
78
- const lineRect = (node as HTMLElement).getBoundingClientRect();
79
- const containerRect = this.view.scrollDOM.getBoundingClientRect();
81
+ const lineHeight = coords.bottom - coords.top;
82
+ const dy = (lineHeight - (options.height ?? 32)) / 2;
80
83
 
81
- // Account for scroll and padding/margin in scrollDOM.
82
- const offsetTop = lineRect.top - containerRect.top + this.view.scrollDOM.scrollTop;
83
- const offsetLeft = this.view.scrollDOM.clientWidth + this.view.scrollDOM.scrollLeft - lineRect.x;
84
+ const offsetTop = coords.top + dy;
85
+ const offsetLeft = x + width + (options.padding ?? 8);
84
86
 
85
- // TODO(burdon): Position is incorrect if cursor is in fenced code block.
86
- // console.log('offsetTop', lineRect, containerRect);
87
+ this.tag.style.top = `${offsetTop}px`;
88
+ this.tag.style.left = `${offsetLeft}px`;
89
+ this.tag.style.display = 'block';
90
+ }
91
+
92
+ scheduleUpdate() {
93
+ if (this.rafId != null) {
94
+ cancelAnimationFrame(this.rafId);
95
+ }
87
96
 
88
- this.button.style.top = `${offsetTop}px`;
89
- this.button.style.left = `${offsetLeft}px`;
90
- this.button.style.display = 'block';
97
+ this.rafId = requestAnimationFrame(this.updateButtonPosition.bind(this));
91
98
  }
92
99
 
93
100
  destroy() {
94
- this.button.remove();
101
+ this.tag.remove();
95
102
  if (this.rafId != null) {
96
103
  cancelAnimationFrame(this.rafId);
97
104
  }
98
105
  }
99
106
  },
100
- );
107
+ ),
108
+
109
+ EditorView.theme({
110
+ '.cm-ref-tag': {
111
+ position: 'fixed',
112
+ padding: '0',
113
+ border: 'none',
114
+ transition: 'opacity 0.3s ease-in-out',
115
+ opacity: 0.1,
116
+ },
117
+ '.cm-ref-tag button': {
118
+ display: 'grid',
119
+ alignItems: 'center',
120
+ justifyContent: 'center',
121
+ width: '2rem',
122
+ height: '2rem',
123
+ },
124
+ '.cm-ref-tag[data-focused="true"]': {
125
+ opacity: 1,
126
+ },
127
+ }),
128
+ ];
@@ -2,25 +2,24 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { StateEffect, StateField } from '@codemirror/state';
6
- import {
7
- showTooltip,
8
- type Command,
9
- type EditorView,
10
- type KeyBinding,
11
- type Tooltip,
12
- type TooltipView,
13
- } from '@codemirror/view';
5
+ import { StateField } from '@codemirror/state';
6
+ import { showTooltip, type EditorView, type Tooltip, type TooltipView } from '@codemirror/view';
14
7
 
8
+ import { closeEffect, type Action, openEffect } from './action';
15
9
  import { type CommandOptions } from './command';
10
+ import { type RenderCallback } from '../../types';
16
11
  import { singleValueFacet } from '../../util';
17
12
 
13
+ export const commandConfig = singleValueFacet<CommandOptions>();
14
+
15
+ export type PopupOptions = {
16
+ renderDialog: RenderCallback<{ onAction: (action?: Action) => void }>;
17
+ };
18
+
18
19
  type CommandState = {
19
20
  tooltip?: Tooltip | null;
20
21
  };
21
22
 
22
- export const commandConfig = singleValueFacet<CommandOptions>();
23
-
24
23
  export const commandState = StateField.define<CommandState>({
25
24
  create: () => ({}),
26
25
  update: (state, tr) => {
@@ -29,8 +28,8 @@ export const commandState = StateField.define<CommandState>({
29
28
  return {};
30
29
  }
31
30
 
32
- if (effect.is(openEffect)) {
33
- const options = tr.state.facet(commandConfig);
31
+ const { renderDialog } = tr.state.facet(commandConfig);
32
+ if (effect.is(openEffect) && renderDialog) {
34
33
  const { pos, fullWidth } = effect.value;
35
34
  const tooltip: Tooltip = {
36
35
  pos,
@@ -38,38 +37,49 @@ export const commandState = StateField.define<CommandState>({
38
37
  arrow: false,
39
38
  strictSide: true,
40
39
  create: (view: EditorView) => {
41
- const dom = document.createElement('div');
40
+ const root = document.createElement('div');
41
+
42
42
  const tooltipView: TooltipView = {
43
- dom,
43
+ dom: root,
44
44
  mount: (view: EditorView) => {
45
45
  if (fullWidth) {
46
- const parent = dom.parentElement!;
46
+ const parent = root.parentElement!;
47
47
  const { paddingLeft, paddingRight } = window.getComputedStyle(parent);
48
48
  const widthWithoutPadding = parent.clientWidth - parseFloat(paddingLeft) - parseFloat(paddingRight);
49
- dom.style.width = `${widthWithoutPadding}px`;
49
+ root.style.width = `${widthWithoutPadding}px`;
50
50
  }
51
51
 
52
52
  // Render react component.
53
- options.onRenderDialog(dom, (action) => {
54
- view.dispatch({ effects: closeEffect.of(null) });
55
- if (action?.insert?.length) {
56
- // Insert into editor.
57
- const text = action.insert + '\n';
58
- view.dispatch({
59
- changes: { from: pos, insert: text },
60
- selection: { anchor: pos + text.length },
61
- });
62
- }
63
-
64
- // NOTE: Truncates text if set focus immediately.
65
- requestAnimationFrame(() => view.focus());
66
- });
53
+ renderDialog(
54
+ root,
55
+ {
56
+ onAction: (action) => {
57
+ view.dispatch({ effects: closeEffect.of(null) });
58
+ switch (action?.type) {
59
+ case 'insert': {
60
+ // Insert into editor.
61
+ const text = action.text + '\n';
62
+ view.dispatch({
63
+ changes: { from: pos, insert: text },
64
+ selection: { anchor: pos + text.length },
65
+ });
66
+ break;
67
+ }
68
+ }
69
+
70
+ // NOTE: Truncates text if set focus immediately.
71
+ requestAnimationFrame(() => view.focus());
72
+ },
73
+ },
74
+ view,
75
+ );
67
76
  },
68
77
  };
69
78
 
70
79
  return tooltipView;
71
80
  },
72
81
  };
82
+
73
83
  return { tooltip };
74
84
  }
75
85
  }
@@ -78,33 +88,3 @@ export const commandState = StateField.define<CommandState>({
78
88
  },
79
89
  provide: (field) => [showTooltip.from(field, (value) => value.tooltip ?? null)],
80
90
  });
81
-
82
- export const openEffect = StateEffect.define<{ pos: number; fullWidth?: boolean }>();
83
- export const closeEffect = StateEffect.define<null>();
84
-
85
- export const openCommand: Command = (view: EditorView) => {
86
- if (view.state.field(commandState, false)) {
87
- const selection = view.state.selection.main;
88
- const line = view.state.doc.lineAt(selection.from);
89
- if (line.from === selection.from && line.from === line.to) {
90
- view.dispatch({ effects: openEffect.of({ pos: selection.anchor, fullWidth: true }) });
91
- return true;
92
- }
93
- }
94
-
95
- return false;
96
- };
97
-
98
- export const closeCommand: Command = (view: EditorView) => {
99
- if (view.state.field(commandState, false)) {
100
- view.dispatch({ effects: closeEffect.of(null) });
101
- return true;
102
- }
103
-
104
- return false;
105
- };
106
-
107
- export const commandKeyBindings: readonly KeyBinding[] = [
108
- { key: '/', run: openCommand },
109
- { key: 'Escape', run: closeCommand },
110
- ];