mirror of
https://github.com/docmost/docmost.git
synced 2026-05-08 23:33:09 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a308e085b5 | |||
| 73b6e95197 | |||
| b79b693f50 | |||
| ea27919079 | |||
| 2b6ac81c61 |
@@ -380,6 +380,7 @@
|
|||||||
"Real-time editor connection lost. Retrying...": "Real-time editor connection lost. Retrying...",
|
"Real-time editor connection lost. Retrying...": "Real-time editor connection lost. Retrying...",
|
||||||
"Table of contents": "Table of contents",
|
"Table of contents": "Table of contents",
|
||||||
"Add headings (H1, H2, H3) to generate a table of contents.": "Add headings (H1, H2, H3) to generate a table of contents.",
|
"Add headings (H1, H2, H3) to generate a table of contents.": "Add headings (H1, H2, H3) to generate a table of contents.",
|
||||||
|
"No table of contents yet": "No table of contents yet",
|
||||||
"Share": "Share",
|
"Share": "Share",
|
||||||
"Public sharing": "Public sharing",
|
"Public sharing": "Public sharing",
|
||||||
"Shared by": "Shared by",
|
"Shared by": "Shared by",
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
IconCalendar,
|
IconCalendar,
|
||||||
IconAppWindow,
|
IconAppWindow,
|
||||||
IconSitemap,
|
IconSitemap,
|
||||||
|
IconAlignLeft2,
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import {
|
import {
|
||||||
CommandProps,
|
CommandProps,
|
||||||
@@ -153,6 +154,14 @@ const CommandGroups: SlashMenuGroupedItemsType = {
|
|||||||
command: ({ editor, range }: CommandProps) =>
|
command: ({ editor, range }: CommandProps) =>
|
||||||
editor.chain().focus().deleteRange(range).setHorizontalRule().run(),
|
editor.chain().focus().deleteRange(range).setHorizontalRule().run(),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Table of contents",
|
||||||
|
description: "Insert table of contents",
|
||||||
|
searchTerms: ["toc"],
|
||||||
|
icon: IconAlignLeft2,
|
||||||
|
command: ({ editor, range }: CommandProps) =>
|
||||||
|
editor.chain().focus().deleteRange(range).insertTableOfContents().run(),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Image",
|
title: "Image",
|
||||||
description: "Upload any image from your device.",
|
description: "Upload any image from your device.",
|
||||||
|
|||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
.container {
|
||||||
|
counter-reset: h1 h2 h3 h4;
|
||||||
|
border-left: 1px solid
|
||||||
|
light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||||
|
}
|
||||||
|
|
||||||
|
.emptyState {
|
||||||
|
color: light-dark(
|
||||||
|
var(--mantine-color-dark-3),
|
||||||
|
var(--mantine-color-dark-2)
|
||||||
|
) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
text-decoration: none;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
text-align: start;
|
||||||
|
word-wrap: break-word;
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--mantine-color-text);
|
||||||
|
font-size: var(--mantine-font-size-sm);
|
||||||
|
line-height: var(--mantine-line-height-sm);
|
||||||
|
padding: 6px;
|
||||||
|
border: none;
|
||||||
|
border-bottom: none !important;
|
||||||
|
padding-left: calc(var(--level) * 1rem);
|
||||||
|
|
||||||
|
&[style*="--level: 1"] {
|
||||||
|
counter-increment: h1;
|
||||||
|
counter-reset: h2 h3 h4;
|
||||||
|
padding-left: 6px;
|
||||||
|
&::before {
|
||||||
|
content: counter(h1) ". ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[style*="--level: 2"] {
|
||||||
|
counter-increment: h2;
|
||||||
|
counter-reset: h3 h4;
|
||||||
|
&::before {
|
||||||
|
content: counter(h2) ". ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[style*="--level: 3"] {
|
||||||
|
counter-increment: h3;
|
||||||
|
counter-reset: h4;
|
||||||
|
&::before {
|
||||||
|
content: counter(h3) ". ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[style*="--level: 4"] {
|
||||||
|
counter-increment: h4;
|
||||||
|
&::before {
|
||||||
|
content: counter(h4) ". ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin hover {
|
||||||
|
background-color: light-dark(
|
||||||
|
var(--mantine-color-gray-2),
|
||||||
|
var(--mantine-color-dark-6)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+81
@@ -0,0 +1,81 @@
|
|||||||
|
import { Editor as CoreEditor } from "@tiptap/core";
|
||||||
|
import { TableOfContentsStorage } from "@tiptap/extension-table-of-contents";
|
||||||
|
import { NodeViewWrapper, useEditorState } from "@tiptap/react";
|
||||||
|
import { memo } from "react";
|
||||||
|
import classes from "./table-of-contents-nodeview.module.css";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { TextSelection } from "@tiptap/pm/state";
|
||||||
|
|
||||||
|
export type TableOfContentsProps = {
|
||||||
|
editor: CoreEditor;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TableOfContentsNodeview = memo(
|
||||||
|
({ editor }: TableOfContentsProps) => {
|
||||||
|
const content = useEditorState({
|
||||||
|
editor,
|
||||||
|
selector: (ctx) =>
|
||||||
|
(ctx.editor.storage.tableOfContents as TableOfContentsStorage)?.content,
|
||||||
|
});
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const onTocItemClick = (e, id) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (editor) {
|
||||||
|
const element = editor.view.dom.querySelector(`[data-toc-id="${id}"`);
|
||||||
|
const pos = editor.view.posAtDOM(element, 0);
|
||||||
|
|
||||||
|
// set focus
|
||||||
|
const tr = editor.view.state.tr;
|
||||||
|
|
||||||
|
tr.setSelection(new TextSelection(tr.doc.resolve(pos)));
|
||||||
|
|
||||||
|
editor.view.dispatch(tr);
|
||||||
|
|
||||||
|
editor.view.focus();
|
||||||
|
|
||||||
|
if (history.pushState) {
|
||||||
|
history.pushState(null, null, `#${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.scrollTo({
|
||||||
|
top: element.getBoundingClientRect().top + window.scrollY,
|
||||||
|
behavior: "smooth",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NodeViewWrapper>
|
||||||
|
<div contentEditable={false}>
|
||||||
|
{content.length > 0 ? (
|
||||||
|
<div className={classes.container}>
|
||||||
|
{content
|
||||||
|
.filter((item) => item.level <= 4)
|
||||||
|
.map((item) => (
|
||||||
|
<a
|
||||||
|
key={item.id}
|
||||||
|
href={`#${item.id}`}
|
||||||
|
style={{ "--level": item.level } as React.CSSProperties}
|
||||||
|
onClick={(e) => onTocItemClick(e, item.id)}
|
||||||
|
className={classes.link}
|
||||||
|
data-item-index={item.itemIndex}
|
||||||
|
draggable="false"
|
||||||
|
>
|
||||||
|
{item.textContent}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={classes.emptyState}>
|
||||||
|
{t("No table of contents yet")}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</NodeViewWrapper>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
TableOfContentsNodeview.displayName = "TableOfContentsNodeview";
|
||||||
@@ -11,6 +11,7 @@ import { Typography } from "@tiptap/extension-typography";
|
|||||||
import { TextStyle } from "@tiptap/extension-text-style";
|
import { TextStyle } from "@tiptap/extension-text-style";
|
||||||
import { Color } from "@tiptap/extension-color";
|
import { Color } from "@tiptap/extension-color";
|
||||||
import SlashCommand from "@/features/editor/extensions/slash-command";
|
import SlashCommand from "@/features/editor/extensions/slash-command";
|
||||||
|
import { TableOfContents as TiptapTableOfContents } from "@tiptap/extension-table-of-contents";
|
||||||
import { Collaboration } from "@tiptap/extension-collaboration";
|
import { Collaboration } from "@tiptap/extension-collaboration";
|
||||||
import { CollaborationCursor } from "@tiptap/extension-collaboration-cursor";
|
import { CollaborationCursor } from "@tiptap/extension-collaboration-cursor";
|
||||||
import { HocuspocusProvider } from "@hocuspocus/provider";
|
import { HocuspocusProvider } from "@hocuspocus/provider";
|
||||||
@@ -40,6 +41,8 @@ import {
|
|||||||
Mention,
|
Mention,
|
||||||
Subpages,
|
Subpages,
|
||||||
TableDndExtension,
|
TableDndExtension,
|
||||||
|
TableOfContentsNode,
|
||||||
|
generateNodeId,
|
||||||
} from "@docmost/editor-ext";
|
} from "@docmost/editor-ext";
|
||||||
import {
|
import {
|
||||||
randomElement,
|
randomElement,
|
||||||
@@ -78,6 +81,7 @@ import { MarkdownClipboard } from "@/features/editor/extensions/markdown-clipboa
|
|||||||
import EmojiCommand from "./emoji-command";
|
import EmojiCommand from "./emoji-command";
|
||||||
import { CharacterCount } from "@tiptap/extension-character-count";
|
import { CharacterCount } from "@tiptap/extension-character-count";
|
||||||
import { countWords } from "alfaaz";
|
import { countWords } from "alfaaz";
|
||||||
|
import { TableOfContentsNodeview } from "@/features/editor/components/table-of-contents/table-of-contents-nodeview.tsx";
|
||||||
|
|
||||||
const lowlight = createLowlight(common);
|
const lowlight = createLowlight(common);
|
||||||
lowlight.register("mermaid", plaintext);
|
lowlight.register("mermaid", plaintext);
|
||||||
@@ -228,19 +232,25 @@ export const mainExtensions = [
|
|||||||
SearchAndReplace.extend({
|
SearchAndReplace.extend({
|
||||||
addKeyboardShortcuts() {
|
addKeyboardShortcuts() {
|
||||||
return {
|
return {
|
||||||
'Mod-f': () => {
|
"Mod-f": () => {
|
||||||
const event = new CustomEvent("openFindDialogFromEditor", {});
|
const event = new CustomEvent("openFindDialogFromEditor", {});
|
||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
'Escape': () => {
|
Escape: () => {
|
||||||
const event = new CustomEvent("closeFindDialogFromEditor", {});
|
const event = new CustomEvent("closeFindDialogFromEditor", {});
|
||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
}).configure(),
|
}).configure(),
|
||||||
|
TiptapTableOfContents.configure({
|
||||||
|
getId: () => generateNodeId(),
|
||||||
|
}),
|
||||||
|
TableOfContentsNode.configure({
|
||||||
|
view: TableOfContentsNodeview,
|
||||||
|
}),
|
||||||
] as any;
|
] as any;
|
||||||
|
|
||||||
type CollabExtensions = (provider: HocuspocusProvider, user: IUser) => any[];
|
type CollabExtensions = (provider: HocuspocusProvider, user: IUser) => any[];
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ import {
|
|||||||
Embed,
|
Embed,
|
||||||
Mention,
|
Mention,
|
||||||
Subpages,
|
Subpages,
|
||||||
|
TableOfContentsNode,
|
||||||
|
generateNodeId,
|
||||||
} from '@docmost/editor-ext';
|
} from '@docmost/editor-ext';
|
||||||
|
import { TableOfContents as TiptapTableOfContents } from '@tiptap/extension-table-of-contents';
|
||||||
import { generateText, getSchema, JSONContent } from '@tiptap/core';
|
import { generateText, getSchema, JSONContent } from '@tiptap/core';
|
||||||
import { generateHTML, generateJSON } from '../common/helpers/prosemirror/html';
|
import { generateHTML, generateJSON } from '../common/helpers/prosemirror/html';
|
||||||
// @tiptap/html library works best for generating prosemirror json state but not HTML
|
// @tiptap/html library works best for generating prosemirror json state but not HTML
|
||||||
@@ -80,6 +83,10 @@ export const tiptapExtensions = [
|
|||||||
Embed,
|
Embed,
|
||||||
Mention,
|
Mention,
|
||||||
Subpages,
|
Subpages,
|
||||||
|
TableOfContentsNode,
|
||||||
|
TiptapTableOfContents.configure({
|
||||||
|
getId: () => generateNodeId(),
|
||||||
|
}),
|
||||||
] as any;
|
] as any;
|
||||||
|
|
||||||
export function jsonToHtml(tiptapJson: any) {
|
export function jsonToHtml(tiptapJson: any) {
|
||||||
|
|||||||
+1
-1
Submodule apps/server/src/ee updated: c92deeeac1...9957da11d6
+42
-39
@@ -21,47 +21,48 @@
|
|||||||
"@braintree/sanitize-url": "^7.1.0",
|
"@braintree/sanitize-url": "^7.1.0",
|
||||||
"@docmost/editor-ext": "workspace:*",
|
"@docmost/editor-ext": "workspace:*",
|
||||||
"@floating-ui/dom": "^1.7.3",
|
"@floating-ui/dom": "^1.7.3",
|
||||||
"@hocuspocus/extension-redis": "^2.15.2",
|
"@hocuspocus/extension-redis": "^2.15.3",
|
||||||
"@hocuspocus/provider": "^2.15.2",
|
"@hocuspocus/provider": "^2.15.3",
|
||||||
"@hocuspocus/server": "^2.15.2",
|
"@hocuspocus/server": "^2.15.3",
|
||||||
"@hocuspocus/transformer": "^2.15.2",
|
"@hocuspocus/transformer": "^2.15.3",
|
||||||
"@joplin/turndown": "^4.0.74",
|
"@joplin/turndown": "^4.0.74",
|
||||||
"@joplin/turndown-plugin-gfm": "^1.0.56",
|
"@joplin/turndown-plugin-gfm": "^1.0.56",
|
||||||
"@sindresorhus/slugify": "1.1.0",
|
"@sindresorhus/slugify": "1.1.0",
|
||||||
"@tiptap/core": "^2.10.3",
|
"@tiptap/core": "^2.27.1",
|
||||||
"@tiptap/extension-code-block": "^2.10.3",
|
"@tiptap/extension-code-block": "^2.27.1",
|
||||||
"@tiptap/extension-code-block-lowlight": "^2.10.3",
|
"@tiptap/extension-code-block-lowlight": "^2.27.1",
|
||||||
"@tiptap/extension-collaboration": "^2.10.3",
|
"@tiptap/extension-collaboration": "^2.27.1",
|
||||||
"@tiptap/extension-collaboration-cursor": "^2.10.3",
|
"@tiptap/extension-collaboration-cursor": "^2.27.1",
|
||||||
"@tiptap/extension-color": "^2.10.3",
|
"@tiptap/extension-color": "^2.27.1",
|
||||||
"@tiptap/extension-document": "^2.10.3",
|
"@tiptap/extension-document": "^2.27.1",
|
||||||
"@tiptap/extension-heading": "^2.10.3",
|
"@tiptap/extension-heading": "^2.27.1",
|
||||||
"@tiptap/extension-highlight": "^2.10.3",
|
"@tiptap/extension-highlight": "^2.27.1",
|
||||||
"@tiptap/extension-history": "^2.10.3",
|
"@tiptap/extension-history": "^2.27.1",
|
||||||
"@tiptap/extension-image": "^2.10.3",
|
"@tiptap/extension-image": "^2.27.1",
|
||||||
"@tiptap/extension-link": "^2.10.3",
|
"@tiptap/extension-link": "^2.27.1",
|
||||||
"@tiptap/extension-list-item": "^2.10.3",
|
"@tiptap/extension-list-item": "^2.27.1",
|
||||||
"@tiptap/extension-list-keymap": "^2.10.3",
|
"@tiptap/extension-list-keymap": "^2.27.1",
|
||||||
"@tiptap/extension-placeholder": "^2.10.3",
|
"@tiptap/extension-placeholder": "^2.27.1",
|
||||||
"@tiptap/extension-subscript": "^2.10.3",
|
"@tiptap/extension-subscript": "^2.27.1",
|
||||||
"@tiptap/extension-superscript": "^2.10.3",
|
"@tiptap/extension-superscript": "^2.27.1",
|
||||||
"@tiptap/extension-table": "^2.10.3",
|
"@tiptap/extension-table": "^2.27.1",
|
||||||
"@tiptap/extension-table-cell": "^2.10.3",
|
"@tiptap/extension-table-cell": "^2.27.1",
|
||||||
"@tiptap/extension-table-header": "^2.10.3",
|
"@tiptap/extension-table-header": "^2.27.1",
|
||||||
"@tiptap/extension-table-row": "^2.10.3",
|
"@tiptap/extension-table-of-contents": "2.26.3",
|
||||||
"@tiptap/extension-task-item": "^2.10.3",
|
"@tiptap/extension-table-row": "^2.27.1",
|
||||||
"@tiptap/extension-task-list": "^2.10.3",
|
"@tiptap/extension-task-item": "^2.27.1",
|
||||||
"@tiptap/extension-text": "^2.10.3",
|
"@tiptap/extension-task-list": "^2.27.1",
|
||||||
"@tiptap/extension-text-align": "^2.10.3",
|
"@tiptap/extension-text": "^2.27.1",
|
||||||
"@tiptap/extension-text-style": "^2.10.3",
|
"@tiptap/extension-text-align": "^2.27.1",
|
||||||
"@tiptap/extension-typography": "^2.10.3",
|
"@tiptap/extension-text-style": "^2.27.1",
|
||||||
"@tiptap/extension-underline": "^2.10.3",
|
"@tiptap/extension-typography": "^2.27.1",
|
||||||
"@tiptap/extension-youtube": "^2.10.3",
|
"@tiptap/extension-underline": "^2.27.1",
|
||||||
"@tiptap/html": "^2.10.3",
|
"@tiptap/extension-youtube": "^2.27.1",
|
||||||
"@tiptap/pm": "^2.10.3",
|
"@tiptap/html": "^2.27.1",
|
||||||
"@tiptap/react": "^2.10.3",
|
"@tiptap/pm": "^2.27.1",
|
||||||
"@tiptap/starter-kit": "^2.10.3",
|
"@tiptap/react": "^2.27.1",
|
||||||
"@tiptap/suggestion": "^2.10.3",
|
"@tiptap/starter-kit": "^2.27.1",
|
||||||
|
"@tiptap/suggestion": "^2.27.1",
|
||||||
"@types/qrcode": "^1.5.5",
|
"@types/qrcode": "^1.5.5",
|
||||||
"bytes": "^3.1.2",
|
"bytes": "^3.1.2",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
@@ -76,6 +77,7 @@
|
|||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"y-indexeddb": "^9.0.12",
|
"y-indexeddb": "^9.0.12",
|
||||||
|
"y-prosemirror": "1.3.7",
|
||||||
"yjs": "^13.6.27"
|
"yjs": "^13.6.27"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -98,7 +100,8 @@
|
|||||||
"react-arborist@3.4.0": "patches/react-arborist@3.4.0.patch"
|
"react-arborist@3.4.0": "patches/react-arborist@3.4.0.patch"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"jsdom": "25.0.1"
|
"jsdom": "25.0.1",
|
||||||
|
"y-prosemirror": "1.3.7"
|
||||||
},
|
},
|
||||||
"neverBuiltDependencies": []
|
"neverBuiltDependencies": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,3 +20,4 @@ export * from "./lib/markdown";
|
|||||||
export * from "./lib/search-and-replace";
|
export * from "./lib/search-and-replace";
|
||||||
export * from "./lib/embed-provider";
|
export * from "./lib/embed-provider";
|
||||||
export * from "./lib/subpages";
|
export * from "./lib/subpages";
|
||||||
|
export * from "./lib/table-of-contents-node";
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export { TableOfContentsNode } from "./table-of-contents";
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { Node } from "@tiptap/core";
|
||||||
|
import { ReactNodeViewRenderer } from "@tiptap/react";
|
||||||
|
|
||||||
|
export interface TableOfContentsNodeOptions {
|
||||||
|
HTMLAttributes: Record<string, any>;
|
||||||
|
view: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "@tiptap/core" {
|
||||||
|
interface Commands<ReturnType> {
|
||||||
|
tableOfContentsNode: {
|
||||||
|
insertTableOfContents: () => ReturnType;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TableOfContentsNode = Node.create<TableOfContentsNodeOptions>({
|
||||||
|
name: "tableOfContentsNode",
|
||||||
|
group: "block",
|
||||||
|
atom: true,
|
||||||
|
selectable: true,
|
||||||
|
draggable: true,
|
||||||
|
inline: false,
|
||||||
|
|
||||||
|
parseHTML() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
tag: 'div[data-type="table-of-content"]',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
renderHTML({ HTMLAttributes }) {
|
||||||
|
return ["div", { ...HTMLAttributes, "data-type": "table-of-content" }];
|
||||||
|
},
|
||||||
|
|
||||||
|
addNodeView() {
|
||||||
|
return ReactNodeViewRenderer(this.options.view);
|
||||||
|
},
|
||||||
|
|
||||||
|
addCommands() {
|
||||||
|
return {
|
||||||
|
insertTableOfContents:
|
||||||
|
() =>
|
||||||
|
({ commands }) => {
|
||||||
|
return commands.insertContent({
|
||||||
|
type: this.name,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -5,6 +5,7 @@ import { CellSelection, TableMap } from "@tiptap/pm/tables";
|
|||||||
import { Node, ResolvedPos } from "@tiptap/pm/model";
|
import { Node, ResolvedPos } from "@tiptap/pm/model";
|
||||||
import Table from "@tiptap/extension-table";
|
import Table from "@tiptap/extension-table";
|
||||||
import { sanitizeUrl as braintreeSanitizeUrl } from "@braintree/sanitize-url";
|
import { sanitizeUrl as braintreeSanitizeUrl } from "@braintree/sanitize-url";
|
||||||
|
import { customAlphabet } from "nanoid";
|
||||||
|
|
||||||
export const isRectSelected = (rect: any) => (selection: CellSelection) => {
|
export const isRectSelected = (rect: any) => (selection: CellSelection) => {
|
||||||
const map = TableMap.get(selection.$anchorCell.node(-1));
|
const map = TableMap.get(selection.$anchorCell.node(-1));
|
||||||
@@ -383,9 +384,12 @@ export function icon(name: string) {
|
|||||||
|
|
||||||
export function sanitizeUrl(url: string | undefined): string {
|
export function sanitizeUrl(url: string | undefined): string {
|
||||||
if (!url) return "";
|
if (!url) return "";
|
||||||
|
|
||||||
const sanitized = braintreeSanitizeUrl(url);
|
const sanitized = braintreeSanitizeUrl(url);
|
||||||
|
|
||||||
// Return empty string instead of "about:blank"
|
// Return empty string instead of "about:blank"
|
||||||
return sanitized === "about:blank" ? "" : sanitized;
|
return sanitized === "about:blank" ? "" : sanitized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const alphabet = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
export const generateNodeId = customAlphabet(alphabet, 15);
|
||||||
|
|||||||
Generated
+404
-378
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user