From 78f4caa3bf3550a5e5615307eb3b682a6e739b1b Mon Sep 17 00:00:00 2001 From: 202304001 <941456317@qq.com> Date: Thu, 3 Apr 2025 18:03:07 +0800 Subject: [PATCH] AI写作工具栏功能、写作提示词修改、pdf/word文件内容上传读取 --- app/components/artifacts.tsx | 7 +++++-- app/components/chat.tsx | 73 +++++++++++++++++++++++++++++++++++++++++++++++++------------------------ app/components/writing/write-siderBar.tsx | 14 +++++++++++++- app/components/writing/writie-panel.tsx | 45 ++++++++++++++++++++++++++++++++++----------- app/components/writing/writing.module.scss | 7 +++++++ app/components/writing/writing.tsx | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------------------------------------------------------------------- app/icons/HTML.svg | 1 + app/locales/cn.ts | 2 ++ app/store/index.ts | 2 +- app/store/message.ts | 36 ------------------------------------ app/store/mindMap.ts | 36 ++++++++++++++++++++++++++++++++++++ app/utils/excelAndWordUtils/export2Excel.ts | 129 --------------------------------------------------------------------------------------------------------------------------------- app/utils/excelAndWordUtils/word.ts | 72 ------------------------------------------------------------------------ app/utils/fileExport/export2Excel.ts | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/utils/fileExport/toPdf.ts | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/utils/fileExport/word.ts | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ app/utils/prompt.ts | 67 ++++++++++++++++++++++++++++++++++++++++--------------------------- package.json | 4 ++++ yarn.lock | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 19 files changed, 1027 insertions(+), 396 deletions(-) create mode 100644 app/icons/HTML.svg delete mode 100644 app/store/message.ts create mode 100644 app/store/mindMap.ts delete mode 100644 app/utils/excelAndWordUtils/export2Excel.ts delete mode 100644 app/utils/excelAndWordUtils/word.ts create mode 100644 app/utils/fileExport/export2Excel.ts create mode 100644 app/utils/fileExport/toPdf.ts create mode 100644 app/utils/fileExport/word.ts diff --git a/app/components/artifacts.tsx b/app/components/artifacts.tsx index ce187fb..00536bf 100644 --- a/app/components/artifacts.tsx +++ b/app/components/artifacts.tsx @@ -27,6 +27,7 @@ type HTMLPreviewProps = { autoHeight?: boolean; height?: number | string; onLoad?: (title?: string) => void; + width?: number | string; //20250403 新增宽度配置 }; export type HTMLPreviewHander = { @@ -78,6 +79,8 @@ export const HTMLPreview = forwardRef<HTMLPreviewHander, HTMLPreviewProps>( : iframeHeight + 40; }, [props.autoHeight, props.height, iframeHeight]); + const width = props.width || "100%"; //20250403 新增宽度配置 + const srcDoc = useMemo(() => { const script = `<script>window.addEventListener("DOMContentLoaded", () => new ResizeObserver((entries) => parent.postMessage({id: '${frameId}', height: entries[0].target.clientHeight}, '*')).observe(document.body))</script>`; if (props.code.includes("<!DOCTYPE html>")) { @@ -98,7 +101,7 @@ export const HTMLPreview = forwardRef<HTMLPreviewHander, HTMLPreviewProps>( key={frameId} ref={iframeRef} sandbox="allow-forms allow-modals allow-scripts" - style={{ height }} + style={{ height, width }} srcDoc={srcDoc} onLoad={handleOnLoad} /> @@ -253,7 +256,7 @@ export function Artifacts() { code={code} ref={previewRef} autoHeight={false} - height={"100%"} + height={"100%"} //20250403 新增宽度配置 onLoad={(title) => { setFileName(title as string); setLoading(false); diff --git a/app/components/chat.tsx b/app/components/chat.tsx index ea54050..4c20051 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -136,10 +136,11 @@ import clsx from "clsx"; import { getAvailableClientsCount, isMcpEnabled } from "../mcp/actions"; //20250317新增 -import { getExcelData, toExcel } from "../utils/excelAndWordUtils/export2Excel"; -import { exportWord } from "../utils/excelAndWordUtils/word"; +import { getExcelData, toExcel } from "../utils/fileExport/export2Excel"; +import { exportWord, getWordData } from "../utils/fileExport/word"; import { getMindPrompt } from "../utils/prompt"; import { message } from "antd"; +import { getPdfData } from "../utils/fileExport/toPdf"; const localStorage = safeLocalStorage(); const ttsPlayer = createTTSPlayer(); @@ -1774,33 +1775,57 @@ function _Chat() { const [fileName, setFileName] = useState(""); async function uploadFile() { - // 创建一个隐藏的文件输入框 const fileInput = document.createElement("input"); fileInput.type = "file"; - fileInput.accept = ".xlsx, .xls"; + fileInput.accept = ".xlsx, .xls, .pdf, .docx, .doc"; fileInput.multiple = false; - fileInput.onchange = (event: Event) => { - const target = event.target as HTMLInputElement; - const files = target.files; - if (files && files.length > 0) { - const file = files[0]; // 获取第一个文件 - getExcelData(file) - .then((data) => { - const value = `'''filedata - ${file.name} - ${JSON.stringify(data)} - '''filedata - `; - setFileData(value); - setFileName(file.name); - console.log(value); - }) - .catch((error) => { - message.error("上传失败"); - }); + fileInput.style.display = "none"; + const handleFileResult = (fileName: string, data: any) => { + if (!data) { + message.error("未读取到内容"); + return; + } + setFileData(`'''filedata + ${fileName} + ${JSON.stringify(data)} + '''filedata`); + setFileName(fileName); + }; + const handleError = (error: any, defaultMsg = "上传失败") => { + console.error(`${defaultMsg}:`, error); + message.error(defaultMsg); + }; + // 文件处理器映射 + const fileHandlers: Record<string, (file: File) => Promise<any>> = { + ".xlsx": getExcelData, + ".xls": getExcelData, + ".doc": getWordData, + ".docx": getWordData, + ".pdf": getPdfData, + }; + fileInput.onchange = async (event: Event) => { + try { + const file = (event.target as HTMLInputElement).files?.[0]; + if (!file) return; + const fileExt = file.name + .toLowerCase() + .slice(file.name.lastIndexOf(".")); + // 校验文件类型 + if (!Object.keys(fileHandlers).includes(fileExt)) { + message.error("不支持的文件类型"); + return; + } + // 获取处理器并执行 + const handler = fileHandlers[fileExt]; + const data = await handler(file); + handleFileResult(file.name, data); + } catch (error) { + handleError(error); + } finally { + fileInput.remove(); } - fileInput.remove(); }; + document.body.appendChild(fileInput); fileInput.click(); } return ( diff --git a/app/components/writing/write-siderBar.tsx b/app/components/writing/write-siderBar.tsx index 95959da..523ea1e 100644 --- a/app/components/writing/write-siderBar.tsx +++ b/app/components/writing/write-siderBar.tsx @@ -21,6 +21,8 @@ export interface WriteSiderBarProps { setHtmlCode: React.Dispatch<React.SetStateAction<string>>; loading: boolean; setLoading: React.Dispatch<React.SetStateAction<boolean>>; + setWidth: React.Dispatch<React.SetStateAction<string>>; + setHtmlheader: React.Dispatch<React.SetStateAction<string>>; } const WritingPanel = dynamic( @@ -31,7 +33,15 @@ const WritingPanel = dynamic( ); export function WriteSiderBar(props: WriteSiderBarProps) { - const { className, htmlCode, setHtmlCode, loading, setLoading } = props; + const { + className, + htmlCode, + setHtmlCode, + loading, + setLoading, + setWidth, + setHtmlheader, + } = props; const isMobileScreen = useMobileScreen(); const { onDragStart, shouldNarrow } = useDragSideBar(); const navigate = useNavigate(); @@ -93,6 +103,8 @@ export function WriteSiderBar(props: WriteSiderBarProps) { setHtmlCode={setHtmlCode} loading={loading} setLoading={setLoading} + setWidth={setWidth} + setHtmlheader={setHtmlheader} /> </SideBarBody> </SideBarContainer> diff --git a/app/components/writing/writie-panel.tsx b/app/components/writing/writie-panel.tsx index 81bc449..33b7127 100644 --- a/app/components/writing/writie-panel.tsx +++ b/app/components/writing/writie-panel.tsx @@ -16,11 +16,10 @@ const mergedData = [ type: "select", default: "公司官网", options: [ - { name: "公司官网", value: "1" }, - { name: "小红书", value: "2" }, - { name: "微信", value: "3" }, - { name: "公众号", value: "4" }, - { name: "今日头条", value: "5" }, + { name: "公司官网", value: "100%" }, + { name: "小红书", value: "400px" }, + { name: "微信公众号", value: "300px" }, + { name: "今日头条", value: "500px" }, ], }, { @@ -62,10 +61,10 @@ const mergedData = [ required: false, default: "产品推广文案", options: [ - { name: "产品推广文案", value: "产品推广文案" }, - { name: "品牌宣传文案", value: "品牌宣传文案" }, - { name: "产品说明书", value: "产品说明书" }, - { name: "产品介绍", value: "产品介绍" }, + { name: "产品推广文案", value: "promotion" }, + { name: "品牌宣传文案", value: "propagandize" }, + { name: "产品说明书", value: "instructionBook" }, + { name: "产品介绍", value: "introduce" }, ], }, { @@ -85,9 +84,18 @@ export interface WritePanelProps { setHtmlCode: React.Dispatch<React.SetStateAction<string>>; loading: boolean; setLoading: React.Dispatch<React.SetStateAction<boolean>>; + setWidth: React.Dispatch<React.SetStateAction<string>>; + setHtmlheader: React.Dispatch<React.SetStateAction<string>>; } export function WritingPanel(props: WritePanelProps) { - const { htmlCode, setHtmlCode, setLoading, loading } = props; + const { + htmlCode, + setHtmlCode, + setLoading, + loading, + setWidth, + setHtmlheader, + } = props; // 为每个选择框单独声明状态,存储name const chatStore = useChatStore(); const [writingPurposeName, setWritingPurposeName] = useState("公司官网"); // 写作用途 @@ -104,10 +112,12 @@ export function WritingPanel(props: WritePanelProps) { const handleSelectChange = (index: number, value: string) => { const options = mergedData[index].options; const selectedName = options.find((opt) => opt.value === value)?.name || ""; - + const selectedValue = + options.find((opt) => opt.value === value)?.value || ""; switch (index) { case 0: setWritingPurposeName(selectedName); + setWidth(selectedValue); break; case 1: setImageModeName(selectedName); @@ -164,6 +174,19 @@ export function WritingPanel(props: WritePanelProps) { if (cleanedContent.endsWith("```")) { cleanedContent = cleanedContent.substring(0, cleanedContent.length - 4); } + + //保存html头部 + const bodyTagRegex = /<body[^>]*>/i; + const bodyTagMatch = cleanedContent.match(bodyTagRegex); + if (bodyTagMatch && bodyTagMatch.index !== undefined) { + // 截取从文档开头到 <body> 标签的起始位置 + const contentUpToBody = cleanedContent.slice( + 0, + bodyTagMatch.index + bodyTagMatch[0].length, + ); + setHtmlheader(contentUpToBody); //保存html头部 + } + localStorage.setItem("htmlCode", cleanedContent); setHtmlCode(cleanedContent); } catch (error) { message.error("生成失败,请重试"); diff --git a/app/components/writing/writing.module.scss b/app/components/writing/writing.module.scss index c1d7e8d..ab16e9f 100644 --- a/app/components/writing/writing.module.scss +++ b/app/components/writing/writing.module.scss @@ -1,3 +1,10 @@ +.write-body{ + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + .loading-content { display: flex; flex-direction: column; diff --git a/app/components/writing/writing.tsx b/app/components/writing/writing.tsx index c497085..79bc3d9 100644 --- a/app/components/writing/writing.tsx +++ b/app/components/writing/writing.tsx @@ -11,12 +11,11 @@ import Locale from "@/app/locales"; import { Path } from "@/app/constant"; import { useNavigate } from "react-router-dom"; import { getClientConfig } from "@/app/config/client"; -import React, { useMemo, useRef, useState } from "react"; -import { useAppConfig } from "@/app/store"; +import React, { useCallback, useMemo, useRef, useState } from "react"; +import { useAppConfig, useMindMapStore } from "@/app/store"; import { ChatAction } from "../chat"; import { useWindowSize } from "@/app/utils"; -import { exportWord } from "@/app/utils/excelAndWordUtils/word"; -import { HTMLPreview } from "../artifacts"; +import { exportHtmlToWord } from "@/app/utils/fileExport/word"; import ReactQuill from "react-quill"; import "react-quill/dist/quill.snow.css"; @@ -34,7 +33,13 @@ import WordIcon from "@/app/icons/word.svg"; import MindIcon from "@/app/icons/mind.svg"; import PptIcon from "@/app/icons/ppt.svg"; import PdfIcon from "@/app/icons/pdf.svg"; +import HtmlIcon from "@/app/icons/HTML.svg"; + import { message } from "antd"; +import { HTMLPreview } from "../artifacts"; +import { getMindPrompt } from "@/app/utils/prompt"; +import { htmlToPdf2 } from "@/app/utils/fileExport/toPdf"; +import { htmlToExcel } from "@/app/utils/fileExport/export2Excel"; export function WritingPage() { const isMobileScreen = useMobileScreen(); @@ -45,10 +50,14 @@ export function WritingPage() { const scrollRef = useRef<HTMLDivElement>(null); const isWriting = location.pathname === Path.Writing; const { height } = useWindowSize(); + const [width, setWidth] = useState("100%"); const [isEdit, setIsEdit] = useState(false); const [loading, setLoading] = useState(false); const quillRef = useRef<ReactQuill | null>(null); - const [htmlCode, setHtmlCode] = useState(""); + const [htmlHeader, setHtmlheader] = useState(""); + const [htmlCode, setHtmlCode] = useState( + localStorage.getItem("htmlCode") || "", + ); //编辑器 const toolbarOptions = [ @@ -60,42 +69,66 @@ export function WritingPage() { ["link", "image"], ]; - const copyToClipboard = () => { - // 检查quillRef.current是否为null - if (quillRef.current) { - const editor = quillRef.current.getEditor(); // 获取编辑器实例 - const range = editor.getSelection(); // 获取当前选择的范围 - const text = editor.getText(); // 获取编辑器中的文本内容 + // 生成完整HTML内容 + const generateFullHtml = useCallback( + () => `${htmlHeader}${htmlCode}</body></html>`, + [htmlHeader, htmlCode], + ); - if (range && range.length > 0) { - // 如果有选中的文本,复制选中的内容 - const selectedText = text.substring( - range.index, - range.index + range.length, - ); - navigator.clipboard - .writeText(selectedText) - .then(() => { - message.success("复制成功"); - }) - .catch((err) => { - message.error("复制失败:"); - }); - } else { - // 如果没有选择文本,就复制全部内容 - navigator.clipboard - .writeText(text) - .then(() => { - message.success("复制成功"); - }) - .catch((err) => { - message.error("复制失败:"); - }); - } - } else { - console.error("Quill编辑器尚未初始化"); + const handleCopy = async () => { + try { + const blob = new Blob([htmlCode], { type: "text/html" }); + const clipboardItem = new ClipboardItem({ "text/html": blob }); + await navigator.clipboard.write([clipboardItem]); + message.success("复制成功!"); + } catch (error) { + message.error("复制失败"); } }; + //跳转到ppt页面 + function toPowerpoint(pptMessage: string) { + navigate("/powerpoint", { state: { msg: true, pptMessage: pptMessage } }); + } + // 转至思维导图页面 + const toMind = useCallback( + (content: string) => { + const { setMindMapData } = useMindMapStore.getState(); + setMindMapData( + [ + { + role: "user", + content: getMindPrompt(content, true), + }, + ], + content, + ); + navigate("/mind", { state: { msg: true } }); + }, + [navigate], + ); + + //导出html文件 + const exportHtml = useCallback(() => { + try { + const htmlContent = generateFullHtml(); + const blob = new Blob([htmlContent], { type: "text/html" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = "output.html"; + a.click(); + URL.revokeObjectURL(url); + message.success("导出成功"); + } catch (error) { + message.error("导出失败"); + } + }, [generateFullHtml]); + + function hasTable(htmlContent: string): boolean { + const parser = new DOMParser(); + const doc = parser.parseFromString(htmlContent, "text/html"); + return doc.querySelector("table") !== null; + } return ( <> @@ -105,6 +138,8 @@ export function WritingPage() { setHtmlCode={setHtmlCode} loading={loading} setLoading={setLoading} + setWidth={setWidth} + setHtmlheader={setHtmlheader} /> <WindowContent> <div className={chatStyles.chat} key={"1"}> @@ -130,66 +165,97 @@ export function WritingPage() { <div className={`window-header-main-title`}>AI-Writing</div> </div> <div className={chatStyles["chat-message-actions"]}> - <div className={chatStyles["chat-input-actions"]}> - <ChatAction - text={Locale.Chat.Actions.ReWrite} - icon={<ReloadIcon />} - onClick={() => {}} - /> - <ChatAction - text={Locale.Chat.Actions.Copy} - icon={<CopyIcon />} - onClick={copyToClipboard} - /> - {!isEdit ? ( + {htmlCode && ( + <div className={chatStyles["chat-input-actions"]}> <ChatAction - text={Locale.Chat.Actions.Edit} - icon={<EditIcon />} - onClick={() => { - setIsEdit(true); + text={Locale.Chat.Actions.ReWrite} + icon={<ReloadIcon />} + onClick={() => {}} + disabled={isEdit} + /> + <ChatAction + text={Locale.Chat.Actions.Copy} + icon={<CopyIcon />} + onClick={handleCopy} + disabled={isEdit} + /> + {!isEdit ? ( + <ChatAction + text={Locale.Chat.Actions.Edit} + icon={<EditIcon />} + onClick={() => { + setIsEdit(true); + }} + /> + ) : ( + <ChatAction + text={Locale.Chat.Actions.CancelEdit} + icon={<EditIcon />} + onClick={() => { + setIsEdit(false); + }} + /> + )} + <ChatAction + text={Locale.Export.Pdf} + icon={<PdfIcon />} + onClick={async () => { + setLoading(true); + await htmlToPdf2(htmlCode); + setLoading(false); }} + disabled={isEdit} /> - ) : ( <ChatAction - text="取消编辑" - icon={<EditIcon />} + text={Locale.Export.Word} + icon={<WordIcon />} onClick={() => { - setIsEdit(false); + const html = `${htmlHeader} + ${htmlCode} + </body> + </html> + `; + exportHtmlToWord(html); }} + disabled={isEdit} /> - )} + {hasTable(htmlCode) && ( + <ChatAction + text={Locale.Export.Excel} + icon={<ExcelIcon />} + onClick={() => { + htmlToExcel(htmlCode); + }} + disabled={isEdit} + /> + )} - <ChatAction - text={Locale.Export.Pdf} - icon={<PdfIcon />} - onClick={() => {}} - /> - {htmlCode && ( <ChatAction - text={Locale.Export.Word} - icon={<WordIcon />} + text={Locale.Export.Ppt} + icon={<PptIcon />} onClick={() => { - exportWord(htmlCode); + toPowerpoint(htmlCode); }} + disabled={isEdit} /> - )} - <ChatAction - text={Locale.Export.Excel} - icon={<ExcelIcon />} - onClick={() => {}} - disabled={true} - /> - <ChatAction - text={Locale.Export.Ppt} - icon={<PptIcon />} - onClick={() => {}} - /> - <ChatAction - text={Locale.Export.Mind.title} - icon={<MindIcon />} - onClick={() => {}} - /> - </div> + <ChatAction + text={Locale.Export.Mind.title} + icon={<MindIcon />} + onClick={() => { + toMind(htmlCode); + }} + disabled={isEdit} + /> + <ChatAction + text={Locale.Export.Html} + icon={<HtmlIcon />} + onClick={() => { + exportHtml(); + }} + disabled={isEdit} + /> + </div> + )} </div> <div className="window-actions"> {showMaxIcon && ( @@ -209,7 +275,10 @@ export function WritingPage() { {isMobileScreen && <SDIcon width={50} height={50} />} </div> </div> - <div className={chatStyles["chat-body"]} ref={scrollRef}> + <div + className={`${chatStyles["chat-body"]} ${styles["write-body"]}`} + ref={scrollRef} + > {loading ? ( <div className={clsx("no-dark", styles["loading-content"])}> <BotIcon /> @@ -231,7 +300,8 @@ export function WritingPage() { <HTMLPreview code={htmlCode} autoHeight={!document.fullscreenElement} - height={!document.fullscreenElement ? 600 : height} + height={!document.fullscreenElement ? "100%" : height} + width={width} /> )) )} diff --git a/app/icons/HTML.svg b/app/icons/HTML.svg new file mode 100644 index 0000000..e414e44 --- /dev/null +++ b/app/icons/HTML.svg @@ -0,0 +1 @@ +<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1743667735062" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4363" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M354.40128 0c-87.04 0-157.44 70.55872-157.44 157.59872v275.68128H78.72c-21.6576 0-39.36256 17.69984-39.36256 39.36256v236.31872c0 21.6576 17.69984 39.35744 39.36256 39.35744h118.24128v118.08256c0 87.04 70.4 157.59872 157.44 157.59872h472.63744c87.04 0 157.59872-70.55872 157.59872-157.59872V315.0336c0-41.74848-38.9888-81.93024-107.52-149.27872l-29.11744-29.12256L818.87744 107.52C751.5392 38.9888 711.39328 0 669.59872 0H354.4064z m0 78.72h287.20128c28.35456 7.0912 27.99616 42.1376 27.99616 76.8v120.16128c0 21.6576 17.69984 39.35744 39.36256 39.35744h118.07744c39.38816 0 78.87872-0.0256 78.87872 39.36256v512c0 43.32032-35.55328 78.87872-78.87872 78.87872H354.4064c-43.32544 0-78.72-35.5584-78.72-78.87872v-118.08256h393.91744c21.66272 0 39.36256-17.69472 39.36256-39.35744V472.64256c0-21.66272-17.69984-39.36256-39.36256-39.36256H275.68128V157.59872c0-43.32032 35.39456-78.87872 78.72-78.87872zM75.03872 493.59872h22.08256v73.92256H176.7936V493.59872h23.04v175.68256h-23.04V587.6736H97.1264v81.60256h-22.08256V493.59872z m151.68 0h121.92256v20.16256h-49.92v155.52H276.6336v-155.52h-49.92v-20.16256z m148.80256 0h32.64l49.92 143.04256h0.95744l48.96256-143.04256h33.59744v175.68256h-22.07744v-106.56256c0-10.88 0.31744-26.55744 0.95744-47.03744h-0.95744l-52.80256 153.6h-19.2l-52.79744-153.6h-0.96256c1.28 22.4 1.92 38.71744 1.92 48.95744v104.64256h-20.15744V493.59872z m214.07744 0h22.08256v155.52h69.12v20.16256h-91.20256V493.59872z" p-id="4364"></path></svg> \ No newline at end of file diff --git a/app/locales/cn.ts b/app/locales/cn.ts index dd48ed8..afae074 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -53,6 +53,7 @@ const cn = { PinToastAction: "查看", Delete: "删除", Edit: "编辑", + CancelEdit: "取消编辑", FullScreen: "全屏", RefreshTitle: "刷新标题", RefreshToast: "已发送刷新标题请求", @@ -148,6 +149,7 @@ const cn = { Excel: "下载Excel", Pdf: "导出PDF", Ppt: "导出PPT", + Html: "导出HTML", }, Select: { Search: "搜索消息", diff --git a/app/store/index.ts b/app/store/index.ts index fb82fc1..8dffb0c 100644 --- a/app/store/index.ts +++ b/app/store/index.ts @@ -3,4 +3,4 @@ export * from "./update"; export * from "./access"; export * from "./config"; export * from "./plugin"; -export * from "./message"; +export * from "./mindMap"; diff --git a/app/store/message.ts b/app/store/message.ts deleted file mode 100644 index 23f2a02..0000000 --- a/app/store/message.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { createPersistStore } from "../utils/store"; - -export const useMindMapStore = createPersistStore< - { newMessages: { role: string; content: string }[]; content: string }, - { - setMindMapData: ( - newMessages: { role: string; content: string }[], - content: string, - ) => void; - clearMindMapData: () => void; - } ->( - { - newMessages: [], - content: "", - }, - (set, get) => ({ - setMindMapData: (newMessages, content) => { - set(() => ({ - newMessages, - content, - })); - }, - - clearMindMapData: () => { - set(() => ({ - newMessages: [], - content: "", - })); - }, - }), - { - name: "mind-map-store", - version: 1, - }, -); diff --git a/app/store/mindMap.ts b/app/store/mindMap.ts new file mode 100644 index 0000000..23f2a02 --- /dev/null +++ b/app/store/mindMap.ts @@ -0,0 +1,36 @@ +import { createPersistStore } from "../utils/store"; + +export const useMindMapStore = createPersistStore< + { newMessages: { role: string; content: string }[]; content: string }, + { + setMindMapData: ( + newMessages: { role: string; content: string }[], + content: string, + ) => void; + clearMindMapData: () => void; + } +>( + { + newMessages: [], + content: "", + }, + (set, get) => ({ + setMindMapData: (newMessages, content) => { + set(() => ({ + newMessages, + content, + })); + }, + + clearMindMapData: () => { + set(() => ({ + newMessages: [], + content: "", + })); + }, + }), + { + name: "mind-map-store", + version: 1, + }, +); diff --git a/app/utils/excelAndWordUtils/export2Excel.ts b/app/utils/excelAndWordUtils/export2Excel.ts deleted file mode 100644 index 8bae2a4..0000000 --- a/app/utils/excelAndWordUtils/export2Excel.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* eslint-disable */ -import * as XLSX from "xlsx"; - -export function toExcel(content: string) { - let sheetName = "result"; // 默认表名 - let tableContent = ""; - - // 查找标题内容(以 ** 或 ## 或 ### 开头) - const titleMatch = content.match(/^(#{2,3}|\*\*)\s*(.*)/m); - if (titleMatch) { - sheetName = titleMatch[2].trim().replace(/\s+/g, "_"); // 使用标题作为表名 - // 提取表格内容(跳过标题部分) - tableContent = content.substring(titleMatch[0].length).trim(); - } else { - tableContent = content; - } - - // 查找表格的起始位置(第一行以 | 开头且以 | 结尾) - let tableStartIndex = -1; - const lines = tableContent.split("\n"); - for (let i = 0; i < lines.length; i++) { - if (lines[i].trim().startsWith("|") && lines[i].trim().endsWith("|")) { - tableStartIndex = i; - break; - } - } - - if (tableStartIndex === -1) { - console.error("表格内容未找到"); - return; - } - - // 查找表格的结束位置(遇到不以 | 开头或者不以 | 结尾的行) - let tableEndIndex = -1; - for (let i = tableStartIndex; i < lines.length; i++) { - if (!lines[i].trim().startsWith("|") || !lines[i].trim().endsWith("|")) { - tableEndIndex = i; - break; - } - } - - if (tableEndIndex === -1) { - tableEndIndex = lines.length; - } - - // 提取表格内容 - const tableData = lines - .slice(tableStartIndex, tableEndIndex) - .join("\n") - .trim(); - - // 解析表格内容 - const rows = tableData.split("\n"); - const data: any[] = []; - let headers: string[] = []; - - rows.forEach((row, rowIndex) => { - // 去掉行首尾的 | 符号,并按 | 分割 - const cells = row - .replace(/^\s*\|/g, "") - .replace(/\|\s*$/g, "") - .split(/\s*\|\s*/); - - // 跳过分隔线(假设分隔线的每个单元格都是由短横线组成) - if ( - rowIndex > 0 && - cells.every( - (cell) => cell.trim().replace(/-/g, "").length === 0, // 检查单元格内容是否只包含短横线 - ) - ) { - return; - } - - if (rowIndex === 0) { - // 第一行是表头 - headers = cells.map((cell) => cell.trim()); - } else { - // 数据行 - const rowData: any = {}; - cells.forEach((cell, cellIndex) => { - if (cellIndex < headers.length) { - rowData[headers[cellIndex]] = cell.trim(); - } - }); - data.push(rowData); - } - }); - - // 创建工作表 - const ws = XLSX.utils.json_to_sheet(data); - const wb = XLSX.utils.book_new(); - XLSX.utils.book_append_sheet(wb, ws, sheetName); - - // 生成文件并下载 - XLSX.writeFile(wb, `${sheetName}.xlsx`); -} - -export function getExcelData(file: File): Promise<any[][]> { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - - reader.onload = (e) => { - try { - const data = e.target?.result; - if (!data) { - reject(new Error("Failed to read file data")); - return; - } - - const workbook = XLSX.read(data, { type: "array" }); - const firstSheetName = workbook.SheetNames[0]; - const worksheet = workbook.Sheets[firstSheetName]; - - // 使用类型断言将 unknown[] 转换为 any[][] - const jsonData = XLSX.utils.sheet_to_json(worksheet, { - header: 1, - }) as any[][]; - resolve(jsonData.slice(0, 100)); - } catch (error) { - reject(error); - } - }; - reader.onerror = (error) => { - reject(error); - }; - - reader.readAsArrayBuffer(file); - }); -} diff --git a/app/utils/excelAndWordUtils/word.ts b/app/utils/excelAndWordUtils/word.ts deleted file mode 100644 index 3f293bb..0000000 --- a/app/utils/excelAndWordUtils/word.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Document, Packer, Paragraph, TextRun } from "docx"; -import { saveAs } from "file-saver"; -import * as cheerio from "cheerio"; - -export function exportWord(content: string) { - console.log(content) - // 简单类型检测示例 - const isHTML = (text: string): boolean => { - return /<html[\s>]/.test(text) && - /<\/html>/.test(text) && - /<head>/.test(text) && - /<body>/.test(text) - } - if (isHTML(content)) { - exportHtmlToWord(content) - } else { - exportMarkdownToWord(content) - } -} - -function exportHtmlToWord(content: string) { - console.log("-----------------------------------"+content) - let cleanedContent = content.startsWith('```html') ? content.substring(8) : content; - if (cleanedContent.endsWith('```')) { - cleanedContent = cleanedContent.substring(0, cleanedContent.length - 4); - } - const blob = new Blob([cleanedContent], { type: 'application/msword' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = 'htmldemo.docx'; - // 触发点击事件,开始下载 - document.body.appendChild(a); - a.click(); - // 下载完成后移除临时链接元素 - document.body.removeChild(a); - // 释放 Blob URL 对象 - URL.revokeObjectURL(url); -} - -function exportMarkdownToWord(content: string) { - // 按换行符拆分内容 - const lines = content.split(/\r?\n/); - const paragraphs: Paragraph[] = []; - for (const line of lines) { - // 去除 Markdown 标记(#、*等) - const cleanedLine = line.replace(/^#+\s*/, "").replace(/^\*\*\s*|\*\s*/g, "").trim(); - // 处理空行 - if (cleanedLine === "") { - paragraphs.push(new Paragraph("")); - continue; - } - // 添加文本段落 - paragraphs.push(new Paragraph(cleanedLine)); - } - - // 创建 Word 文档对象 - const doc = new Document({ - sections: [{ - children: paragraphs, - }], - }); - - // 转换为 Blob 并下载 - Packer.toBlob(doc) - .then((blob) => { - saveAs(blob, "demo.docx"); - }) - .catch((error) => { - console.error("导出 Word 失败:", error); - }); -} diff --git a/app/utils/fileExport/export2Excel.ts b/app/utils/fileExport/export2Excel.ts new file mode 100644 index 0000000..83ab433 --- /dev/null +++ b/app/utils/fileExport/export2Excel.ts @@ -0,0 +1,154 @@ +/* eslint-disable */ +import * as XLSX from "xlsx"; + +export function toExcel(content: string) { + let sheetName = "result"; // 默认表名 + let tableContent = ""; + + // 查找标题内容(以 ** 或 ## 或 ### 开头) + const titleMatch = content.match(/^(#{2,3}|\*\*)\s*(.*)/m); + if (titleMatch) { + sheetName = titleMatch[2].trim().replace(/\s+/g, "_"); // 使用标题作为表名 + // 提取表格内容(跳过标题部分) + tableContent = content.substring(titleMatch[0].length).trim(); + } else { + tableContent = content; + } + + // 查找表格的起始位置(第一行以 | 开头且以 | 结尾) + let tableStartIndex = -1; + const lines = tableContent.split("\n"); + for (let i = 0; i < lines.length; i++) { + if (lines[i].trim().startsWith("|") && lines[i].trim().endsWith("|")) { + tableStartIndex = i; + break; + } + } + + if (tableStartIndex === -1) { + console.error("表格内容未找到"); + return; + } + + // 查找表格的结束位置(遇到不以 | 开头或者不以 | 结尾的行) + let tableEndIndex = -1; + for (let i = tableStartIndex; i < lines.length; i++) { + if (!lines[i].trim().startsWith("|") || !lines[i].trim().endsWith("|")) { + tableEndIndex = i; + break; + } + } + + if (tableEndIndex === -1) { + tableEndIndex = lines.length; + } + + // 提取表格内容 + const tableData = lines + .slice(tableStartIndex, tableEndIndex) + .join("\n") + .trim(); + + // 解析表格内容 + const rows = tableData.split("\n"); + const data: any[] = []; + let headers: string[] = []; + + rows.forEach((row, rowIndex) => { + // 去掉行首尾的 | 符号,并按 | 分割 + const cells = row + .replace(/^\s*\|/g, "") + .replace(/\|\s*$/g, "") + .split(/\s*\|\s*/); + + // 跳过分隔线(假设分隔线的每个单元格都是由短横线组成) + if ( + rowIndex > 0 && + cells.every( + (cell) => cell.trim().replace(/-/g, "").length === 0, // 检查单元格内容是否只包含短横线 + ) + ) { + return; + } + + if (rowIndex === 0) { + // 第一行是表头 + headers = cells.map((cell) => cell.trim()); + } else { + // 数据行 + const rowData: any = {}; + cells.forEach((cell, cellIndex) => { + if (cellIndex < headers.length) { + rowData[headers[cellIndex]] = cell.trim(); + } + }); + data.push(rowData); + } + }); + + // 创建工作表 + const ws = XLSX.utils.json_to_sheet(data); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, sheetName); + + // 生成文件并下载 + XLSX.writeFile(wb, `${sheetName}.xlsx`); +} + +export function getExcelData(file: File): Promise<any[][]> { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + + reader.onload = (e) => { + try { + const data = e.target?.result; + if (!data) { + reject(new Error("Failed to read file data")); + return; + } + const workbook = XLSX.read(data, { type: "array" }); + const firstSheetName = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[firstSheetName]; + + // 使用类型断言将 unknown[] 转换为 any[][] + const jsonData = XLSX.utils.sheet_to_json(worksheet, { + header: 1, + }) as any[][]; + resolve(jsonData.slice(0, 100)); + } catch (error) { + reject(error); + } + }; + reader.onerror = (error) => { + reject(error); + }; + + reader.readAsArrayBuffer(file); + }); +} + +export function htmlToExcel(htmlContent: string) { + const parser = new DOMParser(); + const doc = parser.parseFromString(htmlContent, "text/html"); + const table = doc.querySelector("table"); + if (!table) { + return []; + } + const rows = table.querySelectorAll("tr"); + const tableData: string[][] = []; + // 提取表格数据(包含表头) + rows.forEach((row) => { + const cells = row.querySelectorAll("td, th"); + const rowData: string[] = []; + cells.forEach((cell) => { + rowData.push(cell.textContent?.trim() || ""); + }); + tableData.push(rowData); + }); + // 使用二维数组直接创建工作表 + const ws = XLSX.utils.aoa_to_sheet(tableData); + // 创建工作簿并保存 + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, "Sheet1"); + XLSX.writeFile(wb, "export.xlsx"); +} diff --git a/app/utils/fileExport/toPdf.ts b/app/utils/fileExport/toPdf.ts new file mode 100644 index 0000000..3254d4a --- /dev/null +++ b/app/utils/fileExport/toPdf.ts @@ -0,0 +1,166 @@ +import html2canvas from "html2canvas"; +import jsPDF from "jspdf"; +import { pdfToText } from "pdf-ts"; + +export async function htmlToPdf2(htmlCode: string) { + const container = document.createElement("div"); + container.style.cssText = ` + position: fixed; + left: -9999px; + top: -9999px; + width: 210mm; // 固定 A4 宽度 + background: white; + `; + container.innerHTML = htmlCode; + document.body.appendChild(container); + + try { + // 关键修改 1: 确保所有图片加载完成 + await waitForImages(container); + + // 关键修改 2: 强制触发布局计算 + void container.offsetHeight; + + const canvas = await html2canvas(container, { + scale: 2, + useCORS: true, + logging: true, + scrollY: -window.scrollY, // 消除滚动偏移 + windowWidth: container.scrollWidth, + windowHeight: container.scrollHeight, + }); + + const pdf = new jsPDF("p", "mm", "a4"); + const pageWidth = pdf.internal.pageSize.getWidth(); + const imgRatio = canvas.width / canvas.height; + + // 关键修改 3: 动态计算高度 + const imgHeight = (pageWidth * canvas.height) / canvas.width; + pdf.addImage( + canvas.toDataURL("image/png"), + "PNG", + 0, + 0, + pageWidth, + imgHeight, + ); + + // 关键修改 4: 处理多页内容 + if (imgHeight > pdf.internal.pageSize.getHeight()) { + pdf.addPage(); + pdf.addImage( + canvas.toDataURL("image/png"), + "PNG", + 0, + -pdf.internal.pageSize.getHeight(), + pageWidth, + imgHeight, + ); + } + + pdf.save("document.pdf"); + } catch (error) { + console.error("生成失败:", error); + } finally { + document.body.removeChild(container); + } +} + +// 图片加载等待函数 +const waitForImages = (element: HTMLElement) => { + return new Promise<void>((resolve) => { + const images = element.getElementsByTagName("img"); + let loaded = 0; + + const checkDone = () => { + if (loaded >= images.length) resolve(); + }; + + if (images.length === 0) return resolve(); + + Array.from(images).forEach((img) => { + if (img.complete) { + loaded++; + checkDone(); + } else { + img.onload = () => { + loaded++; + checkDone(); + }; + img.onerror = checkDone; + } + }); + }); +}; + +export function htmlToPdf(htmlCode: string) { + // 1. 动态创建一个 div 元素,并配置为隐藏(display: none) + const container = document.createElement("div"); + container.innerHTML = htmlCode; + container.style.display = "none"; + document.body.appendChild(container); + // 2. 为了让 html2canvas 能捕获内容,将 display 修改为 block,并移至视野之外 + container.style.display = "block"; + container.style.position = "absolute"; + container.style.top = "-9999px"; + container.style.left = "-9999px"; + window.pageYOffset = 0; + document.documentElement.scrollTop = 0; + document.body.scrollTop = 0; + setTimeout(() => { + html2canvas(container, { + allowTaint: true, + useCORS: true, + scale: 2, // 提升画面质量,但是会增加文件大小 + height: container.scrollHeight, // 需要注意,element的 高度 宽度一定要在这里定义一下,不然会存在只下载了当前你能看到的页面 避雷避雷!!! + windowHeight: container.scrollHeight, + }).then(function (canvas) { + var contentWidth = canvas.width; + var contentHeight = canvas.height; + // console.log('contentWidth', contentWidth) + // console.log('contentHeight', contentHeight) + // 一页pdf显示html页面生成的canvas高度; + var pageHeight = (contentWidth * 841.89) / 592.28; + // 未生成pdf的html页面高度 + var leftHeight = contentHeight; + // console.log('pageHeight', pageHeight) + // console.log('leftHeight', leftHeight) + // 页面偏移 + var position = 0; + // a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高 //40是左右页边距 + var imgWidth = 595.28 - 40; + var imgHeight = (592.28 / contentWidth) * contentHeight; + var pageData = canvas.toDataURL("image/jpeg", 1.0); + var pdf = new jsPDF("p", "pt", "a4"); + // 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89) + // 当内容未超过pdf一页显示的范围,无需分页 + if (leftHeight < pageHeight) { + // console.log('没超过1页') + pdf.addImage(pageData, "JPEG", 20, 20, imgWidth, imgHeight); + } else { + while (leftHeight > 0) { + // console.log('超过1页') + pdf.addImage(pageData, "JPEG", 20, position, imgWidth, imgHeight); + leftHeight -= pageHeight; + position -= 841.89; + // 避免添加空白页 + if (leftHeight > 0) { + pdf.addPage(); + } + } + } + pdf.save("out.pdf"); + }); + }, 1000); +} + +export async function getPdfData(file: File) { + try { + const arrayBuffer = await file.arrayBuffer(); + const text = await pdfToText(new Uint8Array(arrayBuffer)); + return text; + } catch (error) { + console.error("Error extracting PDF content:", error); + throw error; + } +} diff --git a/app/utils/fileExport/word.ts b/app/utils/fileExport/word.ts new file mode 100644 index 0000000..c287af7 --- /dev/null +++ b/app/utils/fileExport/word.ts @@ -0,0 +1,91 @@ +import { Document, Packer, Paragraph } from "docx"; +import { saveAs } from "file-saver"; +import * as mammoth from "mammoth"; + +export function exportWord(content: string) { + console.log(content); + // 简单类型检测示例 + const isHTML = (text: string): boolean => { + return ( + /<html[\s>]/.test(text) && + /<\/html>/.test(text) && + /<head>/.test(text) && + /<body>/.test(text) + ); + }; + if (isHTML(content)) { + exportHtmlToWord(content); + } else { + exportMarkdownToWord(content); + } +} + +export function exportHtmlToWord(content: string) { + let cleanedContent = content.startsWith("```html") + ? content.substring(8) + : content; + if (cleanedContent.endsWith("```")) { + cleanedContent = cleanedContent.substring(0, cleanedContent.length - 4); + } + const blob = new Blob([cleanedContent], { type: "application/msword" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = "htmldemo.docx"; + // 触发点击事件,开始下载 + document.body.appendChild(a); + a.click(); + // 下载完成后移除临时链接元素 + document.body.removeChild(a); + // 释放 Blob URL 对象 + URL.revokeObjectURL(url); +} + +function exportMarkdownToWord(content: string) { + // 按换行符拆分内容 + const lines = content.split(/\r?\n/); + const paragraphs: Paragraph[] = []; + for (const line of lines) { + // 去除 Markdown 标记(#、*等) + const cleanedLine = line + .replace(/^#+\s*/, "") + .replace(/^\*\*\s*|\*\s*/g, "") + .trim(); + // 处理空行 + if (cleanedLine === "") { + paragraphs.push(new Paragraph("")); + continue; + } + // 添加文本段落 + paragraphs.push(new Paragraph(cleanedLine)); + } + + // 创建 Word 文档对象 + const doc = new Document({ + sections: [ + { + children: paragraphs, + }, + ], + }); + + // 转换为 Blob 并下载 + Packer.toBlob(doc) + .then((blob) => { + saveAs(blob, "demo.docx"); + }) + .catch((error) => { + console.error("导出 Word 失败:", error); + }); +} + +export async function getWordData(file: File) { + try { + const arrayBuffer = await file.arrayBuffer(); + const { value, messages } = await mammoth.extractRawText({ arrayBuffer }); + return value; + } catch (error) { + console.error("Error extracting Word content:", error); + throw error; + } +} diff --git a/app/utils/prompt.ts b/app/utils/prompt.ts index 970970e..22be8d0 100644 --- a/app/utils/prompt.ts +++ b/app/utils/prompt.ts @@ -1,35 +1,48 @@ import type { writePromptParam } from "@/app/types/prompt"; -export function getWrtingPrompt(param: writePromptParam) { - let writingPurposeName; - switch (param.writingPurposeName) { - case "公司官网": - writingPurposeName = "公司官网的介绍"; - break; - case "小红书": - writingPurposeName = "小红书的介绍"; - break; - case "微信": - writingPurposeName = "微信的介绍"; - break; - case "公众号": - writingPurposeName = "公众号的介绍"; - break; - case "今日头条": - writingPurposeName = "今日头条的介绍"; - break; - } - const isImg = `文案要配上图片,实现图文混排,要美观,要符合${writingPurposeName}的排版标准和写作风格,写作风格要${param.writingStyleName}, +export function getWrtingPrompt(param: writePromptParam): string { + const { + isImgName, + writingPurposeName: rawPurpose, + writingStyleName: rawStyle, + writingTypeName: rawType, + writingLanguageName, + prompt, + writingCount, + } = param; + + const purposeMap: Record<string, string> = { + 公司官网: "公司官网的介绍", + 小红书: "小红书的介绍", + 微信: "微信的介绍", + 公众号: "公众号的介绍", + 今日头条: "今日头条的介绍", + }; + const styleMap: Record<string, string> = { + 专业: "专业的风格", + 活泼: "活泼的风格", + 严谨: "严谨的风格", + }; + const typeMap: Record<string, string> = { + 产品推广文案: "产品推广的文案", + 品牌宣传文案: "品牌宣传的文案", + 产品说明书: "产品的说明书", + 产品介绍: "产品的介绍", + }; + + let isImg = ""; + if (isImgName === "是") { + const purpose = purposeMap[rawPurpose] || "公司官网的介绍"; + const style = styleMap[rawStyle] || "专业的风格"; + + isImg = `文案要配上图片,实现图文混排,要美观,要符合${purpose}的排版标准和写作风格,写作风格要${style}, 你没有图片没关系,把图文混排的效果实现,并在你认为要插入图片的地方将图片的Prompt用英文输出给:,记得图片地址后面的?nologo=true一定不能去掉了, 因为这个语法可以自动按照提示生成并渲染图片。你可以帮我大幅提高生成图片质量和丰富程度,比如增加相机光圈、具体场景描述等内容,注意图片一定要用<img,否则在HTML下图片可能显示不了`; + } + const writingTypeName = typeMap[rawType] || "产品推广文案"; - const input = `帮我使用${param.writingLanguageName}写一篇主题是${ - param.prompt - }的${param.writingTypeName}, - ${param.isImgName === "是" ? isImg : ""} - ,字数要求不少于${ - param.writingCount - }字,字数不包括html代码和图片Prompt。输出成标准的html,直接给结果,不要做任何解释`; + const input = `帮我使用${writingLanguageName}写一篇主题是${prompt}的${writingTypeName},${isImg},字数要求不少于${writingCount}字, + 字数不包括html代码和图片Prompt。输出成标准的html并且样式必须为内联样式,直接给结果,不要做任何解释`; return input; } diff --git a/package.json b/package.json index a843e5a..1fa9808 100644 --- a/package.json +++ b/package.json @@ -43,9 +43,12 @@ "fuse.js": "^7.0.0", "heic2any": "^0.0.4", "html-to-image": "^1.11.11", + "html2canvas": "^1.4.1", "idb-keyval": "^6.2.1", + "jspdf": "^3.0.1", "lodash-es": "^4.17.21", "lucide-react": "^0.484.0", + "mammoth": "^1.9.0", "markdown-to-txt": "^2.0.1", "mermaid": "^10.6.1", "mind-elixir": "^4.5.0", @@ -53,6 +56,7 @@ "next": "^14.1.1", "node-fetch": "^3.3.1", "openapi-client-axios": "^7.5.5", + "pdf-ts": "^0.0.2", "rc-tooltip": "^6.4.0", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/yarn.lock b/yarn.lock index c2b1a86..21d8422 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1271,6 +1271,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.26.7": + version "7.27.0" + resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.27.0.tgz#fbee7cf97c709518ecc1f590984481d5460d4762" + integrity sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.18.10", "@babel/template@^7.20.7": version "7.20.7" resolved "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz" @@ -2626,6 +2633,11 @@ dependencies: parchment "^1.1.2" +"@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@^18.2.7": version "18.2.7" resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz" @@ -2669,6 +2681,11 @@ resolved "https://registry.npmmirror.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz" integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== +"@types/trusted-types@^2.0.7": + version "2.0.7" + resolved "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== + "@types/unist@*", "@types/unist@^2.0.0": version "2.0.6" resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz" @@ -2898,6 +2915,11 @@ "@webassemblyjs/wast-parser" "1.9.0" "@xtuc/long" "4.2.2" +"@xmldom/xmldom@^0.8.6": + version "0.8.10" + resolved "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99" + integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== + "@xmldom/xmldom@^0.9.7": version "0.9.8" resolved "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.9.8.tgz" @@ -2985,12 +3007,12 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv-keywords@^3.5.2: +ajv-keywords@^3.1.0, ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.1.0, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -3109,7 +3131,7 @@ arg@^4.1.0: resolved "https://registry.npmmirror.com/arg/-/arg-4.1.3.tgz" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== -argparse@^1.0.7: +argparse@^1.0.7, argparse@~1.0.3: version "1.0.10" resolved "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== @@ -3198,6 +3220,11 @@ asynckit@^0.4.0: resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.npmmirror.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + available-typed-arrays@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz" @@ -3318,16 +3345,36 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +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.5.1: + version "1.5.1" + resolved "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + bath-es5@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/bath-es5/-/bath-es5-3.0.3.tgz" integrity sha512-PdCioDToH3t84lP40kUFCKWCOCH389Dl1kbC8FGoqOwamxsmqxxnJSXdkTOsPoNHXjem4+sJ+bbNoQm5zeCqxg== +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bluebird@~3.4.0: + version "3.4.7" + resolved "https://registry.npmmirror.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" + integrity sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA== + boolbase@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" @@ -3365,6 +3412,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +btoa@^1.2.1: + version "1.2.1" + resolved "https://registry.npmmirror.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" + integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz" @@ -3444,6 +3496,20 @@ caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001688: resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz" integrity sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A== +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" + ccount@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz" @@ -3758,6 +3824,11 @@ core-js-compat@^3.25.1: dependencies: browserslist "^4.21.5" +core-js@^3.6.0, core-js@^3.8.3: + version "3.41.0" + resolved "https://registry.npmmirror.com/core-js/-/core-js-3.41.0.tgz#57714dafb8c751a6095d028a7428f1fb5834a776" + integrity sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA== + core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz" @@ -3834,6 +3905,13 @@ css-box-model@^1.2.1: dependencies: tiny-invariant "^1.0.6" +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.1.3: version "4.3.0" resolved "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz" @@ -4388,6 +4466,11 @@ diff@^5.0.0: resolved "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz" integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== +dingbat-to-unicode@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz#5091dd673241453e6b5865e26e5a4452cdef5c83" + integrity sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" @@ -4487,6 +4570,13 @@ dompurify@^3.0.5: resolved "https://registry.npmjs.org/dompurify/-/dompurify-3.0.5.tgz" integrity sha512-F9e6wPGtY+8KNMRAVfxeCOHU0/NPWMSENNq4pQctuXRqqdEPW7q3CrLbR5Nse044WwacyjHGOMlvNsBe1y6z9A== +dompurify@^3.2.4: + version "3.2.4" + resolved "https://registry.npmmirror.com/dompurify/-/dompurify-3.2.4.tgz#af5a5a11407524431456cf18836c55d13441cd8e" + integrity sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg== + optionalDependencies: + "@types/trusted-types" "^2.0.7" + domutils@^2.8.0: version "2.8.0" resolved "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz" @@ -4505,6 +4595,13 @@ domutils@^3.0.1, domutils@^3.1.0: domelementtype "^2.3.0" domhandler "^5.0.3" +duck@^0.1.12: + version "0.1.12" + resolved "https://registry.npmmirror.com/duck/-/duck-0.1.12.tgz#de7adf758421230b6d7aee799ce42670586b9efa" + integrity sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg== + dependencies: + underscore "^1.13.1" + dunder-proto@^1.0.1: version "1.0.1" resolved "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz" @@ -4559,6 +4656,11 @@ emoji-regex@^9.2.2: resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + encoding-sniffer@^0.2.0: version "0.2.0" resolved "https://registry.npmmirror.com/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz" @@ -5132,6 +5234,11 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4: node-domexception "^1.0.0" web-streams-polyfill "^3.0.3" +fflate@^0.8.1: + version "0.8.2" + resolved "https://registry.npmmirror.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" + integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" @@ -5626,6 +5733,14 @@ html-to-image@^1.11.11: resolved "https://registry.npmmirror.com/html-to-image/-/html-to-image-1.11.11.tgz" integrity sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA== +html2canvas@^1.0.0-rc.5, html2canvas@^1.4.1: + 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" + htmlparser2@^9.1.0: version "9.1.0" resolved "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-9.1.0.tgz" @@ -6531,7 +6646,7 @@ json2mq@^0.2.0: dependencies: string-convert "^0.2.0" -json5@^1.0.2: +json5@^1.0.1, json5@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== @@ -6543,6 +6658,21 @@ json5@^2.2.2, json5@^2.2.3: resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== +jspdf@^3.0.1: + version "3.0.1" + resolved "https://registry.npmmirror.com/jspdf/-/jspdf-3.0.1.tgz#d81e1964f354f60412516eb2449ea2cccd4d2a3b" + integrity sha512-qaGIxqxetdoNnFQQXxTKUD9/Z7AloLaw94fFsOiJMxbfYdBbrBuhWmbzI8TVjrw7s3jBY1PFHofBKMV/wZPapg== + dependencies: + "@babel/runtime" "^7.26.7" + atob "^2.1.2" + btoa "^1.2.1" + 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.3: version "3.3.3" resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz" @@ -6551,7 +6681,7 @@ json5@^2.2.2, json5@^2.2.3: array-includes "^3.1.5" object.assign "^4.1.3" -jszip@*, jszip@^3.10.1: +jszip@*, jszip@^3.10.1, jszip@^3.7.1: version "3.10.1" resolved "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz" integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== @@ -6680,6 +6810,15 @@ loader-runner@^4.1.0: resolved "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz" integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== +loader-utils@^1.0.0: + version "1.4.2" + resolved "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" + integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz" @@ -6746,6 +6885,15 @@ loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +lop@^0.4.2: + version "0.4.2" + resolved "https://registry.npmmirror.com/lop/-/lop-0.4.2.tgz#c9c2f958a39b9da1c2f36ca9ad66891a9fe84640" + integrity sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw== + dependencies: + duck "^0.1.12" + option "~0.2.1" + underscore "^1.13.1" + lowlight@^2.0.0: version "2.8.1" resolved "https://registry.npmjs.org/lowlight/-/lowlight-2.8.1.tgz" @@ -6805,6 +6953,22 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +mammoth@^1.9.0: + version "1.9.0" + resolved "https://registry.npmmirror.com/mammoth/-/mammoth-1.9.0.tgz#71e34ca280735275788bfe95e653a058dcab4df2" + integrity sha512-F+0NxzankQV9XSUAuVKvkdQK0GbtGGuqVnND9aVf9VSeUA82LQa29GjLqYU6Eez8LHqSJG3eGiDW3224OKdpZg== + dependencies: + "@xmldom/xmldom" "^0.8.6" + argparse "~1.0.3" + base64-js "^1.5.1" + bluebird "~3.4.0" + dingbat-to-unicode "^1.0.1" + jszip "^3.7.1" + lop "^0.4.2" + path-is-absolute "^1.0.0" + underscore "^1.13.1" + xmlbuilder "^10.0.0" + markdown-table@^3.0.0: version "3.0.3" resolved "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz" @@ -7481,6 +7645,11 @@ node-domexception@^1.0.0: resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== +node-ensure@^0.0.0: + version "0.0.0" + resolved "https://registry.npmmirror.com/node-ensure/-/node-ensure-0.0.0.tgz#ecae764150de99861ec5c810fd5d096b183932a7" + integrity sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw== + node-fetch@^3.3.1: version "3.3.1" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz" @@ -7639,6 +7808,11 @@ openapi-types@^12.1.3: resolved "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz" integrity sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw== +option@~0.2.1: + version "0.2.4" + resolved "https://registry.npmmirror.com/option/-/option-0.2.4.tgz#fd475cdf98dcabb3cb397a3ba5284feb45edbfe4" + integrity sha512-pkEqbDyl8ou5cpq+VsnQbe/WlEy5qS7xPzMS1U55OCG9KPvwFD46zDbxQIj3egJSFc3D+XhYOPUzz49zQAVy7A== + optionator@^0.9.3: version "0.9.3" resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz" @@ -7770,6 +7944,26 @@ path-type@^4.0.0: resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pdf-ts@^0.0.2: + version "0.0.2" + resolved "https://registry.npmmirror.com/pdf-ts/-/pdf-ts-0.0.2.tgz#27fc7842f998fdd679ae1aa2b95e42b8e0736758" + integrity sha512-t9VmdLA+8dvX9t3XulCD1hIEWi0N94p2WpfTPwDcvYCW/NElaK+abHj4q5F4XhJcnxzm6dzlileyaH7qcIbnmQ== + dependencies: + pdfjs-dist "1.10.100" + +pdfjs-dist@1.10.100: + version "1.10.100" + resolved "https://registry.npmmirror.com/pdfjs-dist/-/pdfjs-dist-1.10.100.tgz#d5a250b42482ab6e41d763a795ce7cdebe6b1894" + integrity sha512-aCfONGqlBeazYxik3rjd7xaoCKMRYECwZSCC3EC3weqibF2V1Bp/v9WZbF7Lyy5Q6UE4NqOYu126r7U+Le4Uhg== + dependencies: + node-ensure "^0.0.0" + worker-loader "^1.0.0" + +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== + picocolors@^1.0.0, picocolors@^1.1.1: version "1.1.1" resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz" @@ -7943,6 +8137,13 @@ raf-schd@^4.0.3: resolved "https://registry.npmmirror.com/raf-schd/-/raf-schd-4.0.3.tgz" integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ== +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.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz" @@ -8445,6 +8646,11 @@ regenerate@^1.4.2: resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz" 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.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz" @@ -8646,6 +8852,11 @@ rfdc@^1.3.0: resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== +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: version "3.0.2" resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" @@ -8745,6 +8956,14 @@ scheduler@^0.23.0: dependencies: loose-envify "^1.1.0" +schema-utils@^0.4.0: + version "0.4.7" + resolved "https://registry.npmmirror.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" + integrity sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ== + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + schema-utils@^3.0.0: version "3.3.0" resolved "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz" @@ -8976,6 +9195,11 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +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== + statuses@2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" @@ -9173,6 +9397,11 @@ svg-parser@^2.0.4: resolved "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz" 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== + svgo@^2.8.0: version "2.8.0" resolved "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz" @@ -9250,6 +9479,13 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.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.npmjs.org/text-table/-/text-table-0.2.0.tgz" @@ -9444,6 +9680,11 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +underscore@^1.13.1: + version "1.13.7" + resolved "https://registry.npmmirror.com/underscore/-/underscore-1.13.7.tgz#970e33963af9a7dda228f17ebe8399e5fbe63a10" + integrity sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g== + undici-types@~6.19.2: version "6.19.8" resolved "https://registry.npmmirror.com/undici-types/-/undici-types-6.19.8.tgz" @@ -9621,6 +9862,13 @@ util-deprecate@~1.0.1: resolved "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz" 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.0" resolved "https://registry.npmmirror.com/uuid/-/uuid-9.0.0.tgz" @@ -9854,6 +10102,14 @@ word@~0.3.0: resolved "https://registry.npmmirror.com/word/-/word-0.3.0.tgz" integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA== +worker-loader@^1.0.0: + version "1.1.1" + resolved "https://registry.npmmirror.com/worker-loader/-/worker-loader-1.1.1.tgz#920d74ddac6816fc635392653ed8b4af1929fd92" + integrity sha512-qJZLVS/jMCBITDzPo/RuweYSIG8VJP5P67mP/71alGyTZRe1LYJFdwLjLalY3T5ifx0bMDRD3OB6P2p1escvlg== + dependencies: + loader-utils "^1.0.0" + schema-utils "^0.4.0" + wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" @@ -9920,6 +10176,11 @@ xml@^1.0.1: resolved "https://registry.npmmirror.com/xml/-/xml-1.0.1.tgz" integrity sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw== +xmlbuilder@^10.0.0: + version "10.1.1" + resolved "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-10.1.1.tgz#8cae6688cc9b38d850b7c8d3c0a4161dcaf475b0" + integrity sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz" -- libgit2 0.24.0