mirror of
https://github.com/supabase/supabase.git
synced 2026-10-15 14:19:11 +03:00
## Summary Stacked on #49109 (PR 1 — `deriveNotebookDiff`). This is PR 3 of the notebook approval-preview stack: a pure presentational component that renders the cell-level diff for a proposed notebook create/update, for use in the assistant approval UI (wired in a later PR). - `NotebookPreview` — header summary (`"6 cells"` for create, `"+2 −1 ~1 ↕1"` for update) + entry list + "Show N more cells" for long notebooks. - `NotebookPreviewCell` — dispatches per entry tag: `unchanged`/`removed`/`moved` collapse to a muted badge row; `added` renders source via `CodeBlock` (with a max-height/expand toggle); `replaced` renders a `DiffEditor` diff, plus a before → after metadata line when only `database_identifier`/`time_range` changed (SQL/text identical). - `NotebookPreview.utils` — pure helpers (labels, source/metadata extraction, language mapping, summary formatting), unit tested. - **Safety property**: cell content only ever renders through `CodeBlock`/`DiffEditor` (literal source), never through a markdown renderer — agent-authored text can't trigger image loads or link navigation before the user approves. Covered by an adversarial test (``, `[y](evil)`, `<img onerror>` → zero `img`/`[href]`/`[src]` DOM nodes). - Adds `'markdown'` as a supported `CodeBlock` language (small, additive change to `packages/ui-patterns`). Towards FE-4143 ## Test plan - [x] `pnpm --filter studio test` — NotebookPreview suite (21 tests) passes - [x] `pnpm --filter studio exec eslint components/interfaces/Explorer/NotebookPreview` — clean - [x] `pnpm --filter studio exec tsc --noEmit` — no new errors - [x] `pnpm exec prettier --check` — clean <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added notebook previews showing create and update summaries. * Displayed added, removed, moved, unchanged, and replaced cells with metadata and source diffs. * Added expandable previews with truncation and a “Show more cells” option. * Added Markdown syntax highlighting to code blocks. * **Bug Fixes** * Safely render adversarial agent-authored Markdown as literal content. * **Tests** * Added comprehensive coverage for notebook previews, summaries, metadata, formatting, and truncation. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
49 lines
1.6 KiB
TypeScript
49 lines
1.6 KiB
TypeScript
import { useState } from 'react'
|
|
import { Button } from 'ui'
|
|
|
|
import {
|
|
formatNotebookDiffSummary,
|
|
getEntryKey,
|
|
summarizeNotebookDiff,
|
|
} from './NotebookPreview.utils'
|
|
import { NotebookPreviewCell } from './NotebookPreviewCell'
|
|
import type { NotebookCellDiffEntry } from '@/data/content/notebooks/notebook-operations'
|
|
|
|
export interface NotebookPreviewProps {
|
|
entries: NotebookCellDiffEntry[]
|
|
mode: 'create' | 'update'
|
|
}
|
|
|
|
const VISIBLE_ENTRY_LIMIT = 5
|
|
|
|
/**
|
|
* Read-only preview of a proposed notebook create/update, rendered from a pre-computed diff.
|
|
* Pure presentational component: no data fetching, no approval or notebook-editor state — see
|
|
* `deriveNotebookDiff` for how `entries` is produced.
|
|
*/
|
|
export const NotebookPreview = ({ entries, mode }: NotebookPreviewProps) => {
|
|
const [isExpanded, setIsExpanded] = useState(false)
|
|
|
|
const summary = summarizeNotebookDiff(entries, mode)
|
|
const visibleEntries = isExpanded ? entries : entries.slice(0, VISIBLE_ENTRY_LIMIT)
|
|
const hiddenCount = entries.length - visibleEntries.length
|
|
|
|
return (
|
|
<div className="flex flex-col gap-2">
|
|
<p className="text-xs text-foreground-light font-mono">
|
|
{formatNotebookDiffSummary(summary)}
|
|
</p>
|
|
<div className="flex flex-col gap-1.5">
|
|
{visibleEntries.map((entry) => (
|
|
<NotebookPreviewCell key={getEntryKey(entry)} entry={entry} />
|
|
))}
|
|
</div>
|
|
{hiddenCount > 0 && (
|
|
<Button variant="text" className="w-full" onClick={() => setIsExpanded(true)}>
|
|
Show {hiddenCount} more cell{hiddenCount === 1 ? '' : 's'}
|
|
</Button>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|