diff --git a/apps/x/apps/main/package.json b/apps/x/apps/main/package.json index c777a237..74cb1598 100644 --- a/apps/x/apps/main/package.json +++ b/apps/x/apps/main/package.json @@ -17,6 +17,7 @@ "@x/shared": "workspace:*", "chokidar": "^4.0.3", "electron-squirrel-startup": "^1.0.1", + "html-to-docx": "^1.8.0", "mammoth": "^1.11.0", "papaparse": "^5.5.3", "pdf-parse": "^2.4.5", diff --git a/apps/x/apps/main/src/html-to-docx.d.ts b/apps/x/apps/main/src/html-to-docx.d.ts new file mode 100644 index 00000000..24f298ef --- /dev/null +++ b/apps/x/apps/main/src/html-to-docx.d.ts @@ -0,0 +1,7 @@ +declare module 'html-to-docx' { + export default function htmlToDocx( + htmlString: string, + headerHTMLString?: string, + options?: Record, + ): Promise; +} diff --git a/apps/x/apps/main/src/ipc.ts b/apps/x/apps/main/src/ipc.ts index 330b949a..84e2b3e4 100644 --- a/apps/x/apps/main/src/ipc.ts +++ b/apps/x/apps/main/src/ipc.ts @@ -1,4 +1,4 @@ -import { ipcMain, BrowserWindow, shell } from 'electron'; +import { ipcMain, BrowserWindow, shell, dialog } from 'electron'; import { ipc } from '@x/shared'; import path from 'node:path'; import os from 'node:os'; @@ -41,6 +41,71 @@ import { search } from '@x/core/dist/search/search.js'; import { versionHistory, voice } from '@x/core'; import { classifySchedule } from '@x/core/dist/knowledge/inline_tasks.js'; +/** + * Convert markdown to a styled HTML document for PDF/DOCX export. + */ +function markdownToHtml(markdown: string, title: string): string { + // Simple markdown to HTML conversion for export purposes + let html = markdown + // Resolve wiki links [[Folder/Note Name]] or [[Folder/Note Name|Display]] to plain text + .replace(/\[\[([^\]|]+)\|([^\]]+)\]\]/g, (_match, _path, display) => display.trim()) + .replace(/\[\[([^\]]+)\]\]/g, (_match, linkPath: string) => { + // Use the last segment (filename) as the display name + const segments = linkPath.trim().split('/') + return segments[segments.length - 1] + }) + // Escape HTML entities (but preserve markdown syntax) + .replace(/&/g, '&') + .replace(//g, '>') + + // Headings (must come before other processing) + html = html.replace(/^######\s+(.+)$/gm, '
$1
') + html = html.replace(/^#####\s+(.+)$/gm, '
$1
') + html = html.replace(/^####\s+(.+)$/gm, '

$1

') + html = html.replace(/^###\s+(.+)$/gm, '

$1

') + html = html.replace(/^##\s+(.+)$/gm, '

$1

') + html = html.replace(/^#\s+(.+)$/gm, '

$1

') + + // Bold and italic + html = html.replace(/\*\*\*(.+?)\*\*\*/g, '$1') + html = html.replace(/\*\*(.+?)\*\*/g, '$1') + html = html.replace(/\*(.+?)\*/g, '$1') + + // Inline code + html = html.replace(/`([^`]+)`/g, '$1') + + // Horizontal rules + html = html.replace(/^---$/gm, '
') + + // Unordered lists + html = html.replace(/^[-*]\s+(.+)$/gm, '
  • $1
  • ') + + // Links + html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1') + + // Blockquotes + html = html.replace(/^>\s+(.+)$/gm, '
    $1
    ') + + // Paragraphs: wrap remaining lines that aren't already wrapped in HTML tags + html = html.replace(/^(?!<[a-z/])((?!^\s*$).+)$/gm, '

    $1

    ') + + // Clean up consecutive list items into lists + html = html.replace(/(
  • .*<\/li>\n?)+/g, (match) => `
      ${match}
    `) + + return ` +${title} +${html}` +} + type InvokeChannels = ipc.InvokeChannels; type IPCChannels = ipc.IPCChannels; @@ -567,6 +632,68 @@ export function setupIpcHandlers() { return search(args.query, args.limit, args.types); }, // Inline task schedule classification + 'export:note': async (event, args) => { + const { markdown, format, title } = args; + const sanitizedTitle = title.replace(/[/\\?%*:|"<>]/g, '-').trim() || 'Untitled'; + + const filterMap: Record = { + md: [{ name: 'Markdown', extensions: ['md'] }], + pdf: [{ name: 'PDF', extensions: ['pdf'] }], + docx: [{ name: 'Word Document', extensions: ['docx'] }], + }; + + const win = BrowserWindow.fromWebContents(event.sender); + const result = await dialog.showSaveDialog(win!, { + defaultPath: `${sanitizedTitle}.${format}`, + filters: filterMap[format], + }); + + if (result.canceled || !result.filePath) { + return { success: false }; + } + + const filePath = result.filePath; + + if (format === 'md') { + await fs.writeFile(filePath, markdown, 'utf8'); + return { success: true }; + } + + if (format === 'pdf') { + // Render markdown as HTML in a hidden window, then print to PDF + const htmlContent = markdownToHtml(markdown, sanitizedTitle); + const hiddenWin = new BrowserWindow({ + show: false, + width: 800, + height: 600, + webPreferences: { offscreen: true }, + }); + await hiddenWin.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(htmlContent)}`); + // Small delay to ensure CSS/fonts render + await new Promise(resolve => setTimeout(resolve, 300)); + const pdfBuffer = await hiddenWin.webContents.printToPDF({ + printBackground: true, + pageSize: 'A4', + }); + hiddenWin.destroy(); + await fs.writeFile(filePath, pdfBuffer); + return { success: true }; + } + + if (format === 'docx') { + const htmlContent = markdownToHtml(markdown, sanitizedTitle); + const { default: htmlToDocx } = await import('html-to-docx'); + const docxBuffer = await htmlToDocx(htmlContent, undefined, { + table: { row: { cantSplit: true } }, + footer: false, + header: false, + }); + await fs.writeFile(filePath, Buffer.from(docxBuffer as ArrayBuffer)); + return { success: true }; + } + + return { success: false, error: 'Unknown format' }; + }, 'inline-task:classifySchedule': async (_event, args) => { const schedule = await classifySchedule(args.instruction); return { schedule }; diff --git a/apps/x/apps/renderer/src/App.tsx b/apps/x/apps/renderer/src/App.tsx index 9dd70810..45044f5f 100644 --- a/apps/x/apps/renderer/src/App.tsx +++ b/apps/x/apps/renderer/src/App.tsx @@ -3806,6 +3806,15 @@ function App() { } }} editable={!isViewingHistory} + onExport={async (format) => { + const markdown = tabContent + const title = getBaseName(tab.path) + try { + await window.ipc.invoke('export:note', { markdown, format, title }) + } catch (err) { + console.error('Export failed:', err) + } + }} /> ) diff --git a/apps/x/apps/renderer/src/components/editor-toolbar.tsx b/apps/x/apps/renderer/src/components/editor-toolbar.tsx index bf258633..72b1cb35 100644 --- a/apps/x/apps/renderer/src/components/editor-toolbar.tsx +++ b/apps/x/apps/renderer/src/components/editor-toolbar.tsx @@ -25,18 +25,30 @@ import { ExternalLinkIcon, Trash2Icon, ImageIcon, + DownloadIcon, + FileTextIcon, + FileIcon, + FileTypeIcon, } from 'lucide-react' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu' interface EditorToolbarProps { editor: Editor | null onSelectionHighlight?: (range: { from: number; to: number } | null) => void onImageUpload?: (file: File) => Promise | void + onExport?: (format: 'md' | 'pdf' | 'docx') => void } export function EditorToolbar({ editor, onSelectionHighlight, onImageUpload, + onExport, }: EditorToolbarProps) { const [linkUrl, setLinkUrl] = useState('') const [isLinkPopoverOpen, setIsLinkPopoverOpen] = useState(false) @@ -341,6 +353,38 @@ export function EditorToolbar({ )} + + {/* Export */} + {onExport && ( + <> +
    + + + + + + onExport('md')}> + + Markdown (.md) + + onExport('pdf')}> + + PDF (.pdf) + + onExport('docx')}> + + Word (.docx) + + + + + )}
    ) } diff --git a/apps/x/apps/renderer/src/components/markdown-editor.tsx b/apps/x/apps/renderer/src/components/markdown-editor.tsx index 6b5e0c08..776b5a42 100644 --- a/apps/x/apps/renderer/src/components/markdown-editor.tsx +++ b/apps/x/apps/renderer/src/components/markdown-editor.tsx @@ -219,6 +219,7 @@ interface MarkdownEditorProps { editable?: boolean frontmatter?: string | null onFrontmatterChange?: (raw: string | null) => void + onExport?: (format: 'md' | 'pdf' | 'docx') => void } type WikiLinkMatch = { @@ -309,6 +310,7 @@ export function MarkdownEditor({ editable = true, frontmatter, onFrontmatterChange, + onExport, }: MarkdownEditorProps) { const isInternalUpdate = useRef(false) const wrapperRef = useRef(null) @@ -1071,6 +1073,7 @@ export function MarkdownEditor({ editor={editor} onSelectionHighlight={setSelectionHighlight} onImageUpload={handleImageUploadWithPlaceholder} + onExport={onExport} /> {(frontmatter !== undefined) && onFrontmatterChange && ( =8.0'} + + '@oozcitak/dom@1.15.6': + resolution: {integrity: sha512-k4uEIa6DI3FCrFJMGq/05U/59WnS9DjME0kaPqBRCJAqBTkmopbYV1Xs4qFKbDJ/9wOg8W97p+1E0heng/LH7g==} + engines: {node: '>=8.0'} + + '@oozcitak/infra@1.0.3': + resolution: {integrity: sha512-9O2wxXGnRzy76O1XUxESxDGsXT5kzETJPvYbreO4mv6bqe1+YSuux2cZTagjJ/T4UfEwFJz5ixanOqB0QgYAag==} + engines: {node: '>=6.0'} + + '@oozcitak/infra@1.0.5': + resolution: {integrity: sha512-o+zZH7M6l5e3FaAWy3ojaPIVN5eusaYPrKm6MZQt0DKNdgXa2wDYExjpP0t/zx+GoQgQKzLu7cfD8rHCLt8JrQ==} + engines: {node: '>=6.0'} + + '@oozcitak/url@1.0.0': + resolution: {integrity: sha512-LGrMeSxeLzsdaitxq3ZmBRVOrlRRQIgNNci6L0VRnOKlJFuRIkNm4B+BObXPCJA6JT5bEJtrrwjn30jueHJYZQ==} + engines: {node: '>=8.0'} + + '@oozcitak/util@1.0.1': + resolution: {integrity: sha512-dFwFqcKrQnJ2SapOmRD1nQWEZUtbtIy9Y6TyJquzsalWNJsKIPxmTI0KG6Ypyl8j7v89L2wixH9fQDNrF78hKg==} + engines: {node: '>=6.0'} + + '@oozcitak/util@1.0.2': + resolution: {integrity: sha512-4n8B1cWlJleSOSba5gxsMcN4tO8KkkcvXhNWW+ADqvq9Xj+Lrl9uCa90GRpjekqQJyt84aUX015DG81LFpZYXA==} + engines: {node: '>=6.0'} + + '@oozcitak/util@8.0.0': + resolution: {integrity: sha512-+9Hq6yuoq/3TRV/n/xcpydGBq2qN2/DEDMqNTG7rm95K6ZE2/YY/sPyx62+1n8QsE9O26e5M1URlXsk+AnN9Jw==} + engines: {node: '>=6.0'} + + '@oozcitak/util@8.3.3': + resolution: {integrity: sha512-Ufpab7G5PfnEhQyy5kDg9C8ltWJjsVT1P/IYqacjstaqydG4Q21HAT2HUZQYBrC/a1ZLKCz87pfydlDvv8y97w==} + engines: {node: '>=6.0'} + + '@oozcitak/util@8.3.4': + resolution: {integrity: sha512-6gH/bLQJSJEg7OEpkH4wGQdA8KXHRbzL1YkGyUO12YNAgV3jxKy4K9kvfXj4+9T0OLug5k58cnPCKSSIKzp7pg==} + engines: {node: '>=8.0'} + '@openrouter/ai-sdk-provider@1.5.4': resolution: {integrity: sha512-xrSQPUIH8n9zuyYZR0XK7Ba0h2KsjJcMkxnwaYfmv13pKs3sDkjPzVPPhlhzqBGddHb5cFEwJ9VFuFeDcxCDSw==} engines: {node: '>=18'} @@ -3733,6 +3776,9 @@ packages: brotli@1.3.3: resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} + browser-split@0.0.1: + resolution: {integrity: sha512-JhvgRb2ihQhsljNda3BI8/UcRHVzrVwo3Q+P8vDtSiyobXuFpuZ9mq+MbRGMnC22CjW3RrfXdg6j6ITX8M+7Ow==} + browserify-zlib@0.2.0: resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} @@ -3794,6 +3840,9 @@ packages: camel-case@4.1.2: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + camelize@1.0.1: + resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} + caniuse-lite@1.0.30001761: resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==} @@ -4289,12 +4338,24 @@ packages: dir-compare@4.2.0: resolution: {integrity: sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==} + dom-serializer@0.2.2: + resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dom-walk@0.1.2: + resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==} + + domelementtype@1.3.1: + resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==} + domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + domhandler@2.4.2: + resolution: {integrity: sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==} + domhandler@5.0.3: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} @@ -4302,6 +4363,9 @@ packages: dompurify@3.3.1: resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==} + domutils@1.7.0: + resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==} + domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -4386,6 +4450,16 @@ packages: resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} engines: {node: '>=10.13.0'} + ent@2.2.2: + resolution: {integrity: sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==} + engines: {node: '>= 0.4'} + + entities@1.1.2: + resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==} + + entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -4404,6 +4478,9 @@ packages: error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + error@4.4.0: + resolution: {integrity: sha512-SNDKualLUtT4StGFP7xNfuFybL2f6iJujFtrWuvJqGbVQGaN+adE23veqzPz1hjUjTunLi2EnJ+0SJxtbJreKw==} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -4523,6 +4600,9 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + ev-store@7.0.0: + resolution: {integrity: sha512-otazchNRnGzp2YarBJ+GXKVGvhxVATB1zmaStxJBYet0Dyq7A9VhH8IUEB/gRcL6Ch52lfpgPTRJ2m49epyMsQ==} + event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -4866,6 +4946,9 @@ packages: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} engines: {node: '>=10'} + global@4.4.0: + resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -5009,12 +5092,24 @@ packages: hsl-to-rgb-for-reals@1.1.1: resolution: {integrity: sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==} + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + + html-to-docx@1.8.0: + resolution: {integrity: sha512-IiMBWIqXM4+cEsW//RKoonWV7DlXAJBmmKI73XJSVWTIXjGUaxSr2ck1jqzVRZknpvO8xsFnVicldKVAWrBYBA==} + + html-to-vdom@0.7.0: + resolution: {integrity: sha512-k+d2qNkbx0JO00KezQsNcn6k2I/xSBP4yXYFLvXbcasTTDh+RDLUJS3puxqyNnpdyXWRHFGoKU7cRmby8/APcQ==} + html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + htmlparser2@3.10.1: + resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==} + http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} @@ -5072,6 +5167,14 @@ packages: engines: {node: '>=6.9.0'} hasBin: true + image-size@1.2.1: + resolution: {integrity: sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==} + engines: {node: '>=16.x'} + hasBin: true + + image-to-base64@2.2.0: + resolution: {integrity: sha512-Z+aMwm/91UOQqHhrz7Upre2ytKhWejZlWV/JxUTD1sT7GWWKFDJUEV5scVQKnkzSgPHFuQBUEWcanO+ma0PSVw==} + immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} @@ -5091,6 +5194,9 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} + individual@3.0.0: + resolution: {integrity: sha512-rUY5vtT748NMRbEMrTNiFfy29BgGZwGXUi2NFUVMWQrogSLzlJvQV9eeMWi+g1aVaQ53tpyLAQtd5x/JH0Nh1g==} + infer-owner@1.0.4: resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} @@ -5191,6 +5297,9 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-object@1.0.2: + resolution: {integrity: sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==} + is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -5201,6 +5310,10 @@ packages: is-property@1.0.2: resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + is-stream@1.1.0: resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} engines: {node: '>=0.10.0'} @@ -5795,6 +5908,9 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} + min-document@2.19.2: + resolution: {integrity: sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==} + minimatch@10.1.1: resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} engines: {node: 20 || >=22} @@ -5924,6 +6040,9 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + next-tick@0.2.2: + resolution: {integrity: sha512-f7h4svPtl+QidoBv4taKXUjJ70G2asaZ8G28nS0OkqaalX8dwwrtWtyxEDPK62AC00ur/+/E0pUwBwY5EPn15Q==} + nice-try@1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} @@ -6384,6 +6503,9 @@ packages: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} engines: {node: '>=6'} + punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -6672,6 +6794,10 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -6867,6 +6993,9 @@ packages: peerDependencies: react: ^18.0.0 || ^19.0.0 + string-template@0.2.1: + resolution: {integrity: sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -7256,6 +7385,9 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + virtual-dom@2.1.1: + resolution: {integrity: sha512-wb6Qc9Lbqug0kRqo/iuApfBpJJAq14Sk1faAnSmtqXiwahg7PVTvWMs9L02Z8nNIMqbwsxzBAA90bbtRLbw0zg==} + vite-compatible-readable-stream@3.6.1: resolution: {integrity: sha512-t20zYkrSf868+j/p31cRIGN28Phrjm3nRSLR2fyc2tiWi4cZGVdv68yNlwnIINTkMTmPoMiSlc0OadaO7DXZaQ==} engines: {node: '>= 6'} @@ -7405,11 +7537,21 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + x-is-array@0.1.0: + resolution: {integrity: sha512-goHPif61oNrr0jJgsXRfc8oqtYzvfiMJpTqwE7Z4y9uH+T3UozkGqQ4d2nX9mB9khvA8U2o/UbPOFjgC7hLWIA==} + + x-is-string@0.1.0: + resolution: {integrity: sha512-GojqklwG8gpzOVEVki5KudKNoq7MbbjYZCbyWzEz7tyPA7eleiE0+ePwOWQQRb5fm86rD3S8Tc0tSFf3AOv50w==} + xlsx@0.18.5: resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} engines: {node: '>=0.8'} hasBin: true + xmlbuilder2@2.1.2: + resolution: {integrity: sha512-PI710tmtVlQ5VmwzbRTuhmVhKnj9pM8Si+iOZCV2g2SNo3gCrpzR2Ka9wNzZtqfD+mnP+xkrqoNy0sjKZqP4Dg==} + engines: {node: '>=8.0'} + xmlbuilder@10.1.1: resolution: {integrity: sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==} engines: {node: '>=4.0'} @@ -9189,6 +9331,41 @@ snapshots: dependencies: '@octokit/openapi-types': 12.11.0 + '@oozcitak/dom@1.15.5': + dependencies: + '@oozcitak/infra': 1.0.5 + '@oozcitak/url': 1.0.0 + '@oozcitak/util': 8.0.0 + + '@oozcitak/dom@1.15.6': + dependencies: + '@oozcitak/infra': 1.0.5 + '@oozcitak/url': 1.0.0 + '@oozcitak/util': 8.3.4 + + '@oozcitak/infra@1.0.3': + dependencies: + '@oozcitak/util': 1.0.1 + + '@oozcitak/infra@1.0.5': + dependencies: + '@oozcitak/util': 8.0.0 + + '@oozcitak/url@1.0.0': + dependencies: + '@oozcitak/infra': 1.0.3 + '@oozcitak/util': 1.0.2 + + '@oozcitak/util@1.0.1': {} + + '@oozcitak/util@1.0.2': {} + + '@oozcitak/util@8.0.0': {} + + '@oozcitak/util@8.3.3': {} + + '@oozcitak/util@8.3.4': {} + '@openrouter/ai-sdk-provider@1.5.4(ai@5.0.133(zod@4.2.1))(zod@4.2.1)': dependencies: '@openrouter/sdk': 0.1.27 @@ -11647,6 +11824,8 @@ snapshots: dependencies: base64-js: 1.5.1 + browser-split@0.0.1: {} + browserify-zlib@0.2.0: dependencies: pako: 1.0.11 @@ -11741,6 +11920,8 @@ snapshots: pascal-case: 3.1.2 tslib: 2.8.1 + camelize@1.0.1: {} + caniuse-lite@1.0.30001761: {} ccount@2.0.1: {} @@ -12224,14 +12405,27 @@ snapshots: minimatch: 3.1.2 p-limit: 3.1.0 + dom-serializer@0.2.2: + dependencies: + domelementtype: 2.3.0 + entities: 2.2.0 + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 entities: 4.5.0 + dom-walk@0.1.2: {} + + domelementtype@1.3.1: {} + domelementtype@2.3.0: {} + domhandler@2.4.2: + dependencies: + domelementtype: 1.3.1 + domhandler@5.0.3: dependencies: domelementtype: 2.3.0 @@ -12240,6 +12434,11 @@ snapshots: optionalDependencies: '@types/trusted-types': 2.0.7 + domutils@1.7.0: + dependencies: + dom-serializer: 0.2.2 + domelementtype: 1.3.1 + domutils@3.2.2: dependencies: dom-serializer: 2.0.0 @@ -12380,6 +12579,17 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.0 + ent@2.2.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + punycode: 1.4.1 + safe-regex-test: 1.1.0 + + entities@1.1.2: {} + + entities@2.2.0: {} + entities@4.5.0: {} entities@6.0.1: {} @@ -12392,6 +12602,12 @@ snapshots: dependencies: is-arrayish: 0.2.1 + error@4.4.0: + dependencies: + camelize: 1.0.1 + string-template: 0.2.1 + xtend: 4.0.2 + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -12573,6 +12789,10 @@ snapshots: etag@1.8.1: {} + ev-store@7.0.0: + dependencies: + individual: 3.0.0 + event-target-shim@5.0.1: {} eventemitter3@5.0.1: {} @@ -13016,6 +13236,11 @@ snapshots: dependencies: ini: 2.0.0 + global@4.4.0: + dependencies: + min-document: 2.19.2 + process: 0.11.10 + globals@14.0.0: {} globals@16.5.0: {} @@ -13262,10 +13487,44 @@ snapshots: hsl-to-rgb-for-reals@1.1.1: {} + html-entities@2.6.0: {} + + html-to-docx@1.8.0(encoding@0.1.13): + dependencies: + '@oozcitak/dom': 1.15.6 + '@oozcitak/util': 8.3.4 + color-name: 1.1.4 + html-entities: 2.6.0 + html-to-vdom: 0.7.0 + image-size: 1.2.1 + image-to-base64: 2.2.0(encoding@0.1.13) + jszip: 3.10.1 + lodash: 4.17.21 + mime-types: 2.1.35 + nanoid: 3.3.11 + virtual-dom: 2.1.1 + xmlbuilder2: 2.1.2 + transitivePeerDependencies: + - encoding + + html-to-vdom@0.7.0: + dependencies: + ent: 2.2.2 + htmlparser2: 3.10.1 + html-url-attributes@3.0.1: {} html-void-elements@3.0.0: {} + htmlparser2@3.10.1: + dependencies: + domelementtype: 1.3.1 + domhandler: 2.4.2 + domutils: 1.7.0 + entities: 1.1.2 + inherits: 2.0.4 + readable-stream: 3.6.2 + http-cache-semantics@4.2.0: {} http-errors@2.0.1: @@ -13330,6 +13589,16 @@ snapshots: image-size@0.7.5: optional: true + image-size@1.2.1: + dependencies: + queue: 6.0.2 + + image-to-base64@2.2.0(encoding@0.1.13): + dependencies: + node-fetch: 2.7.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + immediate@3.0.6: {} import-fresh@3.3.1: @@ -13344,6 +13613,8 @@ snapshots: indent-string@4.0.0: {} + individual@3.0.0: {} + infer-owner@1.0.4: {} inflight@1.0.6: @@ -13418,6 +13689,8 @@ snapshots: is-number@7.0.0: {} + is-object@1.0.2: {} + is-plain-obj@4.1.0: {} is-promise@4.0.0: {} @@ -13425,6 +13698,13 @@ snapshots: is-property@1.0.2: optional: true + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + is-stream@1.1.0: {} is-stream@2.0.1: {} @@ -14270,6 +14550,10 @@ snapshots: mimic-response@3.1.0: {} + min-document@2.19.2: + dependencies: + dom-walk: 0.1.2 + minimatch@10.1.1: dependencies: '@isaacs/brace-expansion': 5.0.0 @@ -14385,6 +14669,8 @@ snapshots: neo-async@2.6.2: {} + next-tick@0.2.2: {} + nice-try@1.0.5: {} no-case@3.0.4: @@ -14873,6 +15159,8 @@ snapshots: punycode.js@2.3.1: {} + punycode@1.4.1: {} + punycode@2.3.1: {} pusher-js@8.4.0: @@ -15284,6 +15572,12 @@ snapshots: safe-buffer@5.2.1: {} + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + safer-buffer@2.1.2: {} scheduler@0.25.0-rc-603e6108-20241029: {} @@ -15534,6 +15828,8 @@ snapshots: - micromark-util-types - supports-color + string-template@0.2.1: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -15922,6 +16218,17 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 + virtual-dom@2.1.1: + dependencies: + browser-split: 0.0.1 + error: 4.4.0 + ev-store: 7.0.0 + global: 4.4.0 + is-object: 1.0.2 + next-tick: 0.2.2 + x-is-array: 0.1.0 + x-is-string: 0.1.0 + vite-compatible-readable-stream@3.6.1: dependencies: inherits: 2.0.4 @@ -16073,6 +16380,10 @@ snapshots: wrappy@1.0.2: {} + x-is-array@0.1.0: {} + + x-is-string@0.1.0: {} + xlsx@0.18.5: dependencies: adler-32: 1.3.1 @@ -16083,12 +16394,17 @@ snapshots: wmf: 1.0.2 word: 0.3.0 + xmlbuilder2@2.1.2: + dependencies: + '@oozcitak/dom': 1.15.5 + '@oozcitak/infra': 1.0.5 + '@oozcitak/util': 8.3.3 + xmlbuilder@10.1.1: {} xmlbuilder@15.1.1: {} - xtend@4.0.2: - optional: true + xtend@4.0.2: {} y18n@5.0.8: {}