@dxos/react-ui-editor 0.3.11-main.bd26370 → 0.3.11-main.c0ba5b8

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 (262) hide show
  1. package/dist/lib/browser/index.mjs +2561 -1619
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +14 -15
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +20 -17
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +6 -3
  9. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  10. package/dist/types/src/{hooks/automerge → components/TextEditor}/automerge.stories.d.ts +6 -1
  11. package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -0
  12. package/dist/types/src/components/TextEditor/comments.stories.d.ts +26 -0
  13. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -0
  14. package/dist/types/src/components/TextEditor/hooks.stories.d.ts +23 -0
  15. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -0
  16. package/dist/types/src/components/TextEditor/index.d.ts +0 -2
  17. package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
  18. package/dist/types/src/components/Toolbar/Toolbar.d.ts +25 -0
  19. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  20. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +23 -0
  21. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
  22. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  23. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  24. package/dist/types/src/components/index.d.ts +1 -0
  25. package/dist/types/src/components/index.d.ts.map +1 -1
  26. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -0
  27. package/dist/types/src/extensions/automerge/automerge.d.ts +9 -0
  28. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -0
  29. package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
  30. package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
  31. package/dist/types/src/extensions/automerge/automerge.test.d.ts +2 -0
  32. package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -0
  33. package/dist/types/src/extensions/automerge/cursor.d.ts +5 -0
  34. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -0
  35. package/dist/types/src/extensions/automerge/defs.d.ts +24 -0
  36. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -0
  37. package/dist/types/src/extensions/automerge/index.d.ts +3 -0
  38. package/dist/types/src/extensions/automerge/index.d.ts.map +1 -0
  39. package/dist/types/src/extensions/automerge/semaphore.d.ts +15 -0
  40. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -0
  41. package/dist/types/src/extensions/automerge/update-automerge.d.ts +5 -0
  42. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -0
  43. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -0
  44. package/dist/types/src/extensions/awareness.d.ts +46 -0
  45. package/dist/types/src/extensions/awareness.d.ts.map +1 -0
  46. package/dist/types/src/extensions/basic.d.ts +8 -0
  47. package/dist/types/src/extensions/basic.d.ts.map +1 -0
  48. package/dist/types/src/extensions/blast.d.ts.map +1 -0
  49. package/dist/types/src/extensions/comments.d.ts +67 -0
  50. package/dist/types/src/extensions/comments.d.ts.map +1 -0
  51. package/dist/types/src/extensions/cursor.d.ts +25 -0
  52. package/dist/types/src/extensions/cursor.d.ts.map +1 -0
  53. package/dist/types/src/extensions/index.d.ts +13 -0
  54. package/dist/types/src/extensions/index.d.ts.map +1 -0
  55. package/dist/types/src/extensions/listener.d.ts +10 -0
  56. package/dist/types/src/extensions/listener.d.ts.map +1 -0
  57. package/dist/types/src/extensions/markdown/bundle.d.ts +14 -0
  58. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -0
  59. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
  60. package/dist/types/src/extensions/markdown/formatting.d.ts +13 -0
  61. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
  62. package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
  63. package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
  64. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -0
  65. package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
  66. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
  67. package/dist/types/src/extensions/markdown/index.d.ts +11 -0
  68. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -0
  69. package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/link.d.ts +1 -0
  70. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
  71. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
  72. package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
  73. package/dist/types/src/extensions/mention.d.ts.map +1 -0
  74. package/dist/types/src/extensions/outliner.d.ts +4 -0
  75. package/dist/types/src/extensions/outliner.d.ts.map +1 -0
  76. package/dist/types/src/extensions/typewriter.d.ts.map +1 -0
  77. package/dist/types/src/hooks/awareness-provider.d.ts +31 -0
  78. package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -0
  79. package/dist/types/src/hooks/defs.d.ts +25 -0
  80. package/dist/types/src/hooks/defs.d.ts.map +1 -0
  81. package/dist/types/src/hooks/index.d.ts +5 -1
  82. package/dist/types/src/hooks/index.d.ts.map +1 -1
  83. package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
  84. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
  85. package/dist/types/src/hooks/useEditorView.d.ts +17 -0
  86. package/dist/types/src/hooks/useEditorView.d.ts.map +1 -0
  87. package/dist/types/src/hooks/useTextEditor.d.ts +39 -0
  88. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -0
  89. package/dist/types/src/hooks/useTextModel.d.ts +2 -31
  90. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  91. package/dist/types/src/index.d.ts +5 -0
  92. package/dist/types/src/index.d.ts.map +1 -1
  93. package/dist/types/src/styles/cursors.d.ts.map +1 -0
  94. package/dist/types/src/styles/index.d.ts +1 -0
  95. package/dist/types/src/styles/index.d.ts.map +1 -1
  96. package/dist/types/src/styles/markdown.d.ts +2 -2
  97. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  98. package/dist/types/src/styles/tokens.d.ts +1 -0
  99. package/dist/types/src/styles/tokens.d.ts.map +1 -1
  100. package/dist/types/src/{components/TextEditor/themes → themes}/default.d.ts +1 -1
  101. package/dist/types/src/themes/default.d.ts.map +1 -0
  102. package/dist/types/src/themes/index.d.ts.map +1 -0
  103. package/dist/types/src/util.d.ts +7 -0
  104. package/dist/types/src/util.d.ts.map +1 -0
  105. package/package.json +48 -36
  106. package/src/components/TextEditor/MarkdownEditor.stories.tsx +93 -102
  107. package/src/components/TextEditor/TextEditor.stories.tsx +13 -34
  108. package/src/components/TextEditor/TextEditor.tsx +138 -97
  109. package/src/{hooks/automerge → components/TextEditor}/automerge.stories.tsx +66 -6
  110. package/src/components/TextEditor/comments.stories.tsx +357 -0
  111. package/src/components/TextEditor/hooks.stories.tsx +106 -0
  112. package/src/components/TextEditor/index.ts +0 -3
  113. package/src/components/Toolbar/Toolbar.stories.tsx +90 -0
  114. package/src/components/Toolbar/Toolbar.tsx +166 -0
  115. package/src/components/Toolbar/index.ts +5 -0
  116. package/src/components/index.ts +1 -0
  117. package/src/{components/TextEditor/extensions → extensions}/autocomplete.ts +23 -19
  118. package/src/extensions/automerge/automerge.spec.tsx +76 -0
  119. package/src/extensions/automerge/automerge.test.ts +13 -0
  120. package/src/extensions/automerge/automerge.ts +103 -0
  121. package/src/extensions/automerge/cursor.ts +52 -0
  122. package/src/extensions/automerge/defs.ts +41 -0
  123. package/src/extensions/automerge/index.ts +6 -0
  124. package/src/extensions/automerge/semaphore.ts +81 -0
  125. package/src/extensions/automerge/update-automerge.ts +49 -0
  126. package/src/{hooks → extensions}/automerge/update-codemirror.ts +17 -9
  127. package/src/extensions/awareness.ts +310 -0
  128. package/src/{components/TextEditor/extensions → extensions}/basic.ts +17 -10
  129. package/src/extensions/comments.ts +476 -0
  130. package/src/extensions/cursor.ts +50 -0
  131. package/src/{components/TextEditor/extensions → extensions}/index.ts +4 -8
  132. package/src/extensions/listener.ts +35 -0
  133. package/src/{components/TextEditor/extensions → extensions}/markdown/bundle.ts +23 -28
  134. package/src/extensions/markdown/code.ts +150 -0
  135. package/src/extensions/markdown/formatting.ts +188 -0
  136. package/src/extensions/markdown/heading.ts +59 -0
  137. package/src/{components/TextEditor/extensions → extensions}/markdown/highlight.ts +12 -27
  138. package/src/extensions/markdown/hr.ts +71 -0
  139. package/src/extensions/markdown/image.ts +87 -0
  140. package/src/extensions/markdown/index.ts +14 -0
  141. package/src/{components/TextEditor/extensions → extensions/markdown}/link.ts +119 -62
  142. package/src/{components/TextEditor/extensions → extensions/markdown}/table.ts +10 -4
  143. package/src/extensions/markdown/tasklist.ts +110 -0
  144. package/src/{components/TextEditor/extensions → extensions}/mention.ts +9 -2
  145. package/src/extensions/outliner.ts +12 -0
  146. package/src/{components/TextEditor/extensions → extensions}/typewriter.ts +1 -1
  147. package/src/hooks/awareness-provider.ts +124 -0
  148. package/src/hooks/defs.ts +42 -0
  149. package/src/hooks/index.ts +6 -1
  150. package/src/hooks/useActionHandler.ts +62 -0
  151. package/src/hooks/useEditorView.ts +29 -0
  152. package/src/hooks/useTextEditor.ts +153 -0
  153. package/src/hooks/useTextModel.ts +33 -115
  154. package/src/index.ts +5 -0
  155. package/src/styles/index.ts +1 -0
  156. package/src/styles/markdown.ts +9 -12
  157. package/src/styles/tokens.ts +3 -0
  158. package/src/{components/TextEditor/themes → themes}/default.ts +54 -81
  159. package/src/util.ts +41 -0
  160. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts +0 -12
  161. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +0 -1
  162. package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +0 -1
  163. package/dist/types/src/components/TextEditor/extensions/basic.d.ts +0 -9
  164. package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +0 -1
  165. package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +0 -1
  166. package/dist/types/src/components/TextEditor/extensions/code.d.ts.map +0 -1
  167. package/dist/types/src/components/TextEditor/extensions/comments.d.ts +0 -48
  168. package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +0 -1
  169. package/dist/types/src/components/TextEditor/extensions/hr.d.ts.map +0 -1
  170. package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +0 -1
  171. package/dist/types/src/components/TextEditor/extensions/index.d.ts +0 -17
  172. package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +0 -1
  173. package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +0 -1
  174. package/dist/types/src/components/TextEditor/extensions/listener.d.ts +0 -9
  175. package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +0 -1
  176. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts +0 -17
  177. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +0 -1
  178. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +0 -1
  179. package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts +0 -3
  180. package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts.map +0 -1
  181. package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +0 -1
  182. package/dist/types/src/components/TextEditor/extensions/mermaid.d.ts +0 -3
  183. package/dist/types/src/components/TextEditor/extensions/mermaid.d.ts.map +0 -1
  184. package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +0 -1
  185. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +0 -1
  186. package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts +0 -12
  187. package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts.map +0 -1
  188. package/dist/types/src/components/TextEditor/extensions/typewriter.d.ts.map +0 -1
  189. package/dist/types/src/components/TextEditor/extensions/util.d.ts +0 -5
  190. package/dist/types/src/components/TextEditor/extensions/util.d.ts.map +0 -1
  191. package/dist/types/src/components/TextEditor/themes/default.d.ts.map +0 -1
  192. package/dist/types/src/components/TextEditor/themes/index.d.ts.map +0 -1
  193. package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +0 -1
  194. package/dist/types/src/hooks/automerge/handle.d.ts +0 -9
  195. package/dist/types/src/hooks/automerge/handle.d.ts.map +0 -1
  196. package/dist/types/src/hooks/automerge/index.d.ts +0 -3
  197. package/dist/types/src/hooks/automerge/index.d.ts.map +0 -1
  198. package/dist/types/src/hooks/automerge/plugin.d.ts +0 -26
  199. package/dist/types/src/hooks/automerge/plugin.d.ts.map +0 -1
  200. package/dist/types/src/hooks/automerge/semaphore.d.ts +0 -19
  201. package/dist/types/src/hooks/automerge/semaphore.d.ts.map +0 -1
  202. package/dist/types/src/hooks/automerge/update-automerge.d.ts +0 -6
  203. package/dist/types/src/hooks/automerge/update-automerge.d.ts.map +0 -1
  204. package/dist/types/src/hooks/automerge/update-codemirror.d.ts.map +0 -1
  205. package/dist/types/src/hooks/yjs/cursors.d.ts.map +0 -1
  206. package/dist/types/src/hooks/yjs/index.d.ts +0 -3
  207. package/dist/types/src/hooks/yjs/index.d.ts.map +0 -1
  208. package/dist/types/src/hooks/yjs/space-provider.d.ts +0 -27
  209. package/dist/types/src/hooks/yjs/space-provider.d.ts.map +0 -1
  210. package/dist/types/src/hooks/yjs/yjs.stories.d.ts +0 -12
  211. package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +0 -1
  212. package/dist/types/src/hooks/yjs/yjs.test.d.ts +0 -2
  213. package/dist/types/src/hooks/yjs/yjs.test.d.ts.map +0 -1
  214. package/dist/types/src/testing/index.d.ts +0 -3
  215. package/dist/types/src/testing/index.d.ts.map +0 -1
  216. package/dist/types/src/testing/proto/gen/schema.d.ts +0 -15
  217. package/dist/types/src/testing/proto/gen/schema.d.ts.map +0 -1
  218. package/dist/types/src/testing/proto/index.d.ts +0 -2
  219. package/dist/types/src/testing/proto/index.d.ts.map +0 -1
  220. package/dist/types/src/testing/replicator.d.ts +0 -45
  221. package/dist/types/src/testing/replicator.d.ts.map +0 -1
  222. package/src/components/TextEditor/codemirror.stories.ts +0 -114
  223. package/src/components/TextEditor/extensions/code.ts +0 -99
  224. package/src/components/TextEditor/extensions/comments.ts +0 -388
  225. package/src/components/TextEditor/extensions/hr.ts +0 -74
  226. package/src/components/TextEditor/extensions/image.ts +0 -69
  227. package/src/components/TextEditor/extensions/listener.ts +0 -23
  228. package/src/components/TextEditor/extensions/markdown/index.ts +0 -6
  229. package/src/components/TextEditor/extensions/mermaid.ts +0 -11
  230. package/src/components/TextEditor/extensions/tasklist.ts +0 -121
  231. package/src/components/TextEditor/extensions/tooltip.ts +0 -61
  232. package/src/components/TextEditor/extensions/util.ts +0 -18
  233. package/src/hooks/automerge/handle.ts +0 -14
  234. package/src/hooks/automerge/index.ts +0 -6
  235. package/src/hooks/automerge/plugin.ts +0 -134
  236. package/src/hooks/automerge/semaphore.ts +0 -78
  237. package/src/hooks/automerge/update-automerge.ts +0 -44
  238. package/src/hooks/yjs/index.ts +0 -6
  239. package/src/hooks/yjs/space-provider.ts +0 -126
  240. package/src/hooks/yjs/yjs.stories.tsx +0 -56
  241. package/src/hooks/yjs/yjs.test.ts +0 -59
  242. package/src/testing/index.ts +0 -6
  243. package/src/testing/proto/gen/schema.ts +0 -49
  244. package/src/testing/proto/index.ts +0 -5
  245. package/src/testing/proto/schema.proto +0 -15
  246. package/src/testing/replicator.ts +0 -189
  247. /package/dist/types/src/{components/TextEditor/extensions → extensions}/autocomplete.d.ts +0 -0
  248. /package/dist/types/src/{hooks → extensions}/automerge/update-codemirror.d.ts +0 -0
  249. /package/dist/types/src/{components/TextEditor/extensions → extensions}/blast.d.ts +0 -0
  250. /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/code.d.ts +0 -0
  251. /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/highlight.d.ts +0 -0
  252. /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/hr.d.ts +0 -0
  253. /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/image.d.ts +0 -0
  254. /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/table.d.ts +0 -0
  255. /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/tasklist.d.ts +0 -0
  256. /package/dist/types/src/{components/TextEditor/extensions → extensions}/mention.d.ts +0 -0
  257. /package/dist/types/src/{components/TextEditor/extensions → extensions}/typewriter.d.ts +0 -0
  258. /package/dist/types/src/{hooks/yjs → styles}/cursors.d.ts +0 -0
  259. /package/dist/types/src/{components/TextEditor/themes → themes}/index.d.ts +0 -0
  260. /package/src/{components/TextEditor/extensions → extensions}/blast.ts +0 -0
  261. /package/src/{hooks/yjs → styles}/cursors.ts +0 -0
  262. /package/src/{components/TextEditor/themes → themes}/index.ts +0 -0
@@ -4,22 +4,28 @@
4
4
  //
5
5
 
6
6
  import { syntaxTree } from '@codemirror/language';
7
+ import { type Extension, RangeSetBuilder } from '@codemirror/state';
7
8
  import {
8
- type EditorState,
9
- type Extension,
10
- type RangeSet,
11
- RangeSetBuilder,
12
- StateField,
13
- type Transaction,
14
- } from '@codemirror/state';
15
- import { Decoration, EditorView, WidgetType } from '@codemirror/view';
9
+ hoverTooltip,
10
+ Decoration,
11
+ type DecorationSet,
12
+ type EditorView,
13
+ ViewPlugin,
14
+ type ViewUpdate,
15
+ WidgetType,
16
+ } from '@codemirror/view';
17
+ import { type SyntaxNode } from '@lezer/common';
18
+
19
+ import { tooltipContent } from '@dxos/react-ui-theme';
16
20
 
17
21
  // Adapted from:
18
22
  // https://codemirror.net/try/?c=aW1wb3J0IHsgYmFzaWNTZXR1cCwgRWRpdG9yVmlldyB9IGZyb20gImNvZGVtaXJyb3IiCmltcG9ydCB7IG1hcmtkb3duIH0gZnJvbSAiQGNvZGVtaXJyb3IvbGFuZy1tYXJrZG93biIKaW1wb3J0IHsgc3ludGF4VHJlZSB9IGZyb20gIkBjb2RlbWlycm9yL2xhbmd1YWdlIjsKaW1wb3J0IHsgRWRpdG9yU3RhdGUsIFJhbmdlU2V0QnVpbGRlciwgU3RhdGVGaWVsZCB9IGZyb20gIkBjb2RlbWlycm9yL3N0YXRlIjsKaW1wb3J0IHsgRGVjb3JhdGlvbiwgV2lkZ2V0VHlwZSB9IGZyb20gIkBjb2RlbWlycm9yL3ZpZXciOwoKY2xhc3MgTGluayBleHRlbmRzIFdpZGdldFR5cGUgewogIGNvbnN0cnVjdG9yKHRleHQsIHVybCkgewogICAgc3VwZXIoKQogICAgdGhpcy50ZXh0ID0gdGV4dAogICAgdGhpcy51cmwgPSB1cmwKICB9CiAgZXEob3RoZXIpIHsgcmV0dXJuIHRoaXMudGV4dCA9PSBvdGhlci50ZXh0ICYmIHRoaXMudXJsID09IG90aGVyLnVybCB9CiAgdG9ET00oKSB7CiAgICBsZXQgbGluayA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImEiKQogICAgbGluay50ZXh0Q29udGVudCA9IHRoaXMudGV4dAogICAgbGluay5ocmVmID0gdGhpcy51cmwKICAgIHJldHVybiBsaW5rOwogIH0KfQoKbGV0IGRlY29yYXRpb25zRmllbGQgPSBTdGF0ZUZpZWxkLmRlZmluZSh7CiAgY3JlYXRlKCkgewogICAgcmV0dXJuIERlY29yYXRpb24ubm9uZTsKICB9LAogIHVwZGF0ZShfLCB0cikgewogICAgY29uc3QgYnVpbGRlciA9IG5ldyBSYW5nZVNldEJ1aWxkZXIoKTsKICAgIGxldCBjdXJzb3IgPSB0ci5zdGF0ZS5zZWxlY3Rpb24ubWFpbi5oZWFkOwogICAgc3ludGF4VHJlZSh0ci5zdGF0ZSkuaXRlcmF0ZSh7CiAgICAgIGVudGVyOiAobm9kZSkgPT4gewogICAgICAgIGlmICgoY3Vyc29yIDwgbm9kZS5mcm9tIHx8IGN1cnNvciA+IG5vZGUudG8pICYmIG5vZGUubmFtZSA9PSAiTGluayIpIHsKICAgICAgICAgIGxldCBtYXJrcyA9IG5vZGUubm9kZS5nZXRDaGlsZHJlbigiTGlua01hcmsiKTsKICAgICAgICAgIGxldCB0ZXh0ID0gbWFya3MubGVuZ3RoID49IDIgPyB0ci5zdGF0ZS5zbGljZURvYyhtYXJrc1swXS50bywgbWFya3NbMV0uZnJvbSkgOiAiIgoKICAgICAgICAgIGxldCB1cmxOb2RlID0gbm9kZS5ub2RlLmdldENoaWxkKCJVUkwiKTsKICAgICAgICAgIGxldCB1cmwgPSB1cmxOb2RlID8gdHIuc3RhdGUuc2xpY2VEb2ModXJsTm9kZS5mcm9tLCB1cmxOb2RlLnRvKSA6ICIiCiAgICAgICAgICBidWlsZGVyLmFkZChub2RlLmZyb20sIG5vZGUudG8sIERlY29yYXRpb24ucmVwbGFjZSh7CiAgICAgICAgICAgIHdpZGdldDogbmV3IExpbmsodGV4dCwgdXJsKSwKICAgICAgICAgIH0pLAogICAgICAgICAgICAgICAgICAgICApOwogICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgfSwKICAgIH0pOwogICAgcmV0dXJuIGJ1aWxkZXIuZmluaXNoKCk7CiAgfSwKICBwcm92aWRlOiAoZikgPT4gRWRpdG9yVmlldy5kZWNvcmF0aW9ucy5mcm9tKGYpLAp9KTsKCmxldCB2aWV3ID0gbmV3IEVkaXRvclZpZXcoewogIGRvYzogIiMgSGVsbG8gV29ybGRcblxuW1Rlc3RdKGh0dHBzOi8vZXhhbXBsZS5jb20pXG5cblxuIiwKICBleHRlbnNpb25zOiBbZGVjb3JhdGlvbnNGaWVsZCwgbWFya2Rvd24oKV0sCiAgcGFyZW50OiBkb2N1bWVudC5ib2R5LAp9KTsKCnZpZXcuZm9jdXMoKTs=
19
23
  // https://discuss.codemirror.net/t/link-widget-is-clicked-only-if-editor-is-out-of-focus/7433
20
24
 
25
+ // TODO(burdon): Callback to dynamically lookup title of linked pages.
21
26
  export type LinkOptions = {
22
27
  link?: boolean;
28
+ onHover?: (el: Element, url: string) => void;
23
29
  onRender?: (el: Element, url: string) => void;
24
30
  };
25
31
 
@@ -28,27 +34,72 @@ export type LinkOptions = {
28
34
  * https://codemirror.net/docs/ref/#state.StateField
29
35
  */
30
36
  export const link = (options: LinkOptions = {}): Extension => {
31
- return StateField.define<RangeSet<any>>({
32
- create: (state) => update(state, options),
33
- update: (_: RangeSet<any>, tr: Transaction) => update(tr.state, options),
34
- provide: (field) => EditorView.decorations.from(field),
35
- });
37
+ const extensions: Extension[] = [
38
+ ViewPlugin.fromClass(
39
+ class {
40
+ decorations: DecorationSet;
41
+ constructor(view: EditorView) {
42
+ this.decorations = buildDecorations(view, options);
43
+ }
44
+
45
+ update(update: ViewUpdate) {
46
+ if (update.docChanged || update.viewportChanged || update.selectionSet) {
47
+ this.decorations = buildDecorations(update.view, options);
48
+ }
49
+ }
50
+ },
51
+ {
52
+ decorations: (value) => value.decorations,
53
+ },
54
+ ),
55
+ ];
56
+
57
+ if (options.onHover) {
58
+ extensions.push(
59
+ // https://codemirror.net/examples/tooltip
60
+ // https://codemirror.net/docs/ref/#view.hoverTooltip
61
+ // https://github.com/codemirror/view/blob/main/src/tooltip.ts
62
+ hoverTooltip((view, pos, side) => {
63
+ const syntax = syntaxTree(view.state).resolveInner(pos, side);
64
+ let link = null;
65
+ for (let i = 0, node: SyntaxNode | null = syntax; !link && node && i < 5; node = node.parent, i++) {
66
+ link = node.name === 'Link' ? node : null;
67
+ }
68
+
69
+ const url = link && link.getChild('URL');
70
+ if (!url || !link) {
71
+ return null;
72
+ }
73
+
74
+ const urlText = view.state.sliceDoc(url.from, url.to);
75
+ return {
76
+ pos: link.from,
77
+ end: link.to,
78
+ above: true,
79
+ create: () => {
80
+ const el = document.createElement('div');
81
+ el.className = tooltipContent({}, 'pli-2 plb-1');
82
+ options.onHover!(el, urlText);
83
+ return { dom: el, offset: { x: 0, y: 4 } };
84
+ },
85
+ };
86
+ }),
87
+ );
88
+ }
89
+
90
+ return extensions;
36
91
  };
37
92
 
38
93
  class LinkButton extends WidgetType {
39
- constructor(
40
- private readonly _pos: number,
41
- private readonly _url: string,
42
- private readonly _onAttach: LinkOptions['onRender'],
43
- ) {
94
+ constructor(private readonly _url: string, private readonly _onAttach: LinkOptions['onRender']) {
44
95
  super();
45
96
  }
46
97
 
47
- override eq(other: LinkButton) {
48
- return this._pos === other._pos && this._url === other._url;
98
+ override eq(other: this) {
99
+ return this._url === other._url;
49
100
  }
50
101
 
51
- toDOM(view: EditorView) {
102
+ override toDOM(view: EditorView) {
52
103
  const el = document.createElement('span');
53
104
  this._onAttach!(el, this._url);
54
105
  return el;
@@ -56,15 +107,15 @@ class LinkButton extends WidgetType {
56
107
  }
57
108
 
58
109
  class LinkText extends WidgetType {
59
- constructor(private readonly _pos: number, private readonly _text: string, private readonly _url?: string) {
110
+ constructor(private readonly _text: string, private readonly _url?: string) {
60
111
  super();
61
112
  }
62
113
 
63
- override eq(other: LinkText) {
64
- return this._pos === other._pos && this._url === other._url;
114
+ override eq(other: this) {
115
+ return this._url === other._url;
65
116
  }
66
117
 
67
- toDOM(view: EditorView) {
118
+ override toDOM(view: EditorView) {
68
119
  const link = document.createElement('a');
69
120
  link.setAttribute('class', 'cm-link');
70
121
  link.textContent = this._text;
@@ -76,7 +127,7 @@ class LinkText extends WidgetType {
76
127
  link.onclick = () => {
77
128
  view.dispatch({
78
129
  selection: {
79
- anchor: this._pos,
130
+ anchor: view.posAtDOM(link),
80
131
  },
81
132
  });
82
133
  };
@@ -91,46 +142,52 @@ class LinkText extends WidgetType {
91
142
  * possibly overlapping ranges in such a way that they can efficiently be mapped though document changes.
92
143
  * https://codemirror.net/docs/ref/#state
93
144
  */
94
- const update = (state: EditorState, options: LinkOptions) => {
95
- const builder = new RangeSetBuilder();
145
+ const buildDecorations = (view: EditorView, options: LinkOptions): DecorationSet => {
146
+ const builder = new RangeSetBuilder<Decoration>();
147
+ const { state } = view;
96
148
  const cursor = state.selection.main.head;
97
149
 
98
- syntaxTree(state).iterate({
99
- enter: (node) => {
100
- // Check if cursor is inside text.
101
- if (node.name === 'Link') {
102
- const marks = node.node.getChildren('LinkMark');
103
- const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : '';
104
- const urlNode = node.node.getChild('URL');
105
- const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : '';
106
- if (!url) {
107
- return false;
108
- }
109
-
110
- if (state.readOnly || cursor < node.from || cursor > node.to) {
111
- builder.add(
112
- node.from,
113
- node.to,
114
- Decoration.replace({
115
- widget: new LinkText(node.from, text, options.link ? url : undefined),
116
- }),
117
- );
118
- }
150
+ for (const { from, to } of view.visibleRanges) {
151
+ syntaxTree(state).iterate({
152
+ enter: (node) => {
153
+ // Check if cursor is inside text.
154
+ if (node.name === 'Link') {
155
+ const marks = node.node.getChildren('LinkMark');
156
+ const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : '';
157
+ const urlNode = node.node.getChild('URL');
158
+ const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : '';
159
+ if (!url) {
160
+ return false;
161
+ }
162
+
163
+ // TODO(burdon): Don't expand if moving cursor up/down.
164
+ if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
165
+ builder.add(
166
+ node.from,
167
+ node.to,
168
+ Decoration.replace({
169
+ widget: new LinkText(text, options.link ? url : undefined),
170
+ }),
171
+ );
172
+ }
173
+
174
+ if (options.onRender) {
175
+ builder.add(
176
+ node.to,
177
+ node.to,
178
+ Decoration.replace({
179
+ widget: new LinkButton(url, options.onRender),
180
+ }),
181
+ );
182
+ }
119
183
 
120
- if (options.onRender) {
121
- builder.add(
122
- node.to,
123
- node.to,
124
- Decoration.replace({
125
- widget: new LinkButton(node.from, url, options.onRender),
126
- }),
127
- );
184
+ return false;
128
185
  }
129
-
130
- return false;
131
- }
132
- },
133
- });
186
+ },
187
+ from,
188
+ to,
189
+ });
190
+ }
134
191
 
135
192
  return builder.finish();
136
193
  };
@@ -81,7 +81,6 @@ const update = (state: EditorState, options: TableOptions) => {
81
81
  table.from,
82
82
  table.to,
83
83
  Decoration.replace({
84
- block: true,
85
84
  widget: new TableWidget(table),
86
85
  }),
87
86
  );
@@ -98,11 +97,14 @@ class TableWidget extends WidgetType {
98
97
  super();
99
98
  }
100
99
 
101
- override eq(other: WidgetType) {
102
- return this._table.from === (other as any as TableWidget)?._table?.from;
100
+ override eq(other: this) {
101
+ return (
102
+ this._table.header?.join() === other._table.header?.join() &&
103
+ this._table.rows?.join() === other._table.rows?.join()
104
+ );
103
105
  }
104
106
 
105
- toDOM(view: EditorView) {
107
+ override toDOM(view: EditorView) {
106
108
  const table = document.createElement('table');
107
109
 
108
110
  {
@@ -129,4 +131,8 @@ class TableWidget extends WidgetType {
129
131
 
130
132
  return table;
131
133
  }
134
+
135
+ override ignoreEvent(e: Event) {
136
+ return !/^mouse/.test(e.type);
137
+ }
132
138
  }
@@ -0,0 +1,110 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { syntaxTree } from '@codemirror/language';
6
+ import { RangeSetBuilder } from '@codemirror/state';
7
+ import { EditorView, Decoration, WidgetType, ViewPlugin, type DecorationSet, type ViewUpdate } from '@codemirror/view';
8
+
9
+ import { getToken } from '../../styles';
10
+
11
+ // TODO(burdon): Reconcile with theme.
12
+ const styles = EditorView.baseTheme({
13
+ '& .cm-task': {
14
+ color: getToken('extend.colors.blue.500'),
15
+ },
16
+ '& .cm-task-checkbox': {
17
+ marginLeft: '4px',
18
+ marginRight: '4px',
19
+ },
20
+ });
21
+
22
+ class CheckboxWidget extends WidgetType {
23
+ constructor(private _checked: boolean) {
24
+ super();
25
+ }
26
+
27
+ override eq(other: this) {
28
+ return this._checked === other._checked;
29
+ }
30
+
31
+ override toDOM(view: EditorView) {
32
+ const input = document.createElement('input');
33
+ input.className = 'cm-task-checkbox';
34
+ input.type = 'checkbox';
35
+ input.checked = this._checked;
36
+ if (view.state.readOnly) {
37
+ input.setAttribute('disabled', 'true');
38
+ } else {
39
+ input.onmousedown = (event: Event) => {
40
+ const pos = view.posAtDOM(input);
41
+ const text = view.state.sliceDoc(pos, pos + 3);
42
+ if (text === (this._checked ? '[x]' : '[ ]')) {
43
+ view.dispatch({
44
+ changes: { from: pos + 1, to: pos + 2, insert: this._checked ? ' ' : 'x' },
45
+ });
46
+ event.preventDefault();
47
+ }
48
+ };
49
+ }
50
+
51
+ return input;
52
+ }
53
+
54
+ override ignoreEvent() {
55
+ return false;
56
+ }
57
+ }
58
+
59
+ const checkedDecoration = Decoration.replace({ widget: new CheckboxWidget(true) });
60
+ const uncheckedDecoration = Decoration.replace({ widget: new CheckboxWidget(false) });
61
+
62
+ const buildDecorations = (view: EditorView): DecorationSet => {
63
+ const builder = new RangeSetBuilder<Decoration>();
64
+ const { state } = view;
65
+ const cursor = state.selection.main.head;
66
+
67
+ for (const { from, to } of view.visibleRanges) {
68
+ syntaxTree(state).iterate({
69
+ enter: (node) => {
70
+ if (node.name === 'TaskMarker') {
71
+ // Check if cursor is inside text.
72
+ if (cursor < node.from || cursor > node.to) {
73
+ const checked = state.doc.sliceString(node.from + 1, node.to - 1) === 'x';
74
+ builder.add(node.from - 2, node.from - 1, Decoration.mark({ class: 'cm-task' }));
75
+ builder.add(node.from, node.to, checked ? checkedDecoration : uncheckedDecoration);
76
+ }
77
+ }
78
+ },
79
+ from,
80
+ to,
81
+ });
82
+ }
83
+
84
+ return builder.finish();
85
+ };
86
+
87
+ export type TasklistOptions = {};
88
+
89
+ export const tasklist = (options: TasklistOptions = {}) => {
90
+ return [
91
+ ViewPlugin.fromClass(
92
+ class {
93
+ decorations: DecorationSet;
94
+ constructor(view: EditorView) {
95
+ this.decorations = buildDecorations(view);
96
+ }
97
+
98
+ update(update: ViewUpdate) {
99
+ if (update.docChanged || update.viewportChanged || update.selectionSet) {
100
+ this.decorations = buildDecorations(update.view);
101
+ }
102
+ }
103
+ },
104
+ {
105
+ decorations: (value) => value.decorations,
106
+ },
107
+ ),
108
+ styles,
109
+ ];
110
+ };
@@ -9,18 +9,25 @@ export type MentionOptions = {
9
9
  onSearch: (text: string) => string[];
10
10
  };
11
11
 
12
+ // TODO(burdon): Can only have a single autocompletion. Merge configuration with autocomplete.
12
13
  export const mention = ({ onSearch }: MentionOptions): Extension => {
13
14
  return autocompletion({
15
+ // TODO(burdon): Not working.
16
+ activateOnTyping: true,
17
+ // activateOnTypingDelay: 100,
18
+ // selectOnOpen: true,
14
19
  closeOnBlur: false, // For debugging.
20
+ // defaultKeymap: false,
21
+ icons: false,
15
22
  override: [
16
23
  (context: CompletionContext): CompletionResult | null => {
24
+ console.log(context);
25
+
17
26
  const match = context.matchBefore(/@(\w+)?/);
18
27
  if (!match || (match.from === match.to && !context.explicit)) {
19
28
  return null;
20
29
  }
21
30
 
22
- console.log(match);
23
-
24
31
  return {
25
32
  from: match.from,
26
33
  options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({ label: `@${value}` })),
@@ -0,0 +1,12 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type Extension } from '@codemirror/state';
6
+
7
+ export type OutlinerOptions = {};
8
+
9
+ // TODO(burdon): Experimental component: multiple hierarchical draggable editor sections.
10
+ export const outliner = (options: OutlinerOptions = {}): Extension => {
11
+ return [];
12
+ };
@@ -19,7 +19,7 @@ const defaultItems = ['hello world!', 'this is a test.', 'this is [DXOS](https:/
19
19
  */
20
20
  export const typewriter = ({ delay = 75, items = defaultItems }: DemoOptions = {}): Extension => {
21
21
  let t: any;
22
- let idx = 0;
22
+ let idx = 0; // TODO(burdon): Make global.
23
23
 
24
24
  return [
25
25
  keymap.of([
@@ -0,0 +1,124 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { DeferredTask, Event, sleep } from '@dxos/async';
6
+ import { Context } from '@dxos/context';
7
+ import { invariant } from '@dxos/invariant';
8
+ import { log } from '@dxos/log';
9
+ import { type Space } from '@dxos/react-client/echo';
10
+ import { type GossipMessage } from '@dxos/react-client/mesh';
11
+
12
+ import { type AwarenessInfo, type AwarenessPosition, type AwarenessProvider, type AwarenessState } from '../extensions';
13
+
14
+ type ProtocolMessage =
15
+ | {
16
+ kind: 'query';
17
+ }
18
+ | {
19
+ kind: 'post';
20
+ state: AwarenessState;
21
+ };
22
+
23
+ const DEBOUNCE_INTERVAL = 100; // ms
24
+
25
+ export type AwarenessProviderParams = {
26
+ space: Space;
27
+ channel: string;
28
+ peerId: string;
29
+ info: AwarenessInfo;
30
+ };
31
+
32
+ /**
33
+ * Receives and broadcasts profile and cursor position.
34
+ */
35
+ export class SpaceAwarenessProvider implements AwarenessProvider {
36
+ private readonly _remoteStates = new Map<string, AwarenessState>();
37
+
38
+ private readonly _space: Space;
39
+ private readonly _channel: string;
40
+ private readonly _peerId: string;
41
+ private readonly _info: AwarenessInfo;
42
+
43
+ private _ctx?: Context;
44
+ private _postTask?: DeferredTask;
45
+ private _localState?: AwarenessState;
46
+
47
+ public readonly remoteStateChange = new Event<void>();
48
+
49
+ constructor(params: AwarenessProviderParams) {
50
+ this._space = params.space;
51
+ this._channel = params.channel;
52
+ this._peerId = params.peerId;
53
+ this._info = params.info;
54
+ }
55
+
56
+ open() {
57
+ this._ctx = new Context();
58
+ this._postTask = new DeferredTask(this._ctx, async () => {
59
+ if (this._localState) {
60
+ await this._space.postMessage(this._channel, {
61
+ kind: 'post',
62
+ state: this._localState,
63
+ } satisfies ProtocolMessage);
64
+
65
+ await sleep(DEBOUNCE_INTERVAL);
66
+ }
67
+ });
68
+
69
+ this._ctx.onDispose(
70
+ this._space.listen(this._channel, (message: GossipMessage) => {
71
+ switch (message.payload.kind) {
72
+ case 'query': {
73
+ this._handleQueryMessage();
74
+ break;
75
+ }
76
+ case 'post': {
77
+ this._handlePostMessage(message.payload);
78
+ break;
79
+ }
80
+ }
81
+ }),
82
+ );
83
+
84
+ void this._space
85
+ .postMessage(this._channel, {
86
+ kind: 'query',
87
+ } satisfies ProtocolMessage)
88
+ .catch((err) => {
89
+ log.debug('failed to query awareness', { err });
90
+ });
91
+ }
92
+
93
+ close() {
94
+ void this._ctx?.dispose();
95
+ this._ctx = undefined;
96
+ this._postTask = undefined;
97
+ }
98
+
99
+ getRemoteStates(): AwarenessState[] {
100
+ return Array.from(this._remoteStates.values());
101
+ }
102
+
103
+ update(position: AwarenessPosition | undefined): void {
104
+ invariant(this._postTask);
105
+ this._localState = {
106
+ peerId: this._peerId,
107
+ position,
108
+ info: this._info,
109
+ };
110
+
111
+ this._postTask.schedule();
112
+ }
113
+
114
+ private _handleQueryMessage() {
115
+ invariant(this._postTask);
116
+ this._postTask.schedule();
117
+ }
118
+
119
+ private _handlePostMessage(message: ProtocolMessage) {
120
+ invariant(message.kind === 'post');
121
+ this._remoteStates.set(message.state.peerId, message.state);
122
+ this.remoteStateChange.emit();
123
+ }
124
+ }
@@ -0,0 +1,42 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type Extension, StateField } from '@codemirror/state';
6
+
7
+ import { type DocAccessor } from '@dxos/echo-schema';
8
+
9
+ // Runtime data structure.
10
+ export type Range = {
11
+ from: number;
12
+ to: number;
13
+ };
14
+
15
+ // Persistent data structure.
16
+ export type Comment = {
17
+ id: string;
18
+ cursor?: string;
19
+ };
20
+
21
+ export type EditorModel = {
22
+ id: string;
23
+ text: () => string;
24
+ extension?: Extension;
25
+
26
+ // TODO(burdon): Remove.
27
+ content: string | DocAccessor;
28
+
29
+ // TODO(burdon): Remove.
30
+ peer?: {
31
+ id: string;
32
+ name?: string;
33
+ };
34
+ };
35
+
36
+ /**
37
+ * State field makes the model available to other extensions.
38
+ */
39
+ export const modelState = StateField.define<EditorModel | undefined>({
40
+ create: () => undefined,
41
+ update: (model) => model,
42
+ });
@@ -2,5 +2,10 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ export * from './awareness-provider';
6
+ export * from './defs';
7
+
8
+ export * from './useActionHandler';
9
+ export * from './useEditorView';
10
+ export * from './useTextEditor';
5
11
  export * from './useTextModel';
6
- export * from './yjs';
@@ -0,0 +1,62 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import type { EditorView } from '@codemirror/view';
6
+
7
+ import type { ToolbarProps } from '../components';
8
+ import {
9
+ createComment,
10
+ insertCodeblock,
11
+ insertTable,
12
+ setHeading,
13
+ toggleBold,
14
+ toggleItalic,
15
+ toggleList,
16
+ toggleStrikethrough,
17
+ } from '../extensions';
18
+
19
+ export const useActionHandler = (view?: EditorView | null): ToolbarProps['onAction'] => {
20
+ return (action) => {
21
+ if (!view) {
22
+ return;
23
+ }
24
+
25
+ switch (action.type) {
26
+ case 'heading':
27
+ setHeading(parseInt(action.data))(view);
28
+ break;
29
+
30
+ case 'bold':
31
+ toggleBold(view);
32
+ break;
33
+ case 'italic':
34
+ toggleItalic(view);
35
+ break;
36
+ case 'strikethrough':
37
+ toggleStrikethrough(view);
38
+ break;
39
+
40
+ case 'list':
41
+ toggleList(view);
42
+ break;
43
+
44
+ case 'codeblock':
45
+ insertCodeblock(view);
46
+ break;
47
+ case 'table':
48
+ insertTable(view);
49
+ break;
50
+
51
+ case 'comment':
52
+ createComment(view);
53
+ break;
54
+ }
55
+
56
+ // TODO(burdon): Hack otherwise remains on heading selector.
57
+ setTimeout(() => {
58
+ view.focus();
59
+ view.dispatch({ selection: view.state.selection });
60
+ });
61
+ };
62
+ };