feat(ResearchReport): use print-js to export PDF

This commit is contained in:
AnotiaWang
2025-02-24 22:18:21 +08:00
parent 6e541d5fe8
commit c2425bc3a4
9 changed files with 114 additions and 307 deletions

View File

@ -22,7 +22,6 @@
:description="node.error"
color="error"
variant="soft"
:duration="8000"
:actions="[
{
label: $t('webBrowsing.retry'),

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import printJS from 'print-js'
import { marked } from 'marked'
import { writeFinalReport } from '~/lib/deep-research'
import jsPDF from 'jspdf'
import {
feedbackInjectionKey,
formInjectionKey,
@ -33,7 +33,6 @@
loadingExportPdf.value ||
loadingExportMarkdown.value,
)
let pdf: jsPDF | undefined
async function generateReport() {
loading.value = true
@ -55,7 +54,7 @@
} else if (chunk.type === 'text-delta') {
reportContent.value += chunk.textDelta
} else if (chunk.type === 'error') {
error.value = t('researchReport.error', [
error.value = t('researchReport.generateFailed', [
chunk.error instanceof Error
? chunk.error.message
: String(chunk.error),
@ -67,102 +66,54 @@
)}\n\n${visitedUrls.map((url) => `- ${url}`).join('\n')}`
} catch (e: any) {
console.error(`Generate report failed`, e)
error.value = t('researchReport.error', [e.message])
error.value = t('researchReport.generateFailed', [e.message])
} finally {
loading.value = false
}
}
async function exportToPdf() {
const element = document.getElementById('report-content')
if (!element) return
loadingExportPdf.value = true
try {
// 创建 PDF 实例
if (!pdf) {
pdf = new jsPDF({
orientation: 'portrait',
unit: 'mm',
format: 'a4',
// Change the title back
const cleanup = () => {
useHead({
title: 'Deep Research Web UI',
})
}
// Load Chinese font
if (locale.value === 'zh') {
try {
if (!pdf.getFontList().SourceHanSans?.length) {
toast.add({
title: t('researchReport.downloadingFonts'),
duration: 5000,
color: 'info',
})
// Wait for 100ms to avoid toast being blocked by PDF generation
await new Promise((resolve) => setTimeout(resolve, 100))
const fontUrl = '/fonts/SourceHanSansCN-VF.ttf'
pdf.addFont(fontUrl, 'SourceHanSans', 'normal')
pdf.setFont('SourceHanSans')
}
} catch (e: any) {
toast.add({
title: t('researchReport.downloadFontFailed'),
description: e.message,
duration: 8000,
color: 'error',
})
console.warn(
'Failed to load Chinese font, fallback to default font:',
e,
)
}
}
// 设置字体大小和行高
const fontSize = 10.5
const lineHeight = 1.5
pdf.setFontSize(fontSize)
// 设置页面边距单位mm
const margin = {
top: 20,
right: 20,
bottom: 20,
left: 20,
}
// 获取纯文本内容
const content = element.innerText
// 计算可用宽度mm
const pageWidth = pdf.internal.pageSize.getWidth()
const maxWidth = pageWidth - margin.left - margin.right
// 分割文本为行
const lines = pdf.splitTextToSize(content, maxWidth)
// 计算当前位置
let y = margin.top
// 逐行添加文本
for (const line of lines) {
// 检查是否需要新页
if (y > pdf.internal.pageSize.getHeight() - margin.bottom) {
pdf.addPage()
y = margin.top
}
// 添加文本
pdf.text(line, margin.left, y)
y += fontSize * lineHeight
}
pdf.save('research-report.pdf')
} catch (error) {
console.error('Export to PDF failed:', error)
} finally {
loadingExportPdf.value = false
}
loadingExportPdf.value = true
// Temporarily change the document title, which will be used as the filename
useHead({
title: `Deep Research Report - ${form.value.query ?? 'Untitled'}`,
})
// Wait after title is changed
await new Promise((r) => setTimeout(r, 100))
printJS({
printable: reportHtml.value,
type: 'raw-html',
showModal: true,
onIncompatibleBrowser() {
toast.add({
title: t('researchReport.incompatibleBrowser'),
description: t('researchReport.incompatibleBrowserDescription'),
duration: 10_000,
})
cleanup()
},
onError(error, xmlHttpRequest) {
console.error(`[Export PDF] failed:`, error, xmlHttpRequest)
toast.add({
title: t('researchReport.exportFailed'),
description: error instanceof Error ? error.message : String(error),
duration: 10_000,
})
cleanup()
},
onPrintDialogClose() {
cleanup()
},
})
return
}
async function exportToMarkdown() {
@ -210,7 +161,13 @@
</div>
</template>
<div v-if="error" class="text-red-500">{{ error }}</div>
<UAlert
v-if="error"
:title="$t('researchReport.exportFailed')"
:description="error"
color="error"
variant="soft"
/>
<div class="flex mb-4 justify-end">
<UButton
@ -246,7 +203,6 @@
<div
v-if="reportContent"
id="report-content"
class="prose prose-sm max-w-none break-words p-6 bg-gray-50 dark:bg-gray-800 dark:prose-invert dark:text-white rounded-lg shadow"
v-html="reportHtml"
/>

View File

@ -54,7 +54,9 @@ export const useWebSearch = (): WebSearchFunction => {
return async (q: string, o: WebSearchOptions) => {
const results = await tvly.search(q, {
...o,
searchDepth: config.webSearch.tavilyAdvancedSearch ? 'advanced' : 'basic',
searchDepth: config.webSearch.tavilyAdvancedSearch
? 'advanced'
: 'basic',
topic: config.webSearch.tavilySearchTopic,
})
return results.results

View File

@ -92,15 +92,16 @@
},
"researchReport": {
"title": "4. Research Report",
"exportPdf": "Export PDF",
"exportPdf": "Print (PDF)",
"exportMarkdown": "Export Markdown",
"sources": "Sources",
"waiting": "Waiting for report...",
"generating": "Generating report...",
"error": "Generate report failed: {0}",
"downloadingFonts": "Downloading necessary fonts, this may take some time...",
"downloadFontFailed": "Download font failed",
"regenerate": "Regenerate"
"regenerate": "Regenerate",
"incompatibleBrowser": "Incompatibile browser",
"incompatibleBrowserDescription": "Your browser does not support this printing method. Please consider using a modern browser, or export the Markdown and manually convert it into PDF using other services, like https://md-to-pdf.fly.dev (Use at your own risk)",
"exportFailed": "Export failed",
"generateFailed": "Generate report failed: {0}"
},
"error": {
"requestBlockedByCORS": "The current API provider may not allow cross-origin requests. Please try a different service provider or contact the provider for support."

View File

@ -92,15 +92,16 @@
},
"researchReport": {
"title": "4. Onderzoeksrapport",
"exportPdf": "PDF exporteren",
"exportPdf": "Afdrukken (PDF)",
"exportMarkdown": "Markdown exporteren",
"sources": "Bronnen",
"waiting": "Wachten op het rapport...",
"generating": "Rapport genereren...",
"error": "Rapport genereren mislukt: {0}",
"downloadingFonts": "Het downloaden van de benodigde lettertypen kan enige tijd duren...",
"downloadFontFailed": "Downloaden van lettertype mislukt",
"regenerate": "Regenereren"
"regenerate": "Regenereren",
"generateFailed": "Rapport genereren mislukt: {0}",
"exportFailed": "Export mislukt",
"incompatibleBrowser": "Onverenigbaar browser",
"incompatibleBrowserDescription": "Uw browser ondersteunt deze printmethode niet. Overweeg een moderne browser te gebruiken, of exporteer de Markdown en converteer deze handmatig naar PDF met behulp van andere diensten zoals https://md-to-pdf.fly.dev (Gebruik op eigen risico)"
},
"error": {
"requestBlockedByCORS": "De huidige API-provider staat mogelijk geen cross-origin-verzoeken toe. Probeer een andere API-provider of neem contact op met de provider voor ondersteuning.."

View File

@ -92,15 +92,16 @@
},
"researchReport": {
"title": "4. 研究报告",
"exportPdf": "导出 PDF",
"exportPdf": "打印 (PDF)",
"exportMarkdown": "导出 Markdown",
"sources": "来源",
"waiting": "等待报告...",
"generating": "生成报告中...",
"error": "生成报告失败:{0}",
"downloadingFonts": "正在下载必要字体,可能需要较长时间...",
"downloadFontFailed": "下载字体失败",
"regenerate": "重新生成"
"regenerate": "重新生成",
"generateFailed": "生成报告失败:{0}",
"exportFailed": "导出失败",
"incompatibleBrowserDescription": "您的浏览器不支持这种打印方式,请考虑改用更现代的浏览器,或者导出 Markdown 并手动使用其他服务(如 https://md-to-pdf.fly.dev将其转换为 PDF不承诺安全性。",
"incompatibleBrowser": "浏览器可能较旧"
},
"error": {
"requestBlockedByCORS": "当前 API 服务可能不允许接口跨域,请换一个服务试试,或者向服务方反馈。"

View File

@ -31,11 +31,11 @@
"@vue-flow/core": "^1.42.1",
"ai": "^4.1.41",
"js-tiktoken": "^1.0.19",
"jspdf": "^2.5.2",
"marked": "^15.0.7",
"nuxt": "^3.15.4",
"p-limit": "^6.2.0",
"pinia": "^3.0.1",
"print-js": "^1.6.0",
"semver": "^7.7.1",
"tailwindcss": "^4.0.5",
"vue": "latest",

239
pnpm-lock.yaml generated
View File

@ -28,7 +28,7 @@ importers:
version: 1.2.26
'@mendable/firecrawl-js':
specifier: ^1.17.0
version: 1.17.0(ws@8.18.0)
version: 1.18.2(ws@8.18.0)
'@nuxt/ui':
specifier: 3.0.0-alpha.12
version: 3.0.0-alpha.12(@babel/parser@7.26.8)(axios@1.7.9)(change-case@5.4.4)(db0@0.2.3)(embla-carousel@8.5.2)(ioredis@5.5.0)(magicast@0.3.5)(radix-vue@1.9.13(vue@3.5.13(typescript@5.7.3)))(rollup@4.34.6)(typescript@5.7.3)(vite@6.1.0(@types/node@22.13.1)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.38.1)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3))
@ -65,9 +65,6 @@ importers:
js-tiktoken:
specifier: ^1.0.19
version: 1.0.19
jspdf:
specifier: ^2.5.2
version: 2.5.2
marked:
specifier: ^15.0.7
version: 15.0.7
@ -80,6 +77,9 @@ importers:
pinia:
specifier: ^3.0.1
version: 3.0.1(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3))
print-js:
specifier: ^1.6.0
version: 1.6.0
semver:
specifier: ^7.7.1
version: 7.7.1
@ -323,10 +323,6 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/runtime@7.26.7':
resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==}
engines: {node: '>=6.9.0'}
'@babel/standalone@7.26.8':
resolution: {integrity: sha512-WS5Cw/8gWP9qBJ+qPUVr5Le4bCeXTMoVHF9TofgEqAUpEgvVzNXCPf97SNLuDpSRNHNWcH2lFixGUGjaM6VVCg==}
engines: {node: '>=6.9.0'}
@ -524,16 +520,16 @@ packages:
resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/core@0.10.0':
resolution: {integrity: sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/core@0.11.0':
resolution: {integrity: sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/eslintrc@3.2.0':
resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==}
'@eslint/core@0.12.0':
resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/eslintrc@3.3.0':
resolution: {integrity: sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/js@9.20.0':
@ -544,8 +540,8 @@ packages:
resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/plugin-kit@0.2.5':
resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==}
'@eslint/plugin-kit@0.2.7':
resolution: {integrity: sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@floating-ui/core@1.6.9':
@ -576,8 +572,8 @@ packages:
resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
engines: {node: '>=18.18'}
'@humanwhocodes/retry@0.4.1':
resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==}
'@humanwhocodes/retry@0.4.2':
resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==}
engines: {node: '>=18.18'}
'@iconify-json/lucide@1.2.26':
@ -725,8 +721,8 @@ packages:
engines: {node: '>=18'}
hasBin: true
'@mendable/firecrawl-js@1.17.0':
resolution: {integrity: sha512-W8NEbFLtgedSI4CwxDFJ2iwcmwL7F3Gkv8usagYQ764AxnNdmcylVWMuYoQmzS6iYCtmSLFQUNEvHW9NbWigPQ==}
'@mendable/firecrawl-js@1.18.2':
resolution: {integrity: sha512-KkTm5iFI8v1p50xsxFclHNt54XkS9qDYbkGIyc63eBGW8vnJZwc/mmgf+RFBOrQOfMZIQnAAfbbMNUJsdKl3AQ==}
'@miyaneee/rollup-plugin-json5@1.2.0':
resolution: {integrity: sha512-JjTIaXZp9WzhUHpElrqPnl1AzBi/rvRs065F71+aTmlqvTMVkdbjZ8vfFl4nRlgJy+TPBw69ZK4pwFdmOAt4aA==}
@ -1253,9 +1249,6 @@ packages:
'@types/parse-path@7.0.3':
resolution: {integrity: sha512-LriObC2+KYZD3FzCrgWGv/qufdUy4eXrxcLgQMfYXgPbLIecKIsVBaQgUPmxSSLcjmYbDTQbMgr6qr6l/eb7Bg==}
'@types/raf@3.4.3':
resolution: {integrity: sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==}
'@types/resolve@1.20.2':
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
@ -1585,11 +1578,6 @@ packages:
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
atob@2.1.2:
resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==}
engines: {node: '>= 4.5.0'}
hasBin: true
autoprefixer@10.4.20:
resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==}
engines: {node: ^10 || ^12 || >=14}
@ -1609,10 +1597,6 @@ packages:
bare-events@2.5.4:
resolution: {integrity: sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==}
base64-arraybuffer@1.0.2:
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
engines: {node: '>= 0.6.0'}
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@ -1650,11 +1634,6 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
btoa@1.2.1:
resolution: {integrity: sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==}
engines: {node: '>= 0.4.0'}
hasBin: true
buffer-crc32@1.0.0:
resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==}
engines: {node: '>=8.0.0'}
@ -1699,10 +1678,6 @@ packages:
caniuse-lite@1.0.30001699:
resolution: {integrity: sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==}
canvg@3.0.10:
resolution: {integrity: sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==}
engines: {node: '>=10.0.0'}
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
@ -1814,9 +1789,6 @@ packages:
resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
engines: {node: '>=12.13'}
core-js@3.40.0:
resolution: {integrity: sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==}
core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
@ -1853,9 +1825,6 @@ packages:
peerDependencies:
postcss: ^8.0.9
css-line-break@2.1.0:
resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
css-select@5.1.0:
resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
@ -2064,9 +2033,6 @@ packages:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'}
dompurify@2.5.8:
resolution: {integrity: sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==}
domutils@3.2.2:
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
@ -2304,9 +2270,6 @@ packages:
picomatch:
optional: true
fflate@0.8.2:
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
file-entry-cache@8.0.0:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
@ -2329,6 +2292,9 @@ packages:
flatted@3.3.2:
resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==}
flatted@3.3.3:
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
follow-redirects@1.15.9:
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
engines: {node: '>=4.0'}
@ -2476,10 +2442,6 @@ packages:
resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
engines: {node: '>=8'}
html2canvas@1.4.1:
resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==}
engines: {node: '>=8.0.0'}
http-errors@2.0.0:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
@ -2708,9 +2670,6 @@ packages:
jsonfile@6.1.0:
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
jspdf@2.5.2:
resolution: {integrity: sha512-myeX9c+p7znDWPk0eTrujCzNjT+CXdXyk7YmJq5nD5V7uLLKmSXnlQ/Jn/kuo3X09Op70Apm0rQSnFWyGK8uEQ==}
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
@ -2991,8 +2950,8 @@ packages:
engines: {node: ^18 || >=20}
hasBin: true
nanoid@5.1.0:
resolution: {integrity: sha512-zDAl/llz8Ue/EblwSYwdxGBYfj46IM1dhjVi8dyp9LQffoIGxJEAHj2oeZ4uNcgycSRcQ83CnfcZqEJzVDLcDw==}
nanoid@5.1.2:
resolution: {integrity: sha512-b+CiXQCNMUGe0Ri64S9SXFcP9hogjAJ2Rd6GdVxhPLRm7mhGaM7VgOvCAJ1ZshfHbqVDI3uqTI5C8/GaKuLI7g==}
engines: {node: ^18 || >=20}
hasBin: true
@ -3213,9 +3172,6 @@ packages:
perfect-debounce@1.0.0:
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
performance-now@2.1.0:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@ -3436,6 +3392,9 @@ packages:
resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==}
engines: {node: ^14.13.1 || >=16.0.0}
print-js@1.6.0:
resolution: {integrity: sha512-BfnOIzSKbqGRtO4o0rnj/K3681BSd2QUrsIZy/+WdCIugjIswjmx3lDEZpXB2ruGf9d4b3YNINri81+J0FsBWg==}
process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
@ -3468,9 +3427,6 @@ packages:
radix3@1.1.2:
resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
raf@3.4.1:
resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==}
randombytes@2.1.0:
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
@ -3511,12 +3467,6 @@ packages:
resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==}
engines: {node: '>=4'}
regenerator-runtime@0.13.11:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
regexp-tree@0.1.27:
resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==}
hasBin: true
@ -3557,10 +3507,6 @@ packages:
rfdc@1.4.1:
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
rgbcolor@1.0.1:
resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==}
engines: {node: '>= 0.8.15'}
rimraf@5.0.10:
resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==}
hasBin: true
@ -3683,10 +3629,6 @@ packages:
resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
engines: {node: '>=0.10.0'}
stackblur-canvas@2.7.0:
resolution: {integrity: sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==}
engines: {node: '>=0.1.14'}
standard-as-callback@2.1.0:
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
@ -3763,10 +3705,6 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
svg-pathdata@6.0.3:
resolution: {integrity: sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==}
engines: {node: '>=12.0.0'}
svg-tags@1.0.0:
resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
@ -3830,9 +3768,6 @@ packages:
text-decoder@1.2.3:
resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==}
text-segmentation@1.0.3:
resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
thenify-all@1.6.0:
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
engines: {node: '>=0.8'}
@ -4121,9 +4056,6 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
utrie@1.0.2:
resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
vaul-vue@0.2.1:
resolution: {integrity: sha512-iF91R1JQZzxb9mb9uGNHYv8rVFxR5bL5Fj51iqvyXjzMPAzNMciCrXb9OUBu2NdlhcF6rVtEADXnQoTY+pKIeA==}
peerDependencies:
@ -4654,10 +4586,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@babel/runtime@7.26.7':
dependencies:
regenerator-runtime: 0.14.1
'@babel/standalone@7.26.8': {}
'@babel/template@7.26.8':
@ -4793,15 +4721,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@eslint/core@0.10.0':
dependencies:
'@types/json-schema': 7.0.15
'@eslint/core@0.11.0':
dependencies:
'@types/json-schema': 7.0.15
'@eslint/eslintrc@3.2.0':
'@eslint/core@0.12.0':
dependencies:
'@types/json-schema': 7.0.15
'@eslint/eslintrc@3.3.0':
dependencies:
ajv: 6.12.6
debug: 4.4.0(supports-color@9.4.0)
@ -4819,9 +4747,9 @@ snapshots:
'@eslint/object-schema@2.1.6': {}
'@eslint/plugin-kit@0.2.5':
'@eslint/plugin-kit@0.2.7':
dependencies:
'@eslint/core': 0.10.0
'@eslint/core': 0.12.0
levn: 0.4.1
'@floating-ui/core@1.6.9':
@ -4855,7 +4783,7 @@ snapshots:
'@humanwhocodes/retry@0.3.1': {}
'@humanwhocodes/retry@0.4.1': {}
'@humanwhocodes/retry@0.4.2': {}
'@iconify-json/lucide@1.2.26':
dependencies:
@ -5034,7 +4962,7 @@ snapshots:
- encoding
- supports-color
'@mendable/firecrawl-js@1.17.0(ws@8.18.0)':
'@mendable/firecrawl-js@1.18.2(ws@8.18.0)':
dependencies:
axios: 1.7.9
isows: 1.0.6(ws@8.18.0)
@ -5845,9 +5773,6 @@ snapshots:
'@types/parse-path@7.0.3': {}
'@types/raf@3.4.3':
optional: true
'@types/resolve@1.20.2': {}
'@types/web-bluetooth@0.0.20': {}
@ -6277,8 +6202,6 @@ snapshots:
asynckit@0.4.0: {}
atob@2.1.2: {}
autoprefixer@10.4.20(postcss@8.5.1):
dependencies:
browserslist: 4.24.4
@ -6304,9 +6227,6 @@ snapshots:
bare-events@2.5.4:
optional: true
base64-arraybuffer@1.0.2:
optional: true
base64-js@1.5.1: {}
binary-extensions@2.3.0: {}
@ -6345,8 +6265,6 @@ snapshots:
node-releases: 2.0.19
update-browserslist-db: 1.1.2(browserslist@4.24.4)
btoa@1.2.1: {}
buffer-crc32@1.0.0: {}
buffer-from@1.1.2: {}
@ -6407,18 +6325,6 @@ snapshots:
caniuse-lite@1.0.30001699: {}
canvg@3.0.10:
dependencies:
'@babel/runtime': 7.26.7
'@types/raf': 3.4.3
core-js: 3.40.0
raf: 3.4.1
regenerator-runtime: 0.13.11
rgbcolor: 1.0.1
stackblur-canvas: 2.7.0
svg-pathdata: 6.0.3
optional: true
chalk@4.1.2:
dependencies:
ansi-styles: 4.3.0
@ -6518,9 +6424,6 @@ snapshots:
dependencies:
is-what: 4.1.16
core-js@3.40.0:
optional: true
core-util-is@1.0.3: {}
crc-32@1.2.2: {}
@ -6554,11 +6457,6 @@ snapshots:
dependencies:
postcss: 8.5.1
css-line-break@2.1.0:
dependencies:
utrie: 1.0.2
optional: true
css-select@5.1.0:
dependencies:
boolbase: 1.0.0
@ -6737,9 +6635,6 @@ snapshots:
dependencies:
domelementtype: 2.3.0
dompurify@2.5.8:
optional: true
domutils@3.2.2:
dependencies:
dom-serializer: 2.0.0
@ -6877,12 +6772,12 @@ snapshots:
'@eslint-community/regexpp': 4.12.1
'@eslint/config-array': 0.19.2
'@eslint/core': 0.11.0
'@eslint/eslintrc': 3.2.0
'@eslint/eslintrc': 3.3.0
'@eslint/js': 9.20.0
'@eslint/plugin-kit': 0.2.5
'@eslint/plugin-kit': 0.2.7
'@humanfs/node': 0.16.6
'@humanwhocodes/module-importer': 1.0.1
'@humanwhocodes/retry': 0.4.1
'@humanwhocodes/retry': 0.4.2
'@types/estree': 1.0.6
'@types/json-schema': 7.0.15
ajv: 6.12.6
@ -7009,8 +6904,6 @@ snapshots:
optionalDependencies:
picomatch: 4.0.2
fflate@0.8.2: {}
file-entry-cache@8.0.0:
dependencies:
flat-cache: 4.0.1
@ -7028,11 +6921,13 @@ snapshots:
flat-cache@4.0.1:
dependencies:
flatted: 3.3.2
flatted: 3.3.3
keyv: 4.5.4
flatted@3.3.2: {}
flatted@3.3.3: {}
follow-redirects@1.15.9: {}
fontaine@0.5.0:
@ -7199,12 +7094,6 @@ snapshots:
html-tags@3.3.1: {}
html2canvas@1.4.1:
dependencies:
css-line-break: 2.1.0
text-segmentation: 1.0.3
optional: true
http-errors@2.0.0:
dependencies:
depd: 2.0.0
@ -7408,18 +7297,6 @@ snapshots:
optionalDependencies:
graceful-fs: 4.2.11
jspdf@2.5.2:
dependencies:
'@babel/runtime': 7.26.7
atob: 2.1.2
btoa: 1.2.1
fflate: 0.8.2
optionalDependencies:
canvg: 3.0.10
core-js: 3.40.0
dompurify: 2.5.8
html2canvas: 1.4.1
keyv@4.5.4:
dependencies:
json-buffer: 3.0.1
@ -7666,7 +7543,7 @@ snapshots:
nanoid@5.0.9: {}
nanoid@5.1.0: {}
nanoid@5.1.2: {}
nanotar@0.2.0: {}
@ -8071,9 +7948,6 @@ snapshots:
perfect-debounce@1.0.0: {}
performance-now@2.1.0:
optional: true
picocolors@1.1.1: {}
picomatch@2.3.1: {}
@ -8273,6 +8147,8 @@ snapshots:
pretty-bytes@6.1.1: {}
print-js@1.6.0: {}
process-nextick-args@2.0.1: {}
process@0.11.10: {}
@ -8302,18 +8178,13 @@ snapshots:
aria-hidden: 1.2.4
defu: 6.1.4
fast-deep-equal: 3.1.3
nanoid: 5.1.0
nanoid: 5.1.2
vue: 3.5.13(typescript@5.7.3)
transitivePeerDependencies:
- '@vue/composition-api'
radix3@1.1.2: {}
raf@3.4.1:
dependencies:
performance-now: 2.1.0
optional: true
randombytes@2.1.0:
dependencies:
safe-buffer: 5.2.1
@ -8361,11 +8232,6 @@ snapshots:
dependencies:
redis-errors: 1.2.0
regenerator-runtime@0.13.11:
optional: true
regenerator-runtime@0.14.1: {}
regexp-tree@0.1.27: {}
reka-ui@1.0.0-alpha.8(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)):
@ -8405,9 +8271,6 @@ snapshots:
rfdc@1.4.1: {}
rgbcolor@1.0.1:
optional: true
rimraf@5.0.10:
dependencies:
glob: 10.4.5
@ -8546,9 +8409,6 @@ snapshots:
speakingurl@14.0.1: {}
stackblur-canvas@2.7.0:
optional: true
standard-as-callback@2.1.0: {}
statuses@2.0.1: {}
@ -8630,9 +8490,6 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
svg-pathdata@6.0.3:
optional: true
svg-tags@1.0.0: {}
svgo@3.3.2:
@ -8707,11 +8564,6 @@ snapshots:
dependencies:
b4a: 1.6.7
text-segmentation@1.0.3:
dependencies:
utrie: 1.0.2
optional: true
thenify-all@1.6.0:
dependencies:
thenify: 3.3.1
@ -9042,11 +8894,6 @@ snapshots:
util-deprecate@1.0.2: {}
utrie@1.0.2:
dependencies:
base64-arraybuffer: 1.0.2
optional: true
vaul-vue@0.2.1(radix-vue@1.9.13(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3)):
dependencies:
'@vueuse/core': 10.11.1(vue@3.5.13(typescript@5.7.3))

Binary file not shown.