feat: add notebook widget (#294)

* feat: add nestjs replacement, remove nestjs

* feat: add notebook widget

* fix: format issue

* fix: add missing tiptap packages

* refactor: improve structure of table options

* fix: downgrade to tiptap 2.2.5 as not yet supported by mantine/tiptap

* fix: format issue

* fix: deepsource issues

* fix: typecheck issues

* refactor: move default notebook content to seperate file

* fix: format issue
This commit is contained in:
Meier Lukas
2024-04-13 12:32:55 +02:00
committed by GitHub
parent 8f4897186c
commit 6b1879cbb1
15 changed files with 1587 additions and 148 deletions

View File

@@ -37,9 +37,6 @@
"@tanstack/react-query": "^5.29.0",
"@tanstack/react-query-devtools": "^5.29.0",
"@tanstack/react-query-next-experimental": "5.29.0",
"@tiptap/extension-link": "^2.2.5",
"@tiptap/react": "^2.2.5",
"@tiptap/starter-kit": "^2.2.5",
"@trpc/client": "11.0.0-rc.332",
"@trpc/next": "next",
"@trpc/react-query": "next",

View File

@@ -112,6 +112,8 @@ export const WidgetPreviewPageContent = ({
width={dimensions.width}
height={dimensions.height}
isEditMode={editMode}
boardId={undefined}
itemId={undefined}
/>
</Card>
<Affix bottom={12} right={72}>

View File

@@ -88,6 +88,7 @@ interface ItemProps {
}
const BoardItem = ({ item, ...dimensions }: ItemProps) => {
const board = useRequiredBoard();
const editMode = useAtomValue(editModeAtom);
const serverData = useServerDataFor(item.id);
const Comp = loadWidgetDynamic(item.kind);
@@ -104,6 +105,8 @@ const BoardItem = ({ item, ...dimensions }: ItemProps) => {
integrations={item.integrations}
serverData={serverData?.data as never}
isEditMode={editMode}
boardId={board.id}
itemId={item.id}
{...dimensions}
/>
</>

View File

@@ -1,6 +1,8 @@
import { createTRPCRouter } from "../../trpc";
import { notebookRouter } from "./notebook";
import { weatherRouter } from "./weather";
export const widgetRouter = createTRPCRouter({
notebook: notebookRouter,
weather: weatherRouter,
});

View File

@@ -0,0 +1,45 @@
import { TRPCError } from "@trpc/server";
import SuperJSON from "superjson";
import { eq } from "@homarr/db";
import { items } from "@homarr/db/schema/sqlite";
import { z } from "@homarr/validation";
import { createTRPCRouter, publicProcedure } from "../../trpc";
export const notebookRouter = createTRPCRouter({
updateContent: publicProcedure
.input(
z.object({
itemId: z.string(),
content: z.string(),
boardId: z.string(),
}),
)
.mutation(async ({ ctx, input }) => {
const item = await ctx.db.query.items.findFirst({
where: eq(items.id, input.itemId),
with: {
section: {
columns: {
boardId: true,
},
},
},
});
if (!item || item.section.boardId !== input.boardId) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Specified item was not found",
});
}
const options = SuperJSON.parse<{ content: string }>(item.options);
options.content = input.content;
await ctx.db
.update(items)
.set({ options: SuperJSON.stringify(options) })
.where(eq(items.id, input.itemId));
}),
});

View File

@@ -4,5 +4,6 @@ export const widgetKinds = [
"app",
"iframe",
"video",
"notebook",
] as const;
export type WidgetKind = (typeof widgetKinds)[number];

View File

@@ -192,9 +192,11 @@ export default {
common: {
action: {
add: "Add",
apply: "Apply",
backToOverview: "Back to overview",
create: "Create",
edit: "Edit",
insert: "Insert",
remove: "Remove",
save: "Save",
saveChanges: "Save changes",
@@ -372,6 +374,67 @@ export default {
},
},
},
notebook: {
name: "Notebook",
description: "A simple notebook widget that supports markdown",
option: {
showToolbar: {
label: "Show the toolbar to help you write markdown",
},
allowReadOnlyCheck: {
label: "Allow check in read only mode",
},
content: {
label: "The content of the notebook",
},
},
controls: {
bold: "Bold",
italic: "Italic",
strikethrough: "Strikethrough",
underline: "Underline",
colorText: "Color text",
colorHighlight: "Colored highlight text",
code: "Code",
clear: "Clear formatting",
heading: "Heading {level}",
align: "Align text: {position}",
blockquote: "Blockquote",
horizontalLine: "Horizontal line",
bulletList: "Bullet list",
orderedList: "Ordered list",
checkList: "Check list",
increaseIndent: "Increase Indent",
decreaseIndent: "Decrease Indent",
link: "Link",
unlink: "Remove link",
image: "Embed Image",
addTable: "Add table",
deleteTable: "Delete Table",
colorCell: "Color Cell",
mergeCell: "Toggle cell merging",
addColumnLeft: "Add column before",
addColumnRight: "Add column after",
deleteColumn: "Delete column",
addRowTop: "Add row before",
addRowBelow: "Add row after",
deleteRow: "Delete row",
},
align: {
left: "Left",
center: "Center",
right: "Right",
},
popover: {
clearColor: "Clear color",
source: "Source",
widthPlaceholder: "Value in % or pixels",
columns: "Columns",
rows: "Rows",
width: "Width",
height: "Height",
},
},
iframe: {
name: "iFrame",
description:

View File

@@ -45,6 +45,21 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@tiptap/extension-link": "^2.2.5",
"@tiptap/react": "^2.2.5",
"@tiptap/starter-kit": "^2.2.5",
"@tiptap/extension-color": "2.2.5",
"@tiptap/extension-highlight": "2.2.5",
"@tiptap/extension-image": "2.2.5",
"@tiptap/extension-table": "2.2.5",
"@tiptap/extension-table-cell": "2.2.5",
"@tiptap/extension-table-header": "2.2.5",
"@tiptap/extension-table-row": "2.2.5",
"@tiptap/extension-task-item": "2.2.5",
"@tiptap/extension-task-list": "2.2.5",
"@tiptap/extension-text-align": "2.2.5",
"@tiptap/extension-text-style": "2.2.5",
"@tiptap/extension-underline": "2.2.5",
"video.js": "^8.10.0"
}
}

View File

@@ -105,6 +105,8 @@ export type WidgetComponentProps<TKind extends WidgetKind> =
WidgetProps<TKind> & {
serverData?: inferServerDataForKind<TKind>;
} & {
itemId: string | undefined; // undefined when in preview mode
boardId: string | undefined; // undefined when in preview mode
isEditMode: boolean;
width: number;
height: number;

View File

@@ -10,6 +10,7 @@ import * as clock from "./clock";
import type { WidgetComponentProps } from "./definition";
import * as iframe from "./iframe";
import type { WidgetImportRecord } from "./import";
import * as notebook from "./notebook";
import * as video from "./video";
import * as weather from "./weather";
@@ -23,6 +24,7 @@ export const widgetImports = {
clock,
weather,
app,
notebook,
iframe,
video,
} satisfies WidgetImportRecord;

View File

@@ -0,0 +1,18 @@
import dynamic from "next/dynamic";
import "@mantine/tiptap/styles.css";
import type { WidgetComponentProps } from "../definition";
const Notebook = dynamic(
() => import("./notebook").then((module) => module.Notebook),
{
ssr: false,
},
);
export default function NotebookWidget(
props: WidgetComponentProps<"notebook">,
) {
return <Notebook {...props} />;
}

View File

@@ -0,0 +1,132 @@
export const defaultContent = `
<p style="text-align: center">
<img src="/imgs/logo/logo.png" width="25%">
</p>
<h2>Welcome to <strong><span style="color: rgb(250, 82, 82)">Homarr</span>'s</strong> notebook widget</h2>
<p>
The <code>notebook</code> widget focuses on usability and is designed to be as simple as possible to bring a
familiar editing experience to regular users, be it markdown or office type editors.
It is based on <a target="_blank" rel="noopener noreferrer nofollow" href="https://tiptap.dev/">Tiptap.dev</a>
and supports most of its features:
</p>
<table>
<tbody>
<tr>
<td colspan="3" rowspan="1" style="background-color: rgba(95, 95, 95, 0.5)">
<h4 style="text-align: center">General text formatting</h4>
</td>
</tr>
<tr>
<td colspan="1" rowspan="1">
<p><strong>Bold</strong></p>
</td>
<td colspan="1" rowspan="1">
<p style="text-align: center"><em>Italic</em></p>
</td>
<td colspan="1" rowspan="1">
<p style="text-align: right"><u>Underline</u></p>
</td>
</tr>
<tr>
<td colspan="1" rowspan="1">
<p><s>Strike-through</s></p>
</td>
<td colspan="1" rowspan="1">
<p style="text-align: center">Text alignment</p>
</td>
<td colspan="1" rowspan="1">
<p style="text-align: right">Headings</p>
</td>
</tr>
</tbody>
</table>
<table>
<tbody>
<tr>
<td colspan="3" rowspan="1" style="background-color: rgba(95, 95, 95, 0.5)">
<h4 style="text-align: center">Lists</h4>
</td>
</tr>
<tr>
<td colspan="1" rowspan="1">
<ol>
<li>
<p>Ordered</p>
</li>
</ol>
</td>
<td colspan="1" rowspan="1">
<ul>
<li>
<p>Bullet</p>
</li>
</ul>
</td>
<td colspan="1" rowspan="1">
<ul data-type="taskList">
<li data-checked="true" data-type="taskItem">
<label><input type="checkbox" checked="checked"><span></span></label>
<div><p>Check</p></div>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
<table>
<tbody>
<tr>
<td colspan="3" rowspan="1" style="background-color: rgba(95, 95, 95, 0.5)">
<h4 style="text-align: center">Coloring</h4>
</td>
</tr>
<tr>
<td colspan="1" rowspan="1">
<p><span style="color: rgb(250, 82, 82)">Text coloring</span></p>
</td>
<td colspan="1" rowspan="1">
<p style="text-align: center"><mark data-color="#FA5252" style="background-color: #FA5252; color: inherit">highlighting</mark></p>
</td>
<td colspan="1" rowspan="1" style="background-color: rgb(250, 82, 82)">
<p style="text-align: right">Table cells</p>
</td>
</tr>
</tbody>
</table>
<table>
<tbody>
<tr>
<td colspan="3" rowspan="1" style="background-color: rgba(95, 95, 95, 0.5)">
<h4 style="text-align: center">Inserts</h4>
</td>
</tr>
<tr>
<td colspan="1" rowspan="1">
<p>Links</p>
</td>
<td colspan="1" rowspan="1">
<p style="text-align: center">Images</p>
</td>
<td colspan="1" rowspan="1">
<p style="text-align: right">Tables</p>
</td>
</tr>
</tbody>
</table>
<hr>
<blockquote>
<h4>Widget options</h4>
<ul>
<li>
<p>Show the toolbar to help you write markdown:</p>
<p>The toolbar at the top that helps with controls, some not available in markdown.</p>
</li>
<li>
<p>Allow check in read only mode:</p>
<p>Check boxes usable outside of editing, also allows anonymous checks.</p>
</li>
</ul>
</blockquote>`
.split("\n")
.join("")
.replaceAll(" ", "");

View File

@@ -0,0 +1,30 @@
import { IconNotes } from "@homarr/ui";
import { createWidgetDefinition } from "../definition";
import { optionsBuilder } from "../options";
import { defaultContent } from "./default-content";
export const { definition, componentLoader } = createWidgetDefinition(
"notebook",
{
icon: IconNotes,
options: optionsBuilder.from(
(factory) => ({
showToolbar: factory.switch({
defaultValue: true,
}),
allowReadOnlyCheck: factory.switch({
defaultValue: true,
}),
content: factory.text({
defaultValue: defaultContent,
}),
}),
{
content: {
shouldHide: () => true, // Hide the content option as it can be modified in the editor
},
},
),
},
).withDynamicImport(() => import("./component"));

View File

@@ -0,0 +1,989 @@
"use client";
import { useCallback, useEffect, useState } from "react";
import {
ActionIcon,
Button,
ColorPicker,
ColorSwatch,
Group,
NumberInput,
Popover,
ScrollArea,
Stack,
TextInput,
useMantineColorScheme,
useMantineTheme,
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import {
Link,
RichTextEditor,
useRichTextEditorContext,
} from "@mantine/tiptap";
import {
IconCheck,
IconCircleOff,
IconColumnInsertLeft,
IconColumnInsertRight,
IconColumnRemove,
IconDeviceFloppy,
IconEdit,
IconHighlight,
IconIndentDecrease,
IconIndentIncrease,
IconLayoutGrid,
IconLetterA,
IconListCheck,
IconPhoto,
IconRowInsertBottom,
IconRowInsertTop,
IconRowRemove,
IconTableOff,
IconTablePlus,
IconX,
} from "@tabler/icons-react";
import { Color } from "@tiptap/extension-color";
import Highlight from "@tiptap/extension-highlight";
import Image from "@tiptap/extension-image";
import Table from "@tiptap/extension-table";
import TableCell from "@tiptap/extension-table-cell";
import TableHeader from "@tiptap/extension-table-header";
import TableRow from "@tiptap/extension-table-row";
import TaskItem from "@tiptap/extension-task-item";
import TaskList from "@tiptap/extension-task-list";
import TextAlign from "@tiptap/extension-text-align";
import TextStyle from "@tiptap/extension-text-style";
import Underline from "@tiptap/extension-underline";
import type { Editor } from "@tiptap/react";
import { BubbleMenu, useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import type { Node } from "prosemirror-model";
import { clientApi } from "@homarr/api/client";
import { useForm } from "@homarr/form";
import type { TranslationObject } from "@homarr/translation";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import type { TablerIcon } from "@homarr/ui";
import type { WidgetComponentProps } from "../definition";
const iconProps = {
size: "1.25rem",
stroke: 1.5,
};
const controlIconProps = {
size: "1rem",
stroke: 1.5,
};
export function Notebook({
options,
isEditMode,
boardId,
itemId,
}: WidgetComponentProps<"notebook">) {
const [content, setContent] = useState(options.content);
const [toSaveContent, setToSaveContent] = useState(content);
// TODO: Add check for user permissions
const enabled = !isEditMode;
const [isEditing, setIsEditing] = useState(false);
const { primaryColor } = useMantineTheme();
const { colorScheme } = useMantineColorScheme();
const { mutateAsync } = clientApi.widget.notebook.updateContent.useMutation();
const tControls = useScopedI18n("widget.notebook.controls");
const t = useI18n();
const editor = useEditor(
{
extensions: [
Color,
Highlight.configure({ multicolor: true }),
Image.extend({
addAttributes() {
return {
...this.parent?.(),
width: { default: null },
};
},
}).configure({ inline: true }),
Link.configure({
openOnClick: true,
validate(url) {
return /^https?:\/\//.test(url);
},
}).extend({
addAttributes() {
return {
...this.parent?.(),
target: { default: null },
};
},
}),
StarterKit,
Table.configure({
resizable: true,
lastColumnResizable: false,
}),
TableCell.extend({
addAttributes() {
return {
...this.parent?.(),
backgroundColor: {
default: undefined,
renderHTML: (attributes) => ({
style: attributes.backgroundColor
? `background-color: ${attributes.backgroundColor}`
: undefined,
}),
parseHTML: (element) =>
element.style.backgroundColor || undefined,
},
};
},
}),
TableHeader,
TableRow,
TaskItem.configure({
nested: true,
onReadOnlyChecked: (node, checked) => {
if (options.allowReadOnlyCheck && enabled) {
const event = new CustomEvent("onReadOnlyCheck", {
detail: { node, checked },
});
dispatchEvent(event);
return true;
}
return false;
},
}),
TaskList.configure({ itemTypeName: "taskItem" }),
TextAlign.configure({ types: ["heading", "paragraph"] }),
TextStyle,
Underline,
],
content,
onUpdate: ({ editor }) => {
setContent(editor.getHTML());
},
onCreate: ({ editor }) => {
editor.setEditable(false);
},
},
[toSaveContent],
);
const handleOnReadOnlyCheck = (
event: CustomEventInit<{ node: Node; checked: boolean }>,
) => {
if (!options.allowReadOnlyCheck) return;
if (!editor) return;
editor.state.doc.descendants((subnode, pos) => {
if (!event.detail) return;
if (!subnode.eq(event.detail.node)) return;
if (subnode.eq(event.detail.node)) {
const { tr } = editor.state;
tr.setNodeMarkup(pos, undefined, {
...event.detail.node.attrs,
checked: event.detail.checked,
});
editor.view.dispatch(tr);
setContent(editor.getHTML());
handleContentUpdate(editor.getHTML());
}
});
};
addEventListener("onReadOnlyCheck", handleOnReadOnlyCheck);
const handleEditToggleCallback = (previous: boolean) => {
const current = !previous;
if (!editor) return current;
editor.setEditable(current);
handleContentUpdate(content);
return current;
};
const handleEditCancelCallback = () => {
if (!editor) return false;
editor.setEditable(false);
setContent(toSaveContent);
editor.commands.setContent(toSaveContent);
return false;
};
const handleEditCancel = useCallback(() => {
setIsEditing(handleEditCancelCallback);
}, [setIsEditing, handleEditCancelCallback]);
const handleContentUpdate = (contentUpdate: string) => {
setToSaveContent(contentUpdate);
// This is not available in preview mode
if (boardId && itemId) {
void mutateAsync({ boardId, itemId, content: contentUpdate });
}
};
const handleEditToggle = useCallback(() => {
setIsEditing(handleEditToggleCallback);
}, [setIsEditing, handleEditToggleCallback]);
return (
<>
<RichTextEditor
p={0}
mt={0}
h="100%"
editor={editor}
styles={(theme) => ({
root: {
"& .ProseMirror": {
padding: "0 !important",
},
backgroundColor:
colorScheme === "dark" ? theme.colors.dark[6] : "white",
border: "none",
borderRadius: "0.5rem",
display: "flex",
flexDirection: "column",
},
toolbar: {
backgroundColor: "transparent",
padding: "0.5rem",
},
content: {
backgroundColor: "transparent",
padding: "0.5rem",
},
})}
>
<RichTextEditor.Toolbar
style={{
display:
isEditing && options.showToolbar === true ? "flex" : "none",
}}
>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Bold title={tControls("bold")} />
<RichTextEditor.Italic title={tControls("italic")} />
<RichTextEditor.Strikethrough title={tControls("strikethrough")} />
<RichTextEditor.Underline title={tControls("underline")} />
<TextColorControl />
<TextHighlightControl />
<RichTextEditor.Code title={tControls("code")} />
<RichTextEditor.ClearFormatting title={tControls("clear")} />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.H1 title={tControls("heading", { level: 1 })} />
<RichTextEditor.H2 title={tControls("heading", { level: 2 })} />
<RichTextEditor.H3 title={tControls("heading", { level: 3 })} />
<RichTextEditor.H4 title={tControls("heading", { level: 4 })} />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.AlignLeft
title={tControls("align", {
position: t("widget.notebook.align.left"),
})}
/>
<RichTextEditor.AlignCenter
title={tControls("align", {
position: t("widget.notebook.align.center"),
})}
/>
<RichTextEditor.AlignRight
title={tControls("align", {
position: t("widget.notebook.align.right"),
})}
/>
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Blockquote title={tControls("blockquote")} />
<RichTextEditor.Hr title={tControls("horizontalLine")} />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.BulletList title={tControls("bulletList")} />
<RichTextEditor.OrderedList title={tControls("orderedList")} />
<TaskListToggle />
{(editor?.isActive("taskList") ||
editor?.isActive("bulletList") ||
editor?.isActive("orderedList")) && (
<>
<ListIndentIncrease />
<ListIndentDecrease />
</>
)}
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Link title={tControls("link")} />
<RichTextEditor.Unlink title={tControls("unlink")} />
<EmbedImage />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<TableToggle />
{editor?.isActive("table") && (
<>
<ColorCellControl />
<TableToggleMerge />
<TableAddColumnBefore />
<TableAddColumnAfter />
<TableRemoveColumn />
<TableAddRowBefore />
<TableAddRowAfter />
<TableRemoveRow />
</>
)}
</RichTextEditor.ControlsGroup>
</RichTextEditor.Toolbar>
{editor && (
<BubbleMenu editor={editor}>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Bold title={tControls("bold")} />
<RichTextEditor.Italic title={tControls("italic")} />
<RichTextEditor.Link title={tControls("link")} />
</RichTextEditor.ControlsGroup>
</BubbleMenu>
)}
<ScrollArea mih="4rem" offsetScrollbars pl={12} pt={12}>
<RichTextEditor.Content />
</ScrollArea>
</RichTextEditor>
{enabled && (
<>
<ActionIcon
title={
isEditing ? t("common.action.save") : t("common.action.edit")
}
style={{
zIndex: 1,
}}
top={7}
right={7}
pos="absolute"
color={primaryColor}
variant="light"
size={30}
radius={"md"}
onClick={handleEditToggle}
>
{isEditing ? (
<IconDeviceFloppy {...iconProps} />
) : (
<IconEdit {...iconProps} />
)}
</ActionIcon>
{isEditing && (
<ActionIcon
title={t("common.action.cancel")}
style={{
zIndex: 1,
}}
top={44}
right={7}
pos="absolute"
color={primaryColor}
variant="light"
size={30}
radius={"md"}
onClick={handleEditCancel}
>
<IconX {...iconProps} />
</ActionIcon>
)}
</>
)}
</>
);
}
function TextHighlightControl() {
const tControls = useScopedI18n("widget.notebook.controls");
const { editor } = useRichTextEditorContext();
const defaultColor = "transparent";
const getCurrent = useCallback(() => {
return editor?.getAttributes("highlight").color as string | undefined;
}, [editor]);
const update = useCallback(
(value: string) => {
if (value === defaultColor) {
editor?.chain().focus().unsetHighlight().run();
return;
}
editor?.chain().focus().setHighlight({ color: value }).run();
},
[editor, defaultColor],
);
return (
<ColorControl
defaultColor={defaultColor}
getCurrent={getCurrent}
update={update}
icon={IconHighlight}
ariaLabel={tControls("colorHighlight")}
/>
);
}
function TextColorControl() {
const tControls = useScopedI18n("widget.notebook.controls");
const { editor } = useRichTextEditorContext();
const { black, colors } = useMantineTheme();
const { colorScheme } = useMantineColorScheme();
const defaultColor = colorScheme === "dark" ? colors.dark[0] : black;
const getCurrent = useCallback(() => {
return editor?.getAttributes("textStyle").color as string | undefined;
}, [editor]);
const update = useCallback(
(value: string) => {
if (value === defaultColor) {
editor?.chain().focus().unsetColor().run();
return;
}
editor?.chain().focus().setColor(value).run();
},
[editor, defaultColor],
);
return (
<ColorControl
defaultColor={defaultColor}
getCurrent={getCurrent}
update={update}
icon={IconLetterA}
ariaLabel={tControls("colorText")}
/>
);
}
function ColorCellControl() {
const tControls = useScopedI18n("widget.notebook.controls");
const { editor } = useRichTextEditorContext();
const getCurrent = useCallback(() => {
return editor?.getAttributes("tableCell").backgroundColor as
| string
| undefined;
}, [editor]);
const update = useCallback(
(value: string) => {
editor?.chain().focus().setCellAttribute("backgroundColor", value).run();
},
[editor],
);
return (
<ColorControl
defaultColor="transparent"
getCurrent={getCurrent}
update={update}
icon={IconLayoutGrid}
ariaLabel={tControls("colorCell")}
/>
);
}
interface ColorControlProps {
defaultColor: string;
getCurrent: () => string | undefined;
update: (value: string) => void;
icon: TablerIcon;
ariaLabel: string;
}
const ColorControl = ({
defaultColor,
getCurrent,
update,
icon: Icon,
ariaLabel,
}: ColorControlProps) => {
const { editor } = useRichTextEditorContext();
const [color, setColor] = useState(defaultColor);
const { colors, white } = useMantineTheme();
const { colorScheme } = useMantineColorScheme();
const [opened, { close, toggle }] = useDisclosure(false);
const t = useI18n();
const palette = [
"#000000",
colors.dark[9],
colors.dark[6],
colors.dark[3],
colors.dark[0],
"#FFFFFF",
colors.red[9],
colors.pink[7],
colors.grape[8],
colors.violet[9],
colors.indigo[9],
colors.blue[5],
colors.green[6],
"#09D630",
colors.lime[5],
colors.yellow[5],
"#EB8415",
colors.orange[9],
];
const onSelection = useCallback(() => {
setColor(getCurrent() ?? defaultColor);
}, [getCurrent, defaultColor, setColor]);
useEffect(() => {
editor?.on("selectionUpdate", onSelection);
return () => {
editor?.off("selectionUpdate", onSelection);
};
});
const handleApplyColor = useCallback(() => {
update(color);
close();
}, [color, update, close]);
const handleClearColor = useCallback(() => {
update(defaultColor);
setColor(defaultColor);
close();
}, [update, setColor, close, defaultColor]);
return (
<Popover
opened={opened}
onChange={toggle}
styles={{
dropdown: {
backgroundColor: colorScheme === "dark" ? colors.dark[7] : white,
},
}}
>
<Popover.Target>
<RichTextEditor.Control onClick={toggle} title={ariaLabel}>
<Group gap={3} px="0.2rem">
<Icon {...controlIconProps} />
<ColorSwatch size={14} color={color} />
</Group>
</RichTextEditor.Control>
</Popover.Target>
<Popover.Dropdown>
<Stack gap={8}>
<ColorPicker
value={color}
onChange={setColor}
format="hexa"
swatches={palette}
swatchesPerRow={6}
/>
<Group justify="right" gap={8}>
<ActionIcon
title={t("common.action.cancel")}
variant="default"
onClick={close}
>
<IconX stroke={1.5} size="1rem" />
</ActionIcon>
<ActionIcon
title={t("common.action.apply")}
variant="default"
onClick={handleApplyColor}
>
<IconCheck stroke={1.5} size="1rem" />
</ActionIcon>
<ActionIcon
title={t("widget.notebook.popover.clearColor")}
variant="default"
onClick={handleClearColor}
>
<IconCircleOff stroke={1.5} size="1rem" />
</ActionIcon>
</Group>
</Stack>
</Popover.Dropdown>
</Popover>
);
};
function EmbedImage() {
const tControls = useScopedI18n("widget.notebook.controls");
const t = useI18n();
const { editor } = useRichTextEditorContext();
const { colors, white } = useMantineTheme();
const { colorScheme } = useMantineColorScheme();
const [opened, { open, close, toggle }] = useDisclosure(false);
const form = useForm({
initialValues: {
src: (editor?.getAttributes("image").src as string | undefined) ?? "",
width: (editor?.getAttributes("image").width as string | undefined) ?? "",
},
});
const handleOpen = useCallback(() => {
form.reset();
open();
}, [form, open]);
const handleSubmit = useCallback(
(values: { src: string; width: string }) => {
editor?.commands.insertContent({
type: "paragraph",
content: [
{
type: "image",
attrs: values,
},
],
});
close();
},
[editor, close],
);
return (
<Popover
opened={opened}
onClose={close}
onOpen={handleOpen}
position="left"
styles={{
dropdown: {
backgroundColor: colorScheme === "dark" ? colors.dark[7] : white,
},
}}
trapFocus
>
<Popover.Target>
<RichTextEditor.Control
onClick={toggle}
title={tControls("image")}
active={editor?.isActive("image")}
>
<IconPhoto stroke={1.5} size="1rem" />
</RichTextEditor.Control>
</Popover.Target>
<Popover.Dropdown>
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack gap={5}>
<TextInput
label={t("widget.notebook.popover.source")}
placeholder="https://example.com/"
{...form.getInputProps("src")}
/>
<TextInput
label={t("widget.notebook.popover.width")}
placeholder={t("widget.notebook.popover.widthPlaceholder")}
{...form.getInputProps("width")}
/>
<Button type="submit" variant="default" mt={10} mb={5}>
{t("common.action.save")}
</Button>
</Stack>
</form>
</Popover.Dropdown>
</Popover>
);
}
function TaskListToggle() {
const { editor } = useRichTextEditorContext();
const tControls = useScopedI18n("widget.notebook.controls");
const handleToggleTaskList = useCallback(() => {
editor?.chain().focus().toggleTaskList().run();
}, [editor]);
return (
<RichTextEditor.Control
title={tControls("checkList")}
onClick={handleToggleTaskList}
active={editor?.isActive("taskList")}
>
<IconListCheck stroke={1.5} size="1rem" />
</RichTextEditor.Control>
);
}
function ListIndentIncrease() {
const { editor } = useRichTextEditorContext();
const [itemType, setItemType] = useState("listItem");
const tControls = useScopedI18n("widget.notebook.controls");
const handleIncreaseIndent = useCallback(() => {
editor?.chain().focus().sinkListItem(itemType).run();
}, [editor, itemType]);
editor?.on("selectionUpdate", ({ editor }) => {
setItemType(editor?.isActive("taskItem") ? "taskItem" : "listItem");
});
return (
<RichTextEditor.Control
title={tControls("increaseIndent")}
onClick={handleIncreaseIndent}
interactive={editor?.can().sinkListItem(itemType)}
>
<IconIndentIncrease stroke={1.5} size="1rem" />
</RichTextEditor.Control>
);
}
function ListIndentDecrease() {
const { editor } = useRichTextEditorContext();
const [itemType, setItemType] = useState("listItem");
const tControls = useScopedI18n("widget.notebook.controls");
const handleDecreaseIndent = useCallback(() => {
editor?.chain().focus().liftListItem(itemType).run();
}, [editor, itemType]);
editor?.on("selectionUpdate", ({ editor }) => {
setItemType(editor?.isActive("taskItem") ? "taskItem" : "listItem");
});
return (
<RichTextEditor.Control
title={tControls("decreaseIndent")}
onClick={handleDecreaseIndent}
interactive={editor?.can().liftListItem(itemType)}
>
<IconIndentDecrease stroke={1.5} size="1rem" />
</RichTextEditor.Control>
);
}
const handleAddColumnBefore = (editor: Editor) => {
editor.commands.addColumnBefore();
};
const TableAddColumnBefore = () => (
<TableControl
title="addColumnLeft"
onClick={handleAddColumnBefore}
icon={IconColumnInsertLeft}
/>
);
const handleAddColumnAfter = (editor: Editor) => {
editor.commands.addColumnAfter();
};
const TableAddColumnAfter = () => (
<TableControl
title="addColumnRight"
onClick={handleAddColumnAfter}
icon={IconColumnInsertRight}
/>
);
const handleRemoveColumn = (editor: Editor) => {
editor.commands.deleteColumn();
};
const TableRemoveColumn = () => (
<TableControl
title="deleteColumn"
onClick={handleRemoveColumn}
icon={IconColumnRemove}
/>
);
const handleAddRowBefore = (editor: Editor) => {
editor.commands.addRowBefore();
};
const TableAddRowBefore = () => (
<TableControl
title="addRowTop"
onClick={handleAddRowBefore}
icon={IconRowInsertTop}
/>
);
const handleAddRowAfter = (editor: Editor) => {
editor.commands.addRowAfter();
};
const TableAddRowAfter = () => (
<TableControl
title="addRowBelow"
onClick={handleAddRowAfter}
icon={IconRowInsertBottom}
/>
);
const handleRemoveRow = (editor: Editor) => {
editor.commands.deleteRow();
};
const TableRemoveRow = () => (
<TableControl
title="deleteRow"
onClick={handleRemoveRow}
icon={IconRowRemove}
/>
);
interface TableControlProps {
title: Exclude<
keyof TranslationObject["widget"]["notebook"]["controls"],
"align" | "heading"
>;
onClick: (editor: Editor) => void;
icon: TablerIcon;
}
const TableControl = ({ title, onClick, icon: Icon }: TableControlProps) => {
const { editor } = useRichTextEditorContext();
const tControls = useScopedI18n("widget.notebook.controls");
const handleControlClick = useCallback(() => {
if (!editor) return;
onClick(editor);
}, [editor, onClick]);
return (
<RichTextEditor.Control
title={tControls(title)}
onClick={handleControlClick}
>
<Icon {...controlIconProps} />
</RichTextEditor.Control>
);
};
function TableToggleMerge() {
const { editor } = useRichTextEditorContext();
const tControls = useScopedI18n("widget.notebook.controls");
const handleToggleMerge = useCallback(() => {
editor?.commands.mergeOrSplit();
}, [editor]);
return (
<RichTextEditor.Control
title={tControls("mergeCell")}
onClick={handleToggleMerge}
active={editor?.getAttributes("tableCell").colspan > 1}
>
<svg
height="1.25rem"
width="1.25rem"
strokeWidth="0.1"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
{/* No existing icon from tabler, taken from https://icon-sets.iconify.design/fluent/table-cells-merge-24-regular/ */}
<path
fill="currentColor"
d="M15.58 11.25H8.42l.89-1.002a.75.75 0 0 0-1.12-.996l-2 2.25a.75.75 0 0 0 0 .996l2 2.25a.75.75 0 1 0 1.12-.996l-.89-1.002h7.16l-.89 1.002a.75.75 0 0 0 1.12.996l2-2.25l.011-.012a.746.746 0 0 0-.013-.987l-1.997-2.247a.75.75 0 0 0-1.121.996l.89 1.002ZM6.25 3A3.25 3.25 0 0 0 3 6.25v11.5A3.25 3.25 0 0 0 6.25 21h11.5A3.25 3.25 0 0 0 21 17.75V6.25A3.25 3.25 0 0 0 17.75 3H6.25ZM4.5 6.25c0-.966.784-1.75 1.75-1.75h11.5c.966 0 1.75.784 1.75 1.75v.25h-15v-.25ZM4.5 8h15v8h-15V8Zm15 9.5v.25a1.75 1.75 0 0 1-1.75 1.75H6.25a1.75 1.75 0 0 1-1.75-1.75v-.25h15Z"
/>
</svg>
</RichTextEditor.Control>
);
}
function TableToggle() {
const { editor } = useRichTextEditorContext();
const isActive = editor?.isActive("table");
const { colors, white } = useMantineTheme();
const { colorScheme } = useMantineColorScheme();
const [opened, { open, close, toggle }] = useDisclosure(false);
const t = useI18n();
const tControls = useScopedI18n("widget.notebook.controls");
const form = useForm({
initialValues: {
cols: 3,
rows: 3,
},
});
const handleOpen = useCallback(() => {
form.reset();
open();
}, [form, open]);
const handleSubmit = useCallback(
(values: { rows: number; cols: number }) => {
editor?.commands.insertTable({ ...values, withHeaderRow: false });
close();
},
[editor, close],
);
const handleControlClick = useCallback(() => {
if (isActive) {
editor?.commands.deleteTable();
} else {
toggle();
}
}, [isActive, editor, toggle]);
return (
<Popover
opened={opened}
onOpen={handleOpen}
onClose={close}
styles={{
dropdown: {
backgroundColor: colorScheme === "dark" ? colors.dark[7] : white,
},
}}
trapFocus
>
<Popover.Target>
<RichTextEditor.Control
title={tControls(isActive ? "deleteTable" : "addTable")}
active={isActive}
onClick={handleControlClick}
>
{isActive ? (
<IconTableOff stroke={1.5} size="1rem" />
) : (
<IconTablePlus stroke={1.5} size="1rem" />
)}
</RichTextEditor.Control>
</Popover.Target>
<Popover.Dropdown>
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack gap={5}>
<NumberInput
label={t("widget.notebook.popover.columns")}
min={1}
{...form.getInputProps("cols")}
/>
<NumberInput
label={t("widget.notebook.popover.rows")}
min={1}
{...form.getInputProps("rows")}
/>
<Button type="submit" variant="default" mt={10} mb={5}>
{t("common.action.insert")}
</Button>
</Stack>
</form>
</Popover.Dropdown>
</Popover>
);
}

428
pnpm-lock.yaml generated
View File

@@ -106,7 +106,7 @@ importers:
version: 7.7.1(@mantine/core@7.7.1)(@mantine/hooks@7.7.1)(react-dom@18.2.0)(react@18.2.0)
'@mantine/tiptap':
specifier: ^7.7.1
version: 7.7.1(@mantine/core@7.7.1)(@mantine/hooks@7.7.1)(@tiptap/extension-link@2.2.5)(@tiptap/react@2.2.5)(react-dom@18.2.0)(react@18.2.0)
version: 7.7.1(@mantine/core@7.7.1)(@mantine/hooks@7.7.1)(@tiptap/extension-link@2.2.6)(@tiptap/react@2.2.6)(react-dom@18.2.0)(react@18.2.0)
'@t3-oss/env-nextjs':
specifier: ^0.9.2
version: 0.9.2(typescript@5.4.5)(zod@3.22.4)
@@ -119,15 +119,6 @@ importers:
'@tanstack/react-query-next-experimental':
specifier: 5.29.0
version: 5.29.0(@tanstack/react-query@5.29.0)(next@14.1.4)(react@18.2.0)
'@tiptap/extension-link':
specifier: ^2.2.5
version: 2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4)
'@tiptap/react':
specifier: ^2.2.5
version: 2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4)(react-dom@18.2.0)(react@18.2.0)
'@tiptap/starter-kit':
specifier: ^2.2.5
version: 2.2.5(@tiptap/pm@2.2.4)
'@trpc/client':
specifier: 11.0.0-rc.332
version: 11.0.0-rc.332(@trpc/server@11.0.0-next-beta.289)
@@ -160,7 +151,7 @@ importers:
version: 16.4.5
jotai:
specifier: ^2.8.0
version: 2.8.0(@types/react@18.2.76)(react@18.2.0)
version: 2.8.0(@types/react@18.2.78)(react@18.2.0)
next:
specifier: ^14.1.4
version: 14.1.4(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)(sass@1.74.1)
@@ -200,7 +191,7 @@ importers:
version: 20.12.7
'@types/react':
specifier: ^18.2.76
version: 18.2.76
version: 18.2.78
'@types/react-dom':
specifier: ^18.2.25
version: 18.2.25
@@ -723,7 +714,7 @@ importers:
dependencies:
'@mantine/core':
specifier: ^7.7.1
version: 7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.76)(react-dom@18.2.0)(react@18.2.0)
version: 7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
'@mantine/dates':
specifier: ^7.7.1
version: 7.7.1(@mantine/core@7.7.1)(@mantine/hooks@7.7.1)(dayjs@1.11.10)(react-dom@18.2.0)(react@18.2.0)
@@ -813,6 +804,51 @@ importers:
'@homarr/validation':
specifier: workspace:^0.1.0
version: link:../validation
'@tiptap/extension-color':
specifier: 2.2.5
version: 2.2.5(@tiptap/core@2.2.6)(@tiptap/extension-text-style@2.2.5)
'@tiptap/extension-highlight':
specifier: 2.2.5
version: 2.2.5(@tiptap/core@2.2.6)
'@tiptap/extension-image':
specifier: 2.2.5
version: 2.2.5(@tiptap/core@2.2.6)
'@tiptap/extension-link':
specifier: ^2.2.5
version: 2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4)
'@tiptap/extension-table':
specifier: 2.2.5
version: 2.2.5(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4)
'@tiptap/extension-table-cell':
specifier: 2.2.5
version: 2.2.5(@tiptap/core@2.2.6)
'@tiptap/extension-table-header':
specifier: 2.2.5
version: 2.2.5(@tiptap/core@2.2.6)
'@tiptap/extension-table-row':
specifier: 2.2.5
version: 2.2.5(@tiptap/core@2.2.6)
'@tiptap/extension-task-item':
specifier: 2.2.5
version: 2.2.5(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4)
'@tiptap/extension-task-list':
specifier: 2.2.5
version: 2.2.5(@tiptap/core@2.2.6)
'@tiptap/extension-text-align':
specifier: 2.2.5
version: 2.2.5(@tiptap/core@2.2.6)
'@tiptap/extension-text-style':
specifier: 2.2.5
version: 2.2.5(@tiptap/core@2.2.6)
'@tiptap/extension-underline':
specifier: 2.2.5
version: 2.2.5(@tiptap/core@2.2.6)
'@tiptap/react':
specifier: ^2.2.5
version: 2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4)(react-dom@18.2.0)(react@18.2.0)
'@tiptap/starter-kit':
specifier: ^2.2.5
version: 2.2.6(@tiptap/pm@2.2.4)
video.js:
specifier: ^8.10.0
version: 8.10.0
@@ -1069,7 +1105,7 @@ packages:
resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.24.0
'@babel/types': 7.23.9
/@babel/helper-module-transforms@7.23.3(@babel/core@7.23.9):
resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
@@ -1107,7 +1143,7 @@ packages:
resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.24.0
'@babel/types': 7.23.9
/@babel/helper-split-export-declaration@7.22.6:
resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
@@ -2088,7 +2124,7 @@ packages:
chroma-js: 2.4.2
dev: false
/@mantine/core@7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.76)(react-dom@18.2.0)(react@18.2.0):
/@mantine/core@7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-SdPzjvqvEK7uHFuVD3a8w3OZyQVoCwIXLSUfOtRNouDMQgsq6Ac7QjKXBBOk3wNweOWFVOU1vATLHobSmow0lQ==}
peerDependencies:
'@mantine/hooks': 7.7.1
@@ -2101,8 +2137,8 @@ packages:
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-number-format: 5.3.1(react-dom@18.2.0)(react@18.2.0)
react-remove-scroll: 2.5.7(@types/react@18.2.76)(react@18.2.0)
react-textarea-autosize: 8.5.3(@types/react@18.2.76)(react@18.2.0)
react-remove-scroll: 2.5.7(@types/react@18.2.78)(react@18.2.0)
react-textarea-autosize: 8.5.3(@types/react@18.2.78)(react@18.2.0)
type-fest: 4.12.0
transitivePeerDependencies:
- '@types/react'
@@ -2117,7 +2153,7 @@ packages:
react: ^18.2.0
react-dom: ^18.2.0
dependencies:
'@mantine/core': 7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.76)(react-dom@18.2.0)(react@18.2.0)
'@mantine/core': 7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
'@mantine/hooks': 7.7.1(react@18.2.0)
clsx: 2.1.0
dayjs: 1.11.10
@@ -2151,7 +2187,7 @@ packages:
react: ^18.2.0
react-dom: ^18.2.0
dependencies:
'@mantine/core': 7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.76)(react-dom@18.2.0)(react@18.2.0)
'@mantine/core': 7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
'@mantine/hooks': 7.7.1(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
@@ -2165,7 +2201,7 @@ packages:
react: ^18.2.0
react-dom: ^18.2.0
dependencies:
'@mantine/core': 7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.76)(react-dom@18.2.0)(react@18.2.0)
'@mantine/core': 7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
'@mantine/hooks': 7.7.1(react@18.2.0)
'@mantine/store': 7.7.1(react@18.2.0)
react: 18.2.0
@@ -2181,7 +2217,7 @@ packages:
react: ^18.2.0
react-dom: ^18.2.0
dependencies:
'@mantine/core': 7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.76)(react-dom@18.2.0)(react@18.2.0)
'@mantine/core': 7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
'@mantine/hooks': 7.7.1(react@18.2.0)
'@mantine/store': 7.7.1(react@18.2.0)
react: 18.2.0
@@ -2196,7 +2232,7 @@ packages:
react: 18.2.0
dev: false
/@mantine/tiptap@7.7.1(@mantine/core@7.7.1)(@mantine/hooks@7.7.1)(@tiptap/extension-link@2.2.5)(@tiptap/react@2.2.5)(react-dom@18.2.0)(react@18.2.0):
/@mantine/tiptap@7.7.1(@mantine/core@7.7.1)(@mantine/hooks@7.7.1)(@tiptap/extension-link@2.2.6)(@tiptap/react@2.2.6)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-MWX4+0UrP3tgyUeWVQdr0jH91j4VsviDPJF62QH1nxDSaObrcU7xkDU2mudPbSNeb07aKDG8ifIMBkcB/r6p9g==}
peerDependencies:
'@mantine/core': 7.7.1
@@ -2206,10 +2242,10 @@ packages:
react: ^18.2.0
react-dom: ^18.2.0
dependencies:
'@mantine/core': 7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.76)(react-dom@18.2.0)(react@18.2.0)
'@mantine/core': 7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
'@mantine/hooks': 7.7.1(react@18.2.0)
'@tiptap/extension-link': 2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4)
'@tiptap/react': 2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4)(react-dom@18.2.0)(react@18.2.0)
'@tiptap/extension-link': 2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4)
'@tiptap/react': 2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4)(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
@@ -2653,199 +2689,301 @@ packages:
react-error-boundary: 3.1.4(react@17.0.2)
dev: true
/@tiptap/core@2.2.5(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-BgF6tiXhwph0Ig16ZNppMmg30t22Z/yjZ+KnS8EALdJere3ACNw8THz9q/8iQqBWjjBOHUvhrQOgER4BJxfmGA==}
/@tiptap/core@2.2.6(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-v7S7RhQhTXQo9KSk2jM/jJlTd3clU2FsJA3Omjz7GbgYtPSy67qSiaTbH/tWq12GzDHbKymx+oQnKmyx+yPucA==}
peerDependencies:
'@tiptap/pm': ^2.0.0
dependencies:
'@tiptap/pm': 2.2.4
dev: false
/@tiptap/extension-blockquote@2.2.5(@tiptap/core@2.2.5):
resolution: {integrity: sha512-mlCNV+kAK5d2vjEmQQZ54br9Cn48VXz3ZwpQyeqZ9z6I4Rl2nqkeWgoIIX8FBT2/8ddcpHuSJnAkAHxuJ0PQrg==}
/@tiptap/extension-blockquote@2.2.6(@tiptap/core@2.2.6):
resolution: {integrity: sha512-Qoq4Tl4wyEGfuBrMFth5hWP1SroJtgDYPnyzAZeLiGzF3Yxtu7FFqjGtD1/Bos9ftnFVCAj+nIXnuKsM1YUaGg==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-bold@2.2.5(@tiptap/core@2.2.5):
resolution: {integrity: sha512-d33+v7430xpuV5B3qyTzDj8n43wc1lsqmCQKp8niW35N8JtEO+AjkZ+Pg9CZqYYE58CwSduCXm1vnCYIVezWag==}
/@tiptap/extension-bold@2.2.6(@tiptap/core@2.2.6):
resolution: {integrity: sha512-PI/jNH7rmi6hBvWy/z+3KUTYqeaDXBUjidM74gWP6OLV28HTJ5SkIPCriYe4u2j2Wc/nk3gPxs4/hPOAu/YiXA==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-bubble-menu@2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-JFeFaUyin93SL/DZZqfoz/DWvMxtqcZLnPPd60hZBczxcLghRZCyDH5nqVHJybrkcs/xNNPennJ1Hqft+WEHbA==}
/@tiptap/extension-bubble-menu@2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-nRWxbgkInhdGUL+e6iISgALcWh8A1PxeVB66w7yYZHS/WoZO0DXdXYT/BWb/XmEJ8r6B4c9SDZRklCiXT8dSXw==}
peerDependencies:
'@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
'@tiptap/pm': 2.2.4
tippy.js: 6.3.7
dev: false
/@tiptap/extension-bullet-list@2.2.5(@tiptap/core@2.2.5):
resolution: {integrity: sha512-Jt4TGV66jqnWyF9egKuA596ehKbT1NN6Vnzle9Dn/hS20SyZxmPhCu13FE1MbajEDg8mn3rAYcWQ5A8+jrQr/Q==}
/@tiptap/extension-bullet-list@2.2.6(@tiptap/core@2.2.6):
resolution: {integrity: sha512-bSrmYlWfj/bXXoBMVB+gCTlsficVVzWi1jcAjAn+qNAENkhampmlFIUG4DiKGYtn18ZoTbyLgQGDMCO3SBdeDQ==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-code-block@2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-EMOSdP+wqvXmrIO7wkzPCGy4jwgSPY42HR2xXg5jd0oqjYM4ZexWJ6cpdVwcUQ51Zjc8EDwJrmFAsbawnyk+fA==}
/@tiptap/extension-code-block@2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-834gVybNyI4nY6NINqnOosFPa4WKylMQTraEY2KhUH2XU1mh0Ni7EgyK10dfZvOUj90OjaxZtXkyZrZ89RTxog==}
peerDependencies:
'@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
'@tiptap/pm': 2.2.4
dev: false
/@tiptap/extension-code@2.2.5(@tiptap/core@2.2.5):
resolution: {integrity: sha512-BctyvPTzbY6FZLrBMVdauBgwoYDaUos1STGqHDV8v9nCDNypqw8OVVepF1SRhp2ggVM8cRxywsN0QC0AyDuO7Q==}
/@tiptap/extension-code@2.2.6(@tiptap/core@2.2.6):
resolution: {integrity: sha512-UGsSFvVWrWWWQFU4atk+b/qeewTLadOZG/BHZXQDloyP5eJ1SkgUVy9nv3y2cT8QWRbvF6sxkV+SdFoWnvaG3Q==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-document@2.2.5(@tiptap/core@2.2.5):
resolution: {integrity: sha512-05JLDpFUK0M9Oz6fxDv7tmZGRbQFldP8VDrHEh3uCLl8GUZSOplmwlnQ7HLgGo3iCFUX7o1O2OeEJRRQBCzqlA==}
/@tiptap/extension-color@2.2.5(@tiptap/core@2.2.6)(@tiptap/extension-text-style@2.2.5):
resolution: {integrity: sha512-lEqqnHMWDBbwwKcIKPS0QqmXyO+a1v2yAmzGhvyhQdMdcbcfXSslrrOAXJ5fGyU8Hl9TFtBd8Atet0YA/8PuHQ==}
peerDependencies:
'@tiptap/core': ^2.0.0
'@tiptap/extension-text-style': ^2.0.0
dependencies:
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
'@tiptap/extension-text-style': 2.2.5(@tiptap/core@2.2.6)
dev: false
/@tiptap/extension-document@2.2.6(@tiptap/core@2.2.6):
resolution: {integrity: sha512-yT9m5Oo9U/xAypcylaLiDE8qmVd3SCZSc8s5lqyC1OW+psb1oC0d14+TgKetO2s8K2wAbW2DxYG3yoxWffGYsQ==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-dropcursor@2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-GRSYNtEKGekpr3kb/5ci2eR2hN1WqORMUqd8wxBBi5AF1cHyXLbvy08bYe2enu3M1+MA0sW5J7Jh5j7D0HY49w==}
/@tiptap/extension-dropcursor@2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-mCeIbbfe4rl8CuxVQvT7iYSKGVX/ls1LOwALwlHJz5Uw5l3VknAJdjEmHt6hNFdHu162JivL02Il0QYQ8BZwvA==}
peerDependencies:
'@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
'@tiptap/pm': 2.2.4
dev: false
/@tiptap/extension-floating-menu@2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-/PfS9QHEHQsoV6ZqolJ6Rq6ZC6drD7IPJRgywuJi0zTTH0YCbLmJKrc3K2Qj69xbfwcANL8gBdsexv5zOjKAXA==}
/@tiptap/extension-floating-menu@2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-6ONKC6Dx8zCc5YffXpnQ9FxGRoUp5Jm9mOO3losgiDFhdJqaO7SCk1ziOiD7enoWqIc2shZh8ADnqttCfnFVFQ==}
peerDependencies:
'@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
'@tiptap/pm': 2.2.4
tippy.js: 6.3.7
dev: false
/@tiptap/extension-gapcursor@2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-zKAg7KR1BWOSL8LopIb1jnw0PRapxLNJyC3OUSbl6snlxRy+ER5hbv8bHJDznDCC3PjcNemYRUewTTrggHucsA==}
/@tiptap/extension-gapcursor@2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-HDYu+FmL9V+khsiT5904Dy2qG6KrAvnXEjZk1+vVul0TabnQvl2rqHjTxmev3P1rOYTgePmaWXazxAWFIvbMBQ==}
peerDependencies:
'@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
'@tiptap/pm': 2.2.4
dev: false
/@tiptap/extension-hard-break@2.2.5(@tiptap/core@2.2.5):
resolution: {integrity: sha512-oySgYvu4RSrwc7/xPVxpqa4NLnVLUgaoOFE/wU0NWtgwh2wptZdanT2bacsBJTqIRMi6HCZbMzr88qbxk0cWgA==}
/@tiptap/extension-hard-break@2.2.6(@tiptap/core@2.2.6):
resolution: {integrity: sha512-gwavC76sn26XQLyDaDtf28KIcbhMYPP+C5pkbRvAhVSckQB3Ebz3GRttVbm/jp+Uifp3bmoQEzISGCONEdKQoQ==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-heading@2.2.5(@tiptap/core@2.2.5):
resolution: {integrity: sha512-gEihkfeAi5aOZ+WUd/ZLq6D6KopZr8e/EPWD/3eeeI/RyAGBwT2+ZnDTOZSL7/snbp4n/1TCFYhh9vjNSnCDkw==}
/@tiptap/extension-heading@2.2.6(@tiptap/core@2.2.6):
resolution: {integrity: sha512-XOmY+uezm42xSO1ero2bRBMdQxWytpxLJS+2shK0QogZ3sDplnfWfP5KV9Z2juXjTdPgPWG0ZaHzIIaLquEcfA==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-history@2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-/KHFlQApljA2L/CX0qWhT+hPQeI2a8M4m5VynmTYVbQ4y/T2bVnys0k9zNuYwQxeqVSfeSqCZAIkMjen2fDzYQ==}
/@tiptap/extension-highlight@2.2.5(@tiptap/core@2.2.6):
resolution: {integrity: sha512-Ji8F9fkMGjL7723B4dymQKq+P5VBQ/PJMsTrHlbbIm3ZBWxRc43VHzT3bQJ4Qm66mO4YfpEC6eA5JDq44CrfWA==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-history@2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-c2Aeozc+pHcpqghLjXRX/tGU/C+Gp6hApUWPXdhZw5Y/ARj6ZRwx2/ym2K8MOrJ36/W7gc7Xyxd9ZbG7m7pcjA==}
peerDependencies:
'@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
'@tiptap/pm': 2.2.4
dev: false
/@tiptap/extension-horizontal-rule@2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-M0eGP5OsMj3JcfAL122XAmgMW5EYQBjKBNkkhihnSZb7r6/cQuqG4F89YZCOigiXVNW5r4KkYbL+ykSm/lFD9w==}
/@tiptap/extension-horizontal-rule@2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-zyLU+Xlk8y3yBCblE8pFwqAP2Rju1csyAu45hi3NCJ6HDGQGdjy8oh+Xa8y2kTPxRNMZARxqB+vCiEoW3YZn2A==}
peerDependencies:
'@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
'@tiptap/pm': 2.2.4
dev: false
/@tiptap/extension-italic@2.2.5(@tiptap/core@2.2.5):
resolution: {integrity: sha512-FEUaRkEQ1rY3nmOzHp2THHhTjE2Ltv5tXoABnannjpoLFe6AIdmYcsHOOI7pvdZQclejSQoFpuEDillvDHtUkg==}
/@tiptap/extension-image@2.2.5(@tiptap/core@2.2.6):
resolution: {integrity: sha512-sFBuNaTYnsprDV/GczBy8CCGd/9oPYLCRcCuTE+HaRnAh518jVL+n6X8XIOO5u+AEJT6frRGiM68wpyk1Tq31w==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-link@2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-3UyEdD7eOzb31Ro9zh02EIvZhgSFKV+/rSKgAf4wU17QUFid0cYRqaXcVjG0UOSznHqccxGQKD7nNT0jxTg+cw==}
/@tiptap/extension-italic@2.2.6(@tiptap/core@2.2.6):
resolution: {integrity: sha512-wB+Y6p2gbc1f2hKYeGNXRQ7P2xi3+JzD3PjSyC9Ss/yyujZhxSOtxBF0nzFXdI+7nmN0Qm4inwPDU/DVrIPb+A==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-link@2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-Jj0oXSfQ8gZlzzwd669B8sEKBkoK8xV31Lu55tRv9PKHSU6p9CUqBuxY8qR+cquCtO28f3u0cdl5o4HzeIUL5A==}
peerDependencies:
'@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
'@tiptap/pm': 2.2.4
linkifyjs: 4.1.3
dev: false
/@tiptap/extension-list-item@2.2.5(@tiptap/core@2.2.5):
resolution: {integrity: sha512-skmDdzvW0iwtwAkQOpDhAQy2eoWRFUWElu9uoqouC4uh+arbSpAEs1tXgBjb4wda0R2wvLUOLxFUdwwFZwgYhQ==}
/@tiptap/extension-list-item@2.2.6(@tiptap/core@2.2.6):
resolution: {integrity: sha512-3xig1q0jtOyV49TkAbvxBoOJdNypwq6vLYerfblhj6dK+hIIZUM33S+SmGl2+QaB25VwyeSHjiCvrJjB9PKWHQ==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-ordered-list@2.2.5(@tiptap/core@2.2.5):
resolution: {integrity: sha512-zVE21e8EWOQxstJEzpCSl0yI3MxjCciHpRBmnxEiuN7W3vDrjfphwrksZDwNHBDIZYQF7hEtTWgOgjnacxpmWQ==}
/@tiptap/extension-ordered-list@2.2.6(@tiptap/core@2.2.6):
resolution: {integrity: sha512-h4HOv+TAMnoueh3CzUY2/Pp2n8eCdEQtKSfiMtHSO3NTTSlst0XEvq+3Z4K81F+ni3baXc+JUALP5dRVpI4apQ==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-paragraph@2.2.5(@tiptap/core@2.2.5):
resolution: {integrity: sha512-FiyCkGfDPv1UQhVTX2uuEENAJ8G9GA6iDFD6HZH8/vOvkKgEI3B9/PUjOecR0rn+Ncta4f/dMRTyUlt6ezfwJg==}
/@tiptap/extension-paragraph@2.2.6(@tiptap/core@2.2.6):
resolution: {integrity: sha512-M2rM3pfzziUb7xS9x2dANCokO89okbqg5IqU4VPkZhk0Mfq9czyCatt58TYkAsE3ccsGhdTYtFBTDeKBtsHUqg==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-strike@2.2.5(@tiptap/core@2.2.5):
resolution: {integrity: sha512-Zx+6afbx3RbWzVXGSNHSAOVb2O8SDyZqsgEuIepbh5t2kFvZHCBMA7qLlco1zdIelPp+6pD2NoAvHkQnjjrDmw==}
/@tiptap/extension-strike@2.2.6(@tiptap/core@2.2.6):
resolution: {integrity: sha512-0fRh0SwPgqi+ZKD2NpRrmIAHdsgf27ddEUfvlIuFG5b9zqFa6pRZGpXW/6LyBwU0+0bkjW8/Wg3otyaRGjvZGw==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-text@2.2.5(@tiptap/core@2.2.5):
resolution: {integrity: sha512-Sn6Wqa52PVtJQW0W5zE8Tzf+61wH7s1kh0yN9Q05IeSoslK38R8mBdezp0bHnbqFrlqXDxtPrTRgipaWCSV6Lw==}
/@tiptap/extension-table-cell@2.2.5(@tiptap/core@2.2.6):
resolution: {integrity: sha512-vJl4rED9VUUKAA7sJejTzQshdIQ4TKWSiT20vS2hO332E8+fFLAByb0V/eBKC0mKIx2xFP+JbQPuMDPrXwaeKA==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-table-header@2.2.5(@tiptap/core@2.2.6):
resolution: {integrity: sha512-JicDIocBJr07dVcscjsPOEZyfq3ceBoYYvsW9hRq5xBS6OLJwNTNsJoKWKDXkQHG8q0K0v8L3Ce085NDxGTdCQ==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-table-row@2.2.5(@tiptap/core@2.2.6):
resolution: {integrity: sha512-f/yff0eHr7hsNokXaipFece6bib8iLwI/rue2NKIMICzsBXpaWI9V5cwtDcddvWWjBz/ZdB7NVULtATtjUhpdg==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-table@2.2.5(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-+dAlJBtoyXkXH8sR5K872l0oI6sl4swVG/c00ve92h9ZWG3wuPyb74RKfglGK6zK4rxPkfwYnHc/xDMsyh+NgQ==}
peerDependencies:
'@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0
dependencies:
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
'@tiptap/pm': 2.2.4
dev: false
/@tiptap/extension-task-item@2.2.5(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-1lJ4d/7+Bk7N14NUNHmFUbfxgMnGVSxYiXki414UdKKfg5uS2nflwTEpE8nra6YyODotVmIpfluaIrHBGLG2/Q==}
peerDependencies:
'@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0
dependencies:
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
'@tiptap/pm': 2.2.4
dev: false
/@tiptap/extension-task-list@2.2.5(@tiptap/core@2.2.6):
resolution: {integrity: sha512-Gq1/tZYs2axXFgdxCjkmNKXI505acZjp8qUjwt4yTzb9lORcl/62wweB5sryR1Zef7UuwMD7ew+8nimz3JVSjA==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-text-align@2.2.5(@tiptap/core@2.2.6):
resolution: {integrity: sha512-rCnWjEc0hw52lgiEhs96lTn9s9R+sT0VWHP2RlGhIvC0gCCKqlpPbMdUPKc94SIoRurBIikHV4MWnWYoI0/trA==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-text-style@2.2.5(@tiptap/core@2.2.6):
resolution: {integrity: sha512-Hlx9WTS5VXjD9CqPrHIqGjYm5vKFjkKPSOf9H2kxZ8vdVBKpAOzLsfryq4rOLdP1bpmcX3zeOe2DGByUapbs1g==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-text@2.2.6(@tiptap/core@2.2.6):
resolution: {integrity: sha512-wVpo0I/2tJsBK/2yNZfRXOsThOfHCdTY+FDNO/USx9MCJaJ3LPs3H1AuGO549zNmZgkD+1MqcZqrYt9n4i03cw==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/extension-underline@2.2.5(@tiptap/core@2.2.6):
resolution: {integrity: sha512-qKes3UVhktaRLOUFv3tpl3MOY4g1rnY6mxuVRmFeicgEtG0IHv7ByrNwA775CdmKdsewz55P5JYrj1jUzbpBhQ==}
peerDependencies:
'@tiptap/core': ^2.0.0
dependencies:
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
dev: false
/@tiptap/pm@2.2.4:
@@ -2871,44 +3009,44 @@ packages:
prosemirror-view: 1.33.1
dev: false
/@tiptap/react@2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-kpEnkvTXUnr6npLt1mQCBhdgle/ZkttGgKwtwSLdM74zPzPwj8XVeCp+3kyVBK4ME7DXWIo5dPbU0nUi/FyE+g==}
/@tiptap/react@2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-KZLsaia9FYrhjBJ+HP2L2HQLqspgC7JLT5wXcOpgdKwQ8wAMkD3b2a6bhAc9IKZSkbhcX2D+uafGPGn2M6j6Lw==}
peerDependencies:
'@tiptap/core': ^2.0.0
'@tiptap/pm': ^2.0.0
react: ^17.0.0 || ^18.0.0
react-dom: ^17.0.0 || ^18.0.0
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/extension-bubble-menu': 2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4)
'@tiptap/extension-floating-menu': 2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
'@tiptap/extension-bubble-menu': 2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4)
'@tiptap/extension-floating-menu': 2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4)
'@tiptap/pm': 2.2.4
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@tiptap/starter-kit@2.2.5(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-IcMwleJpIaPi2xvFDnxeg/TL2O9LYhgEOQZjTX96dOP2gG7logWYqIuPCfWzD46IKdjMvk4cqMB/SORd8zJAUQ==}
/@tiptap/starter-kit@2.2.6(@tiptap/pm@2.2.4):
resolution: {integrity: sha512-dWdLcx7g9DTYYzlnStft8vNLrnn+nUWj5Hx4i1dRRW31hBvIxnPwFYcEPKd+7xguozuUX5g+P4OYI6M3LOUxlA==}
dependencies:
'@tiptap/core': 2.2.5(@tiptap/pm@2.2.4)
'@tiptap/extension-blockquote': 2.2.5(@tiptap/core@2.2.5)
'@tiptap/extension-bold': 2.2.5(@tiptap/core@2.2.5)
'@tiptap/extension-bullet-list': 2.2.5(@tiptap/core@2.2.5)
'@tiptap/extension-code': 2.2.5(@tiptap/core@2.2.5)
'@tiptap/extension-code-block': 2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4)
'@tiptap/extension-document': 2.2.5(@tiptap/core@2.2.5)
'@tiptap/extension-dropcursor': 2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4)
'@tiptap/extension-gapcursor': 2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4)
'@tiptap/extension-hard-break': 2.2.5(@tiptap/core@2.2.5)
'@tiptap/extension-heading': 2.2.5(@tiptap/core@2.2.5)
'@tiptap/extension-history': 2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4)
'@tiptap/extension-horizontal-rule': 2.2.5(@tiptap/core@2.2.5)(@tiptap/pm@2.2.4)
'@tiptap/extension-italic': 2.2.5(@tiptap/core@2.2.5)
'@tiptap/extension-list-item': 2.2.5(@tiptap/core@2.2.5)
'@tiptap/extension-ordered-list': 2.2.5(@tiptap/core@2.2.5)
'@tiptap/extension-paragraph': 2.2.5(@tiptap/core@2.2.5)
'@tiptap/extension-strike': 2.2.5(@tiptap/core@2.2.5)
'@tiptap/extension-text': 2.2.5(@tiptap/core@2.2.5)
'@tiptap/core': 2.2.6(@tiptap/pm@2.2.4)
'@tiptap/extension-blockquote': 2.2.6(@tiptap/core@2.2.6)
'@tiptap/extension-bold': 2.2.6(@tiptap/core@2.2.6)
'@tiptap/extension-bullet-list': 2.2.6(@tiptap/core@2.2.6)
'@tiptap/extension-code': 2.2.6(@tiptap/core@2.2.6)
'@tiptap/extension-code-block': 2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4)
'@tiptap/extension-document': 2.2.6(@tiptap/core@2.2.6)
'@tiptap/extension-dropcursor': 2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4)
'@tiptap/extension-gapcursor': 2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4)
'@tiptap/extension-hard-break': 2.2.6(@tiptap/core@2.2.6)
'@tiptap/extension-heading': 2.2.6(@tiptap/core@2.2.6)
'@tiptap/extension-history': 2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4)
'@tiptap/extension-horizontal-rule': 2.2.6(@tiptap/core@2.2.6)(@tiptap/pm@2.2.4)
'@tiptap/extension-italic': 2.2.6(@tiptap/core@2.2.6)
'@tiptap/extension-list-item': 2.2.6(@tiptap/core@2.2.6)
'@tiptap/extension-ordered-list': 2.2.6(@tiptap/core@2.2.6)
'@tiptap/extension-paragraph': 2.2.6(@tiptap/core@2.2.6)
'@tiptap/extension-strike': 2.2.6(@tiptap/core@2.2.6)
'@tiptap/extension-text': 2.2.6(@tiptap/core@2.2.6)
transitivePeerDependencies:
- '@tiptap/pm'
dev: false
@@ -3210,11 +3348,11 @@ packages:
/@types/react-dom@18.2.25:
resolution: {integrity: sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==}
dependencies:
'@types/react': 18.2.76
'@types/react': 18.2.78
dev: true
/@types/react@18.2.76:
resolution: {integrity: sha512-T6z/v7YxpswDM61Vq5KoSPTJqCkroJfsDIsvXCr4+qOY6gik5Ju4w0jf67cpC5z7ydOnp/E0V0W08pDRy8u9Xw==}
/@types/react@18.2.78:
resolution: {integrity: sha512-qOwdPnnitQY4xKlKayt42q5W5UQrSHjgoXNVEtxeqdITJ99k4VXJOP3vt8Rkm9HmgJpH50UNU+rlqfkfWOqp0A==}
dependencies:
'@types/prop-types': 15.7.11
csstype: 3.1.3
@@ -6234,7 +6372,7 @@ packages:
resolution: {integrity: sha512-/WByRr4jDcsKlvMd1dRJnPfS1GVO3WuKyaurJ/vvXcOaUQO8rnNObCQMlv/5uCceVQIq5Q4WLF44ohsdiTohdg==}
dev: false
/jotai@2.8.0(@types/react@18.2.76)(react@18.2.0):
/jotai@2.8.0(@types/react@18.2.78)(react@18.2.0):
resolution: {integrity: sha512-yZNMC36FdLOksOr8qga0yLf14miCJlEThlp5DeFJNnqzm2+ZG7wLcJzoOyij5K6U6Xlc5ljQqPDlJRgqW0Y18g==}
engines: {node: '>=12.20.0'}
peerDependencies:
@@ -6246,7 +6384,7 @@ packages:
react:
optional: true
dependencies:
'@types/react': 18.2.76
'@types/react': 18.2.78
react: 18.2.0
dev: false
@@ -6587,7 +6725,7 @@ packages:
react: '>=18.0'
react-dom: '>=18.0'
dependencies:
'@mantine/core': 7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.76)(react-dom@18.2.0)(react@18.2.0)
'@mantine/core': 7.7.1(@mantine/hooks@7.7.1)(@types/react@18.2.78)(react-dom@18.2.0)(react@18.2.0)
'@mantine/dates': 7.7.1(@mantine/core@7.7.1)(@mantine/hooks@7.7.1)(dayjs@1.11.10)(react-dom@18.2.0)(react@18.2.0)
'@mantine/hooks': 7.7.1(react@18.2.0)
'@tabler/icons-react': 3.1.0(react@18.2.0)
@@ -7700,7 +7838,7 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/react-remove-scroll-bar@2.3.4(@types/react@18.2.76)(react@18.2.0):
/react-remove-scroll-bar@2.3.4(@types/react@18.2.78)(react@18.2.0):
resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==}
engines: {node: '>=10'}
peerDependencies:
@@ -7710,13 +7848,13 @@ packages:
'@types/react':
optional: true
dependencies:
'@types/react': 18.2.76
'@types/react': 18.2.78
react: 18.2.0
react-style-singleton: 2.2.1(@types/react@18.2.76)(react@18.2.0)
react-style-singleton: 2.2.1(@types/react@18.2.78)(react@18.2.0)
tslib: 2.6.2
dev: false
/react-remove-scroll@2.5.7(@types/react@18.2.76)(react@18.2.0):
/react-remove-scroll@2.5.7(@types/react@18.2.78)(react@18.2.0):
resolution: {integrity: sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==}
engines: {node: '>=10'}
peerDependencies:
@@ -7726,16 +7864,16 @@ packages:
'@types/react':
optional: true
dependencies:
'@types/react': 18.2.76
'@types/react': 18.2.78
react: 18.2.0
react-remove-scroll-bar: 2.3.4(@types/react@18.2.76)(react@18.2.0)
react-style-singleton: 2.2.1(@types/react@18.2.76)(react@18.2.0)
react-remove-scroll-bar: 2.3.4(@types/react@18.2.78)(react@18.2.0)
react-style-singleton: 2.2.1(@types/react@18.2.78)(react@18.2.0)
tslib: 2.6.2
use-callback-ref: 1.3.1(@types/react@18.2.76)(react@18.2.0)
use-sidecar: 1.1.2(@types/react@18.2.76)(react@18.2.0)
use-callback-ref: 1.3.1(@types/react@18.2.78)(react@18.2.0)
use-sidecar: 1.1.2(@types/react@18.2.78)(react@18.2.0)
dev: false
/react-style-singleton@2.2.1(@types/react@18.2.76)(react@18.2.0):
/react-style-singleton@2.2.1(@types/react@18.2.78)(react@18.2.0):
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
engines: {node: '>=10'}
peerDependencies:
@@ -7745,14 +7883,14 @@ packages:
'@types/react':
optional: true
dependencies:
'@types/react': 18.2.76
'@types/react': 18.2.78
get-nonce: 1.0.1
invariant: 2.2.4
react: 18.2.0
tslib: 2.6.2
dev: false
/react-textarea-autosize@8.5.3(@types/react@18.2.76)(react@18.2.0):
/react-textarea-autosize@8.5.3(@types/react@18.2.78)(react@18.2.0):
resolution: {integrity: sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==}
engines: {node: '>=10'}
peerDependencies:
@@ -7761,7 +7899,7 @@ packages:
'@babel/runtime': 7.23.9
react: 18.2.0
use-composed-ref: 1.3.0(react@18.2.0)
use-latest: 1.2.1(@types/react@18.2.76)(react@18.2.0)
use-latest: 1.2.1(@types/react@18.2.78)(react@18.2.0)
transitivePeerDependencies:
- '@types/react'
dev: false
@@ -8879,7 +9017,7 @@ packages:
resolution: {integrity: sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg==}
dev: false
/use-callback-ref@1.3.1(@types/react@18.2.76)(react@18.2.0):
/use-callback-ref@1.3.1(@types/react@18.2.78)(react@18.2.0):
resolution: {integrity: sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==}
engines: {node: '>=10'}
peerDependencies:
@@ -8889,7 +9027,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@types/react': 18.2.76
'@types/react': 18.2.78
react: 18.2.0
tslib: 2.6.2
dev: false
@@ -8913,7 +9051,7 @@ packages:
react: 18.2.0
dev: false
/use-isomorphic-layout-effect@1.1.2(@types/react@18.2.76)(react@18.2.0):
/use-isomorphic-layout-effect@1.1.2(@types/react@18.2.78)(react@18.2.0):
resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==}
peerDependencies:
'@types/react': '*'
@@ -8922,11 +9060,11 @@ packages:
'@types/react':
optional: true
dependencies:
'@types/react': 18.2.76
'@types/react': 18.2.78
react: 18.2.0
dev: false
/use-latest@1.2.1(@types/react@18.2.76)(react@18.2.0):
/use-latest@1.2.1(@types/react@18.2.78)(react@18.2.0):
resolution: {integrity: sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==}
peerDependencies:
'@types/react': '*'
@@ -8935,12 +9073,12 @@ packages:
'@types/react':
optional: true
dependencies:
'@types/react': 18.2.76
'@types/react': 18.2.78
react: 18.2.0
use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.76)(react@18.2.0)
use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.78)(react@18.2.0)
dev: false
/use-sidecar@1.1.2(@types/react@18.2.76)(react@18.2.0):
/use-sidecar@1.1.2(@types/react@18.2.78)(react@18.2.0):
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
engines: {node: '>=10'}
peerDependencies:
@@ -8950,7 +9088,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@types/react': 18.2.76
'@types/react': 18.2.78
detect-node-es: 1.1.0
react: 18.2.0
tslib: 2.6.2