@dxos/react-ui-editor 0.3.9-main.f27ec14 → 0.3.9-next.35264cb

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 (56) hide show
  1. package/dist/lib/browser/index.mjs +412 -56
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/automerge/automerge-plugin/index.d.ts +1 -1
  5. package/dist/types/src/automerge/automerge-plugin/index.d.ts.map +1 -1
  6. package/dist/types/src/automerge/automerge.stories.d.ts.map +1 -1
  7. package/dist/types/src/components/Editor/Editor.d.ts +0 -1
  8. package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
  9. package/dist/types/src/components/Editor/Editor.stories.d.ts +3 -2
  10. package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
  11. package/dist/types/src/components/Markdown/Markdown.d.ts +4 -2
  12. package/dist/types/src/components/Markdown/Markdown.d.ts.map +1 -1
  13. package/dist/types/src/components/Markdown/Markdown.stories.d.ts +2 -1
  14. package/dist/types/src/components/Markdown/Markdown.stories.d.ts.map +1 -1
  15. package/dist/types/src/components/Markdown/markdownTheme.d.ts +10 -17
  16. package/dist/types/src/components/Markdown/markdownTheme.d.ts.map +1 -1
  17. package/dist/types/src/components/RichText/RichText.stories.d.ts +2 -1
  18. package/dist/types/src/components/RichText/RichText.stories.d.ts.map +1 -1
  19. package/dist/types/src/components/TextEditor/TextEditor.d.ts +39 -0
  20. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -0
  21. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +18 -0
  22. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -0
  23. package/dist/types/src/components/TextEditor/extensions/hyperlinkDecoration.d.ts +11 -0
  24. package/dist/types/src/components/TextEditor/extensions/hyperlinkDecoration.d.ts.map +1 -0
  25. package/dist/types/src/components/TextEditor/extensions/hyperlinkTooltip.d.ts +8 -0
  26. package/dist/types/src/components/TextEditor/extensions/hyperlinkTooltip.d.ts.map +1 -0
  27. package/dist/types/src/components/TextEditor/extensions/hyperlinkWidget.d.ts +10 -0
  28. package/dist/types/src/components/TextEditor/extensions/hyperlinkWidget.d.ts.map +1 -0
  29. package/dist/types/src/components/TextEditor/extensions/index.d.ts +4 -0
  30. package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -0
  31. package/dist/types/src/components/TextEditor/index.d.ts +6 -0
  32. package/dist/types/src/components/TextEditor/index.d.ts.map +1 -0
  33. package/dist/types/src/components/TextEditor/theme.d.ts +9 -0
  34. package/dist/types/src/components/TextEditor/theme.d.ts.map +1 -0
  35. package/dist/types/src/components/index.d.ts +1 -0
  36. package/dist/types/src/components/index.d.ts.map +1 -1
  37. package/dist/types/src/testing/replicator.d.ts.map +1 -1
  38. package/package.json +21 -17
  39. package/src/automerge/automerge-plugin/index.ts +1 -1
  40. package/src/automerge/automerge.stories.tsx +1 -2
  41. package/src/components/Editor/Editor.stories.tsx +4 -2
  42. package/src/components/Editor/Editor.tsx +6 -3
  43. package/src/components/Markdown/Markdown.stories.tsx +15 -1
  44. package/src/components/Markdown/Markdown.tsx +63 -30
  45. package/src/components/Markdown/markdownTheme.ts +32 -17
  46. package/src/components/RichText/RichText.stories.tsx +2 -0
  47. package/src/components/TextEditor/TextEditor.stories.tsx +72 -0
  48. package/src/components/TextEditor/TextEditor.tsx +131 -0
  49. package/src/components/TextEditor/extensions/hyperlinkDecoration.ts +99 -0
  50. package/src/components/TextEditor/extensions/hyperlinkTooltip.tsx +61 -0
  51. package/src/components/TextEditor/extensions/hyperlinkWidget.tsx +119 -0
  52. package/src/components/TextEditor/extensions/index.ts +7 -0
  53. package/src/components/TextEditor/index.ts +11 -0
  54. package/src/components/TextEditor/theme.ts +35 -0
  55. package/src/components/index.ts +1 -0
  56. package/src/testing/replicator.ts +0 -1
@@ -261,10 +261,7 @@ import { tailwindConfig } from "@dxos/react-ui-theme";
261
261
  var tokens = tailwindConfig({}).theme;
262
262
 
263
263
  // packages/ui/react-ui-editor/src/components/Markdown/markdownTheme.ts
264
- var ivory = "#abb2bf";
265
264
  var malibu = "#61afef";
266
- var highlightBackground = "#2c313a";
267
- var tooltipBackground = "#353a42";
268
265
  var cursor = "#ffffff";
269
266
  var monospace = get(tokens, "fontFamily.mono", [
270
267
  "monospace"
@@ -307,24 +304,28 @@ var markdownTheme = {
307
304
  // border: 'none',
308
305
  // color: '#ddd'
309
306
  // },
310
- ".dark & .cm-tooltip": {
311
- border: "none",
312
- backgroundColor: tooltipBackground
313
- },
314
- ".dark & .cm-tooltip .cm-tooltip-arrow:before": {
315
- borderTopColor: "transparent",
316
- borderBottomColor: "transparent"
317
- },
318
- ".dark & .cm-tooltip .cm-tooltip-arrow:after": {
319
- borderTopColor: tooltipBackground,
320
- borderBottomColor: tooltipBackground
321
- },
322
- ".dark & .cm-tooltip-autocomplete": {
323
- "& > ul > li[aria-selected]": {
324
- backgroundColor: highlightBackground,
325
- color: ivory
326
- }
307
+ ".cm-tooltip": {
308
+ backgroundColor: "transparent",
309
+ border: "none"
327
310
  },
311
+ // '.dark & .cm-tooltip': {
312
+ // border: 'none',
313
+ // backgroundColor: tooltipBackground,
314
+ // },
315
+ // '.dark & .cm-tooltip .cm-tooltip-arrow:before': {
316
+ // borderTopColor: 'transparent',
317
+ // borderBottomColor: 'transparent',
318
+ // },
319
+ // '.dark & .cm-tooltip .cm-tooltip-arrow:after': {
320
+ // borderTopColor: tooltipBackground,
321
+ // borderBottomColor: tooltipBackground,
322
+ // },
323
+ // '.dark & .cm-tooltip-autocomplete': {
324
+ // '& > ul > li[aria-selected]': {
325
+ // backgroundColor: highlightBackground,
326
+ // color: ivory,
327
+ // },
328
+ // },
328
329
  "&.cm-focused": {
329
330
  outline: "none"
330
331
  },
@@ -396,6 +397,13 @@ var markdownTheme = {
396
397
  "& .cm-yLineSelection": {
397
398
  margin: "0"
398
399
  },
400
+ ".cm-link": {
401
+ color: "rgb(20 89 208)",
402
+ textDecorationLine: "underline",
403
+ textDecorationThickness: "1px",
404
+ textUnderlineOffset: "2px",
405
+ borderRadius: ".125rem"
406
+ },
399
407
  ...Object.keys(get(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
400
408
  const height = get(tokens, [
401
409
  "extend",
@@ -416,7 +424,7 @@ var markdownTheme = {
416
424
  return acc;
417
425
  }, {})
418
426
  };
419
- var markdownDarkHighlighting = HighlightStyle.define([
427
+ var markdownHighlightStyle = HighlightStyle.define([
420
428
  {
421
429
  tag: [
422
430
  tags.keyword,
@@ -541,7 +549,7 @@ var EditorModes = [
541
549
  "default",
542
550
  "vim"
543
551
  ];
544
- var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, onChange, editorMode }, forwardedRef) => {
552
+ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, editorMode, extensions = [], onChange }, forwardedRef) => {
545
553
  const { id, content, provider, peer } = model ?? {};
546
554
  const { themeMode } = useThemeContext();
547
555
  const tabsterDOMAttribute = useFocusableGroup({
@@ -593,49 +601,56 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, onChange,
593
601
  const state2 = EditorState.create({
594
602
  doc: content?.toString(),
595
603
  extensions: [
596
- // Based on https://github.com/codemirror/dev/issues/44#issuecomment-789093799.
604
+ // Based on https://github.com/codemirror/dev/issues/44#issuecomment-789093799
597
605
  listenChangesExtension,
598
- ...editorMode === "vim" ? [
599
- vim()
600
- ] : [],
606
+ // TODO(burdon): Create custom bundle to make this class more reusable and retire simpler TextEditor component.
601
607
  // All of https://github.com/codemirror/basic-setup minus line numbers and fold gutter.
608
+ // https://codemirror.net/docs/ref/#codemirror.basicSetup
609
+ autocompletion(),
610
+ bracketMatching(),
611
+ closeBrackets(),
612
+ crosshairCursor(),
613
+ dropCursor(),
614
+ drawSelection(),
615
+ highlightActiveLine(),
602
616
  highlightActiveLineGutter(),
617
+ highlightSelectionMatches(),
603
618
  highlightSpecialChars(),
604
619
  history(),
605
- drawSelection(),
606
- dropCursor(),
607
- EditorState.allowMultipleSelections.of(true),
608
620
  indentOnInput(),
621
+ placeholder(slots.editor?.placeholder ?? ""),
622
+ rectangularSelection(),
609
623
  syntaxHighlighting(defaultHighlightStyle, {
610
624
  fallback: true
611
625
  }),
612
- bracketMatching(),
613
- closeBrackets(),
614
- autocompletion(),
615
- rectangularSelection(),
616
- crosshairCursor(),
617
- highlightActiveLine(),
618
- highlightSelectionMatches(),
619
- placeholder(slots.editor?.placeholder ?? ""),
626
+ EditorState.allowMultipleSelections.of(true),
627
+ EditorView.lineWrapping,
620
628
  keymap.of([
621
629
  ...closeBracketsKeymap,
630
+ ...completionKeymap,
622
631
  ...defaultKeymap,
623
- ...searchKeymap,
624
- ...historyKeymap,
625
632
  ...foldKeymap,
626
- ...completionKeymap,
633
+ ...historyKeymap,
627
634
  ...lintKeymap,
635
+ ...searchKeymap,
628
636
  indentWithTab
629
637
  ]),
630
- EditorView.lineWrapping,
631
- // Theme
638
+ // Main extension.
639
+ // https://github.com/codemirror/lang-markdown
632
640
  markdown({
633
641
  base: markdownLanguage2,
634
642
  codeLanguages: languages,
635
643
  extensions: [
644
+ // TODO(burdon): This seems to upgrade the parser.
645
+ // GitHub flavored markdown bundle: Table, TaskList, Strikethrough, and Autolink.
646
+ // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
647
+ // https://github.github.com/gfm
648
+ // GFM,
649
+ // Custom styling.
636
650
  markdownTagsExtension
637
651
  ]
638
652
  }),
653
+ // Theme.
639
654
  EditorView.theme({
640
655
  ...markdownTheme,
641
656
  ...slots.editor?.markdownTheme
@@ -646,11 +661,19 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, onChange,
646
661
  syntaxHighlighting(defaultHighlightStyle)
647
662
  ],
648
663
  // TODO(thure): All but one rule here apply to both themes; rename or refactor.
649
- syntaxHighlighting(markdownDarkHighlighting),
650
- // Collaboration
664
+ syntaxHighlighting(markdownHighlightStyle),
665
+ // Settings.
666
+ ...editorMode === "vim" ? [
667
+ vim()
668
+ ] : [],
669
+ // TODO(burdon): Move to extensions.
670
+ // Replication and awareness (incl. remote selection).
671
+ // https://codemirror.net/docs/ref/#collab
651
672
  ...content instanceof YText ? [
652
673
  yCollab(content, provider?.awareness)
653
- ] : []
674
+ ] : [],
675
+ // Custom.
676
+ ...extensions
654
677
  ]
655
678
  });
656
679
  setState(state2);
@@ -671,14 +694,17 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, onChange,
671
694
  themeMode,
672
695
  editorMode
673
696
  ]);
674
- const handleKeyUp = useCallback(({ key, altKey, shiftKey, metaKey, ctrlKey }) => {
697
+ const handleKeyUp = useCallback((event) => {
698
+ const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
675
699
  switch (key) {
676
- case "Enter":
700
+ case "Enter": {
677
701
  view?.contentDOM.focus();
678
702
  break;
679
- case "Escape":
703
+ }
704
+ case "Escape": {
680
705
  editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey) && parent?.focus();
681
706
  break;
707
+ }
682
708
  }
683
709
  }, [
684
710
  view,
@@ -686,11 +712,11 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, onChange,
686
712
  ]);
687
713
  return /* @__PURE__ */ React.createElement("div", {
688
714
  tabIndex: 0,
715
+ ref: setParent,
689
716
  key: id,
690
717
  ...slots.root,
691
- onKeyUp: handleKeyUp,
692
718
  ...editorMode !== "vim" ? tabsterDOMAttribute : {},
693
- ref: setParent
719
+ onKeyUp: handleKeyUp
694
720
  });
695
721
  });
696
722
 
@@ -706,7 +732,7 @@ import StarterKit from "@tiptap/starter-kit";
706
732
  import React2, { forwardRef as forwardRef2, useImperativeHandle as useImperativeHandle2, useMemo as useMemo3 } from "react";
707
733
  import { generateName as generateName2 } from "@dxos/display-name";
708
734
  import { mx as mx2 } from "@dxos/react-ui-theme";
709
- var useEditor = ({ model, placeholder: placeholder2 = "Enter text\u2026", slots = {} }) => {
735
+ var useEditor = ({ model, placeholder: placeholder3 = "Enter text\u2026", slots = {} }) => {
710
736
  const extensions = useMemo3(() => [
711
737
  StarterKit.configure({
712
738
  // Extensions
@@ -811,7 +837,7 @@ var useEditor = ({ model, placeholder: placeholder2 = "Enter text\u2026", slots
811
837
  })
812
838
  ] : [],
813
839
  Placeholder.configure({
814
- placeholder: placeholder2,
840
+ placeholder: placeholder3,
815
841
  emptyEditorClass: "before:content-[attr(data-placeholder)] before:absolute opacity-50 cursor-text"
816
842
  })
817
843
  ], [
@@ -842,8 +868,11 @@ var RichTextEditor = /* @__PURE__ */ forwardRef2((props, ref) => {
842
868
  });
843
869
 
844
870
  // packages/ui/react-ui-editor/src/components/Editor/Editor.tsx
845
- var Editor = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef3(({ slots, ...options }, forwardedRef) => {
846
- const model = useTextModel(options);
871
+ var Editor = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef3(({ slots, ...params }, forwardedRef) => {
872
+ const model = useTextModel(params);
873
+ if (!model) {
874
+ return null;
875
+ }
847
876
  if (model?.content instanceof YXmlFragment) {
848
877
  return /* @__PURE__ */ React3.createElement(RichTextEditor, {
849
878
  ref: forwardedRef,
@@ -859,18 +888,345 @@ var Editor = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef3(({ slots, ...optio
859
888
  }
860
889
  }));
861
890
 
891
+ // packages/ui/react-ui-editor/src/components/TextEditor/index.ts
892
+ import { tags as tags2 } from "@lezer/highlight";
893
+
894
+ // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
895
+ import { closeBrackets as closeBrackets2 } from "@codemirror/autocomplete";
896
+ import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
897
+ import { EditorState as EditorState2 } from "@codemirror/state";
898
+ import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
899
+ import { EditorView as EditorView2, placeholder as placeholder2 } from "@codemirror/view";
900
+ import React4, { forwardRef as forwardRef4, useEffect as useEffect2, useImperativeHandle as useImperativeHandle3, useState as useState2, useCallback as useCallback2 } from "react";
901
+ import { yCollab as yCollab2 } from "y-codemirror.next";
902
+ import { useThemeContext as useThemeContext2 } from "@dxos/react-ui";
903
+ import { YText as YText2 } from "@dxos/text-model";
904
+
905
+ // packages/ui/react-ui-editor/src/components/TextEditor/theme.ts
906
+ import get2 from "lodash.get";
907
+ import { tailwindConfig as tailwindConfig2 } from "@dxos/react-ui-theme";
908
+ var tokens2 = tailwindConfig2({}).theme;
909
+ var defaultStyles = {
910
+ "&.cm-focused": {
911
+ outline: "none"
912
+ },
913
+ ".cm-scroller": {
914
+ fontFamily: get2(tokens2, "fontFamily.body", []).join(","),
915
+ overflow: "visible"
916
+ },
917
+ ".cm-tooltip": {
918
+ backgroundColor: "transparent",
919
+ border: "none"
920
+ },
921
+ ".cm-link": {
922
+ color: get2(tokens2, "extend.colors.primary.500"),
923
+ textDecoration: "underline"
924
+ }
925
+ };
926
+
927
+ // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
928
+ var TextEditor = /* @__PURE__ */ forwardRef4(({ model, extensions = [], theme = defaultStyles, slots = {}, onKeyDown, ...props }, forwardedRef) => {
929
+ const { id, content } = model ?? {};
930
+ const { themeMode } = useThemeContext2();
931
+ const [parent, setParent] = useState2(null);
932
+ const [state, setState] = useState2();
933
+ const [view, setView] = useState2();
934
+ useImperativeHandle3(forwardedRef, () => {
935
+ return {
936
+ editor: parent,
937
+ state,
938
+ view
939
+ };
940
+ }, [
941
+ view
942
+ ]);
943
+ useEffect2(() => {
944
+ if (!parent) {
945
+ return;
946
+ }
947
+ view?.destroy();
948
+ const state2 = EditorState2.create({
949
+ doc: content?.toString(),
950
+ extensions: [
951
+ bracketMatching2(),
952
+ closeBrackets2(),
953
+ placeholder2(slots.editor?.placeholder ?? ""),
954
+ EditorView2.lineWrapping,
955
+ // Themes.
956
+ EditorView2.theme(theme),
957
+ ...themeMode === "dark" ? [
958
+ syntaxHighlighting2(oneDarkHighlightStyle2)
959
+ ] : [
960
+ syntaxHighlighting2(defaultHighlightStyle2)
961
+ ],
962
+ // Replication.
963
+ // TODO(burdon): Move to default extensions.
964
+ ...content instanceof YText2 ? [
965
+ yCollab2(content, void 0)
966
+ ] : [],
967
+ // Custom.
968
+ ...extensions
969
+ ]
970
+ });
971
+ setState(state2);
972
+ setView(new EditorView2({
973
+ state: state2,
974
+ parent
975
+ }));
976
+ return () => {
977
+ view?.destroy();
978
+ setView(void 0);
979
+ setState(void 0);
980
+ };
981
+ }, [
982
+ parent,
983
+ content,
984
+ themeMode
985
+ ]);
986
+ const handleKeyDown = useCallback2((event) => {
987
+ if (view) {
988
+ const { head, from, to } = view.state.selection.ranges[0];
989
+ const { number } = view.state.doc.lineAt(head);
990
+ const after = view.state.sliceDoc(from);
991
+ onKeyDown?.(event, {
992
+ from,
993
+ to,
994
+ line: number,
995
+ lines: view.state.doc.lines,
996
+ after
997
+ });
998
+ }
999
+ }, [
1000
+ view
1001
+ ]);
1002
+ return /* @__PURE__ */ React4.createElement("div", {
1003
+ key: id,
1004
+ ref: setParent,
1005
+ ...slots.root,
1006
+ onKeyDown: handleKeyDown,
1007
+ ...props
1008
+ });
1009
+ });
1010
+
1011
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlinkDecoration.ts
1012
+ import { syntaxTree } from "@codemirror/language";
1013
+ import { RangeSetBuilder, StateField as StateField2 } from "@codemirror/state";
1014
+ import { Decoration, EditorView as EditorView3, WidgetType } from "@codemirror/view";
1015
+ var Link = class extends WidgetType {
1016
+ constructor(_anchor, _text, _url) {
1017
+ super();
1018
+ this._anchor = _anchor;
1019
+ this._text = _text;
1020
+ this._url = _url;
1021
+ }
1022
+ eq(other) {
1023
+ return this._text === other.text && this._url === other.url;
1024
+ }
1025
+ toDOM(view) {
1026
+ const link = document.createElement("a");
1027
+ link.setAttribute("class", "cm-link");
1028
+ link.textContent = this._text;
1029
+ if (this._url) {
1030
+ link.setAttribute("target", "_blank");
1031
+ link.href = this._url;
1032
+ } else {
1033
+ link.onclick = () => {
1034
+ view.dispatch({
1035
+ selection: {
1036
+ anchor: this._anchor
1037
+ }
1038
+ });
1039
+ };
1040
+ }
1041
+ return link;
1042
+ }
1043
+ };
1044
+ var hyperlinkDecoration = ({ link } = {}) => {
1045
+ return StateField2.define({
1046
+ create: (state) => update(state, link),
1047
+ update: (_, tr) => update(tr.state, link),
1048
+ provide: (field) => EditorView3.decorations.from(field)
1049
+ });
1050
+ };
1051
+ var update = (state, link) => {
1052
+ const builder = new RangeSetBuilder();
1053
+ const cursor2 = state.selection.main.head;
1054
+ syntaxTree(state).iterate({
1055
+ enter: (node) => {
1056
+ if (cursor2 < node.from || cursor2 > node.to) {
1057
+ if (node.name === "Link") {
1058
+ const marks = node.node.getChildren("LinkMark");
1059
+ const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
1060
+ const urlNode = node.node.getChild("URL");
1061
+ const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
1062
+ builder.add(node.from, node.to, Decoration.replace({
1063
+ widget: new Link(node.from, text, link ? url : void 0)
1064
+ }));
1065
+ return false;
1066
+ }
1067
+ }
1068
+ return true;
1069
+ }
1070
+ });
1071
+ return builder.finish();
1072
+ };
1073
+
1074
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlinkTooltip.tsx
1075
+ import { hoverTooltip } from "@codemirror/view";
1076
+ import { tooltipContent } from "@dxos/react-ui-theme";
1077
+ var markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
1078
+ var createHyperlinkTooltip = (onHover, regexp = markdownLinkRegexp) => hoverTooltip((view, pos) => {
1079
+ const { from, text } = view.state.doc.lineAt(pos);
1080
+ const p = pos - from;
1081
+ let idx = 0;
1082
+ let match;
1083
+ do {
1084
+ match = text.substring(idx).match(regexp);
1085
+ if (!match) {
1086
+ match = void 0;
1087
+ break;
1088
+ }
1089
+ idx = text.indexOf(match[0]);
1090
+ if (p >= idx && p < idx + match[0].length) {
1091
+ break;
1092
+ }
1093
+ idx += match[0].length;
1094
+ } while (true);
1095
+ if (!match) {
1096
+ return null;
1097
+ }
1098
+ const [, , url] = match ?? [];
1099
+ return {
1100
+ pos: idx + from,
1101
+ end: idx + from + match[0].length,
1102
+ above: true,
1103
+ create: () => {
1104
+ const el = document.createElement("a");
1105
+ el.innerText = "_";
1106
+ el.className = tooltipContent({}, "mb-2 p-1");
1107
+ el.setAttribute("target", "_blank");
1108
+ el.setAttribute("href", url);
1109
+ onHover(el, url);
1110
+ return {
1111
+ dom: el
1112
+ };
1113
+ }
1114
+ };
1115
+ });
1116
+
1117
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlinkWidget.tsx
1118
+ import { Decoration as Decoration2, EditorView as EditorView4, MatchDecorator, ViewPlugin } from "@codemirror/view";
1119
+ var markdownLinkRegexp2 = /\[([^\]]+)]\(([^)]+)\)(!?)/gi;
1120
+ var linkDecorator = () => new MatchDecorator({
1121
+ regexp: markdownLinkRegexp2,
1122
+ decorate: (add, from, to, match, view) => {
1123
+ const [_, label, url] = match;
1124
+ const p0 = {
1125
+ start: from,
1126
+ end: from + 1
1127
+ };
1128
+ const p1 = {
1129
+ start: from + 1,
1130
+ end: from + 1 + label.length
1131
+ };
1132
+ const p2 = {
1133
+ start: p1.end,
1134
+ end: p1.end + 1
1135
+ };
1136
+ const p3 = {
1137
+ start: p1.end + 1,
1138
+ end: p1.end + 1 + url.length + 2
1139
+ };
1140
+ add(p0.start, p0.end, Decoration2.mark({
1141
+ class: "cm-hyperlink-bracket",
1142
+ _attributes: {
1143
+ style: "display: none"
1144
+ }
1145
+ }));
1146
+ add(p1.start, p1.end, Decoration2.mark({
1147
+ class: "cm-hyperlink-label"
1148
+ }));
1149
+ add(p2.start, p2.end, Decoration2.mark({
1150
+ class: "cm-hyperlink-bracket",
1151
+ _attributes: {
1152
+ style: "display: none"
1153
+ }
1154
+ }));
1155
+ add(p3.start, p3.end, Decoration2.mark({
1156
+ class: "cm-hyperlink-url",
1157
+ _attributes: {
1158
+ style: "display: none"
1159
+ }
1160
+ }));
1161
+ }
1162
+ });
1163
+ function hyperLinkExtension() {
1164
+ return ViewPlugin.fromClass(class HyperLinkView {
1165
+ constructor(view) {
1166
+ this.decorator = linkDecorator();
1167
+ this.decorations = this.decorator.createDeco(view);
1168
+ }
1169
+ update(update2) {
1170
+ if (update2.docChanged || update2.viewportChanged) {
1171
+ this.decorations = this.decorator.updateDeco(update2, this.decorations);
1172
+ }
1173
+ }
1174
+ }, {
1175
+ decorations: (view) => view.decorations,
1176
+ eventHandlers: {
1177
+ // TODO(burdon): CLick to expand link.
1178
+ // https://discuss.codemirror.net/t/decorator-iteration-on-click-event/4226/2
1179
+ mousedown: function(event, view) {
1180
+ const pos = view.posAtDOM(event.target);
1181
+ if (pos !== 0) {
1182
+ console.log(this);
1183
+ }
1184
+ },
1185
+ // TODO(burdon): Hide when cursor moves away.
1186
+ keydown: (event, view) => {
1187
+ view.update([]);
1188
+ }
1189
+ }
1190
+ });
1191
+ }
1192
+ var hyperlinkStyle = EditorView4.baseTheme({
1193
+ ".cm-hyperlink-label": {
1194
+ cursor: "pointer",
1195
+ textDecoration: "underline"
1196
+ },
1197
+ ".cm-hyperlink-bracket": {
1198
+ opacity: 0.2
1199
+ },
1200
+ ".cm-hyperlink-url": {
1201
+ opacity: 0.5,
1202
+ fontFamily: "monospace"
1203
+ }
1204
+ });
1205
+ var hyperlinkWidget = [
1206
+ hyperLinkExtension(),
1207
+ hyperlinkStyle
1208
+ ];
1209
+
862
1210
  // packages/ui/react-ui-editor/src/index.ts
863
1211
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
864
- import { Doc, YText as YText2, YXmlFragment as YXmlFragment2 } from "@dxos/text-model";
1212
+ import { Doc, YText as YText3, YXmlFragment as YXmlFragment2 } from "@dxos/text-model";
865
1213
  export {
866
1214
  Doc,
867
1215
  Editor,
868
1216
  EditorModes,
869
1217
  MarkdownEditor,
870
1218
  RichTextEditor,
1219
+ TextEditor,
871
1220
  TextKind,
872
- YText2 as YText,
1221
+ YText3 as YText,
873
1222
  YXmlFragment2 as YXmlFragment,
1223
+ createHyperlinkTooltip,
1224
+ defaultStyles,
1225
+ hyperLinkExtension,
1226
+ hyperlinkDecoration,
1227
+ hyperlinkStyle,
1228
+ hyperlinkWidget,
1229
+ tags2 as tags,
874
1230
  useTextModel
875
1231
  };
876
1232
  //# sourceMappingURL=index.mjs.map