Merge branch 'feat/release-v2' of https://github.com/classroomio/classroomio into feat/release-v2
This commit is contained in:
+21
-21
@@ -37,33 +37,33 @@
|
|||||||
"vaul-svelte": "1.0.0-next.7"
|
"vaul-svelte": "1.0.0-next.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aarkue/tiptap-math-extension": "^1.4.0",
|
|
||||||
"@floating-ui/dom": "^1.7.4",
|
"@floating-ui/dom": "^1.7.4",
|
||||||
"@tiptap/core": "^3.7.1",
|
"@tiptap/core": "^3.10.7",
|
||||||
"@tiptap/extension-bubble-menu": "^3.7.1",
|
"@tiptap/extension-bubble-menu": "^3.10.7",
|
||||||
"@tiptap/extension-code-block-lowlight": "^3.7.1",
|
"@tiptap/extension-code-block-lowlight": "^3.10.7",
|
||||||
"@tiptap/extension-highlight": "^3.7.1",
|
"@tiptap/extension-highlight": "^3.10.7",
|
||||||
"@tiptap/extension-image": "^3.7.1",
|
"@tiptap/extension-image": "^3.10.7",
|
||||||
"@tiptap/extension-list": "^3.7.1",
|
"@tiptap/extension-list": "^3.10.7",
|
||||||
"@tiptap/extension-subscript": "^3.7.1",
|
"@tiptap/extension-mathematics": "^3.10.7",
|
||||||
"@tiptap/extension-superscript": "^3.7.1",
|
"@tiptap/extension-subscript": "^3.10.7",
|
||||||
"@tiptap/extension-table": "^3.7.1",
|
"@tiptap/extension-superscript": "^3.10.7",
|
||||||
"@tiptap/extension-text-align": "^3.7.1",
|
"@tiptap/extension-table": "^3.10.7",
|
||||||
"@tiptap/extension-text-style": "^3.7.1",
|
"@tiptap/extension-text-align": "^3.10.7",
|
||||||
"@tiptap/extension-typography": "^3.7.1",
|
"@tiptap/extension-text-style": "^3.10.7",
|
||||||
"@tiptap/extensions": "^3.7.1",
|
"@tiptap/extension-typography": "^3.10.7",
|
||||||
"@tiptap/pm": "^3.7.1",
|
"@tiptap/extensions": "^3.10.7",
|
||||||
"@tiptap/starter-kit": "^3.7.1",
|
"@tiptap/markdown": "^3.10.7",
|
||||||
"@tiptap/suggestion": "^3.7.1",
|
"@tiptap/pm": "^3.10.7",
|
||||||
|
"@tiptap/starter-kit": "^3.10.7",
|
||||||
|
"@tiptap/suggestion": "^3.10.7",
|
||||||
"d3-array": "^3.2.4",
|
"d3-array": "^3.2.4",
|
||||||
"d3-scale": "^4.0.2",
|
"d3-scale": "^4.0.2",
|
||||||
"dompurify": "^3.3.0",
|
"dompurify": "^3.3.0",
|
||||||
"katex": "^0.16.22",
|
"katex": "^0.16.25",
|
||||||
"lowlight": "^3.3.0",
|
"lowlight": "^3.3.0",
|
||||||
"svelte-tiptap": "^3.0.0",
|
"svelte-tiptap": "^2.2.0",
|
||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
"tiptap-extension-auto-joiner": "^0.1.3",
|
"tiptap-extension-auto-joiner": "^0.1.3"
|
||||||
"tiptap-markdown": "^0.9.0"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"bits-ui": "^2.14.4"
|
"bits-ui": "^2.14.4"
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ import Table from '@lucide/svelte/icons/table';
|
|||||||
import Underline from '@lucide/svelte/icons/underline';
|
import Underline from '@lucide/svelte/icons/underline';
|
||||||
import Undo from '@lucide/svelte/icons/undo-2';
|
import Undo from '@lucide/svelte/icons/undo-2';
|
||||||
import Video from '@lucide/svelte/icons/video';
|
import Video from '@lucide/svelte/icons/video';
|
||||||
|
import Radical from '@lucide/svelte/icons/radical';
|
||||||
|
import SquareRadical from '@lucide/svelte/icons/square-radical';
|
||||||
|
import { isTextSelection } from '@tiptap/core';
|
||||||
|
import Pilcrow from '@lucide/svelte/icons/pilcrow';
|
||||||
import { isMac } from '../utils';
|
import { isMac } from '../utils';
|
||||||
|
|
||||||
const commands: Record<string, EdraToolBarCommands[]> = {
|
const commands: Record<string, EdraToolBarCommands[]> = {
|
||||||
@@ -64,6 +68,9 @@ const commands: Record<string, EdraToolBarCommands[]> = {
|
|||||||
onClick: (editor) => {
|
onClick: (editor) => {
|
||||||
editor.chain().focus().toggleHeading({ level: 1 }).run();
|
editor.chain().focus().toggleHeading({ level: 1 }).run();
|
||||||
},
|
},
|
||||||
|
turnInto: (editor, pos) => {
|
||||||
|
editor.chain().setNodeSelection(pos).setHeading({ level: 1 }).run();
|
||||||
|
},
|
||||||
clickable: (editor) => {
|
clickable: (editor) => {
|
||||||
return editor.can().toggleHeading({ level: 1 });
|
return editor.can().toggleHeading({ level: 1 });
|
||||||
},
|
},
|
||||||
@@ -79,6 +86,9 @@ const commands: Record<string, EdraToolBarCommands[]> = {
|
|||||||
onClick: (editor) => {
|
onClick: (editor) => {
|
||||||
editor.chain().focus().toggleHeading({ level: 2 }).run();
|
editor.chain().focus().toggleHeading({ level: 2 }).run();
|
||||||
},
|
},
|
||||||
|
turnInto: (editor, pos) => {
|
||||||
|
editor.chain().setNodeSelection(pos).setHeading({ level: 2 }).run();
|
||||||
|
},
|
||||||
clickable: (editor) => {
|
clickable: (editor) => {
|
||||||
return editor.can().toggleHeading({ level: 2 });
|
return editor.can().toggleHeading({ level: 2 });
|
||||||
},
|
},
|
||||||
@@ -94,6 +104,9 @@ const commands: Record<string, EdraToolBarCommands[]> = {
|
|||||||
onClick: (editor) => {
|
onClick: (editor) => {
|
||||||
editor.chain().focus().toggleHeading({ level: 3 }).run();
|
editor.chain().focus().toggleHeading({ level: 3 }).run();
|
||||||
},
|
},
|
||||||
|
turnInto: (editor, pos) => {
|
||||||
|
editor.chain().setNodeSelection(pos).setHeading({ level: 3 }).run();
|
||||||
|
},
|
||||||
clickable: (editor) => {
|
clickable: (editor) => {
|
||||||
return editor.can().toggleHeading({ level: 3 });
|
return editor.can().toggleHeading({ level: 3 });
|
||||||
},
|
},
|
||||||
@@ -109,6 +122,9 @@ const commands: Record<string, EdraToolBarCommands[]> = {
|
|||||||
onClick: (editor) => {
|
onClick: (editor) => {
|
||||||
editor.chain().focus().toggleHeading({ level: 4 }).run();
|
editor.chain().focus().toggleHeading({ level: 4 }).run();
|
||||||
},
|
},
|
||||||
|
turnInto: (editor, pos) => {
|
||||||
|
editor.chain().setNodeSelection(pos).setHeading({ level: 4 }).run();
|
||||||
|
},
|
||||||
clickable: (editor) => {
|
clickable: (editor) => {
|
||||||
return editor.can().toggleHeading({ level: 4 });
|
return editor.can().toggleHeading({ level: 4 });
|
||||||
},
|
},
|
||||||
@@ -136,6 +152,24 @@ const commands: Record<string, EdraToolBarCommands[]> = {
|
|||||||
return editor.isActive('link');
|
return editor.isActive('link');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: Pilcrow,
|
||||||
|
name: 'paragraph',
|
||||||
|
tooltip: 'Paragraph',
|
||||||
|
shortCut: `${isMac ? '⌘⇧' : 'Ctrl+Shift+'}0`,
|
||||||
|
onClick: (editor) => {
|
||||||
|
editor.chain().focus().setParagraph().run();
|
||||||
|
},
|
||||||
|
turnInto: (editor, pos) => {
|
||||||
|
editor.chain().setNodeSelection(pos).setParagraph().run();
|
||||||
|
},
|
||||||
|
clickable: (editor) => {
|
||||||
|
return editor.can().setParagraph();
|
||||||
|
},
|
||||||
|
isActive: (editor) => {
|
||||||
|
return editor.isActive('paragraph');
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: Bold,
|
icon: Bold,
|
||||||
name: 'bold',
|
name: 'bold',
|
||||||
@@ -144,6 +178,9 @@ const commands: Record<string, EdraToolBarCommands[]> = {
|
|||||||
onClick: (editor) => {
|
onClick: (editor) => {
|
||||||
editor.chain().focus().toggleBold().run();
|
editor.chain().focus().toggleBold().run();
|
||||||
},
|
},
|
||||||
|
turnInto: (editor, pos) => {
|
||||||
|
editor.chain().setNodeSelection(pos).setMark('bold').run();
|
||||||
|
},
|
||||||
clickable: (editor) => {
|
clickable: (editor) => {
|
||||||
return editor.can().toggleBold();
|
return editor.can().toggleBold();
|
||||||
},
|
},
|
||||||
@@ -159,6 +196,9 @@ const commands: Record<string, EdraToolBarCommands[]> = {
|
|||||||
onClick: (editor) => {
|
onClick: (editor) => {
|
||||||
editor.chain().focus().toggleItalic().run();
|
editor.chain().focus().toggleItalic().run();
|
||||||
},
|
},
|
||||||
|
turnInto: (editor, pos) => {
|
||||||
|
editor.chain().setNodeSelection(pos).setMark('italic').run();
|
||||||
|
},
|
||||||
clickable: (editor) => {
|
clickable: (editor) => {
|
||||||
return editor.can().toggleItalic();
|
return editor.can().toggleItalic();
|
||||||
},
|
},
|
||||||
@@ -174,6 +214,9 @@ const commands: Record<string, EdraToolBarCommands[]> = {
|
|||||||
onClick: (editor) => {
|
onClick: (editor) => {
|
||||||
editor.chain().focus().toggleUnderline().run();
|
editor.chain().focus().toggleUnderline().run();
|
||||||
},
|
},
|
||||||
|
turnInto: (editor, pos) => {
|
||||||
|
editor.chain().setNodeSelection(pos).setMark('underline').run();
|
||||||
|
},
|
||||||
clickable: (editor) => {
|
clickable: (editor) => {
|
||||||
return editor.can().toggleUnderline();
|
return editor.can().toggleUnderline();
|
||||||
},
|
},
|
||||||
@@ -189,6 +232,9 @@ const commands: Record<string, EdraToolBarCommands[]> = {
|
|||||||
onClick: (editor) => {
|
onClick: (editor) => {
|
||||||
editor.chain().focus().toggleStrike().run();
|
editor.chain().focus().toggleStrike().run();
|
||||||
},
|
},
|
||||||
|
turnInto: (editor, pos) => {
|
||||||
|
editor.chain().setNodeSelection(pos).setMark('strike').run();
|
||||||
|
},
|
||||||
clickable: (editor) => {
|
clickable: (editor) => {
|
||||||
return editor.can().toggleStrike();
|
return editor.can().toggleStrike();
|
||||||
},
|
},
|
||||||
@@ -204,6 +250,9 @@ const commands: Record<string, EdraToolBarCommands[]> = {
|
|||||||
onClick: (editor) => {
|
onClick: (editor) => {
|
||||||
editor.chain().focus().toggleBlockquote().run();
|
editor.chain().focus().toggleBlockquote().run();
|
||||||
},
|
},
|
||||||
|
turnInto: (editor, pos) => {
|
||||||
|
editor.chain().setNodeSelection(pos).toggleBlockquote().run();
|
||||||
|
},
|
||||||
clickable: (editor) => {
|
clickable: (editor) => {
|
||||||
return editor.can().toggleBlockquote();
|
return editor.can().toggleBlockquote();
|
||||||
},
|
},
|
||||||
@@ -219,6 +268,9 @@ const commands: Record<string, EdraToolBarCommands[]> = {
|
|||||||
onClick: (editor) => {
|
onClick: (editor) => {
|
||||||
editor.chain().focus().toggleCode().run();
|
editor.chain().focus().toggleCode().run();
|
||||||
},
|
},
|
||||||
|
turnInto: (editor, pos) => {
|
||||||
|
editor.chain().setNodeSelection(pos).toggleCodeBlock().run();
|
||||||
|
},
|
||||||
clickable: (editor) => {
|
clickable: (editor) => {
|
||||||
return editor.can().toggleCode();
|
return editor.can().toggleCode();
|
||||||
},
|
},
|
||||||
@@ -320,6 +372,9 @@ const commands: Record<string, EdraToolBarCommands[]> = {
|
|||||||
onClick: (editor) => {
|
onClick: (editor) => {
|
||||||
editor.chain().focus().toggleBulletList().run();
|
editor.chain().focus().toggleBulletList().run();
|
||||||
},
|
},
|
||||||
|
turnInto: (editor, pos) => {
|
||||||
|
editor.chain().setNodeSelection(pos).toggleBulletList().run();
|
||||||
|
},
|
||||||
isActive: (editor) => editor.isActive('bulletList')
|
isActive: (editor) => editor.isActive('bulletList')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -330,7 +385,15 @@ const commands: Record<string, EdraToolBarCommands[]> = {
|
|||||||
onClick: (editor) => {
|
onClick: (editor) => {
|
||||||
editor.chain().focus().toggleOrderedList().run();
|
editor.chain().focus().toggleOrderedList().run();
|
||||||
},
|
},
|
||||||
isActive: (editor) => editor.isActive('orderedList')
|
turnInto: (editor, pos) => {
|
||||||
|
editor.chain().setNodeSelection(pos).toggleOrderedList().run();
|
||||||
|
},
|
||||||
|
clickable: (editor) => {
|
||||||
|
return editor.can().toggleOrderedList();
|
||||||
|
},
|
||||||
|
isActive: (editor) => {
|
||||||
|
return editor.isActive('orderedList');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: ListChecks,
|
icon: ListChecks,
|
||||||
@@ -340,7 +403,15 @@ const commands: Record<string, EdraToolBarCommands[]> = {
|
|||||||
onClick: (editor) => {
|
onClick: (editor) => {
|
||||||
editor.chain().focus().toggleTaskList().run();
|
editor.chain().focus().toggleTaskList().run();
|
||||||
},
|
},
|
||||||
isActive: (editor) => editor.isActive('taskList')
|
turnInto: (editor, pos) => {
|
||||||
|
editor.chain().setNodeSelection(pos).toggleTaskList().run();
|
||||||
|
},
|
||||||
|
clickable: (editor) => {
|
||||||
|
return editor.can().toggleTaskList();
|
||||||
|
},
|
||||||
|
isActive: (editor) => {
|
||||||
|
return editor.isActive('taskList');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
media: [
|
media: [
|
||||||
@@ -398,6 +469,34 @@ const commands: Record<string, EdraToolBarCommands[]> = {
|
|||||||
},
|
},
|
||||||
isActive: (editor) => editor.isActive('table')
|
isActive: (editor) => editor.isActive('table')
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
math: [
|
||||||
|
{
|
||||||
|
icon: Radical,
|
||||||
|
name: 'mathematics',
|
||||||
|
tooltip: 'Inline Expression',
|
||||||
|
onClick: (editor) => {
|
||||||
|
let latex = 'a^2 + b^2 = c^2';
|
||||||
|
const chain = editor.chain().focus();
|
||||||
|
if (isTextSelection(editor.view.state.selection)) {
|
||||||
|
const { from, to } = editor.view.state.selection;
|
||||||
|
latex = editor.view.state.doc.textBetween(from, to);
|
||||||
|
chain.deleteRange({ from, to });
|
||||||
|
}
|
||||||
|
chain.insertInlineMath({ latex }).run();
|
||||||
|
},
|
||||||
|
isActive: (editor) => editor.isActive('inlineMath')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: SquareRadical,
|
||||||
|
name: 'mathematics',
|
||||||
|
tooltip: 'Block Expression',
|
||||||
|
onClick: (editor) => {
|
||||||
|
const latex = 'a^2 + b^2 = c^2';
|
||||||
|
editor.chain().focus().insertBlockMath({ latex }).run();
|
||||||
|
},
|
||||||
|
isActive: (editor) => editor.isActive('blockMath')
|
||||||
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export interface EdraToolBarCommands {
|
|||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
shortCut?: string;
|
shortCut?: string;
|
||||||
onClick?: (editor: Editor) => void;
|
onClick?: (editor: Editor) => void;
|
||||||
|
turnInto?: (editor: Editor, pos: number) => void;
|
||||||
isActive?: (editor: Editor) => boolean;
|
isActive?: (editor: Editor) => boolean;
|
||||||
clickable?: (editor: Editor) => boolean;
|
clickable?: (editor: Editor) => boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* For Placeholder */
|
/* For Placeholder */
|
||||||
.tiptap .is-empty::before {
|
|
||||||
|
.tiptap .is-empty:not(blockquote.is-empty)::before {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
float: left;
|
float: left;
|
||||||
height: 0;
|
height: 0;
|
||||||
color: var(--border-color-hover);
|
color: var(--color-muted-foreground);
|
||||||
content: attr(data-placeholder);
|
content: attr(data-placeholder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,12 +67,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tiptap blockquote::before {
|
.tiptap blockquote::before {
|
||||||
content: '';
|
content: ' ';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 0.25rem;
|
width: 0.4rem;
|
||||||
background-color: var(--blockquote-border);
|
background-color: var(--blockquote-border);
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
@@ -208,8 +209,8 @@ input[type='checkbox'] {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
opacity: 100;
|
opacity: 100;
|
||||||
transition-property: opacity;
|
transition-property: all;
|
||||||
transition-duration: 200ms;
|
transition-duration: 300ms;
|
||||||
transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
|
transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
|
||||||
color: var(--border-color-hover);
|
color: var(--border-color-hover);
|
||||||
}
|
}
|
||||||
@@ -452,3 +453,41 @@ input[type='checkbox'] {
|
|||||||
.tiptap iframe {
|
.tiptap iframe {
|
||||||
aspect-ratio: 16 / 9;
|
aspect-ratio: 16 / 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Mathematics extension styles */
|
||||||
|
.tiptap .tiptap-mathematics-render {
|
||||||
|
padding: 0 0.25rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Editable math block */
|
||||||
|
.tiptap .tiptap-mathematics-render--editable {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiptap .tiptap-mathematics-render--editable:hover {
|
||||||
|
background: var(--code-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline math */
|
||||||
|
.tiptap .tiptap-mathematics-render[data-type='inline-math'] {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block math */
|
||||||
|
.tiptap .tiptap-mathematics-render[data-type='block-math'] {
|
||||||
|
display: block;
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Error styles */
|
||||||
|
.tiptap .tiptap-mathematics-render.inline-math-error,
|
||||||
|
.tiptap .tiptap-mathematics-render.block-math-error {
|
||||||
|
background: var(--code-bg);
|
||||||
|
color: var(--color-destructive);
|
||||||
|
border: 1px solid darkred;
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ import SearchAndReplace from './extensions/FindAndReplace';
|
|||||||
import { TaskItem, TaskList } from '@tiptap/extension-list';
|
import { TaskItem, TaskList } from '@tiptap/extension-list';
|
||||||
import { Table, TableCell, TableRow, TableHeader } from './extensions/table';
|
import { Table, TableCell, TableRow, TableHeader } from './extensions/table';
|
||||||
import { Placeholder } from '@tiptap/extensions';
|
import { Placeholder } from '@tiptap/extensions';
|
||||||
import { Markdown } from 'tiptap-markdown';
|
import { Markdown } from '@tiptap/markdown';
|
||||||
import MathExtension from '@aarkue/tiptap-math-extension';
|
import MathMatics from '@tiptap/extension-mathematics';
|
||||||
|
|
||||||
import AutoJoiner from 'tiptap-extension-auto-joiner';
|
import AutoJoiner from 'tiptap-extension-auto-joiner';
|
||||||
import 'katex/dist/katex.min.css';
|
import 'katex/dist/katex.min.css';
|
||||||
|
import { InlineMathReplacer } from './extensions/InlineMathReplacer';
|
||||||
|
|
||||||
export default (
|
export default (
|
||||||
element?: HTMLElement,
|
element?: HTMLElement,
|
||||||
@@ -82,22 +84,41 @@ export default (
|
|||||||
nested: true
|
nested: true
|
||||||
}),
|
}),
|
||||||
SearchAndReplace,
|
SearchAndReplace,
|
||||||
MathExtension.configure({ evaluation: true }),
|
InlineMathReplacer,
|
||||||
|
MathMatics.configure({
|
||||||
|
blockOptions: {
|
||||||
|
onClick: (node, pos) => {
|
||||||
|
const newCalculation = prompt('Enter new calculation:', node.attrs.latex);
|
||||||
|
if (newCalculation) {
|
||||||
|
editor
|
||||||
|
.chain()
|
||||||
|
.setNodeSelection(pos)
|
||||||
|
.updateBlockMath({ latex: newCalculation })
|
||||||
|
.focus()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inlineOptions: {
|
||||||
|
onClick: (node, pos) => {
|
||||||
|
const newCalculation = prompt('Enter new calculation:', node.attrs.latex);
|
||||||
|
if (newCalculation) {
|
||||||
|
editor
|
||||||
|
.chain()
|
||||||
|
.setNodeSelection(pos)
|
||||||
|
.updateInlineMath({ latex: newCalculation })
|
||||||
|
.focus()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
AutoJoiner,
|
AutoJoiner,
|
||||||
Table,
|
Table,
|
||||||
TableHeader,
|
TableHeader,
|
||||||
TableRow,
|
TableRow,
|
||||||
TableCell,
|
TableCell,
|
||||||
Markdown.configure({
|
Markdown,
|
||||||
html: true,
|
|
||||||
tightLists: true,
|
|
||||||
tightListClass: 'tight',
|
|
||||||
bulletListMarker: '-',
|
|
||||||
linkify: true,
|
|
||||||
breaks: true,
|
|
||||||
transformPastedText: true,
|
|
||||||
transformCopiedText: false
|
|
||||||
}),
|
|
||||||
|
|
||||||
...(extensions ?? [])
|
...(extensions ?? [])
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { textInputRule } from '@tiptap/core';
|
||||||
|
import { InlineMath } from '@tiptap/extension-mathematics';
|
||||||
|
|
||||||
|
export const InlineMathReplacer = InlineMath.extend({
|
||||||
|
name: 'inlineMathReplacer',
|
||||||
|
addInputRules() {
|
||||||
|
return [
|
||||||
|
textInputRule({
|
||||||
|
find: /\$\$([^$]+)\$\$/,
|
||||||
|
replace: ({ match, commands }) => {
|
||||||
|
const latex = match[1];
|
||||||
|
// Insert the inline math node with the LaTeX content
|
||||||
|
commands.insertInlineMath({
|
||||||
|
latex
|
||||||
|
});
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -42,6 +42,7 @@ export const GROUPS: Group[] = [
|
|||||||
actions: [
|
actions: [
|
||||||
...commands.media,
|
...commands.media,
|
||||||
...commands.table,
|
...commands.table,
|
||||||
|
...commands.math,
|
||||||
{
|
{
|
||||||
icon: Minus,
|
icon: Minus,
|
||||||
name: 'horizontalRule',
|
name: 'horizontalRule',
|
||||||
|
|||||||
@@ -10,7 +10,10 @@
|
|||||||
import Duplicate from '@lucide/svelte/icons/copy';
|
import Duplicate from '@lucide/svelte/icons/copy';
|
||||||
import Clipboard from '@lucide/svelte/icons/clipboard';
|
import Clipboard from '@lucide/svelte/icons/clipboard';
|
||||||
import Delete from '@lucide/svelte/icons/trash-2';
|
import Delete from '@lucide/svelte/icons/trash-2';
|
||||||
|
import Repeat2 from '@lucide/svelte/icons/repeat-2';
|
||||||
|
import Plus from '@lucide/svelte/icons/plus';
|
||||||
import { NodeSelection } from '@tiptap/pm/state';
|
import { NodeSelection } from '@tiptap/pm/state';
|
||||||
|
import commands from '../commands/toolbar-commands';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
editor: Editor;
|
editor: Editor;
|
||||||
@@ -21,6 +24,9 @@
|
|||||||
let currentNode: Node | null = $state(null);
|
let currentNode: Node | null = $state(null);
|
||||||
let currentNodePos: number = $state(-1);
|
let currentNodePos: number = $state(-1);
|
||||||
let open = $state(false);
|
let open = $state(false);
|
||||||
|
const turnIntoCommand = Object.values(commands)
|
||||||
|
.flat()
|
||||||
|
.filter((c) => c.turnInto !== undefined);
|
||||||
|
|
||||||
const pluginKey = 'globalDragHandle';
|
const pluginKey = 'globalDragHandle';
|
||||||
|
|
||||||
@@ -71,10 +77,39 @@
|
|||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
editor.chain().setMeta('hideDragHandle', true).setNodeSelection(currentNodePos).deleteSelection().run();
|
editor.chain().setMeta('hideDragHandle', true).setNodeSelection(currentNodePos).deleteSelection().run();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAddNodeNext = () => {
|
||||||
|
if (currentNodePos === -1) return;
|
||||||
|
const currentNodeSize = currentNode?.nodeSize || 0;
|
||||||
|
const insertPos = currentNodePos + currentNodeSize;
|
||||||
|
const currentNodeIsEmptyParagraph =
|
||||||
|
currentNode?.type.name === 'paragraph' && currentNode?.content?.size === 0;
|
||||||
|
const focusPos = currentNodeIsEmptyParagraph ? currentNodePos + 2 : insertPos + 2;
|
||||||
|
editor
|
||||||
|
.chain()
|
||||||
|
.command(({ dispatch, tr, state }) => {
|
||||||
|
if (dispatch) {
|
||||||
|
if (currentNodeIsEmptyParagraph) {
|
||||||
|
tr.insertText('/', currentNodePos, currentNodePos + 1);
|
||||||
|
} else {
|
||||||
|
tr.insert(
|
||||||
|
insertPos,
|
||||||
|
state.schema.nodes.paragraph.create(null, [state.schema.text('/')])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dispatch(tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.focus(focusPos)
|
||||||
|
.run();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="drag-handle">
|
<div class="drag-handle">
|
||||||
<Button variant="ghost" class="ui:rounded-sm ui:p-0 !size-6" onclick={() => (open = true)}>
|
<Button variant="ghost" class="!ui:size-6 ui:rounded-sm ui:p-0" onclick={() => (open = true)}>
|
||||||
<GripVertical />
|
<GripVertical />
|
||||||
</Button>
|
</Button>
|
||||||
<DropdownMenu.Root bind:open>
|
<DropdownMenu.Root bind:open>
|
||||||
@@ -82,10 +117,43 @@
|
|||||||
<span>Drag Handle</span>
|
<span>Drag Handle</span>
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
<DropdownMenu.Content>
|
<DropdownMenu.Content>
|
||||||
|
<DropdownMenu.Group>
|
||||||
|
<DropdownMenu.GroupHeading class="ui:text-muted-foreground ui:capitalize">
|
||||||
|
{currentNode?.type.name}
|
||||||
|
</DropdownMenu.GroupHeading>
|
||||||
|
<DropdownMenu.Sub>
|
||||||
|
<DropdownMenu.SubTrigger openDelay={300}>
|
||||||
|
<Repeat2 />
|
||||||
|
Turn Into
|
||||||
|
</DropdownMenu.SubTrigger>
|
||||||
|
<DropdownMenu.SubContent class="ui:max-h-96 ui:overflow-auto ui:duration-300">
|
||||||
|
{#each turnIntoCommand as command (command)}
|
||||||
|
{@const Icon = command.icon}
|
||||||
|
<DropdownMenu.Item
|
||||||
|
onclick={() => {
|
||||||
|
if (currentNode && currentNodePos) command.turnInto?.(editor, currentNodePos);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon />
|
||||||
|
<span>{command.tooltip}</span>
|
||||||
|
<DropdownMenu.Shortcut class="ui:bg-background ui:rounded ui:border ui:p-0.5"
|
||||||
|
>{command.shortCut}</DropdownMenu.Shortcut
|
||||||
|
>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
{/each}
|
||||||
|
</DropdownMenu.SubContent>
|
||||||
|
</DropdownMenu.Sub>
|
||||||
|
</DropdownMenu.Group>
|
||||||
|
<DropdownMenu.Separator />
|
||||||
|
<DropdownMenu.Item onclick={handleAddNodeNext}>
|
||||||
|
<Plus />
|
||||||
|
Add Node
|
||||||
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Item onclick={handleRemoveFormatting}>
|
<DropdownMenu.Item onclick={handleRemoveFormatting}>
|
||||||
<RemoveFormatting />
|
<RemoveFormatting />
|
||||||
Remove Formatting
|
Remove Formatting
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Separator />
|
||||||
<DropdownMenu.Item onclick={handleDuplicate}>
|
<DropdownMenu.Item onclick={handleDuplicate}>
|
||||||
<Duplicate />
|
<Duplicate />
|
||||||
Duplicate Node
|
Duplicate Node
|
||||||
@@ -94,6 +162,7 @@
|
|||||||
<Clipboard />
|
<Clipboard />
|
||||||
Copy to clipboard
|
Copy to clipboard
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
|
<DropdownMenu.Separator />
|
||||||
<DropdownMenu.Item onclick={handleDelete}>
|
<DropdownMenu.Item onclick={handleDelete}>
|
||||||
<Delete class="ui:text-destructive" />
|
<Delete class="ui:text-destructive" />
|
||||||
Delete Node
|
Delete Node
|
||||||
|
|||||||
@@ -106,7 +106,11 @@
|
|||||||
{:else}
|
{:else}
|
||||||
{@const commandGroup = commands[cmd]}
|
{@const commandGroup = commands[cmd]}
|
||||||
{#each commandGroup as command (command)}
|
{#each commandGroup as command (command)}
|
||||||
<ToolBarIcon {editor} {command} />
|
{#if command.name === 'paragraph'}
|
||||||
|
<span></span>
|
||||||
|
{:else}
|
||||||
|
<ToolBarIcon {editor} {command} />
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
Reference in New Issue
Block a user