Restore Ultracite + fix sidebar (#1233)
This commit is contained in:
parent
8fbfc253fa
commit
947ed094a6
177 changed files with 6908 additions and 8306 deletions
|
|
@ -1,15 +1,15 @@
|
|||
import { textblockTypeInputRule } from 'prosemirror-inputrules';
|
||||
import { Schema } from 'prosemirror-model';
|
||||
import { schema } from 'prosemirror-schema-basic';
|
||||
import { addListNodes } from 'prosemirror-schema-list';
|
||||
import type { Transaction } from 'prosemirror-state';
|
||||
import type { EditorView } from 'prosemirror-view';
|
||||
import type { MutableRefObject } from 'react';
|
||||
import { textblockTypeInputRule } from "prosemirror-inputrules";
|
||||
import { Schema } from "prosemirror-model";
|
||||
import { schema } from "prosemirror-schema-basic";
|
||||
import { addListNodes } from "prosemirror-schema-list";
|
||||
import type { Transaction } from "prosemirror-state";
|
||||
import type { EditorView } from "prosemirror-view";
|
||||
import type { MutableRefObject } from "react";
|
||||
|
||||
import { buildContentFromDocument } from './functions';
|
||||
import { buildContentFromDocument } from "./functions";
|
||||
|
||||
export const documentSchema = new Schema({
|
||||
nodes: addListNodes(schema.spec.nodes, 'paragraph block*', 'block'),
|
||||
nodes: addListNodes(schema.spec.nodes, "paragraph block*", "block"),
|
||||
marks: schema.spec.marks,
|
||||
});
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ export function headingRule(level: number) {
|
|||
return textblockTypeInputRule(
|
||||
new RegExp(`^(#{1,${level}})\\s$`),
|
||||
documentSchema.nodes.heading,
|
||||
() => ({ level }),
|
||||
() => ({ level })
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -30,15 +30,17 @@ export const handleTransaction = ({
|
|||
editorRef: MutableRefObject<EditorView | null>;
|
||||
onSaveContent: (updatedContent: string, debounce: boolean) => void;
|
||||
}) => {
|
||||
if (!editorRef || !editorRef.current) return;
|
||||
if (!editorRef || !editorRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newState = editorRef.current.state.apply(transaction);
|
||||
editorRef.current.updateState(newState);
|
||||
|
||||
if (transaction.docChanged && !transaction.getMeta('no-save')) {
|
||||
if (transaction.docChanged && !transaction.getMeta("no-save")) {
|
||||
const updatedContent = buildContentFromDocument(newState.doc);
|
||||
|
||||
if (transaction.getMeta('no-debounce')) {
|
||||
if (transaction.getMeta("no-debounce")) {
|
||||
onSaveContent(updatedContent, false);
|
||||
} else {
|
||||
onSaveContent(updatedContent, true);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Modified from https://github.com/hamflx/prosemirror-diff/blob/master/src/diff.js
|
||||
|
||||
import { diff_match_patch } from 'diff-match-patch';
|
||||
import { Fragment, Node } from 'prosemirror-model';
|
||||
import { diff_match_patch } from "diff-match-patch";
|
||||
import { Fragment, Node } from "prosemirror-model";
|
||||
|
||||
export const DiffType = {
|
||||
Unchanged: 0,
|
||||
|
|
@ -49,7 +49,7 @@ export const patchDocumentNode = (schema, oldNode, newNode) => {
|
|||
const matchedNodes = matchNodes(
|
||||
schema,
|
||||
diffOldChildren,
|
||||
diffNewChildren,
|
||||
diffNewChildren
|
||||
).sort((a, b) => b.count - a.count);
|
||||
const bestMatch = matchedNodes[0];
|
||||
if (bestMatch) {
|
||||
|
|
@ -62,11 +62,11 @@ export const patchDocumentNode = (schema, oldNode, newNode) => {
|
|||
...patchRemainNodes(
|
||||
schema,
|
||||
oldBeforeMatchChildren,
|
||||
newBeforeMatchChildren,
|
||||
),
|
||||
newBeforeMatchChildren
|
||||
)
|
||||
);
|
||||
finalLeftChildren.push(
|
||||
...diffOldChildren.slice(oldStartIndex, oldEndIndex),
|
||||
...diffOldChildren.slice(oldStartIndex, oldEndIndex)
|
||||
);
|
||||
|
||||
const oldAfterMatchChildren = diffOldChildren.slice(oldEndIndex);
|
||||
|
|
@ -76,24 +76,24 @@ export const patchDocumentNode = (schema, oldNode, newNode) => {
|
|||
...patchRemainNodes(
|
||||
schema,
|
||||
oldAfterMatchChildren,
|
||||
newAfterMatchChildren,
|
||||
),
|
||||
newAfterMatchChildren
|
||||
)
|
||||
);
|
||||
} else {
|
||||
finalLeftChildren.push(
|
||||
...patchRemainNodes(schema, diffOldChildren, diffNewChildren),
|
||||
...patchRemainNodes(schema, diffOldChildren, diffNewChildren)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
finalLeftChildren.push(
|
||||
...patchRemainNodes(schema, diffOldChildren, diffNewChildren),
|
||||
...patchRemainNodes(schema, diffOldChildren, diffNewChildren)
|
||||
);
|
||||
}
|
||||
|
||||
return createNewNode(oldNode, [...finalLeftChildren, ...finalRightChildren]);
|
||||
};
|
||||
|
||||
const matchNodes = (schema, oldChildren, newChildren) => {
|
||||
const matchNodes = (_schema, oldChildren, newChildren) => {
|
||||
const matches = [];
|
||||
for (
|
||||
let oldStartIndex = 0;
|
||||
|
|
@ -155,7 +155,7 @@ const patchRemainNodes = (schema, oldChildren, newChildren) => {
|
|||
!isTextNode(rightOldNode) && matchNodeType(rightOldNode, rightNewNode);
|
||||
if (Array.isArray(leftOldNode) && Array.isArray(leftNewNode)) {
|
||||
finalLeftChildren.push(
|
||||
...patchTextNodes(schema, leftOldNode, leftNewNode),
|
||||
...patchTextNodes(schema, leftOldNode, leftNewNode)
|
||||
);
|
||||
left += 1;
|
||||
continue;
|
||||
|
|
@ -165,7 +165,7 @@ const patchRemainNodes = (schema, oldChildren, newChildren) => {
|
|||
const equalityLeft = computeChildEqualityFactor(leftOldNode, leftNewNode);
|
||||
const equalityRight = computeChildEqualityFactor(
|
||||
rightOldNode,
|
||||
rightNewNode,
|
||||
rightNewNode
|
||||
);
|
||||
if (equalityLeft < equalityRight) {
|
||||
updateLeft = false;
|
||||
|
|
@ -175,21 +175,21 @@ const patchRemainNodes = (schema, oldChildren, newChildren) => {
|
|||
}
|
||||
if (updateLeft) {
|
||||
finalLeftChildren.push(
|
||||
patchDocumentNode(schema, leftOldNode, leftNewNode),
|
||||
patchDocumentNode(schema, leftOldNode, leftNewNode)
|
||||
);
|
||||
left += 1;
|
||||
} else if (updateRight) {
|
||||
finalRightChildren.unshift(
|
||||
patchDocumentNode(schema, rightOldNode, rightNewNode),
|
||||
patchDocumentNode(schema, rightOldNode, rightNewNode)
|
||||
);
|
||||
right += 1;
|
||||
} else {
|
||||
// Delete and insert
|
||||
finalLeftChildren.push(
|
||||
createDiffNode(schema, leftOldNode, DiffType.Deleted),
|
||||
createDiffNode(schema, leftOldNode, DiffType.Deleted)
|
||||
);
|
||||
finalLeftChildren.push(
|
||||
createDiffNode(schema, leftNewNode, DiffType.Inserted),
|
||||
createDiffNode(schema, leftNewNode, DiffType.Inserted)
|
||||
);
|
||||
left += 1;
|
||||
}
|
||||
|
|
@ -202,7 +202,7 @@ const patchRemainNodes = (schema, oldChildren, newChildren) => {
|
|||
...oldChildren
|
||||
.slice(left, left + deleteNodeLen)
|
||||
.flat()
|
||||
.map((node) => createDiffNode(schema, node, DiffType.Deleted)),
|
||||
.map((node) => createDiffNode(schema, node, DiffType.Deleted))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +211,7 @@ const patchRemainNodes = (schema, oldChildren, newChildren) => {
|
|||
...newChildren
|
||||
.slice(left, left + insertNodeLen)
|
||||
.flat()
|
||||
.map((node) => createDiffNode(schema, node, DiffType.Inserted)),
|
||||
.map((node) => createDiffNode(schema, node, DiffType.Inserted))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -223,8 +223,8 @@ export const patchTextNodes = (schema, oldNode, newNode) => {
|
|||
const dmp = new diff_match_patch();
|
||||
|
||||
// Concatenate the text from the text nodes
|
||||
const oldText = oldNode.map((n) => getNodeText(n)).join('');
|
||||
const newText = newNode.map((n) => getNodeText(n)).join('');
|
||||
const oldText = oldNode.map((n) => getNodeText(n)).join("");
|
||||
const newText = newNode.map((n) => getNodeText(n)).join("");
|
||||
|
||||
// Tokenize the text into sentences
|
||||
const oldSentences = tokenizeSentences(oldText);
|
||||
|
|
@ -233,7 +233,7 @@ export const patchTextNodes = (schema, oldNode, newNode) => {
|
|||
// Map sentences to unique characters
|
||||
const { chars1, chars2, lineArray } = sentencesToChars(
|
||||
oldSentences,
|
||||
newSentences,
|
||||
newSentences
|
||||
);
|
||||
|
||||
// Perform the diff
|
||||
|
|
@ -242,7 +242,7 @@ export const patchTextNodes = (schema, oldNode, newNode) => {
|
|||
// Convert back to sentences
|
||||
diffs = diffs.map(([type, text]) => {
|
||||
const sentences = text
|
||||
.split('')
|
||||
.split("")
|
||||
.map((char) => lineArray[char.charCodeAt(0)]);
|
||||
return [type, sentences];
|
||||
});
|
||||
|
|
@ -253,7 +253,7 @@ export const patchTextNodes = (schema, oldNode, newNode) => {
|
|||
const node = createTextNode(
|
||||
schema,
|
||||
sentence,
|
||||
type !== DiffType.Unchanged ? [createDiffMark(schema, type)] : [],
|
||||
type !== DiffType.Unchanged ? [createDiffMark(schema, type)] : []
|
||||
);
|
||||
return node;
|
||||
});
|
||||
|
|
@ -284,7 +284,7 @@ const sentencesToChars = (oldSentences, newSentences) => {
|
|||
lineStart++;
|
||||
return String.fromCharCode(lineHash[line]);
|
||||
})
|
||||
.join('');
|
||||
.join("");
|
||||
|
||||
const chars2 = newSentences
|
||||
.map((sentence) => {
|
||||
|
|
@ -297,17 +297,17 @@ const sentencesToChars = (oldSentences, newSentences) => {
|
|||
lineStart++;
|
||||
return String.fromCharCode(lineHash[line]);
|
||||
})
|
||||
.join('');
|
||||
.join("");
|
||||
|
||||
return { chars1, chars2, lineArray };
|
||||
};
|
||||
|
||||
export const computeChildEqualityFactor = (node1, node2) => {
|
||||
export const computeChildEqualityFactor = (_node1, _node2) => {
|
||||
return 0;
|
||||
};
|
||||
|
||||
export const assertNodeTypeEqual = (node1, node2) => {
|
||||
if (getNodeProperty(node1, 'type') !== getNodeProperty(node2, 'type')) {
|
||||
if (getNodeProperty(node1, "type") !== getNodeProperty(node2, "type")) {
|
||||
throw new Error(`node type not equal: ${node1.type} !== ${node2.type}`);
|
||||
}
|
||||
};
|
||||
|
|
@ -329,14 +329,14 @@ export const isNodeEqual = (node1, node2) => {
|
|||
);
|
||||
}
|
||||
|
||||
const type1 = getNodeProperty(node1, 'type');
|
||||
const type2 = getNodeProperty(node2, 'type');
|
||||
const type1 = getNodeProperty(node1, "type");
|
||||
const type2 = getNodeProperty(node2, "type");
|
||||
if (type1 !== type2) {
|
||||
return false;
|
||||
}
|
||||
if (isTextNode(node1)) {
|
||||
const text1 = getNodeProperty(node1, 'text');
|
||||
const text2 = getNodeProperty(node2, 'text');
|
||||
const text1 = getNodeProperty(node1, "text");
|
||||
const text2 = getNodeProperty(node2, "text");
|
||||
if (text1 !== text2) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -396,7 +396,7 @@ export const normalizeNodeContent = (node) => {
|
|||
};
|
||||
|
||||
export const getNodeProperty = (node, property) => {
|
||||
if (property === 'type') {
|
||||
if (property === "type") {
|
||||
return node.type?.name;
|
||||
}
|
||||
return node[property];
|
||||
|
|
@ -413,7 +413,7 @@ export const getNodeChildren = (node) => node.content?.content ?? [];
|
|||
|
||||
export const getNodeText = (node) => node.text;
|
||||
|
||||
export const isTextNode = (node) => node.type?.name === 'text';
|
||||
export const isTextNode = (node) => node.type?.name === "text";
|
||||
|
||||
export const matchNodeType = (node1, node2) =>
|
||||
node1.type?.name === node2.type?.name ||
|
||||
|
|
@ -421,25 +421,25 @@ export const matchNodeType = (node1, node2) =>
|
|||
|
||||
export const createNewNode = (oldNode, children) => {
|
||||
if (!oldNode.type) {
|
||||
throw new Error('oldNode.type is undefined');
|
||||
throw new Error("oldNode.type is undefined");
|
||||
}
|
||||
return new Node(
|
||||
oldNode.type,
|
||||
oldNode.attrs,
|
||||
Fragment.fromArray(children),
|
||||
oldNode.marks,
|
||||
oldNode.marks
|
||||
);
|
||||
};
|
||||
|
||||
export const createDiffNode = (schema, node, type) => {
|
||||
return mapDocumentNode(node, (node) => {
|
||||
if (isTextNode(node)) {
|
||||
return createTextNode(schema, getNodeText(node), [
|
||||
...(node.marks || []),
|
||||
return mapDocumentNode(node, (currentNode) => {
|
||||
if (isTextNode(currentNode)) {
|
||||
return createTextNode(schema, getNodeText(currentNode), [
|
||||
...(currentNode.marks || []),
|
||||
createDiffMark(schema, type),
|
||||
]);
|
||||
}
|
||||
return node;
|
||||
return currentNode;
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -447,21 +447,21 @@ function mapDocumentNode(node, mapper) {
|
|||
const copy = node.copy(
|
||||
Fragment.from(
|
||||
node.content.content
|
||||
.map((node) => mapDocumentNode(node, mapper))
|
||||
.filter((n) => n),
|
||||
),
|
||||
.map((currentNode) => mapDocumentNode(currentNode, mapper))
|
||||
.filter((n) => n)
|
||||
)
|
||||
);
|
||||
return mapper(copy) || copy;
|
||||
}
|
||||
|
||||
export const createDiffMark = (schema, type) => {
|
||||
if (type === DiffType.Inserted) {
|
||||
return schema.mark('diffMark', { type });
|
||||
return schema.mark("diffMark", { type });
|
||||
}
|
||||
if (type === DiffType.Deleted) {
|
||||
return schema.mark('diffMark', { type });
|
||||
return schema.mark("diffMark", { type });
|
||||
}
|
||||
throw new Error('type is not valid');
|
||||
throw new Error("type is not valid");
|
||||
};
|
||||
|
||||
export const createTextNode = (schema, content, marks = []) => {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { defaultMarkdownSerializer } from 'prosemirror-markdown';
|
||||
import { DOMParser, type Node } from 'prosemirror-model';
|
||||
import { Decoration, DecorationSet, type EditorView } from 'prosemirror-view';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import { defaultMarkdownSerializer } from "prosemirror-markdown";
|
||||
import { DOMParser, type Node } from "prosemirror-model";
|
||||
import { Decoration, DecorationSet, type EditorView } from "prosemirror-view";
|
||||
import { renderToString } from "react-dom/server";
|
||||
|
||||
import { Response } from '@/components/elements/response';
|
||||
import { Response } from "@/components/elements/response";
|
||||
|
||||
import { documentSchema } from './config';
|
||||
import { createSuggestionWidget, type UISuggestion } from './suggestions';
|
||||
import { documentSchema } from "./config";
|
||||
import { createSuggestionWidget, type UISuggestion } from "./suggestions";
|
||||
|
||||
export const buildDocumentFromContent = (content: string) => {
|
||||
const parser = DOMParser.fromSchema(documentSchema);
|
||||
const stringFromMarkdown = renderToString(<Response>{content}</Response>);
|
||||
const tempContainer = document.createElement('div');
|
||||
const tempContainer = document.createElement("div");
|
||||
tempContainer.innerHTML = stringFromMarkdown;
|
||||
return parser.parse(tempContainer);
|
||||
};
|
||||
|
|
@ -23,10 +23,10 @@ export const buildContentFromDocument = (document: Node) => {
|
|||
};
|
||||
|
||||
export const createDecorations = (
|
||||
suggestions: Array<UISuggestion>,
|
||||
view: EditorView,
|
||||
suggestions: UISuggestion[],
|
||||
view: EditorView
|
||||
) => {
|
||||
const decorations: Array<Decoration> = [];
|
||||
const decorations: Decoration[] = [];
|
||||
|
||||
for (const suggestion of suggestions) {
|
||||
decorations.push(
|
||||
|
|
@ -34,27 +34,27 @@ export const createDecorations = (
|
|||
suggestion.selectionStart,
|
||||
suggestion.selectionEnd,
|
||||
{
|
||||
class: 'suggestion-highlight',
|
||||
class: "suggestion-highlight",
|
||||
},
|
||||
{
|
||||
suggestionId: suggestion.id,
|
||||
type: 'highlight',
|
||||
},
|
||||
),
|
||||
type: "highlight",
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
decorations.push(
|
||||
Decoration.widget(
|
||||
suggestion.selectionStart,
|
||||
(view) => {
|
||||
const { dom } = createSuggestionWidget(suggestion, view);
|
||||
(currentView) => {
|
||||
const { dom } = createSuggestionWidget(suggestion, currentView);
|
||||
return dom;
|
||||
},
|
||||
{
|
||||
suggestionId: suggestion.id,
|
||||
type: 'widget',
|
||||
},
|
||||
),
|
||||
type: "widget",
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { createRoot } from 'react-dom/client';
|
||||
import { createRoot } from "react-dom/client";
|
||||
|
||||
// biome-ignore lint/complexity/noStaticOnlyClass: "Needs to be static"
|
||||
export class ReactRenderer {
|
||||
static render(component: React.ReactElement, dom: HTMLElement) {
|
||||
const root = createRoot(dom);
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
import type { Node } from 'prosemirror-model';
|
||||
import { Plugin, PluginKey } from 'prosemirror-state';
|
||||
import type { Node } from "prosemirror-model";
|
||||
import { Plugin, PluginKey } from "prosemirror-state";
|
||||
import {
|
||||
type Decoration,
|
||||
DecorationSet,
|
||||
type EditorView,
|
||||
} from 'prosemirror-view';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import { Suggestion as PreviewSuggestion } from '@/components/suggestion';
|
||||
import type { Suggestion } from '@/lib/db/schema';
|
||||
import type { ArtifactKind } from '@/components/artifact';
|
||||
} from "prosemirror-view";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import type { ArtifactKind } from "@/components/artifact";
|
||||
import { Suggestion as PreviewSuggestion } from "@/components/suggestion";
|
||||
import type { Suggestion } from "@/lib/db/schema";
|
||||
|
||||
export interface UISuggestion extends Suggestion {
|
||||
selectionStart: number;
|
||||
selectionEnd: number;
|
||||
}
|
||||
|
||||
interface Position {
|
||||
type Position = {
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
};
|
||||
|
||||
function findPositionsInDoc(doc: Node, searchText: string): Position | null {
|
||||
let positions: { start: number; end: number } | null = null;
|
||||
|
|
@ -46,8 +45,8 @@ function findPositionsInDoc(doc: Node, searchText: string): Position | null {
|
|||
|
||||
export function projectWithPositions(
|
||||
doc: Node,
|
||||
suggestions: Array<Suggestion>,
|
||||
): Array<UISuggestion> {
|
||||
suggestions: Suggestion[]
|
||||
): UISuggestion[] {
|
||||
return suggestions.map((suggestion) => {
|
||||
const positions = findPositionsInDoc(doc, suggestion.originalText);
|
||||
|
||||
|
|
@ -70,12 +69,12 @@ export function projectWithPositions(
|
|||
export function createSuggestionWidget(
|
||||
suggestion: UISuggestion,
|
||||
view: EditorView,
|
||||
artifactKind: ArtifactKind = 'text',
|
||||
artifactKind: ArtifactKind = "text"
|
||||
): { dom: HTMLElement; destroy: () => void } {
|
||||
const dom = document.createElement('span');
|
||||
const dom = document.createElement("span");
|
||||
const root = createRoot(dom);
|
||||
|
||||
dom.addEventListener('mousedown', (event) => {
|
||||
dom.addEventListener("mousedown", (event) => {
|
||||
event.preventDefault();
|
||||
view.dom.blur();
|
||||
});
|
||||
|
|
@ -92,7 +91,7 @@ export function createSuggestionWidget(
|
|||
state.doc,
|
||||
currentDecorations.find().filter((decoration: Decoration) => {
|
||||
return decoration.spec.suggestionId !== suggestion.id;
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
decorationTransaction.setMeta(suggestionsPluginKey, {
|
||||
|
|
@ -105,20 +104,20 @@ export function createSuggestionWidget(
|
|||
const textTransaction = view.state.tr.replaceWith(
|
||||
suggestion.selectionStart,
|
||||
suggestion.selectionEnd,
|
||||
state.schema.text(suggestion.suggestedText),
|
||||
state.schema.text(suggestion.suggestedText)
|
||||
);
|
||||
|
||||
textTransaction.setMeta('no-debounce', true);
|
||||
textTransaction.setMeta("no-debounce", true);
|
||||
|
||||
dispatch(textTransaction);
|
||||
};
|
||||
|
||||
root.render(
|
||||
<PreviewSuggestion
|
||||
suggestion={suggestion}
|
||||
onApply={onApply}
|
||||
artifactKind={artifactKind}
|
||||
/>,
|
||||
onApply={onApply}
|
||||
suggestion={suggestion}
|
||||
/>
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
@ -132,7 +131,7 @@ export function createSuggestionWidget(
|
|||
};
|
||||
}
|
||||
|
||||
export const suggestionsPluginKey = new PluginKey('suggestions');
|
||||
export const suggestionsPluginKey = new PluginKey("suggestions");
|
||||
export const suggestionsPlugin = new Plugin({
|
||||
key: suggestionsPluginKey,
|
||||
state: {
|
||||
|
|
@ -141,7 +140,9 @@ export const suggestionsPlugin = new Plugin({
|
|||
},
|
||||
apply(tr, state) {
|
||||
const newDecorations = tr.getMeta(suggestionsPluginKey);
|
||||
if (newDecorations) return newDecorations;
|
||||
if (newDecorations) {
|
||||
return newDecorations;
|
||||
}
|
||||
|
||||
return {
|
||||
decorations: state.decorations.map(tr.mapping, tr.doc),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue