From 38fa40e851ec541ff51b5c9eb5e569442c1fac13 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Thu, 24 Sep 2026 13:23:38 +0800 Subject: [PATCH] Joshenlim/fe 4445 explorer export as pdf option 2 (#50786) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Context Another take on [this PR](https://github.com/supabase/supabase/pull/50733), decided that it's better UX + DX to use a PDF library (`@react-pdf/renderer`) instead for handling exporting Notebooks to PDF. The previous method using `print` involved a lot of sparse patching in various places + had a lot of limitations, not to mention its awkward UX to "Print". There's a lot of code changes in this PR but it's because we're constructing the PDF document based on the notebook's contents using primitives from `@react-pdf/renderer` which gives us a lot more control over the PDF's output. PDF output will differ slightly from the Notebook UI in the browser - Added a custom header in the report which covers the notebook name + project name, and when its exported - Empty markdown cells are skipped - Query cells will show both SQL content + results (either table or chart) - Opting to skip syntax highlighting for SQL content (Can consider separately) - Results will only be included in the PDF if the notebook's been run (Aligns with Copy as Markdown CTA) - For table results, _all_ rows will be rendered (Aligns with Copy as Markdown CTA) Feel free to give it a spin! πŸ™‚ πŸ™ image ## Summary by CodeRabbit - **New Features** - Added an β€œExport as PDF” option for notebooks. - PDFs include notebook details, Markdown content, SQL queries, query results, tables, and charts. - Query exports show errors, empty results, row counts, and applicable row limits. - Export progress is displayed while the PDF is generated, and failures are reported via notifications. --- .../Explorer/ExplorerNotebookTab.tsx | 34 ++ .../Explorer/NotebookPdf/NotebookPdfChart.tsx | 254 +++++++++++++ .../NotebookPdf/NotebookPdfDocument.tsx | 72 ++++ .../NotebookPdf/NotebookPdfMarkdown.tsx | 172 +++++++++ .../NotebookPdf/NotebookPdfQueryCell.tsx | 115 ++++++ .../Explorer/NotebookPdf/NotebookPdfTable.tsx | 50 +++ .../NotebookPdf/exportNotebookToPdf.tsx | 39 ++ .../interfaces/Explorer/NotebookPdf/theme.ts | 219 +++++++++++ apps/studio/package.json | 4 + pnpm-lock.yaml | 354 ++++++++++++++++-- 10 files changed, 1289 insertions(+), 24 deletions(-) create mode 100644 apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfChart.tsx create mode 100644 apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfDocument.tsx create mode 100644 apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfMarkdown.tsx create mode 100644 apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfQueryCell.tsx create mode 100644 apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfTable.tsx create mode 100644 apps/studio/components/interfaces/Explorer/NotebookPdf/exportNotebookToPdf.tsx create mode 100644 apps/studio/components/interfaces/Explorer/NotebookPdf/theme.ts diff --git a/apps/studio/components/interfaces/Explorer/ExplorerNotebookTab.tsx b/apps/studio/components/interfaces/Explorer/ExplorerNotebookTab.tsx index 6ce6f806161..576f44c1866 100644 --- a/apps/studio/components/interfaces/Explorer/ExplorerNotebookTab.tsx +++ b/apps/studio/components/interfaces/Explorer/ExplorerNotebookTab.tsx @@ -17,6 +17,7 @@ import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { Check, Copy, + Download, FileText, Keyboard, Loader2, @@ -81,6 +82,7 @@ import { import { useUpsertNotebookMutation } from '@/data/content/notebooks/notebook-upsert-mutation' import { acceptUntrustedLogsSql } from '@/data/logs/safe-analytics-sql' import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage' +import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' import { getNotebooksStateSnapshot, useCurrentNotebook, @@ -106,6 +108,7 @@ export const ExplorerNotebookTab = () => { const currentNotebook = useCurrentNotebook() const { name, content } = currentNotebook?.notebook ?? {} const { isNotFound } = useLoadNotebook({ id, projectRef: ref }) + const { data: project } = useSelectedProjectQuery() const cells = content?.cells ?? [] const queryCellIds = cells.filter(isQueryCell).map((cell) => cell._id) @@ -117,6 +120,7 @@ export const ExplorerNotebookTab = () => { mutatingQueries: QueryCellSummary[] } | null>(null) const [skipMutatingCells, setSkipMutatingCells] = useState(false) + const [isExportingPdf, setIsExportingPdf] = useState(false) const queryCellRefs = useRef(new Map()) const savedContentRef = useRef(undefined) const scrollContainerRef = useRef(null) @@ -323,6 +327,24 @@ export const ExplorerNotebookTab = () => { } } + const handleExportPdf = async () => { + setIsExportingPdf(true) + try { + const { exportNotebookToPdf } = await import('./NotebookPdf/exportNotebookToPdf') + await exportNotebookToPdf({ + name: name ?? 'Untitled notebook', + projectName: project?.name, + cells, + getResult: (cellId) => queryCellRefs.current.get(cellId)?.getResult(), + }) + } catch (err: unknown) { + const message = err instanceof Error ? err.message : String(err) + toast.error('Failed to export notebook as PDF: ' + message) + } finally { + setIsExportingPdf(false) + } + } + const handleDragEnd = (event: DragEndEvent) => { persistNotebookTab() @@ -434,6 +456,18 @@ export const ExplorerNotebookTab = () => { Copy as Markdown + + {isExportingPdf ? ( + + ) : ( + + )} + Export as PDF + [] +} + +function buildLogTicks(min: number, max: number): number[] { + const ticks: number[] = [] + let tick = 10 ** Math.floor(Math.log10(min)) + while (tick <= max) { + if (tick >= min) ticks.push(tick) + tick *= 10 + } + return ticks.length > 0 ? ticks : [min, max] +} + +export function NotebookPdfChart({ chart, rows }: NotebookPdfChartProps): ReactElement { + const { + type = 'bar', + x_column, + y_series = [], + cumulative = false, + scale = 'linear', + show_labels = false, + } = chart ?? {} + + if (!x_column || y_series.length === 0) { + return ( + + Chart is not configured. + + ) + } + + const baseRows = rows.map((row) => { + const next: Record = { [x_column]: row[x_column] } + y_series.forEach((column) => { + next[column] = Number(row[column]) || 0 + }) + return next + }) + + const plotRows: Record[] = cumulative + ? getCumulativeResults({ rows: baseRows }, { yKey: [...y_series] }) + : baseRows + + // Logarithmic scale only makes sense for a single series, mirroring DisplaySettingsButton's + // own rule (QueryResultChart resets `scale` to linear once a second Y column is added). + const isLogScale = y_series.length === 1 && scale === 'log' + + let dataMax = 0 + let dataMin = 0 + let smallestPositive = Infinity + plotRows.forEach((row) => { + y_series.forEach((column) => { + const value = Number(row[column]) || 0 + if (value > dataMax) dataMax = value + if (value < dataMin) dataMin = value + if (value > 0 && value < smallestPositive) smallestPositive = value + }) + }) + + const domainMin = isLogScale + ? Math.max(1, smallestPositive === Infinity ? 1 : smallestPositive) + : dataMin + const domainMax = isLogScale + ? Math.max(domainMin * 10, dataMax) + : Math.max(dataMax, domainMin + 1) + + const formatTick = isLogScale ? formatLogTick : formatYAxisTick + const yAxisWidth = show_labels + ? Math.max(formatTick(domainMax).length, formatTick(domainMin).length) * 6 + 14 + : 0 + + const margin = { top: 8, right: 8, bottom: show_labels ? 24 : 8, left: yAxisWidth } + const plotWidth = CHART_WIDTH - margin.left - margin.right + const plotHeight = CHART_HEIGHT - margin.top - margin.bottom + + const mapValueToY = (value: number): number => { + if (isLogScale) { + const clamped = Math.max(value, domainMin) + const logMin = Math.log10(domainMin) + const logMax = Math.log10(domainMax) + const ratio = logMax === logMin ? 0 : (Math.log10(clamped) - logMin) / (logMax - logMin) + return margin.top + plotHeight * (1 - ratio) + } + const ratio = domainMax === domainMin ? 0 : (value - domainMin) / (domainMax - domainMin) + return margin.top + plotHeight * (1 - ratio) + } + + const categoryCount = plotRows.length + const slotWidth = categoryCount > 0 ? plotWidth / categoryCount : plotWidth + const categoryCenterX = (index: number) => margin.left + index * slotWidth + slotWidth / 2 + + const yTicks = isLogScale + ? buildLogTicks(domainMin, domainMax) + : Array.from({ length: 5 }, (_, index) => domainMin + ((domainMax - domainMin) * index) / 4) + + const xLabelStep = Math.max(1, Math.ceil(categoryCount / MAX_X_LABELS)) + const zeroY = mapValueToY(0) + + return ( + + {y_series.length > 1 && ( + + {y_series.map((column, index) => ( + + + {column} + + ))} + + )} + + + + {show_labels && + yTicks.map((tick, index) => ( + + ))} + + + + {type === 'bar' && + plotRows.map((row, rowIndex) => { + const groupWidth = slotWidth * 0.7 + const barWidth = groupWidth / y_series.length + const groupStart = margin.left + rowIndex * slotWidth + (slotWidth - groupWidth) / 2 + + return y_series.map((column, seriesIndex) => { + const value = Number(row[column]) || 0 + const y = mapValueToY(value) + const barTop = Math.min(y, zeroY) + const barHeight = Math.max(1, Math.abs(y - zeroY)) + return ( + + ) + }) + })} + + {type === 'line' && + y_series.map((column, seriesIndex) => { + const points = plotRows.map((row, rowIndex) => { + const value = Number(row[column]) || 0 + return `${rowIndex === 0 ? 'M' : 'L'} ${categoryCenterX(rowIndex)},${mapValueToY(value)}` + }) + return ( + + ) + })} + + + {show_labels && + yTicks.map((tick, index) => ( + + {formatTick(tick)} + + ))} + + {show_labels && + plotRows.map((row, rowIndex) => + rowIndex % xLabelStep === 0 ? ( + + {String(row[x_column])} + + ) : null + )} + + + ) +} diff --git a/apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfDocument.tsx b/apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfDocument.tsx new file mode 100644 index 00000000000..c84c5fb5a90 --- /dev/null +++ b/apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfDocument.tsx @@ -0,0 +1,72 @@ +import { Document, Page, Path, Svg, Text, View } from '@react-pdf/renderer' +import dayjs from 'dayjs' +import type { Snapshot } from 'valtio' + +import { type QueryResult } from '../types' +import { NotebookPdfMarkdown } from './NotebookPdfMarkdown' +import { NotebookPdfQueryCell } from './NotebookPdfQueryCell' +import { pdfStyles } from './theme' +import { isQueryCell, type Cell } from '@/data/content/notebooks/notebook-schema' + +interface NotebookPdfDocumentProps { + name: string + projectName?: string + cells: readonly Snapshot[] + results: ReadonlyMap +} + +function SupabaseLogoMark() { + return ( + + + + + ) +} + +export function NotebookPdfDocument({ + name, + projectName, + cells, + results, +}: NotebookPdfDocumentProps) { + const exportedDate = dayjs().format('MMM D, YYYY') + + return ( + + + + + + + supabase + + EXPLORER NOTEBOOK + + + {name} + Exported {exportedDate} + {projectName !== undefined && ( + Project: {projectName} + )} + + + + + {cells.map((cell) => + isQueryCell(cell) ? ( + + ) : ( + + ) + )} + + + ) +} diff --git a/apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfMarkdown.tsx b/apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfMarkdown.tsx new file mode 100644 index 00000000000..77e115ef9f9 --- /dev/null +++ b/apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfMarkdown.tsx @@ -0,0 +1,172 @@ +import { Link as PdfLink, Text, View } from '@react-pdf/renderer' +import type { ListItem, Content as MdastContent, Root as MdastRoot, PhrasingContent } from 'mdast' +import type { ReactElement, ReactNode } from 'react' +import remarkGfm from 'remark-gfm' +import remarkParse from 'remark-parse' +import { unified } from 'unified' + +import { pdfStyles } from './theme' + +const HEADING_STYLES = [ + pdfStyles.heading1, + pdfStyles.heading2, + pdfStyles.heading3, + pdfStyles.heading4, + pdfStyles.heading5, + pdfStyles.heading6, +] + +export function parseMarkdownToMdast(markdown: string): MdastRoot { + return unified().use(remarkParse).use(remarkGfm).parse(markdown) as MdastRoot +} + +function mapInline(node: PhrasingContent, key: string): ReactNode { + switch (node.type) { + case 'text': + return node.value + case 'strong': + return ( + + {node.children.map((child, index) => mapInline(child, `${key}-${index}`))} + + ) + case 'emphasis': + return ( + + {node.children.map((child, index) => mapInline(child, `${key}-${index}`))} + + ) + case 'delete': + return ( + + {node.children.map((child, index) => mapInline(child, `${key}-${index}`))} + + ) + case 'inlineCode': + return ( + + {node.value} + + ) + case 'link': + return ( + + {node.children.map((child, index) => mapInline(child, `${key}-${index}`))} + + ) + case 'break': + return '\n' + default: + return null + } +} + +function mapListItem( + item: ListItem, + ordered: boolean, + start: number, + index: number, + key: string +): ReactElement { + const marker = ordered ? `${start + index}.` : 'β€’' + + return ( + + {marker} + + {item.children.map((child, childIndex) => + mapBlock(child, `${key}-${childIndex}`, { tight: true }) + )} + + + ) +} + +function mapBlock( + node: MdastContent, + key: string, + { tight = false }: { tight?: boolean } = {} +): ReactElement | null { + switch (node.type) { + case 'heading': + return ( + + {node.children.map((child, index) => mapInline(child, `${key}-${index}`))} + + ) + case 'paragraph': + return ( + + {node.children.map((child, index) => mapInline(child, `${key}-${index}`))} + + ) + case 'list': { + const ordered = node.ordered ?? false + const start = node.start ?? 1 + return ( + + {node.children.map((item, index) => + mapListItem(item, ordered, start, index, `${key}-${index}`) + )} + + ) + } + case 'blockquote': + return ( + + {node.children.map((child, index) => mapBlock(child, `${key}-${index}`))} + + ) + case 'thematicBreak': + return + case 'code': + return ( + + {node.value} + + ) + case 'table': { + const [headerRow, ...bodyRows] = node.children + return ( + + + {headerRow.children.map((cell, cellIndex) => ( + + {cell.children.map((child, childIndex) => + mapInline(child, `${key}-header-${cellIndex}-${childIndex}`) + )} + + ))} + + {bodyRows.map((row, rowIndex) => ( + + {row.children.map((cell, cellIndex) => ( + + {cell.children.map((child, childIndex) => + mapInline(child, `${key}-row-${rowIndex}-${cellIndex}-${childIndex}`) + )} + + ))} + + ))} + + ) + } + default: + return null + } +} + +export function markdownToPdfBlocks(markdown: string): ReactElement[] { + const root = parseMarkdownToMdast(markdown) + return root.children + .map((child, index) => mapBlock(child, `md-${index}`)) + .filter((element): element is ReactElement => element !== null) +} + +export function NotebookPdfMarkdown({ markdown }: { markdown: string }) { + return {markdownToPdfBlocks(markdown)} +} diff --git a/apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfQueryCell.tsx b/apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfQueryCell.tsx new file mode 100644 index 00000000000..8cd723ad82f --- /dev/null +++ b/apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfQueryCell.tsx @@ -0,0 +1,115 @@ +import { Text, View } from '@react-pdf/renderer' +import type { ReactElement } from 'react' +import type { Snapshot } from 'valtio' + +import { type QueryResult } from '../types' +import { NotebookPdfChart } from './NotebookPdfChart' +import { NotebookPdfTable } from './NotebookPdfTable' +import { pdfStyles } from './theme' +import { formatTimeRange } from '@/components/ui/AIAssistantPanel/AssistantNotebookPreview.utils' +import { type QueryCell } from '@/data/content/notebooks/notebook-schema' +import { pluralize } from '@/lib/helpers' + +function SqlBlock({ sql }: { sql: string }) { + return ( + + {sql} + + ) +} + +function ResultsTable({ rows }: { rows: readonly Record[] }) { + return ( + + + + ) +} + +function QueryResultsSection({ + result, + view, + chart, +}: { + result: QueryResult | undefined + view: 'table' | 'chart' + chart: Snapshot['chart'] +}): ReactElement { + if (!result) { + return ( + + Query has not been run. + + ) + } + + if (result.error) { + return ( + + Error: {result.error.message} + + ) + } + + const rows = result.rows ?? [] + if (rows.length === 0) { + return ( + + Success. No rows returned + + ) + } + + if (view === 'chart') { + return ( + + + + ) + } + + return +} + +interface NotebookPdfQueryCellProps { + cell: Snapshot + result: QueryResult | undefined +} + +export function NotebookPdfQueryCell({ cell, result }: NotebookPdfQueryCellProps) { + const view = cell.view ?? 'table' + const rowCount = result?.rows?.length ?? 0 + const rowLimit = cell._tag === 'database_cell' ? cell.row_limit : undefined + + return ( + + + {cell.title ?? 'Untitled query'} + {cell._tag === 'log_cell' && ( + // react-pdf's default font (Helvetica) can't render the 'β†’' from formatTimeRange + + {formatTimeRange(cell.time_range).replace('β†’', '-')} + + )} + + + + + + + + + {rowCount.toLocaleString()} {pluralize(rowCount, 'row')} + + {rowLimit !== undefined && ( + <> + Β· + + {rowLimit < 0 ? 'No row limit' : `Limit ${rowLimit} rows`} + + + )} + + + ) +} diff --git a/apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfTable.tsx b/apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfTable.tsx new file mode 100644 index 00000000000..f8875c92479 --- /dev/null +++ b/apps/studio/components/interfaces/Explorer/NotebookPdf/NotebookPdfTable.tsx @@ -0,0 +1,50 @@ +import { Text, View } from '@react-pdf/renderer' +import type { ReactElement } from 'react' + +import { pdfStyles } from './theme' +import { formatCellValue } from '@/components/ui/DataGridResults/DataGridResults.utils' + +interface NotebookPdfTableProps { + rows: readonly Record[] +} + +export function NotebookPdfTable({ rows }: NotebookPdfTableProps): ReactElement | null { + const columns = Object.keys(rows[0] ?? {}) + if (columns.length === 0) return null + + return ( + + + {columns.map((column) => ( + + {column} + + ))} + + {rows.map((row, rowIndex) => ( + + {columns.map((column) => { + const value = row[column] + return value === null ? ( + + NULL + + ) : ( + + {formatCellValue(value)} + + ) + })} + + ))} + + ) +} diff --git a/apps/studio/components/interfaces/Explorer/NotebookPdf/exportNotebookToPdf.tsx b/apps/studio/components/interfaces/Explorer/NotebookPdf/exportNotebookToPdf.tsx new file mode 100644 index 00000000000..708c00d84bb --- /dev/null +++ b/apps/studio/components/interfaces/Explorer/NotebookPdf/exportNotebookToPdf.tsx @@ -0,0 +1,39 @@ +import { pdf } from '@react-pdf/renderer' +import saveAs from 'file-saver' +import type { Snapshot } from 'valtio' + +import { type QueryResult } from '../types' +import { NotebookPdfDocument } from './NotebookPdfDocument' +import { isQueryCell, type Cell } from '@/data/content/notebooks/notebook-schema' + +export interface ExportNotebookToPdfParams { + name: string + projectName?: string + cells: readonly Snapshot[] + getResult: (cellId: string) => QueryResult | undefined +} + +/** + * Gathers a notebook's cells and each query cell's last in-session result β€” results aren't + * persisted with the notebook, so they're read off the same `queryCellRefs` imperative + * handles `notebookToMarkdown` (ExplorerNotebookTab.utils.ts) already uses β€” then renders + * and downloads a PDF report. + */ +export async function exportNotebookToPdf({ + name, + projectName, + cells, + getResult, +}: ExportNotebookToPdfParams): Promise { + const results = new Map() + cells.filter(isQueryCell).forEach((cell) => { + const result = getResult(cell._id) + if (result) results.set(cell._id, result) + }) + + const blob = await pdf( + + ).toBlob() + + saveAs(blob, `${name}.pdf`) +} diff --git a/apps/studio/components/interfaces/Explorer/NotebookPdf/theme.ts b/apps/studio/components/interfaces/Explorer/NotebookPdf/theme.ts new file mode 100644 index 00000000000..529cea85e89 --- /dev/null +++ b/apps/studio/components/interfaces/Explorer/NotebookPdf/theme.ts @@ -0,0 +1,219 @@ +import { StyleSheet } from '@react-pdf/renderer' + +/** + * Hardcoded light-mode values resolved from the dashboard's theme (packages/ui/build/css/themes/light.css, + * styles/grid.css, MonacoThemeProvider.tsx) β€” react-pdf can't consume CSS variables, so these are the + * concrete equivalents of `--background`/`--foreground`/`--muted-foreground`/`--border`/brand green. + */ +export const pdfColors = { + background: '#ffffff', + text: '#030303', + textParagraph: '#3a3a3a', + textMuted: '#464646', + textTertiary: '#696969', + border: '#e9e9e9', + brand: '#24b47e', + grayHeader: '#f0f0f0', + graySqlBox: '#f8f8f8', + sqlTextColor: '#444444', + errorText: '#a3402c', +} + +export const pdfStyles = StyleSheet.create({ + page: { + backgroundColor: pdfColors.background, + color: pdfColors.text, + fontSize: 10, + paddingTop: 36, + paddingBottom: 40, + paddingHorizontal: 40, + }, + headerBanner: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'flex-start', + }, + headerLeft: { + flexDirection: 'column', + gap: 4, + }, + headerLogoRow: { + flexDirection: 'row', + alignItems: 'center', + gap: 4, + }, + headerLogoMark: { + width: 10, + height: 10.4, + }, + headerLogoText: { + fontSize: 11, + fontWeight: 700, + color: pdfColors.text, + }, + headerLabel: { + fontSize: 8, + letterSpacing: 1, + color: pdfColors.textTertiary, + }, + headerRight: { + flexDirection: 'column', + alignItems: 'flex-end', + gap: 2, + }, + headerTitle: { + fontSize: 11, + fontWeight: 700, + color: pdfColors.text, + }, + headerMeta: { + fontSize: 8, + color: pdfColors.textMuted, + }, + headerRule: { + marginTop: 12, + marginBottom: 20, + borderBottomWidth: 1.5, + borderBottomColor: pdfColors.brand, + }, + notebookTitle: { + fontSize: 22, + fontWeight: 700, + color: pdfColors.text, + marginBottom: 6, + }, + notebookDescription: { + fontSize: 11, + color: pdfColors.textMuted, + marginBottom: 20, + }, + cellSpacing: { + marginBottom: 16, + }, + + // Markdown + heading1: { fontSize: 16, fontWeight: 700, marginTop: 4, marginBottom: 8 }, + heading2: { fontSize: 14, fontWeight: 700, marginTop: 4, marginBottom: 6 }, + heading3: { fontSize: 12, fontWeight: 700, marginTop: 4, marginBottom: 6 }, + heading4: { fontSize: 11, fontWeight: 700, marginTop: 4, marginBottom: 4 }, + heading5: { fontSize: 10, fontWeight: 700, marginTop: 4, marginBottom: 4 }, + heading6: { fontSize: 10, fontWeight: 700, marginTop: 4, marginBottom: 4 }, + paragraph: { fontSize: 10, lineHeight: 1.5, marginBottom: 8, color: pdfColors.textParagraph }, + bold: { fontWeight: 700 }, + italic: { fontStyle: 'italic' }, + inlineCode: { + fontFamily: 'Courier', + fontSize: 9, + backgroundColor: '#f0f0f0', + color: pdfColors.text, + }, + link: { color: pdfColors.brand, textDecoration: 'underline' }, + list: { marginBottom: 8 }, + listItem: { flexDirection: 'row', marginBottom: 3 }, + listItemMarker: { width: 16, fontSize: 10, color: pdfColors.textParagraph }, + listItemContent: { flex: 1, fontSize: 10, lineHeight: 1.5, color: pdfColors.textParagraph }, + blockquote: { + borderLeftWidth: 2, + borderLeftColor: pdfColors.border, + paddingLeft: 8, + marginBottom: 8, + }, + thematicBreak: { + borderBottomWidth: 1, + borderBottomColor: pdfColors.border, + marginVertical: 10, + }, + + // Query cells + queryCard: { + borderWidth: 1, + borderColor: pdfColors.border, + borderRadius: 4, + marginBottom: 16, + }, + queryHeaderRow: { + flexDirection: 'row', + alignItems: 'center', + gap: 6, + paddingHorizontal: 10, + paddingVertical: 8, + backgroundColor: pdfColors.grayHeader, + borderTopLeftRadius: 4, + borderTopRightRadius: 4, + borderBottomWidth: 1, + borderBottomColor: pdfColors.border, + }, + queryTitle: { fontSize: 10, fontWeight: 500, color: pdfColors.text }, + sqlBox: { + backgroundColor: pdfColors.graySqlBox, + paddingHorizontal: 10, + paddingVertical: 8, + borderBottomWidth: 1, + borderBottomColor: pdfColors.border, + }, + sqlText: { fontFamily: 'Courier', fontSize: 8.5, lineHeight: 1.5, color: pdfColors.sqlTextColor }, + resultsBox: { paddingHorizontal: 10, paddingVertical: 8 }, + resultsPlaceholder: { fontSize: 9, color: pdfColors.textTertiary }, + resultsError: { fontSize: 9, color: pdfColors.errorText }, + resultsHeaderRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingHorizontal: 10, + paddingVertical: 6, + borderBottomWidth: 1, + borderBottomColor: pdfColors.border, + }, + resultsHeaderLabel: { fontSize: 8, fontWeight: 700, color: pdfColors.textMuted }, + resultsHeaderCount: { fontSize: 8, color: pdfColors.textTertiary }, + table: { width: '100%' }, + tableRow: { flexDirection: 'row', borderBottomWidth: 1, borderBottomColor: pdfColors.border }, + // The card's own footer already draws a top border below the table, so the last row + // skips its own bottom border rather than stacking a second line on top of that one. + tableRowLast: { borderBottomWidth: 0 }, + tableHeaderRow: { + flexDirection: 'row', + backgroundColor: pdfColors.grayHeader, + borderBottomWidth: 1, + borderBottomColor: pdfColors.border, + }, + tableCell: { + flex: 1, + fontSize: 8, + color: pdfColors.text, + paddingVertical: 4, + paddingHorizontal: 4, + borderRightWidth: 1, + borderRightColor: pdfColors.border, + overflow: 'hidden', + }, + tableHeaderCell: { + flex: 1, + fontSize: 8, + fontWeight: 700, + color: pdfColors.textMuted, + paddingVertical: 4, + paddingHorizontal: 4, + borderRightWidth: 1, + borderRightColor: pdfColors.border, + }, + tableCellNull: { + flex: 1, + fontSize: 8, + color: pdfColors.textTertiary, + fontStyle: 'italic', + paddingVertical: 4, + paddingHorizontal: 4, + borderRightWidth: 1, + borderRightColor: pdfColors.border, + }, + footerRow: { + flexDirection: 'row', + gap: 6, + paddingHorizontal: 10, + paddingVertical: 6, + borderTopWidth: 1, + borderTopColor: pdfColors.border, + }, + footerText: { fontSize: 8, color: pdfColors.textTertiary }, +}) diff --git a/apps/studio/package.json b/apps/studio/package.json index b1e3d63058a..e93e2a12141 100644 --- a/apps/studio/package.json +++ b/apps/studio/package.json @@ -66,6 +66,7 @@ "@next/bundle-analyzer": "16.2.3", "@number-flow/react": "^0.3.2", "@radix-ui/react-use-escape-keydown": "^1.1.1", + "@react-pdf/renderer": "^4.9.0", "@sentry/nextjs": "catalog:", "@sentry/react": "^10.27.0", "@sentry/tanstackstart-react": "catalog:", @@ -154,6 +155,7 @@ "react-use": "^17.5.0", "recharts": "catalog:", "remark-gfm": "^4.0.0", + "remark-parse": "^11.0.0", "shared-data": "workspace:*", "sonner": "^1.5.0", "sql-formatter": "^15.0.0", @@ -162,6 +164,7 @@ "tus-js-client": "^4.1.0", "ui": "workspace:*", "ui-patterns": "workspace:*", + "unified": "^11.0.5", "use-debounce": "^7.0.1", "use-stick-to-bottom": "^1.1.1", "uuid": "^14.0.0", @@ -188,6 +191,7 @@ "@types/file-saver": "^2.0.2", "@types/json-logic-js": "^1.2.1", "@types/lodash": "^4.14.172", + "@types/mdast": "^4.0.4", "@types/mime-db": "^1.43.5", "@types/node": "catalog:", "@types/papaparse": "^5.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b97bd3f4c52..289acde71e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -593,7 +593,7 @@ importers: version: 7.29.7(supports-color@8.1.1) '@graphiql/toolkit': specifier: ^0.9.1 - version: 0.9.1(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0))(graphql@16.11.0) + version: 0.9.1(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3))(graphql@16.11.0) '@graphql-codegen/cli': specifier: 5.0.5 version: 5.0.5(@parcel/watcher@2.5.6)(@types/node@22.13.14)(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.11.0))(graphql@16.11.0)(supports-color@8.1.1)(typescript@6.0.2) @@ -671,7 +671,7 @@ importers: version: 13.2.2 graphiql: specifier: ^4.0.2 - version: 4.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 4.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) npm-run-all: specifier: ^4.1.5 version: 4.1.5 @@ -1032,6 +1032,9 @@ importers: '@radix-ui/react-use-escape-keydown': specifier: ^1.1.1 version: 1.1.1(@types/react@19.2.14)(react@19.2.6) + '@react-pdf/renderer': + specifier: ^4.9.0 + version: 4.9.0(react@19.2.6) '@sentry/nextjs': specifier: 'catalog:' version: 10.59.0(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(encoding@0.1.13)(next@16.3.5(@babel/core@7.29.7(supports-color@8.1.1))(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(@types/node@22.13.14)(babel-plugin-macros@3.1.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(sass@1.77.4))(react@19.2.6)(supports-color@8.1.1)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.28.1)) @@ -1296,6 +1299,9 @@ importers: remark-gfm: specifier: ^4.0.0 version: 4.0.1(supports-color@8.1.1) + remark-parse: + specifier: ^11.0.0 + version: 11.0.0(supports-color@8.1.1) shared-data: specifier: workspace:* version: link:../../packages/shared-data @@ -1320,6 +1326,9 @@ importers: ui-patterns: specifier: workspace:* version: link:../../packages/ui-patterns + unified: + specifier: ^11.0.5 + version: 11.0.5 use-debounce: specifier: ^7.0.1 version: 7.0.1(react@19.2.6) @@ -1393,6 +1402,9 @@ importers: '@types/lodash': specifier: ^4.14.172 version: 4.17.5 + '@types/mdast': + specifier: ^4.0.4 + version: 4.0.4 '@types/mime-db': specifier: ^1.43.5 version: 1.43.5 @@ -7195,6 +7207,52 @@ packages: peerDependencies: react: '>=16.8' + '@react-pdf/fns@3.1.3': + resolution: {integrity: sha512-0I7pApDr1/RLAKbizuLy/IHTEa93LSPy/bEwYniboC3Xqnp6Od8xFJKbKEzGw2wh/5zKFFwl00g4t9RwgIMc3w==} + + '@react-pdf/font@4.1.2': + resolution: {integrity: sha512-RT/jiGWIRjIC9c4S9NiqhEyfuA6YL+DtWL3Rm+k+zQhfeHYvHwGzFxr7ZJzThIMrT8sIQy+mpvFvfYyitEei/Q==} + + '@react-pdf/hyphenate@0.1.0': + resolution: {integrity: sha512-CWulbuusHh2Lnos9ZffT3ZYfjCt332Yl8wjSyCKWbwhbTpe/VdFt53fM5elPp3HU3R6tzH6j3oYlLXDwC2WEHQ==} + + '@react-pdf/image@3.1.2': + resolution: {integrity: sha512-89vlvZCCv1hPunFtyeS2rJTRL8h2yYmTI97AM4ppibS79zGsPFbqz/YrAunndcHB9uwGdn5S3+5k18mj0L2vkg==} + + '@react-pdf/layout@5.2.0': + resolution: {integrity: sha512-Hzf9cShT1bKkr9tZ+A9qK/wHqUloOCgbNer72EquQS6QzqVGiDdtt6Wh9lQXwkd89U7yZolTLnFqP/dRdoRROw==} + + '@react-pdf/paginate@1.0.1': + resolution: {integrity: sha512-JN6teDqkdpkcRhdGZqyrr7Y8flgTtyI8XS3yaBQLyiGjEBPbR9m/19cz2z3JkqYpApau5pEHZlLjwGqMurWysw==} + + '@react-pdf/primitives@4.4.0': + resolution: {integrity: sha512-BFpuhNH6ffSFjTTMnpdUoZxWoXdhPmFDdrSVBl0i/zMR4yDTEfYOW3AcfjjmNIOpm9+LnIXbgELLklQJ+nD3oA==} + + '@react-pdf/reconciler@2.0.0': + resolution: {integrity: sha512-7zaPRujpbHSmCpIrZ+b9HSTJHthcVZzX0Wx7RzvQGsGBUbHP4p6s5itXrAIOuQuPvDepoHGNOvf6xUuMVvdoyw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@react-pdf/render@4.7.0': + resolution: {integrity: sha512-UEMgR7gBmCJiKpuu9alY6QeZVNB+7b4DGHc7Hi8QIl+5PfHvyq+TV70N7+ngZ6wN7hF3XSiWz7GDuYxPIx5eRw==} + + '@react-pdf/renderer@4.9.0': + resolution: {integrity: sha512-RAbARMwjcSYEpqpaWoWXz0uOGF1HjAl5hdJr889r5mgkPhF9xhX4CXSvcEbLTgFVj+KWbcar31MzcXYoANk/Ag==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@react-pdf/stylesheet@6.3.2': + resolution: {integrity: sha512-UR247sNBx2k3RdL8JUCpUiyx39yQsUABagv3uAi91iMZCORE+fSCsOh7MOcEImmpms4GihydLGudbngI5g14PA==} + + '@react-pdf/svg@1.1.1': + resolution: {integrity: sha512-m1GmGxV2wg/3VpoQ0aCJ598fBedCCfN+HtxKx1lq5kdwUWEaTbfXI1DGFAUedprFDd/i24okKFaUhV/KVZIqIQ==} + + '@react-pdf/textkit@7.0.1': + resolution: {integrity: sha512-ljY/YoEETIOR/+zxWdLLmKlupz8/nBsbmxglM3mDRco62UEzEPnIeMEzYVVVraovCAJC2t+50gKFNSV17FYxbw==} + + '@react-pdf/types@2.14.0': + resolution: {integrity: sha512-TYHpThdf2sz/d1g+CP9nWjYCJ8BYBUN5ORL6+60A85Js9S/YouK8OXzi+hDpYUSZJTOdYXdRxUNeG2oW8//Ulg==} + '@react-router/dev@7.13.2': resolution: {integrity: sha512-8Lgf+WCEIPDhp22YB3fyoiWnNyM39sjkfWnSxAwy+Sg83OHxnQFQg0OK1oPM9lm1n/hxJe4lLYOPNwDSyeGiog==} engines: {node: '>=20.0.0'} @@ -9860,6 +9918,9 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + abs-svg-path@0.1.1: + resolution: {integrity: sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -10348,6 +10409,9 @@ packages: peerDependencies: zod: ^3.25.34 || ^4.0 + brotli@1.3.3: + resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} + browserslist@4.28.8: resolution: {integrity: sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -10615,6 +10679,10 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + clsx@1.2.1: resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} engines: {node: '>=6'} @@ -10679,9 +10747,17 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-name@2.1.1: + resolution: {integrity: sha512-p2FdgwVx1a9yWBHP2wI0VgShkDpgN4kZISkxdNipGBJWpa5G6b04OINlVWCyJj0JmfvcPrgqt95E9k8yvaOJFg==} + engines: {node: '>=12.20'} + color-string@1.9.1: resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + color-string@2.1.4: + resolution: {integrity: sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==} + engines: {node: '>=18'} + color-support@1.1.3: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true @@ -11419,6 +11495,9 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dfa@1.2.0: + resolution: {integrity: sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==} + diacritics-map@0.1.0: resolution: {integrity: sha512-3omnDTYrGigU0i4cJjvaKwD52B8aoqyX/NEIkukFFkogBemsIbhSa1O414fpTp5nuszJG6lvQ5vBvDVNCbSsaQ==} engines: {node: '>=0.8.0'} @@ -11540,6 +11619,9 @@ packages: elkjs@0.11.1: resolution: {integrity: sha512-zxxR9k+rx5ktMwT/FwyLdPCrq7xN6e4VGGHH8hA01vVYKjTFik7nHOxBnAYtrgYUB1RpAiLvA1/U2YraWxyKKg==} + emoji-regex-xs@1.0.0: + resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} + emoji-regex@10.3.0: resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} @@ -12328,6 +12410,9 @@ packages: fontace@0.4.1: resolution: {integrity: sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==} + fontkit@2.0.4: + resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==} + fontkitten@1.0.3: resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==} engines: {node: '>=20'} @@ -12938,6 +13023,12 @@ packages: hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + hsl-to-hex@1.0.0: + resolution: {integrity: sha512-K6GVpucS5wFf44X0h2bLVRDsycgJmf9FF2elg+CrqD8GcFU8c6vYhgXn8NjUkFCwj+xDFb70qgLbTUm6sxwPmA==} + + hsl-to-rgb-for-reals@1.1.1: + resolution: {integrity: sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==} + html-encoding-sniffer@6.0.0: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -13012,6 +13103,9 @@ packages: humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + hyphen@1.6.6: + resolution: {integrity: sha512-XtqmnT+b9n5MX+MsqluFAVTIenbtC25iskW0Z+jLd+awfhA+ZbWKWQMIvLJccGoa2bM1R6juWJ27cZxIFOmkWw==} + hyphenate-style-name@1.0.4: resolution: {integrity: sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==} @@ -13447,6 +13541,9 @@ packages: is-upper-case@2.0.2: resolution: {integrity: sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==} + is-url@1.2.4: + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -13524,6 +13621,9 @@ packages: engines: {node: '>=10'} hasBin: true + jay-peg@1.1.1: + resolution: {integrity: sha512-D62KEuBxz/ip2gQKOEhk/mx14o7eiFRaU+VNNSP4MOiIkwb/D6B3G1Mfas7C/Fit8EsSV2/IWjZElx/Gs6A4ww==} + jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} @@ -14295,6 +14395,9 @@ packages: mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + media-engine@2.0.0: + resolution: {integrity: sha512-FqNmlXKYrp5d3g7xEOMJb+6gXZE0fTssdyIQqYR4LbkifbNbS0hDylhNDOh8r6tCgLboHd8+mwgH2njgSYDpnQ==} + media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -15030,6 +15133,9 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + normalize-svg-path@1.1.0: + resolution: {integrity: sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==} + nostics@1.2.0: resolution: {integrity: sha512-FGqEfhQjrvo1lL8KFifdTQiNwwQHJxC1jtYE1Rc54qF/jxONUNL+kC9gS1krX8Q65PgrQ5fCqH/I4NhWBvdSqg==} @@ -15443,6 +15549,9 @@ packages: parse-numeric-range@1.3.0: resolution: {integrity: sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==} + parse-svg-path@0.1.2: + resolution: {integrity: sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==} + parse5-htmlparser2-tree-adapter@7.1.0: resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} @@ -15540,6 +15649,9 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pdfkit@0.20.1: + resolution: {integrity: sha512-1rRXK6x5o8I/3dBrBzXfxibpHpkfCnIA7EBAES7pEpGFc/65inMLlA8SalGWpJfal7BGekxeLf6A30IOpQpc5Q==} + perfect-debounce@2.1.0: resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} @@ -15657,6 +15769,9 @@ packages: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} + png-js@2.0.0: + resolution: {integrity: sha512-GdzJuUMc6ZSpxFJWVxtOH1bzYHym+TOnveqUjb+VJIbZWbZzyiRGFiKhbiielfpYbgMlhHVhsJ0FTazfuRFkMA==} + points-on-curve@0.2.0: resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} @@ -16032,6 +16147,9 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + queue@6.0.2: + resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==} + radix-ui@1.4.3: resolution: {integrity: sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA==} peerDependencies: @@ -16568,6 +16686,9 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} + restructure@3.0.2: + resolution: {integrity: sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==} + ret@0.1.15: resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} engines: {node: '>=0.12'} @@ -16745,6 +16866,9 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} + scheduler@0.25.0-rc-603e6108-20241029: + resolution: {integrity: sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA==} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -17384,6 +17508,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + svg-arc-to-cubic-bezier@3.2.0: + resolution: {integrity: sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==} + svgo@4.1.0: resolution: {integrity: sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==} engines: {node: '>=16'} @@ -17809,6 +17936,9 @@ packages: vite: optional: true + unicode-properties@1.4.1: + resolution: {integrity: sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==} + unicode-trie@2.0.0: resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} @@ -18800,6 +18930,9 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} + yoga-layout@3.2.1: + resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} + yoga-wasm-web@0.3.3: resolution: {integrity: sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==} @@ -20615,9 +20748,9 @@ snapshots: '@floating-ui/utils@0.2.9': {} - '@graphiql/plugin-doc-explorer@0.0.1(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@graphiql/plugin-doc-explorer@0.0.1(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@graphiql/react': 0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@graphiql/react': 0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@headlessui/react': 2.2.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) graphql: 16.11.0 react: 19.2.6 @@ -20645,10 +20778,10 @@ snapshots: - immer - use-sync-external-store - '@graphiql/plugin-history@0.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@graphiql/plugin-history@0.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@graphiql/react': 0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@graphiql/toolkit': 0.11.3(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0))(graphql@16.11.0) + '@graphiql/react': 0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@graphiql/toolkit': 0.11.3(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3))(graphql@16.11.0) react: 19.2.6 react-compiler-runtime: 19.1.0-rc.1(react@19.2.6) react-dom: 19.2.6(react@19.2.6) @@ -20677,9 +20810,9 @@ snapshots: - immer - use-sync-external-store - '@graphiql/react@0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@graphiql/react@0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@graphiql/toolkit': 0.11.3(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0))(graphql@16.11.0) + '@graphiql/toolkit': 0.11.3(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3))(graphql@16.11.0) '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -20747,23 +20880,23 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@graphiql/toolkit@0.11.3(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0))(graphql@16.11.0)': + '@graphiql/toolkit@0.11.3(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3))(graphql@16.11.0)': dependencies: '@n1ru4l/push-pull-async-iterable-iterator': 3.2.0 graphql: 16.11.0 meros: 1.3.0(@types/node@22.13.14) optionalDependencies: - graphql-ws: 6.0.4(graphql@16.11.0)(ws@8.21.0) + graphql-ws: 6.0.4(graphql@16.11.0)(ws@8.21.3) transitivePeerDependencies: - '@types/node' - '@graphiql/toolkit@0.9.1(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0))(graphql@16.11.0)': + '@graphiql/toolkit@0.9.1(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3))(graphql@16.11.0)': dependencies: '@n1ru4l/push-pull-async-iterable-iterator': 3.2.0 graphql: 16.11.0 meros: 1.3.0(@types/node@22.13.14) optionalDependencies: - graphql-ws: 6.0.4(graphql@16.11.0)(ws@8.21.0) + graphql-ws: 6.0.4(graphql@16.11.0)(ws@8.21.3) transitivePeerDependencies: - '@types/node' @@ -23902,6 +24035,104 @@ snapshots: dependencies: react: 19.2.6 + '@react-pdf/fns@3.1.3': {} + + '@react-pdf/font@4.1.2': + dependencies: + '@react-pdf/types': 2.14.0 + fontkit: 2.0.4 + is-url: 1.2.4 + pdfkit: 0.20.1 + + '@react-pdf/hyphenate@0.1.0': + dependencies: + hyphen: 1.6.6 + + '@react-pdf/image@3.1.2': + dependencies: + '@react-pdf/svg': 1.1.1 + jay-peg: 1.1.1 + png-js: 2.0.0 + + '@react-pdf/layout@5.2.0': + dependencies: + '@react-pdf/fns': 3.1.3 + '@react-pdf/image': 3.1.2 + '@react-pdf/paginate': 1.0.1 + '@react-pdf/primitives': 4.4.0 + '@react-pdf/stylesheet': 6.3.2 + '@react-pdf/textkit': 7.0.1 + '@react-pdf/types': 2.14.0 + emoji-regex-xs: 1.0.0 + queue: 6.0.2 + yoga-layout: 3.2.1 + + '@react-pdf/paginate@1.0.1': {} + + '@react-pdf/primitives@4.4.0': {} + + '@react-pdf/reconciler@2.0.0(react@19.2.6)': + dependencies: + object-assign: 4.1.1 + react: 19.2.6 + scheduler: 0.25.0-rc-603e6108-20241029 + + '@react-pdf/render@4.7.0': + dependencies: + '@babel/runtime': 7.26.10 + '@react-pdf/fns': 3.1.3 + '@react-pdf/primitives': 4.4.0 + '@react-pdf/textkit': 7.0.1 + '@react-pdf/types': 2.14.0 + abs-svg-path: 0.1.1 + color-string: 2.1.4 + normalize-svg-path: 1.1.0 + parse-svg-path: 0.1.2 + svg-arc-to-cubic-bezier: 3.2.0 + + '@react-pdf/renderer@4.9.0(react@19.2.6)': + dependencies: + '@babel/runtime': 7.26.10 + '@react-pdf/fns': 3.1.3 + '@react-pdf/font': 4.1.2 + '@react-pdf/layout': 5.2.0 + '@react-pdf/primitives': 4.4.0 + '@react-pdf/reconciler': 2.0.0(react@19.2.6) + '@react-pdf/render': 4.7.0 + '@react-pdf/types': 2.14.0 + events: 3.3.0 + object-assign: 4.1.1 + pdfkit: 0.20.1 + prop-types: 15.8.1 + queue: 6.0.2 + react: 19.2.6 + + '@react-pdf/stylesheet@6.3.2': + dependencies: + '@react-pdf/fns': 3.1.3 + '@react-pdf/types': 2.14.0 + color-string: 2.1.4 + hsl-to-hex: 1.0.0 + media-engine: 2.0.0 + postcss-value-parser: 4.2.0 + + '@react-pdf/svg@1.1.1': + dependencies: + '@react-pdf/primitives': 4.4.0 + + '@react-pdf/textkit@7.0.1': + dependencies: + '@react-pdf/fns': 3.1.3 + '@react-pdf/hyphenate': 0.1.0 + bidi-js: 1.0.3 + unicode-properties: 1.4.1 + + '@react-pdf/types@2.14.0': + dependencies: + '@react-pdf/font': 4.1.2 + '@react-pdf/primitives': 4.4.0 + '@react-pdf/stylesheet': 6.3.2 + '@react-router/dev@7.13.2(@react-router/serve@7.17.0(react-router@7.18.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(supports-color@8.1.1)(typescript@6.0.2))(@types/node@22.13.14)(babel-plugin-macros@3.1.0)(jiti@2.7.0)(lightningcss@1.33.0)(react-router@7.18.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(sass@1.77.4)(supports-color@8.1.1)(terser@5.48.0)(tsx@4.22.4)(typescript@6.0.2)(vite@8.2.1(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.48.0)(tsx@4.22.4)(yaml@2.9.0))(yaml@2.9.0)': dependencies: '@babel/core': 7.29.7(supports-color@8.1.1) @@ -26952,6 +27183,8 @@ snapshots: dependencies: event-target-shim: 5.0.1 + abs-svg-path@0.1.1: {} + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -27612,6 +27845,10 @@ snapshots: - '@aws-sdk/credential-provider-web-identity' - supports-color + brotli@1.3.3: + dependencies: + base64-js: 1.5.1 + browserslist@4.28.8: dependencies: baseline-browser-mapping: 2.11.13 @@ -27917,6 +28154,8 @@ snapshots: clone@1.0.4: {} + clone@2.1.2: {} + clsx@1.2.1: {} clsx@2.1.1: {} @@ -27971,11 +28210,17 @@ snapshots: color-name@1.1.4: {} + color-name@2.1.1: {} + color-string@1.9.1: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 + color-string@2.1.4: + dependencies: + color-name: 2.1.1 + color-support@1.1.3: {} color@4.2.3: @@ -28707,6 +28952,8 @@ snapshots: dependencies: dequal: 2.0.3 + dfa@1.2.0: {} + diacritics-map@0.1.0: {} diff@5.2.2: {} @@ -28818,6 +29065,8 @@ snapshots: elkjs@0.11.1: {} + emoji-regex-xs@1.0.0: {} + emoji-regex@10.3.0: {} emoji-regex@8.0.0: {} @@ -29862,6 +30111,18 @@ snapshots: dependencies: fontkitten: 1.0.3 + fontkit@2.0.4: + dependencies: + '@swc/helpers': 0.5.23 + brotli: 1.3.3 + clone: 2.1.2 + dfa: 1.2.0 + fast-deep-equal: 3.1.3 + restructure: 3.0.2 + tiny-inflate: 1.0.3 + unicode-properties: 1.4.1 + unicode-trie: 2.0.0 + fontkitten@1.0.3: dependencies: tiny-inflate: 1.0.3 @@ -30175,11 +30436,11 @@ snapshots: graceful-fs@4.2.11: {} - graphiql@4.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + graphiql@4.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: - '@graphiql/plugin-doc-explorer': 0.0.1(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@graphiql/plugin-history': 0.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@graphiql/react': 0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@graphiql/plugin-doc-explorer': 0.0.1(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@graphiql/plugin-history': 0.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@graphiql/react': 0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.4.0)(@types/node@22.13.14)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3))(graphql@16.11.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) graphql: 16.11.0 react: 19.2.6 react-compiler-runtime: 19.1.0-rc.1(react@19.2.6) @@ -30272,13 +30533,6 @@ snapshots: dependencies: graphql: 16.11.0 - graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.0): - dependencies: - graphql: 16.11.0 - optionalDependencies: - ws: 8.21.0 - optional: true - graphql-ws@6.0.4(graphql@16.11.0)(ws@8.21.3): dependencies: graphql: 16.11.0 @@ -30616,6 +30870,12 @@ snapshots: hosted-git-info@2.8.9: {} + hsl-to-hex@1.0.0: + dependencies: + hsl-to-rgb-for-reals: 1.1.1 + + hsl-to-rgb-for-reals@1.1.1: {} + html-encoding-sniffer@6.0.0(@noble/hashes@1.8.0): dependencies: '@exodus/bytes': 1.15.0(@noble/hashes@1.8.0) @@ -30703,6 +30963,8 @@ snapshots: dependencies: ms: 2.1.3 + hyphen@1.6.6: {} + hyphenate-style-name@1.0.4: {} iceberg-js@0.8.1: {} @@ -31102,6 +31364,8 @@ snapshots: dependencies: tslib: 2.8.1 + is-url@1.2.4: {} + is-weakmap@2.0.2: {} is-weakref@1.1.1: @@ -31175,6 +31439,10 @@ snapshots: filelist: 1.0.4 minimatch: 3.1.4 + jay-peg@1.1.1: + dependencies: + restructure: 3.0.2 + jest-worker@27.5.1: dependencies: '@types/node': 22.13.14 @@ -32167,6 +32435,8 @@ snapshots: mdurl@2.0.0: {} + media-engine@2.0.0: {} + media-typer@0.3.0: {} media-typer@1.1.0: {} @@ -33386,6 +33656,10 @@ snapshots: normalize-path@3.0.0: {} + normalize-svg-path@1.1.0: + dependencies: + svg-arc-to-cubic-bezier: 3.2.0 + nostics@1.2.0: {} npm-run-all@4.1.5: @@ -33984,6 +34258,8 @@ snapshots: parse-numeric-range@1.3.0: {} + parse-svg-path@0.1.2: {} + parse5-htmlparser2-tree-adapter@7.1.0: dependencies: domhandler: 5.0.3 @@ -34065,6 +34341,15 @@ snapshots: pathe@2.0.3: {} + pdfkit@0.20.1: + dependencies: + '@noble/ciphers': 1.3.0 + '@noble/hashes': 1.8.0 + fflate: 0.8.3 + fontkit: 2.0.4 + linebreak: 1.1.0 + png-js: 2.0.0 + perfect-debounce@2.1.0: {} pg-cloudflare@1.2.7: @@ -34168,6 +34453,10 @@ snapshots: pluralize@8.0.0: {} + png-js@2.0.0: + dependencies: + fflate: 0.8.3 + points-on-curve@0.2.0: {} points-on-path@0.2.1: @@ -34504,6 +34793,10 @@ snapshots: queue-microtask@1.2.3: {} + queue@6.0.2: + dependencies: + inherits: 2.0.4 + radix-ui@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: '@radix-ui/primitive': 1.1.3 @@ -35270,6 +35563,8 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 + restructure@3.0.2: {} + ret@0.1.15: {} ret@0.2.2: {} @@ -35540,6 +35835,8 @@ snapshots: dependencies: xmlchars: 2.2.0 + scheduler@0.25.0-rc-603e6108-20241029: {} + scheduler@0.27.0: {} schema-utils@3.3.0: @@ -36359,6 +36656,8 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svg-arc-to-cubic-bezier@3.2.0: {} + svgo@4.1.0: dependencies: commander: 11.1.0 @@ -36793,6 +37092,11 @@ snapshots: - unloader - webpack + unicode-properties@1.4.1: + dependencies: + base64-js: 1.5.1 + unicode-trie: 2.0.0 + unicode-trie@2.0.0: dependencies: pako: 0.2.9 @@ -37843,6 +38147,8 @@ snapshots: yoctocolors@2.1.2: {} + yoga-layout@3.2.1: {} + yoga-wasm-web@0.3.3: {} youch-core@0.3.3: