@dxos/react-ui-editor 0.3.10 → 0.3.11-main.3f845e1

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 (82) hide show
  1. package/dist/lib/browser/index.mjs +1928 -1717
  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 +16 -9
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +2 -1
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/extensions/code.d.ts.map +1 -1
  9. package/dist/types/src/components/TextEditor/extensions/comments.d.ts +38 -8
  10. package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +1 -1
  11. package/dist/types/src/components/TextEditor/extensions/hr.d.ts +2 -0
  12. package/dist/types/src/components/TextEditor/extensions/hr.d.ts.map +1 -0
  13. package/dist/types/src/components/TextEditor/extensions/index.d.ts +3 -2
  14. package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -1
  15. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts +0 -1
  16. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +1 -1
  17. package/dist/types/src/components/TextEditor/extensions/mermaid.d.ts +3 -0
  18. package/dist/types/src/components/TextEditor/extensions/mermaid.d.ts.map +1 -0
  19. package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +1 -1
  20. package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts.map +1 -1
  21. package/dist/types/src/components/TextEditor/extensions/{demo.d.ts → typewriter.d.ts} +2 -3
  22. package/dist/types/src/components/TextEditor/extensions/typewriter.d.ts.map +1 -0
  23. package/dist/types/src/components/TextEditor/extensions/util.d.ts +5 -0
  24. package/dist/types/src/components/TextEditor/extensions/util.d.ts.map +1 -0
  25. package/dist/types/src/components/TextEditor/themes/default.d.ts +5 -13
  26. package/dist/types/src/components/TextEditor/themes/default.d.ts.map +1 -1
  27. package/dist/types/src/hooks/automerge/automerge.stories.d.ts +2 -3
  28. package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +1 -1
  29. package/dist/types/src/hooks/automerge/plugin.d.ts.map +1 -1
  30. package/dist/types/src/hooks/automerge/{PatchSemaphore.d.ts → semaphore.d.ts} +1 -1
  31. package/dist/types/src/hooks/automerge/semaphore.d.ts.map +1 -0
  32. package/dist/types/src/hooks/automerge/update-automerge.d.ts +6 -0
  33. package/dist/types/src/hooks/automerge/update-automerge.d.ts.map +1 -0
  34. package/dist/types/src/hooks/automerge/update-codemirror.d.ts +5 -0
  35. package/dist/types/src/hooks/automerge/update-codemirror.d.ts.map +1 -0
  36. package/dist/types/src/hooks/useTextModel.d.ts +14 -6
  37. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  38. package/dist/types/src/hooks/yjs/yjs.test.d.ts +2 -0
  39. package/dist/types/src/hooks/yjs/yjs.test.d.ts.map +1 -0
  40. package/dist/types/src/index.d.ts +1 -0
  41. package/dist/types/src/index.d.ts.map +1 -1
  42. package/dist/types/src/testing/replicator.d.ts.map +1 -1
  43. package/package.json +24 -20
  44. package/src/components/TextEditor/MarkdownEditor.stories.tsx +128 -67
  45. package/src/components/TextEditor/TextEditor.stories.tsx +1 -1
  46. package/src/components/TextEditor/TextEditor.tsx +26 -22
  47. package/src/components/TextEditor/extensions/code.ts +12 -12
  48. package/src/components/TextEditor/extensions/comments.ts +307 -92
  49. package/src/components/TextEditor/extensions/hr.ts +74 -0
  50. package/src/components/TextEditor/extensions/image.ts +3 -1
  51. package/src/components/TextEditor/extensions/index.ts +3 -2
  52. package/src/components/TextEditor/extensions/markdown/highlight.ts +5 -8
  53. package/src/components/TextEditor/extensions/mermaid.ts +11 -0
  54. package/src/components/TextEditor/extensions/table.ts +14 -1
  55. package/src/components/TextEditor/extensions/{tooltip.tsx → tooltip.ts} +1 -1
  56. package/src/components/TextEditor/extensions/{demo.ts → typewriter.ts} +1 -2
  57. package/src/components/TextEditor/extensions/util.ts +18 -0
  58. package/src/components/TextEditor/themes/default.ts +61 -38
  59. package/src/hooks/automerge/automerge.stories.tsx +2 -3
  60. package/src/hooks/automerge/plugin.ts +1 -1
  61. package/src/hooks/automerge/{PatchSemaphore.ts → semaphore.ts} +4 -3
  62. package/src/hooks/automerge/{translation/codemirror-to-automerge.ts → update-automerge.ts} +3 -3
  63. package/src/hooks/automerge/{translation/automerge-to-codemirror.ts → update-codemirror.ts} +2 -7
  64. package/src/hooks/useTextModel.ts +59 -22
  65. package/src/hooks/yjs/yjs.test.ts +59 -0
  66. package/src/index.ts +1 -0
  67. package/src/testing/replicator.ts +0 -1
  68. package/dist/types/src/components/TextEditor/extensions/demo.d.ts.map +0 -1
  69. package/dist/types/src/components/TextEditor/extensions/experimental.d.ts +0 -10
  70. package/dist/types/src/components/TextEditor/extensions/experimental.d.ts.map +0 -1
  71. package/dist/types/src/components/TextEditor/extensions/highlight.d.ts +0 -24
  72. package/dist/types/src/components/TextEditor/extensions/highlight.d.ts.map +0 -1
  73. package/dist/types/src/hooks/automerge/PatchSemaphore.d.ts.map +0 -1
  74. package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts +0 -5
  75. package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts.map +0 -1
  76. package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts +0 -6
  77. package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts.map +0 -1
  78. package/dist/types/src/hooks/automerge/translation/index.d.ts +0 -3
  79. package/dist/types/src/hooks/automerge/translation/index.d.ts.map +0 -1
  80. package/src/components/TextEditor/extensions/experimental.tsx +0 -114
  81. package/src/components/TextEditor/extensions/highlight.ts +0 -128
  82. package/src/hooks/automerge/translation/index.ts +0 -6
@@ -1,19 +1,27 @@
1
- import { type Extension } from '@codemirror/state';
1
+ import { type Extension, StateField } from '@codemirror/state';
2
2
  import type * as awarenessProtocol from 'y-protocols/awareness';
3
3
  import { type DocAccessor, type Space, type TextObject } from '@dxos/react-client/echo';
4
4
  import { type Identity } from '@dxos/react-client/halo';
5
5
  import type { YText, YXmlFragment } from '@dxos/text-model';
6
6
  type Awareness = awarenessProtocol.Awareness;
7
- export type DocumentRange = {
8
- id: string;
7
+ /**
8
+ * State field makes the model available to other extensions.
9
+ */
10
+ export declare const modelState: StateField<EditorModel | undefined>;
11
+ export type Range = {
9
12
  from: number;
10
13
  to: number;
11
14
  };
15
+ export type CommentRange = {
16
+ id: string;
17
+ cursor: string;
18
+ };
12
19
  export type EditorModel = {
13
20
  id: string;
14
21
  content: string | YText | YXmlFragment | DocAccessor;
15
22
  text: () => string;
16
- ranges: DocumentRange[];
23
+ getCursorFromRange?: (value: Range) => string;
24
+ getRangeFromCursor?: (cursor: string) => Range;
17
25
  extension?: Extension;
18
26
  awareness?: Awareness;
19
27
  peer?: {
@@ -21,11 +29,11 @@ export type EditorModel = {
21
29
  name?: string;
22
30
  };
23
31
  };
24
- export type UseTextModelOptions = {
32
+ export type UseTextModelProps = {
25
33
  identity?: Identity | null;
26
34
  space?: Space;
27
35
  text?: TextObject;
28
36
  };
29
- export declare const useTextModel: ({ identity, space, text }: UseTextModelOptions) => EditorModel | undefined;
37
+ export declare const useTextModel: (props: UseTextModelProps) => EditorModel | undefined;
30
38
  export {};
31
39
  //# sourceMappingURL=useTextModel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useTextModel.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useTextModel.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAInD,OAAO,KAAK,KAAK,iBAAiB,MAAM,uBAAuB,CAAC;AAGhE,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,KAAK,EACV,KAAK,UAAU,EAIhB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAM5D,KAAK,SAAS,GAAG,iBAAiB,CAAC,SAAS,CAAC;AAE7C,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAGF,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IAEX,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG,YAAY,GAAG,WAAW,CAAC;IACrD,IAAI,EAAE,MAAM,MAAM,CAAC;IACnB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,IAAI,CAAC,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH,CAAC;AAGF,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB,CAAC;AAIF,eAAO,MAAM,YAAY,8BAA+B,mBAAmB,KAAG,WAAW,GAAG,SAM3F,CAAC"}
1
+ {"version":3,"file":"useTextModel.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useTextModel.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAI/D,OAAO,KAAK,KAAK,iBAAiB,MAAM,uBAAuB,CAAC;AAIhE,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,KAAK,EACV,KAAK,UAAU,EAIhB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAO5D,KAAK,SAAS,GAAG,iBAAiB,CAAC,SAAS,CAAC;AAE7C;;GAEG;AAEH,eAAO,MAAM,UAAU,qCAGrB,CAAC;AAEH,MAAM,MAAM,KAAK,GAAG;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IAEX,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAGF,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IAEX,OAAO,EAAE,MAAM,GAAG,KAAK,GAAG,YAAY,GAAG,WAAW,CAAC;IACrD,IAAI,EAAE,MAAM,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC;IAC9C,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,KAAK,CAAC;IAC/C,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,IAAI,CAAC,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH,CAAC;AAGF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB,CAAC;AAIF,eAAO,MAAM,YAAY,UAAW,iBAAiB,KAAG,WAAW,GAAG,SAKrE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=yjs.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yjs.test.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/yjs/yjs.test.ts"],"names":[],"mappings":""}
@@ -1,5 +1,6 @@
1
1
  export { TextKind } from '@dxos/protocols/proto/dxos/echo/model/text';
2
2
  export { Doc, YText, YXmlFragment } from '@dxos/text-model';
3
+ export { type Extension } from '@codemirror/state';
3
4
  export * from './components';
4
5
  export * from './hooks';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,4CAA4C,CAAC;AACtE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE5D,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,4CAA4C,CAAC;AACtE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"replicator.d.ts","sourceRoot":"","sources":["../../../../src/testing/replicator.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,KAAK,iBAAiB,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,OAAO,EAAE,QAAQ,EAAE,MAAM,4CAA4C,CAAC;AAEtE,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzD,KAAK,SAAS,GAAG,iBAAiB,CAAC,SAAS,CAAC;AAM7C;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,UAAU,CAAC,GAAG,CAAC;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAM;gBAEf,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE;QAAE,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QAAC,GAAG,EAAE,GAAG,CAAC;QAAC,SAAS,CAAC,EAAE,SAAS,CAAA;KAAE;IA+BtG,IAAI,SAAS,IAAI,SAAS,CAEzB;IAED,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,YAAY;IA0CpB,OAAO,CAAC,mBAAmB;CAG5B;AAED;;GAEG;AACH,qBAAa,UAAU;IAIT,OAAO,CAAC,QAAQ,CAAC,KAAK;IAHlC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2B;IACnD,OAAO,CAAC,MAAM,CAAqB;gBAEN,KAAK,EAAE,QAAQ;IAE5C,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE;IAI7B,UAAU,OAAQ,MAAM,QAAO,GAAG,KAAe,WAAW,CAqB1D;CACH;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,UAAU,CAAC;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,GAAG,CAAC;CACX,CAAC;AAEF,eAAO,MAAM,WAAW,4BAA6B,kBAAkB,KAAG,WAEzE,CAAC"}
1
+ {"version":3,"file":"replicator.d.ts","sourceRoot":"","sources":["../../../../src/testing/replicator.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,KAAK,iBAAiB,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,OAAO,EAAE,QAAQ,EAAE,MAAM,4CAA4C,CAAC;AAEtE,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzD,KAAK,SAAS,GAAG,iBAAiB,CAAC,SAAS,CAAC;AAM7C;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,UAAU,CAAC,GAAG,CAAC;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAM;gBAEf,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE;QAAE,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QAAC,GAAG,EAAE,GAAG,CAAC;QAAC,SAAS,CAAC,EAAE,SAAS,CAAA;KAAE;IA+BtG,IAAI,SAAS,IAAI,SAAS,CAEzB;IAED,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,YAAY;IA0CpB,OAAO,CAAC,mBAAmB;CAG5B;AAED;;GAEG;AACH,qBAAa,UAAU;IAIT,OAAO,CAAC,QAAQ,CAAC,KAAK;IAHlC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2B;IACnD,OAAO,CAAC,MAAM,CAAqB;gBAEN,KAAK,EAAE,QAAQ;IAE5C,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE;IAI7B,UAAU,OAAQ,MAAM,QAAO,GAAG,KAAe,WAAW,CAoB1D;CACH;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,UAAU,CAAC;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,GAAG,CAAC;CACX,CAAC;AAEF,eAAO,MAAM,WAAW,4BAA6B,kBAAkB,KAAG,WAEzE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-editor",
3
- "version": "0.3.10",
3
+ "version": "0.3.11-main.3f845e1",
4
4
  "description": "Document editing experience within a DXOS shell.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -32,24 +32,25 @@
32
32
  "lib0": "^0.2.65",
33
33
  "lodash.defaultsdeep": "^4.6.1",
34
34
  "lodash.get": "^4.4.2",
35
+ "lodash.pick": "^4.4.0",
35
36
  "lodash.sortby": "^4.7.0",
36
37
  "style-mod": "^4.1.0",
37
38
  "y-codemirror.next": "^0.3.2",
38
39
  "y-protocols": "^1.0.5",
39
40
  "yjs": "^13.6.10",
40
- "@dxos/automerge": "0.3.10",
41
- "@dxos/async": "0.3.10",
42
- "@dxos/debug": "0.3.10",
43
- "@dxos/display-name": "0.3.10",
44
- "@dxos/invariant": "0.3.10",
45
- "@dxos/echo-schema": "0.3.10",
46
- "@dxos/protocols": "0.3.10",
47
- "@dxos/react-async": "0.3.10",
48
- "@dxos/log": "0.3.10",
49
- "@dxos/react-ui": "0.3.10",
50
- "@dxos/react-ui-theme": "0.3.10",
51
- "@dxos/text-model": "0.3.10",
52
- "@dxos/util": "0.3.10"
41
+ "@dxos/async": "0.3.11-main.3f845e1",
42
+ "@dxos/debug": "0.3.11-main.3f845e1",
43
+ "@dxos/automerge": "0.3.11-main.3f845e1",
44
+ "@dxos/display-name": "0.3.11-main.3f845e1",
45
+ "@dxos/echo-schema": "0.3.11-main.3f845e1",
46
+ "@dxos/invariant": "0.3.11-main.3f845e1",
47
+ "@dxos/log": "0.3.11-main.3f845e1",
48
+ "@dxos/protocols": "0.3.11-main.3f845e1",
49
+ "@dxos/react-async": "0.3.11-main.3f845e1",
50
+ "@dxos/react-ui": "0.3.11-main.3f845e1",
51
+ "@dxos/react-ui-theme": "0.3.11-main.3f845e1",
52
+ "@dxos/util": "0.3.11-main.3f845e1",
53
+ "@dxos/text-model": "0.3.11-main.3f845e1"
53
54
  },
54
55
  "devDependencies": {
55
56
  "@automerge/automerge-repo-network-broadcastchannel": "^1.0.19",
@@ -58,22 +59,25 @@
58
59
  "@preact/signals-react": "^1.3.6",
59
60
  "@types/lodash.defaultsdeep": "^4.6.6",
60
61
  "@types/lodash.get": "^4.4.7",
62
+ "@types/lodash.pick": "^4.4.7",
61
63
  "@types/lodash.sortby": "^4.7.7",
62
64
  "@types/react": "^18.0.21",
63
65
  "@types/react-dom": "^18.0.6",
66
+ "deepsignal": "1.4.0-shallow.0",
64
67
  "react": "^18.2.0",
65
68
  "react-dom": "^18.2.0",
66
- "@dxos/config": "0.3.10",
67
- "@dxos/echo-typegen": "0.3.10",
68
- "@dxos/react-ui": "0.3.10",
69
- "@dxos/storybook-utils": "0.3.10",
70
- "@dxos/react-client": "0.3.10"
69
+ "@dxos/echo-signals": "0.3.11-main.3f845e1",
70
+ "@dxos/echo-typegen": "0.3.11-main.3f845e1",
71
+ "@dxos/config": "0.3.11-main.3f845e1",
72
+ "@dxos/react-client": "0.3.11-main.3f845e1",
73
+ "@dxos/storybook-utils": "0.3.11-main.3f845e1",
74
+ "@dxos/react-ui": "0.3.11-main.3f845e1"
71
75
  },
72
76
  "peerDependencies": {
73
77
  "@phosphor-icons/react": "^2.0.5",
74
78
  "react": "^18.2.0",
75
79
  "react-dom": "^18.2.0",
76
- "@dxos/react-client": "0.3.10"
80
+ "@dxos/react-client": "0.3.11-main.3f845e1"
77
81
  },
78
82
  "publishConfig": {
79
83
  "access": "public"
@@ -8,7 +8,7 @@ import { EditorView } from '@codemirror/view';
8
8
  import { faker } from '@faker-js/faker';
9
9
  import { ArrowSquareOut } from '@phosphor-icons/react';
10
10
  import defaultsDeep from 'lodash.defaultsdeep';
11
- import React, { StrictMode, useRef, useState } from 'react';
11
+ import React, { type FC, StrictMode, useRef, useState } from 'react';
12
12
  import { createRoot } from 'react-dom/client';
13
13
 
14
14
  import { TextObject } from '@dxos/echo-schema';
@@ -19,28 +19,32 @@ import { withTheme } from '@dxos/storybook-utils';
19
19
  import { MarkdownEditor, type TextEditorProps, type TextEditorRef } from './TextEditor';
20
20
  import {
21
21
  autocomplete,
22
+ blast,
23
+ code,
24
+ comments,
25
+ defaultOptions,
26
+ hr,
27
+ image,
22
28
  listener,
23
29
  link,
30
+ mention,
31
+ table,
24
32
  tasklist,
25
33
  tooltip,
34
+ typewriter,
35
+ type CommentsOptions,
26
36
  type TooltipOptions,
27
37
  type LinkOptions,
28
- comments,
29
- table,
30
- image,
31
- mention,
32
- blast,
33
- demo,
34
- defaultOptions,
35
- highlight,
36
- code,
38
+ mermaid,
37
39
  } from './extensions';
38
- import { useTextModel } from '../../hooks';
40
+ import { type CommentRange, useTextModel } from '../../hooks';
39
41
 
40
42
  // Extensions:
41
43
  // TODO(burdon): Table of contents.
42
44
  // TODO(burdon): Front-matter
43
45
 
46
+ faker.seed(101);
47
+
44
48
  const str = (...lines: string[]) => lines.join('\n');
45
49
 
46
50
  const num = () => faker.number.int({ min: 0, max: 9999 }).toLocaleString();
@@ -55,6 +59,7 @@ const text = {
55
59
  ' - [ ] state',
56
60
  ' - [ ] indent',
57
61
  ' - [x] style',
62
+ '',
58
63
  ),
59
64
 
60
65
  list: str(
@@ -63,6 +68,7 @@ const text = {
63
68
  '- new york',
64
69
  '- london',
65
70
  '- tokyo',
71
+ '',
66
72
  ),
67
73
 
68
74
  numbered: str(
@@ -87,8 +93,8 @@ const text = {
87
93
  '',
88
94
  ' return () => <div>Test</div>;',
89
95
  '};',
96
+ '```',
90
97
  '',
91
- '```'
92
98
  ),
93
99
 
94
100
  links: str(
@@ -103,11 +109,11 @@ const text = {
103
109
  table: str(
104
110
  '# Table',
105
111
  '',
106
- `| ${faker.lorem.word().padStart(8)} | ${faker.lorem.word().padStart(8)} | ${faker.lorem.word().padStart(8)} |`,
107
- '|----------|----------|----------|',
108
- `| ${num().padStart(8)} | ${num().padStart(8)} | ${num().padStart(8)} |`,
109
- `| ${num().padStart(8)} | ${num().padStart(8)} | ${num().padStart(8)} |`,
110
- `| ${num().padStart(8)} | ${num().padStart(8)} | ${num().padStart(8)} |`,
112
+ `| ${faker.lorem.word().padStart(12)} | ${faker.lorem.word().padStart(12)} | ${faker.lorem.word().padStart(12)} |`,
113
+ `|-${''.padStart(12, '-')}-|-${''.padStart(12, '-')}-|-${''.padStart(12, '-')}-|`,
114
+ `| ${num().padStart(12)} | ${num().padStart(12)} | ${num().padStart(12)} |`,
115
+ `| ${num().padStart(12)} | ${num().padStart(12)} | ${num().padStart(12)} |`,
116
+ `| ${num().padStart(12)} | ${num().padStart(12)} | ${num().padStart(12)} |`,
111
117
  '', // TODO(burdon): Possible GFM parsing bug if no newline?
112
118
  ),
113
119
 
@@ -119,6 +125,8 @@ const text = {
119
125
 
120
126
  paragraphs: str(...faker.helpers.multiple(() => [faker.lorem.paragraph(), ''], { count: 3 }).flat()),
121
127
 
128
+ mermaid: str('```mermaid', 'graph TD;', 'A-->B;', 'A-->C;', 'B-->D;', 'C-->D;', '```'),
129
+
122
130
  footer: str('', '', '', '', '')
123
131
  };
124
132
 
@@ -147,7 +155,6 @@ const document = str(
147
155
  text.table,
148
156
  '---',
149
157
  text.image,
150
- '',
151
158
  text.footer,
152
159
  );
153
160
 
@@ -162,7 +169,7 @@ const links = [
162
169
  const hover =
163
170
  'rounded-sm text-base text-primary-600 hover:text-primary-500 dark:text-primary-300 hover:dark:text-primary-200';
164
171
 
165
- const onHover: TooltipOptions['onHover'] = (el, url) => {
172
+ const onTooltipHover: TooltipOptions['onHover'] = (el, url) => {
166
173
  const web = new URL(url);
167
174
  createRoot(el).render(
168
175
  <StrictMode>
@@ -174,7 +181,28 @@ const onHover: TooltipOptions['onHover'] = (el, url) => {
174
181
  );
175
182
  };
176
183
 
177
- const onRender: LinkOptions['onRender'] = (el, url) => {
184
+ const Key: FC<{ char: string }> = ({ char }) => (
185
+ <span className='flex justify-center items-center w-[24px] h-[24px] rounded text-xs bg-neutral-200 text-black'>
186
+ {char}
187
+ </span>
188
+ );
189
+
190
+ const onCommentsHover: CommentsOptions['onHover'] = (el) => {
191
+ createRoot(el).render(
192
+ <StrictMode>
193
+ <div className='flex items-center gap-2 px-2 py-2 bg-neutral-700 text-white text-xs rounded'>
194
+ <div>Create comment</div>
195
+ {/* TODO(burdon): Unify shortcuts. */}
196
+ <div className='flex gap-1'>
197
+ <Key char='⌘' />
198
+ <Key char="'" />
199
+ </div>
200
+ </div>
201
+ </StrictMode>,
202
+ );
203
+ };
204
+
205
+ const onLinkRender: LinkOptions['onRender'] = (el, url) => {
178
206
  createRoot(el).render(
179
207
  <StrictMode>
180
208
  <a href={url} target='_blank' rel='noreferrer' className={hover}>
@@ -184,14 +212,14 @@ const onRender: LinkOptions['onRender'] = (el, url) => {
184
212
  );
185
213
  };
186
214
 
187
- // TODO(burdon): Pass in model.
188
- const Story = ({
189
- text,
190
- automerge,
191
- ...props
192
- }: { text?: string; automerge?: boolean } & Pick<TextEditorProps, 'readonly' | 'extensions' | 'slots'>) => {
215
+ type StoryProps = {
216
+ text?: string;
217
+ automerge?: boolean;
218
+ } & Pick<TextEditorProps, 'comments' | 'readonly' | 'extensions' | 'slots'>;
219
+
220
+ const Story = ({ text, automerge, ...props }: StoryProps) => {
193
221
  const ref = useRef<TextEditorRef>(null);
194
- const [item] = useState({ text: new TextObject(text, undefined, undefined, { useAutomergeBackend: automerge }) });
222
+ const [item] = useState({ text: new TextObject(text, undefined, undefined, { automerge }) });
195
223
  const model = useTextModel({ text: item.text });
196
224
  if (!model) {
197
225
  return null;
@@ -221,11 +249,12 @@ const extensions = [
221
249
  onSearch: (text) => links.filter(({ label }) => label.toLowerCase().includes(text.toLowerCase())),
222
250
  }),
223
251
  code(),
252
+ hr(),
224
253
  image(),
225
- link({ onRender }),
254
+ link({ onRender: onLinkRender }),
226
255
  table(),
227
256
  tasklist(),
228
- tooltip({ onHover }),
257
+ tooltip({ onHover: onTooltipHover }),
229
258
  ];
230
259
 
231
260
  export const Default = {
@@ -236,12 +265,26 @@ export const Readonly = {
236
265
  render: () => <Story text={document} extensions={extensions} readonly />,
237
266
  };
238
267
 
268
+ export const NoExtensions = {
269
+ render: () => <Story text={document} />,
270
+ };
271
+
272
+ // TODO(burdon): Cursor bug if no newline after BR (cursor down just loops back to start of each paragraph).
273
+ export const HorizontalRule = {
274
+ render: () => (
275
+ <Story
276
+ text={str('# Horizontal Rule', '', text.paragraphs, '---', '', text.paragraphs, '---', '', text.paragraphs)}
277
+ extensions={[hr()]}
278
+ />
279
+ ),
280
+ };
281
+
239
282
  export const Tooltips = {
240
- render: () => <Story text={str(text.links, text.footer)} extensions={[tooltip({ onHover })]} />,
283
+ render: () => <Story text={str(text.links, text.footer)} extensions={[tooltip({ onHover: onTooltipHover })]} />,
241
284
  };
242
285
 
243
286
  export const Links = {
244
- render: () => <Story text={str(text.links, text.footer)} extensions={[link({ onRender })]} />,
287
+ render: () => <Story text={str(text.links, text.footer)} extensions={[link({ onRender: onLinkRender })]} />,
245
288
  };
246
289
 
247
290
  export const Code = {
@@ -256,7 +299,7 @@ export const Image = {
256
299
  render: () => <Story text={str(text.image, text.footer)} readonly extensions={[image()]} />,
257
300
  };
258
301
 
259
- export const TaskList = {
302
+ export const Tasklist = {
260
303
  render: () => (
261
304
  <Story
262
305
  text={str(text.tasks, '', text.list, text.footer)}
@@ -277,7 +320,7 @@ export const Autocomplete = {
277
320
  <Story
278
321
  text={str('# Autocomplete', '', 'Press CTRL-SPACE', text.footer)}
279
322
  extensions={[
280
- link({ onRender }),
323
+ link({ onRender: onLinkRender }),
281
324
  autocomplete({
282
325
  onSearch: (text) => links.filter(({ label }) => label.toLowerCase().includes(text.toLowerCase())),
283
326
  }),
@@ -301,48 +344,56 @@ export const Mention = {
301
344
  ),
302
345
  };
303
346
 
304
- const mark = () => `[^${PublicKey.random().toHex()}]`;
305
- export const Comments = {
306
- render: () => (
307
- <Story
308
- text={str(text.paragraphs, mark(), '', mark(), text.footer)}
309
- extensions={[
310
- comments({
311
- onCreate: () => PublicKey.random().toHex(),
312
- onUpdate: (info) => {
313
- // console.log('update', info);
314
- },
315
- }),
316
- highlight(),
317
- ]}
318
- />
319
- ),
347
+ export const Mermaid = {
348
+ render: () => <Story text={str('# Mermaid', '', text.mermaid, text.footer)} extensions={[code(), mermaid()]} />,
320
349
  };
321
350
 
322
- export const Diagnostics = {
323
- render: () => (
324
- <Story
325
- text={document}
326
- extensions={[
327
- // Cursor moved.
328
- EditorView.updateListener.of((update) => {
329
- console.log('update', update.view.state.selection.main.head);
330
- }),
331
- ]}
332
- />
333
- ),
351
+ export const Comments = {
352
+ render: () => {
353
+ const [commentsStates, setCommentStates] = useState<CommentRange[]>([]);
354
+
355
+ return (
356
+ <Story
357
+ text={str('# Comments', '', text.paragraphs, text.footer)}
358
+ comments={commentsStates}
359
+ extensions={[
360
+ comments({
361
+ onHover: onCommentsHover,
362
+ onCreate: (relPos) => {
363
+ const id = PublicKey.random().toHex();
364
+ setCommentStates((comments) => [...comments, { id, cursor: relPos }]);
365
+ return id;
366
+ },
367
+ onSelect: (state) => {
368
+ const debug = false;
369
+ if (debug) {
370
+ console.log(
371
+ 'update',
372
+ JSON.stringify({
373
+ active: state.active?.slice(0, 8),
374
+ closest: state.closest?.slice(0, 8),
375
+ ranges: state.ranges.length,
376
+ }),
377
+ );
378
+ }
379
+ },
380
+ }),
381
+ ]}
382
+ />
383
+ );
384
+ },
334
385
  };
335
386
 
336
- export const Demo = {
337
- render: () => <Story text={str(text.paragraphs, text.footer)} extensions={[demo()]} />,
387
+ export const Typewriter = {
388
+ render: () => <Story text={str('# Typewriter', '', text.paragraphs, text.footer)} extensions={[typewriter()]} />,
338
389
  };
339
390
 
340
391
  export const Blast = {
341
392
  render: () => (
342
393
  <Story
343
- text={str(text.paragraphs, text.code, text.paragraphs)}
394
+ text={str('# Blast', '', text.paragraphs, text.code, text.paragraphs)}
344
395
  extensions={[
345
- demo({
396
+ typewriter({
346
397
  items: localStorage.getItem('dxos.composer.extension.demo')?.split(','),
347
398
  }),
348
399
  blast(
@@ -362,6 +413,16 @@ export const Blast = {
362
413
  ),
363
414
  };
364
415
 
365
- export const NoExtensions = {
366
- render: () => <Story text={document} />,
416
+ export const Diagnostics = {
417
+ render: () => (
418
+ <Story
419
+ text={document}
420
+ extensions={[
421
+ // Cursor moved.
422
+ EditorView.updateListener.of((update) => {
423
+ console.log('update', update.view.state.selection.main.head);
424
+ }),
425
+ ]}
426
+ />
427
+ ),
367
428
  };
@@ -31,7 +31,7 @@ const Story = ({
31
31
  ...props
32
32
  }: { text?: string; automerge?: boolean } & Pick<TextEditorProps, 'extensions' | 'slots'>) => {
33
33
  const ref = useRef<TextEditorRef>(null);
34
- const [item] = useState({ text: new TextObject(text, undefined, undefined, { useAutomergeBackend: automerge }) });
34
+ const [item] = useState({ text: new TextObject(text, undefined, undefined, { automerge }) });
35
35
  const model = useTextModel({ text: item.text });
36
36
  if (!model) {
37
37
  return null;
@@ -21,9 +21,9 @@ import { generateName } from '@dxos/display-name';
21
21
  import { useThemeContext } from '@dxos/react-ui';
22
22
  import { getColorForValue, inputSurface, mx } from '@dxos/react-ui-theme';
23
23
 
24
- import { basicBundle, markdownBundle } from './extensions';
24
+ import { basicBundle, markdownBundle, setCommentRange } from './extensions';
25
25
  import { defaultTheme, markdownTheme, textTheme } from './themes';
26
- import { type EditorModel } from '../../hooks';
26
+ import { type CommentRange, type EditorModel } from '../../hooks';
27
27
  import { type ThemeStyles } from '../../styles';
28
28
 
29
29
  export const EditorModes = ['default', 'vim'] as const;
@@ -56,6 +56,7 @@ export type TextEditorSlots = {
56
56
 
57
57
  export type TextEditorProps = {
58
58
  model: EditorModel;
59
+ comments?: CommentRange[];
59
60
  readonly?: boolean;
60
61
  editorMode?: EditorMode;
61
62
  extensions?: Extension[];
@@ -67,13 +68,13 @@ export type TextEditorProps = {
67
68
  * NOTE: Rather than adding properties, try to create extensions that can be reused.
68
69
  */
69
70
  export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
70
- ({ model, readonly, editorMode, extensions = [], slots = defaultSlots }, forwardedRef) => {
71
+ ({ model, comments, readonly, editorMode, extensions = [], slots = defaultSlots }, forwardedRef) => {
71
72
  const { themeMode } = useThemeContext();
72
73
  const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
73
-
74
74
  const [root, setRoot] = useState<HTMLDivElement | null>(null);
75
- const [state, setState] = useState<EditorState>();
76
- const [view, setView] = useState<EditorView>();
75
+ const [{ state = undefined, view = undefined } = {}, setEditor] = useState<
76
+ { state?: EditorState; view?: EditorView } | undefined
77
+ >();
77
78
  useImperativeHandle(forwardedRef, () => ({ root, state, view }), [view, state, root]);
78
79
 
79
80
  // TODO(burdon): Factor out?
@@ -88,30 +89,34 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
88
89
  }
89
90
  }, [awareness, peer, themeMode]);
90
91
 
91
- // TODO(burdon): Get from model.
92
- // useHighlights(view, [
93
- // { id: 'c-1', from: 0, to: 20 },
94
- // { id: 'c-2', from: 100, to: 120 },
95
- // ]);
92
+ // TODO(burdon): Factor out as extension.
93
+ useEffect(() => {
94
+ if (view && comments?.length) {
95
+ view.dispatch({
96
+ effects: setCommentRange.of({ model, comments }),
97
+ });
98
+ }
99
+ }, [view, comments]);
96
100
 
97
101
  useEffect(() => {
98
102
  if (!root) {
99
103
  return;
100
104
  }
101
105
 
106
+ // TODO(burdon): Remember cursor position.
107
+ // https://codemirror.net/docs/ref/#state.EditorStateConfig
102
108
  const state = EditorState.create({
103
109
  doc: model.text(),
104
110
  extensions: [
105
111
  readonly && EditorState.readOnly.of(readonly),
106
112
 
107
- // TODO(burdon): Factor out VIM mode?
113
+ // TODO(burdon): Factor out VIM mode? (manage via MarkdownPlugin).
108
114
  editorMode === 'vim' && vim(),
109
115
 
110
116
  // Theme.
111
- // TODO(burdon): Make optional.
117
+ // TODO(burdon): Make theme configurable.
112
118
  EditorView.baseTheme(defaultTheme),
113
119
  EditorView.theme(slots?.editor?.theme ?? {}),
114
- // TODO(burdon): themeMode doesn't change in storybooks.
115
120
  EditorView.darkTheme.of(themeMode === 'dark'),
116
121
 
117
122
  // Storage and replication.
@@ -124,10 +129,11 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
124
129
 
125
130
  // NOTE: This repaints the editor.
126
131
  // If the new state is derived from the old state, it will likely not be visible other than the cursor resetting.
127
- // Ideally this should not be hit except when changing between text objects.
132
+ // Ideally this should not happen except when changing between text objects.
128
133
  view?.destroy();
129
- setView(
130
- new EditorView({
134
+ setEditor({
135
+ state,
136
+ view: new EditorView({
131
137
  state,
132
138
  parent: root,
133
139
  // NOTE: Uncomment to spy on all transactions.
@@ -136,15 +142,13 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
136
142
  // view.update([transaction]);
137
143
  // },
138
144
  }),
139
- );
140
- setState(state);
145
+ });
141
146
 
142
147
  return () => {
143
148
  view?.destroy();
144
- setView(undefined);
145
- setState(undefined);
149
+ setEditor(undefined);
146
150
  };
147
- }, [root, model, themeMode, readonly, editorMode]);
151
+ }, [root, model, readonly, editorMode, themeMode]);
148
152
 
149
153
  const handleKeyUp = useCallback(
150
154
  (event: KeyboardEvent) => {