diff --git a/apps/client/package.json b/apps/client/package.json
index 4c4c7e5..b5559f6 100644
--- a/apps/client/package.json
+++ b/apps/client/package.json
@@ -26,6 +26,7 @@
"clsx": "^2.1.1",
"date-fns": "^3.6.0",
"emoji-mart": "^5.6.0",
+ "file-saver": "^2.0.5",
"jotai": "^2.8.3",
"jotai-optics": "^0.4.0",
"js-cookie": "^3.0.5",
diff --git a/apps/client/src/features/editor/full-editor.tsx b/apps/client/src/features/editor/full-editor.tsx
index 2368425..aa87703 100644
--- a/apps/client/src/features/editor/full-editor.tsx
+++ b/apps/client/src/features/editor/full-editor.tsx
@@ -3,7 +3,7 @@ import React from "react";
import { TitleEditor } from "@/features/editor/title-editor";
import PageEditor from "@/features/editor/page-editor";
import { Container } from "@mantine/core";
-import { useAtom } from "jotai/index";
+import { useAtom } from "jotai";
import { userAtom } from "@/features/user/atoms/current-user-atom.ts";
const MemoizedTitleEditor = React.memo(TitleEditor);
diff --git a/apps/client/src/features/editor/styles/core.css b/apps/client/src/features/editor/styles/core.css
index 75b6f32..94af10a 100644
--- a/apps/client/src/features/editor/styles/core.css
+++ b/apps/client/src/features/editor/styles/core.css
@@ -163,3 +163,4 @@
.actionIconGroup {
background: var(--mantine-color-body);
}
+
diff --git a/apps/client/src/features/editor/styles/editor.module.css b/apps/client/src/features/editor/styles/editor.module.css
index c281ba3..e5e471c 100644
--- a/apps/client/src/features/editor/styles/editor.module.css
+++ b/apps/client/src/features/editor/styles/editor.module.css
@@ -2,5 +2,10 @@
height: 100%;
padding: 8px 20px;
margin: 64px auto;
+
+ @media print {
+ padding: 0;
+ margin: 0;
+ }
}
diff --git a/apps/client/src/features/editor/styles/index.css b/apps/client/src/features/editor/styles/index.css
index d9b0fdb..894d3ee 100644
--- a/apps/client/src/features/editor/styles/index.css
+++ b/apps/client/src/features/editor/styles/index.css
@@ -8,5 +8,7 @@
@import "./youtube.css";
@import "./media.css";
@import "./code.css";
+@import "./print.css";
+
diff --git a/apps/client/src/features/editor/styles/placeholder.css b/apps/client/src/features/editor/styles/placeholder.css
index 67aec13..0ba3151 100644
--- a/apps/client/src/features/editor/styles/placeholder.css
+++ b/apps/client/src/features/editor/styles/placeholder.css
@@ -4,6 +4,10 @@
color: #adb5bd;
pointer-events: none;
height: 0;
+
+ @media print {
+ display: none;
+ }
}
.ProseMirror .is-empty::before {
@@ -12,9 +16,17 @@
color: #adb5bd;
pointer-events: none;
height: 0;
+
+ @media print {
+ display: none;
+ }
}
.ProseMirror table .is-editor-empty:first-child::before,
.ProseMirror table .is-empty::before {
content: '';
+
+ @media print {
+ display: none;
+ }
}
diff --git a/apps/client/src/features/editor/styles/print.css b/apps/client/src/features/editor/styles/print.css
new file mode 100644
index 0000000..ab105b4
--- /dev/null
+++ b/apps/client/src/features/editor/styles/print.css
@@ -0,0 +1,11 @@
+@media print {
+ .mantine-AppShell-header,
+ .mantine-AppShell-navbar,
+ .mantine-AppShell-aside{
+ display: none !important;
+ }
+
+ .mantine-AppShell-main {
+ padding-top: 0 !important;
+ }
+}
diff --git a/apps/client/src/features/editor/styles/youtube.css b/apps/client/src/features/editor/styles/youtube.css
index 4ec2319..8f73600 100644
--- a/apps/client/src/features/editor/styles/youtube.css
+++ b/apps/client/src/features/editor/styles/youtube.css
@@ -17,5 +17,9 @@
&.ProseMirror-selectednode {
background-color: transparent;
}
+
+ @media print {
+ display: none;
+ }
}
}
diff --git a/apps/client/src/features/page/components/header/page-header-menu.tsx b/apps/client/src/features/page/components/header/page-header-menu.tsx
index 718406d..fccb1c1 100644
--- a/apps/client/src/features/page/components/header/page-header-menu.tsx
+++ b/apps/client/src/features/page/components/header/page-header-menu.tsx
@@ -2,16 +2,18 @@ import { ActionIcon, Group, Menu, Tooltip } from "@mantine/core";
import {
IconArrowsHorizontal,
IconDots,
+ IconDownload,
IconHistory,
IconLink,
IconMessage,
+ IconPrinter,
IconTrash,
} from "@tabler/icons-react";
import React from "react";
import useToggleAside from "@/hooks/use-toggle-aside.tsx";
import { useAtom } from "jotai";
import { historyAtoms } from "@/features/page-history/atoms/history-atoms.ts";
-import { useClipboard } from "@mantine/hooks";
+import { useClipboard, useDisclosure } from "@mantine/hooks";
import { useParams } from "react-router-dom";
import { usePageQuery } from "@/features/page/queries/page-query.ts";
import { buildPageUrl } from "@/features/page/page.utils.ts";
@@ -21,6 +23,7 @@ import { extractPageSlugId } from "@/lib";
import { treeApiAtom } from "@/features/page/tree/atoms/tree-api-atom.ts";
import { useDeletePageModal } from "@/features/page/hooks/use-delete-page-modal.tsx";
import { PageWidthToggle } from "@/features/user/components/page-width-pref.tsx";
+import PageExportModal from "@/features/page/components/page-export-modal.tsx";
interface PageHeaderMenuProps {
readOnly?: boolean;
@@ -57,6 +60,8 @@ function PageActionMenu({ readOnly }: PageActionMenuProps) {
});
const { openDeleteModal } = useDeletePageModal();
const [tree] = useAtom(treeApiAtom);
+ const [opened, { open: openExportModal, close: closeExportModal }] =
+ useDisclosure(false);
const handleCopyLink = () => {
const pageUrl =
@@ -66,6 +71,12 @@ function PageActionMenu({ readOnly }: PageActionMenuProps) {
notifications.show({ message: "Link copied" });
};
+ const handlePrint = () => {
+ setTimeout(() => {
+ window.print();
+ }, 250);
+ };
+
const openHistoryModal = () => {
setHistoryModalOpen(true);
};
@@ -75,55 +86,79 @@ function PageActionMenu({ readOnly }: PageActionMenuProps) {
};
return (
-
+
+
+ >
);
}
diff --git a/apps/client/src/features/page/components/header/page-header.module.css b/apps/client/src/features/page/components/header/page-header.module.css
index 4b3b46d..f3e5f7a 100644
--- a/apps/client/src/features/page/components/header/page-header.module.css
+++ b/apps/client/src/features/page/components/header/page-header.module.css
@@ -8,4 +8,8 @@
top: var(--app-shell-header-offset, 0rem);
inset-inline-start: var(--app-shell-navbar-offset, 0rem);
inset-inline-end: var(--app-shell-aside-offset, 0rem);
+
+ @media print {
+ display: none;
+ }
}
diff --git a/apps/client/src/features/page/components/page-export-modal.tsx b/apps/client/src/features/page/components/page-export-modal.tsx
new file mode 100644
index 0000000..21fe19e
--- /dev/null
+++ b/apps/client/src/features/page/components/page-export-modal.tsx
@@ -0,0 +1,84 @@
+import { Modal, Button, Group, Text, Select } from "@mantine/core";
+import { exportPage } from "@/features/page/services/page-service.ts";
+import { useState } from "react";
+import * as React from "react";
+import { ExportFormat } from "@/features/page/types/page.types.ts";
+import { notifications } from "@mantine/notifications";
+
+interface PageExportModalProps {
+ pageId: string;
+ open: boolean;
+ onClose: () => void;
+}
+
+export default function PageExportModal({
+ pageId,
+ open,
+ onClose,
+}: PageExportModalProps) {
+ const [format, setFormat] = useState(ExportFormat.Markdown);
+
+ const handleExport = async () => {
+ try {
+ await exportPage({ pageId: pageId, format });
+ onClose();
+ } catch (err) {
+ notifications.show({
+ message: "Export failed:" + err.response?.data.message,
+ color: "red",
+ });
+ console.error("export error", err);
+ }
+ };
+
+ const handleChange = (format: ExportFormat) => {
+ setFormat(format);
+ };
+
+ return (
+ <>
+
+
+
+ Export format
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+interface ExportFormatSelection {
+ onChange: (value: string) => void;
+}
+function ExportFormatSelection({ onChange }: ExportFormatSelection) {
+ return (
+
+ );
+}
diff --git a/apps/client/src/features/page/services/page-service.ts b/apps/client/src/features/page/services/page-service.ts
index 6a80443..f7228ec 100644
--- a/apps/client/src/features/page/services/page-service.ts
+++ b/apps/client/src/features/page/services/page-service.ts
@@ -1,11 +1,13 @@
import api from "@/lib/api-client";
import {
+ IExportPageParams,
IMovePage,
IPage,
IPageInput,
SidebarPagesParams,
} from "@/features/page/types/page.types";
import { IAttachment, IPagination } from "@/lib/types.ts";
+import { saveAs } from "file-saver";
export async function createPage(data: Partial): Promise {
const req = await api.post("/pages/create", data);
@@ -53,18 +55,28 @@ export async function getRecentChanges(
return req.data;
}
+export async function exportPage(data: IExportPageParams): Promise {
+ const req = await api.post("/pages/export", data, {
+ responseType: "blob",
+ });
+
+ const fileName = req?.headers["content-disposition"]
+ .split("filename=")[1]
+ .replace(/"/g, "");
+
+ saveAs(req.data, fileName);
+}
+
export async function uploadFile(file: File, pageId: string) {
const formData = new FormData();
formData.append("pageId", pageId);
formData.append("file", file);
- // should be file endpoint
const req = await api.post("/files/upload", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
- // console.log("req", req);
return req;
}
diff --git a/apps/client/src/features/page/types/page.types.ts b/apps/client/src/features/page/types/page.types.ts
index f930567..c4bda64 100644
--- a/apps/client/src/features/page/types/page.types.ts
+++ b/apps/client/src/features/page/types/page.types.ts
@@ -44,3 +44,13 @@ export interface IPageInput {
coverPhoto: string;
position: string;
}
+
+export interface IExportPageParams {
+ pageId: string;
+ format: ExportFormat;
+}
+
+export enum ExportFormat {
+ HTML = "html",
+ Markdown = "markdown",
+}
diff --git a/apps/client/src/features/user/components/change-password.tsx b/apps/client/src/features/user/components/change-password.tsx
index afcbeed..7ab71e4 100644
--- a/apps/client/src/features/user/components/change-password.tsx
+++ b/apps/client/src/features/user/components/change-password.tsx
@@ -95,9 +95,11 @@ function ChangePasswordForm({ onClose }: ChangePasswordFormProps) {
{...form.getInputProps("newPassword")}
/>
-
+
+
+
);
}
diff --git a/apps/client/src/lib/api-client.ts b/apps/client/src/lib/api-client.ts
index b1c6ef9..3c6f87e 100644
--- a/apps/client/src/lib/api-client.ts
+++ b/apps/client/src/lib/api-client.ts
@@ -26,11 +26,16 @@ api.interceptors.request.use(
},
(error) => {
return Promise.reject(error);
- }
+ },
);
api.interceptors.response.use(
(response) => {
+ // we need the response headers
+ if (response.request.responseURL.includes("/api/pages/export")) {
+ return response;
+ }
+
return response.data;
},
(error) => {
@@ -67,7 +72,7 @@ api.interceptors.response.use(
}
}
return Promise.reject(error);
- }
+ },
);
function redirectToLogin() {
diff --git a/apps/client/src/lib/types.ts b/apps/client/src/lib/types.ts
index 6613f20..7e15a28 100644
--- a/apps/client/src/lib/types.ts
+++ b/apps/client/src/lib/types.ts
@@ -22,6 +22,10 @@ export interface IRoleData {
description: string;
}
+export interface ApiResponse {
+ data: T;
+}
+
export type IPaginationMeta = {
limit: number;
page: number;
diff --git a/apps/server/src/app.module.ts b/apps/server/src/app.module.ts
index 2883272..8d71816 100644
--- a/apps/server/src/app.module.ts
+++ b/apps/server/src/app.module.ts
@@ -12,6 +12,7 @@ import { QueueModule } from './integrations/queue/queue.module';
import { StaticModule } from './integrations/static/static.module';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { HealthModule } from './integrations/health/health.module';
+import { ExportModule } from './integrations/export/export.module';
@Module({
imports: [
@@ -23,6 +24,7 @@ import { HealthModule } from './integrations/health/health.module';
QueueModule,
StaticModule,
HealthModule,
+ ExportModule,
StorageModule.forRootAsync({
imports: [EnvironmentModule],
}),
diff --git a/apps/server/src/collaboration/collaboration.util.ts b/apps/server/src/collaboration/collaboration.util.ts
index 2b8917f..e17f9ae 100644
--- a/apps/server/src/collaboration/collaboration.util.ts
+++ b/apps/server/src/collaboration/collaboration.util.ts
@@ -60,7 +60,7 @@ export const tiptapExtensions = [
Callout,
] as any;
-export function jsonToHtml(tiptapJson: JSONContent) {
+export function jsonToHtml(tiptapJson: any) {
return generateHTML(tiptapJson, tiptapExtensions);
}
diff --git a/apps/server/src/integrations/export/dto/export-dto.ts b/apps/server/src/integrations/export/dto/export-dto.ts
new file mode 100644
index 0000000..ae73900
--- /dev/null
+++ b/apps/server/src/integrations/export/dto/export-dto.ts
@@ -0,0 +1,26 @@
+import {
+ IsBoolean,
+ IsIn,
+ IsNotEmpty,
+ IsOptional,
+ IsString,
+} from 'class-validator';
+
+export enum ExportFormat {
+ HTML = 'html',
+ Markdown = 'markdown',
+}
+
+export class ExportPageDto {
+ @IsString()
+ @IsNotEmpty()
+ pageId: string;
+
+ @IsString()
+ @IsIn(['html', 'markdown'])
+ format: ExportFormat;
+
+ @IsOptional()
+ @IsBoolean()
+ includeFiles?: boolean;
+}
diff --git a/apps/server/src/integrations/export/export.controller.ts b/apps/server/src/integrations/export/export.controller.ts
new file mode 100644
index 0000000..dbdfe03
--- /dev/null
+++ b/apps/server/src/integrations/export/export.controller.ts
@@ -0,0 +1,69 @@
+import {
+ Body,
+ Controller,
+ ForbiddenException,
+ HttpCode,
+ HttpStatus,
+ NotFoundException,
+ Post,
+ Res,
+ UseGuards,
+} from '@nestjs/common';
+import { ExportService } from './export.service';
+import { ExportPageDto } from './dto/export-dto';
+import { AuthUser } from '../../common/decorators/auth-user.decorator';
+import { User } from '@docmost/db/types/entity.types';
+import SpaceAbilityFactory from '../../core/casl/abilities/space-ability.factory';
+import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
+import { PageRepo } from '@docmost/db/repos/page/page.repo';
+import {
+ SpaceCaslAction,
+ SpaceCaslSubject,
+} from '../../core/casl/interfaces/space-ability.type';
+import { FastifyReply } from 'fastify';
+import { sanitize } from 'sanitize-filename-ts';
+import { getExportExtension } from './utils';
+import { getMimeType } from '../../common/helpers';
+
+@Controller()
+export class ImportController {
+ constructor(
+ private readonly importService: ExportService,
+ private readonly pageRepo: PageRepo,
+ private readonly spaceAbility: SpaceAbilityFactory,
+ ) {}
+
+ @UseGuards(JwtAuthGuard)
+ @HttpCode(HttpStatus.OK)
+ @Post('pages/export')
+ async exportPage(
+ @Body() dto: ExportPageDto,
+ @AuthUser() user: User,
+ @Res() res: FastifyReply,
+ ) {
+ const page = await this.pageRepo.findById(dto.pageId, {
+ includeContent: true,
+ });
+
+ if (!page) {
+ throw new NotFoundException('Page not found');
+ }
+
+ const ability = await this.spaceAbility.createForUser(user, page.spaceId);
+ if (ability.cannot(SpaceCaslAction.Read, SpaceCaslSubject.Page)) {
+ throw new ForbiddenException();
+ }
+
+ const rawContent = await this.importService.exportPage(dto.format, page);
+
+ const fileExt = getExportExtension(dto.format);
+ const fileName = sanitize(page.title || 'Untitled') + fileExt;
+
+ res.headers({
+ 'Content-Type': getMimeType(fileExt),
+ 'Content-Disposition': 'attachment; filename="' + fileName + '"',
+ });
+
+ res.send(rawContent);
+ }
+}
diff --git a/apps/server/src/integrations/export/export.module.ts b/apps/server/src/integrations/export/export.module.ts
new file mode 100644
index 0000000..5a89a40
--- /dev/null
+++ b/apps/server/src/integrations/export/export.module.ts
@@ -0,0 +1,9 @@
+import { Module } from '@nestjs/common';
+import { ExportService } from './export.service';
+import { ImportController } from './export.controller';
+
+@Module({
+ providers: [ExportService],
+ controllers: [ImportController],
+})
+export class ExportModule {}
diff --git a/apps/server/src/integrations/export/export.service.ts b/apps/server/src/integrations/export/export.service.ts
new file mode 100644
index 0000000..76c4321
--- /dev/null
+++ b/apps/server/src/integrations/export/export.service.ts
@@ -0,0 +1,34 @@
+import { Injectable } from '@nestjs/common';
+import { jsonToHtml } from '../../collaboration/collaboration.util';
+import { turndown } from './turndown-utils';
+import { ExportFormat } from './dto/export-dto';
+import { Page } from '@docmost/db/types/entity.types';
+
+@Injectable()
+export class ExportService {
+ async exportPage(format: string, page: Page) {
+ const titleNode = {
+ type: 'heading',
+ attrs: { level: 1 },
+ content: [{ type: 'text', text: page.title }],
+ };
+
+ let prosemirrorJson: any = page.content || { type: 'doc', content: [] };
+
+ if (page.title) {
+ prosemirrorJson.content.unshift(titleNode);
+ }
+
+ const pageHtml = jsonToHtml(prosemirrorJson);
+
+ if (format === ExportFormat.HTML) {
+ return `${page.title}${pageHtml}`;
+ }
+
+ if (format === ExportFormat.Markdown) {
+ return turndown(pageHtml);
+ }
+
+ return;
+ }
+}
diff --git a/apps/server/src/integrations/export/turndown-utils.ts b/apps/server/src/integrations/export/turndown-utils.ts
new file mode 100644
index 0000000..3f72050
--- /dev/null
+++ b/apps/server/src/integrations/export/turndown-utils.ts
@@ -0,0 +1,100 @@
+import * as TurndownService from '@joplin/turndown';
+import * as TurndownPluginGfm from '@joplin/turndown-plugin-gfm';
+
+export function turndown(html: string): string {
+ const turndownService = new TurndownService({
+ headingStyle: 'atx',
+ codeBlockStyle: 'fenced',
+ hr: '---',
+ bulletListMarker: '-',
+ });
+ const tables = TurndownPluginGfm.tables;
+ const strikethrough = TurndownPluginGfm.strikethrough;
+ const highlightedCodeBlock = TurndownPluginGfm.highlightedCodeBlock;
+
+ turndownService.use([
+ tables,
+ strikethrough,
+ highlightedCodeBlock,
+ taskList,
+ callout,
+ toggleListTitle,
+ toggleListBody,
+ listParagraph,
+ ]);
+
+ return turndownService.turndown(html).replaceAll('
', ' ');
+}
+
+function listParagraph(turndownService: TurndownService) {
+ turndownService.addRule('paragraph', {
+ filter: ['p'],
+ replacement: (content: any, node: HTMLInputElement) => {
+ if (node.parentElement?.nodeName === 'LI') {
+ return content;
+ }
+
+ return `\n\n${content}\n\n`;
+ },
+ });
+}
+
+function callout(turndownService: TurndownService) {
+ turndownService.addRule('callout', {
+ filter: function (node: HTMLInputElement) {
+ return (
+ node.nodeName === 'DIV' && node.getAttribute('data-type') === 'callout'
+ );
+ },
+ replacement: function (content: any, node: HTMLInputElement) {
+ const calloutType = node.getAttribute('data-callout-type');
+ return `\n\n:::${calloutType}\n${content.trim()}\n:::\n\n`;
+ },
+ });
+}
+
+function taskList(turndownService: TurndownService) {
+ turndownService.addRule('taskListItem', {
+ filter: function (node: HTMLInputElement) {
+ return (
+ node.getAttribute('data-type') === 'taskItem' &&
+ node.parentNode.nodeName === 'UL'
+ );
+ },
+ replacement: function (content: any, node: HTMLInputElement) {
+ const checkbox = node.querySelector(
+ 'input[type="checkbox"]',
+ ) as HTMLInputElement;
+ const isChecked = checkbox.checked;
+
+ return `- ${isChecked ? '[x]' : '[ ]'} ${content.trim()} \n`;
+ },
+ });
+}
+
+function toggleListTitle(turndownService: TurndownService) {
+ turndownService.addRule('toggleListTitle', {
+ filter: function (node: HTMLInputElement) {
+ return (
+ node.nodeName === 'SUMMARY' && node.parentNode.nodeName === 'DETAILS'
+ );
+ },
+ replacement: function (content: any, node: HTMLInputElement) {
+ return '- ' + content;
+ },
+ });
+}
+
+function toggleListBody(turndownService: TurndownService) {
+ turndownService.addRule('toggleListContent', {
+ filter: function (node: HTMLInputElement) {
+ return (
+ node.getAttribute('data-type') === 'detailsContent' &&
+ node.parentNode.nodeName === 'DETAILS'
+ );
+ },
+ replacement: function (content: any, node: HTMLInputElement) {
+ return ` ${content.replace(/\n/g, '\n ')} `;
+ },
+ });
+}
diff --git a/apps/server/src/integrations/export/utils.ts b/apps/server/src/integrations/export/utils.ts
new file mode 100644
index 0000000..6a6ce70
--- /dev/null
+++ b/apps/server/src/integrations/export/utils.ts
@@ -0,0 +1,12 @@
+import { ExportFormat } from './dto/export-dto';
+
+export function getExportExtension(format: string) {
+ if (format === ExportFormat.HTML) {
+ return '.html';
+ }
+
+ if (format === ExportFormat.Markdown) {
+ return '.md';
+ }
+ return;
+}
diff --git a/package.json b/package.json
index 77f5862..9f75d4d 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,8 @@
"@hocuspocus/provider": "^2.13.5",
"@hocuspocus/server": "^2.13.5",
"@hocuspocus/transformer": "^2.13.5",
+ "@joplin/turndown": "^4.0.74",
+ "@joplin/turndown-plugin-gfm": "^1.0.56",
"@sindresorhus/slugify": "^2.2.1",
"@tiptap/core": "^2.4.0",
"@tiptap/extension-code-block": "^2.4.0",
@@ -66,8 +68,8 @@
"devDependencies": {
"@nx/js": "19.3.2",
"@types/uuid": "^10.0.0",
- "nx": "19.3.2",
"concurrently": "^8.2.2",
+ "nx": "19.3.2",
"tsx": "^4.15.7"
},
"workspaces": {
diff --git a/packages/editor-ext/src/lib/image/image.ts b/packages/editor-ext/src/lib/image/image.ts
index 63b0f1a..d3308b6 100644
--- a/packages/editor-ext/src/lib/image/image.ts
+++ b/packages/editor-ext/src/lib/image/image.ts
@@ -57,9 +57,9 @@ export const TiptapImage = Image.extend({
},
width: {
default: "100%",
- parseHTML: (element) => element.getAttribute("data-width"),
+ parseHTML: (element) => element.getAttribute("width"),
renderHTML: (attributes: ImageAttributes) => ({
- "data-width": attributes.width,
+ width: attributes.width,
}),
},
align: {
diff --git a/packages/editor-ext/src/lib/math/math-block.ts b/packages/editor-ext/src/lib/math/math-block.ts
index 61dbfe0..bc90e5f 100644
--- a/packages/editor-ext/src/lib/math/math-block.ts
+++ b/packages/editor-ext/src/lib/math/math-block.ts
@@ -56,7 +56,7 @@ export const MathBlock = Node.create({
return [
"div",
{},
- ["div", { "data-katex": true }, `$${HTMLAttributes.text}$`],
+ ["div", { "data-katex": true }, `$$${HTMLAttributes.text}$$`],
];
},
@@ -64,10 +64,6 @@ export const MathBlock = Node.create({
return ReactNodeViewRenderer(this.options.view);
},
- renderText({ node }) {
- return node.attrs.text;
- },
-
addCommands() {
return {
setMathBlock:
diff --git a/packages/editor-ext/src/lib/math/math-inline.ts b/packages/editor-ext/src/lib/math/math-inline.ts
index d34267d..24fa9d4 100644
--- a/packages/editor-ext/src/lib/math/math-inline.ts
+++ b/packages/editor-ext/src/lib/math/math-inline.ts
@@ -54,15 +54,7 @@ export const MathInline = Node.create({
},
renderHTML({ HTMLAttributes }) {
- return [
- "div",
- {},
- ["span", { "data-katex": true }, `$${HTMLAttributes.text}$`],
- ];
- },
-
- renderText({ node }) {
- return node.attrs.text;
+ return ["span", { "data-katex": true }, `$${HTMLAttributes.text}$` || {}];
},
addNodeView() {
diff --git a/packages/editor-ext/src/lib/video/video.ts b/packages/editor-ext/src/lib/video/video.ts
index f3d543c..f86659f 100644
--- a/packages/editor-ext/src/lib/video/video.ts
+++ b/packages/editor-ext/src/lib/video/video.ts
@@ -62,9 +62,9 @@ export const TiptapVideo = Node.create({
},
width: {
default: "100%",
- parseHTML: (element) => element.getAttribute("data-width"),
+ parseHTML: (element) => element.getAttribute("width"),
renderHTML: (attributes: VideoAttributes) => ({
- "data-width": attributes.width,
+ width: attributes.width,
}),
},
size: {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8822726..43b78ee 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -23,6 +23,12 @@ importers:
'@hocuspocus/transformer':
specifier: ^2.13.5
version: 2.13.5(@tiptap/pm@2.4.0)(y-prosemirror@1.2.3(prosemirror-model@1.19.4)(prosemirror-state@1.4.3)(prosemirror-view@1.32.7)(y-protocols@1.0.6(yjs@13.6.18))(yjs@13.6.18))(yjs@13.6.18)
+ '@joplin/turndown':
+ specifier: ^4.0.74
+ version: 4.0.74
+ '@joplin/turndown-plugin-gfm':
+ specifier: ^1.0.56
+ version: 1.0.56
'@sindresorhus/slugify':
specifier: ^2.2.1
version: 2.2.1
@@ -216,6 +222,9 @@ importers:
emoji-mart:
specifier: ^5.6.0
version: 5.6.0
+ file-saver:
+ specifier: ^2.0.5
+ version: 2.0.5
jotai:
specifier: ^2.8.3
version: 2.8.3(@types/react@18.3.3)(react@18.3.1)
@@ -566,6 +575,9 @@ packages:
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
engines: {node: '>=0.10.0'}
+ '@adobe/css-tools@4.3.3':
+ resolution: {integrity: sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==}
+
'@alloc/quick-lru@5.2.0':
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
@@ -2055,6 +2067,12 @@ packages:
resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ '@joplin/turndown-plugin-gfm@1.0.56':
+ resolution: {integrity: sha512-q9Pul+xfmjXNHgNgB+ksRkwcBf13X7C89CDxT4sShrh17dmGsc7AUy+GbnwlmavauMDvsdiDIG8pvGqa1L002g==}
+
+ '@joplin/turndown@4.0.74':
+ resolution: {integrity: sha512-yISsLt6wQCVtJHWf6XaSQv3hw4FxzmL8jLa7GJNZAIpFSg9cWBp9f9+tIbEwT6fzCFt1Vs9dQJSVujUYP/hTzA==}
+
'@jridgewell/gen-mapping@0.3.5':
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
engines: {node: '>=6.0.0'}
@@ -4199,6 +4217,10 @@ packages:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
engines: {node: '>= 6.0.0'}
+ agent-base@7.1.1:
+ resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==}
+ engines: {node: '>= 14'}
+
ajv-formats@2.1.1:
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
peerDependencies:
@@ -4754,12 +4776,20 @@ packages:
engines: {node: '>=4'}
hasBin: true
+ cssstyle@3.0.0:
+ resolution: {integrity: sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==}
+ engines: {node: '>=14'}
+
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
dash-get@1.0.2:
resolution: {integrity: sha512-4FbVrHDwfOASx7uQVxeiCTo7ggSdYZbqs8lH+WU6ViypPlDbe9y6IP5VVUDQBv9DcnyaiPT5XT0UWHgJ64zLeQ==}
+ data-urls@5.0.0:
+ resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
+ engines: {node: '>=18'}
+
date-fns@2.30.0:
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
engines: {node: '>=0.11'}
@@ -4780,6 +4810,9 @@ packages:
supports-color:
optional: true
+ decimal.js@10.4.3:
+ resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
+
dedent@1.5.1:
resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==}
peerDependencies:
@@ -5223,6 +5256,9 @@ packages:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
+ file-saver@2.0.5:
+ resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
+
filelist@1.0.4:
resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
@@ -5479,6 +5515,13 @@ packages:
resolution: {integrity: sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==}
engines: {node: ^16.14.0 || >=18.0.0}
+ html-encoding-sniffer@4.0.0:
+ resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
+ engines: {node: '>=18'}
+
+ html-entities@1.4.0:
+ resolution: {integrity: sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==}
+
html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
@@ -5493,10 +5536,18 @@ packages:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
+ http-proxy-agent@7.0.2:
+ resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+ engines: {node: '>= 14'}
+
https-proxy-agent@5.0.1:
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
engines: {node: '>= 6'}
+ https-proxy-agent@7.0.5:
+ resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==}
+ engines: {node: '>= 14'}
+
human-signals@2.1.0:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
@@ -5621,6 +5672,9 @@ packages:
resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
engines: {node: '>=0.10.0'}
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
@@ -5862,6 +5916,15 @@ packages:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
+ jsdom@23.0.1:
+ resolution: {integrity: sha512-2i27vgvlUsGEBO9+/kJQRbtqtm+191b5zAZrU/UezVmnC2dlDAFLgDYJvAEi94T4kjsRKkezEtLQTgsNEsW2lQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ canvas: ^2.11.2
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
jsesc@0.5.0:
resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
hasBin: true
@@ -6398,6 +6461,9 @@ packages:
npmlog@5.0.1:
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
+ nwsapi@2.2.10:
+ resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==}
+
nx@19.3.2:
resolution: {integrity: sha512-eKWs+ahkTKnq9EeWJCE4u8JLeq1cOHnq5DKoiisy2nwUg4KGy1odReegxUMLeEgNBcMI40EUtEJFiTMJSXZQeg==}
hasBin: true
@@ -6511,6 +6577,9 @@ packages:
resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==}
engines: {node: '>= 0.10'}
+ parse5@7.1.2:
+ resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
+
parseley@0.12.1:
resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==}
@@ -6862,6 +6931,9 @@ packages:
prr@1.0.1:
resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
+ psl@1.9.0:
+ resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
+
punycode.js@2.3.1:
resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
engines: {node: '>=6'}
@@ -6877,6 +6949,9 @@ packages:
resolution: {integrity: sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==}
engines: {node: '>=0.6'}
+ querystringify@2.2.0:
+ resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
+
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@@ -7120,6 +7195,9 @@ packages:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
+ requires-port@1.0.0:
+ resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
+
resolve-cwd@3.0.0:
resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
engines: {node: '>=8'}
@@ -7175,6 +7253,9 @@ packages:
rope-sequence@1.3.4:
resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==}
+ rrweb-cssom@0.6.0:
+ resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==}
+
run-async@2.4.1:
resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
engines: {node: '>=0.12.0'}
@@ -7208,6 +7289,10 @@ packages:
sax@1.4.1:
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
@@ -7488,6 +7573,9 @@ packages:
resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==}
engines: {node: '>=0.10'}
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
synckit@0.8.8:
resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -7590,9 +7678,17 @@ packages:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
+ tough-cookie@4.1.4:
+ resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
+ engines: {node: '>=6'}
+
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+ tr46@5.0.0:
+ resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
+ engines: {node: '>=18'}
+
tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
@@ -7757,6 +7853,10 @@ packages:
resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==}
engines: {node: '>=4'}
+ universalify@0.2.0:
+ resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
+ engines: {node: '>= 4.0.0'}
+
universalify@2.0.1:
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
engines: {node: '>= 10.0.0'}
@@ -7770,6 +7870,9 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+ url-parse@1.5.10:
+ resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
+
use-callback-ref@1.3.1:
resolution: {integrity: sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==}
engines: {node: '>=10'}
@@ -7886,6 +7989,10 @@ packages:
w3c-keyname@2.2.8:
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
+
walker@1.0.8:
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
@@ -7899,6 +8006,10 @@ packages:
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+ webidl-conversions@7.0.0:
+ resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
+ engines: {node: '>=12'}
+
webpack-node-externals@3.0.0:
resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==}
engines: {node: '>=6'}
@@ -7927,6 +8038,18 @@ packages:
webpack-cli:
optional: true
+ whatwg-encoding@3.1.1:
+ resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
+ engines: {node: '>=18'}
+
+ whatwg-mimetype@4.0.0:
+ resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+ engines: {node: '>=18'}
+
+ whatwg-url@14.0.0:
+ resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==}
+ engines: {node: '>=18'}
+
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
@@ -7985,6 +8108,13 @@ packages:
utf-8-validate:
optional: true
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
xmlhttprequest-ssl@2.0.0:
resolution: {integrity: sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==}
engines: {node: '>=0.4.0'}
@@ -8065,6 +8195,8 @@ snapshots:
'@aashutoshrathi/word-wrap@1.2.6': {}
+ '@adobe/css-tools@4.3.3': {}
+
'@alloc/quick-lru@5.2.0': {}
'@ampproject/remapping@2.3.0':
@@ -10216,6 +10348,19 @@ snapshots:
'@types/yargs': 17.0.32
chalk: 4.1.2
+ '@joplin/turndown-plugin-gfm@1.0.56': {}
+
+ '@joplin/turndown@4.0.74':
+ dependencies:
+ '@adobe/css-tools': 4.3.3
+ html-entities: 1.4.0
+ jsdom: 23.0.1
+ transitivePeerDependencies:
+ - bufferutil
+ - canvas
+ - supports-color
+ - utf-8-validate
+
'@jridgewell/gen-mapping@0.3.5':
dependencies:
'@jridgewell/set-array': 1.2.1
@@ -12545,6 +12690,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ agent-base@7.1.1:
+ dependencies:
+ debug: 4.3.4
+ transitivePeerDependencies:
+ - supports-color
+
ajv-formats@2.1.1(ajv@8.12.0):
optionalDependencies:
ajv: 8.12.0
@@ -13181,10 +13332,19 @@ snapshots:
cssesc@3.0.0: {}
+ cssstyle@3.0.0:
+ dependencies:
+ rrweb-cssom: 0.6.0
+
csstype@3.1.3: {}
dash-get@1.0.2: {}
+ data-urls@5.0.0:
+ dependencies:
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 14.0.0
+
date-fns@2.30.0:
dependencies:
'@babel/runtime': 7.23.7
@@ -13197,6 +13357,8 @@ snapshots:
dependencies:
ms: 2.1.2
+ decimal.js@10.4.3: {}
+
dedent@1.5.1(babel-plugin-macros@2.8.0):
optionalDependencies:
babel-plugin-macros: 2.8.0
@@ -13726,6 +13888,8 @@ snapshots:
dependencies:
flat-cache: 4.0.1
+ file-saver@2.0.5: {}
+
filelist@1.0.4:
dependencies:
minimatch: 5.1.6
@@ -13998,6 +14162,12 @@ snapshots:
dependencies:
lru-cache: 10.2.0
+ html-encoding-sniffer@4.0.0:
+ dependencies:
+ whatwg-encoding: 3.1.1
+
+ html-entities@1.4.0: {}
+
html-escaper@2.0.2: {}
html-to-text@9.0.5:
@@ -14023,6 +14193,13 @@ snapshots:
statuses: 2.0.1
toidentifier: 1.0.1
+ http-proxy-agent@7.0.2:
+ dependencies:
+ agent-base: 7.1.1
+ debug: 4.3.4
+ transitivePeerDependencies:
+ - supports-color
+
https-proxy-agent@5.0.1:
dependencies:
agent-base: 6.0.2
@@ -14030,6 +14207,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ https-proxy-agent@7.0.5:
+ dependencies:
+ agent-base: 7.1.1
+ debug: 4.3.4
+ transitivePeerDependencies:
+ - supports-color
+
human-signals@2.1.0: {}
iconv-lite@0.4.24:
@@ -14039,7 +14223,6 @@ snapshots:
iconv-lite@0.6.3:
dependencies:
safer-buffer: 2.1.2
- optional: true
ieee754@1.2.1: {}
@@ -14179,6 +14362,8 @@ snapshots:
dependencies:
isobject: 3.0.1
+ is-potential-custom-element-name@1.0.1: {}
+
is-stream@2.0.1: {}
is-unicode-supported@0.1.0: {}
@@ -14602,6 +14787,34 @@ snapshots:
dependencies:
argparse: 2.0.1
+ jsdom@23.0.1:
+ dependencies:
+ cssstyle: 3.0.0
+ data-urls: 5.0.0
+ decimal.js: 10.4.3
+ form-data: 4.0.0
+ html-encoding-sniffer: 4.0.0
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.5
+ is-potential-custom-element-name: 1.0.1
+ nwsapi: 2.2.10
+ parse5: 7.1.2
+ rrweb-cssom: 0.6.0
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 4.1.4
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 7.0.0
+ whatwg-encoding: 3.1.1
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 14.0.0
+ ws: 8.17.1
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
jsesc@0.5.0: {}
jsesc@2.5.2: {}
@@ -15075,6 +15288,8 @@ snapshots:
gauge: 3.0.2
set-blocking: 2.0.0
+ nwsapi@2.2.10: {}
+
nx@19.3.2(@swc/core@1.5.25(@swc/helpers@0.5.11)):
dependencies:
'@nrwl/tao': 19.3.2(@swc/core@1.5.25(@swc/helpers@0.5.11))
@@ -15236,6 +15451,10 @@ snapshots:
parse-node-version@1.0.1:
optional: true
+ parse5@7.1.2:
+ dependencies:
+ entities: 4.5.0
+
parseley@0.12.1:
dependencies:
leac: 0.6.0
@@ -15610,6 +15829,8 @@ snapshots:
prr@1.0.1:
optional: true
+ psl@1.9.0: {}
+
punycode.js@2.3.1: {}
punycode@2.3.1: {}
@@ -15620,6 +15841,8 @@ snapshots:
dependencies:
side-channel: 1.0.6
+ querystringify@2.2.0: {}
+
queue-microtask@1.2.3: {}
quick-format-unescaped@4.0.4: {}
@@ -15943,6 +16166,8 @@ snapshots:
require-from-string@2.0.2: {}
+ requires-port@1.0.0: {}
+
resolve-cwd@3.0.0:
dependencies:
resolve-from: 5.0.0
@@ -16003,6 +16228,8 @@ snapshots:
rope-sequence@1.3.4: {}
+ rrweb-cssom@0.6.0: {}
+
run-async@2.4.1: {}
run-async@3.0.0: {}
@@ -16032,6 +16259,10 @@ snapshots:
sax@1.4.1:
optional: true
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0
@@ -16348,6 +16579,8 @@ snapshots:
symbol-observable@4.0.0: {}
+ symbol-tree@3.2.4: {}
+
synckit@0.8.8:
dependencies:
'@pkgr/core': 0.1.1
@@ -16482,8 +16715,19 @@ snapshots:
toidentifier@1.0.1: {}
+ tough-cookie@4.1.4:
+ dependencies:
+ psl: 1.9.0
+ punycode: 2.3.1
+ universalify: 0.2.0
+ url-parse: 1.5.10
+
tr46@0.0.3: {}
+ tr46@5.0.0:
+ dependencies:
+ punycode: 2.3.1
+
tree-kill@1.2.2: {}
truncate-utf8-bytes@1.0.2:
@@ -16628,6 +16872,8 @@ snapshots:
unicode-property-aliases-ecmascript@2.1.0: {}
+ universalify@0.2.0: {}
+
universalify@2.0.1: {}
update-browserslist-db@1.0.13(browserslist@4.23.0):
@@ -16640,6 +16886,11 @@ snapshots:
dependencies:
punycode: 2.3.1
+ url-parse@1.5.10:
+ dependencies:
+ querystringify: 2.2.0
+ requires-port: 1.0.0
+
use-callback-ref@1.3.1(@types/react@18.3.3)(react@18.3.1):
dependencies:
react: 18.3.1
@@ -16716,6 +16967,10 @@ snapshots:
w3c-keyname@2.2.8: {}
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
walker@1.0.8:
dependencies:
makeerror: 1.0.12
@@ -16731,6 +16986,8 @@ snapshots:
webidl-conversions@3.0.1: {}
+ webidl-conversions@7.0.0: {}
+
webpack-node-externals@3.0.0: {}
webpack-sources@3.2.3: {}
@@ -16797,6 +17054,17 @@ snapshots:
- esbuild
- uglify-js
+ whatwg-encoding@3.1.1:
+ dependencies:
+ iconv-lite: 0.6.3
+
+ whatwg-mimetype@4.0.0: {}
+
+ whatwg-url@14.0.0:
+ dependencies:
+ tr46: 5.0.0
+ webidl-conversions: 7.0.0
+
whatwg-url@5.0.0:
dependencies:
tr46: 0.0.3
@@ -16843,6 +17111,10 @@ snapshots:
ws@8.17.1: {}
+ xml-name-validator@5.0.0: {}
+
+ xmlchars@2.2.0: {}
+
xmlhttprequest-ssl@2.0.0: {}
xtend@4.0.2: {}