mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
## 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! 🙂 🙏 <img width="248" height="207" alt="image" src="https://github.com/user-attachments/assets/e38c14ba-7ee8-40ce-99b1-fa126453bf0f" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## 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. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
73 lines
2.4 KiB
TypeScript
73 lines
2.4 KiB
TypeScript
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<Cell>[]
|
|
results: ReadonlyMap<string, QueryResult>
|
|
}
|
|
|
|
function SupabaseLogoMark() {
|
|
return (
|
|
<Svg viewBox="0 0 109 113" style={pdfStyles.headerLogoMark}>
|
|
<Path
|
|
d="M63.7076 110.284C60.8481 113.885 55.0502 111.912 54.9813 107.314L53.9738 40.0625L99.1935 40.0625C107.384 40.0625 111.952 49.5226 106.859 55.9372L63.7076 110.284Z"
|
|
fill="#3ECF8E"
|
|
/>
|
|
<Path
|
|
d="M45.317 2.07103C48.1765 -1.53037 53.9745 0.442937 54.0434 5.041L54.4849 72.2922H9.83113C1.64038 72.2922 -2.92775 62.8321 2.1655 56.4175L45.317 2.07103Z"
|
|
fill="#3ECF8E"
|
|
/>
|
|
</Svg>
|
|
)
|
|
}
|
|
|
|
export function NotebookPdfDocument({
|
|
name,
|
|
projectName,
|
|
cells,
|
|
results,
|
|
}: NotebookPdfDocumentProps) {
|
|
const exportedDate = dayjs().format('MMM D, YYYY')
|
|
|
|
return (
|
|
<Document title={name}>
|
|
<Page size="A4" style={pdfStyles.page}>
|
|
<View style={pdfStyles.headerBanner}>
|
|
<View style={pdfStyles.headerLeft}>
|
|
<View style={pdfStyles.headerLogoRow}>
|
|
<SupabaseLogoMark />
|
|
<Text style={pdfStyles.headerLogoText}>supabase</Text>
|
|
</View>
|
|
<Text style={pdfStyles.headerLabel}>EXPLORER NOTEBOOK</Text>
|
|
</View>
|
|
<View style={pdfStyles.headerRight}>
|
|
<Text style={pdfStyles.headerTitle}>{name}</Text>
|
|
<Text style={pdfStyles.headerMeta}>Exported {exportedDate}</Text>
|
|
{projectName !== undefined && (
|
|
<Text style={pdfStyles.headerMeta}>Project: {projectName}</Text>
|
|
)}
|
|
</View>
|
|
</View>
|
|
<View style={pdfStyles.headerRule} />
|
|
|
|
{cells.map((cell) =>
|
|
isQueryCell(cell) ? (
|
|
<NotebookPdfQueryCell key={cell._id} cell={cell} result={results.get(cell._id)} />
|
|
) : (
|
|
<NotebookPdfMarkdown key={cell._id} markdown={cell.text} />
|
|
)
|
|
)}
|
|
</Page>
|
|
</Document>
|
|
)
|
|
}
|