From a7816ee34eca00afdb85953522410a63c56c72bc Mon Sep 17 00:00:00 2001 From: MocA-Love Date: Sat, 28 Mar 2026 02:09:32 +0900 Subject: [PATCH] feat(desktop): add Excel/spreadsheet file viewer with diff support Add the ability to view .xlsx, .xls, .xlsm, .xlsb, and .ods files natively instead of showing "Binary file preview not supported". Features: - Full spreadsheet rendering with ExcelJS (formatting, borders, merged cells, fonts, colors, theme colors with tint, rich text, row heights, text wrapping, vertical text) - Print area aware column/row range detection - Auto fit-to-width column scaling - Multiple sheet tab navigation - Side-by-side diff viewer with cell-level change highlighting - Diff navigation (Prev/Next) with synchronized scrolling - Git binary file read via new readGitFileBinary tRPC procedure --- apps/desktop/package.json | 1 + .../lib/trpc/routers/changes/file-contents.ts | 49 ++ .../WorkspaceFilePreviewContent.tsx | 10 + .../TabView/FileViewerPane/FileViewerPane.tsx | 4 + .../FileViewerContent/FileViewerContent.tsx | 37 +- .../SpreadsheetDiffViewer.tsx | 408 ++++++++++++++++ .../SpreadsheetViewer/SpreadsheetViewer.tsx | 266 +++++++++++ .../components/SpreadsheetViewer/index.ts | 2 + .../SpreadsheetViewer/parseWorkbook.ts | 452 ++++++++++++++++++ .../SpreadsheetViewer/useSpreadsheetData.ts | 78 +++ .../SpreadsheetViewer/useSpreadsheetDiff.ts | 316 ++++++++++++ apps/desktop/src/shared/file-types.ts | 10 + bun.lock | 131 +++++ 13 files changed, 1763 insertions(+), 1 deletion(-) create mode 100644 apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/SpreadsheetDiffViewer.tsx create mode 100644 apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/SpreadsheetViewer.tsx create mode 100644 apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/index.ts create mode 100644 apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/parseWorkbook.ts create mode 100644 apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/useSpreadsheetData.ts create mode 100644 apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/useSpreadsheetDiff.ts diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 79100913a92..acd5c2f79db 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -160,6 +160,7 @@ "dotenv": "^17.3.1", "drizzle-orm": "0.45.1", "electron-updater": "^6.7.3", + "exceljs": "^4.4.0", "execa": "^9.6.0", "express": "^5.1.0", "fast-glob": "^3.3.3", diff --git a/apps/desktop/src/lib/trpc/routers/changes/file-contents.ts b/apps/desktop/src/lib/trpc/routers/changes/file-contents.ts index f625d716294..50e24545bdb 100644 --- a/apps/desktop/src/lib/trpc/routers/changes/file-contents.ts +++ b/apps/desktop/src/lib/trpc/routers/changes/file-contents.ts @@ -1,3 +1,5 @@ +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; import type { FileContents } from "shared/changes-types"; import { detectLanguage } from "shared/detect-language"; import type { SimpleGit } from "simple-git"; @@ -5,8 +7,11 @@ import { z } from "zod"; import { publicProcedure, router } from "../.."; import { toRegisteredWorktreeRelativePath } from "../workspace-fs-service"; import { getSimpleGitWithShellPath } from "../workspaces/utils/git-client"; +import { getProcessEnvWithShellPath } from "../workspaces/utils/shell-env"; const MAX_FILE_SIZE = 2 * 1024 * 1024; +const MAX_BINARY_FILE_SIZE = 10 * 1024 * 1024; +const execFileAsync = promisify(execFile); export const createFileContentsRouter = () => { return router({ @@ -51,6 +56,50 @@ export const createFileContentsRouter = () => { }; }), + readGitFileBinary: publicProcedure + .input( + z.object({ + worktreePath: z.string(), + absolutePath: z.string(), + ref: z.string().default("HEAD"), + }), + ) + .query(async ({ input }): Promise<{ content: string | null }> => { + const relativePath = toRegisteredWorktreeRelativePath( + input.worktreePath, + input.absolutePath, + ); + const spec = `${input.ref}:${relativePath}`; + const git = await getSimpleGitWithShellPath(input.worktreePath); + + try { + const sizeOutput = await git.raw(["cat-file", "-s", spec]); + const blobSize = Number.parseInt(sizeOutput.trim(), 10); + if (!Number.isNaN(blobSize) && blobSize > MAX_BINARY_FILE_SIZE) { + return { content: null }; + } + } catch { + return { content: null }; + } + + try { + const env = await getProcessEnvWithShellPath(); + const { stdout } = await execFileAsync( + "git", + ["cat-file", "-p", spec], + { + cwd: input.worktreePath, + encoding: "buffer", + maxBuffer: MAX_BINARY_FILE_SIZE, + env, + }, + ); + return { content: (stdout as unknown as Buffer).toString("base64") }; + } catch { + return { content: null }; + } + }), + getGitOriginalContent: publicProcedure .input( z.object({ diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceFiles/components/WorkspaceFilePreview/components/WorkspaceFilePreviewContent/WorkspaceFilePreviewContent.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceFiles/components/WorkspaceFilePreview/components/WorkspaceFilePreviewContent/WorkspaceFilePreviewContent.tsx index e293635b91a..9114892b53e 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceFiles/components/WorkspaceFilePreview/components/WorkspaceFilePreviewContent/WorkspaceFilePreviewContent.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceFiles/components/WorkspaceFilePreview/components/WorkspaceFilePreviewContent/WorkspaceFilePreviewContent.tsx @@ -1,4 +1,6 @@ import { useFileDocument } from "@superset/workspace-client"; +import { SpreadsheetViewer } from "renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer"; +import { isSpreadsheetFile } from "shared/file-types"; interface WorkspaceFilePreviewContentProps { selectedFilePath: string; @@ -32,6 +34,14 @@ export function WorkspaceFilePreviewContent({ } if (document.state.kind === "binary") { + if (isSpreadsheetFile(selectedFilePath)) { + return ( + + ); + } return (
Binary files are not previewed yet diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx index c22537bfec2..c3ed1de0ea7 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx @@ -676,6 +676,10 @@ export function FileViewerPane({ )}
+ ); + } + if (viewMode === "diff") { if (isLoadingDiff) { return ( @@ -422,6 +448,15 @@ export function FileViewerContent({ ); } + if ( + rawFileData?.ok === false && + rawFileData.reason === "binary" && + isSpreadsheetFile(filePath) && + workspaceId + ) { + return ; + } + if (!rawFileData?.ok) { const errorMessage = rawFileData?.reason === "too-large" diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/SpreadsheetDiffViewer.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/SpreadsheetDiffViewer.tsx new file mode 100644 index 00000000000..f1af2950115 --- /dev/null +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/SpreadsheetDiffViewer.tsx @@ -0,0 +1,408 @@ +import { type RefObject, useCallback, useMemo, useRef, useState } from "react"; +import type { ChangeCategory } from "shared/changes-types"; +import useResizeObserver from "use-resize-observer"; +import type { ParsedCell, RichTextPart } from "./parseWorkbook"; +import { type DiffParsedRow, useSpreadsheetDiff } from "./useSpreadsheetDiff"; + +interface SpreadsheetDiffViewerProps { + workspaceId: string; + worktreePath: string; + filePath: string; + diffCategory?: ChangeCategory; + commitHash?: string; +} + +const ROW_NUM_COL_WIDTH = 30; + +const DIFF_BG = { + added: "rgba(34, 197, 94, 0.25)", + removed: "rgba(239, 68, 68, 0.25)", + modified: "rgba(59, 130, 246, 0.2)", +} as const; + +const DIFF_BORDER = { + added: "2px solid #22c55e", + removed: "2px solid #ef4444", + modified: "2px solid #3b82f6", +} as const; + +function RichTextContent({ parts }: { parts: RichTextPart[] }) { + return ( + <> + {parts.map((part, i) => { + const key = `${i}-${part.text.slice(0, 8)}`; + return Object.keys(part.style).length === 0 ? ( + {part.text} + ) : ( + + {part.text} + + ); + })} + + ); +} + +function CellContent({ cell }: { cell: ParsedCell }) { + if (cell.richText) return ; + return <>{cell.value}; +} + +function DiffTable({ + rows, + columnWidths, + label, + scrollRef, + peerScrollRef, +}: { + rows: DiffParsedRow[]; + columnWidths: number[]; + label: string; + scrollRef: RefObject; + peerScrollRef: RefObject; +}) { + const [containerWidth, setContainerWidth] = useState(null); + const isSyncingRef = useRef(false); + + const onResize = useCallback(({ width }: { width?: number }) => { + if (width) setContainerWidth(width); + }, []); + const { ref: sizeRef } = useResizeObserver({ onResize }); + + const scaledWidths = useMemo(() => { + if (!containerWidth) return columnWidths; + const total = ROW_NUM_COL_WIDTH + columnWidths.reduce((s, w) => s + w, 0); + if (total <= containerWidth) return columnWidths; + const available = containerWidth - ROW_NUM_COL_WIDTH; + const colTotal = columnWidths.reduce((s, w) => s + w, 0); + if (colTotal <= 0) return columnWidths; + return columnWidths.map((w) => Math.floor((w / colTotal) * available)); + }, [columnWidths, containerWidth]); + + const handleScroll = useCallback(() => { + if (isSyncingRef.current) { + isSyncingRef.current = false; + return; + } + const el = scrollRef.current; + const peer = peerScrollRef.current; + if (!el || !peer) return; + isSyncingRef.current = true; + peer.scrollTop = el.scrollTop; + peer.scrollLeft = el.scrollLeft; + }, [scrollRef, peerScrollRef]); + + const setRefs = useCallback( + (node: HTMLDivElement | null) => { + (scrollRef as React.MutableRefObject).current = + node; + if (typeof sizeRef === "function") sizeRef(node); + }, + [scrollRef, sizeRef], + ); + + return ( +
+
+ {label} +
+ + + + {scaledWidths.map((w, i) => ( + + ))} + + + {rows.map((row, rowIdx) => ( + + + {row.cells.map((cell, colIdx) => { + if (cell.hidden) return null; + const cellStyle: React.CSSProperties = { + overflow: "hidden", + padding: "1px 2px", + whiteSpace: "nowrap", + lineHeight: "normal", + boxSizing: "border-box", + ...cell.style, + }; + if (cell.diffStatus) { + cellStyle.backgroundColor = DIFF_BG[cell.diffStatus]; + cellStyle.outline = DIFF_BORDER[cell.diffStatus]; + cellStyle.outlineOffset = "-2px"; + } + if (cell.wrapText) { + cellStyle.whiteSpace = "pre-wrap"; + cellStyle.wordBreak = "break-all"; + } + + return ( + + ); + })} + + ))} + +
+ {rowIdx + 1} + + +
+
+ ); +} + +export function SpreadsheetDiffViewer({ + workspaceId, + worktreePath, + filePath, + diffCategory, + commitHash, +}: SpreadsheetDiffViewerProps) { + const { diffSheets, isLoading, error } = useSpreadsheetDiff({ + workspaceId, + worktreePath, + filePath, + diffCategory, + commitHash, + }); + const leftScrollRef = useRef(null); + const rightScrollRef = useRef(null); + const [activeSheetIndex, setActiveSheetIndex] = useState(0); + + const activeSheet = + diffSheets.length > 0 + ? diffSheets[Math.min(activeSheetIndex, diffSheets.length - 1)] + : null; + + const diffRowIndices = useMemo(() => { + if (!activeSheet) return []; + const indices: number[] = []; + for (let r = 0; r < activeSheet.modifiedRows.length; r++) { + if (activeSheet.modifiedRows[r].cells.some((c) => c.diffStatus)) { + indices.push(r); + } + } + return indices; + }, [activeSheet]); + + const [currentDiffIdx, setCurrentDiffIdx] = useState(0); + + const jumpToDiff = useCallback( + (idx: number) => { + const rowIdx = diffRowIndices[idx]; + if (rowIdx === undefined) return; + setCurrentDiffIdx(idx); + const left = leftScrollRef.current; + const right = rightScrollRef.current; + if (!left) return; + const rows = left.querySelectorAll("tbody tr"); + const target = rows[rowIdx] as HTMLElement | undefined; + if (!target) return; + const containerRect = left.getBoundingClientRect(); + const targetRect = target.getBoundingClientRect(); + const scrollTop = + left.scrollTop + + targetRect.top - + containerRect.top - + containerRect.height / 2 + + targetRect.height / 2; + left.scrollTop = scrollTop; + if (right) right.scrollTop = scrollTop; + }, + [diffRowIndices], + ); + + const goNext = useCallback(() => { + if (diffRowIndices.length === 0) return; + const next = + currentDiffIdx + 1 < diffRowIndices.length ? currentDiffIdx + 1 : 0; + jumpToDiff(next); + }, [currentDiffIdx, diffRowIndices, jumpToDiff]); + + const goPrev = useCallback(() => { + if (diffRowIndices.length === 0) return; + const prev = + currentDiffIdx - 1 >= 0 ? currentDiffIdx - 1 : diffRowIndices.length - 1; + jumpToDiff(prev); + }, [currentDiffIdx, diffRowIndices, jumpToDiff]); + + if (isLoading) { + return ( +
+ Loading diff... +
+ ); + } + + if (error) { + return ( +
+ {error} +
+ ); + } + + if (!activeSheet) { + return ( +
+ No changes found +
+ ); + } + + return ( +
+
+ + {diffRowIndices.length > 0 + ? `${diffRowIndices.length} changes` + : "No changes"} + + {diffRowIndices.length > 0 && ( + <> + + + {currentDiffIdx + 1} / {diffRowIndices.length} + + + + )} +
+
+ +
+ +
+ + {diffSheets.length > 1 && ( +
+ {diffSheets.map((sheet, idx) => ( + + ))} +
+ )} +
+ ); +} + +function getColumnLabel(index: number): string { + let label = ""; + let n = index; + do { + label = String.fromCharCode(65 + (n % 26)) + label; + n = Math.floor(n / 26) - 1; + } while (n >= 0); + return label; +} diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/SpreadsheetViewer.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/SpreadsheetViewer.tsx new file mode 100644 index 00000000000..359aed2f04a --- /dev/null +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/SpreadsheetViewer.tsx @@ -0,0 +1,266 @@ +import { useCallback, useMemo, useState } from "react"; +import useResizeObserver from "use-resize-observer"; +import { + type ParsedCell, + type RichTextPart, + useSpreadsheetData, +} from "./useSpreadsheetData"; + +interface SpreadsheetViewerProps { + workspaceId: string; + filePath: string; +} + +function RichTextContent({ parts }: { parts: RichTextPart[] }) { + return ( + <> + {parts.map((part, i) => { + const key = `${i}-${part.text.slice(0, 8)}`; + if (Object.keys(part.style).length === 0) { + return {part.text}; + } + return ( + + {part.text} + + ); + })} + + ); +} + +function CellContent({ cell }: { cell: ParsedCell }) { + if (cell.richText) { + return ; + } + return <>{cell.value}; +} + +const ROW_NUM_COL_WIDTH = 36; + +export function SpreadsheetViewer({ + workspaceId, + filePath, +}: SpreadsheetViewerProps) { + const { sheets, isLoading, error } = useSpreadsheetData( + workspaceId, + filePath, + ); + const [activeSheetIndex, setActiveSheetIndex] = useState(0); + const [containerWidth, setContainerWidth] = useState(null); + + const onResize = useCallback(({ width }: { width?: number }) => { + if (width) setContainerWidth(width); + }, []); + + const { ref: containerRef } = useResizeObserver({ onResize }); + + const activeSheet = sheets[Math.min(activeSheetIndex, sheets.length - 1)]; + + const scaledColumnWidths = useMemo(() => { + if (!activeSheet) return []; + const widths = activeSheet.columnWidths; + if (!containerWidth) return widths; + + const totalNatural = + ROW_NUM_COL_WIDTH + widths.reduce((sum, w) => sum + w, 0); + if (totalNatural <= containerWidth) return widths; + + const availableForCols = containerWidth - ROW_NUM_COL_WIDTH; + const colTotal = widths.reduce((sum, w) => sum + w, 0); + if (colTotal <= 0) return widths; + + return widths.map((w) => Math.floor((w / colTotal) * availableForCols)); + }, [activeSheet, containerWidth]); + + if (isLoading) { + return ( +
+ Loading spreadsheet... +
+ ); + } + + if (error) { + return ( +
+ {error} +
+ ); + } + + if (sheets.length === 0 || !activeSheet) { + return ( +
+ No sheets found +
+ ); + } + + return ( +
+
+ + + + {scaledColumnWidths.map((w, i) => ( + + ))} + + + + + ); + })} + + + + {activeSheet.rows.map((row, rowIdx) => ( + + + {row.cells.map((cell, colIdx) => { + if (cell.hidden) return null; + + const cellStyle: React.CSSProperties = { + overflow: "hidden", + padding: "1px 3px", + whiteSpace: "nowrap", + lineHeight: "normal", + boxSizing: "border-box", + ...cell.style, + }; + + if (cell.wrapText) { + cellStyle.whiteSpace = "pre-wrap"; + cellStyle.wordBreak = "break-all"; + cellStyle.overflow = "visible"; + } + + if (cell.verticalText) { + cellStyle.writingMode = "vertical-rl"; + cellStyle.textOrientation = "upright"; + cellStyle.letterSpacing = 0; + cellStyle.lineHeight = 1; + cellStyle.textAlign = "center"; + cellStyle.verticalAlign = "middle"; + cellStyle.whiteSpace = "normal"; + cellStyle.wordBreak = "keep-all"; + cellStyle.overflow = "hidden"; + cellStyle.padding = "2px 0"; + } + + return ( + + ); + })} + + ))} + +
+ {Array.from({ length: activeSheet.columnCount }, (_, i) => { + const label = getColumnLabel(i); + return ( + + {label} +
+ {rowIdx + 1} + + +
+ {activeSheet.truncated && ( +
+ Showing first 2,000 rows. Full file contains more rows. +
+ )} +
+ + {sheets.length > 1 && ( +
+ {sheets.map((sheet, idx) => ( + + ))} +
+ )} +
+ ); +} + +function getColumnLabel(index: number): string { + let label = ""; + let n = index; + do { + label = String.fromCharCode(65 + (n % 26)) + label; + n = Math.floor(n / 26) - 1; + } while (n >= 0); + return label; +} diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/index.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/index.ts new file mode 100644 index 00000000000..1485822b8bf --- /dev/null +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/index.ts @@ -0,0 +1,2 @@ +export { SpreadsheetDiffViewer } from "./SpreadsheetDiffViewer"; +export { SpreadsheetViewer } from "./SpreadsheetViewer"; diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/parseWorkbook.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/parseWorkbook.ts new file mode 100644 index 00000000000..97d8fd8d59d --- /dev/null +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/parseWorkbook.ts @@ -0,0 +1,452 @@ +import type React from "react"; + +const MAX_ROWS = 2000; + +// ── Types ── + +type StyleObj = React.CSSProperties; + +export interface RichTextPart { + text: string; + style: StyleObj; +} + +export interface ParsedCell { + value: string; + style: StyleObj; + colSpan?: number; + rowSpan?: number; + hidden?: boolean; + wrapText?: boolean; + verticalText?: boolean; + richText?: RichTextPart[]; +} + +export interface ParsedRow { + cells: ParsedCell[]; + height: number; +} + +export interface ParsedSheet { + name: string; + rows: ParsedRow[]; + columnCount: number; + columnWidths: number[]; + truncated: boolean; +} + +// ── Theme colors (standard Excel Office theme) ── + +const THEME_COLORS: Record = { + 0: "#FFFFFF", + 1: "#000000", + 2: "#E7E6E6", + 3: "#44546A", + 4: "#4472C4", + 5: "#ED7D31", + 6: "#A5A5A5", + 7: "#FFC000", + 8: "#5B9BD5", + 9: "#70AD47", +}; + +const BORDER_STYLES: Record = { + thin: "1px solid", + medium: "2px solid", + thick: "3px solid", + dotted: "1px dotted", + dashed: "1px dashed", + double: "3px double", + mediumDashed: "2px dashed", + dashDot: "1px dashed", + dashDotDot: "1px dashed", + mediumDashDot: "2px dashed", + mediumDashDotDot: "2px dashed", + slantDashDot: "1px dashed", + hair: "1px solid", +}; + +// ── Color resolution ── + +function argbToHex(argb: string | undefined): string | null { + if (!argb || argb.length < 6) return null; + const hex = argb.length === 8 ? argb.slice(2) : argb; + if (/^0+$/.test(hex)) return null; + return `#${hex}`; +} + +function applyTint(hex: string, tint: number): string { + const r = Number.parseInt(hex.slice(1, 3), 16); + const g = Number.parseInt(hex.slice(3, 5), 16); + const b = Number.parseInt(hex.slice(5, 7), 16); + const apply = (c: number) => + tint < 0 ? Math.round(c * (1 + tint)) : Math.round(c + (255 - c) * tint); + const clamp = (v: number) => Math.min(255, Math.max(0, v)); + return `#${clamp(apply(r)).toString(16).padStart(2, "0")}${clamp(apply(g)).toString(16).padStart(2, "0")}${clamp(apply(b)).toString(16).padStart(2, "0")}`; +} + +// biome-ignore lint/suspicious/noExplicitAny: ExcelJS internal types are incomplete +function resolveColor(color: any): string | null { + if (!color) return null; + if (color.argb) return argbToHex(color.argb); + if (color.theme !== undefined) { + const base = THEME_COLORS[color.theme] || "#000000"; + return color.tint ? applyTint(base, color.tint) : base; + } + if (color.indexed !== undefined) + return color.indexed === 64 ? "#000000" : null; + return null; +} + +// biome-ignore lint/suspicious/noExplicitAny: ExcelJS internal types are incomplete +function borderToCSS(b: any): string | null { + if (!b?.style) return null; + const base = BORDER_STYLES[b.style] || "1px solid"; + const col = resolveColor(b.color) || "#000"; + return `${base} ${col}`; +} + +function rowHeightToPx(h: number | undefined): number { + if (!h || h <= 0) return 20; + return Math.round((h * 96) / 72); +} + +function charWidthToPx(w: number | undefined): number { + if (!w || w <= 0) return 64; + return Math.max(4, Math.round(w * 10)); +} + +// biome-ignore lint/suspicious/noExplicitAny: ExcelJS internal types are incomplete +function richTextFontStyle(font: any): StyleObj { + const s: StyleObj = {}; + if (!font) return s; + if (font.size) s.fontSize = `${font.size}pt`; + if (font.name) s.fontFamily = `'${font.name}', sans-serif`; + if (font.bold) s.fontWeight = "bold"; + if (font.italic) s.fontStyle = "italic"; + const decor: string[] = []; + if (font.underline) decor.push("underline"); + if (font.strike) decor.push("line-through"); + if (decor.length) s.textDecoration = decor.join(" "); + const fc = resolveColor(font.color); + if (fc && fc !== "#FFFFFF") s.color = fc; + if (font.vertAlign === "superscript") { + s.verticalAlign = "super"; + s.fontSize = s.fontSize || "0.7em"; + } + if (font.vertAlign === "subscript") { + s.verticalAlign = "sub"; + s.fontSize = s.fontSize || "0.7em"; + } + return s; +} + +// biome-ignore lint/suspicious/noExplicitAny: ExcelJS internal types are incomplete +function getCellStyle(cell: any): StyleObj { + const style: StyleObj = { verticalAlign: "bottom" }; + const al = cell.alignment; + if (al) { + const hmap: Record = { + left: "left", + center: "center", + right: "right", + fill: "left", + justify: "justify", + centerContinuous: "center", + distributed: "center", + }; + const vmap: Record = { + top: "top", + middle: "middle", + center: "middle", + bottom: "bottom", + distributed: "middle", + justify: "middle", + }; + if (al.horizontal) + style.textAlign = (hmap[al.horizontal] || + "left") as StyleObj["textAlign"]; + style.verticalAlign = ((al.vertical && vmap[al.vertical]) || + "bottom") as StyleObj["verticalAlign"]; + if (al.indent) style.paddingLeft = `${al.indent * 8 + 3}px`; + } + const f = cell.font; + if (f) { + if (f.size) style.fontSize = `${f.size}pt`; + if (f.name) style.fontFamily = `'${f.name}', sans-serif`; + if (f.bold) style.fontWeight = "bold"; + if (f.italic) style.fontStyle = "italic"; + const decor: string[] = []; + if (f.underline) decor.push("underline"); + if (f.strike) decor.push("line-through"); + if (decor.length) style.textDecoration = decor.join(" "); + const fc = resolveColor(f.color); + if (fc && fc !== "#FFFFFF") style.color = fc; + } + const fill = cell.fill; + if (fill?.type === "pattern" && fill.pattern === "solid") { + const bg = resolveColor(fill.fgColor); + if (bg) style.backgroundColor = bg; + } + const bd = cell.border; + if (bd) { + const bt = borderToCSS(bd.top); + if (bt) style.borderTop = bt; + const bb = borderToCSS(bd.bottom); + if (bb) style.borderBottom = bb; + const bl = borderToCSS(bd.left); + if (bl) style.borderLeft = bl; + const br = borderToCSS(bd.right); + if (br) style.borderRight = br; + } + return style; +} + +function getMergedCellBorders( + // biome-ignore lint/suspicious/noExplicitAny: ExcelJS internal types are incomplete + ws: any, + r: number, + c: number, + rowspan: number, + colspan: number, +): StyleObj { + const borders: StyleObj = {}; + const getBorder = (row: number, col: number) => + ws.getRow(row).getCell(col).border; + const topBd = getBorder(r, c); + if (topBd?.top) { + const v = borderToCSS(topBd.top); + if (v) borders.borderTop = v; + } + if (topBd?.left) { + const v = borderToCSS(topBd.left); + if (v) borders.borderLeft = v; + } + const bottomRow = r + rowspan - 1; + for (let cc = c; cc < c + colspan; cc++) { + const bd = getBorder(bottomRow, cc); + if (bd?.bottom) { + const v = borderToCSS(bd.bottom); + if (v) { + borders.borderBottom = v; + break; + } + } + } + const rightCol = c + colspan - 1; + for (let rr = r; rr < r + rowspan; rr++) { + const bd = getBorder(rr, rightCol); + if (bd?.right) { + const v = borderToCSS(bd.right); + if (v) { + borders.borderRight = v; + break; + } + } + } + return borders; +} + +// biome-ignore lint/suspicious/noExplicitAny: ExcelJS internal types are incomplete +function getCellDisplayValue(cell: any): string { + if (cell.type === 2) return ""; + if (cell.value?.richText) { + // biome-ignore lint/suspicious/noExplicitAny: ExcelJS internal types are incomplete + return cell.value.richText.map((rt: any) => rt.text || "").join(""); + } + if (cell.value?.formula) { + const r = cell.value.result; + return r != null ? String(r) : ""; + } + if (cell.value instanceof Date) return cell.value.toLocaleDateString(); + if (cell.text != null) return String(cell.text); + if (cell.value != null) return String(cell.value); + return ""; +} + +interface MergeOrigin { + rowspan: number; + colspan: number; +} +type MergeEntry = MergeOrigin | { skip: true }; + +// biome-ignore lint/suspicious/noExplicitAny: ExcelJS internal types are incomplete +function buildMergeMap(ws: any): Record { + const mm: Record = {}; + const model = ws.model; + if (!model?.merges) return mm; + for (const range of model.merges) { + const parts = range.split(":"); + if (parts.length !== 2) continue; + const s = decodeAddr(parts[0]); + const e = decodeAddr(parts[1]); + for (let r = s.r; r <= e.r; r++) { + for (let c = s.c; c <= e.c; c++) { + const key = `${r},${c}`; + if (r === s.r && c === s.c) + mm[key] = { rowspan: e.r - s.r + 1, colspan: e.c - s.c + 1 }; + else mm[key] = { skip: true }; + } + } + } + return mm; +} + +function decodeAddr(addr: string): { r: number; c: number } { + const m = addr.match(/^([A-Z]+)(\d+)$/); + if (!m) return { r: 1, c: 1 }; + const col = m[1] + .split("") + .reduce((a, ch) => a * 26 + ch.charCodeAt(0) - 64, 0); + return { r: Number.parseInt(m[2], 10), c: col }; +} + +interface SheetDims { + minR: number; + maxR: number; + minC: number; + maxC: number; +} + +function parsePrintArea(area: string): SheetDims | null { + const clean = area.replace(/\$/g, ""); + const m = clean.match(/^([A-Z]+)(\d+):([A-Z]+)(\d+)$/); + if (!m) return null; + const colToNum = (s: string) => + s.split("").reduce((a, ch) => a * 26 + ch.charCodeAt(0) - 64, 0); + return { + minC: colToNum(m[1]), + minR: Number.parseInt(m[2], 10), + maxC: colToNum(m[3]), + maxR: Number.parseInt(m[4], 10), + }; +} + +// biome-ignore lint/suspicious/noExplicitAny: ExcelJS internal types are incomplete +function getSheetDimensions(ws: any): SheetDims { + const printArea = ws.pageSetup?.printArea; + if (printArea) { + const parsed = parsePrintArea(printArea.split(",")[0].trim()); + if (parsed) return parsed; + } + const dims = ws.dimensions; + if (dims) + return { + minR: dims.top || 1, + maxR: dims.bottom || 1, + minC: dims.left || 1, + maxC: dims.right || 1, + }; + return { + minR: 1, + maxR: ws.rowCount || 1, + minC: 1, + maxC: ws.columnCount || 1, + }; +} + +// ── Main parser ── + +export async function parseWorkbook( + base64Content: string, +): Promise { + const ExcelJS = await import("exceljs"); + const workbook = new ExcelJS.Workbook(); + const binaryStr = atob(base64Content); + const bytes = new Uint8Array(binaryStr.length); + for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i); + await workbook.xlsx.load(bytes.buffer as ArrayBuffer); + + const sheets: ParsedSheet[] = []; + + workbook.eachSheet((worksheet) => { + const dims = getSheetDimensions(worksheet); + const mergeMap = buildMergeMap(worksheet); + const colCount = dims.maxC - dims.minC + 1; + const columnWidths: number[] = []; + for (let c = dims.minC; c <= dims.maxC; c++) { + const col = worksheet.getColumn(c); + columnWidths.push(col.hidden ? 0 : charWidthToPx(col.width)); + } + + const rows: ParsedRow[] = []; + const maxRow = Math.min(dims.maxR, dims.minR + MAX_ROWS - 1); + const truncated = dims.maxR > maxRow; + + for (let r = dims.minR; r <= maxRow; r++) { + const row = worksheet.getRow(r); + if (row.hidden) continue; + const cells: ParsedCell[] = []; + + for (let c = dims.minC; c <= dims.maxC; c++) { + const key = `${r},${c}`; + const mergeEntry = mergeMap[key]; + if (mergeEntry && "skip" in mergeEntry) { + cells.push({ value: "", style: {}, hidden: true }); + continue; + } + + // biome-ignore lint/suspicious/noExplicitAny: ExcelJS internal types are incomplete + const cell = row.getCell(c) as any; + const val = getCellDisplayValue(cell); + let style = getCellStyle(cell); + const mergeInfo = + mergeEntry && "rowspan" in mergeEntry ? mergeEntry : null; + const colspan = mergeInfo?.colspan ?? 1; + const rowspan = mergeInfo?.rowspan ?? 1; + + if (mergeInfo) { + const { + borderTop: _bt, + borderBottom: _bb, + borderLeft: _bl, + borderRight: _br, + ...rest + } = style as Record; + style = { + ...rest, + ...getMergedCellBorders(worksheet, r, c, rowspan, colspan), + } as StyleObj; + } + + const isRichText = !!cell.value?.richText; + const richText: RichTextPart[] | undefined = isRichText + ? // biome-ignore lint/suspicious/noExplicitAny: ExcelJS internal types are incomplete + cell.value.richText.map((rt: any) => ({ + text: rt.text || "", + style: richTextFontStyle(rt.font), + })) + : undefined; + + const al = cell.alignment; + const wrapText = + al?.wrapText === true || + (typeof val === "string" && val.includes("\n")); + const verticalText = + al?.textRotation === "vertical" || al?.textRotation === 255; + + const parsed: ParsedCell = { value: val, style }; + if (mergeInfo) { + parsed.colSpan = colspan; + parsed.rowSpan = rowspan; + } + if (wrapText) parsed.wrapText = true; + if (verticalText) parsed.verticalText = true; + if (richText) parsed.richText = richText; + cells.push(parsed); + } + + rows.push({ cells, height: rowHeightToPx(row.height) }); + } + + sheets.push({ + name: worksheet.name, + rows, + columnCount: colCount, + columnWidths, + truncated, + }); + }); + + return sheets; +} diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/useSpreadsheetData.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/useSpreadsheetData.ts new file mode 100644 index 00000000000..c459df062b6 --- /dev/null +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/useSpreadsheetData.ts @@ -0,0 +1,78 @@ +import { useEffect, useState } from "react"; +import { electronTrpc } from "renderer/lib/electron-trpc"; +import type { ParsedSheet } from "./parseWorkbook"; + +export type { + ParsedCell, + ParsedRow, + ParsedSheet, + RichTextPart, +} from "./parseWorkbook"; + +const MAX_SPREADSHEET_SIZE = 10 * 1024 * 1024; + +interface UseSpreadsheetDataResult { + sheets: ParsedSheet[]; + isLoading: boolean; + error: string | null; +} + +export function useSpreadsheetData( + workspaceId: string, + filePath: string, +): UseSpreadsheetDataResult { + const [sheets, setSheets] = useState([]); + const [isParsing, setIsParsing] = useState(false); + const [parseError, setParseError] = useState(null); + + const query = electronTrpc.filesystem.readFile.useQuery( + { + workspaceId, + absolutePath: filePath, + maxBytes: MAX_SPREADSHEET_SIZE, + }, + { retry: false, refetchOnWindowFocus: false }, + ); + + useEffect(() => { + if (!query.data) return; + + if (query.data.exceededLimit) { + setParseError("File is too large to preview (>10MB)"); + return; + } + + let cancelled = false; + setIsParsing(true); + setParseError(null); + + import("./parseWorkbook") + .then(({ parseWorkbook }) => parseWorkbook(query.data?.content as string)) + .then((parsed) => { + if (!cancelled) { + setSheets(parsed); + setIsParsing(false); + } + }) + .catch((err) => { + if (!cancelled) { + setParseError( + err instanceof Error ? err.message : "Failed to parse spreadsheet", + ); + setIsParsing(false); + } + }); + + return () => { + cancelled = true; + }; + }, [query.data]); + + const error = query.error ? "Failed to load file" : parseError; + + return { + sheets, + isLoading: query.isLoading || isParsing, + error, + }; +} diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/useSpreadsheetDiff.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/useSpreadsheetDiff.ts new file mode 100644 index 00000000000..69eea1ab308 --- /dev/null +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/useSpreadsheetDiff.ts @@ -0,0 +1,316 @@ +import { useEffect, useMemo, useState } from "react"; +import { electronTrpc } from "renderer/lib/electron-trpc"; +import type { ChangeCategory } from "shared/changes-types"; +import type { ParsedCell, ParsedSheet } from "./useSpreadsheetData"; + +const MAX_SPREADSHEET_SIZE = 10 * 1024 * 1024; + +export interface DiffParsedCell extends ParsedCell { + diffStatus?: "added" | "removed" | "modified"; +} + +export interface DiffParsedRow { + cells: DiffParsedCell[]; + height: number; +} + +export interface DiffParsedSheet { + name: string; + originalRows: DiffParsedRow[]; + modifiedRows: DiffParsedRow[]; + columnCount: number; + columnWidths: number[]; + sheetStatus?: "added" | "removed"; +} + +async function parseBase64Workbook( + base64Content: string, +): Promise { + const { parseWorkbook } = await import("./parseWorkbook"); + return parseWorkbook(base64Content); +} + +function compareCellValue(a: ParsedCell, b: ParsedCell): boolean { + return a.value === b.value; +} + +function buildDiffSheets( + originalSheets: ParsedSheet[], + modifiedSheets: ParsedSheet[], +): DiffParsedSheet[] { + const result: DiffParsedSheet[] = []; + + const origMap = new Map(originalSheets.map((s) => [s.name, s])); + const modMap = new Map(modifiedSheets.map((s) => [s.name, s])); + + const allNames = new Set([...origMap.keys(), ...modMap.keys()]); + + for (const name of allNames) { + const orig = origMap.get(name); + const mod = modMap.get(name); + + if (!orig && mod) { + result.push({ + name, + originalRows: [], + modifiedRows: mod.rows.map((r) => ({ + ...r, + cells: r.cells.map((c) => ({ ...c, diffStatus: "added" as const })), + })), + columnCount: mod.columnCount, + columnWidths: mod.columnWidths, + sheetStatus: "added", + }); + continue; + } + + if (orig && !mod) { + result.push({ + name, + originalRows: orig.rows.map((r) => ({ + ...r, + cells: r.cells.map((c) => ({ + ...c, + diffStatus: "removed" as const, + })), + })), + modifiedRows: [], + columnCount: orig.columnCount, + columnWidths: orig.columnWidths, + sheetStatus: "removed", + }); + continue; + } + + if (orig && mod) { + const maxRows = Math.max(orig.rows.length, mod.rows.length); + const maxCols = Math.max(orig.columnCount, mod.columnCount); + const colWidths = + mod.columnWidths.length >= orig.columnWidths.length + ? mod.columnWidths + : orig.columnWidths; + + const origRows: DiffParsedRow[] = []; + const modRows: DiffParsedRow[] = []; + + for (let r = 0; r < maxRows; r++) { + const origRow = orig.rows[r]; + const modRow = mod.rows[r]; + + const origCells: DiffParsedCell[] = []; + const modCells: DiffParsedCell[] = []; + + for (let c = 0; c < maxCols; c++) { + const origCell = origRow?.cells[c]; + const modCell = modRow?.cells[c]; + + const emptyCell: DiffParsedCell = { + value: "", + style: {}, + }; + + if (!origCell && modCell) { + origCells.push(emptyCell); + modCells.push({ + ...modCell, + diffStatus: modCell.value ? "added" : undefined, + }); + } else if (origCell && !modCell) { + origCells.push({ + ...origCell, + diffStatus: origCell.value ? "removed" : undefined, + }); + modCells.push(emptyCell); + } else if (origCell && modCell) { + const changed = !compareCellValue(origCell, modCell); + origCells.push({ + ...origCell, + diffStatus: changed ? "modified" : undefined, + }); + modCells.push({ + ...modCell, + diffStatus: changed ? "modified" : undefined, + }); + } else { + origCells.push(emptyCell); + modCells.push(emptyCell); + } + } + + origRows.push({ + cells: origCells, + height: origRow?.height ?? modRow?.height ?? 20, + }); + modRows.push({ + cells: modCells, + height: modRow?.height ?? origRow?.height ?? 20, + }); + } + + result.push({ + name, + originalRows: origRows, + modifiedRows: modRows, + columnCount: maxCols, + columnWidths: colWidths, + }); + } + } + + return result; +} + +interface UseSpreadsheetDiffParams { + workspaceId: string; + worktreePath: string; + filePath: string; + diffCategory?: ChangeCategory; + commitHash?: string; +} + +interface UseSpreadsheetDiffResult { + diffSheets: DiffParsedSheet[]; + isLoading: boolean; + error: string | null; + debug: Record; +} + +export function useSpreadsheetDiff({ + workspaceId, + worktreePath, + filePath, + diffCategory, + commitHash, +}: UseSpreadsheetDiffParams): UseSpreadsheetDiffResult { + const [diffSheets, setDiffSheets] = useState([]); + const [isParsing, setIsParsing] = useState(false); + const [parseError, setParseError] = useState(null); + + // Determine git refs for original and modified + const refs = useMemo(() => { + switch (diffCategory) { + case "staged": + return { originalRef: "HEAD", modifiedRef: undefined }; // modified = staged (:0:) + case "committed": + return { + originalRef: commitHash ? `${commitHash}^` : "HEAD", + modifiedRef: commitHash ?? "HEAD", + }; + case "against-base": + return { originalRef: "origin/main", modifiedRef: "HEAD" }; + default: + // unstaged: original from git, modified from disk + return { originalRef: "HEAD", modifiedRef: undefined }; + } + }, [diffCategory, commitHash]); + + const isUnstaged = !diffCategory || diffCategory === "unstaged"; + + // Fetch original from git + const originalQuery = electronTrpc.changes.readGitFileBinary.useQuery( + { + worktreePath, + absolutePath: filePath, + ref: refs.originalRef ?? "HEAD", + }, + { retry: false, refetchOnWindowFocus: false, enabled: !!worktreePath }, + ); + + // Fetch modified: from git ref or from disk + const modifiedGitQuery = electronTrpc.changes.readGitFileBinary.useQuery( + { + worktreePath, + absolutePath: filePath, + ref: refs.modifiedRef ?? "HEAD", + }, + { + retry: false, + refetchOnWindowFocus: false, + enabled: !!worktreePath && !isUnstaged && !!refs.modifiedRef, + }, + ); + + const modifiedDiskQuery = electronTrpc.filesystem.readFile.useQuery( + { + workspaceId, + absolutePath: filePath, + maxBytes: MAX_SPREADSHEET_SIZE, + }, + { + retry: false, + refetchOnWindowFocus: false, + enabled: isUnstaged, + }, + ); + + const originalBase64 = originalQuery.data?.content ?? null; + const modifiedBase64 = isUnstaged + ? ((modifiedDiskQuery.data?.content as string) ?? null) + : (modifiedGitQuery.data?.content ?? null); + + const isLoading = + originalQuery.isLoading || + (isUnstaged ? modifiedDiskQuery.isLoading : modifiedGitQuery.isLoading) || + isParsing; + + useEffect(() => { + if (!originalBase64 && !modifiedBase64) return; + + let cancelled = false; + setIsParsing(true); + setParseError(null); + + Promise.all([ + originalBase64 + ? parseBase64Workbook(originalBase64) + : Promise.resolve([]), + modifiedBase64 + ? parseBase64Workbook(modifiedBase64) + : Promise.resolve([]), + ]) + .then(([origSheets, modSheets]) => { + if (!cancelled) { + setDiffSheets(buildDiffSheets(origSheets, modSheets)); + setIsParsing(false); + } + }) + .catch((err) => { + if (!cancelled) { + setParseError( + err instanceof Error ? err.message : "Failed to parse spreadsheet", + ); + setIsParsing(false); + } + }); + + return () => { + cancelled = true; + }; + }, [originalBase64, modifiedBase64]); + + const error = + originalQuery.error || modifiedGitQuery.error || modifiedDiskQuery.error + ? "Failed to load file" + : parseError; + + const debug = { + diffCategory: diffCategory ?? "undefined", + isUnstaged, + originalRef: refs.originalRef, + modifiedRef: refs.modifiedRef ?? "disk", + originalLoading: originalQuery.isLoading, + originalHasData: !!originalBase64, + originalError: originalQuery.error?.message ?? null, + modifiedLoading: isUnstaged + ? modifiedDiskQuery.isLoading + : modifiedGitQuery.isLoading, + modifiedHasData: !!modifiedBase64, + modifiedError: isUnstaged + ? (modifiedDiskQuery.error?.message ?? null) + : (modifiedGitQuery.error?.message ?? null), + sheetsCount: diffSheets.length, + isParsing, + }; + + return { diffSheets, isLoading, error, debug }; +} diff --git a/apps/desktop/src/shared/file-types.ts b/apps/desktop/src/shared/file-types.ts index cbac5fcc766..49ec6f3c926 100644 --- a/apps/desktop/src/shared/file-types.ts +++ b/apps/desktop/src/shared/file-types.ts @@ -27,6 +27,9 @@ const IMAGE_MIME_TYPES: Record = { ico: "image/x-icon", }; +/** Spreadsheet extensions */ +const SPREADSHEET_EXTENSIONS = new Set(["xlsx", "xls", "xlsm", "xlsb", "ods"]); + /** Markdown extensions */ const MARKDOWN_EXTENSIONS = new Set(["md", "markdown", "mdx"]); @@ -60,6 +63,13 @@ export function isMarkdownFile(filePath: string): boolean { return MARKDOWN_EXTENSIONS.has(getExtension(filePath)); } +/** + * Checks if a file is a spreadsheet based on extension + */ +export function isSpreadsheetFile(filePath: string): boolean { + return SPREADSHEET_EXTENSIONS.has(getExtension(filePath)); +} + /** * Checks if a file supports rendered preview (markdown or image) */ diff --git a/bun.lock b/bun.lock index 21669df53f5..4ef17774d08 100644 --- a/bun.lock +++ b/bun.lock @@ -237,6 +237,7 @@ "dotenv": "^17.3.1", "drizzle-orm": "0.45.1", "electron-updater": "^6.7.3", + "exceljs": "^4.4.0", "execa": "^9.6.0", "express": "^5.1.0", "fast-glob": "^3.3.3", @@ -1468,6 +1469,10 @@ "@expo/xcpretty": ["@expo/xcpretty@4.4.0", "", { "dependencies": { "@babel/code-frame": "^7.20.0", "chalk": "^4.1.0", "js-yaml": "^4.1.0" }, "bin": { "excpretty": "build/cli.js" } }, "sha512-o2qDlTqJ606h4xR36H2zWTywmZ3v3842K6TU8Ik2n1mfW0S580VHlt3eItVYdLYz+klaPp7CXqanja8eASZjRw=="], + "@fast-csv/format": ["@fast-csv/format@4.3.5", "", { "dependencies": { "@types/node": "^14.0.1", "lodash.escaperegexp": "^4.1.2", "lodash.isboolean": "^3.0.3", "lodash.isequal": "^4.5.0", "lodash.isfunction": "^3.0.9", "lodash.isnil": "^4.0.0" } }, "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A=="], + + "@fast-csv/parse": ["@fast-csv/parse@4.3.6", "", { "dependencies": { "@types/node": "^14.0.1", "lodash.escaperegexp": "^4.1.2", "lodash.groupby": "^4.6.0", "lodash.isfunction": "^3.0.9", "lodash.isnil": "^4.0.0", "lodash.isundefined": "^3.0.1", "lodash.uniq": "^4.5.0" } }, "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA=="], + "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="], "@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="], @@ -2928,6 +2933,10 @@ "app-builder-lib": ["app-builder-lib@26.4.0", "", { "dependencies": { "@develar/schema-utils": "~2.6.5", "@electron/asar": "3.4.1", "@electron/fuses": "^1.8.0", "@electron/notarize": "2.5.0", "@electron/osx-sign": "1.3.3", "@electron/rebuild": "4.0.1", "@electron/universal": "2.0.3", "@malept/flatpak-bundler": "^0.4.0", "@types/fs-extra": "9.0.13", "async-exit-hook": "^2.0.1", "builder-util": "26.3.4", "builder-util-runtime": "9.5.1", "chromium-pickle-js": "^0.2.0", "ci-info": "4.3.1", "debug": "^4.3.4", "dotenv": "^16.4.5", "dotenv-expand": "^11.0.6", "ejs": "^3.1.8", "electron-publish": "26.3.4", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "isbinaryfile": "^5.0.0", "jiti": "^2.4.2", "js-yaml": "^4.1.0", "json5": "^2.2.3", "lazy-val": "^1.0.5", "minimatch": "^10.0.3", "plist": "3.1.0", "resedit": "^1.7.0", "semver": "~7.7.3", "tar": "^6.1.12", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0", "which": "^5.0.0" }, "peerDependencies": { "dmg-builder": "26.4.0", "electron-builder-squirrel-windows": "26.4.0" } }, "sha512-Uas6hNe99KzP3xPWxh5LGlH8kWIVjZixzmMJHNB9+6hPyDpjc7NQMkVgi16rQDdpCFy22ZU5sp8ow7tvjeMgYQ=="], + "archiver": ["archiver@5.3.2", "", { "dependencies": { "archiver-utils": "^2.1.0", "async": "^3.2.4", "buffer-crc32": "^0.2.1", "readable-stream": "^3.6.0", "readdir-glob": "^1.1.2", "tar-stream": "^2.2.0", "zip-stream": "^4.1.0" } }, "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw=="], + + "archiver-utils": ["archiver-utils@2.1.0", "", { "dependencies": { "glob": "^7.1.4", "graceful-fs": "^4.2.0", "lazystream": "^1.0.0", "lodash.defaults": "^4.2.0", "lodash.difference": "^4.5.0", "lodash.flatten": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.union": "^4.6.0", "normalize-path": "^3.0.0", "readable-stream": "^2.0.0" } }, "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw=="], + "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], @@ -3044,6 +3053,8 @@ "big-integer": ["big-integer@1.6.52", "", {}, "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg=="], + "binary": ["binary@0.3.0", "", { "dependencies": { "buffers": "~0.1.1", "chainsaw": "~0.1.0" } }, "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg=="], + "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], @@ -3052,6 +3063,8 @@ "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="], + "bluebird": ["bluebird@3.4.7", "", {}, "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA=="], + "blueimp-md5": ["blueimp-md5@2.19.0", "", {}, "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w=="], "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], @@ -3078,6 +3091,10 @@ "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + "buffer-indexof-polyfill": ["buffer-indexof-polyfill@1.0.2", "", {}, "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A=="], + + "buffers": ["buffers@0.1.1", "", {}, "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ=="], + "bufferutil": ["bufferutil@4.1.0", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw=="], "builder-util": ["builder-util@26.3.4", "", { "dependencies": { "7zip-bin": "~5.2.0", "@types/debug": "^4.1.6", "app-builder-bin": "5.0.0-alpha.12", "builder-util-runtime": "9.5.1", "chalk": "^4.1.2", "cross-spawn": "^7.0.6", "debug": "^4.3.4", "fs-extra": "^10.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "js-yaml": "^4.1.0", "sanitize-filename": "^1.6.3", "source-map-support": "^0.5.19", "stat-mode": "^1.0.0", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0" } }, "sha512-aRn88mYMktHxzdqDMF6Ayj0rKoX+ZogJ75Ck7RrIqbY/ad0HBvnS2xA4uHfzrGr5D2aLL3vU6OBEH4p0KMV2XQ=="], @@ -3114,6 +3131,8 @@ "chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="], + "chainsaw": ["chainsaw@0.1.0", "", { "dependencies": { "traverse": ">=0.3.0 <0.4" } }, "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ=="], + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], @@ -3210,6 +3229,8 @@ "compare-version": ["compare-version@0.1.2", "", {}, "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A=="], + "compress-commons": ["compress-commons@4.1.2", "", { "dependencies": { "buffer-crc32": "^0.2.13", "crc32-stream": "^4.0.2", "normalize-path": "^3.0.0", "readable-stream": "^3.6.0" } }, "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg=="], + "compressible": ["compressible@2.0.18", "", { "dependencies": { "mime-db": ">= 1.43.0 < 2" } }, "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg=="], "compression": ["compression@1.8.1", "", { "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" } }, "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w=="], @@ -3256,6 +3277,10 @@ "crc": ["crc@3.8.0", "", { "dependencies": { "buffer": "^5.1.0" } }, "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ=="], + "crc-32": ["crc-32@1.2.2", "", { "bin": { "crc32": "bin/crc32.njs" } }, "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="], + + "crc32-stream": ["crc32-stream@4.0.3", "", { "dependencies": { "crc-32": "^1.2.0", "readable-stream": "^3.4.0" } }, "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw=="], + "crelt": ["crelt@1.0.6", "", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="], "cross-dirname": ["cross-dirname@0.1.0", "", {}, "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q=="], @@ -3472,6 +3497,8 @@ "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + "duplexer2": ["duplexer2@0.1.4", "", { "dependencies": { "readable-stream": "^2.0.2" } }, "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA=="], + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], @@ -3598,6 +3625,8 @@ "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], + "exceljs": ["exceljs@4.4.0", "", { "dependencies": { "archiver": "^5.0.0", "dayjs": "^1.8.34", "fast-csv": "^4.3.1", "jszip": "^3.10.1", "readable-stream": "^3.6.0", "saxes": "^5.0.1", "tmp": "^0.2.0", "unzipper": "^0.10.11", "uuid": "^8.3.0" } }, "sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg=="], + "exec-async": ["exec-async@2.2.0", "", {}, "sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw=="], "execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="], @@ -3690,6 +3719,8 @@ "fast-content-type-parse": ["fast-content-type-parse@3.0.0", "", {}, "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg=="], + "fast-csv": ["fast-csv@4.3.6", "", { "dependencies": { "@fast-csv/format": "4.3.5", "@fast-csv/parse": "4.3.6" } }, "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="], @@ -3776,6 +3807,8 @@ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "fstream": ["fstream@1.0.12", "", { "dependencies": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", "mkdirp": ">=0.5 0", "rimraf": "2" } }, "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg=="], + "fumadocs-core": ["fumadocs-core@16.4.7", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.7.5", "@orama/orama": "^3.1.18", "@shikijs/rehype": "^3.21.0", "@shikijs/transformers": "^3.21.0", "estree-util-value-to-estree": "^3.5.0", "github-slugger": "^2.0.0", "hast-util-to-estree": "^3.1.3", "hast-util-to-jsx-runtime": "^2.3.6", "image-size": "^2.0.2", "negotiator": "^1.0.0", "npm-to-yarn": "^3.0.1", "path-to-regexp": "^8.3.0", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^3.21.0", "tinyglobby": "^0.2.15", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "@mixedbread/sdk": "^0.46.0", "@orama/core": "1.x.x", "@oramacloud/client": "2.x.x", "@tanstack/react-router": "1.x.x", "@types/react": "*", "algoliasearch": "5.x.x", "lucide-react": "*", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router": "7.x.x", "waku": "^0.26.0 || ^0.27.0", "zod": "4.x.x" }, "optionalPeers": ["@mixedbread/sdk", "@orama/core", "@oramacloud/client", "@tanstack/react-router", "@types/react", "algoliasearch", "lucide-react", "next", "react", "react-dom", "react-router", "waku", "zod"] }, "sha512-oEsoha5EjyQnhRb6s5tNYEM+AiDA4BN80RyevRohsKPXGRQ2K3ddMaFAQq5kBaqA/Xxb+vqrElyRtzmdif7w2A=="], "fumadocs-mdx": ["fumadocs-mdx@14.2.5", "", { "dependencies": { "@mdx-js/mdx": "^3.1.1", "@standard-schema/spec": "^1.1.0", "chokidar": "^5.0.0", "esbuild": "^0.27.2", "estree-util-value-to-estree": "^3.5.0", "js-yaml": "^4.1.1", "mdast-util-to-markdown": "^2.1.2", "picocolors": "^1.1.1", "picomatch": "^4.0.3", "remark-mdx": "^3.1.1", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3", "zod": "^4.3.5" }, "peerDependencies": { "@fumadocs/mdx-remote": "^1.4.0", "@types/react": "*", "fumadocs-core": "^15.0.0 || ^16.0.0", "next": "^15.3.0 || ^16.0.0", "react": "*", "vite": "6.x.x || 7.x.x" }, "optionalPeers": ["@fumadocs/mdx-remote", "@types/react", "next", "react", "vite"], "bin": { "fumadocs-mdx": "dist/bin.js" } }, "sha512-1WJeJ1Xago2lRq6GhTvTb+hxDtWUBr7lHi4YgHNBYSpWKsTfOor3UxgZV1UYBrd32cq4xHdtMK33LM67gA0eBA=="], @@ -4032,6 +4065,8 @@ "is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], + "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], + "isbinaryfile": ["isbinaryfile@5.0.7", "", {}, "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ=="], "isbot": ["isbot@5.1.33", "", {}, "sha512-P4Hgb5NqswjkI0J1CM6XKXon/sxKY1SuowE7Qx2hrBhIwICFyXy54mfgB5eMHXsbe/eStzzpbIGNOvGmz+dlKg=="], @@ -4106,6 +4141,8 @@ "jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="], + "jszip": ["jszip@3.10.1", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "setimmediate": "^1.0.5" } }, "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g=="], + "katex": ["katex@0.16.28", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg=="], "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], @@ -4130,6 +4167,8 @@ "lazy-val": ["lazy-val@1.0.5", "", {}, "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q=="], + "lazystream": ["lazystream@1.0.1", "", { "dependencies": { "readable-stream": "^2.0.5" } }, "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw=="], + "lcid": ["lcid@3.1.1", "", { "dependencies": { "invert-kv": "^3.0.0" } }, "sha512-M6T051+5QCGLBQb8id3hdvIW8+zeFV2FyBGFS9IEK5H9Wt4MueD4bW1eWikpHgZp+5xR3l5c8pZUkQsIA0BFZg=="], "leac": ["leac@0.6.0", "", {}, "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg=="], @@ -4174,6 +4213,8 @@ "linkifyjs": ["linkifyjs@4.3.2", "", {}, "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA=="], + "listenercount": ["listenercount@1.0.1", "", {}, "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ=="], + "load-json-file": ["load-json-file@7.0.1", "", {}, "sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ=="], "loader-runner": ["loader-runner@4.3.1", "", {}, "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q=="], @@ -4188,14 +4229,36 @@ "lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="], + "lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="], + + "lodash.difference": ["lodash.difference@4.5.0", "", {}, "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA=="], + "lodash.escaperegexp": ["lodash.escaperegexp@4.1.2", "", {}, "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw=="], + "lodash.flatten": ["lodash.flatten@4.4.0", "", {}, "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="], + + "lodash.groupby": ["lodash.groupby@4.6.0", "", {}, "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw=="], + + "lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="], + "lodash.isequal": ["lodash.isequal@4.5.0", "", {}, "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="], + "lodash.isfunction": ["lodash.isfunction@3.0.9", "", {}, "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw=="], + + "lodash.isnil": ["lodash.isnil@4.0.0", "", {}, "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng=="], + + "lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="], + + "lodash.isundefined": ["lodash.isundefined@3.0.1", "", {}, "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA=="], + "lodash.samplesize": ["lodash.samplesize@4.2.0", "", {}, "sha512-1ZhKV7/nuISuaQdxfCqrs4HHxXIYN+0Z4f7NMQn2PHkxFZJGavJQ1j/paxyJnLJmN2ZamNN6SMepneV+dCgQTA=="], "lodash.throttle": ["lodash.throttle@4.1.1", "", {}, "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="], + "lodash.union": ["lodash.union@4.6.0", "", {}, "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw=="], + + "lodash.uniq": ["lodash.uniq@4.5.0", "", {}, "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ=="], + "log-symbols": ["log-symbols@7.0.1", "", { "dependencies": { "is-unicode-supported": "^2.0.0", "yoctocolors": "^2.1.1" } }, "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg=="], "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], @@ -4588,6 +4651,8 @@ "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], + "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], "parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="], @@ -4712,6 +4777,8 @@ "proc-log": ["proc-log@4.2.0", "", {}, "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA=="], + "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], + "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="], "promise": ["promise@8.3.0", "", { "dependencies": { "asap": "~2.0.6" } }, "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg=="], @@ -4884,6 +4951,8 @@ "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + "readdir-glob": ["readdir-glob@1.1.3", "", { "dependencies": { "minimatch": "^5.1.0" } }, "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA=="], + "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], @@ -5036,6 +5105,8 @@ "sax": ["sax@1.4.4", "", {}, "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw=="], + "saxes": ["saxes@5.0.1", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw=="], + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], "schema-utils": ["schema-utils@4.3.3", "", { "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", "ajv-formats": "^2.1.1", "ajv-keywords": "^5.1.0" } }, "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA=="], @@ -5068,6 +5139,8 @@ "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], + "setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="], + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], "sf-symbols-typescript": ["sf-symbols-typescript@2.2.0", "", {}, "sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw=="], @@ -5350,6 +5423,8 @@ "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], + "traverse": ["traverse@0.3.9", "", {}, "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ=="], + "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], @@ -5476,6 +5551,8 @@ "unplugin": ["unplugin@1.0.1", "", { "dependencies": { "acorn": "^8.8.1", "chokidar": "^3.5.3", "webpack-sources": "^3.2.3", "webpack-virtual-modules": "^0.5.0" } }, "sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA=="], + "unzipper": ["unzipper@0.10.14", "", { "dependencies": { "big-integer": "^1.6.17", "binary": "~0.3.0", "bluebird": "~3.4.1", "buffer-indexof-polyfill": "~1.0.0", "duplexer2": "~0.1.4", "fstream": "^1.0.12", "graceful-fs": "^4.2.2", "listenercount": "~1.0.1", "readable-stream": "~2.3.6", "setimmediate": "~1.0.4" } }, "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g=="], + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], @@ -5614,6 +5691,8 @@ "xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="], + "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], + "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], "xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="], @@ -5638,6 +5717,8 @@ "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="], + "zip-stream": ["zip-stream@4.1.1", "", { "dependencies": { "archiver-utils": "^3.0.4", "compress-commons": "^4.1.2", "readable-stream": "^3.6.0" } }, "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ=="], + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "zod-from-json-schema": ["zod-from-json-schema@0.5.2", "", { "dependencies": { "zod": "^4.0.17" } }, "sha512-/dNaicfdhJTOuUd4RImbLUE2g5yrSzzDjI/S6C2vO2ecAGZzn9UcRVgtyLSnENSmAOBRiSpUdzDS6fDWX3Z35g=="], @@ -5814,6 +5895,10 @@ "@expo/xcpretty/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "@fast-csv/format/@types/node": ["@types/node@14.18.63", "", {}, "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ=="], + + "@fast-csv/parse/@types/node": ["@types/node@14.18.63", "", {}, "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ=="], + "@fumadocs/ui/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], @@ -6010,6 +6095,10 @@ "app-builder-lib/which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], + "archiver-utils/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + + "archiver-utils/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "ava/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "ava/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], @@ -6108,6 +6197,8 @@ "dotenv-expand/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + "duplexer2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "electron-builder/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "electron-publish/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -6130,6 +6221,8 @@ "estree-util-build-jsx/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + "exceljs/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + "execa/is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="], "expo/expo-asset": ["expo-asset@55.0.4", "", { "dependencies": { "@expo/image-utils": "^0.8.12", "expo-constants": "~55.0.4" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-eaPPe9Sw4V0nL/KkEvuWpyeeSoGhP2fu1ZA7wkldqywhMVhhY+7kerMkQ7nPgJVtevIfkQRw7wD8ghZEzrKzmg=="], @@ -6212,6 +6305,8 @@ "fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + "fstream/rimraf": ["rimraf@2.6.3", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA=="], + "fumadocs-mdx/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], "fumadocs-mdx/esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="], @@ -6260,12 +6355,16 @@ "jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + "jszip/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], "launch-ide/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "launch-ide/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + "lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "libsql/detect-libc": ["detect-libc@2.0.2", "", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="], "lighthouse-logger/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], @@ -6430,6 +6529,8 @@ "react-syntax-highlighter/lowlight": ["lowlight@1.20.0", "", { "dependencies": { "fault": "^1.0.0", "highlight.js": "~10.7.0" } }, "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw=="], + "readdir-glob/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], + "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], @@ -6520,6 +6621,8 @@ "uniwind/@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.17", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.17", "@tailwindcss/oxide-darwin-arm64": "4.1.17", "@tailwindcss/oxide-darwin-x64": "4.1.17", "@tailwindcss/oxide-freebsd-x64": "4.1.17", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", "@tailwindcss/oxide-linux-x64-musl": "4.1.17", "@tailwindcss/oxide-wasm32-wasi": "4.1.17", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA=="], + "unzipper/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "use-resize-observer/react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="], "use-resize-observer/react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="], @@ -6552,6 +6655,8 @@ "youch/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + "zip-stream/archiver-utils": ["archiver-utils@3.0.4", "", { "dependencies": { "glob": "^7.2.3", "graceful-fs": "^4.2.0", "lazystream": "^1.0.0", "lodash.defaults": "^4.2.0", "lodash.difference": "^4.5.0", "lodash.flatten": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.union": "^4.6.0", "normalize-path": "^3.0.0", "readable-stream": "^3.6.0" } }, "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw=="], + "zod-from-json-schema-v3/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "@a2a-js/sdk/@types/express/@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.8", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA=="], @@ -6956,6 +7061,10 @@ "app-builder-lib/which/isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], + "archiver-utils/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "archiver-utils/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + "ava/figures/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], "ava/figures/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], @@ -7004,6 +7113,8 @@ "dmg-license/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "duplexer2/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + "electron-winstaller/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], "electron-winstaller/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], @@ -7646,6 +7757,8 @@ "friendly-words/express/type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], + "fstream/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "fumadocs-mdx/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], "fumadocs-mdx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="], @@ -7710,6 +7823,10 @@ "jest-haste-map/jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + "jszip/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + + "lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + "lighthouse-logger/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "metro-babel-transformer/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="], @@ -7868,6 +7985,8 @@ "uniwind/@tailwindcss/oxide/@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.17", "", { "os": "win32", "cpu": "x64" }, "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw=="], + "unzipper/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + "use-resize-observer/react-dom/scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="], @@ -7980,6 +8099,8 @@ "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "zip-stream/archiver-utils/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "@a2a-js/sdk/@types/express/@types/serve-static/@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="], "@a2a-js/sdk/express/accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], @@ -8686,6 +8807,8 @@ "@uiw/react-markdown-preview/rehype-prism-plus/refractor/hastscript": ["hastscript@7.2.0", "", { "dependencies": { "@types/hast": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^3.0.0", "property-information": "^6.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw=="], + "archiver-utils/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "chromium-edge-launcher/rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], "cli-highlight/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -10714,6 +10837,8 @@ "friendly-words/express/type-is/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "fstream/rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "pkg-conf/find-up/locate-path/p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="], "react-native/babel-plugin-syntax-hermes-parser/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="], @@ -10742,6 +10867,8 @@ "uniwind/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "zip-stream/archiver-utils/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "@a2a-js/sdk/express/accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], "@a2a-js/sdk/express/type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], @@ -13538,10 +13665,14 @@ "friendly-words/express/type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "fstream/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "pkg-conf/find-up/locate-path/p-locate/p-limit": ["p-limit@4.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ=="], "temp/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "zip-stream/archiver-utils/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "@expo/cli/expo/@expo/cli/@expo/env/dotenv-expand/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], "@expo/cli/expo/babel-preset-expo/@react-native/babel-preset/@react-native/babel-plugin-codegen/@react-native/codegen": ["@react-native/codegen@0.81.5", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/parser": "^7.25.3", "glob": "^7.1.1", "hermes-parser": "0.29.1", "invariant": "^2.2.4", "nullthrows": "^1.1.1", "yargs": "^17.6.2" } }, "sha512-a2TDA03Up8lpSa9sh5VRGCQDXgCTOyDOFH+aqyinxp1HChG8uk89/G+nkJ9FPd0rqgi25eCTR16TWdS3b+fA6g=="],