@dxos/react-ui-editor 0.6.5 → 0.6.6-staging.23d123d

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 (70) hide show
  1. package/dist/lib/browser/index.mjs +316 -324
  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/Toolbar/Toolbar.d.ts +5 -1
  5. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  6. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +7 -0
  7. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  8. package/dist/types/src/components/index.d.ts +0 -1
  9. package/dist/types/src/components/index.d.ts.map +1 -1
  10. package/dist/types/src/extensions/autocomplete.d.ts +2 -1
  11. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  12. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  13. package/dist/types/src/extensions/automerge/automerge.stories.d.ts +9 -2
  14. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  15. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  16. package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +1 -1
  17. package/dist/types/src/extensions/awareness/awareness.d.ts +6 -6
  18. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
  19. package/dist/types/src/extensions/comments.d.ts +1 -2
  20. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  21. package/dist/types/src/extensions/cursor.d.ts +1 -1
  22. package/dist/types/src/extensions/cursor.d.ts.map +1 -1
  23. package/dist/types/src/extensions/debug.d.ts +3 -0
  24. package/dist/types/src/extensions/debug.d.ts.map +1 -0
  25. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  26. package/dist/types/src/extensions/index.d.ts +1 -0
  27. package/dist/types/src/extensions/index.d.ts.map +1 -1
  28. package/dist/types/src/extensions/markdown/action.d.ts +1 -1
  29. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -1
  30. package/dist/types/src/extensions/modes.d.ts +7 -4
  31. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  32. package/dist/types/src/hooks/{useTextEditor.stories.d.ts → InputMode.stories.d.ts} +9 -9
  33. package/dist/types/src/hooks/InputMode.stories.d.ts.map +1 -0
  34. package/dist/types/src/{components/TextEditor → hooks}/TextEditor.stories.d.ts +8 -16
  35. package/dist/types/src/hooks/TextEditor.stories.d.ts.map +1 -0
  36. package/dist/types/src/hooks/useTextEditor.d.ts +20 -3
  37. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  38. package/dist/types/src/themes/default.d.ts.map +1 -1
  39. package/dist/types/src/translations.d.ts +4 -0
  40. package/dist/types/src/translations.d.ts.map +1 -1
  41. package/package.json +27 -27
  42. package/src/components/Toolbar/Toolbar.stories.tsx +18 -8
  43. package/src/components/Toolbar/Toolbar.tsx +93 -3
  44. package/src/components/index.ts +0 -1
  45. package/src/extensions/autocomplete.ts +3 -3
  46. package/src/extensions/automerge/automerge.stories.tsx +25 -18
  47. package/src/extensions/automerge/automerge.ts +2 -0
  48. package/src/extensions/automerge/cursor.ts +3 -4
  49. package/src/extensions/awareness/awareness-provider.ts +2 -0
  50. package/src/extensions/awareness/awareness.ts +34 -30
  51. package/src/extensions/comments.ts +7 -14
  52. package/src/extensions/cursor.ts +1 -1
  53. package/src/extensions/debug.ts +15 -0
  54. package/src/extensions/factories.ts +19 -13
  55. package/src/extensions/index.ts +1 -0
  56. package/src/extensions/markdown/action.ts +1 -0
  57. package/src/extensions/modes.ts +9 -6
  58. package/src/hooks/{useTextEditor.stories.tsx → InputMode.stories.tsx} +41 -47
  59. package/src/{components/TextEditor → hooks}/TextEditor.stories.tsx +24 -30
  60. package/src/hooks/useTextEditor.ts +75 -23
  61. package/src/themes/default.ts +20 -4
  62. package/src/translations.ts +4 -0
  63. package/dist/types/src/components/TextEditor/TextEditor.d.ts +0 -34
  64. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +0 -1
  65. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +0 -1
  66. package/dist/types/src/components/TextEditor/index.d.ts +0 -2
  67. package/dist/types/src/components/TextEditor/index.d.ts.map +0 -1
  68. package/dist/types/src/hooks/useTextEditor.stories.d.ts.map +0 -1
  69. package/src/components/TextEditor/TextEditor.tsx +0 -184
  70. package/src/components/TextEditor/index.ts +0 -5
@@ -20,7 +20,11 @@ var translations_default = [
20
20
  "table label": "Create table",
21
21
  "heading level label_zero": "Paragraph",
22
22
  "heading level label_one": "Heading level {{count}}",
23
- "heading level label_other": "Heading level {{count}}"
23
+ "heading level label_other": "Heading level {{count}}",
24
+ "view mode label": "Editor view",
25
+ "preview mode label": "Live preview",
26
+ "readonly mode label": "Read only",
27
+ "source mode label": "Source"
24
28
  }
25
29
  }
26
30
  }
@@ -31,14 +35,13 @@ import { keymap as keymap11 } from "@codemirror/view";
31
35
  import { tags as tags2 } from "@lezer/highlight";
32
36
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
33
37
 
34
- // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
35
- import { EditorState as EditorState2 } from "@codemirror/state";
36
- import { EditorView as EditorView16 } from "@codemirror/view";
37
- import { useFocusableGroup } from "@fluentui/react-tabster";
38
- import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState as useState3 } from "react";
39
- import { log as log7 } from "@dxos/log";
40
- import { useDefaultValue } from "@dxos/react-ui";
41
- import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
38
+ // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
39
+ import { ChatText, Code, CodeBlock, Image, Link, ListBullets, ListChecks, ListNumbers, Paragraph, Quotes, TextStrikethrough, Table as Table2, TextB, TextHOne, TextHTwo, TextHThree, TextHFour, TextHFive, TextHSix, TextItalic, CaretDown, Check, PencilSimpleSlash, MarkdownLogo, PencilSimple } from "@phosphor-icons/react";
40
+ import { createContext } from "@radix-ui/react-context";
41
+ import React, { useEffect as useEffect2, useRef, useState as useState3 } from "react";
42
+ import { useDropzone } from "react-dropzone";
43
+ import { Button, DensityProvider, DropdownMenu, ElevationProvider, Toolbar as NaturalToolbar, Tooltip, useTranslation } from "@dxos/react-ui";
44
+ import { getSize } from "@dxos/react-ui-theme";
42
45
 
43
46
  // packages/ui/react-ui-editor/src/extensions/annotations.ts
44
47
  import { StateField } from "@codemirror/state";
@@ -146,7 +149,7 @@ var styles = EditorView.baseTheme({
146
149
  import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
147
150
  import { markdownLanguage } from "@codemirror/lang-markdown";
148
151
  import { keymap } from "@codemirror/view";
149
- var autocomplete = ({ onSearch }) => {
152
+ var autocomplete = ({ activateOnTyping, onSearch }) => {
150
153
  return [
151
154
  // https://codemirror.net/docs/ref/#view.keymap
152
155
  // https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322
@@ -155,13 +158,12 @@ var autocomplete = ({ onSearch }) => {
155
158
  // https://codemirror.net/examples/autocompletion
156
159
  // https://codemirror.net/docs/ref/#autocomplete.autocompletion
157
160
  autocompletion({
158
- activateOnTyping: false,
161
+ activateOnTyping,
159
162
  // closeOnBlur: false,
160
163
  // defaultKeymap: false,
161
164
  // TODO(burdon): Styles/fragments.
162
165
  tooltipClass: () => "shadow rounded"
163
166
  }),
164
- // TODO(burdon): Option to create new page?
165
167
  // TODO(burdon): Optional decoration via addToOptions
166
168
  markdownLanguage.data.of({
167
169
  autocomplete: (context) => {
@@ -185,17 +187,16 @@ import { next as A3 } from "@dxos/automerge/automerge";
185
187
 
186
188
  // packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
187
189
  import { log } from "@dxos/log";
188
- import { toCursor, fromCursor } from "@dxos/react-client/echo";
190
+ import { fromCursor, toCursor } from "@dxos/react-client/echo";
189
191
  var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts";
190
192
  var cursorConverter = (accessor) => ({
191
- // TODO(burdon): Handle assoc to associate with a previous character.
192
- toCursor: (pos) => {
193
+ toCursor: (pos, assoc) => {
193
194
  try {
194
- return toCursor(accessor, pos);
195
+ return toCursor(accessor, pos, assoc);
195
196
  } catch (err) {
196
197
  log.catch(err, void 0, {
197
198
  F: __dxlog_file,
198
- L: 16,
199
+ L: 15,
199
200
  S: void 0,
200
201
  C: (f, a) => f(...a)
201
202
  });
@@ -208,7 +209,7 @@ var cursorConverter = (accessor) => ({
208
209
  } catch (err) {
209
210
  log.catch(err, void 0, {
210
211
  F: __dxlog_file,
211
- L: 25,
212
+ L: 24,
212
213
  S: void 0,
213
214
  C: (f, a) => f(...a)
214
215
  });
@@ -449,6 +450,7 @@ var automerge = (accessor) => {
449
450
  const syncer = new Syncer(accessor.handle, syncState);
450
451
  return [
451
452
  Cursor.converter.of(cursorConverter(accessor)),
453
+ // Track heads.
452
454
  syncState,
453
455
  // Reconcile external updates.
454
456
  ViewPlugin.fromClass(class {
@@ -503,13 +505,11 @@ var awareness = (provider = dummyProvider) => {
503
505
  };
504
506
  var RemoteSelectionsDecorator = class {
505
507
  constructor(view) {
506
- this.decorations = RangeSet.of([]);
507
508
  this._ctx = new Context(void 0, {
508
509
  F: __dxlog_file2,
509
- L: 85
510
+ L: 82
510
511
  });
511
- this._lastAnchor = void 0;
512
- this._lastHead = void 0;
512
+ this.decorations = RangeSet.of([]);
513
513
  this._cursorConverter = view.state.facet(Cursor.converter);
514
514
  this._provider = view.state.facet(awarenessProvider);
515
515
  this._provider.open();
@@ -526,12 +526,12 @@ var RemoteSelectionsDecorator = class {
526
526
  this._provider.close();
527
527
  }
528
528
  update(update2) {
529
- this._updateLocalSelection(update2);
530
- this._updateRemoteSelections(update2);
529
+ this._updateLocalSelection(update2.view);
530
+ this._updateRemoteSelections(update2.view);
531
531
  }
532
- _updateLocalSelection(update2) {
533
- const hasFocus = update2.view.hasFocus && update2.view.dom.ownerDocument.hasFocus();
534
- const { anchor = void 0, head = void 0 } = hasFocus ? update2.state.selection.main : {};
532
+ _updateLocalSelection(view) {
533
+ const hasFocus = view.hasFocus && view.dom.ownerDocument.hasFocus();
534
+ const { anchor = void 0, head = void 0 } = hasFocus ? view.state.selection.main : {};
535
535
  if (this._lastAnchor === anchor && this._lastHead === head) {
536
536
  return;
537
537
  }
@@ -539,10 +539,10 @@ var RemoteSelectionsDecorator = class {
539
539
  this._lastHead = head;
540
540
  this._provider.update(anchor !== void 0 && head !== void 0 ? {
541
541
  anchor: this._cursorConverter.toCursor(anchor),
542
- head: this._cursorConverter.toCursor(head)
542
+ head: this._cursorConverter.toCursor(head, -1)
543
543
  } : void 0);
544
544
  }
545
- _updateRemoteSelections(update2) {
545
+ _updateRemoteSelections(view) {
546
546
  const decorations = [];
547
547
  const awarenessStates = this._provider.getRemoteStates();
548
548
  for (const state2 of awarenessStates) {
@@ -551,12 +551,12 @@ var RemoteSelectionsDecorator = class {
551
551
  if (anchor == null || head == null) {
552
552
  continue;
553
553
  }
554
- const start = Math.min(Math.min(anchor, head), update2.view.state.doc.length);
555
- const end = Math.min(Math.max(anchor, head), update2.view.state.doc.length);
556
- const startLine = update2.view.state.doc.lineAt(start);
557
- const endLine = update2.view.state.doc.lineAt(end);
558
- const color = state2.info.color ?? "#30bced";
559
- const lightColor = state2.info.lightColor ?? color + "33";
554
+ const start = Math.min(Math.min(anchor, head), view.state.doc.length);
555
+ const end = Math.min(Math.max(anchor, head), view.state.doc.length);
556
+ const startLine = view.state.doc.lineAt(start);
557
+ const endLine = view.state.doc.lineAt(end);
558
+ const darkColor = state2.info.darkColor;
559
+ const lightColor = state2.info.lightColor;
560
560
  if (startLine.number === endLine.number) {
561
561
  decorations.push({
562
562
  from: start,
@@ -590,7 +590,7 @@ var RemoteSelectionsDecorator = class {
590
590
  })
591
591
  });
592
592
  for (let i = startLine.number + 1; i < endLine.number; i++) {
593
- const linePos = update2.view.state.doc.line(i).from;
593
+ const linePos = view.state.doc.line(i).from;
594
594
  decorations.push({
595
595
  from: linePos,
596
596
  to: linePos,
@@ -609,7 +609,7 @@ var RemoteSelectionsDecorator = class {
609
609
  value: Decoration2.widget({
610
610
  side: head - anchor > 0 ? -1 : 1,
611
611
  block: false,
612
- widget: new RemoteCaretWidget(state2.info.displayName ?? "Anonymous", color)
612
+ widget: new RemoteCaretWidget(state2.info.displayName ?? "Anonymous", darkColor)
613
613
  })
614
614
  });
615
615
  }
@@ -695,12 +695,11 @@ var styles2 = EditorView3.baseTheme({
695
695
  lineHeight: "normal",
696
696
  userSelect: "none",
697
697
  color: "white",
698
- padding: "2px",
698
+ padding: "2px 6px",
699
699
  zIndex: 101,
700
700
  transition: "opacity .3s ease-in-out",
701
701
  backgroundColor: "inherit",
702
702
  borderRadius: "2px",
703
- // These should be separate.
704
703
  opacity: 0,
705
704
  transitionDelay: "0s",
706
705
  whiteSpace: "nowrap"
@@ -760,7 +759,7 @@ var SpaceAwarenessProvider = class {
760
759
  err
761
760
  }, {
762
761
  F: __dxlog_file3,
763
- L: 89,
762
+ L: 91,
764
763
  S: this,
765
764
  C: (f, a) => f(...a)
766
765
  });
@@ -777,7 +776,7 @@ var SpaceAwarenessProvider = class {
777
776
  update(position) {
778
777
  invariant(this._postTask, void 0, {
779
778
  F: __dxlog_file3,
780
- L: 104,
779
+ L: 106,
781
780
  S: this,
782
781
  A: [
783
782
  "this._postTask",
@@ -794,7 +793,7 @@ var SpaceAwarenessProvider = class {
794
793
  _handleQueryMessage() {
795
794
  invariant(this._postTask, void 0, {
796
795
  F: __dxlog_file3,
797
- L: 115,
796
+ L: 117,
798
797
  S: this,
799
798
  A: [
800
799
  "this._postTask",
@@ -806,7 +805,7 @@ var SpaceAwarenessProvider = class {
806
805
  _handlePostMessage(message) {
807
806
  invariant(message.kind === "post", void 0, {
808
807
  F: __dxlog_file3,
809
- L: 120,
808
+ L: 122,
810
809
  S: this,
811
810
  A: [
812
811
  "message.kind === 'post'",
@@ -1880,16 +1879,12 @@ var hasActiveSelection = (state2) => {
1880
1879
  return state2.selection.ranges.some((range) => !range.empty);
1881
1880
  };
1882
1881
  var ExternalCommentSync = class {
1883
- constructor(view, id, subscribe, getThreads) {
1882
+ constructor(view, id, subscribe, getComments) {
1884
1883
  this.destroy = () => {
1885
1884
  this.unsubscribe();
1886
1885
  };
1887
1886
  const updateComments = () => {
1888
- const threads = getThreads();
1889
- const comments2 = threads.filter(nonNullable).filter((thread) => thread.anchor).map((thread) => ({
1890
- id: thread.id,
1891
- cursor: thread.anchor
1892
- }));
1887
+ const comments2 = getComments();
1893
1888
  if (id === view.state.facet(documentId)) {
1894
1889
  queueMicrotask(() => view.dispatch({
1895
1890
  effects: setComments.of({
@@ -1902,9 +1897,9 @@ var ExternalCommentSync = class {
1902
1897
  this.unsubscribe = subscribe(updateComments);
1903
1898
  }
1904
1899
  };
1905
- var createExternalCommentSync = (id, subscribe, getThreads) => ViewPlugin4.fromClass(class {
1900
+ var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin4.fromClass(class {
1906
1901
  constructor(view) {
1907
- return new ExternalCommentSync(view, id, subscribe, getThreads);
1902
+ return new ExternalCommentSync(view, id, subscribe, getComments);
1908
1903
  }
1909
1904
  });
1910
1905
  var useCommentState = () => {
@@ -1940,7 +1935,7 @@ var useComments = (view, id, comments2) => {
1940
1935
  });
1941
1936
  };
1942
1937
  var useCommentClickListener = (onCommentClick) => {
1943
- const observer = useMemo(() => EditorView7.updateListener.of((update2) => {
1938
+ return useMemo(() => EditorView7.updateListener.of((update2) => {
1944
1939
  update2.transactions.forEach((transaction) => {
1945
1940
  transaction.effects.forEach((effect) => {
1946
1941
  if (effect.is(commentClickedEffect)) {
@@ -1951,7 +1946,19 @@ var useCommentClickListener = (onCommentClick) => {
1951
1946
  }), [
1952
1947
  onCommentClick
1953
1948
  ]);
1954
- return observer;
1949
+ };
1950
+
1951
+ // packages/ui/react-ui-editor/src/extensions/debug.ts
1952
+ import { syntaxTree } from "@codemirror/language";
1953
+ import { StateField as StateField5 } from "@codemirror/state";
1954
+ var debugNodeLogger = (log8 = console.log) => {
1955
+ const logTokens = (state2) => syntaxTree(state2).iterate({
1956
+ enter: (node) => log8(node.type)
1957
+ });
1958
+ return StateField5.define({
1959
+ create: (state2) => logTokens(state2),
1960
+ update: (_, tr) => logTokens(tr.state)
1961
+ });
1955
1962
  };
1956
1963
 
1957
1964
  // packages/ui/react-ui-editor/src/extensions/doc.ts
@@ -2143,19 +2150,34 @@ var defaultTheme = {
2143
2150
  // tooltip
2144
2151
  //
2145
2152
  ".cm-tooltip": {
2146
- border: "none",
2147
- background: "unset"
2153
+ border: "none"
2154
+ },
2155
+ "&light .cm-tooltip": {
2156
+ background: `${get2(tokens, "extend.colors.neutral.100")} !important`
2157
+ },
2158
+ "&dark .cm-tooltip": {
2159
+ background: `${get2(tokens, "extend.colors.neutral.900")} !important`
2148
2160
  },
2149
2161
  ".cm-tooltip-below": {},
2150
2162
  //
2151
2163
  // autocomplete
2164
+ // https://github.com/codemirror/autocomplete/blob/main/src/completion.ts
2152
2165
  //
2153
2166
  ".cm-tooltip-autocomplete": {
2154
2167
  marginTop: "4px",
2155
2168
  marginLeft: "-3px"
2156
2169
  },
2157
- ".cm-tooltip-autocomplete ul li": {},
2158
- ".cm-tooltip-autocomplete ul li[aria-selected]": {},
2170
+ ".cm-tooltip-autocomplete > ul": {
2171
+ maxHeight: "20em !important"
2172
+ },
2173
+ ".cm-tooltip-autocomplete > ul > li": {},
2174
+ ".cm-tooltip-autocomplete > ul > li[aria-selected]": {},
2175
+ // TODO(burdon): Can we add a class prefix to avoid adding !important?
2176
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
2177
+ paddingLeft: "4px !important",
2178
+ borderBottom: "none !important",
2179
+ color: get2(tokens, "extend.colors.primary.500")
2180
+ },
2159
2181
  ".cm-completionIcon": {
2160
2182
  display: "none"
2161
2183
  },
@@ -2163,7 +2185,8 @@ var defaultTheme = {
2163
2185
  fontFamily: get2(tokens, "fontFamily.body", []).join(",")
2164
2186
  },
2165
2187
  ".cm-completionMatchedText": {
2166
- textDecoration: "none"
2188
+ textDecoration: "none !important",
2189
+ opacity: 0.5
2167
2190
  },
2168
2191
  //
2169
2192
  // table
@@ -2349,23 +2372,23 @@ var createThemeExtensions = ({ theme, themeMode, slots: _slots } = {}) => {
2349
2372
  ].filter(isNotFalsy2);
2350
2373
  };
2351
2374
  var createDataExtensions = ({ id, text, space, identity }) => {
2352
- const extensions = text ? [
2353
- automerge(text)
2354
- ] : [];
2375
+ const extensions = [];
2376
+ if (text) {
2377
+ extensions.push(automerge(text));
2378
+ }
2355
2379
  if (space && identity) {
2356
2380
  const peerId = identity?.identityKey.toHex();
2357
2381
  const { cursorLightValue, cursorDarkValue } = hueTokens[identity?.profile?.data?.hue ?? hexToHue(peerId ?? "0")];
2358
- const awarenessProvider2 = new SpaceAwarenessProvider({
2382
+ extensions.push(awareness(new SpaceAwarenessProvider({
2359
2383
  space,
2360
2384
  channel: `awareness.${id}`,
2361
2385
  peerId: identity.identityKey.toHex(),
2362
2386
  info: {
2363
2387
  displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
2364
- color: cursorDarkValue,
2388
+ darkColor: cursorDarkValue,
2365
2389
  lightColor: cursorLightValue
2366
2390
  }
2367
- });
2368
- extensions.push(awareness(awarenessProvider2));
2391
+ })));
2369
2392
  }
2370
2393
  return extensions;
2371
2394
  };
@@ -2386,7 +2409,7 @@ var listener = ({ onFocus, onChange }) => {
2386
2409
 
2387
2410
  // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
2388
2411
  import { snippet } from "@codemirror/autocomplete";
2389
- import { syntaxTree } from "@codemirror/language";
2412
+ import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2390
2413
  import { EditorSelection } from "@codemirror/state";
2391
2414
  import { EditorView as EditorView11, keymap as keymap6 } from "@codemirror/view";
2392
2415
  import { useMemo as useMemo2, useState as useState2 } from "react";
@@ -2411,7 +2434,7 @@ var setHeading = (level) => {
2411
2434
  let prevBlock = -1;
2412
2435
  for (const range of ranges) {
2413
2436
  let sawBlock = false;
2414
- syntaxTree(state2).iterate({
2437
+ syntaxTree2(state2).iterate({
2415
2438
  from: range.from,
2416
2439
  to: range.to,
2417
2440
  enter: (node) => {
@@ -2520,7 +2543,7 @@ var setStyle = (type, enable) => {
2520
2543
  let startCovered = false;
2521
2544
  let endCovered = false;
2522
2545
  let { from, to } = range;
2523
- syntaxTree(state2).iterate({
2546
+ syntaxTree2(state2).iterate({
2524
2547
  from,
2525
2548
  to,
2526
2549
  enter: (node) => {
@@ -2723,7 +2746,7 @@ var insertTable = (view) => {
2723
2746
  snippets.table(view, null, from, from);
2724
2747
  };
2725
2748
  var removeLinkInner = (from, to, changes, state2) => {
2726
- syntaxTree(state2).iterate({
2749
+ syntaxTree2(state2).iterate({
2727
2750
  from,
2728
2751
  to,
2729
2752
  enter: (node) => {
@@ -2768,7 +2791,7 @@ var addLink = ({ url, image: image2 } = {}) => {
2768
2791
  let { from, to } = range;
2769
2792
  const cutStyles = [];
2770
2793
  let okay = null;
2771
- syntaxTree(state2).iterate({
2794
+ syntaxTree2(state2).iterate({
2772
2795
  from,
2773
2796
  to,
2774
2797
  enter: (node) => {
@@ -2863,7 +2886,7 @@ var addList = (type) => {
2863
2886
  let parentColumn = null;
2864
2887
  const blocks = [];
2865
2888
  for (const { from, to } of state2.selection.ranges) {
2866
- syntaxTree(state2).iterate({
2889
+ syntaxTree2(state2).iterate({
2867
2890
  from,
2868
2891
  to,
2869
2892
  enter: (node) => {
@@ -2999,7 +3022,7 @@ var removeList = (type) => {
2999
3022
  const stack = [];
3000
3023
  const targetNodeType = type === 0 ? "OrderedList" : type === 1 ? "BulletList" : "TaskList";
3001
3024
  for (const { from, to } of state2.selection.ranges) {
3002
- syntaxTree(state2).iterate({
3025
+ syntaxTree2(state2).iterate({
3003
3026
  from,
3004
3027
  to,
3005
3028
  enter: (node) => {
@@ -3086,7 +3109,7 @@ var setBlockquote = (enable) => {
3086
3109
  let lastBlock = -1;
3087
3110
  for (const { from, to } of state2.selection.ranges) {
3088
3111
  const sawBlock = false;
3089
- syntaxTree(state2).iterate({
3112
+ syntaxTree2(state2).iterate({
3090
3113
  from,
3091
3114
  to,
3092
3115
  enter: (node) => {
@@ -3176,7 +3199,7 @@ var addCodeblock = (target) => {
3176
3199
  for (const { from, to } of selection.ranges) {
3177
3200
  let blockFrom = from;
3178
3201
  let blockTo = to;
3179
- syntaxTree(state2).iterate({
3202
+ syntaxTree2(state2).iterate({
3180
3203
  from,
3181
3204
  to,
3182
3205
  enter: (node) => {
@@ -3227,7 +3250,7 @@ var removeCodeblock = ({ state: state2, dispatch }) => {
3227
3250
  let lastBlock = -1;
3228
3251
  const changes = [];
3229
3252
  for (const { from, to } of state2.selection.ranges) {
3230
- syntaxTree(state2).iterate({
3253
+ syntaxTree2(state2).iterate({
3231
3254
  from,
3232
3255
  to,
3233
3256
  enter: (node) => {
@@ -3389,7 +3412,7 @@ var getFormatting = (state2) => {
3389
3412
  }
3390
3413
  }
3391
3414
  }
3392
- syntaxTree(state2).iterate({
3415
+ syntaxTree2(state2).iterate({
3393
3416
  from: range.from,
3394
3417
  to: range.to,
3395
3418
  enter: (node) => {
@@ -3739,7 +3762,7 @@ var markdownHighlightStyle = (readonly) => {
3739
3762
  };
3740
3763
 
3741
3764
  // packages/ui/react-ui-editor/src/extensions/markdown/linkPaste.ts
3742
- import { syntaxTree as syntaxTree2 } from "@codemirror/language";
3765
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3743
3766
  import { Transaction } from "@codemirror/state";
3744
3767
  import { ViewPlugin as ViewPlugin5 } from "@codemirror/view";
3745
3768
  var VALID_PROTOCOLS = [
@@ -3813,7 +3836,7 @@ var linkPastePlugin = ViewPlugin5.fromClass(class {
3813
3836
  * checking for CodeBlock or FencedCode nodes.
3814
3837
  */
3815
3838
  isInCodeBlock(state2, pos) {
3816
- const tree = syntaxTree2(state2);
3839
+ const tree = syntaxTree3(state2);
3817
3840
  let node = tree.resolveInner(pos, -1);
3818
3841
  while (node) {
3819
3842
  if (node.name.includes("Code") || node.name.includes("FencedCode")) {
@@ -3873,7 +3896,7 @@ var createMarkdownExtensions = ({ themeMode } = {}) => {
3873
3896
  };
3874
3897
 
3875
3898
  // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
3876
- import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3899
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
3877
3900
  import { RangeSetBuilder as RangeSetBuilder2, StateEffect as StateEffect4 } from "@codemirror/state";
3878
3901
  import { EditorView as EditorView12, Decoration as Decoration5, WidgetType as WidgetType3, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
3879
3902
  import { mx as mx2 } from "@dxos/react-ui-theme";
@@ -3974,7 +3997,7 @@ var buildDecorations = (view, options, focus) => {
3974
3997
  const atomicDeco = new RangeSetBuilder2();
3975
3998
  const { state: state2 } = view;
3976
3999
  for (const { from, to } of view.visibleRanges) {
3977
- syntaxTree3(state2).iterate({
4000
+ syntaxTree4(state2).iterate({
3978
4001
  from,
3979
4002
  to,
3980
4003
  enter: (node) => {
@@ -4180,11 +4203,11 @@ var formattingStyles = EditorView12.baseTheme({
4180
4203
  });
4181
4204
 
4182
4205
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
4183
- import { syntaxTree as syntaxTree4 } from "@codemirror/language";
4184
- import { StateField as StateField5 } from "@codemirror/state";
4206
+ import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4207
+ import { StateField as StateField6 } from "@codemirror/state";
4185
4208
  import { Decoration as Decoration6, EditorView as EditorView13, WidgetType as WidgetType4 } from "@codemirror/view";
4186
4209
  var image = (options = {}) => {
4187
- return StateField5.define({
4210
+ return StateField6.define({
4188
4211
  create: (state2) => {
4189
4212
  return Decoration6.set(buildDecorations2(0, state2.doc.length, state2));
4190
4213
  },
@@ -4223,7 +4246,7 @@ var preloadImage = (url) => {
4223
4246
  var buildDecorations2 = (from, to, state2) => {
4224
4247
  const decorations = [];
4225
4248
  const cursor = state2.selection.main.head;
4226
- syntaxTree4(state2).iterate({
4249
+ syntaxTree5(state2).iterate({
4227
4250
  enter: (node) => {
4228
4251
  if (node.name === "Image") {
4229
4252
  const urlNode = node.node.getChild("URL");
@@ -4263,11 +4286,11 @@ var imageUpload = (options = {}) => {
4263
4286
  };
4264
4287
 
4265
4288
  // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
4266
- import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4289
+ import { syntaxTree as syntaxTree6 } from "@codemirror/language";
4267
4290
  import { hoverTooltip as hoverTooltip2 } from "@codemirror/view";
4268
4291
  import { tooltipContent } from "@dxos/react-ui-theme";
4269
4292
  var linkTooltip = (render) => hoverTooltip2((view, pos, side) => {
4270
- const syntax = syntaxTree5(view.state).resolveInner(pos, side);
4293
+ const syntax = syntaxTree6(view.state).resolveInner(pos, side);
4271
4294
  let link = null;
4272
4295
  for (let i = 0, node = syntax; !link && node && i < 5; node = node.parent, i++) {
4273
4296
  link = node.name === "Link" ? node : null;
@@ -4297,11 +4320,11 @@ var linkTooltip = (render) => hoverTooltip2((view, pos, side) => {
4297
4320
  });
4298
4321
 
4299
4322
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
4300
- import { syntaxTree as syntaxTree6 } from "@codemirror/language";
4301
- import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField6 } from "@codemirror/state";
4323
+ import { syntaxTree as syntaxTree7 } from "@codemirror/language";
4324
+ import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField7 } from "@codemirror/state";
4302
4325
  import { Decoration as Decoration7, EditorView as EditorView14, WidgetType as WidgetType5 } from "@codemirror/view";
4303
4326
  var table = (options = {}) => {
4304
- return StateField6.define({
4327
+ return StateField7.define({
4305
4328
  create: (state2) => update(state2, options),
4306
4329
  update: (_, tr) => update(tr.state, options),
4307
4330
  provide: (field) => EditorView14.decorations.from(field)
@@ -4316,7 +4339,7 @@ var update = (state2, options) => {
4316
4339
  const table2 = getTable();
4317
4340
  return table2.rows?.[table2.rows.length - 1];
4318
4341
  };
4319
- syntaxTree6(state2).iterate({
4342
+ syntaxTree7(state2).iterate({
4320
4343
  enter: (node) => {
4321
4344
  switch (node.name) {
4322
4345
  case "Table": {
@@ -4442,14 +4465,24 @@ import { keymap as keymap8 } from "@codemirror/view";
4442
4465
  import { vim } from "@replit/codemirror-vim";
4443
4466
  import { vscodeKeymap } from "@replit/codemirror-vscode-keymap";
4444
4467
  var focusEvent = "focus.container";
4445
- var editorMode = Facet6.define({
4468
+ var EditorViewModes = [
4469
+ "preview",
4470
+ "readonly",
4471
+ "source"
4472
+ ];
4473
+ var EditorInputModes = [
4474
+ "default",
4475
+ "vim",
4476
+ "vscode"
4477
+ ];
4478
+ var editorInputMode = Facet6.define({
4446
4479
  combine: (modes) => modes[0] ?? {}
4447
4480
  });
4448
- var EditorModes = {
4481
+ var InputModeExtensions = {
4449
4482
  default: [],
4450
4483
  vscode: [
4451
4484
  // https://github.com/replit/codemirror-vscode-keymap
4452
- editorMode.of({
4485
+ editorInputMode.of({
4453
4486
  type: "vscode"
4454
4487
  }),
4455
4488
  keymap8.of(vscodeKeymap)
@@ -4457,7 +4490,7 @@ var EditorModes = {
4457
4490
  vim: [
4458
4491
  // https://github.com/replit/codemirror-vim
4459
4492
  vim(),
4460
- editorMode.of({
4493
+ editorInputMode.of({
4461
4494
  type: "vim",
4462
4495
  noTabster: true
4463
4496
  }),
@@ -4619,200 +4652,125 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
4619
4652
  ];
4620
4653
  };
4621
4654
 
4622
- // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
4623
- var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
4624
- var instanceCount = 0;
4625
- var TextEditor = /* @__PURE__ */ forwardRef(({
4626
- id,
4627
- // TODO(wittjosiah): Rename initialText?
4628
- doc,
4629
- selection,
4630
- extensions,
4631
- className,
4632
- autoFocus,
4633
- scrollTo: propsScrollTo,
4634
- moveToEndOfLine,
4635
- debug,
4636
- dataTestId
4637
- }, forwardedRef) => {
4638
- const [instanceId] = useState3(() => `text-editor-${++instanceCount}`);
4639
- const scrollTo = useDefaultValue(propsScrollTo, EditorView16.scrollIntoView(0, {
4640
- yMargin: 0
4641
- }));
4642
- const tabsterDOMAttribute = useFocusableGroup({
4643
- tabBehavior: "limited"
4644
- });
4645
- const rootRef = useRef(null);
4646
- const [view, setView] = useState3(null);
4647
- useImperativeHandle(forwardedRef, () => view, [
4648
- view
4649
- ]);
4650
- useEffect2(() => {
4651
- if (autoFocus) {
4652
- view?.focus();
4653
- }
4654
- }, [
4655
- view,
4656
- autoFocus
4657
- ]);
4658
- useEffect2(() => {
4659
- log7("create", {
4660
- id,
4661
- instanceId
4662
- }, {
4663
- F: __dxlog_file11,
4664
- L: 91,
4665
- S: void 0,
4666
- C: (f, a) => f(...a)
4667
- });
4668
- const state2 = EditorState2.create({
4669
- doc,
4670
- extensions: [
4671
- id && documentId2.of(id),
4672
- // TODO(burdon): NOTE: Doesn't catch errors in keymap functions.
4673
- EditorView16.exceptionSink.of((err) => {
4674
- log7.catch(err, void 0, {
4675
- F: __dxlog_file11,
4676
- L: 104,
4677
- S: void 0,
4678
- C: (f, a) => f(...a)
4679
- });
4680
- }),
4681
- // Focus.
4682
- EditorView16.updateListener.of((update2) => {
4683
- update2.transactions.forEach((transaction) => {
4684
- if (transaction.isUserEvent(focusEvent)) {
4685
- rootRef.current?.focus();
4686
- }
4687
- });
4688
- }),
4689
- extensions
4690
- ].filter(isNotFalsy4)
4691
- });
4692
- const view2 = new EditorView16({
4693
- state: state2,
4694
- parent: rootRef.current,
4695
- scrollTo,
4696
- // NOTE: Uncomment to debug/monitor all transactions.
4697
- // https://codemirror.net/docs/ref/#view.EditorView.dispatch
4698
- dispatchTransactions: (trs, view3) => {
4699
- if (debug) {
4700
- logChanges(trs);
4701
- }
4702
- view3.update(trs);
4703
- }
4704
- });
4705
- if (moveToEndOfLine && !(scrollTo || selection)) {
4706
- const { to } = view2.state.doc.lineAt(0);
4707
- view2.dispatch({
4708
- selection: {
4709
- anchor: to
4710
- }
4711
- });
4712
- }
4713
- if (state2.facet(editorMode).noTabster) {
4714
- rootRef.current?.removeAttribute("data-tabster");
4715
- }
4716
- setView(view2);
4717
- return () => {
4718
- log7("destroy", {
4719
- id,
4720
- instanceId
4721
- }, {
4722
- F: __dxlog_file11,
4723
- L: 153,
4724
- S: void 0,
4725
- C: (f, a) => f(...a)
4726
- });
4727
- view2?.destroy();
4728
- };
4729
- }, [
4730
- id,
4731
- selection,
4732
- scrollTo,
4733
- editorMode,
4734
- extensions
4735
- ]);
4736
- const handleKeyUp = useCallback((event) => {
4737
- const { key } = event;
4738
- switch (key) {
4739
- case "Enter": {
4740
- view?.focus();
4741
- break;
4742
- }
4743
- }
4744
- }, [
4745
- view
4746
- ]);
4747
- return /* @__PURE__ */ React.createElement("div", {
4748
- role: "none",
4749
- ref: rootRef,
4750
- tabIndex: 0,
4751
- className,
4752
- "data-testid": dataTestId,
4753
- ...tabsterDOMAttribute,
4754
- onKeyUp: handleKeyUp
4755
- });
4756
- });
4757
-
4758
4655
  // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
4759
- import { ChatText, Code, CodeBlock, Image, Link, ListBullets, ListChecks, ListNumbers, Paragraph, Quotes, TextStrikethrough, Table as Table2, TextB, TextHOne, TextHTwo, TextHThree, TextHFour, TextHFive, TextHSix, TextItalic, CaretDown, Check } from "@phosphor-icons/react";
4760
- import { createContext } from "@radix-ui/react-context";
4761
- import React2, { useEffect as useEffect3, useRef as useRef2, useState as useState4 } from "react";
4762
- import { useDropzone } from "react-dropzone";
4763
- import { Button, DensityProvider, DropdownMenu, ElevationProvider, Toolbar as NaturalToolbar, Tooltip, useTranslation } from "@dxos/react-ui";
4764
- import { getSize } from "@dxos/react-ui-theme";
4765
4656
  var iconStyles = getSize(5);
4766
4657
  var buttonStyles = "min-bs-0 p-2";
4767
4658
  var tooltipProps = {
4768
4659
  side: "top",
4769
4660
  classNames: "z-10"
4770
4661
  };
4771
- var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
4662
+ var ToolbarSeparator = () => /* @__PURE__ */ React.createElement("div", {
4772
4663
  role: "separator",
4773
4664
  className: "grow"
4774
4665
  });
4775
4666
  var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
4776
4667
  var ToolbarRoot = ({ children, onAction, classNames, state: state2 }) => {
4777
- return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
4668
+ return /* @__PURE__ */ React.createElement(ToolbarContextProvider, {
4778
4669
  onAction,
4779
4670
  state: state2
4780
- }, /* @__PURE__ */ React2.createElement(DensityProvider, {
4671
+ }, /* @__PURE__ */ React.createElement(DensityProvider, {
4781
4672
  density: "fine"
4782
- }, /* @__PURE__ */ React2.createElement(ElevationProvider, {
4673
+ }, /* @__PURE__ */ React.createElement(ElevationProvider, {
4783
4674
  elevation: "chrome"
4784
- }, /* @__PURE__ */ React2.createElement(NaturalToolbar.Root, {
4675
+ }, /* @__PURE__ */ React.createElement(NaturalToolbar.Root, {
4785
4676
  classNames: [
4786
- "is-full shrink-0 overflow-x-auto overflow-y-hidden p-1",
4677
+ "p-1 is-full shrink-0 overflow-x-auto overflow-y-hidden",
4787
4678
  classNames
4788
- ]
4679
+ ],
4680
+ style: {
4681
+ contain: "layout"
4682
+ }
4789
4683
  }, children))));
4790
4684
  };
4791
4685
  var ToolbarToggleButton = ({ Icon, children, ...props }) => {
4792
- return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
4686
+ return /* @__PURE__ */ React.createElement(Tooltip.Root, null, /* @__PURE__ */ React.createElement(Tooltip.Trigger, {
4793
4687
  asChild: true
4794
- }, /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroupItem, {
4688
+ }, /* @__PURE__ */ React.createElement(NaturalToolbar.ToggleGroupItem, {
4795
4689
  variant: "ghost",
4796
4690
  ...props,
4797
4691
  classNames: buttonStyles
4798
- }, /* @__PURE__ */ React2.createElement(Icon, {
4692
+ }, /* @__PURE__ */ React.createElement(Icon, {
4799
4693
  className: iconStyles
4800
- }), /* @__PURE__ */ React2.createElement("span", {
4694
+ }), /* @__PURE__ */ React.createElement("span", {
4801
4695
  className: "sr-only"
4802
- }, children))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, children, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
4696
+ }, children))), /* @__PURE__ */ React.createElement(Tooltip.Portal, null, /* @__PURE__ */ React.createElement(Tooltip.Content, tooltipProps, children, /* @__PURE__ */ React.createElement(Tooltip.Arrow, null))));
4803
4697
  };
4804
4698
  var ToolbarButton = ({ Icon, children, ...props }) => {
4805
- return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
4699
+ return /* @__PURE__ */ React.createElement(Tooltip.Root, null, /* @__PURE__ */ React.createElement(Tooltip.Trigger, {
4806
4700
  asChild: true
4807
- }, /* @__PURE__ */ React2.createElement(NaturalToolbar.Button, {
4701
+ }, /* @__PURE__ */ React.createElement(NaturalToolbar.Button, {
4808
4702
  variant: "ghost",
4809
4703
  ...props,
4810
4704
  classNames: buttonStyles
4811
- }, /* @__PURE__ */ React2.createElement(Icon, {
4705
+ }, /* @__PURE__ */ React.createElement(Icon, {
4812
4706
  className: iconStyles
4813
- }), /* @__PURE__ */ React2.createElement("span", {
4707
+ }), /* @__PURE__ */ React.createElement("span", {
4814
4708
  className: "sr-only"
4815
- }, children))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, children, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
4709
+ }, children))), /* @__PURE__ */ React.createElement(Tooltip.Portal, null, /* @__PURE__ */ React.createElement(Tooltip.Content, tooltipProps, children, /* @__PURE__ */ React.createElement(Tooltip.Arrow, null))));
4710
+ };
4711
+ var ViewModeIcons = {
4712
+ preview: PencilSimple,
4713
+ readonly: PencilSimpleSlash,
4714
+ source: MarkdownLogo
4715
+ };
4716
+ var MarkdownView = ({ mode }) => {
4717
+ const { t } = useTranslation(translationKey);
4718
+ const { onAction } = useToolbarContext("ViewMode");
4719
+ const ModeIcon = ViewModeIcons[mode ?? "preview"];
4720
+ const suppressNextTooltip = useRef(false);
4721
+ const [tooltipOpen, setTooltipOpen] = useState3(false);
4722
+ const [selectOpen, setSelectOpen] = useState3(false);
4723
+ return /* @__PURE__ */ React.createElement(Tooltip.Root, {
4724
+ open: tooltipOpen,
4725
+ onOpenChange: (nextOpen) => {
4726
+ if (nextOpen && suppressNextTooltip.current) {
4727
+ suppressNextTooltip.current = false;
4728
+ return setTooltipOpen(false);
4729
+ } else {
4730
+ return setTooltipOpen(nextOpen);
4731
+ }
4732
+ }
4733
+ }, /* @__PURE__ */ React.createElement(DropdownMenu.Root, {
4734
+ open: selectOpen,
4735
+ onOpenChange: (nextOpen) => {
4736
+ if (!nextOpen) {
4737
+ suppressNextTooltip.current = true;
4738
+ }
4739
+ return setSelectOpen(nextOpen);
4740
+ }
4741
+ }, /* @__PURE__ */ React.createElement(Tooltip.Trigger, {
4742
+ asChild: true
4743
+ }, /* @__PURE__ */ React.createElement(NaturalToolbar.Button, {
4744
+ asChild: true
4745
+ }, /* @__PURE__ */ React.createElement(DropdownMenu.Trigger, {
4746
+ asChild: true
4747
+ }, /* @__PURE__ */ React.createElement(Button, {
4748
+ variant: "ghost",
4749
+ classNames: buttonStyles
4750
+ }, /* @__PURE__ */ React.createElement("span", {
4751
+ className: "sr-only"
4752
+ }, t("mode label")), /* @__PURE__ */ React.createElement(ModeIcon, {
4753
+ className: iconStyles
4754
+ }), /* @__PURE__ */ React.createElement(CaretDown, null))))), /* @__PURE__ */ React.createElement(DropdownMenu.Portal, null, /* @__PURE__ */ React.createElement(DropdownMenu.Content, {
4755
+ classNames: "is-min md:is-min",
4756
+ onCloseAutoFocus: (e) => e.preventDefault()
4757
+ }, /* @__PURE__ */ React.createElement(DropdownMenu.Viewport, null, EditorViewModes.map((value) => {
4758
+ const Icon = ViewModeIcons[value];
4759
+ return /* @__PURE__ */ React.createElement(DropdownMenu.CheckboxItem, {
4760
+ key: value,
4761
+ checked: value === mode,
4762
+ onClick: () => onAction?.({
4763
+ type: "view-mode",
4764
+ data: value
4765
+ })
4766
+ }, /* @__PURE__ */ React.createElement(Icon, {
4767
+ className: iconStyles
4768
+ }), /* @__PURE__ */ React.createElement("span", {
4769
+ className: "whitespace-nowrap grow"
4770
+ }, t(`${value} mode label`)), /* @__PURE__ */ React.createElement(Check, {
4771
+ className: value === mode ? "visible" : "invisible"
4772
+ }));
4773
+ })), /* @__PURE__ */ React.createElement(DropdownMenu.Arrow, null)))), /* @__PURE__ */ React.createElement(Tooltip.Portal, null, /* @__PURE__ */ React.createElement(Tooltip.Content, tooltipProps, t("view mode label"), /* @__PURE__ */ React.createElement(Tooltip.Arrow, null))));
4816
4774
  };
4817
4775
  var HeadingIcons = {
4818
4776
  "0": Paragraph,
@@ -4830,10 +4788,10 @@ var MarkdownHeading = () => {
4830
4788
  const header = blockType && /heading(\d)/.exec(blockType);
4831
4789
  const value = header ? header[1] : blockType === "paragraph" || !blockType ? "0" : void 0;
4832
4790
  const HeadingIcon = HeadingIcons[value ?? "0"];
4833
- const suppressNextTooltip = useRef2(false);
4834
- const [tooltipOpen, setTooltipOpen] = useState4(false);
4835
- const [selectOpen, setSelectOpen] = useState4(false);
4836
- return /* @__PURE__ */ React2.createElement(Tooltip.Root, {
4791
+ const suppressNextTooltip = useRef(false);
4792
+ const [tooltipOpen, setTooltipOpen] = useState3(false);
4793
+ const [selectOpen, setSelectOpen] = useState3(false);
4794
+ return /* @__PURE__ */ React.createElement(Tooltip.Root, {
4837
4795
  open: tooltipOpen,
4838
4796
  onOpenChange: (nextOpen) => {
4839
4797
  if (nextOpen && suppressNextTooltip.current) {
@@ -4843,7 +4801,7 @@ var MarkdownHeading = () => {
4843
4801
  return setTooltipOpen(nextOpen);
4844
4802
  }
4845
4803
  }
4846
- }, /* @__PURE__ */ React2.createElement(DropdownMenu.Root, {
4804
+ }, /* @__PURE__ */ React.createElement(DropdownMenu.Root, {
4847
4805
  open: selectOpen,
4848
4806
  onOpenChange: (nextOpen) => {
4849
4807
  if (!nextOpen) {
@@ -4851,40 +4809,40 @@ var MarkdownHeading = () => {
4851
4809
  }
4852
4810
  return setSelectOpen(nextOpen);
4853
4811
  }
4854
- }, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
4812
+ }, /* @__PURE__ */ React.createElement(Tooltip.Trigger, {
4855
4813
  asChild: true
4856
- }, /* @__PURE__ */ React2.createElement(NaturalToolbar.Button, {
4814
+ }, /* @__PURE__ */ React.createElement(NaturalToolbar.Button, {
4857
4815
  asChild: true
4858
- }, /* @__PURE__ */ React2.createElement(DropdownMenu.Trigger, {
4816
+ }, /* @__PURE__ */ React.createElement(DropdownMenu.Trigger, {
4859
4817
  asChild: true
4860
- }, /* @__PURE__ */ React2.createElement(Button, {
4818
+ }, /* @__PURE__ */ React.createElement(Button, {
4861
4819
  variant: "ghost",
4862
4820
  classNames: buttonStyles,
4863
4821
  disabled: value === null
4864
- }, /* @__PURE__ */ React2.createElement("span", {
4822
+ }, /* @__PURE__ */ React.createElement("span", {
4865
4823
  className: "sr-only"
4866
- }, t("heading label")), /* @__PURE__ */ React2.createElement(HeadingIcon, {
4824
+ }, t("heading label")), /* @__PURE__ */ React.createElement(HeadingIcon, {
4867
4825
  className: iconStyles
4868
- }), /* @__PURE__ */ React2.createElement(CaretDown, null))))), /* @__PURE__ */ React2.createElement(DropdownMenu.Portal, null, /* @__PURE__ */ React2.createElement(DropdownMenu.Content, {
4826
+ }), /* @__PURE__ */ React.createElement(CaretDown, null))))), /* @__PURE__ */ React.createElement(DropdownMenu.Portal, null, /* @__PURE__ */ React.createElement(DropdownMenu.Content, {
4869
4827
  classNames: "is-min md:is-min",
4870
4828
  onCloseAutoFocus: (e) => e.preventDefault()
4871
- }, /* @__PURE__ */ React2.createElement(DropdownMenu.Viewport, null, Object.keys(HeadingIcons).map((level) => {
4829
+ }, /* @__PURE__ */ React.createElement(DropdownMenu.Viewport, null, Object.keys(HeadingIcons).map((level) => {
4872
4830
  const Icon = HeadingIcons[level];
4873
- return /* @__PURE__ */ React2.createElement(DropdownMenu.CheckboxItem, {
4831
+ return /* @__PURE__ */ React.createElement(DropdownMenu.CheckboxItem, {
4874
4832
  key: level,
4875
4833
  checked: value === level,
4876
4834
  onClick: () => onAction?.({
4877
4835
  type: "heading",
4878
4836
  data: level
4879
4837
  })
4880
- }, /* @__PURE__ */ React2.createElement("span", {
4838
+ }, /* @__PURE__ */ React.createElement("span", {
4881
4839
  className: "sr-only"
4882
4840
  }, t("heading level label", {
4883
4841
  count: parseInt(level)
4884
- })), /* @__PURE__ */ React2.createElement(Icon, {
4842
+ })), /* @__PURE__ */ React.createElement(Icon, {
4885
4843
  className: iconStyles
4886
- }), /* @__PURE__ */ React2.createElement(DropdownMenu.ItemIndicator, null, /* @__PURE__ */ React2.createElement(Check, null)));
4887
- })), /* @__PURE__ */ React2.createElement(DropdownMenu.Arrow, null)))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, t("heading label"), /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
4844
+ }), /* @__PURE__ */ React.createElement(DropdownMenu.ItemIndicator, null, /* @__PURE__ */ React.createElement(Check, null)));
4845
+ })), /* @__PURE__ */ React.createElement(DropdownMenu.Arrow, null)))), /* @__PURE__ */ React.createElement(Tooltip.Portal, null, /* @__PURE__ */ React.createElement(Tooltip.Content, tooltipProps, t("heading label"), /* @__PURE__ */ React.createElement(Tooltip.Arrow, null))));
4888
4846
  };
4889
4847
  var markdownStyles = [
4890
4848
  {
@@ -4916,10 +4874,10 @@ var markdownStyles = [
4916
4874
  var MarkdownStyles = () => {
4917
4875
  const { onAction, state: state2 } = useToolbarContext("MarkdownStyles");
4918
4876
  const { t } = useTranslation(translationKey);
4919
- return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
4877
+ return /* @__PURE__ */ React.createElement(NaturalToolbar.ToggleGroup, {
4920
4878
  type: "multiple",
4921
4879
  value: markdownStyles.filter(({ getState }) => state2 && getState(state2)).map(({ type }) => type)
4922
- }, markdownStyles.map(({ type, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarToggleButton, {
4880
+ }, markdownStyles.map(({ type, getState, Icon }) => /* @__PURE__ */ React.createElement(ToolbarToggleButton, {
4923
4881
  key: type,
4924
4882
  value: type,
4925
4883
  Icon,
@@ -4950,10 +4908,10 @@ var markdownLists = [
4950
4908
  var MarkdownLists = () => {
4951
4909
  const { onAction, state: state2 } = useToolbarContext("MarkdownStyles");
4952
4910
  const { t } = useTranslation(translationKey);
4953
- return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
4911
+ return /* @__PURE__ */ React.createElement(NaturalToolbar.ToggleGroup, {
4954
4912
  type: "single",
4955
4913
  value: state2?.listStyle ? `list-${state2.listStyle}` : ""
4956
- }, markdownLists.map(({ type, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarToggleButton, {
4914
+ }, markdownLists.map(({ type, getState, Icon }) => /* @__PURE__ */ React.createElement(ToolbarToggleButton, {
4957
4915
  key: type,
4958
4916
  value: type,
4959
4917
  Icon,
@@ -4985,10 +4943,10 @@ var MarkdownBlocks = () => {
4985
4943
  const { onAction, state: state2 } = useToolbarContext("MarkdownStyles");
4986
4944
  const { t } = useTranslation(translationKey);
4987
4945
  const value = markdownBlocks.find(({ getState }) => state2 && getState(state2));
4988
- return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
4946
+ return /* @__PURE__ */ React.createElement(NaturalToolbar.ToggleGroup, {
4989
4947
  type: "single",
4990
4948
  value: value?.type ?? ""
4991
- }, markdownBlocks.map(({ type, disabled, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarToggleButton, {
4949
+ }, markdownBlocks.map(({ type, disabled, getState, Icon }) => /* @__PURE__ */ React.createElement(ToolbarToggleButton, {
4992
4950
  key: type,
4993
4951
  value: type,
4994
4952
  Icon,
@@ -4999,7 +4957,7 @@ var MarkdownBlocks = () => {
4999
4957
  }) : void 0
5000
4958
  }, t(`${type} label`))));
5001
4959
  };
5002
- var MarkdownStandard = () => /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(MarkdownHeading, null), /* @__PURE__ */ React2.createElement(MarkdownStyles, null), /* @__PURE__ */ React2.createElement(MarkdownLists, null), /* @__PURE__ */ React2.createElement(MarkdownBlocks, null));
4960
+ var MarkdownStandard = () => /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(MarkdownHeading, null), /* @__PURE__ */ React.createElement(MarkdownStyles, null), /* @__PURE__ */ React.createElement(MarkdownLists, null), /* @__PURE__ */ React.createElement(MarkdownBlocks, null));
5003
4961
  var MarkdownCustom = ({ onUpload } = {}) => {
5004
4962
  const { onAction } = useToolbarContext("MarkdownStyles");
5005
4963
  const { t } = useTranslation(translationKey);
@@ -5015,7 +4973,7 @@ var MarkdownCustom = ({ onUpload } = {}) => {
5015
4973
  ]
5016
4974
  }
5017
4975
  });
5018
- useEffect3(() => {
4976
+ useEffect2(() => {
5019
4977
  if (onUpload && acceptedFiles.length) {
5020
4978
  requestAnimationFrame(async () => {
5021
4979
  const f = acceptedFiles[0];
@@ -5037,7 +4995,7 @@ var MarkdownCustom = ({ onUpload } = {}) => {
5037
4995
  }, [
5038
4996
  acceptedFiles
5039
4997
  ]);
5040
- return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("input", getInputProps()), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4998
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("input", getInputProps()), /* @__PURE__ */ React.createElement(ToolbarButton, {
5041
4999
  value: "image",
5042
5000
  Icon: Image,
5043
5001
  onClick: () => open()
@@ -5046,7 +5004,7 @@ var MarkdownCustom = ({ onUpload } = {}) => {
5046
5004
  var MarkdownActions = () => {
5047
5005
  const { onAction, state: state2 } = useToolbarContext("MarkdownActions");
5048
5006
  const { t } = useTranslation(translationKey);
5049
- return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(ToolbarButton, {
5007
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ToolbarButton, {
5050
5008
  value: "comment",
5051
5009
  Icon: ChatText,
5052
5010
  "data-testid": "editor.toolbar.comment",
@@ -5060,6 +5018,7 @@ var Toolbar = {
5060
5018
  Root: ToolbarRoot,
5061
5019
  Button: ToolbarToggleButton,
5062
5020
  Separator: ToolbarSeparator,
5021
+ View: MarkdownView,
5063
5022
  Markdown: MarkdownStandard,
5064
5023
  Custom: MarkdownCustom,
5065
5024
  Actions: MarkdownActions
@@ -5071,51 +5030,71 @@ var useActionHandler = (view) => {
5071
5030
  };
5072
5031
 
5073
5032
  // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
5074
- import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
5075
- import { EditorView as EditorView17 } from "@codemirror/view";
5076
- import { useFocusableGroup as useFocusableGroup2 } from "@fluentui/react-tabster";
5077
- import { useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState5 } from "react";
5078
- import { log as log8 } from "@dxos/log";
5079
- import { isNotFalsy as isNotFalsy5 } from "@dxos/util";
5080
- var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
5081
- var useTextEditor = (cb = () => ({}), deps = []) => {
5082
- let { id, doc, selection, extensions, autoFocus, scrollTo, debug } = useMemo3(cb, deps ?? []);
5083
- const onUpdate = useRef3();
5084
- const [view, setView] = useState5();
5085
- const parentRef = useRef3(null);
5086
- useEffect4(() => {
5033
+ import { EditorState as EditorState2 } from "@codemirror/state";
5034
+ import { EditorView as EditorView16 } from "@codemirror/view";
5035
+ import { useFocusableGroup } from "@fluentui/react-tabster";
5036
+ import { useCallback, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2, useState as useState4 } from "react";
5037
+ import { log as log7 } from "@dxos/log";
5038
+ import { useDefaultValue } from "@dxos/react-ui";
5039
+ import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
5040
+ var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
5041
+ var instanceCount = 0;
5042
+ var useTextEditor = (props = {}, deps = []) => {
5043
+ const { id, initialValue, selection, extensions, autoFocus, scrollTo: _scrollTo, moveToEndOfLine, debug } = useMemo3(() => {
5044
+ return typeof props === "function" ? props() : props;
5045
+ }, deps ?? []);
5046
+ const [instanceId] = useState4(() => `text-editor-${++instanceCount}`);
5047
+ const scrollTo = useDefaultValue(_scrollTo, EditorView16.scrollIntoView(0, {
5048
+ yMargin: 0
5049
+ }));
5050
+ const onUpdate = useRef2();
5051
+ const [view, setView] = useState4();
5052
+ const parentRef = useRef2(null);
5053
+ useEffect3(() => {
5087
5054
  let view2;
5088
5055
  if (parentRef.current) {
5089
- log8("create", {
5090
- id
5056
+ log7("create", {
5057
+ id,
5058
+ instanceId,
5059
+ doc: initialValue?.length ?? 0
5091
5060
  }, {
5092
- F: __dxlog_file12,
5093
- L: 50,
5061
+ F: __dxlog_file11,
5062
+ L: 86,
5094
5063
  S: void 0,
5095
5064
  C: (f, a) => f(...a)
5096
5065
  });
5097
- const state2 = EditorState3.create({
5098
- doc,
5066
+ let initialSelection = selection;
5067
+ if (moveToEndOfLine && selection === void 0) {
5068
+ const index = initialValue?.indexOf("\n");
5069
+ const anchor = !index || index === -1 ? 0 : index;
5070
+ initialSelection = {
5071
+ anchor
5072
+ };
5073
+ }
5074
+ const state2 = EditorState2.create({
5075
+ doc: initialValue,
5076
+ selection: initialSelection,
5099
5077
  extensions: [
5100
5078
  id && documentId2.of(id),
5101
5079
  // TODO(burdon): Doesn't catch errors in keymap functions.
5102
- EditorView17.exceptionSink.of((err) => {
5103
- log8.catch(err, void 0, {
5104
- F: __dxlog_file12,
5105
- L: 60,
5080
+ EditorView16.exceptionSink.of((err) => {
5081
+ log7.catch(err, void 0, {
5082
+ F: __dxlog_file11,
5083
+ L: 104,
5106
5084
  S: void 0,
5107
5085
  C: (f, a) => f(...a)
5108
5086
  });
5109
5087
  }),
5110
5088
  extensions,
5111
- EditorView17.updateListener.of(() => {
5089
+ EditorView16.updateListener.of(() => {
5112
5090
  onUpdate.current?.();
5113
5091
  })
5114
- ].filter(isNotFalsy5)
5092
+ ].filter(isNotFalsy4)
5115
5093
  });
5116
- view2 = new EditorView17({
5094
+ view2 = new EditorView16({
5117
5095
  parent: parentRef.current,
5118
5096
  scrollTo,
5097
+ selection: initialSelection,
5119
5098
  state: state2,
5120
5099
  // NOTE: Uncomment to debug/monitor all transactions.
5121
5100
  // https://codemirror.net/docs/ref/#view.EditorView.dispatch
@@ -5126,30 +5105,34 @@ var useTextEditor = (cb = () => ({}), deps = []) => {
5126
5105
  view3.update(trs);
5127
5106
  }
5128
5107
  });
5108
+ if (!initialValue && moveToEndOfLine) {
5109
+ const { to } = view2.state.doc.lineAt(0);
5110
+ view2.dispatch({
5111
+ selection: {
5112
+ anchor: to
5113
+ }
5114
+ });
5115
+ }
5129
5116
  setView(view2);
5130
5117
  }
5131
5118
  return () => {
5132
- log8("destroy", {
5119
+ log7("destroy", {
5133
5120
  id
5134
5121
  }, {
5135
- F: __dxlog_file12,
5136
- L: 88,
5122
+ F: __dxlog_file11,
5123
+ L: 139,
5137
5124
  S: void 0,
5138
5125
  C: (f, a) => f(...a)
5139
5126
  });
5140
5127
  view2?.destroy();
5141
5128
  };
5142
5129
  }, deps);
5143
- useEffect4(() => {
5130
+ useEffect3(() => {
5144
5131
  if (view) {
5145
- if (!selection && !view.state.selection.main.anchor) {
5146
- selection = EditorSelection2.single(view.state.doc.line(1).to);
5147
- }
5148
- if (selection || scrollTo) {
5132
+ if (scrollTo) {
5149
5133
  onUpdate.current = () => {
5150
5134
  onUpdate.current = void 0;
5151
5135
  view.dispatch({
5152
- selection,
5153
5136
  effects: scrollTo && [
5154
5137
  scrollTo
5155
5138
  ],
@@ -5157,20 +5140,27 @@ var useTextEditor = (cb = () => ({}), deps = []) => {
5157
5140
  });
5158
5141
  };
5159
5142
  }
5160
- if (autoFocus) {
5161
- view.focus();
5143
+ if (view.state.facet(editorInputMode).noTabster) {
5144
+ parentRef.current?.removeAttribute("data-tabster");
5162
5145
  }
5163
5146
  }
5164
5147
  }, [
5165
5148
  view,
5166
- autoFocus,
5167
5149
  selection,
5168
5150
  scrollTo
5169
5151
  ]);
5170
- const focusableGroup = useFocusableGroup2({
5152
+ useEffect3(() => {
5153
+ if (view && autoFocus) {
5154
+ view.focus();
5155
+ }
5156
+ }, [
5157
+ autoFocus,
5158
+ view
5159
+ ]);
5160
+ const focusableGroup = useFocusableGroup({
5171
5161
  tabBehavior: "limited"
5172
5162
  });
5173
- const handleKeyUp = useCallback2((event) => {
5163
+ const handleKeyUp = useCallback((event) => {
5174
5164
  const { key, target, currentTarget } = event;
5175
5165
  if (target === currentTarget) {
5176
5166
  switch (key) {
@@ -5196,12 +5186,13 @@ var useTextEditor = (cb = () => ({}), deps = []) => {
5196
5186
  };
5197
5187
  export {
5198
5188
  Cursor,
5199
- EditorModes,
5189
+ EditorInputModes,
5190
+ EditorViewModes,
5200
5191
  Inline,
5192
+ InputModeExtensions,
5201
5193
  List,
5202
5194
  RemoteSelectionsDecorator,
5203
5195
  SpaceAwarenessProvider,
5204
- TextEditor,
5205
5196
  TextKind,
5206
5197
  Toolbar,
5207
5198
  addBlockquote,
@@ -5225,6 +5216,7 @@ export {
5225
5216
  createExternalCommentSync,
5226
5217
  createMarkdownExtensions,
5227
5218
  createThemeExtensions,
5219
+ debugNodeLogger,
5228
5220
  decorateMarkdown,
5229
5221
  defaultOptions,
5230
5222
  defaultTheme,
@@ -5232,7 +5224,7 @@ export {
5232
5224
  dropFile,
5233
5225
  editorFillLayoutEditor,
5234
5226
  editorFillLayoutRoot,
5235
- editorMode,
5227
+ editorInputMode,
5236
5228
  editorWithToolbarLayout,
5237
5229
  focusEvent,
5238
5230
  formattingEquals,