@firecms/editor 3.0.0-canary.113 → 3.0.0-canary.114

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.
@@ -0,0 +1,35 @@
1
+ import { Extension } from "@tiptap/core";
2
+ import { PluginKey } from "@tiptap/pm/state";
3
+ import { DecorationSet } from "@tiptap/pm/view";
4
+ declare module "@tiptap/core" {
5
+ interface Commands<ReturnType> {
6
+ highlightDecoration: {
7
+ toggleAutocompleteHighlight: (range?: {
8
+ from: number;
9
+ to: number;
10
+ }) => ReturnType;
11
+ removeAutocompleteHighlight: () => ReturnType;
12
+ };
13
+ }
14
+ }
15
+ export interface HighlightRange {
16
+ from: number;
17
+ to: number;
18
+ }
19
+ interface AutocompleteHighlightState {
20
+ highlight?: HighlightRange;
21
+ decorationSet?: DecorationSet;
22
+ }
23
+ export interface HighlightDecorationOptions {
24
+ pluginKey: PluginKey<AutocompleteHighlightState>;
25
+ highlight?: {
26
+ from: number;
27
+ to: number;
28
+ };
29
+ }
30
+ /**
31
+ * This plugin is used to highlight the current autocomplete suggestion.
32
+ * It allows to set a range and remove it.
33
+ */
34
+ export declare const HighlightDecorationExtension: (initialHighlight?: HighlightRange) => Extension<HighlightDecorationOptions, any>;
35
+ export {};
@@ -0,0 +1,18 @@
1
+ import { PluginKey } from "prosemirror-state";
2
+ import { DecorationSet } from "prosemirror-view";
3
+ import { Extension } from "@tiptap/core";
4
+ declare module "@tiptap/core" {
5
+ interface Commands<ReturnType> {
6
+ textLoadingDecoration: {
7
+ toggleLoadingDecoration: (loadingHtml?: string) => ReturnType;
8
+ removeLoadingDecoration: () => ReturnType;
9
+ };
10
+ }
11
+ }
12
+ export declare const loadingDecorationKey: PluginKey<LoadingDecorationState>;
13
+ interface LoadingDecorationState {
14
+ decorationSet: DecorationSet;
15
+ hasDecoration: boolean;
16
+ }
17
+ declare const TextLoadingDecorationExtension: Extension<any, any>;
18
+ export default TextLoadingDecorationExtension;
package/dist/index.es.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import React, { forwardRef, useRef, useEffect, useMemo, useState, useImperativeHandle, useDeferredValue } from "react";
3
- import TiptapUnderline from "@tiptap/extension-underline";
3
+ import { Underline } from "@tiptap/extension-underline";
4
4
  import TextStyle from "@tiptap/extension-text-style";
5
5
  import { Color } from "@tiptap/extension-color";
6
6
  import BulletList from "@tiptap/extension-bullet-list";
7
7
  import Highlight from "@tiptap/extension-highlight";
8
8
  import { useCurrentEditor, BubbleMenu, isNodeSelection, Node, mergeAttributes, ReactRenderer, EditorProvider } from "@tiptap/react";
9
9
  import { Slot } from "@radix-ui/react-slot";
10
- import { Popover, Button, ExpandMoreIcon, CheckIcon, TextFieldsIcon, LooksOneIcon, LooksTwoIcon, Looks3Icon, CheckBoxIcon, FormatListBulletedIcon, FormatListNumberedIcon, FormatQuoteIcon, CodeIcon, cls, DeleteIcon, FormatBoldIcon, FormatItalicIcon, FormatUnderlinedIcon, FormatStrikethroughIcon, defaultBorderMixin, AutoAwesomeIcon, ImageIcon, useInjectStyles, Separator } from "@firecms/ui";
10
+ import { Popover, Button, ExpandMoreIcon, CheckIcon, TextFieldsIcon, LooksOneIcon, LooksTwoIcon, Looks3Icon, CheckBoxIcon, FormatListBulletedIcon, FormatListNumberedIcon, FormatQuoteIcon, CodeIcon, cls, focusedDisabled, DeleteIcon, FormatBoldIcon, FormatItalicIcon, FormatUnderlinedIcon, FormatStrikethroughIcon, defaultBorderMixin, AutoAwesomeIcon, ImageIcon, useInjectStyles, Separator } from "@firecms/ui";
11
11
  import StarterKit from "@tiptap/starter-kit";
12
12
  import HorizontalRule from "@tiptap/extension-horizontal-rule";
13
13
  import TiptapLink from "@tiptap/extension-link";
@@ -15,7 +15,7 @@ import TiptapImage from "@tiptap/extension-image";
15
15
  import Placeholder from "@tiptap/extension-placeholder";
16
16
  import { TaskItem } from "@tiptap/extension-task-item";
17
17
  import { TaskList } from "@tiptap/extension-task-list";
18
- import { Extension, InputRule, Node as Node$1 } from "@tiptap/core";
18
+ import { Extension, InputRule } from "@tiptap/core";
19
19
  import { PluginKey, Plugin } from "prosemirror-state";
20
20
  import { DecorationSet, Decoration } from "prosemirror-view";
21
21
  import { Markdown } from "tiptap-markdown";
@@ -243,7 +243,7 @@ const LinkSelector = ({
243
243
  autoFocus: open,
244
244
  placeholder: "Paste a link",
245
245
  defaultValue: editor.getAttributes("link").href || "",
246
- className: "text-gray-900 dark:text-white flex-grow bg-transparent p-1 text-sm outline-none"
246
+ className: cls("text-gray-900 dark:text-white flex-grow bg-transparent p-1 text-sm outline-none", focusedDisabled)
247
247
  }
248
248
  ),
249
249
  editor.getAttributes("link").href ? /* @__PURE__ */ jsx(
@@ -350,7 +350,7 @@ function removeClassesFromJson(jsonObj) {
350
350
  return jsonObj;
351
351
  }
352
352
  const loadingDecorationKey = new PluginKey("loadingDecoration");
353
- const LoadingDecoration = Extension.create({
353
+ const TextLoadingDecorationExtension = Extension.create({
354
354
  name: "loadingDecoration",
355
355
  addOptions() {
356
356
  return {
@@ -410,7 +410,6 @@ const LoadingDecoration = Extension.create({
410
410
  })
411
411
  ];
412
412
  },
413
- // @ts-ignore
414
413
  addCommands() {
415
414
  return {
416
415
  toggleLoadingDecoration: (loadingHtml) => ({ state, dispatch }) => {
@@ -418,8 +417,6 @@ const LoadingDecoration = Extension.create({
418
417
  const pos = selection.from;
419
418
  if (!dispatch) return false;
420
419
  const pluginKey = this.options.pluginKey;
421
- const pluginState = pluginKey.get(state);
422
- pluginState?.getState(state)?.hasDecoration ?? false;
423
420
  const tr = state.tr.setMeta(pluginKey, {
424
421
  pos,
425
422
  type: "loadingDecoration",
@@ -1379,72 +1376,109 @@ const suggestionItems = [
1379
1376
  }
1380
1377
  }
1381
1378
  ];
1382
- const AiContentExtensionKey = new PluginKey$1("AiContentExtension");
1383
- const AiContentExtension = Node$1.create({
1384
- name: "aiContent",
1379
+ function buildDecorationSet(highlight, doc) {
1380
+ const decorations = [];
1381
+ if (highlight) {
1382
+ decorations.push(
1383
+ Decoration$1.inline(highlight.from, highlight.to, {
1384
+ class: "dark:bg-slate-700 bg-slate-300"
1385
+ })
1386
+ );
1387
+ }
1388
+ const decorationSet = DecorationSet$1.create(doc, decorations);
1389
+ return decorationSet;
1390
+ }
1391
+ const HighlightDecorationExtension = (initialHighlight) => Extension.create({
1392
+ name: "highlightDecoration",
1393
+ addOptions() {
1394
+ return {
1395
+ pluginKey: new PluginKey$1("highlightDecoration"),
1396
+ highlight: initialHighlight
1397
+ };
1398
+ },
1385
1399
  addProseMirrorPlugins() {
1386
- const pluginKey = AiContentExtensionKey;
1387
- const plugin = new Plugin$1({
1388
- key: pluginKey,
1389
- state: {
1390
- init() {
1391
- return DecorationSet$1.empty;
1400
+ const pluginKey = this.options.pluginKey;
1401
+ return [
1402
+ new Plugin$1({
1403
+ key: pluginKey,
1404
+ state: {
1405
+ init: (_, { doc }) => {
1406
+ const highlight = this.options.highlight;
1407
+ const decorationSet = highlight && doc ? buildDecorationSet(highlight, doc) : DecorationSet$1.empty;
1408
+ return {
1409
+ decorationSet,
1410
+ highlight
1411
+ };
1412
+ },
1413
+ apply(transaction, oldState) {
1414
+ const action = transaction.getMeta(pluginKey);
1415
+ const highlight = action?.range;
1416
+ if (action?.type === "highlightDecoration") {
1417
+ const doc = transaction.doc;
1418
+ const { remove } = action;
1419
+ if (remove) {
1420
+ return {
1421
+ decorationSet: DecorationSet$1.empty
1422
+ };
1423
+ }
1424
+ const decorationSet = buildDecorationSet(highlight, doc);
1425
+ return {
1426
+ decorationSet,
1427
+ highlight
1428
+ };
1429
+ } else {
1430
+ return oldState;
1431
+ }
1432
+ }
1392
1433
  },
1393
- apply(tr, oldValue) {
1394
- return tr.getMeta(pluginKey) || oldValue;
1395
- }
1396
- },
1397
- view() {
1398
- return {
1399
- update(view, prevState) {
1400
- const prevDecos = plugin.getState(prevState);
1401
- const newDecos = plugin.getState(view.state);
1402
- if (prevDecos !== newDecos) {
1403
- view.updateState(view.state);
1434
+ props: {
1435
+ decorations(state) {
1436
+ const autocompleteState = this.getState(state);
1437
+ if (autocompleteState?.decorationSet) {
1438
+ return autocompleteState.decorationSet;
1439
+ } else {
1440
+ return DecorationSet$1.empty;
1404
1441
  }
1405
1442
  }
1406
- };
1443
+ }
1444
+ })
1445
+ ];
1446
+ },
1447
+ addCommands() {
1448
+ return {
1449
+ toggleAutocompleteHighlight: (range) => ({
1450
+ state,
1451
+ dispatch
1452
+ }) => {
1453
+ const { selection } = state;
1454
+ const pos = selection.from;
1455
+ if (!dispatch) return false;
1456
+ const pluginKey = this.options.pluginKey;
1457
+ const tr = state.tr.setMeta(pluginKey, {
1458
+ pos,
1459
+ type: "highlightDecoration",
1460
+ remove: false,
1461
+ range
1462
+ });
1463
+ dispatch(tr);
1464
+ return true;
1465
+ },
1466
+ removeAutocompleteHighlight: () => ({
1467
+ state,
1468
+ dispatch
1469
+ }) => {
1470
+ if (!dispatch) return false;
1471
+ const pluginKey = this.options.pluginKey;
1472
+ const tr = state.tr.setMeta(pluginKey, {
1473
+ pos: 0,
1474
+ // We can pass any position as it will remove the entire decoration set
1475
+ type: "highlightDecoration",
1476
+ remove: true
1477
+ });
1478
+ dispatch(tr);
1479
+ return true;
1407
1480
  }
1408
- // props: {
1409
- // decorations(editorState) {
1410
- // return pluginKey.getState(editorState);
1411
- // },
1412
- // handleKeyDown(view, event) {
1413
- // const pluginState = pluginKey.getState(view.state);
1414
- // if (!pluginState) return false;
1415
- //
1416
- // if (event.key === "Tab") {
1417
- // const selection = view.state.selection;
1418
- // const cursorPos = selection.$head.pos;
1419
- //
1420
- // // Find the suggestion decoration
1421
- // const decoration = pluginState.find(cursorPos)?.[0];
1422
- // console.log(decoration);
1423
- // // @ts-ignore
1424
- // if (decoration && decoration.type?.spec?.suggestionText) {
1425
- // // @ts-ignore
1426
- // const suggestionText = decoration.type.spec.suggestionText;
1427
- // const tr = view.state.tr;
1428
- // tr.insertText(suggestionText, cursorPos);
1429
- //
1430
- // // Dispatch the transaction
1431
- // view.dispatch(tr.setMeta("addToHistory", true));
1432
- //
1433
- // // Clear decorations after applying suggestion
1434
- // const clearDecoTr = view.state.tr;
1435
- // clearDecoTr.setMeta(pluginKey, { decorations: DecorationSet.empty });
1436
- // view.dispatch(clearDecoTr);
1437
- //
1438
- // // Prevent default behavior of the keypress
1439
- // event.preventDefault();
1440
- // return true;
1441
- // }
1442
- // }
1443
- // return false;
1444
- // }
1445
- // }
1446
- });
1447
- return [plugin];
1481
+ };
1448
1482
  }
1449
1483
  });
1450
1484
  const CustomDocument = Document.extend({
@@ -1477,40 +1511,21 @@ const FireCMSEditor = ({
1477
1511
  const deferredHighlight = useDeferredValue(highlight);
1478
1512
  useEffect(() => {
1479
1513
  if (version === void 0) return;
1480
- if (version > 0 && editorRef.current) {
1514
+ if (version > -1 && editorRef.current) {
1481
1515
  editorRef.current?.commands.setContent(content ?? "");
1482
1516
  }
1483
1517
  }, [version]);
1484
- const currentHighlight = useRef(deferredHighlight);
1485
1518
  useEffect(() => {
1486
1519
  if (version === void 0) return;
1487
1520
  if (editorRef.current && version > 0) {
1488
- const {
1489
- from: selectionFrom,
1490
- to: selectionTo
1491
- } = editorRef.current.state.selection;
1492
1521
  const chain = editorRef.current.chain();
1493
- const currentContent = editorRef.current.storage.markdown.getMarkdown();
1494
- if (currentContent !== content) {
1495
- chain.setContent(content ?? "");
1496
- }
1497
1522
  if (deferredHighlight) {
1498
- if (currentHighlight.current?.from !== deferredHighlight.from || currentHighlight.current?.to !== deferredHighlight.to) {
1499
- chain.focus().setTextSelection({
1500
- from: deferredHighlight?.from,
1501
- to: deferredHighlight?.to
1502
- }).toggleHighlight({ color: "#64748B68" });
1503
- }
1523
+ chain.focus().toggleAutocompleteHighlight(deferredHighlight).run();
1504
1524
  } else {
1505
- chain.focus().selectAll().unsetHighlight();
1525
+ chain.focus().removeAutocompleteHighlight().run();
1506
1526
  }
1507
- currentHighlight.current = deferredHighlight;
1508
- chain.focus().setTextSelection({
1509
- from: selectionFrom,
1510
- to: selectionTo
1511
- }).run();
1512
1527
  }
1513
- }, [editorRef.current, deferredHighlight]);
1528
+ }, [deferredHighlight?.from, deferredHighlight?.to]);
1514
1529
  const onEditorUpdate = (editor) => {
1515
1530
  editorRef.current = editor;
1516
1531
  if (onMarkdownContentChange) {
@@ -1527,19 +1542,19 @@ const FireCMSEditor = ({
1527
1542
  };
1528
1543
  const proseClass = proseClasses[textSize];
1529
1544
  const extensions = useMemo(() => [
1530
- // AutocompleteExtension,
1531
- LoadingDecoration,
1532
- TiptapUnderline,
1545
+ starterKit,
1546
+ CustomDocument,
1547
+ HighlightDecorationExtension(highlight),
1548
+ TextLoadingDecorationExtension,
1549
+ Underline,
1533
1550
  TextStyle,
1534
1551
  Color,
1535
1552
  BulletList,
1536
- CustomDocument,
1537
1553
  Highlight.configure({
1538
1554
  multicolor: true
1539
1555
  }),
1540
1556
  CustomKeymap,
1541
1557
  DragAndDrop,
1542
- starterKit,
1543
1558
  placeholder,
1544
1559
  tiptapLink,
1545
1560
  imageExtension,
@@ -1547,9 +1562,6 @@ const FireCMSEditor = ({
1547
1562
  taskItem,
1548
1563
  markdownExtension,
1549
1564
  horizontalRule,
1550
- AiContentExtension.configure({
1551
- aiController
1552
- }),
1553
1565
  SlashCommand.configure({
1554
1566
  HTMLAttributes: {
1555
1567
  class: "mention"