diff --git a/.trae/documents/Excalidraw项目分析与本地化部署计划.md b/.trae/documents/Excalidraw项目分析与本地化部署计划.md new file mode 100644 index 0000000000..2ff6c5e2cc --- /dev/null +++ b/.trae/documents/Excalidraw项目分析与本地化部署计划.md @@ -0,0 +1,86 @@ +# Excalidraw项目分析与本地化部署计划 + +## 一、项目分析 + +### 1. 项目架构 +Excalidraw采用monorepo结构,使用yarn workspaces管理多个包,主要包含: +- **excalidraw-app**:主应用,包含完整的Excalidraw编辑器和协作功能 +- **packages/**:核心库集合 + - common:通用工具和常量 + - element:元素处理和渲染 + - excalidraw:核心React组件 + - math:数学计算 + - laser-pointer:激光笔功能 + - mermaid-to-excalidraw:Mermaid图表转换 +- **examples/**:示例应用,展示集成方式 +- **dev-docs/**:开发文档 + +### 2. 技术栈 +- 前端框架:React 19 +- 构建工具:Vite +- 状态管理:Jotai +- 协作功能:Socket.io-client +- 存储:Firebase, IndexedDB (idb-keyval) +- 国际化:i18next +- 测试:Vitest +- 代码质量:ESLint, Prettier +- 类型检查:TypeScript +- 样式:Sass +- 绘制库:Rough.js, perfect-freehand +- PWA支持:vite-plugin-pwa + +### 3. 核心功能模块 +- 绘图功能:支持多种形状、手绘、文本等 +- 协作编辑:实时多人协作 +- 导出功能:PNG, SVG, JSON等格式 +- 库功能:保存和复用元素 +- 主题支持:深色/浅色模式 +- 响应式设计:支持移动端 +- PWA:支持离线使用 + +## 二、本地化部署计划 + +### 1. 安装依赖 +```bash +yarn install +``` + +### 2. 启动开发服务器 +```bash +yarn start +``` + +### 3. 验证部署 +- 访问 http://localhost:3000 +- 确认应用正常加载 +- 测试基本绘图功能 + +### 4. 构建生产版本(可选) +```bash +yarn build +``` + +### 5. 运行生产版本(可选) +```bash +yarn start:production +``` + +## 三、功能介绍 + +部署完成后,将详细介绍以下功能: +1. 绘图工具和功能 +2. 协作编辑功能 +3. 导出和导入功能 +4. 库管理功能 +5. 主题和设置 +6. 快捷键和效率工具 + +## 四、关键技术实现 + +将深入分析以下技术点: +1. 手绘效果实现(Rough.js + perfect-freehand) +2. 实时协作机制(Socket.io) +3. 状态管理(Jotai) +4. PWA实现 +5. 元素渲染和交互 +6. 国际化支持 \ No newline at end of file diff --git a/.trae/documents/Excalidraw项目改进计划.md b/.trae/documents/Excalidraw项目改进计划.md new file mode 100644 index 0000000000..80a43786a0 --- /dev/null +++ b/.trae/documents/Excalidraw项目改进计划.md @@ -0,0 +1,87 @@ +# Excalidraw PDF导出功能改进计划 + +## 一、技术可行性分析结论 + +经过系统性分析,添加PDF导出功能在技术上是可行的,推荐使用jsPDF库将canvas转换为PDF,主要基于以下考虑: + +1. **架构兼容性**:现有架构支持添加新的导出格式,可通过扩展现有export函数实现 +2. **开发复杂度**:利用现有导出基础设施,开发复杂度最低 +3. **性能影响**:PDF生成只是导出时的一次性操作,对整体性能影响较小 +4. **风险可控**:jsPDF是成熟的PDF生成库,浏览器兼容性良好 + +## 二、改进计划 + +### 1. 安装依赖 +```bash +yarn add jspdf +``` + +### 2. 添加PDF导出功能 + +#### 2.1 在`packages/excalidraw/scene/export.ts`中添加`exportToPdf`函数 +```typescript +export const exportToPdf = async ( + elements: readonly NonDeletedExcalidrawElement[], + appState: AppState, + files: BinaryFiles, + opts: { + exportBackground: boolean; + exportPadding?: number; + viewBackgroundColor: string; + exportingFrame?: ExcalidrawFrameLikeElement | null; + } +) => { + // 首先将场景渲染到canvas + const canvas = await exportToCanvas(elements, appState, files, opts); + + // 使用jsPDF将canvas转换为PDF + const { jsPDF } = await import("jspdf"); + const pdf = new jsPDF({ + orientation: canvas.width > canvas.height ? "landscape" : "portrait", + unit: "px", + format: [canvas.width, canvas.height] + }); + + // 将canvas添加到PDF + const imgData = canvas.toDataURL("image/png"); + pdf.addImage(imgData, "PNG", 0, 0, canvas.width, canvas.height); + + return pdf; +}; +``` + +#### 2.2 更新导出对话框组件 +在`packages/excalidraw/components/ExportDialog.tsx`中添加PDF导出选项 + +#### 2.3 添加i18n字符串 +在所有语言文件中添加PDF导出相关的翻译 + +#### 2.4 更新导出功能管理器 +在`packages/excalidraw/actions/actionExport.tsx`中添加PDF导出支持 + +### 3. 测试 + +1. 单元测试:为新添加的`exportToPdf`函数编写测试用例 +2. 集成测试:测试完整的PDF导出流程 +3. 手动测试:在不同浏览器中测试PDF导出功能 + +## 三、预期效果 + +1. 用户可以在导出对话框中选择PDF格式 +2. 导出的PDF保持原有手绘风格和质量 +3. 支持深色/浅色模式导出 +4. 支持框架导出 +5. 保持与现有导出功能的一致性 + +## 四、风险控制 + +1. **库大小增加**:jsPDF库大小约为1MB,会增加包体积 +2. **浏览器兼容性**:确保在所有支持的浏览器中测试 +3. **PDF输出质量**:优化canvas到PDF的转换参数 +4. **大型场景**:添加内存使用监控和优化 + +## 五、执行时间 + +预计开发时间:2-3天 +预计测试时间:1天 +预计文档更新时间:半天 \ No newline at end of file diff --git a/.trae/documents/Fix TypeScript Error_ 'pdf' is not assignable to FILE_EXTENSION.md b/.trae/documents/Fix TypeScript Error_ 'pdf' is not assignable to FILE_EXTENSION.md new file mode 100644 index 0000000000..5c94e25943 --- /dev/null +++ b/.trae/documents/Fix TypeScript Error_ 'pdf' is not assignable to FILE_EXTENSION.md @@ -0,0 +1,24 @@ +## Problem Analysis +The TypeScript error occurs because: +1. `FILE_EXTENSION` type is defined as `Exclude` in `filesystem.ts` +2. The `fileSave` function expects `extension` parameter to be of type `FILE_EXTENSION` +3. `"pdf"` is being passed as an extension but is not a key in `MIME_TYPES` +4. However, `"pdf"` is included in `EXPORT_IMAGE_TYPES` for PDF export functionality + +## Solution Plan +1. **Update MIME_TYPES in constants.ts**: Add `pdf: "application/pdf"` to the MIME_TYPES constant +2. **Verify the fix**: Run TypeScript compilation to ensure the error is resolved +3. **Check for related issues**: Ensure all PDF export functionality works correctly +4. **Test the build**: Run the project's build process to confirm no other errors + +## Implementation Steps +1. Edit `packages/common/src/constants.ts` to add PDF MIME type +2. Run `yarn tsc` to check for TypeScript errors +3. Run `yarn build` to verify the build process +4. Test the PDF export functionality to ensure it works as expected + +## Expected Outcome +- TypeScript error resolved +- PDF export functionality works correctly +- No other errors introduced +- Type safety maintained throughout the codebase \ No newline at end of file diff --git a/packages/common/src/constants.ts b/packages/common/src/constants.ts index efacd4075f..efb11f8ff6 100644 --- a/packages/common/src/constants.ts +++ b/packages/common/src/constants.ts @@ -234,6 +234,7 @@ export const IMAGE_MIME_TYPES = { ico: "image/x-icon", avif: "image/avif", jfif: "image/jfif", + pdf: "application/pdf", } as const; export const STRING_MIME_TYPES = { @@ -268,6 +269,7 @@ export const ALLOWED_PASTE_MIME_TYPES = [ export const EXPORT_IMAGE_TYPES = { png: "png", svg: "svg", + pdf: "pdf", clipboard: "clipboard", } as const; diff --git a/packages/excalidraw/components/ImageExportDialog.scss b/packages/excalidraw/components/ImageExportDialog.scss index ea9e74f805..4709eb0168 100644 --- a/packages/excalidraw/components/ImageExportDialog.scss +++ b/packages/excalidraw/components/ImageExportDialog.scss @@ -13,6 +13,7 @@ justify-content: space-between; user-select: none; + -webkit-user-select: none; & h3 { font-family: "Assistant"; @@ -28,7 +29,7 @@ } } - & > h3 { + &>h3 { display: none; @include isMobile { @@ -61,8 +62,9 @@ } &__filename { - & > input { + &>input { margin-top: 1rem; + width: 30ch; } } @@ -75,8 +77,7 @@ justify-content: center; align-items: center; - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMUlEQVQ4T2NkYGAQYcAP3uCTZhw1gGGYhAGBZIA/nYDCgBDAm9BGDWAAJyRCgLaBCAAgXwixzAS0pgAAAABJRU5ErkJggg==") - left center; + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMUlEQVQ4T2NkYGAQYcAP3uCTZhw1gGGYhAGBZIA/nYDCgBDAm9BGDWAAJyRCgLaBCAAgXwixzAS0pgAAAABJRU5ErkJggg==") left center; border: 1px solid var(--ImageExportModal-preview-border); border-radius: 12px; @@ -84,7 +85,7 @@ overflow: hidden; padding: 1rem; - & > canvas { + &>canvas { max-width: calc(100% - 2rem); max-height: calc(100% - 2rem); @@ -172,4 +173,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/excalidraw/components/ImageExportDialog.tsx b/packages/excalidraw/components/ImageExportDialog.tsx index e8e0b70f49..b3faad99af 100644 --- a/packages/excalidraw/components/ImageExportDialog.tsx +++ b/packages/excalidraw/components/ImageExportDialog.tsx @@ -180,20 +180,26 @@ const ImageExportModal = ({
{!nativeFileSystemSupported && ( - { - setProjectName(event.target.value); - actionManager.executeAction( - actionChangeProjectName, - "ui", - event.target.value, - ); - }} - /> + <> + + { + setProjectName(event.target.value); + actionManager.executeAction( + actionChangeProjectName, + "ui", + event.target.value, + ); + }} + /> + )}
@@ -310,6 +316,18 @@ const ImageExportModal = ({ > {t("imageExportDialog.button.exportToSvg")} + + onExportImage(EXPORT_IMAGE_TYPES.pdf, exportedElements, { + exportingFrame, + }) + } + icon={downloadIcon} + > + {t("imageExportDialog.button.exportToPdf")} + {(probablySupportsClipboardBlob || isFirefox) && ( .Island { + >.Island { box-sizing: border-box; max-height: 100%; padding: 4px; @@ -292,6 +297,7 @@ body.excalidraw-cursor-resize * { --icon-fill-color: #fff; --keybinding-color: #fff; } + &.active { background-color: var(--color-primary); } @@ -311,7 +317,7 @@ body.excalidraw-cursor-resize * { pointer-events: none; - & > * { + &>* { pointer-events: var(--ui-pointerEvents); } } @@ -338,7 +344,7 @@ body.excalidraw-cursor-resize * { justify-content: center; pointer-events: none !important; - & > * { + &>* { pointer-events: var(--ui-pointerEvents); } } @@ -350,7 +356,7 @@ body.excalidraw-cursor-resize * { cursor: default; pointer-events: none !important; - & > * { + &>* { pointer-events: var(--ui-pointerEvents); } @@ -360,7 +366,7 @@ body.excalidraw-cursor-resize * { } } - .App-menu_top > *:first-child { + .App-menu_top>*:first-child { justify-self: flex-start; } @@ -372,7 +378,7 @@ body.excalidraw-cursor-resize * { } } - .App-menu_top > *:last-child { + .App-menu_top>*:last-child { justify-self: flex-end; } @@ -398,11 +404,11 @@ body.excalidraw-cursor-resize * { } } - .App-menu_bottom > *:first-child { + .App-menu_bottom>*:first-child { justify-self: flex-start; } - .App-menu_bottom > *:last-child { + .App-menu_bottom>*:last-child { justify-self: flex-end; } @@ -445,7 +451,8 @@ body.excalidraw-cursor-resize * { background-position: left 0.7rem top 50%, 0 0; } - background-size: 0.65em auto, 100%; + background-size: 0.65em auto, + 100%; &:focus { box-shadow: 0 0 0 2px var(--focus-highlight-color); @@ -533,6 +540,7 @@ body.excalidraw-cursor-resize * { aside { display: none; } + .scroll-back-to-content { bottom: calc(100px + var(--sab, 0)); z-index: -1; @@ -565,6 +573,18 @@ body.excalidraw-cursor-resize * { pointer-events: none !important; } + .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; + } + &.excalidraw--view-mode { .App-menu { display: flex; @@ -600,6 +620,7 @@ body.excalidraw-cursor-resize * { } @media print { + .App-bottom-bar, .FixedSideContainer, .layer-ui__wrapper { @@ -622,9 +643,11 @@ body.excalidraw-cursor-resize * { background: var(--scrollbar-thumb); border-radius: 10px; } + ::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover); } + ::-webkit-scrollbar-thumb:active { background: var(--scrollbar-thumb); } @@ -662,6 +685,7 @@ body.excalidraw-cursor-resize * { display: none; } } + .UserList-Wrapper { margin: 0; padding: 0; @@ -688,6 +712,7 @@ body.excalidraw-cursor-resize * { } @at-root .excalidraw.theme--dark#{&} { + .App-mobile-menu, .App-menu__left { --button-hover-bg: #363541; @@ -709,7 +734,8 @@ body.excalidraw-cursor-resize * { .excalidraw__paragraph:first-child { margin-top: 0; } - .excalidraw__paragraph + .excalidraw__paragraph { + + .excalidraw__paragraph+.excalidraw__paragraph { margin-top: 0rem; } } @@ -728,6 +754,7 @@ body.excalidraw-cursor-resize * { align-items: center; justify-content: center; user-select: text; + -webkit-user-select: text; .ErrorSplash-messageContainer { display: flex; @@ -785,7 +812,8 @@ body.excalidraw-cursor-resize * { .excalidraw__embeddable__outer { width: 100%; height: 100%; - & > * { + + &>* { border-radius: var(--embeddable-radius); } } @@ -801,4 +829,4 @@ body.excalidraw-cursor-resize * { letter-spacing: 0.6px; font-family: "Assistant"; } -} +} \ No newline at end of file diff --git a/packages/excalidraw/data/index.ts b/packages/excalidraw/data/index.ts index f655e5e8e6..36bbefe7cf 100644 --- a/packages/excalidraw/data/index.ts +++ b/packages/excalidraw/data/index.ts @@ -27,7 +27,7 @@ import { import { t } from "../i18n"; import { getSelectedElements, isSomeElementSelected } from "../scene"; -import { exportToCanvas, exportToSvg } from "../scene/export"; +import { exportToCanvas, exportToSvg, exportToPdf } from "../scene/export"; import { canvasToBlob } from "./blob"; import { fileSave } from "./filesystem"; @@ -59,12 +59,12 @@ export const prepareElementsForExport = ( let exportingFrame: ExcalidrawFrameLikeElement | null = null; let exportedElements = isExportingSelection ? getSelectedElements( - elements, - { selectedElementIds }, - { - includeBoundTextElement: true, - }, - ) + elements, + { selectedElementIds }, + { + includeBoundTextElement: true, + }, + ) : elements; if (isExportingSelection) { @@ -158,6 +158,28 @@ export const exportCanvas = async ( } return; } + } else if (type === "pdf") { + const pdfPromise = exportToPdf(elements, appState, files, { + exportBackground, + viewBackgroundColor, + exportPadding, + exportingFrame, + }); + + return fileSave( + pdfPromise.then((pdf) => { + return new Blob([pdf.output('arraybuffer')], { + type: "application/pdf", + }); + }), + { + description: "Export to PDF", + name, + extension: "pdf", + mimeTypes: ["application/pdf"], + fileHandle, + }, + ); } const tempCanvas = exportToCanvas(elements, appState, files, { diff --git a/packages/excalidraw/locales/en.json b/packages/excalidraw/locales/en.json index 233dcfa7a6..cde8a65ab2 100644 --- a/packages/excalidraw/locales/en.json +++ b/packages/excalidraw/locales/en.json @@ -480,6 +480,8 @@ }, "imageExportDialog": { "header": "Export image", + "filename": "Filename", + "filenamePlaceholder": "Enter filename...", "label": { "withBackground": "Background", "onlySelected": "Only selected", @@ -494,11 +496,13 @@ "title": { "exportToPng": "Export to PNG", "exportToSvg": "Export to SVG", + "exportToPdf": "Export to PDF", "copyPngToClipboard": "Copy PNG to clipboard" }, "button": { "exportToPng": "PNG", "exportToSvg": "SVG", + "exportToPdf": "PDF", "copyPngToClipboard": "Copy to clipboard" } }, @@ -662,4 +666,4 @@ "delete": "Delete", "mmb": "Scroll wheel" } -} +} \ No newline at end of file diff --git a/packages/excalidraw/package.json b/packages/excalidraw/package.json index 5f3d6b2684..493793d148 100644 --- a/packages/excalidraw/package.json +++ b/packages/excalidraw/package.json @@ -97,6 +97,7 @@ "image-blob-reduce": "3.0.1", "jotai": "2.11.0", "jotai-scope": "0.7.2", + "jspdf": "3.0.4", "lodash.debounce": "4.0.8", "lodash.throttle": "4.1.1", "nanoid": "3.3.3", diff --git a/packages/excalidraw/scene/export.ts b/packages/excalidraw/scene/export.ts index f2e5b32df7..22fe8e2f64 100644 --- a/packages/excalidraw/scene/export.ts +++ b/packages/excalidraw/scene/export.ts @@ -408,8 +408,7 @@ export const exportToSvg = async ( const rect = svgRoot.ownerDocument.createElementNS(SVG_NS, "rect"); rect.setAttribute( "transform", - `translate(${frame.x + offsetX} ${frame.y + offsetY}) rotate(${ - frame.angle + `translate(${frame.x + offsetX} ${frame.y + offsetY}) rotate(${frame.angle } ${cx} ${cy})`, ); rect.setAttribute("width", `${frame.width}`); @@ -483,10 +482,10 @@ export const exportToSvg = async ( canvasBackgroundColor: viewBackgroundColor, embedsValidationStatus: renderEmbeddables ? new Map( - elementsForRender - .filter((element) => isFrameLikeElement(element)) - .map((element) => [element.id, true]), - ) + elementsForRender + .filter((element) => isFrameLikeElement(element)) + .map((element) => [element.id, true]), + ) : new Map(), reuseImages: opts?.reuseImages ?? true, }, @@ -575,3 +574,42 @@ export const getExportSize = ( return [width, height]; }; + +export const exportToPdf = async ( + elements: readonly NonDeletedExcalidrawElement[], + appState: AppState, + files: BinaryFiles, + { + exportBackground, + exportPadding = DEFAULT_EXPORT_PADDING, + viewBackgroundColor, + exportingFrame, + }: { + exportBackground: boolean; + exportPadding?: number; + viewBackgroundColor: string; + exportingFrame?: ExcalidrawFrameLikeElement | null; + }, +) => { + // First render the scene to canvas + const canvas = await exportToCanvas(elements, appState, files, { + exportBackground, + exportPadding, + viewBackgroundColor, + exportingFrame, + }); + + // Use jsPDF to convert canvas to PDF + const { jsPDF } = await import("jspdf"); + const pdf = new jsPDF({ + orientation: canvas.width > canvas.height ? "landscape" : "portrait", + unit: "px", + format: [canvas.width, canvas.height], + }); + + // Add canvas to PDF + const imgData = canvas.toDataURL("image/png"); + pdf.addImage(imgData, "PNG", 0, 0, canvas.width, canvas.height); + + return pdf; +}; diff --git a/yarn.lock b/yarn.lock index edd96b0405..b8dccea439 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1010,6 +1010,11 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.28.4": + version "7.28.4" + resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326" + integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== + "@babel/template@^7.25.9", "@babel/template@^7.26.9": version "7.26.9" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" @@ -2965,6 +2970,11 @@ resolved "https://registry.yarnpkg.com/@types/pako/-/pako-2.0.3.tgz#b6993334f3af27c158f3fe0dfeeba987c578afb1" integrity sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q== +"@types/pako@^2.0.3": + version "2.0.4" + resolved "https://registry.npmmirror.com/@types/pako/-/pako-2.0.4.tgz#c3575ef8125e176c345fa0e7b301c1db41170c15" + integrity sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw== + "@types/parse-json@^4.0.0": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" @@ -2975,6 +2985,11 @@ resolved "https://registry.yarnpkg.com/@types/pica/-/pica-5.1.3.tgz#5ef64529a1f83f7d6586a8bf75a8a00be32aca02" integrity sha512-13SEyETRE5psd9bE0AmN+0M1tannde2fwHfLVaVIljkbL9V0OfFvKwCicyeDvVYLkmjQWEydbAlsDsmjrdyTOg== +"@types/raf@^3.4.0": + version "3.4.3" + resolved "https://registry.npmmirror.com/@types/raf/-/raf-3.4.3.tgz#85f1d1d17569b28b8db45e16e996407a56b0ab04" + integrity sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw== + "@types/react-dom@19.0.4": version "19.0.4" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.0.4.tgz#bedba97f9346bd4c0fe5d39e689713804ec9ac89" @@ -3009,7 +3024,7 @@ dependencies: socket.io-client "*" -"@types/trusted-types@^2.0.2": +"@types/trusted-types@^2.0.2", "@types/trusted-types@^2.0.7": version "2.0.7" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== @@ -3785,6 +3800,11 @@ base64-arraybuffer-es6@^0.7.0: resolved "https://registry.yarnpkg.com/base64-arraybuffer-es6/-/base64-arraybuffer-es6-0.7.0.tgz#dbe1e6c87b1bf1ca2875904461a7de40f21abc86" integrity sha512-ESyU/U1CFZDJUdr+neHRhNozeCv72Y7Vm0m1DCbjX3KBjT6eYocvAJlSk6+8+HkVwXlT1FNxhGW6q3UKAlCvvw== +base64-arraybuffer@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" + integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -3952,6 +3972,20 @@ canvas-roundrect-polyfill@0.0.1: resolved "https://registry.yarnpkg.com/canvas-roundrect-polyfill/-/canvas-roundrect-polyfill-0.0.1.tgz#70bf107ebe2037f26d839d7f809a26f4a95f5696" integrity sha512-yWq+R3U3jE+coOeEb3a3GgE2j/0MMiDKM/QpLb6h9ihf5fGY9UXtvK9o4vNqjWXoZz7/3EaSVU3IX53TvFFUOw== +canvg@^3.0.11: + version "3.0.11" + resolved "https://registry.npmmirror.com/canvg/-/canvg-3.0.11.tgz#4b4290a6c7fa36871fac2b14e432eff33b33cf2b" + integrity sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA== + dependencies: + "@babel/runtime" "^7.12.5" + "@types/raf" "^3.4.0" + core-js "^3.8.3" + raf "^3.4.1" + regenerator-runtime "^0.13.7" + rgbcolor "^1.0.1" + stackblur-canvas "^2.0.0" + svg-pathdata "^6.0.3" + chai@4.3.6: version "4.3.6" resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.6.tgz#ffe4ba2d9fa9d6680cc0b370adae709ec9011e9c" @@ -4202,6 +4236,11 @@ core-js@^3.4: resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.41.0.tgz#57714dafb8c751a6095d028a7428f1fb5834a776" integrity sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA== +core-js@^3.6.0, core-js@^3.8.3: + version "3.47.0" + resolved "https://registry.npmmirror.com/core-js/-/core-js-3.47.0.tgz#436ef07650e191afeb84c24481b298bd60eb4a17" + integrity sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg== + corser@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87" @@ -4268,6 +4307,13 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== +css-line-break@^2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0" + integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w== + dependencies: + utrie "^1.0.2" + css-select@^4.2.1: version "4.3.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" @@ -4841,6 +4887,13 @@ domhandler@^4.2.0, domhandler@^4.3.1: resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.1.6.tgz#43c714a94c6a7b8801850f82e756685300a027e2" integrity sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ== +dompurify@^3.2.4: + version "3.3.0" + resolved "https://registry.npmmirror.com/dompurify/-/dompurify-3.3.0.tgz#aaaadbb83d87e1c2fbb066452416359e5b62ec97" + integrity sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ== + optionalDependencies: + "@types/trusted-types" "^2.0.7" + domutils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" @@ -5617,6 +5670,15 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-png@^6.2.0: + version "6.4.0" + resolved "https://registry.npmmirror.com/fast-png/-/fast-png-6.4.0.tgz#807fc353ccab060d09151b7d082786e02d8e92d6" + integrity sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q== + dependencies: + "@types/pako" "^2.0.3" + iobuffer "^5.3.2" + pako "^2.1.0" + fast-uri@^3.0.1: version "3.0.6" resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" @@ -5648,7 +5710,7 @@ fdir@^6.4.3: resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.3.tgz#011cdacf837eca9b811c89dbb902df714273db72" integrity sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw== -fflate@^0.8.2: +fflate@^0.8.1, fflate@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== @@ -6110,6 +6172,14 @@ html-minifier-terser@^6.1.0: relateurl "^0.2.7" terser "^5.10.0" +html2canvas@^1.0.0-rc.5: + version "1.4.1" + resolved "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543" + integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA== + dependencies: + css-line-break "^2.1.0" + text-segmentation "^1.0.3" + http-parser-js@>=0.5.1: version "0.5.9" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.9.tgz#b817b3ca0edea6236225000d795378707c169cec" @@ -6278,6 +6348,11 @@ internmap@^1.0.0: resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95" integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw== +iobuffer@^5.3.2: + version "5.4.0" + resolved "https://registry.npmmirror.com/iobuffer/-/iobuffer-5.4.0.tgz#f85dff957fd0579257472f0a4cfe5ed3430e63e1" + integrity sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA== + is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" @@ -6751,6 +6826,20 @@ jsonpointer@^5.0.0: resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== +jspdf@3.0.4: + version "3.0.4" + resolved "https://registry.npmmirror.com/jspdf/-/jspdf-3.0.4.tgz#f9ad24751eaf3c8a758eccab6f621d723d4b32b6" + integrity sha512-dc6oQ8y37rRcHn316s4ngz/nOjayLF/FFxBF4V9zamQKRqXxyiH1zagkCdktdWhtoQId5K20xt1lB90XzkB+hQ== + dependencies: + "@babel/runtime" "^7.28.4" + fast-png "^6.2.0" + fflate "^0.8.1" + optionalDependencies: + canvg "^3.0.11" + core-js "^3.6.0" + dompurify "^3.2.4" + html2canvas "^1.0.0-rc.5" + "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: version "3.3.5" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" @@ -7678,6 +7767,11 @@ pako@2.0.3: resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.3.tgz#cdf475e31b678565251406de9e759196a0ea7a43" integrity sha512-WjR1hOeg+kki3ZIOjaf4b5WVcay1jaliKSYiEaB1XzwhMQZJxRdQRv0V31EKBYlxb4T7SK3hjfc/jxyU64BoSw== +pako@^2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" + integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== + param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" @@ -7801,6 +7895,11 @@ perfect-freehand@1.2.0: resolved "https://registry.yarnpkg.com/perfect-freehand/-/perfect-freehand-1.2.0.tgz#706a0f854544f6175772440c51d3b0563eb3988a" integrity sha512-h/0ikF1M3phW7CwpZ5MMvKnfpHficWoOEyr//KVNTxV4F6deRK1eYMtHyBKEAKFK0aXIEUK9oBvlF6PNXMDsAw== +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== + pica@7.1.1, pica@^7.1.0: version "7.1.1" resolved "https://registry.yarnpkg.com/pica/-/pica-7.1.1.tgz#c68b42f5cfa6cc26eaec5cfa10cc0a5299ef3b7a" @@ -8056,6 +8155,13 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +raf@^3.4.1: + version "3.4.1" + resolved "https://registry.npmmirror.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" + integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== + dependencies: + performance-now "^2.1.0" + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -8195,6 +8301,11 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== +regenerator-runtime@^0.13.7: + version "0.13.11" + resolved "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + regenerator-runtime@^0.14.0: version "0.14.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" @@ -8316,6 +8427,11 @@ rfdc@^1.3.0: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== +rgbcolor@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/rgbcolor/-/rgbcolor-1.0.1.tgz#d6505ecdb304a6595da26fa4b43307306775945d" + integrity sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw== + rimraf@3.0.2, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -8771,6 +8887,11 @@ stackback@0.0.2: resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== +stackblur-canvas@^2.0.0: + version "2.7.0" + resolved "https://registry.npmmirror.com/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz#af931277d0b5096df55e1f91c530043e066989b6" + integrity sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ== + std-env@^3.8.0: version "3.8.0" resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5" @@ -8991,6 +9112,11 @@ svg-parser@^2.0.4: resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== +svg-pathdata@^6.0.3: + version "6.0.3" + resolved "https://registry.npmmirror.com/svg-pathdata/-/svg-pathdata-6.0.3.tgz#80b0e0283b652ccbafb69ad4f8f73e8d3fbf2cac" + integrity sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw== + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -9078,6 +9204,13 @@ test-exclude@^7.0.1: glob "^10.4.1" minimatch "^9.0.4" +text-segmentation@^1.0.3: + version "1.0.3" + resolved "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943" + integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw== + dependencies: + utrie "^1.0.2" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -9454,6 +9587,13 @@ util-deprecate@^1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +utrie@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645" + integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw== + dependencies: + base64-arraybuffer "^1.0.2" + uuid@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"