camox 0.31.5 → 0.32.0

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.
@@ -1,4 +1,5 @@
1
1
  import { useFrame } from "../../../features/preview/components/Frame.js";
2
+ import { isHttpTextLinkTarget } from "../../lib/textLinks.js";
2
3
  import { lexicalStateToMarkdown } from "../../lib/lexicalState.js";
3
4
  import { createEditorConfig, normalizeLexicalState } from "./editorConfig.js";
4
5
  import { InlineContentEditable } from "./InlineContentEditable.js";
@@ -6,19 +7,21 @@ import { SelectionBroadcaster } from "./SelectionBroadcaster.js";
6
7
  import { c } from "react/compiler-runtime";
7
8
  import * as React from "react";
8
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ import { TOGGLE_LINK_COMMAND } from "@lexical/link";
9
11
  import { LexicalComposer } from "@lexical/react/LexicalComposer";
10
12
  import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
11
13
  import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
14
+ import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
12
15
  import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin";
13
16
  import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
14
- import { COMMAND_PRIORITY_LOW, INSERT_LINE_BREAK_COMMAND, KEY_ENTER_COMMAND, KEY_ESCAPE_COMMAND } from "lexical";
17
+ import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_LOW, INSERT_LINE_BREAK_COMMAND, KEY_ENTER_COMMAND, KEY_ESCAPE_COMMAND, PASTE_COMMAND } from "lexical";
15
18
 
16
19
  //#region src/core/components/lexical/InlineLexicalEditor.tsx
17
20
  function ExternalStateSync(t0) {
18
21
  const $ = c(8);
19
- if ($[0] !== "4a0a85f4e9100a4a1fff0d37d18bcca0d7826f263ae50ef5a99743e122c031e5") {
22
+ if ($[0] !== "c4ca5d751d9d53a7ad565939bb5b4c4c82ea51c5bc23aeca481e23d8515243a2") {
20
23
  for (let $i = 0; $i < 8; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
21
- $[0] = "4a0a85f4e9100a4a1fff0d37d18bcca0d7826f263ae50ef5a99743e122c031e5";
24
+ $[0] = "c4ca5d751d9d53a7ad565939bb5b4c4c82ea51c5bc23aeca481e23d8515243a2";
22
25
  }
23
26
  const { externalState } = t0;
24
27
  const [editor] = useLexicalComposerContext();
@@ -75,9 +78,9 @@ function ExternalStateSync(t0) {
75
78
  }
76
79
  function EscapeHandler() {
77
80
  const $ = c(4);
78
- if ($[0] !== "4a0a85f4e9100a4a1fff0d37d18bcca0d7826f263ae50ef5a99743e122c031e5") {
81
+ if ($[0] !== "c4ca5d751d9d53a7ad565939bb5b4c4c82ea51c5bc23aeca481e23d8515243a2") {
79
82
  for (let $i = 0; $i < 4; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
80
- $[0] = "4a0a85f4e9100a4a1fff0d37d18bcca0d7826f263ae50ef5a99743e122c031e5";
83
+ $[0] = "c4ca5d751d9d53a7ad565939bb5b4c4c82ea51c5bc23aeca481e23d8515243a2";
81
84
  }
82
85
  const [editor] = useLexicalComposerContext();
83
86
  let t0;
@@ -100,9 +103,9 @@ function EscapeHandler() {
100
103
  }
101
104
  function EnterAsLineBreakHandler() {
102
105
  const $ = c(4);
103
- if ($[0] !== "4a0a85f4e9100a4a1fff0d37d18bcca0d7826f263ae50ef5a99743e122c031e5") {
106
+ if ($[0] !== "c4ca5d751d9d53a7ad565939bb5b4c4c82ea51c5bc23aeca481e23d8515243a2") {
104
107
  for (let $i = 0; $i < 4; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
105
- $[0] = "4a0a85f4e9100a4a1fff0d37d18bcca0d7826f263ae50ef5a99743e122c031e5";
108
+ $[0] = "c4ca5d751d9d53a7ad565939bb5b4c4c82ea51c5bc23aeca481e23d8515243a2";
106
109
  }
107
110
  const [editor] = useLexicalComposerContext();
108
111
  let t0;
@@ -124,11 +127,42 @@ function EnterAsLineBreakHandler() {
124
127
  React.useEffect(t0, t1);
125
128
  return null;
126
129
  }
130
+ function PasteUrlAsLinkHandler() {
131
+ const $ = c(4);
132
+ if ($[0] !== "c4ca5d751d9d53a7ad565939bb5b4c4c82ea51c5bc23aeca481e23d8515243a2") {
133
+ for (let $i = 0; $i < 4; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
134
+ $[0] = "c4ca5d751d9d53a7ad565939bb5b4c4c82ea51c5bc23aeca481e23d8515243a2";
135
+ }
136
+ const [editor] = useLexicalComposerContext();
137
+ let t0;
138
+ let t1;
139
+ if ($[1] !== editor) {
140
+ t0 = () => editor.registerCommand(PASTE_COMMAND, (event) => {
141
+ const selection = $getSelection();
142
+ if (!$isRangeSelection(selection) || selection.isCollapsed()) return false;
143
+ if (!("clipboardData" in event) || !event.clipboardData) return false;
144
+ const url = event.clipboardData.getData("text/plain").trim();
145
+ if (!url || !isHttpTextLinkTarget(url)) return false;
146
+ event.preventDefault();
147
+ editor.dispatchCommand(TOGGLE_LINK_COMMAND, url);
148
+ return true;
149
+ }, COMMAND_PRIORITY_LOW);
150
+ t1 = [editor];
151
+ $[1] = editor;
152
+ $[2] = t0;
153
+ $[3] = t1;
154
+ } else {
155
+ t0 = $[2];
156
+ t1 = $[3];
157
+ }
158
+ React.useEffect(t0, t1);
159
+ return null;
160
+ }
127
161
  function FocusBlurHandler(t0) {
128
162
  const $ = c(6);
129
- if ($[0] !== "4a0a85f4e9100a4a1fff0d37d18bcca0d7826f263ae50ef5a99743e122c031e5") {
163
+ if ($[0] !== "c4ca5d751d9d53a7ad565939bb5b4c4c82ea51c5bc23aeca481e23d8515243a2") {
130
164
  for (let $i = 0; $i < 6; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
131
- $[0] = "4a0a85f4e9100a4a1fff0d37d18bcca0d7826f263ae50ef5a99743e122c031e5";
165
+ $[0] = "c4ca5d751d9d53a7ad565939bb5b4c4c82ea51c5bc23aeca481e23d8515243a2";
132
166
  }
133
167
  const { onFocus, onBlur } = t0;
134
168
  const [editor] = useLexicalComposerContext();
@@ -161,6 +195,37 @@ function FocusBlurHandler(t0) {
161
195
  React.useEffect(t1, t2);
162
196
  return null;
163
197
  }
198
+ function LinkStyleInjector() {
199
+ const $ = c(4);
200
+ if ($[0] !== "c4ca5d751d9d53a7ad565939bb5b4c4c82ea51c5bc23aeca481e23d8515243a2") {
201
+ for (let $i = 0; $i < 4; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
202
+ $[0] = "c4ca5d751d9d53a7ad565939bb5b4c4c82ea51c5bc23aeca481e23d8515243a2";
203
+ }
204
+ const [editor] = useLexicalComposerContext();
205
+ let t0;
206
+ let t1;
207
+ if ($[1] !== editor) {
208
+ t0 = () => editor.registerRootListener(_temp);
209
+ t1 = [editor];
210
+ $[1] = editor;
211
+ $[2] = t0;
212
+ $[3] = t1;
213
+ } else {
214
+ t0 = $[2];
215
+ t1 = $[3];
216
+ }
217
+ React.useEffect(t0, t1);
218
+ return null;
219
+ }
220
+ function _temp(root) {
221
+ if (!root) return;
222
+ const doc = root.ownerDocument;
223
+ if (doc.getElementById("camox-editable-text-link-styles")) return;
224
+ const style = doc.createElement("style");
225
+ style.id = "camox-editable-text-link-styles";
226
+ style.textContent = ".camox-text-link { text-decoration-line: underline; }";
227
+ doc.head.appendChild(style);
228
+ }
164
229
  function InlineLexicalEditor({ initialState, externalState, onChange, onFocus, onBlur }) {
165
230
  const { window: iframeWindow } = useFrame();
166
231
  const timerRef = React.useRef(null);
@@ -203,22 +268,25 @@ function InlineLexicalEditor({ initialState, externalState, onChange, onFocus, o
203
268
  ignoreSelectionChange: true
204
269
  }),
205
270
  /* @__PURE__ */ jsx(HistoryPlugin, {}),
271
+ /* @__PURE__ */ jsx(LinkPlugin, {}),
206
272
  /* @__PURE__ */ jsx(ExternalStateSync, { externalState }),
207
273
  /* @__PURE__ */ jsx(EscapeHandler, {}),
208
274
  /* @__PURE__ */ jsx(EnterAsLineBreakHandler, {}),
275
+ /* @__PURE__ */ jsx(PasteUrlAsLinkHandler, {}),
209
276
  /* @__PURE__ */ jsx(FocusBlurHandler, {
210
277
  onFocus: handleFocus,
211
278
  onBlur: handleBlur
212
279
  }),
280
+ /* @__PURE__ */ jsx(LinkStyleInjector, {}),
213
281
  iframeWindow && /* @__PURE__ */ jsx(SelectionBroadcaster, { targetWindow: iframeWindow })
214
282
  ]
215
283
  });
216
284
  }
217
285
  function LexicalErrorBoundary(t0) {
218
286
  const $ = c(3);
219
- if ($[0] !== "4a0a85f4e9100a4a1fff0d37d18bcca0d7826f263ae50ef5a99743e122c031e5") {
287
+ if ($[0] !== "c4ca5d751d9d53a7ad565939bb5b4c4c82ea51c5bc23aeca481e23d8515243a2") {
220
288
  for (let $i = 0; $i < 3; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
221
- $[0] = "4a0a85f4e9100a4a1fff0d37d18bcca0d7826f263ae50ef5a99743e122c031e5";
289
+ $[0] = "c4ca5d751d9d53a7ad565939bb5b4c4c82ea51c5bc23aeca481e23d8515243a2";
222
290
  }
223
291
  const { children } = t0;
224
292
  let t1;
@@ -2,18 +2,33 @@ import { isOverlayMessage, postOverlayMessage } from "../../../features/preview/
2
2
  import { TEXT_MODIFIERS } from "../../lib/modifiers.js";
3
3
  import { c } from "react/compiler-runtime";
4
4
  import * as React from "react";
5
+ import { $createLinkNode, TOGGLE_LINK_COMMAND } from "@lexical/link";
5
6
  import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
6
- import { $getSelection, $isRangeSelection, FORMAT_TEXT_COMMAND } from "lexical";
7
+ import { $createTextNode, $getSelection, $isRangeSelection, $setSelection, FORMAT_TEXT_COMMAND } from "lexical";
7
8
 
8
9
  //#region src/core/components/lexical/SelectionBroadcaster.tsx
10
+ function getLinkTargetFromSelection() {
11
+ const selection = $getSelection();
12
+ if (!$isRangeSelection(selection)) return null;
13
+ let node = selection.anchor.getNode();
14
+ while (node) {
15
+ if (node.getType?.() === "link") {
16
+ const url = node.getURL?.();
17
+ return typeof url === "string" ? url : null;
18
+ }
19
+ node = node.getParent?.();
20
+ }
21
+ return null;
22
+ }
9
23
  function SelectionBroadcaster(t0) {
10
- const $ = c(13);
11
- if ($[0] !== "f181f408b72db1d040043499cda238045ef0e0efdc3d9844365e16efc5215a41") {
12
- for (let $i = 0; $i < 13; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
13
- $[0] = "f181f408b72db1d040043499cda238045ef0e0efdc3d9844365e16efc5215a41";
24
+ const $ = c(18);
25
+ if ($[0] !== "e89fc1c958f4e83679744b18d31c960a1ed541e1ba675eee3f5a0ecee772b5f2") {
26
+ for (let $i = 0; $i < 18; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
27
+ $[0] = "e89fc1c958f4e83679744b18d31c960a1ed541e1ba675eee3f5a0ecee772b5f2";
14
28
  }
15
29
  const { targetWindow } = t0;
16
30
  const [editor] = useLexicalComposerContext();
31
+ const lastTextSelectionRef = React.useRef(null);
17
32
  let t1;
18
33
  if ($[1] !== editor || $[2] !== targetWindow) {
19
34
  t1 = () => {
@@ -22,23 +37,30 @@ function SelectionBroadcaster(t0) {
22
37
  postOverlayMessage({
23
38
  type: "CAMOX_TEXT_SELECTION_STATE",
24
39
  hasSelection: false,
25
- activeFormats: 0
40
+ activeFormats: 0,
41
+ linkTarget: null,
42
+ selectedText: ""
26
43
  });
27
44
  return;
28
45
  }
29
46
  let format = 0;
47
+ let linkTarget = null;
30
48
  editor.getEditorState().read(() => {
31
49
  const selection = $getSelection();
32
50
  if (!$isRangeSelection(selection)) return;
51
+ lastTextSelectionRef.current = selection.clone();
33
52
  for (const modifier of Object.values(TEXT_MODIFIERS)) {
34
53
  const key = modifier === TEXT_MODIFIERS.bold ? "bold" : "italic";
35
54
  if (selection.hasFormat(key)) format = format | modifier.formatFlag;
36
55
  }
56
+ linkTarget = getLinkTargetFromSelection();
37
57
  });
38
58
  postOverlayMessage({
39
59
  type: "CAMOX_TEXT_SELECTION_STATE",
40
60
  hasSelection: true,
41
- activeFormats: format
61
+ activeFormats: format,
62
+ linkTarget,
63
+ selectedText: nativeSelection?.toString() ?? ""
42
64
  });
43
65
  };
44
66
  $[1] = editor;
@@ -68,21 +90,33 @@ function SelectionBroadcaster(t0) {
68
90
  let t4;
69
91
  let t5;
70
92
  if ($[8] !== broadcastSelection || $[9] !== editor || $[10] !== targetWindow) {
71
- t4 = () => {
72
- const handleMessage = (event) => {
73
- const data = event.data;
74
- if (!isOverlayMessage(data) || data.type !== "CAMOX_FORMAT_TEXT") return;
75
- const root = editor.getRootElement();
76
- const nativeSelection_0 = targetWindow.getSelection();
77
- if (!root || !nativeSelection_0 || nativeSelection_0.rangeCount === 0) return;
78
- if (!root.contains(nativeSelection_0.anchorNode)) return;
93
+ t4 = () => editor.registerRootListener((root) => {
94
+ if (!root) return;
95
+ const handleLinkClick = (event) => {
96
+ const target = event.target;
97
+ if (!(target instanceof HTMLElement)) return;
98
+ const anchor = target.closest("a");
99
+ if (!anchor || !root.contains(anchor)) return;
100
+ const href = anchor.getAttribute("href");
101
+ if (!href) return;
102
+ event.preventDefault();
103
+ event.stopPropagation();
104
+ const range = targetWindow.document.createRange();
105
+ range.selectNodeContents(anchor);
106
+ const selection_0 = targetWindow.getSelection();
107
+ selection_0?.removeAllRanges();
108
+ selection_0?.addRange(range);
79
109
  root.focus();
80
- editor.dispatchCommand(FORMAT_TEXT_COMMAND, data.formatKey);
81
- setTimeout(broadcastSelection, 10);
110
+ broadcastSelection();
111
+ postOverlayMessage({
112
+ type: "CAMOX_OPEN_TEXT_LINK_POPOVER",
113
+ target: href,
114
+ text: selection_0?.toString() ?? ""
115
+ });
82
116
  };
83
- targetWindow.addEventListener("message", handleMessage);
84
- return () => targetWindow.removeEventListener("message", handleMessage);
85
- };
117
+ root.addEventListener("click", handleLinkClick);
118
+ return () => root.removeEventListener("click", handleLinkClick);
119
+ });
86
120
  t5 = [
87
121
  editor,
88
122
  targetWindow,
@@ -98,6 +132,58 @@ function SelectionBroadcaster(t0) {
98
132
  t5 = $[12];
99
133
  }
100
134
  React.useEffect(t4, t5);
135
+ let t6;
136
+ let t7;
137
+ if ($[13] !== broadcastSelection || $[14] !== editor || $[15] !== targetWindow) {
138
+ t6 = () => {
139
+ const handleMessage = (event_0) => {
140
+ const data = event_0.data;
141
+ if (!isOverlayMessage(data) || data.type !== "CAMOX_FORMAT_TEXT" && data.type !== "CAMOX_TOGGLE_TEXT_LINK") return;
142
+ const root_0 = editor.getRootElement();
143
+ const nativeSelection_0 = targetWindow.getSelection();
144
+ if (!root_0 || !nativeSelection_0 || nativeSelection_0.rangeCount === 0) return;
145
+ if (!root_0.contains(nativeSelection_0.anchorNode)) return;
146
+ root_0.focus();
147
+ editor.update(() => {
148
+ const selection_1 = $getSelection();
149
+ if ($isRangeSelection(selection_1) && !selection_1.isCollapsed()) {
150
+ lastTextSelectionRef.current = selection_1.clone();
151
+ return;
152
+ }
153
+ if (lastTextSelectionRef.current) $setSelection(lastTextSelectionRef.current.clone());
154
+ });
155
+ if (data.type === "CAMOX_FORMAT_TEXT") editor.dispatchCommand(FORMAT_TEXT_COMMAND, data.formatKey);
156
+ else if (data.target === null) editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
157
+ else if (typeof data.text === "string") {
158
+ const target_0 = data.target;
159
+ editor.update(() => {
160
+ const selection_2 = $getSelection();
161
+ if (!$isRangeSelection(selection_2)) return;
162
+ const linkNode = $createLinkNode(target_0);
163
+ linkNode.append($createTextNode(data.text));
164
+ selection_2.insertNodes([linkNode]);
165
+ });
166
+ } else editor.dispatchCommand(TOGGLE_LINK_COMMAND, data.target);
167
+ setTimeout(broadcastSelection, 10);
168
+ };
169
+ targetWindow.addEventListener("message", handleMessage);
170
+ return () => targetWindow.removeEventListener("message", handleMessage);
171
+ };
172
+ t7 = [
173
+ editor,
174
+ targetWindow,
175
+ broadcastSelection
176
+ ];
177
+ $[13] = broadcastSelection;
178
+ $[14] = editor;
179
+ $[15] = targetWindow;
180
+ $[16] = t6;
181
+ $[17] = t7;
182
+ } else {
183
+ t6 = $[16];
184
+ t7 = $[17];
185
+ }
186
+ React.useEffect(t6, t7);
101
187
  return null;
102
188
  }
103
189