@mdxeditor/editor 0.19.0 → 0.20.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,22 +1,25 @@
1
+ import * as Dialog from "@radix-ui/react-dialog";
1
2
  import classNames from "classnames";
2
3
  import YamlParser from "js-yaml";
3
4
  import React__default from "react";
4
5
  import { useForm, useFieldArray } from "react-hook-form";
5
- import DropDownIcon from "../../icons/arrow_drop_down.svg.js";
6
- import ArrowRight from "../../icons/arrow_right.svg.js";
6
+ import { frontmatterPluginHooks } from "./index.js";
7
+ import CloseIcon from "../../icons/close.svg.js";
7
8
  import DeleteIcon from "../../icons/delete.svg.js";
8
9
  import styles from "../../styles/ui.module.css.js";
9
10
  import { corePluginHooks } from "../core/index.js";
10
11
  const FrontmatterEditor = ({ yaml, onChange }) => {
11
- const [expanded, setExpanded] = React__default.useState(true);
12
- const [readOnly] = corePluginHooks.useEmitterValues("readOnly");
12
+ const [readOnly, editorRootElementRef] = corePluginHooks.useEmitterValues("readOnly", "editorRootElementRef");
13
+ const [frontmatterDialogOpen] = frontmatterPluginHooks.useEmitterValues("frontmatterDialogOpen");
14
+ const setFrontmatterDialogOpen = frontmatterPluginHooks.usePublisher("frontmatterDialogOpen");
15
+ const removeFrontmatter = frontmatterPluginHooks.usePublisher("removeFrontmatter");
13
16
  const yamlConfig = React__default.useMemo(() => {
14
17
  if (!yaml) {
15
18
  return [];
16
19
  }
17
20
  return Object.entries(YamlParser.load(yaml)).map(([key, value]) => ({ key, value }));
18
21
  }, [yaml]);
19
- const { register, control, watch } = useForm({
22
+ const { register, control, handleSubmit } = useForm({
20
23
  defaultValues: {
21
24
  yamlConfig
22
25
  }
@@ -25,8 +28,13 @@ const FrontmatterEditor = ({ yaml, onChange }) => {
25
28
  control,
26
29
  name: "yamlConfig"
27
30
  });
28
- React__default.useEffect(() => {
29
- const subscription = watch(({ yamlConfig: yamlConfig2 }) => {
31
+ const onSubmit = React__default.useCallback(
32
+ ({ yamlConfig: yamlConfig2 }) => {
33
+ if (yamlConfig2.length === 0) {
34
+ removeFrontmatter(true);
35
+ setFrontmatterDialogOpen(false);
36
+ return;
37
+ }
30
38
  const yaml2 = yamlConfig2.reduce((acc, { key, value }) => {
31
39
  if (key && value) {
32
40
  acc[key] = value;
@@ -34,34 +42,24 @@ const FrontmatterEditor = ({ yaml, onChange }) => {
34
42
  return acc;
35
43
  }, {});
36
44
  onChange(YamlParser.dump(yaml2).trim());
37
- });
38
- return () => subscription.unsubscribe();
39
- }, [watch, onChange]);
40
- return /* @__PURE__ */ React__default.createElement("div", { className: styles.frontmatterWrapper, "data-expanded": expanded, "data-editor-type": "frontmatter" }, /* @__PURE__ */ React__default.createElement(
41
- "button",
42
- {
43
- className: styles.frontmatterToggleButton,
44
- onClick: (e) => {
45
- e.preventDefault();
46
- setExpanded((v) => !v);
47
- }
45
+ setFrontmatterDialogOpen(false);
48
46
  },
49
- expanded ? /* @__PURE__ */ React__default.createElement(DropDownIcon, null) : /* @__PURE__ */ React__default.createElement(ArrowRight, null),
50
- "Document frontmatter"
51
- ), expanded && /* @__PURE__ */ React__default.createElement("form", null, /* @__PURE__ */ React__default.createElement("table", { className: styles.propertyEditorTable }, /* @__PURE__ */ React__default.createElement("colgroup", null, /* @__PURE__ */ React__default.createElement("col", null), /* @__PURE__ */ React__default.createElement("col", null), /* @__PURE__ */ React__default.createElement("col", null)), /* @__PURE__ */ React__default.createElement("thead", null, /* @__PURE__ */ React__default.createElement("tr", null, /* @__PURE__ */ React__default.createElement("th", null, "Key"), /* @__PURE__ */ React__default.createElement("th", null, "Value"), /* @__PURE__ */ React__default.createElement("th", null))), /* @__PURE__ */ React__default.createElement("tbody", null, fields.map((item, index) => {
47
+ [onChange, setFrontmatterDialogOpen, removeFrontmatter]
48
+ );
49
+ return /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(Dialog.Root, { open: frontmatterDialogOpen, onOpenChange: (open) => setFrontmatterDialogOpen(open) }, /* @__PURE__ */ React__default.createElement(Dialog.Portal, { container: editorRootElementRef == null ? void 0 : editorRootElementRef.current }, /* @__PURE__ */ React__default.createElement(Dialog.Overlay, { className: styles.dialogOverlay }), /* @__PURE__ */ React__default.createElement(Dialog.Content, { className: styles.largeDialogContent, "data-editor-type": "frontmatter" }, /* @__PURE__ */ React__default.createElement(Dialog.Title, { className: styles.dialogTitle }, "Edit document frontmatter"), /* @__PURE__ */ React__default.createElement("form", { onSubmit: handleSubmit(onSubmit), onReset: () => setFrontmatterDialogOpen(false) }, /* @__PURE__ */ React__default.createElement("table", { className: styles.propertyEditorTable }, /* @__PURE__ */ React__default.createElement("colgroup", null, /* @__PURE__ */ React__default.createElement("col", null), /* @__PURE__ */ React__default.createElement("col", null), /* @__PURE__ */ React__default.createElement("col", null)), /* @__PURE__ */ React__default.createElement("thead", null, /* @__PURE__ */ React__default.createElement("tr", null, /* @__PURE__ */ React__default.createElement("th", null, "Key"), /* @__PURE__ */ React__default.createElement("th", null, "Value"), /* @__PURE__ */ React__default.createElement("th", null))), /* @__PURE__ */ React__default.createElement("tbody", null, fields.map((item, index) => {
52
50
  return /* @__PURE__ */ React__default.createElement("tr", { key: item.id }, /* @__PURE__ */ React__default.createElement("td", null, /* @__PURE__ */ React__default.createElement(TableInput, { ...register(`yamlConfig.${index}.key`, { required: true }), autofocusIfEmpty: true, readOnly })), /* @__PURE__ */ React__default.createElement("td", null, /* @__PURE__ */ React__default.createElement(TableInput, { ...register(`yamlConfig.${index}.value`, { required: true }), readOnly })), /* @__PURE__ */ React__default.createElement("td", null, /* @__PURE__ */ React__default.createElement("button", { type: "button", onClick: () => remove(index), className: styles.iconButton, disabled: readOnly }, /* @__PURE__ */ React__default.createElement(DeleteIcon, null))));
53
51
  })), /* @__PURE__ */ React__default.createElement("tfoot", null, /* @__PURE__ */ React__default.createElement("tr", null, /* @__PURE__ */ React__default.createElement("td", null, /* @__PURE__ */ React__default.createElement(
54
52
  "button",
55
53
  {
56
54
  disabled: readOnly,
57
- className: styles.primaryButton,
55
+ className: classNames(styles.primaryButton, styles.smallButton),
58
56
  type: "button",
59
57
  onClick: () => {
60
58
  append({ key: "", value: "" });
61
59
  }
62
60
  },
63
- "Add new key"
64
- )))))));
61
+ "Add entry"
62
+ ))))), /* @__PURE__ */ React__default.createElement("div", { style: { display: "flex", justifyContent: "flex-end", gap: "var(--spacing-2)" } }, /* @__PURE__ */ React__default.createElement("button", { type: "reset", className: styles.secondaryButton }, "Cancel"), /* @__PURE__ */ React__default.createElement("button", { type: "submit", className: styles.primaryButton }, "Save"))), /* @__PURE__ */ React__default.createElement(Dialog.Close, { asChild: true }, /* @__PURE__ */ React__default.createElement("button", { className: styles.dialogCloseButton, "aria-label": "Close" }, /* @__PURE__ */ React__default.createElement(CloseIcon, null)))))));
65
63
  };
66
64
  const TableInput = React__default.forwardRef(({ className, autofocusIfEmpty: _, ...props }, ref) => {
67
65
  return /* @__PURE__ */ React__default.createElement("input", { className: classNames(styles.propertyEditorInput, className), ...props, ref });
@@ -64,7 +64,7 @@ export declare const frontmatterSystem: SystemSpec<[SystemSpec<[], (r: Realm) =>
64
64
  convertSelectionToNode: RealmNode<() => ElementNode>;
65
65
  insertDecoratorNode: RealmNode<() => DecoratorNode<unknown>>;
66
66
  onBlur: RealmNode<FocusEvent>;
67
- }>], (r: Realm, [{ rootEditor }]: [{
67
+ }>], (r: Realm, [{ rootEditor, createRootEditorSubscription }]: [{
68
68
  activeEditor: RealmNode<LexicalEditor | null>;
69
69
  inFocus: RealmNode<boolean>;
70
70
  historyState: RealmNode<HistoryState>;
@@ -118,6 +118,9 @@ export declare const frontmatterSystem: SystemSpec<[SystemSpec<[], (r: Realm) =>
118
118
  onBlur: RealmNode<FocusEvent>;
119
119
  }]) => {
120
120
  insertFrontmatter: RealmNode<true>;
121
+ removeFrontmatter: RealmNode<true>;
122
+ hasFrontmatter: RealmNode<boolean>;
123
+ frontmatterDialogOpen: RealmNode<boolean>;
121
124
  }>;
122
125
  export declare const
123
126
  /** @internal */
@@ -173,7 +176,7 @@ frontmatterPlugin: PluginConstructor<SystemSpec<[SystemSpec<[], (r: Realm) => {
173
176
  convertSelectionToNode: RealmNode<() => ElementNode>;
174
177
  insertDecoratorNode: RealmNode<() => DecoratorNode<unknown>>;
175
178
  onBlur: RealmNode<FocusEvent>;
176
- }>], (r: Realm, [{ rootEditor }]: [{
179
+ }>], (r: Realm, [{ rootEditor, createRootEditorSubscription }]: [{
177
180
  activeEditor: RealmNode<LexicalEditor | null>;
178
181
  inFocus: RealmNode<boolean>;
179
182
  historyState: RealmNode<HistoryState>;
@@ -227,11 +230,17 @@ frontmatterPlugin: PluginConstructor<SystemSpec<[SystemSpec<[], (r: Realm) => {
227
230
  onBlur: RealmNode<FocusEvent>;
228
231
  }]) => {
229
232
  insertFrontmatter: RealmNode<true>;
233
+ removeFrontmatter: RealmNode<true>;
234
+ hasFrontmatter: RealmNode<boolean>;
235
+ frontmatterDialogOpen: RealmNode<boolean>;
230
236
  }>, object>,
231
237
  /** @internal */
232
238
  frontmatterPluginHooks: {
233
239
  useRealmContext: () => TypedRealm<{
234
240
  insertFrontmatter: RealmNode<true>;
241
+ removeFrontmatter: RealmNode<true>;
242
+ hasFrontmatter: RealmNode<boolean>;
243
+ frontmatterDialogOpen: RealmNode<boolean>;
235
244
  } & {
236
245
  activeEditor: RealmNode<LexicalEditor | null>;
237
246
  inFocus: RealmNode<boolean>;
@@ -285,8 +294,11 @@ frontmatterPluginHooks: {
285
294
  insertDecoratorNode: RealmNode<() => DecoratorNode<unknown>>;
286
295
  onBlur: RealmNode<FocusEvent>;
287
296
  }>;
288
- useEmitter: <K extends "jsxComponentDescriptors" | "jsxIsAvailable" | "toMarkdownOptions" | "toMarkdownExtensions" | "mdastExtensions" | "syntaxExtensions" | "markdown" | "activeEditor" | "inFocus" | "historyState" | "currentSelection" | "initialRootEditorState" | "rootEditor" | "createRootEditorSubscription" | "createActiveEditorSubscription" | "importVisitors" | "usedLexicalNodes" | "addImportVisitor" | "addLexicalNode" | "addSyntaxExtension" | "addMdastExtension" | "addToMarkdownExtension" | "addExportVisitor" | "exportVisitors" | "initialMarkdown" | "setMarkdown" | "editorRootElementRef" | "contentEditableClassName" | "placeholder" | "autoFocus" | "readOnly" | "composerChildren" | "addComposerChild" | "topAreaChildren" | "addTopAreaChild" | "nestedEditorChildren" | "addNestedEditorChild" | "editorWrappers" | "addEditorWrapper" | "currentFormat" | "editorInFocus" | "applyFormat" | "currentBlockType" | "applyBlockType" | "convertSelectionToNode" | "insertDecoratorNode" | "onBlur" | "insertFrontmatter">(key: K, callback: (value: ValueForKey<{
297
+ useEmitter: <K extends "jsxComponentDescriptors" | "jsxIsAvailable" | "toMarkdownOptions" | "toMarkdownExtensions" | "mdastExtensions" | "syntaxExtensions" | "markdown" | "activeEditor" | "inFocus" | "historyState" | "currentSelection" | "initialRootEditorState" | "rootEditor" | "createRootEditorSubscription" | "createActiveEditorSubscription" | "importVisitors" | "usedLexicalNodes" | "addImportVisitor" | "addLexicalNode" | "addSyntaxExtension" | "addMdastExtension" | "addToMarkdownExtension" | "addExportVisitor" | "exportVisitors" | "initialMarkdown" | "setMarkdown" | "editorRootElementRef" | "contentEditableClassName" | "placeholder" | "autoFocus" | "readOnly" | "composerChildren" | "addComposerChild" | "topAreaChildren" | "addTopAreaChild" | "nestedEditorChildren" | "addNestedEditorChild" | "editorWrappers" | "addEditorWrapper" | "currentFormat" | "editorInFocus" | "applyFormat" | "currentBlockType" | "applyBlockType" | "convertSelectionToNode" | "insertDecoratorNode" | "onBlur" | "insertFrontmatter" | "removeFrontmatter" | "hasFrontmatter" | "frontmatterDialogOpen">(key: K, callback: (value: ValueForKey<{
289
298
  insertFrontmatter: RealmNode<true>;
299
+ removeFrontmatter: RealmNode<true>;
300
+ hasFrontmatter: RealmNode<boolean>;
301
+ frontmatterDialogOpen: RealmNode<boolean>;
290
302
  } & {
291
303
  activeEditor: RealmNode<LexicalEditor | null>;
292
304
  inFocus: RealmNode<boolean>;
@@ -342,6 +354,9 @@ frontmatterPluginHooks: {
342
354
  }, K>) => void) => void;
343
355
  useEmitterValues: <K_1 extends SystemKeys<{
344
356
  insertFrontmatter: RealmNode<true>;
357
+ removeFrontmatter: RealmNode<true>;
358
+ hasFrontmatter: RealmNode<boolean>;
359
+ frontmatterDialogOpen: RealmNode<boolean>;
345
360
  } & {
346
361
  activeEditor: RealmNode<LexicalEditor | null>;
347
362
  inFocus: RealmNode<boolean>;
@@ -396,6 +411,9 @@ frontmatterPluginHooks: {
396
411
  onBlur: RealmNode<FocusEvent>;
397
412
  }>>(...keys: K_1) => ValuesForKeys<{
398
413
  insertFrontmatter: RealmNode<true>;
414
+ removeFrontmatter: RealmNode<true>;
415
+ hasFrontmatter: RealmNode<boolean>;
416
+ frontmatterDialogOpen: RealmNode<boolean>;
399
417
  } & {
400
418
  activeEditor: RealmNode<LexicalEditor | null>;
401
419
  inFocus: RealmNode<boolean>;
@@ -451,6 +469,9 @@ frontmatterPluginHooks: {
451
469
  }, K_1>;
452
470
  usePubKeys: () => (values: SystemDict<{
453
471
  insertFrontmatter: RealmNode<true>;
472
+ removeFrontmatter: RealmNode<true>;
473
+ hasFrontmatter: RealmNode<boolean>;
474
+ frontmatterDialogOpen: RealmNode<boolean>;
454
475
  } & {
455
476
  activeEditor: RealmNode<LexicalEditor | null>;
456
477
  inFocus: RealmNode<boolean>;
@@ -504,8 +525,11 @@ frontmatterPluginHooks: {
504
525
  insertDecoratorNode: RealmNode<() => DecoratorNode<unknown>>;
505
526
  onBlur: RealmNode<FocusEvent>;
506
527
  }>) => void;
507
- usePublisher: <K_2 extends "jsxComponentDescriptors" | "jsxIsAvailable" | "toMarkdownOptions" | "toMarkdownExtensions" | "mdastExtensions" | "syntaxExtensions" | "markdown" | "activeEditor" | "inFocus" | "historyState" | "currentSelection" | "initialRootEditorState" | "rootEditor" | "createRootEditorSubscription" | "createActiveEditorSubscription" | "importVisitors" | "usedLexicalNodes" | "addImportVisitor" | "addLexicalNode" | "addSyntaxExtension" | "addMdastExtension" | "addToMarkdownExtension" | "addExportVisitor" | "exportVisitors" | "initialMarkdown" | "setMarkdown" | "editorRootElementRef" | "contentEditableClassName" | "placeholder" | "autoFocus" | "readOnly" | "composerChildren" | "addComposerChild" | "topAreaChildren" | "addTopAreaChild" | "nestedEditorChildren" | "addNestedEditorChild" | "editorWrappers" | "addEditorWrapper" | "currentFormat" | "editorInFocus" | "applyFormat" | "currentBlockType" | "applyBlockType" | "convertSelectionToNode" | "insertDecoratorNode" | "onBlur" | "insertFrontmatter">(key: K_2) => (value: ValueForKey<{
528
+ usePublisher: <K_2 extends "jsxComponentDescriptors" | "jsxIsAvailable" | "toMarkdownOptions" | "toMarkdownExtensions" | "mdastExtensions" | "syntaxExtensions" | "markdown" | "activeEditor" | "inFocus" | "historyState" | "currentSelection" | "initialRootEditorState" | "rootEditor" | "createRootEditorSubscription" | "createActiveEditorSubscription" | "importVisitors" | "usedLexicalNodes" | "addImportVisitor" | "addLexicalNode" | "addSyntaxExtension" | "addMdastExtension" | "addToMarkdownExtension" | "addExportVisitor" | "exportVisitors" | "initialMarkdown" | "setMarkdown" | "editorRootElementRef" | "contentEditableClassName" | "placeholder" | "autoFocus" | "readOnly" | "composerChildren" | "addComposerChild" | "topAreaChildren" | "addTopAreaChild" | "nestedEditorChildren" | "addNestedEditorChild" | "editorWrappers" | "addEditorWrapper" | "currentFormat" | "editorInFocus" | "applyFormat" | "currentBlockType" | "applyBlockType" | "convertSelectionToNode" | "insertDecoratorNode" | "onBlur" | "insertFrontmatter" | "removeFrontmatter" | "hasFrontmatter" | "frontmatterDialogOpen">(key: K_2) => (value: ValueForKey<{
508
529
  insertFrontmatter: RealmNode<true>;
530
+ removeFrontmatter: RealmNode<true>;
531
+ hasFrontmatter: RealmNode<boolean>;
532
+ frontmatterDialogOpen: RealmNode<boolean>;
509
533
  } & {
510
534
  activeEditor: RealmNode<LexicalEditor | null>;
511
535
  inFocus: RealmNode<boolean>;
@@ -8,8 +8,11 @@ import { $isFrontmatterNode, $createFrontmatterNode, FrontmatterNode } from "./F
8
8
  import { system } from "../../gurx/realmFactory.js";
9
9
  import { realmPlugin } from "../../gurx/react.js";
10
10
  const frontmatterSystem = system(
11
- (r, [{ rootEditor }]) => {
11
+ (r, [{ rootEditor, createRootEditorSubscription }]) => {
12
12
  const insertFrontmatter = r.node();
13
+ const removeFrontmatter = r.node();
14
+ const frontmatterDialogOpen = r.node(false);
15
+ const hasFrontmatter = r.node(false);
13
16
  r.sub(r.pipe(insertFrontmatter, r.o.withLatestFrom(rootEditor)), ([, theEditor]) => {
14
17
  theEditor == null ? void 0 : theEditor.update(() => {
15
18
  const firstItem = $getRoot().getFirstChild();
@@ -22,9 +25,29 @@ const frontmatterSystem = system(
22
25
  }
23
26
  }
24
27
  });
28
+ r.pub(frontmatterDialogOpen, true);
29
+ });
30
+ r.sub(r.pipe(removeFrontmatter, r.o.withLatestFrom(rootEditor)), ([, theEditor]) => {
31
+ theEditor == null ? void 0 : theEditor.update(() => {
32
+ const firstItem = $getRoot().getFirstChild();
33
+ if ($isFrontmatterNode(firstItem)) {
34
+ firstItem.remove();
35
+ }
36
+ });
37
+ r.pub(frontmatterDialogOpen, false);
38
+ });
39
+ r.pub(createRootEditorSubscription, (rootEditor2) => {
40
+ return rootEditor2.registerUpdateListener(({ editorState }) => {
41
+ editorState.read(() => {
42
+ r.pub(hasFrontmatter, $isFrontmatterNode($getRoot().getFirstChild()));
43
+ });
44
+ });
25
45
  });
26
46
  return {
27
- insertFrontmatter
47
+ insertFrontmatter,
48
+ removeFrontmatter,
49
+ hasFrontmatter,
50
+ frontmatterDialogOpen
28
51
  };
29
52
  },
30
53
  [coreSystem]
@@ -3,10 +3,11 @@ import { LexicalCommand } from 'lexical';
3
3
  export declare const OPEN_LINK_DIALOG: LexicalCommand<undefined>;
4
4
  interface LinkEditFormProps {
5
5
  initialUrl: string;
6
- onSubmit: (url: string) => void;
6
+ initialTitle: string;
7
+ onSubmit: (link: [string, string]) => void;
7
8
  onCancel: () => void;
8
9
  linkAutocompleteSuggestions: string[];
9
10
  }
10
- export declare function LinkEditForm({ initialUrl, onSubmit, onCancel, linkAutocompleteSuggestions }: LinkEditFormProps): React.JSX.Element;
11
+ export declare function LinkEditForm({ initialUrl, initialTitle, onSubmit, onCancel, linkAutocompleteSuggestions }: LinkEditFormProps): React.JSX.Element;
11
12
  export declare const LinkDialog: React.FC;
12
13
  export {};
@@ -3,7 +3,6 @@ import * as Tooltip from "@radix-ui/react-tooltip";
3
3
  import React__default from "react";
4
4
  import { createCommand } from "lexical";
5
5
  import CheckIcon from "../../icons/check.svg.js";
6
- import CloseIcon from "../../icons/close.svg.js";
7
6
  import CopyIcon from "../../icons/content_copy.svg.js";
8
7
  import EditIcon from "../../icons/edit.svg.js";
9
8
  import LinkOffIcon from "../../icons/link_off.svg.js";
@@ -16,15 +15,16 @@ import { corePluginHooks } from "../core/index.js";
16
15
  import { linkDialogPluginHooks } from "./index.js";
17
16
  createCommand();
18
17
  const MAX_SUGGESTIONS = 20;
19
- function LinkEditForm({ initialUrl, onSubmit, onCancel, linkAutocompleteSuggestions }) {
18
+ function LinkEditForm({ initialUrl, initialTitle, onSubmit, onCancel, linkAutocompleteSuggestions }) {
20
19
  const [items, setItems] = React__default.useState(linkAutocompleteSuggestions.slice(0, MAX_SUGGESTIONS));
21
- const { isOpen, getToggleButtonProps, getMenuProps, getInputProps, highlightedIndex, getItemProps, selectedItem } = useCombobox({
20
+ const [title, setTitle] = React__default.useState(initialTitle);
21
+ const { isOpen, getToggleButtonProps, getMenuProps, getInputProps, highlightedIndex, getItemProps, selectedItem, inputValue } = useCombobox({
22
22
  initialInputValue: initialUrl,
23
- onInputValueChange({ inputValue }) {
24
- inputValue = (inputValue == null ? void 0 : inputValue.toLowerCase()) || "";
23
+ onInputValueChange({ inputValue: inputValue2 }) {
24
+ inputValue2 = (inputValue2 == null ? void 0 : inputValue2.toLowerCase()) || "";
25
25
  const matchingItems = [];
26
26
  for (const url of linkAutocompleteSuggestions) {
27
- if (url.toLowerCase().includes(inputValue)) {
27
+ if (url.toLowerCase().includes(inputValue2)) {
28
28
  matchingItems.push(url);
29
29
  if (matchingItems.length >= MAX_SUGGESTIONS) {
30
30
  break;
@@ -40,7 +40,7 @@ function LinkEditForm({ initialUrl, onSubmit, onCancel, linkAutocompleteSuggesti
40
40
  });
41
41
  const onSubmitEH = (e) => {
42
42
  e.preventDefault();
43
- onSubmit(selectedItem || "");
43
+ onSubmit([inputValue, title]);
44
44
  };
45
45
  const onKeyDownEH = React__default.useCallback(
46
46
  (e) => {
@@ -49,10 +49,10 @@ function LinkEditForm({ initialUrl, onSubmit, onCancel, linkAutocompleteSuggesti
49
49
  (_a = e.target.form) == null ? void 0 : _a.reset();
50
50
  } else if (e.key === "Enter" && (!isOpen || items.length === 0)) {
51
51
  e.preventDefault();
52
- onSubmit(e.target.value);
52
+ onSubmit([e.target.value, title]);
53
53
  }
54
54
  },
55
- [isOpen, items, onSubmit]
55
+ [isOpen, items, onSubmit, title]
56
56
  );
57
57
  const downshiftInputProps = getInputProps();
58
58
  const inputProps = {
@@ -63,7 +63,7 @@ function LinkEditForm({ initialUrl, onSubmit, onCancel, linkAutocompleteSuggesti
63
63
  }
64
64
  };
65
65
  const dropdownIsVisible = isOpen && items.length > 0;
66
- return /* @__PURE__ */ React__default.createElement("form", { onSubmit: onSubmitEH, onReset: onCancel, className: classNames(styles.linkDialogEditForm) }, /* @__PURE__ */ React__default.createElement("div", { className: styles.linkDialogInputContainer }, /* @__PURE__ */ React__default.createElement("div", { "data-visible-dropdown": dropdownIsVisible, className: styles.linkDialogInputWrapper }, /* @__PURE__ */ React__default.createElement("input", { className: styles.linkDialogInput, ...inputProps, autoFocus: true, size: 30, "data-editor-dialog": true }), /* @__PURE__ */ React__default.createElement("button", { "aria-label": "toggle menu", type: "button", ...getToggleButtonProps() }, /* @__PURE__ */ React__default.createElement(DropDownIcon, null))), /* @__PURE__ */ React__default.createElement("div", { className: styles.linkDialogAutocompleteContainer }, /* @__PURE__ */ React__default.createElement("ul", { ...getMenuProps(), "data-visible": dropdownIsVisible }, items.map((item, index) => /* @__PURE__ */ React__default.createElement(
66
+ return /* @__PURE__ */ React__default.createElement("form", { onSubmit: onSubmitEH, onReset: onCancel, className: classNames(styles.linkDialogEditForm) }, /* @__PURE__ */ React__default.createElement("div", null, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "link-url" }, "URL")), /* @__PURE__ */ React__default.createElement("div", { className: styles.linkDialogInputContainer }, /* @__PURE__ */ React__default.createElement("div", { "data-visible-dropdown": dropdownIsVisible, className: styles.linkDialogInputWrapper }, /* @__PURE__ */ React__default.createElement("input", { id: "link-url", className: styles.linkDialogInput, ...inputProps, autoFocus: true, size: 40, "data-editor-dialog": true }), /* @__PURE__ */ React__default.createElement("button", { "aria-label": "toggle menu", type: "button", ...getToggleButtonProps() }, /* @__PURE__ */ React__default.createElement(DropDownIcon, null))), /* @__PURE__ */ React__default.createElement("div", { className: styles.linkDialogAutocompleteContainer }, /* @__PURE__ */ React__default.createElement("ul", { ...getMenuProps(), "data-visible": dropdownIsVisible }, items.map((item, index) => /* @__PURE__ */ React__default.createElement(
67
67
  "li",
68
68
  {
69
69
  "data-selected": selectedItem === item,
@@ -72,7 +72,7 @@ function LinkEditForm({ initialUrl, onSubmit, onCancel, linkAutocompleteSuggesti
72
72
  ...getItemProps({ item, index })
73
73
  },
74
74
  item
75
- ))))), /* @__PURE__ */ React__default.createElement(ActionButton, { type: "submit", title: "Set URL", "aria-label": "Set URL" }, /* @__PURE__ */ React__default.createElement(CheckIcon, null)), /* @__PURE__ */ React__default.createElement(ActionButton, { type: "reset", title: "Cancel change", "aria-label": "Cancel change" }, /* @__PURE__ */ React__default.createElement(CloseIcon, null)));
75
+ ))))), /* @__PURE__ */ React__default.createElement("div", null, /* @__PURE__ */ React__default.createElement("label", { htmlFor: "link-title" }, "Title")), /* @__PURE__ */ React__default.createElement("div", null, /* @__PURE__ */ React__default.createElement("div", { className: styles.linkDialogInputWrapper }, /* @__PURE__ */ React__default.createElement("input", { id: "link-title", className: styles.linkDialogInput, size: 40, value: title, onChange: (e) => setTitle(e.target.value) }))), /* @__PURE__ */ React__default.createElement("div", { style: { display: "flex", justifyContent: "flex-end", gap: "var(--spacing-2)" } }, /* @__PURE__ */ React__default.createElement("button", { type: "reset", title: "Cancel change", "aria-label": "Cancel change", className: classNames(styles.secondaryButton) }, "Cancel"), /* @__PURE__ */ React__default.createElement("button", { type: "submit", title: "Set URL", "aria-label": "Set URL", className: classNames(styles.primaryButton) }, "Save")));
76
76
  }
77
77
  const LinkDialog = () => {
78
78
  const [editorRootElementRef] = corePluginHooks.useEmitterValues("editorRootElementRef");
@@ -103,8 +103,8 @@ const LinkDialog = () => {
103
103
  const [copyUrlTooltipOpen, setCopyUrlTooltipOpen] = React__default.useState(false);
104
104
  const theRect = linkDialogState == null ? void 0 : linkDialogState.rectangle;
105
105
  const onSubmitEH = React__default.useCallback(
106
- (url) => {
107
- updateLinkUrl(url);
106
+ (payload) => {
107
+ updateLinkUrl(payload);
108
108
  applyLinkChanges(true);
109
109
  },
110
110
  [applyLinkChanges, updateLinkUrl]
@@ -134,6 +134,7 @@ const LinkDialog = () => {
134
134
  LinkEditForm,
135
135
  {
136
136
  initialUrl: linkDialogState.url,
137
+ initialTitle: linkDialogState.title,
137
138
  onSubmit: onSubmitEH,
138
139
  onCancel: cancelLinkEdit.bind(null, true),
139
140
  linkAutocompleteSuggestions
@@ -26,7 +26,9 @@ type PreviewLinkDialog = {
26
26
  type EditLinkDialog = {
27
27
  type: 'edit';
28
28
  initialUrl: string;
29
+ initialTitle?: string;
29
30
  url: string;
31
+ title: string;
30
32
  linkNodeKey: string;
31
33
  rectangle: RectData;
32
34
  };
@@ -140,7 +142,7 @@ linkDialogPlugin: PluginConstructor<SystemSpec<[SystemSpec<[], (r: Realm) => {
140
142
  dialogState: RealmNode<boolean>;
141
143
  onWindowChange: RealmNode<true>;
142
144
  linkDialogState: RealmNode<InactiveLinkDialog | PreviewLinkDialog | EditLinkDialog>;
143
- updateLinkUrl: RealmNode<string>;
145
+ updateLinkUrl: RealmNode<[string, string]>;
144
146
  switchFromPreviewToLinkEdit: RealmNode<true>;
145
147
  cancelLinkEdit: RealmNode<true>;
146
148
  removeLink: RealmNode<true>;
@@ -156,7 +158,7 @@ linkDialogPluginHooks: {
156
158
  dialogState: RealmNode<boolean>;
157
159
  onWindowChange: RealmNode<true>;
158
160
  linkDialogState: RealmNode<InactiveLinkDialog | PreviewLinkDialog | EditLinkDialog>;
159
- updateLinkUrl: RealmNode<string>;
161
+ updateLinkUrl: RealmNode<[string, string]>;
160
162
  switchFromPreviewToLinkEdit: RealmNode<true>;
161
163
  cancelLinkEdit: RealmNode<true>;
162
164
  removeLink: RealmNode<true>;
@@ -220,7 +222,7 @@ linkDialogPluginHooks: {
220
222
  dialogState: RealmNode<boolean>;
221
223
  onWindowChange: RealmNode<true>;
222
224
  linkDialogState: RealmNode<InactiveLinkDialog | PreviewLinkDialog | EditLinkDialog>;
223
- updateLinkUrl: RealmNode<string>;
225
+ updateLinkUrl: RealmNode<[string, string]>;
224
226
  switchFromPreviewToLinkEdit: RealmNode<true>;
225
227
  cancelLinkEdit: RealmNode<true>;
226
228
  removeLink: RealmNode<true>;
@@ -284,7 +286,7 @@ linkDialogPluginHooks: {
284
286
  dialogState: RealmNode<boolean>;
285
287
  onWindowChange: RealmNode<true>;
286
288
  linkDialogState: RealmNode<InactiveLinkDialog | PreviewLinkDialog | EditLinkDialog>;
287
- updateLinkUrl: RealmNode<string>;
289
+ updateLinkUrl: RealmNode<[string, string]>;
288
290
  switchFromPreviewToLinkEdit: RealmNode<true>;
289
291
  cancelLinkEdit: RealmNode<true>;
290
292
  removeLink: RealmNode<true>;
@@ -347,7 +349,7 @@ linkDialogPluginHooks: {
347
349
  dialogState: RealmNode<boolean>;
348
350
  onWindowChange: RealmNode<true>;
349
351
  linkDialogState: RealmNode<InactiveLinkDialog | PreviewLinkDialog | EditLinkDialog>;
350
- updateLinkUrl: RealmNode<string>;
352
+ updateLinkUrl: RealmNode<[string, string]>;
351
353
  switchFromPreviewToLinkEdit: RealmNode<true>;
352
354
  cancelLinkEdit: RealmNode<true>;
353
355
  removeLink: RealmNode<true>;
@@ -411,7 +413,7 @@ linkDialogPluginHooks: {
411
413
  dialogState: RealmNode<boolean>;
412
414
  onWindowChange: RealmNode<true>;
413
415
  linkDialogState: RealmNode<InactiveLinkDialog | PreviewLinkDialog | EditLinkDialog>;
414
- updateLinkUrl: RealmNode<string>;
416
+ updateLinkUrl: RealmNode<[string, string]>;
415
417
  switchFromPreviewToLinkEdit: RealmNode<true>;
416
418
  cancelLinkEdit: RealmNode<true>;
417
419
  removeLink: RealmNode<true>;
@@ -475,7 +477,7 @@ linkDialogPluginHooks: {
475
477
  dialogState: RealmNode<boolean>;
476
478
  onWindowChange: RealmNode<true>;
477
479
  linkDialogState: RealmNode<InactiveLinkDialog | PreviewLinkDialog | EditLinkDialog>;
478
- updateLinkUrl: RealmNode<string>;
480
+ updateLinkUrl: RealmNode<[string, string]>;
479
481
  switchFromPreviewToLinkEdit: RealmNode<true>;
480
482
  cancelLinkEdit: RealmNode<true>;
481
483
  removeLink: RealmNode<true>;
@@ -45,7 +45,9 @@ const linkDialogSystem = system(
45
45
  r.pub(linkDialogState, {
46
46
  type: "edit",
47
47
  initialUrl: node.getURL(),
48
+ initialTitle: node.getTitle() || "",
48
49
  url: node.getURL(),
50
+ title: node.getTitle() || "",
49
51
  linkNodeKey: node.getKey(),
50
52
  rectangle
51
53
  });
@@ -53,6 +55,8 @@ const linkDialogSystem = system(
53
55
  r.pub(linkDialogState, {
54
56
  type: "edit",
55
57
  initialUrl: "",
58
+ initialTitle: "",
59
+ title: "",
56
60
  url: "",
57
61
  linkNodeKey: "",
58
62
  rectangle
@@ -84,7 +88,7 @@ const linkDialogSystem = system(
84
88
  (event) => {
85
89
  if (event.key === "k" && (IS_APPLE ? event.metaKey : event.ctrlKey)) {
86
90
  const selection = $getSelection();
87
- if ($isRangeSelection(selection) && !selection.isCollapsed()) {
91
+ if ($isRangeSelection(selection) && (getLinkNodeInSelection(selection) || !selection.isCollapsed())) {
88
92
  r.pub(openLinkEditDialog, true);
89
93
  event.stopPropagation();
90
94
  return true;
@@ -120,8 +124,15 @@ const linkDialogSystem = system(
120
124
  r.sub(r.pipe(applyLinkChanges, r.o.withLatestFrom(linkDialogState, activeEditor)), ([, state, editor]) => {
121
125
  if (state.type === "edit") {
122
126
  const url = state.url.trim();
127
+ const title = state.title.trim();
123
128
  if (url.trim() !== "") {
124
- editor == null ? void 0 : editor.dispatchCommand(TOGGLE_LINK_COMMAND, url);
129
+ editor == null ? void 0 : editor.dispatchCommand(TOGGLE_LINK_COMMAND, { url, title });
130
+ setTimeout(() => {
131
+ editor == null ? void 0 : editor.update(() => {
132
+ const node = getLinkNodeInSelection($getSelection());
133
+ node == null ? void 0 : node.setTitle(title);
134
+ });
135
+ });
125
136
  r.pub(linkDialogState, {
126
137
  type: "preview",
127
138
  linkNodeKey: state.linkNodeKey,
@@ -144,11 +155,12 @@ const linkDialogSystem = system(
144
155
  r.pipe(
145
156
  updateLinkUrl,
146
157
  r.o.withLatestFrom(linkDialogState),
147
- r.o.map(([url, state]) => {
158
+ r.o.map(([[url, title], state]) => {
148
159
  if (state.type === "edit") {
149
160
  return {
150
161
  ...state,
151
- url
162
+ url,
163
+ title
152
164
  };
153
165
  } else {
154
166
  throw new Error("Cannot update link url when not in edit mode");
@@ -2,9 +2,22 @@ import React__default from "react";
2
2
  import { ButtonWithTooltip } from "../primitives/toolbar.js";
3
3
  import FrontmatterIcon from "../../../icons/frontmatter.svg.js";
4
4
  import { frontmatterPluginHooks } from "../../frontmatter/index.js";
5
+ import styles from "../../../styles/ui.module.css.js";
6
+ import classNames from "classnames";
5
7
  const InsertFrontmatter = () => {
6
8
  const insertFrontmatter = frontmatterPluginHooks.usePublisher("insertFrontmatter");
7
- return /* @__PURE__ */ React__default.createElement(ButtonWithTooltip, { title: "Insert frontmatter editor", onClick: insertFrontmatter.bind(null, true) }, /* @__PURE__ */ React__default.createElement(FrontmatterIcon, null));
9
+ const [hasFrontmatter] = frontmatterPluginHooks.useEmitterValues("hasFrontmatter");
10
+ return /* @__PURE__ */ React__default.createElement(
11
+ ButtonWithTooltip,
12
+ {
13
+ title: hasFrontmatter ? "Edit frontmatter" : "Insert frontmatter",
14
+ className: classNames({
15
+ [styles.activeToolbarButton]: hasFrontmatter
16
+ }),
17
+ onClick: insertFrontmatter.bind(null, true)
18
+ },
19
+ /* @__PURE__ */ React__default.createElement(FrontmatterIcon, null)
20
+ );
8
21
  };
9
22
  export {
10
23
  InsertFrontmatter