作者 202304001

AI写作工具栏功能、写作提示词修改、pdf/word文件内容上传读取

... ... @@ -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);
... ...
... ... @@ -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 (
... ...
... ... @@ -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>
... ...
... ... @@ -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("生成失败,请重试");
... ...
.write-body{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.loading-content {
display: flex;
flex-direction: column;
... ...
... ... @@ -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}
/>
))
)}
... ...
<?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
... ...
... ... @@ -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: "搜索消息",
... ...
... ... @@ -3,4 +3,4 @@ export * from "./update";
export * from "./access";
export * from "./config";
export * from "./plugin";
export * from "./message";
export * from "./mindMap";
... ...
... ... @@ -106,7 +106,6 @@ export function getExcelData(file: File): Promise<any[][]> {
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];
... ... @@ -127,3 +126,29 @@ export function getExcelData(file: File): Promise<any[][]> {
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");
}
... ...
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;
}
}
... ...
import { Document, Packer, Paragraph, TextRun } from "docx";
import { Document, Packer, Paragraph } from "docx";
import { saveAs } from "file-saver";
import * as cheerio from "cheerio";
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)
}
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);
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));
// 按换行符拆分内容
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,
}],
// 创建 Word 文档对象
const doc = new Document({
sections: [
{
children: paragraphs,
},
],
});
// 转换为 Blob 并下载
Packer.toBlob(doc)
.then((blob) => {
saveAs(blob, "demo.docx");
})
.catch((error) => {
console.error("导出 Word 失败:", error);
});
}
// 转换为 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;
}
}
... ...
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用英文输出给:![description](https://image.pollinations.ai/prompt/description?nologo=true),记得图片地址后面的?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;
}
... ...
... ... @@ -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",
... ...
... ... @@ -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"
... ...